📄 wdc.c
字号:
if ((cf_flags & ATA_CONFIG_DMA_MODES) == ATA_CONFIG_DMA_DISABLE) { drvp->drive_flags &= ~DRIVE_DMA; } else { drvp->DMA_mode = (cf_flags & ATA_CONFIG_DMA_MODES) >> ATA_CONFIG_DMA_OFF; drvp->drive_flags |= DRIVE_DMA | DRIVE_MODE; } } if (cf_flags & ATA_CONFIG_UDMA_SET) { if ((cf_flags & ATA_CONFIG_UDMA_MODES) == ATA_CONFIG_UDMA_DISABLE) { drvp->drive_flags &= ~DRIVE_UDMA; } else { drvp->UDMA_mode = (cf_flags & ATA_CONFIG_UDMA_MODES) >> ATA_CONFIG_UDMA_OFF; drvp->drive_flags |= DRIVE_UDMA | DRIVE_MODE; } }}voidwdc_output_bytes(drvp, bytes, buflen) struct ata_drive_datas *drvp; void *bytes; unsigned int buflen;{ struct channel_softc *chp = drvp->chnl_softc; unsigned int off = 0; unsigned int len = buflen, roundlen; if (drvp->drive_flags & DRIVE_CAP32) { roundlen = len & ~3; CHP_WRITE_RAW_MULTI_4(chp, (void *)((u_int8_t *)bytes + off), roundlen); off += roundlen; len -= roundlen; } if (len > 0) { roundlen = (len + 1) & ~0x1; CHP_WRITE_RAW_MULTI_2(chp, (void *)((u_int8_t *)bytes + off), roundlen); } return;}voidwdc_input_bytes(drvp, bytes, buflen) struct ata_drive_datas *drvp; void *bytes; unsigned int buflen;{ struct channel_softc *chp = drvp->chnl_softc; unsigned int off = 0; unsigned int len = buflen, roundlen; if (drvp->drive_flags & DRIVE_CAP32) { roundlen = len & ~3; CHP_READ_RAW_MULTI_4(chp, (void *)((u_int8_t *)bytes + off), roundlen); off += roundlen; len -= roundlen; } if (len > 0) { roundlen = (len + 1) & ~0x1; CHP_READ_RAW_MULTI_2(chp, (void *)((u_int8_t *)bytes + off), roundlen); } return;}voidwdc_print_caps(drvp) struct ata_drive_datas *drvp;{ printf("%s: can use ", drvp->drive_name); if (drvp->drive_flags & DRIVE_CAP32) { printf("32-bit"); } else printf("16-bit"); printf(", PIO mode %d", drvp->PIO_cap); if (drvp->drive_flags & DRIVE_DMA) { printf(", DMA mode %d", drvp->DMA_cap); } if (drvp->drive_flags & DRIVE_UDMA) { printf(", Ultra-DMA mode %d", drvp->UDMA_cap); } printf("\n");}/* * downgrade the transfer mode of a drive after an error. return 1 if * downgrade was possible, 0 otherwise. */intwdc_downgrade_mode(drvp) struct ata_drive_datas *drvp;{#ifndef PMON struct channel_softc *chp = drvp->chnl_softc; struct wdc_softc *wdc = chp->wdc; int cf_flags = drvp->cf_flags; /* if drive or controller don't know its mode, we can't do much */ if ((drvp->drive_flags & DRIVE_MODE) == 0 || (wdc->cap & WDC_CAPABILITY_MODE) == 0) return 0; /* current drive mode was set by a config flag, let it this way */ if ((cf_flags & ATA_CONFIG_PIO_SET) || (cf_flags & ATA_CONFIG_DMA_SET) || (cf_flags & ATA_CONFIG_UDMA_SET)) return 0; /* * If we were using Ultra-DMA mode > 2, downgrade to mode 2 first. * Maybe we didn't properly notice the cable type */ if ((drvp->drive_flags & DRIVE_UDMA) && drvp->UDMA_mode >= 2) { drvp->UDMA_mode = (drvp->UDMA_mode == 2) ? 1 : 2; printf("%s: transfer error, downgrading to Ultra-DMA mode %d\n", drvp->drive_name, drvp->UDMA_mode); } else if ((drvp->drive_flags & DRIVE_UDMA) && (drvp->drive_flags & DRIVE_DMAERR) == 0) { /* * If we were using ultra-DMA, don't downgrade to * multiword DMA if we noticed a CRC error. It has * been noticed that CRC errors in ultra-DMA lead to * silent data corruption in multiword DMA. Data * corruption is less likely to occur in PIO mode. */ drvp->drive_flags &= ~DRIVE_UDMA; drvp->drive_flags |= DRIVE_DMA; drvp->DMA_mode = drvp->DMA_cap; printf("%s: transfer error, downgrading to DMA mode %d\n", drvp->drive_name, drvp->DMA_mode); } else if (drvp->drive_flags & (DRIVE_DMA | DRIVE_UDMA)) { drvp->drive_flags &= ~(DRIVE_DMA | DRIVE_UDMA); drvp->PIO_mode = drvp->PIO_cap; printf("%s: transfer error, downgrading to PIO mode %d\n", drvp->drive_name, drvp->PIO_mode); } else /* already using PIO, can't downgrade */ return 0; wdc->set_modes(chp); /* reset the channel, which will schedule all drives for setup */ wdc_reset_channel(drvp); return 1;#else return 0;#endif}intwdc_exec_command(drvp, wdc_c) struct ata_drive_datas *drvp; struct wdc_command *wdc_c;{ struct channel_softc *chp = drvp->chnl_softc; struct wdc_xfer *xfer; int s, ret; WDCDEBUG_PRINT(("wdc_exec_command %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drvp->drive), DEBUG_FUNCS); /* set up an xfer and queue. Wait for completion */ xfer = wdc_get_xfer(wdc_c->flags & AT_WAIT ? WDC_CANSLEEP : WDC_NOSLEEP); if (xfer == NULL) { return WDC_TRY_AGAIN; } if (wdc_c->flags & AT_POLL) xfer->c_flags |= C_POLL; xfer->drive = drvp->drive; xfer->databuf = wdc_c->data; xfer->c_bcount = wdc_c->bcount; xfer->cmd = wdc_c; xfer->c_start = __wdccommand_start; xfer->c_intr = __wdccommand_intr; xfer->c_kill_xfer = __wdccommand_done; s = splbio(); wdc_exec_xfer(chp, xfer);#ifdef DIAGNOSTIC if ((wdc_c->flags & AT_POLL) != 0 && (wdc_c->flags & AT_DONE) == 0) panic("wdc_exec_command: polled command not done\n");#endif if (wdc_c->flags & AT_DONE) { ret = WDC_COMPLETE; } else { if (wdc_c->flags & AT_WAIT) { WDCDEBUG_PRINT(("wdc_exec_command sleeping"), DEBUG_FUNCS); while (!(wdc_c->flags & AT_DONE)) { int error; error = tsleep(wdc_c, PRIBIO, "wdccmd", 0); if (error) { printf ("tsleep error: %d\n", error); } } WDCDEBUG_PRINT(("wdc_exec_command waking"), DEBUG_FUNCS); ret = WDC_COMPLETE; } else { ret = WDC_QUEUED; } } splx(s); return ret;}void__wdccommand_start(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer;{ int drive = xfer->drive; struct wdc_command *wdc_c = xfer->cmd; WDCDEBUG_PRINT(("__wdccommand_start %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS); /* * Disable interrupts if we're polling */ if (xfer->c_flags & C_POLL) { wdc_disable_intr(chp); } /* * For resets, we don't really care to make sure that * the bus is free */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4)); if (wdc_c->r_command != ATAPI_SOFT_RESET) { if (wdcwait(chp, wdc_c->r_st_bmask, wdc_c->r_st_bmask, wdc_c->timeout) != 0) { wdc_c->flags |= AT_TIMEOU; __wdccommand_done(chp, xfer); return; } } else DELAY(10); wdccommand(chp, drive, wdc_c->r_command, wdc_c->r_cyl, wdc_c->r_head, wdc_c->r_sector, wdc_c->r_count, wdc_c->r_precomp);#ifndef PMON if ((wdc_c->flags & AT_POLL) == 0) { chp->ch_flags |= WDCF_IRQ_WAIT; /* wait for interrupt */ timeout(wdctimeout, chp, wdc_c->timeout / 1000 * hz); return; }#endif /* * Polled command. Wait for drive ready or drq. Done in intr(). * Wait for at last 400ns for status bit to be valid. */ delay(10); __wdccommand_intr(chp, xfer, 0);}int__wdccommand_intr(chp, xfer, irq) struct channel_softc *chp; struct wdc_xfer *xfer; int irq;{ struct ata_drive_datas *drvp = &chp->ch_drive[xfer->drive]; struct wdc_command *wdc_c = xfer->cmd; int bcount = wdc_c->bcount; char *data = wdc_c->data; WDCDEBUG_PRINT(("__wdccommand_intr %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_INTR); if (wdcwait(chp, wdc_c->r_st_pmask, wdc_c->r_st_pmask, (irq == 0) ? wdc_c->timeout : 0)) { if (irq && (xfer->c_flags & C_TIMEOU) == 0) return 0; /* IRQ was not for us */ wdc_c->flags |= AT_TIMEOU; __wdccommand_done(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_intr returned\n"), DEBUG_INTR); return 1; } if (wdc_c->flags & AT_READ) { wdc_input_bytes(drvp, data, bcount); } else if (wdc_c->flags & AT_WRITE) { wdc_output_bytes(drvp, data, bcount); } __wdccommand_done(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_intr returned\n"), DEBUG_INTR); return 1;}void__wdccommand_done(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer;{ struct wdc_command *wdc_c = xfer->cmd; WDCDEBUG_PRINT(("__wdccommand_done %s:%d:%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive), DEBUG_FUNCS); if (chp->ch_status & WDCS_DWF) wdc_c->flags |= AT_DF; if (chp->ch_status & WDCS_ERR) { wdc_c->flags |= AT_ERROR; wdc_c->r_error = chp->ch_error; } wdc_c->flags |= AT_DONE; if (wdc_c->flags & AT_READREG && (wdc_c->flags & (AT_ERROR | AT_DF)) == 0) { wdc_c->r_head = CHP_READ_REG(chp, wdr_sdh); wdc_c->r_cyl = CHP_READ_REG(chp, wdr_cyl_hi) << 8; wdc_c->r_cyl |= CHP_READ_REG(chp, wdr_cyl_lo); wdc_c->r_sector = CHP_READ_REG(chp, wdr_sector); wdc_c->r_count = CHP_READ_REG(chp, wdr_seccnt); wdc_c->r_error = CHP_READ_REG(chp, wdr_error); wdc_c->r_precomp = wdc_c->r_error; /* XXX CHP_READ_REG(chp, wdr_precomp); - precomp isn't a readable register */ } if (xfer->c_flags & C_POLL) { wdc_enable_intr(chp); } wdc_free_xfer(chp, xfer); WDCDEBUG_PRINT(("__wdccommand_done before callback\n"), DEBUG_INTR); if (wdc_c->flags & AT_WAIT) wakeup(wdc_c); else if (wdc_c->callback) wdc_c->callback(wdc_c->callback_arg); wdcstart(chp); WDCDEBUG_PRINT(("__wdccommand_done returned\n"), DEBUG_INTR); return;}/* * Send a command. The drive should be ready. * Assumes interrupts are blocked. */voidwdccommand(chp, drive, command, cylin, head, sector, count, precomp) struct channel_softc *chp; u_int8_t drive; u_int8_t command; u_int16_t cylin; u_int8_t head, sector, count, precomp;{ WDCDEBUG_PRINT(("wdccommand %s:%d:%d: command=0x%x cylin=%d head=%d " "sector=%d count=%d precomp=%d\n", chp->wdc->sc_dev.dv_xname, chp->channel, drive, command, cylin, head, sector, count, precomp), DEBUG_FUNCS); /* Select drive, head, and addressing mode. */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4) | head); /* Load parameters. wdr_features(ATA/ATAPI) = wdr_precomp(ST506) */ CHP_WRITE_REG(chp, wdr_precomp, precomp); CHP_WRITE_REG(chp, wdr_cyl_lo, cylin); CHP_WRITE_REG(chp, wdr_cyl_hi, cylin >> 8); CHP_WRITE_REG(chp, wdr_sector, sector); CHP_WRITE_REG(chp, wdr_seccnt, count); /* Send command. */ CHP_WRITE_REG(chp, wdr_command, command); return;}/* * Simplified version of wdccommand(). Unbusy/ready/drq must be * tested by the caller. */voidwdccommandshort(chp, drive, command) struct channel_softc *chp; int drive; int command;{ WDCDEBUG_PRINT(("wdccommandshort %s:%d:%d command 0x%x\n", chp->wdc->sc_dev.dv_xname, chp->channel, drive, command), DEBUG_FUNCS); /* Select drive. */ CHP_WRITE_REG(chp, wdr_sdh, WDSD_IBM | (drive << 4)); CHP_WRITE_REG(chp, wdr_command, command);}/* Add a command to the queue and start controller. Must be called at splbio */voidwdc_exec_xfer(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer;{ WDCDEBUG_PRINT(("wdc_exec_xfer %p channel %d drive %d\n", xfer, chp->channel, xfer->drive), DEBUG_XFERS); /* complete xfer setup */ xfer->chp = chp; /* * If we are a polled command, and the list is not empty, * we are doing a dump. Drop the list to allow the polled command * to complete, we're going to reboot soon anyway. */ if ((xfer->c_flags & C_POLL) != 0 && chp->ch_queue->sc_xfer.tqh_first != NULL) { TAILQ_INIT(&chp->ch_queue->sc_xfer); } /* insert at the end of command list */ TAILQ_INSERT_TAIL(&chp->ch_queue->sc_xfer,xfer , c_xferchain); WDCDEBUG_PRINT(("wdcstart from wdc_exec_xfer, flags 0x%x\n", chp->ch_flags), DEBUG_XFERS); wdcstart(chp);}struct wdc_xfer *wdc_get_xfer(flags) int flags;{ struct wdc_xfer *xfer; int s; s = splbio(); if ((xfer = xfer_free_list.lh_first) != NULL) { LIST_REMOVE(xfer, free_list); splx(s);#ifdef DIAGNOSTIC if ((xfer->c_flags & C_INUSE) != 0) panic("wdc_get_xfer: xfer already in use\n");#endif } else { splx(s); WDCDEBUG_PRINT(("wdc:making xfer %d\n",wdc_nxfer), DEBUG_XFERS); xfer = malloc(sizeof(*xfer), M_DEVBUF, ((flags & WDC_NOSLEEP) != 0 ? M_NOWAIT : M_WAITOK)); if (xfer == NULL) return 0;#ifdef DIAGNOSTIC xfer->c_flags &= ~C_INUSE;#endif#ifdef WDCDEBUG wdc_nxfer++;#endif }#ifdef DIAGNOSTIC if ((xfer->c_flags & C_INUSE) != 0) panic("wdc_get_xfer: xfer already in use\n");#endif bzero(xfer, sizeof(struct wdc_xfer)); xfer->c_flags = C_INUSE; return xfer;}voidwdc_free_xfer(chp, xfer) struct channel_softc *chp; struct wdc_xfer *xfer;{ struct wdc_softc *wdc = chp->wdc; int s; if (wdc->cap & WDC_CAPABILITY_HWLOCK) (*wdc->free_hw)(chp); s = splbio(); chp->ch_flags &= ~WDCF_ACTIVE; TAILQ_REMOVE(&chp->ch_queue->sc_xfer, xfer, c_xferchain); xfer->c_flags &= ~C_INUSE; LIST_INSERT_HEAD(&xfer_free_list, xfer, free_list); splx(s);}/* * Kill off all pending xfers for a channel_softc. * * Must be called at splbio(). */voidwdc_kill_pending(chp) struct channel_softc *chp;{ struct wdc_xfer *xfer; while ((xfer = TAILQ_FIRST(&chp->ch_queue->sc_xfer)) != NULL) { chp = xfer->chp; (*xfer->c_kill_xfer)(chp, xfer); }}#ifndef PMONstatic void__wdcerror(chp, msg) struct channel_softc *chp; char *msg;{ struct wdc_xfer *xfer = chp->ch_queue->sc_xfer.tqh_first; if (xfer == NULL) printf("%s:%d: %s\n", chp->wdc->sc_dev.dv_xname, chp->channel, msg); else printf("%s(%s:%d:%d): %s\n", chp->ch_drive[xfer->drive].drive_name, chp->wdc->sc_dev.dv_xname, chp->channel, xfer->drive, msg);}#endif /*PMON*//* * the bit bucket */voidwdcbit_bucket(chp, size) struct channel_softc *chp; int size;{ CHP_READ_RAW_MULTI_2(chp, NULL, size);}#ifndef __OpenBSD__intwdc_addref(chp) struct channel_softc *chp;{ struct wdc_softc *wdc = chp->wdc; struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter; int s, error = 0; s = splbio(); if (adapter->scsipi_refcnt++ == 0 && adapter->scsipi_enable != NULL) { error = (*adapter->scsipi_enable)(wdc, 1); if (error) adapter->scsipi_refcnt--; } splx(s); return (error);}voidwdc_delref(chp) struct channel_softc *chp;{ struct wdc_softc *wdc = chp->wdc; struct scsipi_adapter *adapter = &wdc->sc_atapi_adapter; int s; s = splbio(); if (adapter->scsipi_refcnt-- == 1 && adapter->scsipi_enable != NULL) (void) (*adapter->scsipi_enable)(wdc, 0); splx(s);}#endif
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -