scsi_ioctl.c revision 1.32
1/*	$OpenBSD: scsi_ioctl.c,v 1.32 2009/09/14 00:03:28 dlg Exp $	*/
2/*	$NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $	*/
3
4/*
5 * Copyright (c) 1994 Charles Hannum.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by Charles Hannum.
18 * 4. The name of the author may not be used to endorse or promote products
19 *    derived from this software without specific prior written permission.
20 *
21 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33/*
34 * Contributed by HD Associates (hd@world.std.com).
35 * Copyright (c) 1992, 1993 HD Associates
36 *
37 * Berkeley style copyright.
38 */
39
40#include <sys/types.h>
41#include <sys/errno.h>
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/file.h>
45#include <sys/malloc.h>
46#include <sys/buf.h>
47#include <sys/proc.h>
48#include <sys/device.h>
49#include <sys/fcntl.h>
50
51#include <scsi/scsi_all.h>
52#include <scsi/scsiconf.h>
53#include <sys/scsiio.h>
54
55int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
56
57const unsigned char scsi_readsafe_cmd[256] = {
58	[0x00] = 1,	/* TEST UNIT READY */
59	[0x03] = 1,	/* REQUEST SENSE */
60	[0x08] = 1,	/* READ(6) */
61	[0x12] = 1,	/* INQUIRY */
62	[0x1a] = 1,	/* MODE SENSE */
63	[0x1b] = 1,	/* START STOP */
64	[0x23] = 1,	/* READ FORMAT CAPACITIES */
65	[0x25] = 1,	/* READ CDVD CAPACITY */
66	[0x28] = 1,	/* READ(10) */
67	[0x2b] = 1,	/* SEEK */
68	[0x2f] = 1,	/* VERIFY(10) */
69	[0x3c] = 1,	/* READ BUFFER */
70	[0x3e] = 1,	/* READ LONG */
71	[0x42] = 1,	/* READ SUBCHANNEL */
72	[0x43] = 1,	/* READ TOC PMA ATIP */
73	[0x44] = 1,	/* READ HEADER */
74	[0x45] = 1,	/* PLAY AUDIO(10) */
75	[0x46] = 1,	/* GET CONFIGURATION */
76	[0x47] = 1,	/* PLAY AUDIO MSF */
77	[0x48] = 1,	/* PLAY AUDIO TI */
78	[0x4a] = 1,	/* GET EVENT STATUS NOTIFICATION */
79	[0x4b] = 1,	/* PAUSE RESUME */
80	[0x4e] = 1,	/* STOP PLAY SCAN */
81	[0x51] = 1,	/* READ DISC INFO */
82	[0x52] = 1,	/* READ TRACK RZONE INFO */
83	[0x5a] = 1,	/* MODE SENSE(10) */
84	[0x88] = 1,	/* READ(16) */
85	[0x8f] = 1,	/* VERIFY(16) */
86	[0xa4] = 1,	/* REPORT KEY */
87	[0xa5] = 1,	/* PLAY AUDIO(12) */
88	[0xa8] = 1,	/* READ(12) */
89	[0xac] = 1,	/* GET PERFORMANCE */
90	[0xad] = 1,	/* READ DVD STRUCTURE */
91	[0xb9] = 1,	/* READ CD MSF */
92	[0xba] = 1,	/* SCAN */
93	[0xbc] = 1,	/* PLAY CD */
94	[0xbd] = 1,	/* MECHANISM STATUS */
95	[0xbe] = 1	/* READ CD */
96};
97
98int
99scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
100{
101	struct scsi_xfer *xs;
102	int err = 0;
103	int s;
104
105	if (screq->cmdlen > sizeof(struct scsi_generic))
106		return (EFAULT);
107
108	xs = scsi_xs_get(link, 0);
109
110	memcpy(xs->cmd, screq->cmd, screq->cmdlen);
111	xs->cmdlen = screq->cmdlen;
112
113	if (screq->datalen > 0) {
114		xs->data = malloc(screq->datalen, M_TEMP, M_WAITOK);
115		xs->datalen = screq->datalen;
116	}
117
118	if (screq->flags & SCCMD_READ)
119		xs->flags |= SCSI_DATA_IN;
120	if (screq->flags & SCCMD_WRITE) {
121		if (screq->datalen > 0) {
122			err = copyin(screq->databuf, xs->data, screq->datalen);
123			if (err != 0)
124				goto err;
125		}
126
127		xs->flags |= SCSI_DATA_OUT;
128	}
129
130	xs->timeout = screq->timeout;
131	xs->retries = 0; /* user must do the retries *//* ignored */
132
133	xs->done = (void (*)(struct scsi_xfer *))wakeup;
134
135	scsi_xs_exec(xs);
136	s = splbio();
137	while (!ISSET(xs->flags, ITSDONE))
138		tsleep(xs, PRIBIO, "scsiioc", 0);
139	splx(s);
140
141	screq->retsts = 0;
142	screq->status = xs->status;
143	switch (xs->error) {
144	case XS_NOERROR:
145		/* probably rubbish */
146		screq->datalen_used = xs->datalen - xs->resid;
147		screq->retsts = SCCMD_OK;
148		break;
149	case XS_SENSE:
150		screq->senselen_used = min(sizeof(xs->sense), SENSEBUFLEN);
151		bcopy(&xs->sense, screq->sense, screq->senselen);
152		screq->retsts = SCCMD_SENSE;
153		break;
154	case XS_SHORTSENSE:
155		screq->senselen_used = min(sizeof(xs->sense), SENSEBUFLEN);
156		bcopy(&xs->sense, screq->sense, screq->senselen);
157		screq->retsts = SCCMD_UNKNOWN;
158		break;
159	case XS_DRIVER_STUFFUP:
160		screq->retsts = SCCMD_UNKNOWN;
161		break;
162	case XS_TIMEOUT:
163		screq->retsts = SCCMD_TIMEOUT;
164		break;
165	case XS_BUSY:
166		screq->retsts = SCCMD_BUSY;
167		break;
168	default:
169		screq->retsts = SCCMD_UNKNOWN;
170		break;
171	}
172
173	if (screq->datalen > 0 && screq->flags & SCCMD_READ) {
174		err = copyout(xs->data, screq->databuf, screq->datalen);
175		if (err != 0)
176			goto err;
177	}
178
179err:
180	if (screq->datalen > 0)
181		free(xs->data, M_TEMP);
182	scsi_xs_put(xs);
183
184	return (err);
185}
186
187/*
188 * Something (e.g. another driver) has called us
189 * with an sc_link for a target/lun/adapter, and a scsi
190 * specific ioctl to perform, better try.
191 * If user-level type command, we must still be running
192 * in the context of the calling process
193 */
194int
195scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
196    int flag, struct proc *p)
197{
198	SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
199
200	switch(cmd) {
201	case SCIOCIDENTIFY: {
202		struct scsi_addr *sca = (struct scsi_addr *)addr;
203
204		if ((sc_link->flags & (SDEV_ATAPI | SDEV_UMASS)) == 0)
205			/* A 'real' SCSI target. */
206			sca->type = TYPE_SCSI;
207		else
208			/* An 'emulated' SCSI target. */
209			sca->type = TYPE_ATAPI;
210		sca->scbus = sc_link->scsibus;
211		sca->target = sc_link->target;
212		sca->lun = sc_link->lun;
213		return (0);
214	}
215	case SCIOCCOMMAND:
216		if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
217			break;
218		/* FALLTHROUGH */
219	case SCIOCDEBUG:
220		if ((flag & FWRITE) == 0)
221			return (EPERM);
222		break;
223	default:
224		if (sc_link->adapter->ioctl)
225			return ((sc_link->adapter->ioctl)(sc_link, cmd, addr,
226			    flag, p));
227		else
228			return (ENOTTY);
229	}
230
231	switch(cmd) {
232	case SCIOCCOMMAND:
233		return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr));
234	case SCIOCDEBUG: {
235		int level = *((int *)addr);
236
237		SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
238		sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
239		if (level & 1)
240			sc_link->flags |= SDEV_DB1;
241		if (level & 2)
242			sc_link->flags |= SDEV_DB2;
243		if (level & 4)
244			sc_link->flags |= SDEV_DB3;
245		if (level & 8)
246			sc_link->flags |= SDEV_DB4;
247		return (0);
248	}
249	default:
250#ifdef DIAGNOSTIC
251		panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
252#endif
253		return (0);
254	}
255}
256