Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 157328) +++ head/sys/dev/ata/ata-all.c (revision 157329) @@ -1,1045 +1,1049 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef __alpha__ #include #endif #include #include /* device structure */ static d_ioctl_t ata_ioctl; static struct cdevsw ata_cdevsw = { .d_version = D_VERSION, .d_flags = D_NEEDGIANT, /* we need this as newbus isn't mpsafe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; /* prototypes */ static void ata_boot_attach(void); static device_t ata_add_child(device_t, struct ata_device *, int); static int ata_getparam(struct ata_device *, int); static void bswap(int8_t *, int); static void btrim(int8_t *, int); static void bpack(int8_t *, int8_t *, int); /* global vars */ MALLOC_DEFINE(M_ATA, "ata_generic", "ATA driver generic layer"); int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data) = NULL; struct intr_config_hook *ata_delayed_attach = NULL; devclass_t ata_devclass; uma_zone_t ata_request_zone; uma_zone_t ata_composite_zone; int ata_wc = 1; /* local vars */ static int ata_dma = 1; static int atapi_dma = 1; /* sysctl vars */ SYSCTL_NODE(_hw, OID_AUTO, ata, CTLFLAG_RD, 0, "ATA driver parameters"); TUNABLE_INT("hw.ata.ata_dma", &ata_dma); SYSCTL_INT(_hw_ata, OID_AUTO, ata_dma, CTLFLAG_RDTUN, &ata_dma, 0, "ATA disk DMA mode control"); TUNABLE_INT("hw.ata.atapi_dma", &atapi_dma); SYSCTL_INT(_hw_ata, OID_AUTO, atapi_dma, CTLFLAG_RDTUN, &atapi_dma, 0, "ATAPI device DMA mode control"); TUNABLE_INT("hw.ata.wc", &ata_wc); SYSCTL_INT(_hw_ata, OID_AUTO, wc, CTLFLAG_RDTUN, &ata_wc, 0, "ATA disk write caching"); /* * newbus device interface related functions */ int ata_probe(device_t dev) { return 0; } int ata_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); int error, rid; /* check that we have a virgin channel to attach */ if (ch->r_irq) return EEXIST; /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); /* reset the controller HW, the channel and device(s) */ while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) tsleep(&error, PRIBIO, "ataatch", 1); ATA_RESET(dev); ATA_LOCKING(dev, ATA_LF_UNLOCK); /* setup interrupt delivery */ rid = ATA_IRQ_RID; ch->r_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if (!ch->r_irq) { device_printf(dev, "unable to allocate interrupt\n"); return ENXIO; } if ((error = bus_setup_intr(dev, ch->r_irq, ATA_INTR_FLAGS, (driver_intr_t *)ata_interrupt, ch, &ch->ih))) { device_printf(dev, "unable to setup interrupt\n"); return error; } /* probe and attach devices on this channel unless we are in early boot */ if (!ata_delayed_attach) ata_identify(dev); return 0; } int ata_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); device_t *children; int nchildren, i; /* check that we have a valid channel to detach */ if (!ch->r_irq) return ENXIO; /* grap the channel lock so no new requests gets launched */ mtx_lock(&ch->state_mtx); ch->state |= ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* release resources */ bus_teardown_intr(dev, ch->r_irq, ch->ih); bus_release_resource(dev, SYS_RES_IRQ, ATA_IRQ_RID, ch->r_irq); ch->r_irq = NULL; mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } 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) tsleep(&dev, PRIBIO, "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; } int ata_suspend(device_t dev) { struct ata_channel *ch; /* check for valid device */ if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* wait for the channel to be IDLE before entering suspend mode */ while (1) { mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE) { ch->state = ATA_ACTIVE; mtx_unlock(&ch->state_mtx); break; } mtx_unlock(&ch->state_mtx); tsleep(ch, PRIBIO, "atasusp", hz/10); } ATA_LOCKING(dev, ATA_LF_UNLOCK); return 0; } 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; } int ata_interrupt(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; mtx_lock(&ch->state_mtx); do { /* ignore interrupt if its not for us */ if (ch->hw.status && !ch->hw.status(ch->dev)) break; /* do we have a running request */ if (!(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* safetycheck for the right state */ if (ch->state == ATA_IDLE) { device_printf(request->dev, "interrupt on idle channel ignored\n"); break; } /* * we have the HW locks, so end the transaction for this request * if it finishes immediately otherwise wait for next interrupt */ if (ch->hw.end_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (ch->state == ATA_ACTIVE) ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); return 1; } } while (0); mtx_unlock(&ch->state_mtx); return 0; } /* * device related interfaces */ static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int32_t flag, struct thread *td) { device_t device, *children; struct ata_ioc_devices *devices = (struct ata_ioc_devices *)data; int *value = (int *)data; int i, nchildren, error = ENOTTY; switch (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)); } } } free(children, M_TEMP); error = 0; } else error = ENODEV; break; default: if (ata_raid_ioctl_func) error = ata_raid_ioctl_func(cmd, data); } return error; } int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data) { struct ata_device *atadev = device_get_softc(dev); struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data; struct ata_params *params = (struct ata_params *)data; int *mode = (int *)data; struct ata_request *request; caddr_t buf; int error; switch (cmd) { case IOCATAREQUEST: if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) { return ENOMEM; } if (!(request = ata_alloc_request())) { free(buf, M_ATA); return ENOMEM; } if (ioc_request->flags & ATA_CMD_WRITE) { error = copyin(ioc_request->data, buf, ioc_request->count); if (error) { free(buf, M_ATA); ata_free_request(request); return error; } } request->dev = dev; if (ioc_request->flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = ioc_request->u.ata.command; request->u.ata.feature = ioc_request->u.ata.feature; request->u.ata.lba = ioc_request->u.ata.lba; request->u.ata.count = ioc_request->u.ata.count; } request->timeout = ioc_request->timeout; request->data = buf; request->bytecount = ioc_request->count; request->transfersize = request->bytecount; if (ioc_request->flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (ioc_request->flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (ioc_request->flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); - if (!(request->flags & ATA_R_ATAPI)) { + if (request->flags & ATA_R_ATAPI) { + bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense, + sizeof(struct atapi_sense)); + } + else { ioc_request->u.ata.command = request->u.ata.command; ioc_request->u.ata.feature = request->u.ata.feature; ioc_request->u.ata.lba = request->u.ata.lba; ioc_request->u.ata.count = request->u.ata.count; } ioc_request->error = request->result; if (ioc_request->flags & ATA_CMD_READ) error = copyout(buf, ioc_request->data, ioc_request->count); else error = 0; free(buf, M_ATA); ata_free_request(request); return error; case IOCATAGPARM: ata_getparam(atadev, 0); bcopy(&atadev->param, params, sizeof(struct ata_params)); return 0; case IOCATASMODE: atadev->mode = *mode; ATA_SETMODE(device_get_parent(dev), dev); return 0; case IOCATAGMODE: *mode = atadev->mode; return 0; default: return ENOTTY; } } static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; mtx_lock(&Giant); /* newbus suckage it needs Giant */ /* kick of probe and attach on all channels */ for (ctlr = 0; ctlr < devclass_get_maxunit(ata_devclass); ctlr++) { if ((ch = devclass_get_softc(ata_devclass, ctlr))) { ata_identify(ch->dev); } } /* release the hook that got us here, we are only needed once during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* * misc support functions */ static device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit) { device_t child; if ((child = device_add_child(parent, NULL, unit))) { device_set_softc(child, atadev); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; } return child; } static int ata_getparam(struct ata_device *atadev, int init) { struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev)); struct ata_request *request; u_int8_t command = 0; int error = ENOMEM, retries = 2; if (ch->devices & (atadev->unit == ATA_MASTER ? ATA_ATA_MASTER : ATA_ATA_SLAVE)) command = ATA_ATA_IDENTIFY; if (ch->devices & (atadev->unit == ATA_MASTER ? ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE)) command = ATA_ATAPI_IDENTIFY; if (!command) return ENXIO; while (retries-- > 0 && error) { if (!(request = ata_alloc_request())) break; request->dev = atadev->dev; request->timeout = 1; request->retries = 0; request->u.ata.command = command; request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT|ATA_R_QUIET); request->data = (void *)&atadev->param; request->bytecount = sizeof(struct ata_params); request->donecount = 0; request->transfersize = DEV_BSIZE; ata_queue_request(request); error = request->result; ata_free_request(request); } if (!error && (isprint(atadev->param.model[0]) || isprint(atadev->param.model[1]))) { struct ata_params *atacap = &atadev->param; char buffer[64]; #if BYTE_ORDER == BIG_ENDIAN int16_t *ptr; for (ptr = (int16_t *)atacap; ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) { *ptr = bswap16(*ptr); } #endif if (!(!strncmp(atacap->model, "FX", 2) || !strncmp(atacap->model, "NEC", 3) || !strncmp(atacap->model, "Pioneer", 7) || !strncmp(atacap->model, "SHARP", 5))) { bswap(atacap->model, sizeof(atacap->model)); bswap(atacap->revision, sizeof(atacap->revision)); bswap(atacap->serial, sizeof(atacap->serial)); } btrim(atacap->model, sizeof(atacap->model)); bpack(atacap->model, atacap->model, sizeof(atacap->model)); btrim(atacap->revision, sizeof(atacap->revision)); bpack(atacap->revision, atacap->revision, sizeof(atacap->revision)); btrim(atacap->serial, sizeof(atacap->serial)); bpack(atacap->serial, atacap->serial, sizeof(atacap->serial)); if (bootverbose) printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n", device_get_unit(ch->dev), atadev->unit == ATA_MASTER ? "master" : "slave", ata_mode2str(ata_pmode(atacap)), ata_mode2str(ata_wmode(atacap)), ata_mode2str(ata_umode(atacap)), (atacap->hwres & ATA_CABLE_ID) ? "80":"40"); if (init) { sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision); device_set_desc_copy(atadev->dev, buffer); if (atadev->param.config & ATA_PROTO_ATAPI) { if (atapi_dma && ch->dma && (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR && ata_umode(&atadev->param) >= ATA_UDMA2) atadev->mode = ATA_DMA_MAX; } else { if (ata_dma && ch->dma && (ata_umode(&atadev->param) > 0 || ata_wmode(&atadev->param) > 0)) atadev->mode = ATA_DMA_MAX; } } } else { if (!error) error = ENXIO; } return error; } int ata_identify(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_device *master = NULL, *slave = NULL; device_t master_child = NULL, slave_child = NULL; int master_unit = -1, slave_unit = -1; if (ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER)) { if (!(master = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } master->unit = ATA_MASTER; } if (ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE)) { if (!(slave = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { free(master, M_ATA); device_printf(dev, "out of memory\n"); return ENOMEM; } slave->unit = ATA_SLAVE; } #ifdef ATA_STATIC_ID if (ch->devices & ATA_ATA_MASTER) master_unit = (device_get_unit(dev) << 1); #endif if (master && !(master_child = ata_add_child(dev, master, master_unit))) { free(master, M_ATA); master = NULL; } #ifdef ATA_STATIC_ID if (ch->devices & ATA_ATA_SLAVE) slave_unit = (device_get_unit(dev) << 1) + 1; #endif if (slave && !(slave_child = ata_add_child(dev, slave, slave_unit))) { free(slave, M_ATA); slave = NULL; } if (slave && ata_getparam(slave, 1)) { device_delete_child(dev, slave_child); free(slave, M_ATA); } if (master && ata_getparam(master, 1)) { device_delete_child(dev, master_child); free(master, M_ATA); } bus_generic_probe(dev); bus_generic_attach(dev); return 0; } void ata_default_registers(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* fill in the defaults from whats setup already */ ch->r_io[ATA_ERROR].res = ch->r_io[ATA_FEATURE].res; ch->r_io[ATA_ERROR].offset = ch->r_io[ATA_FEATURE].offset; ch->r_io[ATA_IREASON].res = ch->r_io[ATA_COUNT].res; ch->r_io[ATA_IREASON].offset = ch->r_io[ATA_COUNT].offset; ch->r_io[ATA_STATUS].res = ch->r_io[ATA_COMMAND].res; ch->r_io[ATA_STATUS].offset = ch->r_io[ATA_COMMAND].offset; ch->r_io[ATA_ALTSTAT].res = ch->r_io[ATA_CONTROL].res; ch->r_io[ATA_ALTSTAT].offset = ch->r_io[ATA_CONTROL].offset; } void ata_modify_if_48bit(struct ata_request *request) { struct ata_channel *ch = device_get_softc(device_get_parent(request->dev)); struct ata_device *atadev = device_get_softc(request->dev); atadev->flags &= ~ATA_D_48BIT_ACTIVE; if ((request->u.ata.lba >= ATA_MAX_28BIT_LBA || request->u.ata.count > 256) && atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) { /* translate command into 48bit version */ switch (request->u.ata.command) { case ATA_READ: request->u.ata.command = ATA_READ48; break; case ATA_READ_MUL: request->u.ata.command = ATA_READ_MUL48; break; case ATA_READ_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA48; break; case ATA_READ_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL48; else request->u.ata.command = ATA_READ48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_READ_DMA_QUEUED48; break; case ATA_WRITE: request->u.ata.command = ATA_WRITE48; break; case ATA_WRITE_MUL: request->u.ata.command = ATA_WRITE_MUL48; break; case ATA_WRITE_DMA: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA48; break; case ATA_WRITE_DMA_QUEUED: if (ch->flags & ATA_NO_48BIT_DMA) { if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL48; else request->u.ata.command = ATA_WRITE48; request->u.ata.command = ATA_WRITE48; request->flags &= ~ATA_R_DMA; } else request->u.ata.command = ATA_WRITE_DMA_QUEUED48; break; case ATA_FLUSHCACHE: request->u.ata.command = ATA_FLUSHCACHE48; break; case ATA_READ_NATIVE_MAX_ADDDRESS: request->u.ata.command = ATA_READ_NATIVE_MAX_ADDDRESS48; break; case ATA_SET_MAX_ADDRESS: request->u.ata.command = ATA_SET_MAX_ADDRESS48; break; default: return; } atadev->flags |= ATA_D_48BIT_ACTIVE; } } void ata_udelay(int interval) { /* for now just use DELAY, the timer/sleep subsytems are not there yet */ if (1 || interval < (1000000/hz) || ata_delayed_attach) DELAY(interval); else tsleep(&interval, PRIBIO, "ataslp", interval/(1000000/hz)); } char * ata_mode2str(int mode) { switch (mode) { case -1: return "UNSUPPORTED"; case ATA_PIO0: return "PIO0"; case ATA_PIO1: return "PIO1"; case ATA_PIO2: return "PIO2"; case ATA_PIO3: return "PIO3"; case ATA_PIO4: return "PIO4"; case ATA_WDMA0: return "WDMA0"; case ATA_WDMA1: return "WDMA1"; case ATA_WDMA2: return "WDMA2"; case ATA_UDMA0: return "UDMA16"; case ATA_UDMA1: return "UDMA25"; case ATA_UDMA2: return "UDMA33"; case ATA_UDMA3: return "UDMA40"; case ATA_UDMA4: return "UDMA66"; case ATA_UDMA5: return "UDMA100"; case ATA_UDMA6: return "UDMA133"; case ATA_SA150: return "SATA150"; case ATA_SA300: return "SATA300"; case ATA_USB: return "USB"; case ATA_USB1: return "USB1"; case ATA_USB2: return "USB2"; default: if (mode & ATA_DMA_MASK) return "BIOSDMA"; else return "BIOSPIO"; } } int ata_pmode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_64_70) { if (ap->apiomodes & 0x02) return ATA_PIO4; if (ap->apiomodes & 0x01) return ATA_PIO3; } if (ap->mwdmamodes & 0x04) return ATA_PIO4; if (ap->mwdmamodes & 0x02) return ATA_PIO3; if (ap->mwdmamodes & 0x01) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x200) return ATA_PIO2; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x100) return ATA_PIO1; if ((ap->retired_piomode & ATA_RETIRED_PIO_MASK) == 0x000) return ATA_PIO0; return ATA_PIO0; } int ata_wmode(struct ata_params *ap) { if (ap->mwdmamodes & 0x04) return ATA_WDMA2; if (ap->mwdmamodes & 0x02) return ATA_WDMA1; if (ap->mwdmamodes & 0x01) return ATA_WDMA0; return -1; } int ata_umode(struct ata_params *ap) { if (ap->atavalid & ATA_FLAG_88) { if (ap->udmamodes & 0x40) return ATA_UDMA6; if (ap->udmamodes & 0x20) return ATA_UDMA5; if (ap->udmamodes & 0x10) return ATA_UDMA4; if (ap->udmamodes & 0x08) return ATA_UDMA3; if (ap->udmamodes & 0x04) return ATA_UDMA2; if (ap->udmamodes & 0x02) return ATA_UDMA1; if (ap->udmamodes & 0x01) return ATA_UDMA0; } return -1; } int ata_limit_mode(device_t dev, int mode, int maxmode) { struct ata_device *atadev = device_get_softc(dev); if (maxmode && mode > maxmode) mode = maxmode; if (mode >= ATA_UDMA0 && ata_umode(&atadev->param) > 0) return min(mode, ata_umode(&atadev->param)); if (mode >= ATA_WDMA0 && ata_wmode(&atadev->param) > 0) return min(mode, ata_wmode(&atadev->param)); if (mode > ata_pmode(&atadev->param)) return min(mode, ata_pmode(&atadev->param)); return mode; } static void bswap(int8_t *buf, int len) { u_int16_t *ptr = (u_int16_t*)(buf + len); while (--ptr >= (u_int16_t*)buf) *ptr = ntohs(*ptr); } static void btrim(int8_t *buf, int len) { int8_t *ptr; for (ptr = buf; ptr < buf+len; ++ptr) if (!*ptr || *ptr == '_') *ptr = ' '; for (ptr = buf + len - 1; ptr >= buf && *ptr == ' '; --ptr) *ptr = 0; } static void bpack(int8_t *src, int8_t *dst, int len) { int i, j, blank; for (i = j = blank = 0 ; i < len; i++) { if (blank && src[i] == ' ') continue; if (blank && src[i] != ' ') { dst[j++] = src[i]; blank = 0; continue; } if (src[i] == ' ') { blank = 1; if (i == 0) continue; } dst[j++] = src[i]; } if (j < len) dst[j] = 0x00; } /* * module handeling */ static int ata_module_event_handler(module_t mod, int what, void *arg) { static struct cdev *atacdev; switch (what) { case MOD_LOAD: /* register controlling device */ atacdev = make_dev(&ata_cdevsw, 0, UID_ROOT, GID_OPERATOR, 0600, "ata"); if (cold) { /* register boot attach to be run when interrupts are enabled */ if (!(ata_delayed_attach = (struct intr_config_hook *) malloc(sizeof(struct intr_config_hook), M_TEMP, M_NOWAIT | M_ZERO))) { printf("ata: malloc of delayed attach hook failed\n"); return EIO; } ata_delayed_attach->ich_func = (void*)ata_boot_attach; if (config_intrhook_establish(ata_delayed_attach) != 0) { printf("ata: config_intrhook_establish failed\n"); free(ata_delayed_attach, M_TEMP); } } return 0; case MOD_UNLOAD: /* deregister controlling device */ destroy_dev(atacdev); return 0; default: return EOPNOTSUPP; } } static moduledata_t ata_moduledata = { "ata", ata_module_event_handler, NULL }; DECLARE_MODULE(ata, ata_moduledata, SI_SUB_CONFIGURE, SI_ORDER_SECOND); MODULE_VERSION(ata, 1); static void ata_init(void) { ata_request_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); ata_composite_zone = uma_zcreate("ata_composite", sizeof(struct ata_composite), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(ata_register, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL); static void ata_uninit(void) { uma_zdestroy(ata_composite_zone); uma_zdestroy(ata_request_zone); } SYSUNINIT(ata_unregister, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_uninit, NULL); Index: head/sys/dev/ata/ata-all.h =================================================================== --- head/sys/dev/ata/ata-all.h (revision 157328) +++ head/sys/dev/ata/ata-all.h (revision 157329) @@ -1,682 +1,643 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ /* ATA register defines */ #define ATA_DATA 0 /* (RW) data */ #define ATA_FEATURE 1 /* (W) feature */ #define ATA_F_DMA 0x01 /* enable DMA */ #define ATA_F_OVL 0x02 /* enable overlap */ #define ATA_COUNT 2 /* (W) sector count */ #define ATA_SECTOR 3 /* (RW) sector # */ #define ATA_CYL_LSB 4 /* (RW) cylinder# LSB */ #define ATA_CYL_MSB 5 /* (RW) cylinder# MSB */ #define ATA_DRIVE 6 /* (W) Sector/Drive/Head */ #define ATA_D_LBA 0x40 /* use LBA addressing */ #define ATA_D_IBM 0xa0 /* 512 byte sectors, ECC */ #define ATA_COMMAND 7 /* (W) command */ #define ATA_ERROR 8 /* (R) error */ #define ATA_E_ILI 0x01 /* illegal length */ #define ATA_E_NM 0x02 /* no media */ #define ATA_E_ABORT 0x04 /* command aborted */ #define ATA_E_MCR 0x08 /* media change request */ #define ATA_E_IDNF 0x10 /* ID not found */ #define ATA_E_MC 0x20 /* media changed */ #define ATA_E_UNC 0x40 /* uncorrectable data */ #define ATA_E_ICRC 0x80 /* UDMA crc error */ -#define ATA_E_MASK 0x0f /* error mask */ -#define ATA_SK_MASK 0xf0 /* sense key mask */ -#define ATA_SK_NO_SENSE 0x00 /* no specific sense key info */ -#define ATA_SK_RECOVERED_ERROR 0x10 /* command OK, data recovered */ -#define ATA_SK_NOT_READY 0x20 /* no access to drive */ -#define ATA_SK_MEDIUM_ERROR 0x30 /* non-recovered data error */ -#define ATA_SK_HARDWARE_ERROR 0x40 /* non-recoverable HW failure */ -#define ATA_SK_ILLEGAL_REQUEST 0x50 /* invalid command param(s) */ -#define ATA_SK_UNIT_ATTENTION 0x60 /* media changed */ -#define ATA_SK_DATA_PROTECT 0x70 /* write protect */ -#define ATA_SK_BLANK_CHECK 0x80 /* blank check */ -#define ATA_SK_VENDOR_SPECIFIC 0x90 /* vendor specific skey */ -#define ATA_SK_COPY_ABORTED 0xa0 /* copy aborted */ -#define ATA_SK_ABORTED_COMMAND 0xb0 /* command aborted, try again */ -#define ATA_SK_EQUAL 0xc0 /* equal */ -#define ATA_SK_VOLUME_OVERFLOW 0xd0 /* volume overflow */ -#define ATA_SK_MISCOMPARE 0xe0 /* data dont match the medium */ -#define ATA_SK_RESERVED 0xf0 +#define ATA_E_ATAPI_SENSE_MASK 0xf0 /* ATAPI sense key mask */ #define ATA_IREASON 9 /* (R) interrupt reason */ #define ATA_I_CMD 0x01 /* cmd (1) | data (0) */ #define ATA_I_IN 0x02 /* read (1) | write (0) */ #define ATA_I_RELEASE 0x04 /* released bus (1) */ #define ATA_I_TAGMASK 0xf8 /* tag mask */ #define ATA_STATUS 10 /* (R) status */ #define ATA_ALTSTAT 11 /* (R) alternate status */ #define ATA_S_ERROR 0x01 /* error */ #define ATA_S_INDEX 0x02 /* index */ #define ATA_S_CORR 0x04 /* data corrected */ #define ATA_S_DRQ 0x08 /* data request */ #define ATA_S_DSC 0x10 /* drive seek completed */ #define ATA_S_SERVICE 0x10 /* drive needs service */ #define ATA_S_DWF 0x20 /* drive write fault */ #define ATA_S_DMA 0x20 /* DMA ready */ #define ATA_S_READY 0x40 /* drive ready */ #define ATA_S_BUSY 0x80 /* busy */ #define ATA_CONTROL 12 /* (W) control */ #define ATA_CTLOFFSET 0x206 /* control register offset */ #define ATA_PCCARD_CTLOFFSET 0x0e /* do for PCCARD devices */ #define ATA_PC98_CTLOFFSET 0x10c /* do for PC98 devices */ #define ATA_A_IDS 0x02 /* disable interrupts */ #define ATA_A_RESET 0x04 /* RESET controller */ #define ATA_A_4BIT 0x08 /* 4 head bits */ #define ATA_A_HOB 0x80 /* High Order Byte enable */ /* SATA register defines */ #define ATA_SSTATUS 13 #define ATA_SS_DET_MASK 0x0000000f #define ATA_SS_DET_NO_DEVICE 0x00000000 #define ATA_SS_DET_DEV_PRESENT 0x00000001 #define ATA_SS_DET_PHY_ONLINE 0x00000003 #define ATA_SS_DET_PHY_OFFLINE 0x00000004 #define ATA_SS_SPD_MASK 0x000000f0 #define ATA_SS_SPD_NO_SPEED 0x00000000 #define ATA_SS_SPD_GEN1 0x00000010 #define ATA_SS_SPD_GEN2 0x00000020 #define ATA_SS_IPM_MASK 0x00000f00 #define ATA_SS_IPM_NO_DEVICE 0x00000000 #define ATA_SS_IPM_ACTIVE 0x00000100 #define ATA_SS_IPM_PARTIAL 0x00000200 #define ATA_SS_IPM_SLUMBER 0x00000600 #define ATA_SS_CONWELL_MASK \ (ATA_SS_DET_MASK|ATA_SS_SPD_MASK|ATA_SS_IPM_MASK) #define ATA_SS_CONWELL_GEN1 \ (ATA_SS_DET_PHY_ONLINE|ATA_SS_SPD_GEN1|ATA_SS_IPM_ACTIVE) #define ATA_SS_CONWELL_GEN2 \ (ATA_SS_DET_PHY_ONLINE|ATA_SS_SPD_GEN2|ATA_SS_IPM_ACTIVE) #define ATA_SERROR 14 #define ATA_SE_DATA_CORRECTED 0x00000001 #define ATA_SE_COMM_CORRECTED 0x00000002 #define ATA_SE_DATA_ERR 0x00000100 #define ATA_SE_COMM_ERR 0x00000200 #define ATA_SE_PROT_ERR 0x00000400 #define ATA_SE_HOST_ERR 0x00000800 #define ATA_SE_PHY_CHANGED 0x00010000 #define ATA_SE_PHY_IERROR 0x00020000 #define ATA_SE_COMM_WAKE 0x00040000 #define ATA_SE_DECODE_ERR 0x00080000 #define ATA_SE_PARITY_ERR 0x00100000 #define ATA_SE_CRC_ERR 0x00200000 #define ATA_SE_HANDSHAKE_ERR 0x00400000 #define ATA_SE_LINKSEQ_ERR 0x00800000 #define ATA_SE_TRANSPORT_ERR 0x01000000 #define ATA_SE_UNKNOWN_FIS 0x02000000 #define ATA_SCONTROL 15 #define ATA_SC_DET_MASK 0x0000000f #define ATA_SC_DET_IDLE 0x00000000 #define ATA_SC_DET_RESET 0x00000001 #define ATA_SC_DET_DISABLE 0x00000004 #define ATA_SC_SPD_MASK 0x000000f0 #define ATA_SC_SPD_NO_SPEED 0x00000000 #define ATA_SC_SPD_SPEED_GEN1 0x00000010 #define ATA_SC_SPD_SPEED_GEN2 0x00000020 #define ATA_SC_IPM_MASK 0x00000f00 #define ATA_SC_IPM_NONE 0x00000000 #define ATA_SC_IPM_DIS_PARTIAL 0x00000100 #define ATA_SC_IPM_DIS_SLUMBER 0x00000200 #define ATA_SACTIVE 16 /* SATA AHCI v1.0 register defines */ #define ATA_AHCI_CAP 0x00 #define ATA_AHCI_NPMASK 0x1f #define ATA_AHCI_GHC 0x04 #define ATA_AHCI_GHC_AE 0x80000000 #define ATA_AHCI_GHC_IE 0x00000002 #define ATA_AHCI_GHC_HR 0x80000001 #define ATA_AHCI_IS 0x08 #define ATA_AHCI_PI 0x0c #define ATA_AHCI_VS 0x10 #define ATA_AHCI_OFFSET 0x80 #define ATA_AHCI_P_CLB 0x100 #define ATA_AHCI_P_CLBU 0x104 #define ATA_AHCI_P_FB 0x108 #define ATA_AHCI_P_FBU 0x10c #define ATA_AHCI_P_IS 0x110 #define ATA_AHCI_P_IE 0x114 #define ATA_AHCI_P_IX_DHR 0x00000001 #define ATA_AHCI_P_IX_PS 0x00000002 #define ATA_AHCI_P_IX_DS 0x00000004 #define ATA_AHCI_P_IX_SDB 0x00000008 #define ATA_AHCI_P_IX_UF 0x00000010 #define ATA_AHCI_P_IX_DP 0x00000020 #define ATA_AHCI_P_IX_PC 0x00000040 #define ATA_AHCI_P_IX_DI 0x00000080 #define ATA_AHCI_P_IX_PRC 0x00400000 #define ATA_AHCI_P_IX_IPM 0x00800000 #define ATA_AHCI_P_IX_OF 0x01000000 #define ATA_AHCI_P_IX_INF 0x04000000 #define ATA_AHCI_P_IX_IF 0x08000000 #define ATA_AHCI_P_IX_HBD 0x10000000 #define ATA_AHCI_P_IX_HBF 0x20000000 #define ATA_AHCI_P_IX_TFE 0x40000000 #define ATA_AHCI_P_IX_CPD 0x80000000 #define ATA_AHCI_P_CMD 0x118 #define ATA_AHCI_P_CMD_ST 0x00000001 #define ATA_AHCI_P_CMD_SUD 0x00000002 #define ATA_AHCI_P_CMD_POD 0x00000004 #define ATA_AHCI_P_CMD_CLO 0x00000008 #define ATA_AHCI_P_CMD_FRE 0x00000010 #define ATA_AHCI_P_CMD_CCS_MASK 0x00001f00 #define ATA_AHCI_P_CMD_ISS 0x00002000 #define ATA_AHCI_P_CMD_FR 0x00004000 #define ATA_AHCI_P_CMD_CR 0x00008000 #define ATA_AHCI_P_CMD_CPS 0x00010000 #define ATA_AHCI_P_CMD_PMA 0x00020000 #define ATA_AHCI_P_CMD_HPCP 0x00040000 #define ATA_AHCI_P_CMD_ISP 0x00080000 #define ATA_AHCI_P_CMD_CPD 0x00100000 #define ATA_AHCI_P_CMD_ATAPI 0x01000000 #define ATA_AHCI_P_CMD_DLAE 0x02000000 #define ATA_AHCI_P_CMD_ALPE 0x04000000 #define ATA_AHCI_P_CMD_ASP 0x08000000 #define ATA_AHCI_P_CMD_ICC_MASK 0xf0000000 #define ATA_AHCI_P_CMD_NOOP 0x00000000 #define ATA_AHCI_P_CMD_ACTIVE 0x10000000 #define ATA_AHCI_P_CMD_PARTIAL 0x20000000 #define ATA_AHCI_P_CMD_SLUMPER 0x60000000 #define ATA_AHCI_P_TFD 0x120 #define ATA_AHCI_P_SIG 0x124 #define ATA_AHCI_P_SSTS 0x128 #define ATA_AHCI_P_SCTL 0x12c #define ATA_AHCI_P_SERR 0x130 #define ATA_AHCI_P_SACT 0x134 #define ATA_AHCI_P_CI 0x138 #define ATA_AHCI_CL_SIZE 32 #define ATA_AHCI_CL_OFFSET 0 #define ATA_AHCI_FB_OFFSET 1024 #define ATA_AHCI_CT_OFFSET 1024+256 #define ATA_AHCI_CT_SG_OFFSET 128 #define ATA_AHCI_CT_SIZE 256 /* DMA register defines */ #define ATA_DMA_ENTRIES 256 #define ATA_DMA_EOT 0x80000000 #define ATA_BMCMD_PORT 17 #define ATA_BMCMD_START_STOP 0x01 #define ATA_BMCMD_WRITE_READ 0x08 #define ATA_BMDEVSPEC_0 18 #define ATA_BMSTAT_PORT 19 #define ATA_BMSTAT_ACTIVE 0x01 #define ATA_BMSTAT_ERROR 0x02 #define ATA_BMSTAT_INTERRUPT 0x04 #define ATA_BMSTAT_MASK 0x07 #define ATA_BMSTAT_DMA_MASTER 0x20 #define ATA_BMSTAT_DMA_SLAVE 0x40 #define ATA_BMSTAT_DMA_SIMPLEX 0x80 #define ATA_BMDEVSPEC_1 20 #define ATA_BMDTP_PORT 21 #define ATA_IDX_ADDR 22 #define ATA_IDX_DATA 23 #define ATA_MAX_RES 24 /* misc defines */ #define ATA_PRIMARY 0x1f0 #define ATA_SECONDARY 0x170 #define ATA_PC98_BANK 0x432 #define ATA_IOSIZE 0x08 #define ATA_PC98_IOSIZE 0x10 #define ATA_CTLIOSIZE 0x01 #define ATA_BMIOSIZE 0x08 #define ATA_PC98_BANKIOSIZE 0x01 #define ATA_IOADDR_RID 0 #define ATA_CTLADDR_RID 1 #define ATA_BMADDR_RID 0x20 #define ATA_PC98_CTLADDR_RID 8 #define ATA_PC98_BANKADDR_RID 9 #define ATA_IRQ_RID 0 #define ATA_DEV(device) ((device == ATA_MASTER) ? 0 : 1) -#define ATA_CFA_MAGIC 0x848A +#define ATA_CFA_MAGIC1 0x844A +#define ATA_CFA_MAGIC2 0x848A #define ATAPI_MAGIC_LSB 0x14 #define ATAPI_MAGIC_MSB 0xeb #define ATAPI_P_READ (ATA_S_DRQ | ATA_I_IN) #define ATAPI_P_WRITE (ATA_S_DRQ) #define ATAPI_P_CMDOUT (ATA_S_DRQ | ATA_I_CMD) #define ATAPI_P_DONEDRQ (ATA_S_DRQ | ATA_I_CMD | ATA_I_IN) #define ATAPI_P_DONE (ATA_I_CMD | ATA_I_IN) #define ATAPI_P_ABORT 0 #define ATA_INTR_FLAGS (INTR_MPSAFE|INTR_TYPE_BIO|INTR_ENTROPY) #define ATA_OP_CONTINUES 0 #define ATA_OP_FINISHED 1 #define ATA_MAX_28BIT_LBA 268435455UL -/* ATAPI request sense structure */ -struct atapi_sense { - u_int8_t error_code :7; /* current or deferred errors */ - u_int8_t valid :1; /* follows ATAPI spec */ - u_int8_t segment; /* Segment number */ - u_int8_t sense_key :4; /* sense key */ - u_int8_t reserved2_4 :1; /* reserved */ - u_int8_t ili :1; /* incorrect length indicator */ - u_int8_t eom :1; /* end of medium */ - u_int8_t filemark :1; /* filemark */ - u_int32_t cmd_info __packed; /* cmd information */ - u_int8_t sense_length; /* additional sense len (n-7) */ - u_int32_t cmd_specific_info __packed; /* additional cmd spec info */ - u_int8_t asc; /* additional sense code */ - u_int8_t ascq; /* additional sense code qual */ - u_int8_t replaceable_unit_code; /* replaceable unit code */ - u_int8_t sk_specific :7; /* sense key specific */ - u_int8_t sksv :1; /* sense key specific info OK */ - u_int8_t sk_specific1; /* sense key specific */ - u_int8_t sk_specific2; /* sense key specific */ -}; - /* structure used for composite atomic operations */ #define MAX_COMPOSITES 32 /* u_int32_t bits */ struct ata_composite { struct mtx lock; /* control lock */ u_int32_t rd_needed; /* needed read subdisks */ u_int32_t rd_done; /* done read subdisks */ u_int32_t wr_needed; /* needed write subdisks */ u_int32_t wr_depend; /* write depends on subdisks */ u_int32_t wr_done; /* done write subdisks */ struct ata_request *request[MAX_COMPOSITES]; u_int32_t residual; /* bytes still to transfer */ caddr_t data_1; caddr_t data_2; }; /* structure used to queue an ATA/ATAPI request */ struct ata_request { device_t dev; /* device handle */ device_t parent; /* channel handle */ union { struct { u_int8_t command; /* command reg */ u_int16_t feature; /* feature reg */ u_int16_t count; /* count reg */ u_int64_t lba; /* lba reg */ } ata; struct { u_int8_t ccb[16]; /* ATAPI command block */ - struct atapi_sense sense_data; /* ATAPI request sense data */ - u_int8_t sense_key; /* ATAPI request sense key */ - u_int8_t sense_cmd; /* ATAPI saved command */ + struct atapi_sense sense; /* ATAPI request sense data */ + u_int8_t saved_cmd; /* ATAPI saved command */ } atapi; } u; u_int32_t bytecount; /* bytes to transfer */ u_int32_t transfersize; /* bytes pr transfer */ caddr_t data; /* pointer to data buf */ int flags; #define ATA_R_CONTROL 0x00000001 #define ATA_R_READ 0x00000002 #define ATA_R_WRITE 0x00000004 #define ATA_R_ATAPI 0x00000008 #define ATA_R_DMA 0x00000010 #define ATA_R_QUIET 0x00000020 #define ATA_R_TIMEOUT 0x00000040 #define ATA_R_ORDERED 0x00000100 #define ATA_R_AT_HEAD 0x00000200 #define ATA_R_REQUEUE 0x00000400 #define ATA_R_THREAD 0x00000800 #define ATA_R_DIRECT 0x00001000 #define ATA_R_DEBUG 0x10000000 #define ATA_R_DANGER1 0x20000000 #define ATA_R_DANGER2 0x40000000 u_int8_t status; /* ATA status */ u_int8_t error; /* ATA error */ u_int8_t dmastat; /* DMA status */ u_int32_t donecount; /* bytes transferred */ int result; /* result error code */ void (*callback)(struct ata_request *request); struct sema done; /* request done sema */ int retries; /* retry count */ int timeout; /* timeout for this cmd */ struct callout callout; /* callout management */ struct task task; /* task management */ struct bio *bio; /* bio for this request */ int this; /* this request ID */ struct ata_composite *composite; /* for composite atomic ops */ void *driver; /* driver specific */ TAILQ_ENTRY(ata_request) chain; /* list management */ }; /* define this for debugging request processing */ #if 0 #define ATA_DEBUG_RQ(request, string) \ { \ if (request->flags & ATA_R_DEBUG) \ device_printf(request->dev, "req=%p %s " string "\n", \ request, ata_cmd2str(request)); \ } #else #define ATA_DEBUG_RQ(request, string) #endif /* structure describing an ATA/ATAPI device */ struct ata_device { device_t dev; /* device handle */ int unit; /* physical unit */ #define ATA_MASTER 0x00 #define ATA_SLAVE 0x10 struct ata_params param; /* ata param structure */ int mode; /* current transfermode */ u_int32_t max_iosize; /* max IO size */ int flags; #define ATA_D_USE_CHS 0x0001 #define ATA_D_MEDIA_CHANGED 0x0002 #define ATA_D_ENC_PRESENT 0x0004 #define ATA_D_48BIT_ACTIVE 0x0008 }; /* structure for holding DMA Physical Region Descriptors (PRD) entries */ struct ata_dma_prdentry { u_int32_t addr; u_int32_t count; }; /* structure used by the setprd function */ struct ata_dmasetprd_args { void *dmatab; int nsegs; int error; }; /* structure holding DMA related information */ struct ata_dma { bus_dma_tag_t dmatag; /* parent DMA tag */ bus_dma_tag_t sg_tag; /* SG list DMA tag */ bus_dmamap_t sg_map; /* SG list DMA map */ void *sg; /* DMA transfer table */ bus_addr_t sg_bus; /* bus address of dmatab */ bus_dma_tag_t data_tag; /* data DMA tag */ bus_dmamap_t data_map; /* data DMA map */ bus_dma_tag_t work_tag; /* workspace DMA tag */ bus_dmamap_t work_map; /* workspace DMA map */ u_int8_t *work; /* workspace */ bus_addr_t work_bus; /* bus address of dmatab */ u_int32_t alignment; /* DMA SG list alignment */ u_int32_t boundary; /* DMA SG list boundary */ u_int32_t segsize; /* DMA SG list segment size */ u_int32_t max_iosize; /* DMA data max IO size */ u_int32_t cur_iosize; /* DMA data current IO size */ int flags; #define ATA_DMA_READ 0x01 /* transaction is a read */ #define ATA_DMA_LOADED 0x02 /* DMA tables etc loaded */ #define ATA_DMA_ACTIVE 0x04 /* DMA transfer in progress */ void (*alloc)(device_t dev); void (*free)(device_t dev); void (*setprd)(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); int (*load)(device_t dev, caddr_t data, int32_t count, int dir, void *addr, int *nsegs); int (*unload)(device_t dev); int (*start)(device_t dev); int (*stop)(device_t dev); void (*reset)(device_t dev); }; /* structure holding lowlevel functions */ struct ata_lowlevel { int (*status)(device_t dev); int (*begin_transaction)(struct ata_request *request); int (*end_transaction)(struct ata_request *request); int (*command)(struct ata_request *request); }; /* structure holding resources for an ATA channel */ struct ata_resource { struct resource *res; int offset; }; /* structure describing an ATA channel */ struct ata_channel { device_t dev; /* device handle */ int unit; /* physical channel */ struct ata_resource r_io[ATA_MAX_RES];/* I/O resources */ struct resource *r_irq; /* interrupt of this channel */ void *ih; /* interrupt handle */ struct ata_lowlevel hw; /* lowlevel HW functions */ struct ata_dma *dma; /* DMA data / functions */ int flags; /* channel flags */ #define ATA_NO_SLAVE 0x01 #define ATA_USE_16BIT 0x02 #define ATA_ATAPI_DMA_RO 0x04 #define ATA_NO_48BIT_DMA 0x08 #define ATA_ALWAYS_DMASTAT 0x10 int devices; /* what is present */ #define ATA_ATA_MASTER 0x01 #define ATA_ATA_SLAVE 0x02 #define ATA_ATAPI_MASTER 0x04 #define ATA_ATAPI_SLAVE 0x08 struct mtx state_mtx; /* state lock */ int state; /* ATA channel state */ #define ATA_IDLE 0x0000 #define ATA_ACTIVE 0x0001 #define ATA_STALL_QUEUE 0x0002 struct mtx queue_mtx; /* queue lock */ TAILQ_HEAD(, ata_request) ata_queue; /* head of ATA queue */ struct ata_request *freezepoint; /* composite freezepoint */ struct ata_request *running; /* currently running request */ }; /* disk bay/enclosure related */ #define ATA_LED_OFF 0x00 #define ATA_LED_RED 0x01 #define ATA_LED_GREEN 0x02 #define ATA_LED_ORANGE 0x03 #define ATA_LED_MASK 0x03 /* externs */ extern int (*ata_raid_ioctl_func)(u_long cmd, caddr_t data); extern struct intr_config_hook *ata_delayed_attach; extern devclass_t ata_devclass; extern int ata_wc; /* public prototypes */ /* ata-all.c: */ int ata_probe(device_t dev); int ata_attach(device_t dev); int ata_detach(device_t dev); int ata_reinit(device_t dev); int ata_suspend(device_t dev); int ata_resume(device_t dev); int ata_interrupt(void *data); int ata_device_ioctl(device_t dev, u_long cmd, caddr_t data); int ata_identify(device_t dev); void ata_default_registers(device_t dev); void ata_modify_if_48bit(struct ata_request *request); void ata_udelay(int interval); char *ata_mode2str(int mode); int ata_pmode(struct ata_params *ap); int ata_wmode(struct ata_params *ap); int ata_umode(struct ata_params *ap); int ata_limit_mode(device_t dev, int mode, int maxmode); /* ata-queue.c: */ int ata_controlcmd(device_t dev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count); int ata_atapicmd(device_t dev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout); void ata_queue_request(struct ata_request *request); void ata_start(device_t dev); void ata_finish(struct ata_request *request); void ata_timeout(struct ata_request *); void ata_catch_inflight(device_t dev); void ata_fail_requests(device_t dev); char *ata_cmd2str(struct ata_request *request); /* ata-lowlevel.c: */ void ata_generic_hw(device_t dev); int ata_begin_transaction(struct ata_request *); int ata_end_transaction(struct ata_request *); void ata_generic_reset(device_t dev); int ata_generic_command(struct ata_request *request); /* macros for alloc/free of struct ata_request */ extern uma_zone_t ata_request_zone; #define ata_alloc_request() uma_zalloc(ata_request_zone, M_NOWAIT | M_ZERO) #define ata_free_request(request) { \ if (!(request->flags & ATA_R_DANGER2)) \ uma_zfree(ata_request_zone, request); \ } /* macros for alloc/free of struct ata_composite */ extern uma_zone_t ata_composite_zone; #define ata_alloc_composite() uma_zalloc(ata_composite_zone, M_NOWAIT | M_ZERO) #define ata_free_composite(composite) uma_zfree(ata_composite_zone, composite) MALLOC_DECLARE(M_ATA); /* misc newbus defines */ #define GRANDPARENT(dev) device_get_parent(device_get_parent(dev)) /* macros to hide busspace uglyness */ #define ATA_INB(res, offset) \ bus_space_read_1(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset)) #define ATA_INW(res, offset) \ bus_space_read_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset)) #define ATA_INL(res, offset) \ bus_space_read_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset)) #define ATA_INSW(res, offset, addr, count) \ bus_space_read_multi_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_INSW_STRM(res, offset, addr, count) \ bus_space_read_multi_stream_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_INSL(res, offset, addr, count) \ bus_space_read_multi_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_INSL_STRM(res, offset, addr, count) \ bus_space_read_multi_stream_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_OUTB(res, offset, value) \ bus_space_write_1(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset), (value)) #define ATA_OUTW(res, offset, value) \ bus_space_write_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset), (value)) #define ATA_OUTL(res, offset, value) \ bus_space_write_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), (offset), (value)) #define ATA_OUTSW(res, offset, addr, count) \ bus_space_write_multi_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_OUTSW_STRM(res, offset, addr, count) \ bus_space_write_multi_stream_2(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_OUTSL(res, offset, addr, count) \ bus_space_write_multi_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_OUTSL_STRM(res, offset, addr, count) \ bus_space_write_multi_stream_4(rman_get_bustag((res)), \ rman_get_bushandle((res)), \ (offset), (addr), (count)) #define ATA_IDX_INB(ch, idx) \ ATA_INB(ch->r_io[idx].res, ch->r_io[idx].offset) #define ATA_IDX_INW(ch, idx) \ ATA_INW(ch->r_io[idx].res, ch->r_io[idx].offset) #define ATA_IDX_INL(ch, idx) \ ATA_INL(ch->r_io[idx].res, ch->r_io[idx].offset) #define ATA_IDX_INSW(ch, idx, addr, count) \ ATA_INSW(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_INSW_STRM(ch, idx, addr, count) \ ATA_INSW_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_INSL(ch, idx, addr, count) \ ATA_INSL(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_INSL_STRM(ch, idx, addr, count) \ ATA_INSL_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_OUTB(ch, idx, value) \ ATA_OUTB(ch->r_io[idx].res, ch->r_io[idx].offset, value) #define ATA_IDX_OUTW(ch, idx, value) \ ATA_OUTW(ch->r_io[idx].res, ch->r_io[idx].offset, value) #define ATA_IDX_OUTL(ch, idx, value) \ ATA_OUTL(ch->r_io[idx].res, ch->r_io[idx].offset, value) #define ATA_IDX_OUTSW(ch, idx, addr, count) \ ATA_OUTSW(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_OUTSW_STRM(ch, idx, addr, count) \ ATA_OUTSW_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_OUTSL(ch, idx, addr, count) \ ATA_OUTSL(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) #define ATA_IDX_OUTSL_STRM(ch, idx, addr, count) \ ATA_OUTSL_STRM(ch->r_io[idx].res, ch->r_io[idx].offset, addr, count) Index: head/sys/dev/ata/ata-disk.c =================================================================== --- head/sys/dev/ata/ata-disk.c (revision 157328) +++ head/sys/dev/ata/ata-disk.c (revision 157329) @@ -1,420 +1,427 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ad_init(device_t); static void ad_done(struct ata_request *); static void ad_describe(device_t dev); static int ad_version(u_int16_t); static disk_strategy_t ad_strategy; static disk_ioctl_t ad_ioctl; static dumper_t ad_dump; /* local vars */ static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver"); static int ad_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (!(atadev->param.config & ATA_PROTO_ATAPI) || - (atadev->param.config == ATA_CFA_MAGIC)) + (atadev->param.config == ATA_CFA_MAGIC1) || + (atadev->param.config == ATA_CFA_MAGIC2)) return 0; else return ENXIO; } static int ad_attach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp; u_int32_t lbasize; u_int64_t lbasize48; /* check that we have a virgin disk to attach */ if (device_get_ivars(dev)) return EEXIST; if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } device_set_ivars(dev, adp); if (atadev->param.atavalid & ATA_FLAG_54_58) { adp->heads = atadev->param.current_heads; adp->sectors = atadev->param.current_sectors; adp->total_secs = (u_int32_t)atadev->param.current_size_1 | ((u_int32_t)atadev->param.current_size_2 << 16); } else { adp->heads = atadev->param.heads; adp->sectors = atadev->param.sectors; adp->total_secs = atadev->param.cylinders * adp->heads * adp->sectors; } lbasize = (u_int32_t)atadev->param.lba_size_1 | ((u_int32_t)atadev->param.lba_size_2 << 16); /* does this device need oldstyle CHS addressing */ if (!ad_version(atadev->param.version_major) || !lbasize) atadev->flags |= ATA_D_USE_CHS; /* use the 28bit LBA size if valid or bigger than the CHS mapping */ if (atadev->param.cylinders == 16383 || adp->total_secs < lbasize) adp->total_secs = lbasize; /* use the 48bit LBA size if valid */ lbasize48 = ((u_int64_t)atadev->param.lba_size48_1) | ((u_int64_t)atadev->param.lba_size48_2 << 16) | ((u_int64_t)atadev->param.lba_size48_3 << 32) | ((u_int64_t)atadev->param.lba_size48_4 << 48); if ((atadev->param.support.command2 & ATA_SUPPORT_ADDRESS48) && lbasize48 > ATA_MAX_28BIT_LBA) adp->total_secs = lbasize48; /* init device parameters */ ad_init(dev); /* announce we are here */ ad_describe(dev); /* create the disk device */ adp->disk = disk_alloc(); adp->disk->d_strategy = ad_strategy; adp->disk->d_ioctl = ad_ioctl; adp->disk->d_dump = ad_dump; adp->disk->d_name = "ad"; adp->disk->d_drv1 = dev; if (ch->dma) adp->disk->d_maxsize = ch->dma->max_iosize; else adp->disk->d_maxsize = DFLTPHYS; adp->disk->d_sectorsize = DEV_BSIZE; adp->disk->d_mediasize = DEV_BSIZE * (off_t)adp->total_secs; adp->disk->d_fwsectors = adp->sectors; adp->disk->d_fwheads = adp->heads; adp->disk->d_unit = device_get_unit(dev); disk_create(adp->disk, DISK_VERSION); device_add_child(dev, "subdisk", device_get_unit(dev)); bus_generic_attach(dev); return 0; } static int ad_detach(device_t dev) { struct ad_softc *adp = device_get_ivars(dev); device_t *children; int nchildren, i; /* check that we have a valid disk to detach */ if (!device_get_ivars(dev)) return ENXIO; /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* detroy disk from the system so we dont get any further requests */ disk_destroy(adp->disk); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(adp, M_AD); return 0; } static void ad_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0); } static int ad_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); /* if detach pending, return error */ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) { return 1; } ad_init(dev); return 0; } static void ad_strategy(struct bio *bp) { device_t dev = bp->bio_disk->d_drv1; struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in start\n"); biofinish(bp, NULL, ENOMEM); return; } /* setup request */ request->dev = dev; request->bio = bp; request->callback = ad_done; request->timeout = 5; request->retries = 2; request->data = bp->bio_data; request->bytecount = bp->bio_bcount; request->u.ata.lba = bp->bio_pblkno; request->u.ata.count = request->bytecount / DEV_BSIZE; request->transfersize = min(bp->bio_bcount, atadev->max_iosize); switch (bp->bio_cmd) { case BIO_READ: request->flags = ATA_R_READ; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; break; case BIO_WRITE: request->flags = ATA_R_WRITE; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; break; default: device_printf(dev, "FAILURE - unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } request->flags |= ATA_R_ORDERED; ata_queue_request(request); } static void ad_done(struct ata_request *request) { struct bio *bp = request->bio; /* finish up transfer */ if ((bp->bio_error = request->result)) bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount - request->donecount; biodone(bp); ata_free_request(request); } static int ad_ioctl(struct disk *disk, u_long cmd, void *data, int flag, struct thread *td) { return ata_device_ioctl(disk->d_drv1, cmd, data); } static int ad_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp = arg; struct bio bp; /* length zero is special and really means flush buffers to media */ - if (!length) - return ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0); + if (!length) { + struct ata_device *atadev = device_get_softc(dp->d_drv1); + int error = 0; + + if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) + error = ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0); + return error; + } bzero(&bp, sizeof(struct bio)); bp.bio_disk = dp; bp.bio_pblkno = offset / DEV_BSIZE; bp.bio_bcount = length; bp.bio_data = virtual; bp.bio_cmd = BIO_WRITE; ad_strategy(&bp); return bp.bio_error; } static void ad_init(device_t dev) { struct ata_device *atadev = device_get_softc(dev); ATA_SETMODE(device_get_parent(dev), dev); /* enable readahead caching */ if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); /* enable write caching if supported and configured */ if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) { if (ata_wc) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); else ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0); } /* use multiple sectors/interrupt if device supports it */ if (ad_version(atadev->param.version_major)) { int secsperint = max(1, min(atadev->param.sectors_intr, 16)); if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint)) atadev->max_iosize = secsperint * DEV_BSIZE; } else atadev->max_iosize = DEV_BSIZE; } void ad_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp = device_get_ivars(dev); u_int8_t *marker, vendor[64], product[64]; /* try to seperate the ATA model string into vendor and model parts */ if ((marker = index(atadev->param.model, ' ')) || (marker = index(atadev->param.model, '-'))) { int len = (marker - atadev->param.model); strncpy(vendor, atadev->param.model, len); vendor[len++] = 0; strcat(vendor, " "); strncpy(product, atadev->param.model + len, 40 - len); vendor[40 - len] = 0; } else { if (!strncmp(atadev->param.model, "ST", 2)) strcpy(vendor, "Seagate "); else strcpy(vendor, ""); strncpy(product, atadev->param.model, 40); } device_printf(dev, "%juMB <%s%s %.8s> at ata%d-%s %s%s\n", adp->total_secs / (1048576 / DEV_BSIZE), vendor, product, atadev->param.revision, device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave", (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", ata_mode2str(atadev->mode)); if (bootverbose) { device_printf(dev, "%ju sectors [%juC/%dH/%dS] " "%d sectors/interrupt %d depth queue\n", adp->total_secs, adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE, adp->num_tags + 1); } } static int ad_version(u_int16_t version) { int bit; if (version == 0xffff) return 0; for (bit = 15; bit >= 0; bit--) if (version & (1< * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ata_completed(void *, int); static void ata_sort_queue(struct ata_channel *ch, struct ata_request *request); static char *ata_skey2str(u_int8_t); void ata_queue_request(struct ata_request *request) { - struct ata_channel *ch = device_get_softc(device_get_parent(request->dev)); + struct ata_channel *ch; - /* mark request as virgin (this might be a ATA_R_REQUEUE) */ + /* treat request as virgin (this might be an ATA_R_REQUEUE) */ request->result = request->status = request->error = 0; - request->parent = device_get_parent(request->dev); - callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); + /* check that that the device is still valid */ + if (!(request->parent = device_get_parent(request->dev))) { + request->result = ENXIO; + if (request->callback) + (request->callback)(request); + return; + } + ch = device_get_softc(request->parent); + callout_init_mtx(&request->callout, &ch->state_mtx, CALLOUT_RETURNUNLOCKED); if (!request->callback && !(request->flags & ATA_R_REQUEUE)) sema_init(&request->done, 0, "ATA request done"); /* in ATA_STALL_QUEUE state we call HW directly */ if ((ch->state & ATA_STALL_QUEUE) && (request->flags & ATA_R_CONTROL)) { mtx_lock(&ch->state_mtx); ch->running = request; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; if (!request->callback) sema_destroy(&request->done); mtx_unlock(&ch->state_mtx); return; } mtx_unlock(&ch->state_mtx); } /* otherwise put request on the locked queue at the specified location */ else { mtx_lock(&ch->queue_mtx); if (request->flags & ATA_R_AT_HEAD) TAILQ_INSERT_HEAD(&ch->ata_queue, request, chain); else if (request->flags & ATA_R_ORDERED) ata_sort_queue(ch, request); else TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain); mtx_unlock(&ch->queue_mtx); ATA_DEBUG_RQ(request, "queued"); ata_start(ch->dev); } /* if this is a requeued request callback/sleep we're done */ if (request->flags & ATA_R_REQUEUE) return; /* if this is not a callback wait until request is completed */ if (!request->callback) { ATA_DEBUG_RQ(request, "wait for completition"); if (!dumping && sema_timedwait(&request->done, request->timeout * hz * 4)) { device_printf(request->dev, "WARNING - %s taskqueue timeout " "- completing request directly\n", ata_cmd2str(request)); request->flags |= ATA_R_DANGER1; ata_completed(request, 0); } sema_destroy(&request->done); } } int ata_controlcmd(device_t dev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count) { struct ata_request *request = ata_alloc_request(); int error = ENOMEM; if (request) { request->dev = dev; request->u.ata.command = command; request->u.ata.lba = lba; request->u.ata.count = count; request->u.ata.feature = feature; request->flags = ATA_R_CONTROL; request->timeout = 1; request->retries = 0; ata_queue_request(request); error = request->result; ata_free_request(request); } return error; } int ata_atapicmd(device_t dev, u_int8_t *ccb, caddr_t data, int count, int flags, int timeout) { struct ata_request *request = ata_alloc_request(); struct ata_device *atadev = device_get_softc(dev); int error = ENOMEM; if (request) { request->dev = dev; if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) bcopy(ccb, request->u.atapi.ccb, 12); else bcopy(ccb, request->u.atapi.ccb, 16); request->data = data; request->bytecount = count; request->transfersize = min(request->bytecount, 65534); request->flags = flags | ATA_R_ATAPI; request->timeout = timeout; request->retries = 0; ata_queue_request(request); error = request->result; ata_free_request(request); } return error; } void ata_start(device_t dev) { struct ata_channel *ch = device_get_softc(dev); struct ata_request *request; struct ata_composite *cptr; int dependencies = 0; /* if we have a request on the queue try to get it running */ mtx_lock(&ch->queue_mtx); if ((request = TAILQ_FIRST(&ch->ata_queue))) { /* we need the locking function to get the lock for this channel */ if (ATA_LOCKING(dev, ATA_LF_LOCK) == ch->unit) { /* check for composite dependencies */ if ((cptr = request->composite)) { mtx_lock(&cptr->lock); if ((request->flags & ATA_R_WRITE) && (cptr->wr_depend & cptr->rd_done) != cptr->wr_depend) { dependencies = 1; } mtx_unlock(&cptr->lock); } /* check we are in the right state and has no dependencies */ mtx_lock(&ch->state_mtx); if (ch->state == ATA_IDLE && !dependencies) { ATA_DEBUG_RQ(request, "starting"); TAILQ_REMOVE(&ch->ata_queue, request, chain); ch->running = request; ch->state = ATA_ACTIVE; /* if we are the freezing point release it */ if (ch->freezepoint == request) ch->freezepoint = NULL; if (ch->hw.begin_transaction(request) == ATA_OP_FINISHED) { ch->running = NULL; ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); ATA_LOCKING(dev, ATA_LF_UNLOCK); ata_finish(request); return; } if (dumping) { mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); while (!ata_interrupt(ch)) DELAY(10); return; } } mtx_unlock(&ch->state_mtx); } } mtx_unlock(&ch->queue_mtx); } void ata_finish(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); /* * if in ATA_STALL_QUEUE state or request has ATA_R_DIRECT flags set * we need to call ata_complete() directly here (no taskqueue involvement) */ if (dumping || (ch->state & ATA_STALL_QUEUE) || (request->flags & ATA_R_DIRECT)) { ATA_DEBUG_RQ(request, "finish directly"); ata_completed(request, 0); } else { /* put request on the proper taskqueue for completition */ if (request->bio && !(request->flags & (ATA_R_THREAD | ATA_R_TIMEOUT))){ ATA_DEBUG_RQ(request, "finish bio_taskqueue"); bio_taskqueue(request->bio, (bio_task_t *)ata_completed, request); } else { TASK_INIT(&request->task, 0, ata_completed, request); ATA_DEBUG_RQ(request, "finish taskqueue_swi"); taskqueue_enqueue(taskqueue_swi, &request->task); } } } static void ata_completed(void *context, int dummy) { struct ata_request *request = (struct ata_request *)context; struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); struct ata_composite *composite; if (request->flags & ATA_R_DANGER2) { device_printf(request->dev, "WARNING - %s freeing taskqueue zombie request\n", ata_cmd2str(request)); request->flags &= ~(ATA_R_DANGER1 | ATA_R_DANGER2); ata_free_request(request); return; } if (request->flags & ATA_R_DANGER1) request->flags |= ATA_R_DANGER2 ATA_DEBUG_RQ(request, "completed entered"); /* if we had a timeout, reinit channel and deal with the falldown */ if (request->flags & ATA_R_TIMEOUT) { /* * if the channel is still present and * reinit succeeds and * the device doesn't get detached and * there are retries left we reinject this request */ if (ch && !ata_reinit(ch->dev) && !request->result && (request->retries-- > 0)) { if (!(request->flags & ATA_R_QUIET)) { device_printf(request->dev, "TIMEOUT - %s retrying (%d retr%s left)", ata_cmd2str(request), request->retries, request->retries == 1 ? "y" : "ies"); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } request->flags &= ~(ATA_R_TIMEOUT | ATA_R_DEBUG); request->flags |= (ATA_R_AT_HEAD | ATA_R_REQUEUE); ATA_DEBUG_RQ(request, "completed reinject"); ata_queue_request(request); return; } /* ran out of good intentions so finish with error */ if (!request->result) { if (!(request->flags & ATA_R_QUIET)) { if (request->dev) { device_printf(request->dev, "FAILURE - %s timed out", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } } request->result = EIO; } } else if (!(request->flags & ATA_R_ATAPI) ){ /* if this is a soft ECC error warn about it */ /* XXX SOS we could do WARF here */ if ((request->status & (ATA_S_CORR | ATA_S_ERROR)) == ATA_S_CORR) { device_printf(request->dev, "WARNING - %s soft error (ECC corrected)", ata_cmd2str(request)); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } /* if this is a UDMA CRC error we reinject if there are retries left */ if (request->flags & ATA_R_DMA && request->error & ATA_E_ICRC) { if (request->retries-- > 0) { device_printf(request->dev, "WARNING - %s UDMA ICRC error (retrying request)", 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_AT_HEAD | ATA_R_REQUEUE); ata_queue_request(request); return; } } } switch (request->flags & ATA_R_ATAPI) { /* ATA errors */ default: if (!request->result && request->status & ATA_S_ERROR) { if (!(request->flags & ATA_R_QUIET)) { device_printf(request->dev, "FAILURE - %s status=%b error=%b", ata_cmd2str(request), request->status, "\20\10BUSY\7READY\6DMA_READY" "\5DSC\4DRQ\3CORRECTABLE\2INDEX\1ERROR", request->error, "\20\10ICRC\7UNCORRECTABLE" "\6MEDIA_CHANGED\5NID_NOT_FOUND" "\4MEDIA_CHANGE_REQEST" "\3ABORTED\2NO_MEDIA\1ILLEGAL_LENGTH"); if ((request->flags & ATA_R_DMA) && (request->dmastat & ATA_BMSTAT_ERROR)) printf(" dma=0x%02x", request->dmastat); if (!(request->flags & (ATA_R_ATAPI | ATA_R_CONTROL))) printf(" LBA=%ju", request->u.ata.lba); printf("\n"); } request->result = EIO; } break; /* ATAPI errors */ case ATA_R_ATAPI: /* skip if result already set */ if (request->result) break; /* if we have a sensekey -> request sense from device */ - if (request->error & ATA_SK_MASK && - request->u.atapi.ccb[0] != ATAPI_REQUEST_SENSE) { + if ((request->error & ATA_E_ATAPI_SENSE_MASK) && + (request->u.atapi.ccb[0] != ATAPI_REQUEST_SENSE)) { static u_int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0, sizeof(struct atapi_sense), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; - request->u.atapi.sense_key = request->error; - request->u.atapi.sense_cmd = request->u.atapi.ccb[0]; + request->u.atapi.saved_cmd = request->u.atapi.ccb[0]; bcopy(ccb, request->u.atapi.ccb, 16); - request->data = (caddr_t)&request->u.atapi.sense_data; + request->data = (caddr_t)&request->u.atapi.sense; request->bytecount = sizeof(struct atapi_sense); request->donecount = 0; request->transfersize = sizeof(struct atapi_sense); request->timeout = 5; request->flags &= (ATA_R_ATAPI | ATA_R_QUIET); request->flags |= (ATA_R_READ | ATA_R_AT_HEAD | ATA_R_REQUEUE); ATA_DEBUG_RQ(request, "autoissue request sense"); ata_queue_request(request); return; } - switch (request->u.atapi.sense_key & ATA_SK_MASK) { - case ATA_SK_RECOVERED_ERROR: + switch (request->u.atapi.sense.key & ATA_SENSE_KEY_MASK) { + case ATA_SENSE_RECOVERED_ERROR: device_printf(request->dev, "WARNING - %s recovered error\n", ata_cmd2str(request)); /* FALLTHROUGH */ - case ATA_SK_NO_SENSE: + case ATA_SENSE_NO_SENSE: request->result = 0; break; - case ATA_SK_NOT_READY: + case ATA_SENSE_NOT_READY: request->result = EBUSY; break; - case ATA_SK_UNIT_ATTENTION: + case ATA_SENSE_UNIT_ATTENTION: atadev->flags |= ATA_D_MEDIA_CHANGED; request->result = EIO; break; default: request->result = EIO; if (request->flags & ATA_R_QUIET) break; device_printf(request->dev, "FAILURE - %s %s asc=0x%02x ascq=0x%02x ", ata_cmd2str(request), ata_skey2str( - (request->u.atapi.sense_key & ATA_SK_MASK) >> 4), - request->u.atapi.sense_data.asc, - request->u.atapi.sense_data.ascq); - if (request->u.atapi.sense_data.sksv) - printf("sks=0x%02x 0x%02x 0x%02x ", - request->u.atapi.sense_data.sk_specific, - request->u.atapi.sense_data.sk_specific1, - request->u.atapi.sense_data.sk_specific2); - printf("error=%b\n", - (request->u.atapi.sense_key & ATA_E_MASK), - "\20\4MEDIA_CHANGE_REQUEST\3ABORTED" - "\2NO_MEDIA\1ILLEGAL_LENGTH"); + (request->u.atapi.sense.key & ATA_SENSE_KEY_MASK)), + request->u.atapi.sense.asc, + request->u.atapi.sense.ascq); + if (request->u.atapi.sense.specific & ATA_SENSE_SPEC_VALID) + printf("sks=0x%02x 0x%02x 0x%02x\n", + request->u.atapi.sense.specific & ATA_SENSE_SPEC_MASK, + request->u.atapi.sense.specific1, + request->u.atapi.sense.specific2); } - if ((request->u.atapi.sense_key ? - request->u.atapi.sense_key : request->error) & ATA_E_MASK) + if ((request->u.atapi.sense.key & ATA_SENSE_KEY_MASK ? + request->u.atapi.sense.key & ATA_SENSE_KEY_MASK : + request->error)) request->result = EIO; } ATA_DEBUG_RQ(request, "completed callback/wakeup"); /* if we are part of a composite operation we need to maintain progress */ if ((composite = request->composite)) { int index = 0; mtx_lock(&composite->lock); /* update whats done */ if (request->flags & ATA_R_READ) composite->rd_done |= (1 << request->this); if (request->flags & ATA_R_WRITE) composite->wr_done |= (1 << request->this); /* find ready to go dependencies */ if (composite->wr_depend && (composite->rd_done & composite->wr_depend)==composite->wr_depend && (composite->wr_needed & (~composite->wr_done))) { index = composite->wr_needed & ~composite->wr_done; } mtx_unlock(&composite->lock); /* if we have any ready candidates kick them off */ if (index) { int bit; for (bit = 0; bit < MAX_COMPOSITES; bit++) { if (index & (1 << bit)) ata_start(device_get_parent(composite->request[bit]->dev)); } } } /* get results back to the initiator for this request */ if (request->callback) (request->callback)(request); else sema_post(&request->done); /* only call ata_start if channel is present */ if (ch) ata_start(ch->dev); } void ata_timeout(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); //request->flags |= ATA_R_DEBUG; ATA_DEBUG_RQ(request, "timeout"); /* * if we have an ATA_ACTIVE request running, we flag the request * ATA_R_TIMEOUT so ata_finish will handle it correctly * also NULL out the running request so we wont loose * the race with an eventual interrupt arriving late */ if (ch->state == ATA_ACTIVE) { request->flags |= ATA_R_TIMEOUT; mtx_unlock(&ch->state_mtx); ATA_LOCKING(ch->dev, ATA_LF_UNLOCK); ata_finish(request); } else { mtx_unlock(&ch->state_mtx); } } void ata_fail_requests(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_request *request, *tmp; TAILQ_HEAD(, ata_request) fail_requests; TAILQ_INIT(&fail_requests); /* grap all channel locks to avoid races */ mtx_lock(&ch->queue_mtx); mtx_lock(&ch->state_mtx); /* do we have any running request to care about ? */ if ((request = ch->running) && (!dev || request->dev == dev)) { callout_stop(&request->callout); ch->running = NULL; request->result = ENXIO; TAILQ_INSERT_TAIL(&fail_requests, request, chain); } /* fail all requests queued on this channel for device dev if !NULL */ TAILQ_FOREACH_SAFE(request, &ch->ata_queue, chain, tmp) { if (!dev || request->dev == dev) { TAILQ_REMOVE(&ch->ata_queue, request, chain); request->result = ENXIO; TAILQ_INSERT_TAIL(&fail_requests, request, chain); } } mtx_unlock(&ch->state_mtx); mtx_unlock(&ch->queue_mtx); /* finish up all requests collected above */ TAILQ_FOREACH_SAFE(request, &fail_requests, chain, tmp) { TAILQ_REMOVE(&fail_requests, request, chain); ata_finish(request); } } static u_int64_t ata_get_lba(struct ata_request *request) { if (request->flags & ATA_R_ATAPI) { switch (request->u.atapi.ccb[0]) { case ATAPI_READ_BIG: case ATAPI_WRITE_BIG: case ATAPI_READ_CD: return (request->u.atapi.ccb[5]) | (request->u.atapi.ccb[4]<<8) | (request->u.atapi.ccb[3]<<16)|(request->u.atapi.ccb[2]<<24); case ATAPI_READ: case ATAPI_WRITE: return (request->u.atapi.ccb[4]) | (request->u.atapi.ccb[3]<<8) | (request->u.atapi.ccb[2]<<16); default: return 0; } } else return request->u.ata.lba; } static void ata_sort_queue(struct ata_channel *ch, struct ata_request *request) { struct ata_request *this, *next; this = TAILQ_FIRST(&ch->ata_queue); /* if the queue is empty just insert */ if (!this) { if (request->composite) ch->freezepoint = request; TAILQ_INSERT_TAIL(&ch->ata_queue, request, chain); return; } /* dont sort frozen parts of the queue */ if (ch->freezepoint) this = ch->freezepoint; /* if position is less than head we add after tipping point */ if (ata_get_lba(request) < ata_get_lba(this)) { while ((next = TAILQ_NEXT(this, chain))) { /* have we reached the tipping point */ if (ata_get_lba(next) < ata_get_lba(this)) { /* sort the insert */ do { if (ata_get_lba(request) < ata_get_lba(next)) break; this = next; } while ((next = TAILQ_NEXT(this, chain))); break; } this = next; } } /* we are after head so sort the insert before tipping point */ else { while ((next = TAILQ_NEXT(this, chain))) { if (ata_get_lba(next) < ata_get_lba(this) || ata_get_lba(request) < ata_get_lba(next)) break; this = next; } } if (request->composite) ch->freezepoint = request; TAILQ_INSERT_AFTER(&ch->ata_queue, this, request, chain); } char * ata_cmd2str(struct ata_request *request) { static char buffer[20]; if (request->flags & ATA_R_ATAPI) { - switch (request->u.atapi.sense_key ? - request->u.atapi.sense_cmd : request->u.atapi.ccb[0]) { + switch (request->u.atapi.sense.key ? + request->u.atapi.saved_cmd : request->u.atapi.ccb[0]) { case 0x00: return ("TEST_UNIT_READY"); case 0x01: return ("REZERO"); case 0x03: return ("REQUEST_SENSE"); case 0x04: return ("FORMAT"); case 0x08: return ("READ"); case 0x0a: return ("WRITE"); case 0x10: return ("WEOF"); case 0x11: return ("SPACE"); case 0x12: return ("INQUIRY"); case 0x15: return ("MODE_SELECT"); case 0x19: return ("ERASE"); case 0x1a: return ("MODE_SENSE"); case 0x1b: return ("START_STOP"); case 0x1e: return ("PREVENT_ALLOW"); case 0x23: return ("ATAPI_READ_FORMAT_CAPACITIES"); case 0x25: return ("READ_CAPACITY"); case 0x28: return ("READ_BIG"); case 0x2a: return ("WRITE_BIG"); case 0x2b: return ("LOCATE"); case 0x34: return ("READ_POSITION"); case 0x35: return ("SYNCHRONIZE_CACHE"); case 0x3b: return ("WRITE_BUFFER"); case 0x3c: return ("READ_BUFFER"); case 0x42: return ("READ_SUBCHANNEL"); case 0x43: return ("READ_TOC"); case 0x45: return ("PLAY_10"); case 0x47: return ("PLAY_MSF"); case 0x48: return ("PLAY_TRACK"); case 0x4b: return ("PAUSE"); case 0x51: return ("READ_DISK_INFO"); case 0x52: return ("READ_TRACK_INFO"); case 0x53: return ("RESERVE_TRACK"); case 0x54: return ("SEND_OPC_INFO"); case 0x55: return ("MODE_SELECT_BIG"); case 0x58: return ("REPAIR_TRACK"); case 0x59: return ("READ_MASTER_CUE"); case 0x5a: return ("MODE_SENSE_BIG"); case 0x5b: return ("CLOSE_TRACK/SESSION"); case 0x5c: return ("READ_BUFFER_CAPACITY"); case 0x5d: return ("SEND_CUE_SHEET"); case 0x96: return ("SERVICE_ACTION_IN"); case 0xa1: return ("BLANK_CMD"); case 0xa3: return ("SEND_KEY"); case 0xa4: return ("REPORT_KEY"); case 0xa5: return ("PLAY_12"); case 0xa6: return ("LOAD_UNLOAD"); case 0xad: return ("READ_DVD_STRUCTURE"); case 0xb4: return ("PLAY_CD"); case 0xbb: return ("SET_SPEED"); case 0xbd: return ("MECH_STATUS"); case 0xbe: return ("READ_CD"); case 0xff: return ("POLL_DSC"); } } else { switch (request->u.ata.command) { case 0x00: return ("NOP"); case 0x08: return ("DEVICE_RESET"); case 0x20: return ("READ"); case 0x24: return ("READ48"); case 0x25: return ("READ_DMA48"); case 0x26: return ("READ_DMA_QUEUED48"); case 0x29: return ("READ_MUL48"); case 0x30: return ("WRITE"); case 0x34: return ("WRITE48"); case 0x35: return ("WRITE_DMA48"); case 0x36: return ("WRITE_DMA_QUEUED48"); case 0x39: return ("WRITE_MUL48"); case 0x70: return ("SEEK"); case 0xa0: return ("PACKET_CMD"); case 0xa1: return ("ATAPI_IDENTIFY"); case 0xa2: return ("SERVICE"); case 0xc0: return ("CFA ERASE"); case 0xc4: return ("READ_MUL"); case 0xc5: return ("WRITE_MUL"); case 0xc6: return ("SET_MULTI"); case 0xc7: return ("READ_DMA_QUEUED"); case 0xc8: return ("READ_DMA"); case 0xca: return ("WRITE_DMA"); case 0xcc: return ("WRITE_DMA_QUEUED"); case 0xe6: return ("SLEEP"); case 0xe7: return ("FLUSHCACHE"); case 0xea: return ("FLUSHCACHE48"); case 0xec: return ("ATA_IDENTIFY"); case 0xef: switch (request->u.ata.feature) { case 0x03: return ("SETFEATURES SET TRANSFER MODE"); case 0x02: return ("SETFEATURES ENABLE WCACHE"); case 0x82: return ("SETFEATURES DISABLE WCACHE"); case 0xaa: return ("SETFEATURES ENABLE RCACHE"); case 0x55: return ("SETFEATURES DISABLE RCACHE"); } sprintf(buffer, "SETFEATURES 0x%02x", request->u.ata.feature); return buffer; } } sprintf(buffer, "unknown CMD (0x%02x)", request->u.ata.command); return buffer; } static char * ata_skey2str(u_int8_t skey) { switch (skey) { case 0x00: return ("NO SENSE"); case 0x01: return ("RECOVERED ERROR"); case 0x02: return ("NOT READY"); case 0x03: return ("MEDIUM ERROR"); case 0x04: return ("HARDWARE ERROR"); case 0x05: return ("ILLEGAL REQUEST"); case 0x06: return ("UNIT ATTENTION"); case 0x07: return ("DATA PROTECT"); case 0x08: return ("BLANK CHECK"); case 0x09: return ("VENDOR SPECIFIC"); case 0x0a: return ("COPY ABORTED"); case 0x0b: return ("ABORTED COMMAND"); case 0x0c: return ("EQUAL"); case 0x0d: return ("VOLUME OVERFLOW"); case 0x0e: return ("MISCOMPARE"); case 0x0f: return ("RESERVED"); default: return("UNKNOWN"); } } Index: head/sys/dev/ata/ata-usb.c =================================================================== --- head/sys/dev/ata/ata-usb.c (revision 157328) +++ head/sys/dev/ata/ata-usb.c (revision 157329) @@ -1,979 +1,975 @@ /*- * Copyright (c) 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* Command Block Wrapper */ struct bbb_cbw { u_int8_t signature[4]; #define CBWSIGNATURE 0x43425355 u_int8_t tag[4]; u_int8_t transfer_length[4]; u_int8_t flags; #define CBWFLAGS_OUT 0x00 #define CBWFLAGS_IN 0x80 u_int8_t lun; u_int8_t length; #define CBWCDBLENGTH 16 u_int8_t cdb[CBWCDBLENGTH]; }; /* Command Status Wrapper */ struct bbb_csw { u_int8_t signature[4]; #define CSWSIGNATURE 0x53425355 u_int8_t tag[4]; u_int8_t residue[4]; u_int8_t status; #define CSWSTATUS_GOOD 0x0 #define CSWSTATUS_FAILED 0x1 #define CSWSTATUS_PHASE 0x2 }; /* USB-ATA 'controller' softc */ struct atausb_softc { USBBASEDEVICE dev; /* base device */ usbd_interface_handle iface; /* interface */ int ifaceno; /* interface number */ u_int8_t bulkin; /* endpoint address's */ u_int8_t bulkout; u_int8_t bulkirq; usbd_pipe_handle bulkin_pipe; /* pipe handle's */ usbd_pipe_handle bulkout_pipe; usbd_pipe_handle bulkirq_pipe; int maxlun; int timeout; struct ata_request *ata_request; usb_device_request_t usb_request; struct bbb_cbw cbw; struct bbb_csw csw; #define ATAUSB_T_BBB_CBW 0 #define ATAUSB_T_BBB_DATA 1 #define ATAUSB_T_BBB_DCLEAR 2 #define ATAUSB_T_BBB_CSW1 3 #define ATAUSB_T_BBB_CSW2 4 #define ATAUSB_T_BBB_SCLEAR 5 #define ATAUSB_T_BBB_RESET1 6 #define ATAUSB_T_BBB_RESET2 7 #define ATAUSB_T_BBB_RESET3 8 #define ATAUSB_T_MAX 9 usbd_xfer_handle transfer[ATAUSB_T_MAX]; int state; #define ATAUSB_S_ATTACH 0 #define ATAUSB_S_IDLE 1 #define ATAUSB_S_BBB_COMMAND 2 #define ATAUSB_S_BBB_DATA 3 #define ATAUSB_S_BBB_DCLEAR 4 #define ATAUSB_S_BBB_STATUS1 5 #define ATAUSB_S_BBB_SCLEAR 6 #define ATAUSB_S_BBB_STATUS2 7 #define ATAUSB_S_BBB_RESET1 8 #define ATAUSB_S_BBB_RESET2 9 #define ATAUSB_S_BBB_RESET3 10 #define ATAUSB_S_DETACH 11 struct mtx locked_mtx; struct ata_channel *locked_ch; struct ata_channel *restart_ch; }; static int atausbdebug = 0; /* prototypes*/ static usbd_status atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe, void *buffer, int buflen, int flags, usbd_xfer_handle xfer); static usbd_status atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev, usb_device_request_t *req, void *buffer, int buflen, int flags, usbd_xfer_handle xfer); static void atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt, usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer); static void atausb_bbb_reset(struct atausb_softc *sc); static int atausb_bbb_start(struct ata_request *request); static void atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err); int ata_usbchannel_begin_transaction(struct ata_request *request); int ata_usbchannel_end_transaction(struct ata_request *request); /* * USB frontend part */ USB_DECLARE_DRIVER(atausb); DRIVER_MODULE(atausb, uhub, atausb_driver, atausb_devclass, 0, 0); MODULE_VERSION(atausb, 1); static int atausb_match(device_t dev) { struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; if (uaa->iface == NULL) return UMATCH_NONE; id = usbd_get_interface_descriptor(uaa->iface); if (!id || id->bInterfaceClass != UICLASS_MASS) return UMATCH_NONE; switch (id->bInterfaceSubClass) { case UISUBCLASS_QIC157: case UISUBCLASS_RBC: case UISUBCLASS_SCSI: case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: case UISUBCLASS_UFI: switch (id->bInterfaceProtocol) { case UIPROTO_MASS_CBI: case UIPROTO_MASS_CBI_I: case UIPROTO_MASS_BBB: case UIPROTO_MASS_BBB_OLD: return UMATCH_IFACECLASS_IFACESUBCLASS_IFACEPROTO; default: return UMATCH_IFACECLASS_IFACESUBCLASS; } break; default: return UMATCH_IFACECLASS; } } static int atausb_attach(device_t dev) { struct atausb_softc *sc = device_get_softc(dev); struct usb_attach_arg *uaa = device_get_ivars(dev); usb_interface_descriptor_t *id; usb_endpoint_descriptor_t *ed; usbd_device_handle udev; usb_device_request_t request; char devinfo[1024], *proto, *subclass; u_int8_t maxlun; int err, i; sc->dev = dev; usbd_devinfo(uaa->device, 0, devinfo); device_set_desc_copy(dev, devinfo); sc->bulkin = sc->bulkout = sc->bulkirq = -1; sc->bulkin_pipe = sc->bulkout_pipe= sc->bulkirq_pipe = NULL; sc->iface = uaa->iface; sc->ifaceno = uaa->ifaceno; sc->maxlun = 0; sc->timeout = 5000; sc->locked_ch = NULL; sc->restart_ch = NULL; mtx_init(&sc->locked_mtx, "ATAUSB lock", NULL, MTX_DEF); id = usbd_get_interface_descriptor(sc->iface); switch (id->bInterfaceProtocol) { case UIPROTO_MASS_BBB: case UIPROTO_MASS_BBB_OLD: proto = "Bulk-Only"; break; case UIPROTO_MASS_CBI: proto = "CBI"; break; case UIPROTO_MASS_CBI_I: proto = "CBI with CCI"; break; default: proto = "Unknown"; } switch (id->bInterfaceSubClass) { case UISUBCLASS_RBC: subclass = "RBC"; break; case UISUBCLASS_QIC157: case UISUBCLASS_SFF8020I: case UISUBCLASS_SFF8070I: subclass = "ATAPI"; break; case UISUBCLASS_SCSI: subclass = "SCSI"; break; case UISUBCLASS_UFI: subclass = "UFI"; break; default: subclass = "Unknown"; } device_printf(dev, "using %s over %s\n", subclass, proto); if (strcmp(proto, "Bulk-Only") || (strcmp(subclass, "ATAPI") && strcmp(subclass, "SCSI"))) return ENXIO; for (i = 0 ; i < id->bNumEndpoints ; i++) { if (!(ed = usbd_interface2endpoint_descriptor(sc->iface, i))) { device_printf(sc->dev, "could not read endpoint descriptor\n"); return ENXIO; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { sc->bulkin = ed->bEndpointAddress; } if (UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_OUT && (ed->bmAttributes & UE_XFERTYPE) == UE_BULK) { sc->bulkout = ed->bEndpointAddress; } if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I && UE_GET_DIR(ed->bEndpointAddress) == UE_DIR_IN && (ed->bmAttributes & UE_XFERTYPE) == UE_INTERRUPT) { sc->bulkirq = ed->bEndpointAddress; } } /* check whether we found at least the endpoints we need */ if (!sc->bulkin || !sc->bulkout) { device_printf(sc->dev, "needed endpoints not found (%d,%d)\n", sc->bulkin, sc->bulkout); atausb_detach(dev); return ENXIO; } /* open the pipes */ if (usbd_open_pipe(sc->iface, sc->bulkout, USBD_EXCLUSIVE_USE, &sc->bulkout_pipe)) { device_printf(sc->dev, "cannot open bulkout pipe (%d)\n", sc->bulkout); atausb_detach(dev); return ENXIO; } if (usbd_open_pipe(sc->iface, sc->bulkin, USBD_EXCLUSIVE_USE, &sc->bulkin_pipe)) { device_printf(sc->dev, "cannot open bulkin pipe (%d)\n", sc->bulkin); atausb_detach(dev); return ENXIO; } if (id->bInterfaceProtocol == UIPROTO_MASS_CBI_I) { if (usbd_open_pipe(sc->iface, sc->bulkirq, USBD_EXCLUSIVE_USE, &sc->bulkirq_pipe)) { device_printf(sc->dev, "cannot open bulkirq pipe (%d)\n", sc->bulkirq); atausb_detach(dev); return ENXIO; } } sc->state = ATAUSB_S_ATTACH; /* alloc needed number of transfer handles */ for (i = 0; i < ATAUSB_T_MAX; i++) { sc->transfer[i] = usbd_alloc_xfer(uaa->device); if (!sc->transfer[i]) { device_printf(sc->dev, "out of memory\n"); atausb_detach(dev); return ENXIO; } } /* driver is ready to process requests here */ sc->state = ATAUSB_S_IDLE; /* get number of devices so we can add matching channels */ usbd_interface2device_handle(sc->iface, &udev); request.bmRequestType = UT_READ_CLASS_INTERFACE; request.bRequest = 0xfe; //GET_MAX_LUN; USETW(request.wValue, 0); USETW(request.wIndex, sc->ifaceno); USETW(request.wLength, sizeof(maxlun)); switch ((err = usbd_do_request(udev, &request, &maxlun))) { case USBD_NORMAL_COMPLETION: if (bootverbose) device_printf(sc->dev, "maxlun=%d\n", maxlun); sc->maxlun = maxlun; break; default: if (bootverbose) device_printf(sc->dev, "get maxlun not supported %s\n", usbd_errstr(err)); } /* ata channels are children to this USB control device */ for (i = 0; i <= sc->maxlun; i++) { if (!device_add_child(sc->dev, "ata", devclass_find_free_unit(ata_devclass, 2))) { device_printf(sc->dev, "failed to attach ata child device\n"); atausb_detach(dev); return ENXIO; } } bus_generic_attach(sc->dev); return 0; } static int atausb_detach(device_ptr_t dev) { struct atausb_softc *sc = device_get_softc(dev); usbd_device_handle udev; device_t *children; int nchildren, i; /* signal that device is going away */ sc->state = ATAUSB_S_DETACH; /* abort all the pipes in case there are active transfers */ usbd_interface2device_handle(sc->iface, &udev); usbd_abort_default_pipe(udev); if (sc->bulkout_pipe) usbd_abort_pipe(sc->bulkout_pipe); if (sc->bulkin_pipe) usbd_abort_pipe(sc->bulkin_pipe); if (sc->bulkirq_pipe) usbd_abort_pipe(sc->bulkirq_pipe); /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* free the transfers */ for (i = 0; i < ATAUSB_T_MAX; i++) if (sc->transfer[i]) usbd_free_xfer(sc->transfer[i]); /* remove all the pipes */ if (sc->bulkout_pipe) usbd_close_pipe(sc->bulkout_pipe); if (sc->bulkin_pipe) usbd_close_pipe(sc->bulkin_pipe); if (sc->bulkirq_pipe) usbd_close_pipe(sc->bulkirq_pipe); mtx_destroy(&sc->locked_mtx); return 0; } /* * Generic USB transfer routines */ static usbd_status atausb_start(struct atausb_softc *sc, usbd_pipe_handle pipe, void *buffer, int buflen, int flags, usbd_xfer_handle xfer) { usbd_status err; if (sc->state == ATAUSB_S_DETACH) return USBD_NOT_STARTED; usbd_setup_xfer(xfer, pipe, (void *)sc, buffer, buflen, flags, sc->timeout, atausb_bbb_finish); err = usbd_transfer(xfer); if (err && (err != USBD_IN_PROGRESS)) { if (atausbdebug) device_printf(sc->dev, "failed to setup transfer, %s\n", usbd_errstr(err)); return err; } return USBD_NORMAL_COMPLETION; } static usbd_status atausb_ctl_start(struct atausb_softc *sc, usbd_device_handle udev, usb_device_request_t *req, void *buffer, int buflen, int flags, usbd_xfer_handle xfer) { usbd_status err; if (sc->state == ATAUSB_S_DETACH) return USBD_NOT_STARTED; usbd_setup_default_xfer(xfer, udev, (void *)sc, sc->timeout, req, buffer, buflen, flags, atausb_bbb_finish); err = usbd_transfer(xfer); if (err && (err != USBD_IN_PROGRESS)) { if (atausbdebug) device_printf(sc->dev, "failed to setup ctl transfer, %s\n", usbd_errstr(err)); return err; } return USBD_NORMAL_COMPLETION; } static void atausb_clear_stall(struct atausb_softc *sc, u_int8_t endpt, usbd_pipe_handle pipe, int state, usbd_xfer_handle xfer) { usbd_device_handle udev; if (atausbdebug) device_printf(sc->dev, "clear endpoint 0x%02x stall\n", endpt); usbd_interface2device_handle(sc->iface, &udev); sc->state = state; usbd_clear_endpoint_toggle(pipe); sc->usb_request.bmRequestType = UT_WRITE_ENDPOINT; sc->usb_request.bRequest = UR_CLEAR_FEATURE; USETW(sc->usb_request.wValue, UF_ENDPOINT_HALT); USETW(sc->usb_request.wIndex, endpt); USETW(sc->usb_request.wLength, 0); atausb_ctl_start(sc, udev, &sc->usb_request, NULL, 0, 0, xfer); } /* * Bulk-Only transport part */ static void atausb_bbb_reset(struct atausb_softc *sc) { usbd_device_handle udev; if (atausbdebug) device_printf(sc->dev, "Bulk Reset\n"); sc->timeout = 5000; sc->state = ATAUSB_S_BBB_RESET1; usbd_interface2device_handle(sc->iface, &udev); sc->usb_request.bmRequestType = UT_WRITE_CLASS_INTERFACE; sc->usb_request.bRequest = 0xff; /* bulk-only reset */ USETW(sc->usb_request.wValue, 0); USETW(sc->usb_request.wIndex, sc->ifaceno); USETW(sc->usb_request.wLength, 0); atausb_ctl_start(sc, udev, &sc->usb_request, NULL, 0, 0, sc->transfer[ATAUSB_T_BBB_RESET1]); } static int atausb_bbb_start(struct ata_request *request) { struct atausb_softc *sc = device_get_softc(device_get_parent(request->parent)); struct ata_channel *ch = device_get_softc(request->parent); sc->timeout = (request->timeout * 1000) + 5000; USETDW(sc->cbw.signature, CBWSIGNATURE); USETDW(sc->cbw.tag, UGETDW(sc->cbw.tag) + 1); USETDW(sc->cbw.transfer_length, request->bytecount); sc->cbw.flags = (request->flags & ATA_R_READ) ? CBWFLAGS_IN : CBWFLAGS_OUT; sc->cbw.lun = ch->unit; sc->cbw.length = 16; bzero(sc->cbw.cdb, 16); bcopy(request->u.atapi.ccb, sc->cbw.cdb, 12); /* XXX SOS */ sc->state = ATAUSB_S_BBB_COMMAND; if (atausb_start(sc, sc->bulkout_pipe, &sc->cbw, sizeof(struct bbb_cbw), 0, sc->transfer[ATAUSB_T_BBB_CBW])) { request->result = EIO; if (atausbdebug) device_printf(request->dev, "cannot setup USB transfer\n"); atausb_bbb_reset(sc); return ATA_OP_FINISHED; } return ATA_OP_CONTINUES; } static void atausb_bbb_finish(usbd_xfer_handle xfer, usbd_private_handle priv, usbd_status err) { struct atausb_softc *sc = (struct atausb_softc *)priv; struct ata_request *request = sc->ata_request; usbd_xfer_handle next_xfer; //device_printf(sc->dev, "BBB state %d: %s\n", sc->state, usbd_errstr(err)); if (sc->state == ATAUSB_S_DETACH) { device_printf(sc->dev, "WARNING - device has been removed\n"); return; } switch (sc->state) { case ATAUSB_S_BBB_COMMAND: /* command transport phase */ if (err) { if (atausbdebug) device_printf(sc->dev, "failed to send CBW\n"); request->result = EIO; atausb_bbb_reset(sc); return; } /* next is data transport phase, setup transfer */ sc->state = ATAUSB_S_BBB_DATA; if (request->flags & ATA_R_READ) { if (atausb_start(sc, sc->bulkin_pipe, request->data, request->bytecount, USBD_SHORT_XFER_OK, sc->transfer[ATAUSB_T_BBB_DATA])) { request->result = EIO; atausb_bbb_reset(sc); } return; } if (request->flags & ATA_R_WRITE) { if (atausb_start(sc, sc->bulkout_pipe, request->data, request->bytecount, 0, sc->transfer[ATAUSB_T_BBB_DATA])) { request->result = EIO; atausb_bbb_reset(sc); } return; } /* FALLTHROUGH */ case ATAUSB_S_BBB_DATA: /* data transport phase */ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) { usbd_get_xfer_status(xfer, NULL, NULL, &request->donecount, NULL); if (err) { if (atausbdebug) device_printf(sc->dev, "data %s count %d failed: %s\n", (request->flags & ATA_R_READ?"read":"write"), request->bytecount, usbd_errstr(err)); if (err == USBD_STALLED) { atausb_clear_stall(sc, (request->flags & ATA_R_READ ? sc->bulkin : sc->bulkout), (request->flags & ATA_R_READ ? sc->bulkin_pipe : sc->bulkout_pipe), ATAUSB_S_BBB_DCLEAR, sc->transfer[ATAUSB_T_BBB_DCLEAR]); } else { request->result = EIO; atausb_bbb_reset(sc); } return; } } /* FALLTHROUGH */ case ATAUSB_S_BBB_DCLEAR: /* stall clear after data phase */ case ATAUSB_S_BBB_SCLEAR: /* stall clear after status phase */ if (err) { if (atausbdebug) device_printf(sc->dev, "bulk%s stall clear failed %s\n", (request->flags & ATA_R_READ ? "in" : "out"), usbd_errstr(err)); request->result = EIO; atausb_bbb_reset(sc); return; } if (sc->state == ATAUSB_S_BBB_COMMAND || sc->state == ATAUSB_S_BBB_DATA || sc->state == ATAUSB_S_BBB_DCLEAR) { /* first attempt on status transport phase setup transfer */ sc->state = ATAUSB_S_BBB_STATUS1; next_xfer = sc->transfer[ATAUSB_T_BBB_CSW1]; } else { /* second attempt of fetching status */ sc->state = ATAUSB_S_BBB_STATUS2; next_xfer = sc->transfer[ATAUSB_T_BBB_CSW2]; } if (atausb_start(sc, sc->bulkin_pipe, &sc->csw, sizeof(struct bbb_csw), USBD_SHORT_XFER_OK, next_xfer)) { request->result = EIO; atausb_bbb_reset(sc); } return; case ATAUSB_S_BBB_STATUS1: /* status transfer first attempt */ case ATAUSB_S_BBB_STATUS2: /* status transfer second attempt */ if (err) { if (atausbdebug) device_printf(sc->dev, "cannot get CSW, %s%s\n", usbd_errstr(err), sc->state == ATAUSB_S_BBB_STATUS1 ? ", retry":""); if (sc->state == ATAUSB_S_BBB_STATUS1) { atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe, ATAUSB_S_BBB_SCLEAR, sc->transfer[ATAUSB_T_BBB_SCLEAR]); } else { request->result = EIO; atausb_bbb_reset(sc); } return; } int residue = UGETDW(sc->csw.residue); if (!residue && (request->bytecount - request->donecount)) residue = request->bytecount - request->donecount; /* check CSW and handle eventual error */ if (UGETDW(sc->csw.signature) != CSWSIGNATURE) { if (atausbdebug) device_printf(sc->dev, "bad CSW signature 0x%08x != 0x%08x\n", UGETDW(sc->csw.signature), CSWSIGNATURE); request->result = EIO; atausb_bbb_reset(sc); return; } else if (UGETDW(sc->csw.tag) != UGETDW(sc->cbw.tag)) { if (atausbdebug) device_printf(sc->dev, "bad CSW tag %d != %d\n", UGETDW(sc->csw.tag), UGETDW(sc->cbw.tag)); request->result = EIO; atausb_bbb_reset(sc); return; } else if (sc->csw.status > CSWSTATUS_PHASE) { if (atausbdebug) device_printf(sc->dev, "bad CSW status %d > %d\n", sc->csw.status, CSWSTATUS_PHASE); request->result = EIO; atausb_bbb_reset(sc); return; } else if (sc->csw.status == CSWSTATUS_PHASE) { if (atausbdebug) device_printf(sc->dev, "phase error residue = %d\n", residue); request->result = EIO; atausb_bbb_reset(sc); return; } else if (request->donecount > request->bytecount) { if (atausbdebug) device_printf(sc->dev, "buffer overrun %d > %d", request->donecount, request->bytecount); request->result = EIO; atausb_bbb_reset(sc); return; } else if (sc->csw.status == CSWSTATUS_FAILED) { if (atausbdebug) device_printf(sc->dev, "CSWSTATUS_FAILED\n"); - //request->result = ENODEV; - request->error = ATA_SK_RESERVED; + request->error = ATA_E_ATAPI_SENSE_MASK ; sc->state = ATAUSB_S_IDLE; ata_interrupt(device_get_softc(request->parent)); return; } else { sc->state = ATAUSB_S_IDLE; ata_interrupt(device_get_softc(request->parent)); return; } /* NOT REACHED */ case ATAUSB_S_BBB_RESET1: if (err) if (atausbdebug) device_printf(sc->dev, "BBB reset failure: %s\n", usbd_errstr(err)); atausb_clear_stall(sc, sc->bulkin, sc->bulkin_pipe, ATAUSB_S_BBB_RESET2, sc->transfer[ATAUSB_T_BBB_RESET2]); return; case ATAUSB_S_BBB_RESET2: if (err) if (atausbdebug) device_printf(sc->dev, "BBB bulkin clear stall failure: %s\n", usbd_errstr(err)); atausb_clear_stall(sc, sc->bulkout, sc->bulkout_pipe, ATAUSB_S_BBB_RESET3, sc->transfer[ATAUSB_T_BBB_RESET3]); return; case ATAUSB_S_BBB_RESET3: if (err) if (atausbdebug) device_printf(sc->dev, "BBB bulk-out clear stall failure: %s\n", usbd_errstr(err)); sc->state = ATAUSB_S_IDLE; if (request) { if (err) request->result = ENXIO; else request->result = EIO; ata_interrupt(device_get_softc(request->parent)); } return; default: if (atausbdebug) device_printf(sc->dev, "unknown state %d", sc->state); } } /* * ATA backend part */ struct atapi_inquiry { u_int8_t device_type; u_int8_t device_modifier; u_int8_t version; u_int8_t response_format; u_int8_t length; u_int8_t reserved[2]; u_int8_t flags; u_int8_t vendor[8]; u_int8_t product[16]; u_int8_t revision[4]; //u_int8_t crap[60]; }; int ata_usbchannel_begin_transaction(struct ata_request *request) { struct atausb_softc *sc = device_get_softc(device_get_parent(request->parent)); if (atausbdebug > 1) device_printf(request->dev, "begin_transaction %s\n", ata_cmd2str(request)); /* sanity just in case */ if (sc->state != ATAUSB_S_IDLE) { printf("begin is busy (%d)\n", sc->state); request->result = EBUSY; return ATA_OP_FINISHED; } /* XXX SOS convert the request into the format used, only BBB for now*/ sc->ata_request = request; /* ATA/ATAPI IDENTIFY needs special treatment */ if (!(request->flags & ATA_R_ATAPI)) { if (request->u.ata.command != ATA_ATAPI_IDENTIFY) { device_printf(request->dev,"%s unsupported\n",ata_cmd2str(request)); request->result = EIO; return ATA_OP_FINISHED; } request->flags |= ATA_R_ATAPI; bzero(request->u.atapi.ccb, 16); request->u.atapi.ccb[0] = ATAPI_INQUIRY; request->u.atapi.ccb[4] = 255; //sizeof(struct atapi_inquiry); request->data += 256; /* arbitrary offset into ata_param */ request->bytecount = 255; //sizeof(struct atapi_inquiry); } return atausb_bbb_start(request); } int ata_usbchannel_end_transaction(struct ata_request *request) { if (atausbdebug > 1) device_printf(request->dev, "end_transaction %s\n", ata_cmd2str(request)); /* XXX SOS convert the request from the format used, only BBB for now*/ /* ATA/ATAPI IDENTIFY needs special treatment */ if ((request->flags & ATA_R_ATAPI) && (request->u.atapi.ccb[0] == ATAPI_INQUIRY)) { struct ata_device *atadev = device_get_softc(request->dev); struct atapi_inquiry *inquiry = (struct atapi_inquiry *)request->data; u_int16_t *ptr; /* convert inquiry data into simple ata_param like format */ atadev->param.config = ATA_PROTO_ATAPI | ATA_PROTO_ATAPI_12; atadev->param.config |= (inquiry->device_type & 0x1f) << 8; bzero(atadev->param.model, sizeof(atadev->param.model)); strncpy(atadev->param.model, inquiry->vendor, 8); strcpy(atadev->param.model, " "); strncpy(atadev->param.model, inquiry->product, 16); ptr = (u_int16_t*)(atadev->param.model + sizeof(atadev->param.model)); while (--ptr >= (u_int16_t*)atadev->param.model) *ptr = ntohs(*ptr); strncpy(atadev->param.revision, inquiry->revision, 4); ptr=(u_int16_t*)(atadev->param.revision+sizeof(atadev->param.revision)); while (--ptr >= (u_int16_t*)atadev->param.revision) *ptr = ntohs(*ptr); request->result = 0; } - if ((request->flags & ATA_R_ATAPI) && - (request->u.atapi.ccb[0] == ATAPI_REQUEST_SENSE)) - request->u.atapi.sense_key = request->u.atapi.sense_data.sense_key << 4; return ATA_OP_FINISHED; } static int ata_usbchannel_probe(device_t dev) { struct ata_channel *ch = device_get_softc(dev); device_t *children; int count, i; char buffer[32]; /* take care of green memory */ bzero(ch, sizeof(struct ata_channel)); /* find channel number on this controller */ device_get_children(device_get_parent(dev), &children, &count); for (i = 0; i < count; i++) { if (children[i] == dev) ch->unit = i; } free(children, M_TEMP); sprintf(buffer, "USB lun %d", ch->unit); device_set_desc_copy(dev, buffer); return 0; } static int ata_usbchannel_attach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); /* initialize the softc basics */ ch->dev = dev; ch->state = ATA_IDLE; ch->hw.begin_transaction = ata_usbchannel_begin_transaction; ch->hw.end_transaction = ata_usbchannel_end_transaction; ch->hw.status = NULL; ch->hw.command = NULL; bzero(&ch->state_mtx, sizeof(struct mtx)); mtx_init(&ch->state_mtx, "ATA state lock", NULL, MTX_DEF); bzero(&ch->queue_mtx, sizeof(struct mtx)); mtx_init(&ch->queue_mtx, "ATA queue lock", NULL, MTX_DEF); TAILQ_INIT(&ch->ata_queue); /* XXX SOS reset the controller HW, the channel and device(s) */ //ATA_RESET(dev); /* probe and attach device on this channel */ ch->devices = ATA_ATAPI_MASTER; if (!ata_delayed_attach) ata_identify(dev); return 0; } static int ata_usbchannel_detach(device_t dev) { struct ata_channel *ch = device_get_softc(dev); device_t *children; int nchildren, i; /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } mtx_destroy(&ch->state_mtx); mtx_destroy(&ch->queue_mtx); return 0; } static void ata_usbchannel_setmode(device_t parent, device_t dev) { struct atausb_softc *sc = device_get_softc(GRANDPARENT(dev)); struct ata_device *atadev = device_get_softc(dev); usbd_device_handle udev; usbd_interface2device_handle(sc->iface, &udev); if (usbd_get_speed(udev) == USB_SPEED_HIGH) atadev->mode = ATA_USB2; else atadev->mode = ATA_USB1; } 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; mtx_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; mtx_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; mtx_unlock(&sc->locked_mtx); return res; } static device_method_t ata_usbchannel_methods[] = { /* device interface */ DEVMETHOD(device_probe, ata_usbchannel_probe), DEVMETHOD(device_attach, ata_usbchannel_attach), DEVMETHOD(device_detach, ata_usbchannel_detach), /* ATA methods */ DEVMETHOD(ata_setmode, ata_usbchannel_setmode), DEVMETHOD(ata_locking, ata_usbchannel_locking), //DEVMETHOD(ata_reset, ata_usbchannel_reset), { 0, 0 } }; static driver_t ata_usbchannel_driver = { "ata", ata_usbchannel_methods, sizeof(struct ata_channel), }; DRIVER_MODULE(ata, atausb, ata_usbchannel_driver, ata_devclass, 0, 0); MODULE_DEPEND(atausb, ata, 1, 1, 1); Index: head/sys/dev/ata/atapi-cam.c =================================================================== --- head/sys/dev/ata/atapi-cam.c (revision 157328) +++ head/sys/dev/ata/atapi-cam.c (revision 157329) @@ -1,901 +1,901 @@ /*- * Copyright (c) 2001-2003 Thomas Quinot * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* private data associated with an ATA bus */ struct atapi_xpt_softc { struct ata_device atapi_cam_dev; /* must be first */ device_t dev; device_t parent; struct ata_channel *ata_ch; struct cam_path *path; struct cam_sim *sim; int flags; #define BUS_REGISTERED 0x01 #define RESOURCE_SHORTAGE 0x02 #define DETACHING 0x04 TAILQ_HEAD(,atapi_hcb) pending_hcbs; struct ata_device *atadev[2]; struct mtx state_lock; }; /* hardware command descriptor block */ struct atapi_hcb { struct atapi_xpt_softc *softc; int unit; int bus; int target; int lun; union ccb *ccb; int flags; #define QUEUED 0x0001 #define AUTOSENSE 0x0002 char *dxfer_alloc; TAILQ_ENTRY(atapi_hcb) chain; }; enum reinit_reason { BOOT_ATTACH, ATTACH, RESET }; /* Device methods */ static void atapi_cam_identify(device_t *dev, device_t parent); static int atapi_cam_probe(device_t dev); static int atapi_cam_attach(device_t dev); static int atapi_cam_detach(device_t dev); static int atapi_cam_reinit(device_t dev); /* CAM XPT methods */ static void atapi_action(struct cam_sim *, union ccb *); static void atapi_poll(struct cam_sim *); static void atapi_async(void *, u_int32_t, struct cam_path *, void *); static void atapi_cb(struct ata_request *); /* Module methods */ static int atapi_cam_event_handler(module_t mod, int what, void *arg); /* internal functions */ static void reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason); static void setup_async_cb(struct atapi_xpt_softc *, uint32_t); static void cam_rescan_callback(struct cam_periph *, union ccb *); static void cam_rescan(struct cam_sim *); static void free_hcb_and_ccb_done(struct atapi_hcb *, u_int32_t); static struct atapi_hcb *allocate_hcb(struct atapi_xpt_softc *, int, int, union ccb *); static void free_hcb(struct atapi_hcb *hcb); static void free_softc(struct atapi_xpt_softc *scp); static MALLOC_DEFINE(M_ATACAM, "ata_cam", "ATA driver CAM-XPT layer"); static device_method_t atapi_cam_methods[] = { DEVMETHOD(device_identify, atapi_cam_identify), DEVMETHOD(device_probe, atapi_cam_probe), DEVMETHOD(device_attach, atapi_cam_attach), DEVMETHOD(device_detach, atapi_cam_detach), DEVMETHOD(ata_reinit, atapi_cam_reinit), {0, 0} }; static driver_t atapi_cam_driver = { "atapicam", atapi_cam_methods, sizeof(struct atapi_xpt_softc) }; static devclass_t atapi_cam_devclass; DRIVER_MODULE(atapicam, ata, atapi_cam_driver, atapi_cam_devclass, atapi_cam_event_handler, /*arg*/NULL); MODULE_VERSION(atapicam, 1); MODULE_DEPEND(atapicam, ata, 1, 1, 1); MODULE_DEPEND(atapicam, cam, 1, 1, 1); static void atapi_cam_identify(device_t *dev, device_t parent) { struct atapi_xpt_softc *scp = malloc (sizeof (struct atapi_xpt_softc), M_ATACAM, M_NOWAIT|M_ZERO); device_t child; if (scp == NULL) { printf ("atapi_cam_identify: out of memory"); return; } /* Assume one atapicam instance per parent channel instance. */ child = device_add_child(parent, "atapicam", -1); if (child == NULL) { printf ("atapi_cam_identify: out of memory, can't add child"); free (scp, M_ATACAM); return; } scp->atapi_cam_dev.unit = -1; scp->atapi_cam_dev.dev = child; device_quiet(child); device_set_softc(child, scp); } static int atapi_cam_probe(device_t dev) { struct ata_device *atadev = device_get_softc (dev); KASSERT(atadev != NULL, ("expect valid struct ata_device")); if (atadev->unit < 0) { device_set_desc(dev, "ATAPI CAM Attachment"); return (0); } else { return ENXIO; } } static int atapi_cam_attach(device_t dev) { struct atapi_xpt_softc *scp = NULL; struct cam_devq *devq = NULL; struct cam_sim *sim = NULL; struct cam_path *path = NULL; int unit, error; scp = (struct atapi_xpt_softc *)device_get_softc(dev); if (scp == NULL) { device_printf(dev, "Cannot get softc\n"); return (ENOMEM); } mtx_init(&scp->state_lock, "ATAPICAM lock", NULL, MTX_DEF); scp->dev = dev; scp->parent = device_get_parent(dev); scp->ata_ch = device_get_softc(scp->parent); TAILQ_INIT(&scp->pending_hcbs); unit = device_get_unit(dev); if ((devq = cam_simq_alloc(16)) == NULL) { error = ENOMEM; goto out; } if ((sim = cam_sim_alloc(atapi_action, atapi_poll, "ata", (void *)scp, unit, 1, 1, devq)) == NULL) { error = ENOMEM; goto out; } scp->sim = sim; if (xpt_bus_register(sim, 0) != CAM_SUCCESS) { error = EINVAL; goto out; } scp->flags |= BUS_REGISTERED; if (xpt_create_path(&path, /*periph*/ NULL, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { error = ENOMEM; goto out; } scp->path = path; CAM_DEBUG(path, CAM_DEBUG_TRACE, ("Registered SIM for ata%d\n", unit)); setup_async_cb(scp, AC_LOST_DEVICE); reinit_bus(scp, cold ? BOOT_ATTACH : ATTACH); error = 0; out: if (error != 0) free_softc(scp); return (error); } static int atapi_cam_detach(device_t dev) { struct atapi_xpt_softc *scp = device_get_softc(dev); mtx_lock(&Giant); xpt_freeze_simq(scp->sim, 1 /*count*/); mtx_unlock(&Giant); mtx_lock(&scp->state_lock); scp->flags |= DETACHING; mtx_unlock(&scp->state_lock); free_softc(scp); return (0); } static int atapi_cam_reinit(device_t dev) { struct atapi_xpt_softc *scp = device_get_softc(dev); /* * scp might be null if the bus is being reinitialised during * the boot-up sequence, before the ATAPI bus is registered. */ if (scp != NULL) { reinit_bus(scp, RESET); } return (0); } static void reinit_bus(struct atapi_xpt_softc *scp, enum reinit_reason reason) { struct ata_device *atadev; device_t *children; int nchildren, i; if (device_get_children(scp->parent, &children, &nchildren) != 0) { return; } mtx_lock(&scp->state_lock); scp->atadev[0] = NULL; scp->atadev[1] = NULL; for (i = 0; i < nchildren; i++) { /* XXX Does the child need to actually be attached yet? */ if (children[i] != NULL) { atadev = device_get_softc(children[i]); if ((atadev->unit == ATA_MASTER) && (scp->ata_ch->devices & ATA_ATAPI_MASTER) != 0) scp->atadev[0] = atadev; if ((atadev->unit == ATA_SLAVE) && (scp->ata_ch->devices & ATA_ATAPI_SLAVE) != 0) scp->atadev[1] = atadev; } } mtx_unlock(&scp->state_lock); free(children, M_TEMP); switch (reason) { case BOOT_ATTACH: break; case RESET: xpt_async(AC_BUS_RESET, scp->path, NULL); /*FALLTHROUGH*/ case ATTACH: cam_rescan(scp->sim); break; } } static void setup_async_cb(struct atapi_xpt_softc *scp, uint32_t events) { struct ccb_setasync csa; mtx_lock(&Giant); xpt_setup_ccb(&csa.ccb_h, scp->path, /*priority*/ 5); csa.ccb_h.func_code = XPT_SASYNC_CB; csa.event_enable = events; csa.callback = &atapi_async; csa.callback_arg = scp->sim; xpt_action((union ccb *) &csa); mtx_unlock(&Giant); } static void atapi_action(struct cam_sim *sim, union ccb *ccb) { struct atapi_xpt_softc *softc = (struct atapi_xpt_softc*)cam_sim_softc(sim); struct ccb_hdr *ccb_h = &ccb->ccb_h; struct atapi_hcb *hcb = NULL; struct ata_request *request = NULL; int unit = cam_sim_unit(sim); int bus = cam_sim_bus(sim); int len; char *buf; switch (ccb_h->func_code) { case XPT_PATH_INQ: { struct ccb_pathinq *cpi = &ccb->cpi; int tid = ccb_h->target_id; cpi->version_num = 1; cpi->hba_inquiry = 0; cpi->target_sprt = 0; cpi->hba_misc = PIM_NO_6_BYTE; cpi->hba_eng_cnt = 0; bzero(cpi->vuhba_flags, sizeof(cpi->vuhba_flags)); cpi->max_target = 1; cpi->max_lun = 0; cpi->async_flags = 0; cpi->hpath_id = 0; cpi->initiator_id = 7; strncpy(cpi->sim_vid, "FreeBSD", sizeof(cpi->sim_vid)); strncpy(cpi->hba_vid, "ATAPI", sizeof(cpi->hba_vid)); strncpy(cpi->dev_name, cam_sim_name(sim), sizeof cpi->dev_name); cpi->unit_number = cam_sim_unit(sim); cpi->bus_id = cam_sim_bus(sim); cpi->base_transfer_speed = 3300; if (softc->ata_ch && tid != CAM_TARGET_WILDCARD) { mtx_lock(&softc->state_lock); if (softc->atadev[tid] == NULL) { ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); mtx_unlock(&softc->state_lock); return; } switch (softc->atadev[ccb_h->target_id]->mode) { case ATA_PIO1: cpi->base_transfer_speed = 5200; break; case ATA_PIO2: cpi->base_transfer_speed = 7000; break; case ATA_PIO3: cpi->base_transfer_speed = 11000; break; case ATA_PIO4: case ATA_DMA: case ATA_WDMA2: cpi->base_transfer_speed = 16000; break; case ATA_UDMA2: cpi->base_transfer_speed = 33000; break; case ATA_UDMA4: cpi->base_transfer_speed = 66000; break; case ATA_UDMA5: cpi->base_transfer_speed = 100000; break; case ATA_UDMA6: cpi->base_transfer_speed = 133000; break; default: break; } mtx_unlock(&softc->state_lock); } ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_RESET_DEV: { int tid = ccb_h->target_id; CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("dev reset\n")); ata_controlcmd(softc->atadev[tid]->dev, ATA_DEVICE_RESET, 0, 0, 0); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_RESET_BUS: CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("bus reset\n")); ata_reinit(softc->parent); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; case XPT_SET_TRAN_SETTINGS: /* ignore these, we're not doing SCSI here */ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("SET_TRAN_SETTINGS not supported\n")); ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_done(ccb); return; case XPT_GET_TRAN_SETTINGS: { struct ccb_trans_settings *cts = &ccb->cts; /* * XXX The default CAM transport code is very SCSI-specific and * doesn't understand IDE speeds very well. Be silent about it * here and let it default to what is set in XPT_PATH_INQ */ CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("GET_TRAN_SETTINGS\n")); cts->valid = (CCB_TRANS_DISC_VALID | CCB_TRANS_TQ_VALID); cts->flags &= ~(CCB_TRANS_DISC_ENB | CCB_TRANS_TAG_ENB); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); return; } case XPT_CALC_GEOMETRY: { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_SUBTRACE, ("CALC_GEOMETRY\n")); cam_calc_geometry(&ccb->ccg, /*extended*/1); xpt_done(ccb); return; } case XPT_SCSI_IO: { struct ccb_scsiio *csio = &ccb->csio; int tid = ccb_h->target_id, lid = ccb_h->target_lun; int request_flags = ATA_R_QUIET | ATA_R_ATAPI; CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("XPT_SCSI_IO\n")); mtx_lock(&softc->state_lock); if (softc->flags & DETACHING) { ccb->ccb_h.status = CAM_REQ_ABORTED; xpt_done(ccb); mtx_unlock(&softc->state_lock); return; } if (softc->atadev[tid] == NULL) { ccb->ccb_h.status = CAM_DEV_NOT_THERE; xpt_done(ccb); mtx_unlock(&softc->state_lock); return; } /* check that this request was not aborted already */ if ((ccb_h->status & CAM_STATUS_MASK) != CAM_REQ_INPROG) { printf("XPT_SCSI_IO received but already in progress?\n"); xpt_done(ccb); mtx_unlock(&softc->state_lock); return; } if (lid > 0) { CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("SCSI IO received for invalid lun %d\n", lid)); goto action_invalid; } if (csio->cdb_len > sizeof request->u.atapi.ccb) { CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("CAM CCB too long for ATAPI")); goto action_invalid; } if ((ccb_h->flags & CAM_SCATTER_VALID)) { /* scatter-gather not supported */ xpt_print_path(ccb_h->path); printf("ATAPI/CAM does not support scatter-gather yet!\n"); goto action_invalid; } switch (ccb_h->flags & CAM_DIR_MASK) { case CAM_DIR_IN: request_flags |= ATA_R_READ|ATA_R_DMA; break; case CAM_DIR_OUT: request_flags |= ATA_R_WRITE|ATA_R_DMA; break; case CAM_DIR_NONE: /* No flags need to be set */ break; default: device_printf(softc->dev, "unknown IO operation\n"); goto action_invalid; } if (softc->atadev[tid]->mode < ATA_DMA) request_flags &= ~ATA_R_DMA; if ((hcb = allocate_hcb(softc, unit, bus, ccb)) == NULL) { printf("cannot allocate ATAPI/CAM hcb\n"); goto action_oom; } if ((request = ata_alloc_request()) == NULL) { printf("cannot allocate ATAPI/CAM request\n"); goto action_oom; } bcopy((ccb_h->flags & CAM_CDB_POINTER) ? csio->cdb_io.cdb_ptr : csio->cdb_io.cdb_bytes, request->u.atapi.ccb, csio->cdb_len); #ifdef CAMDEBUG if (CAM_DEBUGGED(ccb_h->path, CAM_DEBUG_CDB)) { char cdb_str[(SCSI_MAX_CDBLEN * 3) + 1]; printf("atapi_action: hcb@%p: %s\n", hcb, scsi_cdb_string(request->u.atapi.ccb, cdb_str, sizeof(cdb_str))); } #endif len = csio->dxfer_len; buf = csio->data_ptr; /* some SCSI commands require special processing */ switch (request->u.atapi.ccb[0]) { case INQUIRY: { /* * many ATAPI devices seem to report more than * SHORT_INQUIRY_LENGTH bytes of available INQUIRY * information, but respond with some incorrect condition * when actually asked for it, so we are going to pretend * that only SHORT_INQUIRY_LENGTH are expected, anyway. */ struct scsi_inquiry *inq = (struct scsi_inquiry *) &request->u.atapi.ccb[0]; if (inq->byte2 == 0 && inq->page_code == 0 && inq->length > SHORT_INQUIRY_LENGTH) { bzero(buf, len); len = inq->length = SHORT_INQUIRY_LENGTH; } break; } case READ_6: /* FALLTHROUGH */ case WRITE_6: CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("Translating %s into _10 equivalent\n", (request->u.atapi.ccb[0] == READ_6) ? "READ_6" : "WRITE_6")); request->u.atapi.ccb[0] |= 0x20; request->u.atapi.ccb[9] = request->u.atapi.ccb[5]; request->u.atapi.ccb[8] = request->u.atapi.ccb[4]; request->u.atapi.ccb[7] = 0; request->u.atapi.ccb[6] = 0; request->u.atapi.ccb[5] = request->u.atapi.ccb[3]; request->u.atapi.ccb[4] = request->u.atapi.ccb[2]; request->u.atapi.ccb[3] = request->u.atapi.ccb[1] & 0x1f; request->u.atapi.ccb[2] = 0; request->u.atapi.ccb[1] = 0; break; } if ((ccb_h->flags & CAM_DIR_MASK) == CAM_DIR_IN && (len & 1)) { /* ATA always transfers an even number of bytes */ if ((buf = hcb->dxfer_alloc = malloc(++len, M_ATACAM, M_NOWAIT | M_ZERO)) == NULL) { printf("cannot allocate ATAPI/CAM buffer\n"); goto action_oom; } } request->dev = softc->atadev[tid]->dev; request->driver = hcb; request->data = buf; request->bytecount = len; request->transfersize = min(request->bytecount, 65534); request->timeout = ccb_h->timeout / 1000; /* XXX lost granularity */ request->retries = 2; request->callback = &atapi_cb; request->flags = request_flags; TAILQ_INSERT_TAIL(&softc->pending_hcbs, hcb, chain); hcb->flags |= QUEUED; ccb_h->status |= CAM_SIM_QUEUED; mtx_unlock(&softc->state_lock); ata_queue_request(request); return; } default: CAM_DEBUG(ccb_h->path, CAM_DEBUG_SUBTRACE, ("unsupported function code 0x%02x\n", ccb_h->func_code)); goto action_invalid; } /* NOTREACHED */ action_oom: if (request != NULL) ata_free_request(request); if (hcb != NULL) free_hcb(hcb); mtx_unlock(&softc->state_lock); mtx_lock(&Giant); xpt_print_path(ccb_h->path); printf("out of memory, freezing queue.\n"); softc->flags |= RESOURCE_SHORTAGE; xpt_freeze_simq(sim, /*count*/ 1); mtx_unlock(&Giant); ccb_h->status = CAM_REQUEUE_REQ; xpt_done(ccb); return; action_invalid: mtx_unlock(&softc->state_lock); ccb_h->status = CAM_REQ_INVALID; xpt_done(ccb); return; } static void atapi_poll(struct cam_sim *sim) { /* do nothing - we do not actually service any interrupts */ printf("atapi_poll called!\n"); } static void atapi_cb(struct ata_request *request) { struct atapi_xpt_softc *scp; struct atapi_hcb *hcb; struct ccb_scsiio *csio; u_int32_t rc; hcb = (struct atapi_hcb *)request->driver; scp = hcb->softc; csio = &hcb->ccb->csio; #ifdef CAMDEBUG -# define err (request->u.atapi.sense_key) +# define err (request->u.atapi.sense.key) if (CAM_DEBUGGED(csio->ccb_h.path, CAM_DEBUG_CDB)) { printf("atapi_cb: hcb@%p error = %02x: (sk = %02x%s%s%s)\n", hcb, err, err >> 4, (err & 4) ? " ABRT" : "", (err & 2) ? " EOM" : "", (err & 1) ? " ILI" : ""); printf("dev %s: cmd %02x status %02x result %02x\n", device_get_nameunit(request->dev), request->u.atapi.ccb[0], request->status, request->result); } #endif if ((hcb->flags & AUTOSENSE) != 0) { rc = CAM_SCSI_STATUS_ERROR; if (request->result == 0) { csio->ccb_h.status |= CAM_AUTOSNS_VALID; } } else if (request->result != 0) { rc = CAM_SCSI_STATUS_ERROR; csio->scsi_status = SCSI_STATUS_CHECK_COND; if ((csio->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0) { #if 0 static const int8_t ccb[16] = { ATAPI_REQUEST_SENSE, 0, 0, 0, sizeof(struct atapi_sense), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; bcopy (ccb, request->u.atapi.ccb, sizeof ccb); request->data = (caddr_t)&csio->sense_data; request->bytecount = sizeof(struct atapi_sense); request->transfersize = min(request->bytecount, 65534); request->timeout = csio->ccb_h.timeout / 1000; request->retries = 2; request->flags = ATA_R_QUIET|ATA_R_ATAPI|ATA_R_IMMEDIATE; hcb->flags |= AUTOSENSE; ata_queue_request(request); return; #else /* The ATA driver has already requested sense for us. */ if (request->error == 0) { /* The ATA autosense suceeded. */ - bcopy (&request->u.atapi.sense_data, &csio->sense_data, sizeof(struct atapi_sense)); + bcopy (&request->u.atapi.sense, &csio->sense_data, sizeof(struct atapi_sense)); csio->ccb_h.status |= CAM_AUTOSNS_VALID; } #endif } } else { rc = CAM_REQ_CMP; csio->scsi_status = SCSI_STATUS_OK; if (((csio->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) && hcb->dxfer_alloc != NULL) { bcopy(hcb->dxfer_alloc, csio->data_ptr, csio->dxfer_len); } } mtx_lock(&scp->state_lock); free_hcb_and_ccb_done(hcb, rc); mtx_unlock(&scp->state_lock); ata_free_request(request); } static void free_hcb_and_ccb_done(struct atapi_hcb *hcb, u_int32_t status) { struct atapi_xpt_softc *softc; union ccb *ccb; if (hcb == NULL) return; softc = hcb->softc; ccb = hcb->ccb; /* we're about to free a hcb, so the shortage has ended */ if (softc->flags & RESOURCE_SHORTAGE) { softc->flags &= ~RESOURCE_SHORTAGE; status |= CAM_RELEASE_SIMQ; } free_hcb(hcb); ccb->ccb_h.status = status | (ccb->ccb_h.status & ~(CAM_STATUS_MASK | CAM_SIM_QUEUED)); xpt_done(ccb); } static void atapi_async(void *callback_arg, u_int32_t code, struct cam_path* path, void *arg) { struct atapi_xpt_softc *softc; struct cam_sim *sim; int targ; GIANT_REQUIRED; sim = (struct cam_sim *) callback_arg; softc = (struct atapi_xpt_softc *) cam_sim_softc(sim); switch (code) { case AC_LOST_DEVICE: targ = xpt_path_target_id(path); xpt_print_path(path); if (targ == -1) printf("Lost host adapter\n"); else printf("Lost target %d???\n", targ); break; default: break; } } static void cam_rescan_callback(struct cam_periph *periph, union ccb *ccb) { if (ccb->ccb_h.status != CAM_REQ_CMP) { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescan failed, 0x%04x\n", ccb->ccb_h.status)); } else { CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescan succeeded\n")); } xpt_free_path(ccb->ccb_h.path); free(ccb, M_ATACAM); } static void cam_rescan(struct cam_sim *sim) { struct cam_path *path; union ccb *ccb = malloc(sizeof(union ccb), M_ATACAM, M_WAITOK | M_ZERO); mtx_lock(&Giant); if (xpt_create_path(&path, xpt_periph, cam_sim_path(sim), CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { mtx_unlock(&Giant); free(ccb, M_ATACAM); return; } CAM_DEBUG(ccb->ccb_h.path, CAM_DEBUG_TRACE, ("Rescanning ATAPI bus.\n")); xpt_setup_ccb(&ccb->ccb_h, path, 5/*priority (low)*/); ccb->ccb_h.func_code = XPT_SCAN_BUS; ccb->ccb_h.cbfcnp = cam_rescan_callback; ccb->crcn.flags = CAM_FLAG_NONE; xpt_action(ccb); /* scan is in progress now */ mtx_unlock(&Giant); } static struct atapi_hcb * allocate_hcb(struct atapi_xpt_softc *softc, int unit, int bus, union ccb *ccb) { struct atapi_hcb *hcb = (struct atapi_hcb *) malloc(sizeof(struct atapi_hcb), M_ATACAM, M_NOWAIT | M_ZERO); if (hcb != NULL) { hcb->softc = softc; hcb->unit = unit; hcb->bus = bus; hcb->ccb = ccb; } return hcb; } static void free_hcb(struct atapi_hcb *hcb) { if ((hcb->flags & QUEUED) != 0) TAILQ_REMOVE(&hcb->softc->pending_hcbs, hcb, chain); if (hcb->dxfer_alloc != NULL) free(hcb->dxfer_alloc, M_ATACAM); free(hcb, M_ATACAM); } static void free_softc(struct atapi_xpt_softc *scp) { struct atapi_hcb *hcb; if (scp != NULL) { mtx_lock(&scp->state_lock); TAILQ_FOREACH(hcb, &scp->pending_hcbs, chain) { free_hcb_and_ccb_done(hcb, CAM_UNREC_HBA_ERROR); } mtx_unlock(&scp->state_lock); mtx_lock(&Giant); if (scp->path != NULL) { setup_async_cb(scp, 0); xpt_free_path(scp->path); } if ((scp->flags & BUS_REGISTERED) != 0) { if (xpt_bus_deregister(cam_sim_path(scp->sim)) == CAM_REQ_CMP) scp->flags &= ~BUS_REGISTERED; } if (scp->sim != NULL) { if ((scp->flags & BUS_REGISTERED) == 0) cam_sim_free(scp->sim, /*free_devq*/TRUE); else printf("Can't free %s SIM (still registered)\n", cam_sim_name(scp->sim)); } mtx_unlock(&Giant); mtx_destroy(&scp->state_lock); } } static int atapi_cam_event_handler(module_t mod, int what, void *arg) { device_t *devlist; int devcount; switch (what) { case MOD_UNLOAD: if (devclass_get_devices(atapi_cam_devclass, &devlist, &devcount) != 0) return ENXIO; if (devlist != NULL) { while (devlist != NULL && devcount > 0) { device_t child = devlist[--devcount]; struct atapi_xpt_softc *scp = device_get_softc(child); device_delete_child(device_get_parent(child),child); if (scp != NULL) free(scp, M_ATACAM); } free(devlist, M_TEMP); } break; default: break; } return 0; } Index: head/sys/dev/ata/atapi-cd.c =================================================================== --- head/sys/dev/ata/atapi-cd.c (revision 157328) +++ head/sys/dev/ata/atapi-cd.c (revision 157329) @@ -1,1910 +1,1910 @@ /*- * Copyright (c) 1998 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void acd_geom_attach(void *, int); static void acd_geom_detach(void *, int); static void acd_set_ioparm(device_t); static void acd_describe(device_t); static void lba2msf(u_int32_t, u_int8_t *, u_int8_t *, u_int8_t *); static u_int32_t msf2lba(u_int8_t, u_int8_t, u_int8_t); static int acd_geom_access(struct g_provider *, int, int, int); static g_ioctl_t acd_geom_ioctl; static void acd_geom_start(struct bio *); static void acd_strategy(struct bio *); static void acd_done(struct ata_request *); static void acd_read_toc(device_t); static int acd_play(device_t, int, int); static int acd_setchan(device_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t); static int acd_init_writer(device_t, int); static int acd_fixate(device_t, int); static int acd_init_track(device_t, struct cdr_track *); static int acd_flush(device_t); static int acd_read_track_info(device_t, int32_t, struct acd_track_info *); static int acd_get_progress(device_t, int *); static int acd_send_cue(device_t, struct cdr_cuesheet *); static int acd_report_key(device_t, struct dvd_authinfo *); static int acd_send_key(device_t, struct dvd_authinfo *); static int acd_read_structure(device_t, struct dvd_struct *); static int acd_tray(device_t, int); static int acd_blank(device_t, int); static int acd_prevent_allow(device_t, int); static int acd_start_stop(device_t, int); static int acd_pause_resume(device_t, int); static int acd_mode_sense(device_t, int, caddr_t, int); static int acd_mode_select(device_t, caddr_t, int); static int acd_set_speed(device_t, int, int); static void acd_get_cap(device_t); static int acd_read_format_caps(device_t, struct cdr_format_capacities *); static int acd_format(device_t, struct cdr_format_params *); static int acd_test_ready(device_t); /* internal vars */ static MALLOC_DEFINE(M_ACD, "acd_driver", "ATAPI CD driver buffers"); static struct g_class acd_class = { .name = "ACD", .version = G_VERSION, .access = acd_geom_access, .ioctl = acd_geom_ioctl, .start = acd_geom_start, }; //DECLARE_GEOM_CLASS(acd_class, acd); static int acd_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_CDROM) return 0; else return ENXIO; } static int acd_attach(device_t dev) { struct acd_softc *cdp; if (!(cdp = malloc(sizeof(struct acd_softc), M_ACD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } cdp->block_size = 2048; device_set_ivars(dev, cdp); ATA_SETMODE(device_get_parent(dev), dev); ata_controlcmd(dev, ATA_DEVICE_RESET, 0, 0, 0); acd_get_cap(dev); g_post_event(acd_geom_attach, dev, M_WAITOK, NULL); /* announce we are here */ acd_describe(dev); return 0; } static int acd_detach(device_t dev) { g_waitfor_event(acd_geom_detach, dev, M_WAITOK, NULL); return 0; } static void acd_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0); } static int acd_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) { device_set_ivars(dev, NULL); free(cdp, M_ACD); return 1; } ATA_SETMODE(device_get_parent(dev), dev); return 0; } static void acd_geom_attach(void *arg, int flag) { struct ata_device *atadev = device_get_softc(arg); struct acd_softc *cdp = device_get_ivars(arg); struct g_geom *gp; struct g_provider *pp; g_topology_assert(); gp = g_new_geomf(&acd_class, "acd%d", device_get_unit(arg)); gp->softc = arg; cdp->gp = gp; pp = g_new_providerf(gp, "acd%d", device_get_unit(arg)); pp->index = 0; cdp->pp[0] = pp; g_error_provider(pp, 0); atadev->flags |= ATA_D_MEDIA_CHANGED; acd_set_ioparm(arg); } static void acd_geom_detach(void *arg, int flag) { struct acd_softc *cdp = device_get_ivars(arg); /* signal geom so we dont get any further requests */ g_wither_geom(cdp->gp, ENXIO); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(arg); /* dont leave anything behind */ device_set_ivars(arg, NULL); free(cdp, M_ACD); } static int acd_geom_ioctl(struct g_provider *pp, u_long cmd, void *addr, int fflag, struct thread *td) { device_t dev = pp->geom->softc; struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int error = 0, nocopyout = 0; if (!cdp) return ENXIO; if (atadev->flags & ATA_D_MEDIA_CHANGED) { switch (cmd) { case CDIOCRESET: acd_test_ready(dev); break; default: acd_read_toc(dev); acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; break; } } switch (cmd) { case CDIOCRESUME: error = acd_pause_resume(dev, 1); break; case CDIOCPAUSE: error = acd_pause_resume(dev, 0); break; case CDIOCSTART: error = acd_start_stop(dev, 1); break; case CDIOCSTOP: error = acd_start_stop(dev, 0); break; case CDIOCALLOW: error = acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; break; case CDIOCPREVENT: error = acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; break; case CDIOCRESET: error = suser(td); if (error) break; error = acd_test_ready(dev); break; case CDIOCEJECT: if (pp->acr != 1) { error = EBUSY; break; } error = acd_tray(dev, 0); break; case CDIOCCLOSE: if (pp->acr != 1) break; error = acd_tray(dev, 1); break; case CDIOREADTOCHEADER: if (!cdp->toc.hdr.ending_track) { error = EIO; break; } bcopy(&cdp->toc.hdr, addr, sizeof(cdp->toc.hdr)); break; case CDIOREADTOCENTRYS: { struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr; struct toc *toc = &cdp->toc; int starting_track = te->starting_track; int len; if (!toc->hdr.ending_track) { error = EIO; break; } if (te->data_len < sizeof(toc->tab[0]) || (te->data_len % sizeof(toc->tab[0])) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT)) { error = EINVAL; break; } if (!starting_track) starting_track = toc->hdr.starting_track; else if (starting_track == 170) starting_track = toc->hdr.ending_track + 1; else if (starting_track < toc->hdr.starting_track || starting_track > toc->hdr.ending_track + 1) { error = EINVAL; break; } len = ((toc->hdr.ending_track + 1 - starting_track) + 1) * sizeof(toc->tab[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(toc->tab)) { error = EINVAL; break; } if (te->address_format == CD_MSF_FORMAT) { struct cd_toc_entry *entry; if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) { error = ENOMEM; break; } bcopy(&cdp->toc, toc, sizeof(struct toc)); entry = toc->tab + (toc->hdr.ending_track + 1 - toc->hdr.starting_track) + 1; while (--entry >= toc->tab) { lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute, &entry->addr.msf.second, &entry->addr.msf.frame); entry->addr_type = CD_MSF_FORMAT; } } error = copyout(toc->tab + starting_track - toc->hdr.starting_track, te->data, len); if (te->address_format == CD_MSF_FORMAT) free(toc, M_ACD); } break; case CDIOREADTOCENTRY: { struct ioc_read_toc_single_entry *te = (struct ioc_read_toc_single_entry *)addr; struct toc *toc = &cdp->toc; u_char track = te->track; if (!toc->hdr.ending_track) { error = EIO; break; } if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) { error = EINVAL; break; } if (!track) track = toc->hdr.starting_track; else if (track == 170) track = toc->hdr.ending_track + 1; else if (track < toc->hdr.starting_track || track > toc->hdr.ending_track + 1) { error = EINVAL; break; } if (te->address_format == CD_MSF_FORMAT) { struct cd_toc_entry *entry; if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) { error = ENOMEM; break; } bcopy(&cdp->toc, toc, sizeof(struct toc)); entry = toc->tab + (track - toc->hdr.starting_track); lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute, &entry->addr.msf.second, &entry->addr.msf.frame); } bcopy(toc->tab + track - toc->hdr.starting_track, &te->entry, sizeof(struct cd_toc_entry)); if (te->address_format == CD_MSF_FORMAT) free(toc, M_ACD); } break; #if __FreeBSD_version > 600008 case CDIOCREADSUBCHANNEL_SYSSPACE: nocopyout = 1; /* FALLTHROUGH */ #endif case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *)addr; u_int8_t format; int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0, sizeof(cdp->subchan)>>8, sizeof(cdp->subchan), 0, 0, 0, 0, 0, 0, 0 }; if (args->data_len > sizeof(struct cd_sub_channel_info) || args->data_len < sizeof(struct cd_sub_channel_header)) { error = EINVAL; break; } format = args->data_format; if ((format != CD_CURRENT_POSITION) && (format != CD_MEDIA_CATALOG) && (format != CD_TRACK_INFO)) { error = EINVAL; break; } ccb[1] = args->address_format & CD_MSF_FORMAT; if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan, sizeof(cdp->subchan), ATA_R_READ, 10))) break; if ((format == CD_MEDIA_CATALOG) || (format == CD_TRACK_INFO)) { if (cdp->subchan.header.audio_status == 0x11) { error = EINVAL; break; } ccb[3] = format; if (format == CD_TRACK_INFO) ccb[6] = args->track; if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan, sizeof(cdp->subchan),ATA_R_READ,10))){ break; } } if (nocopyout == 0) { error = copyout(&cdp->subchan, args->data, args->data_len); } else { error = 0; bcopy(&cdp->subchan, args->data, args->data_len); } } break; case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *)addr; error = acd_play(dev, msf2lba(args->start_m, args->start_s, args->start_f), msf2lba(args->end_m, args->end_s, args->end_f)); } break; case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr; error = acd_play(dev, args->blk, args->blk + args->len); } break; case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track *)addr; int t1, t2; if (!cdp->toc.hdr.ending_track) { error = EIO; break; } if (args->end_track < cdp->toc.hdr.ending_track + 1) ++args->end_track; if (args->end_track > cdp->toc.hdr.ending_track + 1) args->end_track = cdp->toc.hdr.ending_track + 1; t1 = args->start_track - cdp->toc.hdr.starting_track; t2 = args->end_track - cdp->toc.hdr.starting_track; if (t1 < 0 || t2 < 0 || t1 > (cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track)) { error = EINVAL; break; } error = acd_play(dev, ntohl(cdp->toc.tab[t1].addr.lba), ntohl(cdp->toc.tab[t2].addr.lba)); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) break; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) { error = EIO; break; } arg->vol[0] = cdp->au.port[0].volume; arg->vol[1] = cdp->au.port[1].volume; arg->vol[2] = cdp->au.port[2].volume; arg->vol[3] = cdp->au.port[3].volume; } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) break; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) { error = EIO; break; } if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE_MASK, (caddr_t)&cdp->aumask, sizeof(cdp->aumask)))) break; cdp->au.data_length = 0; cdp->au.port[0].channels = CHANNEL_0; cdp->au.port[1].channels = CHANNEL_1; cdp->au.port[0].volume = arg->vol[0] & cdp->aumask.port[0].volume; cdp->au.port[1].volume = arg->vol[1] & cdp->aumask.port[1].volume; cdp->au.port[2].volume = arg->vol[2] & cdp->aumask.port[2].volume; cdp->au.port[3].volume = arg->vol[3] & cdp->aumask.port[3].volume; error = acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au)); } break; case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; error = acd_setchan(dev, arg->patch[0], arg->patch[1], arg->patch[2], arg->patch[3]); } break; case CDIOCSETMONO: error = acd_setchan(dev, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0,0); break; case CDIOCSETSTEREO: error = acd_setchan(dev, CHANNEL_0, CHANNEL_1, 0, 0); break; case CDIOCSETMUTE: error = acd_setchan(dev, 0, 0, 0, 0); break; case CDIOCSETLEFT: error = acd_setchan(dev, CHANNEL_0, CHANNEL_0, 0, 0); break; case CDIOCSETRIGHT: error = acd_setchan(dev, CHANNEL_1, CHANNEL_1, 0, 0); break; case CDRIOCBLANK: error = acd_blank(dev, (*(int *)addr)); break; case CDRIOCNEXTWRITEABLEADDR: { struct acd_track_info track_info; if ((error = acd_read_track_info(dev, 0xff, &track_info))) break; if (!track_info.nwa_valid) { error = EINVAL; break; } *(int*)addr = track_info.next_writeable_addr; } break; case CDRIOCINITWRITER: error = acd_init_writer(dev, (*(int *)addr)); break; case CDRIOCINITTRACK: error = acd_init_track(dev, (struct cdr_track *)addr); break; case CDRIOCFLUSH: error = acd_flush(dev); break; case CDRIOCFIXATE: error = acd_fixate(dev, (*(int *)addr)); break; case CDRIOCREADSPEED: { int speed = *(int *)addr; /* Preserve old behavior: units in multiples of CDROM speed */ if (speed < 177) speed *= 177; error = acd_set_speed(dev, speed, CDR_MAX_SPEED); } break; case CDRIOCWRITESPEED: { int speed = *(int *)addr; if (speed < 177) speed *= 177; error = acd_set_speed(dev, CDR_MAX_SPEED, speed); } break; case CDRIOCGETBLOCKSIZE: *(int *)addr = cdp->block_size; break; case CDRIOCSETBLOCKSIZE: cdp->block_size = *(int *)addr; pp->sectorsize = cdp->block_size; /* hack for GEOM SOS */ acd_set_ioparm(dev); break; case CDRIOCGETPROGRESS: error = acd_get_progress(dev, (int *)addr); break; case CDRIOCSENDCUE: error = acd_send_cue(dev, (struct cdr_cuesheet *)addr); break; case CDRIOCREADFORMATCAPS: error = acd_read_format_caps(dev, (struct cdr_format_capacities *)addr); break; case CDRIOCFORMAT: error = acd_format(dev, (struct cdr_format_params *)addr); break; case DVDIOCREPORTKEY: if (cdp->cap.media & MST_READ_DVDROM) error = acd_report_key(dev, (struct dvd_authinfo *)addr); else error = EINVAL; break; case DVDIOCSENDKEY: if (cdp->cap.media & MST_READ_DVDROM) error = acd_send_key(dev, (struct dvd_authinfo *)addr); else error = EINVAL; break; case DVDIOCREADSTRUCTURE: if (cdp->cap.media & MST_READ_DVDROM) error = acd_read_structure(dev, (struct dvd_struct *)addr); else error = EINVAL; break; default: error = ata_device_ioctl(dev, cmd, addr); } return error; } static int acd_geom_access(struct g_provider *pp, int dr, int dw, int de) { device_t dev = pp->geom->softc; struct acd_softc *cdp = device_get_ivars(dev); struct ata_request *request; int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int timeout = 60, track; if (!(request = ata_alloc_request())) return ENOMEM; /* wait if drive is not finished loading the medium */ while (timeout--) { bzero(request, sizeof(struct ata_request)); request->dev = dev; bcopy(ccb, request->u.atapi.ccb, 16); request->flags = ATA_R_ATAPI; request->timeout = 5; ata_queue_request(request); if (!request->error && - (request->u.atapi.sense_data.sense_key == 2 || - request->u.atapi.sense_data.sense_key == 7) && - request->u.atapi.sense_data.asc == 4 && - request->u.atapi.sense_data.ascq == 1) + (request->u.atapi.sense.key == 2 || + request->u.atapi.sense.key == 7) && + request->u.atapi.sense.asc == 4 && + request->u.atapi.sense.ascq == 1) tsleep(&timeout, PRIBIO, "acdld", hz / 2); else break; } ata_free_request(request); if (pp->acr == 0) { acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; acd_read_toc(dev); } if (dr + pp->acr == 0) { acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; } if ((track = pp->index)) { pp->sectorsize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352; pp->mediasize = ntohl(cdp->toc.tab[track].addr.lba) - ntohl(cdp->toc.tab[track - 1].addr.lba); } else { pp->sectorsize = cdp->block_size; pp->mediasize = cdp->disk_size; } pp->mediasize *= pp->sectorsize; return 0; } static void acd_geom_start(struct bio *bp) { device_t dev = bp->bio_to->geom->softc; struct acd_softc *cdp = device_get_ivars(dev); if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE) { g_io_deliver(bp, EOPNOTSUPP); return; } if (bp->bio_cmd == BIO_READ && cdp->disk_size == -1) { g_io_deliver(bp, EIO); return; } /* GEOM classes must do their own request limiting */ if (bp->bio_length <= cdp->iomax) { bp->bio_pblkno = bp->bio_offset / bp->bio_to->sectorsize; acd_strategy(bp); } else { u_int pos, size = cdp->iomax - cdp->iomax % bp->bio_to->sectorsize; struct bio *bp2, *bp3; if (!(bp2 = g_clone_bio(bp))) g_io_deliver(bp, EIO); for (pos = 0; bp2; pos += size) { bp3 = NULL; bp2->bio_done = g_std_done; bp2->bio_to = bp->bio_to; bp2->bio_offset += pos; bp2->bio_data += pos; bp2->bio_length = bp->bio_length - pos; if (bp2->bio_length > size) { bp2->bio_length = size; if (!(bp3 = g_clone_bio(bp))) bp->bio_error = ENOMEM; } bp2->bio_pblkno = bp2->bio_offset / bp2->bio_to->sectorsize; acd_strategy(bp2); bp2 = bp3; } } } static void acd_strategy(struct bio *bp) { device_t dev = bp->bio_to->geom->softc; struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); struct ata_request *request; u_int32_t lba, lastlba, count; int8_t ccb[16]; int track, blocksize; /* reject all queued entries if media changed */ if (atadev->flags & ATA_D_MEDIA_CHANGED) { g_io_deliver(bp, EIO); return; } bzero(ccb, sizeof(ccb)); track = bp->bio_to->index; if (track) { blocksize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352; lastlba = ntohl(cdp->toc.tab[track].addr.lba); lba = bp->bio_offset / blocksize; lba += ntohl(cdp->toc.tab[track - 1].addr.lba); } else { blocksize = cdp->block_size; lastlba = cdp->disk_size; lba = bp->bio_offset / blocksize; } count = bp->bio_length / blocksize; if (bp->bio_cmd == BIO_READ) { /* if transfer goes beyond range adjust it to be within limits */ if (lba + count > lastlba) { /* if we are entirely beyond EOM return EOF */ if (lastlba <= lba) { g_io_deliver(bp, 0); return; } count = lastlba - lba; } switch (blocksize) { case 2048: ccb[0] = ATAPI_READ_BIG; break; case 2352: ccb[0] = ATAPI_READ_CD; ccb[9] = 0xf8; break; default: ccb[0] = ATAPI_READ_CD; ccb[9] = 0x10; } } else ccb[0] = ATAPI_WRITE_BIG; ccb[1] = 0; ccb[2] = lba>>24; ccb[3] = lba>>16; ccb[4] = lba>>8; ccb[5] = lba; ccb[6] = count>>16; ccb[7] = count>>8; ccb[8] = count; if (!(request = ata_alloc_request())) { g_io_deliver(bp, ENOMEM); return; } request->dev = dev; request->bio = bp; bcopy(ccb, request->u.atapi.ccb, (atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12 ? 16 : 12); request->data = bp->bio_data; request->bytecount = count * blocksize; request->transfersize = min(request->bytecount, 65534); request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30; request->retries = 2; request->callback = acd_done; request->flags = ATA_R_ATAPI; if (atadev->mode >= ATA_DMA) request->flags |= ATA_R_DMA; switch (bp->bio_cmd) { case BIO_READ: request->flags |= ATA_R_READ; break; case BIO_WRITE: request->flags |= ATA_R_WRITE; break; default: device_printf(dev, "unknown BIO operation\n"); ata_free_request(request); g_io_deliver(bp, EIO); return; } ata_queue_request(request); } static void acd_done(struct ata_request *request) { struct bio *bp = request->bio; /* finish up transfer */ bp->bio_completed = request->donecount; g_io_deliver(bp, request->result); ata_free_request(request); } static void acd_set_ioparm(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct acd_softc *cdp = device_get_ivars(dev); if (ch->dma) cdp->iomax = min(ch->dma->max_iosize, 65534); else cdp->iomax = min(DFLTPHYS, 65534); } static void lba2msf(u_int32_t lba, u_int8_t *m, u_int8_t *s, u_int8_t *f) { lba += 150; lba &= 0xffffff; *m = lba / (60 * 75); lba %= (60 * 75); *s = lba / 75; *f = lba % 75; } static u_int32_t msf2lba(u_int8_t m, u_int8_t s, u_int8_t f) { return (m * 60 + s) * 75 + f - 150; } static void acd_read_toc(device_t dev) { struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); struct g_provider *pp; u_int32_t sizes[2]; int8_t ccb[16]; int track, ntracks, len; atadev->flags &= ~ATA_D_MEDIA_CHANGED; bzero(&cdp->toc, sizeof(cdp->toc)); cdp->disk_size = -1; /* hack for GEOM SOS */ if (acd_test_ready(dev)) return; bzero(ccb, sizeof(ccb)); len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry); ccb[0] = ATAPI_READ_TOC; ccb[7] = len>>8; ccb[8] = len; if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len, ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } ntracks = cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1; if (ntracks <= 0 || ntracks > MAXTRK) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } len = sizeof(struct ioc_toc_header)+(ntracks+1)*sizeof(struct cd_toc_entry); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_TOC; ccb[7] = len>>8; ccb[8] = len; if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len, ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } cdp->toc.hdr.len = ntohs(cdp->toc.hdr.len); cdp->block_size = (cdp->toc.tab[0].control & 4) ? 2048 : 2352; acd_set_ioparm(dev); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_CAPACITY; if (ata_atapicmd(dev, ccb, (caddr_t)sizes, sizeof(sizes), ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } cdp->disk_size = ntohl(sizes[0]) + 1; for (track = 1; track <= ntracks; track ++) { if (cdp->pp[track] != NULL) continue; pp = g_new_providerf(cdp->gp, "acd%dt%02d", device_get_unit(dev),track); pp->index = track; cdp->pp[track] = pp; g_error_provider(pp, 0); } for (; track < MAXTRK; track ++) { if (cdp->pp[track] == NULL) continue; cdp->pp[track]->flags |= G_PF_WITHER; g_orphan_provider(cdp->pp[track], ENXIO); cdp->pp[track] = NULL; } #ifdef ACD_DEBUG if (cdp->disk_size && cdp->toc.hdr.ending_track) { device_printf(dev, "(%d sectors (%d bytes)), %d tracks ", cdp->disk_size, cdp->block_size, cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track+1); if (cdp->toc.tab[0].control & 4) - printf("%dMB\n", cdp->disk_size / 512); + printf("%dMB\n", cdp->disk_size * cdp->block_size / 1048576); else printf("%d:%d audio\n", cdp->disk_size / 75 / 60, cdp->disk_size / 75 % 60); } #endif } static int acd_play(device_t dev, int start, int end) { int8_t ccb[16]; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_PLAY_MSF; lba2msf(start, &ccb[3], &ccb[4], &ccb[5]); lba2msf(end, &ccb[6], &ccb[7], &ccb[8]); return ata_atapicmd(dev, ccb, NULL, 0, 0, 10); } static int acd_setchan(device_t dev, u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3) { struct acd_softc *cdp = device_get_ivars(dev); int error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) return error; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) return EIO; cdp->au.data_length = 0; cdp->au.port[0].channels = c0; cdp->au.port[1].channels = c1; cdp->au.port[2].channels = c2; cdp->au.port[3].channels = c3; return acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au)); } static int acd_init_writer(device_t dev, int test_write) { int8_t ccb[16]; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REZERO; ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60); ccb[0] = ATAPI_SEND_OPC_INFO; ccb[1] = 0x01; ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 30); return 0; } static int acd_fixate(device_t dev, int multisession) { struct acd_softc *cdp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_CLOSE_TRACK, 0x01, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int timeout = 5*60*2; int error, dummy; struct write_param param; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; if (multisession) param.session_type = CDR_SESS_MULTI; else param.session_type = CDR_SESS_NONE; if ((error = acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10))) return error; error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30); if (error) return error; /* some drives just return ready, wait for the expected fixate time */ if ((error = acd_test_ready(dev)) != EBUSY) { timeout = timeout / (cdp->cap.cur_write_speed / 177); tsleep(&error, PRIBIO, "acdfix", timeout * hz / 2); return acd_test_ready(dev); } while (timeout-- > 0) { if ((error = acd_get_progress(dev, &dummy))) return error; if ((error = acd_test_ready(dev)) != EBUSY) return error; tsleep(&error, PRIBIO, "acdcld", hz / 2); } return EIO; } static int acd_init_track(device_t dev, struct cdr_track *track) { struct acd_softc *cdp = device_get_ivars(dev); struct write_param param; int error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE; param.page_length = 0x32; param.test_write = track->test_write ? 1 : 0; param.write_type = CDR_WTYPE_TRACK; param.session_type = CDR_SESS_NONE; param.fp = 0; param.packet_size = 0; if (cdp->cap.capabilities & MST_BURNPROOF) param.burnproof = 1; switch (track->datablock_type) { case CDR_DB_RAW: if (track->preemp) param.track_mode = CDR_TMODE_AUDIO_PREEMP; else param.track_mode = CDR_TMODE_AUDIO; cdp->block_size = 2352; param.datablock_type = CDR_DB_RAW; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_ROM_MODE1: cdp->block_size = 2048; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_ROM_MODE1; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_ROM_MODE2: cdp->block_size = 2336; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_ROM_MODE2; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_XA_MODE1: cdp->block_size = 2048; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE1; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_F1: cdp->block_size = 2056; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_F1; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_F2: cdp->block_size = 2324; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_F2; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_MIX: cdp->block_size = 2332; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_MIX; param.session_format = CDR_SESS_CDROM_XA; break; } acd_set_ioparm(dev); return acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10); } static int acd_flush(device_t dev) { int8_t ccb[16] = { ATAPI_SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60); } static int acd_read_track_info(device_t dev, int32_t lba, struct acd_track_info *info) { int8_t ccb[16] = { ATAPI_READ_TRACK_INFO, 1, lba>>24, lba>>16, lba>>8, lba, 0, sizeof(*info)>>8, sizeof(*info), 0, 0, 0, 0, 0, 0, 0 }; int error; if ((error = ata_atapicmd(dev, ccb, (caddr_t)info, sizeof(*info), ATA_R_READ, 30))) return error; info->track_start_addr = ntohl(info->track_start_addr); info->next_writeable_addr = ntohl(info->next_writeable_addr); info->free_blocks = ntohl(info->free_blocks); info->fixed_packet_size = ntohl(info->fixed_packet_size); info->track_length = ntohl(info->track_length); return 0; } static int acd_get_progress(device_t dev, int *finished) { int8_t ccb[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct ata_request *request; int8_t dummy[8]; if (!(request = ata_alloc_request())) return ENOMEM; request->dev = dev; bcopy(ccb, request->u.atapi.ccb, 16); request->data = dummy; request->bytecount = sizeof(dummy); request->transfersize = min(request->bytecount, 65534); request->flags = ATA_R_ATAPI | ATA_R_READ; request->timeout = 30; ata_queue_request(request); - if (!request->error && request->u.atapi.sense_data.sksv) - *finished = ((request->u.atapi.sense_data.sk_specific2 | - (request->u.atapi.sense_data.sk_specific1<<8))*100)/65535; + if (!request->error && request->u.atapi.sense.error & ATA_SENSE_VALID) + *finished = ((request->u.atapi.sense.specific2 | + (request->u.atapi.sense.specific1 << 8)) * 100) / 65535; else *finished = 0; ata_free_request(request); return 0; } static int acd_send_cue(device_t dev, struct cdr_cuesheet *cuesheet) { struct acd_softc *cdp = device_get_ivars(dev); struct write_param param; int8_t ccb[16] = { ATAPI_SEND_CUE_SHEET, 0, 0, 0, 0, 0, cuesheet->len>>16, cuesheet->len>>8, cuesheet->len, 0, 0, 0, 0, 0, 0, 0 }; int8_t *buffer; int32_t error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE; param.page_length = 0x32; param.test_write = cuesheet->test_write ? 1 : 0; param.write_type = CDR_WTYPE_SESSION; param.session_type = cuesheet->session_type; param.fp = 0; param.packet_size = 0; param.track_mode = CDR_TMODE_AUDIO; param.datablock_type = CDR_DB_RAW; param.session_format = cuesheet->session_format; if (cdp->cap.capabilities & MST_BURNPROOF) param.burnproof = 1; if ((error = acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10))) return error; if (!(buffer = malloc(cuesheet->len, M_ACD, M_NOWAIT))) return ENOMEM; if (!(error = copyin(cuesheet->entries, buffer, cuesheet->len))) error = ata_atapicmd(dev, ccb, buffer, cuesheet->len, 0, 30); free(buffer, M_ACD); return error; } static int acd_report_key(device_t dev, struct dvd_authinfo *ai) { struct dvd_miscauth *d = NULL; u_int32_t lba = 0; int16_t length; int8_t ccb[16]; int error; switch (ai->format) { case DVD_REPORT_AGID: case DVD_REPORT_ASF: case DVD_REPORT_RPC: length = 8; break; case DVD_REPORT_KEY1: length = 12; break; case DVD_REPORT_TITLE_KEY: length = 12; lba = ai->lba; break; case DVD_REPORT_CHALLENGE: length = 16; break; case DVD_INVALIDATE_AGID: length = 0; break; default: return EINVAL; } bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REPORT_KEY; ccb[2] = (lba >> 24) & 0xff; ccb[3] = (lba >> 16) & 0xff; ccb[4] = (lba >> 8) & 0xff; ccb[5] = lba & 0xff; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = (ai->agid << 6) | ai->format; if (length) { if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->length = htons(length - 2); } error = ata_atapicmd(dev, ccb, (caddr_t)d, length, ai->format == DVD_INVALIDATE_AGID ? 0 : ATA_R_READ,10); if (error) { free(d, M_ACD); return error; } switch (ai->format) { case DVD_REPORT_AGID: ai->agid = d->data[3] >> 6; break; case DVD_REPORT_CHALLENGE: bcopy(&d->data[0], &ai->keychal[0], 10); break; case DVD_REPORT_KEY1: bcopy(&d->data[0], &ai->keychal[0], 5); break; case DVD_REPORT_TITLE_KEY: ai->cpm = (d->data[0] >> 7); ai->cp_sec = (d->data[0] >> 6) & 0x1; ai->cgms = (d->data[0] >> 4) & 0x3; bcopy(&d->data[1], &ai->keychal[0], 5); break; case DVD_REPORT_ASF: ai->asf = d->data[3] & 1; break; case DVD_REPORT_RPC: ai->reg_type = (d->data[0] >> 6); ai->vend_rsts = (d->data[0] >> 3) & 0x7; ai->user_rsts = d->data[0] & 0x7; ai->region = d->data[1]; ai->rpc_scheme = d->data[2]; break; case DVD_INVALIDATE_AGID: break; default: error = EINVAL; } free(d, M_ACD); return error; } static int acd_send_key(device_t dev, struct dvd_authinfo *ai) { struct dvd_miscauth *d; int16_t length; int8_t ccb[16]; int error; switch (ai->format) { case DVD_SEND_CHALLENGE: length = 16; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; bcopy(ai->keychal, &d->data[0], 10); break; case DVD_SEND_KEY2: length = 12; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; bcopy(&ai->keychal[0], &d->data[0], 5); break; case DVD_SEND_RPC: length = 8; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->data[0] = ai->region; break; default: return EINVAL; } bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_SEND_KEY; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = (ai->agid << 6) | ai->format; d->length = htons(length - 2); error = ata_atapicmd(dev, ccb, (caddr_t)d, length, 0, 10); free(d, M_ACD); return error; } static int acd_read_structure(device_t dev, struct dvd_struct *s) { struct dvd_miscauth *d; u_int16_t length; int8_t ccb[16]; int error = 0; switch(s->format) { case DVD_STRUCT_PHYSICAL: length = 21; break; case DVD_STRUCT_COPYRIGHT: length = 8; break; case DVD_STRUCT_DISCKEY: length = 2052; break; case DVD_STRUCT_BCA: length = 192; break; case DVD_STRUCT_MANUFACT: length = 2052; break; case DVD_STRUCT_DDS: case DVD_STRUCT_PRERECORDED: case DVD_STRUCT_UNIQUEID: case DVD_STRUCT_LIST: case DVD_STRUCT_CMI: case DVD_STRUCT_RMD_LAST: case DVD_STRUCT_RMD_RMA: case DVD_STRUCT_DCB: return ENOSYS; default: return EINVAL; } if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->length = htons(length - 2); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_STRUCTURE; ccb[6] = s->layer_num; ccb[7] = s->format; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = s->agid << 6; error = ata_atapicmd(dev, ccb, (caddr_t)d, length, ATA_R_READ, 30); if (error) { free(d, M_ACD); return error; } switch (s->format) { case DVD_STRUCT_PHYSICAL: { struct dvd_layer *layer = (struct dvd_layer *)&s->data[0]; layer->book_type = d->data[0] >> 4; layer->book_version = d->data[0] & 0xf; layer->disc_size = d->data[1] >> 4; layer->max_rate = d->data[1] & 0xf; layer->nlayers = (d->data[2] >> 5) & 3; layer->track_path = (d->data[2] >> 4) & 1; layer->layer_type = d->data[2] & 0xf; layer->linear_density = d->data[3] >> 4; layer->track_density = d->data[3] & 0xf; layer->start_sector = d->data[5] << 16 | d->data[6] << 8 | d->data[7]; layer->end_sector = d->data[9] << 16 | d->data[10] << 8 | d->data[11]; layer->end_sector_l0 = d->data[13] << 16 | d->data[14] << 8|d->data[15]; layer->bca = d->data[16] >> 7; break; } case DVD_STRUCT_COPYRIGHT: s->cpst = d->data[0]; s->rmi = d->data[1]; break; case DVD_STRUCT_DISCKEY: bcopy(&d->data[0], &s->data[0], 2048); break; case DVD_STRUCT_BCA: s->length = ntohs(d->length); bcopy(&d->data[0], &s->data[0], s->length); break; case DVD_STRUCT_MANUFACT: s->length = ntohs(d->length); bcopy(&d->data[0], &s->data[0], s->length); break; default: error = EINVAL; } free(d, M_ACD); return error; } static int acd_tray(device_t dev, int close) { struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int error = ENODEV; if (cdp->cap.mechanism & MST_EJECT) { if (close) { if (!(error = acd_start_stop(dev, 3))) { acd_read_toc(dev); acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; } } else { acd_start_stop(dev, 0); acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; atadev->flags |= ATA_D_MEDIA_CHANGED; error = acd_start_stop(dev, 2); } } return error; } static int acd_blank(device_t dev, int blanktype) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_BLANK, 0x10 | (blanktype & 0x7), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; atadev->flags |= ATA_D_MEDIA_CHANGED; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_prevent_allow(device_t dev, int lock) { int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_start_stop(device_t dev, int start) { int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_pause_resume(device_t dev, int pause) { int8_t ccb[16] = { ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, pause, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_mode_sense(device_t dev, int page, caddr_t pagebuf, int pagesize) { int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, page, 0, 0, 0, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10); return error; } static int acd_mode_select(device_t dev, caddr_t pagebuf, int pagesize) { int8_t ccb[16] = { ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, pagebuf, pagesize, 0, 30); } static int acd_set_speed(device_t dev, int rdspeed, int wrspeed) { int8_t ccb[16] = { ATAPI_SET_SPEED, 0, rdspeed >> 8, rdspeed, wrspeed >> 8, wrspeed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30); if (!error) acd_get_cap(dev); return error; } static void acd_get_cap(device_t dev) { struct acd_softc *cdp = device_get_ivars(dev); int count; /* get drive capabilities, some bugridden drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!acd_mode_sense(dev, ATAPI_CDROM_CAP_PAGE, (caddr_t)&cdp->cap, sizeof(cdp->cap)) && cdp->cap.page_code == ATAPI_CDROM_CAP_PAGE) { cdp->cap.max_read_speed = ntohs(cdp->cap.max_read_speed); cdp->cap.cur_read_speed = ntohs(cdp->cap.cur_read_speed); cdp->cap.max_write_speed = ntohs(cdp->cap.max_write_speed); cdp->cap.cur_write_speed = max(ntohs(cdp->cap.cur_write_speed),177); cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels); cdp->cap.buf_size = ntohs(cdp->cap.buf_size); } } } static int acd_read_format_caps(device_t dev, struct cdr_format_capacities *caps) { int8_t ccb[16] = { ATAPI_READ_FORMAT_CAPACITIES, 0, 0, 0, 0, 0, 0, (sizeof(struct cdr_format_capacities) >> 8) & 0xff, sizeof(struct cdr_format_capacities) & 0xff, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, (caddr_t)caps, sizeof(struct cdr_format_capacities), ATA_R_READ, 30); } static int acd_format(device_t dev, struct cdr_format_params* params) { int8_t ccb[16] = { ATAPI_FORMAT, 0x11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, (u_int8_t *)params, sizeof(struct cdr_format_params), 0, 30); return error; } static int acd_test_ready(device_t dev) { int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static void acd_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int comma = 0; char *mechanism; if (bootverbose) { device_printf(dev, "<%.40s/%.8s> %s drive at ata%d as %s\n", atadev->param.model, atadev->param.revision, (cdp->cap.media & MST_WRITE_DVDR) ? "DVDR" : (cdp->cap.media & MST_WRITE_DVDRAM) ? "DVDRAM" : (cdp->cap.media & MST_WRITE_CDRW) ? "CDRW" : (cdp->cap.media & MST_WRITE_CDR) ? "CDR" : (cdp->cap.media & MST_READ_DVDROM) ? "DVDROM":"CDROM", device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave"); device_printf(dev, "%s", ""); if (cdp->cap.cur_read_speed) { printf("read %dKB/s", cdp->cap.cur_read_speed * 1000 / 1024); if (cdp->cap.max_read_speed) printf(" (%dKB/s)", cdp->cap.max_read_speed * 1000 / 1024); if ((cdp->cap.cur_write_speed) && (cdp->cap.media & (MST_WRITE_CDR | MST_WRITE_CDRW | MST_WRITE_DVDR | MST_WRITE_DVDRAM))) { printf(" write %dKB/s", cdp->cap.cur_write_speed * 1000 / 1024); if (cdp->cap.max_write_speed) printf(" (%dKB/s)", cdp->cap.max_write_speed * 1000 / 1024); } comma = 1; } if (cdp->cap.buf_size) { printf("%s %dKB buffer", comma ? "," : "", cdp->cap.buf_size); comma = 1; } printf("%s %s\n", comma ? "," : "", ata_mode2str(atadev->mode)); device_printf(dev, "Reads:"); comma = 0; if (cdp->cap.media & MST_READ_CDR) { printf(" CDR"); comma = 1; } if (cdp->cap.media & MST_READ_CDRW) { printf("%s CDRW", comma ? "," : ""); comma = 1; } if (cdp->cap.capabilities & MST_READ_CDDA) { if (cdp->cap.capabilities & MST_CDDA_STREAM) printf("%s CDDA stream", comma ? "," : ""); else printf("%s CDDA", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_READ_DVDROM) { printf("%s DVDROM", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_READ_DVDR) { printf("%s DVDR", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_READ_DVDRAM) { printf("%s DVDRAM", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_READ_PACKET) printf("%s packet", comma ? "," : ""); printf("\n"); device_printf(dev, "Writes:"); if (cdp->cap.media & (MST_WRITE_CDR | MST_WRITE_CDRW | MST_WRITE_DVDR | MST_WRITE_DVDRAM)) { comma = 0; if (cdp->cap.media & MST_WRITE_CDR) { printf(" CDR" ); comma = 1; } if (cdp->cap.media & MST_WRITE_CDRW) { printf("%s CDRW", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_WRITE_DVDR) { printf("%s DVDR", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_WRITE_DVDRAM) { printf("%s DVDRAM", comma ? "," : ""); comma = 1; } if (cdp->cap.media & MST_WRITE_TEST) { printf("%s test write", comma ? "," : ""); comma = 1; } if (cdp->cap.capabilities & MST_BURNPROOF) printf("%s burnproof", comma ? "," : ""); } printf("\n"); if (cdp->cap.capabilities & MST_AUDIO_PLAY) { device_printf(dev, "Audio: "); if (cdp->cap.capabilities & MST_AUDIO_PLAY) printf("play"); if (cdp->cap.max_vol_levels) printf(", %d volume levels", cdp->cap.max_vol_levels); printf("\n"); } device_printf(dev, "Mechanism: "); switch (cdp->cap.mechanism & MST_MECH_MASK) { case MST_MECH_CADDY: mechanism = "caddy"; break; case MST_MECH_TRAY: mechanism = "tray"; break; case MST_MECH_POPUP: mechanism = "popup"; break; case MST_MECH_CHANGER: mechanism = "changer"; break; case MST_MECH_CARTRIDGE: mechanism = "cartridge"; break; default: mechanism = 0; break; } if (mechanism) printf("%s%s", (cdp->cap.mechanism & MST_EJECT) ? "ejectable " : "", mechanism); else if (cdp->cap.mechanism & MST_EJECT) printf("ejectable"); if (cdp->cap.mechanism & MST_LOCKABLE) printf((cdp->cap.mechanism & MST_LOCKED) ? ", locked":", unlocked"); if (cdp->cap.mechanism & MST_PREVENT) printf(", lock protected"); printf("\n"); if ((cdp->cap.mechanism & MST_MECH_MASK) != MST_MECH_CHANGER) { device_printf(dev, "Medium: "); switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) { case MST_CDROM: printf("CD-ROM "); break; case MST_CDR: printf("CD-R "); break; case MST_CDRW: printf("CD-RW "); break; case MST_DOOR_OPEN: printf("door open"); break; case MST_NO_DISC: printf("no/blank disc"); break; case MST_FMT_ERROR: printf("medium format error"); break; } if ((cdp->cap.medium_type & MST_TYPE_MASK_HIGH)cap.medium_type & MST_TYPE_MASK_LOW) { case MST_DATA_120: printf("120mm data disc"); break; case MST_AUDIO_120: printf("120mm audio disc"); break; case MST_COMB_120: printf("120mm data/audio disc"); break; case MST_PHOTO_120: printf("120mm photo disc"); break; case MST_DATA_80: printf("80mm data disc"); break; case MST_AUDIO_80: printf("80mm audio disc"); break; case MST_COMB_80: printf("80mm data/audio disc"); break; case MST_PHOTO_80: printf("80mm photo disc"); break; case MST_FMT_NONE: switch (cdp->cap.medium_type & MST_TYPE_MASK_HIGH) { case MST_CDROM: printf("unknown"); break; case MST_CDR: case MST_CDRW: printf("blank"); break; } break; default: printf("unknown (0x%x)", cdp->cap.medium_type); break; } } printf("\n"); } } else { device_printf(dev, "%s ", (cdp->cap.media & MST_WRITE_DVDR) ? "DVDR" : (cdp->cap.media & MST_WRITE_DVDRAM) ? "DVDRAM" : (cdp->cap.media & MST_WRITE_CDRW) ? "CDRW" : (cdp->cap.media & MST_WRITE_CDR) ? "CDR" : (cdp->cap.media & MST_READ_DVDROM) ? "DVDROM" : "CDROM"); if (cdp->changer_info) printf("with %d CD changer ", cdp->changer_info->slots); printf("<%.40s/%.8s> at ata%d-%s %s\n", atadev->param.model, atadev->param.revision, device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave", ata_mode2str(atadev->mode) ); } } static device_method_t acd_methods[] = { /* device interface */ DEVMETHOD(device_probe, acd_probe), DEVMETHOD(device_attach, acd_attach), DEVMETHOD(device_detach, acd_detach), DEVMETHOD(device_shutdown, acd_shutdown), /* ATA methods */ DEVMETHOD(ata_reinit, acd_reinit), { 0, 0 } }; static driver_t acd_driver = { "acd", acd_methods, 0, }; static devclass_t acd_devclass; static int acd_modevent(module_t mod, int what, void *arg) { g_modevent(0, what, &acd_class); return 0; } DRIVER_MODULE(acd, ata, acd_driver, acd_devclass, acd_modevent, NULL); MODULE_VERSION(acd, 1); MODULE_DEPEND(acd, ata, 1, 1, 1); Index: head/sys/sys/ata.h =================================================================== --- head/sys/sys/ata.h (revision 157328) +++ head/sys/sys/ata.h (revision 157329) @@ -1,413 +1,456 @@ /*- * Copyright (c) 2000 - 2006 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * $FreeBSD$ */ #ifndef _SYS_ATA_H_ #define _SYS_ATA_H_ #include /* ATA/ATAPI device parameters */ struct ata_params { /*000*/ u_int16_t config; /* configuration info */ #define ATA_PROTO_MASK 0x8003 #define ATA_PROTO_ATAPI 0x8000 #define ATA_PROTO_ATAPI_12 0x8000 #define ATA_PROTO_ATAPI_16 0x8001 #define ATA_ATAPI_TYPE_MASK 0x1f00 #define ATA_ATAPI_TYPE_DIRECT 0x0000 /* disk/floppy */ #define ATA_ATAPI_TYPE_TAPE 0x0100 /* streaming tape */ #define ATA_ATAPI_TYPE_CDROM 0x0500 /* CD-ROM device */ #define ATA_ATAPI_TYPE_OPTICAL 0x0700 /* optical disk */ #define ATA_DRQ_MASK 0x0060 #define ATA_DRQ_SLOW 0x0000 /* cpu 3 ms delay */ #define ATA_DRQ_INTR 0x0020 /* interrupt 10 ms delay */ #define ATA_DRQ_FAST 0x0040 /* accel 50 us delay */ /*001*/ u_int16_t cylinders; /* # of cylinders */ u_int16_t reserved2; /*003*/ u_int16_t heads; /* # heads */ u_int16_t obsolete4; u_int16_t obsolete5; /*006*/ u_int16_t sectors; /* # sectors/track */ /*007*/ u_int16_t vendor7[3]; /*010*/ u_int8_t serial[20]; /* serial number */ /*020*/ u_int16_t retired20; u_int16_t retired21; u_int16_t obsolete22; /*023*/ u_int8_t revision[8]; /* firmware revision */ /*027*/ u_int8_t model[40]; /* model name */ /*047*/ u_int16_t sectors_intr; /* sectors per interrupt */ /*048*/ u_int16_t usedmovsd; /* double word read/write? */ /*049*/ u_int16_t capabilities1; #define ATA_SUPPORT_DMA 0x0100 #define ATA_SUPPORT_LBA 0x0200 #define ATA_SUPPORT_OVERLAP 0x4000 /*050*/ u_int16_t capabilities2; /*051*/ u_int16_t retired_piomode; /* PIO modes 0-2 */ #define ATA_RETIRED_PIO_MASK 0x0300 /*052*/ u_int16_t retired_dmamode; /* DMA modes */ #define ATA_RETIRED_DMA_MASK 0x0003 /*053*/ u_int16_t atavalid; /* fields valid */ #define ATA_FLAG_54_58 0x0001 /* words 54-58 valid */ #define ATA_FLAG_64_70 0x0002 /* words 64-70 valid */ #define ATA_FLAG_88 0x0004 /* word 88 valid */ /*054*/ u_int16_t current_cylinders; /*055*/ u_int16_t current_heads; /*056*/ u_int16_t current_sectors; /*057*/ u_int16_t current_size_1; /*058*/ u_int16_t current_size_2; /*059*/ u_int16_t multi; #define ATA_MULTI_VALID 0x0100 /*060*/ u_int16_t lba_size_1; u_int16_t lba_size_2; u_int16_t obsolete62; /*063*/ u_int16_t mwdmamodes; /* multiword DMA modes */ /*064*/ u_int16_t apiomodes; /* advanced PIO modes */ /*065*/ u_int16_t mwdmamin; /* min. M/W DMA time/word ns */ /*066*/ u_int16_t mwdmarec; /* rec. M/W DMA time ns */ /*067*/ u_int16_t pioblind; /* min. PIO cycle w/o flow */ /*068*/ u_int16_t pioiordy; /* min. PIO cycle IORDY flow */ u_int16_t reserved69; u_int16_t reserved70; /*071*/ u_int16_t rlsovlap; /* rel time (us) for overlap */ /*072*/ u_int16_t rlsservice; /* rel time (us) for service */ u_int16_t reserved73; u_int16_t reserved74; /*075*/ u_int16_t queue; #define ATA_QUEUE_LEN(x) ((x) & 0x001f) u_int16_t satacapabilities; #define ATA_SATA_GEN1 0x0002 #define ATA_SATA_GEN2 0x0004 #define ATA_SUPPORT_NCQ 0x0100 #define ATA_SUPPORT_IFPWRMNGTRCV 0x0200 u_int16_t reserved77; u_int16_t satasupport; #define ATA_SUPPORT_NONZERO 0x0002 #define ATA_SUPPORT_AUTOACTIVATE 0x0004 #define ATA_SUPPORT_IFPWRMNGT 0x0008 #define ATA_SUPPORT_INORDERDATA 0x0010 u_int16_t sataenabled; /*080*/ u_int16_t version_major; /*081*/ u_int16_t version_minor; struct { /*082/085*/ u_int16_t command1; #define ATA_SUPPORT_SMART 0x0001 #define ATA_SUPPORT_SECURITY 0x0002 #define ATA_SUPPORT_REMOVABLE 0x0004 #define ATA_SUPPORT_POWERMGT 0x0008 #define ATA_SUPPORT_PACKET 0x0010 #define ATA_SUPPORT_WRITECACHE 0x0020 #define ATA_SUPPORT_LOOKAHEAD 0x0040 #define ATA_SUPPORT_RELEASEIRQ 0x0080 #define ATA_SUPPORT_SERVICEIRQ 0x0100 #define ATA_SUPPORT_RESET 0x0200 #define ATA_SUPPORT_PROTECTED 0x0400 #define ATA_SUPPORT_WRITEBUFFER 0x1000 #define ATA_SUPPORT_READBUFFER 0x2000 #define ATA_SUPPORT_NOP 0x4000 /*083/086*/ u_int16_t command2; #define ATA_SUPPORT_MICROCODE 0x0001 #define ATA_SUPPORT_QUEUED 0x0002 #define ATA_SUPPORT_CFA 0x0004 #define ATA_SUPPORT_APM 0x0008 #define ATA_SUPPORT_NOTIFY 0x0010 #define ATA_SUPPORT_STANDBY 0x0020 #define ATA_SUPPORT_SPINUP 0x0040 #define ATA_SUPPORT_MAXSECURITY 0x0100 #define ATA_SUPPORT_AUTOACOUSTIC 0x0200 #define ATA_SUPPORT_ADDRESS48 0x0400 #define ATA_SUPPORT_OVERLAY 0x0800 #define ATA_SUPPORT_FLUSHCACHE 0x1000 #define ATA_SUPPORT_FLUSHCACHE48 0x2000 /*084/087*/ u_int16_t extension; } __packed support, enabled; /*088*/ u_int16_t udmamodes; /* UltraDMA modes */ /*089*/ u_int16_t erase_time; /*090*/ u_int16_t enhanced_erase_time; /*091*/ u_int16_t apm_value; /*092*/ u_int16_t master_passwd_revision; /*093*/ u_int16_t hwres; #define ATA_CABLE_ID 0x2000 /*094*/ u_int16_t acoustic; #define ATA_ACOUSTIC_CURRENT(x) ((x) & 0x00ff) #define ATA_ACOUSTIC_VENDOR(x) (((x) & 0xff00) >> 8) /*095*/ u_int16_t stream_min_req_size; /*096*/ u_int16_t stream_transfer_time; /*097*/ u_int16_t stream_access_latency; /*098*/ u_int32_t stream_granularity; /*100*/ u_int16_t lba_size48_1; u_int16_t lba_size48_2; u_int16_t lba_size48_3; u_int16_t lba_size48_4; u_int16_t reserved104[23]; /*127*/ u_int16_t removable_status; /*128*/ u_int16_t security_status; u_int16_t reserved129[31]; /*160*/ u_int16_t cfa_powermode1; u_int16_t reserved161[15]; /*176*/ u_int16_t media_serial[30]; u_int16_t reserved206[49]; /*255*/ u_int16_t integrity; } __packed; /* ATA transfer modes */ #define ATA_MODE_MASK 0x0f #define ATA_DMA_MASK 0xf0 #define ATA_PIO 0x00 #define ATA_PIO0 0x08 #define ATA_PIO1 0x09 #define ATA_PIO2 0x0a #define ATA_PIO3 0x0b #define ATA_PIO4 0x0c #define ATA_PIO_MAX 0x0f #define ATA_DMA 0x10 #define ATA_WDMA0 0x20 #define ATA_WDMA1 0x21 #define ATA_WDMA2 0x22 #define ATA_UDMA0 0x40 #define ATA_UDMA1 0x41 #define ATA_UDMA2 0x42 #define ATA_UDMA3 0x43 #define ATA_UDMA4 0x44 #define ATA_UDMA5 0x45 #define ATA_UDMA6 0x46 #define ATA_SA150 0x47 #define ATA_SA300 0x48 #define ATA_DMA_MAX 0x4f #define ATA_USB 0x80 #define ATA_USB1 0x81 #define ATA_USB2 0x82 /* ATA commands */ #define ATA_NOP 0x00 /* NOP */ #define ATA_NF_FLUSHQUEUE 0x00 /* flush queued cmd's */ #define ATA_NF_AUTOPOLL 0x01 /* start autopoll function */ #define ATA_DEVICE_RESET 0x08 /* reset device */ #define ATA_READ 0x20 /* read */ #define ATA_READ48 0x24 /* read 48bit LBA */ #define ATA_READ_DMA48 0x25 /* read DMA 48bit LBA */ #define ATA_READ_DMA_QUEUED48 0x26 /* read DMA QUEUED 48bit LBA */ #define ATA_READ_NATIVE_MAX_ADDDRESS48 0x27 /* read native max addr 48bit */ #define ATA_READ_MUL48 0x29 /* read multi 48bit LBA */ #define ATA_WRITE 0x30 /* write */ #define ATA_WRITE48 0x34 /* write 48bit LBA */ #define ATA_WRITE_DMA48 0x35 /* write DMA 48bit LBA */ #define ATA_WRITE_DMA_QUEUED48 0x36 /* write DMA QUEUED 48bit LBA*/ #define ATA_SET_MAX_ADDRESS48 0x37 /* set max address 48bit */ #define ATA_WRITE_MUL48 0x39 /* write multi 48bit LBA */ #define ATA_READ_FPDMA_QUEUED 0x60 /* read DMA NCQ */ #define ATA_WRITE_FPDMA_QUEUED 0x61 /* write DMA NCQ */ #define ATA_SEEK 0x70 /* seek */ #define ATA_PACKET_CMD 0xa0 /* packet command */ #define ATA_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/ #define ATA_SERVICE 0xa2 /* service command */ #define ATA_CFA_ERASE 0xc0 /* CFA erase */ #define ATA_READ_MUL 0xc4 /* read multi */ #define ATA_WRITE_MUL 0xc5 /* write multi */ #define ATA_SET_MULTI 0xc6 /* set multi size */ #define ATA_READ_DMA_QUEUED 0xc7 /* read DMA QUEUED */ #define ATA_READ_DMA 0xc8 /* read DMA */ #define ATA_WRITE_DMA 0xca /* write DMA */ #define ATA_WRITE_DMA_QUEUED 0xcc /* write DMA QUEUED */ #define ATA_STANDBY_IMMEDIATE 0xe0 /* standby immediate */ #define ATA_IDLE_IMMEDIATE 0xe1 /* idle immediate */ #define ATA_STANDBY_CMD 0xe2 /* standby */ #define ATA_IDLE_CMD 0xe3 /* idle */ #define ATA_READ_BUFFER 0xe4 /* read buffer */ #define ATA_SLEEP 0xe6 /* sleep */ #define ATA_FLUSHCACHE 0xe7 /* flush cache to disk */ #define ATA_FLUSHCACHE48 0xea /* flush cache to disk */ #define ATA_ATA_IDENTIFY 0xec /* get ATA params */ #define ATA_SETFEATURES 0xef /* features command */ #define ATA_SF_SETXFER 0x03 /* set transfer mode */ #define ATA_SF_ENAB_WCACHE 0x02 /* enable write cache */ #define ATA_SF_DIS_WCACHE 0x82 /* disable write cache */ #define ATA_SF_ENAB_RCACHE 0xaa /* enable readahead cache */ #define ATA_SF_DIS_RCACHE 0x55 /* disable readahead cache */ #define ATA_SF_ENAB_RELIRQ 0x5d /* enable release interrupt */ #define ATA_SF_DIS_RELIRQ 0xdd /* disable release interrupt */ #define ATA_SF_ENAB_SRVIRQ 0x5e /* enable service interrupt */ #define ATA_SF_DIS_SRVIRQ 0xde /* disable service interrupt */ #define ATA_SECURITY_FREEE_LOCK 0xf5 /* freeze security config */ #define ATA_READ_NATIVE_MAX_ADDDRESS 0xf8 /* read native max address */ #define ATA_SET_MAX_ADDRESS 0xf9 /* set max address */ /* ATAPI commands */ #define ATAPI_TEST_UNIT_READY 0x00 /* check if device is ready */ #define ATAPI_REZERO 0x01 /* rewind */ #define ATAPI_REQUEST_SENSE 0x03 /* get sense data */ #define ATAPI_FORMAT 0x04 /* format unit */ #define ATAPI_READ 0x08 /* read data */ #define ATAPI_WRITE 0x0a /* write data */ #define ATAPI_WEOF 0x10 /* write filemark */ #define ATAPI_WF_WRITE 0x01 #define ATAPI_SPACE 0x11 /* space command */ #define ATAPI_SP_FM 0x01 #define ATAPI_SP_EOD 0x03 #define ATAPI_INQUIRY 0x12 /* get inquiry data */ #define ATAPI_MODE_SELECT 0x15 /* mode select */ #define ATAPI_ERASE 0x19 /* erase */ #define ATAPI_MODE_SENSE 0x1a /* mode sense */ #define ATAPI_START_STOP 0x1b /* start/stop unit */ #define ATAPI_SS_LOAD 0x01 #define ATAPI_SS_RETENSION 0x02 #define ATAPI_SS_EJECT 0x04 #define ATAPI_PREVENT_ALLOW 0x1e /* media removal */ #define ATAPI_READ_FORMAT_CAPACITIES 0x23 /* get format capacities */ #define ATAPI_READ_CAPACITY 0x25 /* get volume capacity */ #define ATAPI_READ_BIG 0x28 /* read data */ #define ATAPI_WRITE_BIG 0x2a /* write data */ #define ATAPI_LOCATE 0x2b /* locate to position */ #define ATAPI_READ_POSITION 0x34 /* read position */ #define ATAPI_SYNCHRONIZE_CACHE 0x35 /* flush buf, close channel */ #define ATAPI_WRITE_BUFFER 0x3b /* write device buffer */ #define ATAPI_READ_BUFFER 0x3c /* read device buffer */ #define ATAPI_READ_SUBCHANNEL 0x42 /* get subchannel info */ #define ATAPI_READ_TOC 0x43 /* get table of contents */ #define ATAPI_PLAY_10 0x45 /* play by lba */ #define ATAPI_PLAY_MSF 0x47 /* play by MSF address */ #define ATAPI_PLAY_TRACK 0x48 /* play by track number */ #define ATAPI_PAUSE 0x4b /* pause audio operation */ #define ATAPI_READ_DISK_INFO 0x51 /* get disk info structure */ #define ATAPI_READ_TRACK_INFO 0x52 /* get track info structure */ #define ATAPI_RESERVE_TRACK 0x53 /* reserve track */ #define ATAPI_SEND_OPC_INFO 0x54 /* send OPC structurek */ #define ATAPI_MODE_SELECT_BIG 0x55 /* set device parameters */ #define ATAPI_REPAIR_TRACK 0x58 /* repair track */ #define ATAPI_READ_MASTER_CUE 0x59 /* read master CUE info */ #define ATAPI_MODE_SENSE_BIG 0x5a /* get device parameters */ #define ATAPI_CLOSE_TRACK 0x5b /* close track/session */ #define ATAPI_READ_BUFFER_CAPACITY 0x5c /* get buffer capicity */ #define ATAPI_SEND_CUE_SHEET 0x5d /* send CUE sheet */ #define ATAPI_SERVICE_ACTION_IN 0x96 /* get service data */ #define ATAPI_BLANK 0xa1 /* blank the media */ #define ATAPI_SEND_KEY 0xa3 /* send DVD key structure */ #define ATAPI_REPORT_KEY 0xa4 /* get DVD key structure */ #define ATAPI_PLAY_12 0xa5 /* play by lba */ #define ATAPI_LOAD_UNLOAD 0xa6 /* changer control command */ #define ATAPI_READ_STRUCTURE 0xad /* get DVD structure */ #define ATAPI_PLAY_CD 0xb4 /* universal play command */ #define ATAPI_SET_SPEED 0xbb /* set drive speed */ #define ATAPI_MECH_STATUS 0xbd /* get changer status */ #define ATAPI_READ_CD 0xbe /* read data */ #define ATAPI_POLL_DSC 0xff /* poll DSC status bit */ struct ata_ioc_devices { int channel; char name[2][32]; struct ata_params params[2]; }; /* pr channel ATA ioctl calls */ #define IOCATAGMAXCHANNEL _IOR('a', 1, int) #define IOCATAREINIT _IOW('a', 2, int) #define IOCATAATTACH _IOW('a', 3, int) #define IOCATADETACH _IOW('a', 4, int) #define IOCATADEVICES _IOWR('a', 5, struct ata_ioc_devices) +/* ATAPI request sense structure */ +struct atapi_sense { + u_int8_t error; /* current or deferred errors */ +#define ATA_SENSE_VALID 0x80 + + u_int8_t segment; /* segment number */ + u_int8_t key; /* sense key */ +#define ATA_SENSE_KEY_MASK 0x0f /* sense key mask */ +#define ATA_SENSE_NO_SENSE 0x00 /* no specific sense key info */ +#define ATA_SENSE_RECOVERED_ERROR 0x01 /* command OK, data recovered */ +#define ATA_SENSE_NOT_READY 0x02 /* no access to drive */ +#define ATA_SENSE_MEDIUM_ERROR 0x03 /* non-recovered data error */ +#define ATA_SENSE_HARDWARE_ERROR 0x04 /* non-recoverable HW failure */ +#define ATA_SENSE_ILLEGAL_REQUEST 0x05 /* invalid command param(s) */ +#define ATA_SENSE_UNIT_ATTENTION 0x06 /* media changed */ +#define ATA_SENSE_DATA_PROTECT 0x07 /* write protect */ +#define ATA_SENSE_BLANK_CHECK 0x08 /* blank check */ +#define ATA_SENSE_VENDOR_SPECIFIC 0x09 /* vendor specific skey */ +#define ATA_SENSE_COPY_ABORTED 0x0a /* copy aborted */ +#define ATA_SENSE_ABORTED_COMMAND 0x0b /* command aborted, try again */ +#define ATA_SENSE_EQUAL 0x0c /* equal */ +#define ATA_SENSE_VOLUME_OVERFLOW 0x0d /* volume overflow */ +#define ATA_SENSE_MISCOMPARE 0x0e /* data dont match the medium */ +#define ATA_SENSE_RESERVED 0x0f +#define ATA_SENSE_ILI 0x20; +#define ATA_SENSE_EOM 0x40; +#define ATA_SENSE_FILEMARK 0x80; + + u_int32_t cmd_info; /* cmd information */ + u_int8_t sense_length; /* additional sense len (n-7) */ + u_int32_t cmd_specific_info; /* additional cmd spec info */ + u_int8_t asc; /* additional sense code */ + u_int8_t ascq; /* additional sense code qual */ + u_int8_t replaceable_unit_code; /* replaceable unit code */ + u_int8_t specific; /* sense key specific */ +#define ATA_SENSE_SPEC_VALID 0x80 +#define ATA_SENSE_SPEC_MASK 0x7f + + u_int8_t specific1; /* sense key specific */ + u_int8_t specific2; /* sense key specific */ +} __packed; + struct ata_ioc_request { union { struct { u_int8_t command; u_int8_t feature; u_int64_t lba; u_int16_t count; } ata; struct { char ccb[16]; + struct atapi_sense sense; } atapi; } u; caddr_t data; int count; int flags; #define ATA_CMD_CONTROL 0x01 #define ATA_CMD_READ 0x02 #define ATA_CMD_WRITE 0x04 #define ATA_CMD_ATAPI 0x08 int timeout; int error; }; /* pr device ATA ioctl calls */ #define IOCATAREQUEST _IOWR('a', 100, struct ata_ioc_request) #define IOCATAGPARM _IOR('a', 101, struct ata_params) #define IOCATAGMODE _IOR('a', 102, int) #define IOCATASMODE _IOW('a', 103, int) struct ata_ioc_raid_config { int lun; int type; #define AR_JBOD 0x0001 #define AR_SPAN 0x0002 #define AR_RAID0 0x0004 #define AR_RAID1 0x0008 #define AR_RAID01 0x0010 #define AR_RAID3 0x0020 #define AR_RAID4 0x0040 #define AR_RAID5 0x0080 int interleave; int status; #define AR_READY 1 #define AR_DEGRADED 2 #define AR_REBUILDING 4 int progress; int total_disks; int disks[16]; }; /* ATA RAID ioctl calls */ #define IOCATARAIDCREATE _IOWR('a', 200, struct ata_ioc_raid_config) #define IOCATARAIDDELETE _IOW('a', 201, int) #define IOCATARAIDSTATUS _IOWR('a', 202, struct ata_ioc_raid_config) #define IOCATARAIDADDSPARE _IOW('a', 203, struct ata_ioc_raid_config) #define IOCATARAIDREBUILD _IOW('a', 204, int) #endif /* _SYS_ATA_H_ */