xref: /openbsd-src/sys/scsi/scsi_ioctl.c (revision 91f110e064cd7c194e59e019b83bb7496c1c84d4)
1 /*	$OpenBSD: scsi_ioctl.c,v 1.48 2011/06/21 22:36:42 matthew 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/pool.h>
46 #include <sys/buf.h>
47 #include <sys/device.h>
48 #include <sys/fcntl.h>
49 
50 #include <scsi/scsi_all.h>
51 #include <scsi/scsiconf.h>
52 
53 #include <sys/scsiio.h>
54 #include <sys/ataio.h>
55 
56 int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
57 int			scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *);
58 
59 const unsigned char scsi_readsafe_cmd[256] = {
60 	[0x00] = 1,	/* TEST UNIT READY */
61 	[0x03] = 1,	/* REQUEST SENSE */
62 	[0x08] = 1,	/* READ(6) */
63 	[0x12] = 1,	/* INQUIRY */
64 	[0x1a] = 1,	/* MODE SENSE */
65 	[0x1b] = 1,	/* START STOP */
66 	[0x23] = 1,	/* READ FORMAT CAPACITIES */
67 	[0x25] = 1,	/* READ CDVD CAPACITY */
68 	[0x28] = 1,	/* READ(10) */
69 	[0x2b] = 1,	/* SEEK */
70 	[0x2f] = 1,	/* VERIFY(10) */
71 	[0x3c] = 1,	/* READ BUFFER */
72 	[0x3e] = 1,	/* READ LONG */
73 	[0x42] = 1,	/* READ SUBCHANNEL */
74 	[0x43] = 1,	/* READ TOC PMA ATIP */
75 	[0x44] = 1,	/* READ HEADER */
76 	[0x45] = 1,	/* PLAY AUDIO(10) */
77 	[0x46] = 1,	/* GET CONFIGURATION */
78 	[0x47] = 1,	/* PLAY AUDIO MSF */
79 	[0x48] = 1,	/* PLAY AUDIO TI */
80 	[0x4a] = 1,	/* GET EVENT STATUS NOTIFICATION */
81 	[0x4b] = 1,	/* PAUSE RESUME */
82 	[0x4e] = 1,	/* STOP PLAY SCAN */
83 	[0x51] = 1,	/* READ DISC INFO */
84 	[0x52] = 1,	/* READ TRACK RZONE INFO */
85 	[0x5a] = 1,	/* MODE SENSE(10) */
86 	[0x88] = 1,	/* READ(16) */
87 	[0x8f] = 1,	/* VERIFY(16) */
88 	[0xa4] = 1,	/* REPORT KEY */
89 	[0xa5] = 1,	/* PLAY AUDIO(12) */
90 	[0xa8] = 1,	/* READ(12) */
91 	[0xac] = 1,	/* GET PERFORMANCE */
92 	[0xad] = 1,	/* READ DVD STRUCTURE */
93 	[0xb9] = 1,	/* READ CD MSF */
94 	[0xba] = 1,	/* SCAN */
95 	[0xbc] = 1,	/* PLAY CD */
96 	[0xbd] = 1,	/* MECHANISM STATUS */
97 	[0xbe] = 1	/* READ CD */
98 };
99 
100 int
101 scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
102 {
103 	struct scsi_xfer *xs;
104 	int err = 0;
105 
106 	if (screq->cmdlen > sizeof(struct scsi_generic))
107 		return (EFAULT);
108 	if (screq->datalen > MAXPHYS)
109 		return (EINVAL);
110 
111 	xs = scsi_xs_get(link, 0);
112 	if (xs == NULL)
113 		return (ENOMEM);
114 
115 	memcpy(xs->cmd, screq->cmd, screq->cmdlen);
116 	xs->cmdlen = screq->cmdlen;
117 
118 	if (screq->datalen > 0) {
119 		xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO);
120 		if (xs->data == NULL) {
121 			err = ENOMEM;
122 			goto err;
123 		}
124 		xs->datalen = screq->datalen;
125 	}
126 
127 	if (screq->flags & SCCMD_READ)
128 		xs->flags |= SCSI_DATA_IN;
129 	if (screq->flags & SCCMD_WRITE) {
130 		if (screq->datalen > 0) {
131 			err = copyin(screq->databuf, xs->data, screq->datalen);
132 			if (err != 0)
133 				goto err;
134 		}
135 
136 		xs->flags |= SCSI_DATA_OUT;
137 	}
138 
139 	xs->flags |= SCSI_SILENT;	/* User is responsible for errors. */
140 	xs->timeout = screq->timeout;
141 	xs->retries = 0; /* user must do the retries *//* ignored */
142 
143 	scsi_xs_sync(xs);
144 
145 	screq->retsts = 0;
146 	screq->status = xs->status;
147 	switch (xs->error) {
148 	case XS_NOERROR:
149 		/* probably rubbish */
150 		screq->datalen_used = xs->datalen - xs->resid;
151 		screq->retsts = SCCMD_OK;
152 		break;
153 	case XS_SENSE:
154 #ifdef SCSIDEBUG
155 		scsi_sense_print_debug(xs);
156 #endif
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_SENSE;
161 		break;
162 	case XS_SHORTSENSE:
163 #ifdef SCSIDEBUG
164 		scsi_sense_print_debug(xs);
165 #endif
166 		printf("XS_SHORTSENSE\n");
167 		screq->senselen_used = min(sizeof(xs->sense),
168 		    sizeof(screq->sense));
169 		bcopy(&xs->sense, screq->sense, screq->senselen_used);
170 		screq->retsts = SCCMD_UNKNOWN;
171 		break;
172 	case XS_DRIVER_STUFFUP:
173 		screq->retsts = SCCMD_UNKNOWN;
174 		break;
175 	case XS_TIMEOUT:
176 		screq->retsts = SCCMD_TIMEOUT;
177 		break;
178 	case XS_BUSY:
179 		screq->retsts = SCCMD_BUSY;
180 		break;
181 	default:
182 		screq->retsts = SCCMD_UNKNOWN;
183 		break;
184 	}
185 
186 	if (screq->datalen > 0 && screq->flags & SCCMD_READ) {
187 		err = copyout(xs->data, screq->databuf, screq->datalen);
188 		if (err != 0)
189 			goto err;
190 	}
191 
192 err:
193 	if (xs->data)
194 		dma_free(xs->data, screq->datalen);
195 	scsi_xs_put(xs);
196 
197 	return (err);
198 }
199 
200 int
201 scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq)
202 {
203 	struct scsi_xfer *xs;
204 	struct scsi_ata_passthru_12 *cdb;
205 	int err = 0;
206 
207 	if (atareq->datalen > MAXPHYS)
208 		return (EINVAL);
209 
210 	xs = scsi_xs_get(link, 0);
211 	if (xs == NULL)
212 		return (ENOMEM);
213 
214 	cdb = (struct scsi_ata_passthru_12 *)xs->cmd;
215 	cdb->opcode = ATA_PASSTHRU_12;
216 
217 	if (atareq->datalen > 0) {
218 		if (atareq->flags & ATACMD_READ) {
219 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN;
220 			cdb->flags = ATA_PASSTHRU_T_DIR_READ;
221 		} else {
222 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT;
223 			cdb->flags = ATA_PASSTHRU_T_DIR_WRITE;
224 		}
225 		cdb->flags |= ATA_PASSTHRU_T_LEN_SECTOR_COUNT;
226 	} else {
227 		cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA;
228 		cdb->flags = ATA_PASSTHRU_T_LEN_NONE;
229 	}
230 	cdb->features = atareq->features;
231 	cdb->sector_count = atareq->sec_count;
232 	cdb->lba_low = atareq->sec_num;
233 	cdb->lba_mid = atareq->cylinder;
234 	cdb->lba_high = atareq->cylinder >> 8;
235 	cdb->device = atareq->head & 0x0f;
236 	cdb->command = atareq->command;
237 
238 	xs->cmdlen = sizeof(*cdb);
239 
240 	if (atareq->datalen > 0) {
241 		xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO);
242 		if (xs->data == NULL) {
243 			err = ENOMEM;
244 			goto err;
245 		}
246 		xs->datalen = atareq->datalen;
247 	}
248 
249 	if (atareq->flags & ATACMD_READ)
250 		xs->flags |= SCSI_DATA_IN;
251 	if (atareq->flags & ATACMD_WRITE) {
252 		if (atareq->datalen > 0) {
253 			err = copyin(atareq->databuf, xs->data,
254 			    atareq->datalen);
255 			if (err != 0)
256 				goto err;
257 		}
258 
259 		xs->flags |= SCSI_DATA_OUT;
260 	}
261 
262 	xs->flags |= SCSI_SILENT;	/* User is responsible for errors. */
263 	xs->retries = 0; /* user must do the retries *//* ignored */
264 
265 	scsi_xs_sync(xs);
266 
267 	atareq->retsts = ATACMD_ERROR;
268 	switch (xs->error) {
269 	case XS_SENSE:
270 	case XS_SHORTSENSE:
271 #ifdef SCSIDEBUG
272 		scsi_sense_print_debug(xs);
273 #endif
274 		/* XXX this is not right */
275 	case XS_NOERROR:
276 		atareq->retsts = ATACMD_OK;
277 		break;
278 	default:
279 		atareq->retsts = ATACMD_ERROR;
280 		break;
281 	}
282 
283 	if (atareq->datalen > 0 && atareq->flags & ATACMD_READ) {
284 		err = copyout(xs->data, atareq->databuf, atareq->datalen);
285 		if (err != 0)
286 			goto err;
287 	}
288 
289 err:
290 	if (xs->data)
291 		dma_free(xs->data, atareq->datalen);
292 	scsi_xs_put(xs);
293 
294 	return (err);
295 }
296 
297 /*
298  * Something (e.g. another driver) has called us
299  * with an sc_link for a target/lun/adapter, and a scsi
300  * specific ioctl to perform, better try.
301  */
302 int
303 scsi_do_ioctl(struct scsi_link *sc_link, u_long cmd, caddr_t addr, int flag)
304 {
305 	SC_DEBUG(sc_link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
306 
307 	switch(cmd) {
308 	case SCIOCIDENTIFY: {
309 		struct scsi_addr *sca = (struct scsi_addr *)addr;
310 
311 		if ((sc_link->flags & (SDEV_ATAPI | SDEV_UMASS)) == 0)
312 			/* A 'real' SCSI target. */
313 			sca->type = TYPE_SCSI;
314 		else
315 			/* An 'emulated' SCSI target. */
316 			sca->type = TYPE_ATAPI;
317 		sca->scbus = sc_link->bus->sc_dev.dv_unit;
318 		sca->target = sc_link->target;
319 		sca->lun = sc_link->lun;
320 		return (0);
321 	}
322 	case SCIOCCOMMAND:
323 		if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
324 			break;
325 		/* FALLTHROUGH */
326 	case ATAIOCCOMMAND:
327 	case SCIOCDEBUG:
328 		if ((flag & FWRITE) == 0)
329 			return (EPERM);
330 		break;
331 	default:
332 		if (sc_link->adapter->ioctl)
333 			return ((sc_link->adapter->ioctl)(sc_link, cmd, addr,
334 			    flag));
335 		else
336 			return (ENOTTY);
337 	}
338 
339 	switch(cmd) {
340 	case SCIOCCOMMAND:
341 		return (scsi_ioc_cmd(sc_link, (scsireq_t *)addr));
342 	case ATAIOCCOMMAND:
343 		return (scsi_ioc_ata_cmd(sc_link, (atareq_t *)addr));
344 	case SCIOCDEBUG: {
345 		int level = *((int *)addr);
346 
347 		SC_DEBUG(sc_link, SDEV_DB3, ("debug set to %d\n", level));
348 		sc_link->flags &= ~SDEV_DBX; /* clear debug bits */
349 		if (level & 1)
350 			sc_link->flags |= SDEV_DB1;
351 		if (level & 2)
352 			sc_link->flags |= SDEV_DB2;
353 		if (level & 4)
354 			sc_link->flags |= SDEV_DB3;
355 		if (level & 8)
356 			sc_link->flags |= SDEV_DB4;
357 		return (0);
358 	}
359 	default:
360 #ifdef DIAGNOSTIC
361 		panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
362 #endif
363 		return (0);
364 	}
365 }
366