1 /* $OpenBSD: umass_scsi.c,v 1.38 2011/07/17 22:46:48 matthew Exp $ */ 2 /* $NetBSD: umass_scsipi.c,v 1.9 2003/02/16 23:14:08 augustss Exp $ */ 3 /* 4 * Copyright (c) 2001 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Lennart Augustsson (lennart@augustsson.net) at 9 * Carlstedt Research & Technology. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 20 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 21 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 22 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 23 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 24 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 25 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 26 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 27 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 28 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 29 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 30 * POSSIBILITY OF SUCH DAMAGE. 31 */ 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/kernel.h> 36 #include <sys/conf.h> 37 #include <sys/buf.h> 38 #include <sys/device.h> 39 #include <sys/ioctl.h> 40 #include <sys/malloc.h> 41 42 #include <dev/usb/usb.h> 43 #include <dev/usb/usbdi.h> 44 #include <dev/usb/usbdi_util.h> 45 #include <dev/usb/usbdevs.h> 46 47 #include <dev/usb/umassvar.h> 48 #include <dev/usb/umass_scsi.h> 49 50 #include <scsi/scsi_all.h> 51 #include <scsi/scsiconf.h> 52 #include <scsi/scsi_disk.h> 53 #include <machine/bus.h> 54 55 struct umass_scsi_softc { 56 struct umassbus_softc base; 57 struct scsi_link sc_link; 58 struct scsi_iopool sc_iopool; 59 int sc_open; 60 61 struct scsi_sense sc_sense_cmd; 62 }; 63 64 65 #define UMASS_SCSIID_HOST 0x00 66 #define UMASS_SCSIID_DEVICE 0x01 67 68 int umass_scsi_probe(struct scsi_link *); 69 void umass_scsi_cmd(struct scsi_xfer *); 70 void umass_scsi_minphys(struct buf *, struct scsi_link *); 71 72 struct scsi_adapter umass_scsi_switch = { 73 umass_scsi_cmd, 74 umass_scsi_minphys, 75 umass_scsi_probe 76 }; 77 78 void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, 79 int status); 80 void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 81 int status); 82 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *); 83 84 void *umass_io_get(void *); 85 void umass_io_put(void *, void *); 86 87 int 88 umass_scsi_attach(struct umass_softc *sc) 89 { 90 struct scsibus_attach_args saa; 91 struct umass_scsi_softc *scbus; 92 93 scbus = umass_scsi_setup(sc); 94 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 95 scbus->sc_link.luns = sc->maxlun + 1; 96 scbus->sc_link.flags &= ~SDEV_ATAPI; 97 scbus->sc_link.flags |= SDEV_UMASS; 98 99 bzero(&saa, sizeof(saa)); 100 saa.saa_sc_link = &scbus->sc_link; 101 102 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n" 103 "sc = 0x%x, scbus = 0x%x\n", 104 sc->sc_dev.dv_xname, sc, scbus)); 105 106 sc->sc_refcnt++; 107 scbus->base.sc_child = 108 config_found((struct device *)sc, &saa, scsiprint); 109 if (--sc->sc_refcnt < 0) 110 usb_detach_wakeup(&sc->sc_dev); 111 112 return (0); 113 } 114 115 int 116 umass_atapi_attach(struct umass_softc *sc) 117 { 118 struct scsibus_attach_args saa; 119 struct umass_scsi_softc *scbus; 120 121 scbus = umass_scsi_setup(sc); 122 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 123 scbus->sc_link.luns = 1; 124 scbus->sc_link.openings = 1; 125 scbus->sc_link.flags |= SDEV_ATAPI; 126 127 bzero(&saa, sizeof(saa)); 128 saa.saa_sc_link = &scbus->sc_link; 129 130 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n" 131 "sc = 0x%x, scbus = 0x%x\n", 132 sc->sc_dev.dv_xname, sc, scbus)); 133 134 sc->sc_refcnt++; 135 scbus->base.sc_child = config_found((struct device *)sc, 136 &saa, scsiprint); 137 if (--sc->sc_refcnt < 0) 138 usb_detach_wakeup(&sc->sc_dev); 139 140 return (0); 141 } 142 143 struct umass_scsi_softc * 144 umass_scsi_setup(struct umass_softc *sc) 145 { 146 struct umass_scsi_softc *scbus; 147 148 scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO); 149 150 sc->bus = (struct umassbus_softc *)scbus; 151 152 scsi_iopool_init(&scbus->sc_iopool, scbus, umass_io_get, umass_io_put); 153 154 /* Fill in the link. */ 155 scbus->sc_link.adapter_buswidth = 2; 156 scbus->sc_link.adapter = &umass_scsi_switch; 157 scbus->sc_link.adapter_softc = sc; 158 scbus->sc_link.openings = 1; 159 scbus->sc_link.quirks |= SDEV_ONLYBIG | sc->sc_busquirks; 160 scbus->sc_link.pool = &scbus->sc_iopool; 161 162 return (scbus); 163 } 164 165 int 166 umass_scsi_probe(struct scsi_link *link) 167 { 168 struct umass_softc *sc = link->adapter_softc; 169 struct usb_device_info udi; 170 size_t len; 171 172 /* dont fake devids when more than one scsi device can attach. */ 173 if (sc->maxlun > 0) 174 return (0); 175 176 usbd_fill_deviceinfo(sc->sc_udev, &udi, 1); 177 178 /* 179 * Create a fake devid using the vendor and product ids and the last 180 * 12 characters of serial number, as recommended by Section 4.1.1 of 181 * the USB Mass Storage Class - Bulk Only Transport spec. 182 */ 183 len = strlen(udi.udi_serial); 184 if (len >= 12) { 185 char buf[21]; 186 snprintf(buf, sizeof(buf), "%04x%04x%s", udi.udi_vendorNo, 187 udi.udi_productNo, udi.udi_serial + len - 12); 188 link->id = devid_alloc(DEVID_SERIAL, DEVID_F_PRINT, 189 sizeof(buf) - 1, buf); 190 } 191 192 return (0); 193 } 194 195 void 196 umass_scsi_cmd(struct scsi_xfer *xs) 197 { 198 struct scsi_link *sc_link = xs->sc_link; 199 struct umass_softc *sc = sc_link->adapter_softc; 200 struct scsi_generic *cmd; 201 int cmdlen, dir; 202 203 #ifdef UMASS_DEBUG 204 microtime(&sc->tv); 205 #endif 206 207 DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL); 208 209 DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d " 210 "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", 211 sc->sc_dev.dv_xname, sc->tv.tv_sec, sc->tv.tv_usec, 212 sc_link->target, sc_link->lun, xs, xs->cmd->opcode, 213 xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL)); 214 215 if (sc->sc_dying) { 216 xs->error = XS_DRIVER_STUFFUP; 217 goto done; 218 } 219 220 #if defined(UMASS_DEBUG) 221 if (sc_link->target != UMASS_SCSIID_DEVICE) { 222 DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", 223 sc->sc_dev.dv_xname, sc_link->target)); 224 xs->error = XS_DRIVER_STUFFUP; 225 goto done; 226 } 227 #endif 228 229 cmd = xs->cmd; 230 cmdlen = xs->cmdlen; 231 232 dir = DIR_NONE; 233 if (xs->datalen) { 234 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { 235 case SCSI_DATA_IN: 236 dir = DIR_IN; 237 break; 238 case SCSI_DATA_OUT: 239 dir = DIR_OUT; 240 break; 241 } 242 } 243 244 if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { 245 printf("umass_cmd: large datalen, %d\n", xs->datalen); 246 xs->error = XS_DRIVER_STUFFUP; 247 goto done; 248 } 249 250 if (xs->flags & SCSI_POLL) { 251 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); 252 usbd_set_polling(sc->sc_udev, 1); 253 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 254 sc->polled_xfer_status = USBD_INVAL; 255 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 256 xs->data, xs->datalen, dir, 257 xs->timeout, umass_scsi_cb, xs); 258 sc->sc_xfer_flags = 0; 259 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", 260 sc->polled_xfer_status)); 261 usbd_set_polling(sc->sc_udev, 0); 262 /* scsi_done() has already been called. */ 263 return; 264 } else { 265 DPRINTF(UDMASS_SCSI, 266 ("umass_scsi_cmd: async dir=%d, cmdlen=%d" 267 " datalen=%d\n", 268 dir, cmdlen, xs->datalen)); 269 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 270 xs->data, xs->datalen, dir, 271 xs->timeout, umass_scsi_cb, xs); 272 /* scsi_done() has already been called. */ 273 return; 274 } 275 276 /* Return if command finishes early. */ 277 done: 278 scsi_done(xs); 279 } 280 281 void 282 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl) 283 { 284 if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) 285 bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; 286 287 minphys(bp); 288 } 289 290 void 291 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status) 292 { 293 struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; 294 struct scsi_xfer *xs = priv; 295 struct scsi_link *link = xs->sc_link; 296 int cmdlen; 297 #ifdef UMASS_DEBUG 298 struct timeval tv; 299 u_int delta; 300 microtime(&tv); 301 delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + 302 tv.tv_usec - sc->tv.tv_usec; 303 #endif 304 305 DPRINTF(UDMASS_CMD, 306 ("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" 307 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, 308 status)); 309 310 xs->resid = residue; 311 312 switch (status) { 313 case STATUS_CMD_OK: 314 xs->error = XS_NOERROR; 315 break; 316 317 case STATUS_CMD_UNKNOWN: 318 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n")); 319 /* we can't issue REQUEST SENSE */ 320 if (xs->sc_link->quirks & ADEV_NOSENSE) { 321 /* 322 * If no residue and no other USB error, 323 * command succeeded. 324 */ 325 if (residue == 0) { 326 xs->error = XS_NOERROR; 327 break; 328 } 329 330 /* 331 * Some devices return a short INQUIRY 332 * response, omitting response data from the 333 * "vendor specific data" on... 334 */ 335 if (xs->cmd->opcode == INQUIRY && 336 residue < xs->datalen) { 337 xs->error = XS_NOERROR; 338 break; 339 } 340 341 xs->error = XS_DRIVER_STUFFUP; 342 break; 343 } 344 /* FALLTHROUGH */ 345 case STATUS_CMD_FAILED: 346 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for " 347 "scsi op 0x%02x\n", xs->cmd->opcode)); 348 /* fetch sense data */ 349 sc->sc_sense = 1; 350 memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); 351 scbus->sc_sense_cmd.opcode = REQUEST_SENSE; 352 scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT; 353 scbus->sc_sense_cmd.length = sizeof(xs->sense); 354 355 cmdlen = sizeof(scbus->sc_sense_cmd); 356 if (xs->flags & SCSI_POLL) { 357 usbd_set_polling(sc->sc_udev, 1); 358 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 359 sc->polled_xfer_status = USBD_INVAL; 360 } 361 /* scsi_done() has already been called. */ 362 sc->sc_methods->wire_xfer(sc, link->lun, 363 &scbus->sc_sense_cmd, cmdlen, 364 &xs->sense, sizeof(xs->sense), 365 DIR_IN, xs->timeout, 366 umass_scsi_sense_cb, xs); 367 if (xs->flags & SCSI_POLL) { 368 sc->sc_xfer_flags = 0; 369 usbd_set_polling(sc->sc_udev, 0); 370 } 371 return; 372 373 case STATUS_WIRE_FAILED: 374 xs->error = XS_RESET; 375 break; 376 377 default: 378 panic("%s: Unknown status %d in umass_scsi_cb", 379 sc->sc_dev.dv_xname, status); 380 } 381 382 DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, " 383 "status=0x%x resid=%d\n", 384 tv.tv_sec, tv.tv_usec, 385 xs->error, xs->status, xs->resid)); 386 387 if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) { 388 switch (sc->polled_xfer_status) { 389 case USBD_NORMAL_COMPLETION: 390 xs->error = XS_NOERROR; 391 break; 392 case USBD_TIMEOUT: 393 xs->error = XS_TIMEOUT; 394 break; 395 default: 396 xs->error = XS_DRIVER_STUFFUP; 397 break; 398 } 399 } 400 401 scsi_done(xs); 402 } 403 404 /* 405 * Finalise a completed autosense operation 406 */ 407 void 408 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 409 int status) 410 { 411 struct scsi_xfer *xs = priv; 412 413 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d " 414 "status=%d\n", xs, residue, status)); 415 416 sc->sc_sense = 0; 417 switch (status) { 418 case STATUS_CMD_OK: 419 case STATUS_CMD_UNKNOWN: 420 /* getting sense data succeeded */ 421 if (residue == 0 || residue == 14)/* XXX */ 422 xs->error = XS_SENSE; 423 else 424 xs->error = XS_SHORTSENSE; 425 break; 426 default: 427 DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", 428 sc->sc_dev.dv_xname, status)); 429 xs->error = XS_DRIVER_STUFFUP; 430 break; 431 } 432 433 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, " 434 "xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status, 435 xs->resid)); 436 437 if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) { 438 switch (sc->polled_xfer_status) { 439 case USBD_NORMAL_COMPLETION: 440 xs->error = XS_NOERROR; 441 break; 442 case USBD_TIMEOUT: 443 xs->error = XS_TIMEOUT; 444 break; 445 default: 446 xs->error = XS_DRIVER_STUFFUP; 447 break; 448 } 449 } 450 451 scsi_done(xs); 452 } 453 454 void * 455 umass_io_get(void *cookie) 456 { 457 struct umass_scsi_softc *scbus = cookie; 458 void *io = NULL; 459 int s; 460 461 s = splusb(); 462 if (!scbus->sc_open) { 463 scbus->sc_open = 1; 464 io = scbus; /* just has to be non-NULL */ 465 } 466 splx(s); 467 468 return (io); 469 } 470 471 void 472 umass_io_put(void *cookie, void *io) 473 { 474 struct umass_scsi_softc *scbus = cookie; 475 int s; 476 477 s = splusb(); 478 scbus->sc_open = 0; 479 splx(s); 480 } 481