mirror of
https://github.com/linux-msm/laptops-kernel.git
synced 2026-08-13 14:19:53 -07:00
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:
@@ -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";
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
+19
-27
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -2172,7 +2172,10 @@ unknown:
|
||||
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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user