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