0001
0002
0003
0004
0005
0006
0007
0008 #include <linux/module.h>
0009 #include <linux/netdevice.h>
0010 #include <linux/etherdevice.h>
0011 #include <linux/ctype.h>
0012 #include <linux/ethtool.h>
0013 #include <linux/workqueue.h>
0014 #include <linux/mii.h>
0015 #include <linux/usb.h>
0016 #include <linux/crc32.h>
0017 #include <linux/usb/cdc.h>
0018 #include <linux/usb/usbnet.h>
0019 #include <linux/gfp.h>
0020 #include <linux/if_vlan.h>
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037 #define EEM_HEAD 2
0038
0039
0040
0041 static void eem_linkcmd_complete(struct urb *urb)
0042 {
0043 dev_kfree_skb(urb->context);
0044 usb_free_urb(urb);
0045 }
0046
0047 static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb)
0048 {
0049 struct urb *urb;
0050 int status;
0051
0052 urb = usb_alloc_urb(0, GFP_ATOMIC);
0053 if (!urb)
0054 goto fail;
0055
0056 usb_fill_bulk_urb(urb, dev->udev, dev->out,
0057 skb->data, skb->len, eem_linkcmd_complete, skb);
0058
0059 status = usb_submit_urb(urb, GFP_ATOMIC);
0060 if (status) {
0061 usb_free_urb(urb);
0062 fail:
0063 dev_kfree_skb(skb);
0064 netdev_warn(dev->net, "link cmd failure\n");
0065 return;
0066 }
0067 }
0068
0069 static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
0070 {
0071 int status = 0;
0072
0073 status = usbnet_get_endpoints(dev, intf);
0074 if (status < 0)
0075 return status;
0076
0077
0078
0079 dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN;
0080 dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
0081
0082 return 0;
0083 }
0084
0085
0086
0087
0088
0089 static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
0090 gfp_t flags)
0091 {
0092 struct sk_buff *skb2 = NULL;
0093 u16 len = skb->len;
0094 u32 crc = 0;
0095 int padlen = 0;
0096
0097
0098
0099
0100
0101
0102
0103 if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
0104 padlen += 2;
0105
0106 if (!skb_cloned(skb)) {
0107 int headroom = skb_headroom(skb);
0108 int tailroom = skb_tailroom(skb);
0109
0110 if ((tailroom >= ETH_FCS_LEN + padlen) &&
0111 (headroom >= EEM_HEAD))
0112 goto done;
0113
0114 if ((headroom + tailroom)
0115 > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
0116 skb->data = memmove(skb->head +
0117 EEM_HEAD,
0118 skb->data,
0119 skb->len);
0120 skb_set_tail_pointer(skb, len);
0121 goto done;
0122 }
0123 }
0124
0125 skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
0126 dev_kfree_skb_any(skb);
0127 if (!skb2)
0128 return NULL;
0129
0130 skb = skb2;
0131
0132 done:
0133
0134 crc = crc32_le(~0, skb->data, skb->len);
0135 crc = ~crc;
0136
0137 put_unaligned_le32(crc, skb_put(skb, 4));
0138
0139
0140
0141
0142
0143
0144 len = skb->len;
0145 put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
0146
0147
0148 if (padlen)
0149 put_unaligned_le16(0, skb_put(skb, 2));
0150
0151 return skb;
0152 }
0153
0154 static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
0155 {
0156
0157
0158
0159
0160
0161
0162
0163
0164
0165
0166
0167 do {
0168 struct sk_buff *skb2 = NULL;
0169 u16 header;
0170 u16 len = 0;
0171
0172
0173 if (skb->len < EEM_HEAD)
0174 return 0;
0175
0176
0177
0178
0179
0180
0181 header = get_unaligned_le16(skb->data);
0182 skb_pull(skb, EEM_HEAD);
0183
0184
0185
0186
0187
0188
0189
0190 if (header & BIT(15)) {
0191 u16 bmEEMCmd;
0192
0193
0194
0195
0196
0197
0198
0199
0200 if (header & BIT(14)) {
0201 netdev_dbg(dev->net, "reserved command %04x\n",
0202 header);
0203 continue;
0204 }
0205
0206 bmEEMCmd = (header >> 11) & 0x7;
0207 switch (bmEEMCmd) {
0208
0209
0210 case 0:
0211 len = header & 0x7FF;
0212
0213
0214 if (skb->len < len)
0215 return 0;
0216
0217 skb2 = skb_clone(skb, GFP_ATOMIC);
0218 if (unlikely(!skb2))
0219 goto next;
0220 skb_trim(skb2, len);
0221 put_unaligned_le16(BIT(15) | BIT(11) | len,
0222 skb_push(skb2, 2));
0223 eem_linkcmd(dev, skb2);
0224 break;
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 case 2:
0235 usbnet_device_suggests_idle(dev);
0236 continue;
0237 case 3:
0238 case 4:
0239 continue;
0240
0241
0242
0243
0244
0245
0246 case 1:
0247 case 5:
0248 default:
0249 netdev_warn(dev->net,
0250 "unexpected link command %d\n",
0251 bmEEMCmd);
0252 continue;
0253 }
0254
0255 } else {
0256 u32 crc, crc2;
0257 int is_last;
0258
0259
0260 if (header == 0)
0261 continue;
0262
0263
0264
0265
0266
0267
0268
0269 len = header & 0x3FFF;
0270
0271
0272 if (skb->len < len)
0273 return 0;
0274
0275
0276 if (len < (ETH_HLEN + ETH_FCS_LEN))
0277 goto next;
0278
0279
0280
0281
0282
0283
0284
0285
0286 is_last = (len == skb->len);
0287 if (is_last)
0288 skb2 = skb;
0289 else {
0290 skb2 = skb_clone(skb, GFP_ATOMIC);
0291 if (unlikely(!skb2))
0292 return 0;
0293 }
0294
0295
0296
0297
0298
0299
0300
0301 if (header & BIT(14)) {
0302 crc = get_unaligned_le32(skb2->data
0303 + len - ETH_FCS_LEN);
0304 crc2 = ~crc32_le(~0, skb2->data, skb2->len
0305 - ETH_FCS_LEN);
0306 } else {
0307 crc = get_unaligned_be32(skb2->data
0308 + len - ETH_FCS_LEN);
0309 crc2 = 0xdeadbeef;
0310 }
0311 skb_trim(skb2, len - ETH_FCS_LEN);
0312
0313 if (is_last)
0314 return crc == crc2;
0315
0316 if (unlikely(crc != crc2)) {
0317 dev->net->stats.rx_errors++;
0318 dev_kfree_skb_any(skb2);
0319 } else
0320 usbnet_skb_return(dev, skb2);
0321 }
0322
0323 next:
0324 skb_pull(skb, len);
0325 } while (skb->len);
0326
0327 return 1;
0328 }
0329
0330 static const struct driver_info eem_info = {
0331 .description = "CDC EEM Device",
0332 .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
0333 .bind = eem_bind,
0334 .rx_fixup = eem_rx_fixup,
0335 .tx_fixup = eem_tx_fixup,
0336 };
0337
0338
0339
0340 static const struct usb_device_id products[] = {
0341 {
0342 USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
0343 USB_CDC_PROTO_EEM),
0344 .driver_info = (unsigned long) &eem_info,
0345 },
0346 {
0347
0348 },
0349 };
0350 MODULE_DEVICE_TABLE(usb, products);
0351
0352 static struct usb_driver eem_driver = {
0353 .name = "cdc_eem",
0354 .id_table = products,
0355 .probe = usbnet_probe,
0356 .disconnect = usbnet_disconnect,
0357 .suspend = usbnet_suspend,
0358 .resume = usbnet_resume,
0359 .disable_hub_initiated_lpm = 1,
0360 };
0361
0362 module_usb_driver(eem_driver);
0363
0364 MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
0365 MODULE_DESCRIPTION("USB CDC EEM");
0366 MODULE_LICENSE("GPL");