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