USB and Thunderbolt fixes for 7.1-rc6

Here is a set of USB fixes and new device ids for 7.1-rc6.  Nothing
 major in here, just lots of tiny fixes for reported issues found by
 users and some older patches found by some scanning tools.  Included in
 here are:
   - typec fixes found by fuzzers that have decided to finally look at
     that device interaction path (i.e. before a driver is bound to a
     device).
   - typec fixes for issues found by users
   - thunderbolt driver fixes for reported problems
   - cdns3 driver fixes
   - dwc3 driver fixes
   - new device quirks added
   - usb serial driver fixes for broken devices
   - other small driver fixes
 
 All of these have been in linux-next for over a week with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCahrHyA8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+ymLRgCeN/oLFmGYFHjcJEZ8d0AjKbRS34oAn3r822bO
 1mEsGeOojdWNUm4wzu1k
 =pf1m
 -----END PGP SIGNATURE-----

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

Pull USB and Thunderbolt fixes from Greg KH:
 "Here is a set of USB fixes and new device ids for 7.1-rc6. Nothing
  major in here, just lots of tiny fixes for reported issues found by
  users and some older patches found by some scanning tools. Included in
  here are:

   - typec fixes found by fuzzers that have decided to finally look at
     that device interaction path (i.e. before a driver is bound to a
     device)

   - typec fixes for issues found by users

   - thunderbolt driver fixes for reported problems

   - cdns3 driver fixes

   - dwc3 driver fixes

   - new device quirks added

   - usb serial driver fixes for broken devices

   - other small driver fixes

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

* tag 'usb-7.1-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb: (54 commits)
  USB: serial: cypress_m8: validate interrupt packet headers
  USB: serial: safe_serial: fix memory corruption with small endpoint
  USB: serial: omninet: fix memory corruption with small endpoint
  USB: serial: mxuport: fix memory corruption with small endpoint
  USB: serial: cypress_m8: fix memory corruption with small endpoint
  USB: cdc-acm: Fix bit overlap and move quirk definitions to header
  usb: dwc2: Fix use after free in debug code
  usb: chipidea: core: convert ci_role_switch to local variable
  usb: gadget: f_fs: serialize DMABUF cancel against request completion
  usb: gadget: f_fs: copy only received bytes on short ep0 read
  usb: gadget: dummy_hcd: Reject hub port requests for non-existent ports
  dt-bindings: usb: Fix EIC7700 USB reset's issue
  usbip: vudc: Fix use after free bug in vudc_remove due to race condition
  dt-bindings: usb: ti,omap4-musb: Drop duplicate 'usb-phy' property constraints
  usb: storage: Add quirks for PNY Elite Portable SSD
  USB: quirks: add NO_LPM for Lenovo ThinkPad USB-C Dock Gen2 hub controllers
  usb: usbtmc: reject interrupt endpoints with small wMaxPacketSize
  usb: usbtmc: check URB actual_length for interrupt-IN notifications
  xhci: tegra: Fix ghost USB device on dual-role port unplug
  usb: gadget: uvc: hold opts->lock across XU walks in uvc_function_bind
  ...
This commit is contained in:
Linus Torvalds 2026-05-30 08:37:45 -07:00
commit 670b77dfeb
43 changed files with 445 additions and 206 deletions

View File

@ -41,12 +41,13 @@ properties:
- const: usb_en
resets:
maxItems: 2
maxItems: 3
reset-names:
items:
- const: vaux
- const: usb_rst
- const: usb_phy
eswin,hsp-sp-csr:
description:
@ -85,8 +86,8 @@ examples:
interrupt-parent = <&plic>;
interrupts = <85>;
interrupt-names = "peripheral";
resets = <&reset 84>, <&hspcrg 2>;
reset-names = "vaux", "usb_rst";
resets = <&reset 84>, <&hspcrg 2>, <&hspcrg 4>;
reset-names = "vaux", "usb_rst", "usb_phy";
dr_mode = "peripheral";
maximum-speed = "high-speed";
phy_type = "utmi";

View File

@ -81,9 +81,7 @@ properties:
const: usb2-phy
usb-phy:
$ref: /schemas/types.yaml#/definitions/phandle-array
description: Phandle for the PHY device.
deprecated: true
maxItems: 1
ctrl-module:
$ref: /schemas/types.yaml#/definitions/phandle
@ -96,6 +94,9 @@ required:
- interrupts
- interrupt-names
allOf:
- $ref: usb-hcd.yaml#
unevaluatedProperties: false
examples:

View File

