diff options
author | Thomas Pugliese <thomas.pugliese@gmail.com> | 2013-10-23 15:44:28 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-10-29 19:44:49 -0400 |
commit | f07ddb9ef5c25c1044ab5b6509241320fb5e831a (patch) | |
tree | 6f7bb348ad03cc4e7e52634de48deaeecd468ae2 | |
parent | 2101242cef04991a580c3debb46972dbfaaaf138 (diff) |
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>
-rw-r--r-- | drivers/usb/host/hwa-hc.c | 13 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wa-hc.c | 4 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wa-hc.h | 13 | ||||
-rw-r--r-- | drivers/usb/wusbcore/wa-xfer.c | 49 |
4 files changed, 62 insertions, 17 deletions
diff --git a/drivers/usb/host/hwa-hc.c b/drivers/usb/host/hwa-hc.c index e5fb3cfd57a9..ada0a52797b1 100644 --- a/drivers/usb/host/hwa-hc.c +++ b/drivers/usb/host/hwa-hc.c | |||
@@ -679,7 +679,8 @@ static void hwahc_security_release(struct hwahc *hwahc) | |||
679 | /* nothing to do here so far... */ | 679 | /* nothing to do here so far... */ |
680 | } | 680 | } |
681 | 681 | ||
682 | static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) | 682 | static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface, |
683 | kernel_ulong_t quirks) | ||
683 | { | 684 | { |
684 | int result; | 685 | int result; |
685 | struct device *dev = &iface->dev; | 686 | struct device *dev = &iface->dev; |
@@ -724,7 +725,7 @@ static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface) | |||
724 | dev_err(dev, "Can't create WUSB HC structures: %d\n", result); | 725 | dev_err(dev, "Can't create WUSB HC structures: %d\n", result); |
725 | goto error_wusbhc_create; | 726 | goto error_wusbhc_create; |
726 | } | 727 | } |
727 | result = wa_create(&hwahc->wa, iface); | 728 | result = wa_create(&hwahc->wa, iface, quirks); |
728 | if (result < 0) | 729 | if (result < 0) |
729 | goto error_wa_create; | 730 | goto error_wa_create; |
730 | return 0; | 731 | return 0; |
@@ -780,7 +781,7 @@ static int hwahc_probe(struct usb_interface *usb_iface, | |||
780 | wusbhc = usb_hcd_to_wusbhc(usb_hcd); | 781 | wusbhc = usb_hcd_to_wusbhc(usb_hcd); |
781 | hwahc = container_of(wusbhc, struct hwahc, wusbhc); | 782 | hwahc = container_of(wusbhc, struct hwahc, wusbhc); |
782 | hwahc_init(hwahc); | 783 | hwahc_init(hwahc); |
783 | result = hwahc_create(hwahc, usb_iface); | 784 | result = hwahc_create(hwahc, usb_iface, id->driver_info); |
784 | if (result < 0) { | 785 | if (result < 0) { |
785 | dev_err(dev, "Cannot initialize internals: %d\n", result); | 786 | dev_err(dev, "Cannot initialize internals: %d\n", result); |
786 | goto error_hwahc_create; | 787 | goto error_hwahc_create; |
@@ -824,6 +825,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface) | |||
824 | } | 825 | } |
825 | 826 | ||
826 | static struct usb_device_id hwahc_id_table[] = { | 827 | static struct usb_device_id hwahc_id_table[] = { |
828 | /* Alereon 5310 */ | ||
829 | { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01), | ||
830 | .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, | ||
831 | /* Alereon 5611 */ | ||
832 | { USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01), | ||
833 | .driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC }, | ||
827 | /* FIXME: use class labels for this */ | 834 | /* FIXME: use class labels for this */ |
828 | { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, | 835 | { USB_INTERFACE_INFO(0xe0, 0x02, 0x01), }, |
829 | {}, | 836 | {}, |
diff --git a/drivers/usb/wusbcore/wa-hc.c b/drivers/usb/wusbcore/wa-hc.c index 6c09b0e4672b..368360f9a93a 100644 --- a/drivers/usb/wusbcore/wa-hc.c +++ b/drivers/usb/wusbcore/wa-hc.c | |||
@@ -33,7 +33,8 @@ | |||
33 | * wa->usb_dev and wa->usb_iface initialized and refcounted, | 33 | * wa->usb_dev and wa->usb_iface initialized and refcounted, |
34 | * wa->wa_descr initialized. | 34 | * wa->wa_descr initialized. |
35 | */ | 35 | */ |
36 | int wa_create(struct wahc *wa, struct usb_interface *iface) | 36 | int wa_create(struct wahc *wa, struct usb_interface *iface, |
37 | kernel_ulong_t quirks) | ||
37 | { | 38 | { |
38 | int result; | 39 | int result; |
39 | struct device *dev = &iface->dev; | 40 | struct device *dev = &iface->dev; |
@@ -41,6 +42,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface) | |||
41 | result = wa_rpipes_create(wa); | 42 | result = wa_rpipes_create(wa); |
42 | if (result < 0) | 43 | if (result < 0) |
43 | goto error_rpipes_create; | 44 | goto error_rpipes_create; |
45 | wa->quirks = quirks; | ||
44 | /* Fill up Data Transfer EP pointers */ | 46 | /* Fill up Data Transfer EP pointers */ |
45 | wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; | 47 | wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc; |
46 | wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; | 48 | wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc; |
diff --git a/drivers/usb/wusbcore/wa-hc.h b/drivers/usb/wusbcore/wa-hc.h index 41afaa6d01d2..e614f02f0cf2 100644 --- a/drivers/usb/wusbcore/wa-hc.h +++ b/drivers/usb/wusbcore/wa-hc.h | |||
@@ -128,6 +128,14 @@ enum wa_dti_state { | |||
128 | WA_DTI_ISOC_PACKET_STATUS_PENDING | 128 | WA_DTI_ISOC_PACKET_STATUS_PENDING |
129 | }; | 129 | }; |
130 | 130 | ||
131 | enum wa_quirks { | ||
132 | /* | ||
133 | * The Alereon HWA expects the data frames in isochronous transfer | ||
134 | * requests to be concatenated and not sent as separate packets. | ||
135 | */ | ||
136 | WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01, | ||
137 | }; | ||
138 | |||
131 | /** | 139 | /** |
132 | * Instance of a HWA Host Controller | 140 | * Instance of a HWA Host Controller |
133 | * | 141 | * |
@@ -218,10 +226,13 @@ struct wahc { | |||
218 | struct work_struct xfer_enqueue_work; | 226 | struct work_struct xfer_enqueue_work; |
219 | struct work_struct xfer_error_work; | 227 | struct work_struct xfer_error_work; |
220 | atomic_t xfer_id_count; | 228 | atomic_t xfer_id_count; |
229 | |||
230 | kernel_ulong_t quirks; | ||
221 | }; | 231 | }; |
222 | 232 | ||
223 | 233 | ||
224 | extern int wa_create(struct wahc *wa, struct usb_interface *iface); | 234 | extern int wa_create(struct wahc *wa, struct usb_interface *iface, |
235 | kernel_ulong_t); | ||
225 | extern void __wa_destroy(struct wahc *wa); | 236 | extern void __wa_destroy(struct wahc *wa); |
226 | void wa_reset_all(struct wahc *wa); | 237 | void wa_reset_all(struct wahc *wa); |
227 | 238 | ||
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, | |||
479 | { | 479 | { |
480 | int segment_size = 0, frame_count = 0; | 480 | int segment_size = 0, frame_count = 0; |
481 | int index = isoc_frame_offset; | 481 | int index = isoc_frame_offset; |
482 | struct usb_iso_packet_descriptor *iso_frame_desc = | ||
483 | xfer->urb->iso_frame_desc; | ||
482 | 484 | ||
483 | while ((index < xfer->urb->number_of_packets) | 485 | while ((index < xfer->urb->number_of_packets) |
484 | && ((segment_size + xfer->urb->iso_frame_desc[index].length) | 486 | && ((segment_size + iso_frame_desc[index].length) |
485 | <= xfer->seg_size)) { | 487 | <= xfer->seg_size)) { |
488 | /* | ||
489 | * For Alereon HWA devices, only include an isoc frame in a | ||
490 | * segment if it is physically contiguous with the previous | ||
491 | * frame. This is required because those devices expect | ||
492 | * the isoc frames to be sent as a single USB transaction as | ||
493 | * opposed to one transaction per frame with standard HWA. | ||
494 | */ | ||
495 | if ((xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) | ||
496 | && (index > isoc_frame_offset) | ||
497 | && ((iso_frame_desc[index - 1].offset + | ||
498 | iso_frame_desc[index - 1].length) != | ||
499 | iso_frame_desc[index].offset)) | ||
500 | break; | ||
501 | |||
486 | /* this frame fits. count it. */ | 502 | /* this frame fits. count it. */ |
487 | ++frame_count; | 503 | ++frame_count; |
488 | segment_size += xfer->urb->iso_frame_desc[index].length; | 504 | segment_size += iso_frame_desc[index].length; |
489 | 505 | ||
490 | /* move to the next isoc frame. */ | 506 | /* move to the next isoc frame. */ |
491 | ++index; | 507 | ++index; |
@@ -681,7 +697,11 @@ static void wa_seg_dto_cb(struct urb *urb) | |||
681 | wa = xfer->wa; | 697 | wa = xfer->wa; |
682 | dev = &wa->usb_iface->dev; | 698 | dev = &wa->usb_iface->dev; |
683 | if (usb_pipeisoc(xfer->urb->pipe)) { | 699 | if (usb_pipeisoc(xfer->urb->pipe)) { |
684 | xfer->dto_isoc_frame_index += 1; | 700 | /* Alereon HWA sends all isoc frames in a single transfer. */ |
701 | if (wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) | ||
702 | xfer->dto_isoc_frame_index += seg->isoc_frame_count; | ||
703 | else | ||
704 | xfer->dto_isoc_frame_index += 1; | ||
685 | if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) { | 705 | if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) { |
686 | data_send_done = 0; | 706 | data_send_done = 0; |
687 | holding_dto = 1; /* checked in error cases. */ | 707 | holding_dto = 1; /* checked in error cases. */ |
@@ -1007,17 +1027,18 @@ static struct scatterlist *wa_xfer_create_subset_sg(struct scatterlist *in_sg, | |||
1007 | static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, | 1027 | static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer, |
1008 | struct wa_seg *seg, int curr_iso_frame) | 1028 | struct wa_seg *seg, int curr_iso_frame) |
1009 | { | 1029 | { |
1010 | /* | ||
1011 | * dto urb buffer address and size pulled from | ||
1012 | * iso_frame_desc. | ||
1013 | */ | ||
1014 | seg->dto_urb->transfer_dma = xfer->urb->transfer_dma + | ||
1015 | xfer->urb->iso_frame_desc[curr_iso_frame].offset; | ||
1016 | seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | 1030 | seg->dto_urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; |
1017 | seg->dto_urb->sg = NULL; | 1031 | seg->dto_urb->sg = NULL; |
1018 | seg->dto_urb->num_sgs = 0; | 1032 | seg->dto_urb->num_sgs = 0; |
1019 | seg->dto_urb->transfer_buffer_length = | 1033 | /* dto urb buffer address pulled from iso_frame_desc. */ |
1020 | xfer->urb->iso_frame_desc[curr_iso_frame].length; | 1034 | seg->dto_urb->transfer_dma = xfer->urb->transfer_dma + |
1035 | xfer->urb->iso_frame_desc[curr_iso_frame].offset; | ||
1036 | /* The Alereon HWA sends a single URB with all isoc segs. */ | ||
1037 | if (xfer->wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) | ||
1038 | seg->dto_urb->transfer_buffer_length = seg->isoc_size; | ||
1039 | else | ||
1040 | seg->dto_urb->transfer_buffer_length = | ||
1041 | xfer->urb->iso_frame_desc[curr_iso_frame].length; | ||
1021 | } | 1042 | } |
1022 | 1043 | ||
1023 | /* | 1044 | /* |
@@ -1298,6 +1319,8 @@ static int __wa_seg_submit(struct wa_rpipe *rpipe, struct wa_xfer *xfer, | |||
1298 | } | 1319 | } |
1299 | /* submit the isoc packet descriptor if present. */ | 1320 | /* submit the isoc packet descriptor if present. */ |
1300 | if (seg->isoc_pack_desc_urb) { | 1321 | if (seg->isoc_pack_desc_urb) { |
1322 | struct wahc *wa = xfer->wa; | ||
1323 | |||
1301 | result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); | 1324 | result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC); |
1302 | if (result < 0) { | 1325 | if (result < 0) { |
1303 | pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n", | 1326 | 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, | |||
1308 | /* | 1331 | /* |
1309 | * If this segment contains more than one isoc frame, hold | 1332 | * If this segment contains more than one isoc frame, hold |
1310 | * onto the dto resource until we send all frames. | 1333 | * onto the dto resource until we send all frames. |
1334 | * Only applies to non-Alereon devices. | ||
1311 | */ | 1335 | */ |
1312 | if (seg->isoc_frame_count > 1) | 1336 | if (((wa->quirks & WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC) == 0) |
1337 | && (seg->isoc_frame_count > 1)) | ||
1313 | *dto_done = 0; | 1338 | *dto_done = 0; |
1314 | } | 1339 | } |
1315 | /* submit the out data if this is an out request. */ | 1340 | /* submit the out data if this is an out request. */ |