xref: /openbsd/sys/scsi/scsi_ioctl.c (revision 664c6166)
1 /*	$OpenBSD: scsi_ioctl.c,v 1.67 2020/09/22 19:32:53 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/param.h>
41 #include <sys/errno.h>
42 #include <sys/systm.h>
43 #include <sys/pool.h>
44 #include <sys/device.h>
45 #include <sys/fcntl.h>
46 
47 #include <scsi/scsi_all.h>
48 #include <scsi/scsi_debug.h>
49 #include <scsi/scsiconf.h>
50 
51 #include <sys/scsiio.h>
52 #include <sys/ataio.h>
53 
54 int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
55 int			scsi_ioc_ata_cmd(struct scsi_link *, atareq_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
scsi_ioc_cmd(struct scsi_link * link,scsireq_t * screq)99 scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
100 {
101 	struct scsi_xfer		*xs;
102 	int				 err = 0;
103 
104 	if (screq->cmdlen > sizeof(struct scsi_generic))
105 		return EFAULT;
106 	if (screq->datalen > MAXPHYS)
107 		return EINVAL;
108 
109 	xs = scsi_xs_get(link, 0);
110 	if (xs == NULL)
111 		return ENOMEM;
112 
113 	memcpy(&xs->cmd, screq->cmd, screq->cmdlen);
114 	xs->cmdlen = screq->cmdlen;
115 
116 	if (screq->datalen > 0) {
117 		xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO);
118 		if (xs->data == NULL) {
119 			err = ENOMEM;
120 			goto err;
121 		}
122 		xs->datalen = screq->datalen;
123 	}
124 
125 	if (ISSET(screq->flags, SCCMD_READ))
126 		SET(xs->flags, SCSI_DATA_IN);
127 	if (ISSET(screq->flags, SCCMD_WRITE)) {
128 		if (screq->datalen > 0) {
129 			err = copyin(screq->databuf, xs->data, screq->datalen);
130 			if (err != 0)
131 				goto err;
132 		}
133 
134 		SET(xs->flags, SCSI_DATA_OUT);
135 	}
136 
137 	SET(xs->flags, SCSI_SILENT);	/* User is responsible for errors. */
138 	xs->timeout = screq->timeout;
139 	xs->retries = 0; /* user must do the retries *//* ignored */
140 
141 	scsi_xs_sync(xs);
142 
143 	screq->retsts = 0;
144 	screq->status = xs->status;
145 	switch (xs->error) {
146 	case XS_NOERROR:
147 		/* probably rubbish */
148 		screq->datalen_used = xs->datalen - xs->resid;
149 		screq->retsts = SCCMD_OK;
150 		break;
151 	case XS_SENSE:
152 		SC_DEBUG_SENSE(xs);
153 		screq->senselen_used = min(sizeof(xs->sense),
154 		    sizeof(screq->sense));
155 		memcpy(screq->sense, &xs->sense, screq->senselen_used);
156 		screq->retsts = SCCMD_SENSE;
157 		break;
158 	case XS_SHORTSENSE:
159 		SC_DEBUG_SENSE(xs);
160 		printf("XS_SHORTSENSE\n");
161 		screq->senselen_used = min(sizeof(xs->sense),
162 		    sizeof(screq->sense));
163 		memcpy(screq->sense, &xs->sense, screq->senselen_used);
164 		screq->retsts = SCCMD_UNKNOWN;
165 		break;
166 	case XS_DRIVER_STUFFUP:
167 		screq->retsts = SCCMD_UNKNOWN;
168 		break;
169 	case XS_TIMEOUT:
170 		screq->retsts = SCCMD_TIMEOUT;
171 		break;
172 	case XS_BUSY:
173 		screq->retsts = SCCMD_BUSY;
174 		break;
175 	default:
176 		screq->retsts = SCCMD_UNKNOWN;
177 		break;
178 	}
179 
180 	if (screq->datalen > 0 && ISSET(screq->flags, SCCMD_READ)) {
181 		err = copyout(xs->data, screq->databuf, screq->datalen);
182 		if (err != 0)
183 			goto err;
184 	}
185 
186 err:
187 	if (xs->data)
188 		dma_free(xs->data, screq->datalen);
189 	scsi_xs_put(xs);
190 
191 	return err;
192 }
193 
194 int
scsi_ioc_ata_cmd(struct scsi_link * link,atareq_t * atareq)195 scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq)
196 {
197 	struct scsi_xfer		*xs;
198 	struct scsi_ata_passthru_12	*cdb;
199 	int				 err = 0;
200 
201 	if (atareq->datalen > MAXPHYS)
202 		return EINVAL;
203 
204 	xs = scsi_xs_get(link, 0);
205 	if (xs == NULL)
206 		return ENOMEM;
207 
208 	cdb = (struct scsi_ata_passthru_12 *)&xs->cmd;
209 	cdb->opcode = ATA_PASSTHRU_12;
210 
211 	if (atareq->datalen > 0) {
212 		if (ISSET(atareq->flags, ATACMD_READ)) {
213 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN;
214 			cdb->flags = ATA_PASSTHRU_T_DIR_READ;
215 		} else {
216 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT;
217 			cdb->flags = ATA_PASSTHRU_T_DIR_WRITE;
218 		}
219 		SET(cdb->flags, ATA_PASSTHRU_T_LEN_SECTOR_COUNT);
220 	} else {
221 		cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA;
222 		cdb->flags = ATA_PASSTHRU_T_LEN_NONE;
223 	}
224 	cdb->features = atareq->features;
225 	cdb->sector_count = atareq->sec_count;
226 	cdb->lba_low = atareq->sec_num;
227 	cdb->lba_mid = atareq->cylinder;
228 	cdb->lba_high = atareq->cylinder >> 8;
229 	cdb->device = atareq->head & 0x0f;
230 	cdb->command = atareq->command;
231 
232 	xs->cmdlen = sizeof(*cdb);
233 
234 	if (atareq->datalen > 0) {
235 		xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO);
236 		if (xs->data == NULL) {
237 			err = ENOMEM;
238 			goto err;
239 		}
240 		xs->datalen = atareq->datalen;
241 	}
242 
243 	if (ISSET(atareq->flags, ATACMD_READ))
244 		SET(xs->flags, SCSI_DATA_IN);
245 	if (ISSET(atareq->flags, ATACMD_WRITE)) {
246 		if (atareq->datalen > 0) {
247 			err = copyin(atareq->databuf, xs->data,
248 			    atareq->datalen);
249 			if (err != 0)
250 				goto err;
251 		}
252 
253 		SET(xs->flags, SCSI_DATA_OUT);
254 	}
255 
256 	SET(xs->flags, SCSI_SILENT);	/* User is responsible for errors. */
257 	xs->retries = 0; /* user must do the retries *//* ignored */
258 
259 	scsi_xs_sync(xs);
260 
261 	atareq->retsts = ATACMD_ERROR;
262 	switch (xs->error) {
263 	case XS_SENSE:
264 	case XS_SHORTSENSE:
265 		SC_DEBUG_SENSE(xs);
266 		/* XXX this is not right */
267 	case XS_NOERROR:
268 		atareq->retsts = ATACMD_OK;
269 		break;
270 	default:
271 		atareq->retsts = ATACMD_ERROR;
272 		break;
273 	}
274 
275 	if (atareq->datalen > 0 && ISSET(atareq->flags, ATACMD_READ)) {
276 		err = copyout(xs->data, atareq->databuf, atareq->datalen);
277 		if (err != 0)
278 			goto err;
279 	}
280 
281 err:
282 	if (xs->data)
283 		dma_free(xs->data, atareq->datalen);
284 	scsi_xs_put(xs);
285 
286 	return err;
287 }
288 
289 /*
290  * Something (e.g. another driver) has called us
291  * with a scsi_link for a target/lun/adapter, and a scsi
292  * specific ioctl to perform, better try.
293  */
294 int
scsi_do_ioctl(struct scsi_link * link,u_long cmd,caddr_t addr,int flag)295 scsi_do_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
296 {
297 	SC_DEBUG(link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
298 
299 	switch(cmd) {
300 	case SCIOCIDENTIFY: {
301 		struct scsi_addr *sca = (struct scsi_addr *)addr;
302 
303 		if (!ISSET(link->flags, (SDEV_ATAPI | SDEV_UMASS)))
304 			/* A 'real' SCSI target. */
305 			sca->type = TYPE_SCSI;
306 		else
307 			/* An 'emulated' SCSI target. */
308 			sca->type = TYPE_ATAPI;
309 		sca->scbus = link->bus->sc_dev.dv_unit;
310 		sca->target = link->target;
311 		sca->lun = link->lun;
312 		return 0;
313 	}
314 	case SCIOCCOMMAND:
315 		if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
316 			break;
317 		/* FALLTHROUGH */
318 	case ATAIOCCOMMAND:
319 	case SCIOCDEBUG:
320 		if (!ISSET(flag, FWRITE))
321 			return EPERM;
322 		break;
323 	default:
324 		if (link->bus->sb_adapter->ioctl)
325 			return (link->bus->sb_adapter->ioctl)(link, cmd, addr, flag);
326 		else
327 			return ENOTTY;
328 	}
329 
330 	switch(cmd) {
331 	case SCIOCCOMMAND:
332 		return scsi_ioc_cmd(link, (scsireq_t *)addr);
333 	case ATAIOCCOMMAND:
334 		return scsi_ioc_ata_cmd(link, (atareq_t *)addr);
335 	case SCIOCDEBUG: {
336 		int level = *((int *)addr);
337 
338 		SC_DEBUG(link, SDEV_DB3, ("debug set to %d\n", level));
339 		CLR(link->flags, SDEV_DBX); /* clear debug bits */
340 		if (level & 1)
341 			SET(link->flags, SDEV_DB1);
342 		if (level & 2)
343 			SET(link->flags, SDEV_DB2);
344 		if (level & 4)
345 			SET(link->flags, SDEV_DB3);
346 		if (level & 8)
347 			SET(link->flags, SDEV_DB4);
348 		return 0;
349 	}
350 	default:
351 #ifdef DIAGNOSTIC
352 		panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
353 #endif /* DIAGNOSTIC */
354 		return 0;
355 	}
356 }
357