mirror of
https://github.com/torvalds/linux.git
synced 2026-06-02 19:43:40 +02:00
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:
commit
670b77dfeb
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ 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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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] */
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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];
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) },
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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, ':');
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user