@ -8,6 +8,7 @@
*/
#include <linux/err.h>
#include <linux/overflow.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/uuid.h>
@ -34,10 +35,11 @@ struct tb_property_dir_entry {
};
#define TB_PROPERTY_ROOTDIR_MAGIC 0x55584401
#define TB_PROPERTY_MAX_DEPTH 8
static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
size_t block_len, unsigned int dir_offset, size_t dir_len,
bool is_root);
bool is_root, unsigned int depth);
static inline void parse_dwdata(void *dst, const void *src, size_t dwords)
{
@ -52,13 +54,16 @@ static inline void format_dwdata(void *dst, const void *src, size_t dwords)
static bool tb_property_entry_valid(const struct tb_property_entry *entry,
size_t block_len)
{
u32 end;
switch (entry->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
case TB_PROPERTY_TYPE_DATA:
case TB_PROPERTY_TYPE_TEXT:
if (entry->length > block_len)
return false;
if (entry->value + entry->length > block_len)
if (check_add_overflow(entry->value, entry->length, &end) ||
end > block_len)
return false;
break;
@ -93,7 +98,8 @@ tb_property_alloc(const char *key, enum tb_property_type type)
}
static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
const struct tb_property_entry *entry)
const struct tb_property_entry *entry,
unsigned int depth)
{
char key[TB_PROPERTY_KEY_SIZE + 1];
struct tb_property *property;
@ -114,7 +120,7 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
switch (property->type) {
case TB_PROPERTY_TYPE_DIRECTORY:
dir = __tb_property_parse_dir(block, block_len, entry->value,
entry->length, false);
entry->length, false, depth + 1);
if (!dir) {
kfree(property);
return NULL;
@ -159,21 +165,31 @@ static struct tb_property *tb_property_parse(const u32 *block, size_t block_len,
}
static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root)
size_t block_len, unsigned int dir_offset, size_t dir_len, bool is_root,
unsigned int depth)
{
const struct tb_property_entry *entries;
size_t i, content_len, nentries;
unsigned int content_offset;
struct tb_property_dir *dir;
if (depth > TB_PROPERTY_MAX_DEPTH)
return NULL;
dir = kzalloc_obj(*dir);
if (!dir)
return NULL;
INIT_LIST_HEAD(&dir->properties);
if (is_root) {
content_offset = dir_offset + 2;
content_len = dir_len;
} else {
if (dir_len < 4) {
tb_property_free_dir(dir);
return NULL;
}
dir->uuid = kmemdup(&block[dir_offset], sizeof(*dir->uuid),
GFP_KERNEL);
if (!dir->uuid) {
@ -187,12 +203,10 @@ static struct tb_property_dir *__tb_property_parse_dir(const u32 *block,
entries = (const struct tb_property_entry *)&block[content_offset];
nentries = content_len / (sizeof(*entries) / 4);
INIT_LIST_HEAD(&dir->properties);
for (i = 0; i < nentries; i++) {
struct tb_property *property;
property = tb_property_parse(block, block_len, &entries[i]);
property = tb_property_parse(block, block_len, &entries[i], depth);
if (!property) {
tb_property_free_dir(dir);
return NULL;
@ -231,7 +245,7 @@ struct tb_property_dir *tb_property_parse_dir(const u32 *block,
return NULL;
return __tb_property_parse_dir(block, block_len, 0, rootdir->length,
true);
true, 0);
}
/**

View File

@ -2817,9 +2817,19 @@ int __cdns3_gadget_ep_clear_halt(struct cdns3_endpoint *priv_ep)
priv_ep->flags &= ~(EP_STALLED | EP_STALL_PENDING);
if (request) {
if (trb)
if (trb) {
*trb = trb_tmp;
/*
* Per datasheet, EPRST causes DMA to reposition to the next TD.
* Manually reset EP_TRADDR to the current TRB to prevent
* the hardware from skipping the interrupted request.
*/
writel(EP_TRADDR_TRADDR(priv_ep->trb_pool_dma +
priv_req->start_trb * TRB_SIZE),
&priv_dev->regs->ep_traddr);
}
cdns3_rearm_transfer(priv_ep, 1);
}

View File

@ -126,15 +126,15 @@ static int cdns3_plat_probe(struct platform_device *pdev)
return dev_err_probe(dev, PTR_ERR(cdns->usb2_phy),
"Failed to get cdn3,usb2-phy\n");
ret = phy_init(cdns->usb2_phy);
if (ret)
return ret;
cdns->usb3_phy = devm_phy_optional_get(dev, "cdns3,usb3-phy");
if (IS_ERR(cdns->usb3_phy))
return dev_err_probe(dev, PTR_ERR(cdns->usb3_phy),
"Failed to get cdn3,usb3-phy\n");
ret = phy_init(cdns->usb2_phy);
if (ret)
return ret;
ret = phy_init(cdns->usb3_phy);
if (ret)
goto err_phy3_init;
@ -186,6 +186,9 @@ static void cdns3_plat_remove(struct platform_device *pdev)
struct device *dev = cdns->dev;
pm_runtime_get_sync(dev);
if (!(cdns->pdata && (cdns->pdata->quirks & CDNS3_DEFAULT_PM_RUNTIME_ALLOW)))
pm_runtime_allow(dev);
pm_runtime_disable(dev);
pm_runtime_put_noidle(dev);
cdns_remove(cdns);

View File

@ -655,12 +655,6 @@ static enum ci_role ci_get_role(struct ci_hdrc *ci)
return role;
}
static struct usb_role_switch_desc ci_role_switch = {
.set = ci_usb_role_switch_set,
.get = ci_usb_role_switch_get,
.allow_userspace_control = true,
};
static int ci_get_platdata(struct device *dev,
struct ci_hdrc_platform_data *platdata)
{
@ -787,9 +781,6 @@ static int ci_get_platdata(struct device *dev,
cable->connected = false;
}
if (device_property_read_bool(dev, "usb-role-switch"))
ci_role_switch.fwnode = dev->fwnode;
platdata->pctl = devm_pinctrl_get(dev);
if (!IS_ERR(platdata->pctl)) {
struct pinctrl_state *p;
@ -1033,6 +1024,7 @@ ATTRIBUTE_GROUPS(ci);
static int ci_hdrc_probe(struct platform_device *pdev)
{
struct usb_role_switch_desc ci_role_switch = {};
struct device *dev = &pdev->dev;
struct ci_hdrc *ci;
struct resource *res;
@ -1179,7 +1171,11 @@ static int ci_hdrc_probe(struct platform_device *pdev)
}
}
if (ci_role_switch.fwnode) {
if (device_property_read_bool(dev, "usb-role-switch")) {
ci_role_switch.set = ci_usb_role_switch_set;
ci_role_switch.get = ci_usb_role_switch_get;
ci_role_switch.allow_userspace_control = true;
ci_role_switch.fwnode = dev_fwnode(dev);
ci_role_switch.driver_data = ci;
ci->role_switch = usb_role_switch_register(dev,
&ci_role_switch);

View File

@ -114,8 +114,6 @@ static int acm_ctrl_msg(struct acm *acm, int request, int value,
int retval;
retval = usb_autopm_get_interface(acm->control);
#define VENDOR_CLASS_DATA_IFACE BIT(9) /* data interface uses vendor-specific class */
#define ALWAYS_POLL_CTRL BIT(10) /* keep ctrl URB active even without an open TTY */
if (retval)
return retval;

View File

@ -115,3 +115,5 @@ struct acm {
#define DISABLE_ECHO BIT(7)
#define MISSING_CAP_BRK BIT(8)
#define NO_UNION_12 BIT(9)
#define VENDOR_CLASS_DATA_IFACE BIT(10) /* data interface uses vendor-specific class */
#define ALWAYS_POLL_CTRL BIT(11) /* keep ctrl URB active even without an open TTY */

View File

@ -2306,6 +2306,14 @@ static void usbtmc_interrupt(struct urb *urb)
switch (status) {
case 0: /* SUCCESS */
/* ensure at least two bytes of headers were transferred */
if (urb->actual_length < 2) {
dev_warn(dev,
"actual length %d not sufficient for interrupt headers\n",
urb->actual_length);
goto exit;
}
/* check for valid STB notification */
if (data->iin_buffer[0] > 0x81) {
data->bNotify1 = data->iin_buffer[0];
@ -2432,6 +2440,12 @@ static int usbtmc_probe(struct usb_interface *intf,
data->iin_ep = int_in->bEndpointAddress;
data->iin_wMaxPacketSize = usb_endpoint_maxp(int_in);
data->iin_interval = int_in->bInterval;
/* wMaxPacketSize should be 0x02 or more as per USB488 Table 22 */
if (iface_desc->desc.bInterfaceProtocol == 1 &&
data->iin_wMaxPacketSize < 2) {
retcode = -EINVAL;
goto err_put;
}
dev_dbg(&intf->dev, "Found Int in endpoint at %u\n",
data->iin_ep);
}

View File

@ -56,8 +56,7 @@ static void usb_parse_ssp_isoc_endpoint_companion(struct device *ddev,
desc = (struct usb_ssp_isoc_ep_comp_descriptor *) buffer;
if (size < USB_DT_SSP_ISOC_EP_COMP_SIZE ||
desc->bDescriptorType != USB_DT_SSP_ISOC_ENDPOINT_COMP) {
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion"
"for config %d interface %d altsetting %d ep %d.\n",
dev_notice(ddev, "Invalid SuperSpeedPlus isoc endpoint companion for config %d interface %d altsetting %d ep 0x%X.\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
return;
}
@ -91,7 +90,7 @@ static void usb_parse_eusb2_isoc_endpoint_companion(struct device *ddev,
size -= h->bLength;
}
dev_notice(ddev, "No eUSB2 isoc ep %d companion for config %d interface %d altsetting %d\n",
dev_notice(ddev, "No eUSB2 isoc ep 0x%X companion for config %d interface %d altsetting %d\n",
ep->desc.bEndpointAddress, cfgno, inum, asnum);
}
@ -115,9 +114,7 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
}
if (desc->bDescriptorType != USB_DT_SS_ENDPOINT_COMP) {
dev_notice(ddev, "No SuperSpeed endpoint companion for config %d "
" interface %d altsetting %d ep %d: "
"using minimum values\n",
dev_notice(ddev, "No SuperSpeed endpoint companion for config %d interface %d altsetting %d ep 0x%X: using minimum values\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
/* Fill in some default values.
@ -141,42 +138,32 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
/* Check the various values */
if (usb_endpoint_xfer_control(&ep->desc) && desc->bMaxBurst != 0) {
dev_notice(ddev, "Control endpoint with bMaxBurst = %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to zero\n", desc->bMaxBurst,
cfgno, inum, asnum, ep->desc.bEndpointAddress);
dev_notice(ddev, "Control endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bMaxBurst = 0;
} else if (desc->bMaxBurst > 15) {
dev_notice(ddev, "Endpoint with bMaxBurst = %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to 15\n", desc->bMaxBurst,
cfgno, inum, asnum, ep->desc.bEndpointAddress);
dev_notice(ddev, "Endpoint with bMaxBurst = %d in config %d interface %d altsetting %d ep 0x%X: setting to 15\n",
desc->bMaxBurst, cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bMaxBurst = 15;
}
if ((usb_endpoint_xfer_control(&ep->desc) ||
usb_endpoint_xfer_int(&ep->desc)) &&
desc->bmAttributes != 0) {
dev_notice(ddev, "%s endpoint with bmAttributes = %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to zero\n",
dev_notice(ddev, "%s endpoint with bmAttributes = %d in config %d interface %d altsetting %d ep 0x%X: setting to zero\n",
usb_endpoint_xfer_control(&ep->desc) ? "Control" : "Bulk",
desc->bmAttributes,
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 0;
} else if (usb_endpoint_xfer_bulk(&ep->desc) &&
desc->bmAttributes > 16) {
dev_notice(ddev, "Bulk endpoint with more than 65536 streams in "
"config %d interface %d altsetting %d ep %d: "
"setting to max\n",
dev_notice(ddev, "Bulk endpoint with more than 65536 streams in config %d interface %d altsetting %d ep 0x%X: setting to max\n",
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 16;
} else if (usb_endpoint_xfer_isoc(&ep->desc) &&
!USB_SS_SSP_ISOC_COMP(desc->bmAttributes) &&
USB_SS_MULT(desc->bmAttributes) > 3) {
dev_notice(ddev, "Isoc endpoint has Mult of %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to 3\n",
dev_notice(ddev, "Isoc endpoint has Mult of %d in config %d interface %d altsetting %d ep 0x%X: setting to 3\n",
USB_SS_MULT(desc->bmAttributes),
cfgno, inum, asnum, ep->desc.bEndpointAddress);
ep->ss_ep_comp.bmAttributes = 2;
@ -191,10 +178,15 @@ static void usb_parse_ss_endpoint_companion(struct device *ddev, int cfgno,
(desc->bMaxBurst + 1);
else
max_tx = 999999;
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx) {
dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in "
"config %d interface %d altsetting %d ep %d: "
"setting to %d\n",
/*
* wBytesPerInterval > max_tx is bogus, but USB3 spec doesn't forbid the opposite.
* Experience shows that wBytesPerInterval < wMaxPacketSize on common interrupt IN
* endpoints is usually bogus too, and recent HCs enforce interrupt BW limits.
*/
if (le16_to_cpu(desc->wBytesPerInterval) > max_tx ||
(le16_to_cpu(desc->wBytesPerInterval) < usb_endpoint_maxp(&ep->desc) &&
usb_endpoint_is_int_in(&ep->desc))) {
dev_notice(ddev, "%s endpoint with wBytesPerInterval of %d in config %d interface %d altsetting %d ep 0x%X: setting to %d\n",
usb_endpoint_xfer_isoc(&ep->desc) ? "Isoc" : "Int",
le16_to_cpu(desc->wBytesPerInterval),
cfgno, inum, asnum, ep->desc.bEndpointAddress,

View File

@ -328,9 +328,7 @@ static const u8 ss_rh_config_descriptor[] = {
USB_DT_ENDPOINT, /* __u8 ep_bDescriptorType; Endpoint */
0x81, /* __u8 ep_bEndpointAddress; IN Endpoint 1 */
0x03, /* __u8 ep_bmAttributes; Interrupt */
/* __le16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8)
* see hub.c:hub_configure() for details. */
(USB_MAXCHILDREN + 1 + 7) / 8, 0x00,
0x02, 0x00, /* __le16 ep_wMaxPacketSize; 2 bytes per USB3 10.15.1 */
0x0c, /* __u8 ep_bInterval; (256ms -- usb 2.0 spec) */
/* one SuperSpeed endpoint companion descriptor */

View File

@ -513,6 +513,10 @@ static const struct usb_device_id usb_quirk_list[] = {
/* Lenovo ThinkPad USB-C Dock Gen2 Ethernet (RTL8153 GigE) */
{ USB_DEVICE(0x17ef, 0xa387), .driver_info = USB_QUIRK_NO_LPM },
/* Lenovo ThinkPad USB-C Dock Gen2 USB 3.1 and USB 2.0 hub controllers */
{ USB_DEVICE(0x17ef, 0xa391), .driver_info = USB_QUIRK_NO_LPM },
{ USB_DEVICE(0x17ef, 0xa392), .driver_info = USB_QUIRK_NO_LPM },
/* BUILDWIN Photo Frame */
{ USB_DEVICE(0x1908, 0x1315), .driver_info =
USB_QUIRK_HONOR_BNUMINTERFACES },

View File

@ -4804,6 +4804,7 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
struct dwc2_hsotg *hsotg = dwc2_hcd_to_hsotg(hcd);
int rc;
unsigned long flags;
int urb_status;
dev_dbg(hsotg->dev, "DWC OTG HCD URB Dequeue\n");
dwc2_dump_urb_info(hcd, urb, "urb_dequeue");
@ -4828,11 +4829,12 @@ static int _dwc2_hcd_urb_dequeue(struct usb_hcd *hcd, struct urb *urb,
/* Higher layer software sets URB status */
spin_unlock(&hsotg->lock);
urb_status = urb->status;
usb_hcd_giveback_urb(hcd, urb, status);
spin_lock(&hsotg->lock);
dev_dbg(hsotg->dev, "Called usb_hcd_giveback_urb()\n");
dev_dbg(hsotg->dev, " urb->status = %d\n", urb->status);
dev_dbg(hsotg->dev, " urb->status = %d\n", urb_status);
out:
spin_unlock_irqrestore(&hsotg->lock, flags);

View File

@ -184,15 +184,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
}
ret = phy_init(priv_data->usb3_phy);
if (ret < 0) {
phy_exit(priv_data->usb3_phy);
if (ret < 0)
goto err;
}
ret = reset_control_deassert(apbrst);
if (ret < 0) {
dev_err(dev, "Failed to release APB reset\n");
goto err;
goto err_phy_exit;
}
if (priv_data->usb3_phy) {
@ -208,26 +206,24 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
ret = reset_control_deassert(crst);
if (ret < 0) {
dev_err(dev, "Failed to release core reset\n");
goto err;
goto err_phy_exit;
}
ret = reset_control_deassert(hibrst);
if (ret < 0) {
dev_err(dev, "Failed to release hibernation reset\n");
goto err;
goto err_phy_exit;
}
ret = phy_power_on(priv_data->usb3_phy);
if (ret < 0) {
phy_exit(priv_data->usb3_phy);
goto err;
}
if (ret < 0)
goto err_phy_exit;
/* ulpi reset via gpio-modepin or gpio-framework driver */
reset_gpio = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_HIGH);
if (IS_ERR(reset_gpio)) {
return dev_err_probe(dev, PTR_ERR(reset_gpio),
"Failed to request reset GPIO\n");
ret = PTR_ERR(reset_gpio);
goto err_phy_power_off;
}
if (reset_gpio) {
@ -237,6 +233,13 @@ static int dwc3_xlnx_init_zynqmp(struct dwc3_xlnx *priv_data)
}
dwc3_xlnx_set_coherency(priv_data, XLNX_USB_TRAFFIC_ROUTE_CONFIG);
return 0;
err_phy_power_off:
phy_power_off(priv_data->usb3_phy);
err_phy_exit:
phy_exit(priv_data->usb3_phy);
err:
return ret;
}

View File

@ -2172,7 +2172,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl)
sizeof(url_descriptor->URL)
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset);
if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length)
if (w_length < WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH)
landing_page_length = landing_page_offset;
else if (w_length <
WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_length)
landing_page_length = w_length
- WEBUSB_URL_DESCRIPTOR_HEADER_LENGTH + landing_page_offset;

View File

@ -150,6 +150,8 @@ struct ffs_dma_fence {
struct dma_fence base;
struct ffs_dmabuf_priv *priv;
struct work_struct work;
struct usb_ep *ep;
struct usb_request *req;
};
struct ffs_epfile {
@ -619,7 +621,7 @@ static ssize_t ffs_ep0_read(struct file *file, char __user *buf,
/* unlocks spinlock */
ret = __ffs_ep0_queue_wait(ffs, data, len);
if ((ret > 0) && (copy_to_user(buf, data, len)))
if ((ret > 0) && (copy_to_user(buf, data, ret)))
ret = -EFAULT;
goto done_mutex;
@ -1385,6 +1387,21 @@ static void ffs_dmabuf_cleanup(struct work_struct *work)
struct ffs_dmabuf_priv *priv = dma_fence->priv;
struct dma_buf_attachment *attach = priv->attach;
struct dma_fence *fence = &dma_fence->base;
struct usb_request *req = dma_fence->req;
struct usb_ep *ep = dma_fence->ep;
/*
* eps_lock pairs with the cancel paths so they cannot pass a freed
* req to usb_ep_dequeue(). Only clear if priv->req still names ours;
* a re-queue on the same attachment may have taken that slot.
*/
spin_lock_irq(&priv->ffs->eps_lock);
if (priv->req == req)
priv->req = NULL;
spin_unlock_irq(&priv->ffs->eps_lock);
if (ep && req)
usb_ep_free_request(ep, req);
ffs_dmabuf_put(attach);
dma_fence_put(fence);
@ -1414,8 +1431,8 @@ static void ffs_epfile_dmabuf_io_complete(struct usb_ep *ep,
struct usb_request *req)
{
pr_vdebug("FFS: DMABUF transfer complete, status=%d\n", req->status);
/* req is freed by ffs_dmabuf_cleanup() under eps_lock. */
ffs_dmabuf_signal_done(req->context, req->status);
usb_ep_free_request(ep, req);
}
static const char *ffs_dmabuf_get_driver_name(struct dma_fence *fence)
@ -1699,6 +1716,10 @@ static int ffs_dmabuf_transfer(struct file *file,
usb_req->context = fence;
usb_req->complete = ffs_epfile_dmabuf_io_complete;
/* ffs_dmabuf_cleanup() frees usb_req via these two fields. */
fence->req = usb_req;
fence->ep = ep->ep;
cookie = dma_fence_begin_signalling();
ret = usb_ep_queue(ep->ep, usb_req, GFP_ATOMIC);
dma_fence_end_signalling(cookie);
@ -1708,7 +1729,6 @@ static int ffs_dmabuf_transfer(struct file *file,
} else {
pr_warn("FFS: Failed to queue DMABUF: %d\n", ret);
ffs_dmabuf_signal_done(fence, ret);
usb_ep_free_request(ep->ep, usb_req);
}
spin_unlock_irq(&epfile->ffs->eps_lock);

View File

@ -1622,7 +1622,7 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
hidg->dev.devt = MKDEV(major, opts->minor);
ret = dev_set_name(&hidg->dev, "hidg%d", opts->minor);
if (ret)
goto err_unlock;
goto err_put_device;
hidg->bInterfaceSubClass = opts->subclass;
hidg->bInterfaceProtocol = opts->protocol;
@ -1659,7 +1659,6 @@ static struct usb_function *hidg_alloc(struct usb_function_instance *fi)
err_put_device:
put_device(&hidg->dev);
err_unlock:
mutex_unlock(&opts->lock);
return ERR_PTR(ret);
}

View File

@ -768,6 +768,16 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
uvc_hs_streaming_ep.bEndpointAddress = uvc->video.ep->address;
uvc_ss_streaming_ep.bEndpointAddress = uvc->video.ep->address;
/*
* Hold opts->lock across both the XU string-descriptor fixup below and
* the descriptor-copy block further down. Without this, configfs
* uvcg_extension_drop() (which takes opts->lock) can race with the
* list_for_each_entry() walks here and inside uvc_copy_descriptors(),
* leading to a UAF on a freed struct uvcg_extension. See
* drivers/usb/gadget/function/uvc_configfs.c::uvcg_extension_drop().
*/
mutex_lock(&opts->lock);
/*
* XUs can have an arbitrary string descriptor describing them. If they
* have one pick up the ID.
@ -785,7 +795,7 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
ARRAY_SIZE(uvc_en_us_strings));
if (IS_ERR(us)) {
ret = PTR_ERR(us);
goto error;
goto error_unlock;
}
uvc_iad.iFunction = opts->iad_index ? cdev->usb_strings[opts->iad_index].id :
@ -799,14 +809,14 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
/* Allocate interface IDs. */
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
goto error_unlock;
uvc_iad.bFirstInterface = ret;
uvc_control_intf.bInterfaceNumber = ret;
uvc->control_intf = ret;
opts->control_interface = ret;
if ((ret = usb_interface_id(c, f)) < 0)
goto error;
goto error_unlock;
uvc_streaming_intf_alt0.bInterfaceNumber = ret;
uvc_streaming_intf_alt1.bInterfaceNumber = ret;
uvc->streaming_intf = ret;
@ -817,30 +827,32 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
if (IS_ERR(f->fs_descriptors)) {
ret = PTR_ERR(f->fs_descriptors);
f->fs_descriptors = NULL;
goto error;
goto error_unlock;
}
f->hs_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_HIGH);
if (IS_ERR(f->hs_descriptors)) {
ret = PTR_ERR(f->hs_descriptors);
f->hs_descriptors = NULL;
goto error;
goto error_unlock;
}
f->ss_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER);
if (IS_ERR(f->ss_descriptors)) {
ret = PTR_ERR(f->ss_descriptors);
f->ss_descriptors = NULL;
goto error;
goto error_unlock;
}
f->ssp_descriptors = uvc_copy_descriptors(uvc, USB_SPEED_SUPER_PLUS);
if (IS_ERR(f->ssp_descriptors)) {
ret = PTR_ERR(f->ssp_descriptors);
f->ssp_descriptors = NULL;
goto error;
goto error_unlock;
}
mutex_unlock(&opts->lock);
/* Preallocate control endpoint request. */
uvc->control_req = usb_ep_alloc_request(cdev->gadget->ep0, GFP_KERNEL);
uvc->control_buf = kmalloc(UVC_MAX_REQUEST_SIZE, GFP_KERNEL);
@ -872,6 +884,8 @@ uvc_function_bind(struct usb_configuration *c, struct usb_function *f)
return 0;
error_unlock:
mutex_unlock(&opts->lock);
v4l2_error:
v4l2_device_unregister(&uvc->v4l2_dev);
error:

View File

@ -2134,6 +2134,8 @@ static int dummy_hub_control(
case ClearHubFeature:
break;
case ClearPortFeature:
if (wIndex != 1)
goto error;
switch (wValue) {
case USB_PORT_FEAT_SUSPEND:
if (hcd->speed == HCD_USB3) {
@ -2248,6 +2250,8 @@ static int dummy_hub_control(
retval = -EPIPE;
break;
case SetPortFeature:
if (wIndex != 1)
goto error;
switch (wValue) {
case USB_PORT_FEAT_LINK_STATE:
if (hcd->speed != HCD_USB3) {

View File

@ -3790,10 +3790,8 @@ static int net2280_probe(struct pci_dev *pdev, const struct pci_device_id *id)
return 0;
done:
if (dev) {
if (dev)
net2280_remove(pdev);
kfree(dev);
}
return retval;
}

View File

@ -247,6 +247,7 @@ struct tegra_xusb_soc {
bool has_ipfs;
bool lpm_support;
bool otg_reset_sspi;
bool otg_set_port_power;
bool has_bar2;
};
@ -1352,12 +1353,13 @@ static void tegra_xhci_id_work(struct work_struct *work)
struct tegra_xusb_mbox_msg msg;
struct phy *phy = tegra_xusb_get_phy(tegra, "usb2",
tegra->otg_usb2_port);
bool host_mode = tegra->host_mode;
u32 status;
int ret;
dev_dbg(tegra->dev, "host mode %s\n", str_on_off(tegra->host_mode));
dev_dbg(tegra->dev, "host mode %s\n", str_on_off(host_mode));
if (tegra->host_mode)
if (host_mode)
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_HOST);
else
phy_set_mode_ext(phy, PHY_MODE_USB_OTG, USB_ROLE_NONE);
@ -1366,41 +1368,43 @@ static void tegra_xhci_id_work(struct work_struct *work)
tegra->otg_usb2_port);
pm_runtime_get_sync(tegra->dev);
if (tegra->host_mode) {
/* switch to host mode */
if (tegra->otg_usb3_port >= 0) {
if (tegra->soc->otg_reset_sspi) {
/* set PP=0 */
tegra_xhci_hc_driver.hub_control(
xhci->shared_hcd, GetPortStatus,
0, tegra->otg_usb3_port+1,
(char *) &status, sizeof(status));
if (status & USB_SS_PORT_STAT_POWER)
tegra_xhci_set_port_power(tegra, false,
false);
if (tegra->soc->otg_set_port_power) {
if (host_mode) {
/* switch to host mode */
if (tegra->otg_usb3_port >= 0) {
if (tegra->soc->otg_reset_sspi) {
/* set PP=0 */
tegra_xhci_hc_driver.hub_control(
xhci->shared_hcd, GetPortStatus,
0, tegra->otg_usb3_port+1,
(char *) &status, sizeof(status));
if (status & USB_SS_PORT_STAT_POWER)
tegra_xhci_set_port_power(tegra, false,
false);
/* reset OTG port SSPI */
msg.cmd = MBOX_CMD_RESET_SSPI;
msg.data = tegra->otg_usb3_port+1;
/* reset OTG port SSPI */
msg.cmd = MBOX_CMD_RESET_SSPI;
msg.data = tegra->otg_usb3_port+1;
ret = tegra_xusb_mbox_send(tegra, &msg);
if (ret < 0) {
dev_info(tegra->dev,
"failed to RESET_SSPI %d\n",
ret);
ret = tegra_xusb_mbox_send(tegra, &msg);
if (ret < 0) {
dev_info(tegra->dev,
"failed to RESET_SSPI %d\n",
ret);
}
}
tegra_xhci_set_port_power(tegra, false, true);
}
tegra_xhci_set_port_power(tegra, false, true);
tegra_xhci_set_port_power(tegra, true, true);
} else {
if (tegra->otg_usb3_port >= 0)
tegra_xhci_set_port_power(tegra, false, false);
tegra_xhci_set_port_power(tegra, true, false);
}
tegra_xhci_set_port_power(tegra, true, true);
} else {
if (tegra->otg_usb3_port >= 0)
tegra_xhci_set_port_power(tegra, false, false);
tegra_xhci_set_port_power(tegra, true, false);
}
pm_runtime_put_autosuspend(tegra->dev);
}
@ -2553,6 +2557,7 @@ static const struct tegra_xusb_soc tegra124_soc = {
.scale_ss_clock = true,
.has_ipfs = true,
.otg_reset_sspi = false,
.otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@ -2593,6 +2598,7 @@ static const struct tegra_xusb_soc tegra210_soc = {
.scale_ss_clock = false,
.has_ipfs = true,
.otg_reset_sspi = true,
.otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@ -2640,6 +2646,7 @@ static const struct tegra_xusb_soc tegra186_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
.otg_set_port_power = true,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0xe4,
@ -2673,6 +2680,7 @@ static const struct tegra_xusb_soc tegra194_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
.otg_set_port_power = false,
.ops = &tegra124_ops,
.mbox = {
.cmd = 0x68,
@ -2708,6 +2716,7 @@ static const struct tegra_xusb_soc tegra234_soc = {
.scale_ss_clock = false,
.has_ipfs = false,
.otg_reset_sspi = false,
.otg_set_port_power = false,
.ops = &tegra234_ops,
.mbox = {
.cmd = XUSB_BAR2_ARU_MBOX_CMD,

View File

@ -337,7 +337,6 @@ static int omap2430_probe(struct platform_device *pdev)
} else {
device_set_of_node_from_dev(&musb->dev, &pdev->dev);
}
of_node_put(np);
glue->dev = &pdev->dev;
glue->musb = musb;
@ -455,6 +454,7 @@ static int omap2430_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "failed to register musb device\n");
goto err_disable_rpm;
}
of_node_put(np);
return 0;
@ -464,6 +464,7 @@ static int omap2430_probe(struct platform_device *pdev)
if (!IS_ERR(glue->control_otghs))
put_device(glue->control_otghs);
err_put_musb:
of_node_put(np);
platform_device_put(musb);
return ret;

View File

@ -194,6 +194,9 @@ static void belkin_sa_read_int_callback(struct urb *urb)
usb_serial_debug_data(&port->dev, __func__, urb->actual_length, data);
if (urb->actual_length < BELKIN_SA_MSR_INDEX + 1)
goto exit;
/* Handle known interrupt data */
/* ignore data[0] and data[1] */

View File

@ -445,6 +445,14 @@ static int cypress_generic_port_probe(struct usb_serial_port *port)
return -ENODEV;
}
/*
* The buffer must be large enough for the one or two-byte header (and
* following data), but assume anything smaller than eight bytes is
* broken.
*/
if (port->interrupt_out_size < 8)
return -EINVAL;
priv = kzalloc_obj(struct cypress_private);
if (!priv)
return -ENOMEM;
@ -1017,8 +1025,8 @@ static void cypress_read_int_callback(struct urb *urb)
char tty_flag = TTY_NORMAL;
int bytes = 0;
int result;
int i = 0;
int status = urb->status;
int i;
switch (status) {
case 0: /* success */
@ -1056,22 +1064,32 @@ static void cypress_read_int_callback(struct urb *urb)
spin_lock_irqsave(&priv->lock, flags);
result = urb->actual_length;
i = 0;
switch (priv->pkt_fmt) {
default:
case packet_format_1:
/* This is for the CY7C64013... */
if (result < 2)
break;
priv->current_status = data[0] & 0xF8;
bytes = data[1] + 2;
i = 2;
break;
case packet_format_2:
/* This is for the CY7C63743... */
if (result < 1)
break;
priv->current_status = data[0] & 0xF8;
bytes = (data[0] & 0x07) + 1;
i = 1;
break;
}
spin_unlock_irqrestore(&priv->lock, flags);
if (i == 0) {
dev_dbg(dev, "%s - short packet received: %d bytes\n",
__func__, result);
goto continue_read;
}
if (result < bytes) {
dev_dbg(dev,
"%s - wrong packet size - received %d bytes but packet said %d bytes\n",

View File

@ -1229,15 +1229,34 @@ static int digi_port_init(struct usb_serial_port *port, unsigned port_num)
static int digi_startup(struct usb_serial *serial)
{
struct digi_serial *serial_priv;
int oob_port_num;
int ret;
int i;
/*
* The port bulk-out buffers must be large enough for header and
* buffered data.
*/
for (i = 0; i < serial->type->num_ports; i++) {
if (serial->port[i]->bulk_out_size < DIGI_OUT_BUF_SIZE + 2)
return -EINVAL;
}
/*
* The OOB port bulk-out buffer must be large enough for the two
* commands in digi_set_modem_signals().
*/
oob_port_num = serial->type->num_ports;
if (serial->port[oob_port_num]->bulk_out_size < 8)
return -EINVAL;
serial_priv = kzalloc_obj(*serial_priv);
if (!serial_priv)
return -ENOMEM;
spin_lock_init(&serial_priv->ds_serial_lock);
serial_priv->ds_oob_port_num = serial->type->num_ports;
serial_priv->ds_oob_port = serial->port[serial_priv->ds_oob_port_num];
serial_priv->ds_oob_port_num = oob_port_num;
serial_priv->ds_oob_port = serial->port[oob_port_num];
ret = digi_port_init(serial_priv->ds_oob_port,
serial_priv->ds_oob_port_num);

View File

@ -1187,6 +1187,10 @@ static void usa49wg_indat_callback(struct urb *urb)
len = 0;
while (i < urb->actual_length) {
if (urb->actual_length - i < 3) {
dev_warn_ratelimited(&urb->dev->dev, "malformed indat packet\n");
break;
}
/* Check port number from message */
if (data[i] >= serial->num_ports) {

View File

@ -378,6 +378,7 @@ static int mct_u232_port_probe(struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv;
u16 pid;
/* check first to simplify error handling */
if (!serial->port[1] || !serial->port[1]->interrupt_in_urb) {
@ -385,6 +386,16 @@ static int mct_u232_port_probe(struct usb_serial_port *port)
return -ENODEV;
}
/*
* Compensate for a hardware bug: although the Sitecom U232-P25
* device reports a maximum output packet size of 32 bytes,
* it seems to be able to accept only 16 bytes (and that's what
* SniffUSB says too...)
*/
pid = le16_to_cpu(serial->dev->descriptor.idProduct);
if (pid == MCT_U232_SITECOM_PID)
port->bulk_out_size = min(16, port->bulk_out_size);
priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
@ -410,7 +421,6 @@ static void mct_u232_port_remove(struct usb_serial_port *port)
static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
{
struct usb_serial *serial = port->serial;
struct mct_u232_private *priv = usb_get_serial_port_data(port);
int retval = 0;
unsigned int control_state;
@ -418,15 +428,6 @@ static int mct_u232_open(struct tty_struct *tty, struct usb_serial_port *port)
unsigned char last_lcr;
unsigned char last_msr;
/* Compensate for a hardware bug: although the Sitecom U232-P25
* device reports a maximum output packet size of 32 bytes,
* it seems to be able to accept only 16 bytes (and that's what
* SniffUSB says too...)
*/
if (le16_to_cpu(serial->dev->descriptor.idProduct)
== MCT_U232_SITECOM_PID)
port->bulk_out_size = 16;
/* Do a defined restart: the normal serial device seems to
* always turn on DTR and RTS here, so do the same. I'm not
* sure if this is really necessary. But it should not harm
@ -543,6 +544,11 @@ static void mct_u232_read_int_callback(struct urb *urb)
goto exit;
}
if (urb->actual_length < 2) {
dev_warn_ratelimited(&port->dev, "short interrupt-in packet\n");
goto exit;
}
/*
* The interrupt-in pipe signals exceptional conditions (modem line
* signal changes and errors). data[0] holds MSR, data[1] holds LSR.

View File

@ -962,6 +962,14 @@ static int mxuport_calc_num_ports(struct usb_serial *serial,
*/
BUILD_BUG_ON(ARRAY_SIZE(epds->bulk_out) < 16);
/*
* The bulk-out buffers must be large enough for the four-byte header
* (and following data), but assume anything smaller than eight bytes
* is broken.
*/
if (usb_endpoint_maxp(epds->bulk_out[0]) < 8)
return -EINVAL;
for (i = 1; i < num_ports; ++i)
epds->bulk_out[i] = epds->bulk_out[0];

View File

@ -30,6 +30,10 @@
/* This one seems to be a re-branded ZyXEL device */
#define BT_IGNITIONPRO_ID 0x2000
#define OMNINET_HEADERLEN 4
#define OMNINET_BULKOUTSIZE 64
#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
/* function prototypes */
static void omninet_process_read_urb(struct urb *urb);
static int omninet_prepare_write_buffer(struct usb_serial_port *port,
@ -54,6 +58,7 @@ static struct usb_serial_driver zyxel_omninet_device = {
.description = "ZyXEL - omni.net usb",
.id_table = id_table,
.num_bulk_out = 2,
.bulk_out_size = OMNINET_BULKOUTSIZE,
.calc_num_ports = omninet_calc_num_ports,
.port_probe = omninet_port_probe,
.port_remove = omninet_port_remove,
@ -130,10 +135,6 @@ static void omninet_port_remove(struct usb_serial_port *port)
kfree(od);
}
#define OMNINET_HEADERLEN 4
#define OMNINET_BULKOUTSIZE 64
#define OMNINET_PAYLOADSIZE (OMNINET_BULKOUTSIZE - OMNINET_HEADERLEN)
static void omninet_process_read_urb(struct urb *urb)
{
struct usb_serial_port *port = urb->context;

View File

@ -2450,6 +2450,12 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x30) }, /* MeiG Smart SRM825WN (Diag) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x40) }, /* MeiG Smart SRM825WN (AT) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d38, 0xff, 0xff, 0x60) }, /* MeiG Smart SRM825WN (NMEA) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d63, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x30) }, /* MeiG SRM813Q (Diag) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x40) }, /* MeiG SRM813Q (AT) */
{ USB_DEVICE_AND_INTERFACE_INFO(0x2dee, 0x4d64, 0xff, 0xff, 0x60) }, /* MeiG SRM813Q (NMEA) */
{ USB_DEVICE_INTERFACE_CLASS(0x2df3, 0x9d03, 0xff) }, /* LongSung M5710 */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1404, 0xff) }, /* GosunCn GM500 RNDIS */
{ USB_DEVICE_INTERFACE_CLASS(0x305a, 0x1405, 0xff) }, /* GosunCn GM500 MBIM */
@ -2470,7 +2476,8 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0302, 0xff) }, /* Rolling RW101R-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x0802, 0xff), /* Rolling RW350-GL (laptop MBIM) */
.driver_info = RSVD(5) },
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff) }, /* Rolling RW135R-GL (laptop MBIM) */
{ USB_DEVICE_INTERFACE_CLASS(0x33f8, 0x1003, 0xff), /* Rolling RW135R-GL (laptop MBIM) */
.driver_info = RSVD(5) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x30) }, /* NetPrisma LCUK54-WWD for Global */
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0x00, 0x40) },
{ USB_DEVICE_AND_INTERFACE_INFO(0x3731, 0x0100, 0xff, 0xff, 0x40) },

View File

@ -259,6 +259,7 @@ static int safe_prepare_write_buffer(struct usb_serial_port *port,
static int safe_startup(struct usb_serial *serial)
{
struct usb_interface_descriptor *desc;
int bulk_out_size;
if (serial->dev->descriptor.bDeviceClass != CDC_DEVICE_CLASS)
return -ENODEV;
@ -279,6 +280,16 @@ static int safe_startup(struct usb_serial *serial)
default:
return -EINVAL;
}
/*
* The bulk-out buffer needs to be large enough for the two-byte
* trailer in safe mode, but assume anything smaller than eight bytes
* is broken.
*/
bulk_out_size = serial->port[0]->bulk_out_size;
if (bulk_out_size > 0 && bulk_out_size < 8)
return -EINVAL;
return 0;
}

View File

@ -132,6 +132,13 @@ UNUSUAL_DEV(0x152d, 0x0583, 0x0000, 0x9999,
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_REPORT_OPCODES),
/* Reported-by: Sam Burkels <sam@1a38.nl> */
UNUSUAL_DEV(0x154b, 0xf009, 0x0000, 0x9999,
"PNY",
"PNY ELITE PSSD",
USB_SC_DEVICE, USB_PR_DEVICE, NULL,
US_FL_NO_ATA_1X | US_FL_NO_REPORT_OPCODES),
/* Reported-by: Thinh Nguyen <thinhn@synopsys.com> */
UNUSUAL_DEV(0x154b, 0xf00b, 0x0000, 0x9999,
"PNY",

View File

@ -405,6 +405,8 @@ static int dp_altmode_vdm(struct typec_altmode *alt,
dp->state = DP_STATE_EXIT_PRIME;
break;
case DP_CMD_STATUS_UPDATE:
if (count < 2)
break;
dp->data.status = *vdo;
ret = dp_altmode_status_update(dp);
break;

View File

@ -1751,19 +1751,22 @@ static int fusb302_probe(struct i2c_client *client)
bridge_dev = devm_drm_dp_hpd_bridge_alloc(chip->dev, to_of_node(chip->tcpc_dev.fwnode));
if (IS_ERR(bridge_dev)) {
ret = PTR_ERR(bridge_dev);
dev_err_probe(chip->dev, ret, "failed to alloc bridge\n");
goto destroy_workqueue;
ret = dev_err_probe(chip->dev, PTR_ERR(bridge_dev),
"failed to alloc bridge\n");
goto fwnode_put;
}
chip->tcpm_port = tcpm_register_port(&client->dev, &chip->tcpc_dev);
if (IS_ERR(chip->tcpm_port)) {
fwnode_handle_put(chip->tcpc_dev.fwnode);
ret = dev_err_probe(dev, PTR_ERR(chip->tcpm_port),
"cannot register tcpm port\n");
goto destroy_workqueue;
goto fwnode_put;
}
ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev);
if (ret)
goto tcpm_unregister_port;
ret = request_threaded_irq(chip->gpio_int_n_irq, NULL, fusb302_irq_intn,
IRQF_ONESHOT | IRQF_TRIGGER_LOW,
"fsc_interrupt_int_n", chip);
@ -1774,14 +1777,11 @@ static int fusb302_probe(struct i2c_client *client)
enable_irq_wake(chip->gpio_int_n_irq);
i2c_set_clientdata(client, chip);
ret = devm_drm_dp_hpd_bridge_add(chip->dev, bridge_dev);
if (ret)
return ret;
return ret;
return 0;
tcpm_unregister_port:
tcpm_unregister_port(chip->tcpm_port);
fwnode_put:
fwnode_handle_put(chip->tcpc_dev.fwnode);
destroy_workqueue:
fusb302_debugfs_exit(chip);

View File

@ -181,6 +181,15 @@ static void process_rx(struct max_tcpci_chip *chip, u16 status)
rx_buf_ptr = rx_buf + TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET;
msg.header = cpu_to_le16(*(u16 *)rx_buf_ptr);
rx_buf_ptr = rx_buf_ptr + sizeof(msg.header);
if (count < TCPC_RECEIVE_BUFFER_RX_BYTE_BUF_OFFSET + sizeof(msg.header) +
pd_header_cnt_le(msg.header) * sizeof(msg.payload[0])) {
max_tcpci_write16(chip, TCPC_ALERT, TCPC_ALERT_RX_STATUS);
dev_err(chip->dev, "Invalid TCPC_RX_BYTE_CNT %d for header cnt %d\n",
count, pd_header_cnt_le(msg.header));
return;
}
for (payload_index = 0; payload_index < pd_header_cnt_le(msg.header); payload_index++,
rx_buf_ptr += sizeof(msg.payload[0]))
msg.payload[payload_index] = cpu_to_le32(*(u32 *)rx_buf_ptr);

View File

@ -1855,6 +1855,9 @@ static void svdm_consume_identity(struct tcpm_port *port, const u32 *p, int cnt)
u32 vdo = p[VDO_INDEX_IDH];
u32 product = p[VDO_INDEX_PRODUCT];
if (cnt <= VDO_INDEX_PRODUCT)
return;
memset(&port->mode_data, 0, sizeof(port->mode_data));
port->partner_ident.id_header = vdo;
@ -1875,6 +1878,9 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
u32 product = p[VDO_INDEX_PRODUCT];
int svdm_version;
if (cnt <= VDO_INDEX_CABLE_1)
return;
/*
* Attempt to consume identity only if cable currently is not set
*/
@ -1898,7 +1904,7 @@ static void svdm_consume_identity_sop_prime(struct tcpm_port *port, const u32 *p
switch (port->negotiated_rev_prime) {
case PD_REV30:
port->cable_desc.pd_revision = 0x0300;
if (port->cable_desc.active)
if (port->cable_desc.active && cnt > VDO_INDEX_CABLE_2)
port->cable_ident.vdo[1] = p[VDO_INDEX_CABLE_2];
break;
case PD_REV20:
@ -1986,23 +1992,19 @@ static void svdm_consume_modes(struct tcpm_port *port, const u32 *p, int cnt,
switch (rx_sop_type) {
case TCPC_TX_SOP_PRIME:
pmdata = &port->mode_data_prime;
if (pmdata->altmodes >= ARRAY_SIZE(port->plug_prime_altmode)) {
/* Already logged in svdm_consume_svids() */
return;
}
break;
case TCPC_TX_SOP:
pmdata = &port->mode_data;
if (pmdata->altmodes >= ARRAY_SIZE(port->partner_altmode)) {
/* Already logged in svdm_consume_svids() */
return;
}
break;
default:
return;
}
for (i = 1; i < cnt; i++) {
if (pmdata->altmodes >= ALTMODE_DISCOVERY_MAX) {
/* Already logged in svdm_consume_svids() */
return;
}
paltmode = &pmdata->altmode_desc[pmdata->altmodes];
memset(paltmode, 0, sizeof(*paltmode));
@ -2147,6 +2149,55 @@ static bool tcpm_cable_vdm_supported(struct tcpm_port *port)
tcpm_can_communicate_sop_prime(port);
}
static int tcpm_handle_discover_mode(struct tcpm_port *port, u32 *response,
enum tcpm_transmit_type rx_sop_type,
enum tcpm_transmit_type *response_tx_sop_type)
{
struct typec_port *typec = port->typec_port;
struct pd_mode_data *modep;
if (rx_sop_type == TCPC_TX_SOP) {
modep = &port->mode_data;
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
*response_tx_sop_type = TCPC_TX_SOP;
response[0] = VDO(svid, 1,
typec_get_negotiated_svdm_version(typec),
CMD_DISCOVER_MODES);
return 1;
}
if (tcpm_cable_vdm_supported(port)) {
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
response[0] = VDO(USB_SID_PD, 1,
typec_get_cable_svdm_version(typec),
CMD_DISCOVER_SVID);
return 1;
}
tcpm_register_partner_altmodes(port);
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
modep = &port->mode_data_prime;
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
response[0] = VDO(svid, 1,
typec_get_cable_svdm_version(typec),
CMD_DISCOVER_MODES);
return 1;
}
tcpm_register_plug_altmodes(port);
tcpm_register_partner_altmodes(port);
}
return 0;
}
static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
const u32 *p, int cnt, u32 *response,
enum adev_actions *adev_action,
@ -2404,41 +2455,11 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
}
break;
case CMD_DISCOVER_MODES:
if (rx_sop_type == TCPC_TX_SOP) {
/* 6.4.4.3.3 */
svdm_consume_modes(port, p, cnt, rx_sop_type);
modep->svid_index++;
if (modep->svid_index < modep->nsvids) {
u16 svid = modep->svids[modep->svid_index];
*response_tx_sop_type = TCPC_TX_SOP;
response[0] = VDO(svid, 1, svdm_version,
CMD_DISCOVER_MODES);
rlen = 1;
} else if (tcpm_cable_vdm_supported(port)) {
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
response[0] = VDO(USB_SID_PD, 1,
typec_get_cable_svdm_version(typec),
CMD_DISCOVER_SVID);
rlen = 1;
} else {
tcpm_register_partner_altmodes(port);
}
} else if (rx_sop_type == TCPC_TX_SOP_PRIME) {
/* 6.4.4.3.3 */
svdm_consume_modes(port, p, cnt, rx_sop_type);
modep_prime->svid_index++;
if (modep_prime->svid_index < modep_prime->nsvids) {
u16 svid = modep_prime->svids[modep_prime->svid_index];
*response_tx_sop_type = TCPC_TX_SOP_PRIME;
response[0] = VDO(svid, 1,
typec_get_cable_svdm_version(typec),
CMD_DISCOVER_MODES);
rlen = 1;
} else {
tcpm_register_plug_altmodes(port);
tcpm_register_partner_altmodes(port);
}
}
/* 6.4.4.3.3 */
svdm_consume_modes(port, p, cnt, rx_sop_type);
rlen = tcpm_handle_discover_mode(port, response,
rx_sop_type,
response_tx_sop_type);
break;
case CMD_ENTER_MODE:
*response_tx_sop_type = rx_sop_type;
@ -2481,9 +2502,15 @@ static int tcpm_pd_svdm(struct tcpm_port *port, struct typec_altmode *adev,
switch (cmd) {
case CMD_DISCOVER_IDENT:
case CMD_DISCOVER_SVID:
case CMD_DISCOVER_MODES:
case VDO_CMD_VENDOR(0) ... VDO_CMD_VENDOR(15):
break;
case CMD_DISCOVER_MODES:
tcpm_log(port, "Skip SVID 0x%04x (failed to discover mode)",
PD_VDO_SVID_SVID0(p[0]));
rlen = tcpm_handle_discover_mode(port, response,
rx_sop_type,
response_tx_sop_type);
break;
case CMD_ENTER_MODE:
/* Back to USB Operation */
*adev_action = ADEV_NOTIFY_USB_AND_QUEUE_VDM;

View File

@ -444,9 +444,11 @@ static int wcove_start_toggling(struct tcpc_dev *tcpc,
return regmap_write(wcove->regmap, USBC_CONTROL1, usbc_ctrl);
}
static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
static int wcove_read_rx_buffer(struct wcove_typec *wcove,
struct pd_message *msg)
{
unsigned int info;
unsigned int info, val, len;
u8 *buf = (u8 *)msg;
int ret;
int i;
@ -454,12 +456,13 @@ static int wcove_read_rx_buffer(struct wcove_typec *wcove, void *msg)
if (ret)
return ret;
/* FIXME: Check that USBC_RXINFO_RXBYTES(info) matches the header */
len = min(USBC_RXINFO_RXBYTES(info), sizeof(*msg));
for (i = 0; i < USBC_RXINFO_RXBYTES(info); i++) {
ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, msg + i);
for (i = 0; i < len; i++) {
ret = regmap_read(wcove->regmap, USBC_RX_DATA + i, &val);
if (ret)
return ret;
buf[i] = val;
}
return regmap_write(wcove->regmap, USBC_RXSTATUS,

View File

@ -1835,6 +1835,7 @@ static int tps6598x_probe(struct i2c_client *client)
goto err_role_put;
if (status & TPS_STATUS_PLUG_PRESENT) {
ret = -EINVAL;
if (!tps6598x_read_power_status(tps))
goto err_unregister_port;
if (!tps->data->read_data_status(tps))

View File

@ -240,6 +240,10 @@ static int ucsi_displayport_vdm(struct typec_altmode *alt,
dp->header |= VDO_CMDT(CMDT_RSP_ACK);
break;
case DP_CMD_CONFIGURE:
if (count < 2) {
dp->header |= VDO_CMDT(CMDT_RSP_NAK);
break;
}
dp->data.conf = *data;
if (ucsi_displayport_configure(dp)) {
dp->header |= VDO_CMDT(CMDT_RSP_NAK);

View File

@ -1277,7 +1277,7 @@ static void ucsi_handle_connector_change(struct work_struct *work)
work);
struct ucsi *ucsi = con->ucsi;
u8 curr_scale, volt_scale;
enum typec_role role;
enum typec_role role, prev_role;
u16 change;
int ret;
u32 val;
@ -1288,6 +1288,8 @@ static void ucsi_handle_connector_change(struct work_struct *work)
dev_err_once(ucsi->dev, "%s entered without EVENT_PENDING\n",
__func__);
prev_role = UCSI_CONSTAT(con, PWR_DIR);
ret = ucsi_get_connector_status(con, true);
if (ret) {
dev_err(ucsi->dev, "%s: GET_CONNECTOR_STATUS failed (%d)\n",
@ -1304,9 +1306,14 @@ static void ucsi_handle_connector_change(struct work_struct *work)
change = UCSI_CONSTAT(con, CHANGE);
role = UCSI_CONSTAT(con, PWR_DIR);
if (change & UCSI_CONSTAT_POWER_DIR_CHANGE) {
if ((change & UCSI_CONSTAT_POWER_DIR_CHANGE) && role != prev_role) {
typec_set_pwr_role(con->port, role);
ucsi_port_psy_changed(con);
/* Some power_supply properties vary depending on the power direction when
* connected
*/
if (UCSI_CONSTAT(con, CONNECTED))
ucsi_port_psy_changed(con);
/* Complete pending power role swap */
if (!completion_done(&con->complete))
@ -1380,13 +1387,22 @@ static void ucsi_handle_connector_change(struct work_struct *work)
*/
void ucsi_connector_change(struct ucsi *ucsi, u8 num)
{
struct ucsi_connector *con = &ucsi->connector[num - 1];
struct ucsi_connector *con;
if (!(ucsi->ntfy & UCSI_ENABLE_NTFY_CONNECTOR_CHANGE)) {
dev_dbg(ucsi->dev, "Early connector change event\n");
return;
}
if (!num || num > ucsi->cap.num_connectors) {
dev_warn_ratelimited(ucsi->dev,
"Bogus connector change on %u (max %u)\n",
num, ucsi->cap.num_connectors);
return;
}
con = &ucsi->connector[num - 1];
if (!test_and_set_bit(EVENT_PENDING, &ucsi->flags))
schedule_work(&con->work);
}

View File

@ -1243,6 +1243,11 @@ static int do_flash(struct ucsi_ccg *uc, enum enum_flash_mode mode)
*****************************************************************/
p = strnchr(fw->data, fw->size, ':');
if (!p) {
dev_err(dev, "Bad FW format: no ':' record header found\n");
err = -EINVAL;
goto release_mem;
}
while (p < eof) {
s = strnchr(p + 1, eof - p - 1, ':');

View File

@ -632,6 +632,7 @@ void vudc_remove(struct platform_device *pdev)
{
struct vudc *udc = platform_get_drvdata(pdev);
v_stop_timer(udc);
usb_del_gadget_udc(&udc->gadget);
cleanup_vudc_hw(udc);
kfree(udc);

View File

@ -490,7 +490,8 @@ void v_stop_timer(struct vudc *udc)
{
struct transfer_timer *t = &udc->tr_timer;
/* timer itself will take care of stopping */
/* Delete the timer synchronously before teardown frees udc. */
dev_dbg(&udc->pdev->dev, "timer stop");
timer_delete_sync(&t->timer);
t->state = VUDC_TR_STOPPED;
}