Skip to content

Commit

Permalink
xhci 1.0: Update TD size field format.
Browse files Browse the repository at this point in the history
The xHCI 1.0 specification changes the format of the TD size field in
Normal and Isochronous TRBs.  The field in control TRBs is still set to
reserved zero.  Instead of representing the number of bytes left to
transfer in the TD (including the current TRB's buffer), it now represents
the number of packets left to transfer (*not* including this TRB).

See section 4.11.2.4 of the xHCI 1.0 specification for details.  The math
is basically copied straight from there.

Create a new function, xhci_v1_0_td_remainder(), that should be called for
all xHCI 1.0 host controllers.  The field location and maximum value is
still the same, so reuse the old function, xhci_td_remainder(), to handle
the bit shifting.

Signed-off-by: Sarah Sharp <sarah.a.sharp@linux.intel.com>
  • Loading branch information
Sarah Sharp committed May 2, 2011
1 parent af8b9e6 commit 4da6e6f
Showing 1 changed file with 70 additions and 6 deletions.
76 changes: 70 additions & 6 deletions drivers/usb/host/xhci-ring.c
Original file line number Diff line number Diff line change
Expand Up @@ -2655,6 +2655,35 @@ static u32 xhci_td_remainder(unsigned int remainder)
return (remainder >> 10) << 17;
}

/*
* For xHCI 1.0 host controllers, TD size is the number of packets remaining in
* the TD (*not* including this TRB).
*
* Total TD packet count = total_packet_count =
* roundup(TD size in bytes / wMaxPacketSize)
*
* Packets transferred up to and including this TRB = packets_transferred =
* rounddown(total bytes transferred including this TRB / wMaxPacketSize)
*
* TD size = total_packet_count - packets_transferred
*
* It must fit in bits 21:17, so it can't be bigger than 31.
*/

static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
unsigned int total_packet_count, struct urb *urb)
{
int packets_transferred;

/* All the TRB queueing functions don't count the current TRB in
* running_total.
*/
packets_transferred = (running_total + trb_buff_len) /
le16_to_cpu(urb->ep->desc.wMaxPacketSize);

return xhci_td_remainder(total_packet_count - packets_transferred);
}

static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct urb *urb, int slot_id, unsigned int ep_index)
{
Expand All @@ -2665,6 +2694,7 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
struct scatterlist *sg;
int num_sgs;
int trb_buff_len, this_sg_len, running_total;
unsigned int total_packet_count;
bool first_trb;
u64 addr;
bool more_trbs_coming;
Expand All @@ -2678,6 +2708,8 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,

num_trbs = count_sg_trbs_needed(xhci, urb);
num_sgs = urb->num_sgs;
total_packet_count = roundup(urb->transfer_buffer_length,
le16_to_cpu(urb->ep->desc.wMaxPacketSize));

trb_buff_len = prepare_transfer(xhci, xhci->devs[slot_id],
ep_index, urb->stream_id,
Expand Down Expand Up @@ -2758,11 +2790,20 @@ static int queue_bulk_sg_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
(unsigned int) (addr + TRB_MAX_BUFF_SIZE) & ~(TRB_MAX_BUFF_SIZE - 1),
(unsigned int) addr + trb_buff_len);
}
remainder = xhci_td_remainder(urb->transfer_buffer_length -
running_total) ;

/* 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);
}
length_field = TRB_LEN(trb_buff_len) |
remainder |
TRB_INTR_TARGET(0);

if (num_trbs > 1)
more_trbs_coming = true;
else
Expand Down Expand Up @@ -2819,6 +2860,7 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
u32 field, length_field;

int running_total, trb_buff_len, ret;
unsigned int total_packet_count;
u64 addr;

if (urb->num_sgs)
Expand Down Expand Up @@ -2873,6 +2915,8 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
start_cycle = ep_ring->cycle_state;

running_total = 0;
total_packet_count = roundup(urb->transfer_buffer_length,
le16_to_cpu(urb->ep->desc.wMaxPacketSize));
/* How much data is in the first TRB? */
addr = (u64) urb->transfer_dma;
trb_buff_len = TRB_MAX_BUFF_SIZE -
Expand Down Expand Up @@ -2910,11 +2954,19 @@ int xhci_queue_bulk_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (usb_urb_dir_in(urb))
field |= TRB_ISP;

remainder = xhci_td_remainder(urb->transfer_buffer_length -
running_total);
/* 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);
}
length_field = TRB_LEN(trb_buff_len) |
remainder |
TRB_INTR_TARGET(0);

if (num_trbs > 1)
more_trbs_coming = true;
else
Expand Down Expand Up @@ -3111,12 +3163,15 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,

/* Queue the first TRB, even if it's zero-length */
for (i = 0; i < num_tds; i++) {
first_trb = true;
unsigned int total_packet_count;

first_trb = true;
running_total = 0;
addr = start_addr + urb->iso_frame_desc[i].offset;
td_len = urb->iso_frame_desc[i].length;
td_remain_len = td_len;
total_packet_count = roundup(td_len,
le16_to_cpu(urb->ep->desc.wMaxPacketSize));

trbs_per_td = count_isoc_trbs_needed(xhci, urb, i);

Expand Down Expand Up @@ -3172,10 +3227,19 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
if (trb_buff_len > td_remain_len)
trb_buff_len = td_remain_len;

remainder = xhci_td_remainder(td_len - running_total);
/* 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);
}
length_field = TRB_LEN(trb_buff_len) |
remainder |
TRB_INTR_TARGET(0);

queue_trb(xhci, ep_ring, false, more_trbs_coming,
lower_32_bits(addr),
upper_32_bits(addr),
Expand Down

0 comments on commit 4da6e6f

Please sign in to comment.