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

Pull USB fixes from Greg KH:
 "Here is a large chunk of USB driver fixes for 7.0-rc4. Included in
  here are:

   - usb gadget reverts due to reported issues, and then a follow-on fix
     to hopefully resolve the reported overall problem

   - xhci driver fixes

   - dwc3 driver fixes

   - usb core "killable" bulk message api addition to fix a usbtmc
     driver bug where userspace could hang the driver for forever

   - small USB driver fixes for reported issues

   - new usb device quirks

  All except the last USB device quirk change have been in linux-next
  with no reported issues. That one came in too late, and is 'obviously
  correct' :)"

* tag 'usb-7.0-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (35 commits)
  USB: ezcap401 needs USB_QUIRK_NO_BOS to function on 10gbs usb speed
  usb: roles: get usb role switch from parent only for usb-b-connector
  Revert "tcpm: allow looking for role_sw device in the main node"
  usb: gadget: f_ncm: Fix net_device lifecycle with device_move
  Revert "usb: gadget: u_ether: add gether_opts for config caching"
  Revert "usb: gadget: u_ether: use <linux/hex.h> header file"
  Revert "usb: gadget: u_ether: Add auto-cleanup helper for freeing net_device"
  Revert "usb: gadget: f_ncm: align net_device lifecycle with bind/unbind"
  Revert "usb: legacy: ncm: Fix NPE in gncm_bind"
  Revert "usb: gadget: f_ncm: Fix atomic context locking issue"
  usb: typec: altmode/displayport: set displayport signaling rate in configure message
  usb: dwc3: pci: add support for the Intel Nova Lake -H
  usb/core/quirks: Add Huawei ME906S-device to wakeup quirk
  usb: gadget: uvc: fix interval_duration calculation
  xhci: Fix NULL pointer dereference when reading portli debugfs files
  usb: xhci: Prevent interrupt storm on host controller error (HCE)
  usb: xhci: Fix memory leak in xhci_disable_slot()
  usb: class: cdc-wdm: fix reordering issue in read code path
  usb: renesas_usbhs: fix use-after-free in ISR during device removal
  usb: cdc-acm: Restore CAP_BRK functionnality to CH343
  ...
This commit is contained in:
Linus Torvalds
2026-03-14 09:43:12 -07:00
31 changed files with 329 additions and 368 deletions

View File

@@ -8196,6 +8196,9 @@ Kernel parameters
p = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT
(Reduce timeout of the SET_ADDRESS
request from 5000 ms to 500 ms);
q = USB_QUIRK_FORCE_ONE_CONFIG (Device
claims zero configurations,
forcing to 1);
Example: quirks=0781:5580:bk,0a5c:5834:gij
usbhid.mousepoll=

View File

@@ -1379,6 +1379,8 @@ made_compressed_probe:
acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
if (quirks & NO_CAP_LINE)
acm->ctrl_caps &= ~USB_CDC_CAP_LINE;
if (quirks & MISSING_CAP_BRK)
acm->ctrl_caps |= USB_CDC_CAP_BRK;
acm->ctrlsize = ctrlsize;
acm->readsize = readsize;
acm->rx_buflimit = num_rx_buf;
@@ -2002,6 +2004,9 @@ static const struct usb_device_id acm_ids[] = {
.driver_info = IGNORE_DEVICE,
},
/* CH343 supports CAP_BRK, but doesn't advertise it */
{ USB_DEVICE(0x1a86, 0x55d3), .driver_info = MISSING_CAP_BRK, },
/* control interfaces without any protocol set */
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
USB_CDC_PROTO_NONE) },

View File

