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