Skip to content

Commit 50a0ffa

Browse files
bmorkdavem330
authored andcommitted
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 <[email protected]> Reported by: Yu-an Shih <[email protected]> Signed-off-by: Bjørn Mork <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent a563bab commit 50a0ffa

File tree

3 files changed

+55
-17
lines changed

3 files changed

+55
-17
lines changed

drivers/net/usb/cdc_mbim.c

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,15 +107,54 @@ static const struct net_device_ops cdc_mbim_netdev_ops = {
107107
.ndo_vlan_rx_kill_vid = cdc_mbim_rx_kill_vid,
108108
};
109109

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+
*/
114+
static 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+
110140
static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf)
111141
{
112142
struct cdc_ncm_ctx *ctx;
113143
struct usb_driver *subdriver = ERR_PTR(-ENODEV);
114144
int ret = -ENODEV;
115-
u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf);
145+
u8 data_altsetting = 1;
116146
struct cdc_mbim_state *info = (void *)&dev->data;
117147

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 */
119158
if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
120159
goto err;
121160

drivers/net/usb/cdc_ncm.c

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -541,10 +541,10 @@ void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf)
541541
}
542542
EXPORT_SYMBOL_GPL(cdc_ncm_unbind);
543543

544-
/* Select the MBIM altsetting iff it is preferred and available,
545-
* returning the number of the corresponding data interface altsetting
544+
/* Return the number of the MBIM control interface altsetting iff it
545+
* is preferred and available,
546546
*/
547-
u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
547+
u8 cdc_ncm_select_altsetting(struct usb_interface *intf)
548548
{
549549
struct usb_host_interface *alt;
550550

@@ -563,15 +563,15 @@ u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf)
563563
* the rules given in section 6 (USB Device Model) of this
564564
* specification."
565565
*/
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) {
567570
alt = usb_altnum_to_altsetting(intf, CDC_NCM_COMM_ALTSETTING_MBIM);
568-
if (alt && cdc_ncm_comm_intf_is_mbim(alt) &&
569-
!usb_set_interface(dev->udev,
570-
intf->cur_altsetting->desc.bInterfaceNumber,
571-
CDC_NCM_COMM_ALTSETTING_MBIM))
572-
return CDC_NCM_DATA_ALTSETTING_MBIM;
571+
if (alt && cdc_ncm_comm_intf_is_mbim(alt))
572+
return CDC_NCM_COMM_ALTSETTING_MBIM;
573573
}
574-
return CDC_NCM_DATA_ALTSETTING_NCM;
574+
return CDC_NCM_COMM_ALTSETTING_NCM;
575575
}
576576
EXPORT_SYMBOL_GPL(cdc_ncm_select_altsetting);
577577

@@ -580,12 +580,11 @@ static int cdc_ncm_bind(struct usbnet *dev, struct usb_interface *intf)
580580
int ret;
581581

582582
/* MBIM backwards compatible function? */
583-
cdc_ncm_select_altsetting(dev, intf);
584-
if (cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting))
583+
if (cdc_ncm_select_altsetting(intf) != CDC_NCM_COMM_ALTSETTING_NCM)
585584
return -ENODEV;
586585

587-
/* NCM data altsetting is always 1 */
588-
ret = cdc_ncm_bind_common(dev, intf, 1);
586+
/* The NCM data altsetting is fixed */
587+
ret = cdc_ncm_bind_common(dev, intf, CDC_NCM_DATA_ALTSETTING_NCM);
589588

590589
/*
591590
* We should get an event when network connection is "connected" or

include/linux/usb/cdc_ncm.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ struct cdc_ncm_ctx {
121121
u16 connected;
122122
};
123123

124-
u8 cdc_ncm_select_altsetting(struct usbnet *dev, struct usb_interface *intf);
124+
u8 cdc_ncm_select_altsetting(struct usb_interface *intf);
125125
int cdc_ncm_bind_common(struct usbnet *dev, struct usb_interface *intf, u8 data_altsetting);
126126
void cdc_ncm_unbind(struct usbnet *dev, struct usb_interface *intf);
127127
struct sk_buff *cdc_ncm_fill_tx_frame(struct usbnet *dev, struct sk_buff *skb, __le32 sign);

0 commit comments

Comments
 (0)