#include <linux/blkdev.h>
#include <linux/slab.h>
#include <linux/types.h>
+#include <linux/module.h>
#include <linux/usb.h>
#include <linux/usb/storage.h>
/* 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_KERNEL);
+ 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;
goto free;
iu->iu_id = IU_ID_COMMAND;
- iu->tag = cpu_to_be16(stream_id);
- if (sdev->ordered_tags && (cmnd->request->cmd_flags & REQ_HARDBARRIER))
- iu->prio_attr = UAS_ORDERED_TAG;
+ if (blk_rq_tagged(cmnd->request))
+ iu->tag = cpu_to_be16(cmnd->request->tag + 1);
else
- iu->prio_attr = UAS_SIMPLE_TAG;
+ iu->tag = cpu_to_be16(1);
+ iu->prio_attr = UAS_SIMPLE_TAG;
iu->len = len;
int_to_scsilun(sdev->lun, &iu->lun);
memcpy(iu->cdb, cmnd->cmnd, cmnd->cmd_len);
return 0;
}
-static int uas_queuecommand(struct scsi_cmnd *cmnd,
+static int uas_queuecommand_lck(struct scsi_cmnd *cmnd,
void (*done)(struct scsi_cmnd *))
{
struct scsi_device *sdev = cmnd->device;
return 0;
}
+static DEF_SCSI_QCMD(uas_queuecommand)
+
static int uas_eh_abort_handler(struct scsi_cmnd *cmnd)
{
struct scsi_device *sdev = cmnd->device;
};
MODULE_DEVICE_TABLE(usb, uas_usb_ids);
+static int uas_is_interface(struct usb_host_interface *intf)
+{
+ return (intf->desc.bInterfaceClass == USB_CLASS_MASS_STORAGE &&
+ intf->desc.bInterfaceSubClass == USB_SC_SCSI &&
+ intf->desc.bInterfaceProtocol == USB_PR_UAS);
+}
+
+static int uas_switch_interface(struct usb_device *udev,
+ struct usb_interface *intf)
+{
+ int i;
+
+ if (uas_is_interface(intf->cur_altsetting))
+ return 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))
+ return usb_set_interface(udev,
+ alt->desc.bInterfaceNumber,
+ alt->desc.bAlternateSetting);
+ }
+
+ return -ENODEV;
+}
+
static void uas_configure_endpoints(struct uas_dev_info *devinfo)
{
struct usb_host_endpoint *eps[4] = { };
struct uas_dev_info *devinfo;
struct usb_device *udev = interface_to_usbdev(intf);
- if (id->bInterfaceProtocol == 0x50) {
- int ifnum = intf->cur_altsetting->desc.bInterfaceNumber;
-/* XXX: Shouldn't assume that 1 is the alternative we want */
- int ret = usb_set_interface(udev, ifnum, 1);
- if (ret)
- return -ENODEV;
- }
+ if (uas_switch_interface(udev, intf))
+ return -ENODEV;
devinfo = kmalloc(sizeof(struct uas_dev_info), GFP_KERNEL);
if (!devinfo)