📄 at_wini.c
字号:
*===========================================================================*/PRIVATE int w_transfer(proc_nr, opcode, position, iov, nr_req)int proc_nr; /* process doing the request */int opcode; /* DEV_GATHER or DEV_SCATTER */off_t position; /* offset on device to read or write */iovec_t *iov; /* pointer to read or write request vector */unsigned nr_req; /* length of request vector */{ struct wini *wn = w_wn; iovec_t *iop, *iov_end = iov + nr_req; int r, s, errors; unsigned long block; unsigned long dv_size = cv64ul(w_dv->dv_size); unsigned cylinder, head, sector, nbytes;#if ENABLE_ATAPI if (w_wn->state & ATAPI) { return atapi_transfer(proc_nr, opcode, position, iov, nr_req); }#endif /* Check disk address. */ if ((position & SECTOR_MASK) != 0) return(EINVAL); errors = 0; while (nr_req > 0) { /* How many bytes to transfer? */ nbytes = 0; for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size; if ((nbytes & SECTOR_MASK) != 0) return(EINVAL); /* Which block on disk and how close to EOF? */ if (position >= dv_size) return(OK); /* At EOF */ if (position + nbytes > dv_size) nbytes = dv_size - position; block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE); if (nbytes >= wn->max_count) { /* The drive can't do more then max_count at once. */ nbytes = wn->max_count; } /* First check to see if a reinitialization is needed. */ if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO); /* Tell the controller to transfer nbytes bytes. */ r = do_transfer(wn, wn->precomp, ((nbytes >> SECTOR_SHIFT) & BYTE), block, opcode); while (r == OK && nbytes > 0) { /* 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 (opcode == DEV_GATHER) { /* First an interrupt, then data. */ if ((r = at_intr_wait()) != OK) { /* An error, send data to the bit bucket. */ if (w_wn->w_status & STATUS_DRQ) { if ((s=sys_insw(wn->base_cmd + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK) panic(w_name(),"Call to sys_insw() failed", s); } break; } } /* Wait for data transfer requested. */ if (!w_waitfor(STATUS_DRQ, STATUS_DRQ)) { r = ERR; break; } /* Copy bytes to or from the device's buffer. */ if (opcode == DEV_GATHER) { if ((s=sys_insw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK) panic(w_name(),"Call to sys_insw() failed", s); } else { if ((s=sys_outsw(wn->base_cmd + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK) panic(w_name(),"Call to sys_insw() failed", s); /* Data sent, wait for an interrupt. */ if ((r = at_intr_wait()) != OK) break; } /* Book the bytes successfully transferred. */ nbytes -= SECTOR_SIZE; position += SECTOR_SIZE; iov->iov_addr += SECTOR_SIZE; if ((iov->iov_size -= SECTOR_SIZE) == 0) { iov++; nr_req--; } } /* Any errors? */ 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(EIO); } } } 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_cmd = wn->base_cmd; unsigned base_ctl = wn->base_ctl; 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); } /* Select drive. */ if ((s=sys_outb(base_cmd + REG_LDH, cmd->ldh)) != OK) panic(w_name(),"Couldn't write register to select drive",s); if (!w_waitfor(STATUS_BSY, 0)) { printf("%s: com_out: drive not ready\n", w_name()); return(ERR); } /* Schedule a wakeup call, some controllers are flaky. This is done with * a synchronous alarm. If a timeout occurs a SYN_ALARM message is sent * from HARDWARE, so that w_intr_wait() can call w_timeout() in case the * controller was not able to execute the command. Leftover timeouts are * simply ignored by the main loop. */ sys_setalarm(wakeup_ticks, 0); wn->w_status = STATUS_ADMBSY; w_command = cmd->command; pv_set(outbyte[0], base_ctl + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0); pv_set(outbyte[1], base_cmd + REG_PRECOMP, cmd->precomp); pv_set(outbyte[2], base_cmd + REG_COUNT, cmd->count); pv_set(outbyte[3], base_cmd + REG_SECTOR, cmd->sector); pv_set(outbyte[4], base_cmd + REG_CYL_LO, cmd->cyl_lo); pv_set(outbyte[5], base_cmd + REG_CYL_HI, cmd->cyl_hi); pv_set(outbyte[6], base_cmd + REG_COMMAND, cmd->command); if ((s=sys_voutb(outbyte,7)) != OK) panic(w_name(),"Couldn't write registers with sys_voutb()",s); return(OK);}/*===========================================================================* * w_need_reset * *===========================================================================*/PRIVATE void w_need_reset(){/* The controller needs to be reset. */ struct wini *wn; int dr = 0; for (wn = wini; wn < &wini[MAX_DRIVES]; wn++, dr++) { if (wn->base_cmd == w_wn->base_cmd) { 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--;#if ENABLE_ATAPI if (w_wn->open_ct == 0 && (w_wn->state & ATAPI)) atapi_close();#endif 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 (w_wn->state & IGNORING) return ERR; if ((r = com_out(cmd)) == OK) r = at_intr_wait(); w_command = CMD_IDLE; return(r);}/*===========================================================================* * w_timeout * *===========================================================================*/PRIVATE void w_timeout(void){ 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. */ if (w_testing) wn->state |= IGNORING; /* Kick out this drive. */ else if (!w_silent) printf("%s: timeout on command %02x\n", w_name(), w_command); w_need_reset(); wn->w_status = 0; }}/*===========================================================================* * w_reset * *===========================================================================*/PRIVATE int w_reset(){/* Issue a reset to the controller. This is done after any catastrophe, * like the controller refusing to respond. */ int s; 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); /* Strobe reset bit */ if ((s=sys_outb(wn->base_ctl + REG_CTL, CTL_RESET)) != OK) panic(w_name(),"Couldn't strobe reset bit",s); tickdelay(DELAY_TICKS); if ((s=sys_outb(wn->base_ctl + REG_CTL, 0)) != OK) panic(w_name(),"Couldn't strobe reset bit",s); tickdelay(DELAY_TICKS); /* Wait for controller ready */ if (!w_waitfor(STATUS_BSY, 0)) { 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_cmd == w_wn->base_cmd) { wn->state &= ~DEAF; if (w_wn->irq_need_ack) { /* Make sure irq is actually enabled.. */ sys_irqenable(&w_wn->irq_hook_id); } } } return(OK);}/*===========================================================================* * w_intr_wait * *===========================================================================*/PRIVATE void w_intr_wait(){/* Wait for a task completion interrupt. */ message m; if (w_wn->irq != NO_IRQ) { /* Wait for an interrupt that sets w_status to "not busy". */ while (w_wn->w_status & (STATUS_ADMBSY|STATUS_BSY)) { receive(ANY, &m); /* expect HARD_INT message */ if (m.m_type == SYN_ALARM) { /* but check for timeout */ w_timeout(); /* a.o. set w_status */ } else if (m.m_type == HARD_INT) { sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status); ack_irqs(m.NOTIFY_ARG); } else if (m.m_type == DEV_PING) { notify(m.m_source); } else { printf("AT_WINI got unexpected message %d from %d\n", m.m_type, m.m_source); } } } else { /* Interrupt not yet allocated; use polling. */ (void) w_waitfor(STATUS_BSY, 0); }}/*===========================================================================* * at_intr_wait * *===========================================================================*/PRIVATE int at_intr_wait(){/* Wait for an interrupt, study the status bits and return error/success. */ int r; int s,inbval; /* read value with sys_inb */ w_intr_wait(); if ((w_wn->w_status & (STATUS_BSY | STATUS_WF | STATUS_ERR)) == 0) { r = OK; } else { if ((s=sys_inb(w_wn->base_cmd + REG_ERROR, &inbval)) != OK) panic(w_name(),"Couldn't read register",s); if ((w_wn->w_status & STATUS_ERR) && (inbval & ERROR_BB)) { r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */ } else { r = ERR; /* any other error */ } } w_wn->w_status |= STATUS_ADMBSY; /* assume still busy with I/O */ 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. * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need * ticks. Disabling the alarm is not needed, because a static flag is used * and a leftover timeout cannot do any harm. */ clock_t t0, t1; int s; getuptime(&t0); do { if ((s=sys_inb(w_wn->base_cmd + REG_STATUS, &w_wn->w_status)) != OK) panic(w_name(),"Couldn't read register",s); if ((w_wn->w_status & mask) == value) { return 1; } } while ((s=getuptime(&t1)) == OK && (t1-t0) < timeout_ticks ); if (OK != s) printf("AT_WINI: warning, get_uptime failed: %d\n",s); w_need_reset(); /* controller gone deaf */ return(0);}/*===========================================================================* * w_geometry * *===========================================================================*/PRIVATE void w_geometry(entry)struct partition *entry;{ struct wini *wn = w_wn; if (wn->state & ATAPI) { /* Make up some numbers. */ entry->cylinders = div64u(wn->part[0].dv_size, SECTOR_SIZE) / (64*32); entry->heads = 64; entry->sectors = 32; } else { /* Return logical geometry. */ entry->cylinders = wn->lcylinders; entry->heads = wn->lheads; entry->sectors = wn->lsectors; }}#if ENABLE_ATAPI/*===========================================================================* * atapi_open * *===========================================================================*/PRIVATE int atapi_open(){/* Should load and lock the device and obtain its size. For now just set the * size of the device to something big. What is really needed is a generic * SCSI layer that does all this stuff for ATAPI and SCSI devices (kjb). (XXX) */ w_wn->part[0].dv_size = mul64u(800L*1024, 1024); return(OK);}/*===========================================================================* * atapi_close * *===========================================================================*/PRIVATE void atapi_close(){/* Should unlock the device. For now do nothing. (XXX) */}void sense_request(void){ int r, i; static u8_t sense[100], packet[ATAPI_PACKETSIZE]; packet[0] = SCSI_SENSE; packet[1] = 0; packet[2] = 0; packet[3] = 0; packet[4] = SENSE_PACKETSIZE; packet[5] = 0; packet[7] = 0; packet[8] = 0; packet[9] = 0; packet[10] = 0; packet[11] = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -