xref: /openbsd-src/sys/scsi/scsi_ioctl.c (revision 664c6166bdf5ef0994a16bcce00610f942c9dc79)
1*664c6166Skrw /*	$OpenBSD: scsi_ioctl.c,v 1.67 2020/09/22 19:32:53 krw Exp $	*/
2cb3ad6e3Sdownsj /*	$NetBSD: scsi_ioctl.c,v 1.23 1996/10/12 23:23:17 christos Exp $	*/
3df930be7Sderaadt 
4df930be7Sderaadt /*
5df930be7Sderaadt  * Copyright (c) 1994 Charles Hannum.  All rights reserved.
6df930be7Sderaadt  *
7df930be7Sderaadt  * Redistribution and use in source and binary forms, with or without
8df930be7Sderaadt  * modification, are permitted provided that the following conditions
9df930be7Sderaadt  * are met:
10df930be7Sderaadt  * 1. Redistributions of source code must retain the above copyright
11df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer.
12df930be7Sderaadt  * 2. Redistributions in binary form must reproduce the above copyright
13df930be7Sderaadt  *    notice, this list of conditions and the following disclaimer in the
14df930be7Sderaadt  *    documentation and/or other materials provided with the distribution.
15df930be7Sderaadt  * 3. All advertising materials mentioning features or use of this software
16df930be7Sderaadt  *    must display the following acknowledgement:
17df930be7Sderaadt  *	This product includes software developed by Charles Hannum.
18df930be7Sderaadt  * 4. The name of the author may not be used to endorse or promote products
19df930be7Sderaadt  *    derived from this software without specific prior written permission.
20df930be7Sderaadt  *
21df930be7Sderaadt  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22df930be7Sderaadt  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
23df930be7Sderaadt  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
24df930be7Sderaadt  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
25df930be7Sderaadt  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
26df930be7Sderaadt  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27df930be7Sderaadt  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28df930be7Sderaadt  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29df930be7Sderaadt  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
30df930be7Sderaadt  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31df930be7Sderaadt  */
32df930be7Sderaadt 
33df930be7Sderaadt /*
34df930be7Sderaadt  * Contributed by HD Associates (hd@world.std.com).
35df930be7Sderaadt  * Copyright (c) 1992, 1993 HD Associates
36df930be7Sderaadt  *
37df930be7Sderaadt  * Berkeley style copyright.
38df930be7Sderaadt  */
39df930be7Sderaadt 
40df930be7Sderaadt #include <sys/param.h>
41b27348b2Sderaadt #include <sys/errno.h>
42df930be7Sderaadt #include <sys/systm.h>
435c0b007dSderaadt #include <sys/pool.h>
44df930be7Sderaadt #include <sys/device.h>
45cb3ad6e3Sdownsj #include <sys/fcntl.h>
46df930be7Sderaadt 
47df930be7Sderaadt #include <scsi/scsi_all.h>
48bba39a67Skrw #include <scsi/scsi_debug.h>
49df930be7Sderaadt #include <scsi/scsiconf.h>
50a3ef7ce1Sdlg 
51df930be7Sderaadt #include <sys/scsiio.h>
52a3ef7ce1Sdlg #include <sys/ataio.h>
53df930be7Sderaadt 
54864c175eSdlg int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
55a3ef7ce1Sdlg int			scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *);
56a48bde13Sbriggs 
5701cae9c4Skrw const unsigned char scsi_readsafe_cmd[256] = {
5801cae9c4Skrw 	[0x00] = 1,	/* TEST UNIT READY */
5901cae9c4Skrw 	[0x03] = 1,	/* REQUEST SENSE */
6001cae9c4Skrw 	[0x08] = 1,	/* READ(6) */
6101cae9c4Skrw 	[0x12] = 1,	/* INQUIRY */
6201cae9c4Skrw 	[0x1a] = 1,	/* MODE SENSE */
6301cae9c4Skrw 	[0x1b] = 1,	/* START STOP */
6401cae9c4Skrw 	[0x23] = 1,	/* READ FORMAT CAPACITIES */
6501cae9c4Skrw 	[0x25] = 1,	/* READ CDVD CAPACITY */
6601cae9c4Skrw 	[0x28] = 1,	/* READ(10) */
6701cae9c4Skrw 	[0x2b] = 1,	/* SEEK */
6801cae9c4Skrw 	[0x2f] = 1,	/* VERIFY(10) */
6901cae9c4Skrw 	[0x3c] = 1,	/* READ BUFFER */
7001cae9c4Skrw 	[0x3e] = 1,	/* READ LONG */
7101cae9c4Skrw 	[0x42] = 1,	/* READ SUBCHANNEL */
7201cae9c4Skrw 	[0x43] = 1,	/* READ TOC PMA ATIP */
7301cae9c4Skrw 	[0x44] = 1,	/* READ HEADER */
7401cae9c4Skrw 	[0x45] = 1,	/* PLAY AUDIO(10) */
7501cae9c4Skrw 	[0x46] = 1,	/* GET CONFIGURATION */
7601cae9c4Skrw 	[0x47] = 1,	/* PLAY AUDIO MSF */
7701cae9c4Skrw 	[0x48] = 1,	/* PLAY AUDIO TI */
7801cae9c4Skrw 	[0x4a] = 1,	/* GET EVENT STATUS NOTIFICATION */
7901cae9c4Skrw 	[0x4b] = 1,	/* PAUSE RESUME */
8001cae9c4Skrw 	[0x4e] = 1,	/* STOP PLAY SCAN */
8101cae9c4Skrw 	[0x51] = 1,	/* READ DISC INFO */
8201cae9c4Skrw 	[0x52] = 1,	/* READ TRACK RZONE INFO */
8301cae9c4Skrw 	[0x5a] = 1,	/* MODE SENSE(10) */
8401cae9c4Skrw 	[0x88] = 1,	/* READ(16) */
8501cae9c4Skrw 	[0x8f] = 1,	/* VERIFY(16) */
8601cae9c4Skrw 	[0xa4] = 1,	/* REPORT KEY */
8701cae9c4Skrw 	[0xa5] = 1,	/* PLAY AUDIO(12) */
8801cae9c4Skrw 	[0xa8] = 1,	/* READ(12) */
8901cae9c4Skrw 	[0xac] = 1,	/* GET PERFORMANCE */
9001cae9c4Skrw 	[0xad] = 1,	/* READ DVD STRUCTURE */
9101cae9c4Skrw 	[0xb9] = 1,	/* READ CD MSF */
9201cae9c4Skrw 	[0xba] = 1,	/* SCAN */
9301cae9c4Skrw 	[0xbc] = 1,	/* PLAY CD */
9401cae9c4Skrw 	[0xbd] = 1,	/* MECHANISM STATUS */
9501cae9c4Skrw 	[0xbe] = 1	/* READ CD */
9601cae9c4Skrw };
9701cae9c4Skrw 
98864c175eSdlg int
scsi_ioc_cmd(struct scsi_link * link,scsireq_t * screq)99864c175eSdlg scsi_ioc_cmd(struct scsi_link *link, scsireq_t *screq)
100df930be7Sderaadt {
101864c175eSdlg 	struct scsi_xfer		*xs;
102864c175eSdlg 	int				 err = 0;
103df930be7Sderaadt 
104864c175eSdlg 	if (screq->cmdlen > sizeof(struct scsi_generic))
105b088678dSkrw 		return EFAULT;
10626ef73aaSmatthew 	if (screq->datalen > MAXPHYS)
107b088678dSkrw 		return EINVAL;
108864c175eSdlg 
109864c175eSdlg 	xs = scsi_xs_get(link, 0);
110864c175eSdlg 	if (xs == NULL)
111b088678dSkrw 		return ENOMEM;
112864c175eSdlg 
113*664c6166Skrw 	memcpy(&xs->cmd, screq->cmd, screq->cmdlen);
114864c175eSdlg 	xs->cmdlen = screq->cmdlen;
115864c175eSdlg 
116864c175eSdlg 	if (screq->datalen > 0) {
1175c0b007dSderaadt 		xs->data = dma_alloc(screq->datalen, PR_WAITOK | PR_ZERO);
11826ef73aaSmatthew 		if (xs->data == NULL) {
11926ef73aaSmatthew 			err = ENOMEM;
12026ef73aaSmatthew 			goto err;
12126ef73aaSmatthew 		}
122864c175eSdlg 		xs->datalen = screq->datalen;
123864c175eSdlg 	}
124864c175eSdlg 
125c3094a8aSkrw 	if (ISSET(screq->flags, SCCMD_READ))
12636e286aaSkrw 		SET(xs->flags, SCSI_DATA_IN);
127c3094a8aSkrw 	if (ISSET(screq->flags, SCCMD_WRITE)) {
128864c175eSdlg 		if (screq->datalen > 0) {
129864c175eSdlg 			err = copyin(screq->databuf, xs->data, screq->datalen);
130864c175eSdlg 			if (err != 0)
131864c175eSdlg 				goto err;
132864c175eSdlg 		}
133864c175eSdlg 
13436e286aaSkrw 		SET(xs->flags, SCSI_DATA_OUT);
135864c175eSdlg 	}
136864c175eSdlg 
13736e286aaSkrw 	SET(xs->flags, SCSI_SILENT);	/* User is responsible for errors. */
138864c175eSdlg 	xs->timeout = screq->timeout;
139864c175eSdlg 	xs->retries = 0; /* user must do the retries *//* ignored */
140864c175eSdlg 
141ae1622c8Sdlg 	scsi_xs_sync(xs);
142df930be7Sderaadt 
143df930be7Sderaadt 	screq->retsts = 0;
144df930be7Sderaadt 	screq->status = xs->status;
145df930be7Sderaadt 	switch (xs->error) {
146df930be7Sderaadt 	case XS_NOERROR:
1470722b1a3Sdlg 		/* probably rubbish */
1480722b1a3Sdlg 		screq->datalen_used = xs->datalen - xs->resid;
149df930be7Sderaadt 		screq->retsts = SCCMD_OK;
150df930be7Sderaadt 		break;
151df930be7Sderaadt 	case XS_SENSE:
152f6e3733cSkrw 		SC_DEBUG_SENSE(xs);
153864c175eSdlg 		screq->senselen_used = min(sizeof(xs->sense),
154864c175eSdlg 		    sizeof(screq->sense));
1559e4f07dcStedu 		memcpy(screq->sense, &xs->sense, screq->senselen_used);
156df930be7Sderaadt 		screq->retsts = SCCMD_SENSE;
157df930be7Sderaadt 		break;
158709fb247Skrw 	case XS_SHORTSENSE:
159f6e3733cSkrw 		SC_DEBUG_SENSE(xs);
160864c175eSdlg 		printf("XS_SHORTSENSE\n");
161864c175eSdlg 		screq->senselen_used = min(sizeof(xs->sense),
162864c175eSdlg 		    sizeof(screq->sense));
1639e4f07dcStedu 		memcpy(screq->sense, &xs->sense, screq->senselen_used);
164709fb247Skrw 		screq->retsts = SCCMD_UNKNOWN;
165709fb247Skrw 		break;
166df930be7Sderaadt 	case XS_DRIVER_STUFFUP:
167df930be7Sderaadt 		screq->retsts = SCCMD_UNKNOWN;
168df930be7Sderaadt 		break;
169df930be7Sderaadt 	case XS_TIMEOUT:
170df930be7Sderaadt 		screq->retsts = SCCMD_TIMEOUT;
171df930be7Sderaadt 		break;
172df930be7Sderaadt 	case XS_BUSY:
173df930be7Sderaadt 		screq->retsts = SCCMD_BUSY;
174df930be7Sderaadt 		break;
175df930be7Sderaadt 	default:
176df930be7Sderaadt 		screq->retsts = SCCMD_UNKNOWN;
177df930be7Sderaadt 		break;
178df930be7Sderaadt 	}
1790722b1a3Sdlg 
180c3094a8aSkrw 	if (screq->datalen > 0 && ISSET(screq->flags, SCCMD_READ)) {
181864c175eSdlg 		err = copyout(xs->data, screq->databuf, screq->datalen);
182864c175eSdlg 		if (err != 0)
183864c175eSdlg 			goto err;
184df930be7Sderaadt 	}
185df930be7Sderaadt 
186864c175eSdlg err:
18726ef73aaSmatthew 	if (xs->data)
1885c0b007dSderaadt 		dma_free(xs->data, screq->datalen);
189864c175eSdlg 	scsi_xs_put(xs);
190df930be7Sderaadt 
191b088678dSkrw 	return err;
19201355dc1Smiod }
19301355dc1Smiod 
194a3ef7ce1Sdlg int
scsi_ioc_ata_cmd(struct scsi_link * link,atareq_t * atareq)195a3ef7ce1Sdlg scsi_ioc_ata_cmd(struct scsi_link *link, atareq_t *atareq)
196a3ef7ce1Sdlg {
197a3ef7ce1Sdlg 	struct scsi_xfer		*xs;
198a3ef7ce1Sdlg 	struct scsi_ata_passthru_12	*cdb;
199a3ef7ce1Sdlg 	int				 err = 0;
200a3ef7ce1Sdlg 
20126ef73aaSmatthew 	if (atareq->datalen > MAXPHYS)
202b088678dSkrw 		return EINVAL;
20326ef73aaSmatthew 
204a3ef7ce1Sdlg 	xs = scsi_xs_get(link, 0);
205a3ef7ce1Sdlg 	if (xs == NULL)
206b088678dSkrw 		return ENOMEM;
207a3ef7ce1Sdlg 
208*664c6166Skrw 	cdb = (struct scsi_ata_passthru_12 *)&xs->cmd;
209a3ef7ce1Sdlg 	cdb->opcode = ATA_PASSTHRU_12;
210a3ef7ce1Sdlg 
211a3ef7ce1Sdlg 	if (atareq->datalen > 0) {
212c3094a8aSkrw 		if (ISSET(atareq->flags, ATACMD_READ)) {
213a3ef7ce1Sdlg 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAIN;
214a3ef7ce1Sdlg 			cdb->flags = ATA_PASSTHRU_T_DIR_READ;
215a3ef7ce1Sdlg 		} else {
216a3ef7ce1Sdlg 			cdb->count_proto = ATA_PASSTHRU_PROTO_PIO_DATAOUT;
217a3ef7ce1Sdlg 			cdb->flags = ATA_PASSTHRU_T_DIR_WRITE;
218a3ef7ce1Sdlg 		}
219dacf4336Skrw 		SET(cdb->flags, ATA_PASSTHRU_T_LEN_SECTOR_COUNT);
220a3ef7ce1Sdlg 	} else {
221a3ef7ce1Sdlg 		cdb->count_proto = ATA_PASSTHRU_PROTO_NON_DATA;
222a3ef7ce1Sdlg 		cdb->flags = ATA_PASSTHRU_T_LEN_NONE;
223a3ef7ce1Sdlg 	}
224a3ef7ce1Sdlg 	cdb->features = atareq->features;
225a3ef7ce1Sdlg 	cdb->sector_count = atareq->sec_count;
226a3ef7ce1Sdlg 	cdb->lba_low = atareq->sec_num;
227a3ef7ce1Sdlg 	cdb->lba_mid = atareq->cylinder;
228a3ef7ce1Sdlg 	cdb->lba_high = atareq->cylinder >> 8;
229a3ef7ce1Sdlg 	cdb->device = atareq->head & 0x0f;
230a3ef7ce1Sdlg 	cdb->command = atareq->command;
231a3ef7ce1Sdlg 
232a3ef7ce1Sdlg 	xs->cmdlen = sizeof(*cdb);
233a3ef7ce1Sdlg 
234a3ef7ce1Sdlg 	if (atareq->datalen > 0) {
2355c0b007dSderaadt 		xs->data = dma_alloc(atareq->datalen, PR_WAITOK | PR_ZERO);
23626ef73aaSmatthew 		if (xs->data == NULL) {
23726ef73aaSmatthew 			err = ENOMEM;
23826ef73aaSmatthew 			goto err;
23926ef73aaSmatthew 		}
240a3ef7ce1Sdlg 		xs->datalen = atareq->datalen;
241a3ef7ce1Sdlg 	}
242a3ef7ce1Sdlg 
243c3094a8aSkrw 	if (ISSET(atareq->flags, ATACMD_READ))
24436e286aaSkrw 		SET(xs->flags, SCSI_DATA_IN);
245c3094a8aSkrw 	if (ISSET(atareq->flags, ATACMD_WRITE)) {
246a3ef7ce1Sdlg 		if (atareq->datalen > 0) {
247a3ef7ce1Sdlg 			err = copyin(atareq->databuf, xs->data,
248a3ef7ce1Sdlg 			    atareq->datalen);
249a3ef7ce1Sdlg 			if (err != 0)
250a3ef7ce1Sdlg 				goto err;
251a3ef7ce1Sdlg 		}
252a3ef7ce1Sdlg 
25336e286aaSkrw 		SET(xs->flags, SCSI_DATA_OUT);
254a3ef7ce1Sdlg 	}
255a3ef7ce1Sdlg 
25636e286aaSkrw 	SET(xs->flags, SCSI_SILENT);	/* User is responsible for errors. */
257a3ef7ce1Sdlg 	xs->retries = 0; /* user must do the retries *//* ignored */
258a3ef7ce1Sdlg 
259a3ef7ce1Sdlg 	scsi_xs_sync(xs);
260a3ef7ce1Sdlg 
261a3ef7ce1Sdlg 	atareq->retsts = ATACMD_ERROR;
262a3ef7ce1Sdlg 	switch (xs->error) {
263a3ef7ce1Sdlg 	case XS_SENSE:
264a3ef7ce1Sdlg 	case XS_SHORTSENSE:
265f6e3733cSkrw 		SC_DEBUG_SENSE(xs);
266a3ef7ce1Sdlg 		/* XXX this is not right */
267a3ef7ce1Sdlg 	case XS_NOERROR:
268a3ef7ce1Sdlg 		atareq->retsts = ATACMD_OK;
269a3ef7ce1Sdlg 		break;
270a3ef7ce1Sdlg 	default:
271a3ef7ce1Sdlg 		atareq->retsts = ATACMD_ERROR;
272a3ef7ce1Sdlg 		break;
273a3ef7ce1Sdlg 	}
274a3ef7ce1Sdlg 
275c3094a8aSkrw 	if (atareq->datalen > 0 && ISSET(atareq->flags, ATACMD_READ)) {
276a3ef7ce1Sdlg 		err = copyout(xs->data, atareq->databuf, atareq->datalen);
277a3ef7ce1Sdlg 		if (err != 0)
278a3ef7ce1Sdlg 			goto err;
279a3ef7ce1Sdlg 	}
280a3ef7ce1Sdlg 
281a3ef7ce1Sdlg err:
28226ef73aaSmatthew 	if (xs->data)
2835c0b007dSderaadt 		dma_free(xs->data, atareq->datalen);
284a3ef7ce1Sdlg 	scsi_xs_put(xs);
285a3ef7ce1Sdlg 
286b088678dSkrw 	return err;
287a3ef7ce1Sdlg }
288a3ef7ce1Sdlg 
289df930be7Sderaadt /*
290df930be7Sderaadt  * Something (e.g. another driver) has called us
2916d4b3493Skrw  * with a scsi_link for a target/lun/adapter, and a scsi
292df930be7Sderaadt  * specific ioctl to perform, better try.
293df930be7Sderaadt  */
294df930be7Sderaadt int
scsi_do_ioctl(struct scsi_link * link,u_long cmd,caddr_t addr,int flag)2956d4b3493Skrw scsi_do_ioctl(struct scsi_link *link, u_long cmd, caddr_t addr, int flag)
296df930be7Sderaadt {
2976d4b3493Skrw 	SC_DEBUG(link, SDEV_DB2, ("scsi_do_ioctl(0x%lx)\n", cmd));
298df930be7Sderaadt 
29901cae9c4Skrw 	switch(cmd) {
30001cae9c4Skrw 	case SCIOCIDENTIFY: {
30101cae9c4Skrw 		struct scsi_addr *sca = (struct scsi_addr *)addr;
30201cae9c4Skrw 
303382a5d4cSkrw 		if (!ISSET(link->flags, (SDEV_ATAPI | SDEV_UMASS)))
304b7f9cc29Skrw 			/* A 'real' SCSI target. */
305b7f9cc29Skrw 			sca->type = TYPE_SCSI;
306b7f9cc29Skrw 		else
307b7f9cc29Skrw 			/* An 'emulated' SCSI target. */
308b7f9cc29Skrw 			sca->type = TYPE_ATAPI;
3096d4b3493Skrw 		sca->scbus = link->bus->sc_dev.dv_unit;
3106d4b3493Skrw 		sca->target = link->target;
3116d4b3493Skrw 		sca->lun = link->lun;
312b088678dSkrw 		return 0;
31301cae9c4Skrw 	}
31401cae9c4Skrw 	case SCIOCCOMMAND:
31501cae9c4Skrw 		if (scsi_readsafe_cmd[((scsireq_t *)addr)->cmd[0]])
31601cae9c4Skrw 			break;
31701cae9c4Skrw 		/* FALLTHROUGH */
318a3ef7ce1Sdlg 	case ATAIOCCOMMAND:
31901cae9c4Skrw 	case SCIOCDEBUG:
3201b9e05ceSkrw 		if (!ISSET(flag, FWRITE))
321b088678dSkrw 			return EPERM;
32201cae9c4Skrw 		break;
32301cae9c4Skrw 	default:
32467c3123bSkrw 		if (link->bus->sb_adapter->ioctl)
32567c3123bSkrw 			return (link->bus->sb_adapter->ioctl)(link, cmd, addr, flag);
32601cae9c4Skrw 		else
327b088678dSkrw 			return ENOTTY;
32801cae9c4Skrw 	}
329cb3ad6e3Sdownsj 
330df930be7Sderaadt 	switch(cmd) {
331864c175eSdlg 	case SCIOCCOMMAND:
332b088678dSkrw 		return scsi_ioc_cmd(link, (scsireq_t *)addr);
333a3ef7ce1Sdlg 	case ATAIOCCOMMAND:
334b088678dSkrw 		return scsi_ioc_ata_cmd(link, (atareq_t *)addr);
335df930be7Sderaadt 	case SCIOCDEBUG: {
336df930be7Sderaadt 		int level = *((int *)addr);
337df930be7Sderaadt 
3386d4b3493Skrw 		SC_DEBUG(link, SDEV_DB3, ("debug set to %d\n", level));
33964e2b1d6Skrw 		CLR(link->flags, SDEV_DBX); /* clear debug bits */
340df930be7Sderaadt 		if (level & 1)
341dacf4336Skrw 			SET(link->flags, SDEV_DB1);
342df930be7Sderaadt 		if (level & 2)
343dacf4336Skrw 			SET(link->flags, SDEV_DB2);
344df930be7Sderaadt 		if (level & 4)
345dacf4336Skrw 			SET(link->flags, SDEV_DB3);
346df930be7Sderaadt 		if (level & 8)
347dacf4336Skrw 			SET(link->flags, SDEV_DB4);
348b088678dSkrw 		return 0;
349df930be7Sderaadt 	}
3509566dadaSderaadt 	default:
3519566dadaSderaadt #ifdef DIAGNOSTIC
352ccb87389Smiod 		panic("scsi_do_ioctl: impossible cmd (%#lx)", cmd);
353364ebb70Skrw #endif /* DIAGNOSTIC */
354b088678dSkrw 		return 0;
355df930be7Sderaadt 	}
356df930be7Sderaadt }
357