static void aststrategy(struct buf *bp) { struct ast_softc *stp = bp->b_dev->si_drv1; int s; if (stp->device->flags & ATA_D_DETACHING) { bp->b_flags |= B_ERROR; bp->b_error = ENXIO; biodone(bp); return; } /* if it's a null transfer, return immediatly. */ if (bp->b_bcount == 0) { bp->b_resid = 0; biodone(bp); return; } if (!(bp->b_flags & B_READ) && stp->flags & F_WRITEPROTECT) { bp->b_flags |= B_ERROR; bp->b_error = EPERM; biodone(bp); return; } /* check for != blocksize requests */ if (bp->b_bcount % stp->blksize) { ata_prtdev(stp->device, "transfers must be multiple of %d\n", stp->blksize); bp->b_flags |= B_ERROR; bp->b_error = EIO; biodone(bp); return; } /* warn about transfers bigger than the device suggests */ if (bp->b_bcount > stp->blksize * stp->cap.ctl) { if ((stp->flags & F_CTL_WARN) == 0) { ata_prtdev(stp->device, "WARNING: CTL exceeded %ld>%d\n", bp->b_bcount, stp->blksize * stp->cap.ctl); stp->flags |= F_CTL_WARN; } } s = splbio(); bufq_insert_tail(&stp->queue, bp); splx(s); ata_start(stp->device->channel); }
int ata_resume(device_t dev) { struct ata_channel *ch; int error; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* reinit the devices, we dont know what mode/state they are in */ error = ata_reinit(dev); /* kick off requests on the queue */ ata_start(dev); return error; }
static int ata_cbuschannel_banking(device_t dev, int flags) { struct ata_cbus_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int res; mtx_lock(&ctlr->bank_mtx); switch (flags) { case ATA_LF_LOCK: if (ctlr->locked_bank == -1) ctlr->locked_bank = ch->unit; if (ctlr->locked_bank == ch->unit) { ctlr->hardware_bank = ch->unit; ATA_OUTB(ctlr->bankio, 0, ch->unit); } else ctlr->restart_bank = ch->unit; break; case ATA_LF_UNLOCK: if (ctlr->locked_bank == ch->unit) { ctlr->locked_bank = -1; if (ctlr->restart_bank != -1) { if ((ch = ctlr->interrupt[ctlr->restart_bank].argument)) { ctlr->restart_bank = -1; mtx_unlock(&ctlr->bank_mtx); ata_start(ch->dev); return -1; } } } break; case ATA_LF_WHICH: break; } res = ctlr->locked_bank; mtx_unlock(&ctlr->bank_mtx); return res; }
static int ata_serialize(device_t dev, int flags) { struct ata_pci_controller *ctlr = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); struct ata_serialize *serial; int res; serial = ctlr->chipset_data; mtx_lock(&serial->locked_mtx); switch (flags) { case ATA_LF_LOCK: if (serial->locked_ch == -1) serial->locked_ch = ch->unit; if (serial->locked_ch != ch->unit) serial->restart_ch = ch->unit; break; case ATA_LF_UNLOCK: if (serial->locked_ch == ch->unit) { serial->locked_ch = -1; if (serial->restart_ch != -1) { if ((ch = ctlr->interrupt[serial->restart_ch].argument)) { serial->restart_ch = -1; mtx_unlock(&serial->locked_mtx); ata_start(dev); return -1; } } } break; case ATA_LF_WHICH: break; } res = serial->locked_ch; mtx_unlock(&serial->locked_mtx); return res; }
static int ata_usbchannel_locking(device_t dev, int flags) { struct atausb_softc *sc = device_get_softc(device_get_parent(dev)); struct ata_channel *ch = device_get_softc(dev); int res = -1; spin_lock(&sc->locked_mtx); switch (flags) { case ATA_LF_LOCK: if (sc->locked_ch == NULL) sc->locked_ch = ch; if (sc->locked_ch != ch) sc->restart_ch = ch; break; case ATA_LF_UNLOCK: if (sc->locked_ch == ch) { sc->locked_ch = NULL; if (sc->restart_ch) { ch = sc->restart_ch; sc->restart_ch = NULL; spin_unlock(&sc->locked_mtx); ata_start(ch->dev); return res; } } break; case ATA_LF_WHICH: break; } if (sc->locked_ch) res = sc->locked_ch->unit; spin_unlock(&sc->locked_mtx); return res; }
int ata_reinit(struct ata_channel *ch) { int devices, misdev, newdev; if (!ch->r_io || !ch->r_altio || !ch->r_irq) return ENXIO; ATA_FORCELOCK_CH(ch, ATA_CONTROL); ch->running = NULL; devices = ch->devices; ata_printf(ch, -1, "resetting devices .. "); ata_reset(ch); if ((misdev = devices & ~ch->devices)) { if (misdev) printf("\n"); #if NATADISK > 0 if (misdev & ATA_ATA_MASTER && ch->device[MASTER].driver) ad_detach(&ch->device[MASTER], 0); if (misdev & ATA_ATA_SLAVE && ch->device[SLAVE].driver) ad_detach(&ch->device[SLAVE], 0); #endif #if DEV_ATAPIALL if (misdev & ATA_ATAPI_MASTER && ch->device[MASTER].driver) atapi_detach(&ch->device[MASTER]); if (misdev & ATA_ATAPI_SLAVE && ch->device[SLAVE].driver) atapi_detach(&ch->device[SLAVE]); #endif if (misdev & ATA_ATA_MASTER || misdev & ATA_ATAPI_MASTER) { if (ch->device[MASTER].param) free(ch->device[MASTER].param, M_ATA); ch->device[MASTER].param = NULL; } if (misdev & ATA_ATA_SLAVE || misdev & ATA_ATAPI_SLAVE) { if (ch->device[SLAVE].param) free(ch->device[SLAVE].param, M_ATA); ch->device[SLAVE].param = NULL; } } if ((newdev = ~devices & ch->devices)) { if (newdev & ATA_ATA_MASTER) if (ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY)) ch->devices &= ~ATA_ATA_MASTER; if (newdev & ATA_ATA_SLAVE) if (ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY)) ch->devices &= ~ATA_ATA_SLAVE; if (newdev & ATA_ATAPI_MASTER) if (ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY)) ch->devices &= ~ATA_ATAPI_MASTER; if (newdev & ATA_ATAPI_SLAVE) if (ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY)) ch->devices &= ~ATA_ATAPI_SLAVE; } newdev = ~devices & ch->devices; if (!misdev && newdev) printf("\n"); #if NATADISK > 0 if (newdev & ATA_ATA_MASTER && !ch->device[MASTER].driver) ad_attach(&ch->device[MASTER], 1); else if (ch->devices & ATA_ATA_MASTER && ch->device[MASTER].driver) { ata_getparam(&ch->device[MASTER], ATA_C_ATA_IDENTIFY); ad_reinit(&ch->device[MASTER]); } if (newdev & ATA_ATA_SLAVE && !ch->device[SLAVE].driver) ad_attach(&ch->device[SLAVE], 1); else if (ch->devices & (ATA_ATA_SLAVE) && ch->device[SLAVE].driver) { ata_getparam(&ch->device[SLAVE], ATA_C_ATA_IDENTIFY); ad_reinit(&ch->device[SLAVE]); } #endif #if DEV_ATAPIALL if (newdev & ATA_ATAPI_MASTER && !ch->device[MASTER].driver) atapi_attach(&ch->device[MASTER], 1); else if (ch->devices & (ATA_ATAPI_MASTER) && ch->device[MASTER].driver) { ata_getparam(&ch->device[MASTER], ATA_C_ATAPI_IDENTIFY); atapi_reinit(&ch->device[MASTER]); } if (newdev & ATA_ATAPI_SLAVE && !ch->device[SLAVE].driver) atapi_attach(&ch->device[SLAVE], 1); else if (ch->devices & (ATA_ATAPI_SLAVE) && ch->device[SLAVE].driver) { ata_getparam(&ch->device[SLAVE], ATA_C_ATAPI_IDENTIFY); atapi_reinit(&ch->device[SLAVE]); } #endif #if NATAPICAM > 0 if (ch->devices & (ATA_ATAPI_MASTER | ATA_ATAPI_SLAVE)) atapi_cam_reinit_bus(ch); #endif printf("done\n"); ATA_UNLOCK_CH(ch); ata_start(ch); return 0; }
static void ata_intr(void *data) { struct ata_channel *ch = (struct ata_channel *)data; /* * on PCI systems we might share an interrupt line with another * device or our twin ATA channel, so call ch->intr_func to figure * out if it is really an interrupt we should process here */ if (ch->intr_func && ch->intr_func(ch)) return; /* if drive is busy it didn't interrupt */ if (ATA_INB(ch->r_altio, ATA_ALTSTAT) & ATA_S_BUSY) { DELAY(100); if (!(ATA_INB(ch->r_altio, ATA_ALTSTAT) & ATA_S_DRQ)) return; } /* clear interrupt and get status */ ch->status = ATA_INB(ch->r_io, ATA_STATUS); if (ch->status & ATA_S_ERROR) ch->error = ATA_INB(ch->r_io, ATA_ERROR); /* find & call the responsible driver to process this interrupt */ switch (ch->active) { #if NATADISK > 0 case ATA_ACTIVE_ATA: if (!ch->running || ad_interrupt(ch->running) == ATA_OP_CONTINUES) return; break; #endif #if DEV_ATAPIALL case ATA_ACTIVE_ATAPI: if (!ch->running || atapi_interrupt(ch->running) == ATA_OP_CONTINUES) return; break; #endif case ATA_WAIT_INTR: case ATA_WAIT_INTR | ATA_CONTROL: wakeup((caddr_t)ch); break; case ATA_WAIT_READY: case ATA_WAIT_READY | ATA_CONTROL: break; case ATA_IDLE: if (ch->flags & ATA_QUEUED) { ch->active = ATA_ACTIVE; if (ata_service(ch) == ATA_OP_CONTINUES) return; } /* FALLTHROUGH */ default: #ifdef ATA_DEBUG { static int intr_count = 0; if (intr_count++ < 10) ata_printf(ch, -1, "unwanted interrupt #%d active=%02x s=%02x\n", intr_count, ch->active, ch->status); } #endif break; } ch->active &= ATA_CONTROL; if (ch->active & ATA_CONTROL) return; ch->running = NULL; ata_start(ch); return; }
int ata_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; device_t *children; int nchildren, i; /* check that we have a valid channel to reinit */ if (!ch || !ch->r_irq) return ENXIO; if (bootverbose) device_printf(dev, "reiniting channel ..\n"); /* poll for locking the channel */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) pause("atarini", 1); /* catch eventual request in ch->running */ mtx_lock(&ch->state_mtx); if ((request = ch->running)) callout_stop(&request->callout); ch->running = NULL; /* unconditionally grap the channel lock */ ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ATA_RESET(dev); /* reinit the children and delete any that fails */ if (!device_get_children(dev, &children, &nchildren)) { mtx_lock(&Giant); /* newbus suckage it needs Giant */ for (i = 0; i < nchildren; i++) { /* did any children go missing ? */ if (children[i] && device_is_attached(children[i]) && ATA_REINIT(children[i])) { /* * if we had a running request and its device matches * this child we need to inform the request that the * device is gone. */ if (request && request->dev == children[i]) { request->result = ENXIO; device_printf(request->dev, "FAILURE - device detached\n"); /* if not timeout finish request here */ if (!(request->flags & ATA_R_TIMEOUT)) ata_finish(request); request = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* if we still have a good request put it on the queue again */ if (request && !(request->flags & ATA_R_TIMEOUT)) { device_printf(request->dev, "WARNING - %s requeued due to channel reset", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); request->flags |= ATA_R_REQUEUE; ata_queue_request(request); } /* we're done release the channel for new work */ mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); if (bootverbose) device_printf(dev, "reinit done ..\n"); /* kick off requests on the queue */ ata_start(dev); return 0; }
/* * device related interfaces */ static int ata_ioctl(struct dev_ioctl_args *ap) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)ap->a_data; int *value = (int *)ap->a_data; int i, nchildren, error = ENOTTY; switch (ap->a_cmd) { case IOCATAGMAXCHANNEL: *value = devclass_get_maxunit(ata_devclass); error = 0; break; case IOCATAREINIT: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; error = ata_reinit(device); ata_start(device); break; case IOCATAATTACH: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; /* XXX SOS should enable channel HW on controller */ error = ata_attach(device); break; case IOCATADETACH: if (*value > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, *value))) return ENXIO; error = ata_detach(device); /* XXX SOS should disable channel HW on controller */ break; case IOCATADEVICES: if (devices->channel > devclass_get_maxunit(ata_devclass) || !(device = devclass_get_device(ata_devclass, devices->channel))) return ENXIO; bzero(devices->name[0], 32); bzero(&devices->params[0], sizeof(struct ata_params)); bzero(devices->name[1], 32); bzero(&devices->params[1], sizeof(struct ata_params)); if (!device_get_children(device, &children, &nchildren)) { for (i = 0; i < nchildren; i++) { if (children[i] && device_is_attached(children[i])) { struct ata_device *atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) { strncpy(devices->name[0], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { strncpy(devices->name[1], device_get_nameunit(children[i]), 32); bcopy(&atadev->param, &devices->params[1], sizeof(struct ata_params)); } } } kfree(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(ap->a_cmd, ap->a_data); } return error; }