USB driver fixes for 6.8-rc3

Here are a bunch of small USB driver fixes for 6.8-rc3.  Included in
 here are:
   - new usb-serial driver ids
   - new dwc3 driver id added
   - typec driver change revert
   - ncm gadget driver endian bugfix
   - xhci bugfixes for a number of reported issues
   - usb hub bugfix for alternate settings
   - ulpi driver debugfs memory leak fix
   - chipidea driver bugfix
   - usb gadget driver fixes
 
 All of these have been in linux-next for a while with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCZb60Zg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymczwCcCwsgyz86WT5ncgcMTcCFJ0RHEFUAoMLTb7PO
 Ilvy8z+Wn2I2QEtnDLqT
 =H8kH
 -----END PGP SIGNATURE-----

Merge tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb

Pull USB driver fixes from Greg KH:
 "Here are a bunch of small USB driver fixes for 6.8-rc3. Included in
  here are:

   - new usb-serial driver ids

   - new dwc3 driver id added

   - typec driver change revert

   - ncm gadget driver endian bugfix

   - xhci bugfixes for a number of reported issues

   - usb hub bugfix for alternate settings

   - ulpi driver debugfs memory leak fix

   - chipidea driver bugfix

   - usb gadget driver fixes

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'usb-6.8-rc3' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (24 commits)
  USB: serial: option: add Fibocom FM101-GL variant
  USB: serial: qcserial: add new usb-id for Dell Wireless DW5826e
  USB: serial: cp210x: add ID for IMST iM871A-USB
  usb: typec: tcpm: fix the PD disabled case
  usb: ucsi_acpi: Quirk to ack a connector change ack cmd
  usb: ucsi_acpi: Fix command completion handling
  usb: ucsi: Add missing ppm_lock
  usb: ulpi: Fix debugfs directory leak
  Revert "usb: typec: tcpm: fix cc role at port reset"
  usb: gadget: pch_udc: fix an Excess kernel-doc warning
  usb: f_mass_storage: forbid async queue when shutdown happen
  USB: hub: check for alternate port before enabling A_ALT_HNP_SUPPORT
  usb: chipidea: core: handle power lost in workqueue
  usb: dwc3: gadget: Fix NULL pointer dereference in dwc3_gadget_suspend
  usb: dwc3: pci: add support for the Intel Arrow Lake-H
  usb: core: Prevent null pointer dereference in update_port_device_state
  xhci: handle isoc Babble and Buffer Overrun events properly
  xhci: process isoc TD properly when there was a transaction error mid TD.
  xhci: fix off by one check when adding a secondary interrupter.
  xhci: fix possible null pointer dereference at secondary interrupter removal
  ...
This commit is contained in:
Linus Torvalds 2024-02-04 06:52:29 +00:00
commit 809be620dc
21 changed files with 268 additions and 91 deletions

View File

@ -448,17 +448,17 @@ Function-specific configfs interface
The function name to use when creating the function directory is "ncm". The function name to use when creating the function directory is "ncm".
The NCM function provides these attributes in its function directory: The NCM function provides these attributes in its function directory:
=============== ================================================== ======================= ==================================================
ifname network device interface name associated with this ifname network device interface name associated with this
function instance function instance
qmult queue length multiplier for high and super speed qmult queue length multiplier for high and super speed
host_addr MAC address of host's end of this host_addr MAC address of host's end of this
Ethernet over USB link Ethernet over USB link
dev_addr MAC address of device's end of this dev_addr MAC address of device's end of this
Ethernet over USB link Ethernet over USB link
max_segment_size Segment size required for P2P connections. This max_segment_size Segment size required for P2P connections. This
will set MTU to (max_segment_size - 14 bytes) will set MTU to 14 bytes
=============== ================================================== ======================= ==================================================
and after creating the functions/ncm.<instance name> they contain default and after creating the functions/ncm.<instance name> they contain default
values: qmult is 5, dev_addr and host_addr are randomly selected. values: qmult is 5, dev_addr and host_addr are randomly selected.

View File

