From a86c309e71dc4f43c68483f7e328b1d4f9fef618 Mon Sep 17 00:00:00 2001 From: Mats Karrman Date: Tue, 25 Apr 2017 23:49:47 +0200 Subject: usb: typec: Don't prevent using constant typec_mode_desc initializers In some situations, e.g. when registering alternate modes for local typec ports, it may be handy to use constant mode descriptors. Allow this by changing the mode descriptor arguments of typec_port_register_altmode() et.al. to using const pointers. Signed-off-by: Mats Karrman Reviewed-by: Guenter Roeck Acked-by: Heikki Krogerus Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/typec.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index ec78204964ab..d1d2ebcf36ec 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -117,13 +117,13 @@ struct typec_altmode_desc { struct typec_altmode *typec_partner_register_altmode(struct typec_partner *partner, - struct typec_altmode_desc *desc); + const struct typec_altmode_desc *desc); struct typec_altmode *typec_plug_register_altmode(struct typec_plug *plug, - struct typec_altmode_desc *desc); + const struct typec_altmode_desc *desc); struct typec_altmode *typec_port_register_altmode(struct typec_port *port, - struct typec_altmode_desc *desc); + const struct typec_altmode_desc *desc); void typec_unregister_altmode(struct typec_altmode *altmode); struct typec_port *typec_altmode2port(struct typec_altmode *alt); -- cgit v1.2.3 From 7d21114dc6a2d53babef43a84a8d8db2905d283d Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Fri, 5 May 2017 14:12:24 +0800 Subject: usb: phy: Introduce one extcon device into usb phy Usually usb phy need register one extcon device to get the connection notifications. It will remove some duplicate code if the extcon device is registered using common code instead of each phy driver having its own related extcon APIs. So we add one pointer of extcon device into usb phy structure, and some other helper functions to register extcon. Signed-off-by: Baolin Wang Signed-off-by: Felipe Balbi --- drivers/usb/phy/Kconfig | 6 +++--- drivers/usb/phy/phy.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/usb/phy.h | 7 ++++++ 3 files changed, 67 insertions(+), 3 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/phy/Kconfig b/drivers/usb/phy/Kconfig index 3006f569c068..aff702c0eb9f 100644 --- a/drivers/usb/phy/Kconfig +++ b/drivers/usb/phy/Kconfig @@ -4,6 +4,7 @@ menu "USB Physical Layer drivers" config USB_PHY + select EXTCON def_bool n # @@ -109,7 +110,7 @@ config OMAP_OTG config TAHVO_USB tristate "Tahvo USB transceiver driver" - depends on MFD_RETU && EXTCON + depends on MFD_RETU depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' select USB_PHY help @@ -141,7 +142,6 @@ config USB_MSM_OTG depends on (USB || USB_GADGET) && (ARCH_QCOM || COMPILE_TEST) depends on USB_GADGET || !USB_GADGET # if USB_GADGET=m, this can't be 'y' depends on RESET_CONTROLLER - depends on EXTCON select USB_PHY help Enable this to support the USB OTG transceiver on Qualcomm chips. It @@ -155,7 +155,7 @@ config USB_MSM_OTG config USB_QCOM_8X16_PHY tristate "Qualcomm APQ8016/MSM8916 on-chip USB PHY controller support" depends on ARCH_QCOM || COMPILE_TEST - depends on RESET_CONTROLLER && EXTCON + depends on RESET_CONTROLLER select USB_PHY select USB_ULPI_VIEWPORT help diff --git a/drivers/usb/phy/phy.c b/drivers/usb/phy/phy.c index 98f75d2842b7..032f5afaad4b 100644 --- a/drivers/usb/phy/phy.c +++ b/drivers/usb/phy/phy.c @@ -100,6 +100,54 @@ static int devm_usb_phy_match(struct device *dev, void *res, void *match_data) return *phy == match_data; } +static int usb_add_extcon(struct usb_phy *x) +{ + int ret; + + if (of_property_read_bool(x->dev->of_node, "extcon")) { + x->edev = extcon_get_edev_by_phandle(x->dev, 0); + if (IS_ERR(x->edev)) + return PTR_ERR(x->edev); + + x->id_edev = extcon_get_edev_by_phandle(x->dev, 1); + if (IS_ERR(x->id_edev)) { + x->id_edev = NULL; + dev_info(x->dev, "No separate ID extcon device\n"); + } + + if (x->vbus_nb.notifier_call) { + ret = devm_extcon_register_notifier(x->dev, x->edev, + EXTCON_USB, + &x->vbus_nb); + if (ret < 0) { + dev_err(x->dev, + "register VBUS notifier failed\n"); + return ret; + } + } + + if (x->id_nb.notifier_call) { + struct extcon_dev *id_ext; + + if (x->id_edev) + id_ext = x->id_edev; + else + id_ext = x->edev; + + ret = devm_extcon_register_notifier(x->dev, id_ext, + EXTCON_USB_HOST, + &x->id_nb); + if (ret < 0) { + dev_err(x->dev, + "register ID notifier failed\n"); + return ret; + } + } + } + + return 0; +} + /** * devm_usb_get_phy - find the USB PHY * @dev - device that requests this phy @@ -388,6 +436,10 @@ int usb_add_phy(struct usb_phy *x, enum usb_phy_type type) return -EINVAL; } + ret = usb_add_extcon(x); + if (ret) + return ret; + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); spin_lock_irqsave(&phy_lock, flags); @@ -422,12 +474,17 @@ int usb_add_phy_dev(struct usb_phy *x) { struct usb_phy_bind *phy_bind; unsigned long flags; + int ret; if (!x->dev) { dev_err(x->dev, "no device provided for PHY\n"); return -EINVAL; } + ret = usb_add_extcon(x); + if (ret) + return ret; + ATOMIC_INIT_NOTIFIER_HEAD(&x->notifier); spin_lock_irqsave(&phy_lock, flags); diff --git a/include/linux/usb/phy.h b/include/linux/usb/phy.h index 31a8068c42a5..299245105610 100644 --- a/include/linux/usb/phy.h +++ b/include/linux/usb/phy.h @@ -9,6 +9,7 @@ #ifndef __LINUX_USB_PHY_H #define __LINUX_USB_PHY_H +#include #include #include @@ -85,6 +86,12 @@ struct usb_phy { struct usb_phy_io_ops *io_ops; void __iomem *io_priv; + /* to support extcon device */ + struct extcon_dev *edev; + struct extcon_dev *id_edev; + struct notifier_block vbus_nb; + struct notifier_block id_nb; + /* for notification of usb_phy_events */ struct atomic_notifier_head notifier; -- cgit v1.2.3 From a9548c55295a4268f9187e1ec93264a0682fa745 Mon Sep 17 00:00:00 2001 From: John Youn Date: Fri, 28 Apr 2017 12:55:20 +0400 Subject: usb: gadget: Allow a non-SuperSpeed gadget to support LPM This commit allows a gadget that does not support SuperSpeed to indicate that it supports LPM. It does this by setting the 'lpm_capable' flag in the gadget structure. If a gadget sets this, the composite gadget framework will set the bcdUSB to 0x0201 to indicate that this supports BOS descriptors, and also return a USB 2.0 Extension descriptor as part of the BOS descriptor set. See USB 2.0 LPM ECN Section 3. Signed-off-by: John Youn Signed-off-by: Sevak Arakelyan Signed-off-by: Felipe Balbi --- drivers/usb/gadget/composite.c | 8 ++++++-- include/linux/usb/gadget.h | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/composite.c b/drivers/usb/gadget/composite.c index abec93ab81ee..d62f53d7f418 100644 --- a/drivers/usb/gadget/composite.c +++ b/drivers/usb/gadget/composite.c @@ -1608,7 +1608,10 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) cdev->desc.bcdUSB = cpu_to_le16(0x0210); } } else { - cdev->desc.bcdUSB = cpu_to_le16(0x0200); + if (gadget->lpm_capable) + cdev->desc.bcdUSB = cpu_to_le16(0x0201); + else + cdev->desc.bcdUSB = cpu_to_le16(0x0200); } value = min(w_length, (u16) sizeof cdev->desc); @@ -1639,7 +1642,8 @@ composite_setup(struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) value = min(w_length, (u16) value); break; case USB_DT_BOS: - if (gadget_is_superspeed(gadget)) { + if (gadget_is_superspeed(gadget) || + gadget->lpm_capable) { value = bos_desc(cdev); value = min(w_length, (u16) value); } diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index fbc22a39e7bc..3ee5f2a7c0b4 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -352,6 +352,8 @@ struct usb_gadget_ops { * @deactivated: True if gadget is deactivated - in deactivated state it cannot * be connected. * @connected: True if gadget is connected. + * @lpm_capable: If the gadget max_speed is FULL or HIGH, this flag + * indicates that it supports LPM as per the LPM ECN & errata. * * Gadgets have a mostly-portable "gadget driver" implementing device * functions, handling all usb configurations and interfaces. Gadget @@ -404,6 +406,7 @@ struct usb_gadget { unsigned is_selfpowered:1; unsigned deactivated:1; unsigned connected:1; + unsigned lpm_capable:1; }; #define work_to_gadget(w) (container_of((w), struct usb_gadget, work)) -- cgit v1.2.3 From bab3548078237706f53baafe43ae58257225549d Mon Sep 17 00:00:00 2001 From: Badhri Jagan Sridharan Date: Tue, 30 May 2017 12:39:53 -0700 Subject: usb: typec: Add a sysfs node to manage port type User space applications in some cases have the need to enforce a specific port type(DFP/UFP/DRP). This change allows userspace to attempt setting the desired port type. Low level drivers can however reject the request if the specific port type is not supported. Signed-off-by: Badhri Jagan Sridharan Reviewed-by: Guenter Roeck Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 15 ++++ drivers/usb/typec/typec.c | 106 +++++++++++++++++++++++++--- include/linux/usb/typec.h | 4 ++ 3 files changed, 115 insertions(+), 10 deletions(-) (limited to 'include/linux/usb') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index d4a3d23eb09c..5be552e255e9 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -30,6 +30,21 @@ Description: Valid values: source, sink +What: /sys/class/typec//port_type +Date: May 2017 +Contact: Badhri Jagan Sridharan +Description: + Indicates the type of the port. This attribute can be used for + requesting a change in the port type. Port type change is + supported as a synchronous operation, so write(2) to the + attribute will not return until the operation has finished. + + Valid values: + - source (The port will behave as source only DFP port) + - sink (The port will behave as sink only UFP port) + - dual (The port will behave as dual-role-data and + dual-role-power port) + What: /sys/class/typec//vconn_source Date: April 2017 Contact: Heikki Krogerus diff --git a/drivers/usb/typec/typec.c b/drivers/usb/typec/typec.c index 1a6822ee1a52..24e355ba109d 100644 --- a/drivers/usb/typec/typec.c +++ b/drivers/usb/typec/typec.c @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -69,6 +70,8 @@ struct typec_port { enum typec_role pwr_role; enum typec_role vconn_role; enum typec_pwr_opmode pwr_opmode; + enum typec_port_type port_type; + struct mutex port_type_lock; const struct typec_capability *cap; }; @@ -790,6 +793,18 @@ static const char * const typec_data_roles[] = { [TYPEC_HOST] = "host", }; +static const char * const typec_port_types[] = { + [TYPEC_PORT_DFP] = "source", + [TYPEC_PORT_UFP] = "sink", + [TYPEC_PORT_DRP] = "dual", +}; + +static const char * const typec_port_types_drp[] = { + [TYPEC_PORT_DFP] = "dual [source] sink", + [TYPEC_PORT_UFP] = "dual source [sink]", + [TYPEC_PORT_DRP] = "[dual] source sink", +}; + static ssize_t preferred_role_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t size) @@ -847,11 +862,6 @@ static ssize_t data_role_store(struct device *dev, struct typec_port *port = to_typec_port(dev); int ret; - if (port->cap->type != TYPEC_PORT_DRP) { - dev_dbg(dev, "data role swap only supported with DRP ports\n"); - return -EOPNOTSUPP; - } - if (!port->cap->dr_set) { dev_dbg(dev, "data role swapping not supported\n"); return -EOPNOTSUPP; @@ -861,11 +871,22 @@ static ssize_t data_role_store(struct device *dev, if (ret < 0) return ret; + mutex_lock(&port->port_type_lock); + if (port->port_type != TYPEC_PORT_DRP) { + dev_dbg(dev, "port type fixed at \"%s\"", + typec_port_types[port->port_type]); + ret = -EOPNOTSUPP; + goto unlock_and_ret; + } + ret = port->cap->dr_set(port->cap, ret); if (ret) - return ret; + goto unlock_and_ret; - return size; + ret = size; +unlock_and_ret: + mutex_unlock(&port->port_type_lock); + return ret; } static ssize_t data_role_show(struct device *dev, @@ -886,7 +907,7 @@ static ssize_t power_role_store(struct device *dev, const char *buf, size_t size) { struct typec_port *port = to_typec_port(dev); - int ret = size; + int ret; if (!port->cap->pd_revision) { dev_dbg(dev, "USB Power Delivery not supported\n"); @@ -907,11 +928,22 @@ static ssize_t power_role_store(struct device *dev, if (ret < 0) return ret; + mutex_lock(&port->port_type_lock); + if (port->port_type != TYPEC_PORT_DRP) { + dev_dbg(dev, "port type fixed at \"%s\"", + typec_port_types[port->port_type]); + ret = -EOPNOTSUPP; + goto unlock_and_ret; + } + ret = port->cap->pr_set(port->cap, ret); if (ret) - return ret; + goto unlock_and_ret; - return size; + ret = size; +unlock_and_ret: + mutex_unlock(&port->port_type_lock); + return ret; } static ssize_t power_role_show(struct device *dev, @@ -927,6 +959,57 @@ static ssize_t power_role_show(struct device *dev, } static DEVICE_ATTR_RW(power_role); +static ssize_t +port_type_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t size) +{ + struct typec_port *port = to_typec_port(dev); + int ret; + enum typec_port_type type; + + if (!port->cap->port_type_set || port->cap->type != TYPEC_PORT_DRP) { + dev_dbg(dev, "changing port type not supported\n"); + return -EOPNOTSUPP; + } + + ret = sysfs_match_string(typec_port_types, buf); + if (ret < 0) + return ret; + + type = ret; + mutex_lock(&port->port_type_lock); + + if (port->port_type == type) { + ret = size; + goto unlock_and_ret; + } + + ret = port->cap->port_type_set(port->cap, type); + if (ret) + goto unlock_and_ret; + + port->port_type = type; + ret = size; + +unlock_and_ret: + mutex_unlock(&port->port_type_lock); + return ret; +} + +static ssize_t +port_type_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct typec_port *port = to_typec_port(dev); + + if (port->cap->type == TYPEC_PORT_DRP) + return sprintf(buf, "%s\n", + typec_port_types_drp[port->port_type]); + + return sprintf(buf, "[%s]\n", typec_port_types[port->cap->type]); +} +static DEVICE_ATTR_RW(port_type); + static const char * const typec_pwr_opmodes[] = { [TYPEC_PWR_MODE_USB] = "default", [TYPEC_PWR_MODE_1_5A] = "1.5A", @@ -1036,6 +1119,7 @@ static struct attribute *typec_attrs[] = { &dev_attr_usb_power_delivery_revision.attr, &dev_attr_usb_typec_revision.attr, &dev_attr_vconn_source.attr, + &dev_attr_port_type.attr, NULL, }; ATTRIBUTE_GROUPS(typec); @@ -1231,6 +1315,8 @@ struct typec_port *typec_register_port(struct device *parent, port->id = id; port->cap = cap; + port->port_type = cap->type; + mutex_init(&port->port_type_lock); port->prefer_role = cap->prefer_role; port->dev.class = typec_class; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index d1d2ebcf36ec..ffe7487886ca 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -190,6 +190,7 @@ struct typec_partner_desc { * @pr_set: Set Power Role * @vconn_set: Set VCONN Role * @activate_mode: Enter/exit given Alternate Mode + * @port_type_set: Set port type * * Static capabilities of a single USB Type-C port. */ @@ -214,6 +215,9 @@ struct typec_capability { int (*activate_mode)(const struct typec_capability *, int mode, int activate); + int (*port_type_set)(const struct typec_capability *, + enum typec_port_type); + }; /* Specific to try_role(). Indicates the user want's to clear the preference. */ -- cgit v1.2.3 From 7cf916bd639bd26db7214f2205bccdb4b9306256 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 13 Jun 2017 16:01:13 +1000 Subject: usb: Fix typo in the definition of Endpoint[out]Request The current definition is wrong. This breaks my upcoming Aspeed virtual hub driver. Signed-off-by: Benjamin Herrenschmidt Acked-by: Alan Stern Cc: stable Signed-off-by: Greg Kroah-Hartman --- include/linux/usb/hcd.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index 50398b69ca44..a1f03ebfde47 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -565,9 +565,9 @@ extern void usb_ep0_reinit(struct usb_device *); ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) #define EndpointRequest \ - ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define EndpointOutRequest \ - ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) + ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) /* class requests from the USB 2.0 hub spec, table 11-15 */ #define HUB_CLASS_REQ(dir, type, request) ((((dir) | (type)) << 8) | (request)) -- cgit v1.2.3 From 67fdfda4a99edea939a63bad1797d69dd8de00d6 Mon Sep 17 00:00:00 2001 From: Felipe Balbi Date: Tue, 6 Jun 2017 16:03:19 +0300 Subject: usb: gadget: core: introduce ->udc_set_speed() method Sometimes, the gadget driver we want to run has max_speed lower than what the UDC supports. In such situations, UDC might want to make sure we don't try to connect on speeds not supported by the gadget driver (e.g. super-speed capable dwc3 with high-speed capable g_midi) because that will just fail. In order to make sure this situation never happens, we introduce a new optional ->udc_set_speed() method which can be implemented by interested UDC drivers. Signed-off-by: Felipe Balbi --- drivers/usb/gadget/udc/core.c | 20 ++++++++++++++++++++ include/linux/usb/gadget.h | 1 + 2 files changed, 21 insertions(+) (limited to 'include/linux/usb') diff --git a/drivers/usb/gadget/udc/core.c b/drivers/usb/gadget/udc/core.c index 62d52fc0fcdc..eec388bccb25 100644 --- a/drivers/usb/gadget/udc/core.c +++ b/drivers/usb/gadget/udc/core.c @@ -1055,6 +1055,23 @@ static inline void usb_gadget_udc_stop(struct usb_udc *udc) udc->gadget->ops->udc_stop(udc->gadget); } +/** + * usb_gadget_udc_set_speed - tells usb device controller speed supported by + * current driver + * @udc: The device we want to set maximum speed + * @speed: The maximum speed to allowed to run + * + * This call is issued by the UDC Class driver before calling + * usb_gadget_udc_start() in order to make sure that we don't try to + * connect on speeds the gadget driver doesn't support. + */ +static inline void usb_gadget_udc_set_speed(struct usb_udc *udc, + enum usb_device_speed speed) +{ + if (udc->gadget->ops->udc_set_speed) + udc->gadget->ops->udc_set_speed(udc->gadget, speed); +} + /** * usb_udc_release - release the usb_udc struct * @dev: the dev member within usb_udc @@ -1288,6 +1305,9 @@ static int udc_bind_to_driver(struct usb_udc *udc, struct usb_gadget_driver *dri udc->dev.driver = &driver->driver; udc->gadget->dev.driver = &driver->driver; + if (driver->max_speed < udc->gadget->max_speed) + usb_gadget_udc_set_speed(udc, driver->max_speed); + ret = driver->bind(udc->gadget, driver); if (ret) goto err1; diff --git a/include/linux/usb/gadget.h b/include/linux/usb/gadget.h index 3ee5f2a7c0b4..1a4a4bacfae6 100644 --- a/include/linux/usb/gadget.h +++ b/include/linux/usb/gadget.h @@ -304,6 +304,7 @@ struct usb_gadget_ops { int (*udc_start)(struct usb_gadget *, struct usb_gadget_driver *); int (*udc_stop)(struct usb_gadget *); + void (*udc_set_speed)(struct usb_gadget *, enum usb_device_speed); struct usb_ep *(*match_ep)(struct usb_gadget *, struct usb_endpoint_descriptor *, struct usb_ss_ep_comp_descriptor *); -- cgit v1.2.3 From e1069bbfcf3bcf4feb264397f3451184fd66b907 Mon Sep 17 00:00:00 2001 From: Jim Baxter Date: Wed, 28 Jun 2017 21:35:29 +0100 Subject: net: cdc_ncm: Reduce memory use when kernel memory low MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CDC-NCM driver can require large amounts of memory to create skb's and this can be a problem when the memory becomes fragmented. This especially affects embedded systems that have constrained resources but wish to maximise the throughput of CDC-NCM with 16KiB NTB's. The issue is after running for a while the kernel memory can become fragmented and it needs compacting. If the NTB allocation is needed before the memory has been compacted the atomic allocation can fail which can cause increased latency, large re-transmissions or disconnections depending upon the data being transmitted at the time. This situation occurs for less than a second until the kernel has compacted the memory but the failed devices can take a lot longer to recover from the failed TX packets. To ease this temporary situation I modified the CDC-NCM TX path to temporarily switch into a reduced memory mode which allocates an NTB that will fit into a USB_CDC_NCM_NTB_MIN_OUT_SIZE (default 2048 Bytes) sized memory block and only transmit NTB's with a single network frame until the memory situation is resolved. Each time this issue occurs we wait for an increasing number of reduced size allocations before requesting a full size one to not put additional pressure on a low memory system. Once the memory is compacted the CDC-NCM data can resume transmitting at the normal tx_max rate once again. Signed-off-by: Jim Baxter Reviewed-by: Bjørn Mork Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 54 +++++++++++++++++++++++++++++++++++---------- include/linux/usb/cdc_ncm.h | 3 +++ 2 files changed, 45 insertions(+), 12 deletions(-) (limited to 'include/linux/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index 2067743f51ca..d103a1d4fb36 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -89,6 +89,8 @@ static const struct cdc_ncm_stats cdc_ncm_gstrings_stats[] = { CDC_NCM_SIMPLE_STAT(rx_ntbs), }; +#define CDC_NCM_LOW_MEM_MAX_CNT 10 + static int cdc_ncm_get_sset_count(struct net_device __always_unused *netdev, int sset) { switch (sset) { @@ -1055,10 +1057,10 @@ static struct usb_cdc_ncm_ndp16 *cdc_ncm_ndp(struct cdc_ncm_ctx *ctx, struct sk_ /* align new NDP */ if (!(ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END)) - cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_max); + cdc_ncm_align_tail(skb, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size); /* verify that there is room for the NDP and the datagram (reserve) */ - if ((ctx->tx_max - skb->len - reserve) < ctx->max_ndp_size) + if ((ctx->tx_curr_size - skb->len - reserve) < ctx->max_ndp_size) return NULL; /* link to it */ @@ -1111,13 +1113,41 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* allocate a new OUT skb */ if (!skb_out) { - skb_out = alloc_skb(ctx->tx_max, GFP_ATOMIC); + if (ctx->tx_low_mem_val == 0) { + ctx->tx_curr_size = ctx->tx_max; + skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); + /* If the memory allocation fails we will wait longer + * each time before attempting another full size + * allocation again to not overload the system + * further. + */ + if (skb_out == NULL) { + ctx->tx_low_mem_max_cnt = min(ctx->tx_low_mem_max_cnt + 1, + (unsigned)CDC_NCM_LOW_MEM_MAX_CNT); + ctx->tx_low_mem_val = ctx->tx_low_mem_max_cnt; + } + } if (skb_out == NULL) { - if (skb != NULL) { - dev_kfree_skb_any(skb); - dev->net->stats.tx_dropped++; + /* See if a very small allocation is possible. + * We will send this packet immediately and hope + * that there is more memory available later. + */ + if (skb) + ctx->tx_curr_size = max(skb->len, + (u32)USB_CDC_NCM_NTB_MIN_OUT_SIZE); + else + ctx->tx_curr_size = USB_CDC_NCM_NTB_MIN_OUT_SIZE; + skb_out = alloc_skb(ctx->tx_curr_size, GFP_ATOMIC); + + /* No allocation possible so we will abort */ + if (skb_out == NULL) { + if (skb != NULL) { + dev_kfree_skb_any(skb); + dev->net->stats.tx_dropped++; + } + goto exit_no_skb; } - goto exit_no_skb; + ctx->tx_low_mem_val--; } /* fill out the initial 16-bit NTB header */ nth16 = skb_put_zero(skb_out, sizeof(struct usb_cdc_ncm_nth16)); @@ -1148,10 +1178,10 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) ndp16 = cdc_ncm_ndp(ctx, skb_out, sign, skb->len + ctx->tx_modulus + ctx->tx_remainder); /* align beginning of next frame */ - cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_max); + cdc_ncm_align_tail(skb_out, ctx->tx_modulus, ctx->tx_remainder, ctx->tx_curr_size); /* check if we had enough room left for both NDP and frame */ - if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_max) { + if (!ndp16 || skb_out->len + skb->len + delayed_ndp_size > ctx->tx_curr_size) { if (n == 0) { /* won't fit, MTU problem? */ dev_kfree_skb_any(skb); @@ -1227,7 +1257,7 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) /* If requested, put NDP at end of frame. */ if (ctx->drvflags & CDC_NCM_FLAG_NDP_TO_END) { nth16 = (struct usb_cdc_ncm_nth16 *)skb_out->data; - cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_max); + cdc_ncm_align_tail(skb_out, ctx->tx_ndp_modulus, 0, ctx->tx_curr_size); nth16->wNdpIndex = cpu_to_le16(skb_out->len); skb_put_data(skb_out, ctx->delayed_ndp16, ctx->max_ndp_size); @@ -1246,9 +1276,9 @@ cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign) */ if (!(dev->driver_info->flags & FLAG_SEND_ZLP) && skb_out->len > ctx->min_tx_pkt) { - padding_count = ctx->tx_max - skb_out->len; + padding_count = ctx->tx_curr_size - skb_out->len; skb_put_zero(skb_out, padding_count); - } else if (skb_out->len < ctx->tx_max && + } else if (skb_out->len < ctx->tx_curr_size && (skb_out->len % dev->maxpacket) == 0) { skb_put_u8(skb_out, 0); /* force short packet */ } diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 00d232406f18..021f7a88f52c 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -117,6 +117,9 @@ struct cdc_ncm_ctx { u32 tx_curr_frame_num; u32 rx_max; u32 tx_max; + u32 tx_curr_size; + u32 tx_low_mem_max_cnt; + u32 tx_low_mem_val; u32 max_datagram_size; u16 tx_max_datagrams; u16 tx_remainder; -- cgit v1.2.3 From 2b02c20ce0c28974b44e69a2e2f5ddc6a470ad6f Mon Sep 17 00:00:00 2001 From: Enrico Mioso Date: Tue, 11 Jul 2017 17:21:52 +0200 Subject: cdc_ncm: Set NTB format again after altsetting switch for Huawei devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some firmwares in Huawei E3372H devices have been observed to switch back to NTB 32-bit format after altsetting switch. This patch implements a driver flag to check for the device settings and set NTB format to 16-bit again if needed. The flag has been activated for devices controlled by the huawei_cdc_ncm.c driver. V1->V2: - fixed broken error checks - some corrections to the commit message V2->V3: - variable name changes, to clarify what's happening - check (and possibly set) the NTB format later in the common bind code path Signed-off-by: Enrico Mioso Reported-and-tested-by: Christian Panton Reviewed-by: Bjørn Mork CC: Bjørn Mork CC: Christian Panton CC: linux-usb@vger.kernel.org CC: netdev@vger.kernel.org CC: Oliver Neukum Signed-off-by: David S. Miller --- drivers/net/usb/cdc_ncm.c | 28 ++++++++++++++++++++++++++++ drivers/net/usb/huawei_cdc_ncm.c | 6 ++++++ include/linux/usb/cdc_ncm.h | 1 + 3 files changed, 35 insertions(+) (limited to 'include/linux/usb') diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c index d103a1d4fb36..8f572b9f3625 100644 --- a/drivers/net/usb/cdc_ncm.c +++ b/drivers/net/usb/cdc_ncm.c @@ -768,8 +768,10 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ u8 *buf; int len; int temp; + int err; u8 iface_no; struct usb_cdc_parsed_header hdr; + u16 curr_ntb_format; ctx = kzalloc(sizeof(*ctx), GFP_KERNEL); if (!ctx) @@ -874,6 +876,32 @@ int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_ goto error2; } + /* + * Some Huawei devices have been observed to come out of reset in NDP32 mode. + * Let's check if this is the case, and set the device to NDP16 mode again if + * needed. + */ + if (ctx->drvflags & CDC_NCM_FLAG_RESET_NTB16) { + err = usbnet_read_cmd(dev, USB_CDC_GET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_IN | USB_RECIP_INTERFACE, + 0, iface_no, &curr_ntb_format, 2); + if (err < 0) { + goto error2; + } + + if (curr_ntb_format == USB_CDC_NCM_NTB32_FORMAT) { + dev_info(&intf->dev, "resetting NTB format to 16-bit"); + err = usbnet_write_cmd(dev, USB_CDC_SET_NTB_FORMAT, + USB_TYPE_CLASS | USB_DIR_OUT + | USB_RECIP_INTERFACE, + USB_CDC_NCM_NTB16_FORMAT, + iface_no, NULL, 0); + + if (err < 0) + goto error2; + } + } + cdc_ncm_find_endpoints(dev, ctx->data); cdc_ncm_find_endpoints(dev, ctx->control); if (!dev->in || !dev->out || !dev->status) { diff --git a/drivers/net/usb/huawei_cdc_ncm.c b/drivers/net/usb/huawei_cdc_ncm.c index 2680a65cd5e4..63f28908afda 100644 --- a/drivers/net/usb/huawei_cdc_ncm.c +++ b/drivers/net/usb/huawei_cdc_ncm.c @@ -80,6 +80,12 @@ static int huawei_cdc_ncm_bind(struct usbnet *usbnet_dev, * be at the end of the frame. */ drvflags |= CDC_NCM_FLAG_NDP_TO_END; + + /* Additionally, it has been reported that some Huawei E3372H devices, with + * firmware version 21.318.01.00.541, come out of reset in NTB32 format mode, hence + * needing to be set to the NTB16 one again. + */ + drvflags |= CDC_NCM_FLAG_RESET_NTB16; ret = cdc_ncm_bind_common(usbnet_dev, intf, 1, drvflags); if (ret) goto err; diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h index 021f7a88f52c..1a59699cf82a 100644 --- a/include/linux/usb/cdc_ncm.h +++ b/include/linux/usb/cdc_ncm.h @@ -83,6 +83,7 @@ /* Driver flags */ #define CDC_NCM_FLAG_NDP_TO_END 0x02 /* NDP is placed at end of frame */ #define CDC_MBIM_FLAG_AVOID_ALTSETTING_TOGGLE 0x04 /* Avoid altsetting toggle during init */ +#define CDC_NCM_FLAG_RESET_NTB16 0x08 /* set NDP16 one more time after altsetting switch */ #define cdc_ncm_comm_intf_is_mbim(x) ((x)->desc.bInterfaceSubClass == USB_CDC_SUBCLASS_MBIM && \ (x)->desc.bInterfaceProtocol == USB_CDC_PROTO_NONE) -- cgit v1.2.3 From 8bd226f9a7dc18740a916dcba3112f2bfc3ad9e8 Mon Sep 17 00:00:00 2001 From: Ruslan Bilovol Date: Sun, 25 Jun 2017 16:23:45 +0300 Subject: include: usb: audio: specify exact endiannes of descriptors USB spec says that multiple byte fields are stored in little-endian order (see chapter 8.1 of USB2.0 spec and chapter 7.1 of USB3.0 spec), thus mark such fields as LE for UAC1 and UAC2 headers Signed-off-by: Ruslan Bilovol Signed-off-by: Felipe Balbi --- include/linux/usb/audio-v2.h | 14 +++++++------- include/uapi/linux/usb/audio.h | 6 +++--- 2 files changed, 10 insertions(+), 10 deletions(-) (limited to 'include/linux/usb') diff --git a/include/linux/usb/audio-v2.h b/include/linux/usb/audio-v2.h index c5f2158ab00e..fd73bc0e9027 100644 --- a/include/linux/usb/audio-v2.h +++ b/include/linux/usb/audio-v2.h @@ -115,13 +115,13 @@ struct uac2_input_terminal_descriptor { __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bTerminalID; - __u16 wTerminalType; + __le16 wTerminalType; __u8 bAssocTerminal; __u8 bCSourceID; __u8 bNrChannels; - __u32 bmChannelConfig; + __le32 bmChannelConfig; __u8 iChannelNames; - __u16 bmControls; + __le16 bmControls; __u8 iTerminal; } __attribute__((packed)); @@ -132,11 +132,11 @@ struct uac2_output_terminal_descriptor { __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bTerminalID; - __u16 wTerminalType; + __le16 wTerminalType; __u8 bAssocTerminal; __u8 bSourceID; __u8 bCSourceID; - __u16 bmControls; + __le16 bmControls; __u8 iTerminal; } __attribute__((packed)); @@ -164,9 +164,9 @@ struct uac2_as_header_descriptor { __u8 bTerminalLink; __u8 bmControls; __u8 bFormatType; - __u32 bmFormats; + __le32 bmFormats; __u8 bNrChannels; - __u32 bmChannelConfig; + __le32 bmChannelConfig; __u8 iChannelNames; } __attribute__((packed)); diff --git a/include/uapi/linux/usb/audio.h b/include/uapi/linux/usb/audio.h index d2314be4f0c0..a4680a5bf5dd 100644 --- a/include/uapi/linux/usb/audio.h +++ b/include/uapi/linux/usb/audio.h @@ -333,7 +333,7 @@ struct uac_processing_unit_descriptor { __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bUnitID; - __u16 wProcessType; + __le16 wProcessType; __u8 bNrInPins; __u8 baSourceID[]; } __attribute__ ((packed)); @@ -491,8 +491,8 @@ struct uac_format_type_ii_ext_descriptor { __u8 bDescriptorType; __u8 bDescriptorSubtype; __u8 bFormatType; - __u16 wMaxBitRate; - __u16 wSamplesPerFrame; + __le16 wMaxBitRate; + __le16 wSamplesPerFrame; __u8 bHeaderLength; __u8 bSideBandProtocol; } __attribute__((packed)); -- cgit v1.2.3