blob: c8ae756c071956f6e98f3748404076ee11152b26 [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'Connor94c749c2012-05-28 11:44:02 -040012#include "util.h" // dprintf
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050013#include "cmos.h" // inb_cmos
Kevin O'Connoraa7c2342013-07-14 15:07:21 -040014#include "pic.h" // pic_eoi1
Kevin O'Connor9521e262008-07-04 13:04:29 -040015#include "bregs.h" // struct bregs
Kevin O'Connor72eee3e2010-12-27 19:07:49 -050016#include "boot.h" // boot_add_floppy
Kevin O'Connor031ef552010-12-27 19:26:57 -050017#include "pci.h" // pci_to_bdf
18#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
Kevin O'Connor41639f82013-09-14 19:37:36 -040019#include "romfile.h" // romfile_loadint
Kevin O'Connor3df600b2013-09-14 19:28:55 -040020#include "stacks.h" // yield
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040021#include "string.h" // memset
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050022
Kevin O'Connor7b184d82009-08-23 11:56:55 -040023#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
24#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
25#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
26#define FLOPPY_FILLBYTE 0xf6
27#define FLOPPY_GAPLEN 0x1B
28#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connor068cb4f2013-03-03 11:05:19 -050029#define FLOPPY_PIO_TIMEOUT 1000
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050030
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050031// New diskette parameter table adding 3 parameters from IBM
32// Since no provisions are made for multiple drive types, most
33// values in this table are ignored. I set parameters for 1.44M
34// floppy here
Kevin O'Connor89a2f962013-02-18 23:36:03 -050035struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050036 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040037 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
38 .specify2 = 0x02, // head load time 4ms, DMA used
39 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
40 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050041 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040042 .interblock_len = FLOPPY_GAPLEN,
43 .data_len = FLOPPY_DATALEN,
44 .gap_len = FLOPPY_FORMAT_GAPLEN,
45 .fill_byte = FLOPPY_FILLBYTE,
46 .settle_time = 0x0F, // 15ms
47 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050048 },
49 .max_track = 79, // maximum track
50 .data_rate = 0, // data transfer rate
51 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050052};
53
Kevin O'Connoref719892012-11-26 11:14:00 -050054struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050055
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040056struct floppyinfo_s {
57 struct chs_s chs;
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050058 u8 floppy_size;
59 u8 data_rate;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040060};
61
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050062#define FLOPPY_SIZE_525 0x01
63#define FLOPPY_SIZE_350 0x02
64
65#define FLOPPY_RATE_500K 0x00
66#define FLOPPY_RATE_300K 0x01
67#define FLOPPY_RATE_250K 0x02
68#define FLOPPY_RATE_1M 0x03
69
Kevin O'Connor89a2f962013-02-18 23:36:03 -050070struct floppyinfo_s FloppyInfo[] VARFSEG = {
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
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050074 { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040075 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050076 { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040077 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050078 { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040079 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050080 { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040081 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050082 { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040083 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050084 { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040085 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050086 { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040087 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050088 { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040089};
90
Kevin O'Connor77d227b2009-10-22 21:48:39 -040091struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050092init_floppy(int floppyid, int ftype)
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'Connord7e998f2010-02-15 22:48:28 -050099 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500100 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500101 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400102 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500103 }
104 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400105 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500106 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400107 drive_g->blksize = DISK_SECTOR_SIZE;
108 drive_g->floppy_type = ftype;
109 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400110
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400111 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400112 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500113 return drive_g;
114}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400115
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500116static void
117addFloppy(int floppyid, int ftype)
118{
119 struct drive_s *drive_g = init_floppy(floppyid, ftype);
120 if (!drive_g)
121 return;
122 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400123 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400124 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500125 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400126}
127
128void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500129floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400130{
Kevin O'Connoref719892012-11-26 11:14:00 -0500131 memcpy(&diskette_param_table, &diskette_param_table2
132 , sizeof(diskette_param_table));
133 SET_IVT(0x1E, SEGOFF(SEG_BIOS
134 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
135
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400136 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400137 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400138 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400139
Kevin O'Connor897fb112013-02-07 23:32:48 -0500140 if (CONFIG_QEMU) {
141 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
142 if (type & 0xf0)
143 addFloppy(0, type >> 4);
144 if (type & 0x0f)
145 addFloppy(1, type & 0x0f);
146 } else {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500147 u8 type = romfile_loadint("etc/floppy0", 0);
148 if (type)
149 addFloppy(0, type);
150 type = romfile_loadint("etc/floppy1", 0);
151 if (type)
152 addFloppy(1, type);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400153 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400154
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400155 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400156
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400157 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400158}
159
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400160// Find a floppy type that matches a given image size.
161int
162find_floppy_type(u32 size)
163{
164 int i;
165 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
166 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400167 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400168 return i;
169 }
170 return -1;
171}
172
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500173
174/****************************************************************
175 * Low-level floppy IO
176 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500177
178static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500179floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500180{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500181 outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500182}
183
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500184static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500185floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500186{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500187 u8 frs = GET_BDA(floppy_recalibration_status);
188 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500189 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500190 if (!GET_BDA(floppy_motor_counter)) {
191 floppy_disable_controller();
192 return DISK_RET_ETIMEOUT;
193 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400194 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500195 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500196 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400197 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500198 // bochs, so use yield() instead.
199 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500200 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500201
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500202 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
203 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500204}
205
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500206struct floppy_pio_s {
207 u8 cmdlen;
208 u8 resplen;
209 u8 waitirq;
210 u8 data[9];
211};
212
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400213static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500214floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500215{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500216 // Send command to controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400217 u32 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500218 int i = 0;
219 for (;;) {
220 u8 sts = inb(PORT_FD_STATUS);
221 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400222 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500223 floppy_disable_controller();
224 return DISK_RET_ETIMEOUT;
225 }
226 continue;
227 }
228 if (sts & 0x40) {
229 floppy_disable_controller();
230 return DISK_RET_ECONTROLLER;
231 }
232 outb(pio->data[i++], PORT_FD_DATA);
233 if (i >= pio->cmdlen)
234 break;
235 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500236
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500237 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500238 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500239 int ret = floppy_wait_irq();
240 if (ret)
241 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400242 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500243
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500244 // Read response from controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400245 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500246 i = 0;
247 for (;;) {
248 u8 sts = inb(PORT_FD_STATUS);
249 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400250 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500251 floppy_disable_controller();
252 return DISK_RET_ETIMEOUT;
253 }
254 continue;
255 }
256 if (i >= pio->resplen)
257 break;
258 if (!(sts & 0x40)) {
259 floppy_disable_controller();
260 return DISK_RET_ECONTROLLER;
261 }
262 pio->data[i++] = inb(PORT_FD_DATA);
263 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500264
265 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500266}
267
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500268static int
269floppy_enable_controller(void)
270{
271 outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
272 int ret = floppy_wait_irq();
273 if (ret)
274 return ret;
275
276 struct floppy_pio_s pio;
277 pio.cmdlen = 1;
278 pio.resplen = 2;
279 pio.waitirq = 0;
280 pio.data[0] = 0x08; // 08: Check Interrupt Status
281 return floppy_pio(&pio);
282}
283
284static int
285floppy_select_drive(u8 floppyid)
286{
287 // reset the disk motor timeout value of INT 08
288 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
289
290 // Enable controller if it isn't running.
291 u8 dor = inb(PORT_FD_DOR);
292 if (!(dor & 0x04)) {
293 int ret = floppy_enable_controller();
294 if (ret)
295 return ret;
296 }
297
298 // Turn on motor of selected drive, DMA & int enabled, normal operation
299 dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
300 outb(dor, PORT_FD_DOR);
301
302 return DISK_RET_SUCCESS;
303}
304
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500305
306/****************************************************************
307 * Floppy media sense
308 ****************************************************************/
309
310static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400311set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500312{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400313 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500314}
315
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500316static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400317floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500318{
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500319 int ret = floppy_select_drive(floppyid);
320 if (ret)
321 return ret;
322
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500323 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500324 struct floppy_pio_s pio;
325 pio.cmdlen = 2;
326 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500327 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500328 pio.data[0] = 0x07; // 07: Recalibrate
329 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500330 ret = floppy_pio(&pio);
331 if (ret)
332 return ret;
333
334 pio.cmdlen = 1;
335 pio.resplen = 2;
336 pio.waitirq = 0;
337 pio.data[0] = 0x08; // 08: Check Interrupt Status
338 ret = floppy_pio(&pio);
339 if (ret)
340 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500341
Kevin O'Connore51316d2012-06-10 09:09:22 -0400342 u8 frs = GET_BDA(floppy_recalibration_status);
343 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400344 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500345 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500346}
347
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500348static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500349floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
350{
351 int ret = floppy_select_drive(floppyid);
352 if (ret)
353 return ret;
354
355 // Set data rate.
356 outb(data_rate, PORT_FD_DIR);
357
358 // send Read Sector Id command
359 struct floppy_pio_s pio;
360 pio.cmdlen = 2;
361 pio.resplen = 7;
362 pio.waitirq = 1;
363 pio.data[0] = 0x4a; // 0a: Read Sector Id
364 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
365 ret = floppy_pio(&pio);
366 if (ret)
367 return ret;
368 if (pio.data[0] & 0xc0)
369 return -1;
370 return 0;
371}
372
373static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400374floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500375{
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500376 u8 ftype = GET_GLOBAL(drive_g->floppy_type), stype = ftype;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400377 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500378
379 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
380 int ret = floppy_drive_readid(floppyid, data_rate, 0);
381 if (ret) {
382 // Attempt media sense.
383 for (stype=1; ; stype++) {
384 if (stype >= ARRAY_SIZE(FloppyInfo))
385 return DISK_RET_EMEDIA;
386 if (stype==ftype
387 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
388 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
389 || (GET_GLOBAL(FloppyInfo[stype].chs.heads)
390 > GET_GLOBAL(FloppyInfo[ftype].chs.heads))
391 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
392 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
393 || (GET_GLOBAL(FloppyInfo[stype].chs.spt)
394 > GET_GLOBAL(FloppyInfo[ftype].chs.spt)))
395 continue;
396 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
397 ret = floppy_drive_readid(floppyid, data_rate, 0);
398 if (!ret)
399 break;
400 }
401 }
402
403 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
404 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
405 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
406 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
407 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
408 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
409 fms |= FMS_DOUBLE_STEPPING;
410 SET_BDA(floppy_media_state[floppyid], fms);
411
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400412 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500413}
414
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400415static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400416check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500417{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400418 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400419 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
420 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500421 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400422 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500423
424 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500425 int ret = floppy_drive_recal(floppyid);
426 if (ret)
427 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500428
429 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400430 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500431}
432
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500433
434/****************************************************************
Kevin O'Connorc8494052013-03-03 10:27:30 -0500435 * Floppy DMA
436 ****************************************************************/
437
438// Perform a floppy transfer command (setup DMA and issue PIO).
439static int
440floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
441{
442 int ret = check_recal_drive(op->drive_g);
443 if (ret)
444 return ret;
445
446 // es:bx = pointer to where to place information from diskette
447 u32 addr = (u32)op->buf_fl;
448 int count = op->count * blocksize;
449
450 // check for 64K boundary overrun
451 u16 end = count - 1;
452 u32 last_addr = addr + end;
453 if ((addr >> 16) != (last_addr >> 16))
454 return DISK_RET_EBOUNDARY;
455
456 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
457 if (pio->data[0] == 0xe6)
458 // read
459 mode_register = 0x46;
460
461 //DEBUGF("floppy dma c2\n");
462 outb(0x06, PORT_DMA1_MASK_REG);
463 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
464 outb(addr, PORT_DMA_ADDR_2);
465 outb(addr>>8, PORT_DMA_ADDR_2);
466 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
467 outb(end, PORT_DMA_CNT_2);
468 outb(end>>8, PORT_DMA_CNT_2);
469
470 // port 0b: DMA-1 Mode Register
471 // transfer type=write, channel 2
472 outb(mode_register, PORT_DMA1_MODE_REG);
473
474 // port 81: DMA-1 Page Register, channel 2
475 outb(addr>>16, PORT_DMA_PAGE_2);
476
477 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
478
479 ret = floppy_select_drive(pio->data[1] & 1);
480 if (ret)
481 return ret;
482 pio->resplen = 7;
483 pio->waitirq = 1;
484 ret = floppy_pio(pio);
485 if (ret)
486 return ret;
487
488 // Populate floppy_return_status in BDA
489 int i;
490 for (i=0; i<7; i++)
491 SET_BDA(floppy_return_status[i], pio->data[i]);
492
493 if (pio->data[0] & 0xc0) {
494 if (pio->data[1] & 0x02)
495 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500496 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500497 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
498 , pio->data[4], pio->data[5], pio->data[6]);
499 return DISK_RET_ECONTROLLER;
500 }
501
502 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
503 set_diskette_current_cyl(pio->data[0] & 1, track);
504
505 return DISK_RET_SUCCESS;
506}
507
508
509/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400510 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500511 ****************************************************************/
512
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500513static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400514lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500515{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400516 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400517
518 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400519 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400520 *sector = tmp % nlspt;
521
522 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400523 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400524 *head = tmp % nlh;
525
526 tmp /= nlh;
527 *track = tmp;
528}
529
530// diskette controller reset
531static int
532floppy_reset(struct disk_op_s *op)
533{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400534 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor5bb75522013-03-03 15:32:31 -0500535 SET_BDA(floppy_recalibration_status, 0);
536 SET_BDA(floppy_media_state[0], 0);
537 SET_BDA(floppy_media_state[1], 0);
538 SET_BDA(floppy_track[0], 0);
539 SET_BDA(floppy_track[1], 0);
540 SET_BDA(floppy_last_data_rate, 0);
541 floppy_disable_controller();
542 return floppy_select_drive(floppyid);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500543}
544
545// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400546static int
547floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500548{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400549 u8 track, sector, head;
550 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500551
552 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400553 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500554 struct floppy_pio_s pio;
555 pio.cmdlen = 9;
556 pio.data[0] = 0xe6; // e6: read normal data
557 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
558 pio.data[2] = track;
559 pio.data[3] = head;
560 pio.data[4] = sector;
561 pio.data[5] = FLOPPY_SIZE_CODE;
562 pio.data[6] = sector + op->count - 1; // last sector to read on track
563 pio.data[7] = FLOPPY_GAPLEN;
564 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500565
Kevin O'Connorc8494052013-03-03 10:27:30 -0500566 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400567 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400568 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400569 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400570fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400571 op->count = 0; // no sectors read
572 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500573}
574
575// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400576static int
577floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500578{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400579 u8 track, sector, head;
580 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500581
582 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400583 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500584 struct floppy_pio_s pio;
585 pio.cmdlen = 9;
586 pio.data[0] = 0xc5; // c5: write normal data
587 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
588 pio.data[2] = track;
589 pio.data[3] = head;
590 pio.data[4] = sector;
591 pio.data[5] = FLOPPY_SIZE_CODE;
592 pio.data[6] = sector + op->count - 1; // last sector to write on track
593 pio.data[7] = FLOPPY_GAPLEN;
594 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500595
Kevin O'Connorc8494052013-03-03 10:27:30 -0500596 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400597 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400598 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400599 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400600fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400601 op->count = 0; // no sectors read
602 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500603}
604
605// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400606static int
607floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500608{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400609 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400610 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400611 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500612
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400613 u8 track, sector, head;
614 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500615
616 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400617 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400618 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400619 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400620fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400621 op->count = 0; // no sectors read
622 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500623}
624
625// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400626static int
627floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500628{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400629 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500630
631 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400632 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500633 struct floppy_pio_s pio;
634 pio.cmdlen = 6;
635 pio.data[0] = 0x4d; // 4d: format track
636 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
637 pio.data[2] = FLOPPY_SIZE_CODE;
638 pio.data[3] = op->count; // number of sectors per track
639 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
640 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500641
Kevin O'Connorc8494052013-03-03 10:27:30 -0500642 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500643}
644
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400645int
646process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500647{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400648 if (!CONFIG_FLOPPY)
649 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500650
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400651 switch (op->command) {
652 case CMD_RESET:
653 return floppy_reset(op);
654 case CMD_READ:
655 return floppy_read(op);
656 case CMD_WRITE:
657 return floppy_write(op);
658 case CMD_VERIFY:
659 return floppy_verify(op);
660 case CMD_FORMAT:
661 return floppy_format(op);
662 default:
663 op->count = 0;
664 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500665 }
666}
667
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500668
669/****************************************************************
670 * HW irqs
671 ****************************************************************/
672
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500673// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500674void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400675handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500676{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400677 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500678 return;
679 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400680
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500681 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400682 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500683 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400684
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400685 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500686}
687
688// Called from int08 handler.
689void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500690floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500691{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400692 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400693 return;
694
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500695 // time to turn off drive(s)?
696 u8 fcount = GET_BDA(floppy_motor_counter);
697 if (fcount) {
698 fcount--;
699 SET_BDA(floppy_motor_counter, fcount);
700 if (fcount == 0)
701 // turn motor(s) off
702 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
703 }
704}