Example #1
0
static void
ata_cam_process_sense(device_t dev, struct ata_request *request)
{
	struct ata_channel *ch = device_get_softc(dev);
	union ccb *ccb = request->ccb;
	int fatalerr = 0;

	ch->requestsense = 0;

	if (request->flags & ATA_R_TIMEOUT)
		fatalerr = 1;
	if ((request->flags & ATA_R_TIMEOUT) == 0 &&
	    (request->status & ATA_S_ERROR) == 0 &&
	    request->result == 0) {
		ccb->ccb_h.status |= CAM_AUTOSNS_VALID;
	} else {
		ccb->ccb_h.status &= ~CAM_STATUS_MASK;
		ccb->ccb_h.status |= CAM_AUTOSENSE_FAIL;
	}

	ata_free_request(request);
	xpt_done(ccb);
	/* Do error recovery if needed. */
	if (fatalerr)
		ata_reinit(dev);
}
Example #2
0
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);
}
Example #3
0
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);
}
Example #4
0
static void
ad_done(struct ata_request *request)
{
    struct ad_softc *adp = device_get_ivars(request->dev);
    struct bio *bp = request->bio;
    struct buf *bbp = bp->bio_buf;

    /* finish up transfer */
    if ((bbp->b_error = request->result))
	bbp->b_flags |= B_ERROR;
    bbp->b_resid = bbp->b_bcount - request->donecount;
    devstat_end_transaction_buf(&adp->stats, bbp);
    biodone(bp);
    ata_free_request(request);
}
Example #5
0
static void
ata_cam_end_transaction(device_t dev, struct ata_request *request)
{
	struct ata_channel *ch = device_get_softc(dev);
	union ccb *ccb = request->ccb;
	int fatalerr = 0;

	if (ch->requestsense) {
		ata_cam_process_sense(dev, request);
		return;
	}

	ccb->ccb_h.status &= ~CAM_STATUS_MASK;
	if (request->flags & ATA_R_TIMEOUT) {
		xpt_freeze_simq(ch->sim, 1);
		ccb->ccb_h.status &= ~CAM_STATUS_MASK;
		ccb->ccb_h.status |= CAM_CMD_TIMEOUT | CAM_RELEASE_SIMQ;
		fatalerr = 1;
	} else if (request->status & ATA_S_ERROR) {
		if (ccb->ccb_h.func_code == XPT_ATA_IO) {
			ccb->ccb_h.status |= CAM_ATA_STATUS_ERROR;
		} else {
			ccb->ccb_h.status |= CAM_SCSI_STATUS_ERROR;
			ccb->csio.scsi_status = SCSI_STATUS_CHECK_COND;
		}
	} else if (request->result == ERESTART)
		ccb->ccb_h.status |= CAM_REQUEUE_REQ;
	else if (request->result != 0)
		ccb->ccb_h.status |= CAM_REQ_CMP_ERR;
	else
		ccb->ccb_h.status |= CAM_REQ_CMP;
	if ((ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP &&
	    !(ccb->ccb_h.status & CAM_DEV_QFRZN)) {
		xpt_freeze_devq(ccb->ccb_h.path, 1);
		ccb->ccb_h.status |= CAM_DEV_QFRZN;
	}
	if (ccb->ccb_h.func_code == XPT_ATA_IO &&
	    ((request->status & ATA_S_ERROR) ||
	    (ccb->ataio.cmd.flags & CAM_ATAIO_NEEDRESULT))) {
		struct ata_res *res = &ccb->ataio.res;
		res->status = request->status;
		res->error = request->error;
		res->lba_low = request->u.ata.lba;
		res->lba_mid = request->u.ata.lba >> 8;
		res->lba_high = request->u.ata.lba >> 16;
		res->device = request->u.ata.lba >> 24;
		res->lba_low_exp = request->u.ata.lba >> 24;
		res->lba_mid_exp = request->u.ata.lba >> 32;
		res->lba_high_exp = request->u.ata.lba >> 40;
		res->sector_count = request->u.ata.count;
		res->sector_count_exp = request->u.ata.count >> 8;
	}
	if ((ccb->ccb_h.flags & CAM_DIR_MASK) != CAM_DIR_NONE) {
		if (ccb->ccb_h.func_code == XPT_ATA_IO) {
			ccb->ataio.resid =
			    ccb->ataio.dxfer_len - request->donecount;
		} else {
			ccb->csio.resid =
			    ccb->csio.dxfer_len - request->donecount;
		}
	}
	if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_SCSI_STATUS_ERROR &&
	    (ccb->ccb_h.flags & CAM_DIS_AUTOSENSE) == 0)
		ata_cam_request_sense(dev, request);
	else {
		ata_free_request(request);
		xpt_done(ccb);
	}
	/* Do error recovery if needed. */
	if (fatalerr)
		ata_reinit(dev);
}
static int
ata_getparam(struct ata_device *atadev, int init)
{
    struct ata_channel *ch = device_get_softc(device_get_parent(atadev->dev));
    struct ata_request *request;
    u_int8_t command = 0;
    int error = ENOMEM, retries = 2;

    if (ch->devices &
	(atadev->unit == ATA_MASTER ? ATA_ATA_MASTER : ATA_ATA_SLAVE))
	command = ATA_ATA_IDENTIFY;
    if (ch->devices &
	(atadev->unit == ATA_MASTER ? ATA_ATAPI_MASTER : ATA_ATAPI_SLAVE))
	command = ATA_ATAPI_IDENTIFY;
    if (!command)
	return ENXIO;

    while (retries-- > 0 && error) {
	if (!(request = ata_alloc_request()))
	    break;
	request->dev = atadev->dev;
	request->timeout = 1;
	request->retries = 0;
	request->u.ata.command = command;
	request->flags = (ATA_R_READ|ATA_R_AT_HEAD|ATA_R_DIRECT|ATA_R_QUIET);
	request->data = (void *)&atadev->param;
	request->bytecount = sizeof(struct ata_params);
	request->donecount = 0;
	request->transfersize = DEV_BSIZE;
	ata_queue_request(request);
	error = request->result;
	ata_free_request(request);
    }

    if (!error && (isprint(atadev->param.model[0]) ||
		   isprint(atadev->param.model[1]))) {
	struct ata_params *atacap = &atadev->param;
	char buffer[64];
	int16_t *ptr;

	for (ptr = (int16_t *)atacap;
	     ptr < (int16_t *)atacap + sizeof(struct ata_params)/2; ptr++) {
	    *ptr = le16toh(*ptr);
	}
	if (!(!strncmp(atacap->model, "FX", 2) ||
	      !strncmp(atacap->model, "NEC", 3) ||
	      !strncmp(atacap->model, "Pioneer", 7) ||
	      !strncmp(atacap->model, "SHARP", 5))) {
	    bswap(atacap->model, sizeof(atacap->model));
	    bswap(atacap->revision, sizeof(atacap->revision));
	    bswap(atacap->serial, sizeof(atacap->serial));
	}
	btrim(atacap->model, sizeof(atacap->model));
	bpack(atacap->model, atacap->model, sizeof(atacap->model));
	btrim(atacap->revision, sizeof(atacap->revision));
	bpack(atacap->revision, atacap->revision, sizeof(atacap->revision));
	btrim(atacap->serial, sizeof(atacap->serial));
	bpack(atacap->serial, atacap->serial, sizeof(atacap->serial));

	if (bootverbose)
	    printf("ata%d-%s: pio=%s wdma=%s udma=%s cable=%s wire\n",
		   device_get_unit(ch->dev),
		   atadev->unit == ATA_MASTER ? "master" : "slave",
		   ata_mode2str(ata_pmode(atacap)),
		   ata_mode2str(ata_wmode(atacap)),
		   ata_mode2str(ata_umode(atacap)),
		   (atacap->hwres & ATA_CABLE_ID) ? "80":"40");

	if (init) {
	    sprintf(buffer, "%.40s/%.8s", atacap->model, atacap->revision);
	    device_set_desc_copy(atadev->dev, buffer);
	    if ((atadev->param.config & ATA_PROTO_ATAPI) &&
		(atadev->param.config != ATA_CFA_MAGIC1) &&
		(atadev->param.config != ATA_CFA_MAGIC2)) {
		if (atapi_dma && ch->dma &&
		    (atadev->param.config & ATA_DRQ_MASK) != ATA_DRQ_INTR &&
		    ata_umode(&atadev->param) >= ATA_UDMA2)
		    atadev->mode = ATA_DMA_MAX;
	    }
	    else {
		if (ata_dma && ch->dma &&
		    (ata_umode(&atadev->param) > 0 ||
		     ata_wmode(&atadev->param) > 0))
		    atadev->mode = ATA_DMA_MAX;
	    }
	}
    }
    else {
	if (!error)
	    error = ENXIO;
    }
    return error;
}
int
ata_device_ioctl(device_t dev, u_long cmd, caddr_t data)
{
    struct ata_device *atadev = device_get_softc(dev);
    struct ata_channel *ch = device_get_softc(device_get_parent(dev));
    struct ata_ioc_request *ioc_request = (struct ata_ioc_request *)data;
    struct ata_params *params = (struct ata_params *)data;
    int *mode = (int *)data;
    struct ata_request *request;
    caddr_t buf;
    int error;

    switch (cmd) {
    case IOCATAREQUEST:
	if (ioc_request->count >
	    (ch->dma->max_iosize ? ch->dma->max_iosize : DFLTPHYS)) {
		return (EFBIG);
	}
	if (!(buf = malloc(ioc_request->count, M_ATA, M_NOWAIT))) {
	    return ENOMEM;
	}
	if (!(request = ata_alloc_request())) {
	    free(buf, M_ATA);
	    return  ENOMEM;
	}
	if (ioc_request->flags & ATA_CMD_WRITE) {
	    error = copyin(ioc_request->data, buf, ioc_request->count);
	    if (error) {
		free(buf, M_ATA);
		ata_free_request(request);
		return error;
	    }
	}
	request->dev = dev;
	if (ioc_request->flags & ATA_CMD_ATAPI) {
	    request->flags = ATA_R_ATAPI;
	    bcopy(ioc_request->u.atapi.ccb, request->u.atapi.ccb, 16);
	}
	else {
	    request->u.ata.command = ioc_request->u.ata.command;
	    request->u.ata.feature = ioc_request->u.ata.feature;
	    request->u.ata.lba = ioc_request->u.ata.lba;
	    request->u.ata.count = ioc_request->u.ata.count;
	}
	request->timeout = ioc_request->timeout;
	request->data = buf;
	request->bytecount = ioc_request->count;
	request->transfersize = request->bytecount;
	if (ioc_request->flags & ATA_CMD_CONTROL)
	    request->flags |= ATA_R_CONTROL;
	if (ioc_request->flags & ATA_CMD_READ)
	    request->flags |= ATA_R_READ;
	if (ioc_request->flags & ATA_CMD_WRITE)
	    request->flags |= ATA_R_WRITE;
	ata_queue_request(request);
	if (request->flags & ATA_R_ATAPI) {
	    bcopy(&request->u.atapi.sense, &ioc_request->u.atapi.sense,
		  sizeof(struct atapi_sense));
	}
	else {
	    ioc_request->u.ata.command = request->u.ata.command;
	    ioc_request->u.ata.feature = request->u.ata.feature;
	    ioc_request->u.ata.lba = request->u.ata.lba;
	    ioc_request->u.ata.count = request->u.ata.count;
	}
	ioc_request->error = request->result;
	if (ioc_request->flags & ATA_CMD_READ)
	    error = copyout(buf, ioc_request->data, ioc_request->count);
	else
	    error = 0;
	free(buf, M_ATA);
	ata_free_request(request);
	return error;
   
    case IOCATAGPARM:
	ata_getparam(atadev, 0);
	bcopy(&atadev->param, params, sizeof(struct ata_params));
	return 0;
	
    case IOCATASMODE:
	atadev->mode = *mode;
	ATA_SETMODE(device_get_parent(dev), dev);
	return 0;

    case IOCATAGMODE:
	*mode = atadev->mode;
	return 0;
    case IOCATASSPINDOWN:
	atadev->spindown = *mode;
	return 0;
    case IOCATAGSPINDOWN:
	*mode = atadev->spindown;
	return 0;
    default:
	return ENOTTY;
    }
}
Example #8
0
static int
ad_strategy(struct dev_strategy_args *ap)
{
    device_t dev =  ap->a_head.a_dev->si_drv1;
    struct bio *bp = ap->a_bio;
    struct buf *bbp = bp->bio_buf;
    struct ata_device *atadev = device_get_softc(dev);
    struct ata_request *request;
    struct ad_softc *adp = device_get_ivars(dev);

    if (!(request = ata_alloc_request())) {
	device_printf(dev, "FAILURE - out of memory in strategy\n");
	bbp->b_flags |= B_ERROR;
	bbp->b_error = ENOMEM;
	biodone(bp);
	return(0);
    }

    /* setup request */
    request->dev = dev;
    request->bio = bp;
    request->callback = ad_done;
    request->timeout = ATA_DEFAULT_TIMEOUT;
    request->retries = 2;
    request->data = bbp->b_data;
    request->bytecount = bbp->b_bcount;
    /* lba is block granularity, convert byte granularity bio_offset */
    request->u.ata.lba = (u_int64_t)(bp->bio_offset >> DEV_BSHIFT);
    request->u.ata.count = request->bytecount / DEV_BSIZE;
    request->transfersize = min(bbp->b_bcount, atadev->max_iosize);

    switch (bbp->b_cmd) {
    case BUF_CMD_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 BUF_CMD_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 BUF_CMD_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;
	/* ATA FLUSHCACHE requests may take up to 30 sec to timeout */
	request->timeout = 30;
	break;
    default:
	device_printf(dev, "FAILURE - unknown BUF operation\n");
	ata_free_request(request);
	bbp->b_flags |= B_ERROR;
	bbp->b_error = EIO;
	biodone(bp);
	return(0);
    }
    request->flags |= ATA_R_ORDERED;
    devstat_start_transaction(&adp->stats);
    ata_queue_request(request);
    return(0);
}
Example #9
0
static void 
ad_strategy(struct bio *bp)
{
    device_t dev =  bp->bio_disk->d_drv1;
    struct ata_device *atadev = device_get_softc(dev);
    struct ata_request *request;

    if (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 = MAX(ATA_REQUEST_TIMEOUT, 31);
    }
    else {
	request->timeout = ATA_REQUEST_TIMEOUT;
    }
    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);
}
Example #10
0
static void
ad_power_callback(struct ata_request *request)
{
    device_printf(request->dev, "drive spun down.\n");
    ata_free_request(request);
}
Example #11
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, 16);
    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);
}
Example #12
0
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, 16);
    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);
}
Example #13
0
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 = ATA_REQUEST_TIMEOUT;
	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;
}