1 /* $OpenBSD: umass_scsi.c,v 1.4 2003/05/17 18:25:51 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 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 #include "atapiscsi.h" 41 42 #include <sys/param.h> 43 #include <sys/systm.h> 44 #include <sys/kernel.h> 45 #include <sys/conf.h> 46 #include <sys/buf.h> 47 #include <sys/device.h> 48 #include <sys/ioctl.h> 49 #include <sys/malloc.h> 50 51 #include <dev/usb/usb.h> 52 #include <dev/usb/usbdi.h> 53 #include <dev/usb/usbdi_util.h> 54 #include <dev/usb/usbdevs.h> 55 56 #include <dev/usb/umassvar.h> 57 #include <dev/usb/umass_scsi.h> 58 59 #include <scsi/scsi_all.h> 60 #include <scsi/scsiconf.h> 61 #include <scsi/scsi_disk.h> 62 #include <machine/bus.h> 63 64 struct umass_scsi_softc { 65 struct umassbus_softc base; 66 struct scsi_link sc_link; 67 struct scsi_adapter sc_adapter; 68 69 usbd_status sc_sync_status; 70 struct scsi_sense sc_sense_cmd; 71 }; 72 73 74 #define SHORT_INQUIRY_LENGTH 36 /* XXX */ 75 76 #define UMASS_SCSIID_HOST 0x00 77 #define UMASS_SCSIID_DEVICE 0x01 78 79 #define UMASS_ATAPI_DRIVE 0 80 81 int umass_scsi_cmd(struct scsi_xfer *); 82 void umass_scsi_minphys(struct buf *); 83 84 void umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, 85 int status); 86 void umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 87 int status); 88 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *); 89 90 struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, }; 91 92 #if NATAPISCSI > 0 93 struct scsi_device umass_atapiscsi_dev = { NULL, NULL, NULL, NULL, }; 94 #endif 95 96 int 97 umass_scsi_attach(struct umass_softc *sc) 98 { 99 struct umass_scsi_softc *scbus; 100 101 scbus = umass_scsi_setup(sc); 102 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 103 scbus->sc_link.luns = sc->maxlun + 1; 104 scbus->sc_link.flags &= ~SDEV_ATAPI; 105 scbus->sc_link.device = &umass_scsi_dev; 106 107 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: SCSI\n" 108 "sc = 0x%x, scbus = 0x%x\n", 109 USBDEVNAME(sc->sc_dev), sc, scbus)); 110 111 sc->sc_refcnt++; 112 scbus->base.sc_child = 113 config_found((struct device *)sc, &scbus->sc_link, scsiprint); 114 if (--sc->sc_refcnt < 0) 115 usb_detach_wakeup(USBDEV(sc->sc_dev)); 116 117 return (0); 118 } 119 120 #if NATAPISCSI > 0 121 int 122 umass_atapi_attach(struct umass_softc *sc) 123 { 124 struct umass_scsi_softc *scbus; 125 126 scbus = umass_scsi_setup(sc); 127 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 128 scbus->sc_link.luns = 1; 129 scbus->sc_link.flags |= SDEV_ATAPI; 130 scbus->sc_link.quirks |= SDEV_NOLUNS; 131 scbus->sc_link.device = &umass_atapiscsi_dev; 132 133 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n" 134 "sc = 0x%x, scbus = 0x%x\n", 135 USBDEVNAME(sc->sc_dev), sc, scbus)); 136 137 sc->sc_refcnt++; 138 scbus->base.sc_child = 139 config_found((struct device *)sc, &scbus->sc_link, scsiprint); 140 if (--sc->sc_refcnt < 0) 141 usb_detach_wakeup(USBDEV(sc->sc_dev)); 142 143 return (0); 144 } 145 #endif 146 147 struct umass_scsi_softc * 148 umass_scsi_setup(struct umass_softc *sc) 149 { 150 struct umass_scsi_softc *scbus; 151 152 scbus = malloc(sizeof(struct umass_scsi_softc), M_DEVBUF, M_WAITOK); 153 memset(&scbus->sc_link, 0, sizeof(struct scsi_link)); 154 memset(&scbus->sc_adapter, 0, sizeof(struct scsi_adapter)); 155 156 sc->bus = (struct umassbus_softc *)scbus; 157 158 /* Fill in the adapter. */ 159 scbus->sc_adapter.scsi_cmd = umass_scsi_cmd; 160 scbus->sc_adapter.scsi_minphys = umass_scsi_minphys; 161 162 /* Fill in the link. */ 163 scbus->sc_link.adapter_buswidth = 2; 164 scbus->sc_link.openings = 1; 165 scbus->sc_link.adapter = &scbus->sc_adapter; 166 scbus->sc_link.adapter_softc = sc; 167 scbus->sc_link.openings = 1; 168 scbus->sc_link.quirks |= PQUIRK_ONLYBIG | PQUIRK_NOMODESENSE | 169 sc->sc_busquirks; 170 171 return (scbus); 172 } 173 174 int 175 umass_scsi_cmd(struct scsi_xfer *xs) 176 { 177 struct scsi_link *sc_link = xs->sc_link; 178 struct umass_softc *sc = sc_link->adapter_softc; 179 struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; 180 181 struct scsi_generic *cmd, trcmd; 182 int cmdlen, dir, s; 183 184 #ifdef UMASS_DEBUG 185 microtime(&sc->tv); 186 #endif 187 188 memset(&trcmd, 0, sizeof(trcmd)); 189 190 DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL); 191 192 DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d " 193 "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", 194 USBDEVNAME(sc->sc_dev), sc->tv.tv_sec, sc->tv.tv_usec, 195 sc_link->target, sc_link->lun, xs, xs->cmd->opcode, 196 xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL)); 197 198 #if defined(USB_DEBUG) && defined(SCSIDEBUG) 199 if (umassdebug & UDMASS_SCSI) 200 show_scsi_xs(xs); 201 else if (umassdebug & ~UDMASS_CMD) 202 show_scsi_cmd(xs); 203 #endif 204 205 if (sc->sc_dying) { 206 xs->error = XS_DRIVER_STUFFUP; 207 goto done; 208 } 209 210 #if defined(UMASS_DEBUG) 211 if (sc_link->target != UMASS_SCSIID_DEVICE) { 212 DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", 213 USBDEVNAME(sc->sc_dev), sc_link->target)); 214 xs->error = XS_DRIVER_STUFFUP; 215 goto done; 216 } 217 #endif 218 219 cmd = xs->cmd; 220 cmdlen = xs->cmdlen; 221 222 if (cmd->opcode == MODE_SENSE && 223 (sc_link->quirks & SDEV_NOMODESENSE)) { 224 xs->error = XS_TIMEOUT; 225 goto done; 226 } 227 228 if (cmd->opcode == START_STOP && 229 (sc->sc_quirks & UMASS_QUIRK_NO_START_STOP)) { 230 xs->error = XS_NOERROR; 231 goto done; 232 } 233 234 if (cmd->opcode == INQUIRY && 235 (sc->sc_quirks & UMASS_QUIRK_FORCE_SHORT_INQUIRY)) { 236 /* 237 * Some drives wedge when asked for full inquiry 238 * information. 239 */ 240 memcpy(&trcmd, cmd, sizeof(trcmd)); 241 trcmd.bytes[4] = SHORT_INQUIRY_LENGTH; 242 cmd = &trcmd; 243 xs->datalen = SHORT_INQUIRY_LENGTH; 244 } 245 246 dir = DIR_NONE; 247 if (xs->datalen) { 248 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { 249 case SCSI_DATA_IN: 250 dir = DIR_IN; 251 break; 252 case SCSI_DATA_OUT: 253 dir = DIR_OUT; 254 break; 255 } 256 } 257 258 if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { 259 printf("umass_cmd: large datalen, %d\n", xs->datalen); 260 xs->error = XS_DRIVER_STUFFUP; 261 goto done; 262 } 263 264 if (xs->flags & SCSI_POLL) { 265 /* Use sync transfer. XXX Broken! */ 266 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); 267 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 268 scbus->sc_sync_status = USBD_INVAL; 269 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 270 xs->data, xs->datalen, dir, 271 xs->timeout, 0, xs); 272 sc->sc_xfer_flags = 0; 273 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", 274 scbus->sc_sync_status)); 275 switch (scbus->sc_sync_status) { 276 case USBD_NORMAL_COMPLETION: 277 xs->error = XS_NOERROR; 278 break; 279 case USBD_TIMEOUT: 280 xs->error = XS_TIMEOUT; 281 break; 282 default: 283 xs->error = XS_DRIVER_STUFFUP; 284 break; 285 } 286 goto done; 287 } else { 288 DPRINTF(UDMASS_SCSI, 289 ("umass_scsi_cmd: async dir=%d, cmdlen=%d" 290 " datalen=%d\n", 291 dir, cmdlen, xs->datalen)); 292 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 293 xs->data, xs->datalen, dir, 294 xs->timeout, umass_scsi_cb, xs); 295 return (SUCCESSFULLY_QUEUED); 296 } 297 298 /* Return if command finishes early. */ 299 done: 300 xs->flags |= ITSDONE; 301 302 s = splbio(); 303 scsi_done(xs); 304 splx(s); 305 if (xs->flags & SCSI_POLL) 306 return (COMPLETE); 307 else 308 return (SUCCESSFULLY_QUEUED); 309 } 310 311 void 312 umass_scsi_minphys(struct buf *bp) 313 { 314 if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) 315 bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; 316 317 minphys(bp); 318 } 319 320 void 321 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status) 322 { 323 struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; 324 struct scsi_xfer *xs = priv; 325 struct scsi_link *link = xs->sc_link; 326 int cmdlen; 327 int s; 328 #ifdef UMASS_DEBUG 329 struct timeval tv; 330 u_int delta; 331 microtime(&tv); 332 delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + 333 tv.tv_usec - sc->tv.tv_usec; 334 #endif 335 336 DPRINTF(UDMASS_CMD, 337 ("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" 338 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, 339 status)); 340 341 xs->resid = residue; 342 343 switch (status) { 344 case STATUS_CMD_OK: 345 xs->error = XS_NOERROR; 346 break; 347 348 case STATUS_CMD_UNKNOWN: 349 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n")); 350 /* we can't issue REQUEST SENSE */ 351 if (xs->sc_link->quirks & PQUIRK_NOSENSE) { 352 /* 353 * If no residue and no other USB error, 354 * command succeeded. 355 */ 356 if (residue == 0) { 357 xs->error = XS_NOERROR; 358 break; 359 } 360 361 /* 362 * Some devices return a short INQUIRY 363 * response, omitting response data from the 364 * "vendor specific data" on... 365 */ 366 if (xs->cmd->opcode == INQUIRY && 367 residue < xs->datalen) { 368 xs->error = XS_NOERROR; 369 break; 370 } 371 372 xs->error = XS_DRIVER_STUFFUP; 373 break; 374 } 375 /* FALLTHROUGH */ 376 case STATUS_CMD_FAILED: 377 printf("umass_scsi_cb: status cmd failed for scsi op 0x%02x\n", 378 xs->cmd->opcode); 379 /* fetch sense data */ 380 memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); 381 scbus->sc_sense_cmd.opcode = REQUEST_SENSE; 382 scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT; 383 scbus->sc_sense_cmd.length = sizeof(xs->sense); 384 385 cmdlen = sizeof(scbus->sc_sense_cmd); 386 if (sc->sc_cmd == UMASS_CPROTO_UFI) /* XXX */ 387 cmdlen = UFI_COMMAND_LENGTH; 388 sc->sc_methods->wire_xfer(sc, link->lun, 389 &scbus->sc_sense_cmd, cmdlen, 390 &xs->sense, sizeof(xs->sense), 391 DIR_IN, xs->timeout, 392 umass_scsi_sense_cb, xs); 393 return; 394 395 case STATUS_WIRE_FAILED: 396 xs->error = XS_RESET; 397 break; 398 399 default: 400 panic("%s: Unknown status %d in umass_scsi_cb", 401 USBDEVNAME(sc->sc_dev), status); 402 } 403 404 xs->flags |= ITSDONE; 405 406 DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, " 407 "status=0x%x resid=%d\n", 408 tv.tv_sec, tv.tv_usec, 409 xs->error, xs->status, xs->resid)); 410 411 s = splbio(); 412 scsi_done(xs); 413 splx(s); 414 } 415 416 /* 417 * Finalise a completed autosense operation 418 */ 419 void 420 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 421 int status) 422 { 423 struct scsi_xfer *xs = priv; 424 int s; 425 426 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d " 427 "status=%d\n", xs, residue, status)); 428 429 switch (status) { 430 case STATUS_CMD_OK: 431 case STATUS_CMD_UNKNOWN: 432 /* getting sense data succeeded */ 433 if (xs->cmd->opcode == INQUIRY && (xs->resid < xs->datalen || 434 (sc->sc_quirks & UMASS_QUIRK_RS_NO_CLEAR_UA /* XXX */))) { 435 /* 436 * Some drivers return SENSE errors even after INQUIRY. 437 * The upper layer doesn't like that. 438 */ 439 xs->error = XS_NOERROR; 440 break; 441 } 442 /* XXX look at residue */ 443 if (residue == 0 || residue == 14)/* XXX */ 444 xs->error = XS_SENSE; 445 else 446 xs->error = XS_SHORTSENSE; 447 break; 448 default: 449 DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", 450 USBDEVNAME(sc->sc_dev), status)); 451 xs->error = XS_DRIVER_STUFFUP; 452 break; 453 } 454 455 xs->flags |= ITSDONE; 456 457 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, " 458 "xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status, 459 xs->resid)); 460 461 s = splbio(); 462 scsi_done(xs); 463 splx(s); 464 } 465 466