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
54int			scsi_ioc_cmd(struct scsi_link *, scsireq_t *);
55int			scsi_ioc_ata_cmd(struct scsi_link *, atareq_t *);
56
57const 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
98int
99scsi_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
186err:
187	if (xs->data)
188		dma_free(xs->data, screq->datalen);
189	scsi_xs_put(xs);
190
191	return err;
192}
193
194int
195scsi_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
281err:
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 */
294int
295scsi_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