blob: e73b3035e52db9df0b43ccf6e92027373b9ddb44 [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
Kevin O'Connor9521e262008-07-04 13:04:29 -04008#include "biosvar.h" // SET_BDA
Kevin O'Connor135f3f62013-09-14 23:57:26 -04009#include "block.h" // struct drive_s
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040010#include "bregs.h" // struct bregs
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040011#include "config.h" // CONFIG_FLOPPY
Kevin O'Connor9dea5902013-09-14 20:23:54 -040012#include "malloc.h" // malloc_fseg
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040013#include "output.h" // dprintf
Kevin O'Connor031ef552010-12-27 19:26:57 -050014#include "pci.h" // pci_to_bdf
15#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040016#include "pic.h" // pic_eoi1
Kevin O'Connor41639f82013-09-14 19:37:36 -040017#include "romfile.h" // romfile_loadint
Kevin O'Connor8b7861c2013-09-15 02:29:06 -040018#include "rtc.h" // rtc_read
Kevin O'Connor3df600b2013-09-14 19:28:55 -040019#include "stacks.h" // yield
Kevin O'Connor135f3f62013-09-14 23:57:26 -040020#include "std/disk.h" // DISK_RET_SUCCESS
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040021#include "string.h" // memset
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040022#include "util.h" // timer_calc
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050023
Kevin O'Connor4ade5232013-09-18 21:41:48 -040024#define PORT_FD_BASE 0x03f0
25#define PORT_FD_DOR 0x03f2
26#define PORT_FD_STATUS 0x03f4
27#define PORT_FD_DATA 0x03f5
28#define PORT_FD_DIR 0x03f7
29
Kevin O'Connor7b184d82009-08-23 11:56:55 -040030#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
31#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
32#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
33#define FLOPPY_FILLBYTE 0xf6
34#define FLOPPY_GAPLEN 0x1B
35#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connor068cb4f2013-03-03 11:05:19 -050036#define FLOPPY_PIO_TIMEOUT 1000
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050037
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050038// New diskette parameter table adding 3 parameters from IBM
39// Since no provisions are made for multiple drive types, most
40// values in this table are ignored. I set parameters for 1.44M
41// floppy here
Kevin O'Connor89a2f962013-02-18 23:36:03 -050042struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050043 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040044 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
45 .specify2 = 0x02, // head load time 4ms, DMA used
46 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
47 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050048 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040049 .interblock_len = FLOPPY_GAPLEN,
50 .data_len = FLOPPY_DATALEN,
51 .gap_len = FLOPPY_FORMAT_GAPLEN,
52 .fill_byte = FLOPPY_FILLBYTE,
53 .settle_time = 0x0F, // 15ms
54 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050055 },
56 .max_track = 79, // maximum track
57 .data_rate = 0, // data transfer rate
58 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050059};
60
Kevin O'Connoref719892012-11-26 11:14:00 -050061struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050062
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040063struct floppyinfo_s {
64 struct chs_s chs;
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050065 u8 floppy_size;
66 u8 data_rate;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040067};
68
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050069#define FLOPPY_SIZE_525 0x01
70#define FLOPPY_SIZE_350 0x02
71
72#define FLOPPY_RATE_500K 0x00
73#define FLOPPY_RATE_300K 0x01
74#define FLOPPY_RATE_250K 0x02
75#define FLOPPY_RATE_1M 0x03
76
Kevin O'Connor89a2f962013-02-18 23:36:03 -050077struct floppyinfo_s FloppyInfo[] VARFSEG = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040078 // Unknown
79 { {0, 0, 0}, 0x00, 0x00},
80 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050081 { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040082 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050083 { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040084 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050085 { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040086 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050087 { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040088 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050089 { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040090 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050091 { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040092 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050093 { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040094 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050095 { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040096};
97
Kevin O'Connor77d227b2009-10-22 21:48:39 -040098struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050099init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400100{
101 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
102 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400103 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400104 }
105
Kevin O'Connor1902c942013-10-26 11:48:06 -0400106 struct drive_s *drive = malloc_fseg(sizeof(*drive));
107 if (!drive) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500108 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400109 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500110 }
Kevin O'Connor1902c942013-10-26 11:48:06 -0400111 memset(drive, 0, sizeof(*drive));
112 drive->cntl_id = floppyid;
113 drive->type = DTYPE_FLOPPY;
114 drive->blksize = DISK_SECTOR_SIZE;
115 drive->floppy_type = ftype;
116 drive->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400117
Kevin O'Connor1902c942013-10-26 11:48:06 -0400118 memcpy(&drive->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400119 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connor1902c942013-10-26 11:48:06 -0400120 return drive;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500121}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400122
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500123static void
124addFloppy(int floppyid, int ftype)
125{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400126 struct drive_s *drive = init_floppy(floppyid, ftype);
127 if (!drive)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500128 return;
129 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400130 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400131 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connor1902c942013-10-26 11:48:06 -0400132 boot_add_floppy(drive, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400133}
134
135void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500136floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400137{
Kevin O'Connoref719892012-11-26 11:14:00 -0500138 memcpy(&diskette_param_table, &diskette_param_table2
139 , sizeof(diskette_param_table));
140 SET_IVT(0x1E, SEGOFF(SEG_BIOS
141 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
142
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400143 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400144 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400145 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400146
Kevin O'Connor897fb112013-02-07 23:32:48 -0500147 if (CONFIG_QEMU) {
Kevin O'Connor8b7861c2013-09-15 02:29:06 -0400148 u8 type = rtc_read(CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor897fb112013-02-07 23:32:48 -0500149 if (type & 0xf0)
150 addFloppy(0, type >> 4);
151 if (type & 0x0f)
152 addFloppy(1, type & 0x0f);
153 } else {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500154 u8 type = romfile_loadint("etc/floppy0", 0);
155 if (type)
156 addFloppy(0, type);
157 type = romfile_loadint("etc/floppy1", 0);
158 if (type)
159 addFloppy(1, type);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400160 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400161
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400162 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400163}
164
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400165// Find a floppy type that matches a given image size.
166int
167find_floppy_type(u32 size)
168{
169 int i;
170 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
171 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400172 if (c->cylinder * c->head * c->sector * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400173 return i;
174 }
175 return -1;
176}
177
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500178
179/****************************************************************
180 * Low-level floppy IO
181 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500182
183static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500184floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500185{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500186 outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500187}
188
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500189static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500190floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500191{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500192 u8 frs = GET_BDA(floppy_recalibration_status);
193 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500194 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500195 if (!GET_BDA(floppy_motor_counter)) {
196 floppy_disable_controller();
197 return DISK_RET_ETIMEOUT;
198 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400199 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500200 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500201 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400202 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500203 // bochs, so use yield() instead.
204 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500205 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500206
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500207 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
208 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500209}
210
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500211struct floppy_pio_s {
212 u8 cmdlen;
213 u8 resplen;
214 u8 waitirq;
215 u8 data[9];
216};
217
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400218static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500219floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500220{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500221 // Send command to controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400222 u32 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500223 int i = 0;
224 for (;;) {
225 u8 sts = inb(PORT_FD_STATUS);
226 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400227 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500228 floppy_disable_controller();
229 return DISK_RET_ETIMEOUT;
230 }
231 continue;
232 }
233 if (sts & 0x40) {
234 floppy_disable_controller();
235 return DISK_RET_ECONTROLLER;
236 }
237 outb(pio->data[i++], PORT_FD_DATA);
238 if (i >= pio->cmdlen)
239 break;
240 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500241
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500242 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500243 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500244 int ret = floppy_wait_irq();
245 if (ret)
246 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400247 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500248
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500249 // Read response from controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400250 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500251 i = 0;
252 for (;;) {
253 u8 sts = inb(PORT_FD_STATUS);
254 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400255 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500256 floppy_disable_controller();
257 return DISK_RET_ETIMEOUT;
258 }
259 continue;
260 }
261 if (i >= pio->resplen)
262 break;
263 if (!(sts & 0x40)) {
264 floppy_disable_controller();
265 return DISK_RET_ECONTROLLER;
266 }
267 pio->data[i++] = inb(PORT_FD_DATA);
268 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500269
270 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500271}
272
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500273static int
274floppy_enable_controller(void)
275{
276 outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
277 int ret = floppy_wait_irq();
278 if (ret)
279 return ret;
280
281 struct floppy_pio_s pio;
282 pio.cmdlen = 1;
283 pio.resplen = 2;
284 pio.waitirq = 0;
285 pio.data[0] = 0x08; // 08: Check Interrupt Status
286 return floppy_pio(&pio);
287}
288
289static int
290floppy_select_drive(u8 floppyid)
291{
292 // reset the disk motor timeout value of INT 08
293 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
294
295 // Enable controller if it isn't running.
296 u8 dor = inb(PORT_FD_DOR);
297 if (!(dor & 0x04)) {
298 int ret = floppy_enable_controller();
299 if (ret)
300 return ret;
301 }
302
303 // Turn on motor of selected drive, DMA & int enabled, normal operation
304 dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
305 outb(dor, PORT_FD_DOR);
306
307 return DISK_RET_SUCCESS;
308}
309
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500310
311/****************************************************************
312 * Floppy media sense
313 ****************************************************************/
314
315static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400316set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500317{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400318 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500319}
320
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500321static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400322floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500323{
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500324 int ret = floppy_select_drive(floppyid);
325 if (ret)
326 return ret;
327
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500328 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500329 struct floppy_pio_s pio;
330 pio.cmdlen = 2;
331 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500332 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500333 pio.data[0] = 0x07; // 07: Recalibrate
334 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500335 ret = floppy_pio(&pio);
336 if (ret)
337 return ret;
338
339 pio.cmdlen = 1;
340 pio.resplen = 2;
341 pio.waitirq = 0;
342 pio.data[0] = 0x08; // 08: Check Interrupt Status
343 ret = floppy_pio(&pio);
344 if (ret)
345 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500346
Kevin O'Connore51316d2012-06-10 09:09:22 -0400347 u8 frs = GET_BDA(floppy_recalibration_status);
348 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400349 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500350 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500351}
352
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500353static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500354floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
355{
356 int ret = floppy_select_drive(floppyid);
357 if (ret)
358 return ret;
359
360 // Set data rate.
361 outb(data_rate, PORT_FD_DIR);
362
363 // send Read Sector Id command
364 struct floppy_pio_s pio;
365 pio.cmdlen = 2;
366 pio.resplen = 7;
367 pio.waitirq = 1;
368 pio.data[0] = 0x4a; // 0a: Read Sector Id
369 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
370 ret = floppy_pio(&pio);
371 if (ret)
372 return ret;
373 if (pio.data[0] & 0xc0)
374 return -1;
375 return 0;
376}
377
378static int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400379floppy_media_sense(struct drive_s *drive_gf)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500380{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400381 u8 ftype = GET_GLOBALFLAT(drive_gf->floppy_type), stype = ftype;
382 u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500383
384 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
385 int ret = floppy_drive_readid(floppyid, data_rate, 0);
386 if (ret) {
387 // Attempt media sense.
388 for (stype=1; ; stype++) {
389 if (stype >= ARRAY_SIZE(FloppyInfo))
390 return DISK_RET_EMEDIA;
391 if (stype==ftype
392 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
393 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400394 || (GET_GLOBAL(FloppyInfo[stype].chs.head)
395 > GET_GLOBAL(FloppyInfo[ftype].chs.head))
396 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinder)
397 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinder))
398 || (GET_GLOBAL(FloppyInfo[stype].chs.sector)
399 > GET_GLOBAL(FloppyInfo[ftype].chs.sector)))
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500400 continue;
401 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
402 ret = floppy_drive_readid(floppyid, data_rate, 0);
403 if (!ret)
404 break;
405 }
406 }
407
408 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
409 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
410 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
411 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400412 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinder)
413 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinder))
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500414 fms |= FMS_DOUBLE_STEPPING;
415 SET_BDA(floppy_media_state[floppyid], fms);
416
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400417 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500418}
419
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400420static int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400421check_recal_drive(struct drive_s *drive_gf)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500422{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400423 u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400424 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
425 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500426 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400427 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500428
429 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500430 int ret = floppy_drive_recal(floppyid);
431 if (ret)
432 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500433
434 // Sense media.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400435 return floppy_media_sense(drive_gf);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500436}
437
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500438
439/****************************************************************
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400440 * Floppy DMA transfer
Kevin O'Connorc8494052013-03-03 10:27:30 -0500441 ****************************************************************/
442
443// Perform a floppy transfer command (setup DMA and issue PIO).
444static int
445floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
446{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400447 int ret = check_recal_drive(op->drive_gf);
Kevin O'Connorc8494052013-03-03 10:27:30 -0500448 if (ret)
449 return ret;
450
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400451 // Setup DMA controller
452 int isWrite = pio->data[0] != 0xe6;
453 ret = dma_floppy((u32)op->buf_fl, op->count * blocksize, isWrite);
454 if (ret)
Kevin O'Connorc8494052013-03-03 10:27:30 -0500455 return DISK_RET_EBOUNDARY;
456
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400457 // Invoke floppy controller
Kevin O'Connorc8494052013-03-03 10:27:30 -0500458 ret = floppy_select_drive(pio->data[1] & 1);
459 if (ret)
460 return ret;
461 pio->resplen = 7;
462 pio->waitirq = 1;
463 ret = floppy_pio(pio);
464 if (ret)
465 return ret;
466
467 // Populate floppy_return_status in BDA
468 int i;
469 for (i=0; i<7; i++)
470 SET_BDA(floppy_return_status[i], pio->data[i]);
471
472 if (pio->data[0] & 0xc0) {
473 if (pio->data[1] & 0x02)
474 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500475 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500476 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
477 , pio->data[4], pio->data[5], pio->data[6]);
478 return DISK_RET_ECONTROLLER;
479 }
480
481 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
482 set_diskette_current_cyl(pio->data[0] & 1, track);
483
484 return DISK_RET_SUCCESS;
485}
486
487
488/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400489 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500490 ****************************************************************/
491
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400492static struct chs_s
493lba2chs(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500494{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400495 struct chs_s res = { };
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400496 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400497
498 u32 tmp = lba + 1;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400499 u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400500 res.sector = tmp % nls;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400501
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400502 tmp /= nls;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400503 u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400504 res.head = tmp % nlh;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400505
506 tmp /= nlh;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400507 res.cylinder = tmp;
508
509 return res;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400510}
511
512// diskette controller reset
513static int
514floppy_reset(struct disk_op_s *op)
515{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400516 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor5bb75522013-03-03 15:32:31 -0500517 SET_BDA(floppy_recalibration_status, 0);
518 SET_BDA(floppy_media_state[0], 0);
519 SET_BDA(floppy_media_state[1], 0);
520 SET_BDA(floppy_track[0], 0);
521 SET_BDA(floppy_track[1], 0);
522 SET_BDA(floppy_last_data_rate, 0);
523 floppy_disable_controller();
524 return floppy_select_drive(floppyid);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500525}
526
527// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400528static int
529floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500530{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400531 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500532
533 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400534 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500535 struct floppy_pio_s pio;
536 pio.cmdlen = 9;
537 pio.data[0] = 0xe6; // e6: read normal data
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400538 pio.data[1] = (chs.head << 2) | floppyid; // HD DR1 DR2
539 pio.data[2] = chs.cylinder;
540 pio.data[3] = chs.head;
541 pio.data[4] = chs.sector;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500542 pio.data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400543 pio.data[6] = chs.sector + op->count - 1; // last sector to read on track
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500544 pio.data[7] = FLOPPY_GAPLEN;
545 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500546
Kevin O'Connorc8494052013-03-03 10:27:30 -0500547 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400548 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400549 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400550 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400551fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400552 op->count = 0; // no sectors read
553 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500554}
555
556// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400557static int
558floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500559{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400560 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500561
562 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400563 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500564 struct floppy_pio_s pio;
565 pio.cmdlen = 9;
566 pio.data[0] = 0xc5; // c5: write normal data
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400567 pio.data[1] = (chs.head << 2) | floppyid; // HD DR1 DR2
568 pio.data[2] = chs.cylinder;
569 pio.data[3] = chs.head;
570 pio.data[4] = chs.sector;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500571 pio.data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400572 pio.data[6] = chs.sector + op->count - 1; // last sector to write on track
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500573 pio.data[7] = FLOPPY_GAPLEN;
574 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500575
Kevin O'Connorc8494052013-03-03 10:27:30 -0500576 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400577 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400578 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400579 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400580fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400581 op->count = 0; // no sectors read
582 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500583}
584
585// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400586static int
587floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500588{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400589 int res = check_recal_drive(op->drive_gf);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400590 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400591 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500592
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400593 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500594
595 // ??? should track be new val from return_status[3] ?
Kevin O'Connor1902c942013-10-26 11:48:06 -0400596 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400597 set_diskette_current_cyl(floppyid, chs.cylinder);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400598 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400599fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400600 op->count = 0; // no sectors read
601 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500602}
603
604// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400605static int
606floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500607{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400608 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609
610 // send format-track command (6 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400611 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500612 struct floppy_pio_s pio;
613 pio.cmdlen = 6;
614 pio.data[0] = 0x4d; // 4d: format track
615 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
616 pio.data[2] = FLOPPY_SIZE_CODE;
617 pio.data[3] = op->count; // number of sectors per track
618 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
619 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500620
Kevin O'Connorc8494052013-03-03 10:27:30 -0500621 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500622}
623
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400624int
625process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500626{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400627 if (!CONFIG_FLOPPY)
628 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500629
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400630 switch (op->command) {
631 case CMD_RESET:
632 return floppy_reset(op);
633 case CMD_READ:
634 return floppy_read(op);
635 case CMD_WRITE:
636 return floppy_write(op);
637 case CMD_VERIFY:
638 return floppy_verify(op);
639 case CMD_FORMAT:
640 return floppy_format(op);
641 default:
642 op->count = 0;
643 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500644 }
645}
646
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500647
648/****************************************************************
649 * HW irqs
650 ****************************************************************/
651
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500652// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500653void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400654handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500655{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400656 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500657 return;
658 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400659
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500660 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400661 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500662 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400663
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400664 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500665}
666
667// Called from int08 handler.
668void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500669floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500670{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400671 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400672 return;
673
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500674 // time to turn off drive(s)?
675 u8 fcount = GET_BDA(floppy_motor_counter);
676 if (fcount) {
677 fcount--;
678 SET_BDA(floppy_motor_counter, fcount);
679 if (fcount == 0)
680 // turn motor(s) off
681 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
682 }
683}