Commit 7a263b16 authored by Linus Torvalds's avatar Linus Torvalds

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

Pull USB fixes from Greg KH:
 "Here are a handful of USB driver fixes for 4.14-rc5.

  There is the "usual" usb-serial fixes and device ids, USB gadget
  fixes, and some more fixes found by the fuzz testing that is happening
  on the USB layer right now.

  All of these have been in my tree this week with no reported issues"

* tag 'usb-4.14-rc5' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/usb:
  usb: usbtest: fix NULL pointer dereference
  usb: gadget: configfs: Fix memory leak of interface directory data
  usb: gadget: composite: Fix use-after-free in usb_composite_overwrite_options
  usb: misc: usbtest: Fix overflow in usbtest_do_ioctl()
  usb: renesas_usbhs: Fix DMAC sequence for receiving zero-length packet
  USB: dummy-hcd: Fix deadlock caused by disconnect detection
  usb: phy: tegra: Fix phy suspend for UDC
  USB: serial: console: fix use-after-free after failed setup
  USB: serial: console: fix use-after-free on disconnect
  USB: serial: qcserial: add Dell DW5818, DW5819
  USB: serial: cp210x: add support for ELV TFD500
  USB: serial: cp210x: fix partnum regression
  USB: serial: option: add support for TP-Link LTE module
  USB: serial: ftdi_sio: add id for Cypress WICED dev board