@ -176,6 +176,7 @@ struct hw_bank {
* @enabled_otg_timer_bits: bits of enabled otg timers * @enabled_otg_timer_bits: bits of enabled otg timers
* @next_otg_timer: next nearest enabled timer to be expired * @next_otg_timer: next nearest enabled timer to be expired
* @work: work for role changing * @work: work for role changing
* @power_lost_work: work for power lost handling
* @wq: workqueue thread * @wq: workqueue thread
* @qh_pool: allocation pool for queue heads * @qh_pool: allocation pool for queue heads
* @td_pool: allocation pool for transfer descriptors * @td_pool: allocation pool for transfer descriptors
@ -226,6 +227,7 @@ struct ci_hdrc {
enum otg_fsm_timer next_otg_timer; enum otg_fsm_timer next_otg_timer;
struct usb_role_switch *role_switch; struct usb_role_switch *role_switch;
struct work_struct work; struct work_struct work;
struct work_struct power_lost_work;
struct workqueue_struct *wq; struct workqueue_struct *wq;
struct dma_pool *qh_pool; struct dma_pool *qh_pool;

View File

@ -856,6 +856,27 @@ static int ci_extcon_register(struct ci_hdrc *ci)
return 0; return 0;
} }
static void ci_power_lost_work(struct work_struct *work)
{
struct ci_hdrc *ci = container_of(work, struct ci_hdrc, power_lost_work);
enum ci_role role;
disable_irq_nosync(ci->irq);
pm_runtime_get_sync(ci->dev);
if (!ci_otg_is_fsm_mode(ci)) {
role = ci_get_role(ci);
if (ci->role != role) {
ci_handle_id_switch(ci);
} else if (role == CI_ROLE_GADGET) {
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
usb_gadget_vbus_connect(&ci->gadget);
}
}
pm_runtime_put_sync(ci->dev);
enable_irq(ci->irq);
}
static DEFINE_IDA(ci_ida); static DEFINE_IDA(ci_ida);
struct platform_device *ci_hdrc_add_device(struct device *dev, struct platform_device *ci_hdrc_add_device(struct device *dev,
@ -1045,6 +1066,8 @@ static int ci_hdrc_probe(struct platform_device *pdev)
spin_lock_init(&ci->lock); spin_lock_init(&ci->lock);
mutex_init(&ci->mutex); mutex_init(&ci->mutex);
INIT_WORK(&ci->power_lost_work, ci_power_lost_work);
ci->dev = dev; ci->dev = dev;
ci->platdata = dev_get_platdata(dev); ci->platdata = dev_get_platdata(dev);
ci->imx28_write_fix = !!(ci->platdata->flags & ci->imx28_write_fix = !!(ci->platdata->flags &
@ -1396,25 +1419,6 @@ static int ci_suspend(struct device *dev)
return 0; return 0;
} }
static void ci_handle_power_lost(struct ci_hdrc *ci)
{
enum ci_role role;
disable_irq_nosync(ci->irq);
if (!ci_otg_is_fsm_mode(ci)) {
role = ci_get_role(ci);
if (ci->role != role) {
ci_handle_id_switch(ci);
} else if (role == CI_ROLE_GADGET) {
if (ci->is_otg && hw_read_otgsc(ci, OTGSC_BSV))
usb_gadget_vbus_connect(&ci->gadget);
}
}
enable_irq(ci->irq);
}
static int ci_resume(struct device *dev) static int ci_resume(struct device *dev)
{ {
struct ci_hdrc *ci = dev_get_drvdata(dev); struct ci_hdrc *ci = dev_get_drvdata(dev);
@ -1446,7 +1450,7 @@ static int ci_resume(struct device *dev)
ci_role(ci)->resume(ci, power_lost); ci_role(ci)->resume(ci, power_lost);
if (power_lost) if (power_lost)
ci_handle_power_lost(ci); queue_work(system_freezable_wq, &ci->power_lost_work);
if (ci->supports_runtime_pm) { if (ci->supports_runtime_pm) {
pm_runtime_disable(dev); pm_runtime_disable(dev);

View File

@ -301,7 +301,7 @@ static int ulpi_register(struct device *dev, struct ulpi *ulpi)
return ret; return ret;
} }
root = debugfs_create_dir(dev_name(dev), ulpi_root); root = debugfs_create_dir(dev_name(&ulpi->dev), ulpi_root);
debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops); debugfs_create_file("regs", 0444, root, ulpi, &ulpi_regs_fops);
dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n", dev_dbg(&ulpi->dev, "registered ULPI PHY: vendor %04x, product %04x\n",

View File

@ -2053,9 +2053,19 @@ static void update_port_device_state(struct usb_device *udev)
if (udev->parent) { if (udev->parent) {
hub = usb_hub_to_struct_hub(udev->parent); hub = usb_hub_to_struct_hub(udev->parent);
port_dev = hub->ports[udev->portnum - 1];
WRITE_ONCE(port_dev->state, udev->state); /*
sysfs_notify_dirent(port_dev->state_kn); * The Link Layer Validation System Driver (lvstest)
* has a test step to unbind the hub before running the
* rest of the procedure. This triggers hub_disconnect
* which will set the hub's maxchild to 0, further
* resulting in usb_hub_to_struct_hub returning NULL.
*/
if (hub) {
port_dev = hub->ports[udev->portnum - 1];
WRITE_ONCE(port_dev->state, udev->state);
sysfs_notify_dirent(port_dev->state_kn);
}
} }
} }
@ -2388,17 +2398,25 @@ static int usb_enumerate_device_otg(struct usb_device *udev)
} }
} else if (desc->bLength == sizeof } else if (desc->bLength == sizeof
(struct usb_otg_descriptor)) { (struct usb_otg_descriptor)) {
/* Set a_alt_hnp_support for legacy otg device */ /*
err = usb_control_msg(udev, * We are operating on a legacy OTP device
usb_sndctrlpipe(udev, 0), * These should be told that they are operating
USB_REQ_SET_FEATURE, 0, * on the wrong port if we have another port that does
USB_DEVICE_A_ALT_HNP_SUPPORT, * support HNP
0, NULL, 0, */
USB_CTRL_SET_TIMEOUT); if (bus->otg_port != 0) {
if (err < 0) /* Set a_alt_hnp_support for legacy otg device */
dev_err(&udev->dev, err = usb_control_msg(udev,
"set a_alt_hnp_support failed: %d\n", usb_sndctrlpipe(udev, 0),
err); USB_REQ_SET_FEATURE, 0,
USB_DEVICE_A_ALT_HNP_SUPPORT,
0, NULL, 0,
USB_CTRL_SET_TIMEOUT);
if (err < 0)
dev_err(&udev->dev,
"set a_alt_hnp_support failed: %d\n",
err);
}
} }
} }
#endif #endif