@@ -113,3 +113,4 @@ struct acm {
#define CLEAR_HALT_CONDITIONS BIT(5)
#define SEND_ZERO_PACKET BIT(6)
#define DISABLE_ECHO BIT(7)
#define MISSING_CAP_BRK BIT(8)

View File

@@ -225,7 +225,8 @@ static void wdm_in_callback(struct urb *urb)
/* we may already be in overflow */
if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
memmove(desc->ubuf + desc->length, desc->inbuf, length);
desc->length += length;
smp_wmb(); /* against wdm_read() */
WRITE_ONCE(desc->length, desc->length + length);
}
}
skip_error:
@@ -533,6 +534,7 @@ static ssize_t wdm_read
return -ERESTARTSYS;
cntr = READ_ONCE(desc->length);
smp_rmb(); /* against wdm_in_callback() */
if (cntr == 0) {
desc->read = 0;
retry:

View File

@@ -727,7 +727,7 @@ static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data)
buffer[1] = data->bTag;
buffer[2] = ~data->bTag;
retval = usb_bulk_msg(data->usb_dev,
retval = usb_bulk_msg_killable(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,
data->bulk_out),
buffer, USBTMC_HEADER_SIZE,
@@ -1347,7 +1347,7 @@ static int send_request_dev_dep_msg_in(struct usbtmc_file_data *file_data,
buffer[11] = 0; /* Reserved */
/* Send bulk URB */
retval = usb_bulk_msg(data->usb_dev,
retval = usb_bulk_msg_killable(data->usb_dev,
usb_sndbulkpipe(data->usb_dev,
data->bulk_out),
buffer, USBTMC_HEADER_SIZE,
@@ -1419,7 +1419,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
actual = 0;
/* Send bulk URB */
retval = usb_bulk_msg(data->usb_dev,
retval = usb_bulk_msg_killable(data->usb_dev,
usb_rcvbulkpipe(data->usb_dev,
data->bulk_in),
buffer, bufsize, &actual,

View File

@@ -927,7 +927,11 @@ int usb_get_configuration(struct usb_device *dev)
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
}
if (ncfg < 1) {
if (ncfg < 1 && dev->quirks & USB_QUIRK_FORCE_ONE_CONFIG) {
dev_info(ddev, "Device claims zero configurations, forcing to 1\n");
dev->descriptor.bNumConfigurations = 1;
ncfg = 1;
} else if (ncfg < 1) {
dev_err(ddev, "no configurations\n");
return -EINVAL;
}

View File

@@ -42,16 +42,19 @@ static void usb_api_blocking_completion(struct urb *urb)
/*
* Starts urb and waits for completion or timeout. Note that this call
* is NOT interruptible. Many device driver i/o requests should be
* interruptible and therefore these drivers should implement their
* own interruptible routines.
* Starts urb and waits for completion or timeout.
* Whether or not the wait is killable depends on the flag passed in.
* For example, compare usb_bulk_msg() and usb_bulk_msg_killable().
*
* For non-killable waits, we enforce a maximum limit on the timeout value.
*/
static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length,
bool killable)
{
struct api_context ctx;
unsigned long expire;
int retval;
long rc;
init_completion(&ctx.done);
urb->context = &ctx;
@@ -60,13 +63,24 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
if (unlikely(retval))
goto out;
expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
if (!wait_for_completion_timeout(&ctx.done, expire)) {
if (!killable && (timeout <= 0 || timeout > USB_MAX_SYNCHRONOUS_TIMEOUT))
timeout = USB_MAX_SYNCHRONOUS_TIMEOUT;
expire = (timeout > 0) ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
if (killable)
rc = wait_for_completion_killable_timeout(&ctx.done, expire);
else
rc = wait_for_completion_timeout(&ctx.done, expire);
if (rc <= 0) {
usb_kill_urb(urb);
retval = (ctx.status == -ENOENT ? -ETIMEDOUT : ctx.status);
if (ctx.status != -ENOENT)
retval = ctx.status;
else if (rc == 0)
retval = -ETIMEDOUT;
else
retval = rc;
dev_dbg(&urb->dev->dev,
"%s timed out on ep%d%s len=%u/%u\n",
"%s timed out or killed on ep%d%s len=%u/%u\n",
current->comm,
usb_endpoint_num(&urb->ep->desc),
usb_urb_dir_in(urb) ? "in" : "out",
@@ -100,7 +114,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
len, usb_api_blocking_completion, NULL);
retv = usb_start_wait_urb(urb, timeout, &length);
retv = usb_start_wait_urb(urb, timeout, &length, false);
if (retv < 0)
return retv;
else
@@ -117,8 +131,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
* @index: USB message index value
* @data: pointer to the data to send
* @size: length in bytes of the data to send
* @timeout: time in msecs to wait for the message to complete before timing
* out (if 0 the wait is forever)
* @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -173,8 +186,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
* @index: USB message index value
* @driver_data: pointer to the data to send
* @size: length in bytes of the data to send
* @timeout: time in msecs to wait for the message to complete before timing
* out (if 0 the wait is forever)
* @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -232,8 +244,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_send);
* @index: USB message index value
* @driver_data: pointer to the data to be filled in by the message
* @size: length in bytes of the data to be received
* @timeout: time in msecs to wait for the message to complete before timing
* out (if 0 the wait is forever)
* @timeout: time in msecs to wait for the message to complete before timing out
* @memflags: the flags for memory allocation for buffers
*
* Context: !in_interrupt ()
@@ -304,8 +315,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
* @timeout: time in msecs to wait for the message to complete before
* timing out (if 0 the wait is forever)
* @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -337,8 +347,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
* @timeout: time in msecs to wait for the message to complete before
* timing out (if 0 the wait is forever)
* @timeout: time in msecs to wait for the message to complete before timing out
*
* Context: task context, might sleep.
*
@@ -385,10 +394,59 @@ int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
return usb_start_wait_urb(urb, timeout, actual_length);
return usb_start_wait_urb(urb, timeout, actual_length, false);
}
EXPORT_SYMBOL_GPL(usb_bulk_msg);
/**
* usb_bulk_msg_killable - Builds a bulk urb, sends it off and waits for completion in a killable state
* @usb_dev: pointer to the usb device to send the message to
* @pipe: endpoint "pipe" to send the message to
* @data: pointer to the data to send
* @len: length in bytes of the data to send
* @actual_length: pointer to a location to put the actual length transferred
* in bytes
* @timeout: time in msecs to wait for the message to complete before
* timing out (if <= 0, the wait is as long as possible)
*
* Context: task context, might sleep.
*
* This function is just like usb_blk_msg(), except that it waits in a
* killable state and there is no limit on the timeout length.
*
* Return:
* If successful, 0. Otherwise a negative error number. The number of actual
* bytes transferred will be stored in the @actual_length parameter.
*
*/
int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout)
{
struct urb *urb;
struct usb_host_endpoint *ep;
ep = usb_pipe_endpoint(usb_dev, pipe);
if (!ep || len < 0)
return -EINVAL;
urb = usb_alloc_urb(0, GFP_KERNEL);
if (!urb)
return -ENOMEM;
if ((ep->desc.bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) ==
USB_ENDPOINT_XFER_INT) {
pipe = (pipe & ~(3 << 30)) | (PIPE_INTERRUPT << 30);
usb_fill_int_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL,
ep->desc.bInterval);
} else
usb_fill_bulk_urb(urb, usb_dev, pipe, data, len,
usb_api_blocking_completion, NULL);
return usb_start_wait_urb(urb, timeout, actual_length, true);
}
EXPORT_SYMBOL_GPL(usb_bulk_msg_killable);
/*-------------------------------------------------------------------*/
static void sg_clean(struct usb_sg_request *io)

View File

@@ -200,16 +200,10 @@ int usb_phy_roothub_set_mode(struct usb_phy_roothub *phy_roothub,
list_for_each_entry(roothub_entry, head, list) {
err = phy_set_mode(roothub_entry->phy, mode);
if (err)
goto err_out;
return err;
}
return 0;
err_out:
list_for_each_entry_continue_reverse(roothub_entry, head, list)
phy_power_off(roothub_entry->phy);
return err;
}
EXPORT_SYMBOL_GPL(usb_phy_roothub_set_mode);

View File

@@ -140,6 +140,8 @@ static int quirks_param_set(const char *value, const struct kernel_param *kp)
case 'p':
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
break;
case 'q':
flags |= USB_QUIRK_FORCE_ONE_CONFIG;
/* Ignore unrecognized flag characters */
}
}
@@ -207,6 +209,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* HP v222w 16GB Mini USB Drive */
{ USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
/* Huawei 4G LTE module ME906S */
{ USB_DEVICE(0x03f0, 0xa31d), .driver_info =
USB_QUIRK_DISCONNECT_SUSPEND },
/* Creative SB Audigy 2 NX */
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
@@ -376,6 +382,9 @@ static const struct usb_device_id usb_quirk_list[] = {
/* SanDisk Extreme 55AE */
{ USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
/* Avermedia Live Gamer Ultra 2.1 (GC553G2) - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x07ca, 0x2553), .driver_info = USB_QUIRK_NO_BOS },
/* Realforce 87U Keyboard */
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
@@ -436,6 +445,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x0b05, 0x17e0), .driver_info =
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
/* ASUS TUF 4K PRO - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x0b05, 0x1ab9), .driver_info = USB_QUIRK_NO_BOS },
/* Realtek Semiconductor Corp. Mass Storage Device (Multicard Reader)*/
{ USB_DEVICE(0x0bda, 0x0151), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
@@ -564,6 +576,9 @@ static const struct usb_device_id usb_quirk_list[] = {
{ USB_DEVICE(0x2386, 0x350e), .driver_info = USB_QUIRK_NO_LPM },
/* UGREEN 35871 - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x2b89, 0x5871), .driver_info = USB_QUIRK_NO_BOS },
/* APTIV AUTOMOTIVE HUB */
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT },
@@ -574,12 +589,18 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
{ USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
/* ezcap401 - BOS descriptor fetch hangs at SuperSpeed Plus */
{ USB_DEVICE(0x32ed, 0x0401), .driver_info = USB_QUIRK_NO_BOS },
/* DELL USB GEN2 */
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
/* VCOM device */
{ USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
/* Noji-MCS SmartCard Reader */
{ USB_DEVICE(0x5131, 0x2007), .driver_info = USB_QUIRK_FORCE_ONE_CONFIG },
/* INTEL VALUE SSD */
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },

View File

@@ -56,6 +56,7 @@
#define PCI_DEVICE_ID_INTEL_CNPH 0xa36e
#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
#define PCI_DEVICE_ID_INTEL_RPL 0xa70e
#define PCI_DEVICE_ID_INTEL_NVLH 0xd37f
#define PCI_DEVICE_ID_INTEL_PTLH 0xe332
#define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e
#define PCI_DEVICE_ID_INTEL_PTLU 0xe432
@@ -447,6 +448,7 @@ static const struct pci_device_id dwc3_pci_id_table[] = {
{ PCI_DEVICE_DATA(INTEL, CNPH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, CNPV, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, RPL, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, NVLH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) },
{ PCI_DEVICE_DATA(INTEL, PTLU, &dwc3_pci_intel_swnode) },

View File

@@ -1207,9 +1207,11 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (!hidg->interval_user_set) {
hidg_fs_in_ep_desc.bInterval = 10;
hidg_hs_in_ep_desc.bInterval = 4;
hidg_ss_in_ep_desc.bInterval = 4;
} else {
hidg_fs_in_ep_desc.bInterval = hidg->interval;
hidg_hs_in_ep_desc.bInterval = hidg->interval;
hidg_ss_in_ep_desc.bInterval = hidg->interval;
}
hidg_ss_out_comp_desc.wBytesPerInterval =
@@ -1239,9 +1241,11 @@ static int hidg_bind(struct usb_configuration *c, struct usb_function *f)
if (!hidg->interval_user_set) {
hidg_fs_out_ep_desc.bInterval = 10;
hidg_hs_out_ep_desc.bInterval = 4;
hidg_ss_out_ep_desc.bInterval = 4;
} else {
hidg_fs_out_ep_desc.bInterval = hidg->interval;
hidg_hs_out_ep_desc.bInterval = hidg->interval;
hidg_ss_out_ep_desc.bInterval = hidg->interval;
}
status = usb_assign_descriptors(f,
hidg_fs_descriptors_intout,

View File

@@ -180,6 +180,7 @@
#include <linux/kthread.h>
#include <linux/sched/signal.h>
#include <linux/limits.h>
#include <linux/overflow.h>
#include <linux/pagemap.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
@@ -1853,8 +1854,15 @@ static int check_command_size_in_blocks(struct fsg_common *common,
int cmnd_size, enum data_direction data_dir,
unsigned int mask, int needs_medium, const char *name)
{
if (common->curlun)
common->data_size_from_cmnd <<= common->curlun->blkbits;
if (common->curlun) {
if (check_shl_overflow(common->data_size_from_cmnd,
common->curlun->blkbits,
&common->data_size_from_cmnd)) {
common->phase_error = 1;
return -EINVAL;
}
}
return check_command(common, cmnd_size, data_dir,
mask, needs_medium, name);
}

View File

@@ -83,11 +83,6 @@ static inline struct f_ncm *func_to_ncm(struct usb_function *f)
return container_of(f, struct f_ncm, port.func);
}
static inline struct f_ncm_opts *func_to_ncm_opts(struct usb_function *f)
{
return container_of(f->fi, struct f_ncm_opts, func_inst);
}
/*-------------------------------------------------------------------------*/
/*
@@ -864,7 +859,6 @@ invalid:
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
{
struct f_ncm *ncm = func_to_ncm(f);
struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
/* Control interface has only altsetting 0 */
@@ -887,13 +881,12 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
if (alt > 1)
goto fail;
scoped_guard(mutex, &opts->lock)
if (opts->net) {
DBG(cdev, "reset ncm\n");
opts->net = NULL;
gether_disconnect(&ncm->port);
ncm_reset_values(ncm);
}
if (ncm->netdev) {
DBG(cdev, "reset ncm\n");
ncm->netdev = NULL;
gether_disconnect(&ncm->port);
ncm_reset_values(ncm);
}
/*
* CDC Network only sends data in non-default altsettings.
@@ -926,8 +919,7 @@ static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
net = gether_connect(&ncm->port);
if (IS_ERR(net))
return PTR_ERR(net);
scoped_guard(mutex, &opts->lock)
opts->net = net;
ncm->netdev = net;
}
spin_lock(&ncm->lock);
@@ -1374,16 +1366,14 @@ err:
static void ncm_disable(struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct usb_composite_dev *cdev = f->config->cdev;
DBG(cdev, "ncm deactivated\n");
scoped_guard(mutex, &opts->lock)
if (opts->net) {
opts->net = NULL;
gether_disconnect(&ncm->port);
}
if (ncm->netdev) {
ncm->netdev = NULL;
gether_disconnect(&ncm->port);
}
if (ncm->notify->enabled) {
usb_ep_disable(ncm->notify);
@@ -1443,44 +1433,41 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
{
struct usb_composite_dev *cdev = c->cdev;
struct f_ncm *ncm = func_to_ncm(f);
struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
struct usb_string *us;
int status = 0;
struct usb_ep *ep;
struct f_ncm_opts *ncm_opts;
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
struct net_device *netdev __free(free_gether_netdev) = NULL;
struct net_device *net __free(detach_gadget) = NULL;
struct usb_request *request __free(free_usb_request) = NULL;
if (!can_support_ecm(cdev->gadget))
return -EINVAL;
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
if (cdev->use_os_string) {
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
if (!os_desc_table)
return -ENOMEM;
}
netdev = gether_setup_default();
if (IS_ERR(netdev))
return -ENOMEM;
scoped_guard(mutex, &ncm_opts->lock)
if (ncm_opts->bind_count == 0) {
if (!device_is_registered(&ncm_opts->net->dev)) {
ncm_opts->net->mtu = (ncm_opts->max_segment_size - ETH_HLEN);
gether_set_gadget(ncm_opts->net, cdev->gadget);
status = gether_register_netdev(ncm_opts->net);
} else
status = gether_attach_gadget(ncm_opts->net, cdev->gadget);
scoped_guard(mutex, &ncm_opts->lock) {
gether_apply_opts(netdev, &ncm_opts->net_opts);
netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
}
if (status)
return status;
net = ncm_opts->net;
}
gether_set_gadget(netdev, cdev->gadget);
status = gether_register_netdev(netdev);
if (status)
return status;
/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(netdev, ncm->ethaddr,
sizeof(ncm->ethaddr));
if (status < 12)
return -EINVAL;
ncm_string_defs[STRING_MAC_IDX].s = ncm->ethaddr;
ncm_string_defs[1].s = ncm->ethaddr;
us = usb_gstrings_attach(cdev, ncm_strings,
ARRAY_SIZE(ncm_string_defs));
@@ -1578,8 +1565,9 @@ static int ncm_bind(struct usb_configuration *c, struct usb_function *f)
f->os_desc_n = 1;
}
ncm->notify_req = no_free_ptr(request);
ncm->netdev = no_free_ptr(netdev);
ncm->port.ioport = netdev_priv(ncm->netdev);
ncm_opts->bind_count++;
retain_and_null_ptr(net);
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
ncm->port.in_ep->name, ncm->port.out_ep->name,
@@ -1594,19 +1582,19 @@ static inline struct f_ncm_opts *to_f_ncm_opts(struct config_item *item)
}
/* f_ncm_item_ops */
USB_ETHER_OPTS_ITEM(ncm);
USB_ETHERNET_CONFIGFS_ITEM(ncm);
/* f_ncm_opts_dev_addr */
USB_ETHER_OPTS_ATTR_DEV_ADDR(ncm);
USB_ETHERNET_CONFIGFS_ITEM_ATTR_DEV_ADDR(ncm);
/* f_ncm_opts_host_addr */
USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
/* f_ncm_opts_qmult */
USB_ETHER_OPTS_ATTR_QMULT(ncm);
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
/* f_ncm_opts_ifname */
USB_ETHER_OPTS_ATTR_IFNAME(ncm);
USB_ETHERNET_CONFIGFS_ITEM_ATTR_IFNAME(ncm);
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
char *page)
@@ -1672,27 +1660,34 @@ static void ncm_free_inst(struct usb_function_instance *f)
struct f_ncm_opts *opts;
opts = container_of(f, struct f_ncm_opts, func_inst);
if (device_is_registered(&opts->net->dev))
gether_cleanup(netdev_priv(opts->net));
else
free_netdev(opts->net);
kfree(opts->ncm_interf_group);
kfree(opts);
}
static struct usb_function_instance *ncm_alloc_inst(void)
{
struct usb_function_instance *ret;
struct f_ncm_opts *opts;
struct usb_os_desc *descs[1];
char *names[1];
struct config_group *ncm_interf_group;
struct f_ncm_opts *opts __free(kfree) = kzalloc_obj(*opts);
opts = kzalloc_obj(*opts);
if (!opts)
return ERR_PTR(-ENOMEM);
opts->net = NULL;
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
gether_setup_opts_default(&opts->net_opts, "usb");
mutex_init(&opts->lock);
opts->func_inst.free_func_inst = ncm_free_inst;
opts->net = gether_setup_default();
if (IS_ERR(opts->net)) {
struct net_device *net = opts->net;
kfree(opts);
return ERR_CAST(net);
}
opts->max_segment_size = ETH_FRAME_LEN;
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
@@ -1703,30 +1698,37 @@ static struct usb_function_instance *ncm_alloc_inst(void)
ncm_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE);
if (IS_ERR(ncm_interf_group))
if (IS_ERR(ncm_interf_group)) {
ncm_free_inst(&opts->func_inst);
return ERR_CAST(ncm_interf_group);
}
opts->ncm_interf_group = ncm_interf_group;
ret = &opts->func_inst;
retain_and_null_ptr(opts);
return ret;
return &opts->func_inst;
}
static void ncm_free(struct usb_function *f)
{
struct f_ncm_opts *opts = func_to_ncm_opts(f);
struct f_ncm *ncm;
struct f_ncm_opts *opts;
scoped_guard(mutex, &opts->lock)
opts->refcnt--;
kfree(func_to_ncm(f));
ncm = func_to_ncm(f);
opts = container_of(f->fi, struct f_ncm_opts, func_inst);
kfree(ncm);
mutex_lock(&opts->lock);
opts->refcnt--;
mutex_unlock(&opts->lock);
}
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
{
struct f_ncm *ncm = func_to_ncm(f);
struct f_ncm_opts *ncm_opts;
DBG(c->cdev, "ncm unbind\n");
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
hrtimer_cancel(&ncm->task_timer);
kfree(f->os_desc_table);
@@ -1743,14 +1745,16 @@ static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
kfree(ncm->notify_req->buf);
usb_ep_free_request(ncm->notify, ncm->notify_req);
ncm->port.ioport = NULL;
gether_cleanup(netdev_priv(ncm->netdev));
ncm_opts->bind_count--;
if (ncm_opts->bind_count == 0)
gether_detach_gadget(ncm_opts->net);
}
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
{
struct f_ncm *ncm;
struct f_ncm_opts *opts;
int status;
/* allocate and initialize one new instance */
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
@@ -1758,12 +1762,22 @@ static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
return ERR_PTR(-ENOMEM);
opts = container_of(fi, struct f_ncm_opts, func_inst);
mutex_lock(&opts->lock);
opts->refcnt++;
scoped_guard(mutex, &opts->lock)
opts->refcnt++;
/* export host's Ethernet address in CDC format */
status = gether_get_host_addr_cdc(opts->net, ncm->ethaddr,
sizeof(ncm->ethaddr));
if (status < 12) { /* strlen("01234567890a") */
kfree(ncm);
mutex_unlock(&opts->lock);
return ERR_PTR(-EINVAL);
}
spin_lock_init(&ncm->lock);
ncm_reset_values(ncm);
ncm->port.ioport = netdev_priv(opts->net);
mutex_unlock(&opts->lock);
ncm->port.is_fixed = true;
ncm->port.supports_multi_frame = true;

View File

@@ -1222,6 +1222,13 @@ static void usbg_submit_cmd(struct usbg_cmd *cmd)
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
if (!tv_nexus) {
struct usb_gadget *gadget = fuas_to_gadget(cmd->fu);
dev_err(&gadget->dev, "Missing nexus, ignoring command\n");
return;
}
dir = get_cmd_dir(cmd->cmd_buf);
if (dir < 0)
goto out;
@@ -1483,6 +1490,13 @@ static void bot_cmd_work(struct work_struct *work)
se_cmd = &cmd->se_cmd;
tpg = cmd->fu->tpg;
tv_nexus = tpg->tpg_nexus;
if (!tv_nexus) {
struct usb_gadget *gadget = fuas_to_gadget(cmd->fu);
dev_err(&gadget->dev, "Missing nexus, ignoring command\n");
return;
}
dir = get_cmd_dir(cmd->cmd_buf);
if (dir < 0)
goto out;

View File

@@ -897,6 +897,28 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
}
EXPORT_SYMBOL_GPL(gether_set_gadget);
int gether_attach_gadget(struct net_device *net, struct usb_gadget *g)
{
int ret;
ret = device_move(&net->dev, &g->dev, DPM_ORDER_DEV_AFTER_PARENT);
if (ret)
return ret;
gether_set_gadget(net, g);
return 0;
}
EXPORT_SYMBOL_GPL(gether_attach_gadget);
void gether_detach_gadget(struct net_device *net)
{
struct eth_dev *dev = netdev_priv(net);
device_move(&net->dev, NULL, DPM_ORDER_NONE);
dev->gadget = NULL;
}
EXPORT_SYMBOL_GPL(gether_detach_gadget);
int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
{
struct eth_dev *dev;
@@ -1040,36 +1062,6 @@ int gether_set_ifname(struct net_device *net, const char *name, int len)
}
EXPORT_SYMBOL_GPL(gether_set_ifname);
void gether_setup_opts_default(struct gether_opts *opts, const char *name)
{
opts->qmult = QMULT_DEFAULT;
snprintf(opts->name, sizeof(opts->name), "%s%%d", name);
eth_random_addr(opts->dev_mac);
opts->addr_assign_type = NET_ADDR_RANDOM;
eth_random_addr(opts->host_mac);
}
EXPORT_SYMBOL_GPL(gether_setup_opts_default);
void gether_apply_opts(struct net_device *net, struct gether_opts *opts)
{
struct eth_dev *dev = netdev_priv(net);
dev->qmult = opts->qmult;
if (opts->ifname_set) {
strscpy(net->name, opts->name, sizeof(net->name));
dev->ifname_set = true;
}
memcpy(dev->host_mac, opts->host_mac, sizeof(dev->host_mac));
if (opts->addr_assign_type == NET_ADDR_SET) {
memcpy(dev->dev_mac, opts->dev_mac, sizeof(dev->dev_mac));
net->addr_assign_type = opts->addr_assign_type;
}
}
EXPORT_SYMBOL_GPL(gether_apply_opts);
void gether_suspend(struct gether *link)
{
struct eth_dev *dev = link->ioport;
@@ -1126,21 +1118,6 @@ void gether_cleanup(struct eth_dev *dev)
}
EXPORT_SYMBOL_GPL(gether_cleanup);
void gether_unregister_free_netdev(struct net_device *net)
{
if (!net)
return;
struct eth_dev *dev = netdev_priv(net);
if (net->reg_state == NETREG_REGISTERED) {
unregister_netdev(net);
flush_work(&dev->work);
}
free_netdev(net);
}
EXPORT_SYMBOL_GPL(gether_unregister_free_netdev);
/**
* gether_connect - notify network layer that USB link is active
* @link: the USB link, set up with endpoints, descriptors matching

View File

@@ -38,31 +38,6 @@
struct eth_dev;
/**
* struct gether_opts - Options for Ethernet gadget function instances
* @name: Pattern for the network interface name (e.g., "usb%d").
* Used to generate the net device name.
* @qmult: Queue length multiplier for high/super speed.
* @host_mac: The MAC address to be used by the host side.
* @dev_mac: The MAC address to be used by the device side.
* @ifname_set: True if the interface name pattern has been set by userspace.
* @addr_assign_type: The method used for assigning the device MAC address
* (e.g., NET_ADDR_RANDOM, NET_ADDR_SET).
*
* This structure caches network-related settings provided through configfs
* before the net_device is fully instantiated. This allows for early
* configuration while deferring net_device allocation until the function
* is bound.
*/
struct gether_opts {
char name[IFNAMSIZ];
unsigned int qmult;
u8 host_mac[ETH_ALEN];
u8 dev_mac[ETH_ALEN];
bool ifname_set;
unsigned char addr_assign_type;
};
/*
* This represents the USB side of an "ethernet" link, managed by a USB
* function which provides control and (maybe) framing. Two functions
@@ -175,6 +150,32 @@ static inline struct net_device *gether_setup_default(void)
*/
void gether_set_gadget(struct net_device *net, struct usb_gadget *g);
/**
* gether_attach_gadget - Reparent net_device to the gadget device.
* @net: The network device to reparent.
* @g: The target USB gadget device to parent to.
*
* This function moves the network device to be a child of the USB gadget
* device in the device hierarchy. This is typically done when the function
* is bound to a configuration.
*
* Returns 0 on success, or a negative error code on failure.
*/
int gether_attach_gadget(struct net_device *net, struct usb_gadget *g);
/**
* gether_detach_gadget - Detach net_device from its gadget parent.
* @net: The network device to detach.
*
* This function moves the network device to be a child of the virtual
* devices parent, effectively detaching it from the USB gadget device
* hierarchy. This is typically done when the function is unbound
* from a configuration but the instance is not yet freed.
*/
void gether_detach_gadget(struct net_device *net);
DEFINE_FREE(detach_gadget, struct net_device *, if (_T) gether_detach_gadget(_T))
/**
* gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
* @net: device representing this link
@@ -283,11 +284,6 @@ int gether_get_ifname(struct net_device *net, char *name, int len);
int gether_set_ifname(struct net_device *net, const char *name, int len);
void gether_cleanup(struct eth_dev *dev);
void gether_unregister_free_netdev(struct net_device *net);
DEFINE_FREE(free_gether_netdev, struct net_device *, gether_unregister_free_netdev(_T));
void gether_setup_opts_default(struct gether_opts *opts, const char *name);
void gether_apply_opts(struct net_device *net, struct gether_opts *opts);
void gether_suspend(struct gether *link);
void gether_resume(struct gether *link);

View File

@@ -13,13 +13,6 @@
#ifndef __U_ETHER_CONFIGFS_H
#define __U_ETHER_CONFIGFS_H
#include <linux/cleanup.h>
#include <linux/hex.h>
#include <linux/if_ether.h>
#include <linux/mutex.h>
#include <linux/netdevice.h>
#include <linux/rtnetlink.h>
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
static void _f_##_attr_release(struct config_item *item) \
{ \
@@ -204,174 +197,4 @@ out: \
\
CONFIGFS_ATTR(_f_##_opts_, _n_)
#define USB_ETHER_OPTS_ITEM(_f_) \
static void _f_##_attr_release(struct config_item *item) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
\
usb_put_function_instance(&opts->func_inst); \
} \
\
static struct configfs_item_operations _f_##_item_ops = { \
.release = _f_##_attr_release, \
}
#define USB_ETHER_OPTS_ATTR_DEV_ADDR(_f_) \
static ssize_t _f_##_opts_dev_addr_show(struct config_item *item, \
char *page) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
\
guard(mutex)(&opts->lock); \
return sysfs_emit(page, "%pM\n", opts->net_opts.dev_mac); \
} \
\
static ssize_t _f_##_opts_dev_addr_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
u8 new_addr[ETH_ALEN]; \
const char *p = page; \
\
guard(mutex)(&opts->lock); \
if (opts->refcnt) \
return -EBUSY; \
\
for (int i = 0; i < ETH_ALEN; i++) { \
unsigned char num; \
if ((*p == '.') || (*p == ':')) \
p++; \
num = hex_to_bin(*p++) << 4; \
num |= hex_to_bin(*p++); \
new_addr[i] = num; \
} \
if (!is_valid_ether_addr(new_addr)) \
return -EINVAL; \
memcpy(opts->net_opts.dev_mac, new_addr, ETH_ALEN); \
opts->net_opts.addr_assign_type = NET_ADDR_SET; \
return len; \
} \
\
CONFIGFS_ATTR(_f_##_opts_, dev_addr)
#define USB_ETHER_OPTS_ATTR_HOST_ADDR(_f_) \
static ssize_t _f_##_opts_host_addr_show(struct config_item *item, \
char *page) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
\
guard(mutex)(&opts->lock); \
return sysfs_emit(page, "%pM\n", opts->net_opts.host_mac); \
} \
\
static ssize_t _f_##_opts_host_addr_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
u8 new_addr[ETH_ALEN]; \
const char *p = page; \
\
guard(mutex)(&opts->lock); \
if (opts->refcnt) \
return -EBUSY; \
\
for (int i = 0; i < ETH_ALEN; i++) { \
unsigned char num; \
if ((*p == '.') || (*p == ':')) \
p++; \
num = hex_to_bin(*p++) << 4; \
num |= hex_to_bin(*p++); \
new_addr[i] = num; \
} \
if (!is_valid_ether_addr(new_addr)) \
return -EINVAL; \
memcpy(opts->net_opts.host_mac, new_addr, ETH_ALEN); \
return len; \
} \
\
CONFIGFS_ATTR(_f_##_opts_, host_addr)
#define USB_ETHER_OPTS_ATTR_QMULT(_f_) \
static ssize_t _f_##_opts_qmult_show(struct config_item *item, \
char *page) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
\
guard(mutex)(&opts->lock); \
return sysfs_emit(page, "%u\n", opts->net_opts.qmult); \
} \
\
static ssize_t _f_##_opts_qmult_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
u32 val; \
int ret; \
\
guard(mutex)(&opts->lock); \
if (opts->refcnt) \
return -EBUSY; \
\
ret = kstrtou32(page, 0, &val); \
if (ret) \
return ret; \
\
opts->net_opts.qmult = val; \
return len; \
} \
\
CONFIGFS_ATTR(_f_##_opts_, qmult)
#define USB_ETHER_OPTS_ATTR_IFNAME(_f_) \
static ssize_t _f_##_opts_ifname_show(struct config_item *item, \
char *page) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
const char *name; \
\
guard(mutex)(&opts->lock); \
rtnl_lock(); \
if (opts->net_opts.ifname_set) \
name = opts->net_opts.name; \
else if (opts->net) \
name = netdev_name(opts->net); \
else \
name = "(inactive net_device)"; \
rtnl_unlock(); \
return sysfs_emit(page, "%s\n", name); \
} \
\
static ssize_t _f_##_opts_ifname_store(struct config_item *item, \
const char *page, size_t len) \
{ \
struct f_##_f_##_opts *opts = to_f_##_f_##_opts(item); \
char tmp[IFNAMSIZ]; \
const char *p; \
size_t c_len = len; \
\
if (c_len > 0 && page[c_len - 1] == '\n') \
c_len--; \
\
if (c_len >= sizeof(tmp)) \
return -E2BIG; \
\
strscpy(tmp, page, c_len + 1); \
if (!dev_valid_name(tmp)) \
return -EINVAL; \
\
/* Require exactly one %d */ \
p = strchr(tmp, '%'); \
if (!p || p[1] != 'd' || strchr(p + 2, '%')) \
return -EINVAL; \
\
guard(mutex)(&opts->lock); \
if (opts->refcnt) \
return -EBUSY; \
strscpy(opts->net_opts.name, tmp, sizeof(opts->net_opts.name)); \
opts->net_opts.ifname_set = true; \
return len; \
} \
\
CONFIGFS_ATTR(_f_##_opts_, ifname)
#endif /* __U_ETHER_CONFIGFS_H */

View File

@@ -15,13 +15,11 @@
#include <linux/usb/composite.h>
#include "u_ether.h"
struct f_ncm_opts {
struct usb_function_instance func_inst;
struct net_device *net;
int bind_count;
struct gether_opts net_opts;
struct config_group *ncm_interf_group;
struct usb_os_desc ncm_os_desc;
char ncm_ext_compat_id[16];

View File

@@ -513,7 +513,7 @@ uvc_video_prep_requests(struct uvc_video *video)
return;
}
interval_duration = 2 << (video->ep->desc->bInterval - 1);
interval_duration = 1 << (video->ep->desc->bInterval - 1);
if (cdev->gadget->speed < USB_SPEED_HIGH)
interval_duration *= 10000;
else

View File

@@ -386,11 +386,19 @@ static const struct file_operations port_fops = {
static int xhci_portli_show(struct seq_file *s, void *unused)
{
struct xhci_port *port = s->private;
struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
struct xhci_hcd *xhci;
u32 portli;
portli = readl(&port->port_reg->portli);
/* port without protocol capability isn't added to a roothub */
if (!port->rhub) {
seq_printf(s, "0x%08x\n", portli);
return 0;
}
xhci = hcd_to_xhci(port->rhub->hcd);
/* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
if (port->rhub == &xhci->usb3_rhub)
seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,

View File

@@ -3195,6 +3195,7 @@ irqreturn_t xhci_irq(struct usb_hcd *hcd)
if (status & STS_HCE) {
xhci_warn(xhci, "WARNING: Host Controller Error\n");
xhci_halt(xhci);
goto out;
}

View File

@@ -4146,7 +4146,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
(xhci->xhc_state & XHCI_STATE_HALTED)) {
spin_unlock_irqrestore(&xhci->lock, flags);
kfree(command);
xhci_free_command(xhci, command);
return -ENODEV;
}
@@ -4154,7 +4154,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
slot_id);
if (ret) {
spin_unlock_irqrestore(&xhci->lock, flags);
kfree(command);
xhci_free_command(xhci, command);
return ret;
}
xhci_ring_cmd_db(xhci);

View File

@@ -707,7 +707,7 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
if (signal_pending (current))
{
mutex_unlock(&mdc800->io_lock);
return -EINTR;
return len == left ? -EINTR : len-left;
}
sts=left > (mdc800->out_count-mdc800->out_ptr)?mdc800->out_count-mdc800->out_ptr:left;
@@ -730,9 +730,11 @@ static ssize_t mdc800_device_read (struct file *file, char __user *buf, size_t l
mutex_unlock(&mdc800->io_lock);
return len-left;
}
wait_event_timeout(mdc800->download_wait,
retval = wait_event_timeout(mdc800->download_wait,
mdc800->downloaded,
msecs_to_jiffies(TO_DOWNLOAD_GET_READY));
if (!retval)
usb_kill_urb(mdc800->download_urb);
mdc800->downloaded = 0;
if (mdc800->download_urb->status != 0)
{

View File

@@ -736,7 +736,7 @@ static int uss720_probe(struct usb_interface *intf,
ret = get_1284_register(pp, 0, &reg, GFP_KERNEL);
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
if (ret < 0)
return ret;
goto probe_abort;
ret = usb_find_last_int_in_endpoint(interface, &epd);
if (!ret) {

View File

@@ -272,6 +272,7 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
dev->int_buffer, YUREX_BUF_SIZE, yurex_interrupt,
dev, 1);
dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
dev->bbu = -1;
if (usb_submit_urb(dev->urb, GFP_KERNEL)) {
retval = -EIO;
dev_err(&interface->dev, "Could not submitting URB\n");
@@ -280,7 +281,6 @@ static int yurex_probe(struct usb_interface *interface, const struct usb_device_
/* save our data pointer in this interface device */
usb_set_intfdata(interface, dev);
dev->bbu = -1;
/* we can register the device now, as it is ready */
retval = usb_register_dev(interface, &yurex_class);

View File

@@ -815,6 +815,15 @@ static void usbhs_remove(struct platform_device *pdev)
usbhs_platform_call(priv, hardware_exit, pdev);
reset_control_assert(priv->rsts);
/*
* Explicitly free the IRQ to ensure the interrupt handler is
* disabled and synchronized before freeing resources.
* devm_free_irq() calls free_irq() which waits for any running
* ISR to complete, preventing UAF.
*/
devm_free_irq(&pdev->dev, priv->irq, priv);
usbhs_mod_remove(priv);
usbhs_fifo_remove(priv);
usbhs_pipe_remove(priv);

View File

@@ -139,9 +139,14 @@ static void *usb_role_switch_match(const struct fwnode_handle *fwnode, const cha
static struct usb_role_switch *
usb_role_switch_is_parent(struct fwnode_handle *fwnode)
{
struct fwnode_handle *parent = fwnode_get_parent(fwnode);
struct fwnode_handle *parent;
struct device *dev;
if (!fwnode_device_is_compatible(fwnode, "usb-b-connector"))
return NULL;
parent = fwnode_get_parent(fwnode);
if (!fwnode_property_present(parent, "usb-role-switch")) {
fwnode_handle_put(parent);
return NULL;

View File

@@ -100,9 +100,14 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
{
u8 pin_assign = 0;
u32 conf;
u32 signal;
/* DP Signalling */
conf = (dp->data.conf & DP_CONF_SIGNALLING_MASK) >> DP_CONF_SIGNALLING_SHIFT;
signal = DP_CAP_DP_SIGNALLING(dp->port->vdo) & DP_CAP_DP_SIGNALLING(dp->alt->vdo);
if (dp->plug_prime)
signal &= DP_CAP_DP_SIGNALLING(dp->plug_prime->vdo);
conf = signal << DP_CONF_SIGNALLING_SHIFT;
switch (con) {
case DP_STATUS_CON_DISABLED:

View File

@@ -7890,7 +7890,7 @@ struct tcpm_port *tcpm_register_port(struct device *dev, struct tcpc_dev *tcpc)
port->partner_desc.identity = &port->partner_ident;
port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
if (IS_ERR_OR_NULL(port->role_sw))
if (!port->role_sw)
port->role_sw = usb_role_switch_get(port->dev);
if (IS_ERR(port->role_sw)) {
err = PTR_ERR(port->role_sw);

View File

@@ -1862,14 +1862,18 @@ void usb_free_noncoherent(struct usb_device *dev, size_t size,
* SYNCHRONOUS CALL SUPPORT *
*-------------------------------------------------------------------*/
/* Maximum value allowed for timeout in synchronous routines below */
#define USB_MAX_SYNCHRONOUS_TIMEOUT 60000 /* ms */
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
__u8 request, __u8 requesttype, __u16 value, __u16 index,
void *data, __u16 size, int timeout);
extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout);
extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length,
int timeout);
void *data, int len, int *actual_length, int timeout);
extern int usb_bulk_msg_killable(struct usb_device *usb_dev, unsigned int pipe,
void *data, int len, int *actual_length, int timeout);
/* wrappers around usb_control_msg() for the most common standard requests */
int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,

View File

@@ -78,4 +78,7 @@
/* skip BOS descriptor request */
#define USB_QUIRK_NO_BOS BIT(17)
/* Device claims zero configurations, forcing to 1 */
#define USB_QUIRK_FORCE_ONE_CONFIG BIT(18)
#endif /* __LINUX_USB_QUIRKS_H */