Merge branch 'for-usb-linus' of git+ssh://master.kernel.org/pub/scm/linux/kernel/git/sarah/xhci into usb-linus
* 'for-usb-linus' of git+ssh://master.kernel.org/pub/scm/linux/kernel/git/sarah/xhci: xhci: Handle zero-length isochronous packets. USB: Avoid NULL pointer deref in usb_hcd_alloc_bandwidth. xhci: Remove TDs from TD lists when URBs are canceled. xhci: Fix failed enqueue in the middle of isoch TD. xhci: Fix memory leak during failed enqueue. xHCI: report USB2 port in resuming as suspend xHCI: fix port U3 status check condition
This commit is contained in:
commit
ea8c7fd9b0
4 changed files with 102 additions and 35 deletions
|
@ -1775,6 +1775,8 @@ int usb_hcd_alloc_bandwidth(struct usb_device *udev,
|
||||||
struct usb_interface *iface = usb_ifnum_to_if(udev,
|
struct usb_interface *iface = usb_ifnum_to_if(udev,
|
||||||
cur_alt->desc.bInterfaceNumber);
|
cur_alt->desc.bInterfaceNumber);
|
||||||
|
|
||||||
|
if (!iface)
|
||||||
|
return -EINVAL;
|
||||||
if (iface->resetting_device) {
|
if (iface->resetting_device) {
|
||||||
/*
|
/*
|
||||||
* The USB core just reset the device, so the xHCI host
|
* The USB core just reset the device, so the xHCI host
|
||||||
|
|
|
@ -463,11 +463,12 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||||
&& (temp & PORT_POWER))
|
&& (temp & PORT_POWER))
|
||||||
status |= USB_PORT_STAT_SUSPEND;
|
status |= USB_PORT_STAT_SUSPEND;
|
||||||
}
|
}
|
||||||
if ((temp & PORT_PLS_MASK) == XDEV_RESUME) {
|
if ((temp & PORT_PLS_MASK) == XDEV_RESUME &&
|
||||||
|
!DEV_SUPERSPEED(temp)) {
|
||||||
if ((temp & PORT_RESET) || !(temp & PORT_PE))
|
if ((temp & PORT_RESET) || !(temp & PORT_PE))
|
||||||
goto error;
|
goto error;
|
||||||
if (!DEV_SUPERSPEED(temp) && time_after_eq(jiffies,
|
if (time_after_eq(jiffies,
|
||||||
bus_state->resume_done[wIndex])) {
|
bus_state->resume_done[wIndex])) {
|
||||||
xhci_dbg(xhci, "Resume USB2 port %d\n",
|
xhci_dbg(xhci, "Resume USB2 port %d\n",
|
||||||
wIndex + 1);
|
wIndex + 1);
|
||||||
bus_state->resume_done[wIndex] = 0;
|
bus_state->resume_done[wIndex] = 0;
|
||||||
|
@ -487,6 +488,14 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||||
xhci_ring_device(xhci, slot_id);
|
xhci_ring_device(xhci, slot_id);
|
||||||
bus_state->port_c_suspend |= 1 << wIndex;
|
bus_state->port_c_suspend |= 1 << wIndex;
|
||||||
bus_state->suspended_ports &= ~(1 << wIndex);
|
bus_state->suspended_ports &= ~(1 << wIndex);
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* The resume has been signaling for less than
|
||||||
|
* 20ms. Report the port status as SUSPEND,
|
||||||
|
* let the usbcore check port status again
|
||||||
|
* and clear resume signaling later.
|
||||||
|
*/
|
||||||
|
status |= USB_PORT_STAT_SUSPEND;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ((temp & PORT_PLS_MASK) == XDEV_U0
|
if ((temp & PORT_PLS_MASK) == XDEV_U0
|
||||||
|
@ -664,7 +673,7 @@ int xhci_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue,
|
||||||
xhci_dbg(xhci, "PORTSC %04x\n", temp);
|
xhci_dbg(xhci, "PORTSC %04x\n", temp);
|
||||||
if (temp & PORT_RESET)
|
if (temp & PORT_RESET)
|
||||||
goto error;
|
goto error;
|
||||||
if (temp & XDEV_U3) {
|
if ((temp & PORT_PLS_MASK) == XDEV_U3) {
|
||||||
if ((temp & PORT_PE) == 0)
|
if ((temp & PORT_PE) == 0)
|
||||||
goto error;
|
goto error;
|
||||||
|
|
||||||
|
|
|
@ -514,8 +514,12 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||||
(unsigned long long) addr);
|
(unsigned long long) addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* flip_cycle means flip the cycle bit of all but the first and last TRB.
|
||||||
|
* (The last TRB actually points to the ring enqueue pointer, which is not part
|
||||||
|
* of this TD.) This is used to remove partially enqueued isoc TDs from a ring.
|
||||||
|
*/
|
||||||
static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||||
struct xhci_td *cur_td)
|
struct xhci_td *cur_td, bool flip_cycle)
|
||||||
{
|
{
|
||||||
struct xhci_segment *cur_seg;
|
struct xhci_segment *cur_seg;
|
||||||
union xhci_trb *cur_trb;
|
union xhci_trb *cur_trb;
|
||||||
|
@ -528,6 +532,12 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||||
* leave the pointers intact.
|
* leave the pointers intact.
|
||||||
*/
|
*/
|
||||||
cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN);
|
cur_trb->generic.field[3] &= cpu_to_le32(~TRB_CHAIN);
|
||||||
|
/* Flip the cycle bit (link TRBs can't be the first
|
||||||
|
* or last TRB).
|
||||||
|
*/
|
||||||
|
if (flip_cycle)
|
||||||
|
cur_trb->generic.field[3] ^=
|
||||||
|
cpu_to_le32(TRB_CYCLE);
|
||||||
xhci_dbg(xhci, "Cancel (unchain) link TRB\n");
|
xhci_dbg(xhci, "Cancel (unchain) link TRB\n");
|
||||||
xhci_dbg(xhci, "Address = %p (0x%llx dma); "
|
xhci_dbg(xhci, "Address = %p (0x%llx dma); "
|
||||||
"in seg %p (0x%llx dma)\n",
|
"in seg %p (0x%llx dma)\n",
|
||||||
|
@ -541,6 +551,11 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring,
|
||||||
cur_trb->generic.field[2] = 0;
|
cur_trb->generic.field[2] = 0;
|
||||||
/* Preserve only the cycle bit of this TRB */
|
/* Preserve only the cycle bit of this TRB */
|
||||||
cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
|
cur_trb->generic.field[3] &= cpu_to_le32(TRB_CYCLE);
|
||||||
|
/* Flip the cycle bit except on the first or last TRB */
|
||||||
|
if (flip_cycle && cur_trb != cur_td->first_trb &&
|
||||||
|
cur_trb != cur_td->last_trb)
|
||||||
|
cur_trb->generic.field[3] ^=
|
||||||
|
cpu_to_le32(TRB_CYCLE);
|
||||||
cur_trb->generic.field[3] |= cpu_to_le32(
|
cur_trb->generic.field[3] |= cpu_to_le32(
|
||||||
TRB_TYPE(TRB_TR_NOOP));
|
TRB_TYPE(TRB_TR_NOOP));
|
||||||
xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) "
|
xhci_dbg(xhci, "Cancel TRB %p (0x%llx dma) "
|
||||||
|
@ -719,14 +734,14 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
||||||
cur_td->urb->stream_id,
|
cur_td->urb->stream_id,
|
||||||
cur_td, &deq_state);
|
cur_td, &deq_state);
|
||||||
else
|
else
|
||||||
td_to_noop(xhci, ep_ring, cur_td);
|
td_to_noop(xhci, ep_ring, cur_td, false);
|
||||||
remove_finished_td:
|
remove_finished_td:
|
||||||
/*
|
/*
|
||||||
* The event handler won't see a completion for this TD anymore,
|
* The event handler won't see a completion for this TD anymore,
|
||||||
* so remove it from the endpoint ring's TD list. Keep it in
|
* so remove it from the endpoint ring's TD list. Keep it in
|
||||||
* the cancelled TD list for URB completion later.
|
* the cancelled TD list for URB completion later.
|
||||||
*/
|
*/
|
||||||
list_del(&cur_td->td_list);
|
list_del_init(&cur_td->td_list);
|
||||||
}
|
}
|
||||||
last_unlinked_td = cur_td;
|
last_unlinked_td = cur_td;
|
||||||
xhci_stop_watchdog_timer_in_irq(xhci, ep);
|
xhci_stop_watchdog_timer_in_irq(xhci, ep);
|
||||||
|
@ -754,7 +769,7 @@ static void handle_stopped_endpoint(struct xhci_hcd *xhci,
|
||||||
do {
|
do {
|
||||||
cur_td = list_entry(ep->cancelled_td_list.next,
|
cur_td = list_entry(ep->cancelled_td_list.next,
|
||||||
struct xhci_td, cancelled_td_list);
|
struct xhci_td, cancelled_td_list);
|
||||||
list_del(&cur_td->cancelled_td_list);
|
list_del_init(&cur_td->cancelled_td_list);
|
||||||
|
|
||||||
/* Clean up the cancelled URB */
|
/* Clean up the cancelled URB */
|
||||||
/* Doesn't matter what we pass for status, since the core will
|
/* Doesn't matter what we pass for status, since the core will
|
||||||
|
@ -862,9 +877,9 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
|
||||||
cur_td = list_first_entry(&ring->td_list,
|
cur_td = list_first_entry(&ring->td_list,
|
||||||
struct xhci_td,
|
struct xhci_td,
|
||||||
td_list);
|
td_list);
|
||||||
list_del(&cur_td->td_list);
|
list_del_init(&cur_td->td_list);
|
||||||
if (!list_empty(&cur_td->cancelled_td_list))
|
if (!list_empty(&cur_td->cancelled_td_list))
|
||||||
list_del(&cur_td->cancelled_td_list);
|
list_del_init(&cur_td->cancelled_td_list);
|
||||||
xhci_giveback_urb_in_irq(xhci, cur_td,
|
xhci_giveback_urb_in_irq(xhci, cur_td,
|
||||||
-ESHUTDOWN, "killed");
|
-ESHUTDOWN, "killed");
|
||||||
}
|
}
|
||||||
|
@ -873,7 +888,7 @@ void xhci_stop_endpoint_command_watchdog(unsigned long arg)
|
||||||
&temp_ep->cancelled_td_list,
|
&temp_ep->cancelled_td_list,
|
||||||
struct xhci_td,
|
struct xhci_td,
|
||||||
cancelled_td_list);
|
cancelled_td_list);
|
||||||
list_del(&cur_td->cancelled_td_list);
|
list_del_init(&cur_td->cancelled_td_list);
|
||||||
xhci_giveback_urb_in_irq(xhci, cur_td,
|
xhci_giveback_urb_in_irq(xhci, cur_td,
|
||||||
-ESHUTDOWN, "killed");
|
-ESHUTDOWN, "killed");
|
||||||
}
|
}
|
||||||
|
@ -1565,10 +1580,10 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||||
else
|
else
|
||||||
*status = 0;
|
*status = 0;
|
||||||
}
|
}
|
||||||
list_del(&td->td_list);
|
list_del_init(&td->td_list);
|
||||||
/* Was this TD slated to be cancelled but completed anyway? */
|
/* Was this TD slated to be cancelled but completed anyway? */
|
||||||
if (!list_empty(&td->cancelled_td_list))
|
if (!list_empty(&td->cancelled_td_list))
|
||||||
list_del(&td->cancelled_td_list);
|
list_del_init(&td->cancelled_td_list);
|
||||||
|
|
||||||
urb_priv->td_cnt++;
|
urb_priv->td_cnt++;
|
||||||
/* Giveback the urb when all the tds are completed */
|
/* Giveback the urb when all the tds are completed */
|
||||||
|
@ -2500,11 +2515,8 @@ static int prepare_transfer(struct xhci_hcd *xhci,
|
||||||
|
|
||||||
if (td_index == 0) {
|
if (td_index == 0) {
|
||||||
ret = usb_hcd_link_urb_to_ep(bus_to_hcd(urb->dev->bus), urb);
|
ret = usb_hcd_link_urb_to_ep(bus_to_hcd(urb->dev->bus), urb);
|
||||||
if (unlikely(ret)) {
|
if (unlikely(ret))
|
||||||
xhci_urb_free_priv(xhci, urb_priv);
|
|
||||||
urb->hcpriv = NULL;
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
td->urb = urb;
|
td->urb = urb;
|
||||||
|
@ -2672,6 +2684,10 @@ static u32 xhci_v1_0_td_remainder(int running_total, int trb_buff_len,
|
||||||
{
|
{
|
||||||
int packets_transferred;
|
int packets_transferred;
|
||||||
|
|
||||||
|
/* One TRB with a zero-length data packet. */
|
||||||
|
if (running_total == 0 && trb_buff_len == 0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* All the TRB queueing functions don't count the current TRB in
|
/* All the TRB queueing functions don't count the current TRB in
|
||||||
* running_total.
|
* running_total.
|
||||||
*/
|
*/
|
||||||
|
@ -3113,21 +3129,16 @@ static int count_isoc_trbs_needed(struct xhci_hcd *xhci,
|
||||||
struct urb *urb, int i)
|
struct urb *urb, int i)
|
||||||
{
|
{
|
||||||
int num_trbs = 0;
|
int num_trbs = 0;
|
||||||
u64 addr, td_len, running_total;
|
u64 addr, td_len;
|
||||||
|
|
||||||
addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
|
addr = (u64) (urb->transfer_dma + urb->iso_frame_desc[i].offset);
|
||||||
td_len = urb->iso_frame_desc[i].length;
|
td_len = urb->iso_frame_desc[i].length;
|
||||||
|
|
||||||
running_total = TRB_MAX_BUFF_SIZE - (addr & (TRB_MAX_BUFF_SIZE - 1));
|
num_trbs = DIV_ROUND_UP(td_len + (addr & (TRB_MAX_BUFF_SIZE - 1)),
|
||||||
running_total &= TRB_MAX_BUFF_SIZE - 1;
|
TRB_MAX_BUFF_SIZE);
|
||||||
if (running_total != 0)
|
if (num_trbs == 0)
|
||||||
num_trbs++;
|
num_trbs++;
|
||||||
|
|
||||||
while (running_total < td_len) {
|
|
||||||
num_trbs++;
|
|
||||||
running_total += TRB_MAX_BUFF_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
return num_trbs;
|
return num_trbs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3226,6 +3237,7 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
start_trb = &ep_ring->enqueue->generic;
|
start_trb = &ep_ring->enqueue->generic;
|
||||||
start_cycle = ep_ring->cycle_state;
|
start_cycle = ep_ring->cycle_state;
|
||||||
|
|
||||||
|
urb_priv = urb->hcpriv;
|
||||||
/* Queue the first TRB, even if it's zero-length */
|
/* Queue the first TRB, even if it's zero-length */
|
||||||
for (i = 0; i < num_tds; i++) {
|
for (i = 0; i < num_tds; i++) {
|
||||||
unsigned int total_packet_count;
|
unsigned int total_packet_count;
|
||||||
|
@ -3237,9 +3249,11 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
addr = start_addr + urb->iso_frame_desc[i].offset;
|
addr = start_addr + urb->iso_frame_desc[i].offset;
|
||||||
td_len = urb->iso_frame_desc[i].length;
|
td_len = urb->iso_frame_desc[i].length;
|
||||||
td_remain_len = td_len;
|
td_remain_len = td_len;
|
||||||
/* FIXME: Ignoring zero-length packets, can those happen? */
|
|
||||||
total_packet_count = roundup(td_len,
|
total_packet_count = roundup(td_len,
|
||||||
le16_to_cpu(urb->ep->desc.wMaxPacketSize));
|
le16_to_cpu(urb->ep->desc.wMaxPacketSize));
|
||||||
|
/* A zero-length transfer still involves at least one packet. */
|
||||||
|
if (total_packet_count == 0)
|
||||||
|
total_packet_count++;
|
||||||
burst_count = xhci_get_burst_count(xhci, urb->dev, urb,
|
burst_count = xhci_get_burst_count(xhci, urb->dev, urb,
|
||||||
total_packet_count);
|
total_packet_count);
|
||||||
residue = xhci_get_last_burst_packet_count(xhci,
|
residue = xhci_get_last_burst_packet_count(xhci,
|
||||||
|
@ -3249,12 +3263,13 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
|
|
||||||
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
|
ret = prepare_transfer(xhci, xhci->devs[slot_id], ep_index,
|
||||||
urb->stream_id, trbs_per_td, urb, i, mem_flags);
|
urb->stream_id, trbs_per_td, urb, i, mem_flags);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
return ret;
|
if (i == 0)
|
||||||
|
return ret;
|
||||||
|
goto cleanup;
|
||||||
|
}
|
||||||
|
|
||||||
urb_priv = urb->hcpriv;
|
|
||||||
td = urb_priv->td[i];
|
td = urb_priv->td[i];
|
||||||
|
|
||||||
for (j = 0; j < trbs_per_td; j++) {
|
for (j = 0; j < trbs_per_td; j++) {
|
||||||
u32 remainder = 0;
|
u32 remainder = 0;
|
||||||
field = TRB_TBC(burst_count) | TRB_TLBPC(residue);
|
field = TRB_TBC(burst_count) | TRB_TLBPC(residue);
|
||||||
|
@ -3344,6 +3359,27 @@ static int xhci_queue_isoc_tx(struct xhci_hcd *xhci, gfp_t mem_flags,
|
||||||
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
giveback_first_trb(xhci, slot_id, ep_index, urb->stream_id,
|
||||||
start_cycle, start_trb);
|
start_cycle, start_trb);
|
||||||
return 0;
|
return 0;
|
||||||
|
cleanup:
|
||||||
|
/* Clean up a partially enqueued isoc transfer. */
|
||||||
|
|
||||||
|
for (i--; i >= 0; i--)
|
||||||
|
list_del_init(&urb_priv->td[i]->td_list);
|
||||||
|
|
||||||
|
/* Use the first TD as a temporary variable to turn the TDs we've queued
|
||||||
|
* into No-ops with a software-owned cycle bit. That way the hardware
|
||||||
|
* won't accidentally start executing bogus TDs when we partially
|
||||||
|
* overwrite them. td->first_trb and td->start_seg are already set.
|
||||||
|
*/
|
||||||
|
urb_priv->td[0]->last_trb = ep_ring->enqueue;
|
||||||
|
/* Every TRB except the first & last will have its cycle bit flipped. */
|
||||||
|
td_to_noop(xhci, ep_ring, urb_priv->td[0], true);
|
||||||
|
|
||||||
|
/* Reset the ring enqueue back to the first TRB and its cycle bit. */
|
||||||
|
ep_ring->enqueue = urb_priv->td[0]->first_trb;
|
||||||
|
ep_ring->enq_seg = urb_priv->td[0]->start_seg;
|
||||||
|
ep_ring->cycle_state = start_cycle;
|
||||||
|
usb_hcd_unlink_urb_from_ep(bus_to_hcd(urb->dev->bus), urb);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -1085,8 +1085,11 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||||
if (urb->dev->speed == USB_SPEED_FULL) {
|
if (urb->dev->speed == USB_SPEED_FULL) {
|
||||||
ret = xhci_check_maxpacket(xhci, slot_id,
|
ret = xhci_check_maxpacket(xhci, slot_id,
|
||||||
ep_index, urb);
|
ep_index, urb);
|
||||||
if (ret < 0)
|
if (ret < 0) {
|
||||||
|
xhci_urb_free_priv(xhci, urb_priv);
|
||||||
|
urb->hcpriv = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* We have a spinlock and interrupts disabled, so we must pass
|
/* We have a spinlock and interrupts disabled, so we must pass
|
||||||
|
@ -1097,6 +1100,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||||
goto dying;
|
goto dying;
|
||||||
ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
|
ret = xhci_queue_ctrl_tx(xhci, GFP_ATOMIC, urb,
|
||||||
slot_id, ep_index);
|
slot_id, ep_index);
|
||||||
|
if (ret)
|
||||||
|
goto free_priv;
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
} else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
|
} else if (usb_endpoint_xfer_bulk(&urb->ep->desc)) {
|
||||||
spin_lock_irqsave(&xhci->lock, flags);
|
spin_lock_irqsave(&xhci->lock, flags);
|
||||||
|
@ -1117,6 +1122,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||||
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
|
ret = xhci_queue_bulk_tx(xhci, GFP_ATOMIC, urb,
|
||||||
slot_id, ep_index);
|
slot_id, ep_index);
|
||||||
}
|
}
|
||||||
|
if (ret)
|
||||||
|
goto free_priv;
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
} else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
|
} else if (usb_endpoint_xfer_int(&urb->ep->desc)) {
|
||||||
spin_lock_irqsave(&xhci->lock, flags);
|
spin_lock_irqsave(&xhci->lock, flags);
|
||||||
|
@ -1124,6 +1131,8 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||||
goto dying;
|
goto dying;
|
||||||
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
|
ret = xhci_queue_intr_tx(xhci, GFP_ATOMIC, urb,
|
||||||
slot_id, ep_index);
|
slot_id, ep_index);
|
||||||
|
if (ret)
|
||||||
|
goto free_priv;
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
} else {
|
} else {
|
||||||
spin_lock_irqsave(&xhci->lock, flags);
|
spin_lock_irqsave(&xhci->lock, flags);
|
||||||
|
@ -1131,18 +1140,22 @@ int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flags)
|
||||||
goto dying;
|
goto dying;
|
||||||
ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
|
ret = xhci_queue_isoc_tx_prepare(xhci, GFP_ATOMIC, urb,
|
||||||
slot_id, ep_index);
|
slot_id, ep_index);
|
||||||
|
if (ret)
|
||||||
|
goto free_priv;
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
}
|
}
|
||||||
exit:
|
exit:
|
||||||
return ret;
|
return ret;
|
||||||
dying:
|
dying:
|
||||||
xhci_urb_free_priv(xhci, urb_priv);
|
|
||||||
urb->hcpriv = NULL;
|
|
||||||
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
|
xhci_dbg(xhci, "Ep 0x%x: URB %p submitted for "
|
||||||
"non-responsive xHCI host.\n",
|
"non-responsive xHCI host.\n",
|
||||||
urb->ep->desc.bEndpointAddress, urb);
|
urb->ep->desc.bEndpointAddress, urb);
|
||||||
|
ret = -ESHUTDOWN;
|
||||||
|
free_priv:
|
||||||
|
xhci_urb_free_priv(xhci, urb_priv);
|
||||||
|
urb->hcpriv = NULL;
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
return -ESHUTDOWN;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Get the right ring for the given URB.
|
/* Get the right ring for the given URB.
|
||||||
|
@ -1239,6 +1252,13 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
|
||||||
if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
|
if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
|
||||||
xhci_dbg(xhci, "HW died, freeing TD.\n");
|
xhci_dbg(xhci, "HW died, freeing TD.\n");
|
||||||
urb_priv = urb->hcpriv;
|
urb_priv = urb->hcpriv;
|
||||||
|
for (i = urb_priv->td_cnt; i < urb_priv->length; i++) {
|
||||||
|
td = urb_priv->td[i];
|
||||||
|
if (!list_empty(&td->td_list))
|
||||||
|
list_del_init(&td->td_list);
|
||||||
|
if (!list_empty(&td->cancelled_td_list))
|
||||||
|
list_del_init(&td->cancelled_td_list);
|
||||||
|
}
|
||||||
|
|
||||||
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
usb_hcd_unlink_urb_from_ep(hcd, urb);
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
|
|
Loading…
Reference in a new issue