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