1 /* 2 * Copyright (c) 2012 Smith Micro Software, Inc. 3 * Copyright (c) 2012 Bjørn Mork <bjorn@mork.no> 4 * 5 * This driver is based on and reuse most of cdc_ncm, which is 6 * Copyright (C) ST-Ericsson 2010-2012 7 * 8 * This program is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU General Public License 10 * version 2 as published by the Free Software Foundation. 11 */ 12 13 #include <linux/module.h> 14 #include <linux/netdevice.h> 15 #include <linux/ethtool.h> 16 #include <linux/if_vlan.h> 17 #include <linux/ip.h> 18 #include <linux/mii.h> 19 #include <linux/usb.h> 20 #include <linux/usb/cdc.h> 21 #include <linux/usb/usbnet.h> 22 #include <linux/usb/cdc-wdm.h> 23 #include <linux/usb/cdc_ncm.h> 24 25 /* driver specific data - must match cdc_ncm usage */ 26 struct cdc_mbim_state { 27 struct cdc_ncm_ctx *ctx; 28 atomic_t pmcount; 29 struct usb_driver *subdriver; 30 struct usb_interface *control; 31 struct usb_interface *data; 32 }; 33 34 /* using a counter to merge subdriver requests with our own into a combined state */ 35 static int cdc_mbim_manage_power(struct usbnet *dev, int on) 36 { 37 struct cdc_mbim_state *info = (void *)&dev->data; 38 int rv = 0; 39 40 dev_dbg(&dev->intf->dev, "%s() pmcount=%d, on=%d\n", __func__, atomic_read(&info->pmcount), on); 41 42 if ((on && atomic_add_return(1, &info->pmcount) == 1) || (!on && atomic_dec_and_test(&info->pmcount))) { 43 /* need autopm_get/put here to ensure the usbcore sees the new value */ 44 rv = usb_autopm_get_interface(dev->intf); 45 if (rv < 0) 46 goto err; 47 dev->intf->needs_remote_wakeup = on; 48 usb_autopm_put_interface(dev->intf); 49 } 50 err: 51 return rv; 52 } 53 54 static int cdc_mbim_wdm_manage_power(struct usb_interface *intf, int status) 55 { 56 struct usbnet *dev = usb_get_intfdata(intf); 57 58 /* can be called while disconnecting */ 59 if (!dev) 60 return 0; 61 62 return cdc_mbim_manage_power(dev, status); 63 } 64 65 66 static int cdc_mbim_bind(struct usbnet *dev, struct usb_interface *intf) 67 { 68 struct cdc_ncm_ctx *ctx; 69 struct usb_driver *subdriver = ERR_PTR(-ENODEV); 70 int ret = -ENODEV; 71 u8 data_altsetting = cdc_ncm_select_altsetting(dev, intf); 72 struct cdc_mbim_state *info = (void *)&dev->data; 73 74 /* Probably NCM, defer for cdc_ncm_bind */ 75 if (!cdc_ncm_comm_intf_is_mbim(intf->cur_altsetting)) 76 goto err; 77 78 ret = cdc_ncm_bind_common(dev, intf, data_altsetting); 79 if (ret) 80 goto err; 81 82 ctx = info->ctx; 83 84 /* The MBIM descriptor and the status endpoint are required */ 85 if (ctx->mbim_desc && dev->status) 86 subdriver = usb_cdc_wdm_register(ctx->control, 87 &dev->status->desc, 88 le16_to_cpu(ctx->mbim_desc->wMaxControlMessage), 89 cdc_mbim_wdm_manage_power); 90 if (IS_ERR(subdriver)) { 91 ret = PTR_ERR(subdriver); 92 cdc_ncm_unbind(dev, intf); 93 goto err; 94 } 95 96 /* can't let usbnet use the interrupt endpoint */ 97 dev->status = NULL; 98 info->subdriver = subdriver; 99 100 /* MBIM cannot do ARP */ 101 dev->net->flags |= IFF_NOARP; 102 103 /* no need to put the VLAN tci in the packet headers */ 104 dev->net->features |= NETIF_F_HW_VLAN_CTAG_TX; 105 err: 106 return ret; 107 } 108 109 static void cdc_mbim_unbind(struct usbnet *dev, struct usb_interface *intf) 110 { 111 struct cdc_mbim_state *info = (void *)&dev->data; 112 struct cdc_ncm_ctx *ctx = info->ctx; 113 114 /* disconnect subdriver from control interface */ 115 if (info->subdriver && info->subdriver->disconnect) 116 info->subdriver->disconnect(ctx->control); 117 info->subdriver = NULL; 118 119 /* let NCM unbind clean up both control and data interface */ 120 cdc_ncm_unbind(dev, intf); 121 } 122 123 124 static struct sk_buff *cdc_mbim_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags) 125 { 126 struct sk_buff *skb_out; 127 struct cdc_mbim_state *info = (void *)&dev->data; 128 struct cdc_ncm_ctx *ctx = info->ctx; 129 __le32 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN); 130 u16 tci = 0; 131 u8 *c; 132 133 if (!ctx) 134 goto error; 135 136 if (skb) { 137 if (skb->len <= ETH_HLEN) 138 goto error; 139 140 /* mapping VLANs to MBIM sessions: 141 * no tag => IPS session <0> 142 * 1 - 255 => IPS session <vlanid> 143 * 256 - 511 => DSS session <vlanid - 256> 144 * 512 - 4095 => unsupported, drop 145 */ 146 vlan_get_tag(skb, &tci); 147 148 switch (tci & 0x0f00) { 149 case 0x0000: /* VLAN ID 0 - 255 */ 150 /* verify that datagram is IPv4 or IPv6 */ 151 skb_reset_mac_header(skb); 152 switch (eth_hdr(skb)->h_proto) { 153 case htons(ETH_P_IP): 154 case htons(ETH_P_IPV6): 155 break; 156 default: 157 goto error; 158 } 159 c = (u8 *)&sign; 160 c[3] = tci; 161 break; 162 case 0x0100: /* VLAN ID 256 - 511 */ 163 sign = cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN); 164 c = (u8 *)&sign; 165 c[3] = tci; 166 break; 167 default: 168 netif_err(dev, tx_err, dev->net, 169 "unsupported tci=0x%04x\n", tci); 170 goto error; 171 } 172 skb_pull(skb, ETH_HLEN); 173 } 174 175 spin_lock_bh(&ctx->mtx); 176 skb_out = cdc_ncm_fill_tx_frame(ctx, skb, sign); 177 spin_unlock_bh(&ctx->mtx); 178 return skb_out; 179 180 error: 181 if (skb) 182 dev_kfree_skb_any(skb); 183 184 return NULL; 185 } 186 187 static struct sk_buff *cdc_mbim_process_dgram(struct usbnet *dev, u8 *buf, size_t len, u16 tci) 188 { 189 __be16 proto = htons(ETH_P_802_3); 190 struct sk_buff *skb = NULL; 191 192 if (tci < 256) { /* IPS session? */ 193 if (len < sizeof(struct iphdr)) 194 goto err; 195 196 switch (*buf & 0xf0) { 197 case 0x40: 198 proto = htons(ETH_P_IP); 199 break; 200 case 0x60: 201 proto = htons(ETH_P_IPV6); 202 break; 203 default: 204 goto err; 205 } 206 } 207 208 skb = netdev_alloc_skb_ip_align(dev->net, len + ETH_HLEN); 209 if (!skb) 210 goto err; 211 212 /* add an ethernet header */ 213 skb_put(skb, ETH_HLEN); 214 skb_reset_mac_header(skb); 215 eth_hdr(skb)->h_proto = proto; 216 memset(eth_hdr(skb)->h_source, 0, ETH_ALEN); 217 memcpy(eth_hdr(skb)->h_dest, dev->net->dev_addr, ETH_ALEN); 218 219 /* add datagram */ 220 memcpy(skb_put(skb, len), buf, len); 221 222 /* map MBIM session to VLAN */ 223 if (tci) 224 vlan_put_tag(skb, htons(ETH_P_8021Q), tci); 225 err: 226 return skb; 227 } 228 229 static int cdc_mbim_rx_fixup(struct usbnet *dev, struct sk_buff *skb_in) 230 { 231 struct sk_buff *skb; 232 struct cdc_mbim_state *info = (void *)&dev->data; 233 struct cdc_ncm_ctx *ctx = info->ctx; 234 int len; 235 int nframes; 236 int x; 237 int offset; 238 struct usb_cdc_ncm_ndp16 *ndp16; 239 struct usb_cdc_ncm_dpe16 *dpe16; 240 int ndpoffset; 241 int loopcount = 50; /* arbitrary max preventing infinite loop */ 242 u8 *c; 243 u16 tci; 244 245 ndpoffset = cdc_ncm_rx_verify_nth16(ctx, skb_in); 246 if (ndpoffset < 0) 247 goto error; 248 249 next_ndp: 250 nframes = cdc_ncm_rx_verify_ndp16(skb_in, ndpoffset); 251 if (nframes < 0) 252 goto error; 253 254 ndp16 = (struct usb_cdc_ncm_ndp16 *)(skb_in->data + ndpoffset); 255 256 switch (ndp16->dwSignature & cpu_to_le32(0x00ffffff)) { 257 case cpu_to_le32(USB_CDC_MBIM_NDP16_IPS_SIGN): 258 c = (u8 *)&ndp16->dwSignature; 259 tci = c[3]; 260 break; 261 case cpu_to_le32(USB_CDC_MBIM_NDP16_DSS_SIGN): 262 c = (u8 *)&ndp16->dwSignature; 263 tci = c[3] + 256; 264 break; 265 default: 266 netif_dbg(dev, rx_err, dev->net, 267 "unsupported NDP signature <0x%08x>\n", 268 le32_to_cpu(ndp16->dwSignature)); 269 goto err_ndp; 270 271 } 272 273 dpe16 = ndp16->dpe16; 274 for (x = 0; x < nframes; x++, dpe16++) { 275 offset = le16_to_cpu(dpe16->wDatagramIndex); 276 len = le16_to_cpu(dpe16->wDatagramLength); 277 278 /* 279 * CDC NCM ch. 3.7 280 * All entries after first NULL entry are to be ignored 281 */ 282 if ((offset == 0) || (len == 0)) { 283 if (!x) 284 goto err_ndp; /* empty NTB */ 285 break; 286 } 287 288 /* sanity checking */ 289 if (((offset + len) > skb_in->len) || (len > ctx->rx_max)) { 290 netif_dbg(dev, rx_err, dev->net, 291 "invalid frame detected (ignored) offset[%u]=%u, length=%u, skb=%p\n", 292 x, offset, len, skb_in); 293 if (!x) 294 goto err_ndp; 295 break; 296 } else { 297 skb = cdc_mbim_process_dgram(dev, skb_in->data + offset, len, tci); 298 if (!skb) 299 goto error; 300 usbnet_skb_return(dev, skb); 301 } 302 } 303 err_ndp: 304 /* are there more NDPs to process? */ 305 ndpoffset = le16_to_cpu(ndp16->wNextNdpIndex); 306 if (ndpoffset && loopcount--) 307 goto next_ndp; 308 309 return 1; 310 error: 311 return 0; 312 } 313 314 static int cdc_mbim_suspend(struct usb_interface *intf, pm_message_t message) 315 { 316 int ret = 0; 317 struct usbnet *dev = usb_get_intfdata(intf); 318 struct cdc_mbim_state *info = (void *)&dev->data; 319 struct cdc_ncm_ctx *ctx = info->ctx; 320 321 if (ctx == NULL) { 322 ret = -1; 323 goto error; 324 } 325 326 /* 327 * Both usbnet_suspend() and subdriver->suspend() MUST return 0 328 * in system sleep context, otherwise, the resume callback has 329 * to recover device from previous suspend failure. 330 */ 331 ret = usbnet_suspend(intf, message); 332 if (ret < 0) 333 goto error; 334 335 if (intf == ctx->control && info->subdriver && info->subdriver->suspend) 336 ret = info->subdriver->suspend(intf, message); 337 if (ret < 0) 338 usbnet_resume(intf); 339 340 error: 341 return ret; 342 } 343 344 static int cdc_mbim_resume(struct usb_interface *intf) 345 { 346 int ret = 0; 347 struct usbnet *dev = usb_get_intfdata(intf); 348 struct cdc_mbim_state *info = (void *)&dev->data; 349 struct cdc_ncm_ctx *ctx = info->ctx; 350 bool callsub = (intf == ctx->control && info->subdriver && info->subdriver->resume); 351 352 if (callsub) 353 ret = info->subdriver->resume(intf); 354 if (ret < 0) 355 goto err; 356 ret = usbnet_resume(intf); 357 if (ret < 0 && callsub && info->subdriver->suspend) 358 info->subdriver->suspend(intf, PMSG_SUSPEND); 359 err: 360 return ret; 361 } 362 363 static const struct driver_info cdc_mbim_info = { 364 .description = "CDC MBIM", 365 .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN, 366 .bind = cdc_mbim_bind, 367 .unbind = cdc_mbim_unbind, 368 .manage_power = cdc_mbim_manage_power, 369 .rx_fixup = cdc_mbim_rx_fixup, 370 .tx_fixup = cdc_mbim_tx_fixup, 371 }; 372 373 /* MBIM and NCM devices should not need a ZLP after NTBs with 374 * dwNtbOutMaxSize length. This driver_info is for the exceptional 375 * devices requiring it anyway, allowing them to be supported without 376 * forcing the performance penalty on all the sane devices. 377 */ 378 static const struct driver_info cdc_mbim_info_zlp = { 379 .description = "CDC MBIM", 380 .flags = FLAG_NO_SETINT | FLAG_MULTI_PACKET | FLAG_WWAN | FLAG_SEND_ZLP, 381 .bind = cdc_mbim_bind, 382 .unbind = cdc_mbim_unbind, 383 .manage_power = cdc_mbim_manage_power, 384 .rx_fixup = cdc_mbim_rx_fixup, 385 .tx_fixup = cdc_mbim_tx_fixup, 386 }; 387 388 static const struct usb_device_id mbim_devs[] = { 389 /* This duplicate NCM entry is intentional. MBIM devices can 390 * be disguised as NCM by default, and this is necessary to 391 * allow us to bind the correct driver_info to such devices. 392 * 393 * bind() will sort out this for us, selecting the correct 394 * entry and reject the other 395 */ 396 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_NCM, USB_CDC_PROTO_NONE), 397 .driver_info = (unsigned long)&cdc_mbim_info, 398 }, 399 /* Sierra Wireless MC7710 need ZLPs */ 400 { USB_DEVICE_AND_INTERFACE_INFO(0x1199, 0x68a2, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), 401 .driver_info = (unsigned long)&cdc_mbim_info_zlp, 402 }, 403 /* HP hs2434 Mobile Broadband Module needs ZLPs */ 404 { USB_DEVICE_AND_INTERFACE_INFO(0x3f0, 0x4b1d, USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), 405 .driver_info = (unsigned long)&cdc_mbim_info_zlp, 406 }, 407 { USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_MBIM, USB_CDC_PROTO_NONE), 408 .driver_info = (unsigned long)&cdc_mbim_info, 409 }, 410 { 411 }, 412 }; 413 MODULE_DEVICE_TABLE(usb, mbim_devs); 414 415 static struct usb_driver cdc_mbim_driver = { 416 .name = "cdc_mbim", 417 .id_table = mbim_devs, 418 .probe = usbnet_probe, 419 .disconnect = usbnet_disconnect, 420 .suspend = cdc_mbim_suspend, 421 .resume = cdc_mbim_resume, 422 .reset_resume = cdc_mbim_resume, 423 .supports_autosuspend = 1, 424 .disable_hub_initiated_lpm = 1, 425 }; 426 module_usb_driver(cdc_mbim_driver); 427 428 MODULE_AUTHOR("Greg Suarez <gsuarez@smithmicro.com>"); 429 MODULE_AUTHOR("Bjørn Mork <bjorn@mork.no>"); 430 MODULE_DESCRIPTION("USB CDC MBIM host driver"); 431 MODULE_LICENSE("GPL"); 432