You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
369 lines
9.5 KiB
369 lines
9.5 KiB
/* $OpenBSD: scsi_ioctl.c,v 1.46 2010/07/22 05:32:10 matthew 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/malloc.h>
|
|
#include <sys/buf.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) {
|
|
/* XXX dma accessible */
|
|
xs->data = malloc(screq->datalen, M_TEMP,
|
|
M_WAITOK | M_CANFAIL | M_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));
|
|
bcopy(&xs->sense, screq->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));
|
|
bcopy(&xs->sense, screq->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)
|
|
free(xs->data, M_TEMP);
|
|
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) {
|
|
/* XXX dma accessible */
|
|
xs->data = malloc(atareq->datalen, M_TEMP,
|
|
M_WAITOK | M_CANFAIL | M_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)
|
|
free(xs->data, M_TEMP);
|
|
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->scsibus;
|
|
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);
|
|
}
|
|
}
|
|
|