mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-27 06:04:23 +08:00
USB fixes for 5.7-rc3
Here are a number of USB driver fixes for 5.7-rc3.
Nothing huge, just the usual collection of:
- xhci fixes
- gadget driver fixes
- syzkaller fuzzing fixes
- new device ids and DT bindings
- new quirks added for broken devices
A few of the gadget driver fixes show up twice here as they were applied
to my branch, and also by Felipe to his branch which I then pulled in as
we got out of sync a bit.
All of these have been in linux-next with no reported issues.
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-----BEGIN PGP SIGNATURE-----
iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXqVfog8cZ3JlZ0Brcm9h
aC5jb20ACgkQMUfUDdst+ynVFwCfb33URz0KrXE+ipecpheVEoMdvDsAoIuYG4cb
qxLwEE6203jdICq9/nDg
=8f/s
-----END PGP SIGNATURE-----
Merge tag 'usb-5.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb
Pull USB fixes from Greg KH:
"Here are a number of USB driver fixes for 5.7-rc3.
Nothing huge, just the usual collection of:
- xhci fixes
- gadget driver fixes
- syzkaller fuzzing fixes
- new device ids and DT bindings
- new quirks added for broken devices
A few of the gadget driver fixes show up twice here as they were
applied to my branch, and also by Felipe to his branch which I then
pulled in as we got out of sync a bit.
All of these have been in linux-next with no reported issues"
* tag 'usb-5.7-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (33 commits)
USB: sisusbvga: Change port variable from signed to unsigned
usb-storage: Add unusual_devs entry for JMicron JMS566
USB: hub: Revert commit bd0e6c9614
("usb: hub: try old enumeration scheme first for high speed devices")
USB: hub: Fix handling of connect changes during sleep
usb: typec: altmode: Fix typec_altmode_get_partner sometimes returning an invalid pointer
xhci: Don't clear hub TT buffer on ep0 protocol stall
xhci: prevent bus suspend if a roothub port detected a over-current condition
xhci: Fix handling halted endpoint even if endpoint ring appears empty
usb: raw-gadget: Fix copy_to/from_user() checks
usb: raw-gadget: fix raw_event_queue_fetch locking
usb: gadget: udc: atmel: Fix vbus disconnect handling
usb: dwc3: gadget: Fix request completion check
USB: Add USB_QUIRK_DELAY_CTRL_MSG and USB_QUIRK_DELAY_INIT for Corsair K70 RGB RAPIDFIRE
phy: tegra: Select USB_COMMON for usb_get_maximum_speed()
usb: typec: tcpm: Ignore CC and vbus changes in PORT_RESET change
usb: f_fs: Clear OS Extended descriptor counts to zero in ffs_data_reset()
cdc-acm: introduce a cool down
cdc-acm: close race betrween suspend() and acm_softint
UAS: fix deadlock in error handling and PM flushing work
UAS: no use logging any details in case of ENODEV
...
This commit is contained in:
commit
e9a61afb69
@ -5187,8 +5187,7 @@
|
||||
|
||||
usbcore.old_scheme_first=
|
||||
[USB] Start with the old device initialization
|
||||
scheme, applies only to low and full-speed devices
|
||||
(default 0 = off).
|
||||
scheme (default 0 = off).
|
||||
|
||||
usbcore.usbfs_memory_mb=
|
||||
[USB] Memory limit (in MB) for buffers allocated by
|
||||
|
@ -18,6 +18,7 @@ properties:
|
||||
- renesas,r8a774c0-usb3-peri # RZ/G2E
|
||||
- renesas,r8a7795-usb3-peri # R-Car H3
|
||||
- renesas,r8a7796-usb3-peri # R-Car M3-W
|
||||
- renesas,r8a77961-usb3-peri # R-Car M3-W+
|
||||
- renesas,r8a77965-usb3-peri # R-Car M3-N
|
||||
- renesas,r8a77990-usb3-peri # R-Car E3
|
||||
- const: renesas,rcar-gen3-usb3-peri
|
||||
|
@ -40,6 +40,7 @@ properties:
|
||||
- renesas,usbhs-r8a774c0 # RZ/G2E
|
||||
- renesas,usbhs-r8a7795 # R-Car H3
|
||||
- renesas,usbhs-r8a7796 # R-Car M3-W
|
||||
- renesas,usbhs-r8a77961 # R-Car M3-W+
|
||||
- renesas,usbhs-r8a77965 # R-Car M3-N
|
||||
- renesas,usbhs-r8a77990 # R-Car E3
|
||||
- renesas,usbhs-r8a77995 # R-Car D3
|
||||
|
@ -16,7 +16,8 @@ Required properties:
|
||||
- "renesas,xhci-r8a7791" for r8a7791 SoC
|
||||
- "renesas,xhci-r8a7793" for r8a7793 SoC
|
||||
- "renesas,xhci-r8a7795" for r8a7795 SoC
|
||||
- "renesas,xhci-r8a7796" for r8a7796 SoC
|
||||
- "renesas,xhci-r8a7796" for r8a77960 SoC
|
||||
- "renesas,xhci-r8a77961" for r8a77961 SoC
|
||||
- "renesas,xhci-r8a77965" for r8a77965 SoC
|
||||
- "renesas,xhci-r8a77990" for r8a77990 SoC
|
||||
- "renesas,rcar-gen2-xhci" for a generic R-Car Gen2 or RZ/G1 compatible
|
||||
|
@ -1,7 +1,8 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
config PHY_TEGRA_XUSB
|
||||
tristate "NVIDIA Tegra XUSB pad controller driver"
|
||||
depends on ARCH_TEGRA
|
||||
depends on ARCH_TEGRA && USB_SUPPORT
|
||||
select USB_COMMON
|
||||
select USB_CONN_GPIO
|
||||
select USB_PHY
|
||||
help
|
||||
|
@ -412,9 +412,12 @@ static void acm_ctrl_irq(struct urb *urb)
|
||||
|
||||
exit:
|
||||
retval = usb_submit_urb(urb, GFP_ATOMIC);
|
||||
if (retval && retval != -EPERM)
|
||||
if (retval && retval != -EPERM && retval != -ENODEV)
|
||||
dev_err(&acm->control->dev,
|
||||
"%s - usb_submit_urb failed: %d\n", __func__, retval);
|
||||
else
|
||||
dev_vdbg(&acm->control->dev,
|
||||
"control resubmission terminated %d\n", retval);
|
||||
}
|
||||
|
||||
static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
|
||||
@ -430,6 +433,8 @@ static int acm_submit_read_urb(struct acm *acm, int index, gfp_t mem_flags)
|
||||
dev_err(&acm->data->dev,
|
||||
"urb %d failed submission with %d\n",
|
||||
index, res);
|
||||
} else {
|
||||
dev_vdbg(&acm->data->dev, "intended failure %d\n", res);
|
||||
}
|
||||
set_bit(index, &acm->read_urbs_free);
|
||||
return res;
|
||||
@ -471,6 +476,7 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
int status = urb->status;
|
||||
bool stopped = false;
|
||||
bool stalled = false;
|
||||
bool cooldown = false;
|
||||
|
||||
dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n",
|
||||
rb->index, urb->actual_length, status);
|
||||
@ -497,6 +503,14 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
__func__, status);
|
||||
stopped = true;
|
||||
break;
|
||||
case -EOVERFLOW:
|
||||
case -EPROTO:
|
||||
dev_dbg(&acm->data->dev,
|
||||
"%s - cooling babbling device\n", __func__);
|
||||
usb_mark_last_busy(acm->dev);
|
||||
set_bit(rb->index, &acm->urbs_in_error_delay);
|
||||
cooldown = true;
|
||||
break;
|
||||
default:
|
||||
dev_dbg(&acm->data->dev,
|
||||
"%s - nonzero urb status received: %d\n",
|
||||
@ -518,9 +532,11 @@ static void acm_read_bulk_callback(struct urb *urb)
|
||||
*/
|
||||
smp_mb__after_atomic();
|
||||
|
||||
if (stopped || stalled) {
|
||||
if (stopped || stalled || cooldown) {
|
||||
if (stalled)
|
||||
schedule_work(&acm->work);
|
||||
else if (cooldown)
|
||||
schedule_delayed_work(&acm->dwork, HZ / 2);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -557,14 +573,20 @@ static void acm_softint(struct work_struct *work)
|
||||
struct acm *acm = container_of(work, struct acm, work);
|
||||
|
||||
if (test_bit(EVENT_RX_STALL, &acm->flags)) {
|
||||
if (!(usb_autopm_get_interface(acm->data))) {
|
||||
smp_mb(); /* against acm_suspend() */
|
||||
if (!acm->susp_count) {
|
||||
for (i = 0; i < acm->rx_buflimit; i++)
|
||||
usb_kill_urb(acm->read_urbs[i]);
|
||||
usb_clear_halt(acm->dev, acm->in);
|
||||
acm_submit_read_urbs(acm, GFP_KERNEL);
|
||||
usb_autopm_put_interface(acm->data);
|
||||
clear_bit(EVENT_RX_STALL, &acm->flags);
|
||||
}
|
||||
clear_bit(EVENT_RX_STALL, &acm->flags);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(ACM_ERROR_DELAY, &acm->flags)) {
|
||||
for (i = 0; i < ACM_NR; i++)
|
||||
if (test_and_clear_bit(i, &acm->urbs_in_error_delay))
|
||||
acm_submit_read_urb(acm, i, GFP_NOIO);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(EVENT_TTY_WAKEUP, &acm->flags))
|
||||
@ -1333,6 +1355,7 @@ made_compressed_probe:
|
||||
acm->readsize = readsize;
|
||||
acm->rx_buflimit = num_rx_buf;
|
||||
INIT_WORK(&acm->work, acm_softint);
|
||||
INIT_DELAYED_WORK(&acm->dwork, acm_softint);
|
||||
init_waitqueue_head(&acm->wioctl);
|
||||
spin_lock_init(&acm->write_lock);
|
||||
spin_lock_init(&acm->read_lock);
|
||||
@ -1542,6 +1565,7 @@ static void acm_disconnect(struct usb_interface *intf)
|
||||
|
||||
acm_kill_urbs(acm);
|
||||
cancel_work_sync(&acm->work);
|
||||
cancel_delayed_work_sync(&acm->dwork);
|
||||
|
||||
tty_unregister_device(acm_tty_driver, acm->minor);
|
||||
|
||||
@ -1584,6 +1608,8 @@ static int acm_suspend(struct usb_interface *intf, pm_message_t message)
|
||||
|
||||
acm_kill_urbs(acm);
|
||||
cancel_work_sync(&acm->work);
|
||||
cancel_delayed_work_sync(&acm->dwork);
|
||||
acm->urbs_in_error_delay = 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -109,8 +109,11 @@ struct acm {
|
||||
# define EVENT_TTY_WAKEUP 0
|
||||
# define EVENT_RX_STALL 1
|
||||
# define ACM_THROTTLED 2
|
||||
# define ACM_ERROR_DELAY 3
|
||||
unsigned long urbs_in_error_delay; /* these need to be restarted after a delay */
|
||||
struct usb_cdc_line_coding line; /* bits, stop, parity */
|
||||
struct work_struct work; /* work queue entry for line discipline waking up */
|
||||
struct work_struct work; /* work queue entry for various purposes*/
|
||||
struct delayed_work dwork; /* for cool downs needed in error recovery */
|
||||
unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */
|
||||
unsigned int ctrlout; /* output control lines (DTR, RTS) */
|
||||
struct async_icount iocount; /* counters for control line changes */
|
||||
|
@ -1223,6 +1223,11 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
|
||||
#ifdef CONFIG_PM
|
||||
udev->reset_resume = 1;
|
||||
#endif
|
||||
/* Don't set the change_bits when the device
|
||||
* was powered off.
|
||||
*/
|
||||
if (test_bit(port1, hub->power_bits))
|
||||
set_bit(port1, hub->change_bits);
|
||||
|
||||
} else {
|
||||
/* The power session is gone; tell hub_wq */
|
||||
@ -2723,13 +2728,11 @@ static bool use_new_scheme(struct usb_device *udev, int retry,
|
||||
{
|
||||
int old_scheme_first_port =
|
||||
port_dev->quirks & USB_PORT_QUIRK_OLD_SCHEME;
|
||||
int quick_enumeration = (udev->speed == USB_SPEED_HIGH);
|
||||
|
||||
if (udev->speed >= USB_SPEED_SUPER)
|
||||
return false;
|
||||
|
||||
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first
|
||||
|| quick_enumeration);
|
||||
return USE_NEW_SCHEME(retry, old_scheme_first_port || old_scheme_first);
|
||||
}
|
||||
|
||||
/* Is a USB 3.0 port in the Inactive or Compliance Mode state?
|
||||
@ -3088,6 +3091,15 @@ static int check_port_resume_type(struct usb_device *udev,
|
||||
if (portchange & USB_PORT_STAT_C_ENABLE)
|
||||
usb_clear_port_feature(hub->hdev, port1,
|
||||
USB_PORT_FEAT_C_ENABLE);
|
||||
|
||||
/*
|
||||
* Whatever made this reset-resume necessary may have
|
||||
* turned on the port1 bit in hub->change_bits. But after
|
||||
* a successful reset-resume we want the bit to be clear;
|
||||
* if it was on it would indicate that something happened
|
||||
* following the reset-resume.
|
||||
*/
|
||||
clear_bit(port1, hub->change_bits);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
@ -589,12 +589,13 @@ void usb_sg_cancel(struct usb_sg_request *io)
|
||||
int i, retval;
|
||||
|
||||
spin_lock_irqsave(&io->lock, flags);
|
||||
if (io->status) {
|
||||
if (io->status || io->count == 0) {
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
return;
|
||||
}
|
||||
/* shut everything down */
|
||||
io->status = -ECONNRESET;
|
||||
io->count++; /* Keep the request alive until we're done */
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
|
||||
for (i = io->entries - 1; i >= 0; --i) {
|
||||
@ -608,6 +609,12 @@ void usb_sg_cancel(struct usb_sg_request *io)
|
||||
dev_warn(&io->dev->dev, "%s, unlink --> %d\n",
|
||||
__func__, retval);
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&io->lock, flags);
|
||||
io->count--;
|
||||
if (!io->count)
|
||||
complete(&io->complete);
|
||||
spin_unlock_irqrestore(&io->lock, flags);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_sg_cancel);
|
||||
|
||||
|
@ -430,6 +430,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
||||
/* Corsair K70 LUX */
|
||||
{ USB_DEVICE(0x1b1c, 0x1b36), .driver_info = USB_QUIRK_DELAY_INIT },
|
||||
|
||||
/* Corsair K70 RGB RAPDIFIRE */
|
||||
{ USB_DEVICE(0x1b1c, 0x1b38), .driver_info = USB_QUIRK_DELAY_INIT |
|
||||
USB_QUIRK_DELAY_CTRL_MSG },
|
||||
|
||||
/* MIDI keyboard WORLDE MINI */
|
||||
{ USB_DEVICE(0x1c75, 0x0204), .driver_info =
|
||||
USB_QUIRK_CONFIG_INTF_STRINGS },
|
||||
|
@ -307,10 +307,14 @@
|
||||
|
||||
/* Global TX Fifo Size Register */
|
||||
#define DWC31_GTXFIFOSIZ_TXFRAMNUM BIT(15) /* DWC_usb31 only */
|
||||
#define DWC31_GTXFIFOSIZ_TXFDEF(n) ((n) & 0x7fff) /* DWC_usb31 only */
|
||||
#define DWC3_GTXFIFOSIZ_TXFDEF(n) ((n) & 0xffff)
|
||||
#define DWC31_GTXFIFOSIZ_TXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
|
||||
#define DWC3_GTXFIFOSIZ_TXFDEP(n) ((n) & 0xffff)
|
||||
#define DWC3_GTXFIFOSIZ_TXFSTADDR(n) ((n) & 0xffff0000)
|
||||
|
||||
/* Global RX Fifo Size Register */
|
||||
#define DWC31_GRXFIFOSIZ_RXFDEP(n) ((n) & 0x7fff) /* DWC_usb31 only */
|
||||
#define DWC3_GRXFIFOSIZ_RXFDEP(n) ((n) & 0xffff)
|
||||
|
||||
/* Global Event Size Registers */
|
||||
#define DWC3_GEVNTSIZ_INTMASK BIT(31)
|
||||
#define DWC3_GEVNTSIZ_SIZE(n) ((n) & 0xffff)
|
||||
|
@ -1728,7 +1728,6 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
||||
u32 reg;
|
||||
|
||||
u8 link_state;
|
||||
u8 speed;
|
||||
|
||||
/*
|
||||
* According to the Databook Remote wakeup request should
|
||||
@ -1738,16 +1737,13 @@ static int __dwc3_gadget_wakeup(struct dwc3 *dwc)
|
||||
*/
|
||||
reg = dwc3_readl(dwc->regs, DWC3_DSTS);
|
||||
|
||||
speed = reg & DWC3_DSTS_CONNECTSPD;
|
||||
if ((speed == DWC3_DSTS_SUPERSPEED) ||
|
||||
(speed == DWC3_DSTS_SUPERSPEED_PLUS))
|
||||
return 0;
|
||||
|
||||
link_state = DWC3_DSTS_USBLNKST(reg);
|
||||
|
||||
switch (link_state) {
|
||||
case DWC3_LINK_STATE_RESET:
|
||||
case DWC3_LINK_STATE_RX_DET: /* in HS, means Early Suspend */
|
||||
case DWC3_LINK_STATE_U3: /* in HS, means SUSPEND */
|
||||
case DWC3_LINK_STATE_RESUME:
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
@ -2227,7 +2223,6 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int mdwidth;
|
||||
int kbytes;
|
||||
int size;
|
||||
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
@ -2236,24 +2231,24 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
|
||||
|
||||
size = dwc3_readl(dwc->regs, DWC3_GTXFIFOSIZ(dep->number >> 1));
|
||||
if (dwc3_is_usb31(dwc))
|
||||
size = DWC31_GTXFIFOSIZ_TXFDEF(size);
|
||||
size = DWC31_GTXFIFOSIZ_TXFDEP(size);
|
||||
else
|
||||
size = DWC3_GTXFIFOSIZ_TXFDEF(size);
|
||||
size = DWC3_GTXFIFOSIZ_TXFDEP(size);
|
||||
|
||||
/* FIFO Depth is in MDWDITH bytes. Multiply */
|
||||
size *= mdwidth;
|
||||
|
||||
kbytes = size / 1024;
|
||||
if (kbytes == 0)
|
||||
kbytes = 1;
|
||||
|
||||
/*
|
||||
* FIFO sizes account an extra MDWIDTH * (kbytes + 1) bytes for
|
||||
* internal overhead. We don't really know how these are used,
|
||||
* but documentation say it exists.
|
||||
* To meet performance requirement, a minimum TxFIFO size of 3x
|
||||
* MaxPacketSize is recommended for endpoints that support burst and a
|
||||
* minimum TxFIFO size of 2x MaxPacketSize for endpoints that don't
|
||||
* support burst. Use those numbers and we can calculate the max packet
|
||||
* limit as below.
|
||||
*/
|
||||
size -= mdwidth * (kbytes + 1);
|
||||
size /= kbytes;
|
||||
if (dwc->maximum_speed >= USB_SPEED_SUPER)
|
||||
size /= 3;
|
||||
else
|
||||
size /= 2;
|
||||
|
||||
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
|
||||
|
||||
@ -2271,8 +2266,39 @@ static int dwc3_gadget_init_in_endpoint(struct dwc3_ep *dep)
|
||||
static int dwc3_gadget_init_out_endpoint(struct dwc3_ep *dep)
|
||||
{
|
||||
struct dwc3 *dwc = dep->dwc;
|
||||
int mdwidth;
|
||||
int size;
|
||||
|
||||
usb_ep_set_maxpacket_limit(&dep->endpoint, 1024);
|
||||
mdwidth = DWC3_MDWIDTH(dwc->hwparams.hwparams0);
|
||||
|
||||
/* MDWIDTH is represented in bits, convert to bytes */
|
||||
mdwidth /= 8;
|
||||
|
||||
/* All OUT endpoints share a single RxFIFO space */
|
||||
size = dwc3_readl(dwc->regs, DWC3_GRXFIFOSIZ(0));
|
||||
if (dwc3_is_usb31(dwc))
|
||||
size = DWC31_GRXFIFOSIZ_RXFDEP(size);
|
||||
else
|
||||
size = DWC3_GRXFIFOSIZ_RXFDEP(size);
|
||||
|
||||
/* FIFO depth is in MDWDITH bytes */
|
||||
size *= mdwidth;
|
||||
|
||||
/*
|
||||
* To meet performance requirement, a minimum recommended RxFIFO size
|
||||
* is defined as follow:
|
||||
* RxFIFO size >= (3 x MaxPacketSize) +
|
||||
* (3 x 8 bytes setup packets size) + (16 bytes clock crossing margin)
|
||||
*
|
||||
* Then calculate the max packet limit as below.
|
||||
*/
|
||||
size -= (3 * 8) + 16;
|
||||
if (size < 0)
|
||||
size = 0;
|
||||
else
|
||||
size /= 3;
|
||||
|
||||
usb_ep_set_maxpacket_limit(&dep->endpoint, size);
|
||||
dep->endpoint.max_streams = 15;
|
||||
dep->endpoint.ops = &dwc3_gadget_ep_ops;
|
||||
list_add_tail(&dep->endpoint.ep_list,
|
||||
@ -2484,14 +2510,7 @@ static int dwc3_gadget_ep_reclaim_trb_linear(struct dwc3_ep *dep,
|
||||
|
||||
static bool dwc3_gadget_ep_request_completed(struct dwc3_request *req)
|
||||
{
|
||||
/*
|
||||
* For OUT direction, host may send less than the setup
|
||||
* length. Return true for all OUT requests.
|
||||
*/
|
||||
if (!req->direction)
|
||||
return true;
|
||||
|
||||
return req->request.actual == req->request.length;
|
||||
return req->num_pending_sgs == 0;
|
||||
}
|
||||
|
||||
static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
|
||||
@ -2515,8 +2534,7 @@ static int dwc3_gadget_ep_cleanup_completed_request(struct dwc3_ep *dep,
|
||||
|
||||
req->request.actual = req->request.length - req->remaining;
|
||||
|
||||
if (!dwc3_gadget_ep_request_completed(req) ||
|
||||
req->num_pending_sgs) {
|
||||
if (!dwc3_gadget_ep_request_completed(req)) {
|
||||
__dwc3_gadget_kick_transfer(dep);
|
||||
goto out;
|
||||
}
|
||||
|
@ -728,19 +728,19 @@ static void xdbc_handle_tx_event(struct xdbc_trb *evt_trb)
|
||||
case COMP_USB_TRANSACTION_ERROR:
|
||||
case COMP_STALL_ERROR:
|
||||
default:
|
||||
if (ep_id == XDBC_EPID_OUT)
|
||||
if (ep_id == XDBC_EPID_OUT || ep_id == XDBC_EPID_OUT_INTEL)
|
||||
xdbc.flags |= XDBC_FLAGS_OUT_STALL;
|
||||
if (ep_id == XDBC_EPID_IN)
|
||||
if (ep_id == XDBC_EPID_IN || ep_id == XDBC_EPID_IN_INTEL)
|
||||
xdbc.flags |= XDBC_FLAGS_IN_STALL;
|
||||
|
||||
xdbc_trace("endpoint %d stalled\n", ep_id);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ep_id == XDBC_EPID_IN) {
|
||||
if (ep_id == XDBC_EPID_IN || ep_id == XDBC_EPID_IN_INTEL) {
|
||||
xdbc.flags &= ~XDBC_FLAGS_IN_PROCESS;
|
||||
xdbc_bulk_transfer(NULL, XDBC_MAX_PACKET, true);
|
||||
} else if (ep_id == XDBC_EPID_OUT) {
|
||||
} else if (ep_id == XDBC_EPID_OUT || ep_id == XDBC_EPID_OUT_INTEL) {
|
||||
xdbc.flags &= ~XDBC_FLAGS_OUT_PROCESS;
|
||||
} else {
|
||||
xdbc_trace("invalid endpoint id %d\n", ep_id);
|
||||
|
@ -120,8 +120,22 @@ struct xdbc_ring {
|
||||
u32 cycle_state;
|
||||
};
|
||||
|
||||
#define XDBC_EPID_OUT 2
|
||||
#define XDBC_EPID_IN 3
|
||||
/*
|
||||
* These are the "Endpoint ID" (also known as "Context Index") values for the
|
||||
* OUT Transfer Ring and the IN Transfer Ring of a Debug Capability Context data
|
||||
* structure.
|
||||
* According to the "eXtensible Host Controller Interface for Universal Serial
|
||||
* Bus (xHCI)" specification, section "7.6.3.2 Endpoint Contexts and Transfer
|
||||
* Rings", these should be 0 and 1, and those are the values AMD machines give
|
||||
* you; but Intel machines seem to use the formula from section "4.5.1 Device
|
||||
* Context Index", which is supposed to be used for the Device Context only.
|
||||
* Luckily the values from Intel don't overlap with those from AMD, so we can
|
||||
* just test for both.
|
||||
*/
|
||||
#define XDBC_EPID_OUT 0
|
||||
#define XDBC_EPID_IN 1
|
||||
#define XDBC_EPID_OUT_INTEL 2
|
||||
#define XDBC_EPID_IN_INTEL 3
|
||||
|
||||
struct xdbc_state {
|
||||
u16 vendor;
|
||||
|
@ -1813,6 +1813,10 @@ static void ffs_data_reset(struct ffs_data *ffs)
|
||||
ffs->state = FFS_READ_DESCRIPTORS;
|
||||
ffs->setup_state = FFS_NO_SETUP;
|
||||
ffs->flags = 0;
|
||||
|
||||
ffs->ms_os_descs_ext_prop_count = 0;
|
||||
ffs->ms_os_descs_ext_prop_name_len = 0;
|
||||
ffs->ms_os_descs_ext_prop_data_len = 0;
|
||||
}
|
||||
|
||||
|
||||
|
@ -81,6 +81,7 @@ static int raw_event_queue_add(struct raw_event_queue *queue,
|
||||
static struct usb_raw_event *raw_event_queue_fetch(
|
||||
struct raw_event_queue *queue)
|
||||
{
|
||||
int ret;
|
||||
unsigned long flags;
|
||||
struct usb_raw_event *event;
|
||||
|
||||
@ -89,11 +90,18 @@ static struct usb_raw_event *raw_event_queue_fetch(
|
||||
* there's at least one event queued by decrementing the semaphore,
|
||||
* and then take the lock to protect queue struct fields.
|
||||
*/
|
||||
if (down_interruptible(&queue->sema))
|
||||
return NULL;
|
||||
ret = down_interruptible(&queue->sema);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
spin_lock_irqsave(&queue->lock, flags);
|
||||
if (WARN_ON(!queue->size))
|
||||
return NULL;
|
||||
/*
|
||||
* queue->size must have the same value as queue->sema counter (before
|
||||
* the down_interruptible() call above), so this check is a fail-safe.
|
||||
*/
|
||||
if (WARN_ON(!queue->size)) {
|
||||
spin_unlock_irqrestore(&queue->lock, flags);
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
event = queue->events[0];
|
||||
queue->size--;
|
||||
memmove(&queue->events[0], &queue->events[1],
|
||||
@ -392,9 +400,8 @@ static int raw_ioctl_init(struct raw_dev *dev, unsigned long value)
|
||||
char *udc_device_name;
|
||||
unsigned long flags;
|
||||
|
||||
ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
|
||||
return -EFAULT;
|
||||
|
||||
switch (arg.speed) {
|
||||
case USB_SPEED_UNKNOWN:
|
||||
@ -501,15 +508,13 @@ out_unlock:
|
||||
|
||||
static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
|
||||
{
|
||||
int ret = 0;
|
||||
struct usb_raw_event arg;
|
||||
unsigned long flags;
|
||||
struct usb_raw_event *event;
|
||||
uint32_t length;
|
||||
|
||||
ret = copy_from_user(&arg, (void __user *)value, sizeof(arg));
|
||||
if (ret)
|
||||
return ret;
|
||||
if (copy_from_user(&arg, (void __user *)value, sizeof(arg)))
|
||||
return -EFAULT;
|
||||
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
if (dev->state != STATE_DEV_RUNNING) {
|
||||
@ -525,25 +530,31 @@ static int raw_ioctl_event_fetch(struct raw_dev *dev, unsigned long value)
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
|
||||
event = raw_event_queue_fetch(&dev->queue);
|
||||
if (!event) {
|
||||
if (PTR_ERR(event) == -EINTR) {
|
||||
dev_dbg(&dev->gadget->dev, "event fetching interrupted\n");
|
||||
return -EINTR;
|
||||
}
|
||||
if (IS_ERR(event)) {
|
||||
dev_err(&dev->gadget->dev, "failed to fetch event\n");
|
||||
spin_lock_irqsave(&dev->lock, flags);
|
||||
dev->state = STATE_DEV_FAILED;
|
||||
spin_unlock_irqrestore(&dev->lock, flags);
|
||||
return -ENODEV;
|
||||
}
|
||||
length = min(arg.length, event->length);
|
||||
ret = copy_to_user((void __user *)value, event,
|
||||
sizeof(*event) + length);
|
||||
return ret;
|
||||
if (copy_to_user((void __user *)value, event, sizeof(*event) + length))
|
||||
return -EFAULT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void *raw_alloc_io_data(struct usb_raw_ep_io *io, void __user *ptr,
|
||||
bool get_from_user)
|
||||
{
|
||||
int ret;
|
||||
void *data;
|
||||
|
||||
ret = copy_from_user(io, ptr, sizeof(*io));
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
if (copy_from_user(io, ptr, sizeof(*io)))
|
||||
return ERR_PTR(-EFAULT);
|
||||
if (io->ep >= USB_RAW_MAX_ENDPOINTS)
|
||||
return ERR_PTR(-EINVAL);
|
||||
if (!usb_raw_io_flags_valid(io->flags))
|
||||
@ -658,12 +669,13 @@ static int raw_ioctl_ep0_read(struct raw_dev *dev, unsigned long value)
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
ret = raw_process_ep0_io(dev, &io, data, false);
|
||||
if (ret < 0) {
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
length = min(io.length, (unsigned int)ret);
|
||||
ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
|
||||
if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
|
||||
ret = -EFAULT;
|
||||
free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
@ -952,12 +964,13 @@ static int raw_ioctl_ep_read(struct raw_dev *dev, unsigned long value)
|
||||
if (IS_ERR(data))
|
||||
return PTR_ERR(data);
|
||||
ret = raw_process_ep_io(dev, &io, data, false);
|
||||
if (ret < 0) {
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
goto free;
|
||||
|
||||
length = min(io.length, (unsigned int)ret);
|
||||
ret = copy_to_user((void __user *)(value + sizeof(io)), data, length);
|
||||
if (copy_to_user((void __user *)(value + sizeof(io)), data, length))
|
||||
ret = -EFAULT;
|
||||
free:
|
||||
kfree(data);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1951,10 +1951,10 @@ static irqreturn_t usba_vbus_irq_thread(int irq, void *devid)
|
||||
usba_start(udc);
|
||||
} else {
|
||||
udc->suspended = false;
|
||||
usba_stop(udc);
|
||||
|
||||
if (udc->driver->disconnect)
|
||||
udc->driver->disconnect(&udc->gadget);
|
||||
|
||||
usba_stop(udc);
|
||||
}
|
||||
udc->vbus_prev = vbus;
|
||||
}
|
||||
|
@ -540,7 +540,7 @@ static void bdc_req_complete(struct bdc_ep *ep, struct bdc_req *req,
|
||||
{
|
||||
struct bdc *bdc = ep->bdc;
|
||||
|
||||
if (req == NULL || &req->queue == NULL || &req->usb_req == NULL)
|
||||
if (req == NULL)
|
||||
return;
|
||||
|
||||
dev_dbg(bdc->dev, "%s ep:%s status:%d\n", __func__, ep->name, status);
|
||||
|
@ -1571,6 +1571,8 @@ int xhci_hub_status_data(struct usb_hcd *hcd, char *buf)
|
||||
}
|
||||
if ((temp & PORT_RC))
|
||||
reset_change = true;
|
||||
if (temp & PORT_OC)
|
||||
status = 1;
|
||||
}
|
||||
if (!status && !reset_change) {
|
||||
xhci_dbg(xhci, "%s: stopping port polling.\n", __func__);
|
||||
@ -1636,6 +1638,13 @@ retry:
|
||||
port_index);
|
||||
goto retry;
|
||||
}
|
||||
/* bail out if port detected a over-current condition */
|
||||
if (t1 & PORT_OC) {
|
||||
bus_state->bus_suspended = 0;
|
||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||
xhci_dbg(xhci, "Bus suspend bailout, port over-current detected\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
/* suspend ports in U0, or bail out for new connect changes */
|
||||
if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) {
|
||||
if ((t1 & PORT_CSC) && wake_enabled) {
|
||||
|
@ -547,6 +547,23 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
stream_id);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
* A cancelled TD can complete with a stall if HW cached the trb.
|
||||
* In this case driver can't find cur_td, but if the ring is empty we
|
||||
* can move the dequeue pointer to the current enqueue position.
|
||||
*/
|
||||
if (!cur_td) {
|
||||
if (list_empty(&ep_ring->td_list)) {
|
||||
state->new_deq_seg = ep_ring->enq_seg;
|
||||
state->new_deq_ptr = ep_ring->enqueue;
|
||||
state->new_cycle_state = ep_ring->cycle_state;
|
||||
goto done;
|
||||
} else {
|
||||
xhci_warn(xhci, "Can't find new dequeue state, missing cur_td\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* Dig out the cycle state saved by the xHC during the stop ep cmd */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Finding endpoint context");
|
||||
@ -592,6 +609,7 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
state->new_deq_seg = new_seg;
|
||||
state->new_deq_ptr = new_deq;
|
||||
|
||||
done:
|
||||
/* Don't update the ring cycle state for the producer (us). */
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
|
||||
"Cycle state = 0x%x", state->new_cycle_state);
|
||||
@ -1856,8 +1874,8 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
|
||||
|
||||
if (reset_type == EP_HARD_RESET) {
|
||||
ep->ep_state |= EP_HARD_CLEAR_TOGGLE;
|
||||
xhci_cleanup_stalled_ring(xhci, ep_index, stream_id, td);
|
||||
xhci_clear_hub_tt_buffer(xhci, td, ep);
|
||||
xhci_cleanup_stalled_ring(xhci, slot_id, ep_index, stream_id,
|
||||
td);
|
||||
}
|
||||
xhci_ring_cmd_db(xhci);
|
||||
}
|
||||
@ -1978,11 +1996,18 @@ static int finish_td(struct xhci_hcd *xhci, struct xhci_td *td,
|
||||
if (trb_comp_code == COMP_STALL_ERROR ||
|
||||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
|
||||
trb_comp_code)) {
|
||||
/* Issue a reset endpoint command to clear the host side
|
||||
* halt, followed by a set dequeue command to move the
|
||||
* dequeue pointer past the TD.
|
||||
* The class driver clears the device side halt later.
|
||||
/*
|
||||
* xhci internal endpoint state will go to a "halt" state for
|
||||
* any stall, including default control pipe protocol stall.
|
||||
* To clear the host side halt we need to issue a reset endpoint
|
||||
* command, followed by a set dequeue command to move past the
|
||||
* TD.
|
||||
* Class drivers clear the device side halt from a functional
|
||||
* stall later. Hub TT buffer should only be cleared for FS/LS
|
||||
* devices behind HS hubs for functional stalls.
|
||||
*/
|
||||
if ((ep_index != 0) || (trb_comp_code != COMP_STALL_ERROR))
|
||||
xhci_clear_hub_tt_buffer(xhci, td, ep);
|
||||
xhci_cleanup_halted_endpoint(xhci, slot_id, ep_index,
|
||||
ep_ring->stream_id, td, EP_HARD_RESET);
|
||||
} else {
|
||||
@ -2539,6 +2564,15 @@ static int handle_tx_event(struct xhci_hcd *xhci,
|
||||
xhci_dbg(xhci, "td_list is empty while skip flag set. Clear skip flag for slot %u ep %u.\n",
|
||||
slot_id, ep_index);
|
||||
}
|
||||
if (trb_comp_code == COMP_STALL_ERROR ||
|
||||
xhci_requires_manual_halt_cleanup(xhci, ep_ctx,
|
||||
trb_comp_code)) {
|
||||
xhci_cleanup_halted_endpoint(xhci, slot_id,
|
||||
ep_index,
|
||||
ep_ring->stream_id,
|
||||
NULL,
|
||||
EP_HARD_RESET);
|
||||
}
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
|
@ -3031,19 +3031,19 @@ static void xhci_setup_input_ctx_for_quirk(struct xhci_hcd *xhci,
|
||||
added_ctxs, added_ctxs);
|
||||
}
|
||||
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
|
||||
unsigned int stream_id, struct xhci_td *td)
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
unsigned int ep_index, unsigned int stream_id,
|
||||
struct xhci_td *td)
|
||||
{
|
||||
struct xhci_dequeue_state deq_state;
|
||||
struct usb_device *udev = td->urb->dev;
|
||||
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
|
||||
"Cleaning up stalled endpoint ring");
|
||||
/* We need to move the HW's dequeue pointer past this TD,
|
||||
* or it will attempt to resend it on the next doorbell ring.
|
||||
*/
|
||||
xhci_find_new_dequeue_state(xhci, udev->slot_id,
|
||||
ep_index, stream_id, td, &deq_state);
|
||||
xhci_find_new_dequeue_state(xhci, slot_id, ep_index, stream_id, td,
|
||||
&deq_state);
|
||||
|
||||
if (!deq_state.new_deq_ptr || !deq_state.new_deq_seg)
|
||||
return;
|
||||
@ -3054,7 +3054,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
|
||||
if (!(xhci->quirks & XHCI_RESET_EP_QUIRK)) {
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_reset_ep,
|
||||
"Queueing new dequeue state");
|
||||
xhci_queue_new_dequeue_state(xhci, udev->slot_id,
|
||||
xhci_queue_new_dequeue_state(xhci, slot_id,
|
||||
ep_index, &deq_state);
|
||||
} else {
|
||||
/* Better hope no one uses the input context between now and the
|
||||
@ -3065,7 +3065,7 @@ void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
|
||||
xhci_dbg_trace(xhci, trace_xhci_dbg_quirks,
|
||||
"Setting up input context for "
|
||||
"configure endpoint command");
|
||||
xhci_setup_input_ctx_for_quirk(xhci, udev->slot_id,
|
||||
xhci_setup_input_ctx_for_quirk(xhci, slot_id,
|
||||
ep_index, &deq_state);
|
||||
}
|
||||
}
|
||||
|
@ -2116,8 +2116,9 @@ void xhci_find_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
void xhci_queue_new_dequeue_state(struct xhci_hcd *xhci,
|
||||
unsigned int slot_id, unsigned int ep_index,
|
||||
struct xhci_dequeue_state *deq_state);
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int ep_index,
|
||||
unsigned int stream_id, struct xhci_td *td);
|
||||
void xhci_cleanup_stalled_ring(struct xhci_hcd *xhci, unsigned int slot_id,
|
||||
unsigned int ep_index, unsigned int stream_id,
|
||||
struct xhci_td *td);
|
||||
void xhci_stop_endpoint_command_watchdog(struct timer_list *t);
|
||||
void xhci_handle_command_timeout(struct work_struct *work);
|
||||
|
||||
|
@ -1199,18 +1199,18 @@ static int sisusb_read_mem_bulk(struct sisusb_usb_data *sisusb, u32 addr,
|
||||
/* High level: Gfx (indexed) register access */
|
||||
|
||||
#ifdef CONFIG_USB_SISUSBVGA_CON
|
||||
int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data)
|
||||
int sisusb_setreg(struct sisusb_usb_data *sisusb, u32 port, u8 data)
|
||||
{
|
||||
return sisusb_write_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
|
||||
}
|
||||
|
||||
int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data)
|
||||
int sisusb_getreg(struct sisusb_usb_data *sisusb, u32 port, u8 *data)
|
||||
{
|
||||
return sisusb_read_memio_byte(sisusb, SISUSB_TYPE_IO, port, data);
|
||||
}
|
||||
#endif
|
||||
|
||||
int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
int sisusb_setidxreg(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 data)
|
||||
{
|
||||
int ret;
|
||||
@ -1220,7 +1220,7 @@ int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
int sisusb_getidxreg(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 *data)
|
||||
{
|
||||
int ret;
|
||||
@ -1230,7 +1230,7 @@ int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
|
||||
int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, u32 port, u8 idx,
|
||||
u8 myand, u8 myor)
|
||||
{
|
||||
int ret;
|
||||
@ -1245,7 +1245,7 @@ int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx,
|
||||
}
|
||||
|
||||
static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb,
|
||||
int port, u8 idx, u8 data, u8 mask)
|
||||
u32 port, u8 idx, u8 data, u8 mask)
|
||||
{
|
||||
int ret;
|
||||
u8 tmp;
|
||||
@ -1258,13 +1258,13 @@ static int sisusb_setidxregmask(struct sisusb_usb_data *sisusb,
|
||||
return ret;
|
||||
}
|
||||
|
||||
int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
|
||||
int sisusb_setidxregor(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 myor)
|
||||
{
|
||||
return sisusb_setidxregandor(sisusb, port, index, 0xff, myor);
|
||||
}
|
||||
|
||||
int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
|
||||
int sisusb_setidxregand(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 idx, u8 myand)
|
||||
{
|
||||
return sisusb_setidxregandor(sisusb, port, idx, myand, 0x00);
|
||||
@ -2785,8 +2785,8 @@ static loff_t sisusb_lseek(struct file *file, loff_t offset, int orig)
|
||||
static int sisusb_handle_command(struct sisusb_usb_data *sisusb,
|
||||
struct sisusb_command *y, unsigned long arg)
|
||||
{
|
||||
int retval, port, length;
|
||||
u32 address;
|
||||
int retval, length;
|
||||
u32 port, address;
|
||||
|
||||
/* All our commands require the device
|
||||
* to be initialized.
|
||||
|
@ -812,17 +812,17 @@ static const struct SiS_VCLKData SiSUSB_VCLKData[] = {
|
||||
int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
|
||||
int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo);
|
||||
|
||||
extern int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data);
|
||||
extern int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 * data);
|
||||
extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
extern int sisusb_setreg(struct sisusb_usb_data *sisusb, u32 port, u8 data);
|
||||
extern int sisusb_getreg(struct sisusb_usb_data *sisusb, u32 port, u8 * data);
|
||||
extern int sisusb_setidxreg(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 data);
|
||||
extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port,
|
||||
extern int sisusb_getidxreg(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 * data);
|
||||
extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port,
|
||||
extern int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 idx, u8 myand, u8 myor);
|
||||
extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port,
|
||||
extern int sisusb_setidxregor(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 index, u8 myor);
|
||||
extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port,
|
||||
extern int sisusb_setidxregand(struct sisusb_usb_data *sisusb, u32 port,
|
||||
u8 idx, u8 myand);
|
||||
|
||||
void sisusb_delete(struct kref *kref);
|
||||
|
@ -81,6 +81,19 @@ static void uas_free_streams(struct uas_dev_info *devinfo);
|
||||
static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix,
|
||||
int status);
|
||||
|
||||
/*
|
||||
* This driver needs its own workqueue, as we need to control memory allocation.
|
||||
*
|
||||
* In the course of error handling and power management uas_wait_for_pending_cmnds()
|
||||
* needs to flush pending work items. In these contexts we cannot allocate memory
|
||||
* by doing block IO as we would deadlock. For the same reason we cannot wait
|
||||
* for anything allocating memory not heeding these constraints.
|
||||
*
|
||||
* So we have to control all work items that can be on the workqueue we flush.
|
||||
* Hence we cannot share a queue and need our own.
|
||||
*/
|
||||
static struct workqueue_struct *workqueue;
|
||||
|
||||
static void uas_do_work(struct work_struct *work)
|
||||
{
|
||||
struct uas_dev_info *devinfo =
|
||||
@ -109,7 +122,7 @@ static void uas_do_work(struct work_struct *work)
|
||||
if (!err)
|
||||
cmdinfo->state &= ~IS_IN_WORK_LIST;
|
||||
else
|
||||
schedule_work(&devinfo->work);
|
||||
queue_work(workqueue, &devinfo->work);
|
||||
}
|
||||
out:
|
||||
spin_unlock_irqrestore(&devinfo->lock, flags);
|
||||
@ -134,7 +147,7 @@ static void uas_add_work(struct uas_cmd_info *cmdinfo)
|
||||
|
||||
lockdep_assert_held(&devinfo->lock);
|
||||
cmdinfo->state |= IS_IN_WORK_LIST;
|
||||
schedule_work(&devinfo->work);
|
||||
queue_work(workqueue, &devinfo->work);
|
||||
}
|
||||
|
||||
static void uas_zap_pending(struct uas_dev_info *devinfo, int result)
|
||||
@ -190,6 +203,9 @@ static void uas_log_cmd_state(struct scsi_cmnd *cmnd, const char *prefix,
|
||||
struct uas_cmd_info *ci = (void *)&cmnd->SCp;
|
||||
struct uas_cmd_info *cmdinfo = (void *)&cmnd->SCp;
|
||||
|
||||
if (status == -ENODEV) /* too late */
|
||||
return;
|
||||
|
||||
scmd_printk(KERN_INFO, cmnd,
|
||||
"%s %d uas-tag %d inflight:%s%s%s%s%s%s%s%s%s%s%s%s ",
|
||||
prefix, status, cmdinfo->uas_tag,
|
||||
@ -1226,7 +1242,31 @@ static struct usb_driver uas_driver = {
|
||||
.id_table = uas_usb_ids,
|
||||
};
|
||||
|
||||
module_usb_driver(uas_driver);
|
||||
static int __init uas_init(void)
|
||||
{
|
||||
int rv;
|
||||
|
||||
workqueue = alloc_workqueue("uas", WQ_MEM_RECLAIM, 0);
|
||||
if (!workqueue)
|
||||
return -ENOMEM;
|
||||
|
||||
rv = usb_register(&uas_driver);
|
||||
if (rv) {
|
||||
destroy_workqueue(workqueue);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __exit uas_exit(void)
|
||||
{
|
||||
usb_deregister(&uas_driver);
|
||||
destroy_workqueue(workqueue);
|
||||
}
|
||||
|
||||
module_init(uas_init);
|
||||
module_exit(uas_exit);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_IMPORT_NS(USB_STORAGE);
|
||||
|
@ -2323,6 +2323,13 @@ UNUSUAL_DEV( 0x3340, 0xffff, 0x0000, 0x0000,
|
||||
USB_SC_DEVICE,USB_PR_DEVICE,NULL,
|
||||
US_FL_MAX_SECTORS_64 ),
|
||||
|
||||
/* Reported by Cyril Roelandt <tipecaml@gmail.com> */
|
||||
UNUSUAL_DEV( 0x357d, 0x7788, 0x0114, 0x0114,
|
||||
"JMicron",
|
||||
"USB to ATA/ATAPI Bridge",
|
||||
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
|
||||
US_FL_BROKEN_FUA ),
|
||||
|
||||
/* Reported by Andrey Rahmatullin <wrar@altlinux.org> */
|
||||
UNUSUAL_DEV( 0x4102, 0x1020, 0x0100, 0x0100,
|
||||
"iRiver",
|
||||
|
@ -198,7 +198,10 @@ EXPORT_SYMBOL_GPL(typec_altmode_vdm);
|
||||
const struct typec_altmode *
|
||||
typec_altmode_get_partner(struct typec_altmode *adev)
|
||||
{
|
||||
return adev ? &to_altmode(adev)->partner->adev : NULL;
|
||||
if (!adev || !to_altmode(adev)->partner)
|
||||
return NULL;
|
||||
|
||||
return &to_altmode(adev)->partner->adev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(typec_altmode_get_partner);
|
||||
|
||||
|
@ -114,8 +114,8 @@ pi3usb30532_mux_set(struct typec_mux *mux, struct typec_mux_state *state)
|
||||
static int pi3usb30532_probe(struct i2c_client *client)
|
||||
{
|
||||
struct device *dev = &client->dev;
|
||||
struct typec_switch_desc sw_desc;
|
||||
struct typec_mux_desc mux_desc;
|
||||
struct typec_switch_desc sw_desc = { };
|
||||
struct typec_mux_desc mux_desc = { };
|
||||
struct pi3usb30532 *pi;
|
||||
int ret;
|
||||
|
||||
|
@ -3794,6 +3794,14 @@ static void _tcpm_cc_change(struct tcpm_port *port, enum typec_cc_status cc1,
|
||||
*/
|
||||
break;
|
||||
|
||||
case PORT_RESET:
|
||||
case PORT_RESET_WAIT_OFF:
|
||||
/*
|
||||
* State set back to default mode once the timer completes.
|
||||
* Ignore CC changes here.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
if (tcpm_port_is_disconnected(port))
|
||||
tcpm_set_state(port, unattached_state(port), 0);
|
||||
@ -3855,6 +3863,15 @@ static void _tcpm_pd_vbus_on(struct tcpm_port *port)
|
||||
case SRC_TRY_DEBOUNCE:
|
||||
/* Do nothing, waiting for sink detection */
|
||||
break;
|
||||
|
||||
case PORT_RESET:
|
||||
case PORT_RESET_WAIT_OFF:
|
||||
/*
|
||||
* State set back to default mode once the timer completes.
|
||||
* Ignore vbus changes here.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -3908,10 +3925,19 @@ static void _tcpm_pd_vbus_off(struct tcpm_port *port)
|
||||
case PORT_RESET_WAIT_OFF:
|
||||
tcpm_set_state(port, tcpm_default_state(port), 0);
|
||||
break;
|
||||
|
||||
case SRC_TRY_WAIT:
|
||||
case SRC_TRY_DEBOUNCE:
|
||||
/* Do nothing, waiting for sink detection */
|
||||
break;
|
||||
|
||||
case PORT_RESET:
|
||||
/*
|
||||
* State set back to default mode once the timer completes.
|
||||
* Ignore vbus changes here.
|
||||
*/
|
||||
break;
|
||||
|
||||
default:
|
||||
if (port->pwr_role == TYPEC_SINK &&
|
||||
port->attached)
|
||||
|
Loading…
Reference in New Issue
Block a user