blob: a8e2ac9bb63a932c23f190542d7c1523a0c6ae1a [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to access floppy drives.
2//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
8#include "types.h" // u8
9#include "disk.h" // DISK_RET_SUCCESS
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040010#include "config.h" // CONFIG_FLOPPY
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "biosvar.h" // SET_BDA
Kevin O'Connor10ad7992009-10-24 11:06:08 -040012#include "util.h" // wait_irq
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050013#include "cmos.h" // inb_cmos
Kevin O'Connord21c0892008-11-26 17:02:43 -050014#include "pic.h" // eoi_pic1
Kevin O'Connor9521e262008-07-04 13:04:29 -040015#include "bregs.h" // struct bregs
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050016
Kevin O'Connor7b184d82009-08-23 11:56:55 -040017#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
18#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
19#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
20#define FLOPPY_FILLBYTE 0xf6
21#define FLOPPY_GAPLEN 0x1B
22#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050023
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050024// New diskette parameter table adding 3 parameters from IBM
25// Since no provisions are made for multiple drive types, most
26// values in this table are ignored. I set parameters for 1.44M
27// floppy here
Kevin O'Connor372e0712009-09-09 09:51:31 -040028struct floppy_ext_dbt_s diskette_param_table2 VAR16VISIBLE = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050029 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040030 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
31 .specify2 = 0x02, // head load time 4ms, DMA used
32 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
33 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050034 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040035 .interblock_len = FLOPPY_GAPLEN,
36 .data_len = FLOPPY_DATALEN,
37 .gap_len = FLOPPY_FORMAT_GAPLEN,
38 .fill_byte = FLOPPY_FILLBYTE,
39 .settle_time = 0x0F, // 15ms
40 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050041 },
42 .max_track = 79, // maximum track
43 .data_rate = 0, // data transfer rate
44 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050045};
46
Kevin O'Connor30853762009-01-17 18:49:20 -050047// Since no provisions are made for multiple drive types, most
48// values in this table are ignored. I set parameters for 1.44M
49// floppy here
50struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7) = {
51 .specify1 = 0xAF,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040052 .specify2 = 0x02,
53 .shutoff_ticks = FLOPPY_MOTOR_TICKS,
54 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor30853762009-01-17 18:49:20 -050055 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040056 .interblock_len = FLOPPY_GAPLEN,
57 .data_len = FLOPPY_DATALEN,
58 .gap_len = FLOPPY_FORMAT_GAPLEN,
59 .fill_byte = FLOPPY_FILLBYTE,
Kevin O'Connor30853762009-01-17 18:49:20 -050060 .settle_time = 0x0F,
61 .startup_time = 0x08,
62};
63
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040064struct floppyinfo_s {
65 struct chs_s chs;
66 u8 config_data;
67 u8 media_state;
68};
69
Kevin O'Connor372e0712009-09-09 09:51:31 -040070struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040071 // Unknown
72 { {0, 0, 0}, 0x00, 0x00},
73 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
74 { {2, 40, 9}, 0x00, 0x25},
75 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
76 { {2, 80, 15}, 0x00, 0x25},
77 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
78 { {2, 80, 9}, 0x00, 0x17},
79 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
80 { {2, 80, 18}, 0x00, 0x17},
81 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
82 { {2, 80, 36}, 0xCC, 0xD7},
83 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
84 { {1, 40, 8}, 0x00, 0x27},
85 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
86 { {1, 40, 9}, 0x00, 0x27},
87 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
88 { {2, 40, 8}, 0x00, 0x27},
89};
90
Kevin O'Connor77d227b2009-10-22 21:48:39 -040091struct drive_s *
Kevin O'Connora3855ad2009-08-16 21:59:40 -040092addFloppy(int floppyid, int ftype, int driver)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040093{
94 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
95 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040096 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040097 }
98
Kevin O'Connor77d227b2009-10-22 21:48:39 -040099 struct drive_s *drive_g = allocDrive();
100 if (!drive_g)
101 return NULL;
102 drive_g->cntl_id = floppyid;
103 drive_g->type = driver;
104 drive_g->blksize = DISK_SECTOR_SIZE;
105 drive_g->floppy_type = ftype;
106 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400107
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400108 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400109 , sizeof(FloppyInfo[ftype].chs));
110
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400111 map_floppy_drive(drive_g);
112 return drive_g;
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400113}
114
115void
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400116describe_floppy(struct drive_s *drive_g)
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400117{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400118 printf("drive %c", 'A' + drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400119}
120
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400121void
Kevin O'Connorc892b132009-08-11 21:59:37 -0400122floppy_setup()
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400123{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400124 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400125 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400126 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400127
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500128 if (CONFIG_COREBOOT) {
129 // XXX - disable floppies on coreboot for now.
130 } else {
131 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400132 if (type & 0xf0)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400133 addFloppy(0, type >> 4, DTYPE_FLOPPY);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400134 if (type & 0x0f)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400135 addFloppy(1, type & 0x0f, DTYPE_FLOPPY);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400136 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400137
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400138 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400139
Kevin O'Connord21c0892008-11-26 17:02:43 -0500140 enable_hwirq(6, entry_0e);
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400141}
142
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400143// Find a floppy type that matches a given image size.
144int
145find_floppy_type(u32 size)
146{
147 int i;
148 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
149 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400150 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400151 return i;
152 }
153 return -1;
154}
155
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500156
157/****************************************************************
158 * Low-level floppy IO
159 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500160
161static void
162floppy_reset_controller()
163{
164 // Reset controller
165 u8 val8 = inb(PORT_FD_DOR);
166 outb(val8 & ~0x04, PORT_FD_DOR);
167 outb(val8 | 0x04, PORT_FD_DOR);
168
169 // Wait for controller to come out of reset
170 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
171 ;
172}
173
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500174static int
175wait_floppy_irq()
176{
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400177 ASSERT16();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500178 u8 v;
179 for (;;) {
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400180 if (!GET_BDA(floppy_motor_counter))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500181 return -1;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500182 v = GET_BDA(floppy_recalibration_status);
183 if (v & FRS_TIMEOUT)
184 break;
Kevin O'Connoree2efa72009-09-20 15:33:08 -0400185 wait_irq();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500186 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500187
188 v &= ~FRS_TIMEOUT;
189 SET_BDA(floppy_recalibration_status, v);
190 return 0;
191}
192
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500193static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400194floppy_prepare_controller(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500195{
196 CLEARBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
197
198 // turn on motor of selected drive, DMA & int enabled, normal operation
199 u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
200 u8 dor = 0x10;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400201 if (floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500202 dor = 0x20;
203 dor |= 0x0c;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400204 dor |= floppyid;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500205 outb(dor, PORT_FD_DOR);
206
207 // reset the disk motor timeout value of INT 08
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400208 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500209
210 // wait for drive readiness
211 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
212 ;
213
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500214 if (!prev_reset)
215 wait_floppy_irq();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500216}
217
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400218static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500219floppy_pio(u8 *cmd, u8 cmdlen)
220{
221 floppy_prepare_controller(cmd[1] & 1);
222
223 // send command to controller
224 u8 i;
225 for (i=0; i<cmdlen; i++)
226 outb(cmd[i], PORT_FD_DATA);
227
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500228 int ret = wait_floppy_irq();
229 if (ret) {
230 floppy_reset_controller();
231 return -1;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400232 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500233
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500234 return 0;
235}
236
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400237static int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400238floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500239{
240 // es:bx = pointer to where to place information from diskette
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400241 u32 addr = (u32)op->buf_fl;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500242
243 // check for 64K boundary overrun
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400244 u16 end = count - 1;
245 u32 last_addr = addr + end;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400246 if ((addr >> 16) != (last_addr >> 16))
247 return DISK_RET_EBOUNDARY;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500248
249 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
250 if (cmd[0] == 0xe6)
251 // read
252 mode_register = 0x46;
253
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500254 //DEBUGF("floppy dma c2\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500255 outb(0x06, PORT_DMA1_MASK_REG);
256 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connore10d3452008-07-09 22:45:12 -0400257 outb(addr, PORT_DMA_ADDR_2);
258 outb(addr>>8, PORT_DMA_ADDR_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500259 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400260 outb(end, PORT_DMA_CNT_2);
261 outb(end>>8, PORT_DMA_CNT_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500262
263 // port 0b: DMA-1 Mode Register
264 // transfer type=write, channel 2
265 outb(mode_register, PORT_DMA1_MODE_REG);
266
267 // port 81: DMA-1 Page Register, channel 2
Kevin O'Connore10d3452008-07-09 22:45:12 -0400268 outb(addr>>16, PORT_DMA_PAGE_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500269
270 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
271
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400272 int ret = floppy_pio(cmd, cmdlen);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400273 if (ret)
274 return DISK_RET_ETIMEOUT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500275
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500276 // check port 3f4 for accessibility to status bytes
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400277 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
278 return DISK_RET_ECONTROLLER;
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500279
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500280 // read 7 return status bytes from controller
281 u8 i;
282 for (i=0; i<7; i++) {
283 u8 v = inb(PORT_FD_DATA);
284 cmd[i] = v;
285 SET_BDA(floppy_return_status[i], v);
286 }
287
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400288 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500289}
290
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500291
292/****************************************************************
293 * Floppy media sense
294 ****************************************************************/
295
296static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400297set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500298{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400299 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500300}
301
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500302static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400303floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500304{
305 // send Recalibrate command (2 bytes) to controller
306 u8 data[12];
307 data[0] = 0x07; // 07: Recalibrate
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400308 data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500309 floppy_pio(data, 2);
310
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400311 SETBITS_BDA(floppy_recalibration_status, 1<<floppyid);
312 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500313}
314
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500315static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400316floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500317{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500318 // for now cheat and get drive type from CMOS,
319 // assume media is same as drive type
320
321 // ** config_data **
322 // Bitfields for diskette media control:
323 // Bit(s) Description (Table M0028)
324 // 7-6 last data rate set by controller
325 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
326 // 5-4 last diskette drive step rate selected
327 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
328 // 3-2 {data rate at start of operation}
329 // 1-0 reserved
330
331 // ** media_state **
332 // Bitfields for diskette drive media state:
333 // Bit(s) Description (Table M0030)
334 // 7-6 data rate
335 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
336 // 5 double stepping required (e.g. 360kB in 1.2MB)
337 // 4 media type established
338 // 3 drive capable of supporting 4MB media
339 // 2-0 on exit from BIOS, contains
340 // 000 trying 360kB in 360kB
341 // 001 trying 360kB in 1.2MB
342 // 010 trying 1.2MB in 1.2MB
343 // 011 360kB in 360kB established
344 // 100 360kB in 1.2MB established
345 // 101 1.2MB in 1.2MB established
346 // 110 reserved
347 // 111 all other formats/drives
348
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400349 u8 ftype = GET_GLOBAL(drive_g->floppy_type);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400350 SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400351 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400352 SET_BDA(floppy_media_state[floppyid]
353 , GET_GLOBAL(FloppyInfo[ftype].media_state));
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400354 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500355}
356
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400357static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400358check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500359{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400360 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400361 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
362 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500363 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400364 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500365
366 // Recalibrate drive.
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400367 floppy_drive_recal(floppyid);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500368
369 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400370 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500371}
372
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500373
374/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400375 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500376 ****************************************************************/
377
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500378static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400379lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500380{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400381 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400382
383 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400384 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400385 *sector = tmp % nlspt;
386
387 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400388 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400389 *head = tmp % nlh;
390
391 tmp /= nlh;
392 *track = tmp;
393}
394
395// diskette controller reset
396static int
397floppy_reset(struct disk_op_s *op)
398{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400399 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400400 set_diskette_current_cyl(floppyid, 0); // current cylinder
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400401 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500402}
403
404// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400405static int
406floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500407{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400408 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400409 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400410 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500411
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400412 u8 track, sector, head;
413 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500414
415 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400416 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500417 u8 data[12];
418 data[0] = 0xe6; // e6: read normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400419 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500420 data[2] = track;
421 data[3] = head;
422 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400423 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400424 data[6] = sector + op->count - 1; // last sector to read on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400425 data[7] = FLOPPY_GAPLEN;
426 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500427
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400428 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400429 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400430 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500431
432 if (data[0] & 0xc0) {
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400433 res = DISK_RET_ECONTROLLER;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400434 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500435 }
436
437 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400438 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400439 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400440fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400441 op->count = 0; // no sectors read
442 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500443}
444
445// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400446static int
447floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500448{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400449 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400450 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400451 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500452
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400453 u8 track, sector, head;
454 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500455
456 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400457 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500458 u8 data[12];
459 data[0] = 0xc5; // c5: write normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400460 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500461 data[2] = track;
462 data[3] = head;
463 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400464 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400465 data[6] = sector + op->count - 1; // last sector to write on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400466 data[7] = FLOPPY_GAPLEN;
467 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500468
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400469 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400470 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400471 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500472
473 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500474 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400475 res = DISK_RET_EWRITEPROTECT;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500476 else
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400477 res = DISK_RET_ECONTROLLER;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500478 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500479 }
480
481 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400482 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400483 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400484fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400485 op->count = 0; // no sectors read
486 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500487}
488
489// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400490static int
491floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500492{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400493 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400494 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400495 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500496
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400497 u8 track, sector, head;
498 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500499
500 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400501 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400502 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400503 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400504fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400505 op->count = 0; // no sectors read
506 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500507}
508
509// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400510static int
511floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500512{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400513 int ret = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400514 if (ret)
515 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500516
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400517 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500518
519 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400520 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500521 u8 data[12];
522 data[0] = 0x4d; // 4d: format track
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400523 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400524 data[2] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400525 data[3] = op->count; // number of sectors per track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400526 data[4] = FLOPPY_FORMAT_GAPLEN;
527 data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500528
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400529 ret = floppy_cmd(op, op->count * 4, data, 6);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400530 if (ret)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400531 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500532
533 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500534 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400535 return DISK_RET_EWRITEPROTECT;
536 return DISK_RET_ECONTROLLER;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500537 }
538
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400539 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400540 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500541}
542
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400543int
544process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500545{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400546 if (!CONFIG_FLOPPY)
547 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500548
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400549 switch (op->command) {
550 case CMD_RESET:
551 return floppy_reset(op);
552 case CMD_READ:
553 return floppy_read(op);
554 case CMD_WRITE:
555 return floppy_write(op);
556 case CMD_VERIFY:
557 return floppy_verify(op);
558 case CMD_FORMAT:
559 return floppy_format(op);
560 default:
561 op->count = 0;
562 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500563 }
564}
565
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500566
567/****************************************************************
568 * HW irqs
569 ****************************************************************/
570
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500571// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500572void VISIBLE16
Kevin O'Connored128492008-03-11 11:14:59 -0400573handle_0e()
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500574{
Kevin O'Connor15c1f222008-06-12 22:59:43 -0400575 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400576 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400577 goto done;
578
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500579 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
580 outb(0x08, PORT_FD_DATA); // sense interrupt status
581 while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
582 ;
583 do {
584 inb(PORT_FD_DATA);
585 } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
586 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500587 // diskette interrupt has occurred
588 SETBITS_BDA(floppy_recalibration_status, FRS_TIMEOUT);
Kevin O'Connor40967022008-07-21 22:23:05 -0400589
590done:
591 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500592}
593
594// Called from int08 handler.
595void
596floppy_tick()
597{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400598 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400599 return;
600
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500601 // time to turn off drive(s)?
602 u8 fcount = GET_BDA(floppy_motor_counter);
603 if (fcount) {
604 fcount--;
605 SET_BDA(floppy_motor_counter, fcount);
606 if (fcount == 0)
607 // turn motor(s) off
608 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
609 }
610}