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)
{
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;
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,
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
}
+
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ field |= TRB_ISP;
+
xhci_dbg(xhci, " sg entry: dma = %#x, len = %#x (%d), "
"64KB boundary at %#x, end dma = %#x\n",
(unsigned int) addr, trb_buff_len, trb_buff_len,
(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
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
- /* We always want to know if the TRB was short,
- * or we won't get an event when it completes.
- * (Unless we use event data TRBs, which are a
- * waste of space and HC resources.)
- */
- field | TRB_ISP | TRB_TYPE(TRB_NORMAL));
+ field | TRB_TYPE(TRB_NORMAL));
--num_trbs;
running_total += trb_buff_len;
u32 field, length_field;
int running_total, trb_buff_len, ret;
+ unsigned int total_packet_count;
u64 addr;
if (urb->num_sgs)
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 -
td->last_trb = ep_ring->enqueue;
field |= TRB_IOC;
}
- remainder = xhci_td_remainder(urb->transfer_buffer_length -
- running_total);
+
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ 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);
+ }
length_field = TRB_LEN(trb_buff_len) |
remainder |
TRB_INTR_TARGET(0);
+
if (num_trbs > 1)
more_trbs_coming = true;
else
lower_32_bits(addr),
upper_32_bits(addr),
length_field,
- /* We always want to know if the TRB was short,
- * or we won't get an event when it completes.
- * (Unless we use event data TRBs, which are a
- * waste of space and HC resources.)
- */
- field | TRB_ISP | TRB_TYPE(TRB_NORMAL));
+ field | TRB_TYPE(TRB_NORMAL));
--num_trbs;
running_total += trb_buff_len;
field);
/* If there's data, queue data TRBs */
- field = 0;
+ /* Only set interrupt on short packet for IN endpoints */
+ if (usb_urb_dir_in(urb))
+ field = TRB_ISP | TRB_TYPE(TRB_DATA);
+ else
+ field = TRB_TYPE(TRB_DATA);
+
length_field = TRB_LEN(urb->transfer_buffer_length) |
xhci_td_remainder(urb->transfer_buffer_length) |
TRB_INTR_TARGET(0);
lower_32_bits(urb->transfer_dma),
upper_32_bits(urb->transfer_dma),
length_field,
- /* Event on short tx */
- field | TRB_ISP | TRB_TYPE(TRB_DATA) | ep_ring->cycle_state);
+ field | ep_ring->cycle_state);
}
/* Save the DMA address of the last TRB in the TD */
/* 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);
field |= ep_ring->cycle_state;
}
+ /* Only set interrupt on short packet for IN EPs */
+ if (usb_urb_dir_in(urb))
+ field |= TRB_ISP;
+
/* Chain all the TRBs together; clear the chain bit in
* the last TRB to indicate it's the last TRB in the
* chain.
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),
length_field,
- /* We always want to know if the TRB was short,
- * or we won't get an event when it completes.
- * (Unless we use event data TRBs, which are a
- * waste of space and HC resources.)
- */
- field | TRB_ISP);
+ field);
running_total += trb_buff_len;
addr += trb_buff_len;