blob: 48958e6881bcdaa373d350cad8235be2ad415c20 [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
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500183u8 FloppyDOR VARLOW;
184
185static inline void
186floppy_dor_write(u8 val)
187{
188 outb(val, PORT_FD_DOR);
189 SET_LOW(FloppyDOR, val);
190}
191
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500192static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500193floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500194{
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500195 dprintf(2, "Floppy_disable_controller\n");
196 floppy_dor_write(0x00);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500197}
198
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500199static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500200floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500201{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500202 u8 frs = GET_BDA(floppy_recalibration_status);
203 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500204 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500205 if (!GET_BDA(floppy_motor_counter)) {
Kevin O'Connorb66456c2013-12-04 10:31:51 -0500206 warn_timeout();
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500207 floppy_disable_controller();
208 return DISK_RET_ETIMEOUT;
209 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400210 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500211 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500212 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400213 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500214 // bochs, so use yield() instead.
215 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500216 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500217
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500218 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
219 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500220}
221
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500222struct floppy_pio_s {
223 u8 cmdlen;
224 u8 resplen;
225 u8 waitirq;
226 u8 data[9];
227};
228
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400229static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500230floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500231{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500232 // Send command to controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400233 u32 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500234 int i = 0;
235 for (;;) {
236 u8 sts = inb(PORT_FD_STATUS);
237 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400238 if (timer_check(end)) {
Kevin O'Connorb66456c2013-12-04 10:31:51 -0500239 warn_timeout();
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500240 floppy_disable_controller();
241 return DISK_RET_ETIMEOUT;
242 }
243 continue;
244 }
245 if (sts & 0x40) {
246 floppy_disable_controller();
247 return DISK_RET_ECONTROLLER;
248 }
249 outb(pio->data[i++], PORT_FD_DATA);
250 if (i >= pio->cmdlen)
251 break;
252 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500253
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500254 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500255 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500256 int ret = floppy_wait_irq();
257 if (ret)
258 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400259 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500260
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500261 // Read response from controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400262 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500263 i = 0;
264 for (;;) {
265 u8 sts = inb(PORT_FD_STATUS);
266 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400267 if (timer_check(end)) {
Kevin O'Connorb66456c2013-12-04 10:31:51 -0500268 warn_timeout();
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500269 floppy_disable_controller();
270 return DISK_RET_ETIMEOUT;
271 }
272 continue;
273 }
274 if (i >= pio->resplen)
275 break;
276 if (!(sts & 0x40)) {
277 floppy_disable_controller();
278 return DISK_RET_ECONTROLLER;
279 }
280 pio->data[i++] = inb(PORT_FD_DATA);
281 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500282
283 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500284}
285
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500286static int
287floppy_enable_controller(void)
288{
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500289 dprintf(2, "Floppy_enable_controller\n");
290 floppy_dor_write(0x00);
291 floppy_dor_write(0x0c);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500292 int ret = floppy_wait_irq();
293 if (ret)
294 return ret;
295
296 struct floppy_pio_s pio;
297 pio.cmdlen = 1;
298 pio.resplen = 2;
299 pio.waitirq = 0;
300 pio.data[0] = 0x08; // 08: Check Interrupt Status
301 return floppy_pio(&pio);
302}
303
304static int
305floppy_select_drive(u8 floppyid)
306{
307 // reset the disk motor timeout value of INT 08
308 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
309
310 // Enable controller if it isn't running.
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500311 if (!(GET_LOW(FloppyDOR) & 0x04)) {
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500312 int ret = floppy_enable_controller();
313 if (ret)
314 return ret;
315 }
316
317 // Turn on motor of selected drive, DMA & int enabled, normal operation
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500318 floppy_dor_write((floppyid ? 0x20 : 0x10) | 0x0c | floppyid);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500319
320 return DISK_RET_SUCCESS;
321}
322
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500323
324/****************************************************************
325 * Floppy media sense
326 ****************************************************************/
327
328static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400329set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500330{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400331 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500332}
333
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500334static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400335floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500336{
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500337 dprintf(2, "Floppy_drive_recal %d\n", floppyid);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500338 int ret = floppy_select_drive(floppyid);
339 if (ret)
340 return ret;
341
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500342 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500343 struct floppy_pio_s pio;
344 pio.cmdlen = 2;
345 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500346 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500347 pio.data[0] = 0x07; // 07: Recalibrate
348 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500349 ret = floppy_pio(&pio);
350 if (ret)
351 return ret;
352
353 pio.cmdlen = 1;
354 pio.resplen = 2;
355 pio.waitirq = 0;
356 pio.data[0] = 0x08; // 08: Check Interrupt Status
357 ret = floppy_pio(&pio);
358 if (ret)
359 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500360
Kevin O'Connore51316d2012-06-10 09:09:22 -0400361 u8 frs = GET_BDA(floppy_recalibration_status);
362 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400363 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500364 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500365}
366
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500367static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500368floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
369{
370 int ret = floppy_select_drive(floppyid);
371 if (ret)
372 return ret;
373
374 // Set data rate.
375 outb(data_rate, PORT_FD_DIR);
376
377 // send Read Sector Id command
378 struct floppy_pio_s pio;
379 pio.cmdlen = 2;
380 pio.resplen = 7;
381 pio.waitirq = 1;
382 pio.data[0] = 0x4a; // 0a: Read Sector Id
383 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
384 ret = floppy_pio(&pio);
385 if (ret)
386 return ret;
387 if (pio.data[0] & 0xc0)
388 return -1;
389 return 0;
390}
391
392static int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400393floppy_media_sense(struct drive_s *drive_gf)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500394{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400395 u8 ftype = GET_GLOBALFLAT(drive_gf->floppy_type), stype = ftype;
396 u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500397
398 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
399 int ret = floppy_drive_readid(floppyid, data_rate, 0);
400 if (ret) {
401 // Attempt media sense.
402 for (stype=1; ; stype++) {
403 if (stype >= ARRAY_SIZE(FloppyInfo))
404 return DISK_RET_EMEDIA;
405 if (stype==ftype
406 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
407 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400408 || (GET_GLOBAL(FloppyInfo[stype].chs.head)
409 > GET_GLOBAL(FloppyInfo[ftype].chs.head))
410 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinder)
411 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinder))
412 || (GET_GLOBAL(FloppyInfo[stype].chs.sector)
413 > GET_GLOBAL(FloppyInfo[ftype].chs.sector)))
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500414 continue;
415 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
416 ret = floppy_drive_readid(floppyid, data_rate, 0);
417 if (!ret)
418 break;
419 }
420 }
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500421 dprintf(2, "Floppy_media_sense on drive %d found rate %d\n"
422 , floppyid, data_rate);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500423
424 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
425 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
426 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
427 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400428 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinder)
429 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinder))
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500430 fms |= FMS_DOUBLE_STEPPING;
431 SET_BDA(floppy_media_state[floppyid], fms);
432
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400433 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500434}
435
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400436static int
Kevin O'Connor1902c942013-10-26 11:48:06 -0400437check_recal_drive(struct drive_s *drive_gf)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500438{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400439 u8 floppyid = GET_GLOBALFLAT(drive_gf->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400440 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
441 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500442 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400443 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500444
445 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500446 int ret = floppy_drive_recal(floppyid);
447 if (ret)
448 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500449
450 // Sense media.
Kevin O'Connor1902c942013-10-26 11:48:06 -0400451 return floppy_media_sense(drive_gf);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500452}
453
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500454
455/****************************************************************
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400456 * Floppy DMA transfer
Kevin O'Connorc8494052013-03-03 10:27:30 -0500457 ****************************************************************/
458
459// Perform a floppy transfer command (setup DMA and issue PIO).
460static int
461floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
462{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400463 int ret = check_recal_drive(op->drive_gf);
Kevin O'Connorc8494052013-03-03 10:27:30 -0500464 if (ret)
465 return ret;
466
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400467 // Setup DMA controller
468 int isWrite = pio->data[0] != 0xe6;
469 ret = dma_floppy((u32)op->buf_fl, op->count * blocksize, isWrite);
470 if (ret)
Kevin O'Connorc8494052013-03-03 10:27:30 -0500471 return DISK_RET_EBOUNDARY;
472
Kevin O'Connor7b9f2972013-09-18 21:04:03 -0400473 // Invoke floppy controller
Kevin O'Connorc8494052013-03-03 10:27:30 -0500474 ret = floppy_select_drive(pio->data[1] & 1);
475 if (ret)
476 return ret;
477 pio->resplen = 7;
478 pio->waitirq = 1;
479 ret = floppy_pio(pio);
480 if (ret)
481 return ret;
482
483 // Populate floppy_return_status in BDA
484 int i;
485 for (i=0; i<7; i++)
486 SET_BDA(floppy_return_status[i], pio->data[i]);
487
488 if (pio->data[0] & 0xc0) {
489 if (pio->data[1] & 0x02)
490 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500491 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500492 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
493 , pio->data[4], pio->data[5], pio->data[6]);
494 return DISK_RET_ECONTROLLER;
495 }
496
497 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
498 set_diskette_current_cyl(pio->data[0] & 1, track);
499
500 return DISK_RET_SUCCESS;
501}
502
503
504/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400505 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500506 ****************************************************************/
507
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400508static struct chs_s
509lba2chs(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500510{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400511 struct chs_s res = { };
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400512
Kevin O'Connor302a6e82013-12-07 11:46:37 -0500513 u32 tmp = op->lba;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400514 u16 nls = GET_GLOBALFLAT(op->drive_gf->lchs.sector);
Kevin O'Connor302a6e82013-12-07 11:46:37 -0500515 res.sector = (tmp % nls) + 1;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400516
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400517 tmp /= nls;
Kevin O'Connor1902c942013-10-26 11:48:06 -0400518 u16 nlh = GET_GLOBALFLAT(op->drive_gf->lchs.head);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400519 res.head = tmp % nlh;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400520
521 tmp /= nlh;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400522 res.cylinder = tmp;
523
524 return res;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400525}
526
527// diskette controller reset
528static int
529floppy_reset(struct disk_op_s *op)
530{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400531 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor5bb75522013-03-03 15:32:31 -0500532 SET_BDA(floppy_recalibration_status, 0);
533 SET_BDA(floppy_media_state[0], 0);
534 SET_BDA(floppy_media_state[1], 0);
535 SET_BDA(floppy_track[0], 0);
536 SET_BDA(floppy_track[1], 0);
537 SET_BDA(floppy_last_data_rate, 0);
538 floppy_disable_controller();
539 return floppy_select_drive(floppyid);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500540}
541
542// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400543static int
544floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500545{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400546 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500547
548 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400549 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500550 struct floppy_pio_s pio;
551 pio.cmdlen = 9;
552 pio.data[0] = 0xe6; // e6: read normal data
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400553 pio.data[1] = (chs.head << 2) | floppyid; // HD DR1 DR2
554 pio.data[2] = chs.cylinder;
555 pio.data[3] = chs.head;
556 pio.data[4] = chs.sector;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500557 pio.data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400558 pio.data[6] = chs.sector + op->count - 1; // last sector to read on track
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500559 pio.data[7] = FLOPPY_GAPLEN;
560 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500561
Kevin O'Connorc8494052013-03-03 10:27:30 -0500562 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400563 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400564 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400565 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400566fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400567 op->count = 0; // no sectors read
568 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500569}
570
571// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400572static int
573floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500574{
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400575 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500576
577 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400578 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500579 struct floppy_pio_s pio;
580 pio.cmdlen = 9;
581 pio.data[0] = 0xc5; // c5: write normal data
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400582 pio.data[1] = (chs.head << 2) | floppyid; // HD DR1 DR2
583 pio.data[2] = chs.cylinder;
584 pio.data[3] = chs.head;
585 pio.data[4] = chs.sector;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500586 pio.data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400587 pio.data[6] = chs.sector + op->count - 1; // last sector to write on track
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500588 pio.data[7] = FLOPPY_GAPLEN;
589 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500590
Kevin O'Connorc8494052013-03-03 10:27:30 -0500591 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400592 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400593 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400594 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400595fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400596 op->count = 0; // no sectors read
597 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500598}
599
600// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400601static int
602floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500603{
Kevin O'Connor1902c942013-10-26 11:48:06 -0400604 int res = check_recal_drive(op->drive_gf);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400605 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400606 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500607
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400608 struct chs_s chs = lba2chs(op);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609
610 // ??? should track be new val from return_status[3] ?
Kevin O'Connor1902c942013-10-26 11:48:06 -0400611 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor8ab9a342013-09-28 23:34:49 -0400612 set_diskette_current_cyl(floppyid, chs.cylinder);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400613 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400614fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400615 op->count = 0; // no sectors read
616 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500617}
618
619// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400620static int
621floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500622{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400623 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500624
625 // send format-track command (6 bytes) to controller
Kevin O'Connor1902c942013-10-26 11:48:06 -0400626 u8 floppyid = GET_GLOBALFLAT(op->drive_gf->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500627 struct floppy_pio_s pio;
628 pio.cmdlen = 6;
629 pio.data[0] = 0x4d; // 4d: format track
630 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
631 pio.data[2] = FLOPPY_SIZE_CODE;
632 pio.data[3] = op->count; // number of sectors per track
633 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
634 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500635
Kevin O'Connorc8494052013-03-03 10:27:30 -0500636 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500637}
638
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400639int
640process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500641{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400642 if (!CONFIG_FLOPPY)
643 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500644
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400645 switch (op->command) {
646 case CMD_RESET:
647 return floppy_reset(op);
648 case CMD_READ:
649 return floppy_read(op);
650 case CMD_WRITE:
651 return floppy_write(op);
652 case CMD_VERIFY:
653 return floppy_verify(op);
654 case CMD_FORMAT:
655 return floppy_format(op);
656 default:
657 op->count = 0;
658 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500659 }
660}
661
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500662
663/****************************************************************
664 * HW irqs
665 ****************************************************************/
666
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500667// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500668void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400669handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500670{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400671 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500672 return;
673 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400674
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500675 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400676 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500677 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400678
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400679 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500680}
681
682// Called from int08 handler.
683void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500684floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500685{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400686 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400687 return;
688
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500689 // time to turn off drive(s)?
690 u8 fcount = GET_BDA(floppy_motor_counter);
691 if (fcount) {
692 fcount--;
693 SET_BDA(floppy_motor_counter, fcount);
694 if (fcount == 0)
695 // turn motor(s) off
Kevin O'Connor402f9a42013-12-07 12:48:11 -0500696 floppy_dor_write(GET_LOW(FloppyDOR) & ~0xf0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500697 }
698}