UAS bug fixes for 3.4.
This includes the merge of the uas_for_sarah signed tag from Sebastian Andrzej Siewior. These patches should be merged into usb-next, and queued for 3.4. The UAS driver error handling has been broken for over a year now, and the (future) changes that are needed to completely fix will be too big to go into stable, so there's no point in queueing this set for stable. -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.11 (GNU/Linux) iQIcBAABAgAGBQJPKxSsAAoJEBMGWMLi1Gc5648QAIzNx1O0eKYJNvWbcsc3hquJ BBnoQNqUuhrErZ4QGjoDhQd2juSlrsFixSujvm+157l+0fFz6EFq9RNDbT1luOj3 BIHMzHfSc5y5PZMGWG7RjA23dSU9h3rzJ//pq/CZaZQLVNnfcCe38LIsLobbI8gx lASMsA8BaFXvwdTSBI59uK59kasje43nbizStySm7z/pakAa76M8znB5m79lc5vY kQrH51G1w2CBAKwstWiRpQDMqzVBZ8/nCysJ8ILKX0hy1rQVfIbX5qzZmOlq854I KvskIWAXOiJUIOhwzqLVZd4hjHl8KDsFKVEkZTfEPi0JOoH02uUUpt8jQnpfNYYt qMLOUC0HEUOZgTzFeg8uxtsE3kkbIOn2P+nwHyG7UUNYpfuK0cSRVj0wfpbn9n96 BRqqGjhIDiSsY3nzdF67ZQBj1nLs8SP8Gm03tXk0yF8wM3HBt+UUHpxs3yCJGsOn 91uiejAkBzrGcq5zGNP7kSWNxSqhLzUrqDZZXXkh3t0lYVATvIbUl/4Mz7cc4kJA SyivKhwmcYvL0heDFY5HOFOn6L0LR3mlwT3vFc+QsXA8C8J/AgOS+NyhGeIalTes nv4dEfAob+Zb7b8sXsxQ46BV8hdSGPqRq0q0KZg9r2wtNWt8mOo0f0C0rhY+3CL+ 76hi0c0uUlpElkyLqGiy =lT7E -----END PGP SIGNATURE----- Merge tag 'for-uas-next-2012-02-02' of git://git.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-next UAS bug fixes for 3.4. This includes the merge of the uas_for_sarah signed tag from Sebastian Andrzej Siewior. These patches should be merged into usb-next, and queued for 3.4. The UAS driver error handling has been broken for over a year now, and the (future) changes that are needed to completely fix will be too big to go into stable, so there's no point in queueing this set for stable.
This commit is contained in:
commit
c39c654fa2
2 changed files with 282 additions and 117 deletions
|
@ -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>
|
||||
|
@ -22,49 +24,6 @@
|
|||
#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 {
|
||||
|
@ -109,6 +58,9 @@ enum {
|
|||
SUBMIT_DATA_OUT_URB = (1 << 5),
|
||||
ALLOC_CMD_URB = (1 << 6),
|
||||
SUBMIT_CMD_URB = (1 << 7),
|
||||
COMPLETED_DATA_IN = (1 << 8),
|
||||
COMPLETED_DATA_OUT = (1 << 9),
|
||||
DATA_COMPLETES_CMD = (1 << 10),
|
||||
};
|
||||
|
||||
/* Overrides scsi_pointer */
|
||||
|
@ -116,6 +68,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,33 +78,43 @@ 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;
|
||||
struct scsi_device *sdev = cmnd->device;
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
if (urb->actual_length > 16) {
|
||||
unsigned len = be16_to_cpup(&sense_iu->len);
|
||||
|
@ -169,16 +132,15 @@ 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);
|
||||
if (!(cmdinfo->state & DATA_COMPLETES_CMD))
|
||||
cmnd->scsi_done(cmnd);
|
||||
}
|
||||
|
||||
static void uas_sense_old(struct urb *urb, struct scsi_cmnd *cmnd)
|
||||
{
|
||||
struct sense_iu_old *sense_iu = urb->transfer_buffer;
|
||||
struct scsi_device *sdev = cmnd->device;
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
if (urb->actual_length > 8) {
|
||||
unsigned len = be16_to_cpup(&sense_iu->len) - 2;
|
||||
|
@ -196,10 +158,8 @@ 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);
|
||||
if (!(cmdinfo->state & DATA_COMPLETES_CMD))
|
||||
cmnd->scsi_done(cmnd);
|
||||
}
|
||||
|
||||
static void uas_xfer_data(struct urb *urb, struct scsi_cmnd *cmnd,
|
||||
|
@ -208,7 +168,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 +181,61 @@ 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;
|
||||
struct uas_cmd_info *cmdinfo;
|
||||
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;
|
||||
}
|
||||
cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
switch (iu->iu_id) {
|
||||
case IU_ID_STATUS:
|
||||
if (devinfo->cmnd == cmnd)
|
||||
devinfo->cmnd = NULL;
|
||||
|
||||
if (!(cmdinfo->state & COMPLETED_DATA_IN) &&
|
||||
cmdinfo->data_in_urb) {
|
||||
if (devinfo->use_streams) {
|
||||
cmdinfo->state |= DATA_COMPLETES_CMD;
|
||||
usb_unlink_urb(cmdinfo->data_in_urb);
|
||||
} else {
|
||||
usb_free_urb(cmdinfo->data_in_urb);
|
||||
}
|
||||
}
|
||||
if (!(cmdinfo->state & COMPLETED_DATA_OUT) &&
|
||||
cmdinfo->data_out_urb) {
|
||||
if (devinfo->use_streams) {
|
||||
cmdinfo->state |= DATA_COMPLETES_CMD;
|
||||
usb_unlink_urb(cmdinfo->data_in_urb);
|
||||
} else {
|
||||
usb_free_urb(cmdinfo->data_out_urb);
|
||||
}
|
||||
}
|
||||
|
||||
if (urb->actual_length < 16)
|
||||
devinfo->uas_sense_old = 1;
|
||||
if (devinfo->uas_sense_old)
|
||||
|
@ -259,29 +253,70 @@ 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)
|
||||
static void uas_data_out_cmplt(struct urb *urb)
|
||||
{
|
||||
struct scsi_data_buffer *sdb = urb->context;
|
||||
struct scsi_cmnd *cmnd = urb->context;
|
||||
struct scsi_data_buffer *sdb = scsi_out(cmnd);
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
cmdinfo->state |= COMPLETED_DATA_OUT;
|
||||
|
||||
sdb->resid = sdb->length - urb->actual_length;
|
||||
usb_free_urb(urb);
|
||||
|
||||
if (cmdinfo->state & DATA_COMPLETES_CMD)
|
||||
cmnd->scsi_done(cmnd);
|
||||
}
|
||||
|
||||
static void uas_data_in_cmplt(struct urb *urb)
|
||||
{
|
||||
struct scsi_cmnd *cmnd = urb->context;
|
||||
struct scsi_data_buffer *sdb = scsi_in(cmnd);
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
cmdinfo->state |= COMPLETED_DATA_IN;
|
||||
|
||||
sdb->resid = sdb->length - urb->actual_length;
|
||||
usb_free_urb(urb);
|
||||
|
||||
if (cmdinfo->state & DATA_COMPLETES_CMD)
|
||||
cmnd->scsi_done(cmnd);
|
||||
}
|
||||
|
||||
static struct urb *uas_alloc_data_urb(struct uas_dev_info *devinfo, gfp_t gfp,
|
||||
unsigned int pipe, u16 stream_id,
|
||||
struct scsi_data_buffer *sdb,
|
||||
enum dma_data_direction dir)
|
||||
unsigned int pipe, struct scsi_cmnd *cmnd,
|
||||
enum dma_data_direction dir)
|
||||
{
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
struct usb_device *udev = devinfo->udev;
|
||||
struct urb *urb = usb_alloc_urb(0, gfp);
|
||||
struct scsi_data_buffer *sdb;
|
||||
usb_complete_t complete_fn;
|
||||
u16 stream_id = cmdinfo->stream;
|
||||
|
||||
if (!urb)
|
||||
goto out;
|
||||
usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length, uas_data_cmplt,
|
||||
sdb);
|
||||
if (devinfo->use_streams)
|
||||
urb->stream_id = stream_id;
|
||||
if (dir == DMA_FROM_DEVICE) {
|
||||
sdb = scsi_in(cmnd);
|
||||
complete_fn = uas_data_in_cmplt;
|
||||
} else {
|
||||
sdb = scsi_out(cmnd);
|
||||
complete_fn = uas_data_out_cmplt;
|
||||
}
|
||||
usb_fill_bulk_urb(urb, udev, pipe, NULL, sdb->length,
|
||||
complete_fn, cmnd);
|
||||
urb->stream_id = stream_id;
|
||||
urb->num_sgs = udev->bus->sg_tablesize ? sdb->table.nents : 0;
|
||||
urb->sg = sdb->table.sgl;
|
||||
out:
|
||||
|
@ -289,7 +324,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 +338,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 +369,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 +400,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;
|
||||
|
@ -380,8 +418,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
|||
|
||||
if (cmdinfo->state & ALLOC_DATA_IN_URB) {
|
||||
cmdinfo->data_in_urb = uas_alloc_data_urb(devinfo, gfp,
|
||||
devinfo->data_in_pipe, cmdinfo->stream,
|
||||
scsi_in(cmnd), DMA_FROM_DEVICE);
|
||||
devinfo->data_in_pipe, cmnd,
|
||||
DMA_FROM_DEVICE);
|
||||
if (!cmdinfo->data_in_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
cmdinfo->state &= ~ALLOC_DATA_IN_URB;
|
||||
|
@ -398,8 +436,8 @@ static int uas_submit_urbs(struct scsi_cmnd *cmnd,
|
|||
|
||||
if (cmdinfo->state & ALLOC_DATA_OUT_URB) {
|
||||
cmdinfo->data_out_urb = uas_alloc_data_urb(devinfo, gfp,
|
||||
devinfo->data_out_pipe, cmdinfo->stream,
|
||||
scsi_out(cmnd), DMA_TO_DEVICE);
|
||||
devinfo->data_out_pipe, cmnd,
|
||||
DMA_TO_DEVICE);
|
||||
if (!cmdinfo->data_out_urb)
|
||||
return SCSI_MLQUEUE_DEVICE_BUSY;
|
||||
cmdinfo->state &= ~ALLOC_DATA_OUT_URB;
|
||||
|
@ -444,13 +482,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 +510,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 +590,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 +628,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 +670,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 +722,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 +785,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);
|
||||
if (result)
|
||||
goto free;
|
||||
shost->hostdata[0] = (unsigned long)devinfo;
|
||||
|
||||
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;
|
||||
|
||||
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 +833,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);
|
||||
}
|
||||
|
||||
|
|
69
include/linux/usb/uas.h
Normal file
69
include/linux/usb/uas.h
Normal file
|
@ -0,0 +1,69 @@
|
|||
#ifndef __USB_UAS_H__
|
||||
#define __USB_UAS_H__
|
||||
|
||||
#include <scsi/scsi.h>
|
||||
#include <scsi/scsi_cmnd.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];
|
||||
};
|
||||
|
||||
struct usb_pipe_usage_descriptor {
|
||||
__u8 bLength;
|
||||
__u8 bDescriptorType;
|
||||
|
||||
__u8 bPipeID;
|
||||
__u8 Reserved;
|
||||
} __attribute__((__packed__));
|
||||
|
||||
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,
|
||||
};
|
||||
#endif
|
Loading…
Add table
Reference in a new issue