diff options
Diffstat (limited to 'drivers/usb/host/xhci-ring.c')
-rw-r--r-- | drivers/usb/host/xhci-ring.c | 168 |
1 files changed, 159 insertions, 9 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c index ece0787d62bf..5bfe47816ac3 100644 --- a/drivers/usb/host/xhci-ring.c +++ b/drivers/usb/host/xhci-ring.c @@ -310,7 +310,7 @@ static void xhci_handle_stopped_cmd_ring(struct xhci_hcd *xhci, i_cmd->status = COMP_CMD_STOP; - xhci_dbg(xhci, "Turn aborted command %p to no-op\n", + xhci_dbg(xhci, "Turn aborted command %pK to no-op\n", i_cmd->command_trb); /* get cycle state from the original cmd trb */ cycle_state = le32_to_cpu( @@ -592,7 +592,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci, "Cycle state = 0x%x", state->new_cycle_state); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "New dequeue segment = %p (virtual)", + "New dequeue segment = %pK (virtual)", state->new_deq_seg); addr = xhci_trb_virt_to_dma(state->new_deq_seg, state->new_deq_ptr); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, @@ -627,8 +627,8 @@ static void td_to_noop(struct xhci_hcd *xhci, struct xhci_ring *ep_ring, xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, "Cancel (unchain) link TRB"); xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Address = %p (0x%llx dma); " - "in seg %p (0x%llx dma)", + "Address = %pK (0x%llx dma); " + "in seg %pK (0x%llx dma)", cur_trb, (unsigned long long)xhci_trb_virt_to_dma(cur_seg, cur_trb), cur_seg, @@ -764,7 +764,7 @@ static void xhci_handle_cmd_stop_ep(struct xhci_hcd *xhci, int slot_id, * short, don't muck with the stream ID after * submission. */ - xhci_warn(xhci, "WARN Cancelled URB %p " + xhci_warn(xhci, "WARN Cancelled URB %pK " "has invalid stream ID %u.\n", cur_td->urb, cur_td->urb->stream_id); @@ -1103,7 +1103,7 @@ static void xhci_handle_cmd_set_deq(struct xhci_hcd *xhci, int slot_id, ep_ring, ep_index); } else { xhci_warn(xhci, "Mismatch between completed Set TR Deq Ptr command & xHCI internal state.\n"); - xhci_warn(xhci, "ep deq seg = %p, deq ptr = %p\n", + xhci_warn(xhci, "ep deq seg = %pK, deq ptr = %pK\n", ep->queued_deq_seg, ep->queued_deq_ptr); } } @@ -2624,7 +2624,7 @@ cleanup: URB_SHORT_NOT_OK)) || (status != 0 && !usb_endpoint_xfer_isoc(&urb->ep->desc))) - xhci_dbg(xhci, "Giveback URB %p, len = %d, " + xhci_dbg(xhci, "Giveback URB %pK, len = %d, " "expected = %d, status = %d\n", urb, urb->actual_length, urb->transfer_buffer_length, @@ -3574,6 +3574,156 @@ int xhci_queue_ctrl_tx(struct xhci_hcd *xhci, gfp_t mem_flags, return 0; } +/* + * Variant of xhci_queue_ctrl_tx() used to implement EHSET + * SINGLE_STEP_SET_FEATURE test mode. It differs in that the control + * transfer is broken up so that the SETUP stage can happen and call + * the URB's completion handler before the DATA/STATUS stages are + * executed by the xHC hardware. This assumes the control transfer is a + * GetDescriptor, with a DATA stage in the IN direction, and an OUT + * STATUS stage. + * + * This function is called twice, usually with a 15-second delay in between. + * - with is_setup==true, the SETUP stage for the control request + * (GetDescriptor) is queued in the TRB ring and sent to HW immediately + * - with is_setup==false, the DATA and STATUS TRBs are queued and exceuted + * + * Caller must have locked xhci->lock + */ +int xhci_submit_single_step_set_feature(struct usb_hcd *hcd, struct urb *urb, + int is_setup) +{ + struct xhci_hcd *xhci = hcd_to_xhci(hcd); + struct xhci_ring *ep_ring; + int num_trbs; + int ret; + unsigned int slot_id, ep_index; + struct usb_ctrlrequest *setup; + struct xhci_generic_trb *start_trb; + int start_cycle; + u32 field, length_field, remainder; + struct urb_priv *urb_priv; + struct xhci_td *td; + + ep_ring = xhci_urb_to_transfer_ring(xhci, urb); + if (!ep_ring) + return -EINVAL; + + /* Need buffer for data stage */ + if (urb->transfer_buffer_length <= 0) + return -EINVAL; + + /* + * Need to copy setup packet into setup TRB, so we can't use the setup + * DMA address. + */ + if (!urb->setup_packet) + return -EINVAL; + setup = (struct usb_ctrlrequest *) urb->setup_packet; + + slot_id = urb->dev->slot_id; + ep_index = xhci_get_endpoint_index(&urb->ep->desc); + + urb_priv = kzalloc(sizeof(struct urb_priv) + + sizeof(struct xhci_td *), GFP_ATOMIC); + if (!urb_priv) + return -ENOMEM; + + td = urb_priv->td[0] = kzalloc(sizeof(struct xhci_td), GFP_ATOMIC); + if (!td) { + kfree(urb_priv); + return -ENOMEM; + } + + urb_priv->length = 1; + urb_priv->td_cnt = 0; + urb->hcpriv = urb_priv; + + num_trbs = is_setup ? 1 : 2; + + ret = prepare_transfer(xhci, xhci->devs[slot_id], + ep_index, urb->stream_id, + num_trbs, urb, 0, GFP_ATOMIC); + if (ret < 0) { + kfree(td); + kfree(urb_priv); + return ret; + } + + /* + * Don't give the first TRB to the hardware (by toggling the cycle bit) + * until we've finished creating all the other TRBs. The ring's cycle + * state may change as we enqueue the other TRBs, so save it too. + */ + start_trb = &ep_ring->enqueue->generic; + start_cycle = ep_ring->cycle_state; + + if (is_setup) { + /* Queue only the setup TRB */ + field = TRB_IDT | TRB_IOC | TRB_TYPE(TRB_SETUP); + if (start_cycle == 0) + field |= 0x1; + + /* xHCI 1.0 6.4.1.2.1: Transfer Type field */ + if (xhci->hci_version == 0x100) { + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_TX_TYPE(TRB_DATA_IN); + else + field |= TRB_TX_TYPE(TRB_DATA_OUT); + } + + /* Save the DMA address of the last TRB in the TD */ + td->last_trb = ep_ring->enqueue; + + queue_trb(xhci, ep_ring, false, + setup->bRequestType | setup->bRequest << 8 | + le16_to_cpu(setup->wValue) << 16, + le16_to_cpu(setup->wIndex) | + le16_to_cpu(setup->wLength) << 16, + TRB_LEN(8) | TRB_INTR_TARGET(0), + field); + } else { + /* Queue data TRB */ + field = TRB_ISP | TRB_TYPE(TRB_DATA); + if (start_cycle == 0) + field |= 0x1; + if (setup->bRequestType & USB_DIR_IN) + field |= TRB_DIR_IN; + + remainder = xhci_td_remainder(xhci, 0, + urb->transfer_buffer_length, + urb->transfer_buffer_length, + urb, 1); + + length_field = TRB_LEN(urb->transfer_buffer_length) | + TRB_TD_SIZE(remainder) | + TRB_INTR_TARGET(0); + + queue_trb(xhci, ep_ring, true, + lower_32_bits(urb->transfer_dma), + upper_32_bits(urb->transfer_dma), + length_field, + field); + + /* Save the DMA address of the last TRB in the TD */ + td->last_trb = ep_ring->enqueue; + + /* Queue status TRB */ + field = TRB_IOC | TRB_TYPE(TRB_STATUS); + if (!(setup->bRequestType & USB_DIR_IN)) + field |= TRB_DIR_IN; + + queue_trb(xhci, ep_ring, false, + 0, + 0, + TRB_INTR_TARGET(0), + field | ep_ring->cycle_state); + } + + giveback_first_trb(xhci, slot_id, ep_index, 0, start_cycle, start_trb); + return 0; +} + static int count_isoc_trbs_needed(struct xhci_hcd *xhci, struct urb *urb, int i) { @@ -4178,7 +4328,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, int ret; xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb, - "Set TR Deq Ptr cmd, new deq seg = %p (0x%llx dma), new deq ptr = %p (0x%llx dma), new cycle = %u", + "Set TR Deq Ptr cmd, new deq seg = %pK (0x%llx dma), new deq ptr = %pK (0x%llx dma), new cycle = %u", deq_state->new_deq_seg, (unsigned long long)deq_state->new_deq_seg->dma, deq_state->new_deq_ptr, @@ -4190,7 +4340,7 @@ void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci, deq_state->new_deq_ptr); if (addr == 0) { xhci_warn(xhci, "WARN Cannot submit Set TR Deq Ptr\n"); - xhci_warn(xhci, "WARN deq seg = %p, deq pt = %p\n", + xhci_warn(xhci, "WARN deq seg = %pK, deq pt = %pK\n", deq_state->new_deq_seg, deq_state->new_deq_ptr); return; } |