View File

@ -51,6 +51,8 @@
#define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1 #define PCI_DEVICE_ID_INTEL_MTLP 0x7ec1
#define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f #define PCI_DEVICE_ID_INTEL_MTLS 0x7f6f
#define PCI_DEVICE_ID_INTEL_MTL 0x7e7e #define PCI_DEVICE_ID_INTEL_MTL 0x7e7e
#define PCI_DEVICE_ID_INTEL_ARLH 0x7ec1
#define PCI_DEVICE_ID_INTEL_ARLH_PCH 0x777e
#define PCI_DEVICE_ID_INTEL_TGL 0x9a15 #define PCI_DEVICE_ID_INTEL_TGL 0x9a15
#define PCI_DEVICE_ID_AMD_MR 0x163a #define PCI_DEVICE_ID_AMD_MR 0x163a
@ -421,6 +423,8 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLP, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, MTLS, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ARLH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, ARLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) }, { PCI_DEVICE_DATA(INTEL, TGL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) }, { PCI_DEVICE_DATA(AMD, NL_USB, &dwc3_pci_amd_swnode) },

View File

@ -4709,15 +4709,13 @@ int dwc3_gadget_suspend(struct dwc3 *dwc)
unsigned long flags; unsigned long flags;
int ret; int ret;
if (!dwc->gadget_driver)
return 0;
ret = dwc3_gadget_soft_disconnect(dwc); ret = dwc3_gadget_soft_disconnect(dwc);
if (ret) if (ret)
goto err; goto err;
spin_lock_irqsave(&dwc->lock, flags); spin_lock_irqsave(&dwc->lock, flags);
dwc3_disconnect_gadget(dwc); if (dwc->gadget_driver)
dwc3_disconnect_gadget(dwc);
spin_unlock_irqrestore(&dwc->lock, flags); spin_unlock_irqrestore(&dwc->lock, flags);
return 0; return 0;

View File

@ -61,7 +61,7 @@ out:
int dwc3_host_init(struct dwc3 *dwc) int dwc3_host_init(struct dwc3 *dwc)
{ {
struct property_entry props[4]; struct property_entry props[5];
struct platform_device *xhci; struct platform_device *xhci;
int ret, irq; int ret, irq;
int prop_idx = 0; int prop_idx = 0;
@ -89,6 +89,8 @@ int dwc3_host_init(struct dwc3 *dwc)
memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props)); memset(props, 0, sizeof(struct property_entry) * ARRAY_SIZE(props));
props[prop_idx++] = PROPERTY_ENTRY_BOOL("xhci-sg-trb-cache-size-quirk");
if (dwc->usb3_lpm_capable) if (dwc->usb3_lpm_capable)
props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable"); props[prop_idx++] = PROPERTY_ENTRY_BOOL("usb3-lpm-capable");

