Index: head/sys/dev/ata/ata-all.c =================================================================== --- head/sys/dev/ata/ata-all.c (revision 145101) +++ head/sys/dev/ata/ata-all.c (revision 145102) @@ -1,850 +1,845 @@ /*- * Copyright (c) 1998 - 2005 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. * 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 "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 #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 safe */ .d_ioctl = ata_ioctl, .d_name = "ata", }; /* prototypes */ static void ata_interrupt(void *); static void ata_boot_attach(void); -device_t ata_add_child(driver_t *driver, device_t parent, struct ata_device *atadev, const char *name, int unit); +device_t ata_add_child(device_t parent, struct ata_device *atadev, int unit); +static int ata_identify(device_t dev); /* global vars */ MALLOC_DEFINE(M_ATA, "ATA generic", "ATA driver generic layer"); int (*ata_ioctl_func)(struct ata_cmd *iocmd) = NULL; devclass_t ata_devclass; uma_zone_t ata_zone; int ata_wc = 1; /* local vars */ static struct intr_config_hook *ata_delayed_attach = NULL; 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); ch->hw.reset(ch); 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, ata_interrupt, ch, &ch->ih))) { device_printf(dev, "unable to setup interrupt\n"); return error; } /* do not attach devices if we are in early boot */ if (ata_delayed_attach) return 0; /* probe and attach devices on this channel */ - bus_generic_probe(dev); - bus_generic_attach(dev); + 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 vaild channel to detach */ if (!ch->r_irq) 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); } - /* fail outstanding requests on this channel (SOS shouldn't be any XXX ) */ - ata_fail_requests(ch, NULL); - /* 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); device_t *children; int nchildren, i; 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); /* grap the channel lock */ mtx_lock(&ch->state_mtx); ch->state = ATA_STALL_QUEUE; mtx_unlock(&ch->state_mtx); /* reset the controller HW, the channel and device(s) */ ch->hw.reset(ch); /* 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++) { if (children[i] && device_is_attached(children[i])) if (ATA_REINIT(children[i])) { if (ch->running->dev == children[i]) { device_printf(ch->running->dev, "FAILURE - device detached\n"); ch->running->dev = NULL; ch->running = NULL; } device_delete_child(dev, children[i]); } } free(children, M_TEMP); mtx_unlock(&Giant); /* newbus suckage dealt with, release Giant */ } /* catch running request if any */ ata_catch_inflight(ch); /* 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; if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* wait for the channel to be IDLE before when enter 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; if (!dev || !(ch = device_get_softc(dev))) return ENXIO; /* reinit the devices, we dont know what mode/state they have */ error = ata_reinit(dev); /* kick off requests on the queue */ ata_start(dev); return error; } static void ata_interrupt(void *data) { struct ata_channel *ch = (struct ata_channel *)data; struct ata_request *request; mtx_lock(&ch->state_mtx); do { /* do we have a running request */ if (ch->state & ATA_TIMEOUT || !(request = ch->running)) break; ATA_DEBUG_RQ(request, "interrupt"); /* ignore interrupt if device is busy */ if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) { DELAY(100); if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) break; } /* check for the right state */ if (ch->state == ATA_ACTIVE || ch->state == ATA_STALL_QUEUE) { request->flags |= ATA_R_INTR_SEEN; } else { device_printf(request->dev, "interrupt state=%d unexpected\n", ch->state); break; } /* * we have the HW locks, so start the tranaction for this request * if it finishes immediately we dont need to wait for 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; } else { request->flags &= ~ATA_R_INTR_SEEN; } } while (0); mtx_unlock(&ch->state_mtx); } /* * device related interfaces */ static int ata_ioctl(struct cdev *dev, u_long cmd, caddr_t addr, int32_t flag, struct thread *td) { struct ata_cmd *iocmd = (struct ata_cmd *)addr; device_t *children, device = NULL; struct ata_request *request; caddr_t buf; int nchildren, i; int error = ENOTTY; if (cmd != IOCATA) return ENOTSUP; if (iocmd->cmd == ATAGMAXCHANNEL) { iocmd->u.maxchan = devclass_get_maxunit(ata_devclass); return 0; } if (iocmd->channel < 0 || iocmd->channel >= devclass_get_maxunit(ata_devclass)) { return ENXIO; } if (!(device = devclass_get_device(ata_devclass, iocmd->channel))) return ENXIO; switch (iocmd->cmd) { case ATAGPARM: if (!device_get_children(device, &children, &nchildren)) { struct ata_channel *ch; if (!(ch = device_get_softc(device))) return ENXIO; iocmd->u.param.type[0] = ch->devices & (ATA_ATA_MASTER | ATA_ATAPI_MASTER); iocmd->u.param.type[1] = ch->devices & (ATA_ATA_SLAVE | ATA_ATAPI_SLAVE); 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) { strcpy(iocmd->u.param.name[0], device_get_nameunit(children[i])); bcopy(&atadev->param, &iocmd->u.param.params[0], sizeof(struct ata_params)); } if (atadev->unit == ATA_SLAVE) { strcpy(iocmd->u.param.name[1], device_get_nameunit(children[i])); bcopy(&atadev->param, &iocmd->u.param.params[1], sizeof(struct ata_params)); } } } free(children, M_TEMP); error = 0; } else error = ENXIO; break; case ATAGMODE: 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]); atadev = device_get_softc(children[i]); if (atadev->unit == ATA_MASTER) iocmd->u.mode.mode[0] = atadev->mode; if (atadev->unit == ATA_SLAVE) iocmd->u.mode.mode[1] = atadev->mode; } free(children, M_TEMP); } error = 0; } else error = ENXIO; break; case ATASMODE: 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) { atadev->mode = iocmd->u.mode.mode[0]; ATA_SETMODE(device, children[i]); iocmd->u.mode.mode[0] = atadev->mode; } if (atadev->unit == ATA_SLAVE) { atadev->mode = iocmd->u.mode.mode[1]; ATA_SETMODE(device, children[i]); iocmd->u.mode.mode[1] = atadev->mode; } } } free(children, M_TEMP); error = 0; } else error = ENXIO; break; case ATAREQUEST: 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 (ATA_DEV(atadev->unit) == iocmd->device) { if (!(buf = malloc(iocmd->u.request.count, M_ATA, M_NOWAIT))) { error = ENOMEM; break; } if (!(request = ata_alloc_request())) { error = ENOMEM; free(buf, M_ATA); break; } if (iocmd->u.request.flags & ATA_CMD_WRITE) { error = copyin(iocmd->u.request.data, buf, iocmd->u.request.count); if (error) { free(buf, M_ATA); ata_free_request(request); break; } } request->dev = atadev->dev; if (iocmd->u.request.flags & ATA_CMD_ATAPI) { request->flags = ATA_R_ATAPI; bcopy(iocmd->u.request.u.atapi.ccb, request->u.atapi.ccb, 16); } else { request->u.ata.command = iocmd->u.request.u.ata.command; request->u.ata.feature = iocmd->u.request.u.ata.feature; request->u.ata.lba = iocmd->u.request.u.ata.lba; request->u.ata.count = iocmd->u.request.u.ata.count; } request->timeout = iocmd->u.request.timeout; request->data = buf; request->bytecount = iocmd->u.request.count; request->transfersize = request->bytecount; if (iocmd->u.request.flags & ATA_CMD_CONTROL) request->flags |= ATA_R_CONTROL; if (iocmd->u.request.flags & ATA_CMD_READ) request->flags |= ATA_R_READ; if (iocmd->u.request.flags & ATA_CMD_WRITE) request->flags |= ATA_R_WRITE; ata_queue_request(request); if (!(request->flags & ATA_R_ATAPI)) { iocmd->u.request.u.ata.command = request->u.ata.command; iocmd->u.request.u.ata.feature = request->u.ata.feature; iocmd->u.request.u.ata.lba = request->u.ata.lba; iocmd->u.request.u.ata.count = request->u.ata.count; } iocmd->u.request.error = request->result; if (iocmd->u.request.flags & ATA_CMD_READ) error = copyout(buf, iocmd->u.request.data, iocmd->u.request.count); else error = 0; free(buf, M_ATA); ata_free_request(request); break; } } } free(children, M_TEMP); } else error = ENXIO; break; case ATAREINIT: error = ata_reinit(device); ata_start(device); break; case ATAATTACH: /* SOS should enable channel HW on controller XXX */ error = ata_attach(device); break; case ATADETACH: error = ata_detach(device); /* SOS should disable channel HW on controller XXX */ break; default: if (ata_ioctl_func) error = ata_ioctl_func(iocmd); } return error; } static void ata_boot_attach(void) { struct ata_channel *ch; int ctlr; /* release the hook that got us here, only needed during boot */ if (ata_delayed_attach) { config_intrhook_disestablish(ata_delayed_attach); free(ata_delayed_attach, M_TEMP); ata_delayed_attach = NULL; } /* 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))) { - bus_generic_probe(ch->dev); - bus_generic_attach(ch->dev); + ata_identify(ch->dev); } } } /* * misc support functions */ device_t -ata_add_child(driver_t *driver, device_t parent, struct ata_device *atadev, - const char *name, int unit) +ata_add_child(device_t parent, struct ata_device *atadev, int unit) { struct ata_channel *ch = device_get_softc(parent); device_t child; - if ((child = device_add_child(parent, name, unit))) { + if ((child = device_add_child(parent, NULL, unit))) { char buffer[64]; - device_set_driver(child, driver); device_set_softc(child, atadev); sprintf(buffer, "%.40s/%.8s", atadev->param.model, atadev->param.revision); device_set_desc_copy(child, buffer); device_quiet(child); atadev->dev = child; atadev->max_iosize = DEV_BSIZE; atadev->mode = ATA_PIO_MAX; - if ((atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12) { + 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) atadev->mode = ATA_DMA_MAX; } } return child; } -void -ata_identify(driver_t *driver, device_t parent, int type, const char *name) +static int +ata_identify(device_t dev) { - struct ata_channel *ch = device_get_softc(parent); + struct ata_channel *ch = device_get_softc(dev); struct ata_device *master, *slave; int master_res = EIO, slave_res = EIO, master_unit = -1, slave_unit = -1; if (!(master = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { - device_printf(parent, "out of memory\n"); - return; + device_printf(dev, "out of memory\n"); + return ENOMEM; } master->unit = ATA_MASTER; if (!(slave = malloc(sizeof(struct ata_device), M_ATA, M_NOWAIT | M_ZERO))) { free(master, M_ATA); - device_printf(parent, "out of memory\n"); - return; + device_printf(dev, "out of memory\n"); + return ENOMEM; } slave->unit = ATA_SLAVE; /* wait for the channel to be IDLE then grab it before touching HW */ - while (ATA_LOCKING(parent, ATA_LF_LOCK) != ch->unit) - tsleep(ch, PRIBIO, "ataidnt2", 1); + while (ATA_LOCKING(dev, ATA_LF_LOCK) != ch->unit) + tsleep(ch, PRIBIO, "ataidnt1", 1); 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, "ataidnt1", 1); + tsleep(ch, PRIBIO, "ataidnt2", 1); } - if (type < 0) { - if (ch->devices & ATA_ATA_SLAVE) - slave_res = ata_getparam(parent, slave, ATA_ATA_IDENTIFY); - if (ch->devices & ATA_ATA_MASTER) - master_res = ata_getparam(parent, master, ATA_ATA_IDENTIFY); + if (ch->devices & ATA_ATA_SLAVE) { + slave_res = ata_getparam(dev, slave, ATA_ATA_IDENTIFY); #ifdef ATA_STATIC_ID - master_unit = (device_get_unit(parent) << 1); slave_unit = (device_get_unit(parent) << 1) + 1; #endif } - else { - if (ch->devices & ATA_ATAPI_SLAVE) - slave_res = ata_getparam(parent, slave, ATA_ATAPI_IDENTIFY); - if (ch->devices & ATA_ATAPI_MASTER) - master_res = ata_getparam(parent, master, ATA_ATAPI_IDENTIFY); + else if (ch->devices & ATA_ATAPI_SLAVE) + slave_res = ata_getparam(dev, slave, ATA_ATAPI_IDENTIFY); + + if (ch->devices & ATA_ATA_MASTER) { + master_res = ata_getparam(dev, master, ATA_ATA_IDENTIFY); +#ifdef ATA_STATIC_ID + master_unit = (device_get_unit(parent) << 1); +#endif } + else if (ch->devices & ATA_ATAPI_MASTER) + master_res = ata_getparam(dev, master, ATA_ATAPI_IDENTIFY); - if (master_res || - !(type < 0 || (master->param.config & ATA_ATAPI_TYPE_MASK) == type) || - !ata_add_child(driver, parent, master, name, master_unit)) + if (master_res || !ata_add_child(dev, master, master_unit)) free(master, M_ATA); - if (slave_res || - !(type < 0 || (slave->param.config & ATA_ATAPI_TYPE_MASK) == type) || - !ata_add_child(driver, parent, slave, name, slave_unit)) + if (slave_res || !ata_add_child(dev, slave, slave_unit)) free(slave, M_ATA); mtx_lock(&ch->state_mtx); ch->state = ATA_IDLE; mtx_unlock(&ch->state_mtx); - ATA_LOCKING(parent, ATA_LF_UNLOCK); + ATA_LOCKING(dev, ATA_LF_UNLOCK); + + bus_generic_probe(dev); + bus_generic_attach(dev); + return 0; } void ata_default_registers(struct ata_channel *ch) { /* 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_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 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"; 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(struct ata_device *atadev, int mode, int maxmode) { 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; } /* * 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) { /* init our UMA zone for ATA requests */ ata_zone = uma_zcreate("ata_request", sizeof(struct ata_request), NULL, NULL, NULL, NULL, 0, 0); } SYSINIT(atadev, SI_SUB_DRIVERS, SI_ORDER_SECOND, ata_init, NULL) Index: head/sys/dev/ata/ata-all.h =================================================================== --- head/sys/dev/ata/ata-all.h (revision 145101) +++ head/sys/dev/ata/ata-all.h (revision 145102) @@ -1,581 +1,580 @@ /*- * Copyright (c) 1998 - 2005 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. * 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. * * $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_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 /* DMA register defines */ #define ATA_DMA_ENTRIES 256 #define ATA_DMA_EOT 0x80000000 #define ATA_BMCMD_PORT 16 #define ATA_BMCMD_START_STOP 0x01 #define ATA_BMCMD_WRITE_READ 0x08 #define ATA_BMDEVSPEC_0 17 #define ATA_BMSTAT_PORT 18 #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 19 #define ATA_BMDTP_PORT 20 #define ATA_IDX_ADDR 21 #define ATA_IDX_DATA 22 #define ATA_MAX_RES 23 /* 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 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 268435455 /* 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 */ 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[32]; /* size must match maps above */ caddr_t data_1; caddr_t data_2; }; /* structure used to queue an ATA/ATAPI request */ struct ata_request { device_t dev; /* device 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 */ } 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_DMA 0x00000008 #define ATA_R_ATAPI 0x00000010 #define ATA_R_QUIET 0x00000020 #define ATA_R_INTR_SEEN 0x00000040 #define ATA_R_TIMEOUT 0x00000080 #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 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 cmd; /* last cmd executed */ int flags; #define ATA_D_USE_CHS 0x0001 #define ATA_D_MEDIA_CHANGED 0x0002 #define ATA_D_ENC_PRESENT 0x0004 }; /* 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 error; }; struct ata_channel {}; /* 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 engine alignment */ u_int32_t boundary; /* DMA engine boundary */ u_int32_t max_iosize; /* DMA engine max IO size */ u_int32_t cur_iosize; /* DMA engine 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)(struct ata_channel *ch); void (*free)(struct ata_channel *ch); void (*setprd)(void *xsc, bus_dma_segment_t *segs, int nsegs, int error); int (*load)(struct ata_device *atadev, caddr_t data, int32_t count,int dir); int (*unload)(struct ata_channel *ch); int (*start)(struct ata_channel *ch); int (*stop)(struct ata_channel *ch); }; /* structure holding lowlevel functions */ struct ata_lowlevel { int (*begin_transaction)(struct ata_request *request); int (*end_transaction)(struct ata_request *request); void (*reset)(struct ata_channel *ch); int (*command)(struct ata_device *atadev, u_int8_t command, u_int64_t lba, u_int16_t count, u_int16_t feature); }; /* 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_48BIT_ACTIVE 0x08 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 #define ATA_TIMEOUT 0x0004 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_ioctl_func)(struct ata_cmd *iocmd); 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); -void ata_identify(driver_t *driver, device_t parent, int type, const char *name); void ata_default_registers(struct ata_channel *ch); 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(struct ata_device *atadev, int mode, int maxmode); /* ata-queue.c: */ int ata_controlcmd(struct ata_device *atadev, u_int8_t command, u_int16_t feature, u_int64_t lba, u_int16_t count); int ata_atapicmd(struct ata_device *atadev, 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_catch_inflight(struct ata_channel *ch); void ata_fail_requests(struct ata_channel *ch, device_t dev); char *ata_cmd2str(struct ata_request *request); /* ata-lowlevel.c: */ void ata_generic_hw(struct ata_channel *ch); int ata_generic_command(struct ata_device *atadev, u_int8_t command, u_int64_t lba, u_int16_t count, u_int16_t feature); int ata_getparam(device_t parent, struct ata_device *atadev, u_int8_t command); /* macros for alloc/free of ata_requests */ extern uma_zone_t ata_zone; #define ata_alloc_request() uma_zalloc(ata_zone, M_NOWAIT | M_ZERO) #define ata_free_request(request) uma_zfree(ata_zone, request) 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 145101) +++ head/sys/dev/ata/ata-disk.c (revision 145102) @@ -1,460 +1,437 @@ /*- * Copyright (c) 1998 - 2005 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. * 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 "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 dumper_t ad_dump; /* local vars */ static MALLOC_DEFINE(M_AD, "AD driver", "ATA disk driver"); -static void -ad_identify(driver_t *driver, device_t parent) -{ - ata_identify(driver, parent, -1, "ad"); -} - static int ad_probe(device_t dev) { - return 0; + struct ata_device *atadev = device_get_softc(dev); + + if (!(atadev->param.config & ATA_PROTO_ATAPI)) + 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"); - device_set_softc(dev, NULL); - free(atadev, M_ATA); 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_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 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); 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(ch, dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(adp, M_AD); - device_set_softc(dev, NULL); - free(atadev, M_ATA); 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(atadev, 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 flag set, 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 (atadev->max_iosize > 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 (atadev->max_iosize > 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_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp = arg; device_t dev = dp->d_drv1; struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp = device_get_ivars(dev); struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_request request; if (!adp) return ENXIO; bzero(&request, sizeof(struct ata_request)); request.dev = dev; if (length) { request.data = virtual; request.bytecount = length; request.transfersize = min(length, atadev->max_iosize); request.flags = ATA_R_WRITE; if (atadev->max_iosize > DEV_BSIZE) request.u.ata.command = ATA_WRITE_MUL; else request.u.ata.command = ATA_WRITE; request.u.ata.lba = offset / DEV_BSIZE; request.u.ata.count = request.bytecount / DEV_BSIZE; } else { request.u.ata.command = ATA_FLUSHCACHE; request.flags = ATA_R_CONTROL; } if (ch->hw.begin_transaction(&request) == ATA_OP_CONTINUES) { do { DELAY(20); } while (ch->hw.end_transaction(&request) == ATA_OP_CONTINUES); ata_finish(&request); } if (request.status & ATA_S_ERROR) return EIO; return 0; } static void ad_init(device_t dev) { struct ata_device *atadev = device_get_softc(dev); ATA_SETMODE(device_get_parent(dev), dev); /* enable read caching */ ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); /* enable write caching if enabled */ if (ata_wc) ata_controlcmd(atadev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); else ata_controlcmd(atadev, 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(atadev, 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, "%lluMB <%s%s %.8s> at ata%d-%s %s%s\n", (unsigned long long)(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, "%llu sectors [%lldC/%dH/%dS] " "%d sectors/interrupt %d depth queue\n", (unsigned long long)adp->total_secs, (unsigned long long)(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. * 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 int atapi_cam_identify(device_t *dev, device_t parent); +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 transport", "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, 0, 0); +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 int +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. */ - device_add_child(parent, "atapicam", -1); - return (0); + 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) { - device_set_desc(dev, "ATAPI CAM Attachment"); - return (0); + 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); - /* - * The ATA core expects all of its children to have an ata_device. - * Assign an invalid unit number so that we can be properly ignored. - */ - scp->atapi_cam_dev.unit = -1; - scp->atapi_cam_dev.dev = dev; - 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], ATA_ATAPI_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) 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)); 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 145101) +++ head/sys/dev/ata/atapi-cd.c (revision 145102) @@ -1,1965 +1,1945 @@ /*- * Copyright (c) 1998 - 2005 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. * 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 "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 void -acd_identify(driver_t *driver, device_t parent) -{ - ata_identify(driver, parent, ATA_ATAPI_TYPE_CDROM, "acd"); -} - static int acd_probe(device_t dev) { - return 0; + 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 ata_device *atadev = device_get_softc(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"); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return ENOMEM; } cdp->block_size = 2048; device_set_ivars(dev, cdp); ATA_SETMODE(device_get_parent(dev), dev); 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(atadev, 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 ata_channel *ch = device_get_softc(device_get_parent(arg)); - struct ata_device *atadev = device_get_softc(arg); 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(ch, arg); /* dont leave anything behind */ device_set_ivars(arg, NULL); free(cdp, M_ACD); - device_set_softc(arg, NULL); - free(atadev, M_ATA); } 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); } 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(atadev, 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(atadev, 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 = ENOTTY; } 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) 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; for (pos = 0; pos < bp->bio_length; pos += size) { if (!(bp2 = g_clone_bio(bp))) { bp->bio_error = ENOMEM; break; } bp2->bio_done = g_std_done; bp2->bio_to = bp->bio_to; bp2->bio_offset += pos; bp2->bio_data += pos; bp2->bio_length = MIN(size, bp->bio_length - pos); bp2->bio_pblkno = bp2->bio_offset / bp2->bio_to->sectorsize; acd_strategy(bp2); } } } 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->driver = 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->driver; /* 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; if (acd_test_ready(dev)) return; if (!(atadev->flags & ATA_D_MEDIA_CHANGED)) return; atadev->flags &= ~ATA_D_MEDIA_CHANGED; bzero(&cdp->toc, sizeof(cdp->toc)); bzero(ccb, sizeof(ccb)); cdp->disk_size = -1; /* hack for GEOM SOS */ 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(atadev, 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(atadev, 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(atadev, 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_printd(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); 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) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, 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) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16]; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REZERO; ata_atapicmd(atadev, ccb, NULL, 0, ATA_R_QUIET, 60); ccb[0] = ATAPI_SEND_OPC_INFO; ccb[1] = 0x01; ata_atapicmd(atadev, ccb, NULL, 0, ATA_R_QUIET, 30); return 0; } static int acd_fixate(device_t dev, int multisession) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, 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) { struct ata_device *atadev = device_get_softc(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(atadev, ccb, NULL, 0, ATA_R_QUIET, 60); } static int acd_read_track_info(device_t dev, int32_t lba, struct acd_track_info *info) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, 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; else *finished = 0; ata_free_request(request); return 0; } static int acd_send_cue(device_t dev, struct cdr_cuesheet *cuesheet) { struct ata_device *atadev = device_get_softc(dev); 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; #ifdef ACD_DEBUG int i; #endif 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))) { #ifdef ACD_DEBUG printf("acd: cuesheet lenght = %d\n", cuesheet->len); for (i=0; ilen; i++) if (i%8) printf(" %02x", buffer[i]); else printf("\n%02x", buffer[i]); printf("\n"); #endif error = ata_atapicmd(atadev, 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 ata_device *atadev = device_get_softc(dev); 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(atadev, 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 ata_device *atadev = device_get_softc(dev); 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(atadev, 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 ata_device *atadev = device_get_softc(dev); 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(atadev, 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[0]; 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(atadev, ccb, NULL, 0, 0, 30); } static int acd_prevent_allow(device_t dev, int lock) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, ccb, NULL, 0, 0, 30); } static int acd_start_stop(device_t dev, int start) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, ccb, NULL, 0, 0, 30); } static int acd_pause_resume(device_t dev, int pause) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, pause, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(atadev, ccb, NULL, 0, 0, 30); } static int acd_mode_sense(device_t dev, int page, caddr_t pagebuf, int pagesize) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, ccb, pagebuf, pagesize, ATA_R_READ, 10); #ifdef ACD_DEBUG atapi_dump("acd: mode sense ", pagebuf, pagesize); #endif return error; } static int acd_mode_select(device_t dev, caddr_t pagebuf, int pagesize) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 }; #ifdef ACD_DEBUG device_printf(dev, "modeselect pagesize=%d\n", pagesize); atapi_dump("mode select ", pagebuf, pagesize); #endif return ata_atapicmd(atadev, ccb, pagebuf, pagesize, 0, 30); } static int acd_set_speed(device_t dev, int rdspeed, int wrspeed) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, 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) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, 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) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, ccb, (u_int8_t *)params, sizeof(struct cdr_format_params), 0, 30); return error; } static int acd_test_ready(device_t dev) { struct ata_device *atadev = device_get_softc(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(atadev, 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_identify, acd_identify), 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) { - device_t *devs; - int ndevs, i; - - if (what == MOD_LOAD) { - g_modevent(0, what, &acd_class); - } - if (what == MOD_UNLOAD) { - if (!devclass_get_devices(acd_devclass, &devs, &ndevs) && devs) { - for (i = 0; i < ndevs; i++) - device_delete_child(device_get_parent(devs[i]), devs[i]); - free(devs, M_TEMP); - } - g_modevent(0, what, &acd_class); - } + 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/dev/ata/atapi-fd.c =================================================================== --- head/sys/dev/ata/atapi-fd.c (revision 145101) +++ head/sys/dev/ata/atapi-fd.c (revision 145102) @@ -1,442 +1,418 @@ /*- * Copyright (c) 1998 - 2005 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. * 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 /* prototypes */ static disk_open_t afd_open; static disk_close_t afd_close; static disk_strategy_t afd_strategy; static int afd_sense(device_t); static void afd_describe(device_t); static void afd_done(struct ata_request *); static int afd_prevent_allow(device_t, int); static int afd_test_ready(device_t); /* internal vars */ static MALLOC_DEFINE(M_AFD, "AFD driver", "ATAPI floppy driver buffers"); -static void -afd_identify(driver_t *driver, device_t parent) -{ - ata_identify(driver, parent, ATA_ATAPI_TYPE_DIRECT, "afd"); -} - static int afd_probe(device_t dev) { - return 0; + 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_DIRECT) + return 0; + else + return ENXIO; } static int afd_attach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp; if (!(fdp = malloc(sizeof(struct afd_softc), M_AFD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return ENOMEM; } device_set_ivars(dev, fdp); ATA_SETMODE(device_get_parent(dev), dev); if (afd_sense(dev)) { device_set_ivars(dev, NULL); free(fdp, M_AFD); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return ENXIO; } atadev->flags |= ATA_D_MEDIA_CHANGED; /* announce we are here */ afd_describe(dev); /* create the disk device */ fdp->disk = disk_alloc(); fdp->disk->d_open = afd_open; fdp->disk->d_close = afd_close; fdp->disk->d_strategy = afd_strategy; fdp->disk->d_name = "afd"; fdp->disk->d_drv1 = dev; if (ch->dma) fdp->disk->d_maxsize = ch->dma->max_iosize; else fdp->disk->d_maxsize = DFLTPHYS; fdp->disk->d_unit = device_get_unit(dev); disk_create(fdp->disk, DISK_VERSION); return 0; } static int afd_detach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); - struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); /* detroy disk from the system so we dont get any further requests */ disk_destroy(fdp->disk); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(ch, dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(fdp, M_AFD); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return 0; } static void afd_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(atadev, ATA_FLUSHCACHE, 0, 0, 0); } static int afd_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = 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(fdp, M_AFD); return 1; } ATA_SETMODE(device_get_parent(dev), dev); return 0; } static int afd_open(struct disk *dp) { device_t dev = dp->d_drv1; struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); if (!fdp) return ENXIO; if (!device_is_attached(dev)) return EBUSY; afd_test_ready(dev); afd_prevent_allow(dev, 1); if (afd_sense(dev)) device_printf(dev, "sense media type failed\n"); atadev->flags &= ~ATA_D_MEDIA_CHANGED; fdp->disk->d_sectorsize = fdp->cap.sector_size; fdp->disk->d_mediasize = (off_t)fdp->cap.sector_size * fdp->cap.sectors * fdp->cap.heads * fdp->cap.cylinders; fdp->disk->d_fwsectors = fdp->cap.sectors; fdp->disk->d_fwheads = fdp->cap.heads; return 0; } static int afd_close(struct disk *dp) { device_t dev = dp->d_drv1; afd_prevent_allow(dev, 0); return 0; } static void afd_strategy(struct bio *bp) { device_t dev = bp->bio_disk->d_drv1; struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); struct ata_request *request; u_int16_t count; int8_t ccb[16]; /* if it's a null transfer, return immediatly. */ if (bp->bio_bcount == 0) { bp->bio_resid = 0; biodone(bp); return; } /* should reject all queued entries if media have changed. */ if (atadev->flags & ATA_D_MEDIA_CHANGED) { biofinish(bp, NULL, EIO); return; } count = bp->bio_bcount / fdp->cap.sector_size; bp->bio_resid = bp->bio_bcount; bzero(ccb, sizeof(ccb)); if (bp->bio_cmd == BIO_READ) ccb[0] = ATAPI_READ_BIG; else ccb[0] = ATAPI_WRITE_BIG; ccb[2] = bp->bio_pblkno >> 24; ccb[3] = bp->bio_pblkno >> 16; ccb[4] = bp->bio_pblkno >> 8; ccb[5] = bp->bio_pblkno; ccb[7] = count>>8; ccb[8] = count; if (!(request = ata_alloc_request())) { biofinish(bp, NULL, 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 * fdp->cap.sector_size; request->transfersize = min(request->bytecount, 65534); request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30; request->retries = 2; request->callback = afd_done; switch (bp->bio_cmd) { case BIO_READ: request->flags = (ATA_R_ATAPI | ATA_R_READ); break; case BIO_WRITE: request->flags = (ATA_R_ATAPI | ATA_R_WRITE); break; default: device_printf(dev, "unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } if (atadev->mode >= ATA_DMA) request->flags |= ATA_R_DMA; request->flags |= ATA_R_ORDERED; ata_queue_request(request); } static void afd_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 afd_sense(device_t dev) { struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE, 0, 0, 0, 0, sizeof(struct afd_cappage) >> 8, sizeof(struct afd_cappage) & 0xff, 0, 0, 0, 0, 0, 0, 0 }; int count; /* The IOMEGA Clik! doesn't support reading the cap page, fake it */ if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12)) { fdp->cap.transfer_rate = 500; fdp->cap.heads = 1; fdp->cap.sectors = 2; fdp->cap.cylinders = 39441; fdp->cap.sector_size = 512; afd_test_ready(dev); return 0; } /* get drive capabilities, some bugridden drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!ata_atapicmd(atadev, ccb, (caddr_t)&fdp->cap, sizeof(struct afd_cappage), ATA_R_READ, 30) && fdp->cap.page_code == ATAPI_REWRITEABLE_CAP_PAGE) { fdp->cap.cylinders = ntohs(fdp->cap.cylinders); fdp->cap.sector_size = ntohs(fdp->cap.sector_size); fdp->cap.transfer_rate = ntohs(fdp->cap.transfer_rate); return 0; } } return 1; } static int afd_prevent_allow(device_t dev, int lock) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12)) return 0; return ata_atapicmd(atadev, ccb, NULL, 0, 0, 30); } static int afd_test_ready(device_t dev) { struct ata_device *atadev = device_get_softc(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(atadev, ccb, NULL, 0, 0, 30); } static void afd_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); if (bootverbose) { device_printf(dev, "<%.40s/%.8s> removable drive at ata%d as %s\n", atadev->param.model, atadev->param.revision, device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave"); device_printf(dev, "%luMB (%u sectors), %u cyls, %u heads, %u S/T, %u B/S\n", (fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors) / ((1024L * 1024L) / fdp->cap.sector_size), fdp->cap.cylinders * fdp->cap.heads * fdp->cap.sectors, fdp->cap.cylinders, fdp->cap.heads, fdp->cap.sectors, fdp->cap.sector_size); device_printf(dev, "%dKB/s,", fdp->cap.transfer_rate / 8); printf(" %s\n", ata_mode2str(atadev->mode)); if (fdp->cap.medium_type) { device_printf(dev, "Medium: "); switch (fdp->cap.medium_type) { case MFD_2DD: printf("720KB DD disk"); break; case MFD_HD_12: printf("1.2MB HD disk"); break; case MFD_HD_144: printf("1.44MB HD disk"); break; case MFD_UHD: printf("120MB UHD disk"); break; default: printf("Unknown (0x%x)", fdp->cap.medium_type); } if (fdp->cap.wp) printf(", writeprotected"); printf("\n"); } } else { device_printf(dev, "REMOVABLE <%.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 afd_methods[] = { /* device interface */ - DEVMETHOD(device_identify, afd_identify), DEVMETHOD(device_probe, afd_probe), DEVMETHOD(device_attach, afd_attach), DEVMETHOD(device_detach, afd_detach), DEVMETHOD(device_shutdown, afd_shutdown), /* ATA methods */ DEVMETHOD(ata_reinit, afd_reinit), { 0, 0 } }; static driver_t afd_driver = { "afd", afd_methods, 0, }; static devclass_t afd_devclass; -static int -afd_modevent(module_t mod, int what, void *arg) -{ - device_t *devs; - int ndevs, i; - - if (what == MOD_UNLOAD) { - if (!devclass_get_devices(afd_devclass, &devs, &ndevs) && devs) { - for (i = 0; i < ndevs; i++) - device_delete_child(device_get_parent(devs[i]), devs[i]); - free(devs, M_TEMP); - } - } - return 0; -} - -DRIVER_MODULE(afd, ata, afd_driver, afd_devclass, afd_modevent, NULL); +DRIVER_MODULE(afd, ata, afd_driver, afd_devclass, NULL, NULL); MODULE_VERSION(afd, 1); MODULE_DEPEND(afd, ata, 1, 1, 1); Index: head/sys/dev/ata/atapi-tape.c =================================================================== --- head/sys/dev/ata/atapi-tape.c (revision 145101) +++ head/sys/dev/ata/atapi-tape.c (revision 145102) @@ -1,793 +1,769 @@ /*- * Copyright (c) 1998 - 2005 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. * 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 "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* device structure */ static d_open_t ast_open; static d_close_t ast_close; static d_ioctl_t ast_ioctl; static d_strategy_t ast_strategy; static struct cdevsw ast_cdevsw = { .d_version = D_VERSION, .d_open = ast_open, .d_close = ast_close, .d_read = physread, .d_write = physwrite, .d_ioctl = ast_ioctl, .d_strategy = ast_strategy, .d_name = "ast", .d_flags = D_TAPE | D_TRACKCLOSE, }; /* prototypes */ static int ast_sense(device_t); static void ast_describe(device_t); static void ast_done(struct ata_request *); static int ast_mode_sense(device_t, int, void *, int); static int ast_mode_select(device_t, void *, int); static int ast_write_filemark(device_t, u_int8_t); static int ast_read_position(device_t, int, struct ast_readposition *); static int ast_space(device_t, u_int8_t, int32_t); static int ast_locate(device_t, int, u_int32_t); static int ast_prevent_allow(device_t, int); static int ast_load_unload(device_t, u_int8_t); static int ast_rewind(device_t); static int ast_erase(device_t); static int ast_test_ready(device_t); static int ast_wait_dsc(device_t, int); /* internal vars */ static u_int64_t ast_total = 0; static MALLOC_DEFINE(M_AST, "AST driver", "ATAPI tape driver buffers"); -static void -ast_identify(driver_t *driver, device_t parent) -{ - ata_identify(driver, parent, ATA_ATAPI_TYPE_TAPE, "ast"); -} - static int ast_probe(device_t dev) { - return 0; + 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_TAPE) + return 0; + else + return ENXIO; } static int ast_attach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp; struct ast_readposition position; struct cdev *device; if (!(stp = malloc(sizeof(struct ast_softc), M_AST, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return ENOMEM; } device_set_ivars(dev, stp); ATA_SETMODE(device_get_parent(dev), dev); if (ast_sense(dev)) { device_set_ivars(dev, NULL); free(stp, M_AST); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return ENXIO; } if (!strcmp(atadev->param.model, "OnStream DI-30")) { struct ast_transferpage transfer; struct ast_identifypage identify; stp->flags |= F_ONSTREAM; bzero(&transfer, sizeof(struct ast_transferpage)); ast_mode_sense(dev, ATAPI_TAPE_TRANSFER_PAGE, &transfer, sizeof(transfer)); bzero(&identify, sizeof(struct ast_identifypage)); ast_mode_sense(dev, ATAPI_TAPE_IDENTIFY_PAGE, &identify, sizeof(identify)); strncpy(identify.ident, "FBSD", 4); ast_mode_select(dev, &identify, sizeof(identify)); ast_read_position(dev, 0, &position); } stp->stats = devstat_new_entry("ast", device_get_unit(dev), DEV_BSIZE, DEVSTAT_NO_ORDERED_TAGS, DEVSTAT_TYPE_SEQUENTIAL | DEVSTAT_TYPE_IF_IDE, DEVSTAT_PRIORITY_TAPE); device = make_dev(&ast_cdevsw, 2 * device_get_unit(dev), UID_ROOT, GID_OPERATOR, 0640, "ast%d", device_get_unit(dev)); device->si_drv1 = dev; if (ch->dma) device->si_iosize_max = ch->dma->max_iosize; else device->si_iosize_max = DFLTPHYS; stp->dev1 = device; device = make_dev(&ast_cdevsw, 2 * device_get_unit(dev) + 1, UID_ROOT, GID_OPERATOR, 0640, "nast%d", device_get_unit(dev)); device->si_drv1 = dev; if (ch->dma) device->si_iosize_max = ch->dma->max_iosize; else device->si_iosize_max = DFLTPHYS; stp->dev2 = device; /* announce we are here and ready */ ast_describe(dev); return 0; } static int ast_detach(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); - struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); /* detroy devices from the system so we dont get any further requests */ destroy_dev(stp->dev1); destroy_dev(stp->dev2); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(ch, dev); /* dont leave anything behind */ devstat_remove_entry(stp->stats); device_set_ivars(dev, NULL); free(stp, M_AST); - device_set_softc(dev, NULL); - free(atadev, M_ATA); return 0; } static void ast_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(atadev, ATA_FLUSHCACHE, 0, 0, 0); } static int ast_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = 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(stp, M_AST); return 1; } ATA_SETMODE(device_get_parent(dev), dev); return 0; } static int ast_open(struct cdev *cdev, int flags, int fmt, struct thread *td) { device_t dev = cdev->si_drv1; struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); if (!stp) return ENXIO; if (!device_is_attached(dev)) return EBUSY; ast_test_ready(dev); if (stp->cap.lock) ast_prevent_allow(dev, 1); if (ast_sense(dev)) device_printf(dev, "sense media type failed\n"); atadev->flags &= ~ATA_D_MEDIA_CHANGED; stp->flags &= ~(F_DATA_WRITTEN | F_FM_WRITTEN); ast_total = 0; return 0; } static int ast_close(struct cdev *cdev, int flags, int fmt, struct thread *td) { device_t dev = cdev->si_drv1; struct ast_softc *stp = device_get_ivars(dev); /* flush buffers, some drives fail here, they should report ctl = 0 */ if (stp->cap.ctl && (stp->flags & F_DATA_WRITTEN)) ast_write_filemark(dev, 0); /* write filemark if data written to tape */ if (!(stp->flags & F_ONSTREAM) && (stp->flags & (F_DATA_WRITTEN | F_FM_WRITTEN)) == F_DATA_WRITTEN) ast_write_filemark(dev, ATAPI_WF_WRITE); /* if minor is even rewind on close */ if (!(minor(cdev) & 0x01)) ast_rewind(dev); if (stp->cap.lock && count_dev(cdev) == 1) ast_prevent_allow(dev, 0); stp->flags &= ~F_CTL_WARN; #ifdef AST_DEBUG device_printf(dev, "%ju total bytes transferred\n", (uintmax_t)ast_total); #endif return 0; } static int ast_ioctl(struct cdev *cdev, u_long cmd, caddr_t addr, int flag, struct thread *td) { device_t dev = cdev->si_drv1; struct ast_softc *stp = device_get_ivars(dev); int error = 0; switch (cmd) { case MTIOCGET: { struct mtget *g = (struct mtget *) addr; bzero(g, sizeof(struct mtget)); g->mt_type = 7; g->mt_density = 1; g->mt_blksiz = stp->blksize; g->mt_comp = stp->cap.compress; g->mt_density0 = 0; g->mt_density1 = 0; g->mt_density2 = 0; g->mt_density3 = 0; g->mt_blksiz0 = 0; g->mt_blksiz1 = 0; g->mt_blksiz2 = 0; g->mt_blksiz3 = 0; g->mt_comp0 = 0; g->mt_comp1 = 0; g->mt_comp2 = 0; g->mt_comp3 = 0; } break; case MTIOCTOP: { int i; struct mtop *mt = (struct mtop *)addr; switch ((int16_t) (mt->mt_op)) { case MTWEOF: for (i=0; i < mt->mt_count && !error; i++) error = ast_write_filemark(dev, ATAPI_WF_WRITE); break; case MTFSF: if (mt->mt_count) error = ast_space(dev, ATAPI_SP_FM, mt->mt_count); break; case MTBSF: if (mt->mt_count) error = ast_space(dev, ATAPI_SP_FM, -(mt->mt_count)); break; case MTREW: error = ast_rewind(dev); break; case MTOFFL: error = ast_load_unload(dev, ATAPI_SS_EJECT); break; case MTNOP: error = ast_write_filemark(dev, 0); break; case MTERASE: error = ast_erase(dev); break; case MTEOD: error = ast_space(dev, ATAPI_SP_EOD, 0); break; case MTRETENS: error = ast_load_unload(dev, ATAPI_SS_RETENSION|ATAPI_SS_LOAD); break; case MTFSR: case MTBSR: case MTCACHE: case MTNOCACHE: case MTSETBSIZ: case MTSETDNSTY: case MTCOMP: default: error = EINVAL; } } break; case MTIOCRDSPOS: { struct ast_readposition position; if ((error = ast_read_position(dev, 0, &position))) break; *(u_int32_t *)addr = position.tape; } break; case MTIOCRDHPOS: { struct ast_readposition position; if ((error = ast_read_position(dev, 1, &position))) break; *(u_int32_t *)addr = position.tape; } break; case MTIOCSLOCATE: error = ast_locate(dev, 0, *(u_int32_t *)addr); break; case MTIOCHLOCATE: error = ast_locate(dev, 1, *(u_int32_t *)addr); break; default: error = ENOTTY; } return error; } static void ast_strategy(struct bio *bp) { device_t dev = bp->bio_dev->si_drv1; struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); struct ata_request *request; u_int32_t blkcount; int8_t ccb[16]; /* if it's a null transfer, return immediatly. */ if (bp->bio_bcount == 0) { bp->bio_resid = 0; biodone(bp); return; } if (!(bp->bio_cmd == BIO_READ) && stp->flags & F_WRITEPROTECT) { biofinish(bp, NULL, EPERM); return; } /* check for != blocksize requests */ if (bp->bio_bcount % stp->blksize) { device_printf(dev, "transfers must be multiple of %d\n", stp->blksize); biofinish(bp, NULL, EIO); return; } /* warn about transfers bigger than the device suggests */ if (bp->bio_bcount > stp->blksize * stp->cap.ctl) { if ((stp->flags & F_CTL_WARN) == 0) { device_printf(dev, "WARNING: CTL exceeded %ld>%d\n", bp->bio_bcount, stp->blksize * stp->cap.ctl); stp->flags |= F_CTL_WARN; } } bzero(ccb, sizeof(ccb)); if (bp->bio_cmd == BIO_READ) ccb[0] = ATAPI_READ; else ccb[0] = ATAPI_WRITE; blkcount = bp->bio_bcount / stp->blksize; ccb[1] = 1; ccb[2] = blkcount >> 16; ccb[3] = blkcount >> 8; ccb[4] = blkcount; if (!(request = ata_alloc_request())) { biofinish(bp, NULL, ENOMEM); return; } request->dev = dev; request->driver = 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 = blkcount * stp->blksize; request->transfersize = min(request->bytecount, 65534); request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 180 : 120; request->retries = 2; request->callback = ast_done; switch (bp->bio_cmd) { case BIO_READ: request->flags |= (ATA_R_ATAPI | ATA_R_READ); break; case BIO_WRITE: request->flags |= (ATA_R_ATAPI | ATA_R_WRITE); break; default: device_printf(dev, "unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } devstat_start_transaction_bio(stp->stats, bp); ata_queue_request(request); } static void ast_done(struct ata_request *request) { struct ast_softc *stp = device_get_ivars(request->dev); struct bio *bp = request->driver; /* finish up transfer */ if ((bp->bio_error = request->result)) bp->bio_flags |= BIO_ERROR; if (bp->bio_cmd == BIO_WRITE) stp->flags |= F_DATA_WRITTEN; bp->bio_resid = bp->bio_bcount - request->donecount; ast_total += (bp->bio_bcount - bp->bio_resid); biofinish(bp, stp->stats, 0); ata_free_request(request); } static int ast_sense(device_t dev) { struct ast_softc *stp = device_get_ivars(dev); int count; /* get drive capabilities, some bugridden drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!ast_mode_sense(dev, ATAPI_TAPE_CAP_PAGE, &stp->cap, sizeof(stp->cap)) && stp->cap.page_code == ATAPI_TAPE_CAP_PAGE) { if (stp->cap.blk32k) stp->blksize = 32768; if (stp->cap.blk1024) stp->blksize = 1024; if (stp->cap.blk512) stp->blksize = 512; if (!stp->blksize) continue; stp->cap.max_speed = ntohs(stp->cap.max_speed); stp->cap.max_defects = ntohs(stp->cap.max_defects); stp->cap.ctl = ntohs(stp->cap.ctl); stp->cap.speed = ntohs(stp->cap.speed); stp->cap.buffer_size = ntohs(stp->cap.buffer_size); return 0; } } return 1; } static int ast_mode_sense(device_t dev, int page, void *pagebuf, int pagesize) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_MODE_SENSE, 0x08, page, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(atadev, ccb, pagebuf, pagesize, ATA_R_READ, 10); return error; } static int ast_mode_select(device_t dev, void *pagebuf, int pagesize) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_MODE_SELECT, 0x10, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(atadev, ccb, pagebuf, pagesize, 0, 10); } static int ast_write_filemark(device_t dev, u_int8_t function) { struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_WEOF, 0x01, 0, 0, function, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; if (stp->flags & F_ONSTREAM) ccb[4] = 0x00; /* only flush buffers supported */ else { if (function) { if (stp->flags & F_FM_WRITTEN) stp->flags &= ~F_DATA_WRITTEN; else stp->flags |= F_FM_WRITTEN; } } error = ata_atapicmd(atadev, ccb, NULL, 0, 0, 10); if (error) return error; return ast_wait_dsc(dev, 10*60); } static int ast_read_position(device_t dev, int hard, struct ast_readposition *position) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_READ_POSITION, (hard ? 0x01 : 0), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(atadev, ccb, (caddr_t)position, sizeof(struct ast_readposition), ATA_R_READ, 10); position->tape = ntohl(position->tape); position->host = ntohl(position->host); return error; } static int ast_space(device_t dev, u_int8_t function, int32_t count) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_SPACE, function, count>>16, count>>8, count, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(atadev, ccb, NULL, 0, 0, 60*60); } static int ast_locate(device_t dev, int hard, u_int32_t pos) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_LOCATE, 0x01 | (hard ? 0x4 : 0), 0, pos>>24, pos>>16, pos>>8, pos, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(atadev, ccb, NULL, 0, 0, 10); if (error) return error; return ast_wait_dsc(dev, 60*60); } static int ast_prevent_allow(device_t dev, int lock) { struct ata_device *atadev = device_get_softc(dev); 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(atadev, ccb, NULL, 0, 0, 30); } static int ast_load_unload(device_t dev, u_int8_t function) { struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_START_STOP, 0x01, 0, 0, function, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; if ((function & ATAPI_SS_EJECT) && !stp->cap.eject) return 0; error = ata_atapicmd(atadev, ccb, NULL, 0, 0, 10); if (error) return error; tsleep((caddr_t)&error, PRIBIO, "astlu", 1 * hz); if (function == ATAPI_SS_EJECT) return 0; return ast_wait_dsc(dev, 60*60); } static int ast_rewind(device_t dev) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_REZERO, 0x01, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(atadev, ccb, NULL, 0, 0, 10); if (error) return error; return ast_wait_dsc(dev, 60*60); } static int ast_erase(device_t dev) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_ERASE, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; if ((error = ast_rewind(dev))) return error; return ata_atapicmd(atadev, ccb, NULL, 0, 0, 60*60); } static int ast_test_ready(device_t dev) { struct ata_device *atadev = device_get_softc(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(atadev, ccb, NULL, 0, 0, 30); } static int ast_wait_dsc(device_t dev, int timeout) { struct ata_device *atadev = device_get_softc(dev); int error = 0; int8_t ccb[16] = { ATAPI_POLL_DSC, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; timeout *= hz; while (timeout > 0) { error = ata_atapicmd(atadev, ccb, NULL, 0, 0, 0); if (error != EBUSY) break; tsleep(&error, PRIBIO, "atpwt", hz / 2); timeout -= (hz / 2); } return error; } static void ast_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct ast_softc *stp = device_get_ivars(dev); if (bootverbose) { device_printf(dev, "<%.40s/%.8s> tape drive at ata%d as %s\n", atadev->param.model, atadev->param.revision, device_get_unit(ch->dev), (atadev->unit == ATA_MASTER) ? "master" : "slave"); device_printf(dev, "%dKB/s, ", stp->cap.max_speed); printf("transfer limit %d blk%s, ", stp->cap.ctl, (stp->cap.ctl > 1) ? "s" : ""); printf("%dKB buffer, ", (stp->cap.buffer_size * DEV_BSIZE) / 1024); printf("%s\n", ata_mode2str(atadev->mode)); device_printf(dev, "Medium: "); switch (stp->cap.medium_type) { case 0x00: printf("none"); break; case 0x17: printf("Travan 1 (400 Mbyte)"); break; case 0xb6: printf("Travan 4 (4 Gbyte)"); break; case 0xda: printf("OnStream ADR (15Gyte)"); break; default: printf("unknown (0x%x)", stp->cap.medium_type); } if (stp->cap.readonly) printf(", readonly"); if (stp->cap.reverse) printf(", reverse"); if (stp->cap.eformat) printf(", eformat"); if (stp->cap.qfa) printf(", qfa"); if (stp->cap.lock) printf(", lock"); if (stp->cap.locked) printf(", locked"); if (stp->cap.prevent) printf(", prevent"); if (stp->cap.eject) printf(", eject"); if (stp->cap.disconnect) printf(", disconnect"); if (stp->cap.ecc) printf(", ecc"); if (stp->cap.compress) printf(", compress"); if (stp->cap.blk512) printf(", 512b"); if (stp->cap.blk1024) printf(", 1024b"); if (stp->cap.blk32k) printf(", 32kb"); printf("\n"); } else { device_printf(dev, "TAPE <%.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 ast_methods[] = { /* device interface */ - DEVMETHOD(device_identify, ast_identify), DEVMETHOD(device_probe, ast_probe), DEVMETHOD(device_attach, ast_attach), DEVMETHOD(device_detach, ast_detach), DEVMETHOD(device_shutdown, ast_shutdown), /* ATA methods */ DEVMETHOD(ata_reinit, ast_reinit), { 0, 0 } }; static driver_t ast_driver = { "ast", ast_methods, 0, }; static devclass_t ast_devclass; -static int -ast_modevent(module_t mod, int what, void *arg) -{ - device_t *devs; - int ndevs, i; - - if (what == MOD_UNLOAD) { - if (!devclass_get_devices(ast_devclass, &devs, &ndevs) && devs) { - for (i = 0; i < ndevs; i++) - device_delete_child(device_get_parent(devs[i]), devs[i]); - free(devs, M_TEMP); - } - } - return 0; -} - -DRIVER_MODULE(ast, ata, ast_driver, ast_devclass, ast_modevent, NULL); +DRIVER_MODULE(ast, ata, ast_driver, ast_devclass, NULL, NULL); MODULE_VERSION(ast, 1); MODULE_DEPEND(ast, ata, 1, 1, 1); Index: head/sys/sys/ata.h =================================================================== --- head/sys/sys/ata.h (revision 145101) +++ head/sys/sys/ata.h (revision 145102) @@ -1,418 +1,418 @@ /*- * Copyright (c) 2000 - 2005 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. * 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. * * $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_ATA 0x0000 +#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_DMA_MAX 0x4f /* ATA commands */ #define ATA_NOP 0x00 /* NOP command */ #define ATA_NF_FLUSHQUEUE 0x00 /* flush queued cmd's */ #define ATA_NF_AUTOPOLL 0x01 /* start autopoll function */ #define ATA_ATAPI_RESET 0x08 /* reset ATAPI device */ #define ATA_READ 0x20 /* read command */ #define ATA_READ48 0x24 /* read command */ #define ATA_READ_DMA48 0x25 /* read w/DMA command */ #define ATA_READ_DMA_QUEUED48 0x26 /* read w/DMA QUEUED command */ #define ATA_READ_MUL48 0x29 /* read multi command */ #define ATA_WRITE 0x30 /* write command */ #define ATA_WRITE48 0x34 /* write command */ #define ATA_WRITE_DMA48 0x35 /* write w/DMA command */ #define ATA_WRITE_DMA_QUEUED48 0x36 /* write w/DMA QUEUED command */ #define ATA_WRITE_MUL48 0x39 /* write multi command */ #define ATA_PACKET_CMD 0xa0 /* packet command */ #define ATA_ATAPI_IDENTIFY 0xa1 /* get ATAPI params*/ #define ATA_SERVICE 0xa2 /* service command */ #define ATA_READ_MUL 0xc4 /* read multi command */ #define ATA_WRITE_MUL 0xc5 /* write multi command */ #define ATA_SET_MULTI 0xc6 /* set multi size command */ #define ATA_READ_DMA_QUEUED 0xc7 /* read w/DMA QUEUED command */ #define ATA_READ_DMA 0xc8 /* read w/DMA command */ #define ATA_WRITE_DMA 0xca /* write w/DMA command */ #define ATA_WRITE_DMA_QUEUED 0xcc /* write w/DMA QUEUED command */ #define ATA_SLEEP 0xe6 /* sleep command */ #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 */ /* 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_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_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_cmd { int channel; int device; int cmd; #define ATAGMAXCHANNEL 0x0101 #define ATAGPARM 0x0102 #define ATAGMODE 0x0103 #define ATASMODE 0x0104 #define ATAREQUEST 0x0108 #define ATAREINIT 0x0110 #define ATAATTACH 0x0111 #define ATADETACH 0x0112 #define ATARAIDCREATE 0x0120 #define ATARAIDDELETE 0x0121 #define ATARAIDSTATUS 0x0122 #define ATARAIDADDSPARE 0x0123 #define ATARAIDREBUILD 0x0124 #define ATAENCSTAT 0x0130 union { int maxchan; struct { int type[2]; char name[2][32]; struct ata_params params[2]; } param; struct { int mode[2]; } mode; struct { union { struct { u_int8_t command; u_int8_t feature; u_int64_t lba; u_int16_t count; } ata; struct { char ccb[16]; } 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; } request; struct raid_setup { int type; #define AR_JBOD 0x01 #define AR_SPAN 0x02 #define AR_RAID0 0x04 #define AR_RAID1 0x08 #define AR_RAID01 0x10 #define AR_RAID3 0x20 #define AR_RAID4 0x40 #define AR_RAID5 0x80 int total_disks; int disks[16]; int interleave; int unit; } raid_setup; struct raid_status { int type; int total_disks; int disks[16]; int interleave; int status; #define AR_READY 1 #define AR_DEGRADED 2 #define AR_REBUILDING 4 int progress; } raid_status; struct { int disk; } raid_spare; struct { int fan; int temp; int v05; int v12; } enclosure; } u; }; #define IOCATA _IOWR('a', 1, struct ata_cmd) #endif /* _SYS_ATA_H_ */