summaryrefslogtreecommitdiffstats
path: root/drivers/usb/wusbcore/wa-xfer.c
diff options
context:
space:
mode:
authorThomas Pugliese <thomas.pugliese@gmail.com>2013-10-23 14:44:28 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-10-29 16:44:49 -0700
commitf07ddb9ef5c25c1044ab5b6509241320fb5e831a (patch)
tree6f7bb348ad03cc4e7e52634de48deaeecd468ae2 /drivers/usb/wusbcore/wa-xfer.c
parent2101242cef04991a580c3debb46972dbfaaaf138 (diff)
downloadlinux-f07ddb9ef5c25c1044ab5b6509241320fb5e831a.tar.bz2
usb: wusbcore: add a quirk for Alereon HWA device isoc behavior
Add a quirk for Alereon HWA devices to concatenate the frames of isoc transfer requests. Signed-off-by: Thomas Pugliese <thomas.pugliese@gmail.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/wusbcore/wa-xfer.c')
-rw-r--r--drivers/usb/wusbcore/wa-xfer.c49
1 files changed, 37 insertions, 12 deletions
diff --git a/drivers/usb/wusbcore/wa-xfer.c b/drivers/usb/wusbcore/wa-xfer.c
index 9325d27453c4..090ac308c756 100644
--- a/drivers/usb/wusbcore/wa-xfer.c
+++ b/drivers/usb/wusbcore/wa-xfer.c
@@ -479,13 +479,29 @@ static int __wa_seg_calculate_isoc_frame_count(struct wa_xfer *xfer,
{
int segment_size = 0, frame_count = 0;
int index = isoc_frame_offset;
+ struct usb_iso_packet_descriptor *iso_frame_desc =
+ xfer->urb->iso_frame_desc;
while ((index < xfer->urb->number_of_packets)
- && ((segment_size + xfer->urb->iso_frame_desc[index].length)
+ && ((segment_size + iso_frame_desc[index].length)
<= xfer->seg_size)) {
+ /*
+ * For Alereon HWA devices, only include an isoc frame in a
+ * segment if it is physically contiguous with the previous
+ * frame. This is required because those devices expect
+ * the isoc frames to be sent as a single USB transaction as
+ * opposed to one transaction per frame with standard HWA.
+ */
+ if ((xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+ && (index > isoc_frame_offset)
+ && ((iso_frame_desc[index - 1].offset +
+ iso_frame_desc[index - 1].length) !=
+ iso_frame_desc[index].offset))
+ break;
+
/* this frame fits. count it. */
++frame_count;
- segment_size += xfer->urb->iso_frame_desc[index].length;
+ segment_size += iso_frame_desc[index].length;
/* move to the next isoc frame. */
++index;
@@ -681,7 +697,11 @@ static void wa_seg_dto_cb(struct urb *urb)
wa = xfer->wa;
dev = &wa->usb_iface->dev;
if (usb_pipeisoc(xfer->urb->pipe)) {
- xfer->dto_isoc_frame_index += 1;
+ /* Alereon HWA sends all isoc frames in a single transfer. */
+ if (wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+ xfer->dto_isoc_frame_index += seg->isoc_frame_count;
+ else
+ xfer->dto_isoc_frame_index += 1;
if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) {
data_send_done = 0;
holding_dto = 1; /* checked in error cases. */
@@ -1007,17 +1027,18 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg,
static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
struct wa_seg *seg, int curr_iso_frame)
{
- /*
- * dto urb buffer address and size pulled from
- * iso_frame_desc.
- */
- seg->dto_urb->transfer_dma = xfer->urb->transfer_dma +
- xfer->urb->iso_frame_desc[curr_iso_frame].offset;
seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
seg->dto_urb->sg = NULL;
seg->dto_urb->num_sgs = 0;
- seg->dto_urb->transfer_buffer_length =
- xfer->urb->iso_frame_desc[curr_iso_frame].length;
+ /* dto urb buffer address pulled from iso_frame_desc. */
+ seg->dto_urb->transfer_dma = xfer->urb->transfer_dma +
+ xfer->urb->iso_frame_desc[curr_iso_frame].offset;
+ /* The Alereon HWA sends a single URB with all isoc segs. */
+ if (xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC)
+ seg->dto_urb->transfer_buffer_length = seg->isoc_size;
+ else
+ seg->dto_urb->transfer_buffer_length =
+ xfer->urb->iso_frame_desc[curr_iso_frame].length;
}
/*
@@ -1298,6 +1319,8 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
}
/* submit the isoc packet descriptor if present. */
if (seg->isoc_pack_desc_urb) {
+ struct wahc *wa = xfer->wa;
+
result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
if (result < 0) {
pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
@@ -1308,8 +1331,10 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer,
/*
* If this segment contains more than one isoc frame, hold
* onto the dto resource until we send all frames.
+ * Only applies to non-Alereon devices.
*/
- if (seg->isoc_frame_count > 1)
+ if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0)
+ && (seg->isoc_frame_count > 1))
*dto_done = 0;
}
/* submit the out data if this is an out request. */