| 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 | |