123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365 |
- /* $OpenBSD: scsi_ioctl.c,v 1.51 2015/06/07 19:13:27 krw Exp $ */
- /* $NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $ */
- /*
- * Copyright (c) 1994 Charles Hannum. 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.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- * must display the following acknowledgement:
- * This product includes software developed by Charles Hannum.
- * 4. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
- * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
- * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
- * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
- * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- */
- /*
- * Contributed by HD Associates (hd@world.std.com).
- * Copyright (c) 1992, 1993 HD Associates
- *
- * Berkeley style copyright.
- */
- #include <sys/types.h>
- #include <sys/errno.h>
- #include <sys/param.h>
- #include <sys/systm.h>
- #include <sys/file.h>
- #include <sys/pool.h>
- #include <sys/device.h>
- #include <sys/fcntl.h>
- #include <scsi/scsi_all.h>
- #include <scsi/scsiconf.h>
- #include <sys/scsiio.h>
- #include <sys/ataio.h>
- int scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
- int scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *);
- const unsigned char scsi_readsafe_cmd[256] = {
- [0x00] = 1, /* TEST UNIT READY */
- [0x03] = 1, /* REQUEST SENSE */
- [0x08] = 1, /* READ(6) */
- [0x12] = 1, /* INQUIRY */
- [0x1a] = 1, /* MODE SENSE */
- [0x1b] = 1, /* START STOP */
- [0x23] = 1, /* READ FORMAT CAPACITIES */
- [0x25] = 1, /* READ CDVD CAPACITY */
- [0x28] = 1, /* READ(10) */
- [0x2b] = 1, /* SEEK */
- [0x2f] = 1, /* VERIFY(10) */
- [0x3c] = 1, /* READ BUFFER */
- [0x3e] = 1, /* READ LONG */
- [0x42] = 1, /* READ SUBCHANNEL */
- [0x43] = 1, /* READ TOC PMA ATIP */
- [0x44] = 1, /* READ HEADER */
- [0x45] = 1, /* PLAY AUDIO(10) */
- [0x46] = 1, /* GET CONFIGURATION */
- [0x47] = 1, /* PLAY AUDIO MSF */
- [0x48] = 1, /* PLAY AUDIO TI */
- [0x4a] = 1, /* GET EVENT STATUS NOTIFICATION */
- [0x4b] = 1, /* PAUSE RESUME */
- [0x4e] = 1, /* STOP PLAY SCAN */
- [0x51] = 1, /* READ DISC INFO */
- [0x52] = 1, /* READ TRACK RZONE INFO */
- [0x5a] = 1, /* MODE SENSE(10) */
- [0x88] = 1, /* READ(16) */
- [0x8f] = 1, /* VERIFY(16) */
- [0xa4] = 1, /* REPORT KEY */
- [0xa5] = 1, /* PLAY AUDIO(12) */
- [0xa8] = 1, /* READ(12) */
- [0xac] = 1, /* GET PERFORMANCE */
- [0xad] = 1, /* READ DVD STRUCTURE */
- [0xb9] = 1, /* READ CD MSF */
- [0xba] = 1, /* SCAN */
- [0xbc] = 1, /* PLAY CD */
- [0xbd] = 1, /* MECHANISM STATUS */
- [0xbe] = 1 /* READ CD */
- };
- int
- scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
- {
- struct scsi_xfer *xs;
- int err = 0;
- if (screq->cmdlen > sizeof(struct scsi_generic))
- return (EFAULT);
- if (screq->datalen > MAXPHYS)
- return (EINVAL);
- xs = scsi_xs_get(link, 0);
- if (xs == NULL)
- return (ENOMEM);
- memcpy(xs->cmd, screq->cmd, screq->cmdlen);
- xs->cmdlen = screq->cmdlen;
- if (screq->datalen > 0) {
- xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO);
- if (xs->data == NULL) {
- err = ENOMEM;
- goto err;
- }
- xs->datalen = screq->datalen;
- }
- if (screq->flags & SCCMD_READ)
- xs->flags |= SCSI_DATA_IN;
- if (screq->flags & SCCMD_WRITE) {
- if (screq->datalen > 0) {
- err = copyin(screq->databuf, xs->data, screq->datalen);
- if (err != 0)
- goto err;
- }
- xs->flags |= SCSI_DATA_OUT;
- }
- xs->flags |= SCSI_SILENT; /* User is responsible for errors. */
- xs->timeout = screq->timeout;
- xs->retries = 0; /* user must do the retries *//* ignored */
- scsi_xs_sync(xs);
- screq->retsts = 0;
- screq->status = xs->status;
- switch (xs->error) {
- case XS_NOERROR:
- /* probably rubbish */
- screq->datalen_used = xs->datalen - xs->resid;
- screq->retsts = SCCMD_OK;
- break;
- case XS_SENSE:
- #ifdef SCSIDEBUG
- scsi_sense_print_debug(xs);
- #endif
- screq->senselen_used = min(sizeof(xs->sense),
- sizeof(screq->sense));
- memcpy(screq->sense, &xs->sense, screq->senselen_used);
- screq->retsts = SCCMD_SENSE;
- break;
- case XS_SHORTSENSE:
- #ifdef SCSIDEBUG
- scsi_sense_print_debug(xs);
- #endif
- printf("XS_SHORTSENSE\n");
- screq->senselen_used = min(sizeof(xs->sense),
- sizeof(screq->sense));
- memcpy(screq->sense, &xs->sense, screq->senselen_used);
- screq->retsts = SCCMD_UNKNOWN;
- break;
- case XS_DRIVER_STUFFUP:
- screq->retsts = SCCMD_UNKNOWN;
- break;
- case XS_TIMEOUT:
- screq->retsts = SCCMD_TIMEOUT;
- break;
- case XS_BUSY:
- screq->retsts = SCCMD_BUSY;
- break;
- default:
- screq->retsts = SCCMD_UNKNOWN;
- break;
- }
- if (screq->datalen > 0 && screq->flags & SCCMD_READ) {
- err = copyout(xs->data, screq->databuf, screq->datalen);
- if (err != 0)
- goto err;
- }
- err:
- if (xs->data)
- dma_free(xs->data, screq->datalen);
- scsi_xs_put(xs);
- return (err);
- }
- int
- scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq)
- {
- struct scsi_xfer *xs;
- struct scsi_ata_passthru_12 *cdb;
- int err = 0;
- if (atareq->datalen > MAXPHYS)
- return (EINVAL);
- xs = scsi_xs_get(link, 0);
- if (xs == NULL)
- return (ENOMEM);
- cdb = (struct scsi_ata_passthru_12 *)xs->cmd;
- cdb->opcode = ATA_PASSTHRU_12;
- if (atareq->datalen > 0) {
- if (atareq->flags & ATACMD_READ) {
- cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN;
- cdb->flags = ATA_PASSTHRU_T_DIR_READ;
- } else {
- cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT;
- cdb->flags = ATA_PASSTHRU_T_DIR_WRITE;
- }
- cdb->flags |= ATA_PASSTHRU_T_LEN_SECTOR_COUNT;
- } else {
- cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA;
- cdb->flags = ATA_PASSTHRU_T_LEN_NONE;
- }
- cdb->features = atareq->features;
- cdb->sector_count = atareq->sec_count;
- cdb->lba_low = atareq->sec_num;
- cdb->lba_mid = atareq->cylinder;
- cdb->lba_high = atareq->cylinder >> 8;
- cdb->device = atareq->head & 0x0f;
- cdb->command = atareq->command;
- xs->cmdlen = sizeof(*cdb);
- if (atareq->datalen > 0) {
- xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO);
- if (xs->data == NULL) {
- err = ENOMEM;
- goto err;
- }
- xs->datalen = atareq->datalen;
- }
- if (atareq->flags & ATACMD_READ)
- xs->flags |= SCSI_DATA_IN;
- if (atareq->flags & ATACMD_WRITE) {
- if (atareq->datalen > 0) {
- err = copyin(atareq->databuf, xs->data,
- atareq->datalen);
- if (err != 0)
- goto err;
- }
- xs->flags |= SCSI_DATA_OUT;
- }
- xs->flags |= SCSI_SILENT; /* User is responsible for errors. */
- xs->retries = 0; /* user must do the retries *//* ignored */
- scsi_xs_sync(xs);
- atareq->retsts = ATACMD_ERROR;
- switch (xs->error) {
- case XS_SENSE:
- case XS_SHORTSENSE:
- #ifdef SCSIDEBUG
- scsi_sense_print_debug(xs);
- #endif
- /* XXX this is not right */
- case XS_NOERROR:
- atareq->retsts = ATACMD_OK;
- break;
- default:
- atareq->retsts = ATACMD_ERROR;
- break;
- }
- if (atareq->datalen > 0 && atareq->flags & ATACMD_READ) {
- err = copyout(xs->data, atareq->databuf, atareq->datalen);
- if (err != 0)
- goto err;
- }
- err:
- if (xs->data)
- dma_free(xs->data, atareq->datalen);
- scsi_xs_put(xs);
- return (err);
- }
- /*
- * Something (e.g. another driver) has called us
- * with an sc_link for a target/lun/adapter, and a scsi
- * specific ioctl to perform, better try.
- */
- int
- scsi_do_ioctl(struct scsi_link *sc_link, u_long cmd, caddr_t addr, int flag)
- {
- SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
- switch(cmd) {
- case SCIOCIDENTIFY: {
- struct scsi_addr *sca = (struct scsi_addr *)addr;
- if ((sc_link->flags & (SDEV_ATAPI | SDEV_UMASS)) == 0)
- /* A 'real' SCSI target. */
- sca->type = TYPE_SCSI;
- else
- /* An 'emulated' SCSI target. */
- sca->type = TYPE_ATAPI;
- sca->scbus = sc_link->bus->sc_dev.dv_unit;
- sca->target = sc_link->target;
- sca->lun = sc_link->lun;
- return (0);
- }
- case SCIOCCOMMAND:
- if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
- break;
- /* FALLTHROUGH */
- case ATAIOCCOMMAND:
- case SCIOCDEBUG:
- if ((flag & FWRITE) == 0)
- return (EPERM);
- break;
- default:
- if (sc_link->adapter->ioctl)
- return ((sc_link->adapter->ioctl)(sc_link, cmd, addr,
- flag));
- else
- return (ENOTTY);
- }
- switch(cmd) {
- case SCIOCCOMMAND:
- return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr));
- case ATAIOCCOMMAND:
- return (scsi_ioc_ata_cmd(sc_link, (atareq_t *)addr));
- case SCIOCDEBUG: {
- int level = *((int *)addr);
- SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
- sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
- if (level & 1)
- sc_link->flags |= SDEV_DB1;
- if (level & 2)
- sc_link->flags |= SDEV_DB2;
- if (level & 4)
- sc_link->flags |= SDEV_DB3;
- if (level & 8)
- sc_link->flags |= SDEV_DB4;
- return (0);
- }
- default:
- #ifdef DIAGNOSTIC
- panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
- #endif
- return (0);
- }
- }
|