/* Some controllers don't interrupt, the clock will wake us up. */
#define WAKEUP (32*HZ) /* drive may be out for 31 seconds max */
-int wakeup_ticks = WAKEUP;
-
/* Miscellaneous. */
#define MAX_DRIVES 4 /* this driver supports 4 drives (d0 - d3) */
#if _WORD_SIZE > 2
#define ATAPI 0 /* don't bother with ATAPI; optimise out */
#endif
#define IDENTIFIED 0x10 /* w_identify done successfully */
+#define IGNORING 0x20 /* w_identify failed once */
+/* Timeouts and max retries. */
int timeout_ticks = DEF_TIMEOUT_TICKS, max_errors = MAX_ERRORS;
+int wakeup_ticks = WAKEUP;
+
+int w_testing = 0;
/* Variables. */
PRIVATE struct wini { /* main drive struct, one entry per drive */
struct device subpart[SUB_PER_DRIVE]; /* subpartitions */
} wini[MAX_DRIVES], *w_wn;
+PRIVATE int w_device = -1;
+PRIVATE char w_id_string[40];
+
PRIVATE int win_tasknr; /* my task number */
PRIVATE int w_command; /* current command in execution */
PRIVATE u8_t w_byteval; /* used for SYS_IRQCTL */
FORWARD _PROTOTYPE( int w_identify, (void) );
FORWARD _PROTOTYPE( char *w_name, (void) );
FORWARD _PROTOTYPE( int w_specify, (void) );
+FORWARD _PROTOTYPE( int w_io_test, (void) );
FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position,
iovec_t *iov, unsigned nr_req) );
FORWARD _PROTOTYPE( int com_out, (struct command *cmd) );
wn = w_wn;
- if (!(wn->state & (IDENTIFIED)) || (wn->state & DEAF)) {
+ /* If we've probed it before and it failed, don't probe it again. */
+ if (wn->state & IGNORING) return ENXIO;
+
+ /* If we haven't identified it yet, or it's gone deaf,
+ * (re-)identify it.
+ */
+ if (!(wn->state & IDENTIFIED) || (wn->state & DEAF)) {
/* Try to identify the device. */
if (w_identify() != OK) {
printf("%s: probe failed\n", w_name());
if (wn->state & DEAF) w_reset();
- wn->state = 0;
+ wn->state = IGNORING;
return(ENXIO);
}
-
+ /* Do a test transaction unless it's a CD drive (then
+ * we can believe the controller, and a test may fail
+ * due to no CD being in the drive). If it fails, ignore
+ * the device forever.
+ */
+ if(!(wn->state & ATAPI) && w_io_test() != OK) {
+ wn->state |= IGNORING;
+ return(ENXIO);
+ }
+
+ printf("%s: AT driver detected ", w_name());
+ if (wn->state & (SMART|ATAPI)) {
+ printf("%.40s\n", w_id_string);
+ } else {
+ printf("%ux%ux%u\n", wn->pcylinders, wn->pheads, wn->psectors);
+ }
}
+
+ /* Partition the drive if it's being opened for the first time,
+ * or being opened after being closed.
+ */
if (wn->open_ct == 0) {
#if ENABLE_ATAPI
if (wn->state & ATAPI) {
if ((r = atapi_open()) != OK) return(r);
}
#endif
+ /* If it's not an ATAPI device, then don't open read-only. */
+ if (!(wn->state & ATAPI) && (m_ptr->COUNT & RO_BIT)) return EACCES;
+
/* Partition the disk. */
partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY);
wn->open_ct++;
{
/* Prepare for I/O on a device. */
+ w_device = device;
if (device < NR_DEVICES) { /* d0, d0p[0-3], d1, ... */
w_drive = device / DEV_PER_DRIVE; /* save drive number */
w_wn = &wini[w_drive];
w_wn = &wini[w_drive];
w_dv = &w_wn->subpart[device % SUB_PER_DRIVE];
} else {
+ w_device = -1;
return(NIL_DEV);
}
return(w_dv);
struct wini *wn = w_wn;
struct command cmd;
- char id_string[40];
int i, r, s;
unsigned long size;
#define id_byte(n) (&tmp_buf[2 * (n)])
panic(w_name(),"Call to sys_insw() failed", s);
/* Why are the strings byte swapped??? */
- for (i = 0; i < 40; i++) id_string[i] = id_byte(27)[i^1];
+ for (i = 0; i < 40; i++) w_id_string[i] = id_byte(27)[i^1];
/* Preferred CHS translation mode. */
wn->pcylinders = id_word(1);
panic(w_name(),"Call to sys_insw() failed", s);
/* Why are the strings byte swapped??? */
- for (i = 0; i < 40; i++) id_string[i] = id_byte(27)[i^1];
+ for (i = 0; i < 40; i++) w_id_string[i] = id_byte(27)[i^1];
size = 0; /* Size set later. */
#endif
/* Size of the whole drive */
wn->part[0].dv_size = mul64u(size, SECTOR_SIZE);
+ /* Reset/calibrate (where necessary) */
if (w_specify() != OK && w_specify() != OK) {
return(ERR);
}
return name;
}
+/*===========================================================================*
+ * w_io_test *
+ *===========================================================================*/
+PRIVATE int w_io_test(void)
+{
+ int r, save_dev;
+ int save_timeout, save_errors, save_wakeup;
+ iovec_t iov;
+#ifdef CD_SECTOR_SIZE
+ static char buf[CD_SECTOR_SIZE];
+#else
+ static char buf[SECTOR_SIZE];
+#endif
+
+ iov.iov_addr = (vir_bytes) buf;
+ iov.iov_size = sizeof(buf);
+ save_dev = w_device;
+
+ /* Reduce timeout values for this test transaction. */
+ save_timeout = timeout_ticks;
+ save_errors = max_errors;
+ save_wakeup = wakeup_ticks;
+
+ timeout_ticks = HZ * 2;
+ wakeup_ticks = HZ * 5;
+ max_errors = 2;
+ w_testing = 1;
+
+ /* Try I/O on the actual drive (not any (sub)partition). */
+ if(w_prepare(w_drive * DEV_PER_DRIVE) == NIL_DEV)
+ panic(w_name(), "Couldn't switch devices", NO_NUM);
+
+ r = w_transfer(SELF, DEV_GATHER, 0, &iov, 1);
+
+ /* Switch back. */
+ if(w_prepare(save_dev) == NIL_DEV)
+ panic(w_name(), "Couldn't switch back devices", NO_NUM);
+
+ /* Restore parameters. */
+ timeout_ticks = save_timeout;
+ max_errors = save_errors;
+ wakeup_ticks = save_wakeup;
+ w_testing = 0;
+
+ /* Test if everything worked. */
+ if(r != OK || iov.iov_size != 0) {
+ return ERR;
+ }
+
+ /* Everything worked. */
+
+ return OK;
+}
/*===========================================================================*
* w_specify *
}
#endif
+
+
/* Check disk address. */
if ((position & SECTOR_MASK) != 0) return(EINVAL);
pvb_pair_t outbyte[7]; /* vector for sys_voutb() */
int s; /* status for sys_(v)outb() */
+ if(w_wn->state & IGNORING) return ERR;
+
if (!w_waitfor(STATUS_BSY, 0)) {
printf("%s: controller not ready\n", w_name());
return(ERR);
/* A simple controller command, only one interrupt and no data-out phase. */
int r;
+ if(w_wn->state & IGNORING) return ERR;
+
if ((r = com_out(cmd)) == OK) r = at_intr_wait();
w_command = CMD_IDLE;
return(r);
/*FALL THROUGH*/
default:
/* Some other command. */
- printf("%s: timeout on command %02x\n", w_name(), w_command);
+ if(w_testing) wn->state |= IGNORING; /* Kick out this drive. */
+ else printf("%s: timeout on command %02x\n", w_name(), w_command);
w_need_reset();
w_status = 0;
}
* like the controller refusing to respond.
*/
int s;
- struct wini *wn;
+ struct wini *wn = w_wn;
+
+ /* Don't bother if this drive is forgotten. */
+ if(w_wn->state & IGNORING) return ERR;
/* Wait for any internal drive recovery. */
tickdelay(RECOVERY_TICKS);
pvb_pair_t outbyte[6]; /* vector for sys_voutb() */
int s;
+ if(wn->state & IGNORING) return ERR;
+
/* Select Master/Slave drive */
if ((s=sys_outb(wn->base + REG_DRIVE, wn->ldhpref)) != OK)
panic(w_name(),"Couldn't select master/ slave drive",s);