diff options
author | Bjørn Mork <bjorn@mork.no> | 2013-03-12 22:25:17 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-03-13 11:33:42 -0400 |
commit | b701f16dd490d3f346724050f17d60beda094998 (patch) | |
tree | 7fda5d6dbe6187a87f06494fbe2e69c8bc6bdd3a /drivers/net/usb/qmi_wwan.c | |
parent | 5b9e12dbf92b441b37136ea71dac59f05f2673a9 (diff) |
net: qmi_wwan: set correct altsetting for Gobi 1K devices
commit bd877e4 ("net: qmi_wwan: use a single bind function for
all device types") made Gobi 1K devices fail probing.
Using the number of endpoints in the default altsetting to decide
whether the function use one or two interfaces is wrong. Other
altsettings may provide more endpoints.
With Gobi 1K devices, USB interface #3's altsetting is 0 by default, but
altsetting 0 only provides one interrupt endpoint and is not sufficent
for QMI. Altsetting 1 provides all 3 endpoints required for qmi_wwan
and works with QMI. Gobi 1K layout for intf#3 is:
Interface Descriptor: 255/255/255
bInterfaceNumber 3
bAlternateSetting 0
Endpoint Descriptor: Interrupt IN
Interface Descriptor: 255/255/255
bInterfaceNumber 3
bAlternateSetting 1
Endpoint Descriptor: Interrupt IN
Endpoint Descriptor: Bulk IN
Endpoint Descriptor: Bulk OUT
Prior to commit bd877e4, we would call usbnet_get_endpoints
before giving up finding enough endpoints. Removing the early
endpoint number test and the strict functional descriptor
requirement allow qmi_wwan_bind to continue until
usbnet_get_endpoints has made the final attempt to collect
endpoints. This restores the behaviour from before commit
bd877e4 without losing the added benefit of using a single bind
function.
The driver has always required a CDC Union functional descriptor
for two-interface functions. Using the existence of this
descriptor to detect two-interface functions is the logically
correct method.
Reported-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: Bjørn Mork <bjorn@mork.no>
Tested-by: Dan Williams <dcbw@redhat.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb/qmi_wwan.c')
-rw-r--r-- | drivers/net/usb/qmi_wwan.c | 49 |
1 files changed, 16 insertions, 33 deletions
diff --git a/drivers/net/usb/qmi_wwan.c b/drivers/net/usb/qmi_wwan.c index efb5c7c33a28..968d5d50751d 100644 --- a/drivers/net/usb/qmi_wwan.c +++ b/drivers/net/usb/qmi_wwan.c | |||
@@ -139,16 +139,9 @@ static int qmi_wwan_bind(struct usbnet *dev, struct usb_interface *intf) | |||
139 | 139 | ||
140 | BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); | 140 | BUILD_BUG_ON((sizeof(((struct usbnet *)0)->data) < sizeof(struct qmi_wwan_state))); |
141 | 141 | ||
142 | /* control and data is shared? */ | 142 | /* set up initial state */ |
143 | if (intf->cur_altsetting->desc.bNumEndpoints == 3) { | 143 | info->control = intf; |
144 | info->control = intf; | 144 | info->data = intf; |
145 | info->data = intf; | ||
146 | goto shared; | ||
147 | } | ||
148 | |||
149 | /* else require a single interrupt status endpoint on control intf */ | ||
150 | if (intf->cur_altsetting->desc.bNumEndpoints != 1) | ||
151 | goto err; | ||
152 | 145 | ||
153 | /* and a number of CDC descriptors */ | 146 | /* and a number of CDC descriptors */ |
154 | while (len > 3) { | 147 | while (len > 3) { |
@@ -207,25 +200,14 @@ next_desc: | |||
207 | buf += h->bLength; | 200 | buf += h->bLength; |
208 | } | 201 | } |
209 | 202 | ||
210 | /* did we find all the required ones? */ | 203 | /* Use separate control and data interfaces if we found a CDC Union */ |
211 | if (!(found & (1 << USB_CDC_HEADER_TYPE)) || | 204 | if (cdc_union) { |
212 | !(found & (1 << USB_CDC_UNION_TYPE))) { | 205 | info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); |
213 | dev_err(&intf->dev, "CDC functional descriptors missing\n"); | 206 | if (desc->bInterfaceNumber != cdc_union->bMasterInterface0 || !info->data) { |
214 | goto err; | 207 | dev_err(&intf->dev, "bogus CDC Union: master=%u, slave=%u\n", |
215 | } | 208 | cdc_union->bMasterInterface0, cdc_union->bSlaveInterface0); |
216 | 209 | goto err; | |
217 | /* verify CDC Union */ | 210 | } |
218 | if (desc->bInterfaceNumber != cdc_union->bMasterInterface0) { | ||
219 | dev_err(&intf->dev, "bogus CDC Union: master=%u\n", cdc_union->bMasterInterface0); | ||
220 | goto err; | ||
221 | } | ||
222 | |||
223 | /* need to save these for unbind */ | ||
224 | info->control = intf; | ||
225 | info->data = usb_ifnum_to_if(dev->udev, cdc_union->bSlaveInterface0); | ||
226 | if (!info->data) { | ||
227 | dev_err(&intf->dev, "bogus CDC Union: slave=%u\n", cdc_union->bSlaveInterface0); | ||
228 | goto err; | ||
229 | } | 211 | } |
230 | 212 | ||
231 | /* errors aren't fatal - we can live with the dynamic address */ | 213 | /* errors aren't fatal - we can live with the dynamic address */ |
@@ -235,11 +217,12 @@ next_desc: | |||
235 | } | 217 | } |
236 | 218 | ||
237 | /* claim data interface and set it up */ | 219 | /* claim data interface and set it up */ |
238 | status = usb_driver_claim_interface(driver, info->data, dev); | 220 | if (info->control != info->data) { |
239 | if (status < 0) | 221 | status = usb_driver_claim_interface(driver, info->data, dev); |
240 | goto err; | 222 | if (status < 0) |
223 | goto err; | ||
224 | } | ||
241 | 225 | ||
242 | shared: | ||
243 | status = qmi_wwan_register_subdriver(dev); | 226 | status = qmi_wwan_register_subdriver(dev); |
244 | if (status < 0 && info->control != info->data) { | 227 | if (status < 0 && info->control != info->data) { |
245 | usb_set_intfdata(info->data, NULL); | 228 | usb_set_intfdata(info->data, NULL); |