blob: ce54d0c678f424780109b852da7d85a1590e18fa [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'Connorf076a3e2008-02-25 22:25:15 -050026
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050027// New diskette parameter table adding 3 parameters from IBM
28// Since no provisions are made for multiple drive types, most
29// values in this table are ignored. I set parameters for 1.44M
30// floppy here
Kevin O'Connor372e0712009-09-09 09:51:31 -040031struct floppy_ext_dbt_s diskette_param_table2 VAR16VISIBLE = {
Kevin O'Connor44c631d2008-03-02 11:24:36 -050032 .dbt = {
Kevin O'Connor7b184d82009-08-23 11:56:55 -040033 .specify1 = 0xAF, // step rate 12ms, head unload 240ms
34 .specify2 = 0x02, // head load time 4ms, DMA used
35 .shutoff_ticks = FLOPPY_MOTOR_TICKS, // ~2 seconds
36 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor44c631d2008-03-02 11:24:36 -050037 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040038 .interblock_len = FLOPPY_GAPLEN,
39 .data_len = FLOPPY_DATALEN,
40 .gap_len = FLOPPY_FORMAT_GAPLEN,
41 .fill_byte = FLOPPY_FILLBYTE,
42 .settle_time = 0x0F, // 15ms
43 .startup_time = 0x08, // 1 second
Kevin O'Connor44c631d2008-03-02 11:24:36 -050044 },
45 .max_track = 79, // maximum track
46 .data_rate = 0, // data transfer rate
47 .drive_type = 4, // drive type in cmos
Kevin O'Connorf076a3e2008-02-25 22:25:15 -050048};
49
Kevin O'Connor30853762009-01-17 18:49:20 -050050// Since no provisions are made for multiple drive types, most
51// values in this table are ignored. I set parameters for 1.44M
52// floppy here
53struct floppy_dbt_s diskette_param_table VAR16FIXED(0xefc7) = {
54 .specify1 = 0xAF,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040055 .specify2 = 0x02,
56 .shutoff_ticks = FLOPPY_MOTOR_TICKS,
57 .bps_code = FLOPPY_SIZE_CODE,
Kevin O'Connor30853762009-01-17 18:49:20 -050058 .sectors = 18,
Kevin O'Connor7b184d82009-08-23 11:56:55 -040059 .interblock_len = FLOPPY_GAPLEN,
60 .data_len = FLOPPY_DATALEN,
61 .gap_len = FLOPPY_FORMAT_GAPLEN,
62 .fill_byte = FLOPPY_FILLBYTE,
Kevin O'Connor30853762009-01-17 18:49:20 -050063 .settle_time = 0x0F,
64 .startup_time = 0x08,
65};
66
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040067struct floppyinfo_s {
68 struct chs_s chs;
69 u8 config_data;
70 u8 media_state;
71};
72
Kevin O'Connor372e0712009-09-09 09:51:31 -040073struct floppyinfo_s FloppyInfo[] VAR16VISIBLE = {
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040074 // Unknown
75 { {0, 0, 0}, 0x00, 0x00},
76 // 1 - 360KB, 5.25" - 2 heads, 40 tracks, 9 sectors
77 { {2, 40, 9}, 0x00, 0x25},
78 // 2 - 1.2MB, 5.25" - 2 heads, 80 tracks, 15 sectors
79 { {2, 80, 15}, 0x00, 0x25},
80 // 3 - 720KB, 3.5" - 2 heads, 80 tracks, 9 sectors
81 { {2, 80, 9}, 0x00, 0x17},
82 // 4 - 1.44MB, 3.5" - 2 heads, 80 tracks, 18 sectors
83 { {2, 80, 18}, 0x00, 0x17},
84 // 5 - 2.88MB, 3.5" - 2 heads, 80 tracks, 36 sectors
85 { {2, 80, 36}, 0xCC, 0xD7},
86 // 6 - 160k, 5.25" - 1 heads, 40 tracks, 8 sectors
87 { {1, 40, 8}, 0x00, 0x27},
88 // 7 - 180k, 5.25" - 1 heads, 40 tracks, 9 sectors
89 { {1, 40, 9}, 0x00, 0x27},
90 // 8 - 320k, 5.25" - 2 heads, 40 tracks, 8 sectors
91 { {2, 40, 8}, 0x00, 0x27},
92};
93
Kevin O'Connor77d227b2009-10-22 21:48:39 -040094struct drive_s *
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -050095init_floppy(int floppyid, int ftype)
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -040096{
97 if (ftype <= 0 || ftype >= ARRAY_SIZE(FloppyInfo)) {
98 dprintf(1, "Bad floppy type %d\n", ftype);
Kevin O'Connor77d227b2009-10-22 21:48:39 -040099 return NULL;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400100 }
101
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500102 struct drive_s *drive_g = malloc_fseg(sizeof(*drive_g));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500103 if (!drive_g) {
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500104 warn_noalloc();
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400105 return NULL;
Kevin O'Connord7e998f2010-02-15 22:48:28 -0500106 }
107 memset(drive_g, 0, sizeof(*drive_g));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400108 drive_g->cntl_id = floppyid;
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500109 drive_g->type = DTYPE_FLOPPY;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400110 drive_g->blksize = DISK_SECTOR_SIZE;
111 drive_g->floppy_type = ftype;
112 drive_g->sectors = (u64)-1;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400113
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400114 memcpy(&drive_g->lchs, &FloppyInfo[ftype].chs
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400115 , sizeof(FloppyInfo[ftype].chs));
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500116 return drive_g;
117}
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400118
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500119static void
120addFloppy(int floppyid, int ftype)
121{
122 struct drive_s *drive_g = init_floppy(floppyid, ftype);
123 if (!drive_g)
124 return;
125 char *desc = znprintf(MAXDESCSIZE, "Floppy [drive %c]", 'A' + floppyid);
Kevin O'Connor0cd70052011-07-02 14:04:19 -0400126 struct pci_device *pci = pci_find_class(PCI_CLASS_BRIDGE_ISA); /* isa-to-pci bridge */
Kevin O'Connor03e589c2011-07-09 14:35:37 -0400127 int prio = bootprio_find_fdc_device(pci, PORT_FD_BASE, floppyid);
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500128 boot_add_floppy(drive_g, desc, prio);
Kevin O'Connor51fd0a12009-09-12 13:20:14 -0400129}
130
131void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500132floppy_setup(void)
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400133{
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'Connor7661f2f2009-02-07 01:21:00 -0500138 if (CONFIG_COREBOOT) {
139 // XXX - disable floppies on coreboot for now.
140 } else {
141 u8 type = inb_cmos(CMOS_FLOPPY_DRIVE_TYPE);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400142 if (type & 0xf0)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500143 addFloppy(0, type >> 4);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400144 if (type & 0x0f)
Kevin O'Connorca2bc1c2010-12-29 21:41:19 -0500145 addFloppy(1, type & 0x0f);
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400146 }
Kevin O'Connor88c00ee2008-05-18 00:14:13 -0400147
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400148 outb(0x02, PORT_DMA1_MASK_REG);
Kevin O'Connorf54c1502008-06-14 15:56:16 -0400149
Kevin O'Connorcc9e1bf2010-07-28 21:31:38 -0400150 enable_hwirq(6, FUNC16(entry_0e));
Kevin O'Connor3bbcc142008-04-13 17:07:33 -0400151}
152
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400153// Find a floppy type that matches a given image size.
154int
155find_floppy_type(u32 size)
156{
157 int i;
158 for (i=1; i<ARRAY_SIZE(FloppyInfo); i++) {
159 struct chs_s *c = &FloppyInfo[i].chs;
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400160 if (c->cylinders * c->heads * c->spt * DISK_SECTOR_SIZE == size)
Kevin O'Connora3855ad2009-08-16 21:59:40 -0400161 return i;
162 }
163 return -1;
164}
165
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500166
167/****************************************************************
168 * Low-level floppy IO
169 ****************************************************************/
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500170
171static void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500172floppy_reset_controller(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500173{
174 // Reset controller
175 u8 val8 = inb(PORT_FD_DOR);
176 outb(val8 & ~0x04, PORT_FD_DOR);
177 outb(val8 | 0x04, PORT_FD_DOR);
178
179 // Wait for controller to come out of reset
180 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
181 ;
182}
183
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500184static int
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500185wait_floppy_irq(void)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500186{
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400187 ASSERT16();
Kevin O'Connore51316d2012-06-10 09:09:22 -0400188 u8 frs;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500189 for (;;) {
Kevin O'Connor10ad7992009-10-24 11:06:08 -0400190 if (!GET_BDA(floppy_motor_counter))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500191 return -1;
Kevin O'Connore51316d2012-06-10 09:09:22 -0400192 frs = GET_BDA(floppy_recalibration_status);
193 if (frs & FRS_TIMEOUT)
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'Connore51316d2012-06-10 09:09:22 -0400200 frs &= ~FRS_TIMEOUT;
201 SET_BDA(floppy_recalibration_status, frs);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500202 return 0;
203}
204
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500205static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400206floppy_prepare_controller(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500207{
Kevin O'Connore51316d2012-06-10 09:09:22 -0400208 u8 frs = GET_BDA(floppy_recalibration_status);
209 SET_BDA(floppy_recalibration_status, frs & ~FRS_TIMEOUT);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500210
211 // turn on motor of selected drive, DMA & int enabled, normal operation
212 u8 prev_reset = inb(PORT_FD_DOR) & 0x04;
213 u8 dor = 0x10;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400214 if (floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500215 dor = 0x20;
216 dor |= 0x0c;
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400217 dor |= floppyid;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500218 outb(dor, PORT_FD_DOR);
219
220 // reset the disk motor timeout value of INT 08
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400221 SET_BDA(floppy_motor_counter, FLOPPY_MOTOR_TICKS);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500222
223 // wait for drive readiness
224 while ((inb(PORT_FD_STATUS) & 0xc0) != 0x80)
225 ;
226
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500227 if (!prev_reset)
228 wait_floppy_irq();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500229}
230
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400231static int
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500232floppy_pio(u8 *cmd, u8 cmdlen)
233{
234 floppy_prepare_controller(cmd[1] & 1);
235
236 // send command to controller
237 u8 i;
238 for (i=0; i<cmdlen; i++)
239 outb(cmd[i], PORT_FD_DATA);
240
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500241 int ret = wait_floppy_irq();
242 if (ret) {
243 floppy_reset_controller();
244 return -1;
Kevin O'Connor06ad44e2008-04-05 19:30:02 -0400245 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500246
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500247 return 0;
248}
249
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400250static int
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400251floppy_cmd(struct disk_op_s *op, u16 count, u8 *cmd, u8 cmdlen)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500252{
253 // es:bx = pointer to where to place information from diskette
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400254 u32 addr = (u32)op->buf_fl;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500255
256 // check for 64K boundary overrun
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400257 u16 end = count - 1;
258 u32 last_addr = addr + end;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400259 if ((addr >> 16) != (last_addr >> 16))
260 return DISK_RET_EBOUNDARY;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500261
262 u8 mode_register = 0x4a; // single mode, increment, autoinit disable,
263 if (cmd[0] == 0xe6)
264 // read
265 mode_register = 0x46;
266
Kevin O'Connor44c631d2008-03-02 11:24:36 -0500267 //DEBUGF("floppy dma c2\n");
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500268 outb(0x06, PORT_DMA1_MASK_REG);
269 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connore10d3452008-07-09 22:45:12 -0400270 outb(addr, PORT_DMA_ADDR_2);
271 outb(addr>>8, PORT_DMA_ADDR_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500272 outb(0x00, PORT_DMA1_CLEAR_FF_REG); // clear flip-flop
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400273 outb(end, PORT_DMA_CNT_2);
274 outb(end>>8, PORT_DMA_CNT_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500275
276 // port 0b: DMA-1 Mode Register
277 // transfer type=write, channel 2
278 outb(mode_register, PORT_DMA1_MODE_REG);
279
280 // port 81: DMA-1 Page Register, channel 2
Kevin O'Connore10d3452008-07-09 22:45:12 -0400281 outb(addr>>16, PORT_DMA_PAGE_2);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500282
283 outb(0x02, PORT_DMA1_MASK_REG); // unmask channel 2
284
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400285 int ret = floppy_pio(cmd, cmdlen);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400286 if (ret)
287 return DISK_RET_ETIMEOUT;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500288
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500289 // check port 3f4 for accessibility to status bytes
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400290 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
291 return DISK_RET_ECONTROLLER;
Kevin O'Connorc65a3802008-03-02 13:58:23 -0500292
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500293 // read 7 return status bytes from controller
294 u8 i;
295 for (i=0; i<7; i++) {
296 u8 v = inb(PORT_FD_DATA);
297 cmd[i] = v;
298 SET_BDA(floppy_return_status[i], v);
299 }
300
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400301 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500302}
303
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500304
305/****************************************************************
306 * Floppy media sense
307 ****************************************************************/
308
309static inline void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400310set_diskette_current_cyl(u8 floppyid, u8 cyl)
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500311{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400312 SET_BDA(floppy_track[floppyid], cyl);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500313}
314
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500315static void
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400316floppy_drive_recal(u8 floppyid)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500317{
318 // send Recalibrate command (2 bytes) to controller
319 u8 data[12];
320 data[0] = 0x07; // 07: Recalibrate
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400321 data[1] = floppyid; // 0=drive0, 1=drive1
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500322 floppy_pio(data, 2);
323
Kevin O'Connore51316d2012-06-10 09:09:22 -0400324 u8 frs = GET_BDA(floppy_recalibration_status);
325 SET_BDA(floppy_recalibration_status, frs | (1<<floppyid));
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400326 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500327}
328
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500329static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400330floppy_media_sense(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500331{
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500332 // for now cheat and get drive type from CMOS,
333 // assume media is same as drive type
334
335 // ** config_data **
336 // Bitfields for diskette media control:
337 // Bit(s) Description (Table M0028)
338 // 7-6 last data rate set by controller
339 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
340 // 5-4 last diskette drive step rate selected
341 // 00=0Ch, 01=0Dh, 10=0Eh, 11=0Ah
342 // 3-2 {data rate at start of operation}
343 // 1-0 reserved
344
345 // ** media_state **
346 // Bitfields for diskette drive media state:
347 // Bit(s) Description (Table M0030)
348 // 7-6 data rate
349 // 00=500kbps, 01=300kbps, 10=250kbps, 11=1Mbps
350 // 5 double stepping required (e.g. 360kB in 1.2MB)
351 // 4 media type established
352 // 3 drive capable of supporting 4MB media
353 // 2-0 on exit from BIOS, contains
354 // 000 trying 360kB in 360kB
355 // 001 trying 360kB in 1.2MB
356 // 010 trying 1.2MB in 1.2MB
357 // 011 360kB in 360kB established
358 // 100 360kB in 1.2MB established
359 // 101 1.2MB in 1.2MB established
360 // 110 reserved
361 // 111 all other formats/drives
362
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400363 u8 ftype = GET_GLOBAL(drive_g->floppy_type);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400364 SET_BDA(floppy_last_data_rate, GET_GLOBAL(FloppyInfo[ftype].config_data));
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400365 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400366 SET_BDA(floppy_media_state[floppyid]
367 , GET_GLOBAL(FloppyInfo[ftype].media_state));
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400368 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500369}
370
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400371static int
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400372check_recal_drive(struct drive_s *drive_g)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500373{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400374 u8 floppyid = GET_GLOBAL(drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400375 if ((GET_BDA(floppy_recalibration_status) & (1<<floppyid))
376 && (GET_BDA(floppy_media_state[floppyid]) & FMS_MEDIA_DRIVE_ESTABLISHED))
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500377 // Media is known.
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400378 return DISK_RET_SUCCESS;
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500379
380 // Recalibrate drive.
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400381 floppy_drive_recal(floppyid);
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500382
383 // Sense media.
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400384 return floppy_media_sense(drive_g);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500385}
386
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500387
388/****************************************************************
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400389 * Floppy handlers
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500390 ****************************************************************/
391
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500392static void
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400393lba2chs(struct disk_op_s *op, u8 *track, u8 *sector, u8 *head)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500394{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400395 u32 lba = op->lba;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400396
397 u32 tmp = lba + 1;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400398 u16 nlspt = GET_GLOBAL(op->drive_g->lchs.spt);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400399 *sector = tmp % nlspt;
400
401 tmp /= nlspt;
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400402 u16 nlh = GET_GLOBAL(op->drive_g->lchs.heads);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400403 *head = tmp % nlh;
404
405 tmp /= nlh;
406 *track = tmp;
407}
408
409// diskette controller reset
410static int
411floppy_reset(struct disk_op_s *op)
412{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400413 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400414 set_diskette_current_cyl(floppyid, 0); // current cylinder
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400415 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500416}
417
418// Read Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400419static int
420floppy_read(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500421{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400422 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400423 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400424 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500425
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400426 u8 track, sector, head;
427 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500428
429 // send read-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400430 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500431 u8 data[12];
432 data[0] = 0xe6; // e6: read normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400433 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500434 data[2] = track;
435 data[3] = head;
436 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400437 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400438 data[6] = sector + op->count - 1; // last sector to read on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400439 data[7] = FLOPPY_GAPLEN;
440 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500441
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400442 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400443 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400444 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500445
446 if (data[0] & 0xc0) {
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400447 res = DISK_RET_ECONTROLLER;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400448 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500449 }
450
451 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400452 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400453 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400454fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400455 op->count = 0; // no sectors read
456 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500457}
458
459// Write Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400460static int
461floppy_write(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500462{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400463 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400464 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400465 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500466
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400467 u8 track, sector, head;
468 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500469
470 // send write-normal-data command (9 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400471 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500472 u8 data[12];
473 data[0] = 0xc5; // c5: write normal data
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400474 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500475 data[2] = track;
476 data[3] = head;
477 data[4] = sector;
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400478 data[5] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400479 data[6] = sector + op->count - 1; // last sector to write on track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400480 data[7] = FLOPPY_GAPLEN;
481 data[8] = FLOPPY_DATALEN;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500482
Kevin O'Connor36c93a52009-09-12 19:35:04 -0400483 res = floppy_cmd(op, op->count * DISK_SECTOR_SIZE, data, 9);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400484 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400485 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500486
487 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500488 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400489 res = DISK_RET_EWRITEPROTECT;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500490 else
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400491 res = DISK_RET_ECONTROLLER;
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500492 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500493 }
494
495 // ??? should track be new val from return_status[3] ?
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400496 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400497 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400498fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400499 op->count = 0; // no sectors read
500 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500501}
502
503// Verify Diskette Sectors
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400504static int
505floppy_verify(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500506{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400507 int res = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400508 if (res)
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400509 goto fail;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500510
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400511 u8 track, sector, head;
512 lba2chs(op, &track, &sector, &head);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500513
514 // ??? should track be new val from return_status[3] ?
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400515 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400516 set_diskette_current_cyl(floppyid, track);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400517 return DISK_RET_SUCCESS;
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400518fail:
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400519 op->count = 0; // no sectors read
520 return res;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500521}
522
523// format diskette track
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400524static int
525floppy_format(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500526{
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400527 int ret = check_recal_drive(op->drive_g);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400528 if (ret)
529 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500530
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400531 u8 head = op->lba;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500532
533 // send format-track command (6 bytes) to controller
Kevin O'Connor77d227b2009-10-22 21:48:39 -0400534 u8 floppyid = GET_GLOBAL(op->drive_g->cntl_id);
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500535 u8 data[12];
536 data[0] = 0x4d; // 4d: format track
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400537 data[1] = (head << 2) | floppyid; // HD DR1 DR2
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400538 data[2] = FLOPPY_SIZE_CODE;
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400539 data[3] = op->count; // number of sectors per track
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400540 data[4] = FLOPPY_FORMAT_GAPLEN;
541 data[5] = FLOPPY_FILLBYTE;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500542
Kevin O'Connor7b184d82009-08-23 11:56:55 -0400543 ret = floppy_cmd(op, op->count * 4, data, 6);
Kevin O'Connora68aeaf2008-07-07 21:37:10 -0400544 if (ret)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400545 return ret;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500546
547 if (data[0] & 0xc0) {
Kevin O'Connor7661f2f2009-02-07 01:21:00 -0500548 if (data[1] & 0x02)
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400549 return DISK_RET_EWRITEPROTECT;
550 return DISK_RET_ECONTROLLER;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500551 }
552
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400553 set_diskette_current_cyl(floppyid, 0);
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400554 return DISK_RET_SUCCESS;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500555}
556
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400557int
558process_floppy_op(struct disk_op_s *op)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500559{
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400560 if (!CONFIG_FLOPPY)
561 return 0;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500562
Kevin O'Connoraf5aabb2009-08-16 18:48:38 -0400563 switch (op->command) {
564 case CMD_RESET:
565 return floppy_reset(op);
566 case CMD_READ:
567 return floppy_read(op);
568 case CMD_WRITE:
569 return floppy_write(op);
570 case CMD_VERIFY:
571 return floppy_verify(op);
572 case CMD_FORMAT:
573 return floppy_format(op);
574 default:
575 op->count = 0;
576 return DISK_RET_EPARAM;
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500577 }
578}
579
Kevin O'Connora3b612e2009-02-07 11:25:29 -0500580
581/****************************************************************
582 * HW irqs
583 ****************************************************************/
584
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500585// INT 0Eh Diskette Hardware ISR Entry Point
Kevin O'Connor19786762008-03-05 21:09:59 -0500586void VISIBLE16
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400587handle_0e(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500588{
Kevin O'Connor1297e5d2012-06-02 20:30:58 -0400589 debug_isr(DEBUG_ISR_0e);
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400590 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400591 goto done;
592
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500593 if ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0) {
594 outb(0x08, PORT_FD_DATA); // sense interrupt status
595 while ((inb(PORT_FD_STATUS) & 0xc0) != 0xc0)
596 ;
597 do {
598 inb(PORT_FD_DATA);
599 } while ((inb(PORT_FD_STATUS) & 0xc0) == 0xc0);
600 }
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500601 // diskette interrupt has occurred
Kevin O'Connore51316d2012-06-10 09:09:22 -0400602 u8 frs = GET_BDA(floppy_recalibration_status);
603 SET_BDA(floppy_recalibration_status, frs | FRS_TIMEOUT);
Kevin O'Connor40967022008-07-21 22:23:05 -0400604
605done:
606 eoi_pic1();
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500607}
608
609// Called from int08 handler.
610void
Kevin O'Connor1ca05b02010-01-03 17:43:37 -0500611floppy_tick(void)
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500612{
Kevin O'Connor0a0e42e2009-08-16 12:09:44 -0400613 if (! CONFIG_FLOPPY)
Kevin O'Connor40967022008-07-21 22:23:05 -0400614 return;
615
Kevin O'Connorf076a3e2008-02-25 22:25:15 -0500616 // time to turn off drive(s)?
617 u8 fcount = GET_BDA(floppy_motor_counter);
618 if (fcount) {
619 fcount--;
620 SET_BDA(floppy_motor_counter, fcount);
621 if (fcount == 0)
622 // turn motor(s) off
623 outb(inb(PORT_FD_DOR) & 0xcf, PORT_FD_DOR);
624 }
625}