📄 at_wini.c
字号:
cmd.sector = 0;
cmd.cyl_lo = 0;
cmd.cyl_hi = 0;
cmd.ldh = w_wn->ldhpref;
cmd.command = CMD_RECALIBRATE;
if (com_simple(&cmd) != OK) return(ERR);
}
wn->state |= INITIALIZED;
return(OK);
}
/*===========================================================================*
* w_schedule *
*===========================================================================*/
PRIVATE int w_schedule(proc_nr, iop)
int proc_nr; /* process doing the request */
struct iorequest_s *iop; /* pointer to read or write request */
{
/* Gather I/O requests on consecutive blocks so they may be read/written
* in one controller command. (There is enough time to compute the next
* consecutive request while an unwanted block passes by.)
*/
struct wini *wn = w_wn;
int r, opcode;
unsigned long pos;
unsigned nbytes, count;
unsigned long block;
phys_bytes user_phys;
/* This many bytes to read/write */
nbytes = iop->io_nbytes;
if ((nbytes & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);
/* From/to this position on the device */
pos = iop->io_position;
if ((pos & SECTOR_MASK) != 0) return(iop->io_nbytes = EINVAL);
/* To/from this user address */
user_phys = numap(proc_nr, (vir_bytes) iop->io_buf, nbytes);
if (user_phys == 0) return(iop->io_nbytes = EINVAL);
/* Read or write? */
opcode = iop->io_request & ~OPTIONAL_IO;
/* Which block on disk and how close to EOF? */
if (pos >= w_dv->dv_size) return(OK); /* At EOF */
if (pos + nbytes > w_dv->dv_size) nbytes = w_dv->dv_size - pos;
block = (w_dv->dv_base + pos) >> SECTOR_SHIFT;
if (w_count > 0 && block != w_nextblock) {
/* This new request can't be chained to the job being built */
if ((r = w_finish()) != OK) return(r);
}
/* The next consecutive block */
w_nextblock = block + (nbytes >> SECTOR_SHIFT);
/* While there are "unscheduled" bytes in the request: */
do {
count = nbytes;
if (w_count == wn->max_count) {
/* The drive can't do more then max_count at once */
if ((r = w_finish()) != OK) return(r);
}
if (w_count + count > wn->max_count)
count = wn->max_count - w_count;
if (w_count == 0) {
/* The first request in a row, initialize. */
w_opcode = opcode;
w_tp = wtrans;
}
/* Store I/O parameters */
w_tp->iop = iop;
w_tp->block = block;
w_tp->count = count;
w_tp->phys = user_phys;
/* Update counters */
w_tp++;
w_count += count;
block += count >> SECTOR_SHIFT;
user_phys += count;
nbytes -= count;
} while (nbytes > 0);
return(OK);
}
/*===========================================================================*
* w_finish *
*===========================================================================*/
PRIVATE int w_finish()
{
/* Carry out the I/O requests gathered in wtrans[]. */
struct trans *tp = wtrans;
struct wini *wn = w_wn;
int r, errors;
struct command cmd;
unsigned cylinder, head, sector, secspcyl;
if (w_count == 0) return(OK); /* Spurious finish. */
r = ERR; /* Trigger the first com_out */
errors = 0;
do {
if (r != OK) {
/* The controller must be (re)programmed. */
/* First check to see if a reinitialization is needed. */
if (!(wn->state & INITIALIZED) && w_specify() != OK)
return(tp->iop->io_nbytes = EIO);
/* Tell the controller to transfer w_count bytes */
cmd.precomp = wn->precomp;
cmd.count = (w_count >> SECTOR_SHIFT) & BYTE;
if (wn->ldhpref & LDH_LBA) {
cmd.sector = (tp->block >> 0) & 0xFF;
cmd.cyl_lo = (tp->block >> 8) & 0xFF;
cmd.cyl_hi = (tp->block >> 16) & 0xFF;
cmd.ldh = wn->ldhpref | ((tp->block >> 24) & 0xF);
} else {
secspcyl = wn->pheads * wn->psectors;
cylinder = tp->block / secspcyl;
head = (tp->block % secspcyl) / wn->psectors;
sector = tp->block % wn->psectors;
cmd.sector = sector + 1;
cmd.cyl_lo = cylinder & BYTE;
cmd.cyl_hi = (cylinder >> 8) & BYTE;
cmd.ldh = wn->ldhpref | head;
}
cmd.command = w_opcode == DEV_WRITE ? CMD_WRITE : CMD_READ;
if ((r = com_out(&cmd)) != OK) {
if (++errors == MAX_ERRORS) {
w_command = CMD_IDLE;
return(tp->iop->io_nbytes = EIO);
}
continue; /* Retry */
}
}
/* For each sector, wait for an interrupt and fetch the data (read),
* or supply data to the controller and wait for an interrupt (write).
*/
if (w_opcode == DEV_READ) {
if ((r = w_intr_wait()) == OK) {
/* Copy data from the device's buffer to user space. */
port_read(wn->base + REG_DATA, tp->phys, SECTOR_SIZE);
tp->phys += SECTOR_SIZE;
tp->iop->io_nbytes -= SECTOR_SIZE;
w_count -= SECTOR_SIZE;
if ((tp->count -= SECTOR_SIZE) == 0) tp++;
} else {
/* Any faulty data? */
if (w_status & STATUS_DRQ) {
port_read(wn->base + REG_DATA, tmp_phys,
SECTOR_SIZE);
}
}
} else {
/* Wait for data requested. */
if (!waitfor(STATUS_DRQ, STATUS_DRQ)) {
r = ERR;
} else {
/* Fill the buffer of the drive. */
port_write(wn->base + REG_DATA, tp->phys, SECTOR_SIZE);
r = w_intr_wait();
}
if (r == OK) {
/* Book the bytes successfully written. */
tp->phys += SECTOR_SIZE;
tp->iop->io_nbytes -= SECTOR_SIZE;
w_count -= SECTOR_SIZE;
if ((tp->count -= SECTOR_SIZE) == 0) tp++;
}
}
if (r != OK) {
/* Don't retry if sector marked bad or too many errors */
if (r == ERR_BAD_SECTOR || ++errors == MAX_ERRORS) {
w_command = CMD_IDLE;
return(tp->iop->io_nbytes = EIO);
}
/* Reset if halfway, but bail out if optional I/O. */
if (errors == MAX_ERRORS / 2) {
w_need_reset();
if (tp->iop->io_request & OPTIONAL_IO) {
w_command = CMD_IDLE;
return(tp->iop->io_nbytes = EIO);
}
}
continue; /* Retry */
}
errors = 0;
} while (w_count > 0);
w_command = CMD_IDLE;
return(OK);
}
/*============================================================================*
* com_out *
*============================================================================*/
PRIVATE int com_out(cmd)
struct command *cmd; /* Command block */
{
/* Output the command block to the winchester controller and return status */
struct wini *wn = w_wn;
unsigned base = wn->base;
if (!waitfor(STATUS_BSY, 0)) {
printf("%s: controller not ready\n", w_name());
return(ERR);
}
/* Select drive. */
out_byte(base + REG_LDH, cmd->ldh);
if (!waitfor(STATUS_BSY, 0)) {
printf("%s: drive not ready\n", w_name());
return(ERR);
}
/* Schedule a wakeup call, some controllers are flaky. */
clock_mess(WAKEUP, w_timeout);
out_byte(base + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0);
out_byte(base + REG_PRECOMP, cmd->precomp);
out_byte(base + REG_COUNT, cmd->count);
out_byte(base + REG_SECTOR, cmd->sector);
out_byte(base + REG_CYL_LO, cmd->cyl_lo);
out_byte(base + REG_CYL_HI, cmd->cyl_hi);
lock();
out_byte(base + REG_COMMAND, cmd->command);
w_command = cmd->command;
w_status = STATUS_BSY;
unlock();
return(OK);
}
/*===========================================================================*
* w_need_reset *
*===========================================================================*/
PRIVATE void w_need_reset()
{
/* The controller needs to be reset. */
struct wini *wn;
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
wn->state |= DEAF;
wn->state &= ~INITIALIZED;
}
}
/*============================================================================*
* w_do_close *
*============================================================================*/
PRIVATE int w_do_close(dp, m_ptr)
struct driver *dp;
message *m_ptr;
{
/* Device close: Release a device. */
if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO);
w_wn->open_ct--;
return(OK);
}
/*============================================================================*
* com_simple *
*============================================================================*/
PRIVATE int com_simple(cmd)
struct command *cmd; /* Command block */
{
/* A simple controller command, only one interrupt and no data-out phase. */
int r;
if ((r = com_out(cmd)) == OK) r = w_intr_wait();
w_command = CMD_IDLE;
return(r);
}
/*===========================================================================*
* w_timeout *
*===========================================================================*/
PRIVATE void w_timeout()
{
struct wini *wn = w_wn;
switch (w_command) {
case CMD_IDLE:
break; /* fine */
case CMD_READ:
case CMD_WRITE:
/* Impossible, but not on PC's: The controller does not respond. */
/* Limiting multisector I/O seems to help. */
if (wn->max_count > 8 * SECTOR_SIZE) {
wn->max_count = 8 * SECTOR_SIZE;
} else {
wn->max_count = SECTOR_SIZE;
}
/*FALL THROUGH*/
default:
/* Some other command. */
printf("%s: timeout on command %02x\n", w_name(), w_command);
w_need_reset();
w_status = 0;
interrupt(WINCHESTER);
}
}
/*===========================================================================*
* w_reset *
*===========================================================================*/
PRIVATE int w_reset()
{
/* Issue a reset to the controller. This is done after any catastrophe,
* like the controller refusing to respond.
*/
struct wini *wn;
int err;
/* Wait for any internal drive recovery. */
milli_delay(RECOVERYTIME);
/* Strobe reset bit */
out_byte(w_wn->base + REG_CTL, CTL_RESET);
milli_delay(1);
out_byte(w_wn->base + REG_CTL, 0);
milli_delay(1);
/* Wait for controller ready */
if (!w_waitfor(STATUS_BSY | STATUS_RDY, STATUS_RDY)) {
printf("%s: reset failed, drive busy\n", w_name());
return(ERR);
}
/* The error register should be checked now, but some drives mess it up. */
for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) {
if (wn->base == w_wn->base) wn->state &= ~DEAF;
}
return(OK);
}
/*============================================================================*
* w_intr_wait *
*============================================================================*/
PRIVATE int w_intr_wait()
{
/* Wait for a task completion interrupt and return results. */
message mess;
int r;
/* Wait for an interrupt that sets w_status to "not busy". */
while (w_status & STATUS_BSY) receive(HARDWARE, &mess);
/* Check status. */
lock();
if ((w_status & (STATUS_BSY | STATUS_RDY | STATUS_WF | STATUS_ERR))
== STATUS_RDY) {
r = OK;
w_status |= STATUS_BSY; /* assume still busy with I/O */
} else
if ((w_status & STATUS_ERR) && (in_byte(w_wn->base + REG_ERROR) & ERROR_BB)) {
r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */
} else {
r = ERR; /* any other error */
}
unlock();
return(r);
}
/*==========================================================================*
* w_waitfor *
*==========================================================================*/
PRIVATE int w_waitfor(mask, value)
int mask; /* status mask */
int value; /* required status */
{
/* Wait until controller is in the required state. Return zero on timeout. */
struct milli_state ms;
milli_start(&ms);
do {
if ((in_byte(w_wn->base + REG_STATUS) & mask) == value) return 1;
} while (milli_elapsed(&ms) < TIMEOUT);
w_need_reset(); /* Controller gone deaf. */
return(0);
}
/*==========================================================================*
* w_handler *
*==========================================================================*/
PRIVATE int w_handler(irq)
int irq;
{
/* Disk interrupt, send message to winchester task and reenable interrupts. */
w_status = in_byte(w_wn->base + REG_STATUS); /* acknowledge interrupt */
interrupt(WINCHESTER);
return 1;
}
/*============================================================================*
* w_geometry *
*============================================================================*/
PRIVATE void w_geometry(entry)
struct partition *entry;
{
entry->cylinders = w_wn->lcylinders;
entry->heads = w_wn->lheads;
entry->sectors = w_wn->lsectors;
}
#endif /* ENABLE_AT_WINI */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -