usb/uas: move UAS structs / defines into a header file
[linux-flexiantxendom0.git] / drivers / usb / storage / uas.c
index 1d10d5b..98d313c 100644 (file)
@@ -13,7 +13,9 @@
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/usb.h>
+#include <linux/usb/hcd.h>
 #include <linux/usb/storage.h>
+#include <linux/usb/uas.h>
 
 #include <scsi/scsi.h>
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_tcq.h>
 
-/* Common header for all IUs */
-struct iu {
-       __u8 iu_id;
-       __u8 rsvd1;
-       __be16 tag;
-};
-
-enum {
-       IU_ID_COMMAND           = 0x01,
-       IU_ID_STATUS            = 0x03,
-       IU_ID_RESPONSE          = 0x04,
-       IU_ID_TASK_MGMT         = 0x05,
-       IU_ID_READ_READY        = 0x06,
-       IU_ID_WRITE_READY       = 0x07,
-};
-
-struct command_iu {
-       __u8 iu_id;
-       __u8 rsvd1;
-       __be16 tag;
-       __u8 prio_attr;
-       __u8 rsvd5;
-       __u8 len;
-       __u8 rsvd7;
-       struct scsi_lun lun;
-       __u8 cdb[16];   /* XXX: Overflow-checking tools may misunderstand */
-};
-
-/*
- * Also used for the Read Ready and Write Ready IUs since they have the
- * same first four bytes
- */
-struct sense_iu {
-       __u8 iu_id;
-       __u8 rsvd1;
-       __be16 tag;
-       __be16 status_qual;
-       __u8 status;
-       __u8 rsvd7[7];
-       __be16 len;
-       __u8 sense[SCSI_SENSE_BUFFERSIZE];
-};
-
 /*
  * The r00-r01c specs define this version of the SENSE IU data structure.
  * It's still in use by several different firmware releases.
@@ -79,18 +38,6 @@ struct sense_iu_old {
        __u8 sense[SCSI_SENSE_BUFFERSIZE];
 };
 
-enum {
-       CMD_PIPE_ID             = 1,
-       STATUS_PIPE_ID          = 2,
-       DATA_IN_PIPE_ID         = 3,
-       DATA_OUT_PIPE_ID        = 4,
-
-       UAS_SIMPLE_TAG          = 0,
-       UAS_HEAD_TAG            = 1,
-       UAS_ORDERED_TAG         = 2,
-       UAS_ACA                 = 4,
-};
-
 struct uas_dev_info {
        struct usb_interface *intf;
        struct usb_device *udev;
@@ -98,6 +45,8 @@ struct uas_dev_info {
        unsigned cmd_pipe, status_pipe, data_in_pipe, data_out_pipe;
        unsigned use_streams:1;
        unsigned uas_sense_old:1;
+       struct scsi_cmnd *cmnd;
+       struct urb *status_urb; /* used only if stream support is available */
 };
 
 enum {
@@ -116,6 +65,7 @@ struct uas_cmd_info {
        unsigned int state;
        unsigned int stream;
        struct urb *cmd_urb;
+       /* status_urb is used only if stream support isn't available */
        struct urb *status_urb;
        struct urb *data_in_urb;
        struct urb *data_out_urb;
@@ -125,29 +75,38 @@ struct uas_cmd_info {
 /* I hate forward declarations, but I actually have a loop */
 static int uas_submit_urbs(struct scsi_cmnd *cmnd,
                                struct uas_dev_info *devinfo, gfp_t gfp);
+static void uas_do_work(struct work_struct *work);
 
+static DECLARE_WORK(uas_work, uas_do_work);
 static DEFINE_SPINLOCK(uas_work_lock);
 static LIST_HEAD(uas_work_list);
 
 static void uas_do_work(struct work_struct *work)
 {
        struct uas_cmd_info *cmdinfo;
+       struct uas_cmd_info *temp;
        struct list_head list;
+       int err;
 
        spin_lock_irq(&uas_work_lock);
        list_replace_init(&uas_work_list, &list);
        spin_unlock_irq(&uas_work_lock);
 
-       list_for_each_entry(cmdinfo, &list, list) {
+       list_for_each_entry_safe(cmdinfo, temp, &list, list) {
                struct scsi_pointer *scp = (void *)cmdinfo;
                struct scsi_cmnd *cmnd = container_of(scp,
                                                        struct scsi_cmnd, SCp);
-               uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
+               err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO);
+               if (err) {
+                       list_del(&cmdinfo->list);
+                       spin_lock_irq(&uas_work_lock);
+                       list_add_tail(&cmdinfo->list, &uas_work_list);
+                       spin_unlock_irq(&uas_work_lock);
+                       schedule_work(&uas_work);
+               }
        }
 }
 
-static DECLARE_WORK(uas_work, uas_do_work);
-
 static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
 {
        struct sense_iu *sense_iu = urb->transfer_buffer;
@@ -169,10 +128,7 @@ static void uas_sense(struct urb *urb, struct scsi_cmnd *cmnd)
        }
 
        cmnd->result = sense_iu->status;
-       if (sdev->current_cmnd)
-               sdev->current_cmnd = NULL;
        cmnd->scsi_done(cmnd);
-       usb_free_urb(urb);
 }
 
 static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
@@ -196,10 +152,7 @@ static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
        }
 
        cmnd->result = sense_iu->status;
-       if (sdev->current_cmnd)
-               sdev->current_cmnd = NULL;
        cmnd->scsi_done(cmnd);
-       usb_free_urb(urb);
 }
 
 static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
@@ -208,7 +161,7 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
        int err;
 
-       cmdinfo->state = direction | SUBMIT_STATUS_URB;
+       cmdinfo->state = direction;
        err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC);
        if (err) {
                spin_lock(&uas_work_lock);
@@ -221,27 +174,40 @@ static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
 static void uas_stat_cmplt(struct urb *urb)
 {
        struct iu *iu = urb->transfer_buffer;
-       struct scsi_device *sdev = urb->context;
-       struct uas_dev_info *devinfo = sdev->hostdata;
+       struct Scsi_Host *shost = urb->context;
+       struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
        struct scsi_cmnd *cmnd;
        u16 tag;
+       int ret;
 
        if (urb->status) {
                dev_err(&urb->dev->dev, "URB BAD STATUS %d\n", urb->status);
-               usb_free_urb(urb);
+               if (devinfo->use_streams)
+                       usb_free_urb(urb);
                return;
        }
 
        tag = be16_to_cpup(&iu->tag) - 1;
-       if (sdev->current_cmnd)
-               cmnd = sdev->current_cmnd;
+       if (tag == 0)
+               cmnd = devinfo->cmnd;
        else
-               cmnd = scsi_find_tag(sdev, tag);
-       if (!cmnd)
+               cmnd = scsi_host_find_tag(shost, tag - 1);
+       if (!cmnd) {
+               if (devinfo->use_streams) {
+                       usb_free_urb(urb);
+                       return;
+               }
+               ret = usb_submit_urb(urb, GFP_ATOMIC);
+               if (ret)
+                       dev_err(&urb->dev->dev, "failed submit status urb\n");
                return;
+       }
 
        switch (iu->iu_id) {
        case IU_ID_STATUS:
+               if (devinfo->cmnd == cmnd)
+                       devinfo->cmnd = NULL;
+
                if (urb->actual_length < 16)
                        devinfo->uas_sense_old = 1;
                if (devinfo->uas_sense_old)
@@ -259,6 +225,15 @@ static void uas_stat_cmplt(struct urb *urb)
                scmd_printk(KERN_ERR, cmnd,
                        "Bogus IU (%d) received on status pipe\n", iu->iu_id);
        }
+
+       if (devinfo->use_streams) {
+               usb_free_urb(urb);
+               return;
+       }
+
+       ret = usb_submit_urb(urb, GFP_ATOMIC);
+       if (ret)
+               dev_err(&urb->dev->dev, "failed submit status urb\n");
 }
 
 static void uas_data_cmplt(struct urb *urb)
@@ -289,7 +264,7 @@ static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
 }
 
 static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
-                                       struct scsi_cmnd *cmnd, u16 stream_id)
+               struct Scsi_Host *shost, u16 stream_id)
 {
        struct usb_device *udev = devinfo->udev;
        struct urb *urb = usb_alloc_urb(0, gfp);
@@ -303,7 +278,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp,
                goto free;
 
        usb_fill_bulk_urb(urb, udev, devinfo->status_pipe, iu, sizeof(*iu),
-                                               uas_stat_cmplt, cmnd->device);
+                                               uas_stat_cmplt, shost);
        urb->stream_id = stream_id;
        urb->transfer_flags |= URB_FREE_BUFFER;
  out:
@@ -334,7 +309,10 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp,
                goto free;
 
        iu->iu_id = IU_ID_COMMAND;
-       iu->tag = cpu_to_be16(stream_id);
+       if (blk_rq_tagged(cmnd->request))
+               iu->tag = cpu_to_be16(cmnd->request->tag + 2);
+       else
+               iu->tag = cpu_to_be16(1);
        iu->prio_attr = UAS_SIMPLE_TAG;
        iu->len = len;
        int_to_scsilun(sdev->lun, &iu->lun);
@@ -362,8 +340,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
        struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
 
        if (cmdinfo->state & ALLOC_STATUS_URB) {
-               cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd,
-                                                         cmdinfo->stream);
+               cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp,
+                               cmnd->device->host, cmdinfo->stream);
                if (!cmdinfo->status_urb)
                        return SCSI_MLQUEUE_DEVICE_BUSY;
                cmdinfo->state &= ~ALLOC_STATUS_URB;
@@ -444,13 +422,13 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
 
        BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer));
 
-       if (!cmdinfo->status_urb && sdev->current_cmnd)
+       if (devinfo->cmnd)
                return SCSI_MLQUEUE_DEVICE_BUSY;
 
        if (blk_rq_tagged(cmnd->request)) {
-               cmdinfo->stream = cmnd->request->tag + 1;
+               cmdinfo->stream = cmnd->request->tag + 2;
        } else {
-               sdev->current_cmnd = cmnd;
+               devinfo->cmnd = cmnd;
                cmdinfo->stream = 1;
        }
 
@@ -472,7 +450,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
        }
 
        if (!devinfo->use_streams) {
-               cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB);
+               cmdinfo->state &= ~(SUBMIT_DATA_IN_URB | SUBMIT_DATA_OUT_URB |
+                               ALLOC_STATUS_URB | SUBMIT_STATUS_URB);
                cmdinfo->stream = 0;
        }
 
@@ -551,7 +530,7 @@ static int uas_slave_configure(struct scsi_device *sdev)
 {
        struct uas_dev_info *devinfo = sdev->hostdata;
        scsi_set_tag_type(sdev, MSG_ORDERED_TAG);
-       scsi_activate_tcq(sdev, devinfo->qdepth - 1);
+       scsi_activate_tcq(sdev, devinfo->qdepth - 2);
        return 0;
 }
 
@@ -589,22 +568,34 @@ static int uas_is_interface(struct usb_host_interface *intf)
                intf->desc.bInterfaceProtocol == USB_PR_UAS);
 }
 
+static int uas_isnt_supported(struct usb_device *udev)
+{
+       struct usb_hcd *hcd = bus_to_hcd(udev->bus);
+
+       dev_warn(&udev->dev, "The driver for the USB controller %s does not "
+                       "support scatter-gather which is\n",
+                       hcd->driver->description);
+       dev_warn(&udev->dev, "required by the UAS driver. Please try an"
+                       "alternative USB controller if you wish to use UAS.\n");
+       return -ENODEV;
+}
+
 static int uas_switch_interface(struct usb_device *udev,
                                                struct usb_interface *intf)
 {
        int i;
-
-       if (uas_is_interface(intf->cur_altsetting))
-               return 0;
+       int sg_supported = udev->bus->sg_tablesize != 0;
 
        for (i = 0; i < intf->num_altsetting; i++) {
                struct usb_host_interface *alt = &intf->altsetting[i];
-               if (alt == intf->cur_altsetting)
-                       continue;
-               if (uas_is_interface(alt))
+
+               if (uas_is_interface(alt)) {
+                       if (!sg_supported)
+                               return uas_isnt_supported(udev);
                        return usb_set_interface(udev,
                                                alt->desc.bInterfaceNumber,
                                                alt->desc.bAlternateSetting);
+               }
        }
 
        return -ENODEV;
@@ -619,6 +610,7 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
        unsigned i, n_endpoints = intf->cur_altsetting->desc.bNumEndpoints;
 
        devinfo->uas_sense_old = 0;
+       devinfo->cmnd = NULL;
 
        for (i = 0; i < n_endpoints; i++) {
                unsigned char *extra = endpoint[i].extra;
@@ -670,6 +662,40 @@ static void uas_configure_endpoints(struct uas_dev_info *devinfo)
        }
 }
 
