blob: 39efaadc8472436a014703f2c59e54e8ebd0cc1d [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
11#include "cmos.h" // inb_cmos
12#include "config.h" // CONFIG_FLOPPY
Kevin O'Connor9dea5902013-09-14 20:23:54 -040013#include "malloc.h" // malloc_fseg
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040014#include "output.h" // dprintf
Kevin O'Connor031ef552010-12-27 19:26:57 -050015#include "pci.h" // pci_to_bdf
16#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
Kevin O'Connor2d2fa312013-09-14 21:55:26 -040017#include "pic.h" // pic_eoi1
Kevin O'Connor41639f82013-09-14 19:37:36 -040018#include "romfile.h" // romfile_loadint
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'Connor7b184d82009-08-23 11:56:55 -040024#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
25#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
26#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
27#define FLOPPY_FILLBYTE 0xf6
28#define FLOPPY_GAPLEN 0x1B
29#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connor068cb4f2013-03-03 11:05:19 -050030#define FLOPPY_PIO_TIMEOUT 1000
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050031
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050032// New diskette parameter table adding 3 parameters from IBM
33// Since no provisions are made for multiple drive types, most
34// values in this table are ignored. I set parameters for 1.44M
35// floppy here
Kevin O'Connor89a2f962013-02-18 23:36:03 -050036struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050037 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040038 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
39 .specify2 = 0x02, // head load time 4ms, DMA used
40 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
41 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050042 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040043 .interblock_len = FLOPPY_GAPLEN,
44 .data_len = FLOPPY_DATALEN,
45 .gap_len = FLOPPY_FORMAT_GAPLEN,
46 .fill_byte = FLOPPY_FILLBYTE,
47 .settle_time = 0x0F, // 15ms
48 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050049 },
50 .max_track = 79, // maximum track
51 .data_rate = 0, // data transfer rate
52 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050053};
54
Kevin O'Connoref719892012-11-26 11:14:00 -050055struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050056
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040057struct floppyinfo_s {
58 struct chs_s chs;
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050059 u8 floppy_size;
60 u8 data_rate;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040061};
62
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050063#define FLOPPY_SIZE_525 0x01
64#define FLOPPY_SIZE_350 0x02
65
66#define FLOPPY_RATE_500K 0x00
67#define FLOPPY_RATE_300K 0x01
68#define FLOPPY_RATE_250K 0x02
69#define FLOPPY_RATE_1M 0x03
70
Kevin O'Connor89a2f962013-02-18 23:36:03 -050071struct floppyinfo_s FloppyInfo[] VARFSEG = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040072 // Unknown
73 { {0, 0, 0}, 0x00, 0x00},
74 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050075 { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040076 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050077 { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040078 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050079 { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040080 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050081 { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040082 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050083 { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040084 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050085 { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040086 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050087 { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040088 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050089 { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040090};
91
Kevin O'Connor77d227b2009-10-22 21:48:39 -040092struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050093init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040094{
95 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
96 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040097 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040098 }
99
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500100 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500101 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500102 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400103 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500104 }
105 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400106 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500107 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400108 drive_g->blksize = DISK_SECTOR_SIZE;
109 drive_g->floppy_type = ftype;
110 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400111
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400112 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400113 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500114 return drive_g;
115}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400116
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500117static void
118addFloppy(int floppyid, int ftype)
119{
120 struct drive_s *drive_g = init_floppy(floppyid, ftype);
121 if (!drive_g)
122 return;
123 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400124 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400125 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500126 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400127}
128
129void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500130floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400131{
Kevin O'Connoref719892012-11-26 11:14:00 -0500132 memcpy(&diskette_param_table, &diskette_param_table2
133 , sizeof(diskette_param_table));
134 SET_IVT(0x1E, SEGOFF(SEG_BIOS
135 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
136
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400137 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400138 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400139 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400140
Kevin O'Connor897fb112013-02-07 23:32:48 -0500141 if (CONFIG_QEMU) {
142 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
143 if (type & 0xf0)
144 addFloppy(0, type >> 4);
145 if (type & 0x0f)
146 addFloppy(1, type & 0x0f);
147 } else {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500148 u8 type = romfile_loadint("etc/floppy0", 0);
149 if (type)
150 addFloppy(0, type);
151 type = romfile_loadint("etc/floppy1", 0);
152 if (type)
153 addFloppy(1, type);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400154 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400155
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400156 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400157
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400158 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400159}
160
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400161// Find a floppy type that matches a given image size.
162int
163find_floppy_type(u32 size)
164{
165 int i;
166 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
167 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400168 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400169 return i;
170 }
171 return -1;
172}
173
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500174
175/****************************************************************
176 * Low-level floppy IO
177 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500178
179static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500180floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500181{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500182 outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500183}
184
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500185static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500186floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500187{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500188 u8 frs = GET_BDA(floppy_recalibration_status);
189 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500190 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500191 if (!GET_BDA(floppy_motor_counter)) {
192 floppy_disable_controller();
193 return DISK_RET_ETIMEOUT;
194 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400195 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500196 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500197 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400198 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500199 // bochs, so use yield() instead.
200 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500201 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500202
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500203 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
204 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500205}
206
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500207struct floppy_pio_s {
208 u8 cmdlen;
209 u8 resplen;
210 u8 waitirq;
211 u8 data[9];
212};
213
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400214static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500215floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500216{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500217 // Send command to controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400218 u32 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500219 int i = 0;
220 for (;;) {
221 u8 sts = inb(PORT_FD_STATUS);
222 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400223 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500224 floppy_disable_controller();
225 return DISK_RET_ETIMEOUT;
226 }
227 continue;
228 }
229 if (sts & 0x40) {
230 floppy_disable_controller();
231 return DISK_RET_ECONTROLLER;
232 }
233 outb(pio->data[i++], PORT_FD_DATA);
234 if (i >= pio->cmdlen)
235 break;
236 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500237
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500238 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500239 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500240 int ret = floppy_wait_irq();
241 if (ret)
242 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400243 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500244
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500245 // Read response from controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400246 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500247 i = 0;
248 for (;;) {
249 u8 sts = inb(PORT_FD_STATUS);
250 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400251 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500252 floppy_disable_controller();
253 return DISK_RET_ETIMEOUT;
254 }
255 continue;
256 }
257 if (i >= pio->resplen)
258 break;
259 if (!(sts & 0x40)) {
260 floppy_disable_controller();
261 return DISK_RET_ECONTROLLER;
262 }
263 pio->data[i++] = inb(PORT_FD_DATA);
264 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500265
266 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500267}
268
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500269static int
270floppy_enable_controller(void)
271{
272 outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
273 int ret = floppy_wait_irq();
274 if (ret)
275 return ret;
276
277 struct floppy_pio_s pio;
278 pio.cmdlen = 1;
279 pio.resplen = 2;
280 pio.waitirq = 0;
281 pio.data[0] = 0x08; // 08: Check Interrupt Status
282 return floppy_pio(&pio);
283}
284
285static int
286floppy_select_drive(u8 floppyid)
287{
288 // reset the disk motor timeout value of INT 08
289 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
290
291 // Enable controller if it isn't running.
292 u8 dor = inb(PORT_FD_DOR);
293 if (!(dor & 0x04)) {
294 int ret = floppy_enable_controller();
295 if (ret)
296 return ret;
297 }
298
299 // Turn on motor of selected drive, DMA & int enabled, normal operation
300 dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
301 outb(dor, PORT_FD_DOR);
302
303 return DISK_RET_SUCCESS;
304}
305
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500306
307/****************************************************************
308 * Floppy media sense
309 ****************************************************************/
310
311static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400312set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500313{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400314 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500315}
316
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500317static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400318floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500319{
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500320 int ret = floppy_select_drive(floppyid);
321 if (ret)
322 return ret;
323
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500324 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500325 struct floppy_pio_s pio;
326 pio.cmdlen = 2;
327 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500328 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500329 pio.data[0] = 0x07; // 07: Recalibrate
330 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500331 ret = floppy_pio(&pio);
332 if (ret)
333 return ret;
334
335 pio.cmdlen = 1;
336 pio.resplen = 2;
337 pio.waitirq = 0;
338 pio.data[0] = 0x08; // 08: Check Interrupt Status
339 ret = floppy_pio(&pio);
340 if (ret)
341 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500342
Kevin O'Connore51316d2012-06-10 09:09:22 -0400343 u8 frs = GET_BDA(floppy_recalibration_status);
344 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400345 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500346 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500347}
348
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500349static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500350floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
351{
352 int ret = floppy_select_drive(floppyid);
353 if (ret)
354 return ret;
355
356 // Set data rate.
357 outb(data_rate, PORT_FD_DIR);
358
359 // send Read Sector Id command
360 struct floppy_pio_s pio;
361 pio.cmdlen = 2;
362 pio.resplen = 7;
363 pio.waitirq = 1;
364 pio.data[0] = 0x4a; // 0a: Read Sector Id
365 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
366 ret = floppy_pio(&pio);
367 if (ret)
368 return ret;
369 if (pio.data[0] & 0xc0)
370 return -1;
371 return 0;
372}
373
374static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400375floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500376{
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500377 u8 ftype = GET_GLOBAL(drive_g->floppy_type), stype = ftype;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400378 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500379
380 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
381 int ret = floppy_drive_readid(floppyid, data_rate, 0);
382 if (ret) {
383 // Attempt media sense.
384 for (stype=1; ; stype++) {
385 if (stype >= ARRAY_SIZE(FloppyInfo))
386 return DISK_RET_EMEDIA;
387 if (stype==ftype
388 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
389 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
390 || (GET_GLOBAL(FloppyInfo[stype].chs.heads)
391 > GET_GLOBAL(FloppyInfo[ftype].chs.heads))
392 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
393 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
394 || (GET_GLOBAL(FloppyInfo[stype].chs.spt)
395 > GET_GLOBAL(FloppyInfo[ftype].chs.spt)))
396 continue;
397 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
398 ret = floppy_drive_readid(floppyid, data_rate, 0);
399 if (!ret)
400 break;
401 }
402 }
403
404 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
405 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
406 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
407 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
408 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
409 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
410 fms |= FMS_DOUBLE_STEPPING;
411 SET_BDA(floppy_media_state[floppyid], fms);
412
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400413 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500414}
415
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400416static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400417check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500418{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400419 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400420 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
421 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500422 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400423 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500424
425 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500426 int ret = floppy_drive_recal(floppyid);
427 if (ret)
428 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500429
430 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400431 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500432}
433
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500434
435/****************************************************************
Kevin O'Connorc8494052013-03-03 10:27:30 -0500436 * Floppy DMA
437 ****************************************************************/
438
439// Perform a floppy transfer command (setup DMA and issue PIO).
440static int
441floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
442{
443 int ret = check_recal_drive(op->drive_g);
444 if (ret)
445 return ret;
446
447 // es:bx = pointer to where to place information from diskette
448 u32 addr = (u32)op->buf_fl;
449 int count = op->count * blocksize;
450
451 // check for 64K boundary overrun
452 u16 end = count - 1;
453 u32 last_addr = addr + end;
454 if ((addr >> 16) != (last_addr >> 16))
455 return DISK_RET_EBOUNDARY;
456
457 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
458 if (pio->data[0] == 0xe6)
459 // read
460 mode_register = 0x46;
461
462 //DEBUGF("floppy dma c2\n");
463 outb(0x06, PORT_DMA1_MASK_REG);
464 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
465 outb(addr, PORT_DMA_ADDR_2);
466 outb(addr>>8, PORT_DMA_ADDR_2);
467 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
468 outb(end, PORT_DMA_CNT_2);
469 outb(end>>8, PORT_DMA_CNT_2);
470
471 // port 0b: DMA-1 Mode Register
472 // transfer type=write, channel 2
473 outb(mode_register, PORT_DMA1_MODE_REG);
474
475 // port 81: DMA-1 Page Register, channel 2
476 outb(addr>>16, PORT_DMA_PAGE_2);
477
478 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
479
480 ret = floppy_select_drive(pio->data[1] & 1);
481 if (ret)
482 return ret;
483 pio->resplen = 7;
484 pio->waitirq = 1;
485 ret = floppy_pio(pio);
486 if (ret)
487 return ret;
488
489 // Populate floppy_return_status in BDA
490 int i;
491 for (i=0; i<7; i++)
492 SET_BDA(floppy_return_status[i], pio->data[i]);
493
494 if (pio->data[0] & 0xc0) {
495 if (pio->data[1] & 0x02)
496 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500497 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500498 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
499 , pio->data[4], pio->data[5], pio->data[6]);
500 return DISK_RET_ECONTROLLER;
501 }
502
503 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
504 set_diskette_current_cyl(pio->data[0] & 1, track);
505
506 return DISK_RET_SUCCESS;
507}
508
509
510/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400511 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500512 ****************************************************************/
513
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500514static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400515lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500516{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400517 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400518
519 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400520 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400521 *sector = tmp % nlspt;
522
523 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400524 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400525 *head = tmp % nlh;
526
527 tmp /= nlh;
528 *track = tmp;
529}
530
531// diskette controller reset
532static int
533floppy_reset(struct disk_op_s *op)
534{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400535 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor5bb75522013-03-03 15:32:31 -0500536 SET_BDA(floppy_recalibration_status, 0);
537 SET_BDA(floppy_media_state[0], 0);
538 SET_BDA(floppy_media_state[1], 0);
539 SET_BDA(floppy_track[0], 0);
540 SET_BDA(floppy_track[1], 0);
541 SET_BDA(floppy_last_data_rate, 0);
542 floppy_disable_controller();
543 return floppy_select_drive(floppyid);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500544}
545
546// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400547static int
548floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500549{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400550 u8 track, sector, head;
551 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500552
553 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400554 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500555 struct floppy_pio_s pio;
556 pio.cmdlen = 9;
557 pio.data[0] = 0xe6; // e6: read normal data
558 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
559 pio.data[2] = track;
560 pio.data[3] = head;
561 pio.data[4] = sector;
562 pio.data[5] = FLOPPY_SIZE_CODE;
563 pio.data[6] = sector + op->count - 1; // last sector to read on track
564 pio.data[7] = FLOPPY_GAPLEN;
565 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500566
Kevin O'Connorc8494052013-03-03 10:27:30 -0500567 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400568 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400569 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400570 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400571fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400572 op->count = 0; // no sectors read
573 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500574}
575
576// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400577static int
578floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500579{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400580 u8 track, sector, head;
581 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500582
583 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400584 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500585 struct floppy_pio_s pio;
586 pio.cmdlen = 9;
587 pio.data[0] = 0xc5; // c5: write normal data
588 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
589 pio.data[2] = track;
590 pio.data[3] = head;
591 pio.data[4] = sector;
592 pio.data[5] = FLOPPY_SIZE_CODE;
593 pio.data[6] = sector + op->count - 1; // last sector to write on track
594 pio.data[7] = FLOPPY_GAPLEN;
595 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500596
Kevin O'Connorc8494052013-03-03 10:27:30 -0500597 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400598 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400599 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400600 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400601fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400602 op->count = 0; // no sectors read
603 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500604}
605
606// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400607static int
608floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400610 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400611 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400612 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500613
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400614 u8 track, sector, head;
615 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500616
617 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400618 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400619 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400620 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400621fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400622 op->count = 0; // no sectors read
623 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500624}
625
626// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400627static int
628floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500629{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400630 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500631
632 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400633 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500634 struct floppy_pio_s pio;
635 pio.cmdlen = 6;
636 pio.data[0] = 0x4d; // 4d: format track
637 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
638 pio.data[2] = FLOPPY_SIZE_CODE;
639 pio.data[3] = op->count; // number of sectors per track
640 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
641 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500642
Kevin O'Connorc8494052013-03-03 10:27:30 -0500643 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500644}
645
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400646int
647process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500648{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400649 if (!CONFIG_FLOPPY)
650 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500651
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400652 switch (op->command) {
653 case CMD_RESET:
654 return floppy_reset(op);
655 case CMD_READ:
656 return floppy_read(op);
657 case CMD_WRITE:
658 return floppy_write(op);
659 case CMD_VERIFY:
660 return floppy_verify(op);
661 case CMD_FORMAT:
662 return floppy_format(op);
663 default:
664 op->count = 0;
665 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500666 }
667}
668
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500669
670/****************************************************************
671 * HW irqs
672 ****************************************************************/
673
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500674// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500675void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400676handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500677{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400678 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500679 return;
680 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400681
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500682 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400683 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500684 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400685
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400686 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500687}
688
689// Called from int08 handler.
690void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500691floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500692{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400693 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400694 return;
695
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500696 // time to turn off drive(s)?
697 u8 fcount = GET_BDA(floppy_motor_counter);
698 if (fcount) {
699 fcount--;
700 SET_BDA(floppy_motor_counter, fcount);
701 if (fcount == 0)
702 // turn motor(s) off
703 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
704 }
705}