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>
This commit is contained in:
parent
2101242cef
commit
f07ddb9ef5
|
@ -679,7 +679,8 @@ static void hwahc_security_release(struct hwahc *hwahc)
|
||||||
/* nothing to do here so far... */
|
/* nothing to do here so far... */
|
||||||
}
|
}
|
||||||
|
|
||||||
static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
|
static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface,
|
||||||
|
kernel_ulong_t quirks)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
struct device *dev = &iface->dev;
|
struct device *dev = &iface->dev;
|
||||||
|
@ -724,7 +725,7 @@ static int hwahc_create(struct hwahc *hwahc, struct usb_interface *iface)
|
||||||
dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
|
dev_err(dev, "Can't create WUSB HC structures: %d\n", result);
|
||||||
goto error_wusbhc_create;
|
goto error_wusbhc_create;
|
||||||
}
|
}
|
||||||
result = wa_create(&hwahc->wa, iface);
|
result = wa_create(&hwahc->wa, iface, quirks);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
goto error_wa_create;
|
goto error_wa_create;
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -780,7 +781,7 @@ static int hwahc_probe(struct usb_interface *usb_iface,
|
||||||
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
wusbhc = usb_hcd_to_wusbhc(usb_hcd);
|
||||||
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
hwahc = container_of(wusbhc, struct hwahc, wusbhc);
|
||||||
hwahc_init(hwahc);
|
hwahc_init(hwahc);
|
||||||
result = hwahc_create(hwahc, usb_iface);
|
result = hwahc_create(hwahc, usb_iface, id->driver_info);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
dev_err(dev, "Cannot initialize internals: %d\n", result);
|
dev_err(dev, "Cannot initialize internals: %d\n", result);
|
||||||
goto error_hwahc_create;
|
goto error_hwahc_create;
|
||||||
|
@ -824,6 +825,12 @@ static void hwahc_disconnect(struct usb_interface *usb_iface)
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_device_id hwahc_id_table[] = {
|
static struct usb_device_id hwahc_id_table[] = {
|
||||||
|
/* Alereon 5310 */
|
||||||
|
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5310, 0xe0, 0x02, 0x01),
|
||||||
|
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
|
||||||
|
/* Alereon 5611 */
|
||||||
|
{ USB_DEVICE_AND_INTERFACE_INFO(0x13dc, 0x5611, 0xe0, 0x02, 0x01),
|
||||||
|
.driver_info = WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC },
|
||||||
/* FIXME: use class labels for this */
|
/* FIXME: use class labels for this */
|
||||||
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
|
{ USB_INTERFACE_INFO(0xe0, 0x02, 0x01), },
|
||||||
{},
|
{},
|
||||||
|
|
|
@ -33,7 +33,8 @@
|
||||||
* wa->usb_dev and wa->usb_iface initialized and refcounted,
|
* wa->usb_dev and wa->usb_iface initialized and refcounted,
|
||||||
* wa->wa_descr initialized.
|
* wa->wa_descr initialized.
|
||||||
*/
|
*/
|
||||||
int wa_create(struct wahc *wa, struct usb_interface *iface)
|
int wa_create(struct wahc *wa, struct usb_interface *iface,
|
||||||
|
kernel_ulong_t quirks)
|
||||||
{
|
{
|
||||||
int result;
|
int result;
|
||||||
struct device *dev = &iface->dev;
|
struct device *dev = &iface->dev;
|
||||||
|
@ -41,6 +42,7 @@ int wa_create(struct wahc *wa, struct usb_interface *iface)
|
||||||
result = wa_rpipes_create(wa);
|
result = wa_rpipes_create(wa);
|
||||||
if (result < 0)
|
if (result < 0)
|
||||||
goto error_rpipes_create;
|
goto error_rpipes_create;
|
||||||
|
wa->quirks = quirks;
|
||||||
/* Fill up Data Transfer EP pointers */
|
/* Fill up Data Transfer EP pointers */
|
||||||
wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
|
wa->dti_epd = &iface->cur_altsetting->endpoint[1].desc;
|
||||||
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
|
wa->dto_epd = &iface->cur_altsetting->endpoint[2].desc;
|
||||||
|
|
|
@ -128,6 +128,14 @@ enum wa_dti_state {
|
||||||
WA_DTI_ISOC_PACKET_STATUS_PENDING
|
WA_DTI_ISOC_PACKET_STATUS_PENDING
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum wa_quirks {
|
||||||
|
/*
|
||||||
|
* The Alereon HWA expects the data frames in isochronous transfer
|
||||||
|
* requests to be concatenated and not sent as separate packets.
|
||||||
|
*/
|
||||||
|
WUSB_QUIRK_ALEREON_HWA_CONCAT_ISOC = 0x01,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Instance of a HWA Host Controller
|
* Instance of a HWA Host Controller
|
||||||
*
|
*
|
||||||
|
@ -218,10 +226,13 @@ struct wahc {
|
||||||
struct work_struct xfer_enqueue_work;
|
struct work_struct xfer_enqueue_work;
|
||||||
struct work_struct xfer_error_work;
|
struct work_struct xfer_error_work;
|
||||||
atomic_t xfer_id_count;
|
atomic_t xfer_id_count;
|
||||||
|
|
||||||
|
kernel_ulong_t quirks;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
extern int wa_create(struct wahc *wa, struct usb_interface *iface);
|
extern int wa_create(struct wahc *wa, struct usb_interface *iface,
|
||||||
|
kernel_ulong_t);
|
||||||
extern void __wa_destroy(struct wahc *wa);
|
extern void __wa_destroy(struct wahc *wa);
|
||||||
void wa_reset_all(struct wahc *wa);
|
void wa_reset_all(struct wahc *wa);
|
||||||
|
|
||||||
|
|
|
@ -479,13 +479,29 @@ static int __wa_seg_calculate_isoc_frame_count(struct wa_xfer *xfer,
|
||||||
{
|
{
|
||||||
int segment_size = 0, frame_count = 0;
|
int segment_size = 0, frame_count = 0;
|
||||||
int index = isoc_frame_offset;
|
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)
|
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)) {
|
<= 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. */
|
/* this frame fits. count it. */
|
||||||
++frame_count;
|
++frame_count;
|
||||||
segment_size += xfer->urb->iso_frame_desc[index].length;
|
segment_size += iso_frame_desc[index].length;
|
||||||
|
|
||||||
/* move to the next isoc frame. */
|
/* move to the next isoc frame. */
|
||||||
++index;
|
++index;
|
||||||
|
@ -681,7 +697,11 @@ static void wa_seg_dto_cb(struct urb *urb)
|
||||||
wa = xfer->wa;
|
wa = xfer->wa;
|
||||||
dev = &wa->usb_iface->dev;
|
dev = &wa->usb_iface->dev;
|
||||||
if (usb_pipeisoc(xfer->urb->pipe)) {
|
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) {
|
if (xfer->dto_isoc_frame_index < seg->isoc_frame_count) {
|
||||||
data_send_done = 0;
|
data_send_done = 0;
|
||||||
holding_dto = 1; /* checked in error cases. */
|
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,
|
static void __wa_populate_dto_urb_isoc(struct wa_xfer *xfer,
|
||||||
struct wa_seg *seg, int curr_iso_frame)
|
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->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||||
seg->dto_urb->sg = NULL;
|
seg->dto_urb->sg = NULL;
|
||||||
seg->dto_urb->num_sgs = 0;
|
seg->dto_urb->num_sgs = 0;
|
||||||
seg->dto_urb->transfer_buffer_length =
|
/* dto urb buffer address pulled from iso_frame_desc. */
|
||||||
xfer->urb->iso_frame_desc[curr_iso_frame].length;
|
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. */
|
/* submit the isoc packet descriptor if present. */
|
||||||
if (seg->isoc_pack_desc_urb) {
|
if (seg->isoc_pack_desc_urb) {
|
||||||
|
struct wahc *wa = xfer->wa;
|
||||||
|
|
||||||
result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
|
result = usb_submit_urb(seg->isoc_pack_desc_urb, GFP_ATOMIC);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
pr_err("%s: xfer %p#%u: ISO packet descriptor submit failed: %d\n",
|
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
|
* If this segment contains more than one isoc frame, hold
|
||||||
* onto the dto resource until we send all frames.
|
* 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;
|
*dto_done = 0;
|
||||||
}
|
}
|
||||||
/* submit the out data if this is an out request. */
|
/* submit the out data if this is an out request. */
|
||||||
|
|
Loading…
Reference in New Issue