blob: ae66bc0c12e35f46314e157e549df06e7563626e [file] [log] [blame]
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05001// 16bit code to access floppy drives.
2//
Kevin O'Connorc892b132009-08-11 21:59:37 -04003// Copyright (C) 2008,2009 Kevin O'Connor <kevin@koconnor.net>
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05004// Copyright (C) 2002 MandrakeSoft S.A.
5//
Kevin O'Connorb1b7c2a2009-01-15 20:52:58 -05006// This file may be distributed under the terms of the GNU LGPLv3 license.
Kevin O'Connorf076a3e2008-02-25 22:25:15 -05007
8#include "types.h" // u8
9#include "disk.h" // DISK_RET_SUCCESS
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040010#include "config.h" // CONFIG_FLOPPY
Kevin O'Connor9521e262008-07-04 13:04:29 -040011#include "biosvar.h" // SET_BDA
Kevin O'Connor94c749c2012-05-28 11:44:02 -040012#include "util.h" // dprintf
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050013#include "cmos.h" // inb_cmos
Kevin O'Connoraa7c2342013-07-14 15:07:21 -040014#include "pic.h" // pic_eoi1
Kevin O'Connor9521e262008-07-04 13:04:29 -040015#include "bregs.h" // struct bregs
Kevin O'Connor72eee3e2010-12-27 19:07:49 -050016#include "boot.h" // boot_add_floppy
Kevin O'Connor031ef552010-12-27 19:26:57 -050017#include "pci.h" // pci_to_bdf
18#include "pci_ids.h" // PCI_CLASS_BRIDGE_ISA
Kevin O'Connorfa9c66a2013-09-14 19:10:40 -040019#include "string.h" // memset
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050020
Kevin O'Connor7b184d82009-08-23 11:56:55 -040021#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
22#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
23#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
24#define FLOPPY_FILLBYTE 0xf6
25#define FLOPPY_GAPLEN 0x1B
26#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connor068cb4f2013-03-03 11:05:19 -050027#define FLOPPY_PIO_TIMEOUT 1000
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050028
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050029// New diskette parameter table adding 3 parameters from IBM
30// Since no provisions are made for multiple drive types, most
31// values in this table are ignored. I set parameters for 1.44M
32// floppy here
Kevin O'Connor89a2f962013-02-18 23:36:03 -050033struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050034 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040035 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
36 .specify2 = 0x02, // head load time 4ms, DMA used
37 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
38 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050039 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040040 .interblock_len = FLOPPY_GAPLEN,
41 .data_len = FLOPPY_DATALEN,
42 .gap_len = FLOPPY_FORMAT_GAPLEN,
43 .fill_byte = FLOPPY_FILLBYTE,
44 .settle_time = 0x0F, // 15ms
45 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050046 },
47 .max_track = 79, // maximum track
48 .data_rate = 0, // data transfer rate
49 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050050};
51
Kevin O'Connoref719892012-11-26 11:14:00 -050052struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050053
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040054struct floppyinfo_s {
55 struct chs_s chs;
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050056 u8 floppy_size;
57 u8 data_rate;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040058};
59
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050060#define FLOPPY_SIZE_525 0x01
61#define FLOPPY_SIZE_350 0x02
62
63#define FLOPPY_RATE_500K 0x00
64#define FLOPPY_RATE_300K 0x01
65#define FLOPPY_RATE_250K 0x02
66#define FLOPPY_RATE_1M 0x03
67
Kevin O'Connor89a2f962013-02-18 23:36:03 -050068struct floppyinfo_s FloppyInfo[] VARFSEG = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040069 // Unknown
70 { {0, 0, 0}, 0x00, 0x00},
71 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050072 { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040073 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050074 { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040075 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050076 { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040077 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050078 { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040079 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050080 { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040081 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050082 { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040083 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050084 { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040085 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050086 { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040087};
88
Kevin O'Connor77d227b2009-10-22 21:48:39 -040089struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050090init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040091{
92 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
93 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040094 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040095 }
96
Kevin O'Connord7e998f2010-02-15 22:48:28 -050097 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050098 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -050099 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400100 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500101 }
102 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400103 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500104 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400105 drive_g->blksize = DISK_SECTOR_SIZE;
106 drive_g->floppy_type = ftype;
107 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400108
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400109 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400110 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500111 return drive_g;
112}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400113
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500114static void
115addFloppy(int floppyid, int ftype)
116{
117 struct drive_s *drive_g = init_floppy(floppyid, ftype);
118 if (!drive_g)
119 return;
120 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400121 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400122 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500123 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400124}
125
126void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500127floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400128{
Kevin O'Connoref719892012-11-26 11:14:00 -0500129 memcpy(&diskette_param_table, &diskette_param_table2
130 , sizeof(diskette_param_table));
131 SET_IVT(0x1E, SEGOFF(SEG_BIOS
132 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
133
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400134 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400135 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400136 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400137
Kevin O'Connor897fb112013-02-07 23:32:48 -0500138 if (CONFIG_QEMU) {
139 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
140 if (type & 0xf0)
141 addFloppy(0, type >> 4);
142 if (type & 0x0f)
143 addFloppy(1, type & 0x0f);
144 } else {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500145 u8 type = romfile_loadint("etc/floppy0", 0);
146 if (type)
147 addFloppy(0, type);
148 type = romfile_loadint("etc/floppy1", 0);
149 if (type)
150 addFloppy(1, type);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400151 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400152
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400153 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400154
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400155 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400156}
157
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400158// Find a floppy type that matches a given image size.
159int
160find_floppy_type(u32 size)
161{
162 int i;
163 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
164 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400165 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400166 return i;
167 }
168 return -1;
169}
170
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500171
172/****************************************************************
173 * Low-level floppy IO
174 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500175
176static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500177floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500178{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500179 outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500180}
181
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500182static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500183floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500184{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500185 u8 frs = GET_BDA(floppy_recalibration_status);
186 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500187 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500188 if (!GET_BDA(floppy_motor_counter)) {
189 floppy_disable_controller();
190 return DISK_RET_ETIMEOUT;
191 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400192 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500193 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500194 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400195 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500196 // bochs, so use yield() instead.
197 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500198 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500199
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500200 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
201 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500202}
203
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500204struct floppy_pio_s {
205 u8 cmdlen;
206 u8 resplen;
207 u8 waitirq;
208 u8 data[9];
209};
210
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400211static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500212floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500213{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500214 // Send command to controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400215 u32 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500216 int i = 0;
217 for (;;) {
218 u8 sts = inb(PORT_FD_STATUS);
219 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400220 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500221 floppy_disable_controller();
222 return DISK_RET_ETIMEOUT;
223 }
224 continue;
225 }
226 if (sts & 0x40) {
227 floppy_disable_controller();
228 return DISK_RET_ECONTROLLER;
229 }
230 outb(pio->data[i++], PORT_FD_DATA);
231 if (i >= pio->cmdlen)
232 break;
233 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500234
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500235 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500236 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500237 int ret = floppy_wait_irq();
238 if (ret)
239 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400240 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500241
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500242 // Read response from controller.
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400243 end = timer_calc(FLOPPY_PIO_TIMEOUT);
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500244 i = 0;
245 for (;;) {
246 u8 sts = inb(PORT_FD_STATUS);
247 if (!(sts & 0x80)) {
Kevin O'Connor018bdd72013-07-20 18:22:57 -0400248 if (timer_check(end)) {
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500249 floppy_disable_controller();
250 return DISK_RET_ETIMEOUT;
251 }
252 continue;
253 }
254 if (i >= pio->resplen)
255 break;
256 if (!(sts & 0x40)) {
257 floppy_disable_controller();
258 return DISK_RET_ECONTROLLER;
259 }
260 pio->data[i++] = inb(PORT_FD_DATA);
261 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500262
263 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500264}
265
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500266static int
267floppy_enable_controller(void)
268{
269 outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
270 int ret = floppy_wait_irq();
271 if (ret)
272 return ret;
273
274 struct floppy_pio_s pio;
275 pio.cmdlen = 1;
276 pio.resplen = 2;
277 pio.waitirq = 0;
278 pio.data[0] = 0x08; // 08: Check Interrupt Status
279 return floppy_pio(&pio);
280}
281
282static int
283floppy_select_drive(u8 floppyid)
284{
285 // reset the disk motor timeout value of INT 08
286 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
287
288 // Enable controller if it isn't running.
289 u8 dor = inb(PORT_FD_DOR);
290 if (!(dor & 0x04)) {
291 int ret = floppy_enable_controller();
292 if (ret)
293 return ret;
294 }
295
296 // Turn on motor of selected drive, DMA & int enabled, normal operation
297 dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
298 outb(dor, PORT_FD_DOR);
299
300 return DISK_RET_SUCCESS;
301}
302
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500303
304/****************************************************************
305 * Floppy media sense
306 ****************************************************************/
307
308static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400309set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500310{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400311 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500312}
313
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500314static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400315floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500316{
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500317 int ret = floppy_select_drive(floppyid);
318 if (ret)
319 return ret;
320
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500321 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500322 struct floppy_pio_s pio;
323 pio.cmdlen = 2;
324 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500325 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500326 pio.data[0] = 0x07; // 07: Recalibrate
327 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500328 ret = floppy_pio(&pio);
329 if (ret)
330 return ret;
331
332 pio.cmdlen = 1;
333 pio.resplen = 2;
334 pio.waitirq = 0;
335 pio.data[0] = 0x08; // 08: Check Interrupt Status
336 ret = floppy_pio(&pio);
337 if (ret)
338 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500339
Kevin O'Connore51316d2012-06-10 09:09:22 -0400340 u8 frs = GET_BDA(floppy_recalibration_status);
341 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400342 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500343 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500344}
345
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500346static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500347floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
348{
349 int ret = floppy_select_drive(floppyid);
350 if (ret)
351 return ret;
352
353 // Set data rate.
354 outb(data_rate, PORT_FD_DIR);
355
356 // send Read Sector Id command
357 struct floppy_pio_s pio;
358 pio.cmdlen = 2;
359 pio.resplen = 7;
360 pio.waitirq = 1;
361 pio.data[0] = 0x4a; // 0a: Read Sector Id
362 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
363 ret = floppy_pio(&pio);
364 if (ret)
365 return ret;
366 if (pio.data[0] & 0xc0)
367 return -1;
368 return 0;
369}
370
371static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400372floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500373{
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500374 u8 ftype = GET_GLOBAL(drive_g->floppy_type), stype = ftype;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400375 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500376
377 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
378 int ret = floppy_drive_readid(floppyid, data_rate, 0);
379 if (ret) {
380 // Attempt media sense.
381 for (stype=1; ; stype++) {
382 if (stype >= ARRAY_SIZE(FloppyInfo))
383 return DISK_RET_EMEDIA;
384 if (stype==ftype
385 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
386 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
387 || (GET_GLOBAL(FloppyInfo[stype].chs.heads)
388 > GET_GLOBAL(FloppyInfo[ftype].chs.heads))
389 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
390 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
391 || (GET_GLOBAL(FloppyInfo[stype].chs.spt)
392 > GET_GLOBAL(FloppyInfo[ftype].chs.spt)))
393 continue;
394 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
395 ret = floppy_drive_readid(floppyid, data_rate, 0);
396 if (!ret)
397 break;
398 }
399 }
400
401 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
402 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
403 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
404 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
405 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
406 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
407 fms |= FMS_DOUBLE_STEPPING;
408 SET_BDA(floppy_media_state[floppyid], fms);
409
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400410 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500411}
412
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400413static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400414check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500415{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400416 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400417 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
418 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500419 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400420 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500421
422 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500423 int ret = floppy_drive_recal(floppyid);
424 if (ret)
425 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500426
427 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400428 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500429}
430
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500431
432/****************************************************************
Kevin O'Connorc8494052013-03-03 10:27:30 -0500433 * Floppy DMA
434 ****************************************************************/
435
436// Perform a floppy transfer command (setup DMA and issue PIO).
437static int
438floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
439{
440 int ret = check_recal_drive(op->drive_g);
441 if (ret)
442 return ret;
443
444 // es:bx = pointer to where to place information from diskette
445 u32 addr = (u32)op->buf_fl;
446 int count = op->count * blocksize;
447
448 // check for 64K boundary overrun
449 u16 end = count - 1;
450 u32 last_addr = addr + end;
451 if ((addr >> 16) != (last_addr >> 16))
452 return DISK_RET_EBOUNDARY;
453
454 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
455 if (pio->data[0] == 0xe6)
456 // read
457 mode_register = 0x46;
458
459 //DEBUGF("floppy dma c2\n");
460 outb(0x06, PORT_DMA1_MASK_REG);
461 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
462 outb(addr, PORT_DMA_ADDR_2);
463 outb(addr>>8, PORT_DMA_ADDR_2);
464 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
465 outb(end, PORT_DMA_CNT_2);
466 outb(end>>8, PORT_DMA_CNT_2);
467
468 // port 0b: DMA-1 Mode Register
469 // transfer type=write, channel 2
470 outb(mode_register, PORT_DMA1_MODE_REG);
471
472 // port 81: DMA-1 Page Register, channel 2
473 outb(addr>>16, PORT_DMA_PAGE_2);
474
475 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
476
477 ret = floppy_select_drive(pio->data[1] & 1);
478 if (ret)
479 return ret;
480 pio->resplen = 7;
481 pio->waitirq = 1;
482 ret = floppy_pio(pio);
483 if (ret)
484 return ret;
485
486 // Populate floppy_return_status in BDA
487 int i;
488 for (i=0; i<7; i++)
489 SET_BDA(floppy_return_status[i], pio->data[i]);
490
491 if (pio->data[0] & 0xc0) {
492 if (pio->data[1] & 0x02)
493 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500494 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500495 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
496 , pio->data[4], pio->data[5], pio->data[6]);
497 return DISK_RET_ECONTROLLER;
498 }
499
500 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
501 set_diskette_current_cyl(pio->data[0] & 1, track);
502
503 return DISK_RET_SUCCESS;
504}
505
506
507/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400508 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500509 ****************************************************************/
510
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500511static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400512lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500513{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400514 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400515
516 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400517 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400518 *sector = tmp % nlspt;
519
520 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400521 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400522 *head = tmp % nlh;
523
524 tmp /= nlh;
525 *track = tmp;
526}
527
528// diskette controller reset
529static int
530floppy_reset(struct disk_op_s *op)
531{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400532 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor5bb75522013-03-03 15:32:31 -0500533 SET_BDA(floppy_recalibration_status, 0);
534 SET_BDA(floppy_media_state[0], 0);
535 SET_BDA(floppy_media_state[1], 0);
536 SET_BDA(floppy_track[0], 0);
537 SET_BDA(floppy_track[1], 0);
538 SET_BDA(floppy_last_data_rate, 0);
539 floppy_disable_controller();
540 return floppy_select_drive(floppyid);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500541}
542
543// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400544static int
545floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500546{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400547 u8 track, sector, head;
548 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500549
550 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400551 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500552 struct floppy_pio_s pio;
553 pio.cmdlen = 9;
554 pio.data[0] = 0xe6; // e6: read normal data
555 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
556 pio.data[2] = track;
557 pio.data[3] = head;
558 pio.data[4] = sector;
559 pio.data[5] = FLOPPY_SIZE_CODE;
560 pio.data[6] = sector + op->count - 1; // last sector to read on track
561 pio.data[7] = FLOPPY_GAPLEN;
562 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500563
Kevin O'Connorc8494052013-03-03 10:27:30 -0500564 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400565 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400566 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400567 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400568fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400569 op->count = 0; // no sectors read
570 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500571}
572
573// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400574static int
575floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500576{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400577 u8 track, sector, head;
578 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500579
580 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400581 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500582 struct floppy_pio_s pio;
583 pio.cmdlen = 9;
584 pio.data[0] = 0xc5; // c5: write normal data
585 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
586 pio.data[2] = track;
587 pio.data[3] = head;
588 pio.data[4] = sector;
589 pio.data[5] = FLOPPY_SIZE_CODE;
590 pio.data[6] = sector + op->count - 1; // last sector to write on track
591 pio.data[7] = FLOPPY_GAPLEN;
592 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500593
Kevin O'Connorc8494052013-03-03 10:27:30 -0500594 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400595 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400596 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400597 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400598fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400599 op->count = 0; // no sectors read
600 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500601}
602
603// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400604static int
605floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500606{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400607 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400608 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400609 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500610
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400611 u8 track, sector, head;
612 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500613
614 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400615 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400616 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400617 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400618fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400619 op->count = 0; // no sectors read
620 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500621}
622
623// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400624static int
625floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500626{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400627 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500628
629 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400630 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500631 struct floppy_pio_s pio;
632 pio.cmdlen = 6;
633 pio.data[0] = 0x4d; // 4d: format track
634 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
635 pio.data[2] = FLOPPY_SIZE_CODE;
636 pio.data[3] = op->count; // number of sectors per track
637 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
638 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500639
Kevin O'Connorc8494052013-03-03 10:27:30 -0500640 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500641}
642
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400643int
644process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500645{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400646 if (!CONFIG_FLOPPY)
647 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500648
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400649 switch (op->command) {
650 case CMD_RESET:
651 return floppy_reset(op);
652 case CMD_READ:
653 return floppy_read(op);
654 case CMD_WRITE:
655 return floppy_write(op);
656 case CMD_VERIFY:
657 return floppy_verify(op);
658 case CMD_FORMAT:
659 return floppy_format(op);
660 default:
661 op->count = 0;
662 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500663 }
664}
665
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500666
667/****************************************************************
668 * HW irqs
669 ****************************************************************/
670
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500671// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500672void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400673handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500674{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400675 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500676 return;
677 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400678
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500679 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400680 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500681 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400682
Kevin O'Connoraa7c2342013-07-14 15:07:21 -0400683 pic_eoi1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500684}
685
686// Called from int08 handler.
687void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500688floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500689{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400690 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400691 return;
692
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500693 // time to turn off drive(s)?
694 u8 fcount = GET_BDA(floppy_motor_counter);
695 if (fcount) {
696 fcount--;
697 SET_BDA(floppy_motor_counter, fcount);
698 if (fcount == 0)
699 // turn motor(s) off
700 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
701 }
702}