View File

@ -545,21 +545,37 @@ static int start_transfer(struct fsg_dev *fsg, struct usb_ep *ep,
static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh) static bool start_in_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
{ {
int rc;
if (!fsg_is_set(common)) if (!fsg_is_set(common))
return false; return false;
bh->state = BUF_STATE_SENDING; bh->state = BUF_STATE_SENDING;
if (start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq)) rc = start_transfer(common->fsg, common->fsg->bulk_in, bh->inreq);
if (rc) {
bh->state = BUF_STATE_EMPTY; bh->state = BUF_STATE_EMPTY;
if (rc == -ESHUTDOWN) {
common->running = 0;
return false;
}
}
return true; return true;
} }
static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh) static bool start_out_transfer(struct fsg_common *common, struct fsg_buffhd *bh)
{ {
int rc;
if (!fsg_is_set(common)) if (!fsg_is_set(common))
return false; return false;
bh->state = BUF_STATE_RECEIVING; bh->state = BUF_STATE_RECEIVING;
if (start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq)) rc = start_transfer(common->fsg, common->fsg->bulk_out, bh->outreq);
if (rc) {
bh->state = BUF_STATE_FULL; bh->state = BUF_STATE_FULL;
if (rc == -ESHUTDOWN) {
common->running = 0;
return false;
}
}
return true; return true;
} }

View File

@ -105,8 +105,8 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
/* /*
* Although max mtu as dictated by u_ether is 15412 bytes, setting * Although max mtu as dictated by u_ether is 15412 bytes, setting
* max_segment_sizeto 15426 would not be efficient. If user chooses segment * max_segment_size to 15426 would not be efficient. If user chooses segment
* size to be (>= 8192), then we can't aggregate more than one buffer in each * size to be (>= 8192), then we can't aggregate more than one buffer in each
* NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep * NTB (assuming each packet coming from network layer is >= 8192 bytes) as ep
* maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow * maxpacket limit is 16384. So let max_segment_size be limited to 8000 to allow
* at least 2 packets to be aggregated reducing wastage of NTB buffer space * at least 2 packets to be aggregated reducing wastage of NTB buffer space
@ -1489,7 +1489,7 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
ncm_data_intf.bInterfaceNumber = status; ncm_data_intf.bInterfaceNumber = status;
ncm_union_desc.bSlaveInterface0 = status; ncm_union_desc.bSlaveInterface0 = status;
ecm_desc.wMaxSegmentSize = ncm_opts->max_segment_size; ecm_desc.wMaxSegmentSize = cpu_to_le16(ncm_opts->max_segment_size);
status = -ENODEV; status = -ENODEV;
@ -1685,7 +1685,7 @@ static struct usb_function_instance *ncm_alloc_inst(void)
kfree(opts); kfree(opts);
return ERR_CAST(net); return ERR_CAST(net);
} }
opts->max_segment_size = cpu_to_le16(ETH_FRAME_LEN); opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop); INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
descs[0] = &opts->ncm_os_desc; descs[0] = &opts->ncm_os_desc;

View File

@ -274,7 +274,6 @@ struct pch_udc_cfg_data {
* @td_data: for data request * @td_data: for data request
* @dev: reference to device struct * @dev: reference to device struct
* @offset_addr: offset address of ep register * @offset_addr: offset address of ep register
* @desc: for this ep
* @queue: queue for requests * @queue: queue for requests
* @num: endpoint number * @num: endpoint number
* @in: endpoint is IN * @in: endpoint is IN

View File

@ -1861,15 +1861,15 @@ void xhci_remove_secondary_interrupter(struct usb_hcd *hcd, struct xhci_interrup
struct xhci_hcd *xhci = hcd_to_xhci(hcd); struct xhci_hcd *xhci = hcd_to_xhci(hcd);
unsigned int intr_num; unsigned int intr_num;
/* interrupter 0 is primary interrupter, don't touch it */
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters)
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
/* fixme, should we check xhci->interrupter[intr_num] == ir */
/* fixme locking */
spin_lock_irq(&xhci->lock); spin_lock_irq(&xhci->lock);
/* interrupter 0 is primary interrupter, don't touch it */
if (!ir || !ir->intr_num || ir->intr_num >= xhci->max_interrupters) {
xhci_dbg(xhci, "Invalid secondary interrupter, can't remove\n");
spin_unlock_irq(&xhci->lock);
return;
}
intr_num = ir->intr_num; intr_num = ir->intr_num;
xhci_remove_interrupter(xhci, ir); xhci_remove_interrupter(xhci, ir);
@ -2322,7 +2322,7 @@ xhci_add_interrupter(struct xhci_hcd *xhci, struct xhci_interrupter *ir,
u64 erst_base; u64 erst_base;
u32 erst_size; u32 erst_size;
if (intr_num > xhci->max_interrupters) { if (intr_num >= xhci->max_interrupters) {
xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n", xhci_warn(xhci, "Can't add interrupter %d, max interrupters %d\n",
intr_num, xhci->max_interrupters); intr_num, xhci->max_interrupters);
return -EINVAL; return -EINVAL;

View File

@ -253,6 +253,9 @@ int xhci_plat_probe(struct platform_device *pdev, struct device *sysdev, const s
if (device_property_read_bool(tmpdev, "quirk-broken-port-ped")) if (device_property_read_bool(tmpdev, "quirk-broken-port-ped"))
xhci->quirks |= XHCI_BROKEN_PORT_PED; xhci->quirks |= XHCI_BROKEN_PORT_PED;
if (device_property_read_bool(tmpdev, "xhci-sg-trb-cache-size-quirk"))
xhci->quirks |= XHCI_SG_TRB_CACHE_SIZE_QUIRK;
device_property_read_u32(tmpdev, "imod-interval-ns", device_property_read_u32(tmpdev, "imod-interval-ns",
&xhci->imod_interval); &xhci->imod_interval);
} }

