blob: 62802ba5ac5d58d6ac1ec8fd437ae68fb517cbd2 [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'Connord21c0892008-11-26 17:02:43 -050014#include "pic.h" // eoi_pic1
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'Connorf076a3e2008-02-25 22:25:15 -050019
Kevin O'Connor7b184d82009-08-23 11:56:55 -040020#define FLOPPY_SIZE_CODE 0x02 // 512 byte sectors
21#define FLOPPY_DATALEN 0xff // Not used - because size code is 0x02
22#define FLOPPY_MOTOR_TICKS 37 // ~2 seconds
23#define FLOPPY_FILLBYTE 0xf6
24#define FLOPPY_GAPLEN 0x1B
25#define FLOPPY_FORMAT_GAPLEN 0x6c
Kevin O'Connor068cb4f2013-03-03 11:05:19 -050026#define FLOPPY_PIO_TIMEOUT 1000
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050027
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050028// New diskette parameter table adding 3 parameters from IBM
29// Since no provisions are made for multiple drive types, most
30// values in this table are ignored. I set parameters for 1.44M
31// floppy here
Kevin O'Connor89a2f962013-02-18 23:36:03 -050032struct floppy_ext_dbt_s diskette_param_table2 VARFSEG = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050033 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040034 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
35 .specify2 = 0x02, // head load time 4ms, DMA used
36 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
37 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050038 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040039 .interblock_len = FLOPPY_GAPLEN,
40 .data_len = FLOPPY_DATALEN,
41 .gap_len = FLOPPY_FORMAT_GAPLEN,
42 .fill_byte = FLOPPY_FILLBYTE,
43 .settle_time = 0x0F, // 15ms
44 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050045 },
46 .max_track = 79, // maximum track
47 .data_rate = 0, // data transfer rate
48 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050049};
50
Kevin O'Connoref719892012-11-26 11:14:00 -050051struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7);
Kevin O'Connor30853762009-01-17 18:49:20 -050052
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040053struct floppyinfo_s {
54 struct chs_s chs;
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050055 u8 floppy_size;
56 u8 data_rate;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040057};
58
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050059#define FLOPPY_SIZE_525 0x01
60#define FLOPPY_SIZE_350 0x02
61
62#define FLOPPY_RATE_500K 0x00
63#define FLOPPY_RATE_300K 0x01
64#define FLOPPY_RATE_250K 0x02
65#define FLOPPY_RATE_1M 0x03
66
Kevin O'Connor89a2f962013-02-18 23:36:03 -050067struct floppyinfo_s FloppyInfo[] VARFSEG = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040068 // Unknown
69 { {0, 0, 0}, 0x00, 0x00},
70 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050071 { {2, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040072 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050073 { {2, 80, 15}, FLOPPY_SIZE_525, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040074 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050075 { {2, 80, 9}, FLOPPY_SIZE_350, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040076 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050077 { {2, 80, 18}, FLOPPY_SIZE_350, FLOPPY_RATE_500K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040078 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050079 { {2, 80, 36}, FLOPPY_SIZE_350, FLOPPY_RATE_1M},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040080 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050081 { {1, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040082 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050083 { {1, 40, 9}, FLOPPY_SIZE_525, FLOPPY_RATE_300K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040084 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -050085 { {2, 40, 8}, FLOPPY_SIZE_525, FLOPPY_RATE_250K},
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040086};
87
Kevin O'Connor77d227b2009-10-22 21:48:39 -040088struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050089init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040090{
91 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
92 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040093 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040094 }
95
Kevin O'Connord7e998f2010-02-15 22:48:28 -050096 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050097 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -050098 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -040099 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500100 }
101 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400102 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500103 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400104 drive_g->blksize = DISK_SECTOR_SIZE;
105 drive_g->floppy_type = ftype;
106 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400107
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400108 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400109 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500110 return drive_g;
111}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400112
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500113static void
114addFloppy(int floppyid, int ftype)
115{
116 struct drive_s *drive_g = init_floppy(floppyid, ftype);
117 if (!drive_g)
118 return;
119 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400120 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400121 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500122 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400123}
124
125void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500126floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400127{
Kevin O'Connoref719892012-11-26 11:14:00 -0500128 memcpy(&diskette_param_table, &diskette_param_table2
129 , sizeof(diskette_param_table));
130 SET_IVT(0x1E, SEGOFF(SEG_BIOS
131 , (u32)&diskette_param_table2 - BUILD_BIOS_ADDR));
132
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400133 if (! CONFIG_FLOPPY)
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400134 return;
Kevin O'Connor35192dd2008-06-08 19:18:33 -0400135 dprintf(3, "init floppy drives\n");
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400136
Kevin O'Connor897fb112013-02-07 23:32:48 -0500137 if (CONFIG_QEMU) {
138 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
139 if (type & 0xf0)
140 addFloppy(0, type >> 4);
141 if (type & 0x0f)
142 addFloppy(1, type & 0x0f);
143 } else {
Kevin O'Connor8b73b832012-11-26 11:18:11 -0500144 u8 type = romfile_loadint("etc/floppy0", 0);
145 if (type)
146 addFloppy(0, type);
147 type = romfile_loadint("etc/floppy1", 0);
148 if (type)
149 addFloppy(1, type);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400150 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400151
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400152 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400153
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400154 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400155}
156
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400157// Find a floppy type that matches a given image size.
158int
159find_floppy_type(u32 size)
160{
161 int i;
162 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
163 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400164 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400165 return i;
166 }
167 return -1;
168}
169
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500170
171/****************************************************************
172 * Low-level floppy IO
173 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500174
175static void
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500176floppy_disable_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500177{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500178 outb(inb(PORT_FD_DOR) & ~0x04, PORT_FD_DOR);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500179}
180
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500181static int
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500182floppy_wait_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500183{
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500184 u8 frs = GET_BDA(floppy_recalibration_status);
185 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500186 for (;;) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500187 if (!GET_BDA(floppy_motor_counter)) {
188 floppy_disable_controller();
189 return DISK_RET_ETIMEOUT;
190 }
Kevin O'Connore51316d2012-06-10 09:09:22 -0400191 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500192 if (frs & FRS_IRQ)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500193 break;
Kevin O'Connor94c749c2012-05-28 11:44:02 -0400194 // Could use yield_toirq() here, but that causes issues on
Kevin O'Connor7a98fd02010-01-17 13:00:49 -0500195 // bochs, so use yield() instead.
196 yield();
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500197 }
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500198
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500199 SET_BDA(floppy_recalibration_status, frs & ~FRS_IRQ);
200 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500201}
202
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500203struct floppy_pio_s {
204 u8 cmdlen;
205 u8 resplen;
206 u8 waitirq;
207 u8 data[9];
208};
209
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400210static int
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500211floppy_pio(struct floppy_pio_s *pio)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500212{
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500213 // Send command to controller.
214 u64 end = calc_future_tsc(FLOPPY_PIO_TIMEOUT);
215 int i = 0;
216 for (;;) {
217 u8 sts = inb(PORT_FD_STATUS);
218 if (!(sts & 0x80)) {
219 if (check_tsc(end)) {
220 floppy_disable_controller();
221 return DISK_RET_ETIMEOUT;
222 }
223 continue;
224 }
225 if (sts & 0x40) {
226 floppy_disable_controller();
227 return DISK_RET_ECONTROLLER;
228 }
229 outb(pio->data[i++], PORT_FD_DATA);
230 if (i >= pio->cmdlen)
231 break;
232 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500233
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500234 // Wait for command to complete.
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500235 if (pio->waitirq) {
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500236 int ret = floppy_wait_irq();
237 if (ret)
238 return ret;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400239 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500240
Kevin O'Connor068cb4f2013-03-03 11:05:19 -0500241 // Read response from controller.
242 end = calc_future_tsc(FLOPPY_PIO_TIMEOUT);
243 i = 0;
244 for (;;) {
245 u8 sts = inb(PORT_FD_STATUS);
246 if (!(sts & 0x80)) {
247 if (check_tsc(end)) {
248 floppy_disable_controller();
249 return DISK_RET_ETIMEOUT;
250 }
251 continue;
252 }
253 if (i >= pio->resplen)
254 break;
255 if (!(sts & 0x40)) {
256 floppy_disable_controller();
257 return DISK_RET_ECONTROLLER;
258 }
259 pio->data[i++] = inb(PORT_FD_DATA);
260 }
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500261
262 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500263}
264
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500265static int
266floppy_enable_controller(void)
267{
268 outb(inb(PORT_FD_DOR) | 0x04, PORT_FD_DOR);
269 int ret = floppy_wait_irq();
270 if (ret)
271 return ret;
272
273 struct floppy_pio_s pio;
274 pio.cmdlen = 1;
275 pio.resplen = 2;
276 pio.waitirq = 0;
277 pio.data[0] = 0x08; // 08: Check Interrupt Status
278 return floppy_pio(&pio);
279}
280
281static int
282floppy_select_drive(u8 floppyid)
283{
284 // reset the disk motor timeout value of INT 08
285 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
286
287 // Enable controller if it isn't running.
288 u8 dor = inb(PORT_FD_DOR);
289 if (!(dor & 0x04)) {
290 int ret = floppy_enable_controller();
291 if (ret)
292 return ret;
293 }
294
295 // Turn on motor of selected drive, DMA & int enabled, normal operation
296 dor = (floppyid ? 0x20 : 0x10) | 0x0c | floppyid;
297 outb(dor, PORT_FD_DOR);
298
299 return DISK_RET_SUCCESS;
300}
301
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500302
303/****************************************************************
304 * Floppy media sense
305 ****************************************************************/
306
307static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400308set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500309{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400310 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500311}
312
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500313static int
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400314floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500315{
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500316 int ret = floppy_select_drive(floppyid);
317 if (ret)
318 return ret;
319
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500320 // send Recalibrate command (2 bytes) to controller
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500321 struct floppy_pio_s pio;
322 pio.cmdlen = 2;
323 pio.resplen = 0;
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500324 pio.waitirq = 1;
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500325 pio.data[0] = 0x07; // 07: Recalibrate
326 pio.data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500327 ret = floppy_pio(&pio);
328 if (ret)
329 return ret;
330
331 pio.cmdlen = 1;
332 pio.resplen = 2;
333 pio.waitirq = 0;
334 pio.data[0] = 0x08; // 08: Check Interrupt Status
335 ret = floppy_pio(&pio);
336 if (ret)
337 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500338
Kevin O'Connore51316d2012-06-10 09:09:22 -0400339 u8 frs = GET_BDA(floppy_recalibration_status);
340 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400341 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500342 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500343}
344
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500345static int
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500346floppy_drive_readid(u8 floppyid, u8 data_rate, u8 head)
347{
348 int ret = floppy_select_drive(floppyid);
349 if (ret)
350 return ret;
351
352 // Set data rate.
353 outb(data_rate, PORT_FD_DIR);
354
355 // send Read Sector Id command
356 struct floppy_pio_s pio;
357 pio.cmdlen = 2;
358 pio.resplen = 7;
359 pio.waitirq = 1;
360 pio.data[0] = 0x4a; // 0a: Read Sector Id
361 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
362 ret = floppy_pio(&pio);
363 if (ret)
364 return ret;
365 if (pio.data[0] & 0xc0)
366 return -1;
367 return 0;
368}
369
370static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400371floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500372{
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500373 u8 ftype = GET_GLOBAL(drive_g->floppy_type), stype = ftype;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400374 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connore7c5a7e2013-03-03 13:59:58 -0500375
376 u8 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
377 int ret = floppy_drive_readid(floppyid, data_rate, 0);
378 if (ret) {
379 // Attempt media sense.
380 for (stype=1; ; stype++) {
381 if (stype >= ARRAY_SIZE(FloppyInfo))
382 return DISK_RET_EMEDIA;
383 if (stype==ftype
384 || (GET_GLOBAL(FloppyInfo[stype].floppy_size)
385 != GET_GLOBAL(FloppyInfo[ftype].floppy_size))
386 || (GET_GLOBAL(FloppyInfo[stype].chs.heads)
387 > GET_GLOBAL(FloppyInfo[ftype].chs.heads))
388 || (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
389 > GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
390 || (GET_GLOBAL(FloppyInfo[stype].chs.spt)
391 > GET_GLOBAL(FloppyInfo[ftype].chs.spt)))
392 continue;
393 data_rate = GET_GLOBAL(FloppyInfo[stype].data_rate);
394 ret = floppy_drive_readid(floppyid, data_rate, 0);
395 if (!ret)
396 break;
397 }
398 }
399
400 u8 old_data_rate = GET_BDA(floppy_media_state[floppyid]) >> 6;
401 SET_BDA(floppy_last_data_rate, (old_data_rate<<2) | (data_rate<<6));
402 u8 media = (stype == 1 ? 0x04 : (stype == 2 ? 0x05 : 0x07));
403 u8 fms = (data_rate<<6) | FMS_MEDIA_DRIVE_ESTABLISHED | media;
404 if (GET_GLOBAL(FloppyInfo[stype].chs.cylinders)
405 < GET_GLOBAL(FloppyInfo[ftype].chs.cylinders))
406 fms |= FMS_DOUBLE_STEPPING;
407 SET_BDA(floppy_media_state[floppyid], fms);
408
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400409 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500410}
411
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400412static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400413check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500414{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400415 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400416 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
417 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500418 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400419 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500420
421 // Recalibrate drive.
Kevin O'Connor1f711d22013-03-02 21:26:54 -0500422 int ret = floppy_drive_recal(floppyid);
423 if (ret)
424 return ret;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500425
426 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400427 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500428}
429
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500430
431/****************************************************************
Kevin O'Connorc8494052013-03-03 10:27:30 -0500432 * Floppy DMA
433 ****************************************************************/
434
435// Perform a floppy transfer command (setup DMA and issue PIO).
436static int
437floppy_cmd(struct disk_op_s *op, int blocksize, struct floppy_pio_s *pio)
438{
439 int ret = check_recal_drive(op->drive_g);
440 if (ret)
441 return ret;
442
443 // es:bx = pointer to where to place information from diskette
444 u32 addr = (u32)op->buf_fl;
445 int count = op->count * blocksize;
446
447 // check for 64K boundary overrun
448 u16 end = count - 1;
449 u32 last_addr = addr + end;
450 if ((addr >> 16) != (last_addr >> 16))
451 return DISK_RET_EBOUNDARY;
452
453 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
454 if (pio->data[0] == 0xe6)
455 // read
456 mode_register = 0x46;
457
458 //DEBUGF("floppy dma c2\n");
459 outb(0x06, PORT_DMA1_MASK_REG);
460 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
461 outb(addr, PORT_DMA_ADDR_2);
462 outb(addr>>8, PORT_DMA_ADDR_2);
463 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
464 outb(end, PORT_DMA_CNT_2);
465 outb(end>>8, PORT_DMA_CNT_2);
466
467 // port 0b: DMA-1 Mode Register
468 // transfer type=write, channel 2
469 outb(mode_register, PORT_DMA1_MODE_REG);
470
471 // port 81: DMA-1 Page Register, channel 2
472 outb(addr>>16, PORT_DMA_PAGE_2);
473
474 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
475
476 ret = floppy_select_drive(pio->data[1] & 1);
477 if (ret)
478 return ret;
479 pio->resplen = 7;
480 pio->waitirq = 1;
481 ret = floppy_pio(pio);
482 if (ret)
483 return ret;
484
485 // Populate floppy_return_status in BDA
486 int i;
487 for (i=0; i<7; i++)
488 SET_BDA(floppy_return_status[i], pio->data[i]);
489
490 if (pio->data[0] & 0xc0) {
491 if (pio->data[1] & 0x02)
492 return DISK_RET_EWRITEPROTECT;
Kevin O'Connor8413b322013-03-09 20:07:38 -0500493 dprintf(1, "floppy error: %02x %02x %02x %02x %02x %02x %02x\n"
Kevin O'Connorc8494052013-03-03 10:27:30 -0500494 , pio->data[0], pio->data[1], pio->data[2], pio->data[3]
495 , pio->data[4], pio->data[5], pio->data[6]);
496 return DISK_RET_ECONTROLLER;
497 }
498
499 u8 track = (pio->cmdlen == 9 ? pio->data[3] : 0);
500 set_diskette_current_cyl(pio->data[0] & 1, track);
501
502 return DISK_RET_SUCCESS;
503}
504
505
506/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400507 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500508 ****************************************************************/
509
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500510static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400511lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500512{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400513 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400514
515 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400516 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400517 *sector = tmp % nlspt;
518
519 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400520 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400521 *head = tmp % nlh;
522
523 tmp /= nlh;
524 *track = tmp;
525}
526
527// diskette controller reset
528static int
529floppy_reset(struct disk_op_s *op)
530{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400531 u8 floppyid = GET_GLOBAL(op->drive_g->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'Connoraf5aabb2009-08-16 18:48:38 -0400546 u8 track, sector, head;
547 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500548
549 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400550 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500551 struct floppy_pio_s pio;
552 pio.cmdlen = 9;
553 pio.data[0] = 0xe6; // e6: read normal data
554 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
555 pio.data[2] = track;
556 pio.data[3] = head;
557 pio.data[4] = sector;
558 pio.data[5] = FLOPPY_SIZE_CODE;
559 pio.data[6] = sector + op->count - 1; // last sector to read on track
560 pio.data[7] = FLOPPY_GAPLEN;
561 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500562
Kevin O'Connorc8494052013-03-03 10:27:30 -0500563 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400564 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400565 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400566 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400567fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400568 op->count = 0; // no sectors read
569 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500570}
571
572// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400573static int
574floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500575{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400576 u8 track, sector, head;
577 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500578
579 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400580 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500581 struct floppy_pio_s pio;
582 pio.cmdlen = 9;
583 pio.data[0] = 0xc5; // c5: write normal data
584 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
585 pio.data[2] = track;
586 pio.data[3] = head;
587 pio.data[4] = sector;
588 pio.data[5] = FLOPPY_SIZE_CODE;
589 pio.data[6] = sector + op->count - 1; // last sector to write on track
590 pio.data[7] = FLOPPY_GAPLEN;
591 pio.data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500592
Kevin O'Connorc8494052013-03-03 10:27:30 -0500593 int res = floppy_cmd(op, DISK_SECTOR_SIZE, &pio);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400594 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400595 goto fail;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400596 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400597fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400598 op->count = 0; // no sectors read
599 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500600}
601
602// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400603static int
604floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500605{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400606 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400607 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400608 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500609
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400610 u8 track, sector, head;
611 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500612
613 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400614 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400615 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400616 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400617fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400618 op->count = 0; // no sectors read
619 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500620}
621
622// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400623static int
624floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500625{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400626 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500627
628 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400629 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor9ba374c2013-03-02 18:37:04 -0500630 struct floppy_pio_s pio;
631 pio.cmdlen = 6;
632 pio.data[0] = 0x4d; // 4d: format track
633 pio.data[1] = (head << 2) | floppyid; // HD DR1 DR2
634 pio.data[2] = FLOPPY_SIZE_CODE;
635 pio.data[3] = op->count; // number of sectors per track
636 pio.data[4] = FLOPPY_FORMAT_GAPLEN;
637 pio.data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500638
Kevin O'Connorc8494052013-03-03 10:27:30 -0500639 return floppy_cmd(op, 4, &pio);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500640}
641
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400642int
643process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500644{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400645 if (!CONFIG_FLOPPY)
646 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500647
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400648 switch (op->command) {
649 case CMD_RESET:
650 return floppy_reset(op);
651 case CMD_READ:
652 return floppy_read(op);
653 case CMD_WRITE:
654 return floppy_write(op);
655 case CMD_VERIFY:
656 return floppy_verify(op);
657 case CMD_FORMAT:
658 return floppy_format(op);
659 default:
660 op->count = 0;
661 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500662 }
663}
664
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500665
666/****************************************************************
667 * HW irqs
668 ****************************************************************/
669
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500670// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500671void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400672handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500673{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400674 if (! CONFIG_FLOPPY)
Kevin O'Connorff5e0052012-12-07 11:25:58 -0500675 return;
676 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor40967022008-07-21 22:23:05 -0400677
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500678 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400679 u8 frs = GET_BDA(floppy_recalibration_status);
Kevin O'Connor6e529bd2013-03-02 20:30:50 -0500680 SET_BDA(floppy_recalibration_status, frs | FRS_IRQ);
Kevin O'Connor40967022008-07-21 22:23:05 -0400681
Kevin O'Connor40967022008-07-21 22:23:05 -0400682 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500683}
684
685// Called from int08 handler.
686void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500687floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500688{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400689 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400690 return;
691
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500692 // time to turn off drive(s)?
693 u8 fcount = GET_BDA(floppy_motor_counter);
694 if (fcount) {
695 fcount--;
696 SET_BDA(floppy_motor_counter, fcount);
697 if (fcount == 0)
698 // turn motor(s) off
699 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
700 }
701}