xref: /openbsd/sys/scsi/scsi_ioctl.c (revision 404b540a)
1 /*	$OpenBSD: scsi_ioctl.c,v 1.33 2009/10/12 12:04:11 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 
55 int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
56 
57 const 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 
98 int
99 scsi_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),
151 		    sizeof(screq->sense));
152 		bcopy(&xs->sense, screq->sense, screq->senselen_used);
153 		screq->retsts = SCCMD_SENSE;
154 		break;
155 	case XS_SHORTSENSE:
156 		printf("XS_SHORTSENSE\n");
157 		screq->senselen_used = min(sizeof(xs->sense),
158 		    sizeof(screq->sense));
159 		bcopy(&xs->sense, screq->sense, screq->senselen_used);
160 		screq->retsts = SCCMD_UNKNOWN;
161 		break;
162 	case XS_DRIVER_STUFFUP:
163 		screq->retsts = SCCMD_UNKNOWN;
164 		break;
165 	case XS_TIMEOUT:
166 		screq->retsts = SCCMD_TIMEOUT;
167 		break;
168 	case XS_BUSY:
169 		screq->retsts = SCCMD_BUSY;
170 		break;
171 	default:
172 		screq->retsts = SCCMD_UNKNOWN;
173 		break;
174 	}
175 
176 	if (screq->datalen > 0 && screq->flags & SCCMD_READ) {
177 		err = copyout(xs->data, screq->databuf, screq->datalen);
178 		if (err != 0)
179 			goto err;
180 	}
181 
182 err:
183 	if (screq->datalen > 0)
184 		free(xs->data, M_TEMP);
185 	scsi_xs_put(xs);
186 
187 	return (err);
188 }
189 
190 /*
191  * Something (e.g. another driver) has called us
192  * with an sc_link for a target/lun/adapter, and a scsi
193  * specific ioctl to perform, better try.
194  * If user-level type command, we must still be running
195  * in the context of the calling process
196  */
197 int
198 scsi_do_ioctl(struct scsi_link *sc_link, dev_t dev, u_long cmd, caddr_t addr,
199     int flag, struct proc *p)
200 {
201 	SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
202 
203 	switch(cmd) {
204 	case SCIOCIDENTIFY: {
205 		struct scsi_addr *sca = (struct scsi_addr *)addr;
206 
207 		if ((sc_link->flags & (SDEV_ATAPI | SDEV_UMASS)) == 0)
208 			/* A 'real' SCSI target. */
209 			sca->type = TYPE_SCSI;
210 		else
211 			/* An 'emulated' SCSI target. */
212 			sca->type = TYPE_ATAPI;
213 		sca->scbus = sc_link->scsibus;
214 		sca->target = sc_link->target;
215 		sca->lun = sc_link->lun;
216 		return (0);
217 	}
218 	case SCIOCCOMMAND:
219 		if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
220 			break;
221 		/* FALLTHROUGH */
222 	case SCIOCDEBUG:
223 		if ((flag & FWRITE) == 0)
224 			return (EPERM);
225 		break;
226 	default:
227 		if (sc_link->adapter->ioctl)
228 			return ((sc_link->adapter->ioctl)(sc_link, cmd, addr,
229 			    flag, p));
230 		else
231 			return (ENOTTY);
232 	}
233 
234 	switch(cmd) {
235 	case SCIOCCOMMAND:
236 		return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr));
237 	case SCIOCDEBUG: {
238 		int level = *((int *)addr);
239 
240 		SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
241 		sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
242 		if (level & 1)
243 			sc_link->flags |= SDEV_DB1;
244 		if (level & 2)
245 			sc_link->flags |= SDEV_DB2;
246 		if (level & 4)
247 			sc_link->flags |= SDEV_DB3;
248 		if (level & 8)
249 			sc_link->flags |= SDEV_DB4;
250 		return (0);
251 	}
252 	default:
253 #ifdef DIAGNOSTIC
254 		panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
255 #endif
256 		return (0);
257 	}
258 }
259