mirror of
git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-03-21 23:16:50 +08:00
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:
@@ -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=
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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 },
|
||||
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -736,7 +736,7 @@ static int uss720_probe(struct usb_interface *intf,
|
||||
ret = get_1284_register(pp, 0, ®, 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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user