Index: head/sys/dev/ata/ata-disk.c =================================================================== --- head/sys/dev/ata/ata-disk.c (revision 178855) +++ head/sys/dev/ata/ata-disk.c (revision 178856) @@ -1,579 +1,579 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void ad_init(device_t dev); static void ad_get_geometry(device_t dev); static void ad_set_geometry(device_t dev); static void ad_done(struct ata_request *request); static void ad_describe(device_t dev); static int ad_version(u_int16_t version); static disk_strategy_t ad_strategy; static disk_ioctl_t ad_ioctl; static dumper_t ad_dump; /* * Most platforms map firmware geom to actual, but some don't. If * not overridden, default to nothing. */ #ifndef ad_firmware_geom_adjust #define ad_firmware_geom_adjust(dev, disk) #endif /* local vars */ static MALLOC_DEFINE(M_AD, "ad_driver", "ATA disk driver"); static int ad_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (!(atadev->param.config & ATA_PROTO_ATAPI) || (atadev->param.config == ATA_CFA_MAGIC1) || (atadev->param.config == ATA_CFA_MAGIC2) || (atadev->param.config == ATA_CFA_MAGIC3)) 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; /* check that we have a virgin disk to attach */ if (device_get_ivars(dev)) return EEXIST; if (!(adp = malloc(sizeof(struct ad_softc), M_AD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } device_set_ivars(dev, adp); /* get device geometry into internal structs */ ad_get_geometry(dev); /* set the max size if configured */ if (ata_setmax) ad_set_geometry(dev); /* init device parameters */ ad_init(dev); /* announce we are here */ ad_describe(dev); /* create the disk device */ adp->disk = disk_alloc(); adp->disk->d_strategy = ad_strategy; adp->disk->d_ioctl = ad_ioctl; adp->disk->d_dump = ad_dump; adp->disk->d_name = "ad"; adp->disk->d_drv1 = dev; - adp->disk->d_maxsize = ch->dma.max_iosize; + adp->disk->d_maxsize = ch->dma.max_iosize ? ch->dma.max_iosize : 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); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) adp->disk->d_flags = DISKFLAG_CANFLUSHCACHE; if ((atadev->param.support.command2 & ATA_SUPPORT_CFA) || atadev->param.config == ATA_PROTO_CFA) adp->disk->d_flags = DISKFLAG_CANDELETE; snprintf(adp->disk->d_ident, sizeof(adp->disk->d_ident), "ad:%s", atadev->param.serial); disk_create(adp->disk, DISK_VERSION); device_add_child(dev, "subdisk", device_get_unit(dev)); ad_firmware_geom_adjust(dev, adp->disk); bus_generic_attach(dev); callout_init(&atadev->spindown_timer, 1); return 0; } static int ad_detach(device_t dev) { struct ad_softc *adp = device_get_ivars(dev); struct ata_device *atadev = device_get_softc(dev); device_t *children; int nchildren, i; /* check that we have a valid disk to detach */ if (!device_get_ivars(dev)) return ENXIO; /* destroy the power timeout */ callout_drain(&atadev->spindown_timer); /* detach & delete all children */ if (!device_get_children(dev, &children, &nchildren)) { for (i = 0; i < nchildren; i++) if (children[i]) device_delete_child(dev, children[i]); free(children, M_TEMP); } /* detroy disk from the system so we dont get any further requests */ disk_destroy(adp->disk); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(adp, M_AD); return 0; } static void ad_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0); } static int ad_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); /* if detach pending, return error */ if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATA_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATA_SLAVE))) { return 1; } ad_init(dev); return 0; } static void ad_power_callback(struct ata_request *request) { device_printf(request->dev, "drive spun down.\n"); ata_free_request(request); } static void ad_spindown(void *priv) { device_t dev = priv; struct ata_device *atadev = device_get_softc(dev); struct ata_request *request; if (!atadev->spindown) return; device_printf(dev, "Idle, spin down\n"); atadev->spindown_state = 1; if (!(request = ata_alloc_request())) { device_printf(dev, "FAILURE - out of memory in ad_spindown\n"); return; } request->dev = dev; request->flags = ATA_R_CONTROL; request->timeout = 5; request->retries = 1; request->callback = ad_power_callback; request->u.ata.command = ATA_STANDBY_IMMEDIATE; ata_queue_request(request); } 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 (atadev->spindown) callout_reset(&atadev->spindown_timer, hz * atadev->spindown, ad_spindown, dev); 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; if (atadev->spindown_state) { device_printf(dev, "request while spun down, starting.\n"); atadev->spindown_state = 0; request->timeout = 31; } else { request->timeout = 5; } request->retries = 2; request->data = bp->bio_data; request->bytecount = bp->bio_bcount; request->u.ata.lba = bp->bio_pblkno; request->u.ata.count = request->bytecount / DEV_BSIZE; request->transfersize = min(bp->bio_bcount, atadev->max_iosize); switch (bp->bio_cmd) { case BIO_READ: request->flags = ATA_R_READ; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_READ_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_READ_MUL; else request->u.ata.command = ATA_READ; break; case BIO_WRITE: request->flags = ATA_R_WRITE; if (atadev->mode >= ATA_DMA) { request->u.ata.command = ATA_WRITE_DMA; request->flags |= ATA_R_DMA; } else if (request->transfersize > DEV_BSIZE) request->u.ata.command = ATA_WRITE_MUL; else request->u.ata.command = ATA_WRITE; break; case BIO_DELETE: request->flags = ATA_R_CONTROL; request->u.ata.command = ATA_CFA_ERASE; request->transfersize = 0; request->donecount = bp->bio_bcount; break; case BIO_FLUSH: request->u.ata.lba = 0; request->u.ata.count = 0; request->u.ata.feature = 0; request->bytecount = 0; request->transfersize = 0; request->flags = ATA_R_CONTROL; request->u.ata.command = ATA_FLUSHCACHE; break; default: device_printf(dev, "FAILURE - unknown BIO operation\n"); ata_free_request(request); biofinish(bp, NULL, EIO); return; } request->flags |= ATA_R_ORDERED; ata_queue_request(request); } static void ad_done(struct ata_request *request) { struct bio *bp = request->bio; /* finish up transfer */ if ((bp->bio_error = request->result)) bp->bio_flags |= BIO_ERROR; bp->bio_resid = bp->bio_bcount - request->donecount; biodone(bp); ata_free_request(request); } static int ad_ioctl(struct disk *disk, u_long cmd, void *data, int flag, struct thread *td) { return ata_device_ioctl(disk->d_drv1, cmd, data); } static int ad_dump(void *arg, void *virtual, vm_offset_t physical, off_t offset, size_t length) { struct disk *dp = arg; struct bio bp; /* length zero is special and really means flush buffers to media */ if (!length) { struct ata_device *atadev = device_get_softc(dp->d_drv1); int error = 0; if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) error = ata_controlcmd(dp->d_drv1, ATA_FLUSHCACHE, 0, 0, 0); return error; } bzero(&bp, sizeof(struct bio)); bp.bio_disk = dp; bp.bio_pblkno = offset / DEV_BSIZE; bp.bio_bcount = length; bp.bio_data = virtual; bp.bio_cmd = BIO_WRITE; ad_strategy(&bp); return bp.bio_error; } static void ad_init(device_t dev) { struct ata_device *atadev = device_get_softc(dev); ATA_SETMODE(device_get_parent(dev), dev); /* enable readahead caching */ if (atadev->param.support.command1 & ATA_SUPPORT_LOOKAHEAD) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_RCACHE, 0, 0); /* enable write caching if supported and configured */ if (atadev->param.support.command1 & ATA_SUPPORT_WRITECACHE) { if (ata_wc) ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_ENAB_WCACHE, 0, 0); else ata_controlcmd(dev, ATA_SETFEATURES, ATA_SF_DIS_WCACHE, 0, 0); } /* use multiple sectors/interrupt if device supports it */ if (ad_version(atadev->param.version_major)) { int secsperint = max(1, min(atadev->param.sectors_intr, 16)); if (!ata_controlcmd(dev, ATA_SET_MULTI, 0, 0, secsperint)) atadev->max_iosize = secsperint * DEV_BSIZE; } else atadev->max_iosize = DEV_BSIZE; } static void ad_get_geometry(device_t dev) { struct ata_device *atadev = device_get_softc(dev); struct ad_softc *adp = device_get_ivars(dev); u_int64_t lbasize48; u_int32_t lbasize; if ((atadev->param.atavalid & ATA_FLAG_54_58) && atadev->param.current_heads && atadev->param.current_sectors) { 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; } static void ad_set_geometry(device_t dev) { struct ad_softc *adp = device_get_ivars(dev); struct ata_request *request; if (1 | bootverbose) device_printf(dev, "ORG %ju sectors [%juC/%dH/%dS]\n", adp->total_secs, adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors); if (!(request = ata_alloc_request())) return; /* get the max native size the device supports */ request->dev = dev; request->u.ata.command = ATA_READ_NATIVE_MAX_ADDRESS; request->u.ata.lba = 0; request->u.ata.count = 0; request->u.ata.feature = 0; request->flags = ATA_R_CONTROL | ATA_R_QUIET; request->timeout = 5; request->retries = 0; ata_queue_request(request); if (request->status & ATA_S_ERROR) goto out; if (1 | bootverbose) device_printf(dev, "MAX %ju sectors\n", request->u.ata.lba + 1); /* if original size equals max size nothing more todo */ if (adp->total_secs >= request->u.ata.lba) goto out; /* set the max native size to its max */ request->dev = dev; request->u.ata.command = ATA_SET_MAX_ADDRESS; request->u.ata.count = 1; request->u.ata.feature = 0; request->flags = ATA_R_CONTROL; request->timeout = 5; request->retries = 0; ata_queue_request(request); if (request->status & ATA_S_ERROR) goto out; /* refresh geometry from drive */ ata_getparam(device_get_softc(dev), 0); ad_get_geometry(dev); if (1 | bootverbose) device_printf(dev, "NEW %ju sectors [%juC/%dH/%dS]\n", adp->total_secs, adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors); out: ata_free_request(request); } static 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 if (!strncmp(atadev->param.model, "HDS", 3)) strcpy(vendor, "Hitachi "); else strcpy(vendor, ""); strncpy(product, atadev->param.model, 40); } device_printf(dev, "%juMB <%s%s %.8s> at ata%d-%s %s%s\n", adp->total_secs / (1048576 / DEV_BSIZE), vendor, product, atadev->param.revision, device_get_unit(ch->dev), ata_unit2str(atadev), (adp->flags & AD_F_TAG_ENABLED) ? "tagged " : "", ata_mode2str(atadev->mode)); if (bootverbose) { device_printf(dev, "%ju sectors [%juC/%dH/%dS] " "%d sectors/interrupt %d depth queue\n", adp->total_secs, adp->total_secs / (adp->heads * adp->sectors), adp->heads, adp->sectors, atadev->max_iosize / DEV_BSIZE, adp->num_tags + 1); } } static int ad_version(u_int16_t version) { int bit; if (version == 0xffff) return 0; for (bit = 15; bit >= 0; bit--) if (version & (1< * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static int ata_generic_status(device_t dev); static int ata_wait(struct ata_channel *ch, struct ata_device *, u_int8_t); static void ata_pio_read(struct ata_request *, int); static void ata_pio_write(struct ata_request *, int); static void ata_tf_read(struct ata_request *); static void ata_tf_write(struct ata_request *); /* * low level ATA functions */ void ata_generic_hw(device_t dev) { struct ata_channel *ch = device_get_softc(dev); ch->hw.begin_transaction = ata_begin_transaction; ch->hw.end_transaction = ata_end_transaction; ch->hw.status = ata_generic_status; ch->hw.softreset = NULL; ch->hw.command = ata_generic_command; ch->hw.tf_read = ata_tf_read; ch->hw.tf_write = ata_tf_write; ch->hw.pm_read = NULL; ch->hw.pm_write = NULL; } /* must be called with ATA channel locked and state_mtx held */ int ata_begin_transaction(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); int dummy, error; ATA_DEBUG_RQ(request, "begin transaction"); /* disable ATAPI DMA writes if HW doesn't support it */ if ((ch->flags & ATA_ATAPI_DMA_RO) && ((request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE)) == (ATA_R_ATAPI | ATA_R_DMA | ATA_R_WRITE))) request->flags &= ~ATA_R_DMA; /* check for 48 bit access and convert if needed */ ata_modify_if_48bit(request); switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA)) { /* ATA PIO data transfer and control commands */ default: { /* record command direction here as our request might be gone later */ int write = (request->flags & ATA_R_WRITE); /* issue command */ if (ch->hw.command(request)) { device_printf(request->dev, "error issuing %s command\n", ata_cmd2str(request)); request->result = EIO; goto begin_finished; } /* device reset doesn't interrupt */ if (request->u.ata.command == ATA_DEVICE_RESET) { int timeout = 1000000; do { DELAY(10); request->status = ATA_IDX_INB(ch, ATA_STATUS); } while (request->status & ATA_S_BUSY && timeout--); if (request->status & ATA_S_ERROR) request->error = ATA_IDX_INB(ch, ATA_ERROR); goto begin_finished; } /* if write command output the data */ if (write) { if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) { device_printf(request->dev, "timeout waiting for write DRQ\n"); request->result = EIO; goto begin_finished; } ata_pio_write(request, request->transfersize); } } goto begin_continue; /* ATA DMA data transfer commands */ case ATA_R_DMA: /* check sanity, setup SG list and DMA engine */ if ((error = ch->dma.load(request, NULL, &dummy))) { device_printf(request->dev, "setting up DMA failed\n"); request->result = error; goto begin_finished; } /* issue command */ if (ch->hw.command(request)) { device_printf(request->dev, "error issuing %s command\n", ata_cmd2str(request)); request->result = EIO; goto begin_finished; } /* start DMA engine */ if (ch->dma.start && ch->dma.start(request)) { device_printf(request->dev, "error starting DMA\n"); request->result = EIO; goto begin_finished; } goto begin_continue; /* ATAPI PIO commands */ case ATA_R_ATAPI: /* is this just a POLL DSC command ? */ if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit)); DELAY(10); if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC)) request->result = EBUSY; goto begin_finished; } /* start ATAPI operation */ if (ch->hw.command(request)) { device_printf(request->dev, "error issuing ATA PACKET command\n"); request->result = EIO; goto begin_finished; } goto begin_continue; /* ATAPI DMA commands */ case ATA_R_ATAPI|ATA_R_DMA: /* is this just a POLL DSC command ? */ if (request->u.atapi.ccb[0] == ATAPI_POLL_DSC) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit)); DELAY(10); if (!(ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_DSC)) request->result = EBUSY; goto begin_finished; } /* check sanity, setup SG list and DMA engine */ if ((error = ch->dma.load(request, NULL, &dummy))) { device_printf(request->dev, "setting up DMA failed\n"); request->result = error; goto begin_finished; } /* start ATAPI operation */ if (ch->hw.command(request)) { device_printf(request->dev, "error issuing ATA PACKET command\n"); request->result = EIO; goto begin_finished; } /* start DMA engine */ if (ch->dma.start && ch->dma.start(request)) { request->result = EIO; goto begin_finished; } goto begin_continue; } /* NOT REACHED */ printf("ata_begin_transaction OOPS!!!\n"); begin_finished: - ch->dma.unload(request); + if (ch->dma.unload) { + ch->dma.unload(request); + } return ATA_OP_FINISHED; begin_continue: callout_reset(&request->callout, request->timeout * hz, (timeout_t*)ata_timeout, request); return ATA_OP_CONTINUES; } /* must be called with ATA channel locked and state_mtx held */ int ata_end_transaction(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); int length; ATA_DEBUG_RQ(request, "end transaction"); /* clear interrupt and get status */ request->status = ATA_IDX_INB(ch, ATA_STATUS); switch (request->flags & (ATA_R_ATAPI | ATA_R_DMA | ATA_R_CONTROL)) { /* ATA PIO data transfer and control commands */ default: /* on timeouts we have no data or anything so just return */ if (request->flags & ATA_R_TIMEOUT) goto end_finished; /* on control commands read back registers to the request struct */ if (request->flags & ATA_R_CONTROL) { ch->hw.tf_read(request); } /* if we got an error we are done with the HW */ if (request->status & ATA_S_ERROR) { request->error = ATA_IDX_INB(ch, ATA_ERROR); goto end_finished; } /* are we moving data ? */ if (request->flags & (ATA_R_READ | ATA_R_WRITE)) { /* if read data get it */ if (request->flags & ATA_R_READ) { int flags = ATA_S_DRQ; if (request->u.ata.command != ATA_ATAPI_IDENTIFY) flags |= ATA_S_READY; if (ata_wait(ch, atadev, flags) < 0) { device_printf(request->dev, "timeout waiting for read DRQ\n"); request->result = EIO; goto end_finished; } ata_pio_read(request, request->transfersize); } /* update how far we've gotten */ request->donecount += request->transfersize; /* do we need a scoop more ? */ if (request->bytecount > request->donecount) { /* set this transfer size according to HW capabilities */ request->transfersize = min((request->bytecount - request->donecount), request->transfersize); /* if data write command, output the data */ if (request->flags & ATA_R_WRITE) { /* if we get an error here we are done with the HW */ if (ata_wait(ch, atadev, (ATA_S_READY | ATA_S_DRQ)) < 0) { device_printf(request->dev, "timeout waiting for write DRQ\n"); request->status = ATA_IDX_INB(ch, ATA_STATUS); goto end_finished; } /* output data and return waiting for new interrupt */ ata_pio_write(request, request->transfersize); goto end_continue; } /* if data read command, return & wait for interrupt */ if (request->flags & ATA_R_READ) goto end_continue; } } /* done with HW */ goto end_finished; /* ATA DMA data transfer commands */ case ATA_R_DMA: /* stop DMA engine and get status */ if (ch->dma.stop) request->dma->status = ch->dma.stop(request); /* did we get error or data */ if (request->status & ATA_S_ERROR) request->error = ATA_IDX_INB(ch, ATA_ERROR); else if (request->dma->status & ATA_BMSTAT_ERROR) request->status |= ATA_S_ERROR; else if (!(request->flags & ATA_R_TIMEOUT)) request->donecount = request->bytecount; /* release SG list etc */ ch->dma.unload(request); /* done with HW */ goto end_finished; /* ATAPI PIO commands */ case ATA_R_ATAPI: length = ATA_IDX_INB(ch, ATA_CYL_LSB)|(ATA_IDX_INB(ch, ATA_CYL_MSB)<<8); /* on timeouts we have no data or anything so just return */ if (request->flags & ATA_R_TIMEOUT) goto end_finished; switch ((ATA_IDX_INB(ch, ATA_IREASON) & (ATA_I_CMD | ATA_I_IN)) | (request->status & ATA_S_DRQ)) { case ATAPI_P_CMDOUT: /* this seems to be needed for some (slow) devices */ DELAY(10); if (!(request->status & ATA_S_DRQ)) { device_printf(request->dev, "command interrupt without DRQ\n"); request->status = ATA_S_ERROR; goto end_finished; } ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb, (atadev->param.config & ATA_PROTO_MASK)== ATA_PROTO_ATAPI_12 ? 6 : 8); /* return wait for interrupt */ goto end_continue; case ATAPI_P_WRITE: if (request->flags & ATA_R_READ) { request->status = ATA_S_ERROR; device_printf(request->dev, "%s trying to write on read buffer\n", ata_cmd2str(request)); goto end_finished; break; } ata_pio_write(request, length); request->donecount += length; /* set next transfer size according to HW capabilities */ request->transfersize = min((request->bytecount-request->donecount), request->transfersize); /* return wait for interrupt */ goto end_continue; case ATAPI_P_READ: if (request->flags & ATA_R_WRITE) { request->status = ATA_S_ERROR; device_printf(request->dev, "%s trying to read on write buffer\n", ata_cmd2str(request)); goto end_finished; } ata_pio_read(request, length); request->donecount += length; /* set next transfer size according to HW capabilities */ request->transfersize = min((request->bytecount-request->donecount), request->transfersize); /* return wait for interrupt */ goto end_continue; case ATAPI_P_DONEDRQ: device_printf(request->dev, "WARNING - %s DONEDRQ non conformant device\n", ata_cmd2str(request)); if (request->flags & ATA_R_READ) { ata_pio_read(request, length); request->donecount += length; } else if (request->flags & ATA_R_WRITE) { ata_pio_write(request, length); request->donecount += length; } else request->status = ATA_S_ERROR; /* FALLTHROUGH */ case ATAPI_P_ABORT: case ATAPI_P_DONE: if (request->status & (ATA_S_ERROR | ATA_S_DWF)) request->error = ATA_IDX_INB(ch, ATA_ERROR); goto end_finished; default: device_printf(request->dev, "unknown transfer phase\n"); request->status = ATA_S_ERROR; } /* done with HW */ goto end_finished; /* ATAPI DMA commands */ case ATA_R_ATAPI|ATA_R_DMA: /* stop DMA engine and get status */ if (ch->dma.stop) request->dma->status = ch->dma.stop(request); /* did we get error or data */ if (request->status & (ATA_S_ERROR | ATA_S_DWF)) request->error = ATA_IDX_INB(ch, ATA_ERROR); else if (request->dma->status & ATA_BMSTAT_ERROR) request->status |= ATA_S_ERROR; else if (!(request->flags & ATA_R_TIMEOUT)) request->donecount = request->bytecount; /* release SG list etc */ ch->dma.unload(request); /* done with HW */ goto end_finished; } /* NOT REACHED */ printf("ata_end_transaction OOPS!!\n"); end_finished: callout_stop(&request->callout); return ATA_OP_FINISHED; end_continue: return ATA_OP_CONTINUES; } /* must be called with ATA channel locked and state_mtx held */ void ata_generic_reset(device_t dev) { struct ata_channel *ch = device_get_softc(dev); u_int8_t ostat0 = 0, stat0 = 0, ostat1 = 0, stat1 = 0; u_int8_t err = 0, lsb = 0, msb = 0; int mask = 0, timeout; /* do we have any signs of ATA/ATAPI HW being present ? */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_MASTER)); DELAY(10); ostat0 = ATA_IDX_INB(ch, ATA_STATUS); if ((ostat0 & 0xf8) != 0xf8 && ostat0 != 0xa5) { stat0 = ATA_S_BUSY; mask |= 0x01; } /* in some setups we dont want to test for a slave */ if (!(ch->flags & ATA_NO_SLAVE)) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_SLAVE)); DELAY(10); ostat1 = ATA_IDX_INB(ch, ATA_STATUS); if ((ostat1 & 0xf8) != 0xf8 && ostat1 != 0xa5) { stat1 = ATA_S_BUSY; mask |= 0x02; } } if (bootverbose) device_printf(dev, "reset tp1 mask=%02x ostat0=%02x ostat1=%02x\n", mask, ostat0, ostat1); /* if nothing showed up there is no need to get any further */ /* XXX SOS is that too strong?, we just might loose devices here */ ch->devices = 0; if (!mask) return; /* reset (both) devices on this channel */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(ATA_MASTER)); DELAY(10); ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS | ATA_A_RESET); ata_udelay(10000); ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_IDS); ata_udelay(100000); ATA_IDX_INB(ch, ATA_ERROR); /* wait for BUSY to go inactive */ for (timeout = 0; timeout < 310; timeout++) { if ((mask & 0x01) && (stat0 & ATA_S_BUSY)) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_MASTER)); DELAY(10); err = ATA_IDX_INB(ch, ATA_ERROR); lsb = ATA_IDX_INB(ch, ATA_CYL_LSB); msb = ATA_IDX_INB(ch, ATA_CYL_MSB); stat0 = ATA_IDX_INB(ch, ATA_STATUS); if (bootverbose) device_printf(dev, "stat0=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n", stat0, err, lsb, msb); if (stat0 == err && lsb == err && msb == err && timeout > (stat0 & ATA_S_BUSY ? 100 : 10)) mask &= ~0x01; if (!(stat0 & ATA_S_BUSY)) { if ((err & 0x7f) == ATA_E_ILI) { if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) { ch->devices |= ATA_ATAPI_MASTER; } else if (stat0 & ATA_S_READY) { ch->devices |= ATA_ATA_MASTER; } } else if ((stat0 & 0x0f) && err == lsb && err == msb) { stat0 |= ATA_S_BUSY; } } } if ((mask & 0x02) && (stat1 & ATA_S_BUSY) && !((mask & 0x01) && (stat0 & ATA_S_BUSY))) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(ATA_SLAVE)); DELAY(10); err = ATA_IDX_INB(ch, ATA_ERROR); lsb = ATA_IDX_INB(ch, ATA_CYL_LSB); msb = ATA_IDX_INB(ch, ATA_CYL_MSB); stat1 = ATA_IDX_INB(ch, ATA_STATUS); if (bootverbose) device_printf(dev, "stat1=0x%02x err=0x%02x lsb=0x%02x msb=0x%02x\n", stat1, err, lsb, msb); if (stat1 == err && lsb == err && msb == err && timeout > (stat1 & ATA_S_BUSY ? 100 : 10)) mask &= ~0x02; if (!(stat1 & ATA_S_BUSY)) { if ((err & 0x7f) == ATA_E_ILI) { if (lsb == ATAPI_MAGIC_LSB && msb == ATAPI_MAGIC_MSB) { ch->devices |= ATA_ATAPI_SLAVE; } else if (stat1 & ATA_S_READY) { ch->devices |= ATA_ATA_SLAVE; } } else if ((stat1 & 0x0f) && err == lsb && err == msb) { stat1 |= ATA_S_BUSY; } } } if (mask == 0x00) /* nothing to wait for */ break; if (mask == 0x01) /* wait for master only */ if (!(stat0 & ATA_S_BUSY) || (stat0 == 0xff && timeout > 10)) break; if (mask == 0x02) /* wait for slave only */ if (!(stat1 & ATA_S_BUSY) || (stat1 == 0xff && timeout > 10)) break; if (mask == 0x03) { /* wait for both master & slave */ if (!(stat0 & ATA_S_BUSY) && !(stat1 & ATA_S_BUSY)) break; if ((stat0 == 0xff) && (timeout > 20)) mask &= ~0x01; if ((stat1 == 0xff) && (timeout > 20)) mask &= ~0x02; } ata_udelay(100000); } if (bootverbose) device_printf(dev, "reset tp2 stat0=%02x stat1=%02x devices=0x%x\n", stat0, stat1, ch->devices); } /* must be called with ATA channel locked and state_mtx held */ int ata_generic_status(device_t dev) { struct ata_channel *ch = device_get_softc(dev); if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) { DELAY(100); if (ATA_IDX_INB(ch, ATA_ALTSTAT) & ATA_S_BUSY) return 0; } return 1; } static int ata_wait(struct ata_channel *ch, struct ata_device *atadev, u_int8_t mask) { u_int8_t status; int timeout = 0; DELAY(1); /* wait at max 1 second for device to get !BUSY */ while (timeout < 1000000) { status = ATA_IDX_INB(ch, ATA_ALTSTAT); /* if drive fails status, reselect the drive and try again */ if (status == 0xff) { ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit)); timeout += 1000; DELAY(1000); continue; } /* are we done ? */ if (!(status & ATA_S_BUSY)) break; if (timeout > 1000) { timeout += 1000; DELAY(1000); } else { timeout += 10; DELAY(10); } } if (timeout >= 1000000) return -2; if (!mask) return (status & ATA_S_ERROR); DELAY(1); /* wait 50 msec for bits wanted */ timeout = 5000; while (timeout--) { status = ATA_IDX_INB(ch, ATA_ALTSTAT); if ((status & mask) == mask) return (status & ATA_S_ERROR); DELAY(10); } return -3; } int ata_generic_command(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); /* select device */ ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(atadev->unit)); /* ready to issue command ? */ if (ata_wait(ch, atadev, 0) < 0) { device_printf(request->dev, "timeout waiting to issue command\n"); return -1; } /* enable interrupt */ ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT); if (request->flags & ATA_R_ATAPI) { int timeout = 5000; /* issue packet command to controller */ if (request->flags & ATA_R_DMA) { ATA_IDX_OUTB(ch, ATA_FEATURE, ATA_F_DMA); ATA_IDX_OUTB(ch, ATA_CYL_LSB, 0); ATA_IDX_OUTB(ch, ATA_CYL_MSB, 0); } else { ATA_IDX_OUTB(ch, ATA_FEATURE, 0); ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->transfersize); ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->transfersize >> 8); } ATA_IDX_OUTB(ch, ATA_COMMAND, ATA_PACKET_CMD); /* command interrupt device ? just return and wait for interrupt */ if ((atadev->param.config & ATA_DRQ_MASK) == ATA_DRQ_INTR) return 0; /* wait for ready to write ATAPI command block */ while (timeout--) { int reason = ATA_IDX_INB(ch, ATA_IREASON); int status = ATA_IDX_INB(ch, ATA_STATUS); if (((reason & (ATA_I_CMD | ATA_I_IN)) | (status & (ATA_S_DRQ | ATA_S_BUSY))) == ATAPI_P_CMDOUT) break; DELAY(20); } if (timeout <= 0) { device_printf(request->dev, "timeout waiting for ATAPI ready\n"); request->result = EIO; return -1; } /* this seems to be needed for some (slow) devices */ DELAY(10); /* output command block */ ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (int16_t *)request->u.atapi.ccb, (atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12 ? 6 : 8); } else { ch->hw.tf_write(request); /* issue command to controller */ ATA_IDX_OUTB(ch, ATA_COMMAND, request->u.ata.command); } return 0; } static void ata_tf_read(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); if (atadev->flags & ATA_D_48BIT_ACTIVE) { ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT | ATA_A_HOB); request->u.ata.count = (ATA_IDX_INB(ch, ATA_COUNT) << 8); request->u.ata.lba = ((u_int64_t)(ATA_IDX_INB(ch, ATA_SECTOR)) << 24) | ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_LSB)) << 32) | ((u_int64_t)(ATA_IDX_INB(ch, ATA_CYL_MSB)) << 40); ATA_IDX_OUTB(ch, ATA_CONTROL, ATA_A_4BIT); request->u.ata.count |= ATA_IDX_INB(ch, ATA_COUNT); request->u.ata.lba |= (ATA_IDX_INB(ch, ATA_SECTOR) | (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) | (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16)); } else { request->u.ata.count = ATA_IDX_INB(ch, ATA_COUNT); request->u.ata.lba = ATA_IDX_INB(ch, ATA_SECTOR) | (ATA_IDX_INB(ch, ATA_CYL_LSB) << 8) | (ATA_IDX_INB(ch, ATA_CYL_MSB) << 16) | ((ATA_IDX_INB(ch, ATA_DRIVE) & 0xf) << 24); } } static void ata_tf_write(struct ata_request *request) { struct ata_channel *ch = device_get_softc(request->parent); struct ata_device *atadev = device_get_softc(request->dev); if (atadev->flags & ATA_D_48BIT_ACTIVE) { ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature >> 8); ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature); ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count >> 8); ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count); ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba >> 24); ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba); ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 32); ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8); ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 40); ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16); ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_LBA | ATA_DEV(atadev->unit)); } else { ATA_IDX_OUTB(ch, ATA_FEATURE, request->u.ata.feature); ATA_IDX_OUTB(ch, ATA_COUNT, request->u.ata.count); if (atadev->flags & ATA_D_USE_CHS) { int heads, sectors; if (atadev->param.atavalid & ATA_FLAG_54_58) { heads = atadev->param.current_heads; sectors = atadev->param.current_sectors; } else { heads = atadev->param.heads; sectors = atadev->param.sectors; } ATA_IDX_OUTB(ch, ATA_SECTOR, (request->u.ata.lba % sectors)+1); ATA_IDX_OUTB(ch, ATA_CYL_LSB, (request->u.ata.lba / (sectors * heads))); ATA_IDX_OUTB(ch, ATA_CYL_MSB, (request->u.ata.lba / (sectors * heads)) >> 8); ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_DEV(atadev->unit) | (((request->u.ata.lba% (sectors * heads)) / sectors) & 0xf)); } else { ATA_IDX_OUTB(ch, ATA_SECTOR, request->u.ata.lba); ATA_IDX_OUTB(ch, ATA_CYL_LSB, request->u.ata.lba >> 8); ATA_IDX_OUTB(ch, ATA_CYL_MSB, request->u.ata.lba >> 16); ATA_IDX_OUTB(ch, ATA_DRIVE, ATA_D_IBM | ATA_D_LBA | ATA_DEV(atadev->unit) | ((request->u.ata.lba >> 24) & 0x0f)); } } } static void ata_pio_read(struct ata_request *request, int length) { struct ata_channel *ch = device_get_softc(request->parent); int size = min(request->transfersize, length); int resid; if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t))) ATA_IDX_INSW_STRM(ch, ATA_DATA, (void*)((uintptr_t)request->data+request->donecount), size / sizeof(int16_t)); else ATA_IDX_INSL_STRM(ch, ATA_DATA, (void*)((uintptr_t)request->data+request->donecount), size / sizeof(int32_t)); if (request->transfersize < length) { device_printf(request->dev, "WARNING - %s read data overrun %d>%d\n", ata_cmd2str(request), length, request->transfersize); for (resid = request->transfersize; resid < length; resid += sizeof(int16_t)) ATA_IDX_INW(ch, ATA_DATA); } } static void ata_pio_write(struct ata_request *request, int length) { struct ata_channel *ch = device_get_softc(request->parent); int size = min(request->transfersize, length); int resid; if (ch->flags & ATA_USE_16BIT || (size % sizeof(int32_t))) ATA_IDX_OUTSW_STRM(ch, ATA_DATA, (void*)((uintptr_t)request->data+request->donecount), size / sizeof(int16_t)); else ATA_IDX_OUTSL_STRM(ch, ATA_DATA, (void*)((uintptr_t)request->data+request->donecount), size / sizeof(int32_t)); if (request->transfersize < length) { device_printf(request->dev, "WARNING - %s write data underrun %d>%d\n", ata_cmd2str(request), length, request->transfersize); for (resid = request->transfersize; resid < length; resid += sizeof(int16_t)) ATA_IDX_OUTW(ch, ATA_DATA, 0); } } Index: head/sys/dev/ata/atapi-cd.c =================================================================== --- head/sys/dev/ata/atapi-cd.c (revision 178855) +++ head/sys/dev/ata/atapi-cd.c (revision 178856) @@ -1,1912 +1,1915 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* prototypes */ static void acd_geom_attach(void *, int); static void acd_geom_detach(void *, int); static void acd_set_ioparm(device_t); static void acd_describe(device_t); static void lba2msf(u_int32_t, u_int8_t *, u_int8_t *, u_int8_t *); static u_int32_t msf2lba(u_int8_t, u_int8_t, u_int8_t); static int acd_geom_access(struct g_provider *, int, int, int); static g_ioctl_t acd_geom_ioctl; static void acd_geom_start(struct bio *); static void acd_strategy(struct bio *); static void acd_done(struct ata_request *); static void acd_read_toc(device_t); static int acd_play(device_t, int, int); static int acd_setchan(device_t, u_int8_t, u_int8_t, u_int8_t, u_int8_t); static int acd_init_writer(device_t, int); static int acd_fixate(device_t, int); static int acd_init_track(device_t, struct cdr_track *); static int acd_flush(device_t); static int acd_read_track_info(device_t, int32_t, struct acd_track_info *); static int acd_get_progress(device_t, int *); static int acd_send_cue(device_t, struct cdr_cuesheet *); static int acd_report_key(device_t, struct dvd_authinfo *); static int acd_send_key(device_t, struct dvd_authinfo *); static int acd_read_structure(device_t, struct dvd_struct *); static int acd_tray(device_t, int); static int acd_blank(device_t, int); static int acd_prevent_allow(device_t, int); static int acd_start_stop(device_t, int); static int acd_pause_resume(device_t, int); static int acd_mode_sense(device_t, int, caddr_t, int); static int acd_mode_select(device_t, caddr_t, int); static int acd_set_speed(device_t, int, int); static void acd_get_cap(device_t); static int acd_read_format_caps(device_t, struct cdr_format_capacities *); static int acd_format(device_t, struct cdr_format_params *); static int acd_test_ready(device_t); /* internal vars */ static MALLOC_DEFINE(M_ACD, "acd_driver", "ATAPI CD driver buffers"); static struct g_class acd_class = { .name = "ACD", .version = G_VERSION, .access = acd_geom_access, .ioctl = acd_geom_ioctl, .start = acd_geom_start, }; //DECLARE_GEOM_CLASS(acd_class, acd); static int acd_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_CDROM) return 0; else return ENXIO; } static int acd_attach(device_t dev) { struct acd_softc *cdp; if (!(cdp = malloc(sizeof(struct acd_softc), M_ACD, M_NOWAIT | M_ZERO))) { device_printf(dev, "out of memory\n"); return ENOMEM; } cdp->block_size = 2048; device_set_ivars(dev, cdp); ATA_SETMODE(device_get_parent(dev), dev); ata_controlcmd(dev, ATA_DEVICE_RESET, 0, 0, 0); acd_get_cap(dev); g_post_event(acd_geom_attach, dev, M_WAITOK, NULL); /* announce we are here */ acd_describe(dev); return 0; } static int acd_detach(device_t dev) { g_waitfor_event(acd_geom_detach, dev, M_WAITOK, NULL); return 0; } static void acd_shutdown(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if (atadev->param.support.command2 & ATA_SUPPORT_FLUSHCACHE) ata_controlcmd(dev, ATA_FLUSHCACHE, 0, 0, 0); } static int acd_reinit(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) { return 1; } ATA_SETMODE(device_get_parent(dev), dev); return 0; } static void acd_geom_attach(void *arg, int flag) { struct ata_device *atadev = device_get_softc(arg); struct acd_softc *cdp = device_get_ivars(arg); struct g_geom *gp; struct g_provider *pp; g_topology_assert(); gp = g_new_geomf(&acd_class, "acd%d", device_get_unit(arg)); gp->softc = arg; cdp->gp = gp; pp = g_new_providerf(gp, "acd%d", device_get_unit(arg)); pp->index = 0; cdp->pp[0] = pp; g_error_provider(pp, 0); atadev->flags |= ATA_D_MEDIA_CHANGED; acd_set_ioparm(arg); } static void acd_geom_detach(void *arg, int flag) { struct acd_softc *cdp = device_get_ivars(arg); /* signal geom so we dont get any further requests */ g_wither_geom(cdp->gp, ENXIO); /* fail requests on the queue and any thats "in flight" for this device */ ata_fail_requests(arg); /* dont leave anything behind */ device_set_ivars(arg, NULL); free(cdp, M_ACD); } static int acd_geom_ioctl(struct g_provider *pp, u_long cmd, void *addr, int fflag, struct thread *td) { device_t dev = pp->geom->softc; struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int error = 0, nocopyout = 0; if (!cdp) return ENXIO; if (atadev->flags & ATA_D_MEDIA_CHANGED) { switch (cmd) { case CDIOCRESET: acd_test_ready(dev); break; default: acd_read_toc(dev); acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; break; } } switch (cmd) { case CDIOCRESUME: error = acd_pause_resume(dev, 1); break; case CDIOCPAUSE: error = acd_pause_resume(dev, 0); break; case CDIOCSTART: error = acd_start_stop(dev, 1); break; case CDIOCSTOP: error = acd_start_stop(dev, 0); break; case CDIOCALLOW: error = acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; break; case CDIOCPREVENT: error = acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; break; /* * XXXRW: Why does this require privilege? */ case CDIOCRESET: error = priv_check(td, PRIV_DRIVER); if (error) break; error = acd_test_ready(dev); break; case CDIOCEJECT: if (pp->acr != 1) { error = EBUSY; break; } error = acd_tray(dev, 0); break; case CDIOCCLOSE: if (pp->acr != 1) break; error = acd_tray(dev, 1); break; case CDIOREADTOCHEADER: if (!cdp->toc.hdr.ending_track) { error = EIO; break; } bcopy(&cdp->toc.hdr, addr, sizeof(cdp->toc.hdr)); break; case CDIOREADTOCENTRYS: { struct ioc_read_toc_entry *te = (struct ioc_read_toc_entry *)addr; struct toc *toc = &cdp->toc; int starting_track = te->starting_track; int len; if (!toc->hdr.ending_track) { error = EIO; break; } if (te->data_len < sizeof(toc->tab[0]) || (te->data_len % sizeof(toc->tab[0])) != 0 || (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT)) { error = EINVAL; break; } if (!starting_track) starting_track = toc->hdr.starting_track; else if (starting_track == 170) starting_track = toc->hdr.ending_track + 1; else if (starting_track < toc->hdr.starting_track || starting_track > toc->hdr.ending_track + 1) { error = EINVAL; break; } len = ((toc->hdr.ending_track + 1 - starting_track) + 1) * sizeof(toc->tab[0]); if (te->data_len < len) len = te->data_len; if (len > sizeof(toc->tab)) { error = EINVAL; break; } if (te->address_format == CD_MSF_FORMAT) { struct cd_toc_entry *entry; if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) { error = ENOMEM; break; } bcopy(&cdp->toc, toc, sizeof(struct toc)); entry = toc->tab + (toc->hdr.ending_track + 1 - toc->hdr.starting_track) + 1; while (--entry >= toc->tab) { lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute, &entry->addr.msf.second, &entry->addr.msf.frame); entry->addr_type = CD_MSF_FORMAT; } } error = copyout(toc->tab + starting_track - toc->hdr.starting_track, te->data, len); if (te->address_format == CD_MSF_FORMAT) free(toc, M_ACD); } break; case CDIOREADTOCENTRY: { struct ioc_read_toc_single_entry *te = (struct ioc_read_toc_single_entry *)addr; struct toc *toc = &cdp->toc; u_char track = te->track; if (!toc->hdr.ending_track) { error = EIO; break; } if (te->address_format != CD_MSF_FORMAT && te->address_format != CD_LBA_FORMAT) { error = EINVAL; break; } if (!track) track = toc->hdr.starting_track; else if (track == 170) track = toc->hdr.ending_track + 1; else if (track < toc->hdr.starting_track || track > toc->hdr.ending_track + 1) { error = EINVAL; break; } if (te->address_format == CD_MSF_FORMAT) { struct cd_toc_entry *entry; if (!(toc = malloc(sizeof(struct toc), M_ACD, M_NOWAIT))) { error = ENOMEM; break; } bcopy(&cdp->toc, toc, sizeof(struct toc)); entry = toc->tab + (track - toc->hdr.starting_track); lba2msf(ntohl(entry->addr.lba), &entry->addr.msf.minute, &entry->addr.msf.second, &entry->addr.msf.frame); } bcopy(toc->tab + track - toc->hdr.starting_track, &te->entry, sizeof(struct cd_toc_entry)); if (te->address_format == CD_MSF_FORMAT) free(toc, M_ACD); } break; #if __FreeBSD_version > 600008 case CDIOCREADSUBCHANNEL_SYSSPACE: nocopyout = 1; /* FALLTHROUGH */ #endif case CDIOCREADSUBCHANNEL: { struct ioc_read_subchannel *args = (struct ioc_read_subchannel *)addr; u_int8_t format; int8_t ccb[16] = { ATAPI_READ_SUBCHANNEL, 0, 0x40, 1, 0, 0, 0, sizeof(cdp->subchan)>>8, sizeof(cdp->subchan), 0, 0, 0, 0, 0, 0, 0 }; if (args->data_len > sizeof(struct cd_sub_channel_info) || args->data_len < sizeof(struct cd_sub_channel_header)) { error = EINVAL; break; } format = args->data_format; if ((format != CD_CURRENT_POSITION) && (format != CD_MEDIA_CATALOG) && (format != CD_TRACK_INFO)) { error = EINVAL; break; } ccb[1] = args->address_format & CD_MSF_FORMAT; if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan, sizeof(cdp->subchan), ATA_R_READ, 10))) break; if ((format == CD_MEDIA_CATALOG) || (format == CD_TRACK_INFO)) { if (cdp->subchan.header.audio_status == 0x11) { error = EINVAL; break; } ccb[3] = format; if (format == CD_TRACK_INFO) ccb[6] = args->track; if ((error = ata_atapicmd(dev, ccb, (caddr_t)&cdp->subchan, sizeof(cdp->subchan),ATA_R_READ,10))){ break; } } if (nocopyout == 0) { error = copyout(&cdp->subchan, args->data, args->data_len); } else { error = 0; bcopy(&cdp->subchan, args->data, args->data_len); } } break; case CDIOCPLAYMSF: { struct ioc_play_msf *args = (struct ioc_play_msf *)addr; error = acd_play(dev, msf2lba(args->start_m, args->start_s, args->start_f), msf2lba(args->end_m, args->end_s, args->end_f)); } break; case CDIOCPLAYBLOCKS: { struct ioc_play_blocks *args = (struct ioc_play_blocks *)addr; error = acd_play(dev, args->blk, args->blk + args->len); } break; case CDIOCPLAYTRACKS: { struct ioc_play_track *args = (struct ioc_play_track *)addr; int t1, t2; if (!cdp->toc.hdr.ending_track) { error = EIO; break; } if (args->end_track < cdp->toc.hdr.ending_track + 1) ++args->end_track; if (args->end_track > cdp->toc.hdr.ending_track + 1) args->end_track = cdp->toc.hdr.ending_track + 1; t1 = args->start_track - cdp->toc.hdr.starting_track; t2 = args->end_track - cdp->toc.hdr.starting_track; if (t1 < 0 || t2 < 0 || t1 > (cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track)) { error = EINVAL; break; } error = acd_play(dev, ntohl(cdp->toc.tab[t1].addr.lba), ntohl(cdp->toc.tab[t2].addr.lba)); } break; case CDIOCGETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) break; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) { error = EIO; break; } arg->vol[0] = cdp->au.port[0].volume; arg->vol[1] = cdp->au.port[1].volume; arg->vol[2] = cdp->au.port[2].volume; arg->vol[3] = cdp->au.port[3].volume; } break; case CDIOCSETVOL: { struct ioc_vol *arg = (struct ioc_vol *)addr; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) break; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) { error = EIO; break; } if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE_MASK, (caddr_t)&cdp->aumask, sizeof(cdp->aumask)))) break; cdp->au.data_length = 0; cdp->au.port[0].channels = CHANNEL_0; cdp->au.port[1].channels = CHANNEL_1; cdp->au.port[0].volume = arg->vol[0] & cdp->aumask.port[0].volume; cdp->au.port[1].volume = arg->vol[1] & cdp->aumask.port[1].volume; cdp->au.port[2].volume = arg->vol[2] & cdp->aumask.port[2].volume; cdp->au.port[3].volume = arg->vol[3] & cdp->aumask.port[3].volume; error = acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au)); } break; case CDIOCSETPATCH: { struct ioc_patch *arg = (struct ioc_patch *)addr; error = acd_setchan(dev, arg->patch[0], arg->patch[1], arg->patch[2], arg->patch[3]); } break; case CDIOCSETMONO: error = acd_setchan(dev, CHANNEL_0|CHANNEL_1, CHANNEL_0|CHANNEL_1, 0,0); break; case CDIOCSETSTEREO: error = acd_setchan(dev, CHANNEL_0, CHANNEL_1, 0, 0); break; case CDIOCSETMUTE: error = acd_setchan(dev, 0, 0, 0, 0); break; case CDIOCSETLEFT: error = acd_setchan(dev, CHANNEL_0, CHANNEL_0, 0, 0); break; case CDIOCSETRIGHT: error = acd_setchan(dev, CHANNEL_1, CHANNEL_1, 0, 0); break; case CDRIOCBLANK: error = acd_blank(dev, (*(int *)addr)); break; case CDRIOCNEXTWRITEABLEADDR: { struct acd_track_info track_info; if ((error = acd_read_track_info(dev, 0xff, &track_info))) break; if (!track_info.nwa_valid) { error = EINVAL; break; } *(int*)addr = track_info.next_writeable_addr; } break; case CDRIOCINITWRITER: error = acd_init_writer(dev, (*(int *)addr)); break; case CDRIOCINITTRACK: error = acd_init_track(dev, (struct cdr_track *)addr); break; case CDRIOCFLUSH: error = acd_flush(dev); break; case CDRIOCFIXATE: error = acd_fixate(dev, (*(int *)addr)); break; case CDRIOCREADSPEED: { int speed = *(int *)addr; /* Preserve old behavior: units in multiples of CDROM speed */ if (speed < 177) speed *= 177; error = acd_set_speed(dev, speed, CDR_MAX_SPEED); } break; case CDRIOCWRITESPEED: { int speed = *(int *)addr; if (speed < 177) speed *= 177; error = acd_set_speed(dev, CDR_MAX_SPEED, speed); } break; case CDRIOCGETBLOCKSIZE: *(int *)addr = cdp->block_size; break; case CDRIOCSETBLOCKSIZE: cdp->block_size = *(int *)addr; pp->sectorsize = cdp->block_size; /* hack for GEOM SOS */ acd_set_ioparm(dev); break; case CDRIOCGETPROGRESS: error = acd_get_progress(dev, (int *)addr); break; case CDRIOCSENDCUE: error = acd_send_cue(dev, (struct cdr_cuesheet *)addr); break; case CDRIOCREADFORMATCAPS: error = acd_read_format_caps(dev, (struct cdr_format_capacities *)addr); break; case CDRIOCFORMAT: error = acd_format(dev, (struct cdr_format_params *)addr); break; case DVDIOCREPORTKEY: if (cdp->cap.media & MST_READ_DVDROM) error = acd_report_key(dev, (struct dvd_authinfo *)addr); else error = EINVAL; break; case DVDIOCSENDKEY: if (cdp->cap.media & MST_READ_DVDROM) error = acd_send_key(dev, (struct dvd_authinfo *)addr); else error = EINVAL; break; case DVDIOCREADSTRUCTURE: if (cdp->cap.media & MST_READ_DVDROM) error = acd_read_structure(dev, (struct dvd_struct *)addr); else error = EINVAL; break; default: error = ata_device_ioctl(dev, cmd, addr); } return error; } static int acd_geom_access(struct g_provider *pp, int dr, int dw, int de) { device_t dev = pp->geom->softc; struct acd_softc *cdp = device_get_ivars(dev); struct ata_request *request; int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int timeout = 60, track; if (!(request = ata_alloc_request())) return ENOMEM; /* wait if drive is not finished loading the medium */ while (timeout--) { 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.key == 2 || request->u.atapi.sense.key == 7) && request->u.atapi.sense.asc == 4 && request->u.atapi.sense.ascq == 1) pause("acdld", hz / 2); else break; } ata_free_request(request); if (pp->acr == 0) { acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; acd_read_toc(dev); } if (dr + pp->acr == 0) { acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; } if ((track = pp->index)) { pp->sectorsize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352; pp->mediasize = ntohl(cdp->toc.tab[track].addr.lba) - ntohl(cdp->toc.tab[track - 1].addr.lba); } else { pp->sectorsize = cdp->block_size; pp->mediasize = cdp->disk_size; } pp->mediasize *= pp->sectorsize; return 0; } static void acd_geom_start(struct bio *bp) { device_t dev = bp->bio_to->geom->softc; struct acd_softc *cdp = device_get_ivars(dev); if (bp->bio_cmd != BIO_READ && bp->bio_cmd != BIO_WRITE) { g_io_deliver(bp, EOPNOTSUPP); return; } if (bp->bio_cmd == BIO_READ && cdp->disk_size == -1) { g_io_deliver(bp, EIO); return; } /* GEOM classes must do their own request limiting */ if (bp->bio_length <= cdp->iomax) { bp->bio_pblkno = bp->bio_offset / bp->bio_to->sectorsize; acd_strategy(bp); } else { u_int pos, size = cdp->iomax - cdp->iomax % bp->bio_to->sectorsize; struct bio *bp2, *bp3; if (!(bp2 = g_clone_bio(bp))) g_io_deliver(bp, EIO); for (pos = 0; bp2; pos += size) { bp3 = NULL; bp2->bio_done = g_std_done; bp2->bio_to = bp->bio_to; bp2->bio_offset += pos; bp2->bio_data += pos; bp2->bio_length = bp->bio_length - pos; if (bp2->bio_length > size) { bp2->bio_length = size; if (!(bp3 = g_clone_bio(bp))) bp->bio_error = ENOMEM; } bp2->bio_pblkno = bp2->bio_offset / bp2->bio_to->sectorsize; acd_strategy(bp2); bp2 = bp3; } } } static void acd_strategy(struct bio *bp) { device_t dev = bp->bio_to->geom->softc; struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); struct ata_request *request; u_int32_t lba, lastlba, count; int8_t ccb[16]; int track, blocksize; /* reject all queued entries if media changed */ if (atadev->flags & ATA_D_MEDIA_CHANGED) { g_io_deliver(bp, EIO); return; } bzero(ccb, sizeof(ccb)); track = bp->bio_to->index; if (track) { blocksize = (cdp->toc.tab[track - 1].control & 4) ? 2048 : 2352; lastlba = ntohl(cdp->toc.tab[track].addr.lba); lba = bp->bio_offset / blocksize; lba += ntohl(cdp->toc.tab[track - 1].addr.lba); } else { blocksize = cdp->block_size; lastlba = cdp->disk_size; lba = bp->bio_offset / blocksize; } count = bp->bio_length / blocksize; if (bp->bio_cmd == BIO_READ) { /* if transfer goes beyond range adjust it to be within limits */ if (lba + count > lastlba) { /* if we are entirely beyond EOM return EOF */ if (lastlba <= lba) { g_io_deliver(bp, 0); return; } count = lastlba - lba; } switch (blocksize) { case 2048: ccb[0] = ATAPI_READ_BIG; break; case 2352: ccb[0] = ATAPI_READ_CD; ccb[9] = 0xf8; break; default: ccb[0] = ATAPI_READ_CD; ccb[9] = 0x10; } } else ccb[0] = ATAPI_WRITE_BIG; ccb[1] = 0; ccb[2] = lba>>24; ccb[3] = lba>>16; ccb[4] = lba>>8; ccb[5] = lba; ccb[6] = count>>16; ccb[7] = count>>8; ccb[8] = count; if (!(request = ata_alloc_request())) { g_io_deliver(bp, ENOMEM); return; } request->dev = dev; request->bio = bp; bcopy(ccb, request->u.atapi.ccb, (atadev->param.config & ATA_PROTO_MASK) == ATA_PROTO_ATAPI_12 ? 16 : 12); request->data = bp->bio_data; request->bytecount = count * blocksize; request->transfersize = min(request->bytecount, 65534); request->timeout = (ccb[0] == ATAPI_WRITE_BIG) ? 60 : 30; request->retries = 2; request->callback = acd_done; request->flags = ATA_R_ATAPI; if (atadev->mode >= ATA_DMA) request->flags |= ATA_R_DMA; switch (bp->bio_cmd) { case BIO_READ: request->flags |= ATA_R_READ; break; case BIO_WRITE: request->flags |= ATA_R_WRITE; break; default: device_printf(dev, "unknown BIO operation\n"); ata_free_request(request); g_io_deliver(bp, EIO); return; } ata_queue_request(request); } static void acd_done(struct ata_request *request) { struct bio *bp = request->bio; /* finish up transfer */ bp->bio_completed = request->donecount; g_io_deliver(bp, request->result); ata_free_request(request); } static void acd_set_ioparm(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct acd_softc *cdp = device_get_ivars(dev); + uint32_t max_iosize; - cdp->iomax = min(ch->dma.max_iosize, 65534); + max_iosize = ch->dma.max_iosize ? ch->dma.max_iosize : DFLTPHYS; + + cdp->iomax = min(max_iosize, 65534); } static void lba2msf(u_int32_t lba, u_int8_t *m, u_int8_t *s, u_int8_t *f) { lba += 150; lba &= 0xffffff; *m = lba / (60 * 75); lba %= (60 * 75); *s = lba / 75; *f = lba % 75; } static u_int32_t msf2lba(u_int8_t m, u_int8_t s, u_int8_t f) { return (m * 60 + s) * 75 + f - 150; } static void acd_read_toc(device_t dev) { struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); struct g_provider *pp; u_int32_t sizes[2]; int8_t ccb[16]; int track, ntracks, len; atadev->flags &= ~ATA_D_MEDIA_CHANGED; bzero(&cdp->toc, sizeof(cdp->toc)); cdp->disk_size = -1; /* hack for GEOM SOS */ if (acd_test_ready(dev)) return; bzero(ccb, sizeof(ccb)); len = sizeof(struct ioc_toc_header) + sizeof(struct cd_toc_entry); ccb[0] = ATAPI_READ_TOC; ccb[7] = len>>8; ccb[8] = len; if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len, ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } ntracks = cdp->toc.hdr.ending_track - cdp->toc.hdr.starting_track + 1; if (ntracks <= 0 || ntracks > MAXTRK) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } len = sizeof(struct ioc_toc_header)+(ntracks+1)*sizeof(struct cd_toc_entry); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_TOC; ccb[7] = len>>8; ccb[8] = len; if (ata_atapicmd(dev, ccb, (caddr_t)&cdp->toc, len, ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } cdp->toc.hdr.len = ntohs(cdp->toc.hdr.len); cdp->block_size = (cdp->toc.tab[0].control & 4) ? 2048 : 2352; acd_set_ioparm(dev); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_CAPACITY; if (ata_atapicmd(dev, ccb, (caddr_t)sizes, sizeof(sizes), ATA_R_READ | ATA_R_QUIET, 30)) { bzero(&cdp->toc, sizeof(cdp->toc)); return; } cdp->disk_size = ntohl(sizes[0]) + 1; for (track = 1; track <= ntracks; track ++) { if (cdp->pp[track] != NULL) continue; pp = g_new_providerf(cdp->gp, "acd%dt%02d", device_get_unit(dev),track); pp->index = track; cdp->pp[track] = pp; g_error_provider(pp, 0); } for (; track < MAXTRK; track ++) { if (cdp->pp[track] == NULL) continue; cdp->pp[track]->flags |= G_PF_WITHER; g_orphan_provider(cdp->pp[track], ENXIO); cdp->pp[track] = NULL; } #ifdef ACD_DEBUG if (cdp->disk_size && cdp->toc.hdr.ending_track) { device_printf(dev, "(%d sectors (%d bytes)), %d tracks ", cdp->disk_size, cdp->block_size, cdp->toc.hdr.ending_track-cdp->toc.hdr.starting_track+1); if (cdp->toc.tab[0].control & 4) printf("%dMB\n", cdp->disk_size * cdp->block_size / 1048576); else printf("%d:%d audio\n", cdp->disk_size / 75 / 60, cdp->disk_size / 75 % 60); } #endif } static int acd_play(device_t dev, int start, int end) { int8_t ccb[16]; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_PLAY_MSF; lba2msf(start, &ccb[3], &ccb[4], &ccb[5]); lba2msf(end, &ccb[6], &ccb[7], &ccb[8]); return ata_atapicmd(dev, ccb, NULL, 0, 0, 10); } static int acd_setchan(device_t dev, u_int8_t c0, u_int8_t c1, u_int8_t c2, u_int8_t c3) { struct acd_softc *cdp = device_get_ivars(dev); int error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_AUDIO_PAGE, (caddr_t)&cdp->au, sizeof(cdp->au)))) return error; if (cdp->au.page_code != ATAPI_CDROM_AUDIO_PAGE) return EIO; cdp->au.data_length = 0; cdp->au.port[0].channels = c0; cdp->au.port[1].channels = c1; cdp->au.port[2].channels = c2; cdp->au.port[3].channels = c3; return acd_mode_select(dev, (caddr_t)&cdp->au, sizeof(cdp->au)); } static int acd_init_writer(device_t dev, int test_write) { int8_t ccb[16]; bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REZERO; ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60); ccb[0] = ATAPI_SEND_OPC_INFO; ccb[1] = 0x01; ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 30); return 0; } static int acd_fixate(device_t dev, int multisession) { struct acd_softc *cdp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_CLOSE_TRACK, 0x01, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int timeout = 5*60*2; int error, dummy; struct write_param param; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; if (multisession) param.session_type = CDR_SESS_MULTI; else param.session_type = CDR_SESS_NONE; if ((error = acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10))) return error; error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30); if (error) return error; /* some drives just return ready, wait for the expected fixate time */ if ((error = acd_test_ready(dev)) != EBUSY) { timeout = timeout / (cdp->cap.cur_write_speed / 177); pause("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; pause("acdcld", hz / 2); } return EIO; } static int acd_init_track(device_t dev, struct cdr_track *track) { struct acd_softc *cdp = device_get_ivars(dev); struct write_param param; int error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE; param.page_length = 0x32; param.test_write = track->test_write ? 1 : 0; param.write_type = CDR_WTYPE_TRACK; param.session_type = CDR_SESS_NONE; param.fp = 0; param.packet_size = 0; if (cdp->cap.capabilities & MST_BURNPROOF) param.burnproof = 1; switch (track->datablock_type) { case CDR_DB_RAW: if (track->preemp) param.track_mode = CDR_TMODE_AUDIO_PREEMP; else param.track_mode = CDR_TMODE_AUDIO; cdp->block_size = 2352; param.datablock_type = CDR_DB_RAW; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_ROM_MODE1: cdp->block_size = 2048; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_ROM_MODE1; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_ROM_MODE2: cdp->block_size = 2336; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_ROM_MODE2; param.session_format = CDR_SESS_CDROM; break; case CDR_DB_XA_MODE1: cdp->block_size = 2048; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE1; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_F1: cdp->block_size = 2056; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_F1; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_F2: cdp->block_size = 2324; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_F2; param.session_format = CDR_SESS_CDROM_XA; break; case CDR_DB_XA_MODE2_MIX: cdp->block_size = 2332; param.track_mode = CDR_TMODE_DATA; param.datablock_type = CDR_DB_XA_MODE2_MIX; param.session_format = CDR_SESS_CDROM_XA; break; } acd_set_ioparm(dev); return acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10); } static int acd_flush(device_t dev) { int8_t ccb[16] = { ATAPI_SYNCHRONIZE_CACHE, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, ATA_R_QUIET, 60); } static int acd_read_track_info(device_t dev, int32_t lba, struct acd_track_info *info) { int8_t ccb[16] = { ATAPI_READ_TRACK_INFO, 1, lba>>24, lba>>16, lba>>8, lba, 0, sizeof(*info)>>8, sizeof(*info), 0, 0, 0, 0, 0, 0, 0 }; int error; if ((error = ata_atapicmd(dev, ccb, (caddr_t)info, sizeof(*info), ATA_R_READ, 30))) return error; info->track_start_addr = ntohl(info->track_start_addr); info->next_writeable_addr = ntohl(info->next_writeable_addr); info->free_blocks = ntohl(info->free_blocks); info->fixed_packet_size = ntohl(info->fixed_packet_size); info->track_length = ntohl(info->track_length); return 0; } static int acd_get_progress(device_t dev, int *finished) { int8_t ccb[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; struct ata_request *request; int8_t dummy[8]; if (!(request = ata_alloc_request())) return ENOMEM; request->dev = dev; bcopy(ccb, request->u.atapi.ccb, 16); request->data = dummy; request->bytecount = sizeof(dummy); request->transfersize = min(request->bytecount, 65534); request->flags = ATA_R_ATAPI | ATA_R_READ; request->timeout = 30; ata_queue_request(request); if (!request->error && request->u.atapi.sense.specific & ATA_SENSE_SPEC_VALID) *finished = ((request->u.atapi.sense.specific2 | (request->u.atapi.sense.specific1 << 8)) * 100) / 65535; else *finished = 0; ata_free_request(request); return 0; } static int acd_send_cue(device_t dev, struct cdr_cuesheet *cuesheet) { struct acd_softc *cdp = device_get_ivars(dev); struct write_param param; int8_t ccb[16] = { ATAPI_SEND_CUE_SHEET, 0, 0, 0, 0, 0, cuesheet->len>>16, cuesheet->len>>8, cuesheet->len, 0, 0, 0, 0, 0, 0, 0 }; int8_t *buffer; int32_t error; if ((error = acd_mode_sense(dev, ATAPI_CDROM_WRITE_PARAMETERS_PAGE, (caddr_t)¶m, sizeof(param)))) return error; param.data_length = 0; param.page_code = ATAPI_CDROM_WRITE_PARAMETERS_PAGE; param.page_length = 0x32; param.test_write = cuesheet->test_write ? 1 : 0; param.write_type = CDR_WTYPE_SESSION; param.session_type = cuesheet->session_type; param.fp = 0; param.packet_size = 0; param.track_mode = CDR_TMODE_AUDIO; param.datablock_type = CDR_DB_RAW; param.session_format = cuesheet->session_format; if (cdp->cap.capabilities & MST_BURNPROOF) param.burnproof = 1; if ((error = acd_mode_select(dev, (caddr_t)¶m, param.page_length + 10))) return error; if (!(buffer = malloc(cuesheet->len, M_ACD, M_NOWAIT))) return ENOMEM; if (!(error = copyin(cuesheet->entries, buffer, cuesheet->len))) error = ata_atapicmd(dev, ccb, buffer, cuesheet->len, 0, 30); free(buffer, M_ACD); return error; } static int acd_report_key(device_t dev, struct dvd_authinfo *ai) { struct dvd_miscauth *d = NULL; u_int32_t lba = 0; int16_t length; int8_t ccb[16]; int error; switch (ai->format) { case DVD_REPORT_AGID: case DVD_REPORT_ASF: case DVD_REPORT_RPC: length = 8; break; case DVD_REPORT_KEY1: length = 12; break; case DVD_REPORT_TITLE_KEY: length = 12; lba = ai->lba; break; case DVD_REPORT_CHALLENGE: length = 16; break; case DVD_INVALIDATE_AGID: length = 0; break; default: return EINVAL; } bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_REPORT_KEY; ccb[2] = (lba >> 24) & 0xff; ccb[3] = (lba >> 16) & 0xff; ccb[4] = (lba >> 8) & 0xff; ccb[5] = lba & 0xff; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = (ai->agid << 6) | ai->format; if (length) { if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->length = htons(length - 2); } error = ata_atapicmd(dev, ccb, (caddr_t)d, length, ai->format == DVD_INVALIDATE_AGID ? 0 : ATA_R_READ,10); if (error) { if (length) 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; } if (length) free(d, M_ACD); return error; } static int acd_send_key(device_t dev, struct dvd_authinfo *ai) { struct dvd_miscauth *d; int16_t length; int8_t ccb[16]; int error; switch (ai->format) { case DVD_SEND_CHALLENGE: length = 16; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; bcopy(ai->keychal, &d->data[0], 10); break; case DVD_SEND_KEY2: length = 12; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; bcopy(&ai->keychal[0], &d->data[0], 5); break; case DVD_SEND_RPC: length = 8; if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->data[0] = ai->region; break; default: return EINVAL; } bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_SEND_KEY; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = (ai->agid << 6) | ai->format; d->length = htons(length - 2); error = ata_atapicmd(dev, ccb, (caddr_t)d, length, 0, 10); free(d, M_ACD); return error; } static int acd_read_structure(device_t dev, struct dvd_struct *s) { struct dvd_miscauth *d; u_int16_t length; int8_t ccb[16]; int error = 0; switch(s->format) { case DVD_STRUCT_PHYSICAL: length = 21; break; case DVD_STRUCT_COPYRIGHT: length = 8; break; case DVD_STRUCT_DISCKEY: length = 2052; break; case DVD_STRUCT_BCA: length = 192; break; case DVD_STRUCT_MANUFACT: length = 2052; break; case DVD_STRUCT_DDS: case DVD_STRUCT_PRERECORDED: case DVD_STRUCT_UNIQUEID: case DVD_STRUCT_LIST: case DVD_STRUCT_CMI: case DVD_STRUCT_RMD_LAST: case DVD_STRUCT_RMD_RMA: case DVD_STRUCT_DCB: return ENOSYS; default: return EINVAL; } if (!(d = malloc(length, M_ACD, M_NOWAIT | M_ZERO))) return ENOMEM; d->length = htons(length - 2); bzero(ccb, sizeof(ccb)); ccb[0] = ATAPI_READ_STRUCTURE; ccb[6] = s->layer_num; ccb[7] = s->format; ccb[8] = (length >> 8) & 0xff; ccb[9] = length & 0xff; ccb[10] = s->agid << 6; error = ata_atapicmd(dev, ccb, (caddr_t)d, length, ATA_R_READ, 30); if (error) { free(d, M_ACD); return error; } switch (s->format) { case DVD_STRUCT_PHYSICAL: { struct dvd_layer *layer = (struct dvd_layer *)&s->data[0]; layer->book_type = d->data[0] >> 4; layer->book_version = d->data[0] & 0xf; layer->disc_size = d->data[1] >> 4; layer->max_rate = d->data[1] & 0xf; layer->nlayers = (d->data[2] >> 5) & 3; layer->track_path = (d->data[2] >> 4) & 1; layer->layer_type = d->data[2] & 0xf; layer->linear_density = d->data[3] >> 4; layer->track_density = d->data[3] & 0xf; layer->start_sector = d->data[5] << 16 | d->data[6] << 8 | d->data[7]; layer->end_sector = d->data[9] << 16 | d->data[10] << 8 | d->data[11]; layer->end_sector_l0 = d->data[13] << 16 | d->data[14] << 8|d->data[15]; layer->bca = d->data[16] >> 7; break; } case DVD_STRUCT_COPYRIGHT: s->cpst = d->data[0]; s->rmi = d->data[1]; break; case DVD_STRUCT_DISCKEY: bcopy(&d->data[0], &s->data[0], 2048); break; case DVD_STRUCT_BCA: s->length = ntohs(d->length); bcopy(&d->data[0], &s->data[0], s->length); break; case DVD_STRUCT_MANUFACT: s->length = ntohs(d->length); bcopy(&d->data[0], &s->data[0], s->length); break; default: error = EINVAL; } free(d, M_ACD); return error; } static int acd_tray(device_t dev, int close) { struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int error = ENODEV; if (cdp->cap.mechanism & MST_EJECT) { if (close) { if (!(error = acd_start_stop(dev, 3))) { acd_read_toc(dev); acd_prevent_allow(dev, 1); cdp->flags |= F_LOCKED; } } else { acd_start_stop(dev, 0); acd_prevent_allow(dev, 0); cdp->flags &= ~F_LOCKED; atadev->flags |= ATA_D_MEDIA_CHANGED; error = acd_start_stop(dev, 2); } } return error; } static int acd_blank(device_t dev, int blanktype) { struct ata_device *atadev = device_get_softc(dev); int8_t ccb[16] = { ATAPI_BLANK, 0x10 | (blanktype & 0x7), 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; atadev->flags |= ATA_D_MEDIA_CHANGED; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_prevent_allow(device_t dev, int lock) { int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_start_stop(device_t dev, int start) { int8_t ccb[16] = { ATAPI_START_STOP, 0, 0, 0, start, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_pause_resume(device_t dev, int pause) { int8_t ccb[16] = { ATAPI_PAUSE, 0, 0, 0, 0, 0, 0, 0, pause, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int acd_mode_sense(device_t dev, int page, caddr_t pagebuf, int pagesize) { int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, page, 0, 0, 0, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10); return error; } static int acd_mode_select(device_t dev, caddr_t pagebuf, int pagesize) { int8_t ccb[16] = { ATAPI_MODE_SELECT_BIG, 0x10, 0, 0, 0, 0, 0, pagesize>>8, pagesize, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, pagebuf, pagesize, 0, 30); } static int acd_set_speed(device_t dev, int rdspeed, int wrspeed) { int8_t ccb[16] = { ATAPI_SET_SPEED, 0, rdspeed >> 8, rdspeed, wrspeed >> 8, wrspeed, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, NULL, 0, 0, 30); if (!error) acd_get_cap(dev); return error; } static void acd_get_cap(device_t dev) { struct acd_softc *cdp = device_get_ivars(dev); int8_t ccb[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_CDROM_CAP_PAGE, 0, 0, 0, 0, sizeof(cdp->cap)>>8, sizeof(cdp->cap), 0, 0, 0, 0, 0, 0, 0 }; int count; /* get drive capabilities, some bugridden drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!ata_atapicmd(dev, ccb, (caddr_t)&cdp->cap, sizeof(cdp->cap), ATA_R_READ | ATA_R_QUIET, 5)) { cdp->cap.max_read_speed = ntohs(cdp->cap.max_read_speed); cdp->cap.cur_read_speed = ntohs(cdp->cap.cur_read_speed); cdp->cap.max_write_speed = ntohs(cdp->cap.max_write_speed); cdp->cap.cur_write_speed = max(ntohs(cdp->cap.cur_write_speed),177); cdp->cap.max_vol_levels = ntohs(cdp->cap.max_vol_levels); cdp->cap.buf_size = ntohs(cdp->cap.buf_size); } } } static int acd_read_format_caps(device_t dev, struct cdr_format_capacities *caps) { int8_t ccb[16] = { ATAPI_READ_FORMAT_CAPACITIES, 0, 0, 0, 0, 0, 0, (sizeof(struct cdr_format_capacities) >> 8) & 0xff, sizeof(struct cdr_format_capacities) & 0xff, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, (caddr_t)caps, sizeof(struct cdr_format_capacities), ATA_R_READ, 30); } static int acd_format(device_t dev, struct cdr_format_params* params) { int8_t ccb[16] = { ATAPI_FORMAT, 0x11, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int error; error = ata_atapicmd(dev, ccb, (u_int8_t *)params, sizeof(struct cdr_format_params), 0, 30); return error; } static int acd_test_ready(device_t dev) { int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static void acd_describe(device_t dev) { struct ata_channel *ch = device_get_softc(device_get_parent(dev)); struct ata_device *atadev = device_get_softc(dev); struct acd_softc *cdp = device_get_ivars(dev); int comma = 0; char *mechanism; if (bootverbose) { device_printf(dev, "<%.40s/%.8s> %s drive at ata%d as %s\n", atadev->param.model, atadev->param.revision, (cdp->cap.media & MST_WRITE_DVDR) ? "DVDR" : (cdp->cap.media & MST_WRITE_DVDRAM) ? "DVDRAM" : (cdp->cap.media & MST_WRITE_CDRW) ? "CDRW" : (cdp->cap.media & MST_WRITE_CDR) ? "CDR" : (cdp->cap.media & MST_READ_DVDROM) ? "DVDROM":"CDROM", device_get_unit(ch->dev), ata_unit2str(atadev)); 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_DVD: printf("DVD "); 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), ata_unit2str(atadev), ata_mode2str(atadev->mode) ); } } static device_method_t acd_methods[] = { /* device interface */ DEVMETHOD(device_probe, acd_probe), DEVMETHOD(device_attach, acd_attach), DEVMETHOD(device_detach, acd_detach), DEVMETHOD(device_shutdown, acd_shutdown), /* ATA methods */ DEVMETHOD(ata_reinit, acd_reinit), { 0, 0 } }; static driver_t acd_driver = { "acd", acd_methods, 0, }; static devclass_t acd_devclass; static int acd_modevent(module_t mod, int what, void *arg) { g_modevent(0, what, &acd_class); return 0; } DRIVER_MODULE(acd, ata, acd_driver, acd_devclass, acd_modevent, NULL); MODULE_VERSION(acd, 1); MODULE_DEPEND(acd, ata, 1, 1, 1); Index: head/sys/dev/ata/atapi-fd.c =================================================================== --- head/sys/dev/ata/atapi-fd.c (revision 178855) +++ head/sys/dev/ata/atapi-fd.c (revision 178856) @@ -1,440 +1,440 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include #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 disk_ioctl_t afd_ioctl; 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 int afd_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_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"); 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); 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_ioctl = afd_ioctl; fdp->disk->d_name = "afd"; fdp->disk->d_drv1 = dev; - fdp->disk->d_maxsize = ch->dma.max_iosize; + fdp->disk->d_maxsize = ch->dma.max_iosize ? ch->dma.max_iosize : 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 afd_softc *fdp = device_get_ivars(dev); /* check that we have a valid device to detach */ if (!device_get_ivars(dev)) return ENXIO; /* 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(dev); /* dont leave anything behind */ device_set_ivars(dev, NULL); free(fdp, M_AFD); 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(dev, 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); if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) { 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; if (!fdp->mediasize) return ENXIO; fdp->disk->d_sectorsize = fdp->sectorsize; fdp->disk->d_mediasize = fdp->mediasize; fdp->disk->d_fwsectors = fdp->sectors; fdp->disk->d_fwheads = fdp->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->sectorsize; 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->sectorsize; 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_ioctl(struct disk *disk, u_long cmd, void *data, int flag,struct thread *td) { return ata_device_ioctl(disk->d_drv1, cmd, data); } static int afd_sense(device_t dev) { struct ata_device *atadev = device_get_softc(dev); struct afd_softc *fdp = device_get_ivars(dev); struct afd_capacity capacity; struct afd_capacity_big capacity_big; struct afd_capabilities capabilities; int8_t ccb1[16] = { ATAPI_READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; int8_t ccb2[16] = { ATAPI_SERVICE_ACTION_IN, 0x10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, sizeof(struct afd_capacity_big) & 0xff, 0, 0 }; int8_t ccb3[16] = { ATAPI_MODE_SENSE_BIG, 0, ATAPI_REWRITEABLE_CAP_PAGE, 0, 0, 0, 0, sizeof(struct afd_capabilities) >> 8, sizeof(struct afd_capabilities) & 0xff, 0, 0, 0, 0, 0, 0, 0 }; int timeout = 20; int error, count; fdp->mediasize = 0; /* wait for device to get ready */ while ((error = afd_test_ready(dev)) && timeout--) { DELAY(100000); } if (error == EBUSY) return 1; /* The IOMEGA Clik! doesn't support reading the cap page, fake it */ if (!strncmp(atadev->param.model, "IOMEGA Clik!", 12)) { fdp->heads = 1; fdp->sectors = 2; fdp->mediasize = 39441 * 1024; fdp->sectorsize = 512; afd_test_ready(dev); return 0; } /* get drive capacity */ if (!ata_atapicmd(dev, ccb1, (caddr_t)&capacity, sizeof(struct afd_capacity), ATA_R_READ, 30)) { fdp->heads = 16; fdp->sectors = 63; fdp->sectorsize = be32toh(capacity.blocksize); fdp->mediasize = (u_int64_t)be32toh(capacity.capacity)*fdp->sectorsize; afd_test_ready(dev); return 0; } /* get drive capacity big */ if (!ata_atapicmd(dev, ccb2, (caddr_t)&capacity_big, sizeof(struct afd_capacity_big), ATA_R_READ | ATA_R_QUIET, 30)) { fdp->heads = 16; fdp->sectors = 63; fdp->sectorsize = be32toh(capacity_big.blocksize); fdp->mediasize = be64toh(capacity_big.capacity)*fdp->sectorsize; afd_test_ready(dev); return 0; } /* get drive capabilities, some bugridden drives needs this repeated */ for (count = 0 ; count < 5 ; count++) { if (!ata_atapicmd(dev, ccb3, (caddr_t)&capabilities, sizeof(struct afd_capabilities), ATA_R_READ, 30) && capabilities.page_code == ATAPI_REWRITEABLE_CAP_PAGE) { fdp->heads = capabilities.heads; fdp->sectors = capabilities.sectors; fdp->sectorsize = be16toh(capabilities.sector_size); fdp->mediasize = be16toh(capabilities.cylinders) * fdp->heads * fdp->sectors * fdp->sectorsize; if (!capabilities.medium_type) fdp->mediasize = 0; 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(dev, ccb, NULL, 0, 0, 30); } static int afd_test_ready(device_t dev) { int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static void 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); char sizestring[16]; if (fdp->mediasize > 1048576 * 5) sprintf(sizestring, "%juMB", fdp->mediasize / 1048576); else if (fdp->mediasize) sprintf(sizestring, "%juKB", fdp->mediasize / 1024); else strcpy(sizestring, "(no media)"); device_printf(dev, "%s <%.40s %.8s> at ata%d-%s %s\n", sizestring, atadev->param.model, atadev->param.revision, device_get_unit(ch->dev), ata_unit2str(atadev), ata_mode2str(atadev->mode)); if (bootverbose) { device_printf(dev, "%ju sectors [%juC/%dH/%dS]\n", fdp->mediasize / fdp->sectorsize, fdp->mediasize /(fdp->sectorsize*fdp->sectors*fdp->heads), fdp->heads, fdp->sectors); } } static device_method_t afd_methods[] = { /* device interface */ 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; 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 178855) +++ head/sys/dev/ata/atapi-tape.c (revision 178856) @@ -1,741 +1,741 @@ /*- * Copyright (c) 1998 - 2008 Søren Schmidt * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer, * without modification, immediately at the beginning of the file. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include __FBSDID("$FreeBSD$"); #include "opt_ata.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 int ast_probe(device_t dev) { struct ata_device *atadev = device_get_softc(dev); if ((atadev->param.config & ATA_PROTO_ATAPI) && (atadev->param.config & ATA_ATAPI_TYPE_MASK) == ATA_ATAPI_TYPE_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"); 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); 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; - device->si_iosize_max = ch->dma.max_iosize; + device->si_iosize_max = ch->dma.max_iosize ? ch->dma.max_iosize : 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; device->si_iosize_max = ch->dma.max_iosize; stp->dev2 = device; /* announce we are here and ready */ ast_describe(dev); return 0; } static int ast_detach(device_t 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(dev); /* dont leave anything behind */ devstat_remove_entry(stp->stats); device_set_ivars(dev, NULL); free(stp, M_AST); 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(dev, 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); if (((atadev->unit == ATA_MASTER) && !(ch->devices & ATA_ATAPI_MASTER)) || ((atadev->unit == ATA_SLAVE) && !(ch->devices & ATA_ATAPI_SLAVE))) { 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 data, 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 *) data; 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 *)data; 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 *)data = position.tape; } break; case MTIOCRDHPOS: { struct ast_readposition position; if ((error = ast_read_position(dev, 1, &position))) break; *(u_int32_t *)data = position.tape; } break; case MTIOCSLOCATE: error = ast_locate(dev, 0, *(u_int32_t *)data); break; case MTIOCHLOCATE: error = ast_locate(dev, 1, *(u_int32_t *)data); break; default: error = ata_device_ioctl(dev, cmd, data); } 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) { 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(dev, ccb, pagebuf, pagesize, ATA_R_READ, 10); return error; } static int ast_mode_select(device_t dev, void *pagebuf, int pagesize) { 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(dev, ccb, pagebuf, pagesize, 0, 10); } static int ast_write_filemark(device_t dev, u_int8_t function) { 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(dev, 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) { 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(dev, 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) { 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(dev, ccb, NULL, 0, 0, 60*60); } static int ast_locate(device_t dev, int hard, u_int32_t pos) { 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(dev, 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) { int8_t ccb[16] = { ATAPI_PREVENT_ALLOW, 0, 0, 0, lock, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int ast_load_unload(device_t dev, u_int8_t function) { 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(dev, ccb, NULL, 0, 0, 10); if (error) return error; pause("astlu", 1 * hz); if (function == ATAPI_SS_EJECT) return 0; return ast_wait_dsc(dev, 60*60); } static int ast_rewind(device_t 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(dev, ccb, NULL, 0, 0, 10); if (error) return error; return ast_wait_dsc(dev, 60*60); } static int ast_erase(device_t 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(dev, ccb, NULL, 0, 0, 60*60); } static int ast_test_ready(device_t dev) { int8_t ccb[16] = { ATAPI_TEST_UNIT_READY, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; return ata_atapicmd(dev, ccb, NULL, 0, 0, 30); } static int ast_wait_dsc(device_t dev, int timeout) { 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(dev, ccb, NULL, 0, 0, 0); if (error != EBUSY) break; pause("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), ata_unit2str(atadev)); 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), ata_unit2str(atadev), ata_mode2str(atadev->mode)); } } static device_method_t ast_methods[] = { /* device interface */ 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; DRIVER_MODULE(ast, ata, ast_driver, ast_devclass, NULL, NULL); MODULE_VERSION(ast, 1); MODULE_DEPEND(ast, ata, 1, 1, 1);