parents 7a23c5ab 2d30408e
...@@ -2026,6 +2026,8 @@ static DEVICE_ATTR_RO(suspended); ...@@ -2026,6 +2026,8 @@ static DEVICE_ATTR_RO(suspended);
static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
{ {
struct usb_composite_dev *cdev = get_gadget_data(gadget); struct usb_composite_dev *cdev = get_gadget_data(gadget);
struct usb_gadget_strings *gstr = cdev->driver->strings[0];
struct usb_string *dev_str = gstr->strings;
/* composite_disconnect() must already have been called /* composite_disconnect() must already have been called
* by the underlying peripheral controller driver! * by the underlying peripheral controller driver!
...@@ -2045,6 +2047,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver) ...@@ -2045,6 +2047,9 @@ static void __composite_unbind(struct usb_gadget *gadget, bool unbind_driver)
composite_dev_cleanup(cdev); composite_dev_cleanup(cdev);
if (dev_str[USB_GADGET_MANUFACTURER_IDX].s == cdev->def_manufacturer)
dev_str[USB_GADGET_MANUFACTURER_IDX].s = "";
kfree(cdev->def_manufacturer); kfree(cdev->def_manufacturer);
kfree(cdev); kfree(cdev);
set_gadget_data(gadget, NULL); set_gadget_data(gadget, NULL);
......
...@@ -1143,7 +1143,8 @@ static struct configfs_attribute *interf_grp_attrs[] = { ...@@ -1143,7 +1143,8 @@ static struct configfs_attribute *interf_grp_attrs[] = {
NULL NULL
}; };
int usb_os_desc_prepare_interf_dir(struct config_group *parent, struct config_group *usb_os_desc_prepare_interf_dir(
struct config_group *parent,
int n_interf, int n_interf,
struct usb_os_desc **desc, struct usb_os_desc **desc,
char **names, char **names,
...@@ -1159,7 +1160,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent, ...@@ -1159,7 +1160,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL); char *vlabuf = kzalloc(vla_group_size(data_chunk), GFP_KERNEL);
if (!vlabuf) if (!vlabuf)
return -ENOMEM; return ERR_PTR(-ENOMEM);
os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group); os_desc_group = vla_ptr(vlabuf, data_chunk, os_desc_group);
os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type); os_desc_type = vla_ptr(vlabuf, data_chunk, os_desc_type);
...@@ -1184,7 +1185,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent, ...@@ -1184,7 +1185,7 @@ int usb_os_desc_prepare_interf_dir(struct config_group *parent,
configfs_add_default_group(&d->group, os_desc_group); configfs_add_default_group(&d->group, os_desc_group);
} }
return 0; return os_desc_group;
} }
EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir); EXPORT_SYMBOL(usb_os_desc_prepare_interf_dir);
......
...@@ -5,7 +5,8 @@ ...@@ -5,7 +5,8 @@
void unregister_gadget_item(struct config_item *item); void unregister_gadget_item(struct config_item *item);
int usb_os_desc_prepare_interf_dir(struct config_group *parent, struct config_group *usb_os_desc_prepare_interf_dir(
struct config_group *parent,
int n_interf, int n_interf,
struct usb_os_desc **desc, struct usb_os_desc **desc,
char **names, char **names,
......
...@@ -908,6 +908,7 @@ static void rndis_free_inst(struct usb_function_instance *f) ...@@ -908,6 +908,7 @@ static void rndis_free_inst(struct usb_function_instance *f)
free_netdev(opts->net); free_netdev(opts->net);
} }
kfree(opts->rndis_interf_group); /* single VLA chunk */
kfree(opts); kfree(opts);
} }
...@@ -916,6 +917,7 @@ static struct usb_function_instance *rndis_alloc_inst(void) ...@@ -916,6 +917,7 @@ static struct usb_function_instance *rndis_alloc_inst(void)
struct f_rndis_opts *opts; struct f_rndis_opts *opts;
struct usb_os_desc *descs[1]; struct usb_os_desc *descs[1];
char *names[1]; char *names[1];
struct config_group *rndis_interf_group;
opts = kzalloc(sizeof(*opts), GFP_KERNEL); opts = kzalloc(sizeof(*opts), GFP_KERNEL);
if (!opts) if (!opts)
...@@ -940,8 +942,14 @@ static struct usb_function_instance *rndis_alloc_inst(void) ...@@ -940,8 +942,14 @@ static struct usb_function_instance *rndis_alloc_inst(void)
names[0] = "rndis"; names[0] = "rndis";
config_group_init_type_name(&opts->func_inst.group, "", config_group_init_type_name(&opts->func_inst.group, "",
&rndis_func_type); &rndis_func_type);
rndis_interf_group =
usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs, usb_os_desc_prepare_interf_dir(&opts->func_inst.group, 1, descs,
names, THIS_MODULE); names, THIS_MODULE);
if (IS_ERR(rndis_interf_group)) {
rndis_free_inst(&opts->func_inst);
return ERR_CAST(rndis_interf_group);
}
opts->rndis_interf_group = rndis_interf_group;
return &opts->func_inst; return &opts->func_inst;
} }
......
...@@ -26,6 +26,7 @@ struct f_rndis_opts { ...@@ -26,6 +26,7 @@ struct f_rndis_opts {
bool bound; bool bound;
bool borrowed_net; bool borrowed_net;
struct config_group *rndis_interf_group;
struct usb_os_desc rndis_os_desc; struct usb_os_desc rndis_os_desc;
char rndis_ext_compat_id[16]; char rndis_ext_compat_id[16];
......
...@@ -419,6 +419,7 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd) ...@@ -419,6 +419,7 @@ static void set_link_state_by_speed(struct dummy_hcd *dum_hcd)
static void set_link_state(struct dummy_hcd *dum_hcd) static void set_link_state(struct dummy_hcd *dum_hcd)
{ {
struct dummy *dum = dum_hcd->dum; struct dummy *dum = dum_hcd->dum;
unsigned int power_bit;
dum_hcd->active = 0; dum_hcd->active = 0;
if (dum->pullup) if (dum->pullup)
...@@ -429,17 +430,19 @@ static void set_link_state(struct dummy_hcd *dum_hcd) ...@@ -429,17 +430,19 @@ static void set_link_state(struct dummy_hcd *dum_hcd)
return; return;
set_link_state_by_speed(dum_hcd); set_link_state_by_speed(dum_hcd);
power_bit = (dummy_hcd_to_hcd(dum_hcd)->speed == HCD_USB3 ?
USB_SS_PORT_STAT_POWER : USB_PORT_STAT_POWER);
if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 || if ((dum_hcd->port_status & USB_PORT_STAT_ENABLE) == 0 ||
dum_hcd->active) dum_hcd->active)
dum_hcd->resuming = 0; dum_hcd->resuming = 0;
/* Currently !connected or in reset */ /* Currently !connected or in reset */
if ((dum_hcd->port_status & USB_PORT_STAT_CONNECTION) == 0 || if ((dum_hcd->port_status & power_bit) == 0 ||
(dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) { (dum_hcd->port_status & USB_PORT_STAT_RESET) != 0) {
unsigned disconnect = USB_PORT_STAT_CONNECTION & unsigned int disconnect = power_bit &
dum_hcd->old_status & (~dum_hcd->port_status); dum_hcd->old_status & (~dum_hcd->port_status);
unsigned reset = USB_PORT_STAT_RESET & unsigned int reset = USB_PORT_STAT_RESET &
(~dum_hcd->old_status) & dum_hcd->port_status; (~dum_hcd->old_status) & dum_hcd->port_status;
/* Report reset and disconnect events to the driver */ /* Report reset and disconnect events to the driver */
......
...@@ -202,12 +202,13 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf) ...@@ -202,12 +202,13 @@ get_endpoints(struct usbtest_dev *dev, struct usb_interface *intf)
return tmp; return tmp;
} }
if (in) { if (in)
dev->in_pipe = usb_rcvbulkpipe(udev, dev->in_pipe = usb_rcvbulkpipe(udev,
in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); in->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
if (out)
dev->out_pipe = usb_sndbulkpipe(udev, dev->out_pipe = usb_sndbulkpipe(udev,
out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK); out->desc.bEndpointAddress & USB_ENDPOINT_NUMBER_MASK);
}
if (iso_in) { if (iso_in) {
dev->iso_in = &iso_in->desc; dev->iso_in = &iso_in->desc;
dev->in_iso_pipe = usb_rcvisocpipe(udev, dev->in_iso_pipe = usb_rcvisocpipe(udev,
...@@ -1964,6 +1965,9 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param, ...@@ -1964,6 +1965,9 @@ test_queue(struct usbtest_dev *dev, struct usbtest_param_32 *param,
int status = 0; int status = 0;
struct urb *urbs[param->sglen]; struct urb *urbs[param->sglen];
if (!param->sglen || param->iterations > UINT_MAX / param->sglen)
return -EINVAL;
memset(&context, 0, sizeof(context)); memset(&context, 0, sizeof(context));
context.count = param->iterations * param->sglen; context.count = param->iterations * param->sglen;
context.dev = dev; context.dev = dev;
...@@ -2087,6 +2091,8 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param) ...@@ -2087,6 +2091,8 @@ usbtest_do_ioctl(struct usb_interface *intf, struct usbtest_param_32 *param)
if (param->iterations <= 0) if (param->iterations <= 0)
return -EINVAL; return -EINVAL;
if (param->sglen > MAX_SGLEN)
return -EINVAL;
/* /*
* Just a bunch of test cases that every HCD is expected to handle. * Just a bunch of test cases that every HCD is expected to handle.
* *
......
...@@ -329,6 +329,14 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy) ...@@ -329,6 +329,14 @@ static void utmi_phy_clk_disable(struct tegra_usb_phy *phy)
unsigned long val; unsigned long val;
void __iomem *base = phy->regs; void __iomem *base = phy->regs;
/*
* The USB driver may have already initiated the phy clock
* disable so wait to see if the clock turns off and if not
* then proceed with gating the clock.
*/
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, 0) == 0)
return;
if (phy->is_legacy_phy) { if (phy->is_legacy_phy) {
val = readl(base + USB_SUSP_CTRL); val = readl(base + USB_SUSP_CTRL);
val |= USB_SUSP_SET; val |= USB_SUSP_SET;
...@@ -351,6 +359,15 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy) ...@@ -351,6 +359,15 @@ static void utmi_phy_clk_enable(struct tegra_usb_phy *phy)
unsigned long val; unsigned long val;
void __iomem *base = phy->regs; void __iomem *base = phy->regs;
/*
* The USB driver may have already initiated the phy clock
* enable so wait to see if the clock turns on and if not
* then proceed with ungating the clock.
*/
if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID,
USB_PHY_CLK_VALID) == 0)
return;
if (phy->is_legacy_phy) { if (phy->is_legacy_phy) {
val = readl(base + USB_SUSP_CTRL); val = readl(base + USB_SUSP_CTRL);
val |= USB_SUSP_CLR; val |= USB_SUSP_CLR;
......
...@@ -857,9 +857,9 @@ static void xfer_work(struct work_struct *work) ...@@ -857,9 +857,9 @@ static void xfer_work(struct work_struct *work)
fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero); fifo->name, usbhs_pipe_number(pipe), pkt->length, pkt->zero);
usbhs_pipe_running(pipe, 1); usbhs_pipe_running(pipe, 1);
usbhsf_dma_start(pipe, fifo);
usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans); usbhs_pipe_set_trans_count_if_bulk(pipe, pkt->trans);
dma_async_issue_pending(chan); dma_async_issue_pending(chan);
usbhsf_dma_start(pipe, fifo);
usbhs_pipe_enable(pipe); usbhs_pipe_enable(pipe);
xfer_work_end: xfer_work_end:
......
...@@ -186,6 +186,7 @@ static int usb_console_setup(struct console *co, char *options) ...@@ -186,6 +186,7 @@ static int usb_console_setup(struct console *co, char *options)
tty_kref_put(tty); tty_kref_put(tty);
reset_open_count: reset_open_count:
port->port.count = 0; port->port.count = 0;
info->port = NULL;
usb_autopm_put_interface(serial->interface); usb_autopm_put_interface(serial->interface);
error_get_interface: error_get_interface:
usb_serial_put(serial); usb_serial_put(serial);
...@@ -265,7 +266,7 @@ static struct console usbcons = { ...@@ -265,7 +266,7 @@ static struct console usbcons = {
void usb_serial_console_disconnect(struct usb_serial *serial) void usb_serial_console_disconnect(struct usb_serial *serial)
{ {
if (serial->port[0] == usbcons_info.port) { if (serial->port[0] && serial->port[0] == usbcons_info.port) {
usb_serial_console_exit(); usb_serial_console_exit();
usb_serial_put(serial); usb_serial_put(serial);
} }
......
...@@ -177,6 +177,7 @@ static const struct usb_device_id id_table[] = { ...@@ -177,6 +177,7 @@ static const struct usb_device_id id_table[] = {
{ USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */ { USB_DEVICE(0x1843, 0x0200) }, /* Vaisala USB Instrument Cable */
{ USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */ { USB_DEVICE(0x18EF, 0xE00F) }, /* ELV USB-I2C-Interface */
{ USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */ { USB_DEVICE(0x18EF, 0xE025) }, /* ELV Marble Sound Board 1 */
{ USB_DEVICE(0x18EF, 0xE032) }, /* ELV TFD500 Data Logger */
{ USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */ { USB_DEVICE(0x1901, 0x0190) }, /* GE B850 CP2105 Recorder interface */
{ USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */ { USB_DEVICE(0x1901, 0x0193) }, /* GE B650 CP2104 PMC interface */
{ USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */ { USB_DEVICE(0x1901, 0x0194) }, /* GE Healthcare Remote Alarm Box */
...@@ -352,6 +353,7 @@ static struct usb_serial_driver * const serial_drivers[] = { ...@@ -352,6 +353,7 @@ static struct usb_serial_driver * const serial_drivers[] = {
#define CP210X_PARTNUM_CP2104 0x04 #define CP210X_PARTNUM_CP2104 0x04
#define CP210X_PARTNUM_CP2105 0x05 #define CP210X_PARTNUM_CP2105 0x05
#define CP210X_PARTNUM_CP2108 0x08 #define CP210X_PARTNUM_CP2108 0x08
#define CP210X_PARTNUM_UNKNOWN 0xFF
/* CP210X_GET_COMM_STATUS returns these 0x13 bytes */ /* CP210X_GET_COMM_STATUS returns these 0x13 bytes */
struct cp210x_comm_status { struct cp210x_comm_status {
...@@ -1491,8 +1493,11 @@ static int cp210x_attach(struct usb_serial *serial) ...@@ -1491,8 +1493,11 @@ static int cp210x_attach(struct usb_serial *serial)
result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST, result = cp210x_read_vendor_block(serial, REQTYPE_DEVICE_TO_HOST,
CP210X_GET_PARTNUM, &priv->partnum, CP210X_GET_PARTNUM, &priv->partnum,
sizeof(priv->partnum)); sizeof(priv->partnum));
if (result < 0) if (result < 0) {
goto err_free_priv; dev_warn(&serial->interface->dev,
"querying part number failed\n");
priv->partnum = CP210X_PARTNUM_UNKNOWN;
}
usb_set_serial_data(serial, priv); usb_set_serial_data(serial, priv);
...@@ -1505,10 +1510,6 @@ static int cp210x_attach(struct usb_serial *serial) ...@@ -1505,10 +1510,6 @@ static int cp210x_attach(struct usb_serial *serial)
} }
return 0; return 0;
err_free_priv:
kfree(priv);
return result;
} }
static void cp210x_disconnect(struct usb_serial *serial) static void cp210x_disconnect(struct usb_serial *serial)
......
...@@ -1015,6 +1015,8 @@ static const struct usb_device_id id_table_combined[] = { ...@@ -1015,6 +1015,8 @@ static const struct usb_device_id id_table_combined[] = {
{ USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) }, { USB_DEVICE(WICED_VID, WICED_USB20706V2_PID) },
{ USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID), { USB_DEVICE(TI_VID, TI_CC3200_LAUNCHPAD_PID),
.driver_info = (kernel_ulong_t)&ftdi_jtag_quirk }, .driver_info = (kernel_ulong_t)&ftdi_jtag_quirk },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_BT_USB_PID) },
{ USB_DEVICE(CYPRESS_VID, CYPRESS_WICED_WL_USB_PID) },
{ } /* Terminating entry */ { } /* Terminating entry */
}; };
......
...@@ -609,6 +609,13 @@ ...@@ -609,6 +609,13 @@
#define ADI_GNICE_PID 0xF000 #define ADI_GNICE_PID 0xF000
#define ADI_GNICEPLUS_PID 0xF001 #define ADI_GNICEPLUS_PID 0xF001
/*
* Cypress WICED USB UART
*/
#define CYPRESS_VID 0x04B4
#define CYPRESS_WICED_BT_USB_PID 0x009B
#define CYPRESS_WICED_WL_USB_PID 0xF900
/* /*
* Microchip Technology, Inc. * Microchip Technology, Inc.
* *
......
...@@ -522,6 +522,7 @@ static void option_instat_callback(struct urb *urb); ...@@ -522,6 +522,7 @@ static void option_instat_callback(struct urb *urb);
/* TP-LINK Incorporated products */ /* TP-LINK Incorporated products */
#define TPLINK_VENDOR_ID 0x2357 #define TPLINK_VENDOR_ID 0x2357
#define TPLINK_PRODUCT_LTE 0x000D
#define TPLINK_PRODUCT_MA180 0x0201 #define TPLINK_PRODUCT_MA180 0x0201
/* Changhong products */ /* Changhong products */
...@@ -2011,6 +2012,7 @@ static const struct usb_device_id option_ids[] = { ...@@ -2011,6 +2012,7 @@ static const struct usb_device_id option_ids[] = {
{ USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) }, { USB_DEVICE(CELLIENT_VENDOR_ID, CELLIENT_PRODUCT_MEN200) },
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) }, { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600A) },
{ USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) }, { USB_DEVICE(PETATEL_VENDOR_ID, PETATEL_PRODUCT_NP10T_600E) },
{ USB_DEVICE_AND_INTERFACE_INFO(TPLINK_VENDOR_ID, TPLINK_PRODUCT_LTE, 0xff, 0x00, 0x00) }, /* TP-Link LTE Module */
{ USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180), { USB_DEVICE(TPLINK_VENDOR_ID, TPLINK_PRODUCT_MA180),
.driver_info = (kernel_ulong_t)&net_intf4_blacklist }, .driver_info = (kernel_ulong_t)&net_intf4_blacklist },
{ USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */ { USB_DEVICE(TPLINK_VENDOR_ID, 0x9000), /* TP-Link MA260 */
......
...@@ -174,6 +174,10 @@ static const struct usb_device_id id_table[] = { ...@@ -174,6 +174,10 @@ static const struct usb_device_id id_table[] = {
{DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */ {DEVICE_SWI(0x413c, 0x81b3)}, /* Dell Wireless 5809e Gobi(TM) 4G LTE Mobile Broadband Card (rev3) */
{DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */ {DEVICE_SWI(0x413c, 0x81b5)}, /* Dell Wireless 5811e QDL */
{DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */ {DEVICE_SWI(0x413c, 0x81b6)}, /* Dell Wireless 5811e QDL */
{DEVICE_SWI(0x413c, 0x81cf)}, /* Dell Wireless 5819 */
{DEVICE_SWI(0x413c, 0x81d0)}, /* Dell Wireless 5819 */
{DEVICE_SWI(0x413c, 0x81d1)}, /* Dell Wireless 5818 */
{DEVICE_SWI(0x413c, 0x81d2)}, /* Dell Wireless 5818 */
/* Huawei devices */ /* Huawei devices */
{DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */ {DEVICE_HWI(0x03f0, 0x581d)}, /* HP lt4112 LTE/HSPA+ Gobi 4G Modem (Huawei me906e) */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment