root/dev/usb/umass_scsi.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. umass_scsi_attach
  2. umass_atapi_attach
  3. umass_scsi_setup
  4. umass_scsi_cmd
  5. umass_scsi_minphys
  6. umass_scsi_cb
  7. umass_scsi_sense_cb

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

/* [<][>][^][v][top][bottom][index][help] */