1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20#include <linux/module.h>
21#include <linux/netdevice.h>
22#include <linux/etherdevice.h>
23#include <linux/ctype.h>
24#include <linux/ethtool.h>
25#include <linux/workqueue.h>
26#include <linux/mii.h>
27#include <linux/usb.h>
28#include <linux/crc32.h>
29#include <linux/usb/cdc.h>
30#include <linux/usb/usbnet.h>
31#include <linux/gfp.h>
32#include <linux/if_vlan.h>
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49#define EEM_HEAD 2
50
51
52
53static void eem_linkcmd_complete(struct urb *urb)
54{
55 dev_kfree_skb(urb->context);
56 usb_free_urb(urb);
57}
58
59static void eem_linkcmd(struct usbnet *dev, struct sk_buff *skb)
60{
61 struct urb *urb;
62 int status;
63
64 urb = usb_alloc_urb(0, GFP_ATOMIC);
65 if (!urb)
66 goto fail;
67
68 usb_fill_bulk_urb(urb, dev->udev, dev->out,
69 skb->data, skb->len, eem_linkcmd_complete, skb);
70
71 status = usb_submit_urb(urb, GFP_ATOMIC);
72 if (status) {
73 usb_free_urb(urb);
74fail:
75 dev_kfree_skb(skb);
76 netdev_warn(dev->net, "link cmd failure\n");
77 return;
78 }
79}
80
81static int eem_bind(struct usbnet *dev, struct usb_interface *intf)
82{
83 int status = 0;
84
85 status = usbnet_get_endpoints(dev, intf);
86 if (status < 0) {
87 usb_set_intfdata(intf, NULL);
88 usb_driver_release_interface(driver_of(intf), intf);
89 return status;
90 }
91
92
93
94 dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN;
95 dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
96
97 return 0;
98}
99
100
101
102
103
104static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
105 gfp_t flags)
106{
107 struct sk_buff *skb2 = NULL;
108 u16 len = skb->len;
109 u32 crc = 0;
110 int padlen = 0;
111
112
113
114
115
116
117
118 if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
119 padlen += 2;
120
121 if (!skb_cloned(skb)) {
122 int headroom = skb_headroom(skb);
123 int tailroom = skb_tailroom(skb);
124
125 if ((tailroom >= ETH_FCS_LEN + padlen) &&
126 (headroom >= EEM_HEAD))
127 goto done;
128
129 if ((headroom + tailroom)
130 > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
131 skb->data = memmove(skb->head +
132 EEM_HEAD,
133 skb->data,
134 skb->len);
135 skb_set_tail_pointer(skb, len);
136 goto done;
137 }
138 }
139
140 skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
141 if (!skb2)
142 return NULL;
143
144 dev_kfree_skb_any(skb);
145 skb = skb2;
146
147done:
148
149 crc = crc32_le(~0, skb->data, skb->len);
150 crc = ~crc;
151
152 put_unaligned_le32(crc, skb_put(skb, 4));
153
154
155
156
157
158
159 len = skb->len;
160 put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
161
162
163 if (padlen)
164 put_unaligned_le16(0, skb_put(skb, 2));
165
166 return skb;
167}
168
169static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
170{
171
172
173
174
175
176
177
178
179
180
181
182 do {
183 struct sk_buff *skb2 = NULL;
184 u16 header;
185 u16 len = 0;
186
187
188 if (skb->len < EEM_HEAD)
189 return 0;
190
191
192
193
194
195
196 header = get_unaligned_le16(skb->data);
197 skb_pull(skb, EEM_HEAD);
198
199
200
201
202
203
204
205 if (header & BIT(15)) {
206 u16 bmEEMCmd;
207
208
209
210
211
212
213
214
215 if (header & BIT(14)) {
216 netdev_dbg(dev->net, "reserved command %04x\n",
217 header);
218 continue;
219 }
220
221 bmEEMCmd = (header >> 11) & 0x7;
222 switch (bmEEMCmd) {
223
224
225 case 0:
226 len = header & 0x7FF;
227
228
229 if (skb->len < len)
230 return 0;
231
232 skb2 = skb_clone(skb, GFP_ATOMIC);
233 if (unlikely(!skb2))
234 goto next;
235 skb_trim(skb2, len);
236 put_unaligned_le16(BIT(15) | (1 << 11) | len,
237 skb_push(skb2, 2));
238 eem_linkcmd(dev, skb2);
239 break;
240
241
242
243
244
245
246
247
248
249 case 2:
250 usbnet_device_suggests_idle(dev);
251 continue;
252 case 3:
253 case 4:
254 continue;
255
256
257
258
259
260
261 case 1:
262 case 5:
263 default:
264 netdev_warn(dev->net,
265 "unexpected link command %d\n",
266 bmEEMCmd);
267 continue;
268 }
269
270 } else {
271 u32 crc, crc2;
272 int is_last;
273
274
275 if (header == 0)
276 continue;
277
278
279
280
281
282
283
284 len = header & 0x3FFF;
285
286
287 if (skb->len < len)
288 return 0;
289
290
291 if (len < (ETH_HLEN + ETH_FCS_LEN))
292 goto next;
293
294
295
296
297
298
299
300
301 is_last = (len == skb->len);
302 if (is_last)
303 skb2 = skb;
304 else {
305 skb2 = skb_clone(skb, GFP_ATOMIC);
306 if (unlikely(!skb2))
307 return 0;
308 }
309
310
311
312
313
314
315
316 if (header & BIT(14)) {
317 crc = get_unaligned_le32(skb2->data
318 + len - ETH_FCS_LEN);
319 crc2 = ~crc32_le(~0, skb2->data, skb2->len
320 - ETH_FCS_LEN);
321 } else {
322 crc = get_unaligned_be32(skb2->data
323 + len - ETH_FCS_LEN);
324 crc2 = 0xdeadbeef;
325 }
326 skb_trim(skb2, len - ETH_FCS_LEN);
327
328 if (is_last)
329 return crc == crc2;
330
331 if (unlikely(crc != crc2)) {
332 dev->net->stats.rx_errors++;
333 dev_kfree_skb_any(skb2);
334 } else
335 usbnet_skb_return(dev, skb2);
336 }
337
338next:
339 skb_pull(skb, len);
340 } while (skb->len);
341
342 return 1;
343}
344
345static const struct driver_info eem_info = {
346 .description = "CDC EEM Device",
347 .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
348 .bind = eem_bind,
349 .rx_fixup = eem_rx_fixup,
350 .tx_fixup = eem_tx_fixup,
351};
352
353
354
355static const struct usb_device_id products[] = {
356{
357 USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
358 USB_CDC_PROTO_EEM),
359 .driver_info = (unsigned long) &eem_info,
360},
361{
362
363},
364};
365MODULE_DEVICE_TABLE(usb, products);
366
367static struct usb_driver eem_driver = {
368 .name = "cdc_eem",
369 .id_table = products,
370 .probe = usbnet_probe,
371 .disconnect = usbnet_disconnect,
372 .suspend = usbnet_suspend,
373 .resume = usbnet_resume,
374 .disable_hub_initiated_lpm = 1,
375};
376
377module_usb_driver(eem_driver);
378
379MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
380MODULE_DESCRIPTION("USB CDC EEM");
381MODULE_LICENSE("GPL");
382