1 /* $OpenBSD: umass_scsi.c,v 1.25 2009/07/02 18:50:37 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 "atapiscsi.h" 34 35 #include <sys/param.h> 36 #include <sys/systm.h> 37 #include <sys/kernel.h> 38 #include <sys/conf.h> 39 #include <sys/buf.h> 40 #include <sys/device.h> 41 #include <sys/ioctl.h> 42 #include <sys/malloc.h> 43 44 #include <dev/usb/usb.h> 45 #include <dev/usb/usbdi.h> 46 #include <dev/usb/usbdi_util.h> 47 #include <dev/usb/usbdevs.h> 48 49 #include <dev/usb/umassvar.h> 50 #include <dev/usb/umass_scsi.h> 51 52 #include <scsi/scsi_all.h> 53 #include <scsi/scsiconf.h> 54 #include <scsi/scsi_disk.h> 55 #include <machine/bus.h> 56 57 struct umass_scsi_softc { 58 struct umassbus_softc base; 59 struct scsi_link sc_link; 60 struct scsi_adapter sc_adapter; 61 62 struct scsi_sense sc_sense_cmd; 63 }; 64 65 66 #define UMASS_SCSIID_HOST 0x00 67 #define UMASS_SCSIID_DEVICE 0x01 68 69 #define UMASS_ATAPI_DRIVE 0 70 71 int umass_scsi_cmd(struct scsi_xfer *); 72 void umass_scsi_minphys(struct buf *, struct scsi_link *); 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 struct umass_scsi_softc *umass_scsi_setup(struct umass_softc *); 79 80 struct scsi_device umass_scsi_dev = { NULL, NULL, NULL, NULL, }; 81 82 #if NATAPISCSI > 0 83 struct scsi_device umass_atapiscsi_dev = { NULL, NULL, NULL, NULL, }; 84 #endif 85 86 int 87 umass_scsi_attach(struct umass_softc *sc) 88 { 89 struct scsibus_attach_args saa; 90 struct umass_scsi_softc *scbus; 91 92 scbus = umass_scsi_setup(sc); 93 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 94 scbus->sc_link.luns = sc->maxlun + 1; 95 scbus->sc_link.flags &= ~SDEV_ATAPI; 96 scbus->sc_link.flags |= SDEV_UMASS; 97 scbus->sc_link.device = &umass_scsi_dev; 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 #if NATAPISCSI > 0 116 int 117 umass_atapi_attach(struct umass_softc *sc) 118 { 119 struct scsibus_attach_args saa; 120 struct umass_scsi_softc *scbus; 121 122 scbus = umass_scsi_setup(sc); 123 scbus->sc_link.adapter_target = UMASS_SCSIID_HOST; 124 scbus->sc_link.luns = 1; 125 scbus->sc_link.openings = 1; 126 scbus->sc_link.flags |= SDEV_ATAPI; 127 scbus->sc_link.device = &umass_atapiscsi_dev; 128 129 bzero(&saa, sizeof(saa)); 130 saa.saa_sc_link = &scbus->sc_link; 131 132 DPRINTF(UDMASS_USB, ("%s: umass_attach_bus: ATAPI\n" 133 "sc = 0x%x, scbus = 0x%x\n", 134 sc->sc_dev.dv_xname, sc, scbus)); 135 136 sc->sc_refcnt++; 137 scbus->base.sc_child = config_found((struct device *)sc, 138 &saa, scsiprint); 139 if (--sc->sc_refcnt < 0) 140 usb_detach_wakeup(&sc->sc_dev); 141 142 return (0); 143 } 144 #endif 145 146 struct umass_scsi_softc * 147 umass_scsi_setup(struct umass_softc *sc) 148 { 149 struct umass_scsi_softc *scbus; 150 151 scbus = malloc(sizeof(*scbus), M_DEVBUF, M_WAITOK | M_ZERO); 152 153 sc->bus = (struct umassbus_softc *)scbus; 154 155 /* Fill in the adapter. */ 156 scbus->sc_adapter.scsi_cmd = umass_scsi_cmd; 157 scbus->sc_adapter.scsi_minphys = umass_scsi_minphys; 158 159 /* Fill in the link. */ 160 scbus->sc_link.adapter_buswidth = 2; 161 scbus->sc_link.adapter = &scbus->sc_adapter; 162 scbus->sc_link.adapter_softc = sc; 163 scbus->sc_link.openings = 1; 164 scbus->sc_link.quirks |= SDEV_ONLYBIG | sc->sc_busquirks; 165 166 return (scbus); 167 } 168 169 int 170 umass_scsi_cmd(struct scsi_xfer *xs) 171 { 172 struct scsi_link *sc_link = xs->sc_link; 173 struct umass_softc *sc = sc_link->adapter_softc; 174 175 struct scsi_generic *cmd; 176 int cmdlen, dir, rslt, s; 177 178 #ifdef UMASS_DEBUG 179 microtime(&sc->tv); 180 #endif 181 182 DIF(UDMASS_UPPER, sc_link->flags |= SCSIDEBUG_LEVEL); 183 184 DPRINTF(UDMASS_CMD, ("%s: umass_scsi_cmd: at %lu.%06lu: %d:%d " 185 "xs=%p cmd=0x%02x datalen=%d (quirks=0x%x, poll=%d)\n", 186 sc->sc_dev.dv_xname, sc->tv.tv_sec, sc->tv.tv_usec, 187 sc_link->target, sc_link->lun, xs, xs->cmd->opcode, 188 xs->datalen, sc_link->quirks, xs->flags & SCSI_POLL)); 189 190 if (sc->sc_dying) { 191 xs->error = XS_DRIVER_STUFFUP; 192 goto done; 193 } 194 195 #if defined(UMASS_DEBUG) 196 if (sc_link->target != UMASS_SCSIID_DEVICE) { 197 DPRINTF(UDMASS_SCSI, ("%s: wrong SCSI ID %d\n", 198 sc->sc_dev.dv_xname, sc_link->target)); 199 xs->error = XS_DRIVER_STUFFUP; 200 goto done; 201 } 202 #endif 203 204 cmd = xs->cmd; 205 cmdlen = xs->cmdlen; 206 207 dir = DIR_NONE; 208 if (xs->datalen) { 209 switch (xs->flags & (SCSI_DATA_IN | SCSI_DATA_OUT)) { 210 case SCSI_DATA_IN: 211 dir = DIR_IN; 212 break; 213 case SCSI_DATA_OUT: 214 dir = DIR_OUT; 215 break; 216 } 217 } 218 219 if (xs->datalen > UMASS_MAX_TRANSFER_SIZE) { 220 printf("umass_cmd: large datalen, %d\n", xs->datalen); 221 xs->error = XS_DRIVER_STUFFUP; 222 goto done; 223 } 224 225 if (xs->flags & SCSI_POLL) { 226 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: sync dir=%d\n", dir)); 227 usbd_set_polling(sc->sc_udev, 1); 228 sc->sc_xfer_flags = USBD_SYNCHRONOUS; 229 sc->polled_xfer_status = USBD_INVAL; 230 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 231 xs->data, xs->datalen, dir, 232 xs->timeout, umass_scsi_cb, xs); 233 sc->sc_xfer_flags = 0; 234 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done err=%d\n", 235 sc->polled_xfer_status)); 236 if (xs->error == XS_NOERROR) { 237 switch (sc->polled_xfer_status) { 238 case USBD_NORMAL_COMPLETION: 239 xs->error = XS_NOERROR; 240 break; 241 case USBD_TIMEOUT: 242 xs->error = XS_TIMEOUT; 243 break; 244 default: 245 xs->error = XS_DRIVER_STUFFUP; 246 break; 247 } 248 } 249 usbd_set_polling(sc->sc_udev, 0); 250 DPRINTF(UDMASS_SCSI, ("umass_scsi_cmd: done, error=%d\n", 251 xs->error)); 252 } else { 253 DPRINTF(UDMASS_SCSI, 254 ("umass_scsi_cmd: async dir=%d, cmdlen=%d" 255 " datalen=%d\n", 256 dir, cmdlen, xs->datalen)); 257 sc->sc_methods->wire_xfer(sc, sc_link->lun, cmd, cmdlen, 258 xs->data, xs->datalen, dir, 259 xs->timeout, umass_scsi_cb, xs); 260 return (SUCCESSFULLY_QUEUED); 261 } 262 263 /* Return if command finishes early. */ 264 done: 265 xs->flags |= ITSDONE; 266 if (xs->flags & SCSI_POLL) 267 rslt = COMPLETE; 268 else 269 rslt = SUCCESSFULLY_QUEUED; 270 271 s = splbio(); 272 scsi_done(xs); 273 splx(s); 274 275 return (rslt); 276 } 277 278 void 279 umass_scsi_minphys(struct buf *bp, struct scsi_link *sl) 280 { 281 if (bp->b_bcount > UMASS_MAX_TRANSFER_SIZE) 282 bp->b_bcount = UMASS_MAX_TRANSFER_SIZE; 283 284 minphys(bp); 285 } 286 287 void 288 umass_scsi_cb(struct umass_softc *sc, void *priv, int residue, int status) 289 { 290 struct umass_scsi_softc *scbus = (struct umass_scsi_softc *)sc->bus; 291 struct scsi_xfer *xs = priv; 292 struct scsi_link *link = xs->sc_link; 293 int cmdlen; 294 int s; 295 #ifdef UMASS_DEBUG 296 struct timeval tv; 297 u_int delta; 298 microtime(&tv); 299 delta = (tv.tv_sec - sc->tv.tv_sec) * 1000000 + 300 tv.tv_usec - sc->tv.tv_usec; 301 #endif 302 303 DPRINTF(UDMASS_CMD, 304 ("umass_scsi_cb: at %lu.%06lu, delta=%u: xs=%p residue=%d" 305 " status=%d\n", tv.tv_sec, tv.tv_usec, delta, xs, residue, 306 status)); 307 308 xs->resid = residue; 309 310 switch (status) { 311 case STATUS_CMD_OK: 312 xs->error = XS_NOERROR; 313 break; 314 315 case STATUS_CMD_UNKNOWN: 316 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd unknown\n")); 317 /* we can't issue REQUEST SENSE */ 318 if (xs->sc_link->quirks & ADEV_NOSENSE) { 319 /* 320 * If no residue and no other USB error, 321 * command succeeded. 322 */ 323 if (residue == 0) { 324 xs->error = XS_NOERROR; 325 break; 326 } 327 328 /* 329 * Some devices return a short INQUIRY 330 * response, omitting response data from the 331 * "vendor specific data" on... 332 */ 333 if (xs->cmd->opcode == INQUIRY && 334 residue < xs->datalen) { 335 xs->error = XS_NOERROR; 336 break; 337 } 338 339 xs->error = XS_DRIVER_STUFFUP; 340 break; 341 } 342 /* FALLTHROUGH */ 343 case STATUS_CMD_FAILED: 344 DPRINTF(UDMASS_CMD, ("umass_scsi_cb: status cmd failed for " 345 "scsi op 0x%02x\n", xs->cmd->opcode)); 346 /* fetch sense data */ 347 sc->sc_sense = 1; 348 memset(&scbus->sc_sense_cmd, 0, sizeof(scbus->sc_sense_cmd)); 349 scbus->sc_sense_cmd.opcode = REQUEST_SENSE; 350 scbus->sc_sense_cmd.byte2 = link->lun << SCSI_CMD_LUN_SHIFT; 351 scbus->sc_sense_cmd.length = sizeof(xs->sense); 352 353 cmdlen = sizeof(scbus->sc_sense_cmd); 354 sc->sc_methods->wire_xfer(sc, link->lun, 355 &scbus->sc_sense_cmd, cmdlen, 356 &xs->sense, sizeof(xs->sense), 357 DIR_IN, xs->timeout, 358 umass_scsi_sense_cb, xs); 359 return; 360 361 case STATUS_WIRE_FAILED: 362 xs->error = XS_RESET; 363 break; 364 365 default: 366 panic("%s: Unknown status %d in umass_scsi_cb", 367 sc->sc_dev.dv_xname, status); 368 } 369 370 if (xs->flags & SCSI_POLL) 371 return; 372 373 xs->flags |= ITSDONE; 374 375 DPRINTF(UDMASS_CMD,("umass_scsi_cb: at %lu.%06lu: return error=%d, " 376 "status=0x%x resid=%d\n", 377 tv.tv_sec, tv.tv_usec, 378 xs->error, xs->status, xs->resid)); 379 380 s = splbio(); 381 scsi_done(xs); 382 splx(s); 383 } 384 385 /* 386 * Finalise a completed autosense operation 387 */ 388 void 389 umass_scsi_sense_cb(struct umass_softc *sc, void *priv, int residue, 390 int status) 391 { 392 struct scsi_xfer *xs = priv; 393 int s; 394 395 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: xs=%p residue=%d " 396 "status=%d\n", xs, residue, status)); 397 398 sc->sc_sense = 0; 399 switch (status) { 400 case STATUS_CMD_OK: 401 case STATUS_CMD_UNKNOWN: 402 /* getting sense data succeeded */ 403 if (residue == 0 || residue == 14)/* XXX */ 404 xs->error = XS_SENSE; 405 else 406 xs->error = XS_SHORTSENSE; 407 break; 408 default: 409 DPRINTF(UDMASS_SCSI, ("%s: Autosense failed, status %d\n", 410 sc->sc_dev.dv_xname, status)); 411 xs->error = XS_DRIVER_STUFFUP; 412 break; 413 } 414 415 xs->flags |= ITSDONE; 416 417 DPRINTF(UDMASS_CMD,("umass_scsi_sense_cb: return xs->error=%d, " 418 "xs->flags=0x%x xs->resid=%d\n", xs->error, xs->status, 419 xs->resid)); 420 421 s = splbio(); 422 scsi_done(xs); 423 splx(s); 424 } 425 426