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
|
p = USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT
|
||||||
(Reduce timeout of the SET_ADDRESS
|
(Reduce timeout of the SET_ADDRESS
|
||||||
request from 5000 ms to 500 ms);
|
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
|
Example: quirks=0781:5580:bk,0a5c:5834:gij
|
||||||
|
|
||||||
usbhid.mousepoll=
|
usbhid.mousepoll=
|
||||||
|
|||||||
@@ -1379,6 +1379,8 @@ made_compressed_probe:
|
|||||||
acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
|
acm->ctrl_caps = h.usb_cdc_acm_descriptor->bmCapabilities;
|
||||||
if (quirks & NO_CAP_LINE)
|
if (quirks & NO_CAP_LINE)
|
||||||
acm->ctrl_caps &= ~USB_CDC_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->ctrlsize = ctrlsize;
|
||||||
acm->readsize = readsize;
|
acm->readsize = readsize;
|
||||||
acm->rx_buflimit = num_rx_buf;
|
acm->rx_buflimit = num_rx_buf;
|
||||||
@@ -2002,6 +2004,9 @@ static const struct usb_device_id acm_ids[] = {
|
|||||||
.driver_info = IGNORE_DEVICE,
|
.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 */
|
/* control interfaces without any protocol set */
|
||||||
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
{ USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_ACM,
|
||||||
USB_CDC_PROTO_NONE) },
|
USB_CDC_PROTO_NONE) },
|
||||||
|
|||||||
@@ -113,3 +113,4 @@ struct acm {
|
|||||||
#define CLEAR_HALT_CONDITIONS BIT(5)
|
#define CLEAR_HALT_CONDITIONS BIT(5)
|
||||||
#define SEND_ZERO_PACKET BIT(6)
|
#define SEND_ZERO_PACKET BIT(6)
|
||||||
#define DISABLE_ECHO BIT(7)
|
#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 */
|
/* we may already be in overflow */
|
||||||
if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
|
if (!test_bit(WDM_OVERFLOW, &desc->flags)) {
|
||||||
memmove(desc->ubuf + desc->length, desc->inbuf, length);
|
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:
|
skip_error:
|
||||||
@@ -533,6 +534,7 @@ static ssize_t wdm_read
|
|||||||
return -ERESTARTSYS;
|
return -ERESTARTSYS;
|
||||||
|
|
||||||
cntr = READ_ONCE(desc->length);
|
cntr = READ_ONCE(desc->length);
|
||||||
|
smp_rmb(); /* against wdm_in_callback() */
|
||||||
if (cntr == 0) {
|
if (cntr == 0) {
|
||||||
desc->read = 0;
|
desc->read = 0;
|
||||||
retry:
|
retry:
|
||||||
|
|||||||
@@ -727,7 +727,7 @@ static int usbtmc488_ioctl_trigger(struct usbtmc_file_data *file_data)
|
|||||||
buffer[1] = data->bTag;
|
buffer[1] = data->bTag;
|
||||||
buffer[2] = ~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,
|
usb_sndbulkpipe(data->usb_dev,
|
||||||
data->bulk_out),
|
data->bulk_out),
|
||||||
buffer, USBTMC_HEADER_SIZE,
|
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 */
|
buffer[11] = 0; /* Reserved */
|
||||||
|
|
||||||
/* Send bulk URB */
|
/* Send bulk URB */
|
||||||
retval = usb_bulk_msg(data->usb_dev,
|
retval = usb_bulk_msg_killable(data->usb_dev,
|
||||||
usb_sndbulkpipe(data->usb_dev,
|
usb_sndbulkpipe(data->usb_dev,
|
||||||
data->bulk_out),
|
data->bulk_out),
|
||||||
buffer, USBTMC_HEADER_SIZE,
|
buffer, USBTMC_HEADER_SIZE,
|
||||||
@@ -1419,7 +1419,7 @@ static ssize_t usbtmc_read(struct file *filp, char __user *buf,
|
|||||||
actual = 0;
|
actual = 0;
|
||||||
|
|
||||||
/* Send bulk URB */
|
/* Send bulk URB */
|
||||||
retval = usb_bulk_msg(data->usb_dev,
|
retval = usb_bulk_msg_killable(data->usb_dev,
|
||||||
usb_rcvbulkpipe(data->usb_dev,
|
usb_rcvbulkpipe(data->usb_dev,
|
||||||
data->bulk_in),
|
data->bulk_in),
|
||||||
buffer, bufsize, &actual,
|
buffer, bufsize, &actual,
|
||||||
|
|||||||
@@ -927,7 +927,11 @@ int usb_get_configuration(struct usb_device *dev)
|
|||||||
dev->descriptor.bNumConfigurations = ncfg = USB_MAXCONFIG;
|
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");
|
dev_err(ddev, "no configurations\n");
|
||||||
return -EINVAL;
|
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
|
* Starts urb and waits for completion or timeout.
|
||||||
* is NOT interruptible. Many device driver i/o requests should be
|
* Whether or not the wait is killable depends on the flag passed in.
|
||||||
* interruptible and therefore these drivers should implement their
|
* For example, compare usb_bulk_msg() and usb_bulk_msg_killable().
|
||||||
* own interruptible routines.
|
*
|
||||||
|
* 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;
|
struct api_context ctx;
|
||||||
unsigned long expire;
|
unsigned long expire;
|
||||||
int retval;
|
int retval;
|
||||||
|
long rc;
|
||||||
|
|
||||||
init_completion(&ctx.done);
|
init_completion(&ctx.done);
|
||||||
urb->context = &ctx;
|
urb->context = &ctx;
|
||||||
@@ -60,13 +63,24 @@ static int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length)
|
|||||||
if (unlikely(retval))
|
if (unlikely(retval))
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
expire = timeout ? msecs_to_jiffies(timeout) : MAX_SCHEDULE_TIMEOUT;
|
if (!killable && (timeout <= 0 || timeout > USB_MAX_SYNCHRONOUS_TIMEOUT))
|
||||||
if (!wait_for_completion_timeout(&ctx.done, expire)) {
|
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);
|
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,
|
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,
|
current->comm,
|
||||||
usb_endpoint_num(&urb->ep->desc),
|
usb_endpoint_num(&urb->ep->desc),
|
||||||
usb_urb_dir_in(urb) ? "in" : "out",
|
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,
|
usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *)cmd, data,
|
||||||
len, usb_api_blocking_completion, NULL);
|
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)
|
if (retv < 0)
|
||||||
return retv;
|
return retv;
|
||||||
else
|
else
|
||||||
@@ -117,8 +131,7 @@ static int usb_internal_control_msg(struct usb_device *usb_dev,
|
|||||||
* @index: USB message index value
|
* @index: USB message index value
|
||||||
* @data: pointer to the data to send
|
* @data: pointer to the data to send
|
||||||
* @size: length in bytes of 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
|
* @timeout: time in msecs to wait for the message to complete before timing out
|
||||||
* out (if 0 the wait is forever)
|
|
||||||
*
|
*
|
||||||
* Context: task context, might sleep.
|
* Context: task context, might sleep.
|
||||||
*
|
*
|
||||||
@@ -173,8 +186,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg);
|
|||||||
* @index: USB message index value
|
* @index: USB message index value
|
||||||
* @driver_data: pointer to the data to send
|
* @driver_data: pointer to the data to send
|
||||||
* @size: length in bytes of 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
|
* @timeout: time in msecs to wait for the message to complete before timing out
|
||||||
* out (if 0 the wait is forever)
|
|
||||||
* @memflags: the flags for memory allocation for buffers
|
* @memflags: the flags for memory allocation for buffers
|
||||||
*
|
*
|
||||||
* Context: !in_interrupt ()
|
* Context: !in_interrupt ()
|
||||||
@@ -232,8 +244,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_send);
|
|||||||
* @index: USB message index value
|
* @index: USB message index value
|
||||||
* @driver_data: pointer to the data to be filled in by the message
|
* @driver_data: pointer to the data to be filled in by the message
|
||||||
* @size: length in bytes of the data to be received
|
* @size: length in bytes of the data to be received
|
||||||
* @timeout: time in msecs to wait for the message to complete before timing
|
* @timeout: time in msecs to wait for the message to complete before timing out
|
||||||
* out (if 0 the wait is forever)
|
|
||||||
* @memflags: the flags for memory allocation for buffers
|
* @memflags: the flags for memory allocation for buffers
|
||||||
*
|
*
|
||||||
* Context: !in_interrupt ()
|
* Context: !in_interrupt ()
|
||||||
@@ -304,8 +315,7 @@ EXPORT_SYMBOL_GPL(usb_control_msg_recv);
|
|||||||
* @len: length in bytes of 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
|
* @actual_length: pointer to a location to put the actual length transferred
|
||||||
* in bytes
|
* in bytes
|
||||||
* @timeout: time in msecs to wait for the message to complete before
|
* @timeout: time in msecs to wait for the message to complete before timing out
|
||||||
* timing out (if 0 the wait is forever)
|
|
||||||
*
|
*
|
||||||
* Context: task context, might sleep.
|
* Context: task context, might sleep.
|
||||||
*
|
*
|
||||||
@@ -337,8 +347,7 @@ EXPORT_SYMBOL_GPL(usb_interrupt_msg);
|
|||||||
* @len: length in bytes of 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
|
* @actual_length: pointer to a location to put the actual length transferred
|
||||||
* in bytes
|
* in bytes
|
||||||
* @timeout: time in msecs to wait for the message to complete before
|
* @timeout: time in msecs to wait for the message to complete before timing out
|
||||||
* timing out (if 0 the wait is forever)
|
|
||||||
*
|
*
|
||||||
* Context: task context, might sleep.
|
* 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_fill_bulk_urb(urb, usb_dev, pipe, data, len,
|
||||||
usb_api_blocking_completion, NULL);
|
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);
|
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)
|
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) {
|
list_for_each_entry(roothub_entry, head, list) {
|
||||||
err = phy_set_mode(roothub_entry->phy, mode);
|
err = phy_set_mode(roothub_entry->phy, mode);
|
||||||
if (err)
|
if (err)
|
||||||
goto err_out;
|
return err;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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);
|
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':
|
case 'p':
|
||||||
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
|
flags |= USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT;
|
||||||
break;
|
break;
|
||||||
|
case 'q':
|
||||||
|
flags |= USB_QUIRK_FORCE_ONE_CONFIG;
|
||||||
/* Ignore unrecognized flag characters */
|
/* Ignore unrecognized flag characters */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -207,6 +209,10 @@ static const struct usb_device_id usb_quirk_list[] = {
|
|||||||
/* HP v222w 16GB Mini USB Drive */
|
/* HP v222w 16GB Mini USB Drive */
|
||||||
{ USB_DEVICE(0x03f0, 0x3f40), .driver_info = USB_QUIRK_DELAY_INIT },
|
{ 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 */
|
/* Creative SB Audigy 2 NX */
|
||||||
{ USB_DEVICE(0x041e, 0x3020), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ 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 */
|
/* SanDisk Extreme 55AE */
|
||||||
{ USB_DEVICE(0x0781, 0x55ae), .driver_info = USB_QUIRK_NO_LPM },
|
{ 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 */
|
/* Realforce 87U Keyboard */
|
||||||
{ USB_DEVICE(0x0853, 0x011b), .driver_info = USB_QUIRK_NO_LPM },
|
{ 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_DEVICE(0x0b05, 0x17e0), .driver_info =
|
||||||
USB_QUIRK_IGNORE_REMOTE_WAKEUP },
|
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)*/
|
/* Realtek Semiconductor Corp. Mass Storage Device (Multicard Reader)*/
|
||||||
{ USB_DEVICE(0x0bda, 0x0151), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
|
{ 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 },
|
{ 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 */
|
/* APTIV AUTOMOTIVE HUB */
|
||||||
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
|
{ USB_DEVICE(0x2c48, 0x0132), .driver_info =
|
||||||
USB_QUIRK_SHORT_SET_ADDRESS_REQ_TIMEOUT },
|
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 */
|
/* Alcor Link AK9563 SC Reader used in 2022 Lenovo ThinkPads */
|
||||||
{ USB_DEVICE(0x2ce3, 0x9563), .driver_info = USB_QUIRK_NO_LPM },
|
{ 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 */
|
/* DELL USB GEN2 */
|
||||||
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
|
{ USB_DEVICE(0x413c, 0xb062), .driver_info = USB_QUIRK_NO_LPM | USB_QUIRK_RESET_RESUME },
|
||||||
|
|
||||||
/* VCOM device */
|
/* VCOM device */
|
||||||
{ USB_DEVICE(0x4296, 0x7570), .driver_info = USB_QUIRK_CONFIG_INTF_STRINGS },
|
{ 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 */
|
/* INTEL VALUE SSD */
|
||||||
{ USB_DEVICE(0x8086, 0xf1a5), .driver_info = USB_QUIRK_RESET_RESUME },
|
{ 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_CNPH 0xa36e
|
||||||
#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
|
#define PCI_DEVICE_ID_INTEL_CNPV 0xa3b0
|
||||||
#define PCI_DEVICE_ID_INTEL_RPL 0xa70e
|
#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 0xe332
|
||||||
#define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e
|
#define PCI_DEVICE_ID_INTEL_PTLH_PCH 0xe37e
|
||||||
#define PCI_DEVICE_ID_INTEL_PTLU 0xe432
|
#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, CNPH, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, CNPV, &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, 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, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) },
|
{ PCI_DEVICE_DATA(INTEL, PTLH_PCH, &dwc3_pci_intel_swnode) },
|
||||||
{ PCI_DEVICE_DATA(INTEL, PTLU, &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) {
|
if (!hidg->interval_user_set) {
|
||||||
hidg_fs_in_ep_desc.bInterval = 10;
|
hidg_fs_in_ep_desc.bInterval = 10;
|
||||||
hidg_hs_in_ep_desc.bInterval = 4;
|
hidg_hs_in_ep_desc.bInterval = 4;
|
||||||
|
hidg_ss_in_ep_desc.bInterval = 4;
|
||||||
} else {
|
} else {
|
||||||
hidg_fs_in_ep_desc.bInterval = hidg->interval;
|
hidg_fs_in_ep_desc.bInterval = hidg->interval;
|
||||||
hidg_hs_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 =
|
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) {
|
if (!hidg->interval_user_set) {
|
||||||
hidg_fs_out_ep_desc.bInterval = 10;
|
hidg_fs_out_ep_desc.bInterval = 10;
|
||||||
hidg_hs_out_ep_desc.bInterval = 4;
|
hidg_hs_out_ep_desc.bInterval = 4;
|
||||||
|
hidg_ss_out_ep_desc.bInterval = 4;
|
||||||
} else {
|
} else {
|
||||||
hidg_fs_out_ep_desc.bInterval = hidg->interval;
|
hidg_fs_out_ep_desc.bInterval = hidg->interval;
|
||||||
hidg_hs_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,
|
status = usb_assign_descriptors(f,
|
||||||
hidg_fs_descriptors_intout,
|
hidg_fs_descriptors_intout,
|
||||||
|
|||||||
@@ -180,6 +180,7 @@
|
|||||||
#include <linux/kthread.h>
|
#include <linux/kthread.h>
|
||||||
#include <linux/sched/signal.h>
|
#include <linux/sched/signal.h>
|
||||||
#include <linux/limits.h>
|
#include <linux/limits.h>
|
||||||
|
#include <linux/overflow.h>
|
||||||
#include <linux/pagemap.h>
|
#include <linux/pagemap.h>
|
||||||
#include <linux/rwsem.h>
|
#include <linux/rwsem.h>
|
||||||
#include <linux/slab.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,
|
int cmnd_size, enum data_direction data_dir,
|
||||||
unsigned int mask, int needs_medium, const char *name)
|
unsigned int mask, int needs_medium, const char *name)
|
||||||
{
|
{
|
||||||
if (common->curlun)
|
if (common->curlun) {
|
||||||
common->data_size_from_cmnd <<= common->curlun->blkbits;
|
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,
|
return check_command(common, cmnd_size, data_dir,
|
||||||
mask, needs_medium, name);
|
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);
|
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)
|
static int ncm_set_alt(struct usb_function *f, unsigned intf, unsigned alt)
|
||||||
{
|
{
|
||||||
struct f_ncm *ncm = func_to_ncm(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;
|
struct usb_composite_dev *cdev = f->config->cdev;
|
||||||
|
|
||||||
/* Control interface has only altsetting 0 */
|
/* 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)
|
if (alt > 1)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
scoped_guard(mutex, &opts->lock)
|
if (ncm->netdev) {
|
||||||
if (opts->net) {
|
DBG(cdev, "reset ncm\n");
|
||||||
DBG(cdev, "reset ncm\n");
|
ncm->netdev = NULL;
|
||||||
opts->net = NULL;
|
gether_disconnect(&ncm->port);
|
||||||
gether_disconnect(&ncm->port);
|
ncm_reset_values(ncm);
|
||||||
ncm_reset_values(ncm);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* CDC Network only sends data in non-default altsettings.
|
* 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);
|
net = gether_connect(&ncm->port);
|
||||||
if (IS_ERR(net))
|
if (IS_ERR(net))
|
||||||
return PTR_ERR(net);
|
return PTR_ERR(net);
|
||||||
scoped_guard(mutex, &opts->lock)
|
ncm->netdev = net;
|
||||||
opts->net = net;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
spin_lock(&ncm->lock);
|
spin_lock(&ncm->lock);
|
||||||
@@ -1374,16 +1366,14 @@ err:
|
|||||||
static void ncm_disable(struct usb_function *f)
|
static void ncm_disable(struct usb_function *f)
|
||||||
{
|
{
|
||||||
struct f_ncm *ncm = func_to_ncm(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;
|
struct usb_composite_dev *cdev = f->config->cdev;
|
||||||
|
|
||||||
DBG(cdev, "ncm deactivated\n");
|
DBG(cdev, "ncm deactivated\n");
|
||||||
|
|
||||||
scoped_guard(mutex, &opts->lock)
|
if (ncm->netdev) {
|
||||||
if (opts->net) {
|
ncm->netdev = NULL;
|
||||||
opts->net = NULL;
|
gether_disconnect(&ncm->port);
|
||||||
gether_disconnect(&ncm->port);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (ncm->notify->enabled) {
|
if (ncm->notify->enabled) {
|
||||||
usb_ep_disable(ncm->notify);
|
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 usb_composite_dev *cdev = c->cdev;
|
||||||
struct f_ncm *ncm = func_to_ncm(f);
|
struct f_ncm *ncm = func_to_ncm(f);
|
||||||
struct f_ncm_opts *ncm_opts = func_to_ncm_opts(f);
|
|
||||||
struct usb_string *us;
|
struct usb_string *us;
|
||||||
int status = 0;
|
int status = 0;
|
||||||
struct usb_ep *ep;
|
struct usb_ep *ep;
|
||||||
|
struct f_ncm_opts *ncm_opts;
|
||||||
|
|
||||||
struct usb_os_desc_table *os_desc_table __free(kfree) = NULL;
|
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;
|
struct usb_request *request __free(free_usb_request) = NULL;
|
||||||
|
|
||||||
if (!can_support_ecm(cdev->gadget))
|
if (!can_support_ecm(cdev->gadget))
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
|
|
||||||
|
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||||
|
|
||||||
if (cdev->use_os_string) {
|
if (cdev->use_os_string) {
|
||||||
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
|
os_desc_table = kzalloc(sizeof(*os_desc_table), GFP_KERNEL);
|
||||||
if (!os_desc_table)
|
if (!os_desc_table)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
}
|
}
|
||||||
|
|
||||||
netdev = gether_setup_default();
|
scoped_guard(mutex, &ncm_opts->lock)
|
||||||
if (IS_ERR(netdev))
|
if (ncm_opts->bind_count == 0) {
|
||||||
return -ENOMEM;
|
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) {
|
if (status)
|
||||||
gether_apply_opts(netdev, &ncm_opts->net_opts);
|
return status;
|
||||||
netdev->mtu = ncm_opts->max_segment_size - ETH_HLEN;
|
net = ncm_opts->net;
|
||||||
}
|
}
|
||||||
|
|
||||||
gether_set_gadget(netdev, cdev->gadget);
|
ncm_string_defs[1].s = ncm->ethaddr;
|
||||||
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;
|
|
||||||
|
|
||||||
us = usb_gstrings_attach(cdev, ncm_strings,
|
us = usb_gstrings_attach(cdev, ncm_strings,
|
||||||
ARRAY_SIZE(ncm_string_defs));
|
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;
|
f->os_desc_n = 1;
|
||||||
}
|
}
|
||||||
ncm->notify_req = no_free_ptr(request);
|
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",
|
DBG(cdev, "CDC Network: IN/%s OUT/%s NOTIFY/%s\n",
|
||||||
ncm->port.in_ep->name, ncm->port.out_ep->name,
|
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 */
|
/* f_ncm_item_ops */
|
||||||
USB_ETHER_OPTS_ITEM(ncm);
|
USB_ETHERNET_CONFIGFS_ITEM(ncm);
|
||||||
|
|
||||||
/* f_ncm_opts_dev_addr */
|
/* 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 */
|
/* f_ncm_opts_host_addr */
|
||||||
USB_ETHER_OPTS_ATTR_HOST_ADDR(ncm);
|
USB_ETHERNET_CONFIGFS_ITEM_ATTR_HOST_ADDR(ncm);
|
||||||
|
|
||||||
/* f_ncm_opts_qmult */
|
/* f_ncm_opts_qmult */
|
||||||
USB_ETHER_OPTS_ATTR_QMULT(ncm);
|
USB_ETHERNET_CONFIGFS_ITEM_ATTR_QMULT(ncm);
|
||||||
|
|
||||||
/* f_ncm_opts_ifname */
|
/* 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,
|
static ssize_t ncm_opts_max_segment_size_show(struct config_item *item,
|
||||||
char *page)
|
char *page)
|
||||||
@@ -1672,27 +1660,34 @@ static void ncm_free_inst(struct usb_function_instance *f)
|
|||||||
struct f_ncm_opts *opts;
|
struct f_ncm_opts *opts;
|
||||||
|
|
||||||
opts = container_of(f, struct f_ncm_opts, func_inst);
|
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->ncm_interf_group);
|
||||||
kfree(opts);
|
kfree(opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_function_instance *ncm_alloc_inst(void)
|
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];
|
struct usb_os_desc *descs[1];
|
||||||
char *names[1];
|
char *names[1];
|
||||||
struct config_group *ncm_interf_group;
|
struct config_group *ncm_interf_group;
|
||||||
|
|
||||||
struct f_ncm_opts *opts __free(kfree) = kzalloc_obj(*opts);
|
opts = kzalloc_obj(*opts);
|
||||||
if (!opts)
|
if (!opts)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
opts->net = NULL;
|
|
||||||
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
|
opts->ncm_os_desc.ext_compat_id = opts->ncm_ext_compat_id;
|
||||||
gether_setup_opts_default(&opts->net_opts, "usb");
|
|
||||||
|
|
||||||
mutex_init(&opts->lock);
|
mutex_init(&opts->lock);
|
||||||
opts->func_inst.free_func_inst = ncm_free_inst;
|
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;
|
opts->max_segment_size = ETH_FRAME_LEN;
|
||||||
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
INIT_LIST_HEAD(&opts->ncm_os_desc.ext_prop);
|
||||||
|
|
||||||
@@ -1703,30 +1698,37 @@ static struct usb_function_instance *ncm_alloc_inst(void)
|
|||||||
ncm_interf_group =
|
ncm_interf_group =
|
||||||
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
|
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
|
||||||
names, THIS_MODULE);
|
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);
|
return ERR_CAST(ncm_interf_group);
|
||||||
|
}
|
||||||
opts->ncm_interf_group = ncm_interf_group;
|
opts->ncm_interf_group = ncm_interf_group;
|
||||||
|
|
||||||
ret = &opts->func_inst;
|
return &opts->func_inst;
|
||||||
retain_and_null_ptr(opts);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncm_free(struct usb_function *f)
|
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)
|
ncm = func_to_ncm(f);
|
||||||
opts->refcnt--;
|
opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||||
kfree(func_to_ncm(f));
|
kfree(ncm);
|
||||||
|
mutex_lock(&opts->lock);
|
||||||
|
opts->refcnt--;
|
||||||
|
mutex_unlock(&opts->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
static void ncm_unbind(struct usb_configuration *c, struct usb_function *f)
|
||||||
{
|
{
|
||||||
struct f_ncm *ncm = func_to_ncm(f);
|
struct f_ncm *ncm = func_to_ncm(f);
|
||||||
|
struct f_ncm_opts *ncm_opts;
|
||||||
|
|
||||||
DBG(c->cdev, "ncm unbind\n");
|
DBG(c->cdev, "ncm unbind\n");
|
||||||
|
|
||||||
|
ncm_opts = container_of(f->fi, struct f_ncm_opts, func_inst);
|
||||||
|
|
||||||
hrtimer_cancel(&ncm->task_timer);
|
hrtimer_cancel(&ncm->task_timer);
|
||||||
|
|
||||||
kfree(f->os_desc_table);
|
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);
|
kfree(ncm->notify_req->buf);
|
||||||
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
usb_ep_free_request(ncm->notify, ncm->notify_req);
|
||||||
|
|
||||||
ncm->port.ioport = NULL;
|
ncm_opts->bind_count--;
|
||||||
gether_cleanup(netdev_priv(ncm->netdev));
|
if (ncm_opts->bind_count == 0)
|
||||||
|
gether_detach_gadget(ncm_opts->net);
|
||||||
}
|
}
|
||||||
|
|
||||||
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
static struct usb_function *ncm_alloc(struct usb_function_instance *fi)
|
||||||
{
|
{
|
||||||
struct f_ncm *ncm;
|
struct f_ncm *ncm;
|
||||||
struct f_ncm_opts *opts;
|
struct f_ncm_opts *opts;
|
||||||
|
int status;
|
||||||
|
|
||||||
/* allocate and initialize one new instance */
|
/* allocate and initialize one new instance */
|
||||||
ncm = kzalloc(sizeof(*ncm), GFP_KERNEL);
|
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);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
|
||||||
opts = container_of(fi, struct f_ncm_opts, func_inst);
|
opts = container_of(fi, struct f_ncm_opts, func_inst);
|
||||||
|
mutex_lock(&opts->lock);
|
||||||
|
opts->refcnt++;
|
||||||
|
|
||||||
scoped_guard(mutex, &opts->lock)
|
/* export host's Ethernet address in CDC format */
|
||||||
opts->refcnt++;
|
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);
|
spin_lock_init(&ncm->lock);
|
||||||
ncm_reset_values(ncm);
|
ncm_reset_values(ncm);
|
||||||
|
ncm->port.ioport = netdev_priv(opts->net);
|
||||||
|
mutex_unlock(&opts->lock);
|
||||||
ncm->port.is_fixed = true;
|
ncm->port.is_fixed = true;
|
||||||
ncm->port.supports_multi_frame = 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;
|
se_cmd = &cmd->se_cmd;
|
||||||
tpg = cmd->fu->tpg;
|
tpg = cmd->fu->tpg;
|
||||||
tv_nexus = tpg->tpg_nexus;
|
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);
|
dir = get_cmd_dir(cmd->cmd_buf);
|
||||||
if (dir < 0)
|
if (dir < 0)
|
||||||
goto out;
|
goto out;
|
||||||
@@ -1483,6 +1490,13 @@ static void bot_cmd_work(struct work_struct *work)
|
|||||||
se_cmd = &cmd->se_cmd;
|
se_cmd = &cmd->se_cmd;
|
||||||
tpg = cmd->fu->tpg;
|
tpg = cmd->fu->tpg;
|
||||||
tv_nexus = tpg->tpg_nexus;
|
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);
|
dir = get_cmd_dir(cmd->cmd_buf);
|
||||||
if (dir < 0)
|
if (dir < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|||||||
@@ -897,6 +897,28 @@ void gether_set_gadget(struct net_device *net, struct usb_gadget *g)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gether_set_gadget);
|
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)
|
int gether_set_dev_addr(struct net_device *net, const char *dev_addr)
|
||||||
{
|
{
|
||||||
struct eth_dev *dev;
|
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);
|
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)
|
void gether_suspend(struct gether *link)
|
||||||
{
|
{
|
||||||
struct eth_dev *dev = link->ioport;
|
struct eth_dev *dev = link->ioport;
|
||||||
@@ -1126,21 +1118,6 @@ void gether_cleanup(struct eth_dev *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(gether_cleanup);
|
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
|
* gether_connect - notify network layer that USB link is active
|
||||||
* @link: the USB link, set up with endpoints, descriptors matching
|
* @link: the USB link, set up with endpoints, descriptors matching
|
||||||
|
|||||||
@@ -38,31 +38,6 @@
|
|||||||
|
|
||||||
struct eth_dev;
|
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
|
* This represents the USB side of an "ethernet" link, managed by a USB
|
||||||
* function which provides control and (maybe) framing. Two functions
|
* 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);
|
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
|
* gether_set_dev_addr - initialize an ethernet-over-usb link with eth address
|
||||||
* @net: device representing this link
|
* @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);
|
int gether_set_ifname(struct net_device *net, const char *name, int len);
|
||||||
|
|
||||||
void gether_cleanup(struct eth_dev *dev);
|
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_suspend(struct gether *link);
|
||||||
void gether_resume(struct gether *link);
|
void gether_resume(struct gether *link);
|
||||||
|
|||||||
@@ -13,13 +13,6 @@
|
|||||||
#ifndef __U_ETHER_CONFIGFS_H
|
#ifndef __U_ETHER_CONFIGFS_H
|
||||||
#define __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_) \
|
#define USB_ETHERNET_CONFIGFS_ITEM(_f_) \
|
||||||
static void _f_##_attr_release(struct config_item *item) \
|
static void _f_##_attr_release(struct config_item *item) \
|
||||||
{ \
|
{ \
|
||||||
@@ -204,174 +197,4 @@ out: \
|
|||||||
\
|
\
|
||||||
CONFIGFS_ATTR(_f_##_opts_, _n_)
|
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 */
|
#endif /* __U_ETHER_CONFIGFS_H */
|
||||||
|
|||||||
@@ -15,13 +15,11 @@
|
|||||||
|
|
||||||
#include <linux/usb/composite.h>
|
#include <linux/usb/composite.h>
|
||||||
|
|
||||||
#include "u_ether.h"
|
|
||||||
|
|
||||||
struct f_ncm_opts {
|
struct f_ncm_opts {
|
||||||
struct usb_function_instance func_inst;
|
struct usb_function_instance func_inst;
|
||||||
struct net_device *net;
|
struct net_device *net;
|
||||||
|
int bind_count;
|
||||||
|
|
||||||
struct gether_opts net_opts;
|
|
||||||
struct config_group *ncm_interf_group;
|
struct config_group *ncm_interf_group;
|
||||||
struct usb_os_desc ncm_os_desc;
|
struct usb_os_desc ncm_os_desc;
|
||||||
char ncm_ext_compat_id[16];
|
char ncm_ext_compat_id[16];
|
||||||
|
|||||||
@@ -513,7 +513,7 @@ uvc_video_prep_requests(struct uvc_video *video)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
interval_duration = 2 << (video->ep->desc->bInterval - 1);
|
interval_duration = 1 << (video->ep->desc->bInterval - 1);
|
||||||
if (cdev->gadget->speed < USB_SPEED_HIGH)
|
if (cdev->gadget->speed < USB_SPEED_HIGH)
|
||||||
interval_duration *= 10000;
|
interval_duration *= 10000;
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -386,11 +386,19 @@ static const struct file_operations port_fops = {
|
|||||||
static int xhci_portli_show(struct seq_file *s, void *unused)
|
static int xhci_portli_show(struct seq_file *s, void *unused)
|
||||||
{
|
{
|
||||||
struct xhci_port *port = s->private;
|
struct xhci_port *port = s->private;
|
||||||
struct xhci_hcd *xhci = hcd_to_xhci(port->rhub->hcd);
|
struct xhci_hcd *xhci;
|
||||||
u32 portli;
|
u32 portli;
|
||||||
|
|
||||||
portli = readl(&port->port_reg->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 */
|
/* PORTLI fields are valid if port is a USB3 or eUSB2V2 port */
|
||||||
if (port->rhub == &xhci->usb3_rhub)
|
if (port->rhub == &xhci->usb3_rhub)
|
||||||
seq_printf(s, "0x%08x LEC=%u RLC=%u TLC=%u\n", portli,
|
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) {
|
if (status & STS_HCE) {
|
||||||
xhci_warn(xhci, "WARNING: Host Controller Error\n");
|
xhci_warn(xhci, "WARNING: Host Controller Error\n");
|
||||||
|
xhci_halt(xhci);
|
||||||
goto out;
|
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) ||
|
if (state == 0xffffffff || (xhci->xhc_state & XHCI_STATE_DYING) ||
|
||||||
(xhci->xhc_state & XHCI_STATE_HALTED)) {
|
(xhci->xhc_state & XHCI_STATE_HALTED)) {
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
kfree(command);
|
xhci_free_command(xhci, command);
|
||||||
return -ENODEV;
|
return -ENODEV;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -4154,7 +4154,7 @@ int xhci_disable_slot(struct xhci_hcd *xhci, u32 slot_id)
|
|||||||
slot_id);
|
slot_id);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
spin_unlock_irqrestore(&xhci->lock, flags);
|
spin_unlock_irqrestore(&xhci->lock, flags);
|
||||||
kfree(command);
|
xhci_free_command(xhci, command);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
xhci_ring_cmd_db(xhci);
|
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))
|
if (signal_pending (current))
|
||||||
{
|
{
|
||||||
mutex_unlock(&mdc800->io_lock);
|
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;
|
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);
|
mutex_unlock(&mdc800->io_lock);
|
||||||
return len-left;
|
return len-left;
|
||||||
}
|
}
|
||||||
wait_event_timeout(mdc800->download_wait,
|
retval = wait_event_timeout(mdc800->download_wait,
|
||||||
mdc800->downloaded,
|
mdc800->downloaded,
|
||||||
msecs_to_jiffies(TO_DOWNLOAD_GET_READY));
|
msecs_to_jiffies(TO_DOWNLOAD_GET_READY));
|
||||||
|
if (!retval)
|
||||||
|
usb_kill_urb(mdc800->download_urb);
|
||||||
mdc800->downloaded = 0;
|
mdc800->downloaded = 0;
|
||||||
if (mdc800->download_urb->status != 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);
|
ret = get_1284_register(pp, 0, ®, GFP_KERNEL);
|
||||||
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
|
dev_dbg(&intf->dev, "reg: %7ph\n", priv->reg);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return ret;
|
goto probe_abort;
|
||||||
|
|
||||||
ret = usb_find_last_int_in_endpoint(interface, &epd);
|
ret = usb_find_last_int_in_endpoint(interface, &epd);
|
||||||
if (!ret) {
|
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->int_buffer, YUREX_BUF_SIZE, yurex_interrupt,
|
||||||
dev, 1);
|
dev, 1);
|
||||||
dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
dev->urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
|
||||||
|
dev->bbu = -1;
|
||||||
if (usb_submit_urb(dev->urb, GFP_KERNEL)) {
|
if (usb_submit_urb(dev->urb, GFP_KERNEL)) {
|
||||||
retval = -EIO;
|
retval = -EIO;
|
||||||
dev_err(&interface->dev, "Could not submitting URB\n");
|
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 */
|
/* save our data pointer in this interface device */
|
||||||
usb_set_intfdata(interface, dev);
|
usb_set_intfdata(interface, dev);
|
||||||
dev->bbu = -1;
|
|
||||||
|
|
||||||
/* we can register the device now, as it is ready */
|
/* we can register the device now, as it is ready */
|
||||||
retval = usb_register_dev(interface, &yurex_class);
|
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);
|
usbhs_platform_call(priv, hardware_exit, pdev);
|
||||||
reset_control_assert(priv->rsts);
|
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_mod_remove(priv);
|
||||||
usbhs_fifo_remove(priv);
|
usbhs_fifo_remove(priv);
|
||||||
usbhs_pipe_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 *
|
static struct usb_role_switch *
|
||||||
usb_role_switch_is_parent(struct fwnode_handle *fwnode)
|
usb_role_switch_is_parent(struct fwnode_handle *fwnode)
|
||||||
{
|
{
|
||||||
struct fwnode_handle *parent = fwnode_get_parent(fwnode);
|
struct fwnode_handle *parent;
|
||||||
struct device *dev;
|
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")) {
|
if (!fwnode_property_present(parent, "usb-role-switch")) {
|
||||||
fwnode_handle_put(parent);
|
fwnode_handle_put(parent);
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -100,9 +100,14 @@ static int dp_altmode_configure(struct dp_altmode *dp, u8 con)
|
|||||||
{
|
{
|
||||||
u8 pin_assign = 0;
|
u8 pin_assign = 0;
|
||||||
u32 conf;
|
u32 conf;
|
||||||
|
u32 signal;
|
||||||
|
|
||||||
/* DP Signalling */
|
/* 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) {
|
switch (con) {
|
||||||
case DP_STATUS_CON_DISABLED:
|
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->partner_desc.identity = &port->partner_ident;
|
||||||
|
|
||||||
port->role_sw = fwnode_usb_role_switch_get(tcpc->fwnode);
|
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);
|
port->role_sw = usb_role_switch_get(port->dev);
|
||||||
if (IS_ERR(port->role_sw)) {
|
if (IS_ERR(port->role_sw)) {
|
||||||
err = PTR_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 *
|
* 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,
|
extern int usb_control_msg(struct usb_device *dev, unsigned int pipe,
|
||||||
__u8 request, __u8 requesttype, __u16 value, __u16 index,
|
__u8 request, __u8 requesttype, __u16 value, __u16 index,
|
||||||
void *data, __u16 size, int timeout);
|
void *data, __u16 size, int timeout);
|
||||||
extern int usb_interrupt_msg(struct usb_device *usb_dev, unsigned int pipe,
|
extern int usb_interrupt_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(struct usb_device *usb_dev, unsigned int pipe,
|
extern int usb_bulk_msg(struct usb_device *usb_dev, unsigned int pipe,
|
||||||
void *data, int len, int *actual_length,
|
void *data, int len, int *actual_length, int timeout);
|
||||||
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 */
|
/* wrappers around usb_control_msg() for the most common standard requests */
|
||||||
int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,
|
int usb_control_msg_send(struct usb_device *dev, __u8 endpoint, __u8 request,
|
||||||
|
|||||||
@@ -78,4 +78,7 @@
|
|||||||
/* skip BOS descriptor request */
|
/* skip BOS descriptor request */
|
||||||
#define USB_QUIRK_NO_BOS BIT(17)
|
#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 */
|
#endif /* __LINUX_USB_QUIRKS_H */
|
||||||
|
|||||||
Reference in New Issue
Block a user