+static int uas_alloc_status_urb(struct uas_dev_info *devinfo,
+               struct Scsi_Host *shost)
+{
+       if (devinfo->use_streams) {
+               devinfo->status_urb = NULL;
+               return 0;
+       }
+
+       devinfo->status_urb = uas_alloc_sense_urb(devinfo, GFP_KERNEL,
+                       shost, 0);
+       if (!devinfo->status_urb)
+               goto err_s_urb;
+
+       if (usb_submit_urb(devinfo->status_urb, GFP_KERNEL))
+               goto err_submit_urb;
+
+       return 0;
+err_submit_urb:
+       usb_free_urb(devinfo->status_urb);
+err_s_urb:
+       return -ENOMEM;
+}
+
+static void uas_free_streams(struct uas_dev_info *devinfo)
+{
+       struct usb_device *udev = devinfo->udev;
+       struct usb_host_endpoint *eps[3];
+
+       eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe);
+       eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
+       eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
+       usb_free_streams(devinfo->intf, eps, 3, GFP_KERNEL);
+}
+
 /*
  * XXX: What I'd like to do here is register a SCSI host for each USB host in
  * the system.  Follow usb-storage's design of registering a SCSI host for
@@ -699,18 +725,33 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id)
        shost->max_id = 1;
        shost->sg_tablesize = udev->bus->sg_tablesize;
 
-       result = scsi_add_host(shost, &intf->dev);
+       devinfo->intf = intf;
+       devinfo->udev = udev;
+       uas_configure_endpoints(devinfo);
+
+       result = scsi_init_shared_tag_map(shost, devinfo->qdepth - 2);
        if (result)
                goto free;
+
+       result = scsi_add_host(shost, &intf->dev);
+       if (result)
+               goto deconfig_eps;
+
        shost->hostdata[0] = (unsigned long)devinfo;
 
-       devinfo->intf = intf;
-       devinfo->udev = udev;
-       uas_configure_endpoints(devinfo);
+       result = uas_alloc_status_urb(devinfo, shost);
+       if (result)
+               goto err_alloc_status;
 
        scsi_scan_host(shost);
        usb_set_intfdata(intf, shost);
        return result;
+
+err_alloc_status:
+       scsi_remove_host(shost);
+       shost = NULL;
+deconfig_eps:
+       uas_free_streams(devinfo);
  free:
        kfree(devinfo);
        if (shost)
@@ -732,18 +773,13 @@ static int uas_post_reset(struct usb_interface *intf)
 
 static void uas_disconnect(struct usb_interface *intf)
 {
-       struct usb_device *udev = interface_to_usbdev(intf);
-       struct usb_host_endpoint *eps[3];
        struct Scsi_Host *shost = usb_get_intfdata(intf);
        struct uas_dev_info *devinfo = (void *)shost->hostdata[0];
 
        scsi_remove_host(shost);
-
-       eps[0] = usb_pipe_endpoint(udev, devinfo->status_pipe);
-       eps[1] = usb_pipe_endpoint(udev, devinfo->data_in_pipe);
-       eps[2] = usb_pipe_endpoint(udev, devinfo->data_out_pipe);
-       usb_free_streams(intf, eps, 3, GFP_KERNEL);
-
+       usb_kill_urb(devinfo->status_urb);
+       usb_free_urb(devinfo->status_urb);
+       uas_free_streams(devinfo);
        kfree(devinfo);
 }