View File

@ -2376,6 +2376,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
/* handle completion code */ /* handle completion code */
switch (trb_comp_code) { switch (trb_comp_code) {
case COMP_SUCCESS: case COMP_SUCCESS:
/* Don't overwrite status if TD had an error, see xHCI 4.9.1 */
if (td->error_mid_td)
break;
if (remaining) { if (remaining) {
frame->status = short_framestatus; frame->status = short_framestatus;
if (xhci->quirks & XHCI_TRUST_TX_LENGTH) if (xhci->quirks & XHCI_TRUST_TX_LENGTH)
@ -2391,9 +2394,13 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
case COMP_BANDWIDTH_OVERRUN_ERROR: case COMP_BANDWIDTH_OVERRUN_ERROR:
frame->status = -ECOMM; frame->status = -ECOMM;
break; break;
case COMP_ISOCH_BUFFER_OVERRUN:
case COMP_BABBLE_DETECTED_ERROR: case COMP_BABBLE_DETECTED_ERROR:
sum_trbs_for_length = true;
fallthrough;
case COMP_ISOCH_BUFFER_OVERRUN:
frame->status = -EOVERFLOW; frame->status = -EOVERFLOW;
if (ep_trb != td->last_trb)
td->error_mid_td = true;
break; break;
case COMP_INCOMPATIBLE_DEVICE_ERROR: case COMP_INCOMPATIBLE_DEVICE_ERROR:
case COMP_STALL_ERROR: case COMP_STALL_ERROR:
@ -2401,8 +2408,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
break; break;
case COMP_USB_TRANSACTION_ERROR: case COMP_USB_TRANSACTION_ERROR:
frame->status = -EPROTO; frame->status = -EPROTO;
sum_trbs_for_length = true;
if (ep_trb != td->last_trb) if (ep_trb != td->last_trb)
return 0; td->error_mid_td = true;
break; break;
case COMP_STOPPED: case COMP_STOPPED:
sum_trbs_for_length = true; sum_trbs_for_length = true;
@ -2422,6 +2430,9 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
break; break;
} }
if (td->urb_length_set)
goto finish_td;
if (sum_trbs_for_length) if (sum_trbs_for_length)
frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) + frame->actual_length = sum_trb_lengths(xhci, ep->ring, ep_trb) +
ep_trb_len - remaining; ep_trb_len - remaining;
@ -2430,6 +2441,14 @@ static int process_isoc_td(struct xhci_hcd *xhci, struct xhci_virt_ep *ep,
td->urb->actual_length += frame->actual_length; td->urb->actual_length += frame->actual_length;
finish_td:
/* Don't give back TD yet if we encountered an error mid TD */
if (td->error_mid_td && ep_trb != td->last_trb) {
xhci_dbg(xhci, "Error mid isoc TD, wait for final completion event\n");
td->urb_length_set = true;
return 0;
}
return finish_td(xhci, ep, ep_ring, td, trb_comp_code); return finish_td(xhci, ep, ep_ring, td, trb_comp_code);
} }
@ -2808,17 +2827,51 @@ static int handle_tx_event(struct xhci_hcd *xhci,
} }
if (!ep_seg) { if (!ep_seg) {
if (!ep->skip ||
!usb_endpoint_xfer_isoc(&td->urb->ep->desc)) { if (ep->skip && usb_endpoint_xfer_isoc(&td->urb->ep->desc)) {
/* Some host controllers give a spurious skip_isoc_td(xhci, td, ep, status);
* successful event after a short transfer. goto cleanup;
* Ignore it. }
*/
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) && /*
ep_ring->last_td_was_short) { * Some hosts give a spurious success event after a short
ep_ring->last_td_was_short = false; * transfer. Ignore it.
goto cleanup; */
if ((xhci->quirks & XHCI_SPURIOUS_SUCCESS) &&
ep_ring->last_td_was_short) {
ep_ring->last_td_was_short = false;
goto cleanup;
}
/*
* xhci 4.10.2 states isoc endpoints should continue
* processing the next TD if there was an error mid TD.
* So host like NEC don't generate an event for the last
* isoc TRB even if the IOC flag is set.
* xhci 4.9.1 states that if there are errors in mult-TRB
* TDs xHC should generate an error for that TRB, and if xHC
* proceeds to the next TD it should genete an event for
* any TRB with IOC flag on the way. Other host follow this.
* So this event might be for the next TD.
*/
if (td->error_mid_td &&
!list_is_last(&td->td_list, &ep_ring->td_list)) {
struct xhci_td *td_next = list_next_entry(td, td_list);
ep_seg = trb_in_td(xhci, td_next->start_seg, td_next->first_trb,
td_next->last_trb, ep_trb_dma, false);
if (ep_seg) {
/* give back previous TD, start handling new */
xhci_dbg(xhci, "Missing TD completion event after mid TD error\n");
ep_ring->dequeue = td->last_trb;
ep_ring->deq_seg = td->last_trb_seg;
inc_deq(xhci, ep_ring);
xhci_td_cleanup(xhci, td, ep_ring, td->status);
td = td_next;
} }
}
if (!ep_seg) {
/* HC is busted, give up! */ /* HC is busted, give up! */
xhci_err(xhci, xhci_err(xhci,
"ERROR Transfer event TRB DMA ptr not " "ERROR Transfer event TRB DMA ptr not "
@ -2830,9 +2883,6 @@ static int handle_tx_event(struct xhci_hcd *xhci,
ep_trb_dma, true); ep_trb_dma, true);
return -ESHUTDOWN; return -ESHUTDOWN;
} }
skip_isoc_td(xhci, td, ep, status);
goto cleanup;
} }
if (trb_comp_code == COMP_SHORT_PACKET) if (trb_comp_code == COMP_SHORT_PACKET)
ep_ring->last_td_was_short = true; ep_ring->last_td_was_short = true;

