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 return status;
88
89
90
91 dev->net->hard_header_len += EEM_HEAD + ETH_FCS_LEN + VLAN_HLEN;
92 dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len;
93
94 return 0;
95}
96
97
98
99
100
101static struct sk_buff *eem_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
102 gfp_t flags)
103{
104 struct sk_buff *skb2 = NULL;
105 u16 len = skb->len;
106 u32 crc = 0;
107 int padlen = 0;
108
109
110
111
112
113
114
115 if (!((len + EEM_HEAD + ETH_FCS_LEN) % dev->maxpacket))
116 padlen += 2;
117
118 if (!skb_cloned(skb)) {
119 int headroom = skb_headroom(skb);
120 int tailroom = skb_tailroom(skb);
121
122 if ((tailroom >= ETH_FCS_LEN + padlen) &&
123 (headroom >= EEM_HEAD))
124 goto done;
125
126 if ((headroom + tailroom)
127 > (EEM_HEAD + ETH_FCS_LEN + padlen)) {
128 skb->data = memmove(skb->head +
129 EEM_HEAD,
130 skb->data,
131 skb->len);
132 skb_set_tail_pointer(skb, len);
133 goto done;
134 }
135 }
136
137 skb2 = skb_copy_expand(skb, EEM_HEAD, ETH_FCS_LEN + padlen, flags);
138 if (!skb2)
139 return NULL;
140
141 dev_kfree_skb_any(skb);
142 skb = skb2;
143
144done:
145
146 crc = crc32_le(~0, skb->data, skb->len);
147 crc = ~crc;
148
149 put_unaligned_le32(crc, skb_put(skb, 4));
150
151
152
153
154
155
156 len = skb->len;
157 put_unaligned_le16(BIT(14) | len, skb_push(skb, 2));
158
159
160 if (padlen)
161 put_unaligned_le16(0, skb_put(skb, 2));
162
163 return skb;
164}
165
166static int eem_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
167{
168
169
170
171
172
173
174
175
176
177
178
179 do {
180 struct sk_buff *skb2 = NULL;
181 u16 header;
182 u16 len = 0;
183
184
185 if (skb->len < EEM_HEAD)
186 return 0;
187
188
189
190
191
192
193 header = get_unaligned_le16(skb->data);
194 skb_pull(skb, EEM_HEAD);
195
196
197
198
199
200
201
202 if (header & BIT(15)) {
203 u16 bmEEMCmd;
204
205
206
207
208
209
210
211
212 if (header & BIT(14)) {
213 netdev_dbg(dev->net, "reserved command %04x\n",
214 header);
215 continue;
216 }
217
218 bmEEMCmd = (header >> 11) & 0x7;
219 switch (bmEEMCmd) {
220
221
222 case 0:
223 len = header & 0x7FF;
224
225
226 if (skb->len < len)
227 return 0;
228
229 skb2 = skb_clone(skb, GFP_ATOMIC);
230 if (unlikely(!skb2))
231 goto next;
232 skb_trim(skb2, len);
233 put_unaligned_le16(BIT(15) | (1 << 11) | len,
234 skb_push(skb2, 2));
235 eem_linkcmd(dev, skb2);
236 break;
237
238
239
240
241
242
243
244
245
246 case 2:
247 usbnet_device_suggests_idle(dev);
248 continue;
249 case 3:
250 case 4:
251 continue;
252
253
254
255
256
257
258 case 1:
259 case 5:
260 default:
261 netdev_warn(dev->net,
262 "unexpected link command %d\n",
263 bmEEMCmd);
264 continue;
265 }
266
267 } else {
268 u32 crc, crc2;
269 int is_last;
270
271
272 if (header == 0)
273 continue;
274
275
276
277
278
279
280
281 len = header & 0x3FFF;
282
283
284 if (skb->len < len)
285 return 0;
286
287
288 if (len < (ETH_HLEN + ETH_FCS_LEN))
289 goto next;
290
291
292
293
294
295
296
297
298 is_last = (len == skb->len);
299 if (is_last)
300 skb2 = skb;
301 else {
302 skb2 = skb_clone(skb, GFP_ATOMIC);
303 if (unlikely(!skb2))
304 return 0;
305 }
306
307
308
309
310
311
312
313 if (header & BIT(14)) {
314 crc = get_unaligned_le32(skb2->data
315 + len - ETH_FCS_LEN);
316 crc2 = ~crc32_le(~0, skb2->data, skb2->len
317 - ETH_FCS_LEN);
318 } else {
319 crc = get_unaligned_be32(skb2->data
320 + len - ETH_FCS_LEN);
321 crc2 = 0xdeadbeef;
322 }
323 skb_trim(skb2, len - ETH_FCS_LEN);
324
325 if (is_last)
326 return crc == crc2;
327
328 if (unlikely(crc != crc2)) {
329 dev->net->stats.rx_errors++;
330 dev_kfree_skb_any(skb2);
331 } else
332 usbnet_skb_return(dev, skb2);
333 }
334
335next:
336 skb_pull(skb, len);
337 } while (skb->len);
338
339 return 1;
340}
341
342static const struct driver_info eem_info = {
343 .description = "CDC EEM Device",
344 .flags = FLAG_ETHER | FLAG_POINTTOPOINT,
345 .bind = eem_bind,
346 .rx_fixup = eem_rx_fixup,
347 .tx_fixup = eem_tx_fixup,
348};
349
350
351
352static const struct usb_device_id products[] = {
353{
354 USB_INTERFACE_INFO(USB_CLASS_COMM, USB_CDC_SUBCLASS_EEM,
355 USB_CDC_PROTO_EEM),
356 .driver_info = (unsigned long) &eem_info,
357},
358{
359
360},
361};
362MODULE_DEVICE_TABLE(usb, products);
363
364static struct usb_driver eem_driver = {
365 .name = "cdc_eem",
366 .id_table = products,
367 .probe = usbnet_probe,
368 .disconnect = usbnet_disconnect,
369 .suspend = usbnet_suspend,
370 .resume = usbnet_resume,
371 .disable_hub_initiated_lpm = 1,
372};
373
374module_usb_driver(eem_driver);
375
376MODULE_AUTHOR("Omar Laazimani <omar.oberthur@gmail.com>");
377MODULE_DESCRIPTION("USB CDC EEM");
378MODULE_LICENSE("GPL");
379