Files
omarchy-pkgs/pkgbuilds/linux-omarchy-eevdf/0607-usb-fixes.patch
T

1674 lines
55 KiB
Diff

diff --git a/drivers/usb/cdns3/cdns3-gadget.c b/drivers/usb/cdns3/cdns3-gadget.c
--- a/drivers/usb/cdns3/cdns3-gadget.c
+++ b/drivers/usb/cdns3/cdns3-gadget.c
@@ -3269,6 +3269,10 @@ static void cdns3_gadget_exit(struct cdns *cdns)
usb_del_gadget(&priv_dev->gadget);
devm_free_irq(cdns->dev, cdns->dev_irq, priv_dev);
+ /* The works can still be queued until the IRQ is freed. */
+ cancel_work_sync(&priv_dev->pending_status_wq);
+ cancel_work_sync(&priv_dev->aligned_buf_wq);
+
cdns3_free_all_eps(priv_dev);
while (!list_empty(&priv_dev->aligned_buf_list)) {
diff --git a/drivers/usb/cdns3/cdns3-pci-wrap.c b/drivers/usb/cdns3/cdns3-pci-wrap.c
--- a/drivers/usb/cdns3/cdns3-pci-wrap.c
+++ b/drivers/usb/cdns3/cdns3-pci-wrap.c
@@ -96,6 +96,11 @@ static int cdns3_pci_probe(struct pci_dev *pdev,
if (pci_is_enabled(func)) {
wrap = pci_get_drvdata(func);
+ if (!wrap) {
+ dev_err(&pdev->dev,
+ "second function not initialized, retrying\n");
+ return -EPROBE_DEFER;
+ }
} else {
wrap = kzalloc_obj(*wrap);
if (!wrap)
diff --git a/drivers/usb/chipidea/ci_hdrc_tegra.c b/drivers/usb/chipidea/ci_hdrc_tegra.c
--- a/drivers/usb/chipidea/ci_hdrc_tegra.c
+++ b/drivers/usb/chipidea/ci_hdrc_tegra.c
@@ -306,8 +306,10 @@ static int tegra_usb_probe(struct platform_device *pdev)
pm_runtime_enable(&pdev->dev);
err = pm_runtime_resume_and_get(&pdev->dev);
- if (err)
+ if (err) {
+ pm_runtime_disable(&pdev->dev);
return err;
+ }
if (device_property_present(&pdev->dev, "nvidia,needs-double-reset"))
usb->needs_double_reset = true;
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c
--- a/drivers/usb/class/cdc-acm.c
+++ b/drivers/usb/class/cdc-acm.c
@@ -331,7 +331,6 @@ static void acm_process_notification(struct acm *acm, unsigned char *buf)
spin_lock_irqsave(&acm->read_lock, flags);
acm->ctrlin = newctrl;
- acm->oldcount = acm->iocount;
if (difference & USB_CDC_SERIAL_STATE_DSR)
acm->iocount.dsr++;
@@ -788,6 +787,9 @@ static void acm_port_shutdown(struct tty_port *port)
usb_autopm_put_interface_async(acm->control);
}
+ if (acm->disconnected)
+ return;
+
acm_unpoison_urbs(acm);
if (acm->quirks & ALWAYS_POLL_CTRL) {
@@ -1027,11 +1029,16 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
DECLARE_WAITQUEUE(wait, current);
struct async_icount old, new;
- do {
+ spin_lock_irq(&acm->read_lock);
+ old = acm->iocount;
+ spin_unlock_irq(&acm->read_lock);
+
+ add_wait_queue(&acm->wioctl, &wait);
+ for (;;) {
+ set_current_state(TASK_INTERRUPTIBLE);
+
spin_lock_irq(&acm->read_lock);
- old = acm->oldcount;
new = acm->iocount;
- acm->oldcount = new;
spin_unlock_irq(&acm->read_lock);
if ((arg & TIOCM_DSR) &&
@@ -1044,22 +1051,20 @@ static int wait_serial_change(struct acm *acm, unsigned long arg)
old.rng != new.rng)
break;
- add_wait_queue(&acm->wioctl, &wait);
- set_current_state(TASK_INTERRUPTIBLE);
- schedule();
- remove_wait_queue(&acm->wioctl, &wait);
if (acm->disconnected) {
- if (arg & TIOCM_CD)
- break;
- else
- rv = -ENODEV;
- } else {
- if (signal_pending(current))
- rv = -ERESTARTSYS;
+ rv = -ENODEV;
+ break;
}
- } while (!rv);
-
+ schedule();
+
+ if (signal_pending(current)) {
+ rv = -ERESTARTSYS;
+ break;
+ }
+ }
+ __set_current_state(TASK_RUNNING);
+ remove_wait_queue(&acm->wioctl, &wait);
return rv;
}
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h
--- a/drivers/usb/class/cdc-acm.h
+++ b/drivers/usb/class/cdc-acm.h
@@ -90,7 +90,6 @@ struct acm {
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 */
- struct async_icount oldcount; /* for comparison of counter */
wait_queue_head_t wioctl; /* for ioctl */
unsigned int writesize; /* max packet size for the output bulk endpoint */
unsigned int readsize,ctrlsize; /* buffer sizes for freeing */
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -73,8 +73,11 @@
/*
* Give SS hubs 200ms time after wake to train downstream links before
* assuming no port activity and allowing hub to runtime suspend back.
+ * Root hubs have no upstream hub whose wake propagation needs to be
+ * accounted for, so they need less time, use 120ms for them.
*/
#define USB_SS_PORT_U0_WAKE_TIME 200 /* ms */
+#define USB_SS_RH_PORT_U0_WAKE_TIME 120 /* ms */
/* Protect struct usb_device->state and ->children members
* Note: Both are also protected by ->dev.sem, except that ->state can
@@ -1358,7 +1361,9 @@ static void hub_activate(struct usb_hub *hub, enum hub_activation_type type)
queue_delayed_work(system_power_efficient_wq,
&hub->post_resume_work,
- msecs_to_jiffies(USB_SS_PORT_U0_WAKE_TIME));
+ msecs_to_jiffies(hdev->parent ?
+ USB_SS_PORT_U0_WAKE_TIME :
+ USB_SS_RH_PORT_U0_WAKE_TIME));
return;
}
diff --git a/drivers/usb/core/message.c b/drivers/usb/core/message.c
--- a/drivers/usb/core/message.c
+++ b/drivers/usb/core/message.c
@@ -1337,7 +1337,8 @@ static void remove_intf_ep_devs(struct usb_interface *intf)
*
* Disables the endpoint for URB submission and nukes all pending URBs.
* If @reset_hardware is set then also deallocates hcd/hardware state
- * for the endpoint.
+ * for the endpoint (clearing both ep_in and ep_out pointers for
+ * bidirectional non-ep0 control endpoints).
*/
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
bool reset_hardware)
@@ -1358,6 +1359,11 @@ void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
dev->ep_in[epnum] = NULL;
}
if (ep) {
+ if (reset_hardware && epnum != 0 &&
+ usb_endpoint_xfer_control(&ep->desc)) {
+ dev->ep_out[epnum] = NULL;
+ dev->ep_in[epnum] = NULL;
+ }
ep->enabled = 0;
usb_hcd_flush_endpoint(dev, ep);
if (reset_hardware)
diff --git a/drivers/usb/dwc2/hcd.c b/drivers/usb/dwc2/hcd.c
--- a/drivers/usb/dwc2/hcd.c
+++ b/drivers/usb/dwc2/hcd.c
@@ -5082,14 +5082,13 @@ static void dwc2_hcd_free(struct dwc2_hsotg *hsotg)
}
cancel_work_sync(&hsotg->phy_reset_work);
-
- timer_delete(&hsotg->wkp_timer);
}
static void dwc2_hcd_release(struct dwc2_hsotg *hsotg)
{
/* Turn off all host-specific interrupts */
dwc2_disable_host_interrupts(hsotg);
+ timer_shutdown_sync(&hsotg->wkp_timer);
dwc2_hcd_free(hsotg);
}
diff --git a/drivers/usb/dwc3/core.h b/drivers/usb/dwc3/core.h
--- a/drivers/usb/dwc3/core.h
+++ b/drivers/usb/dwc3/core.h
@@ -49,6 +49,7 @@
#define DWC3_ENDPOINTS_NUM 32
#define DWC3_XHCI_RESOURCES_NUM 2
#define DWC3_ISOC_MAX_RETRIES 5
+#define DWC3_ERR_RECOVERY_MAX 3
#define DWC3_SCRATCHBUF_SIZE 4096 /* each buffer is assumed to be 4KiB */
#define DWC3_EVENT_BUFFERS_SIZE 4096
@@ -841,6 +842,12 @@ enum dwc3_link_state {
DWC3_LINK_STATE_MASK = 0x0f,
};
+enum dwc3_err_state {
+ DWC3_ERR_NONE = 0,
+ DWC3_ERR_RECOVERY,
+ DWC3_ERR_UNRECOVERABLE,
+};
+
/* TRB Length, PCM and Status */
#define DWC3_TRB_SIZE_MASK (0x00ffffff)
#define DWC3_TRB_SIZE_LENGTH(n) ((n) & DWC3_TRB_SIZE_MASK)
@@ -1004,6 +1011,7 @@ struct dwc3_glue_ops {
/**
* struct dwc3 - representation of our controller
* @drd_work: workqueue used for role swapping
+ * @err_recovery_work: workqueue used for controller error recovery
* @ep0_trb: trb which is used for the ctrl_req
* @bounce: address of bounce buffer
* @setup_buf: used while precessing STD USB requests
@@ -1013,6 +1021,7 @@ struct dwc3_glue_ops {
* @ep0_in_setup: one control transfer is completed and enter setup phase
* @lock: for synchronizing
* @mutex: for mode switching
+ * @connect_mutex: for the pull-up and err_recovery_work
* @dev: pointer to our struct device
* @sysdev: pointer to the DMA-capable device
* @xhci: pointer to our xHCI child
@@ -1079,6 +1088,9 @@ struct dwc3_glue_ops {
* @ep0_next_event: hold the next expected event
* @ep0state: state of endpoint zero
* @link_state: link state
+ * @err_state: current error recovery state.
+ * @err_recovery_count: number of consecutive error recovery attempts until
+ * confirmed healthy and reset to 0 on reset event.
* @speed: device speed (super, high, full, low)
* @hwparams: copy of hwparams registers
* @regset: debugfs pointer to regdump file
@@ -1189,6 +1201,7 @@ struct dwc3_glue_ops {
*/
struct dwc3 {
struct work_struct drd_work;
+ struct work_struct err_recovery_work;
struct dwc3_trb *ep0_trb;
void *bounce;
u8 *setup_buf;
@@ -1203,6 +1216,9 @@ struct dwc3 {
/* mode switching lock */
struct mutex mutex;
+ /* serializes pull-up run/stop vs error recovery */
+ struct mutex connect_mutex;
+
struct device *dev;
struct device *sysdev;
@@ -1332,6 +1348,9 @@ struct dwc3 {
enum dwc3_ep0_next ep0_next_event;
enum dwc3_ep0_state ep0state;
enum dwc3_link_state link_state;
+ enum dwc3_err_state err_state;
+
+ u32 err_recovery_count;
u16 u2sel;
u16 u2pel;
diff --git a/drivers/usb/dwc3/dwc3-am62.c b/drivers/usb/dwc3/dwc3-am62.c
--- a/drivers/usb/dwc3/dwc3-am62.c
+++ b/drivers/usb/dwc3/dwc3-am62.c
@@ -299,6 +299,7 @@ static int dwc3_ti_probe(struct platform_device *pdev)
err_pm_disable:
clk_disable_unprepare(am62->usb2_refclk);
+ pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
pm_runtime_set_suspended(dev);
return ret;
diff --git a/drivers/usb/dwc3/ep0.c b/drivers/usb/dwc3/ep0.c
--- a/drivers/usb/dwc3/ep0.c
+++ b/drivers/usb/dwc3/ep0.c
@@ -200,6 +200,11 @@ int dwc3_gadget_ep0_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ ret = -ESHUTDOWN;
+ goto out;
+ }
+
if (!dep->endpoint.desc || !dwc->pullups_connected || !dwc->connected) {
dev_err(dwc->dev, "%s: can't queue to disabled endpoint\n",
dep->name);
@@ -271,6 +276,10 @@ int dwc3_gadget_ep0_set_halt(struct usb_ep *ep, int value)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep0_set_halt(ep, value);
spin_unlock_irqrestore(&dwc->lock, flags);
diff --git a/drivers/usb/dwc3/gadget.c b/drivers/usb/dwc3/gadget.c
--- a/drivers/usb/dwc3/gadget.c
+++ b/drivers/usb/dwc3/gadget.c
@@ -1149,6 +1149,10 @@ static int dwc3_gadget_ep_enable(struct usb_ep *ep,
return 0;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_enable(dep, DWC3_DEPCFG_ACTION_INIT);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2060,6 +2064,10 @@ static int dwc3_gadget_ep_queue(struct usb_ep *ep, struct usb_request *request,
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_queue(dep, req);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2292,6 +2300,10 @@ static int dwc3_gadget_ep_set_halt(struct usb_ep *ep, int value)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
ret = __dwc3_gadget_ep_set_halt(dep, value, false);
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2306,6 +2318,10 @@ static int dwc3_gadget_ep_set_wedge(struct usb_ep *ep)
int ret;
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
dep->flags |= DWC3_EP_WEDGE;
if (dep->number == 0 || dep->number == 1)
@@ -2437,6 +2453,10 @@ static int dwc3_gadget_wakeup(struct usb_gadget *g)
}
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
if (!dwc->gadget->wakeup_armed) {
dev_err(dwc->dev, "not armed for remote wakeup\n");
spin_unlock_irqrestore(&dwc->lock, flags);
@@ -2464,6 +2484,10 @@ static int dwc3_gadget_func_wakeup(struct usb_gadget *g, int intf_id)
}
spin_lock_irqsave(&dwc->lock, flags);
+ if (dwc->err_state != DWC3_ERR_NONE) {
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return -ESHUTDOWN;
+ }
/*
* If the link is in U3, signal for remote wakeup and wait for the
* link to transition to U0 before sending device notification.
@@ -2833,10 +2857,13 @@ static int dwc3_gadget_pullup(struct usb_gadget *g, int is_on)
synchronize_irq(dwc->irq_gadget);
+ /* Serialize against dwc3_err_recovery_work() */
+ mutex_lock(&dwc->connect_mutex);
if (!is_on)
ret = dwc3_gadget_soft_disconnect(dwc);
else
ret = dwc3_gadget_soft_connect(dwc);
+ mutex_unlock(&dwc->connect_mutex);
pm_runtime_put(dwc->dev);
@@ -4153,6 +4180,10 @@ static void dwc3_gadget_reset_interrupt(struct dwc3 *dwc)
dwc->suspended = false;
+ /* The controller is recovered. */
+ if (dwc->err_state == DWC3_ERR_NONE)
+ dwc->err_recovery_count = 0;
+
/*
* Ideally, dwc3_reset_gadget() would trigger the function
* drivers to stop any active transfers through ep disable.
@@ -4640,6 +4671,12 @@ static irqreturn_t dwc3_thread_interrupt(int irq, void *_evt)
return ret;
}
+static void dwc3_schedule_err_recovery(struct dwc3 *dwc)
+{
+ dwc->err_state = DWC3_ERR_RECOVERY;
+ schedule_work(&dwc->err_recovery_work);
+}
+
static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
{
struct dwc3 *dwc = evt->dwc;
@@ -4673,9 +4710,21 @@ static irqreturn_t dwc3_check_event_buf(struct dwc3_event_buffer *evt)
return IRQ_NONE;
if (count > evt->length) {
- dev_err_ratelimited(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
+ dev_err(dwc->dev, "invalid count(%u) > evt->length(%u)\n",
count, evt->length);
- return IRQ_NONE;
+ /*
+ * This is a fatal error - the driver and controller are out of
+ * sync on which event has been consumed. Reinitializing the
+ * controller is required to recover. Write the bogus count back
+ * to GEVNTCOUNT to clear the IRQ source, consistent with the
+ * stale event clearing in dwc3_event_buffers_setup(), then
+ * schedule error recovery.
+ */
+ spin_lock(&dwc->lock);
+ dwc3_schedule_err_recovery(dwc);
+ spin_unlock(&dwc->lock);
+ dwc3_writel(dwc, DWC3_GEVNTCOUNT(0), count);
+ return IRQ_HANDLED;
}
evt->count = count;
@@ -4735,6 +4784,57 @@ static void dwc_gadget_release(struct device *dev)
kfree(gadget);
}
+static void dwc3_err_recovery_work(struct work_struct *work)
+{
+ struct dwc3 *dwc = container_of(work, struct dwc3, err_recovery_work);
+ unsigned long flags;
+ int ret;
+
+ /* serializes against dwc3_gadget_pullup() */
+ mutex_lock(&dwc->connect_mutex);
+
+ ret = dwc3_gadget_soft_disconnect(dwc);
+
+ dwc3_disconnect_gadget_sleepable(dwc);
+
+ if (ret)
+ goto err_unrecoverable;
+
+ if (dwc->softconnect) {
+ u32 count;
+ /*
+ * Wait irq to finish before soft_connect resets evt->lpos and
+ * the event buffer registers to avoid racing with
+ * dwc3_process_event_buf().
+ */
+ synchronize_irq(dwc->irq_gadget);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ count = ++dwc->err_recovery_count;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+
+ if (count > DWC3_ERR_RECOVERY_MAX)
+ goto err_unrecoverable;
+
+ ret = dwc3_gadget_soft_connect(dwc);
+ if (ret)
+ goto err_unrecoverable;
+ }
+ mutex_unlock(&dwc->connect_mutex);
+
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->err_state = DWC3_ERR_NONE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+ return;
+
+err_unrecoverable:
+ dev_err(dwc->dev, "Unable to recover the controller\n");
+ mutex_unlock(&dwc->connect_mutex);
+ spin_lock_irqsave(&dwc->lock, flags);
+ dwc->err_state = DWC3_ERR_UNRECOVERABLE;
+ spin_unlock_irqrestore(&dwc->lock, flags);
+}
+
/**
* dwc3_gadget_init - initializes gadget related registers
* @dwc: pointer to our controller context structure
@@ -4778,6 +4878,8 @@ int dwc3_gadget_init(struct dwc3 *dwc)
}
init_completion(&dwc->ep0_in_setup);
+ INIT_WORK(&dwc->err_recovery_work, dwc3_err_recovery_work);
+ mutex_init(&dwc->connect_mutex);
dwc->gadget = kzalloc_obj(struct usb_gadget);
if (!dwc->gadget) {
ret = -ENOMEM;
@@ -4874,6 +4976,8 @@ void dwc3_gadget_exit(struct dwc3 *dwc)
if (!dwc->gadget)
return;
+ cancel_work_sync(&dwc->err_recovery_work);
+ mutex_destroy(&dwc->connect_mutex);
dwc3_enable_susphy(dwc, true);
usb_del_gadget(dwc->gadget);
dwc3_gadget_free_endpoints(dwc);
diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c
--- a/drivers/usb/gadget/function/f_fs.c
+++ b/drivers/usb/gadget/function/f_fs.c
@@ -1877,8 +1877,9 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
break;
case FUNCTIONFS_ENDPOINT_DESC:
{
+ const struct usb_endpoint_descriptor *desc;
+ struct usb_endpoint_descriptor desc1;
int desc_idx;
- struct usb_endpoint_descriptor desc1, *desc;
switch (epfile->ffs->gadget->speed) {
case USB_SPEED_SUPER:
@@ -1892,7 +1893,17 @@ static long ffs_epfile_ioctl(struct file *file, unsigned code,
desc_idx = 0;
}
- desc = epfile->ep->descs[desc_idx];
+ do {
+ desc = epfile->ep->descs[desc_idx];
+ } while (!desc && --desc_idx >= 0);
+
+ if (!desc)
+ desc = epfile->ep->ep->desc;
+ if (!desc) {
+ ret = -EINVAL;
+ break;
+ }
+
memcpy(&desc1, desc, desc->bLength);
spin_unlock_irq(&epfile->ffs->eps_lock);
diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c
--- a/drivers/usb/gadget/function/f_midi2.c
+++ b/drivers/usb/gadget/function/f_midi2.c
@@ -1633,8 +1633,8 @@ struct f_midi2_usb_config {
/* MIDI 1.0 jacks */
unsigned char jack_in, jack_out, jack_id;
- struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES];
- struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES];
+ struct usb_midi_in_jack_descriptor jack_ins[MAX_CABLES * 2];
+ struct usb_midi_out_jack_descriptor_1 jack_outs[MAX_CABLES * 2];
};
static int append_config(struct f_midi2_usb_config *config, void *d)
diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c
--- a/drivers/usb/gadget/function/f_uac1_legacy.c
+++ b/drivers/usb/gadget/function/f_uac1_legacy.c
@@ -797,6 +797,9 @@ f_audio_bind(struct usb_configuration *c, struct usb_function *f)
static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
{
+ if (cmd >= ARRAY_SIZE(con->data))
+ return -EINVAL;
+
con->data[cmd] = value;
return 0;
@@ -804,6 +807,9 @@ static int generic_set_cmd(struct usb_audio_control *con, u8 cmd, int value)
static int generic_get_cmd(struct usb_audio_control *con, u8 cmd)
{
+ if (cmd >= ARRAY_SIZE(con->data))
+ return -EINVAL;
+
return con->data[cmd];
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/core.c b/drivers/usb/gadget/udc/aspeed-vhub/core.c
--- a/drivers/usb/gadget/udc/aspeed-vhub/core.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/core.c
@@ -267,6 +267,9 @@ static void ast_vhub_remove(struct platform_device *pdev)
for (i = 0; i < vhub->max_ports; i++)
ast_vhub_del_dev(&vhub->ports[i].dev);
+ /* Final drain; the worker takes vhub->lock, so stay outside of it */
+ cancel_work_sync(&vhub->wake_work);
+
spin_lock_irqsave(&vhub->lock, flags);
/* Mask & ack all interrupts */
@@ -328,6 +331,7 @@ static int ast_vhub_probe(struct platform_device *pdev)
return -ENOMEM;
spin_lock_init(&vhub->lock);
+ INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
vhub->pdev = pdev;
vhub->port_irq_mask = GENMASK(VHUB_IRQ_DEV1_BIT + vhub->max_ports - 1,
VHUB_IRQ_DEV1_BIT);
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/dev.c b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
--- a/drivers/usb/gadget/udc/aspeed-vhub/dev.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/dev.c
@@ -280,7 +280,7 @@ static int ast_vhub_udc_wakeup(struct usb_gadget* gadget)
int rc = -EINVAL;
spin_lock_irqsave(&d->vhub->lock, flags);
- if (!d->wakeup_en)
+ if (!d->wakeup_en || !d->registered)
goto err;
DDBG(d, "Device initiated wakeup\n");
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/hub.c b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
--- a/drivers/usb/gadget/udc/aspeed-vhub/hub.c
+++ b/drivers/usb/gadget/udc/aspeed-vhub/hub.c
@@ -558,7 +558,7 @@ void ast_vhub_device_connect(struct ast_vhub *vhub,
ast_vhub_send_host_wakeup(vhub);
}
-static void ast_vhub_wake_work(struct work_struct *work)
+void ast_vhub_wake_work(struct work_struct *work)
{
struct ast_vhub *vhub = container_of(work,
struct ast_vhub,
@@ -588,6 +588,8 @@ static void ast_vhub_wake_work(struct work_struct *work)
void ast_vhub_hub_wake_all(struct ast_vhub *vhub)
{
+ lockdep_assert_held(&vhub->lock);
+
/*
* A device is trying to wake the world, because this
* can recurse into the device, we break the call chain
@@ -1076,7 +1078,6 @@ static int ast_vhub_init_desc(struct ast_vhub *vhub)
int ast_vhub_init_hub(struct ast_vhub *vhub)
{
vhub->speed = USB_SPEED_UNKNOWN;
- INIT_WORK(&vhub->wake_work, ast_vhub_wake_work);
return ast_vhub_init_desc(vhub);
}
diff --git a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
--- a/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
+++ b/drivers/usb/gadget/udc/aspeed-vhub/vhub.h
@@ -547,6 +547,7 @@ void ast_vhub_hub_suspend(struct ast_vhub *vhub);
void ast_vhub_hub_resume(struct ast_vhub *vhub);
void ast_vhub_hub_reset(struct ast_vhub *vhub);
void ast_vhub_hub_wake_all(struct ast_vhub *vhub);
+void ast_vhub_wake_work(struct work_struct *work);
/* dev.c */
int ast_vhub_init_dev(struct ast_vhub *vhub, unsigned int idx);
diff --git a/drivers/usb/gadget/udc/dummy_hcd.c b/drivers/usb/gadget/udc/dummy_hcd.c
--- a/drivers/usb/gadget/udc/dummy_hcd.c
+++ b/drivers/usb/gadget/udc/dummy_hcd.c
@@ -343,9 +343,11 @@ static void dummy_giveback(struct dummy *dum, struct usb_ep *_ep,
{
bool fifo = req == &dum->fifo_req;
+ ++dum->callback_usage;
spin_unlock(&dum->lock);
usb_gadget_giveback_request(_ep, &req->req);
spin_lock(&dum->lock);
+ --dum->callback_usage;
if (fifo)
dum->fifo_req_busy = 0;
}
@@ -759,11 +761,13 @@ static int dummy_queue(struct usb_ep *_ep, struct usb_request *_req,
req->req.complete = fifo_complete;
list_add_tail(&req->queue, &ep->queue);
+ ++dum->callback_usage;
spin_unlock(&dum->lock);
_req->actual = _req->length;
_req->status = 0;
usb_gadget_giveback_request(_ep, _req);
spin_lock(&dum->lock);
+ --dum->callback_usage;
} else
list_add_tail(&req->queue, &ep->queue);
spin_unlock_irqrestore(&dum->lock, flags);
diff --git a/drivers/usb/host/octeon-hcd.c b/drivers/usb/host/octeon-hcd.c
--- a/drivers/usb/host/octeon-hcd.c
+++ b/drivers/usb/host/octeon-hcd.c
@@ -378,8 +378,22 @@ struct octeon_hcd {
struct cvmx_usb_transaction *active_split;
struct cvmx_usb_tx_fifo periodic;
struct cvmx_usb_tx_fifo nonperiodic;
+ struct hrtimer sof_timer;
};
+static int cvmx_usb_poll(struct octeon_hcd *usb);
+
+static enum hrtimer_restart octeon_usb_sof_timer(struct hrtimer *t)
+{
+ struct octeon_hcd *usb = container_of(t, struct octeon_hcd, sof_timer);
+ unsigned long flags;
+
+ spin_lock_irqsave(&usb->lock, flags);
+ cvmx_usb_poll(usb);
+ spin_unlock_irqrestore(&usb->lock, flags);
+ return HRTIMER_NORESTART;
+}
+
/*
* This macro logically sets a single field in a CSR. It does the sequence
* read, modify, and write
@@ -578,7 +592,7 @@ static int cvmx_wait_tx_rx(struct octeon_hcd *usb, int fflsh_type)
int result;
u64 address = CVMX_USBCX_GRSTCTL(usb->index);
u64 done = cvmx_get_cycle() + 100 *
- (u64)octeon_get_clock_rate / 1000000;
+ (u64)octeon_get_clock_rate() / 1000000;
union cvmx_usbcx_grstctl c;
while (1) {
@@ -853,6 +867,14 @@ static int cvmx_usb_initialize(struct device *dev,
* USBC_GAHBCFG[PTXFEMPLVL]
* Global interrupt mask, USBC_GAHBCFG[GLBLINTRMSK] = 1
*/
+ usbcx_gahbcfg.u32 = cvmx_usb_read_csr32(usb,
+ CVMX_USBCX_GHWCFG3(usb->index));
+ if (usbcx_gahbcfg.u32 == 0xffffffff || usbcx_gahbcfg.u32 == 0) {
+ dev_err(dev, "USB core is not responding (GHWCFG3=0x%08x)\n",
+ usbcx_gahbcfg.u32);
+ return -ENODEV;
+ }
+
usbcx_gahbcfg.u32 = 0;
usbcx_gahbcfg.s.dmaen = !(usb->init_flags &
CVMX_USB_INITIALIZE_FLAGS_NO_DMA);
@@ -1900,6 +1922,7 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
int channel;
struct cvmx_usb_pipe *pipe;
int need_sof;
+ u64 min_due;
enum cvmx_usb_transfer ttype;
if (usb->init_flags & CVMX_USB_INITIALIZE_FLAGS_NO_DMA) {
@@ -1940,15 +1963,33 @@ static void cvmx_usb_schedule(struct octeon_hcd *usb, int is_sof)
* future that might need to be scheduled
*/
need_sof = 0;
+ min_due = ~0ull;
for (ttype = CVMX_USB_TRANSFER_CONTROL;
ttype <= CVMX_USB_TRANSFER_INTERRUPT; ttype++) {
list_for_each_entry(pipe, &usb->active_pipes[ttype], node) {
- if (pipe->next_tx_frame > usb->frame_number) {
- need_sof = 1;
- break;
- }
+ if (pipe->next_tx_frame > usb->frame_number &&
+ pipe->next_tx_frame < min_due)
+ min_due = pipe->next_tx_frame;
}
}
+ if (min_due != ~0ull) {
+ u64 delta = min_due - usb->frame_number;
+
+ /*
+ * frame_number is resynced from HFNUM on every poll, so a
+ * deadline that is many frames away does not need an
+ * interrupt on every SOF to count them down - sleep on the
+ * timer instead and keep SOF interrupts for deadlines within
+ * a few frames. Stay well below the 16383-frame wrap of
+ * HFNUM. One (micro)frame is 125us in high-speed mode.
+ */
+ if (delta <= 4 || delta > 8000)
+ need_sof = 1;
+ else
+ hrtimer_start(&usb->sof_timer,
+ ns_to_ktime((delta - 2) * 125000),
+ HRTIMER_MODE_REL);
+ }
USB_SET_FIELD32(CVMX_USBCX_GINTMSK(usb->index),
cvmx_usbcx_gintmsk, sofmsk, need_sof);
}
@@ -3638,6 +3679,8 @@ static int octeon_usb_probe(struct platform_device *pdev)
usb = (struct octeon_hcd *)hcd->hcd_priv;
spin_lock_init(&usb->lock);
+ hrtimer_setup(&usb->sof_timer, octeon_usb_sof_timer, CLOCK_MONOTONIC,
+ HRTIMER_MODE_REL);
usb->init_flags = initialize_flags;
@@ -3688,6 +3731,7 @@ static void octeon_usb_remove(struct platform_device *pdev)
unsigned long flags;
usb_remove_hcd(hcd);
+ hrtimer_cancel(&usb->sof_timer);
spin_lock_irqsave(&usb->lock, flags);
status = cvmx_usb_shutdown(usb);
spin_unlock_irqrestore(&usb->lock, flags);
diff --git a/drivers/usb/host/ohci-da8xx.c b/drivers/usb/host/ohci-da8xx.c
--- a/drivers/usb/host/ohci-da8xx.c
+++ b/drivers/usb/host/ohci-da8xx.c
@@ -447,6 +447,7 @@ static int ohci_da8xx_probe(struct platform_device *pdev)
err_remove_hcd:
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
err:
usb_put_hcd(hcd);
return error;
@@ -457,6 +458,7 @@ static void ohci_da8xx_remove(struct platform_device *pdev)
struct usb_hcd *hcd = platform_get_drvdata(pdev);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
usb_put_hcd(hcd);
}
diff --git a/drivers/usb/host/ohci-s3c2410.c b/drivers/usb/host/ohci-s3c2410.c
--- a/drivers/usb/host/ohci-s3c2410.c
+++ b/drivers/usb/host/ohci-s3c2410.c
@@ -335,6 +335,7 @@ ohci_hcd_s3c2410_remove(struct platform_device *dev)
struct usb_hcd *hcd = platform_get_drvdata(dev);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
s3c2410_stop_hc(dev);
usb_put_hcd(hcd);
}
diff --git a/drivers/usb/host/ohci-spear.c b/drivers/usb/host/ohci-spear.c
--- a/drivers/usb/host/ohci-spear.c
+++ b/drivers/usb/host/ohci-spear.c
@@ -103,6 +103,7 @@ static void spear_ohci_hcd_drv_remove(struct platform_device *pdev)
struct spear_ohci *sohci_p = to_spear_ohci(hcd);
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
clk_disable_unprepare(sohci_p->clk);
usb_put_hcd(hcd);
diff --git a/drivers/usb/host/ohci-st.c b/drivers/usb/host/ohci-st.c
--- a/drivers/usb/host/ohci-st.c
+++ b/drivers/usb/host/ohci-st.c
@@ -235,6 +235,7 @@ static void st_ohci_platform_remove(struct platform_device *dev)
int clk;
usb_remove_hcd(hcd);
+ device_wakeup_disable(hcd->self.controller);
if (pdata->power_off)
pdata->power_off(dev);
diff --git a/drivers/usb/serial/bus.c b/drivers/usb/serial/bus.c
--- a/drivers/usb/serial/bus.c
+++ b/drivers/usb/serial/bus.c
@@ -165,7 +165,11 @@ int usb_serial_bus_register(struct usb_serial_driver *driver)
void usb_serial_bus_deregister(struct usb_serial_driver *driver)
{
- free_dynids(driver);
driver_unregister(&driver->driver);
+ free_dynids(driver);
}
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver)
+{
+ driver_remove_file(&driver->driver, &driver_attr_new_id);
+}
diff --git a/drivers/usb/serial/cp210x.c b/drivers/usb/serial/cp210x.c
--- a/drivers/usb/serial/cp210x.c
+++ b/drivers/usb/serial/cp210x.c
@@ -223,6 +223,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x19CF, 0x3000) }, /* Parrot NMEA GPS Flight Recorder */
{ USB_DEVICE(0x1ADB, 0x0001) }, /* Schweitzer Engineering C662 Cable */
{ USB_DEVICE(0x1B1C, 0x1C00) }, /* Corsair USB Dongle */
+ { USB_DEVICE(0x1B1C, 0x1C02) }, /* Corsair AX1500i Power Supply */
{ USB_DEVICE(0x1B93, 0x1013) }, /* Phoenix Contact UPS Device */
{ USB_DEVICE(0x1BA4, 0x0002) }, /* Silicon Labs 358x factory default */
{ USB_DEVICE(0x1BE3, 0x07A6) }, /* WAGO 750-923 USB Service Cable */
diff --git a/drivers/usb/serial/generic.c b/drivers/usb/serial/generic.c
--- a/drivers/usb/serial/generic.c
+++ b/drivers/usb/serial/generic.c
@@ -266,6 +266,7 @@ EXPORT_SYMBOL_GPL(usb_serial_generic_chars_in_buffer);
void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
unsigned int bps;
unsigned long period;
unsigned long expire;
@@ -285,7 +286,14 @@ void usb_serial_generic_wait_until_sent(struct tty_struct *tty, long timeout)
__func__, jiffies_to_msecs(timeout),
jiffies_to_msecs(period));
expire = jiffies + timeout;
- while (!port->serial->type->tx_empty(port)) {
+ for (;;) {
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty) || port->serial->type->tx_empty(port)) {
+ mutex_unlock(&tport->mutex);
+ break;
+ }
+ mutex_unlock(&tport->mutex);
+
schedule_timeout_interruptible(period);
if (signal_pending(current))
break;
diff --git a/drivers/usb/serial/option.c b/drivers/usb/serial/option.c
--- a/drivers/usb/serial/option.c
+++ b/drivers/usb/serial/option.c
@@ -260,6 +260,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EM060K_12a 0x012a
#define QUECTEL_PRODUCT_EM060K_12b 0x012b
#define QUECTEL_PRODUCT_EM060K_12c 0x012c
+#define QUECTEL_PRODUCT_RG660QB 0x013d
#define QUECTEL_PRODUCT_EG91 0x0191
#define QUECTEL_PRODUCT_EG95 0x0195
#define QUECTEL_PRODUCT_BG96 0x0296
@@ -280,6 +281,7 @@ static void option_instat_callback(struct urb *urb);
#define QUECTEL_PRODUCT_EC200U 0x0901
#define QUECTEL_PRODUCT_EG912Y 0x6001
#define QUECTEL_PRODUCT_EC200S_CN 0x6002
+#define QUECTEL_PRODUCT_EG060W 0x6004
#define QUECTEL_PRODUCT_EC200A 0x6005
#define QUECTEL_PRODUCT_EG916Q 0x6007
#define QUECTEL_PRODUCT_EM061K_LWW 0x6008
@@ -1268,12 +1270,15 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200A, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200U, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200S_CN, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG060W, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EC200T, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG912Y, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_EG916Q, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RM500K, 0xff, 0x00, 0x00) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG650V, 0xff, 0, 0) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0xff, 0x30) },
+ { USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG660QB, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x30) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0, 0) },
{ USB_DEVICE_AND_INTERFACE_INFO(QUECTEL_VENDOR_ID, QUECTEL_PRODUCT_RG255C, 0xff, 0xff, 0x40) },
@@ -2168,6 +2173,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9003, 0xff) }, /* Simcom SIM7500/SIM7600 MBIM mode */
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9011, 0xff), /* Simcom SIM7500/SIM7600 RNDIS mode */
.driver_info = RSVD(7) },
+ { USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x902b, 0xff),
+ .driver_info = RSVD(4) },
{ USB_DEVICE(0x1e0e, 0x9071), /* Simcom SIM8230 RMNET mode */
.driver_info = RSVD(3) | RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x1e0e, 0x9078, 0xff), /* Simcom SIM8230 ECM mode */
@@ -2429,6 +2436,13 @@ static const struct usb_device_id option_ids[] = {
.driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x0489, 0xe167, 0xff), /* Foxconn T99W640 MBIM */
.driver_info = RSVD(3) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x01) }, /* Compal EXC-T1 */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x02) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x03) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x4d22, 0xff, 0x10, 0x04) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x30) }, /* Compal EXM-G1x */
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x40) },
+ { USB_DEVICE_AND_INTERFACE_INFO(0x04b7, 0x8217, 0xff, 0xff, 0x60) },
{ USB_DEVICE(0x1508, 0x1001), /* Fibocom NL668 (IOT version) */
.driver_info = RSVD(4) | RSVD(5) | RSVD(6) },
{ USB_DEVICE(0x1782, 0x4d10) }, /* Fibocom L610 (AT mode) */
diff --git a/drivers/usb/serial/quatech2.c b/drivers/usb/serial/quatech2.c
--- a/drivers/usb/serial/quatech2.c
+++ b/drivers/usb/serial/quatech2.c
@@ -52,6 +52,7 @@
#define QT2_XMIT_FLUSH 0x05 /* no following info */
#define QT2_CONTROL_ESCAPE 0xff /* pass through previous 2 control bytes */
+#define MIN_BAUD_RATE 50
#define MAX_BAUD_RATE 921600
#define DEFAULT_BAUD_RATE 9600
@@ -156,7 +157,7 @@ static inline int calc_baud_divisor(int baudrate)
static inline int qt2_set_port_config(struct usb_device *dev,
unsigned char port_number,
- u16 baudrate, u16 lcr)
+ speed_t baudrate, u16 lcr)
{
int divisor = calc_baud_divisor(baudrate);
u16 index = ((u16) (lcr << 8) | (u16) (port_number));
@@ -257,9 +258,9 @@ static void qt2_set_termios(struct tty_struct *tty,
struct usb_device *dev = port->serial->dev;
struct qt2_port_private *port_priv;
struct ktermios *termios = &tty->termios;
- u16 baud;
unsigned int cflag = termios->c_cflag;
u16 new_lcr = 0;
+ speed_t baud;
int status;
port_priv = usb_get_serial_port_data(port);
@@ -277,6 +278,18 @@ static void qt2_set_termios(struct tty_struct *tty,
if (!baud)
baud = 9600;
+ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+ tty_encode_baud_rate(tty, baud, baud);
+
+ if (!baud)
+ baud = 9600;
+ }
+
status = qt2_set_port_config(dev, port_priv->device_port, baud,
new_lcr);
if (status < 0)
@@ -373,7 +386,7 @@ static int qt2_open(struct tty_struct *tty, struct usb_serial_port *port)
port_priv->device_port = (u8) device_port;
if (tty)
- qt2_set_termios(tty, port, &tty->termios);
+ qt2_set_termios(tty, port, NULL);
return 0;
diff --git a/drivers/usb/serial/ssu100.c b/drivers/usb/serial/ssu100.c
--- a/drivers/usb/serial/ssu100.c
+++ b/drivers/usb/serial/ssu100.c
@@ -32,6 +32,7 @@
#define SERIAL_EVEN_PARITY (UART_LCR_PARITY | UART_LCR_EPAR)
+#define MIN_BAUD_RATE 50
#define MAX_BAUD_RATE 460800
#define ATC_DISABLED 0x00
@@ -217,9 +218,10 @@ static void ssu100_set_termios(struct tty_struct *tty,
{
struct usb_device *dev = port->serial->dev;
struct ktermios *termios = &tty->termios;
- u16 baud, divisor, remainder;
+ speed_t baud, remainder;
unsigned int cflag = termios->c_cflag;
u16 urb_value = 0; /* will hold the new flags */
+ u16 divisor;
int result;
if (cflag & PARENB) {
@@ -235,6 +237,18 @@ static void ssu100_set_termios(struct tty_struct *tty,
if (!baud)
baud = 9600;
+ if (baud < MIN_BAUD_RATE || baud > MAX_BAUD_RATE) {
+ if (old_termios)
+ baud = tty_termios_baud_rate(old_termios);
+ else
+ baud = clamp(baud, MIN_BAUD_RATE, MAX_BAUD_RATE);
+
+ tty_encode_baud_rate(tty, baud, baud);
+
+ if (!baud)
+ baud = 9600;
+ }
+
dev_dbg(&port->dev, "%s - got baud = %d\n", __func__, baud);
@@ -310,7 +324,7 @@ static int ssu100_open(struct tty_struct *tty, struct usb_serial_port *port)
dev_dbg(&port->dev, "%s - set uart failed\n", __func__);
if (tty)
- ssu100_set_termios(tty, port, &tty->termios);
+ ssu100_set_termios(tty, port, NULL);
return usb_serial_generic_open(tty, port);
}
diff --git a/drivers/usb/serial/usb-serial.c b/drivers/usb/serial/usb-serial.c
--- a/drivers/usb/serial/usb-serial.c
+++ b/drivers/usb/serial/usb-serial.c
@@ -400,17 +400,13 @@ static unsigned int serial_chars_in_buffer(struct tty_struct *tty)
static void serial_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct usb_serial_port *port = tty->driver_data;
- struct usb_serial *serial = port->serial;
dev_dbg(&port->dev, "%s\n", __func__);
if (!port->serial->type->wait_until_sent)
return;
- mutex_lock(&serial->disc_mutex);
- if (!serial->disconnected)
- port->serial->type->wait_until_sent(tty, timeout);
- mutex_unlock(&serial->disc_mutex);
+ port->serial->type->wait_until_sent(tty, timeout);
}
static void serial_throttle(struct tty_struct *tty)
@@ -438,8 +434,13 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
struct usb_serial_port *port = tty->driver_data;
struct tty_port *tport = &port->port;
unsigned int close_delay, closing_wait;
+ int ret = 0;
mutex_lock(&tport->mutex);
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
close_delay = jiffies_to_msecs(tport->close_delay) / 10;
closing_wait = tport->closing_wait;
@@ -452,10 +453,10 @@ static int serial_get_serial(struct tty_struct *tty, struct serial_struct *ss)
if (port->serial->type->get_serial)
port->serial->type->get_serial(tty, ss);
-
+out_unlock:
mutex_unlock(&tport->mutex);
- return 0;
+ return ret;
}
static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
@@ -471,6 +472,10 @@ static int serial_set_serial(struct tty_struct *tty, struct serial_struct *ss)
closing_wait = msecs_to_jiffies(closing_wait * 10);
mutex_lock(&tport->mutex);
+ if (tty_io_error(tty)) {
+ ret = -EIO;
+ goto out_unlock;
+ }
if (!capable(CAP_SYS_ADMIN)) {
if (close_delay != tport->close_delay ||
@@ -498,6 +503,7 @@ static int serial_ioctl(struct tty_struct *tty,
unsigned int cmd, unsigned long arg)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
int retval = -ENOIOCTLCMD;
dev_dbg(&port->dev, "%s - cmd 0x%04x\n", __func__, cmd);
@@ -508,8 +514,21 @@ static int serial_ioctl(struct tty_struct *tty,
retval = port->serial->type->tiocmiwait(tty, arg);
break;
default:
- if (port->serial->type->ioctl)
+ if (!port->serial->type->ioctl)
+ break;
+
+ if (cmd == TIOCSRS485)
+ down_write(&tty->termios_rwsem);
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ retval = -EIO;
+ else
retval = port->serial->type->ioctl(tty, cmd, arg);
+ mutex_unlock(&tport->mutex);
+
+ if (cmd == TIOCSRS485)
+ up_write(&tty->termios_rwsem);
}
return retval;
@@ -519,25 +538,40 @@ static void serial_set_termios(struct tty_struct *tty,
const struct ktermios *old)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->set_termios)
- port->serial->type->set_termios(tty, port, old);
- else
+ if (!port->serial->type->set_termios) {
tty_termios_copy_hw(&tty->termios, old);
+ return;
+ }
+
+ mutex_lock(&tport->mutex);
+ if (!tty_io_error(tty))
+ port->serial->type->set_termios(tty, port, old);
+ mutex_unlock(&tport->mutex);
}
static int serial_break(struct tty_struct *tty, int break_state)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->break_ctl)
- return port->serial->type->break_ctl(tty, break_state);
+ if (!port->serial->type->break_ctl)
+ return -ENOTTY;
- return -ENOTTY;
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->break_ctl(tty, break_state);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_proc_show(struct seq_file *m, void *v)
@@ -578,24 +612,44 @@ static int serial_proc_show(struct seq_file *m, void *v)
static int serial_tiocmget(struct tty_struct *tty)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->tiocmget)
- return port->serial->type->tiocmget(tty);
- return -ENOTTY;
+ if (!port->serial->type->tiocmget)
+ return -ENOTTY;
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->tiocmget(tty);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_tiocmset(struct tty_struct *tty,
unsigned int set, unsigned int clear)
{
struct usb_serial_port *port = tty->driver_data;
+ struct tty_port *tport = &port->port;
+ int ret;
dev_dbg(&port->dev, "%s\n", __func__);
- if (port->serial->type->tiocmset)
- return port->serial->type->tiocmset(tty, set, clear);
- return -ENOTTY;
+ if (!port->serial->type->tiocmset)
+ return -ENOTTY;
+
+ mutex_lock(&tport->mutex);
+ if (tty_io_error(tty))
+ ret = -EIO;
+ else
+ ret = port->serial->type->tiocmset(tty, set, clear);
+ mutex_unlock(&tport->mutex);
+
+ return ret;
}
static int serial_get_icount(struct tty_struct *tty,
@@ -1191,11 +1245,16 @@ static void usb_serial_disconnect(struct usb_interface *interface)
usb_serial_port_poison_urbs(port);
wake_up_interruptible(&port->port.delta_msr_wait);
cancel_work_sync(&port->work);
+ }
+
+ if (serial->type->disconnect)
+ serial->type->disconnect(serial);
+
+ for (i = 0; i < serial->num_ports; ++i) {
+ port = serial->port[i];
if (device_is_registered(&port->dev))
device_del(&port->dev);
}
- if (serial->type->disconnect)
- serial->type->disconnect(serial);
release_sibling(serial, interface);
@@ -1464,7 +1523,7 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
{
int rc;
struct usb_driver *udriver;
- struct usb_serial_driver * const *sd;
+ struct usb_serial_driver * const *sd, * const *s;
/*
* udriver must be registered before any of the serial drivers,
@@ -1517,9 +1576,11 @@ int __usb_serial_register_drivers(struct usb_serial_driver *const serial_drivers
return 0;
err_deregister_drivers:
+ for (s = serial_drivers; s < sd; ++s)
+ usb_serial_bus_remove_new_id(*s);
+ usb_deregister(udriver);
while (sd-- > serial_drivers)
usb_serial_deregister(*sd);
- usb_deregister(udriver);
err_free_driver:
kfree(udriver);
return rc;
@@ -1537,10 +1598,23 @@ EXPORT_SYMBOL_GPL(__usb_serial_register_drivers);
void usb_serial_deregister_drivers(struct usb_serial_driver *const serial_drivers[])
{
struct usb_driver *udriver = (*serial_drivers)->usb_driver;
+ struct usb_serial_driver * const *sd;
+
+ /*
+ * udriver must be deregistered before the serial drivers so that
+ * I/O is stopped before unbinding the ports.
+ *
+ * Remove the new_id attributes to prevent ids from being added and
+ * triggering a probe of udriver after it has been deregistered.
+ */
+ for (sd = serial_drivers; *sd; ++sd)
+ usb_serial_bus_remove_new_id(*sd);
- for (; *serial_drivers; ++serial_drivers)
- usb_serial_deregister(*serial_drivers);
usb_deregister(udriver);
+
+ for (sd = serial_drivers; *sd; ++sd)
+ usb_serial_deregister(*sd);
+
kfree(udriver);
}
EXPORT_SYMBOL_GPL(usb_serial_deregister_drivers);
diff --git a/drivers/usb/serial/xr_serial.c b/drivers/usb/serial/xr_serial.c
--- a/drivers/usb/serial/xr_serial.c
+++ b/drivers/usb/serial/xr_serial.c
@@ -850,12 +850,9 @@ static int xr_get_rs485_config(struct tty_struct *tty,
struct usb_serial_port *port = tty->driver_data;
struct xr_data *data = usb_get_serial_port_data(port);
- down_read(&tty->termios_rwsem);
- if (copy_to_user(argp, &data->rs485, sizeof(data->rs485))) {
- up_read(&tty->termios_rwsem);
+ /* core holds port mutex */
+ if (copy_to_user(argp, &data->rs485, sizeof(data->rs485)))
return -EFAULT;
- }
- up_read(&tty->termios_rwsem);
return 0;
}
@@ -871,10 +868,9 @@ static int xr_set_rs485_config(struct tty_struct *tty,
return -EFAULT;
xr_sanitize_serial_rs485(&rs485);
- down_write(&tty->termios_rwsem);
+ /* core holds termios rwsem and port mutex */
data->rs485 = rs485;
xr_set_flow_mode(tty, port, NULL);
- up_write(&tty->termios_rwsem);
if (copy_to_user(argp, &rs485, sizeof(rs485)))
return -EFAULT;
diff --git a/drivers/usb/storage/sierra_ms.c b/drivers/usb/storage/sierra_ms.c
--- a/drivers/usb/storage/sierra_ms.c
+++ b/drivers/usb/storage/sierra_ms.c
@@ -77,6 +77,13 @@ static int sierra_get_swoc_info(struct usb_device *udev,
sizeof(struct swoc_info), /* __u16 size */
USB_CTRL_SET_TIMEOUT); /* int timeout */
+ /*
+ * A short IN transfer leaves the tail of swocInfo uninitialized;
+ * only a full transfer is valid.
+ */
+ if (result != sizeof(struct swoc_info))
+ return -EIO;
+
swocInfo->LinuxSKU = le16_to_cpu(swocInfo->LinuxSKU);
swocInfo->LinuxVer = le16_to_cpu(swocInfo->LinuxVer);
return result;
diff --git a/drivers/usb/typec/anx7411.c b/drivers/usb/typec/anx7411.c
--- a/drivers/usb/typec/anx7411.c
+++ b/drivers/usb/typec/anx7411.c
@@ -1566,8 +1566,11 @@ static void anx7411_i2c_remove(struct i2c_client *client)
anx7411_partner_unregister_altmode(plat);
anx7411_unregister_partner(plat);
- if (plat->workqueue)
+ if (plat->workqueue) {
+ disable_irq(plat->intp_irq);
+ cancel_work_sync(&plat->work);
destroy_workqueue(plat->workqueue);
+ }
i2c_unregister_device(plat->spi_client);
diff --git a/drivers/usb/typec/port-mapper.c b/drivers/usb/typec/port-mapper.c
--- a/drivers/usb/typec/port-mapper.c
+++ b/drivers/usb/typec/port-mapper.c
@@ -42,6 +42,23 @@ static int usb4_port_compare(struct device *dev, void *fwnode)
return usb4_usb3_port_match(dev, fwnode);
}
+static bool typec_has_usb4_host_interface(const struct fwnode_handle *fwnode)
+{
+ if (!IS_REACHABLE(CONFIG_USB4))
+ return false;
+
+ struct fwnode_handle *nhi_fwnode __free(fwnode_handle) =
+ fwnode_find_reference(fwnode, "usb4-host-interface", 0);
+
+ /*
+ * The USB4 port can only appear if the host interface is enabled in
+ * the firmware and has been enumerated as a device. The latter is
+ * the same check as in usb_acpi_add_usb4_devlink().
+ */
+ return !IS_ERR(nhi_fwnode) && fwnode_device_is_available(nhi_fwnode) &&
+ nhi_fwnode->dev;
+}
+
static int typec_port_compare(struct device *dev, void *fwnode)
{
return device_match_fwnode(dev, fwnode);
@@ -64,11 +81,11 @@ static int typec_port_match(struct device *dev, void *data)
adev_fwnode);
/*
- * If dev is USB 3.x port, it may have reference to the
+ * If dev is USB 3.x port, it may have reference to an available
* USB4 host interface in which case we can also link the
* Type-C port with the USB4 port.
*/
- if (fwnode_property_present(adev_fwnode, "usb4-host-interface"))
+ if (typec_has_usb4_host_interface(adev_fwnode))
component_match_add(&arg->port->dev, &arg->match,
usb4_port_compare, adev_fwnode);
}
diff --git a/drivers/usb/typec/tcpm/tcpm.c b/drivers/usb/typec/tcpm/tcpm.c
--- a/drivers/usb/typec/tcpm/tcpm.c
+++ b/drivers/usb/typec/tcpm/tcpm.c
@@ -1756,7 +1756,8 @@ static void mod_enable_frs_delayed_work(struct tcpm_port *port, unsigned int del
static void mod_vdm_discovery_cancel_delayed_work(struct tcpm_port *port)
{
hrtimer_cancel(&port->vdm_discovery_timer);
- kthread_cancel_work_sync(&port->vdm_discovery_work);
+ if (port->wq)
+ kthread_cancel_work_sync(&port->vdm_discovery_work);
}
static void mod_vdm_discovery_delayed_work(struct tcpm_port *port, unsigned int delay_ms)
@@ -7113,7 +7114,7 @@ static void tcpm_pd_event_handler(struct kthread_work *work)
port->upcoming_state = FR_SWAP_SEND;
ret = tcpm_ams_start(port, FAST_ROLE_SWAP);
if (ret == -EAGAIN)
- port->upcoming_state = INVALID_STATE;
+ tcpm_set_state(port, ERROR_RECOVERY, 0);
} else {
tcpm_log(port, "Discarding FRS_SIGNAL! Not in sink ready");
}
@@ -8977,6 +8978,7 @@ void tcpm_unregister_port(struct tcpm_port *port)
port->registered = false;
kthread_destroy_worker(port->wq);
+ port->wq = NULL;
hrtimer_cancel(&port->vdm_discovery_timer);
hrtimer_cancel(&port->enable_frs_timer);
diff --git a/drivers/usb/typec/tipd/core.c b/drivers/usb/typec/tipd/core.c
--- a/drivers/usb/typec/tipd/core.c
+++ b/drivers/usb/typec/tipd/core.c
@@ -343,7 +343,7 @@ static void tps6598x_set_data_role(struct tps6598x *tps,
static int tps6598x_connect(struct tps6598x *tps, u32 status)
{
- struct typec_partner_desc desc;
+ struct typec_partner_desc desc = { };
enum typec_pwr_opmode mode;
int ret;
@@ -354,7 +354,6 @@ static int tps6598x_connect(struct tps6598x *tps, u32 status)
desc.usb_pd = mode == TYPEC_PWR_MODE_PD;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
- desc.identity = NULL;
if (desc.usb_pd) {
ret = tps6598x_read_partner_identity(tps);
@@ -850,11 +849,10 @@ static void cd321x_update_work(struct work_struct *work)
/* Set up partner if we were previously disconnected (or changed). */
if (!tps->partner) {
- struct typec_partner_desc desc;
+ struct typec_partner_desc desc = { };
desc.usb_pd = is_pd;
desc.accessory = TYPEC_ACCESSORY_NONE; /* XXX: handle accessories */
- desc.identity = NULL;
if (desc.usb_pd)
desc.identity = &st.partner_identity;
@@ -1792,6 +1790,7 @@ static int tps6598x_probe(struct i2c_client *client)
const struct tipd_data *data;
struct tps6598x *tps;
struct fwnode_handle *fwnode;
+ bool patch_loaded = false;
u32 status;
u32 vid = 0;
int ret;
@@ -1847,6 +1846,7 @@ static int tps6598x_probe(struct i2c_client *client)
return ret;
if (ret == TPS_MODE_PTCH) {
+ patch_loaded = true;
ret = tps->data->init(tps);
if (ret)
return ret;
@@ -1937,7 +1937,8 @@ static int tps6598x_probe(struct i2c_client *client)
tps6598x_write64(tps, TPS_REG_INT_MASK1, 0);
err_reset_controller:
/* Reset PD controller to remove any applied patch */
- tps->data->reset(tps);
+ if (patch_loaded)
+ tps->data->reset(tps);
return ret;
}
diff --git a/drivers/usb/typec/ucsi/displayport.c b/drivers/usb/typec/ucsi/displayport.c
--- a/drivers/usb/typec/ucsi/displayport.c
+++ b/drivers/usb/typec/ucsi/displayport.c
@@ -71,11 +71,14 @@ static int ucsi_displayport_enter(struct typec_altmode *alt, u32 *vdo)
if (ret < 0) {
if (ucsi->version > 0x0100)
goto err_unlock;
- cur = 0xff;
}
- if (cur < UCSI_MAX_ALTMODES) {
- ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ if (cur != 0xff) {
+ if (cur < UCSI_MAX_ALTMODES)
+ ret = dp->con->port_altmode[cur] == alt ? 0 : -EBUSY;
+ else
+ ret = -EINVAL;
+
goto err_unlock;
}
diff --git a/drivers/usb/typec/ucsi/ucsi.c b/drivers/usb/typec/ucsi/ucsi.c
--- a/drivers/usb/typec/ucsi/ucsi.c
+++ b/drivers/usb/typec/ucsi/ucsi.c
@@ -1801,10 +1801,21 @@ static struct fwnode_handle *ucsi_find_fwnode(struct ucsi_connector *con)
{
struct fwnode_handle *fwnode;
int i = 1;
+ int ret;
+ u32 port;
- device_for_each_child_node(con->ucsi->dev, fwnode)
- if (i++ == con->num)
- return fwnode;
+ device_for_each_child_node(con->ucsi->dev, fwnode) {
+ ret = fwnode_property_read_u32(fwnode, "reg", &port);
+ if (ret < 0) {
+ if (i == con->num)
+ return fwnode;
+ } else {
+ if (port == con->num - 1)
+ return fwnode;
+ }
+
+ i++;
+ }
return NULL;
}
diff --git a/drivers/usb/typec/ucsi/ucsi_acpi.c b/drivers/usb/typec/ucsi/ucsi_acpi.c
--- a/drivers/usb/typec/ucsi/ucsi_acpi.c
+++ b/drivers/usb/typec/ucsi/ucsi_acpi.c
@@ -121,6 +121,33 @@ static const struct ucsi_operations ucsi_acpi_ops = {
.async_control = ucsi_acpi_async_control
};
+static int ucsi_acer_read_version(struct ucsi *ucsi, u16 *version)
+{
+ struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
+ int ret;
+
+ ret = ucsi_acpi_read_version(ucsi, version);
+ if (ret)
+ return ret;
+
+ if (!*version) {
+ dev_warn(ua->dev, "UCSI version is zero, assuming 1.2\n");
+ *version = UCSI_VERSION_1_2;
+ }
+
+ return 0;
+}
+
+static const struct ucsi_operations ucsi_acer_ops = {
+ .read_version = ucsi_acer_read_version,
+ .read_cci = ucsi_acpi_read_cci,
+ .poll_cci = ucsi_acpi_poll_cci,
+ .read_message_in = ucsi_acpi_read_message_in,
+ .write_message_out = ucsi_acpi_write_message_out,
+ .sync_control = ucsi_sync_control_common,
+ .async_control = ucsi_acpi_async_control
+};
+
static int ucsi_gram_sync_control(struct ucsi *ucsi, u64 command, u32 *cci,
void *val, size_t len, void *msg_out,
size_t msg_out_size)
@@ -162,6 +189,13 @@ static const struct ucsi_operations ucsi_gram_ops = {
};
static const struct dmi_system_id ucsi_acpi_quirks[] = {
+ {
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Nitro ANV15-41"),
+ },
+ .driver_data = (void *)&ucsi_acer_ops,
+ },
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LG Electronics"),
diff --git a/drivers/usb/typec/ucsi/ucsi_glink.c b/drivers/usb/typec/ucsi/ucsi_glink.c
--- a/drivers/usb/typec/ucsi/ucsi_glink.c
+++ b/drivers/usb/typec/ucsi/ucsi_glink.c
@@ -362,6 +362,10 @@ static void pmic_glink_ucsi_destroy(void *data)
{
struct pmic_glink_ucsi *ucsi = data;
+ /* Drain the work items before ucsi_destroy() frees the ucsi instance */
+ cancel_work_sync(&ucsi->notify_work);
+ cancel_work_sync(&ucsi->register_work);
+
/* Protect to make sure we're not in a middle of a transaction from a glink callback */
mutex_lock(&ucsi->lock);
ucsi_destroy(ucsi->ucsi);
@@ -467,8 +471,15 @@ static void pmic_glink_ucsi_remove(struct auxiliary_device *adev)
{
struct pmic_glink_ucsi *ucsi = dev_get_drvdata(&adev->dev);
- /* Unregister first to stop having read & writes */
- ucsi_unregister(ucsi->ucsi);
+ /* Callbacks can queue work until devres releases the client */
+ disable_work_sync(&ucsi->notify_work);
+ disable_work_sync(&ucsi->register_work);
+
+ /* register_work may have unregistered the instance already */
+ if (ucsi->ucsi_registered) {
+ ucsi->ucsi_registered = false;
+ ucsi_unregister(ucsi->ucsi);
+ }
}
static const struct auxiliary_device_id pmic_glink_ucsi_id_table[] = {
diff --git a/include/linux/usb/serial.h b/include/linux/usb/serial.h
--- a/include/linux/usb/serial.h
+++ b/include/linux/usb/serial.h
@@ -381,6 +381,7 @@ void usb_serial_handle_dcd_change(struct usb_serial_port *usb_port,
int usb_serial_bus_register(struct usb_serial_driver *device);
void usb_serial_bus_deregister(struct usb_serial_driver *device);
+void usb_serial_bus_remove_new_id(struct usb_serial_driver *driver);
extern const struct bus_type usb_serial_bus_type;
extern struct tty_driver *usb_serial_tty_driver;