2017-11-03 18:28:30 +08:00
|
|
|
// SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
|
2013-03-12 08:47:59 +08:00
|
|
|
/*
|
|
|
|
* hcd_intr.c - DesignWare HS OTG Controller host-mode interrupt handling
|
|
|
|
*
|
|
|
|
* Copyright (C) 2004-2013 Synopsys, Inc.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions, and the following disclaimer,
|
|
|
|
* without modification.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* 3. The names of the above-listed copyright holders may not be used
|
|
|
|
* to endorse or promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* ALTERNATIVELY, this software may be distributed under the terms of the
|
|
|
|
* GNU General Public License ("GPL") as published by the Free Software
|
|
|
|
* Foundation; either version 2 of the License, or (at your option) any
|
|
|
|
* later version.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
|
|
|
|
* IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
|
|
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
|
|
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
|
|
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
|
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
|
|
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
|
|
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
|
|
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
|
|
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This file contains the interrupt handlers for Host mode
|
|
|
|
*/
|
|
|
|
#include <linux/kernel.h>
|
|
|
|
#include <linux/module.h>
|
|
|
|
#include <linux/spinlock.h>
|
|
|
|
#include <linux/interrupt.h>
|
|
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <linux/io.h>
|
|
|
|
#include <linux/slab.h>
|
|
|
|
#include <linux/usb.h>
|
|
|
|
|
|
|
|
#include <linux/usb/hcd.h>
|
|
|
|
#include <linux/usb/ch11.h>
|
|
|
|
|
|
|
|
#include "core.h"
|
|
|
|
#include "hcd.h"
|
|
|
|
|
usb: dwc2: host: Don't retry NAKed transactions right away
On rk3288-veyron devices on Chrome OS it was found that plugging in an
Arduino-based USB device could cause the system to lockup, especially
if the CPU Frequency was at one of the slower operating points (like
100 MHz / 200 MHz).
Upon tracing, I found that the following was happening:
* The USB device (full speed) was connected to a high speed hub and
then to the rk3288. Thus, we were dealing with split transactions,
which is all handled in software on dwc2.
* Userspace was initiating a BULK IN transfer
* When we sent the SSPLIT (to start the split transaction), we got an
ACK. Good. Then we issued the CSPLIT.
* When we sent the CSPLIT, we got back a NAK. We immediately (from
the interrupt handler) started to retry and sent another SSPLIT.
* The device kept NAKing our CSPLIT, so we kept ping-ponging between
sending a SSPLIT and a CSPLIT, each time sending from the interrupt
handler.
* The handling of the interrupts was (because of the low CPU speed and
the inefficiency of the dwc2 interrupt handler) was actually taking
_longer_ than it took the other side to send the ACK/NAK. Thus we
were _always_ in the USB interrupt routine.
* The fact that USB interrupts were always going off was preventing
other things from happening in the system. This included preventing
the system from being able to transition to a higher CPU frequency.
As I understand it, there is no requirement to retry super quickly
after a NAK, we just have to retry sometime in the future. Thus one
solution to the above is to just add a delay between getting a NAK and
retrying the transmission. If this delay is sufficiently long to get
out of the interrupt routine then the rest of the system will be able
to make forward progress. Even a 25 us delay would probably be
enough, but we'll be extra conservative and try to delay 1 ms (the
exact amount depends on HZ and the accuracy of the jiffy and how close
the current jiffy is to ticking, but could be as much as 20 ms or as
little as 1 ms).
Presumably adding a delay like this could impact the USB throughput,
so we only add the delay with repeated NAKs.
NOTE: Upon further testing of a pl2303 serial adapter, I found that
this fix may help with problems there. Specifically I found that the
pl2303 serial adapters tend to respond with a NAK when they have
nothing to say and thus we end with this same sequence.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2017-12-13 02:30:31 +08:00
|
|
|
/*
|
|
|
|
* If we get this many NAKs on a split transaction we'll slow down
|
|
|
|
* retransmission. A 1 here means delay after the first NAK.
|
|
|
|
*/
|
|
|
|
#define DWC2_NAKS_BEFORE_DELAY 3
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
/* This function is for debug only */
|
|
|
|
static void dwc2_track_missed_sofs(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
u16 curr_frame_number = hsotg->frame_number;
|
2016-01-29 10:20:07 +08:00
|
|
|
u16 expected = dwc2_frame_num_inc(hsotg->last_frame_num, 1);
|
|
|
|
|
|
|
|
if (expected != curr_frame_number)
|
|
|
|
dwc2_sch_vdbg(hsotg, "MISSED SOF %04x != %04x\n",
|
2017-01-18 12:30:27 +08:00
|
|
|
expected, curr_frame_number);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2016-01-29 10:20:07 +08:00
|
|
|
#ifdef CONFIG_USB_DWC2_TRACK_MISSED_SOFS
|
2013-03-12 08:47:59 +08:00
|
|
|
if (hsotg->frame_num_idx < FRAME_NUM_ARRAY_SIZE) {
|
2016-01-29 10:20:07 +08:00
|
|
|
if (expected != curr_frame_number) {
|
2013-03-12 08:47:59 +08:00
|
|
|
hsotg->frame_num_array[hsotg->frame_num_idx] =
|
|
|
|
curr_frame_number;
|
|
|
|
hsotg->last_frame_num_array[hsotg->frame_num_idx] =
|
|
|
|
hsotg->last_frame_num;
|
|
|
|
hsotg->frame_num_idx++;
|
|
|
|
}
|
|
|
|
} else if (!hsotg->dumped_frame_num_array) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
dev_info(hsotg->dev, "Frame Last Frame\n");
|
|
|
|
dev_info(hsotg->dev, "----- ----------\n");
|
|
|
|
for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) {
|
|
|
|
dev_info(hsotg->dev, "0x%04x 0x%04x\n",
|
|
|
|
hsotg->frame_num_array[i],
|
|
|
|
hsotg->last_frame_num_array[i]);
|
|
|
|
}
|
|
|
|
hsotg->dumped_frame_num_array = 1;
|
|
|
|
}
|
|
|
|
#endif
|
2016-01-29 10:20:07 +08:00
|
|
|
hsotg->last_frame_num = curr_frame_number;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
static void dwc2_hc_handle_tt_clear(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
usb: dwc2: host: There's not really a TT for the root hub
I find that when I plug a full speed (NOT high speed) hub into a dwc2
port and then I plug a bunch of devices into that full speed hub that
dwc2 goes bat guano crazy. Specifically, it just spews errors like this
in the console:
usb usb1: clear tt 1 (9043) error -22
The specific test case I used looks like this:
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
|__ Port 1: Dev 17, If 0, Class=Hub, Driver=hub/4p, 12M
|__ Port 2: Dev 19, If 0, ..., Driver=usbhid, 1.5M
|__ Port 4: Dev 20, If 0, ..., Driver=usbhid, 12M
|__ Port 4: Dev 20, If 1, ..., Driver=usbhid, 12M
|__ Port 4: Dev 20, If 2, ..., Driver=usbhid, 12M
Showing VID/PID:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 017: ID 03eb:3301 Atmel Corp. at43301 4-Port Hub
Bus 001 Device 020: ID 045e:0745 Microsoft Corp. Nano Transceiver ...
Bus 001 Device 019: ID 046d:c404 Logitech, Inc. TrackMan Wheel
I spent a bunch of time trying to figure out why there are errors to
begin with. I believe that the issue may be a hardware issue where the
transceiver sometimes accidentally sends a PREAMBLE packet if you send a
packet to a full speed device right after one to a low speed device.
Luckily the USB driver retries and the second time things work OK.
In any case, things kinda seem work despite the errors, except for the
"clear tt" spew mucking up my console. Chalk it up for a win for
retries and robust protocols.
So getting back to the "clear tt" problem, it appears that we get those
because there's not actually a TT here to clear. It's my understanding
that when dwc2 operates in low speed or full speed mode that there's no
real TT out there. That makes all these attempts to "clear the TT"
somewhat meaningless and also causes the spew in the log.
Let's just skip all the useless TT clears. Eventually we should root
cause the errors, but even if we do this is still a proper fix and is
likely to avoid the "clear tt" error in the future.
Note that hooking up a Full Speed USB Audio Device (Jabra 510) to this
same hub with the keyboard / trackball shows that even audio works over
this janky connection. As a point to note, this particular change (skip
bogus TT clears) compared to just commenting out the dev_err() in
hub_tt_work() actually produces better audio.
Note: don't ask me where I got a full speed USB hub or whether the
massive amount of dust that accumulated on it while it was in my junk
box affected its funtionality. Just smile and nod.
Acked-by: John Youn <johnyoun@synopsys.com>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
2016-01-29 10:20:02 +08:00
|
|
|
struct usb_device *root_hub = dwc2_hsotg_to_hcd(hsotg)->self.root_hub;
|
2013-03-12 08:47:59 +08:00
|
|
|
struct urb *usb_urb;
|
|
|
|
|
2013-07-14 05:53:50 +08:00
|
|
|
if (!chan->qh)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (chan->qh->dev_speed == USB_SPEED_HIGH)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (!qtd->urb)
|
2013-03-12 08:47:59 +08:00
|
|
|
return;
|
|
|
|
|
|
|
|
usb_urb = qtd->urb->priv;
|
2013-07-14 05:53:50 +08:00
|
|
|
if (!usb_urb || !usb_urb->dev || !usb_urb->dev->tt)
|
2013-03-12 08:47:59 +08:00
|
|
|
return;
|
|
|
|
|
usb: dwc2: host: There's not really a TT for the root hub
I find that when I plug a full speed (NOT high speed) hub into a dwc2
port and then I plug a bunch of devices into that full speed hub that
dwc2 goes bat guano crazy. Specifically, it just spews errors like this
in the console:
usb usb1: clear tt 1 (9043) error -22
The specific test case I used looks like this:
/: Bus 01.Port 1: Dev 1, Class=root_hub, Driver=dwc2/1p, 480M
|__ Port 1: Dev 17, If 0, Class=Hub, Driver=hub/4p, 12M
|__ Port 2: Dev 19, If 0, ..., Driver=usbhid, 1.5M
|__ Port 4: Dev 20, If 0, ..., Driver=usbhid, 12M
|__ Port 4: Dev 20, If 1, ..., Driver=usbhid, 12M
|__ Port 4: Dev 20, If 2, ..., Driver=usbhid, 12M
Showing VID/PID:
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 001 Device 017: ID 03eb:3301 Atmel Corp. at43301 4-Port Hub
Bus 001 Device 020: ID 045e:0745 Microsoft Corp. Nano Transceiver ...
Bus 001 Device 019: ID 046d:c404 Logitech, Inc. TrackMan Wheel
I spent a bunch of time trying to figure out why there are errors to
begin with. I believe that the issue may be a hardware issue where the
transceiver sometimes accidentally sends a PREAMBLE packet if you send a
packet to a full speed device right after one to a low speed device.
Luckily the USB driver retries and the second time things work OK.
In any case, things kinda seem work despite the errors, except for the
"clear tt" spew mucking up my console. Chalk it up for a win for
retries and robust protocols.
So getting back to the "clear tt" problem, it appears that we get those
because there's not actually a TT here to clear. It's my understanding
that when dwc2 operates in low speed or full speed mode that there's no
real TT out there. That makes all these attempts to "clear the TT"
somewhat meaningless and also causes the spew in the log.
Let's just skip all the useless TT clears. Eventually we should root
cause the errors, but even if we do this is still a proper fix and is
likely to avoid the "clear tt" error in the future.
Note that hooking up a Full Speed USB Audio Device (Jabra 510) to this
same hub with the keyboard / trackball shows that even audio works over
this janky connection. As a point to note, this particular change (skip
bogus TT clears) compared to just commenting out the dev_err() in
hub_tt_work() actually produces better audio.
Note: don't ask me where I got a full speed USB hub or whether the
massive amount of dust that accumulated on it while it was in my junk
box affected its funtionality. Just smile and nod.
Acked-by: John Youn <johnyoun@synopsys.com>
Reviewed-by: Kever Yang <kever.yang@rock-chips.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@kernel.org>
2016-01-29 10:20:02 +08:00
|
|
|
/*
|
|
|
|
* The root hub doesn't really have a TT, but Linux thinks it
|
|
|
|
* does because how could you have a "high speed hub" that
|
|
|
|
* directly talks directly to low speed devices without a TT?
|
|
|
|
* It's all lies. Lies, I tell you.
|
|
|
|
*/
|
|
|
|
if (usb_urb->dev->tt->hub == root_hub)
|
|
|
|
return;
|
|
|
|
|
2013-07-14 05:53:50 +08:00
|
|
|
if (qtd->urb->status != -EPIPE && qtd->urb->status != -EREMOTEIO) {
|
2013-03-12 08:47:59 +08:00
|
|
|
chan->qh->tt_buffer_dirty = 1;
|
|
|
|
if (usb_hub_clear_tt_buffer(usb_urb))
|
|
|
|
/* Clear failed; let's hope things work anyway */
|
|
|
|
chan->qh->tt_buffer_dirty = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles the start-of-frame interrupt in host mode. Non-periodic
|
|
|
|
* transactions may be queued to the DWC_otg controller for the current
|
|
|
|
* (micro)frame. Periodic transactions may be queued to the controller
|
|
|
|
* for the next (micro)frame.
|
|
|
|
*/
|
|
|
|
static void dwc2_sof_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
struct list_head *qh_entry;
|
|
|
|
struct dwc2_qh *qh;
|
|
|
|
enum dwc2_transaction_type tr_type;
|
|
|
|
|
2015-11-21 01:06:28 +08:00
|
|
|
/* Clear interrupt */
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, GINTSTS_SOF, GINTSTS);
|
2015-11-21 01:06:28 +08:00
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
#ifdef DEBUG_SOF
|
|
|
|
dev_vdbg(hsotg->dev, "--Start of Frame Interrupt--\n");
|
|
|
|
#endif
|
|
|
|
|
2013-04-30 03:40:23 +08:00
|
|
|
hsotg->frame_number = dwc2_hcd_get_frame_number(hsotg);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
dwc2_track_missed_sofs(hsotg);
|
|
|
|
|
|
|
|
/* Determine whether any periodic QHs should be executed */
|
|
|
|
qh_entry = hsotg->periodic_sched_inactive.next;
|
|
|
|
while (qh_entry != &hsotg->periodic_sched_inactive) {
|
|
|
|
qh = list_entry(qh_entry, struct dwc2_qh, qh_list_entry);
|
|
|
|
qh_entry = qh_entry->next;
|
2016-01-29 10:20:04 +08:00
|
|
|
if (dwc2_frame_num_le(qh->next_active_frame,
|
|
|
|
hsotg->frame_number)) {
|
|
|
|
dwc2_sch_vdbg(hsotg, "QH=%p ready fn=%04x, nxt=%04x\n",
|
|
|
|
qh, hsotg->frame_number,
|
|
|
|
qh->next_active_frame);
|
2016-01-29 10:19:58 +08:00
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
/*
|
|
|
|
* Move QH to the ready list to be executed next
|
|
|
|
* (micro)frame
|
|
|
|
*/
|
2016-01-29 10:19:56 +08:00
|
|
|
list_move_tail(&qh->qh_list_entry,
|
2017-01-18 12:30:27 +08:00
|
|
|
&hsotg->periodic_sched_ready);
|
2016-01-29 10:19:58 +08:00
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
tr_type = dwc2_hcd_select_transactions(hsotg);
|
|
|
|
if (tr_type != DWC2_TRANSACTION_NONE)
|
|
|
|
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles the Rx FIFO Level Interrupt, which indicates that there is
|
|
|
|
* at least one packet in the Rx FIFO. The packets are moved from the FIFO to
|
|
|
|
* memory if the DWC_otg controller is operating in Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_rx_fifo_level_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
u32 grxsts, chnum, bcnt, dpid, pktsts;
|
|
|
|
struct dwc2_host_chan *chan;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_perio())
|
|
|
|
dev_vdbg(hsotg->dev, "--RxFIFO Level Interrupt--\n");
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
grxsts = dwc2_readl(hsotg, GRXSTSP);
|
2013-08-31 00:45:15 +08:00
|
|
|
chnum = (grxsts & GRXSTS_HCHNUM_MASK) >> GRXSTS_HCHNUM_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
chan = hsotg->hc_ptr_array[chnum];
|
|
|
|
if (!chan) {
|
|
|
|
dev_err(hsotg->dev, "Unable to get corresponding channel\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-08-31 00:45:15 +08:00
|
|
|
bcnt = (grxsts & GRXSTS_BYTECNT_MASK) >> GRXSTS_BYTECNT_SHIFT;
|
|
|
|
dpid = (grxsts & GRXSTS_DPID_MASK) >> GRXSTS_DPID_SHIFT;
|
2013-08-31 00:45:13 +08:00
|
|
|
pktsts = (grxsts & GRXSTS_PKTSTS_MASK) >> GRXSTS_PKTSTS_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/* Packet Status */
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_perio()) {
|
|
|
|
dev_vdbg(hsotg->dev, " Ch num = %d\n", chnum);
|
|
|
|
dev_vdbg(hsotg->dev, " Count = %d\n", bcnt);
|
|
|
|
dev_vdbg(hsotg->dev, " DPID = %d, chan.dpid = %d\n", dpid,
|
|
|
|
chan->data_pid_start);
|
2013-08-31 00:45:13 +08:00
|
|
|
dev_vdbg(hsotg->dev, " PStatus = %d\n", pktsts);
|
2013-04-10 15:55:50 +08:00
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
switch (pktsts) {
|
|
|
|
case GRXSTS_PKTSTS_HCHIN:
|
|
|
|
/* Read the data into the host buffer */
|
|
|
|
if (bcnt > 0) {
|
|
|
|
dwc2_read_packet(hsotg, chan->xfer_buf, bcnt);
|
|
|
|
|
|
|
|
/* Update the HC fields for the next packet received */
|
|
|
|
chan->xfer_count += bcnt;
|
|
|
|
chan->xfer_buf += bcnt;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case GRXSTS_PKTSTS_HCHIN_XFER_COMP:
|
|
|
|
case GRXSTS_PKTSTS_DATATOGGLEERR:
|
|
|
|
case GRXSTS_PKTSTS_HCHHALTED:
|
|
|
|
/* Handled in interrupt, just ignore data */
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(hsotg->dev,
|
|
|
|
"RxFIFO Level Interrupt: Unknown status %d\n", pktsts);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This interrupt occurs when the non-periodic Tx FIFO is half-empty. More
|
|
|
|
* data packets may be written to the FIFO for OUT transfers. More requests
|
|
|
|
* may be written to the non-periodic request queue for IN transfers. This
|
|
|
|
* interrupt is enabled only in Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_np_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
dev_vdbg(hsotg->dev, "--Non-Periodic TxFIFO Empty Interrupt--\n");
|
|
|
|
dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_NON_PERIODIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This interrupt occurs when the periodic Tx FIFO is half-empty. More data
|
|
|
|
* packets may be written to the FIFO for OUT transfers. More requests may be
|
|
|
|
* written to the periodic request queue for IN transfers. This interrupt is
|
|
|
|
* enabled only in Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_perio_tx_fifo_empty_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_perio())
|
|
|
|
dev_vdbg(hsotg->dev, "--Periodic TxFIFO Empty Interrupt--\n");
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_queue_transactions(hsotg, DWC2_TRANSACTION_PERIODIC);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void dwc2_hprt0_enable(struct dwc2_hsotg *hsotg, u32 hprt0,
|
|
|
|
u32 *hprt0_modify)
|
|
|
|
{
|
2016-11-04 08:55:53 +08:00
|
|
|
struct dwc2_core_params *params = &hsotg->params;
|
2013-03-12 08:47:59 +08:00
|
|
|
int do_reset = 0;
|
|
|
|
u32 usbcfg;
|
|
|
|
u32 prtspd;
|
|
|
|
u32 hcfg;
|
2013-04-30 03:42:00 +08:00
|
|
|
u32 fslspclksel;
|
2013-03-12 08:47:59 +08:00
|
|
|
u32 hfir;
|
|
|
|
|
|
|
|
dev_vdbg(hsotg->dev, "%s(%p)\n", __func__, hsotg);
|
|
|
|
|
|
|
|
/* Every time when port enables calculate HFIR.FrInterval */
|
2018-07-26 22:00:13 +08:00
|
|
|
hfir = dwc2_readl(hsotg, HFIR);
|
2013-03-12 08:47:59 +08:00
|
|
|
hfir &= ~HFIR_FRINT_MASK;
|
|
|
|
hfir |= dwc2_calc_frame_interval(hsotg) << HFIR_FRINT_SHIFT &
|
|
|
|
HFIR_FRINT_MASK;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hfir, HFIR);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/* Check if we need to adjust the PHY clock speed for low power */
|
|
|
|
if (!params->host_support_fs_ls_low_power) {
|
|
|
|
/* Port has been enabled, set the reset change flag */
|
|
|
|
hsotg->flags.b.port_reset_change = 1;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
usbcfg = dwc2_readl(hsotg, GUSBCFG);
|
2013-08-31 00:45:13 +08:00
|
|
|
prtspd = (hprt0 & HPRT0_SPD_MASK) >> HPRT0_SPD_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (prtspd == HPRT0_SPD_LOW_SPEED || prtspd == HPRT0_SPD_FULL_SPEED) {
|
|
|
|
/* Low power */
|
|
|
|
if (!(usbcfg & GUSBCFG_PHY_LP_CLK_SEL)) {
|
|
|
|
/* Set PHY low power clock select for FS/LS devices */
|
|
|
|
usbcfg |= GUSBCFG_PHY_LP_CLK_SEL;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
2013-03-12 08:47:59 +08:00
|
|
|
do_reset = 1;
|
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hcfg = dwc2_readl(hsotg, HCFG);
|
2013-08-31 00:45:13 +08:00
|
|
|
fslspclksel = (hcfg & HCFG_FSLSPCLKSEL_MASK) >>
|
|
|
|
HCFG_FSLSPCLKSEL_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (prtspd == HPRT0_SPD_LOW_SPEED &&
|
2017-01-24 06:57:26 +08:00
|
|
|
params->host_ls_low_power_phy_clk) {
|
2013-03-12 08:47:59 +08:00
|
|
|
/* 6 MHZ */
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"FS_PHY programming HCFG to 6 MHz\n");
|
2013-04-30 03:42:00 +08:00
|
|
|
if (fslspclksel != HCFG_FSLSPCLKSEL_6_MHZ) {
|
2013-08-31 00:45:13 +08:00
|
|
|
fslspclksel = HCFG_FSLSPCLKSEL_6_MHZ;
|
2013-03-12 08:47:59 +08:00
|
|
|
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
|
2013-08-31 00:45:13 +08:00
|
|
|
hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hcfg, HCFG);
|
2013-03-12 08:47:59 +08:00
|
|
|
do_reset = 1;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* 48 MHZ */
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"FS_PHY programming HCFG to 48 MHz\n");
|
2013-04-30 03:42:00 +08:00
|
|
|
if (fslspclksel != HCFG_FSLSPCLKSEL_48_MHZ) {
|
2013-08-31 00:45:13 +08:00
|
|
|
fslspclksel = HCFG_FSLSPCLKSEL_48_MHZ;
|
2013-03-12 08:47:59 +08:00
|
|
|
hcfg &= ~HCFG_FSLSPCLKSEL_MASK;
|
2013-08-31 00:45:13 +08:00
|
|
|
hcfg |= fslspclksel << HCFG_FSLSPCLKSEL_SHIFT;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hcfg, HCFG);
|
2013-03-12 08:47:59 +08:00
|
|
|
do_reset = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Not low power */
|
|
|
|
if (usbcfg & GUSBCFG_PHY_LP_CLK_SEL) {
|
|
|
|
usbcfg &= ~GUSBCFG_PHY_LP_CLK_SEL;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, usbcfg, GUSBCFG);
|
2013-03-12 08:47:59 +08:00
|
|
|
do_reset = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (do_reset) {
|
|
|
|
*hprt0_modify |= HPRT0_RST;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, *hprt0_modify, HPRT0);
|
2013-03-12 08:47:59 +08:00
|
|
|
queue_delayed_work(hsotg->wq_otg, &hsotg->reset_work,
|
|
|
|
msecs_to_jiffies(60));
|
|
|
|
} else {
|
|
|
|
/* Port has been enabled, set the reset change flag */
|
|
|
|
hsotg->flags.b.port_reset_change = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* There are multiple conditions that can cause a port interrupt. This function
|
|
|
|
* determines which interrupt conditions have occurred and handles them
|
|
|
|
* appropriately.
|
|
|
|
*/
|
|
|
|
static void dwc2_port_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
u32 hprt0;
|
|
|
|
u32 hprt0_modify;
|
|
|
|
|
|
|
|
dev_vdbg(hsotg->dev, "--Port Interrupt--\n");
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hprt0 = dwc2_readl(hsotg, HPRT0);
|
2013-03-12 08:47:59 +08:00
|
|
|
hprt0_modify = hprt0;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clear appropriate bits in HPRT0 to clear the interrupt bit in
|
|
|
|
* GINTSTS
|
|
|
|
*/
|
|
|
|
hprt0_modify &= ~(HPRT0_ENA | HPRT0_CONNDET | HPRT0_ENACHG |
|
|
|
|
HPRT0_OVRCURRCHG);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Port Connect Detected
|
|
|
|
* Set flag and clear if detected
|
|
|
|
*/
|
|
|
|
if (hprt0 & HPRT0_CONNDET) {
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hprt0_modify | HPRT0_CONNDET, HPRT0);
|
2015-11-21 01:06:28 +08:00
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"--Port Interrupt HPRT0=0x%08x Port Connect Detected--\n",
|
|
|
|
hprt0);
|
2015-11-20 05:23:14 +08:00
|
|
|
dwc2_hcd_connect(hsotg);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* The Hub driver asserts a reset when it sees port connect
|
|
|
|
* status change flag
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Port Enable Changed
|
|
|
|
* Clear if detected - Set internal flag if disabled
|
|
|
|
*/
|
|
|
|
if (hprt0 & HPRT0_ENACHG) {
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hprt0_modify | HPRT0_ENACHG, HPRT0);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" --Port Interrupt HPRT0=0x%08x Port Enable Changed (now %d)--\n",
|
|
|
|
hprt0, !!(hprt0 & HPRT0_ENA));
|
2015-11-20 18:49:28 +08:00
|
|
|
if (hprt0 & HPRT0_ENA) {
|
|
|
|
hsotg->new_connection = true;
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hprt0_enable(hsotg, hprt0, &hprt0_modify);
|
2015-11-20 18:49:28 +08:00
|
|
|
} else {
|
2013-03-12 08:47:59 +08:00
|
|
|
hsotg->flags.b.port_enable_change = 1;
|
2016-11-04 08:55:53 +08:00
|
|
|
if (hsotg->params.dma_desc_fs_enable) {
|
2015-11-20 18:49:28 +08:00
|
|
|
u32 hcfg;
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.dma_desc_enable = false;
|
2015-11-20 18:49:28 +08:00
|
|
|
hsotg->new_connection = false;
|
2018-07-26 22:00:13 +08:00
|
|
|
hcfg = dwc2_readl(hsotg, HCFG);
|
2015-11-20 18:49:28 +08:00
|
|
|
hcfg &= ~HCFG_DESCDMA;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hcfg, HCFG);
|
2015-11-20 18:49:28 +08:00
|
|
|
}
|
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Overcurrent Change Interrupt */
|
|
|
|
if (hprt0 & HPRT0_OVRCURRCHG) {
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hprt0_modify | HPRT0_OVRCURRCHG,
|
|
|
|
HPRT0);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" --Port Interrupt HPRT0=0x%08x Port Overcurrent Changed--\n",
|
|
|
|
hprt0);
|
|
|
|
hsotg->flags.b.port_over_current_change = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Gets the actual length of a transfer after the transfer halts. halt_status
|
|
|
|
* holds the reason for the halt.
|
|
|
|
*
|
|
|
|
* For IN transfers where halt_status is DWC2_HC_XFER_COMPLETE, *short_read
|
|
|
|
* is set to 1 upon return if less than the requested number of bytes were
|
|
|
|
* transferred. short_read may also be NULL on entry, in which case it remains
|
|
|
|
* unchanged.
|
|
|
|
*/
|
|
|
|
static u32 dwc2_get_actual_xfer_length(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status,
|
|
|
|
int *short_read)
|
|
|
|
{
|
|
|
|
u32 hctsiz, count, length;
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (halt_status == DWC2_HC_XFER_COMPLETE) {
|
|
|
|
if (chan->ep_is_in) {
|
2013-08-31 00:45:15 +08:00
|
|
|
count = (hctsiz & TSIZ_XFERSIZE_MASK) >>
|
|
|
|
TSIZ_XFERSIZE_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
length = chan->xfer_len - count;
|
2017-01-18 12:30:27 +08:00
|
|
|
if (short_read)
|
2013-03-12 08:47:59 +08:00
|
|
|
*short_read = (count != 0);
|
|
|
|
} else if (chan->qh->do_split) {
|
|
|
|
length = qtd->ssplit_out_xfer_count;
|
|
|
|
} else {
|
|
|
|
length = chan->xfer_len;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Must use the hctsiz.pktcnt field to determine how much data
|
|
|
|
* has been transferred. This field reflects the number of
|
|
|
|
* packets that have been transferred via the USB. This is
|
|
|
|
* always an integral number of packets if the transfer was
|
|
|
|
* halted before its normal completion. (Can't use the
|
|
|
|
* hctsiz.xfersize field because that reflects the number of
|
|
|
|
* bytes transferred via the AHB, not the USB).
|
|
|
|
*/
|
2013-08-31 00:45:15 +08:00
|
|
|
count = (hctsiz & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
length = (chan->start_pkt_count - count) * chan->max_packet;
|
|
|
|
}
|
|
|
|
|
|
|
|
return length;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dwc2_update_urb_state() - Updates the state of the URB after a Transfer
|
|
|
|
* Complete interrupt on the host channel. Updates the actual_length field
|
|
|
|
* of the URB based on the number of bytes transferred via the host channel.
|
|
|
|
* Sets the URB status if the data transfer is finished.
|
|
|
|
*
|
2018-05-16 16:04:24 +08:00
|
|
|
* @hsotg: Programming view of the DWC_otg controller
|
|
|
|
* @chan: Programming view of host channel
|
|
|
|
* @chnum: Channel number
|
|
|
|
* @urb: Processing URB
|
|
|
|
* @qtd: Queue transfer descriptor
|
|
|
|
*
|
2013-03-12 08:47:59 +08:00
|
|
|
* Return: 1 if the data transfer specified by the URB is completely finished,
|
|
|
|
* 0 otherwise
|
|
|
|
*/
|
|
|
|
static int dwc2_update_urb_state(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_hcd_urb *urb,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
u32 hctsiz;
|
|
|
|
int xfer_done = 0;
|
|
|
|
int short_read = 0;
|
|
|
|
int xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd,
|
|
|
|
DWC2_HC_XFER_COMPLETE,
|
|
|
|
&short_read);
|
|
|
|
|
|
|
|
if (urb->actual_length + xfer_length > urb->length) {
|
|
|
|
dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);
|
|
|
|
xfer_length = urb->length - urb->actual_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_vdbg(hsotg->dev, "urb->actual_length=%d xfer_length=%d\n",
|
|
|
|
urb->actual_length, xfer_length);
|
|
|
|
urb->actual_length += xfer_length;
|
|
|
|
|
|
|
|
if (xfer_length && chan->ep_type == USB_ENDPOINT_XFER_BULK &&
|
|
|
|
(urb->flags & URB_SEND_ZERO_PACKET) &&
|
|
|
|
urb->actual_length >= urb->length &&
|
|
|
|
!(urb->length % chan->max_packet)) {
|
|
|
|
xfer_done = 0;
|
|
|
|
} else if (short_read || urb->actual_length >= urb->length) {
|
|
|
|
xfer_done = 1;
|
|
|
|
urb->status = 0;
|
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
|
|
|
|
__func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
|
|
|
|
dev_vdbg(hsotg->dev, " chan->xfer_len %d\n", chan->xfer_len);
|
|
|
|
dev_vdbg(hsotg->dev, " hctsiz.xfersize %d\n",
|
2013-08-31 00:45:15 +08:00
|
|
|
(hctsiz & TSIZ_XFERSIZE_MASK) >> TSIZ_XFERSIZE_SHIFT);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev, " urb->transfer_buffer_length %d\n", urb->length);
|
|
|
|
dev_vdbg(hsotg->dev, " urb->actual_length %d\n", urb->actual_length);
|
|
|
|
dev_vdbg(hsotg->dev, " short_read %d, xfer_done %d\n", short_read,
|
|
|
|
xfer_done);
|
|
|
|
|
|
|
|
return xfer_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Save the starting data toggle for the next transfer. The data toggle is
|
|
|
|
* saved in the QH for non-control transfers and it's saved in the QTD for
|
|
|
|
* control transfers.
|
|
|
|
*/
|
|
|
|
void dwc2_hcd_save_data_toggle(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
2018-07-26 22:00:13 +08:00
|
|
|
u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2013-08-31 00:45:13 +08:00
|
|
|
u32 pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (chan->ep_type != USB_ENDPOINT_XFER_CONTROL) {
|
2016-02-17 07:02:07 +08:00
|
|
|
if (WARN(!chan || !chan->qh,
|
|
|
|
"chan->qh must be specified for non-control eps\n"))
|
|
|
|
return;
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
if (pid == TSIZ_SC_MC_PID_DATA0)
|
|
|
|
chan->qh->data_toggle = DWC2_HC_PID_DATA0;
|
|
|
|
else
|
|
|
|
chan->qh->data_toggle = DWC2_HC_PID_DATA1;
|
|
|
|
} else {
|
2016-02-17 07:02:07 +08:00
|
|
|
if (WARN(!qtd,
|
|
|
|
"qtd must be specified for control eps\n"))
|
|
|
|
return;
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
if (pid == TSIZ_SC_MC_PID_DATA0)
|
|
|
|
qtd->data_toggle = DWC2_HC_PID_DATA0;
|
|
|
|
else
|
|
|
|
qtd->data_toggle = DWC2_HC_PID_DATA1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dwc2_update_isoc_urb_state() - Updates the state of an Isochronous URB when
|
|
|
|
* the transfer is stopped for any reason. The fields of the current entry in
|
|
|
|
* the frame descriptor array are set based on the transfer state and the input
|
|
|
|
* halt_status. Completes the Isochronous URB if all the URB frames have been
|
|
|
|
* completed.
|
|
|
|
*
|
2018-05-16 16:04:24 +08:00
|
|
|
* @hsotg: Programming view of the DWC_otg controller
|
|
|
|
* @chan: Programming view of host channel
|
|
|
|
* @chnum: Channel number
|
|
|
|
* @halt_status: Reason for halting a host channel
|
|
|
|
* @qtd: Queue transfer descriptor
|
|
|
|
*
|
2013-03-12 08:47:59 +08:00
|
|
|
* Return: DWC2_HC_XFER_COMPLETE if there are more frames remaining to be
|
|
|
|
* transferred in the URB. Otherwise return DWC2_HC_XFER_URB_COMPLETE.
|
|
|
|
*/
|
|
|
|
static enum dwc2_halt_status dwc2_update_isoc_urb_state(
|
|
|
|
struct dwc2_hsotg *hsotg, struct dwc2_host_chan *chan,
|
|
|
|
int chnum, struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
|
|
|
struct dwc2_hcd_urb *urb = qtd->urb;
|
|
|
|
|
|
|
|
if (!urb)
|
|
|
|
return DWC2_HC_XFER_NO_HALT_STATUS;
|
|
|
|
|
|
|
|
frame_desc = &urb->iso_descs[qtd->isoc_frame_index];
|
|
|
|
|
|
|
|
switch (halt_status) {
|
|
|
|
case DWC2_HC_XFER_COMPLETE:
|
|
|
|
frame_desc->status = 0;
|
|
|
|
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
|
|
|
chan, chnum, qtd, halt_status, NULL);
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_FRAME_OVERRUN:
|
|
|
|
urb->error_count++;
|
|
|
|
if (chan->ep_is_in)
|
|
|
|
frame_desc->status = -ENOSR;
|
|
|
|
else
|
|
|
|
frame_desc->status = -ECOMM;
|
|
|
|
frame_desc->actual_length = 0;
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_BABBLE_ERR:
|
|
|
|
urb->error_count++;
|
|
|
|
frame_desc->status = -EOVERFLOW;
|
|
|
|
/* Don't need to update actual_length in this case */
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_XACT_ERR:
|
|
|
|
urb->error_count++;
|
|
|
|
frame_desc->status = -EPROTO;
|
|
|
|
frame_desc->actual_length = dwc2_get_actual_xfer_length(hsotg,
|
|
|
|
chan, chnum, qtd, halt_status, NULL);
|
|
|
|
|
|
|
|
/* Skip whole frame */
|
|
|
|
if (chan->qh->do_split &&
|
|
|
|
chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.host_dma) {
|
2013-03-12 08:47:59 +08:00
|
|
|
qtd->complete_split = 0;
|
|
|
|
qtd->isoc_split_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dev_err(hsotg->dev, "Unhandled halt_status (%d)\n",
|
|
|
|
halt_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (++qtd->isoc_frame_index == urb->packet_count) {
|
|
|
|
/*
|
|
|
|
* urb->status is not used for isoc transfers. The individual
|
|
|
|
* frame_desc statuses are used instead.
|
|
|
|
*/
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, 0);
|
2013-03-12 08:47:59 +08:00
|
|
|
halt_status = DWC2_HC_XFER_URB_COMPLETE;
|
|
|
|
} else {
|
|
|
|
halt_status = DWC2_HC_XFER_COMPLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
return halt_status;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic
|
|
|
|
* QHs, removes the QH from the active non-periodic schedule. If any QTDs are
|
|
|
|
* still linked to the QH, the QH is added to the end of the inactive
|
|
|
|
* non-periodic schedule. For periodic QHs, removes the QH from the periodic
|
|
|
|
* schedule if no more QTDs are linked to the QH.
|
|
|
|
*/
|
|
|
|
static void dwc2_deactivate_qh(struct dwc2_hsotg *hsotg, struct dwc2_qh *qh,
|
|
|
|
int free_qtd)
|
|
|
|
{
|
|
|
|
int continue_split = 0;
|
|
|
|
struct dwc2_qtd *qtd;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_qh(qh))
|
|
|
|
dev_vdbg(hsotg->dev, " %s(%p,%p,%d)\n", __func__,
|
|
|
|
hsotg, qh, free_qtd);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (list_empty(&qh->qtd_list)) {
|
|
|
|
dev_dbg(hsotg->dev, "## QTD list empty ##\n");
|
|
|
|
goto no_qtd;
|
|
|
|
}
|
|
|
|
|
|
|
|
qtd = list_first_entry(&qh->qtd_list, struct dwc2_qtd, qtd_list_entry);
|
|
|
|
|
|
|
|
if (qtd->complete_split)
|
|
|
|
continue_split = 1;
|
|
|
|
else if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_MID ||
|
|
|
|
qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_END)
|
|
|
|
continue_split = 1;
|
|
|
|
|
|
|
|
if (free_qtd) {
|
|
|
|
dwc2_hcd_qtd_unlink_and_free(hsotg, qtd, qh);
|
|
|
|
continue_split = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
no_qtd:
|
|
|
|
qh->channel = NULL;
|
|
|
|
dwc2_hcd_qh_deactivate(hsotg, qh, continue_split);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* dwc2_release_channel() - Releases a host channel for use by other transfers
|
|
|
|
*
|
|
|
|
* @hsotg: The HCD state structure
|
|
|
|
* @chan: The host channel to release
|
|
|
|
* @qtd: The QTD associated with the host channel. This QTD may be
|
|
|
|
* freed if the transfer is complete or an error has occurred.
|
|
|
|
* @halt_status: Reason the channel is being released. This status
|
|
|
|
* determines the actions taken by this function.
|
|
|
|
*
|
|
|
|
* Also attempts to select and queue more transactions since at least one host
|
|
|
|
* channel is available.
|
|
|
|
*/
|
|
|
|
static void dwc2_release_channel(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan,
|
|
|
|
struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
|
|
|
enum dwc2_transaction_type tr_type;
|
|
|
|
u32 haintmsk;
|
|
|
|
int free_qtd = 0;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, " %s: channel %d, halt_status %d\n",
|
|
|
|
__func__, chan->hc_num, halt_status);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
switch (halt_status) {
|
|
|
|
case DWC2_HC_XFER_URB_COMPLETE:
|
|
|
|
free_qtd = 1;
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_AHB_ERR:
|
|
|
|
case DWC2_HC_XFER_STALL:
|
|
|
|
case DWC2_HC_XFER_BABBLE_ERR:
|
|
|
|
free_qtd = 1;
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_XACT_ERR:
|
2013-03-26 03:00:25 +08:00
|
|
|
if (qtd && qtd->error_count >= 3) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" Complete URB with transaction error\n");
|
|
|
|
free_qtd = 1;
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EPROTO);
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_URB_DEQUEUE:
|
|
|
|
/*
|
|
|
|
* The QTD has already been removed and the QH has been
|
|
|
|
* deactivated. Don't want to do anything except release the
|
|
|
|
* host channel and try to queue more transfers.
|
|
|
|
*/
|
|
|
|
goto cleanup;
|
|
|
|
case DWC2_HC_XFER_PERIODIC_INCOMPLETE:
|
|
|
|
dev_vdbg(hsotg->dev, " Complete URB with I/O error\n");
|
|
|
|
free_qtd = 1;
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EIO);
|
2013-03-12 08:47:59 +08:00
|
|
|
break;
|
|
|
|
case DWC2_HC_XFER_NO_HALT_STATUS:
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dwc2_deactivate_qh(hsotg, chan->qh, free_qtd);
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
/*
|
|
|
|
* Release the host channel for use by other transfers. The cleanup
|
|
|
|
* function clears the channel interrupt enables and conditions, so
|
|
|
|
* there's no need to clear the Channel Halted interrupt separately.
|
|
|
|
*/
|
|
|
|
if (!list_empty(&chan->hc_list_entry))
|
|
|
|
list_del(&chan->hc_list_entry);
|
|
|
|
dwc2_hc_cleanup(hsotg, chan);
|
|
|
|
list_add_tail(&chan->hc_list_entry, &hsotg->free_hc_list);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.uframe_sched) {
|
staging: dwc2: add microframe scheduler from downstream Pi kernel
The transfer scheduler in the dwc2 driver is pretty basic, not to
mention buggy. It works fairly well with just a couple of devices
plugged in, but if you add, say, multiple devices with periodic
endpoints, the scheduler breaks down and can't even enumerate all
the devices.
To improve this, import the "microframe scheduler" patch from the
driver in the downstream Raspberry Pi kernel, which is based on
the Synopsys vendor driver. The original patch came from Denx
(http://git.denx.de/?p=linux-denx.git) and was commited to the
raspberrypi.org git tree by "popcornmix" (Dom Cobley).
I have added a driver parameter for this, enabled by default, in
case anyone has problems with it and needs to disable it. I don't
think we should add a DT binding for that, though, since I plan
to remove the option once any bugs are fixed.
[raspberrypi.org patch from Dom Cobley]
Signed-off-by: Dom Cobley <popcornmix@gmail.com>
[adapted to dwc2 driver by Paul Zimmerman]
Signed-off-by: Paul Zimmerman <paulz@synopsys.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2013-09-24 05:23:34 +08:00
|
|
|
hsotg->available_host_channels++;
|
|
|
|
} else {
|
|
|
|
switch (chan->ep_type) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
hsotg->non_periodic_channels--;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
/*
|
|
|
|
* Don't release reservations for periodic channels
|
|
|
|
* here. That's done when a periodic transfer is
|
|
|
|
* descheduled (i.e. when the QH is removed from the
|
|
|
|
* periodic schedule).
|
|
|
|
*/
|
|
|
|
break;
|
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
haintmsk = dwc2_readl(hsotg, HAINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
haintmsk &= ~(1 << chan->hc_num);
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, haintmsk, HAINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/* Try to queue more transfers now that there's a free channel */
|
|
|
|
tr_type = dwc2_hcd_select_transactions(hsotg);
|
|
|
|
if (tr_type != DWC2_TRANSACTION_NONE)
|
|
|
|
dwc2_hcd_queue_transactions(hsotg, tr_type);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Halts a host channel. If the channel cannot be halted immediately because
|
|
|
|
* the request queue is full, this function ensures that the FIFO empty
|
|
|
|
* interrupt for the appropriate queue is enabled so that the halt request can
|
|
|
|
* be queued when there is space in the request queue.
|
|
|
|
*
|
|
|
|
* This function may also be called in DMA mode. In that case, the channel is
|
|
|
|
* simply released since the core always halts the channel automatically in
|
|
|
|
* DMA mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_halt_channel(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.host_dma) {
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "DMA enabled\n");
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_release_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Slave mode processing */
|
|
|
|
dwc2_hc_halt(hsotg, chan, halt_status);
|
|
|
|
|
|
|
|
if (chan->halt_on_queue) {
|
|
|
|
u32 gintmsk;
|
|
|
|
|
|
|
|
dev_vdbg(hsotg->dev, "Halt on queue\n");
|
|
|
|
if (chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
|
|
|
|
chan->ep_type == USB_ENDPOINT_XFER_BULK) {
|
|
|
|
dev_vdbg(hsotg->dev, "control/bulk\n");
|
|
|
|
/*
|
|
|
|
* Make sure the Non-periodic Tx FIFO empty interrupt
|
|
|
|
* is enabled so that the non-periodic schedule will
|
|
|
|
* be processed
|
|
|
|
*/
|
2018-07-26 22:00:13 +08:00
|
|
|
gintmsk = dwc2_readl(hsotg, GINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
gintmsk |= GINTSTS_NPTXFEMP;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, gintmsk, GINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
} else {
|
|
|
|
dev_vdbg(hsotg->dev, "isoc/intr\n");
|
|
|
|
/*
|
|
|
|
* Move the QH from the periodic queued schedule to
|
|
|
|
* the periodic assigned schedule. This allows the
|
|
|
|
* halt to be queued when the periodic schedule is
|
|
|
|
* processed.
|
|
|
|
*/
|
2016-01-29 10:19:56 +08:00
|
|
|
list_move_tail(&chan->qh->qh_list_entry,
|
2017-01-18 12:30:27 +08:00
|
|
|
&hsotg->periodic_sched_assigned);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Make sure the Periodic Tx FIFO Empty interrupt is
|
|
|
|
* enabled so that the periodic schedule will be
|
|
|
|
* processed
|
|
|
|
*/
|
2018-07-26 22:00:13 +08:00
|
|
|
gintmsk = dwc2_readl(hsotg, GINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
gintmsk |= GINTSTS_PTXFEMP;
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, gintmsk, GINTMSK);
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Performs common cleanup for non-periodic transfers after a Transfer
|
|
|
|
* Complete interrupt. This function should be called after any endpoint type
|
|
|
|
* specific handling is finished to release the host channel.
|
|
|
|
*/
|
|
|
|
static void dwc2_complete_non_periodic_xfer(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan,
|
|
|
|
int chnum, struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
|
|
|
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
qtd->error_count = 0;
|
|
|
|
|
|
|
|
if (chan->hcint & HCINTMSK_NYET) {
|
|
|
|
/*
|
|
|
|
* Got a NYET on the last transaction of the transfer. This
|
|
|
|
* means that the endpoint should be in the PING state at the
|
|
|
|
* beginning of the next transfer.
|
|
|
|
*/
|
|
|
|
dev_vdbg(hsotg->dev, "got NYET\n");
|
|
|
|
chan->qh->ping_state = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Always halt and release the host channel to make it available for
|
|
|
|
* more transfers. There may still be more phases for a control
|
|
|
|
* transfer or more data packets for a bulk transfer at this point,
|
|
|
|
* but the host channel is still halted. A channel will be reassigned
|
|
|
|
* to the transfer when the non-periodic schedule is processed after
|
|
|
|
* the channel is released. This allows transactions to be queued
|
|
|
|
* properly via dwc2_hcd_queue_transactions, which also enables the
|
|
|
|
* Tx FIFO Empty interrupt if necessary.
|
|
|
|
*/
|
|
|
|
if (chan->ep_is_in) {
|
|
|
|
/*
|
|
|
|
* IN transfers in Slave mode require an explicit disable to
|
|
|
|
* halt the channel. (In DMA mode, this call simply releases
|
|
|
|
* the channel.)
|
|
|
|
*/
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* The channel is automatically disabled by the core for OUT
|
|
|
|
* transfers in Slave mode
|
|
|
|
*/
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Performs common cleanup for periodic transfers after a Transfer Complete
|
|
|
|
* interrupt. This function should be called after any endpoint type specific
|
|
|
|
* handling is finished to release the host channel.
|
|
|
|
*/
|
|
|
|
static void dwc2_complete_periodic_xfer(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
2018-07-26 22:00:13 +08:00
|
|
|
u32 hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
qtd->error_count = 0;
|
|
|
|
|
|
|
|
if (!chan->ep_is_in || (hctsiz & TSIZ_PKTCNT_MASK) == 0)
|
|
|
|
/* Core halts channel in these cases */
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
else
|
|
|
|
/* Flush any outstanding requests from the Tx queue */
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int dwc2_xfercomp_isoc_split_in(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
|
|
|
u32 len;
|
2016-11-17 07:33:52 +08:00
|
|
|
u32 hctsiz;
|
|
|
|
u32 pid;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (!qtd->urb)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index];
|
|
|
|
len = dwc2_get_actual_xfer_length(hsotg, chan, chnum, qtd,
|
|
|
|
DWC2_HC_XFER_COMPLETE, NULL);
|
2018-05-11 17:46:32 +08:00
|
|
|
if (!len && !qtd->isoc_split_offset) {
|
2013-03-12 08:47:59 +08:00
|
|
|
qtd->complete_split = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
frame_desc->actual_length += len;
|
|
|
|
|
usb: dwc2: alloc dma aligned buffer for isoc split in
The commit 3bc04e28a030 ("usb: dwc2: host: Get aligned DMA in
a more supported way") rips out a lot of code to simply the
allocation of aligned DMA. However, it also introduces a new
issue when use isoc split in transfer.
In my test case, I connect the dwc2 controller with an usb hs
Hub (GL852G-12), and plug an usb fs audio device (Plantronics
headset) into the downstream port of Hub. Then use the usb mic
to record, we can find noise when playback.
It's because that the usb Hub uses an MDATA for the first
transaction and a DATA0 for the second transaction for the isoc
split in transaction. An typical isoc split in transaction sequence
like this:
- SSPLIT IN transaction
- CSPLIT IN transaction
- MDATA packet
- CSPLIT IN transaction
- DATA0 packet
The DMA address of MDATA (urb->dma) is always DWORD-aligned, but
the DMA address of DATA0 (urb->dma + qtd->isoc_split_offset) may
not be DWORD-aligned, it depends on the qtd->isoc_split_offset (the
length of MDATA). In my test case, the length of MDATA is usually
unaligned, this cause DATA0 packet transmission error.
This patch use kmem_cache to allocate aligned DMA buf for isoc
split in transaction. Note that according to usb 2.0 spec, the
maximum data payload size is 1023 bytes for each fs isoc ep,
and the maximum allowable interrupt data payload size is 64 bytes
or less for fs interrupt ep. So we set the size of object to be
1024 bytes in the kmem cache.
Tested-by: Gevorg Sahakyan <sahakyan@synopsys.com>
Tested-by: Heiko Stuebner <heiko@sntech.de>
Acked-by: Minas Harutyunyan hminas@synopsys.com>
Signed-off-by: William Wu <william.wu@rock-chips.com>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2018-05-11 17:46:31 +08:00
|
|
|
if (chan->align_buf) {
|
|
|
|
dev_vdbg(hsotg->dev, "non-aligned buffer\n");
|
|
|
|
dma_unmap_single(hsotg->dev, chan->qh->dw_align_buf_dma,
|
|
|
|
DWC2_KMEM_UNALIGNED_BUF_SIZE, DMA_FROM_DEVICE);
|
|
|
|
memcpy(qtd->urb->buf + (chan->xfer_dma - qtd->urb->dma),
|
|
|
|
chan->qh->dw_align_buf, len);
|
|
|
|
}
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
qtd->isoc_split_offset += len;
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2016-11-17 07:33:52 +08:00
|
|
|
pid = (hctsiz & TSIZ_SC_MC_PID_MASK) >> TSIZ_SC_MC_PID_SHIFT;
|
|
|
|
|
|
|
|
if (frame_desc->actual_length >= frame_desc->length || pid == 0) {
|
2013-03-12 08:47:59 +08:00
|
|
|
frame_desc->status = 0;
|
|
|
|
qtd->isoc_frame_index++;
|
|
|
|
qtd->complete_split = 0;
|
|
|
|
qtd->isoc_split_offset = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (qtd->isoc_frame_index == qtd->urb->packet_count) {
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, 0);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_release_channel(hsotg, chan, qtd,
|
|
|
|
DWC2_HC_XFER_URB_COMPLETE);
|
|
|
|
} else {
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd,
|
|
|
|
DWC2_HC_XFER_NO_HALT_STATUS);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1; /* Indicates that channel released */
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel Transfer Complete interrupt. This handler may be
|
|
|
|
* called in either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_xfercomp_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_urb *urb = qtd->urb;
|
|
|
|
enum dwc2_halt_status halt_status = DWC2_HC_XFER_COMPLETE;
|
2014-02-13 09:44:35 +08:00
|
|
|
int pipe_type;
|
2013-03-12 08:47:59 +08:00
|
|
|
int urb_xfer_done;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"--Host Channel %d Interrupt: Transfer Complete--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2014-02-13 09:44:35 +08:00
|
|
|
if (!urb)
|
|
|
|
goto handle_xfercomp_done;
|
|
|
|
|
|
|
|
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum, halt_status);
|
|
|
|
if (pipe_type == USB_ENDPOINT_XFER_ISOC)
|
|
|
|
/* Do not disable the interrupt, just clear it */
|
|
|
|
return;
|
|
|
|
goto handle_xfercomp_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Handle xfer complete on CSPLIT */
|
|
|
|
if (chan->qh->do_split) {
|
|
|
|
if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && chan->ep_is_in &&
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.host_dma) {
|
2013-03-12 08:47:59 +08:00
|
|
|
if (qtd->complete_split &&
|
|
|
|
dwc2_xfercomp_isoc_split_in(hsotg, chan, chnum,
|
|
|
|
qtd))
|
|
|
|
goto handle_xfercomp_done;
|
|
|
|
} else {
|
|
|
|
qtd->complete_split = 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Update the QTD and URB states */
|
|
|
|
switch (pipe_type) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
switch (qtd->control_phase) {
|
|
|
|
case DWC2_CONTROL_SETUP:
|
|
|
|
if (urb->length > 0)
|
|
|
|
qtd->control_phase = DWC2_CONTROL_DATA;
|
|
|
|
else
|
|
|
|
qtd->control_phase = DWC2_CONTROL_STATUS;
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" Control setup transaction done\n");
|
|
|
|
halt_status = DWC2_HC_XFER_COMPLETE;
|
|
|
|
break;
|
|
|
|
case DWC2_CONTROL_DATA:
|
|
|
|
urb_xfer_done = dwc2_update_urb_state(hsotg, chan,
|
|
|
|
chnum, urb, qtd);
|
|
|
|
if (urb_xfer_done) {
|
|
|
|
qtd->control_phase = DWC2_CONTROL_STATUS;
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" Control data transfer done\n");
|
|
|
|
} else {
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum,
|
|
|
|
qtd);
|
|
|
|
}
|
|
|
|
halt_status = DWC2_HC_XFER_COMPLETE;
|
|
|
|
break;
|
|
|
|
case DWC2_CONTROL_STATUS:
|
|
|
|
dev_vdbg(hsotg->dev, " Control transfer complete\n");
|
|
|
|
if (urb->status == -EINPROGRESS)
|
|
|
|
urb->status = 0;
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, urb->status);
|
2013-03-12 08:47:59 +08:00
|
|
|
halt_status = DWC2_HC_XFER_URB_COMPLETE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dwc2_complete_non_periodic_xfer(hsotg, chan, chnum, qtd,
|
|
|
|
halt_status);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
dev_vdbg(hsotg->dev, " Bulk transfer complete\n");
|
|
|
|
urb_xfer_done = dwc2_update_urb_state(hsotg, chan, chnum, urb,
|
|
|
|
qtd);
|
|
|
|
if (urb_xfer_done) {
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, urb->status);
|
2013-03-12 08:47:59 +08:00
|
|
|
halt_status = DWC2_HC_XFER_URB_COMPLETE;
|
|
|
|
} else {
|
|
|
|
halt_status = DWC2_HC_XFER_COMPLETE;
|
|
|
|
}
|
|
|
|
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
dwc2_complete_non_periodic_xfer(hsotg, chan, chnum, qtd,
|
|
|
|
halt_status);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
dev_vdbg(hsotg->dev, " Interrupt transfer complete\n");
|
|
|
|
urb_xfer_done = dwc2_update_urb_state(hsotg, chan, chnum, urb,
|
|
|
|
qtd);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Interrupt URB is done on the first transfer complete
|
|
|
|
* interrupt
|
|
|
|
*/
|
|
|
|
if (urb_xfer_done) {
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, urb->status);
|
|
|
|
halt_status = DWC2_HC_XFER_URB_COMPLETE;
|
2013-03-12 08:47:59 +08:00
|
|
|
} else {
|
2013-07-14 05:53:48 +08:00
|
|
|
halt_status = DWC2_HC_XFER_COMPLETE;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd,
|
|
|
|
halt_status);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_perio())
|
|
|
|
dev_vdbg(hsotg->dev, " Isochronous transfer complete\n");
|
2013-03-12 08:47:59 +08:00
|
|
|
if (qtd->isoc_split_pos == DWC2_HCSPLT_XACTPOS_ALL)
|
|
|
|
halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
|
2017-01-18 12:31:58 +08:00
|
|
|
chnum, qtd,
|
|
|
|
DWC2_HC_XFER_COMPLETE);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_complete_periodic_xfer(hsotg, chan, chnum, qtd,
|
|
|
|
halt_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_xfercomp_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_XFERCOMPL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel STALL interrupt. This handler may be called in
|
|
|
|
* either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_stall_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_urb *urb = qtd->urb;
|
2014-02-13 09:44:35 +08:00
|
|
|
int pipe_type;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: STALL Received--\n",
|
|
|
|
chnum);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
DWC2_HC_XFER_STALL);
|
|
|
|
goto handle_stall_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!urb)
|
|
|
|
goto handle_stall_halt;
|
|
|
|
|
2014-02-13 09:44:35 +08:00
|
|
|
pipe_type = dwc2_hcd_get_pipe_type(&urb->pipe_info);
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
if (pipe_type == USB_ENDPOINT_XFER_CONTROL)
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EPIPE);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (pipe_type == USB_ENDPOINT_XFER_BULK ||
|
|
|
|
pipe_type == USB_ENDPOINT_XFER_INT) {
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EPIPE);
|
2013-03-12 08:47:59 +08:00
|
|
|
/*
|
|
|
|
* USB protocol requires resetting the data toggle for bulk
|
|
|
|
* and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT)
|
|
|
|
* setup command is issued to the endpoint. Anticipate the
|
|
|
|
* CLEAR_FEATURE command since a STALL has occurred and reset
|
|
|
|
* the data toggle now.
|
|
|
|
*/
|
|
|
|
chan->qh->data_toggle = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_stall_halt:
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_STALL);
|
|
|
|
|
|
|
|
handle_stall_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_STALL);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Updates the state of the URB when a transfer has been stopped due to an
|
|
|
|
* abnormal condition before the transfer completes. Modifies the
|
|
|
|
* actual_length field of the URB to reflect the number of bytes that have
|
|
|
|
* actually been transferred via the host channel.
|
|
|
|
*/
|
|
|
|
static void dwc2_update_urb_state_abn(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_hcd_urb *urb,
|
|
|
|
struct dwc2_qtd *qtd,
|
|
|
|
enum dwc2_halt_status halt_status)
|
|
|
|
{
|
|
|
|
u32 xfer_length = dwc2_get_actual_xfer_length(hsotg, chan, chnum,
|
|
|
|
qtd, halt_status, NULL);
|
|
|
|
u32 hctsiz;
|
|
|
|
|
|
|
|
if (urb->actual_length + xfer_length > urb->length) {
|
|
|
|
dev_warn(hsotg->dev, "%s(): trimming xfer length\n", __func__);
|
|
|
|
xfer_length = urb->length - urb->actual_length;
|
|
|
|
}
|
|
|
|
|
|
|
|
urb->actual_length += xfer_length;
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev, "DWC_otg: %s: %s, channel %d\n",
|
|
|
|
__func__, (chan->ep_is_in ? "IN" : "OUT"), chnum);
|
|
|
|
dev_vdbg(hsotg->dev, " chan->start_pkt_count %d\n",
|
|
|
|
chan->start_pkt_count);
|
|
|
|
dev_vdbg(hsotg->dev, " hctsiz.pktcnt %d\n",
|
2013-08-31 00:45:15 +08:00
|
|
|
(hctsiz & TSIZ_PKTCNT_MASK) >> TSIZ_PKTCNT_SHIFT);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev, " chan->max_packet %d\n", chan->max_packet);
|
|
|
|
dev_vdbg(hsotg->dev, " bytes_transferred %d\n",
|
|
|
|
xfer_length);
|
|
|
|
dev_vdbg(hsotg->dev, " urb->actual_length %d\n",
|
|
|
|
urb->actual_length);
|
|
|
|
dev_vdbg(hsotg->dev, " urb->transfer_buffer_length %d\n",
|
|
|
|
urb->length);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel NAK interrupt. This handler may be called in either
|
|
|
|
* DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_nak_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
2015-04-30 04:09:20 +08:00
|
|
|
if (!qtd) {
|
|
|
|
dev_dbg(hsotg->dev, "%s: qtd is NULL\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!qtd->urb) {
|
|
|
|
dev_dbg(hsotg->dev, "%s: qtd->urb is NULL\n", __func__);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NAK Received--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and
|
|
|
|
* interrupt. Re-start the SSPLIT transfer.
|
usb: dwc2: host: Don't retry NAKed transactions right away
On rk3288-veyron devices on Chrome OS it was found that plugging in an
Arduino-based USB device could cause the system to lockup, especially
if the CPU Frequency was at one of the slower operating points (like
100 MHz / 200 MHz).
Upon tracing, I found that the following was happening:
* The USB device (full speed) was connected to a high speed hub and
then to the rk3288. Thus, we were dealing with split transactions,
which is all handled in software on dwc2.
* Userspace was initiating a BULK IN transfer
* When we sent the SSPLIT (to start the split transaction), we got an
ACK. Good. Then we issued the CSPLIT.
* When we sent the CSPLIT, we got back a NAK. We immediately (from
the interrupt handler) started to retry and sent another SSPLIT.
* The device kept NAKing our CSPLIT, so we kept ping-ponging between
sending a SSPLIT and a CSPLIT, each time sending from the interrupt
handler.
* The handling of the interrupts was (because of the low CPU speed and
the inefficiency of the dwc2 interrupt handler) was actually taking
_longer_ than it took the other side to send the ACK/NAK. Thus we
were _always_ in the USB interrupt routine.
* The fact that USB interrupts were always going off was preventing
other things from happening in the system. This included preventing
the system from being able to transition to a higher CPU frequency.
As I understand it, there is no requirement to retry super quickly
after a NAK, we just have to retry sometime in the future. Thus one
solution to the above is to just add a delay between getting a NAK and
retrying the transmission. If this delay is sufficiently long to get
out of the interrupt routine then the rest of the system will be able
to make forward progress. Even a 25 us delay would probably be
enough, but we'll be extra conservative and try to delay 1 ms (the
exact amount depends on HZ and the accuracy of the jiffy and how close
the current jiffy is to ticking, but could be as much as 20 ms or as
little as 1 ms).
Presumably adding a delay like this could impact the USB throughput,
so we only add the delay with repeated NAKs.
NOTE: Upon further testing of a pl2303 serial adapter, I found that
this fix may help with problems there. Specifically I found that the
pl2303 serial adapters tend to respond with a NAK when they have
nothing to say and thus we end with this same sequence.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2017-12-13 02:30:31 +08:00
|
|
|
*
|
|
|
|
* Normally for non-periodic transfers we'll retry right away, but to
|
|
|
|
* avoid interrupt storms we'll wait before retrying if we've got
|
|
|
|
* several NAKs. If we didn't do this we'd retry directly from the
|
|
|
|
* interrupt handler and could end up quickly getting another
|
2018-06-16 06:01:29 +08:00
|
|
|
* interrupt (another NAK), which we'd retry. Note that we do not
|
|
|
|
* delay retries for IN parts of control requests, as those are expected
|
|
|
|
* to complete fairly quickly, and if we delay them we risk confusing
|
|
|
|
* the device and cause it issue STALL.
|
usb: dwc2: host: Don't retry NAKed transactions right away
On rk3288-veyron devices on Chrome OS it was found that plugging in an
Arduino-based USB device could cause the system to lockup, especially
if the CPU Frequency was at one of the slower operating points (like
100 MHz / 200 MHz).
Upon tracing, I found that the following was happening:
* The USB device (full speed) was connected to a high speed hub and
then to the rk3288. Thus, we were dealing with split transactions,
which is all handled in software on dwc2.
* Userspace was initiating a BULK IN transfer
* When we sent the SSPLIT (to start the split transaction), we got an
ACK. Good. Then we issued the CSPLIT.
* When we sent the CSPLIT, we got back a NAK. We immediately (from
the interrupt handler) started to retry and sent another SSPLIT.
* The device kept NAKing our CSPLIT, so we kept ping-ponging between
sending a SSPLIT and a CSPLIT, each time sending from the interrupt
handler.
* The handling of the interrupts was (because of the low CPU speed and
the inefficiency of the dwc2 interrupt handler) was actually taking
_longer_ than it took the other side to send the ACK/NAK. Thus we
were _always_ in the USB interrupt routine.
* The fact that USB interrupts were always going off was preventing
other things from happening in the system. This included preventing
the system from being able to transition to a higher CPU frequency.
As I understand it, there is no requirement to retry super quickly
after a NAK, we just have to retry sometime in the future. Thus one
solution to the above is to just add a delay between getting a NAK and
retrying the transmission. If this delay is sufficiently long to get
out of the interrupt routine then the rest of the system will be able
to make forward progress. Even a 25 us delay would probably be
enough, but we'll be extra conservative and try to delay 1 ms (the
exact amount depends on HZ and the accuracy of the jiffy and how close
the current jiffy is to ticking, but could be as much as 20 ms or as
little as 1 ms).
Presumably adding a delay like this could impact the USB throughput,
so we only add the delay with repeated NAKs.
NOTE: Upon further testing of a pl2303 serial adapter, I found that
this fix may help with problems there. Specifically I found that the
pl2303 serial adapters tend to respond with a NAK when they have
nothing to say and thus we end with this same sequence.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2017-12-13 02:30:31 +08:00
|
|
|
*
|
|
|
|
* Note that in DMA mode software only gets involved to re-send NAKed
|
|
|
|
* transfers for split transactions, so we only need to apply this
|
|
|
|
* delaying logic when handling splits. In non-DMA mode presumably we
|
|
|
|
* might want a similar delay if someone can demonstrate this problem
|
|
|
|
* affects that code path too.
|
2013-03-12 08:47:59 +08:00
|
|
|
*/
|
|
|
|
if (chan->do_split) {
|
|
|
|
if (chan->complete_split)
|
|
|
|
qtd->error_count = 0;
|
|
|
|
qtd->complete_split = 0;
|
usb: dwc2: host: Don't retry NAKed transactions right away
On rk3288-veyron devices on Chrome OS it was found that plugging in an
Arduino-based USB device could cause the system to lockup, especially
if the CPU Frequency was at one of the slower operating points (like
100 MHz / 200 MHz).
Upon tracing, I found that the following was happening:
* The USB device (full speed) was connected to a high speed hub and
then to the rk3288. Thus, we were dealing with split transactions,
which is all handled in software on dwc2.
* Userspace was initiating a BULK IN transfer
* When we sent the SSPLIT (to start the split transaction), we got an
ACK. Good. Then we issued the CSPLIT.
* When we sent the CSPLIT, we got back a NAK. We immediately (from
the interrupt handler) started to retry and sent another SSPLIT.
* The device kept NAKing our CSPLIT, so we kept ping-ponging between
sending a SSPLIT and a CSPLIT, each time sending from the interrupt
handler.
* The handling of the interrupts was (because of the low CPU speed and
the inefficiency of the dwc2 interrupt handler) was actually taking
_longer_ than it took the other side to send the ACK/NAK. Thus we
were _always_ in the USB interrupt routine.
* The fact that USB interrupts were always going off was preventing
other things from happening in the system. This included preventing
the system from being able to transition to a higher CPU frequency.
As I understand it, there is no requirement to retry super quickly
after a NAK, we just have to retry sometime in the future. Thus one
solution to the above is to just add a delay between getting a NAK and
retrying the transmission. If this delay is sufficiently long to get
out of the interrupt routine then the rest of the system will be able
to make forward progress. Even a 25 us delay would probably be
enough, but we'll be extra conservative and try to delay 1 ms (the
exact amount depends on HZ and the accuracy of the jiffy and how close
the current jiffy is to ticking, but could be as much as 20 ms or as
little as 1 ms).
Presumably adding a delay like this could impact the USB throughput,
so we only add the delay with repeated NAKs.
NOTE: Upon further testing of a pl2303 serial adapter, I found that
this fix may help with problems there. Specifically I found that the
pl2303 serial adapters tend to respond with a NAK when they have
nothing to say and thus we end with this same sequence.
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Reviewed-by: Julius Werner <jwerner@chromium.org>
Tested-by: Stefan Wahren <stefan.wahren@i2se.com>
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Felipe Balbi <felipe.balbi@linux.intel.com>
2017-12-13 02:30:31 +08:00
|
|
|
qtd->num_naks++;
|
2018-06-16 06:01:29 +08:00
|
|
|
qtd->qh->want_wait = qtd->num_naks >= DWC2_NAKS_BEFORE_DELAY &&
|
|
|
|
!(chan->ep_type == USB_ENDPOINT_XFER_CONTROL &&
|
|
|
|
chan->ep_is_in);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
|
|
|
goto handle_nak_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.host_dma && chan->ep_is_in) {
|
2013-03-12 08:47:59 +08:00
|
|
|
/*
|
|
|
|
* NAK interrupts are enabled on bulk/control IN
|
|
|
|
* transfers in DMA mode for the sole purpose of
|
|
|
|
* resetting the error count after a transaction error
|
|
|
|
* occurs. The core will continue transferring data.
|
|
|
|
*/
|
|
|
|
qtd->error_count = 0;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* NAK interrupts normally occur during OUT transfers in DMA
|
|
|
|
* or Slave mode. For IN transfers, more requests will be
|
|
|
|
* queued as request queue space is available.
|
|
|
|
*/
|
|
|
|
qtd->error_count = 0;
|
|
|
|
|
|
|
|
if (!chan->qh->ping_state) {
|
|
|
|
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
|
|
|
|
qtd, DWC2_HC_XFER_NAK);
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
|
|
|
|
if (chan->speed == USB_SPEED_HIGH)
|
|
|
|
chan->qh->ping_state = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Halt the channel so the transfer can be re-started from
|
|
|
|
* the appropriate point or the PING protocol will
|
|
|
|
* start/continue
|
|
|
|
*/
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
qtd->error_count = 0;
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NAK);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
/* Should never get called for isochronous transfers */
|
|
|
|
dev_err(hsotg->dev, "NACK interrupt for ISOC transfer\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_nak_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_NAK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel ACK interrupt. This interrupt is enabled when
|
|
|
|
* performing the PING protocol in Slave mode, when errors occur during
|
|
|
|
* either Slave mode or DMA mode, and during Start Split transactions.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_ack_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_iso_packet_desc *frame_desc;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: ACK Received--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (chan->do_split) {
|
|
|
|
/* Handle ACK on SSPLIT. ACK should not occur in CSPLIT. */
|
|
|
|
if (!chan->ep_is_in &&
|
|
|
|
chan->data_pid_start != DWC2_HC_PID_SETUP)
|
|
|
|
qtd->ssplit_out_xfer_count = chan->xfer_len;
|
|
|
|
|
|
|
|
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC || chan->ep_is_in) {
|
|
|
|
qtd->complete_split = 1;
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_ACK);
|
|
|
|
} else {
|
|
|
|
/* ISOC OUT */
|
|
|
|
switch (chan->xact_pos) {
|
|
|
|
case DWC2_HCSPLT_XACTPOS_ALL:
|
|
|
|
break;
|
|
|
|
case DWC2_HCSPLT_XACTPOS_END:
|
|
|
|
qtd->isoc_split_pos = DWC2_HCSPLT_XACTPOS_ALL;
|
|
|
|
qtd->isoc_split_offset = 0;
|
|
|
|
break;
|
|
|
|
case DWC2_HCSPLT_XACTPOS_BEGIN:
|
|
|
|
case DWC2_HCSPLT_XACTPOS_MID:
|
|
|
|
/*
|
|
|
|
* For BEGIN or MID, calculate the length for
|
|
|
|
* the next microframe to determine the correct
|
|
|
|
* SSPLIT token, either MID or END
|
|
|
|
*/
|
|
|
|
frame_desc = &qtd->urb->iso_descs[
|
|
|
|
qtd->isoc_frame_index];
|
|
|
|
qtd->isoc_split_offset += 188;
|
|
|
|
|
|
|
|
if (frame_desc->length - qtd->isoc_split_offset
|
|
|
|
<= 188)
|
|
|
|
qtd->isoc_split_pos =
|
|
|
|
DWC2_HCSPLT_XACTPOS_END;
|
|
|
|
else
|
|
|
|
qtd->isoc_split_pos =
|
|
|
|
DWC2_HCSPLT_XACTPOS_MID;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
qtd->error_count = 0;
|
|
|
|
|
|
|
|
if (chan->qh->ping_state) {
|
|
|
|
chan->qh->ping_state = 0;
|
|
|
|
/*
|
|
|
|
* Halt the channel so the transfer can be re-started
|
|
|
|
* from the appropriate point. This only happens in
|
|
|
|
* Slave mode. In DMA mode, the ping_state is cleared
|
|
|
|
* when the transfer is started because the core
|
|
|
|
* automatically executes the PING, then the transfer.
|
|
|
|
*/
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_ACK);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* If the ACK occurred when _not_ in the PING state, let the channel
|
|
|
|
* continue transferring data after clearing the error count
|
|
|
|
*/
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_ACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel NYET interrupt. This interrupt should only occur on
|
|
|
|
* Bulk and Control OUT endpoints and for complete split transactions. If a
|
|
|
|
* NYET occurs at the same time as a Transfer Complete interrupt, it is
|
|
|
|
* handled in the xfercomp interrupt handler, not here. This handler may be
|
|
|
|
* called in either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_nyet_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: NYET Received--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* NYET on CSPLIT
|
|
|
|
* re-do the CSPLIT immediately on non-periodic
|
|
|
|
*/
|
|
|
|
if (chan->do_split && chan->complete_split) {
|
|
|
|
if (chan->ep_is_in && chan->ep_type == USB_ENDPOINT_XFER_ISOC &&
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.host_dma) {
|
2013-03-12 08:47:59 +08:00
|
|
|
qtd->complete_split = 0;
|
|
|
|
qtd->isoc_split_offset = 0;
|
2013-07-14 05:53:48 +08:00
|
|
|
qtd->isoc_frame_index++;
|
2013-04-23 05:00:18 +08:00
|
|
|
if (qtd->urb &&
|
2013-07-14 05:53:48 +08:00
|
|
|
qtd->isoc_frame_index == qtd->urb->packet_count) {
|
|
|
|
dwc2_host_complete(hsotg, qtd, 0);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_release_channel(hsotg, chan, qtd,
|
2013-04-23 05:00:18 +08:00
|
|
|
DWC2_HC_XFER_URB_COMPLETE);
|
2013-03-12 08:47:59 +08:00
|
|
|
} else {
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd,
|
2017-01-18 12:31:58 +08:00
|
|
|
DWC2_HC_XFER_NO_HALT_STATUS);
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
goto handle_nyet_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
|
|
|
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
|
2016-01-29 10:20:13 +08:00
|
|
|
struct dwc2_qh *qh = chan->qh;
|
|
|
|
bool past_end;
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (!hsotg->params.uframe_sched) {
|
2016-01-29 10:20:13 +08:00
|
|
|
int frnum = dwc2_hcd_get_frame_number(hsotg);
|
|
|
|
|
|
|
|
/* Don't have num_hs_transfers; simple logic */
|
|
|
|
past_end = dwc2_full_frame_num(frnum) !=
|
|
|
|
dwc2_full_frame_num(qh->next_active_frame);
|
|
|
|
} else {
|
|
|
|
int end_frnum;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
2017-01-18 12:31:13 +08:00
|
|
|
* Figure out the end frame based on
|
|
|
|
* schedule.
|
|
|
|
*
|
|
|
|
* We don't want to go on trying again
|
|
|
|
* and again forever. Let's stop when
|
|
|
|
* we've done all the transfers that
|
|
|
|
* were scheduled.
|
|
|
|
*
|
|
|
|
* We're going to be comparing
|
|
|
|
* start_active_frame and
|
|
|
|
* next_active_frame, both of which
|
|
|
|
* are 1 before the time the packet
|
|
|
|
* goes on the wire, so that cancels
|
|
|
|
* out. Basically if had 1 transfer
|
|
|
|
* and we saw 1 NYET then we're done.
|
|
|
|
* We're getting a NYET here so if
|
|
|
|
* next >= (start + num_transfers)
|
|
|
|
* we're done. The complexity is that
|
|
|
|
* for all but ISOC_OUT we skip one
|
|
|
|
* slot.
|
|
|
|
*/
|
2016-01-29 10:20:13 +08:00
|
|
|
end_frnum = dwc2_frame_num_inc(
|
|
|
|
qh->start_active_frame,
|
|
|
|
qh->num_hs_transfers);
|
|
|
|
|
|
|
|
if (qh->ep_type != USB_ENDPOINT_XFER_ISOC ||
|
|
|
|
qh->ep_is_in)
|
|
|
|
end_frnum =
|
|
|
|
dwc2_frame_num_inc(end_frnum, 1);
|
|
|
|
|
|
|
|
past_end = dwc2_frame_num_le(
|
|
|
|
end_frnum, qh->next_active_frame);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (past_end) {
|
|
|
|
/* Treat this as a transaction error. */
|
2013-03-12 08:47:59 +08:00
|
|
|
#if 0
|
|
|
|
/*
|
|
|
|
* Todo: Fix system performance so this can
|
|
|
|
* be treated as an error. Right now complete
|
|
|
|
* splits cannot be scheduled precisely enough
|
|
|
|
* due to other system activity, so this error
|
|
|
|
* occurs regularly in Slave mode.
|
|
|
|
*/
|
|
|
|
qtd->error_count++;
|
|
|
|
#endif
|
|
|
|
qtd->complete_split = 0;
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd,
|
|
|
|
DWC2_HC_XFER_XACT_ERR);
|
|
|
|
/* Todo: add support for isoc release */
|
|
|
|
goto handle_nyet_done;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NYET);
|
|
|
|
goto handle_nyet_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
chan->qh->ping_state = 1;
|
|
|
|
qtd->error_count = 0;
|
|
|
|
|
|
|
|
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb, qtd,
|
|
|
|
DWC2_HC_XFER_NYET);
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Halt the channel and re-start the transfer so the PING protocol
|
|
|
|
* will start
|
|
|
|
*/
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_NYET);
|
|
|
|
|
|
|
|
handle_nyet_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_NYET);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel babble interrupt. This handler may be called in
|
|
|
|
* either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_babble_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: Babble Error--\n",
|
|
|
|
chnum);
|
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
DWC2_HC_XFER_BABBLE_ERR);
|
2013-07-14 05:53:48 +08:00
|
|
|
goto disable_int;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->ep_type != USB_ENDPOINT_XFER_ISOC) {
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EOVERFLOW);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_BABBLE_ERR);
|
|
|
|
} else {
|
|
|
|
enum dwc2_halt_status halt_status;
|
|
|
|
|
|
|
|
halt_status = dwc2_update_isoc_urb_state(hsotg, chan, chnum,
|
2017-01-18 12:31:58 +08:00
|
|
|
qtd, DWC2_HC_XFER_BABBLE_ERR);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
}
|
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
disable_int:
|
2013-03-12 08:47:59 +08:00
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_BBLERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel AHB error interrupt. This handler is only called in
|
|
|
|
* DMA mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_ahberr_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
struct dwc2_hcd_urb *urb = qtd->urb;
|
|
|
|
char *pipetype, *speed;
|
|
|
|
u32 hcchar;
|
|
|
|
u32 hcsplt;
|
|
|
|
u32 hctsiz;
|
|
|
|
u32 hc_dma;
|
|
|
|
|
|
|
|
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: AHB Error--\n",
|
|
|
|
chnum);
|
|
|
|
|
|
|
|
if (!urb)
|
|
|
|
goto handle_ahberr_halt;
|
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
|
|
|
|
hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
|
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
|
|
|
hc_dma = dwc2_readl(hsotg, HCDMA(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
dev_err(hsotg->dev, "AHB ERROR, Channel %d\n", chnum);
|
|
|
|
dev_err(hsotg->dev, " hcchar 0x%08x, hcsplt 0x%08x\n", hcchar, hcsplt);
|
|
|
|
dev_err(hsotg->dev, " hctsiz 0x%08x, hc_dma 0x%08x\n", hctsiz, hc_dma);
|
|
|
|
dev_err(hsotg->dev, " Device address: %d\n",
|
|
|
|
dwc2_hcd_get_dev_addr(&urb->pipe_info));
|
|
|
|
dev_err(hsotg->dev, " Endpoint: %d, %s\n",
|
|
|
|
dwc2_hcd_get_ep_num(&urb->pipe_info),
|
|
|
|
dwc2_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT");
|
|
|
|
|
|
|
|
switch (dwc2_hcd_get_pipe_type(&urb->pipe_info)) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
pipetype = "CONTROL";
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
pipetype = "BULK";
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
pipetype = "INTERRUPT";
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
pipetype = "ISOCHRONOUS";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
pipetype = "UNKNOWN";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_err(hsotg->dev, " Endpoint type: %s\n", pipetype);
|
|
|
|
|
|
|
|
switch (chan->speed) {
|
|
|
|
case USB_SPEED_HIGH:
|
|
|
|
speed = "HIGH";
|
|
|
|
break;
|
|
|
|
case USB_SPEED_FULL:
|
|
|
|
speed = "FULL";
|
|
|
|
break;
|
|
|
|
case USB_SPEED_LOW:
|
|
|
|
speed = "LOW";
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
speed = "UNKNOWN";
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
dev_err(hsotg->dev, " Speed: %s\n", speed);
|
|
|
|
|
2019-06-01 04:04:12 +08:00
|
|
|
dev_err(hsotg->dev, " Max packet size: %d (mult %d)\n",
|
|
|
|
dwc2_hcd_get_maxp(&urb->pipe_info),
|
|
|
|
dwc2_hcd_get_maxp_mult(&urb->pipe_info));
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_err(hsotg->dev, " Data buffer length: %d\n", urb->length);
|
2013-03-15 04:12:00 +08:00
|
|
|
dev_err(hsotg->dev, " Transfer buffer: %p, Transfer DMA: %08lx\n",
|
|
|
|
urb->buf, (unsigned long)urb->dma);
|
|
|
|
dev_err(hsotg->dev, " Setup buffer: %p, Setup DMA: %08lx\n",
|
|
|
|
urb->setup_packet, (unsigned long)urb->setup_dma);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_err(hsotg->dev, " Interval: %d\n", urb->interval);
|
|
|
|
|
|
|
|
/* Core halts the channel for Descriptor DMA mode */
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
DWC2_HC_XFER_AHB_ERR);
|
|
|
|
goto handle_ahberr_done;
|
|
|
|
}
|
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_host_complete(hsotg, qtd, -EIO);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
handle_ahberr_halt:
|
|
|
|
/*
|
|
|
|
* Force a channel halt. Don't call dwc2_halt_channel because that won't
|
|
|
|
* write to the HCCHARn register in DMA mode to force the halt.
|
|
|
|
*/
|
|
|
|
dwc2_hc_halt(hsotg, chan, DWC2_HC_XFER_AHB_ERR);
|
|
|
|
|
|
|
|
handle_ahberr_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_AHBERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel transaction error interrupt. This handler may be
|
|
|
|
* called in either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_xacterr_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"--Host Channel %d Interrupt: Transaction Error--\n", chnum);
|
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
DWC2_HC_XFER_XACT_ERR);
|
|
|
|
goto handle_xacterr_done;
|
|
|
|
}
|
|
|
|
|
|
|
|
switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
qtd->error_count++;
|
|
|
|
if (!chan->qh->ping_state) {
|
|
|
|
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
|
|
|
|
qtd, DWC2_HC_XFER_XACT_ERR);
|
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
if (!chan->ep_is_in && chan->speed == USB_SPEED_HIGH)
|
|
|
|
chan->qh->ping_state = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Halt the channel so the transfer can be re-started from
|
|
|
|
* the appropriate point or the PING protocol will start
|
|
|
|
*/
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
qtd->error_count++;
|
|
|
|
if (chan->do_split && chan->complete_split)
|
|
|
|
qtd->complete_split = 0;
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
{
|
|
|
|
enum dwc2_halt_status halt_status;
|
|
|
|
|
|
|
|
halt_status = dwc2_update_isoc_urb_state(hsotg, chan,
|
2017-01-18 12:31:58 +08:00
|
|
|
chnum, qtd, DWC2_HC_XFER_XACT_ERR);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
handle_xacterr_done:
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_XACTERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel frame overrun interrupt. This handler may be called
|
|
|
|
* in either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_frmovrun_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
enum dwc2_halt_status halt_status;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_dbg(hsotg->dev, "--Host Channel %d Interrupt: Frame Overrun--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2013-07-14 05:53:48 +08:00
|
|
|
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
switch (dwc2_hcd_get_pipe_type(&qtd->urb->pipe_info)) {
|
|
|
|
case USB_ENDPOINT_XFER_CONTROL:
|
|
|
|
case USB_ENDPOINT_XFER_BULK:
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_INT:
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_FRAME_OVERRUN);
|
|
|
|
break;
|
|
|
|
case USB_ENDPOINT_XFER_ISOC:
|
|
|
|
halt_status = dwc2_update_isoc_urb_state(hsotg, chan, chnum,
|
2017-01-18 12:31:58 +08:00
|
|
|
qtd, DWC2_HC_XFER_FRAME_OVERRUN);
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, halt_status);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_FRMOVRUN);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel data toggle error interrupt. This handler may be
|
|
|
|
* called in either DMA mode or Slave mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_datatglerr_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"--Host Channel %d Interrupt: Data Toggle Error--\n", chnum);
|
|
|
|
|
|
|
|
if (chan->ep_is_in)
|
|
|
|
qtd->error_count = 0;
|
|
|
|
else
|
|
|
|
dev_err(hsotg->dev,
|
|
|
|
"Data Toggle Error on OUT transfer, channel %d\n",
|
|
|
|
chnum);
|
|
|
|
|
|
|
|
dwc2_hc_handle_tt_clear(hsotg, chan, qtd);
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_DATATGLERR);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* For debug only. It checks that a valid halt status is set and that
|
|
|
|
* HCCHARn.chdis is clear. If there's a problem, corrective action is
|
|
|
|
* taken and a warning is issued.
|
|
|
|
*
|
|
|
|
* Return: true if halt status is ok, false otherwise
|
|
|
|
*/
|
|
|
|
static bool dwc2_halt_status_ok(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
u32 hcchar;
|
|
|
|
u32 hctsiz;
|
|
|
|
u32 hcintmsk;
|
|
|
|
u32 hcsplt;
|
|
|
|
|
|
|
|
if (chan->halt_status == DWC2_HC_XFER_NO_HALT_STATUS) {
|
|
|
|
/*
|
|
|
|
* This code is here only as a check. This condition should
|
|
|
|
* never happen. Ignore the halt if it does occur.
|
|
|
|
*/
|
2018-07-26 22:00:13 +08:00
|
|
|
hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
|
|
|
|
hctsiz = dwc2_readl(hsotg, HCTSIZ(chnum));
|
|
|
|
hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
|
|
|
|
hcsplt = dwc2_readl(hsotg, HCSPLT(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"%s: chan->halt_status DWC2_HC_XFER_NO_HALT_STATUS,\n",
|
|
|
|
__func__);
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"channel %d, hcchar 0x%08x, hctsiz 0x%08x,\n",
|
|
|
|
chnum, hcchar, hctsiz);
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"hcint 0x%08x, hcintmsk 0x%08x, hcsplt 0x%08x,\n",
|
|
|
|
chan->hcint, hcintmsk, hcsplt);
|
2013-03-26 03:00:25 +08:00
|
|
|
if (qtd)
|
|
|
|
dev_dbg(hsotg->dev, "qtd->complete_split %d\n",
|
|
|
|
qtd->complete_split);
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_warn(hsotg->dev,
|
|
|
|
"%s: no halt status, channel %d, ignoring interrupt\n",
|
|
|
|
__func__, chnum);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This code is here only as a check. hcchar.chdis should never be set
|
|
|
|
* when the halt interrupt occurs. Halt the channel again if it does
|
|
|
|
* occur.
|
|
|
|
*/
|
2018-07-26 22:00:13 +08:00
|
|
|
hcchar = dwc2_readl(hsotg, HCCHAR(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
if (hcchar & HCCHAR_CHDIS) {
|
|
|
|
dev_warn(hsotg->dev,
|
|
|
|
"%s: hcchar.chdis set unexpectedly, hcchar 0x%08x, trying to halt again\n",
|
|
|
|
__func__, hcchar);
|
|
|
|
chan->halt_pending = 0;
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, chan->halt_status);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host Channel Halted interrupt in DMA mode. This handler
|
|
|
|
* determines the reason the channel halted and proceeds accordingly.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_chhltd_intr_dma(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
|
|
|
u32 hcintmsk;
|
|
|
|
int out_nak_enh = 0;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"--Host Channel %d Interrupt: DMA Channel Halted--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* For core with OUT NAK enhancement, the flow for high-speed
|
|
|
|
* CONTROL/BULK OUT is handled a little differently
|
|
|
|
*/
|
2013-08-31 00:45:21 +08:00
|
|
|
if (hsotg->hw_params.snpsid >= DWC2_CORE_REV_2_71a) {
|
2013-03-12 08:47:59 +08:00
|
|
|
if (chan->speed == USB_SPEED_HIGH && !chan->ep_is_in &&
|
|
|
|
(chan->ep_type == USB_ENDPOINT_XFER_CONTROL ||
|
|
|
|
chan->ep_type == USB_ENDPOINT_XFER_BULK)) {
|
|
|
|
out_nak_enh = 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE ||
|
|
|
|
(chan->halt_status == DWC2_HC_XFER_AHB_ERR &&
|
2017-01-24 06:57:26 +08:00
|
|
|
!hsotg->params.dma_desc_enable)) {
|
|
|
|
if (hsotg->params.dma_desc_enable)
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
chan->halt_status);
|
|
|
|
else
|
|
|
|
/*
|
|
|
|
* Just release the channel. A dequeue can happen on a
|
|
|
|
* transfer timeout. In the case of an AHB Error, the
|
|
|
|
* channel was forced to halt because there's no way to
|
|
|
|
* gracefully recover.
|
|
|
|
*/
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd,
|
|
|
|
chan->halt_status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
|
|
|
|
if (chan->hcint & HCINTMSK_XFERCOMPL) {
|
|
|
|
/*
|
|
|
|
* Todo: This is here because of a possible hardware bug. Spec
|
|
|
|
* says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT
|
|
|
|
* interrupt w/ACK bit set should occur, but I only see the
|
|
|
|
* XFERCOMP bit, even with it masked out. This is a workaround
|
|
|
|
* for that behavior. Should fix this when hardware is fixed.
|
|
|
|
*/
|
|
|
|
if (chan->ep_type == USB_ENDPOINT_XFER_ISOC && !chan->ep_is_in)
|
|
|
|
dwc2_hc_ack_intr(hsotg, chan, chnum, qtd);
|
|
|
|
dwc2_hc_xfercomp_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if (chan->hcint & HCINTMSK_STALL) {
|
|
|
|
dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if ((chan->hcint & HCINTMSK_XACTERR) &&
|
2017-01-24 06:57:26 +08:00
|
|
|
!hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
if (out_nak_enh) {
|
|
|
|
if (chan->hcint &
|
|
|
|
(HCINTMSK_NYET | HCINTMSK_NAK | HCINTMSK_ACK)) {
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"XactErr with NYET/NAK/ACK\n");
|
|
|
|
qtd->error_count = 0;
|
|
|
|
} else {
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"XactErr without NYET/NAK/ACK\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Must handle xacterr before nak or ack. Could get a xacterr
|
|
|
|
* at the same time as either of these on a BULK/CONTROL OUT
|
|
|
|
* that started with a PING. The xacterr takes precedence.
|
|
|
|
*/
|
|
|
|
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if ((chan->hcint & HCINTMSK_XCS_XACT) &&
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if ((chan->hcint & HCINTMSK_AHBERR) &&
|
2017-01-24 06:57:26 +08:00
|
|
|
hsotg->params.dma_desc_enable) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if (chan->hcint & HCINTMSK_BBLERR) {
|
|
|
|
dwc2_hc_babble_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if (chan->hcint & HCINTMSK_FRMOVRUN) {
|
|
|
|
dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if (!out_nak_enh) {
|
|
|
|
if (chan->hcint & HCINTMSK_NYET) {
|
|
|
|
/*
|
|
|
|
* Must handle nyet before nak or ack. Could get a nyet
|
|
|
|
* at the same time as either of those on a BULK/CONTROL
|
|
|
|
* OUT that started with a PING. The nyet takes
|
|
|
|
* precedence.
|
|
|
|
*/
|
|
|
|
dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if ((chan->hcint & HCINTMSK_NAK) &&
|
|
|
|
!(hcintmsk & HCINTMSK_NAK)) {
|
|
|
|
/*
|
|
|
|
* If nak is not masked, it's because a non-split IN
|
|
|
|
* transfer is in an error state. In that case, the nak
|
|
|
|
* is handled by the nak interrupt handler, not here.
|
|
|
|
* Handle nak here for BULK/CONTROL OUT transfers, which
|
|
|
|
* halt on a NAK to allow rewinding the buffer pointer.
|
|
|
|
*/
|
|
|
|
dwc2_hc_nak_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else if ((chan->hcint & HCINTMSK_ACK) &&
|
|
|
|
!(hcintmsk & HCINTMSK_ACK)) {
|
|
|
|
/*
|
|
|
|
* If ack is not masked, it's because a non-split IN
|
|
|
|
* transfer is in an error state. In that case, the ack
|
|
|
|
* is handled by the ack interrupt handler, not here.
|
|
|
|
* Handle ack here for split transfers. Start splits
|
|
|
|
* halt on ACK.
|
|
|
|
*/
|
|
|
|
dwc2_hc_ack_intr(hsotg, chan, chnum, qtd);
|
|
|
|
} else {
|
|
|
|
if (chan->ep_type == USB_ENDPOINT_XFER_INT ||
|
|
|
|
chan->ep_type == USB_ENDPOINT_XFER_ISOC) {
|
|
|
|
/*
|
|
|
|
* A periodic transfer halted with no other
|
|
|
|
* channel interrupts set. Assume it was halted
|
|
|
|
* by the core because it could not be completed
|
|
|
|
* in its scheduled (micro)frame.
|
|
|
|
*/
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
"%s: Halt channel %d (assume incomplete periodic transfer)\n",
|
|
|
|
__func__, chnum);
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd,
|
2017-01-18 12:31:58 +08:00
|
|
|
DWC2_HC_XFER_PERIODIC_INCOMPLETE);
|
2013-03-12 08:47:59 +08:00
|
|
|
} else {
|
|
|
|
dev_err(hsotg->dev,
|
|
|
|
"%s: Channel %d - ChHltd set, but reason is unknown\n",
|
|
|
|
__func__, chnum);
|
|
|
|
dev_err(hsotg->dev,
|
|
|
|
"hcint 0x%08x, intsts 0x%08x\n",
|
|
|
|
chan->hcint,
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_readl(hsotg, GINTSTS));
|
2014-09-12 06:22:48 +08:00
|
|
|
goto error;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
dev_info(hsotg->dev,
|
|
|
|
"NYET/NAK/ACK/other in non-error case, 0x%08x\n",
|
|
|
|
chan->hcint);
|
2014-09-12 06:22:48 +08:00
|
|
|
error:
|
|
|
|
/* Failthrough: use 3-strikes rule */
|
|
|
|
qtd->error_count++;
|
|
|
|
dwc2_update_urb_state_abn(hsotg, chan, chnum, qtd->urb,
|
|
|
|
qtd, DWC2_HC_XFER_XACT_ERR);
|
usb: dwc2: Abort transaction after errors with unknown reason
In some situations, the following error messages are reported.
dwc2 ff540000.usb: dwc2_hc_chhltd_intr_dma: Channel 1 - ChHltd set, but reason is unknown
dwc2 ff540000.usb: hcint 0x00000002, intsts 0x04000021
This is sometimes followed by:
dwc2 ff540000.usb: dwc2_update_urb_state_abn(): trimming xfer length
and then:
WARNING: CPU: 0 PID: 0 at kernel/v4.19/drivers/usb/dwc2/hcd.c:2913
dwc2_assign_and_init_hc+0x98c/0x990
The warning suggests that an odd buffer address is to be used for DMA.
After an error is observed, the receive buffer may be full
(urb->actual_length >= urb->length). However, the urb is still left in
the queue unless three errors were observed in a row. When it is queued
again, the dwc2 hcd code translates this into a 1-block transfer.
If urb->actual_length (ie the total expected receive length) is not
DMA-aligned, the buffer pointer programmed into the chip will be
unaligned. This results in the observed warning.
To solve the problem, abort input transactions after an error with
unknown cause if the entire packet was already received. This may be
a bit drastic, but we don't really know why the transfer was aborted
even though the entire packet was received. Aborting the transfer in
this situation is less risky than accepting a potentially corrupted
packet.
With this patch in place, the 'ChHltd set' and 'trimming xfer length'
messages are still observed, but there are no more transfer attempts
with odd buffer addresses.
Fixes: 151d0cbdbe860 ("usb: dwc2: make the scheduler handle excessive NAKs better")
Cc: Boris ARZUR <boris@konbu.org>
Cc: Douglas Anderson <dianders@chromium.org>
Tested-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Reviewed-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Signed-off-by: Nicolas Saenz Julienne <nsaenzjulienne@suse.de>
Link: https://lore.kernel.org/r/20210113112052.17063-3-nsaenzjulienne@suse.de
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
2021-01-13 19:20:50 +08:00
|
|
|
/*
|
|
|
|
* We can get here after a completed transaction
|
|
|
|
* (urb->actual_length >= urb->length) which was not reported
|
|
|
|
* as completed. If that is the case, and we do not abort
|
|
|
|
* the transfer, a transfer of size 0 will be enqueued
|
|
|
|
* subsequently. If urb->actual_length is not DMA-aligned,
|
|
|
|
* the buffer will then point to an unaligned address, and
|
|
|
|
* the resulting behavior is undefined. Bail out in that
|
|
|
|
* situation.
|
|
|
|
*/
|
|
|
|
if (qtd->urb->actual_length >= qtd->urb->length)
|
|
|
|
qtd->error_count = 3;
|
2014-09-12 06:22:48 +08:00
|
|
|
dwc2_hcd_save_data_toggle(hsotg, chan, chnum, qtd);
|
|
|
|
dwc2_halt_channel(hsotg, chan, qtd, DWC2_HC_XFER_XACT_ERR);
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Handles a host channel Channel Halted interrupt
|
|
|
|
*
|
|
|
|
* In slave mode, this handler is called only when the driver specifically
|
|
|
|
* requests a halt. This occurs during handling other host channel interrupts
|
|
|
|
* (e.g. nak, xacterr, stall, nyet, etc.).
|
|
|
|
*
|
|
|
|
* In DMA mode, this is the interrupt that occurs when the core has finished
|
|
|
|
* processing a transfer on a channel. Other host channel interrupts (except
|
|
|
|
* ahberr) are disabled in DMA mode.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_chhltd_intr(struct dwc2_hsotg *hsotg,
|
|
|
|
struct dwc2_host_chan *chan, int chnum,
|
|
|
|
struct dwc2_qtd *qtd)
|
|
|
|
{
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_hc(chan))
|
|
|
|
dev_vdbg(hsotg->dev, "--Host Channel %d Interrupt: Channel Halted--\n",
|
|
|
|
chnum);
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.host_dma) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_chhltd_intr_dma(hsotg, chan, chnum, qtd);
|
|
|
|
} else {
|
|
|
|
if (!dwc2_halt_status_ok(hsotg, chan, chnum, qtd))
|
|
|
|
return;
|
|
|
|
dwc2_release_channel(hsotg, chan, qtd, chan->halt_status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
/*
|
|
|
|
* Check if the given qtd is still the top of the list (and thus valid).
|
|
|
|
*
|
|
|
|
* If dwc2_hcd_qtd_unlink_and_free() has been called since we grabbed
|
|
|
|
* the qtd from the top of the list, this will return false (otherwise true).
|
|
|
|
*/
|
|
|
|
static bool dwc2_check_qtd_still_ok(struct dwc2_qtd *qtd, struct dwc2_qh *qh)
|
|
|
|
{
|
|
|
|
struct dwc2_qtd *cur_head;
|
|
|
|
|
2017-01-18 12:30:27 +08:00
|
|
|
if (!qh)
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
return false;
|
|
|
|
|
|
|
|
cur_head = list_first_entry(&qh->qtd_list, struct dwc2_qtd,
|
|
|
|
qtd_list_entry);
|
|
|
|
return (cur_head == qtd);
|
|
|
|
}
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
/* Handles interrupt for a specific Host Channel */
|
|
|
|
static void dwc2_hc_n_intr(struct dwc2_hsotg *hsotg, int chnum)
|
|
|
|
{
|
|
|
|
struct dwc2_qtd *qtd;
|
|
|
|
struct dwc2_host_chan *chan;
|
|
|
|
u32 hcint, hcintmsk;
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
chan = hsotg->hc_ptr_array[chnum];
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
hcint = dwc2_readl(hsotg, HCINT(chnum));
|
|
|
|
hcintmsk = dwc2_readl(hsotg, HCINTMSK(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
if (!chan) {
|
|
|
|
dev_err(hsotg->dev, "## hc_ptr_array for channel is NULL ##\n");
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hcint, HCINT(chnum));
|
2013-03-12 08:47:59 +08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-10-30 06:46:55 +08:00
|
|
|
if (dbg_hc(chan)) {
|
|
|
|
dev_vdbg(hsotg->dev, "--Host Channel Interrupt--, Channel %d\n",
|
|
|
|
chnum);
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
" hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
|
|
|
|
hcint, hcintmsk, hcint & hcintmsk);
|
|
|
|
}
|
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_writel(hsotg, hcint, HCINT(chnum));
|
2016-01-29 10:19:55 +08:00
|
|
|
|
|
|
|
/*
|
|
|
|
* If we got an interrupt after someone called
|
|
|
|
* dwc2_hcd_endpoint_disable() we don't want to crash below
|
|
|
|
*/
|
|
|
|
if (!chan->qh) {
|
|
|
|
dev_warn(hsotg->dev, "Interrupt on disabled channel\n");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
chan->hcint = hcint;
|
|
|
|
hcint &= hcintmsk;
|
|
|
|
|
2013-03-26 03:00:25 +08:00
|
|
|
/*
|
|
|
|
* If the channel was halted due to a dequeue, the qtd list might
|
|
|
|
* be empty or at least the first entry will not be the active qtd.
|
|
|
|
* In this case, take a shortcut and just release the channel.
|
|
|
|
*/
|
|
|
|
if (chan->halt_status == DWC2_HC_XFER_URB_DEQUEUE) {
|
|
|
|
/*
|
|
|
|
* If the channel was halted, this should be the only
|
|
|
|
* interrupt unmasked
|
|
|
|
*/
|
|
|
|
WARN_ON(hcint != HCINTMSK_CHHLTD);
|
2017-01-24 06:57:26 +08:00
|
|
|
if (hsotg->params.dma_desc_enable)
|
2013-03-26 03:00:25 +08:00
|
|
|
dwc2_hcd_complete_xfer_ddma(hsotg, chan, chnum,
|
|
|
|
chan->halt_status);
|
|
|
|
else
|
|
|
|
dwc2_release_channel(hsotg, chan, NULL,
|
|
|
|
chan->halt_status);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2013-03-12 08:47:59 +08:00
|
|
|
if (list_empty(&chan->qh->qtd_list)) {
|
2013-03-26 03:00:25 +08:00
|
|
|
/*
|
|
|
|
* TODO: Will this ever happen with the
|
|
|
|
* DWC2_HC_XFER_URB_DEQUEUE handling above?
|
|
|
|
*/
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_dbg(hsotg->dev, "## no QTD queued for channel %d ##\n",
|
|
|
|
chnum);
|
|
|
|
dev_dbg(hsotg->dev,
|
|
|
|
" hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n",
|
|
|
|
chan->hcint, hcintmsk, hcint);
|
|
|
|
chan->halt_status = DWC2_HC_XFER_NO_HALT_STATUS;
|
|
|
|
disable_hc_int(hsotg, chnum, HCINTMSK_CHHLTD);
|
|
|
|
chan->hcint = 0;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
qtd = list_first_entry(&chan->qh->qtd_list, struct dwc2_qtd,
|
|
|
|
qtd_list_entry);
|
|
|
|
|
2017-01-24 06:57:26 +08:00
|
|
|
if (!hsotg->params.host_dma) {
|
2013-03-12 08:47:59 +08:00
|
|
|
if ((hcint & HCINTMSK_CHHLTD) && hcint != HCINTMSK_CHHLTD)
|
|
|
|
hcint &= ~HCINTMSK_CHHLTD;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (hcint & HCINTMSK_XFERCOMPL) {
|
|
|
|
dwc2_hc_xfercomp_intr(hsotg, chan, chnum, qtd);
|
|
|
|
/*
|
|
|
|
* If NYET occurred at same time as Xfer Complete, the NYET is
|
|
|
|
* handled by the Xfer Complete interrupt handler. Don't want
|
|
|
|
* to call the NYET interrupt handler in this case.
|
|
|
|
*/
|
|
|
|
hcint &= ~HCINTMSK_NYET;
|
|
|
|
}
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
|
|
|
|
if (hcint & HCINTMSK_CHHLTD) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_chhltd_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_AHBERR) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_ahberr_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_STALL) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_stall_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_NAK) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_nak_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_ACK) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_ack_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_NYET) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_nyet_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_XACTERR) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_xacterr_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_BBLERR) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_babble_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_FRMOVRUN) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_frmovrun_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
|
|
|
if (hcint & HCINTMSK_DATATGLERR) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dwc2_hc_datatglerr_intr(hsotg, chan, chnum, qtd);
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
if (!dwc2_check_qtd_still_ok(qtd, chan->qh))
|
|
|
|
goto exit;
|
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
|
usb: dwc2: host: Fix use after free w/ simultaneous irqs
While plugging / unplugging on a DWC2 host port with "slub_debug=FZPUA"
enabled, I found a crash that was quite obviously a use after free.
It appears that in some cases when we handle the various sub-cases of
HCINT we may end up freeing the QTD. If there is more than one bit set
in HCINT we may then end up continuing to use the QTD, which is bad.
Let's be paranoid and check for this after each sub-case. This should
be safe since we officially have the "hsotg->lock" (it was grabbed in
dwc2_handle_hcd_intr).
The specific crash I found was:
Unable to handle kernel paging request at virtual address 6b6b6b9f
At the time of the crash, the kernel reported:
(dwc2_hc_nak_intr+0x5c/0x198)
(dwc2_handle_hcd_intr+0xa84/0xbf8)
(_dwc2_hcd_irq+0x1c/0x20)
(usb_hcd_irq+0x34/0x48)
Popping into kgdb found that "*qtd" was filled with "0x6b", AKA qtd had
been freed and filled with slub_debug poison.
kgdb gave a little better stack crawl:
0 dwc2_hc_nak_intr (hsotg=hsotg@entry=0xec42e058,
chan=chan@entry=0xec546dc0, chnum=chnum@entry=4,
qtd=qtd@entry=0xec679600) at drivers/usb/dwc2/hcd_intr.c:1237
1 dwc2_hc_n_intr (chnum=4, hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2041
2 dwc2_hc_intr (hsotg=0xec42e058) at drivers/usb/dwc2/hcd_intr.c:2078
3 dwc2_handle_hcd_intr (hsotg=0xec42e058) at
drivers/usb/dwc2/hcd_intr.c:2128
4 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
5 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Popping up to frame #1 (dwc2_hc_n_intr) found:
(gdb) print /x hcint
$12 = 0x12
AKA:
#define HCINTMSK_CHHLTD (1 << 1)
#define HCINTMSK_NAK (1 << 4)
Further debugging found that by simulating receiving those two
interrupts at the same time it was trivial to replicate the
use-after-free. See <http://crosreview.com/305712> for a patch and
instructions. This lead to getting the following stack crawl of the
actual free:
0 arch_kgdb_breakpoint () at arch/arm/include/asm/outercache.h:103
1 kgdb_breakpoint () at kernel/debug/debug_core.c:1054
2 dwc2_hcd_qtd_unlink_and_free (hsotg=<optimized out>, qh=<optimized
out>, qtd=0xe4479a00) at drivers/usb/dwc2/hcd.h:488
3 dwc2_deactivate_qh (free_qtd=<optimized out>, qh=0xe5efa280,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:671
4 dwc2_release_channel (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, qtd=<optimized out>,
halt_status=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:742
5 dwc2_halt_channel (hsotg=0xed424618, chan=0xed5be000, qtd=<optimized
out>, halt_status=<optimized out>) at
drivers/usb/dwc2/hcd_intr.c:804
6 dwc2_complete_non_periodic_xfer (chnum=<optimized out>,
halt_status=<optimized out>, qtd=<optimized out>, chan=<optimized
out>, hsotg=<optimized out>) at drivers/usb/dwc2/hcd_intr.c:889
7 dwc2_hc_xfercomp_intr (hsotg=hsotg@entry=0xed424618,
chan=chan@entry=0xed5be000, chnum=chnum@entry=6,
qtd=qtd@entry=0xe4479a00) at drivers/usb/dwc2/hcd_intr.c:1065
8 dwc2_hc_chhltd_intr_dma (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1823
9 dwc2_hc_chhltd_intr (qtd=0xe4479a00, chnum=6, chan=0xed5be000,
hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:1944
10 dwc2_hc_n_intr (chnum=6, hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2052
11 dwc2_hc_intr (hsotg=0xed424618) at drivers/usb/dwc2/hcd_intr.c:2097
12 dwc2_handle_hcd_intr (hsotg=0xed424618) at
drivers/usb/dwc2/hcd_intr.c:2147
13 _dwc2_hcd_irq (hcd=<optimized out>) at drivers/usb/dwc2/hcd.c:2837
14 usb_hcd_irq (irq=<optimized out>, __hcd=<optimized out>) at
drivers/usb/core/hcd.c:2353
Though we could add specific code to handle this case, adding the
general purpose code to check for all cases where qtd might be freed
seemed safer.
Acked-by: John Youn <johnyoun@synopsys.com>
Signed-off-by: Douglas Anderson <dianders@chromium.org>
Signed-off-by: Felipe Balbi <balbi@ti.com>
2015-10-17 07:01:32 +08:00
|
|
|
exit:
|
2013-03-12 08:47:59 +08:00
|
|
|
chan->hcint = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* This interrupt indicates that one or more host channels has a pending
|
|
|
|
* interrupt. There are multiple conditions that can cause each host channel
|
|
|
|
* interrupt. This function determines which conditions have occurred for each
|
|
|
|
* host channel interrupt and handles them appropriately.
|
|
|
|
*/
|
|
|
|
static void dwc2_hc_intr(struct dwc2_hsotg *hsotg)
|
|
|
|
{
|
|
|
|
u32 haint;
|
|
|
|
int i;
|
2016-01-29 10:19:57 +08:00
|
|
|
struct dwc2_host_chan *chan, *chan_tmp;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2018-07-26 22:00:13 +08:00
|
|
|
haint = dwc2_readl(hsotg, HAINT);
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_perio()) {
|
|
|
|
dev_vdbg(hsotg->dev, "%s()\n", __func__);
|
|
|
|
|
|
|
|
dev_vdbg(hsotg->dev, "HAINT=%08x\n", haint);
|
|
|
|
}
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2016-01-29 10:19:57 +08:00
|
|
|
/*
|
|
|
|
* According to USB 2.0 spec section 11.18.8, a host must
|
|
|
|
* issue complete-split transactions in a microframe for a
|
|
|
|
* set of full-/low-speed endpoints in the same relative
|
|
|
|
* order as the start-splits were issued in a microframe for.
|
|
|
|
*/
|
|
|
|
list_for_each_entry_safe(chan, chan_tmp, &hsotg->split_order,
|
|
|
|
split_order_list_entry) {
|
|
|
|
int hc_num = chan->hc_num;
|
|
|
|
|
|
|
|
if (haint & (1 << hc_num)) {
|
|
|
|
dwc2_hc_n_intr(hsotg, hc_num);
|
|
|
|
haint &= ~(1 << hc_num);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-04 08:55:53 +08:00
|
|
|
for (i = 0; i < hsotg->params.host_channels; i++) {
|
2013-03-12 08:47:59 +08:00
|
|
|
if (haint & (1 << i))
|
|
|
|
dwc2_hc_n_intr(hsotg, i);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This function handles interrupts for the HCD */
|
2013-04-26 05:39:15 +08:00
|
|
|
irqreturn_t dwc2_handle_hcd_intr(struct dwc2_hsotg *hsotg)
|
2013-03-12 08:47:59 +08:00
|
|
|
{
|
2013-04-10 15:55:50 +08:00
|
|
|
u32 gintsts, dbg_gintsts;
|
2013-04-26 05:39:14 +08:00
|
|
|
irqreturn_t retval = IRQ_NONE;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2013-11-26 05:42:44 +08:00
|
|
|
if (!dwc2_is_controller_alive(hsotg)) {
|
2013-11-23 08:43:51 +08:00
|
|
|
dev_warn(hsotg->dev, "Controller is dead\n");
|
2013-04-26 05:39:14 +08:00
|
|
|
return retval;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
spin_lock(&hsotg->lock);
|
|
|
|
|
|
|
|
/* Check if HOST Mode */
|
|
|
|
if (dwc2_is_host_mode(hsotg)) {
|
|
|
|
gintsts = dwc2_read_core_intr(hsotg);
|
|
|
|
if (!gintsts) {
|
|
|
|
spin_unlock(&hsotg->lock);
|
2013-04-26 05:39:14 +08:00
|
|
|
return retval;
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
|
2013-04-26 05:39:14 +08:00
|
|
|
retval = IRQ_HANDLED;
|
2013-03-12 08:47:59 +08:00
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
dbg_gintsts = gintsts;
|
2013-03-12 08:47:59 +08:00
|
|
|
#ifndef DEBUG_SOF
|
2013-04-10 15:55:50 +08:00
|
|
|
dbg_gintsts &= ~GINTSTS_SOF;
|
2013-03-12 08:47:59 +08:00
|
|
|
#endif
|
2013-04-10 15:55:50 +08:00
|
|
|
if (!dbg_perio())
|
|
|
|
dbg_gintsts &= ~(GINTSTS_HCHINT | GINTSTS_RXFLVL |
|
|
|
|
GINTSTS_PTXFEMP);
|
|
|
|
|
|
|
|
/* Only print if there are any non-suppressed interrupts left */
|
|
|
|
if (dbg_gintsts)
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x\n",
|
|
|
|
gintsts);
|
|
|
|
|
|
|
|
if (gintsts & GINTSTS_SOF)
|
|
|
|
dwc2_sof_intr(hsotg);
|
|
|
|
if (gintsts & GINTSTS_RXFLVL)
|
|
|
|
dwc2_rx_fifo_level_intr(hsotg);
|
|
|
|
if (gintsts & GINTSTS_NPTXFEMP)
|
|
|
|
dwc2_np_tx_fifo_empty_intr(hsotg);
|
|
|
|
if (gintsts & GINTSTS_PRTINT)
|
|
|
|
dwc2_port_intr(hsotg);
|
|
|
|
if (gintsts & GINTSTS_HCHINT)
|
|
|
|
dwc2_hc_intr(hsotg);
|
|
|
|
if (gintsts & GINTSTS_PTXFEMP)
|
|
|
|
dwc2_perio_tx_fifo_empty_intr(hsotg);
|
|
|
|
|
2013-04-10 15:55:50 +08:00
|
|
|
if (dbg_gintsts) {
|
2013-03-12 08:47:59 +08:00
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"DWC OTG HCD Finished Servicing Interrupts\n");
|
|
|
|
dev_vdbg(hsotg->dev,
|
|
|
|
"DWC OTG HCD gintsts=0x%08x gintmsk=0x%08x\n",
|
2018-07-26 22:00:13 +08:00
|
|
|
dwc2_readl(hsotg, GINTSTS),
|
|
|
|
dwc2_readl(hsotg, GINTMSK));
|
2013-03-12 08:47:59 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
spin_unlock(&hsotg->lock);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|