source: branches/minix3-book/drivers/at_wini/at_wini.c@ 20

Last change on this file since 20 was 4, checked in by Mattia Monga, 14 years ago

Importazione sorgenti libro

File size: 39.8 KB
Line 
1/* This file contains the device dependent part of a driver for the IBM-AT
2 * winchester controller. Written by Adri Koppes.
3 *
4 * The file contains one entry point:
5 *
6 * at_winchester_task: main entry when system is brought up
7 *
8 * Changes:
9 * Aug 19, 2005 ata pci support, supports SATA (Ben Gras)
10 * Nov 18, 2004 moved AT disk driver to user-space (Jorrit N. Herder)
11 * Aug 20, 2004 watchdogs replaced by sync alarms (Jorrit N. Herder)
12 * Mar 23, 2000 added ATAPI CDROM support (Michael Temari)
13 * May 14, 2000 d-d/i rewrite (Kees J. Bot)
14 * Apr 13, 1992 device dependent/independent split (Kees J. Bot)
15 */
16
17#include "at_wini.h"
18#include "../libpci/pci.h"
19
20#include <minix/sysutil.h>
21#include <minix/keymap.h>
22#include <sys/ioc_disk.h>
23
24#define ATAPI_DEBUG 0 /* To debug ATAPI code. */
25
26/* I/O Ports used by winchester disk controllers. */
27
28/* Read and write registers */
29#define REG_CMD_BASE0 0x1F0 /* command base register of controller 0 */
30#define REG_CMD_BASE1 0x170 /* command base register of controller 1 */
31#define REG_CTL_BASE0 0x3F6 /* control base register of controller 0 */
32#define REG_CTL_BASE1 0x376 /* control base register of controller 1 */
33
34#define REG_DATA 0 /* data register (offset from the base reg.) */
35#define REG_PRECOMP 1 /* start of write precompensation */
36#define REG_COUNT 2 /* sectors to transfer */
37#define REG_SECTOR 3 /* sector number */
38#define REG_CYL_LO 4 /* low byte of cylinder number */
39#define REG_CYL_HI 5 /* high byte of cylinder number */
40#define REG_LDH 6 /* lba, drive and head */
41#define LDH_DEFAULT 0xA0 /* ECC enable, 512 bytes per sector */
42#define LDH_LBA 0x40 /* Use LBA addressing */
43#define ldh_init(drive) (LDH_DEFAULT | ((drive) << 4))
44
45/* Read only registers */
46#define REG_STATUS 7 /* status */
47#define STATUS_BSY 0x80 /* controller busy */
48#define STATUS_RDY 0x40 /* drive ready */
49#define STATUS_WF 0x20 /* write fault */
50#define STATUS_SC 0x10 /* seek complete (obsolete) */
51#define STATUS_DRQ 0x08 /* data transfer request */
52#define STATUS_CRD 0x04 /* corrected data */
53#define STATUS_IDX 0x02 /* index pulse */
54#define STATUS_ERR 0x01 /* error */
55#define STATUS_ADMBSY 0x100 /* administratively busy (software) */
56#define REG_ERROR 1 /* error code */
57#define ERROR_BB 0x80 /* bad block */
58#define ERROR_ECC 0x40 /* bad ecc bytes */
59#define ERROR_ID 0x10 /* id not found */
60#define ERROR_AC 0x04 /* aborted command */
61#define ERROR_TK 0x02 /* track zero error */
62#define ERROR_DM 0x01 /* no data address mark */
63
64/* Write only registers */
65#define REG_COMMAND 7 /* command */
66#define CMD_IDLE 0x00 /* for w_command: drive idle */
67#define CMD_RECALIBRATE 0x10 /* recalibrate drive */
68#define CMD_READ 0x20 /* read data */
69#define CMD_READ_EXT 0x24 /* read data (LBA48 addressed) */
70#define CMD_WRITE 0x30 /* write data */
71#define CMD_WRITE_EXT 0x34 /* write data (LBA48 addressed) */
72#define CMD_READVERIFY 0x40 /* read verify */
73#define CMD_FORMAT 0x50 /* format track */
74#define CMD_SEEK 0x70 /* seek cylinder */
75#define CMD_DIAG 0x90 /* execute device diagnostics */
76#define CMD_SPECIFY 0x91 /* specify parameters */
77#define ATA_IDENTIFY 0xEC /* identify drive */
78/* #define REG_CTL 0x206 */ /* control register */
79#define REG_CTL 0 /* control register */
80#define CTL_NORETRY 0x80 /* disable access retry */
81#define CTL_NOECC 0x40 /* disable ecc retry */
82#define CTL_EIGHTHEADS 0x08 /* more than eight heads */
83#define CTL_RESET 0x04 /* reset controller */
84#define CTL_INTDISABLE 0x02 /* disable interrupts */
85
86#define REG_STATUS 7 /* status */
87#define STATUS_BSY 0x80 /* controller busy */
88#define STATUS_DRDY 0x40 /* drive ready */
89#define STATUS_DMADF 0x20 /* dma ready/drive fault */
90#define STATUS_SRVCDSC 0x10 /* service or dsc */
91#define STATUS_DRQ 0x08 /* data transfer request */
92#define STATUS_CORR 0x04 /* correctable error occurred */
93#define STATUS_CHECK 0x01 /* check error */
94
95/* Interrupt request lines. */
96#define NO_IRQ 0 /* no IRQ set yet */
97
98#define ATAPI_PACKETSIZE 12
99#define SENSE_PACKETSIZE 18
100
101/* Common command block */
102struct command {
103 u8_t precomp; /* REG_PRECOMP, etc. */
104 u8_t count;
105 u8_t sector;
106 u8_t cyl_lo;
107 u8_t cyl_hi;
108 u8_t ldh;
109 u8_t command;
110};
111
112/* Error codes */
113#define ERR (-1) /* general error */
114#define ERR_BAD_SECTOR (-2) /* block marked bad detected */
115
116/* Some controllers don't interrupt, the clock will wake us up. */
117#define WAKEUP (32*HZ) /* drive may be out for 31 seconds max */
118
119/* Miscellaneous. */
120#define MAX_DRIVES 8
121#define COMPAT_DRIVES 4
122#define MAX_SECS 256 /* controller can transfer this many sectors */
123#define MAX_ERRORS 4 /* how often to try rd/wt before quitting */
124#define NR_MINORS (MAX_DRIVES * DEV_PER_DRIVE)
125#define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS)
126#define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE)
127#define DELAY_USECS 1000 /* controller timeout in microseconds */
128#define DELAY_TICKS 1 /* controller timeout in ticks */
129#define DEF_TIMEOUT_TICKS 300 /* controller timeout in ticks */
130#define RECOVERY_USECS 500000 /* controller recovery time in microseconds */
131#define RECOVERY_TICKS 30 /* controller recovery time in ticks */
132#define INITIALIZED 0x01 /* drive is initialized */
133#define DEAF 0x02 /* controller must be reset */
134#define SMART 0x04 /* drive supports ATA commands */
135#define ATAPI 0 /* don't bother with ATAPI; optimise out */
136#define IDENTIFIED 0x10 /* w_identify done successfully */
137#define IGNORING 0x20 /* w_identify failed once */
138
139/* Timeouts and max retries. */
140int timeout_ticks = DEF_TIMEOUT_TICKS, max_errors = MAX_ERRORS;
141int wakeup_ticks = WAKEUP;
142long w_standard_timeouts = 0, w_pci_debug = 0, w_instance = 0,
143 w_lba48 = 0, atapi_debug = 0;
144
145int w_testing = 0, w_silent = 0;
146
147int w_next_drive = 0;
148
149/* Variables. */
150
151/* wini is indexed by controller first, then drive (0-3).
152 * controller 0 is always the 'compatability' ide controller, at
153 * the fixed locations, whether present or not.
154 */
155PRIVATE struct wini { /* main drive struct, one entry per drive */
156 unsigned state; /* drive state: deaf, initialized, dead */
157 unsigned w_status; /* device status register */
158 unsigned base_cmd; /* command base register */
159 unsigned base_ctl; /* control base register */
160 unsigned irq; /* interrupt request line */
161 unsigned irq_mask; /* 1 << irq */
162 unsigned irq_need_ack; /* irq needs to be acknowledged */
163 int irq_hook_id; /* id of irq hook at the kernel */
164 int lba48; /* supports lba48 */
165 unsigned lcylinders; /* logical number of cylinders (BIOS) */
166 unsigned lheads; /* logical number of heads */
167 unsigned lsectors; /* logical number of sectors per track */
168 unsigned pcylinders; /* physical number of cylinders (translated) */
169 unsigned pheads; /* physical number of heads */
170 unsigned psectors; /* physical number of sectors per track */
171 unsigned ldhpref; /* top four bytes of the LDH (head) register */
172 unsigned precomp; /* write precompensation cylinder / 4 */
173 unsigned max_count; /* max request for this drive */
174 unsigned open_ct; /* in-use count */
175 struct device part[DEV_PER_DRIVE]; /* disks and partitions */
176 struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
177} wini[MAX_DRIVES], *w_wn;
178
179PRIVATE int w_device = -1;
180PRIVATE int w_controller = -1;
181PRIVATE int w_major = -1;
182PRIVATE char w_id_string[40];
183
184PRIVATE int win_tasknr; /* my task number */
185PRIVATE int w_command; /* current command in execution */
186PRIVATE u8_t w_byteval; /* used for SYS_IRQCTL */
187PRIVATE int w_drive; /* selected drive */
188PRIVATE int w_controller; /* selected controller */
189PRIVATE struct device *w_dv; /* device's base and size */
190
191FORWARD _PROTOTYPE( void init_params, (void) );
192FORWARD _PROTOTYPE( void init_drive, (struct wini *, int, int, int, int, int, int));
193FORWARD _PROTOTYPE( void init_params_pci, (int) );
194FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) );
195FORWARD _PROTOTYPE( struct device *w_prepare, (int dev) );
196FORWARD _PROTOTYPE( int w_identify, (void) );
197FORWARD _PROTOTYPE( char *w_name, (void) );
198FORWARD _PROTOTYPE( int w_specify, (void) );
199FORWARD _PROTOTYPE( int w_io_test, (void) );
200FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position,
201 iovec_t *iov, unsigned nr_req) );
202FORWARD _PROTOTYPE( int com_out, (struct command *cmd) );
203FORWARD _PROTOTYPE( void w_need_reset, (void) );
204FORWARD _PROTOTYPE( void ack_irqs, (unsigned int) );
205FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) );
206FORWARD _PROTOTYPE( int w_other, (struct driver *dp, message *m_ptr) );
207FORWARD _PROTOTYPE( int w_hw_int, (struct driver *dp, message *m_ptr) );
208FORWARD _PROTOTYPE( int com_simple, (struct command *cmd) );
209FORWARD _PROTOTYPE( void w_timeout, (void) );
210FORWARD _PROTOTYPE( int w_reset, (void) );
211FORWARD _PROTOTYPE( void w_intr_wait, (void) );
212FORWARD _PROTOTYPE( int at_intr_wait, (void) );
213FORWARD _PROTOTYPE( int w_waitfor, (int mask, int value) );
214FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry) );
215
216/* Entry points to this driver. */
217PRIVATE struct driver w_dtab = {
218 w_name, /* current device's name */
219 w_do_open, /* open or mount request, initialize device */
220 w_do_close, /* release device */
221 do_diocntl, /* get or set a partition's geometry */
222 w_prepare, /* prepare for I/O on a given minor device */
223 w_transfer, /* do the I/O */
224 nop_cleanup, /* nothing to clean up */
225 w_geometry, /* tell the geometry of the disk */
226 nop_signal, /* no cleanup needed on shutdown */
227 nop_alarm, /* ignore leftover alarms */
228 nop_cancel, /* ignore CANCELs */
229 nop_select, /* ignore selects */
230 w_other, /* catch-all for unrecognized commands and ioctls */
231 w_hw_int /* leftover hardware interrupts */
232};
233
234/*===========================================================================*
235 * at_winchester_task *
236 *===========================================================================*/
237PUBLIC int main()
238{
239/* Set special disk parameters then call the generic main loop. */
240 init_params();
241 driver_task(&w_dtab);
242 return(OK);
243}
244
245/*===========================================================================*
246 * init_params *
247 *===========================================================================*/
248PRIVATE void init_params()
249{
250/* This routine is called at startup to initialize the drive parameters. */
251
252 u16_t parv[2];
253 unsigned int vector, size;
254 int drive, nr_drives;
255 struct wini *wn;
256 u8_t params[16];
257 int s;
258
259 /* Boot variables. */
260 env_parse("ata_std_timeout", "d", 0, &w_standard_timeouts, 0, 1);
261 env_parse("ata_pci_debug", "d", 0, &w_pci_debug, 0, 1);
262 env_parse("ata_instance", "d", 0, &w_instance, 0, 8);
263 env_parse("ata_lba48", "d", 0, &w_lba48, 0, 1);
264 env_parse("atapi_debug", "d", 0, &atapi_debug, 0, 1);
265
266 if (w_instance == 0) {
267 /* Get the number of drives from the BIOS data area */
268 if ((s=sys_vircopy(SELF, BIOS_SEG, NR_HD_DRIVES_ADDR,
269 SELF, D, (vir_bytes) params, NR_HD_DRIVES_SIZE)) != OK)
270 panic(w_name(), "Couldn't read BIOS", s);
271 if ((nr_drives = params[0]) > 2) nr_drives = 2;
272
273 for (drive = 0, wn = wini; drive < COMPAT_DRIVES; drive++, wn++) {
274 if (drive < nr_drives) {
275 /* Copy the BIOS parameter vector */
276 vector = (drive == 0) ? BIOS_HD0_PARAMS_ADDR:BIOS_HD1_PARAMS_ADDR;
277 size = (drive == 0) ? BIOS_HD0_PARAMS_SIZE:BIOS_HD1_PARAMS_SIZE;
278 if ((s=sys_vircopy(SELF, BIOS_SEG, vector,
279 SELF, D, (vir_bytes) parv, size)) != OK)
280 panic(w_name(), "Couldn't read BIOS", s);
281
282 /* Calculate the address of the parameters and copy them */
283 if ((s=sys_vircopy(
284 SELF, BIOS_SEG, hclick_to_physb(parv[1]) + parv[0],
285 SELF, D, (phys_bytes) params, 16L))!=OK)
286 panic(w_name(),"Couldn't copy parameters", s);
287
288 /* Copy the parameters to the structures of the drive */
289 wn->lcylinders = bp_cylinders(params);
290 wn->lheads = bp_heads(params);
291 wn->lsectors = bp_sectors(params);
292 wn->precomp = bp_precomp(params) >> 2;
293 }
294
295 /* Fill in non-BIOS parameters. */
296 init_drive(wn,
297 drive < 2 ? REG_CMD_BASE0 : REG_CMD_BASE1,
298 drive < 2 ? REG_CTL_BASE0 : REG_CTL_BASE1,
299 NO_IRQ, 0, 0, drive);
300 w_next_drive++;
301 }
302 }
303
304 /* Look for controllers on the pci bus. Skip none the first instance,
305 * skip one and then 2 for every instance, for every next instance.
306 */
307 if (w_instance == 0)
308 init_params_pci(0);
309 else
310 init_params_pci(w_instance*2-1);
311
312}
313
314#define ATA_IF_NOTCOMPAT1 (1L << 0)
315#define ATA_IF_NOTCOMPAT2 (1L << 2)
316
317/*===========================================================================*
318 * init_drive *
319 *===========================================================================*/
320PRIVATE void init_drive(struct wini *w, int base_cmd, int base_ctl, int irq, int ack, int hook, int drive)
321{
322 w->state = 0;
323 w->w_status = 0;
324 w->base_cmd = base_cmd;
325 w->base_ctl = base_ctl;
326 w->irq = irq;
327 w->irq_mask = 1 << irq;
328 w->irq_need_ack = ack;
329 w->irq_hook_id = hook;
330 w->ldhpref = ldh_init(drive);
331 w->max_count = MAX_SECS << SECTOR_SHIFT;
332 w->lba48 = 0;
333}
334
335/*===========================================================================*
336 * init_params_pci *
337 *===========================================================================*/
338PRIVATE void init_params_pci(int skip)
339{
340 int r, devind, drive;
341 u16_t vid, did;
342 pci_init();
343 for(drive = w_next_drive; drive < MAX_DRIVES; drive++)
344 wini[drive].state = IGNORING;
345 for(r = pci_first_dev(&devind, &vid, &did);
346 r != 0 && w_next_drive < MAX_DRIVES; r = pci_next_dev(&devind, &vid, &did)) {
347 int interface, irq, irq_hook;
348 /* Base class must be 01h (mass storage), subclass must
349 * be 01h (ATA).
350 */
351 if (pci_attr_r8(devind, PCI_BCR) != 0x01 ||
352 pci_attr_r8(devind, PCI_SCR) != 0x01) {
353 continue;
354 }
355 /* Found a controller.
356 * Programming interface register tells us more.
357 */
358 interface = pci_attr_r8(devind, PCI_PIFR);
359 irq = pci_attr_r8(devind, PCI_ILR);
360
361 /* Any non-compat drives? */
362 if (interface & (ATA_IF_NOTCOMPAT1 | ATA_IF_NOTCOMPAT2)) {
363 int s;
364 irq_hook = irq;
365 if (skip > 0) {
366 if (w_pci_debug) printf("atapci skipping controller (remain %d)\n", skip);
367 skip--;
368 continue;
369 }
370 if ((s=sys_irqsetpolicy(irq, 0, &irq_hook)) != OK) {
371 printf("atapci: couldn't set IRQ policy %d\n", irq);
372 continue;
373 }
374 if ((s=sys_irqenable(&irq_hook)) != OK) {
375 printf("atapci: couldn't enable IRQ line %d\n", irq);
376 continue;
377 }
378 } else {
379 /* If not.. this is not the ata-pci controller we're
380 * looking for.
381 */
382 if (w_pci_debug) printf("atapci skipping compatability controller\n");
383 continue;
384 }
385
386 /* Primary channel not in compatability mode? */
387 if (interface & ATA_IF_NOTCOMPAT1) {
388 u32_t base_cmd, base_ctl;
389 base_cmd = pci_attr_r32(devind, PCI_BAR) & 0xffffffe0;
390 base_ctl = pci_attr_r32(devind, PCI_BAR_2) & 0xffffffe0;
391 if (base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
392 init_drive(&wini[w_next_drive],
393 base_cmd, base_ctl, irq, 1, irq_hook, 0);
394 init_drive(&wini[w_next_drive+1],
395 base_cmd, base_ctl, irq, 1, irq_hook, 1);
396 if (w_pci_debug)
397 printf("atapci %d: 0x%x 0x%x irq %d\n", devind, base_cmd, base_ctl, irq);
398 } else printf("atapci: ignored drives on primary channel, base %x\n", base_cmd);
399 }
400
401 /* Secondary channel not in compatability mode? */
402 if (interface & ATA_IF_NOTCOMPAT2) {
403 u32_t base_cmd, base_ctl;
404 base_cmd = pci_attr_r32(devind, PCI_BAR_3) & 0xffffffe0;
405 base_ctl = pci_attr_r32(devind, PCI_BAR_4) & 0xffffffe0;
406 if (base_cmd != REG_CMD_BASE0 && base_cmd != REG_CMD_BASE1) {
407 init_drive(&wini[w_next_drive+2],
408 base_cmd, base_ctl, irq, 1, irq_hook, 2);
409 init_drive(&wini[w_next_drive+3],
410 base_cmd, base_ctl, irq, 1, irq_hook, 3);
411 if (w_pci_debug)
412 printf("atapci %d: 0x%x 0x%x irq %d\n", devind, base_cmd, base_ctl, irq);
413 } else printf("atapci: ignored drives on secondary channel, base %x\n", base_cmd);
414 }
415 w_next_drive += 4;
416 }
417}
418
419/*===========================================================================*
420 * w_do_open *
421 *===========================================================================*/
422PRIVATE int w_do_open(dp, m_ptr)
423struct driver *dp;
424message *m_ptr;
425{
426/* Device open: Initialize the controller and read the partition table. */
427
428 struct wini *wn;
429
430 if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
431
432 wn = w_wn;
433
434 /* If we've probed it before and it failed, don't probe it again. */
435 if (wn->state & IGNORING) return ENXIO;
436
437 /* If we haven't identified it yet, or it's gone deaf,
438 * (re-)identify it.
439 */
440 if (!(wn->state & IDENTIFIED) || (wn->state & DEAF)) {
441 /* Try to identify the device. */
442 if (w_identify() != OK) {
443 if (wn->state & DEAF) w_reset();
444 wn->state = IGNORING;
445 return(ENXIO);
446 }
447 /* Do a test transaction unless it's a CD drive (then
448 * we can believe the controller, and a test may fail
449 * due to no CD being in the drive). If it fails, ignore
450 * the device forever.
451 */
452 if (!(wn->state & ATAPI) && w_io_test() != OK) {
453 wn->state |= IGNORING;
454 return(ENXIO);
455 }
456 }
457
458 /* If it's not an ATAPI device, then don't open with RO_BIT. */
459 if (!(wn->state & ATAPI) && (m_ptr->COUNT & RO_BIT)) return EACCES;
460
461 /* Partition the drive if it's being opened for the first time,
462 * or being opened after being closed.
463 */
464 if (wn->open_ct == 0) {
465
466 /* Partition the disk. */
467 memset(wn->part, sizeof(wn->part), 0);
468 memset(wn->subpart, sizeof(wn->subpart), 0);
469 partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY, wn->state & ATAPI);
470 }
471 wn->open_ct++;
472 return(OK);
473}
474
475/*===========================================================================*
476 * w_prepare *
477 *===========================================================================*/
478PRIVATE struct device *w_prepare(int device)
479{
480/* Prepare for I/O on a device. */
481struct wini *prev_wn;
482prev_wn = w_wn;
483 w_device = device;
484
485 if (device < NR_MINORS) { /* d0, d0p[0-3], d1, ... */
486 w_drive = device / DEV_PER_DRIVE; /* save drive number */
487 w_wn = &wini[w_drive];
488 w_dv = &w_wn->part[device % DEV_PER_DRIVE];
489 } else
490 if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/
491 w_drive = device / SUB_PER_DRIVE;
492 w_wn = &wini[w_drive];
493 w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
494 } else {
495 w_device = -1;
496 return(NIL_DEV);
497 }
498 return(w_dv);
499}
500
501/*===========================================================================*
502 * w_identify *
503 *===========================================================================*/
504PRIVATE int w_identify()
505{
506/* Find out if a device exists, if it is an old AT disk, or a newer ATA
507 * drive, a removable media device, etc.
508 */
509
510 struct wini *wn = w_wn;
511 struct command cmd;
512 int i, s;
513 unsigned long size;
514#define id_byte(n) (&tmp_buf[2 * (n)])
515#define id_word(n) (((u16_t) id_byte(n)[0] << 0) \
516 |((u16_t) id_byte(n)[1] << 8))
517#define id_longword(n) (((u32_t) id_byte(n)[0] << 0) \
518 |((u32_t) id_byte(n)[1] << 8) \
519 |((u32_t) id_byte(n)[2] << 16) \
520 |((u32_t) id_byte(n)[3] << 24))
521
522 /* Try to identify the device. */
523 cmd.ldh = wn->ldhpref;
524 cmd.command = ATA_IDENTIFY;
525 if (com_simple(&cmd) == OK) {
526 /* This is an ATA device. */
527 wn->state |= SMART;
528
529 /* Device information. */
530 if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
531 panic(w_name(),"Call to sys_insw() failed", s);
532
533 /* Why are the strings byte swapped??? */
534 for (i = 0; i < 40; i++) w_id_string[i] = id_byte(27)[i^1];
535
536 /* Preferred CHS translation mode. */
537 wn->pcylinders = id_word(1);
538 wn->pheads = id_word(3);
539 wn->psectors = id_word(6);
540 size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors;
541
542 if ((id_byte(49)[1] & 0x02) && size > 512L*1024*2) {
543 /* Drive is LBA capable and is big enough to trust it to
544 * not make a mess of it.
545 */
546 wn->ldhpref |= LDH_LBA;
547 size = id_longword(60);
548
549 if (w_lba48 && ((id_word(83)) & (1L << 10))) {
550 /* Drive is LBA48 capable (and LBA48 is turned on). */
551 if (id_word(102) || id_word(103)) {
552 /* If no. of sectors doesn't fit in 32 bits,
553 * trunacte to this. So it's LBA32 for now.
554 * This can still address devices up to 2TB
555 * though.
556 */
557 size = ULONG_MAX;
558 } else {
559 /* Actual number of sectors fits in 32 bits. */
560 size = id_longword(100);
561 }
562
563 wn->lba48 = 1;
564 }
565 }
566
567 if (wn->lcylinders == 0) {
568 /* No BIOS parameters? Then make some up. */
569 wn->lcylinders = wn->pcylinders;
570 wn->lheads = wn->pheads;
571 wn->lsectors = wn->psectors;
572 while (wn->lcylinders > 1024) {
573 wn->lheads *= 2;
574 wn->lcylinders /= 2;
575 }
576 }
577 } else {
578 /* Not an ATA device; no translations, no special features. Don't
579 * touch it unless the BIOS knows about it.
580 */
581 if (wn->lcylinders == 0) { return(ERR); } /* no BIOS parameters */
582 wn->pcylinders = wn->lcylinders;
583 wn->pheads = wn->lheads;
584 wn->psectors = wn->lsectors;
585 size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors;
586 }
587
588 /* Size of the whole drive */
589 wn->part[0].dv_size = mul64u(size, SECTOR_SIZE);
590
591 /* Reset/calibrate (where necessary) */
592 if (w_specify() != OK && w_specify() != OK) {
593 return(ERR);
594 }
595
596 if (wn->irq == NO_IRQ) {
597 /* Everything looks OK; register IRQ so we can stop polling. */
598 wn->irq = w_drive < 2 ? AT_WINI_0_IRQ : AT_WINI_1_IRQ;
599 wn->irq_hook_id = wn->irq; /* id to be returned if interrupt occurs */
600 if ((s=sys_irqsetpolicy(wn->irq, IRQ_REENABLE, &wn->irq_hook_id)) != OK)
601 panic(w_name(), "couldn't set IRQ policy", s);
602 if ((s=sys_irqenable(&wn->irq_hook_id)) != OK)
603 panic(w_name(), "couldn't enable IRQ line", s);
604 }
605 wn->state |= IDENTIFIED;
606 return(OK);
607}
608
609/*===========================================================================*
610 * w_name *
611 *===========================================================================*/
612PRIVATE char *w_name()
613{
614/* Return a name for the current device. */
615 static char name[] = "AT-D0";
616
617 name[4] = '0' + w_drive;
618 return name;
619}
620
621/*===========================================================================*
622 * w_io_test *
623 *===========================================================================*/
624PRIVATE int w_io_test(void)
625{
626 int r, save_dev;
627 int save_timeout, save_errors, save_wakeup;
628 iovec_t iov;
629 static char buf[SECTOR_SIZE];
630 iov.iov_addr = (vir_bytes) buf;
631 iov.iov_size = sizeof(buf);
632 save_dev = w_device;
633
634 /* Reduce timeout values for this test transaction. */
635 save_timeout = timeout_ticks;
636 save_errors = max_errors;
637 save_wakeup = wakeup_ticks;
638
639 if (!w_standard_timeouts) {
640 timeout_ticks = HZ * 4;
641 wakeup_ticks = HZ * 6;
642 max_errors = 3;
643 }
644
645 w_testing = 1;
646
647 /* Try I/O on the actual drive (not any (sub)partition). */
648 if (w_prepare(w_drive * DEV_PER_DRIVE) == NIL_DEV)
649 panic(w_name(), "Couldn't switch devices", NO_NUM);
650
651 r = w_transfer(SELF, DEV_GATHER, 0, &iov, 1);
652
653 /* Switch back. */
654 if (w_prepare(save_dev) == NIL_DEV)
655 panic(w_name(), "Couldn't switch back devices", NO_NUM);
656
657 /* Restore parameters. */
658 timeout_ticks = save_timeout;
659 max_errors = save_errors;
660 wakeup_ticks = save_wakeup;
661 w_testing = 0;
662
663 /* Test if everything worked. */
664 if (r != OK || iov.iov_size != 0) {
665 return ERR;
666 }
667
668 /* Everything worked. */
669
670 return OK;
671}
672
673/*===========================================================================*
674 * w_specify *
675 *===========================================================================*/
676PRIVATE int w_specify()
677{
678/* Routine to initialize the drive after boot or when a reset is needed. */
679
680 struct wini *wn = w_wn;
681 struct command cmd;
682
683 if ((wn->state & DEAF) && w_reset() != OK) {
684 return(ERR);
685 }
686
687 if (!(wn->state & ATAPI)) {
688 /* Specify parameters: precompensation, number of heads and sectors. */
689 cmd.precomp = wn->precomp;
690 cmd.count = wn->psectors;
691 cmd.ldh = w_wn->ldhpref | (wn->pheads - 1);
692 cmd.command = CMD_SPECIFY; /* Specify some parameters */
693
694 /* Output command block and see if controller accepts the parameters. */
695 if (com_simple(&cmd) != OK) return(ERR);
696
697 if (!(wn->state & SMART)) {
698 /* Calibrate an old disk. */
699 cmd.sector = 0;
700 cmd.cyl_lo = 0;
701 cmd.cyl_hi = 0;
702 cmd.ldh = w_wn->ldhpref;
703 cmd.command = CMD_RECALIBRATE;
704
705 if (com_simple(&cmd) != OK) return(ERR);
706 }
707 }
708 wn->state |= INITIALIZED;
709 return(OK);
710}
711
712/*===========================================================================*
713 * do_transfer *
714 *===========================================================================*/
715PRIVATE int do_transfer(struct wini *wn, unsigned int precomp, unsigned int count,
716 unsigned int sector, unsigned int opcode)
717{
718 struct command cmd;
719 unsigned secspcyl = wn->pheads * wn->psectors;
720
721 cmd.precomp = precomp;
722 cmd.count = count;
723 cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ;
724 /*
725 if (w_lba48 && wn->lba48) {
726 } else */
727 if (wn->ldhpref & LDH_LBA) {
728 cmd.sector = (sector >> 0) & 0xFF;
729 cmd.cyl_lo = (sector >> 8) & 0xFF;
730 cmd.cyl_hi = (sector >> 16) & 0xFF;
731 cmd.ldh = wn->ldhpref | ((sector >> 24) & 0xF);
732 } else {
733 int cylinder, head, sec;
734 cylinder = sector / secspcyl;
735 head = (sector % secspcyl) / wn->psectors;
736 sec = sector % wn->psectors;
737 cmd.sector = sec + 1;
738 cmd.cyl_lo = cylinder & BYTE;
739 cmd.cyl_hi = (cylinder >> 8) & BYTE;
740 cmd.ldh = wn->ldhpref | head;
741 }
742
743 return com_out(&cmd);
744}
745
746/*===========================================================================*
747 * w_transfer *
748 *===========================================================================*/
749PRIVATE int w_transfer(proc_nr, opcode, position, iov, nr_req)
750int proc_nr; /* process doing the request */
751int opcode; /* DEV_GATHER or DEV_SCATTER */
752off_t position; /* offset on device to read or write */
753iovec_t *iov; /* pointer to read or write request vector */
754unsigned nr_req; /* length of request vector */
755{
756 struct wini *wn = w_wn;
757 iovec_t *iop, *iov_end = iov + nr_req;
758 int r, s, errors;
759 unsigned long block;
760 unsigned long dv_size = cv64ul(w_dv->dv_size);
761 unsigned cylinder, head, sector, nbytes;
762
763 /* Check disk address. */
764 if ((position & SECTOR_MASK) != 0) return(EINVAL);
765
766 errors = 0;
767
768 while (nr_req > 0) {
769 /* How many bytes to transfer? */
770 nbytes = 0;
771 for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size;
772 if ((nbytes & SECTOR_MASK) != 0) return(EINVAL);
773
774 /* Which block on disk and how close to EOF? */
775 if (position >= dv_size) return(OK); /* At EOF */
776 if (position + nbytes > dv_size) nbytes = dv_size - position;
777 block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE);
778
779 if (nbytes >= wn->max_count) {
780 /* The drive can't do more then max_count at once. */
781 nbytes = wn->max_count;
782 }
783
784 /* First check to see if a reinitialization is needed. */
785 if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO);
786
787 /* Tell the controller to transfer nbytes bytes. */
788 r = do_transfer(wn, wn->precomp, ((nbytes >> SECTOR_SHIFT) & BYTE),
789 block, opcode);
790
791 while (r == OK && nbytes > 0) {
792 /* For each sector, wait for an interrupt and fetch the data
793 * (read), or supply data to the controller and wait for an
794 * interrupt (write).
795 */
796
797 if (opcode == DEV_GATHER) {
798 /* First an interrupt, then data. */
799 if ((r = at_intr_wait()) != OK) {
800 /* An error, send data to the bit bucket. */
801 if (w_wn->w_status & STATUS_DRQ) {
802 if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK)
803 panic(w_name(),"Call to sys_insw() failed", s);
804 }
805 break;
806 }
807 }
808
809 /* Wait for data transfer requested. */
810 if (!w_waitfor(STATUS_DRQ, STATUS_DRQ)) { r = ERR; break; }
811
812 /* Copy bytes to or from the device's buffer. */
813 if (opcode == DEV_GATHER) {
814 if ((s=sys_insw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
815 panic(w_name(),"Call to sys_insw() failed", s);
816 } else {
817 if ((s=sys_outsw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK)
818 panic(w_name(),"Call to sys_insw() failed", s);
819
820 /* Data sent, wait for an interrupt. */
821 if ((r = at_intr_wait()) != OK) break;
822 }
823
824 /* Book the bytes successfully transferred. */
825 nbytes -= SECTOR_SIZE;
826 position += SECTOR_SIZE;
827 iov->iov_addr += SECTOR_SIZE;
828 if ((iov->iov_size -= SECTOR_SIZE) == 0) { iov++; nr_req--; }
829 }
830
831 /* Any errors? */
832 if (r != OK) {
833 /* Don't retry if sector marked bad or too many errors. */
834 if (r == ERR_BAD_SECTOR || ++errors == max_errors) {
835 w_command = CMD_IDLE;
836 return(EIO);
837 }
838 }
839 }
840
841 w_command = CMD_IDLE;
842 return(OK);
843}
844
845/*===========================================================================*
846 * com_out *
847 *===========================================================================*/
848PRIVATE int com_out(cmd)
849struct command *cmd; /* Command block */
850{
851/* Output the command block to the winchester controller and return status */
852
853 struct wini *wn = w_wn;
854 unsigned base_cmd = wn->base_cmd;
855 unsigned base_ctl = wn->base_ctl;
856 pvb_pair_t outbyte[7]; /* vector for sys_voutb() */
857 int s; /* status for sys_(v)outb() */
858
859 if (w_wn->state & IGNORING) return ERR;
860
861 if (!w_waitfor(STATUS_BSY, 0)) {
862 printf("%s: controller not ready\n", w_name());
863 return(ERR);
864 }
865
866 /* Select drive. */
867 if ((s=sys_outb(base_cmd + REG_LDH, cmd->ldh)) != OK)
868 panic(w_name(),"Couldn't write register to select drive",s);
869
870 if (!w_waitfor(STATUS_BSY, 0)) {
871 printf("%s: com_out: drive not ready\n", w_name());
872 return(ERR);
873 }
874
875 /* Schedule a wakeup call, some controllers are flaky. This is done with
876 * a synchronous alarm. If a timeout occurs a SYN_ALARM message is sent
877 * from HARDWARE, so that w_intr_wait() can call w_timeout() in case the
878 * controller was not able to execute the command. Leftover timeouts are
879 * simply ignored by the main loop.
880 */
881 sys_setalarm(wakeup_ticks, 0);
882
883 wn->w_status = STATUS_ADMBSY;
884 w_command = cmd->command;
885 pv_set(outbyte[0], base_ctl + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0);
886 pv_set(outbyte[1], base_cmd + REG_PRECOMP, cmd->precomp);
887 pv_set(outbyte[2], base_cmd + REG_COUNT, cmd->count);
888 pv_set(outbyte[3], base_cmd + REG_SECTOR, cmd->sector);
889 pv_set(outbyte[4], base_cmd + REG_CYL_LO, cmd->cyl_lo);
890 pv_set(outbyte[5], base_cmd + REG_CYL_HI, cmd->cyl_hi);
891 pv_set(outbyte[6], base_cmd + REG_COMMAND, cmd->command);
892 if ((s=sys_voutb(outbyte,7)) != OK)
893 panic(w_name(),"Couldn't write registers with sys_voutb()",s);
894 return(OK);
895}
896
897/*===========================================================================*
898 * w_need_reset *
899 *===========================================================================*/
900PRIVATE void w_need_reset()
901{
902/* The controller needs to be reset. */
903 struct wini *wn;
904 int dr = 0;
905
906 for (wn = wini; wn < &wini[MAX_DRIVES]; wn++, dr++) {
907 if (wn->base_cmd == w_wn->base_cmd) {
908 wn->state |= DEAF;
909 wn->state &= ~INITIALIZED;
910 }
911 }
912}
913
914/*===========================================================================*
915 * w_do_close *
916 *===========================================================================*/
917PRIVATE int w_do_close(dp, m_ptr)
918struct driver *dp;
919message *m_ptr;
920{
921/* Device close: Release a device. */
922 if (w_prepare(m_ptr->DEVICE) == NIL_DEV)
923 return(ENXIO);
924 w_wn->open_ct--;
925 return(OK);
926}
927
928/*===========================================================================*
929 * com_simple *
930 *===========================================================================*/
931PRIVATE int com_simple(cmd)
932struct command *cmd; /* Command block */
933{
934/* A simple controller command, only one interrupt and no data-out phase. */
935 int r;
936
937 if (w_wn->state & IGNORING) return ERR;
938
939 if ((r = com_out(cmd)) == OK) r = at_intr_wait();
940 w_command = CMD_IDLE;
941 return(r);
942}
943
944/*===========================================================================*
945 * w_timeout *
946 *===========================================================================*/
947PRIVATE void w_timeout(void)
948{
949 struct wini *wn = w_wn;
950
951 switch (w_command) {
952 case CMD_IDLE:
953 break; /* fine */
954 case CMD_READ:
955 case CMD_WRITE:
956 /* Impossible, but not on PC's: The controller does not respond. */
957
958 /* Limiting multisector I/O seems to help. */
959 if (wn->max_count > 8 * SECTOR_SIZE) {
960 wn->max_count = 8 * SECTOR_SIZE;
961 } else {
962 wn->max_count = SECTOR_SIZE;
963 }
964 /*FALL THROUGH*/
965 default:
966 /* Some other command. */
967 if (w_testing) wn->state |= IGNORING; /* Kick out this drive. */
968 else if (!w_silent) printf("%s: timeout on command %02x\n", w_name(), w_command);
969 w_need_reset();
970 wn->w_status = 0;
971 }
972}
973
974/*===========================================================================*
975 * w_reset *
976 *===========================================================================*/
977PRIVATE int w_reset()
978{
979/* Issue a reset to the controller. This is done after any catastrophe,
980 * like the controller refusing to respond.
981 */
982 int s;
983 struct wini *wn = w_wn;
984
985 /* Don't bother if this drive is forgotten. */
986 if (w_wn->state & IGNORING) return ERR;
987
988 /* Wait for any internal drive recovery. */
989 tickdelay(RECOVERY_TICKS);
990
991 /* Strobe reset bit */
992 if ((s=sys_outb(wn->base_ctl + REG_CTL, CTL_RESET)) != OK)
993 panic(w_name(),"Couldn't strobe reset bit",s);
994 tickdelay(DELAY_TICKS);
995 if ((s=sys_outb(wn->base_ctl + REG_CTL, 0)) != OK)
996 panic(w_name(),"Couldn't strobe reset bit",s);
997 tickdelay(DELAY_TICKS);
998
999 /* Wait for controller ready */
1000 if (!w_waitfor(STATUS_BSY, 0)) {
1001 printf("%s: reset failed, drive busy\n", w_name());
1002 return(ERR);
1003 }
1004
1005 /* The error register should be checked now, but some drives mess it up. */
1006
1007 for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
1008 if (wn->base_cmd == w_wn->base_cmd) {
1009 wn->state &= ~DEAF;
1010 if (w_wn->irq_need_ack) {
1011 /* Make sure irq is actually enabled.. */
1012 sys_irqenable(&w_wn->irq_hook_id);
1013 }
1014 }
1015 }
1016
1017
1018 return(OK);
1019}
1020
1021/*===========================================================================*
1022 * w_intr_wait *
1023 *===========================================================================*/
1024PRIVATE void w_intr_wait()
1025{
1026/* Wait for a task completion interrupt. */
1027
1028 message m;
1029
1030 if (w_wn->irq != NO_IRQ) {
1031 /* Wait for an interrupt that sets w_status to "not busy". */
1032 while (w_wn->w_status & (STATUS_ADMBSY|STATUS_BSY)) {
1033 receive(ANY, &m); /* expect HARD_INT message */
1034 if (m.m_type == SYN_ALARM) { /* but check for timeout */
1035 w_timeout(); /* a.o. set w_status */
1036 } else if (m.m_type == HARD_INT) {
1037 sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status);
1038 ack_irqs(m.NOTIFY_ARG);
1039 } else {
1040 printf("AT_WINI got unexpected message %d from %d\n",
1041 m.m_type, m.m_source);
1042 }
1043 }
1044 } else {
1045 /* Interrupt not yet allocated; use polling. */
1046 (void) w_waitfor(STATUS_BSY, 0);
1047 }
1048}
1049
1050/*===========================================================================*
1051 * at_intr_wait *
1052 *===========================================================================*/
1053PRIVATE int at_intr_wait()
1054{
1055/* Wait for an interrupt, study the status bits and return error/success. */
1056 int r;
1057 int s,inbval; /* read value with sys_inb */
1058
1059 w_intr_wait();
1060 if ((w_wn->w_status & (STATUS_BSY | STATUS_WF | STATUS_ERR)) == 0) {
1061 r = OK;
1062 } else {
1063 if ((s=sys_inb(w_wn->base_cmd + REG_ERROR, &inbval)) != OK)
1064 panic(w_name(),"Couldn't read register",s);
1065 if ((w_wn->w_status & STATUS_ERR) && (inbval & ERROR_BB)) {
1066 r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */
1067 } else {
1068 r = ERR; /* any other error */
1069 }
1070 }
1071 w_wn->w_status |= STATUS_ADMBSY; /* assume still busy with I/O */
1072 return(r);
1073}
1074
1075/*===========================================================================*
1076 * w_waitfor *
1077 *===========================================================================*/
1078PRIVATE int w_waitfor(mask, value)
1079int mask; /* status mask */
1080int value; /* required status */
1081{
1082/* Wait until controller is in the required state. Return zero on timeout.
1083 * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need
1084 * ticks. Disabling the alarm is not needed, because a static flag is used
1085 * and a leftover timeout cannot do any harm.
1086 */
1087 clock_t t0, t1;
1088 int s;
1089 getuptime(&t0);
1090 do {
1091 if ((s=sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status)) != OK)
1092 panic(w_name(),"Couldn't read register",s);
1093 if ((w_wn->w_status & mask) == value) {
1094 return 1;
1095 }
1096 } while ((s=getuptime(&t1)) == OK && (t1-t0) < timeout_ticks );
1097 if (OK != s) printf("AT_WINI: warning, get_uptime failed: %d\n",s);
1098
1099 w_need_reset(); /* controller gone deaf */
1100 return(0);
1101}
1102
1103/*===========================================================================*
1104 * w_geometry *
1105 *===========================================================================*/
1106PRIVATE void w_geometry(entry)
1107struct partition *entry;
1108{
1109 struct wini *wn = w_wn;
1110
1111 if (wn->state & ATAPI) { /* Make up some numbers. */
1112 entry->cylinders = div64u(wn->part[0].dv_size, SECTOR_SIZE) / (64*32);
1113 entry->heads = 64;
1114 entry->sectors = 32;
1115 } else { /* Return logical geometry. */
1116 entry->cylinders = wn->lcylinders;
1117 entry->heads = wn->lheads;
1118 entry->sectors = wn->lsectors;
1119 }
1120}
1121
1122/*===========================================================================*
1123 * w_other *
1124 *===========================================================================*/
1125PRIVATE int w_other(dr, m)
1126struct driver *dr;
1127message *m;
1128{
1129 int r, timeout, prev;
1130
1131 if (m->m_type != DEV_IOCTL ) {
1132 return EINVAL;
1133 }
1134
1135 if (m->REQUEST == DIOCTIMEOUT) {
1136 if ((r=sys_datacopy(m->PROC_NR, (vir_bytes)m->ADDRESS,
1137 SELF, (vir_bytes)&timeout, sizeof(timeout))) != OK)
1138 return r;
1139
1140 if (timeout == 0) {
1141 /* Restore defaults. */
1142 timeout_ticks = DEF_TIMEOUT_TICKS;
1143 max_errors = MAX_ERRORS;
1144 wakeup_ticks = WAKEUP;
1145 w_silent = 0;
1146 } else if (timeout < 0) {
1147 return EINVAL;
1148 } else {
1149 prev = wakeup_ticks;
1150
1151 if (!w_standard_timeouts) {
1152 /* Set (lower) timeout, lower error
1153 * tolerance and set silent mode.
1154 */
1155 wakeup_ticks = timeout;
1156 max_errors = 3;
1157 w_silent = 1;
1158
1159 if (timeout_ticks > timeout)
1160 timeout_ticks = timeout;
1161 }
1162
1163 if ((r=sys_datacopy(SELF, (vir_bytes)&prev,
1164 m->PROC_NR, (vir_bytes)m->ADDRESS, sizeof(prev))) != OK)
1165 return r;
1166 }
1167
1168 return OK;
1169 } else if (m->REQUEST == DIOCOPENCT) {
1170 int count;
1171 if (w_prepare(m->DEVICE) == NIL_DEV) return ENXIO;
1172 count = w_wn->open_ct;
1173 if ((r=sys_datacopy(SELF, (vir_bytes)&count,
1174 m->PROC_NR, (vir_bytes)m->ADDRESS, sizeof(count))) != OK)
1175 return r;
1176 return OK;
1177 }
1178 return EINVAL;
1179}
1180
1181/*===========================================================================*
1182 * w_hw_int *
1183 *===========================================================================*/
1184PRIVATE int w_hw_int(dr, m)
1185struct driver *dr;
1186message *m;
1187{
1188 /* Leftover interrupt(s) received; ack it/them. */
1189 ack_irqs(m->NOTIFY_ARG);
1190
1191 return OK;
1192}
1193
1194
1195/*===========================================================================*
1196 * ack_irqs *
1197 *===========================================================================*/
1198PRIVATE void ack_irqs(unsigned int irqs)
1199{
1200 unsigned int drive;
1201 for (drive = 0; drive < MAX_DRIVES && irqs; drive++) {
1202 if (!(wini[drive].state & IGNORING) && wini[drive].irq_need_ack &&
1203 (wini[drive].irq_mask & irqs)) {
1204 if (sys_inb((wini[drive].base_cmd + REG_STATUS), &wini[drive].w_status) != OK)
1205 printf("couldn't ack irq on drive %d\n", drive);
1206 if (sys_irqenable(&wini[drive].irq_hook_id) != OK)
1207 printf("couldn't re-enable drive %d\n", drive);
1208 irqs &= ~wini[drive].irq_mask;
1209 }
1210 }
1211}
1212
1213
1214#define STSTR(a) if (status & STATUS_ ## a) { strcat(str, #a); strcat(str, " "); }
1215#define ERRSTR(a) if (e & ERROR_ ## a) { strcat(str, #a); strcat(str, " "); }
1216char *strstatus(int status)
1217{
1218 static char str[200];
1219 str[0] = '\0';
1220
1221 STSTR(BSY);
1222 STSTR(DRDY);
1223 STSTR(DMADF);
1224 STSTR(SRVCDSC);
1225 STSTR(DRQ);
1226 STSTR(CORR);
1227 STSTR(CHECK);
1228 return str;
1229}
1230
1231char *strerr(int e)
1232{
1233 static char str[200];
1234 str[0] = '\0';
1235
1236 ERRSTR(BB);
1237 ERRSTR(ECC);
1238 ERRSTR(ID);
1239 ERRSTR(AC);
1240 ERRSTR(TK);
1241 ERRSTR(DM);
1242
1243 return str;
1244}
Note: See TracBrowser for help on using the repository browser.