1 | /* $NetBSD: scsipi_ioctl.c,v 1.69 2016/11/20 15:37:19 mlelstv Exp $ */ |
2 | |
3 | /*- |
4 | * Copyright (c) 1998, 2004 The NetBSD Foundation, Inc. |
5 | * All rights reserved. |
6 | * |
7 | * This code is derived from software contributed to The NetBSD Foundation |
8 | * by Charles M. Hannum. |
9 | * |
10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions |
12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. |
15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. |
18 | * |
19 | * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS |
20 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
21 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
22 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS |
23 | * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
24 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
25 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
26 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
27 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
28 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
29 | * POSSIBILITY OF SUCH DAMAGE. |
30 | */ |
31 | |
32 | /* |
33 | * Contributed by HD Associates (hd@world.std.com). |
34 | * Copyright (c) 1992, 1993 HD Associates |
35 | * |
36 | * Berkeley style copyright. |
37 | */ |
38 | |
39 | #include <sys/cdefs.h> |
40 | __KERNEL_RCSID(0, "$NetBSD: scsipi_ioctl.c,v 1.69 2016/11/20 15:37:19 mlelstv Exp $" ); |
41 | |
42 | #ifdef _KERNEL_OPT |
43 | #include "opt_compat_freebsd.h" |
44 | #include "opt_compat_netbsd.h" |
45 | #endif |
46 | |
47 | #include <sys/param.h> |
48 | #include <sys/errno.h> |
49 | #include <sys/systm.h> |
50 | #include <sys/malloc.h> |
51 | #include <sys/buf.h> |
52 | #include <sys/proc.h> |
53 | #include <sys/device.h> |
54 | #include <sys/fcntl.h> |
55 | |
56 | #include <dev/scsipi/scsipi_all.h> |
57 | #include <dev/scsipi/scsipiconf.h> |
58 | #include <dev/scsipi/scsipi_base.h> |
59 | #include <dev/scsipi/scsiconf.h> |
60 | #include <sys/scsiio.h> |
61 | |
62 | #include "scsibus.h" |
63 | #include "atapibus.h" |
64 | |
65 | struct scsi_ioctl { |
66 | LIST_ENTRY(scsi_ioctl) si_list; |
67 | struct buf si_bp; |
68 | struct uio si_uio; |
69 | struct iovec si_iov; |
70 | scsireq_t si_screq; |
71 | struct scsipi_periph *si_periph; |
72 | }; |
73 | |
74 | static LIST_HEAD(, scsi_ioctl) si_head; |
75 | static kmutex_t si_lock; |
76 | |
77 | void |
78 | scsipi_ioctl_init(void) |
79 | { |
80 | |
81 | mutex_init(&si_lock, MUTEX_DEFAULT, IPL_BIO); |
82 | } |
83 | |
84 | static struct scsi_ioctl * |
85 | si_get(void) |
86 | { |
87 | struct scsi_ioctl *si; |
88 | |
89 | si = malloc(sizeof(struct scsi_ioctl), M_TEMP, M_WAITOK|M_ZERO); |
90 | buf_init(&si->si_bp); |
91 | mutex_enter(&si_lock); |
92 | LIST_INSERT_HEAD(&si_head, si, si_list); |
93 | mutex_exit(&si_lock); |
94 | return (si); |
95 | } |
96 | |
97 | static void |
98 | si_free(struct scsi_ioctl *si) |
99 | { |
100 | |
101 | mutex_enter(&si_lock); |
102 | LIST_REMOVE(si, si_list); |
103 | mutex_exit(&si_lock); |
104 | buf_destroy(&si->si_bp); |
105 | free(si, M_TEMP); |
106 | } |
107 | |
108 | static struct scsi_ioctl * |
109 | si_find(struct buf *bp) |
110 | { |
111 | struct scsi_ioctl *si; |
112 | |
113 | mutex_enter(&si_lock); |
114 | for (si = si_head.lh_first; si != 0; si = si->si_list.le_next) |
115 | if (bp == &si->si_bp) |
116 | break; |
117 | mutex_exit(&si_lock); |
118 | return (si); |
119 | } |
120 | |
121 | /* |
122 | * We let the user interpret his own sense in the generic scsi world. |
123 | * This routine is called at interrupt time if the XS_CTL_USERCMD bit was set |
124 | * in the flags passed to scsi_scsipi_cmd(). No other completion processing |
125 | * takes place, even if we are running over another device driver. |
126 | * The lower level routines that call us here, will free the xs and restart |
127 | * the device's queue if such exists. |
128 | */ |
129 | void |
130 | scsipi_user_done(struct scsipi_xfer *xs) |
131 | { |
132 | struct buf *bp; |
133 | struct scsi_ioctl *si; |
134 | scsireq_t *screq; |
135 | struct scsipi_periph *periph = xs->xs_periph; |
136 | |
137 | bp = xs->bp; |
138 | #ifdef DIAGNOSTIC |
139 | if (bp == NULL) { |
140 | scsipi_printaddr(periph); |
141 | printf("user command with no buf\n" ); |
142 | panic("scsipi_user_done" ); |
143 | } |
144 | #endif |
145 | si = si_find(bp); |
146 | #ifdef DIAGNOSTIC |
147 | if (si == NULL) { |
148 | scsipi_printaddr(periph); |
149 | printf("user command with no ioctl\n" ); |
150 | panic("scsipi_user_done" ); |
151 | } |
152 | #endif |
153 | |
154 | screq = &si->si_screq; |
155 | |
156 | SC_DEBUG(xs->xs_periph, SCSIPI_DB2, ("user-done\n" )); |
157 | |
158 | screq->retsts = 0; |
159 | screq->status = xs->status; |
160 | switch (xs->error) { |
161 | case XS_NOERROR: |
162 | SC_DEBUG(periph, SCSIPI_DB3, ("no error\n" )); |
163 | screq->datalen_used = |
164 | xs->datalen - xs->resid; /* probably rubbish */ |
165 | screq->retsts = SCCMD_OK; |
166 | break; |
167 | case XS_SENSE: |
168 | SC_DEBUG(periph, SCSIPI_DB3, ("have sense\n" )); |
169 | screq->senselen_used = min(sizeof(xs->sense.scsi_sense), |
170 | SENSEBUFLEN); |
171 | memcpy(screq->sense, &xs->sense.scsi_sense, screq->senselen); |
172 | screq->retsts = SCCMD_SENSE; |
173 | break; |
174 | case XS_SHORTSENSE: |
175 | SC_DEBUG(periph, SCSIPI_DB3, ("have short sense\n" )); |
176 | screq->senselen_used = min(sizeof(xs->sense.atapi_sense), |
177 | SENSEBUFLEN); |
178 | memcpy(screq->sense, &xs->sense.scsi_sense, screq->senselen); |
179 | screq->retsts = SCCMD_UNKNOWN; /* XXX need a shortsense here */ |
180 | break; |
181 | case XS_DRIVER_STUFFUP: |
182 | scsipi_printaddr(periph); |
183 | printf("passthrough: adapter inconsistency\n" ); |
184 | screq->retsts = SCCMD_UNKNOWN; |
185 | break; |
186 | case XS_SELTIMEOUT: |
187 | SC_DEBUG(periph, SCSIPI_DB3, ("seltimeout\n" )); |
188 | screq->retsts = SCCMD_TIMEOUT; |
189 | break; |
190 | case XS_TIMEOUT: |
191 | SC_DEBUG(periph, SCSIPI_DB3, ("timeout\n" )); |
192 | screq->retsts = SCCMD_TIMEOUT; |
193 | break; |
194 | case XS_BUSY: |
195 | SC_DEBUG(periph, SCSIPI_DB3, ("busy\n" )); |
196 | screq->retsts = SCCMD_BUSY; |
197 | break; |
198 | default: |
199 | scsipi_printaddr(periph); |
200 | printf("unknown error category %d from adapter\n" , |
201 | xs->error); |
202 | screq->retsts = SCCMD_UNKNOWN; |
203 | break; |
204 | } |
205 | |
206 | if (xs->xs_control & XS_CTL_ASYNC) { |
207 | mutex_enter(chan_mtx(periph->periph_channel)); |
208 | scsipi_put_xs(xs); |
209 | mutex_exit(chan_mtx(periph->periph_channel)); |
210 | } |
211 | } |
212 | |
213 | |
214 | /* Pseudo strategy function |
215 | * Called by scsipi_do_ioctl() via physio/physstrat if there is to |
216 | * be data transfered, and directly if there is no data transfer. |
217 | * |
218 | * Should I reorganize this so it returns to physio instead |
219 | * of sleeping in scsiio_scsipi_cmd? Is there any advantage, other |
220 | * than avoiding the probable duplicate wakeup in iodone? [PD] |
221 | * |
222 | * No, seems ok to me... [JRE] |
223 | * (I don't see any duplicate wakeups) |
224 | * |
225 | * Can't be used with block devices or raw_read/raw_write directly |
226 | * from the cdevsw/bdevsw tables because they couldn't have added |
227 | * the screq structure. [JRE] |
228 | */ |
229 | static void |
230 | scsistrategy(struct buf *bp) |
231 | { |
232 | struct scsi_ioctl *si; |
233 | scsireq_t *screq; |
234 | struct scsipi_periph *periph; |
235 | int error; |
236 | int flags = 0; |
237 | |
238 | si = si_find(bp); |
239 | if (si == NULL) { |
240 | printf("scsistrategy: " |
241 | "No matching ioctl request found in queue\n" ); |
242 | error = EINVAL; |
243 | goto done; |
244 | } |
245 | screq = &si->si_screq; |
246 | periph = si->si_periph; |
247 | SC_DEBUG(periph, SCSIPI_DB2, ("user_strategy\n" )); |
248 | |
249 | /* |
250 | * We're in trouble if physio tried to break up the transfer. |
251 | */ |
252 | if (bp->b_bcount != screq->datalen) { |
253 | scsipi_printaddr(periph); |
254 | printf("physio split the request.. cannot proceed\n" ); |
255 | error = EIO; |
256 | goto done; |
257 | } |
258 | |
259 | if (screq->timeout == 0) { |
260 | error = EINVAL; |
261 | goto done; |
262 | } |
263 | |
264 | if (screq->cmdlen > sizeof(struct scsipi_generic)) { |
265 | scsipi_printaddr(periph); |
266 | printf("cmdlen too big\n" ); |
267 | error = EFAULT; |
268 | goto done; |
269 | } |
270 | |
271 | if ((screq->flags & SCCMD_READ) && screq->datalen > 0) |
272 | flags |= XS_CTL_DATA_IN; |
273 | if ((screq->flags & SCCMD_WRITE) && screq->datalen > 0) |
274 | flags |= XS_CTL_DATA_OUT; |
275 | if (screq->flags & SCCMD_TARGET) |
276 | flags |= XS_CTL_TARGET; |
277 | if (screq->flags & SCCMD_ESCAPE) |
278 | flags |= XS_CTL_ESCAPE; |
279 | |
280 | error = scsipi_command(periph, (void *)screq->cmd, screq->cmdlen, |
281 | (void *)bp->b_data, screq->datalen, |
282 | 0, /* user must do the retries *//* ignored */ |
283 | screq->timeout, bp, flags | XS_CTL_USERCMD); |
284 | |
285 | done: |
286 | if (error) |
287 | bp->b_resid = bp->b_bcount; |
288 | bp->b_error = error; |
289 | biodone(bp); |
290 | return; |
291 | } |
292 | |
293 | /* |
294 | * Something (e.g. another driver) has called us |
295 | * with a periph and a scsi-specific ioctl to perform, |
296 | * better try. If user-level type command, we must |
297 | * still be running in the context of the calling process |
298 | */ |
299 | int |
300 | scsipi_do_ioctl(struct scsipi_periph *periph, dev_t dev, u_long cmd, |
301 | void *addr, int flag, struct lwp *l) |
302 | { |
303 | int error; |
304 | |
305 | SC_DEBUG(periph, SCSIPI_DB2, ("scsipi_do_ioctl(0x%lx)\n" , cmd)); |
306 | |
307 | if (addr == NULL) |
308 | return EINVAL; |
309 | |
310 | /* Check for the safe-ness of this request. */ |
311 | switch (cmd) { |
312 | case OSCIOCIDENTIFY: |
313 | case SCIOCIDENTIFY: |
314 | break; |
315 | case SCIOCCOMMAND: |
316 | if ((((scsireq_t *)addr)->flags & SCCMD_READ) == 0 && |
317 | (flag & FWRITE) == 0) |
318 | return (EBADF); |
319 | break; |
320 | default: |
321 | if ((flag & FWRITE) == 0) |
322 | return (EBADF); |
323 | } |
324 | |
325 | switch (cmd) { |
326 | case SCIOCCOMMAND: { |
327 | scsireq_t *screq = (scsireq_t *)addr; |
328 | struct scsi_ioctl *si; |
329 | int len; |
330 | |
331 | si = si_get(); |
332 | si->si_screq = *screq; |
333 | si->si_periph = periph; |
334 | len = screq->datalen; |
335 | if (len) { |
336 | si->si_iov.iov_base = screq->databuf; |
337 | si->si_iov.iov_len = len; |
338 | si->si_uio.uio_iov = &si->si_iov; |
339 | si->si_uio.uio_iovcnt = 1; |
340 | si->si_uio.uio_resid = len; |
341 | si->si_uio.uio_offset = 0; |
342 | si->si_uio.uio_rw = |
343 | (screq->flags & SCCMD_READ) ? UIO_READ : UIO_WRITE; |
344 | if ((flag & FKIOCTL) == 0) { |
345 | si->si_uio.uio_vmspace = l->l_proc->p_vmspace; |
346 | } else { |
347 | UIO_SETUP_SYSSPACE(&si->si_uio); |
348 | } |
349 | error = physio(scsistrategy, &si->si_bp, dev, |
350 | (screq->flags & SCCMD_READ) ? B_READ : B_WRITE, |
351 | periph->periph_channel->chan_adapter->adapt_minphys, |
352 | &si->si_uio); |
353 | } else { |
354 | /* if no data, no need to translate it.. */ |
355 | si->si_bp.b_flags = 0; |
356 | si->si_bp.b_data = 0; |
357 | si->si_bp.b_bcount = 0; |
358 | si->si_bp.b_dev = dev; |
359 | si->si_bp.b_proc = l->l_proc; |
360 | scsistrategy(&si->si_bp); |
361 | error = si->si_bp.b_error; |
362 | } |
363 | *screq = si->si_screq; |
364 | si_free(si); |
365 | return (error); |
366 | } |
367 | case SCIOCDEBUG: { |
368 | int level = *((int *)addr); |
369 | |
370 | SC_DEBUG(periph, SCSIPI_DB3, ("debug set to %d\n" , level)); |
371 | periph->periph_dbflags = 0; |
372 | if (level & 1) |
373 | periph->periph_dbflags |= SCSIPI_DB1; |
374 | if (level & 2) |
375 | periph->periph_dbflags |= SCSIPI_DB2; |
376 | if (level & 4) |
377 | periph->periph_dbflags |= SCSIPI_DB3; |
378 | if (level & 8) |
379 | periph->periph_dbflags |= SCSIPI_DB4; |
380 | return (0); |
381 | } |
382 | case SCIOCRECONFIG: |
383 | case SCIOCDECONFIG: |
384 | return (EINVAL); |
385 | case SCIOCIDENTIFY: { |
386 | struct scsi_addr *sca = (struct scsi_addr *)addr; |
387 | |
388 | switch (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(periph))) { |
389 | case SCSIPI_BUSTYPE_SCSI: |
390 | sca->type = TYPE_SCSI; |
391 | sca->addr.scsi.scbus = |
392 | device_unit(device_parent(periph->periph_dev)); |
393 | sca->addr.scsi.target = periph->periph_target; |
394 | sca->addr.scsi.lun = periph->periph_lun; |
395 | return (0); |
396 | case SCSIPI_BUSTYPE_ATAPI: |
397 | sca->type = TYPE_ATAPI; |
398 | sca->addr.atapi.atbus = |
399 | device_unit(device_parent(periph->periph_dev)); |
400 | sca->addr.atapi.drive = periph->periph_target; |
401 | return (0); |
402 | } |
403 | return (ENXIO); |
404 | } |
405 | #if defined(COMPAT_12) || defined(COMPAT_FREEBSD) |
406 | /* SCIOCIDENTIFY before ATAPI staff merge */ |
407 | case OSCIOCIDENTIFY: { |
408 | struct oscsi_addr *sca = (struct oscsi_addr *)addr; |
409 | |
410 | switch (SCSIPI_BUSTYPE_TYPE(scsipi_periph_bustype(periph))) { |
411 | case SCSIPI_BUSTYPE_SCSI: |
412 | sca->scbus = |
413 | device_unit(device_parent(periph->periph_dev)); |
414 | sca->target = periph->periph_target; |
415 | sca->lun = periph->periph_lun; |
416 | return (0); |
417 | } |
418 | return (ENODEV); |
419 | } |
420 | #endif |
421 | default: |
422 | return (ENOTTY); |
423 | } |
424 | |
425 | #ifdef DIAGNOSTIC |
426 | panic("scsipi_do_ioctl: impossible" ); |
427 | #endif |
428 | } |
429 | |