aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2014-05-11 04:47:15 -0400
committerDavid S. Miller <davem@davemloft.net>2014-05-13 17:46:09 -0400
commit50a0ffaf75e9d2d97200b523f2c600f40e9756b1 (patch)
tree6ffcf988584c3725a7e146afae06e9cd11ac2355
parenta563babeb5fbe721a046adf6f1cdc02e0a207c8d (diff)
net: cdc_ncm/cdc_mbim: rework probing of NCM/MBIM functions
The NCM class match in the cdc_mbim driver is confusing and cause unexpected behaviour. The USB core guarantees that a USB interface is in altsetting 0 when probing starts. This means that devices implementing a NCM 1.0 backwards compatible MBIM function (a "NCM/MBIM function") always hit the NCM entry in the cdc_mbim driver match table. Such functions will never match any of the MBIM entries. This causes unexpeced behaviour for cases where the NCM and MBIM entries are differet, which is currently the case for all except Ericsson devices. Improve the probing of NCM/MBIM functions by looking up the device again in the cdc_mbim match table after switching to the MBIM identity. The shared altsetting selection is updated to better accommodate the new probing logic, returning the preferred altsetting for the control interface instead of the data interface. The control interface altsetting update is moved to the cdc_mbim driver. It is never necessary to change the control interface altsetting for NCM. Cc: Greg Suarez <gsuarez@smithmicro.com> Reported by: Yu-an Shih <yshih@nvidia.com> Signed-off-by: Bjørn Mork <bjorn@mork.no> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/usb/cdc_mbim.c43
-rw-r--r--drivers/net/usb/cdc_ncm.c27
-rw-r--r--include/linux/usb/cdc_ncm.h2
3 files changed, 55 insertions, 17 deletions
diff --git a/drivers/net/usb/cdc_mbim.c b/drivers/net/usb/cdc_mbim.c
index 80d27719ba38..bc23273d0455 100644
--- a/drivers/net/usb/cdc_mbim.c
+++ b/drivers/net/usb/cdc_mbim.c
@@ -107,15 +107,54 @@ static const struct net_device_ops cdc_mbim_netdev_ops = {
107 .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid, 107 .ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid,
108}; 108};
109 109
110/* Change the control interface altsetting and update the .driver_info
111 * pointer if the matching entry after changing class codes points to
112 * a different struct
113 */
114static int cdc_mbim_set_ctrlalt(struct usbnet *dev, struct usb_interface *intf, u8 alt)
115{
116 struct usb_driver *driver = to_usb_driver(intf->dev.driver);
117 const struct usb_device_id *id;
118 struct driver_info *info;
119 int ret;
120
121 ret = usb_set_interface(dev->udev,
122 intf->cur_altsetting->desc.bInterfaceNumber,
123 alt);
124 if (ret)
125 return ret;
126
127 id = usb_match_id(intf, driver->id_table);
128 if (!id)
129 return -ENODEV;
130
131 info = (struct driver_info *)id->driver_info;
132 if (info != dev->driver_info) {
133 dev_dbg(&intf->dev, "driver_info updated to '%s'\n",
134 info->description);
135 dev->driver_info = info;
136 }
137 return 0;
138}
139
110static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) 140static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
111{ 141{
112 struct cdc_ncm_ctx *ctx; 142 struct cdc_ncm_ctx *ctx;
113 struct usb_driver *subdriver = ERR_PTR(-ENODEV); 143 struct usb_driver *subdriver = ERR_PTR(-ENODEV);
114 int ret = -ENODEV; 144 int ret = -ENODEV;
115 u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf); 145 u8 data_altsetting = 1;
116 struct cdc_mbim_state *info = (void *)&dev->data; 146 struct cdc_mbim_state *info = (void *)&dev->data;
117 147
118 /* Probably NCM, defer for cdc_ncm_bind */ 148 /* should we change control altsetting on a NCM/MBIM function? */
149 if (cdc_ncm_select_altsetting(intf) == CDC_NCM_COMM_ALTSETTING_MBIM) {
150 data_altsetting = CDC_NCM_DATA_ALTSETTING_MBIM;
151 ret = cdc_mbim_set_ctrlalt(dev, intf, CDC_NCM_COMM_ALTSETTING_MBIM);
152 if (ret)
153 goto err;
154 ret = -ENODEV;
155 }
156
157 /* we will hit this for NCM/MBIM functions if prefer_mbim is false */
119 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) 158 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
120 goto err; 159 goto err;
121 160
diff --git a/drivers/net/usb/cdc_ncm.c b/drivers/net/usb/cdc_ncm.c
index 9a2bd11943eb..d23bca57a23f 100644
--- a/drivers/net/usb/cdc_ncm.c
+++ b/drivers/net/usb/cdc_ncm.c
@@ -541,10 +541,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
541} 541}
542EXPORT_SYMBOL_GPL(cdc_ncm_unbind); 542EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
543 543
544/* Select the MBIM altsetting iff it is preferred and available, 544/* Return the number of the MBIM control interface altsetting iff it
545 * returning the number of the corresponding data interface altsetting 545 * is preferred and available,
546 */ 546 */
547u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf) 547u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
548{ 548{
549 struct usb_host_interface *alt; 549 struct usb_host_interface *alt;
550 550
@@ -563,15 +563,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
563 * the rules given in section 6 (USB Device Model) of this 563 * the rules given in section 6 (USB Device Model) of this
564 * specification." 564 * specification."
565 */ 565 */
566 if (prefer_mbim && intf->num_altsetting == 2) { 566 if (intf->num_altsetting < 2)
567 return intf->cur_altsetting->desc.bAlternateSetting;
568
569 if (prefer_mbim) {
567 alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM); 570 alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
568 if (alt && cdc_ncm_comm_intf_is_mbim(alt) && 571 if (alt && cdc_ncm_comm_intf_is_mbim(alt))
569 !usb_set_interface(dev->udev, 572 return CDC_NCM_COMM_ALTSETTING_MBIM;
570 intf->cur_altsetting->desc.bInterfaceNumber,
571 CDC_NCM_COMM_ALTSETTING_MBIM))
572 return CDC_NCM_DATA_ALTSETTING_MBIM;
573 } 573 }
574 return CDC_NCM_DATA_ALTSETTING_NCM; 574 return CDC_NCM_COMM_ALTSETTING_NCM;
575} 575}
576EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting); 576EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
577 577
@@ -580,12 +580,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
580 int ret; 580 int ret;
581 581
582 /* MBIM backwards compatible function? */ 582 /* MBIM backwards compatible function? */
583 cdc_ncm_select_altsetting(dev, intf); 583 if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
584 if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
585 return -ENODEV; 584 return -ENODEV;
586 585
587 /* NCM data altsetting is always 1 */ 586 /* The NCM data altsetting is fixed */
588 ret = cdc_ncm_bind_common(dev, intf, 1); 587 ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);
589 588
590 /* 589 /*
591 * We should get an event when network connection is "connected" or 590 * We should get an event when network connection is "connected" or
diff --git a/include/linux/usb/cdc_ncm.h b/include/linux/usb/cdc_ncm.h
index 44b38b92236a..55b6feead93b 100644
--- a/include/linux/usb/cdc_ncm.h
+++ b/include/linux/usb/cdc_ncm.h
@@ -121,7 +121,7 @@ struct cdc_ncm_ctx {
121 u16 connected; 121 u16 connected;
122}; 122};
123 123
124u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf); 124u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
125int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting); 125int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
126void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf); 126void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
127struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign); 127struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);