1 /* $OpenBSD: umass_scsi.c,v 1.64 2023/05/10 15:28:26 krw 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 46 #include <dev/usb/umassvar.h> 47 #include <dev/usb/umass_scsi.h> 48 49 #include <scsi/scsi_all.h> 50 #include <scsi/scsiconf.h> 51 #include <scsi/scsi_disk.h> 52 #include <machine/bus.h> 53 54 struct umass_scsi_softc { 55 struct device *sc_child; 56 struct scsi_iopool sc_iopool; 57 int sc_open; 58 59 struct scsi_sense sc_sense_cmd; 60 }; 61 62 63 #define UMASS_SCSIID_HOST 0x00 64 #define UMASS_SCSIID_DEVICE 0x01 65 66 int umass_scsi_probe(struct scsi_link *); 67 void umass_scsi_cmd(struct scsi_xfer *); 68 69 const struct scsi_adapter umass_scsi_switch = { 70 umass_scsi_cmd, NULL, umass_scsi_probe, NULL, NULL 71 }; 72 73 void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, 74 int status); 75 void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 76 int status); 77 void *umass_io_get(void *); 78 void umass_io_put(void *, void *); 79 80 int 81 umass_scsi_attach(struct umass_softc *sc) 82 { 83 struct scsibus_attach_args saa; 84 struct umass_scsi_softc *scbus; 85 u_int16_t flags = 0; 86 87 scbus = malloc(sizeof(*scbus), M_USBDEV, M_WAITOK | M_ZERO); 88 89 sc->bus = scbus; 90 91 switch (sc->sc_cmd) { 92 case UMASS_CPROTO_RBC: 93 case UMASS_CPROTO_SCSI: 94 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n" 95 "sc = 0x%p, scbus = 0x%p\n", 96 sc->sc_dev.dv_xname, sc, scbus)); 97 break; 98 case UMASS_CPROTO_UFI: 99 flags |= SDEV_UFI | SDEV_ATAPI; 100 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: UFI\n" 101 "sc = 0x%p, scbus = 0x%p\n", 102 sc->sc_dev.dv_xname, sc, scbus)); 103 break; 104 case UMASS_CPROTO_ATAPI: 105 flags |= SDEV_ATAPI; 106 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n" 107 "sc = 0x%p, scbus = 0x%p\n", 108 sc->sc_dev.dv_xname, sc, scbus)); 109 break; 110 default: 111 break; 112 } 113 114 scsi_iopool_init(&scbus->sc_iopool, scbus, umass_io_get, umass_io_put); 115 116 saa.saa_adapter_buswidth = 2; 117 saa.saa_adapter = &umass_scsi_switch; 118 saa.saa_adapter_softc = sc; 119 saa.saa_adapter_target = UMASS_SCSIID_HOST; 120 saa.saa_luns = sc->maxlun + 1; 121 saa.saa_openings = 1; 122 saa.saa_quirks = sc->sc_busquirks; 123 saa.saa_pool = &scbus->sc_iopool; 124 saa.saa_flags = SDEV_UMASS | flags; 125 saa.saa_wwpn = saa.saa_wwnn = 0; 126 127 sc->sc_refcnt++; 128 scbus->sc_child = config_found((struct device *)sc, &saa, scsiprint); 129 if (--sc->sc_refcnt < 0) 130 usb_detach_wakeup(&sc->sc_dev); 131 132 return (0); 133 } 134 135 int 136 umass_scsi_detach(struct umass_softc *sc, int flags) 137 { 138 struct umass_scsi_softc *scbus = sc->bus; 139 int rv = 0; 140 141 if (scbus != NULL) { 142 if (scbus->sc_child != NULL) 143 rv = config_detach(scbus->sc_child, flags); 144 free(scbus, M_USBDEV, sizeof(*scbus)); 145 sc->bus = NULL; 146 } 147 148 return (rv); 149 } 150 151 int 152 umass_scsi_probe(struct scsi_link *link) 153 { 154 struct umass_softc *sc = link->bus->sb_adapter_softc; 155 struct usb_device_info udi; 156 size_t len; 157 158 /* dont fake devids when more than one scsi device can attach. */ 159 if (sc->maxlun > 0) 160 return (0); 161 162 usbd_fill_deviceinfo(sc->sc_udev, &udi); 163 164 /* 165 * Create a fake devid using the vendor and product ids and the last 166 * 12 characters of serial number, as recommended by Section 4.1.1 of 167 * the USB Mass Storage Class - Bulk Only Transport spec. 168 */ 169 len = strlen(udi.udi_serial); 170 if (len >= 12) { 171 char buf[21]; 172 snprintf(buf, sizeof(buf), "%04x%04x%s", udi.udi_vendorNo, 173 udi.udi_productNo, udi.udi_serial + len - 12); 174 link->id = devid_alloc(DEVID_SERIAL, DEVID_F_PRINT, 175 sizeof(buf) - 1, buf); 176 } 177 178 return (0); 179 } 180 181 void 182 umass_scsi_cmd(struct scsi_xfer *xs) 183 { 184 struct scsi_link *sc_link = xs->sc_link; 185 struct umass_softc *sc = sc_link->bus->sb_adapter_softc; 186 struct scsi_generic *cmd; 187 int cmdlen, dir; 188 189 #ifdef UMASS_DEBUG 190 microtime(&sc->tv); 191 #endif 192 193 DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lld.%06ld: %d:%d " 194 "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", 195 sc->sc_dev.dv_xname, (long long)sc->tv.tv_sec, sc->tv.tv_usec, 196 sc_link->target, sc_link->lun, xs, xs->cmd.opcode, 197 xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL)); 198 199 if (usbd_is_dying(sc->sc_udev)) { 200 xs->error = XS_DRIVER_STUFFUP; 201 goto done; 202 } 203 204 #if defined(UMASS_DEBUG) 205 if (sc_link->target != UMASS_SCSIID_DEVICE) { 206 DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", 207 sc->sc_dev.dv_xname, sc_link->target)); 208 xs->error = XS_DRIVER_STUFFUP; 209 goto done; 210 } 211 #endif 212 213 cmd = &xs->cmd; 214 cmdlen = xs->cmdlen; 215 216 dir = DIR_NONE; 217 if (xs->datalen) { 218 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { 219 case SCSI_DATA_IN: 220 dir = DIR_IN; 221 break; 222 case SCSI_DATA_OUT: 223 dir = DIR_OUT; 224 break; 225 } 226 } 227 228 if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { 229 printf("umass_cmd: large datalen, %d\n", xs->datalen); 230 xs->error = XS_DRIVER_STUFFUP; 231 goto done; 232 } 233 234 if (xs->flags & SCSI_POLL) { 235 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); 236 usbd_set_polling(sc->sc_udev, 1); 237 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 238 sc->polled_xfer_status = USBD_INVAL; 239 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 240 xs->data, xs->datalen, dir, 241 xs->timeout, umass_scsi_cb, xs); 242 sc->sc_xfer_flags = 0; 243 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", 244 sc->polled_xfer_status)); 245 usbd_set_polling(sc->sc_udev, 0); 246 /* scsi_done() has already been called. */ 247 return; 248 } else { 249 DPRINTF(UDMASS_SCSI, 250 ("umass_scsi_cmd: async dir=%d, cmdlen=%d" 251 " datalen=%d\n", 252 dir, cmdlen, xs->datalen)); 253 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 254 xs->data, xs->datalen, dir, 255 xs->timeout, umass_scsi_cb, xs); 256 /* scsi_done() has already been called. */ 257 return; 258 } 259 260 /* Return if command finishes early. */ 261 done: 262 scsi_done(xs); 263 } 264 265 void 266 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status) 267 { 268 struct umass_scsi_softc *scbus = sc->bus; 269 struct scsi_xfer *xs = priv; 270 struct scsi_link *link = xs->sc_link; 271 int cmdlen; 272 #ifdef UMASS_DEBUG 273 struct timeval tv; 274 u_int delta; 275 microtime(&tv); 276 delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + 277 tv.tv_usec - sc->tv.tv_usec; 278 #endif 279 280 DPRINTF(UDMASS_CMD, 281 ("umass_scsi_cb: at %lld.%06ld, delta=%u: xs=%p residue=%d" 282 " status=%d\n", (long long)tv.tv_sec, tv.tv_usec, delta, xs, residue, 283 status)); 284 285 xs->resid = residue; 286 287 switch (status) { 288 case STATUS_CMD_OK: 289 xs->error = XS_NOERROR; 290 break; 291 292 case STATUS_CMD_UNKNOWN: 293 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n")); 294 /* we can't issue REQUEST SENSE */ 295 if (xs->sc_link->quirks & ADEV_NOSENSE) { 296 /* 297 * If no residue and no other USB error, 298 * command succeeded. 299 */ 300 if (residue == 0) { 301 xs->error = XS_NOERROR; 302 break; 303 } 304 305 /* 306 * Some devices return a short INQUIRY 307 * response, omitting response data from the 308 * "vendor specific data" on... 309 */ 310 if (xs->cmd.opcode == INQUIRY && 311 residue < xs->datalen) { 312 xs->error = XS_NOERROR; 313 break; 314 } 315 316 xs->error = XS_DRIVER_STUFFUP; 317 break; 318 } 319 /* FALLTHROUGH */ 320 case STATUS_CMD_FAILED: 321 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for " 322 "scsi op 0x%02x\n", xs->cmd.opcode)); 323 /* fetch sense data */ 324 sc->sc_sense = 1; 325 memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); 326 scbus->sc_sense_cmd.opcode = REQUEST_SENSE; 327 scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT; 328 scbus->sc_sense_cmd.length = sizeof(xs->sense); 329 330 cmdlen = sizeof(scbus->sc_sense_cmd); 331 if (xs->flags & SCSI_POLL) { 332 usbd_set_polling(sc->sc_udev, 1); 333 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 334 sc->polled_xfer_status = USBD_INVAL; 335 } 336 /* scsi_done() has already been called. */ 337 sc->sc_methods->wire_xfer(sc, link->lun, 338 &scbus->sc_sense_cmd, cmdlen, 339 &xs->sense, sizeof(xs->sense), 340 DIR_IN, xs->timeout, 341 umass_scsi_sense_cb, xs); 342 if (xs->flags & SCSI_POLL) { 343 sc->sc_xfer_flags = 0; 344 usbd_set_polling(sc->sc_udev, 0); 345 } 346 return; 347 348 case STATUS_WIRE_FAILED: 349 xs->error = XS_RESET; 350 break; 351 352 default: 353 panic("%s: Unknown status %d in umass_scsi_cb", 354 sc->sc_dev.dv_xname, status); 355 } 356 357 DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lld.%06ld: return error=%d, " 358 "status=0x%x resid=%zu\n", 359 (long long)tv.tv_sec, tv.tv_usec, 360 xs->error, xs->status, xs->resid)); 361 362 if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) { 363 switch (sc->polled_xfer_status) { 364 case USBD_NORMAL_COMPLETION: 365 xs->error = XS_NOERROR; 366 break; 367 case USBD_TIMEOUT: 368 xs->error = XS_TIMEOUT; 369 break; 370 default: 371 xs->error = XS_DRIVER_STUFFUP; 372 break; 373 } 374 } 375 376 scsi_done(xs); 377 } 378 379 /* 380 * Finalise a completed autosense operation 381 */ 382 void 383 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 384 int status) 385 { 386 struct scsi_xfer *xs = priv; 387 388 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d " 389 "status=%d\n", xs, residue, status)); 390 391 sc->sc_sense = 0; 392 switch (status) { 393 case STATUS_CMD_OK: 394 case STATUS_CMD_UNKNOWN: 395 /* getting sense data succeeded */ 396 if (residue == 0 || residue == 14)/* XXX */ 397 xs->error = XS_SENSE; 398 else 399 xs->error = XS_SHORTSENSE; 400 break; 401 default: 402 DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", 403 sc->sc_dev.dv_xname, status)); 404 xs->error = XS_DRIVER_STUFFUP; 405 break; 406 } 407 408 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, " 409 "xs->flags=0x%x xs->resid=%zu\n", xs->error, xs->status, 410 xs->resid)); 411 412 if ((xs->flags & SCSI_POLL) && (xs->error == XS_NOERROR)) { 413 switch (sc->polled_xfer_status) { 414 case USBD_NORMAL_COMPLETION: 415 xs->error = XS_NOERROR; 416 break; 417 case USBD_TIMEOUT: 418 xs->error = XS_TIMEOUT; 419 break; 420 default: 421 xs->error = XS_DRIVER_STUFFUP; 422 break; 423 } 424 } 425 426 scsi_done(xs); 427 } 428 429 void * 430 umass_io_get(void *cookie) 431 { 432 struct umass_scsi_softc *scbus = cookie; 433 void *io = NULL; 434 int s; 435 436 s = splusb(); 437 if (!scbus->sc_open) { 438 scbus->sc_open = 1; 439 io = scbus; /* just has to be non-NULL */ 440 } 441 splx(s); 442 443 return (io); 444 } 445 446 void 447 umass_io_put(void *cookie, void *io) 448 { 449 struct umass_scsi_softc *scbus = cookie; 450 int s; 451 452 s = splusb(); 453 scbus->sc_open = 0; 454 splx(s); 455 } 456