View File

@ -1549,6 +1549,7 @@ struct xhci_td {
struct xhci_segment *bounce_seg; struct xhci_segment *bounce_seg;
/* actual_length of the URB has already been set */ /* actual_length of the URB has already been set */
bool urb_length_set; bool urb_length_set;
bool error_mid_td;
unsigned int num_trbs; unsigned int num_trbs;
}; };

View File

@ -146,6 +146,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */ { USB_DEVICE(0x10C4, 0x85F8) }, /* Virtenio Preon32 */
{ USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */ { USB_DEVICE(0x10C4, 0x8664) }, /* AC-Services CAN-IF */
{ USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */ { USB_DEVICE(0x10C4, 0x8665) }, /* AC-Services OBD-IF */
{ USB_DEVICE(0x10C4, 0x87ED) }, /* IMST USB-Stick for Smart Meter */
{ USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */ { USB_DEVICE(0x10C4, 0x8856) }, /* CEL EM357 ZigBee USB Stick - LR */
{ USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */ { USB_DEVICE(0x10C4, 0x8857) }, /* CEL EM357 ZigBee USB Stick */
{ USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */ { USB_DEVICE(0x10C4, 0x88A4) }, /* MMB Networks ZigBee USB Device */

View File

@ -2269,6 +2269,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x0111, 0xff) }, /* Fibocom FM160 (MBIM mode) */
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a0, 0xff) }, /* Fibocom NL668-AM/NL652-EU (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a2, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a3, 0xff) }, /* Fibocom FM101-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */ { USB_DEVICE_INTERFACE_CLASS(0x2cb7, 0x01a4, 0xff), /* Fibocom FM101-GL (laptop MBIM) */
.driver_info = RSVD(4) }, .driver_info = RSVD(4) },
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */ { USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */

View File

@ -184,6 +184,8 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */ {DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
{DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */ {DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
{DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */ {DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
{DEVICE_SWI(0x413c, 0x8217)}, /* Dell Wireless DW5826e */
{DEVICE_SWI(0x413c, 0x8218)}, /* Dell Wireless DW5826e QDL */
/* Huawei devices */ /* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */

View File

@ -4876,8 +4876,7 @@ static void run_state_machine(struct tcpm_port *port)
break; break;
case PORT_RESET: case PORT_RESET:
tcpm_reset_port(port); tcpm_reset_port(port);
tcpm_set_cc(port, tcpm_default_state(port) == SNK_UNATTACHED ? tcpm_set_cc(port, TYPEC_CC_OPEN);
TYPEC_CC_RD : tcpm_rp_cc(port));
tcpm_set_state(port, PORT_RESET_WAIT_OFF, tcpm_set_state(port, PORT_RESET_WAIT_OFF,
PD_T_ERROR_RECOVERY); PD_T_ERROR_RECOVERY);
break; break;
@ -6848,7 +6847,8 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
if (err) if (err)
goto out_role_sw_put; goto out_role_sw_put;
port->typec_caps.pd = port->pds[0]; if (port->pds)
port->typec_caps.pd = port->pds[0];
port->typec_port = typec_register_port(port->dev, &port->typec_caps); port->typec_port = typec_register_port(port->dev, &port->typec_caps);
if (IS_ERR(port->typec_port)) { if (IS_ERR(port->typec_port)) {

View File

@ -938,7 +938,9 @@ static void ucsi_handle_connector_change(struct work_struct *work)
clear_bit(EVENT_PENDING, &con->ucsi->flags); clear_bit(EVENT_PENDING, &con->ucsi->flags);
mutex_lock(&ucsi->ppm_lock);
ret = ucsi_acknowledge_connector_change(ucsi); ret = ucsi_acknowledge_connector_change(ucsi);
mutex_unlock(&ucsi->ppm_lock);
if (ret) if (ret)
dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret); dev_err(ucsi->dev, "%s: ACK failed (%d)", __func__, ret);

View File

@ -25,6 +25,8 @@ struct ucsi_acpi {
unsigned long flags; unsigned long flags;
guid_t guid; guid_t guid;
u64 cmd; u64 cmd;
bool dell_quirk_probed;
bool dell_quirk_active;
}; };
static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func) static int ucsi_acpi_dsm(struct ucsi_acpi *ua, int func)
@ -73,9 +75,13 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len) const void *val, size_t val_len)
{ {
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi); struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
bool ack = UCSI_COMMAND(*(u64 *)val) == UCSI_ACK_CC_CI;
int ret; int ret;
set_bit(COMMAND_PENDING, &ua->flags); if (ack)
set_bit(ACK_PENDING, &ua->flags);
else
set_bit(COMMAND_PENDING, &ua->flags);
ret = ucsi_acpi_async_write(ucsi, offset, val, val_len); ret = ucsi_acpi_async_write(ucsi, offset, val, val_len);
if (ret) if (ret)
@ -85,7 +91,10 @@ static int ucsi_acpi_sync_write(struct ucsi *ucsi, unsigned int offset,
ret = -ETIMEDOUT; ret = -ETIMEDOUT;
out_clear_bit: out_clear_bit:
clear_bit(COMMAND_PENDING, &ua->flags); if (ack)
clear_bit(ACK_PENDING, &ua->flags);
else
clear_bit(COMMAND_PENDING, &ua->flags);
return ret; return ret;
} }
@ -119,12 +128,73 @@ static const struct ucsi_operations ucsi_zenbook_ops = {
.async_write = ucsi_acpi_async_write .async_write = ucsi_acpi_async_write
}; };
static const struct dmi_system_id zenbook_dmi_id[] = { /*
* Some Dell laptops expect that an ACK command with the
* UCSI_ACK_CONNECTOR_CHANGE bit set is followed by a (separate)
* ACK command that only has the UCSI_ACK_COMMAND_COMPLETE bit set.
* If this is not done events are not delivered to OSPM and
* subsequent commands will timeout.
*/
static int
ucsi_dell_sync_write(struct ucsi *ucsi, unsigned int offset,
const void *val, size_t val_len)
{
struct ucsi_acpi *ua = ucsi_get_drvdata(ucsi);
u64 cmd = *(u64 *)val, ack = 0;
int ret;
if (UCSI_COMMAND(cmd) == UCSI_ACK_CC_CI &&
cmd & UCSI_ACK_CONNECTOR_CHANGE)
ack = UCSI_ACK_CC_CI | UCSI_ACK_COMMAND_COMPLETE;
ret = ucsi_acpi_sync_write(ucsi, offset, val, val_len);
if (ret != 0)
return ret;
if (ack == 0)
return ret;
if (!ua->dell_quirk_probed) {
ua->dell_quirk_probed = true;
cmd = UCSI_GET_CAPABILITY;
ret = ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &cmd,
sizeof(cmd));
if (ret == 0)
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL,
&ack, sizeof(ack));
if (ret != -ETIMEDOUT)
return ret;
ua->dell_quirk_active = true;
dev_err(ua->dev, "Firmware bug: Additional ACK required after ACKing a connector change.\n");
dev_err(ua->dev, "Firmware bug: Enabling workaround\n");
}
if (!ua->dell_quirk_active)
return ret;
return ucsi_acpi_sync_write(ucsi, UCSI_CONTROL, &ack, sizeof(ack));
}
static const struct ucsi_operations ucsi_dell_ops = {
.read = ucsi_acpi_read,
.sync_write = ucsi_dell_sync_write,
.async_write = ucsi_acpi_async_write
};
static const struct dmi_system_id ucsi_acpi_quirks[] = {
{ {
.matches = { .matches = {
DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."), DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"), DMI_MATCH(DMI_PRODUCT_NAME, "ZenBook UX325UA_UM325UA"),
}, },
.driver_data = (void *)&ucsi_zenbook_ops,
},
{
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
},
.driver_data = (void *)&ucsi_dell_ops,
}, },
{ } { }
}; };
@ -142,8 +212,10 @@ static void ucsi_acpi_notify(acpi_handle handle, u32 event, void *data)
if (UCSI_CCI_CONNECTOR(cci)) if (UCSI_CCI_CONNECTOR(cci))
ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci)); ucsi_connector_change(ua->ucsi, UCSI_CCI_CONNECTOR(cci));
if (test_bit(COMMAND_PENDING, &ua->flags) && if (cci & UCSI_CCI_ACK_COMPLETE && test_bit(ACK_PENDING, &ua->flags))
cci & (UCSI_CCI_ACK_COMPLETE | UCSI_CCI_COMMAND_COMPLETE)) complete(&ua->complete);
if (cci & UCSI_CCI_COMMAND_COMPLETE &&
test_bit(COMMAND_PENDING, &ua->flags))
complete(&ua->complete); complete(&ua->complete);
} }
@ -151,6 +223,7 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
{ {
struct acpi_device *adev = ACPI_COMPANION(&pdev->dev); struct acpi_device *adev = ACPI_COMPANION(&pdev->dev);
const struct ucsi_operations *ops = &ucsi_acpi_ops; const struct ucsi_operations *ops = &ucsi_acpi_ops;
const struct dmi_system_id *id;
struct ucsi_acpi *ua; struct ucsi_acpi *ua;
struct resource *res; struct resource *res;
acpi_status status; acpi_status status;
@ -180,8 +253,9 @@ static int ucsi_acpi_probe(struct platform_device *pdev)
init_completion(&ua->complete); init_completion(&ua->complete);
ua->dev = &pdev->dev; ua->dev = &pdev->dev;
if (dmi_check_system(zenbook_dmi_id)) id = dmi_first_match(ucsi_acpi_quirks);
ops = &ucsi_zenbook_ops; if (id)
ops = id->driver_data;
ua->ucsi = ucsi_create(&pdev->dev, ops); ua->ucsi = ucsi_create(&pdev->dev, ops);
if (IS_ERR(ua->ucsi)) if (IS_ERR(ua->ucsi))