diff options
Diffstat (limited to 'drivers/usb/storage/uas.c')
-rw-r--r-- | drivers/usb/storage/uas.c | 82 |
1 files changed, 54 insertions, 28 deletions
diff --git a/drivers/usb/storage/uas.c b/drivers/usb/storage/uas.c index 339fac3949d..23f0dd9c36d 100644 --- a/drivers/usb/storage/uas.c +++ b/drivers/usb/storage/uas.c @@ -49,14 +49,17 @@ struct command_iu { __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 service_response; - __u8 rsvd8[6]; + __u8 rsvd7[7]; __be16 len; __u8 sense[SCSI_SENSE_BUFFERSIZE]; }; @@ -97,8 +100,8 @@ struct uas_dev_info { }; enum { - ALLOC_SENSE_URB = (1 << 0), - SUBMIT_SENSE_URB = (1 << 1), + ALLOC_STATUS_URB = (1 << 0), + SUBMIT_STATUS_URB = (1 << 1), ALLOC_DATA_IN_URB = (1 << 2), SUBMIT_DATA_IN_URB = (1 << 3), ALLOC_DATA_OUT_URB = (1 << 4), @@ -112,7 +115,7 @@ struct uas_cmd_info { unsigned int state; unsigned int stream; struct urb *cmd_urb; - struct urb *sense_urb; + struct urb *status_urb; struct urb *data_in_urb; struct urb *data_out_urb; struct list_head list; @@ -138,7 +141,7 @@ static void uas_do_work(struct work_struct *work) 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); + uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_NOIO); } } @@ -204,7 +207,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_SENSE_URB; + cmdinfo->state = direction | SUBMIT_STATUS_URB; err = uas_submit_urbs(cmnd, cmnd->device->hostdata, GFP_ATOMIC); if (err) { spin_lock(&uas_work_lock); @@ -294,7 +297,7 @@ static struct urb *uas_alloc_sense_urb(struct uas_dev_info *devinfo, gfp_t gfp, if (!urb) goto out; - iu = kmalloc(sizeof(*iu), gfp); + iu = kzalloc(sizeof(*iu), gfp); if (!iu) goto free; @@ -325,7 +328,7 @@ static struct urb *uas_alloc_cmd_urb(struct uas_dev_info *devinfo, gfp_t gfp, if (len < 0) len = 0; len = ALIGN(len, 4); - iu = kmalloc(sizeof(*iu) + len, gfp); + iu = kzalloc(sizeof(*iu) + len, gfp); if (!iu) goto free; @@ -357,21 +360,21 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd, { struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp; - if (cmdinfo->state & ALLOC_SENSE_URB) { - cmdinfo->sense_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd, - cmdinfo->stream); - if (!cmdinfo->sense_urb) + if (cmdinfo->state & ALLOC_STATUS_URB) { + cmdinfo->status_urb = uas_alloc_sense_urb(devinfo, gfp, cmnd, + cmdinfo->stream); + if (!cmdinfo->status_urb) return SCSI_MLQUEUE_DEVICE_BUSY; - cmdinfo->state &= ~ALLOC_SENSE_URB; + cmdinfo->state &= ~ALLOC_STATUS_URB; } - if (cmdinfo->state & SUBMIT_SENSE_URB) { - if (usb_submit_urb(cmdinfo->sense_urb, gfp)) { + if (cmdinfo->state & SUBMIT_STATUS_URB) { + if (usb_submit_urb(cmdinfo->status_urb, gfp)) { scmd_printk(KERN_INFO, cmnd, "sense urb submission failure\n"); return SCSI_MLQUEUE_DEVICE_BUSY; } - cmdinfo->state &= ~SUBMIT_SENSE_URB; + cmdinfo->state &= ~SUBMIT_STATUS_URB; } if (cmdinfo->state & ALLOC_DATA_IN_URB) { @@ -440,7 +443,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, BUILD_BUG_ON(sizeof(struct uas_cmd_info) > sizeof(struct scsi_pointer)); - if (!cmdinfo->sense_urb && sdev->current_cmnd) + if (!cmdinfo->status_urb && sdev->current_cmnd) return SCSI_MLQUEUE_DEVICE_BUSY; if (blk_rq_tagged(cmnd->request)) { @@ -452,7 +455,7 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, cmnd->scsi_done = done; - cmdinfo->state = ALLOC_SENSE_URB | SUBMIT_SENSE_URB | + cmdinfo->state = ALLOC_STATUS_URB | SUBMIT_STATUS_URB | ALLOC_CMD_URB | SUBMIT_CMD_URB; switch (cmnd->sc_data_direction) { @@ -475,8 +478,8 @@ static int uas_queuecommand_lck(struct scsi_cmnd *cmnd, err = uas_submit_urbs(cmnd, devinfo, GFP_ATOMIC); if (err) { /* If we did nothing, give up now */ - if (cmdinfo->state & SUBMIT_SENSE_URB) { - usb_free_urb(cmdinfo->sense_urb); + if (cmdinfo->state & SUBMIT_STATUS_URB) { + usb_free_urb(cmdinfo->status_urb); return SCSI_MLQUEUE_DEVICE_BUSY; } spin_lock(&uas_work_lock); @@ -578,6 +581,34 @@ static struct usb_device_id uas_usb_ids[] = { }; 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] = { }; @@ -651,13 +682,8 @@ static int uas_probe(struct usb_interface *intf, const struct usb_device_id *id) 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) |