Hi, It works ok when I add MTK's quirk into xhci_td_remainder(), and test it by usb camera, udisk, usb ethernet adapter, and hub. On Tue, 2015-09-08 at 14:09 +0300, Mathias Nyman wrote: > xhci versions 1.0 and later report the untransferred data remaining in a > TD a bit differently than older hosts. > > We used to have separate functions for these, and needed to check host > version before calling the right function. > > Now Mediatek host has an additional quirk on how it uses the TD Size > field for remaining data. To prevent yet another function for calculating > remainder we instead want to make one quirk friendly unified function. > > Signed-off-by: Mathias Nyman <mathias.nyman@xxxxxxxxxxxxxxx> > --- > drivers/usb/host/xhci-ring.c | 106 ++++++++++++++++++------------------------- > drivers/usb/host/xhci.h | 2 + > 2 files changed, 46 insertions(+), 62 deletions(-) > > diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c > index a47a1e8..57f40a1 100644 > --- a/drivers/usb/host/xhci-ring.c > +++ b/drivers/usb/host/xhci-ring.c > @@ -3020,21 +3020,6 @@ int xhci_queue_intr_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > } > > /* > - * The TD size is the number of bytes remaining in the TD (including this TRB), > - * right shifted by 10. > - * It must fit in bits 21:17, so it can't be bigger than 31. > - */ > -static u32 xhci_td_remainder(unsigned int remainder) > -{ > - u32 max = (1 << (21 - 17 + 1)) - 1; > - > - if ((remainder >> 10) >= max) > - return max << 17; > - else > - return (remainder >> 10) << 17; > -} > - > -/* > * For xHCI 1.0 host controllers, TD size is the number of max packet sized > * packets remaining in the TD (*not* including this TRB). > * > @@ -3046,30 +3031,36 @@ static u32 xhci_td_remainder(unsigned int remainder) > * > * TD size = total_packet_count - packets_transferred > * > - * It must fit in bits 21:17, so it can't be bigger than 31. > + * For xHCI 0.96 and older, TD size field should be the remaining bytes > + * including this TRB, right shifted by 10 > + * > + * For all hosts it must fit in bits 21:17, so it can't be bigger than 31. > + * This is taken care of in the TRB_TD_SIZE() macro > + * > * The last TRB in a TD must have the TD size set to zero. > */ > -static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len, > - unsigned int total_packet_count, struct urb *urb, > - unsigned int num_trbs_left) > +static u32 xhci_td_remainder(struct xhci_hcd *xhci, int transferred, > + int trb_buff_len, unsigned int td_total_len, > + struct urb *urb, unsigned int num_trbs_left) > { > - int packets_transferred; > + u32 maxp, total_packet_count; > + > + if (xhci->hci_version < 0x100) > + return ((td_total_len - transferred) >> 10); > + > + maxp = GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); > + total_packet_count = DIV_ROUND_UP(td_total_len, maxp); > > /* One TRB with a zero-length data packet. */ > - if (num_trbs_left == 0 || (running_total == 0 && trb_buff_len == 0)) > + if (num_trbs_left == 0 || (transferred == 0 && trb_buff_len == 0) || > + trb_buff_len == td_total_len) > return 0; > > - /* All the TRB queueing functions don't count the current TRB in > - * running_total. > - */ > - packets_transferred = (running_total + trb_buff_len) / > - GET_MAX_PACKET(usb_endpoint_maxp(&urb->ep->desc)); > - > - if ((total_packet_count - packets_transferred) > 31) > - return 31 << 17; > - return (total_packet_count - packets_transferred) << 17; > + /* Queueing functions don't count the current TRB into transferred */ > + return (total_packet_count - ((transferred + trb_buff_len) / maxp)); > } > > + > static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > struct urb *urb, int slot_id, unsigned int ep_index) > { > @@ -3191,17 +3182,12 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > } > > /* Set the TRB length, TD size, and interrupter fields. */ > - if (xhci->hci_version < 0x100) { > - remainder = xhci_td_remainder( > - urb->transfer_buffer_length - > - running_total); > - } else { > - remainder = xhci_v1_0_td_remainder(running_total, > - trb_buff_len, total_packet_count, urb, > - num_trbs - 1); > - } > + remainder = xhci_td_remainder(xhci, running_total, trb_buff_len, > + urb->transfer_buffer_length, > + urb, num_trbs - 1); > + > length_field = TRB_LEN(trb_buff_len) | > - remainder | > + TRB_TD_SIZE(remainder) | > TRB_INTR_TARGET(0); > > if (num_trbs > 1) > @@ -3364,17 +3350,12 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > field |= TRB_ISP; > > /* Set the TRB length, TD size, and interrupter fields. */ > - if (xhci->hci_version < 0x100) { > - remainder = xhci_td_remainder( > - urb->transfer_buffer_length - > - running_total); > - } else { > - remainder = xhci_v1_0_td_remainder(running_total, > - trb_buff_len, total_packet_count, urb, > - num_trbs - 1); > - } > + remainder = xhci_td_remainder(xhci, running_total, trb_buff_len, > + urb->transfer_buffer_length, > + urb, num_trbs - 1); > + > length_field = TRB_LEN(trb_buff_len) | > - remainder | > + TRB_TD_SIZE(remainder) | > TRB_INTR_TARGET(0); > > if (num_trbs > 1) > @@ -3412,7 +3393,7 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > struct usb_ctrlrequest *setup; > struct xhci_generic_trb *start_trb; > int start_cycle; > - u32 field, length_field; > + u32 field, length_field, remainder; > struct urb_priv *urb_priv; > struct xhci_td *td; > > @@ -3485,9 +3466,15 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > else > field = TRB_TYPE(TRB_DATA); > > + remainder = xhci_td_remainder(xhci, 0, > + urb->transfer_buffer_length, > + urb->transfer_buffer_length, > + urb, 1); > + > length_field = TRB_LEN(urb->transfer_buffer_length) | > - xhci_td_remainder(urb->transfer_buffer_length) | > + TRB_TD_SIZE(remainder) | > TRB_INTR_TARGET(0); > + > if (urb->transfer_buffer_length > 0) { > if (setup->bRequestType & USB_DIR_IN) > field |= TRB_DIR_IN; > @@ -3816,17 +3803,12 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags, > trb_buff_len = td_remain_len; > > /* Set the TRB length, TD size, & interrupter fields. */ > - if (xhci->hci_version < 0x100) { > - remainder = xhci_td_remainder( > - td_len - running_total); > - } else { > - remainder = xhci_v1_0_td_remainder( > - running_total, trb_buff_len, > - total_packet_count, urb, > - (trbs_per_td - j - 1)); > - } > + remainder = xhci_td_remainder(xhci, running_total, > + trb_buff_len, td_len, > + urb, trbs_per_td - j - 1); > + > length_field = TRB_LEN(trb_buff_len) | > - remainder | > + TRB_TD_SIZE(remainder) | > TRB_INTR_TARGET(0); > > queue_trb(xhci, ep_ring, more_trbs_coming, > diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h > index dbda41e..ea8b904 100644 > --- a/drivers/usb/host/xhci.h > +++ b/drivers/usb/host/xhci.h > @@ -1136,6 +1136,8 @@ enum xhci_setup_dev { > /* Normal TRB fields */ > /* transfer_len bitmasks - bits 0:16 */ > #define TRB_LEN(p) ((p) & 0x1ffff) > +/* TD Size, packets remaining in this TD, bits 21:17 (5 bits, so max 31) */ > +#define TRB_TD_SIZE(p) (min((p), (u32)31) << 17) > /* Interrupter Target - which MSI-X vector to target the completion event at */ > #define TRB_INTR_TARGET(p) (((p) & 0x3ff) << 22) > #define GET_INTR_TARGET(p) (((p) >> 22) & 0x3ff) -- To unsubscribe from this list: send the line "unsubscribe linux-usb" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html