blob: 2bf565e8d0b3ed35e6af836663e93a65fabb74a2 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * net/core/ethtool.c - Ethtool ioctl handler
3 * Copyright (c) 2003 Matthew Wilcox <matthew@wil.cx>
4 *
5 * This file is where we call all the ethtool_ops commands to get
Matthew Wilcox61a44b92007-07-31 14:00:02 -07006 * the information ethtool needs.
Linus Torvalds1da177e2005-04-16 15:20:36 -07007 *
Matthew Wilcox61a44b92007-07-31 14:00:02 -07008 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
Linus Torvalds1da177e2005-04-16 15:20:36 -070012 */
13
14#include <linux/module.h>
15#include <linux/types.h>
Randy Dunlap4fc268d2006-01-11 12:17:47 -080016#include <linux/capability.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070017#include <linux/errno.h>
18#include <linux/ethtool.h>
19#include <linux/netdevice.h>
20#include <asm/uaccess.h>
21
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +090022/*
Linus Torvalds1da177e2005-04-16 15:20:36 -070023 * Some useful ethtool_ops methods that're device independent.
24 * If we find that all drivers want to do the same thing here,
25 * we can turn these into dev_() function calls.
26 */
27
28u32 ethtool_op_get_link(struct net_device *dev)
29{
30 return netif_carrier_ok(dev) ? 1 : 0;
31}
32
33u32 ethtool_op_get_tx_csum(struct net_device *dev)
34{
Herbert Xu8648b302006-06-17 22:06:05 -070035 return (dev->features & NETIF_F_ALL_CSUM) != 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070036}
37
38int ethtool_op_set_tx_csum(struct net_device *dev, u32 data)
39{
40 if (data)
41 dev->features |= NETIF_F_IP_CSUM;
42 else
43 dev->features &= ~NETIF_F_IP_CSUM;
44
45 return 0;
46}
47
Jon Mason69f6a0f2005-05-29 20:27:24 -070048int ethtool_op_set_tx_hw_csum(struct net_device *dev, u32 data)
49{
50 if (data)
51 dev->features |= NETIF_F_HW_CSUM;
52 else
53 dev->features &= ~NETIF_F_HW_CSUM;
54
55 return 0;
56}
Michael Chan6460d942007-07-14 19:07:52 -070057
58int ethtool_op_set_tx_ipv6_csum(struct net_device *dev, u32 data)
59{
60 if (data)
61 dev->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM;
62 else
63 dev->features &= ~(NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM);
64
65 return 0;
66}
67
Linus Torvalds1da177e2005-04-16 15:20:36 -070068u32 ethtool_op_get_sg(struct net_device *dev)
69{
70 return (dev->features & NETIF_F_SG) != 0;
71}
72
73int ethtool_op_set_sg(struct net_device *dev, u32 data)
74{
75 if (data)
76 dev->features |= NETIF_F_SG;
77 else
78 dev->features &= ~NETIF_F_SG;
79
80 return 0;
81}
82
83u32 ethtool_op_get_tso(struct net_device *dev)
84{
85 return (dev->features & NETIF_F_TSO) != 0;
86}
87
88int ethtool_op_set_tso(struct net_device *dev, u32 data)
89{
90 if (data)
91 dev->features |= NETIF_F_TSO;
92 else
93 dev->features &= ~NETIF_F_TSO;
94
95 return 0;
96}
97
Jon Wetzela6f9a702005-08-20 17:15:54 -070098int ethtool_op_get_perm_addr(struct net_device *dev, struct ethtool_perm_addr *addr, u8 *data)
99{
100 unsigned char len = dev->addr_len;
101 if ( addr->size < len )
102 return -ETOOSMALL;
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900103
Jon Wetzela6f9a702005-08-20 17:15:54 -0700104 addr->size = len;
105 memcpy(data, dev->perm_addr, len);
106 return 0;
107}
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900108
Jon Wetzela6f9a702005-08-20 17:15:54 -0700109
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700110u32 ethtool_op_get_ufo(struct net_device *dev)
111{
112 return (dev->features & NETIF_F_UFO) != 0;
113}
114
115int ethtool_op_set_ufo(struct net_device *dev, u32 data)
116{
117 if (data)
118 dev->features |= NETIF_F_UFO;
119 else
120 dev->features &= ~NETIF_F_UFO;
121 return 0;
122}
123
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124/* Handlers for each ethtool command */
125
126static int ethtool_get_settings(struct net_device *dev, void __user *useraddr)
127{
128 struct ethtool_cmd cmd = { ETHTOOL_GSET };
129 int err;
130
131 if (!dev->ethtool_ops->get_settings)
132 return -EOPNOTSUPP;
133
134 err = dev->ethtool_ops->get_settings(dev, &cmd);
135 if (err < 0)
136 return err;
137
138 if (copy_to_user(useraddr, &cmd, sizeof(cmd)))
139 return -EFAULT;
140 return 0;
141}
142
143static int ethtool_set_settings(struct net_device *dev, void __user *useraddr)
144{
145 struct ethtool_cmd cmd;
146
147 if (!dev->ethtool_ops->set_settings)
148 return -EOPNOTSUPP;
149
150 if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
151 return -EFAULT;
152
153 return dev->ethtool_ops->set_settings(dev, &cmd);
154}
155
156static int ethtool_get_drvinfo(struct net_device *dev, void __user *useraddr)
157{
158 struct ethtool_drvinfo info;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700159 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700160
161 if (!ops->get_drvinfo)
162 return -EOPNOTSUPP;
163
164 memset(&info, 0, sizeof(info));
165 info.cmd = ETHTOOL_GDRVINFO;
166 ops->get_drvinfo(dev, &info);
167
168 if (ops->self_test_count)
169 info.testinfo_len = ops->self_test_count(dev);
170 if (ops->get_stats_count)
171 info.n_stats = ops->get_stats_count(dev);
172 if (ops->get_regs_len)
173 info.regdump_len = ops->get_regs_len(dev);
174 if (ops->get_eeprom_len)
175 info.eedump_len = ops->get_eeprom_len(dev);
176
177 if (copy_to_user(useraddr, &info, sizeof(info)))
178 return -EFAULT;
179 return 0;
180}
181
182static int ethtool_get_regs(struct net_device *dev, char __user *useraddr)
183{
184 struct ethtool_regs regs;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700185 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700186 void *regbuf;
187 int reglen, ret;
188
189 if (!ops->get_regs || !ops->get_regs_len)
190 return -EOPNOTSUPP;
191
192 if (copy_from_user(&regs, useraddr, sizeof(regs)))
193 return -EFAULT;
194
195 reglen = ops->get_regs_len(dev);
196 if (regs.len > reglen)
197 regs.len = reglen;
198
199 regbuf = kmalloc(reglen, GFP_USER);
200 if (!regbuf)
201 return -ENOMEM;
202
203 ops->get_regs(dev, &regs, regbuf);
204
205 ret = -EFAULT;
206 if (copy_to_user(useraddr, &regs, sizeof(regs)))
207 goto out;
208 useraddr += offsetof(struct ethtool_regs, data);
209 if (copy_to_user(useraddr, regbuf, regs.len))
210 goto out;
211 ret = 0;
212
213 out:
214 kfree(regbuf);
215 return ret;
216}
217
218static int ethtool_get_wol(struct net_device *dev, char __user *useraddr)
219{
220 struct ethtool_wolinfo wol = { ETHTOOL_GWOL };
221
222 if (!dev->ethtool_ops->get_wol)
223 return -EOPNOTSUPP;
224
225 dev->ethtool_ops->get_wol(dev, &wol);
226
227 if (copy_to_user(useraddr, &wol, sizeof(wol)))
228 return -EFAULT;
229 return 0;
230}
231
232static int ethtool_set_wol(struct net_device *dev, char __user *useraddr)
233{
234 struct ethtool_wolinfo wol;
235
236 if (!dev->ethtool_ops->set_wol)
237 return -EOPNOTSUPP;
238
239 if (copy_from_user(&wol, useraddr, sizeof(wol)))
240 return -EFAULT;
241
242 return dev->ethtool_ops->set_wol(dev, &wol);
243}
244
245static int ethtool_get_msglevel(struct net_device *dev, char __user *useraddr)
246{
247 struct ethtool_value edata = { ETHTOOL_GMSGLVL };
248
249 if (!dev->ethtool_ops->get_msglevel)
250 return -EOPNOTSUPP;
251
252 edata.data = dev->ethtool_ops->get_msglevel(dev);
253
254 if (copy_to_user(useraddr, &edata, sizeof(edata)))
255 return -EFAULT;
256 return 0;
257}
258
259static int ethtool_set_msglevel(struct net_device *dev, char __user *useraddr)
260{
261 struct ethtool_value edata;
262
263 if (!dev->ethtool_ops->set_msglevel)
264 return -EOPNOTSUPP;
265
266 if (copy_from_user(&edata, useraddr, sizeof(edata)))
267 return -EFAULT;
268
269 dev->ethtool_ops->set_msglevel(dev, edata.data);
270 return 0;
271}
272
273static int ethtool_nway_reset(struct net_device *dev)
274{
275 if (!dev->ethtool_ops->nway_reset)
276 return -EOPNOTSUPP;
277
278 return dev->ethtool_ops->nway_reset(dev);
279}
280
281static int ethtool_get_link(struct net_device *dev, void __user *useraddr)
282{
283 struct ethtool_value edata = { ETHTOOL_GLINK };
284
285 if (!dev->ethtool_ops->get_link)
286 return -EOPNOTSUPP;
287
288 edata.data = dev->ethtool_ops->get_link(dev);
289
290 if (copy_to_user(useraddr, &edata, sizeof(edata)))
291 return -EFAULT;
292 return 0;
293}
294
295static int ethtool_get_eeprom(struct net_device *dev, void __user *useraddr)
296{
297 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700298 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700299 u8 *data;
300 int ret;
301
302 if (!ops->get_eeprom || !ops->get_eeprom_len)
303 return -EOPNOTSUPP;
304
305 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
306 return -EFAULT;
307
308 /* Check for wrap and zero */
309 if (eeprom.offset + eeprom.len <= eeprom.offset)
310 return -EINVAL;
311
312 /* Check for exceeding total eeprom len */
313 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
314 return -EINVAL;
315
316 data = kmalloc(eeprom.len, GFP_USER);
317 if (!data)
318 return -ENOMEM;
319
320 ret = -EFAULT;
321 if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
322 goto out;
323
324 ret = ops->get_eeprom(dev, &eeprom, data);
325 if (ret)
326 goto out;
327
328 ret = -EFAULT;
329 if (copy_to_user(useraddr, &eeprom, sizeof(eeprom)))
330 goto out;
331 if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
332 goto out;
333 ret = 0;
334
335 out:
336 kfree(data);
337 return ret;
338}
339
340static int ethtool_set_eeprom(struct net_device *dev, void __user *useraddr)
341{
342 struct ethtool_eeprom eeprom;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700343 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700344 u8 *data;
345 int ret;
346
347 if (!ops->set_eeprom || !ops->get_eeprom_len)
348 return -EOPNOTSUPP;
349
350 if (copy_from_user(&eeprom, useraddr, sizeof(eeprom)))
351 return -EFAULT;
352
353 /* Check for wrap and zero */
354 if (eeprom.offset + eeprom.len <= eeprom.offset)
355 return -EINVAL;
356
357 /* Check for exceeding total eeprom len */
358 if (eeprom.offset + eeprom.len > ops->get_eeprom_len(dev))
359 return -EINVAL;
360
361 data = kmalloc(eeprom.len, GFP_USER);
362 if (!data)
363 return -ENOMEM;
364
365 ret = -EFAULT;
366 if (copy_from_user(data, useraddr + sizeof(eeprom), eeprom.len))
367 goto out;
368
369 ret = ops->set_eeprom(dev, &eeprom, data);
370 if (ret)
371 goto out;
372
373 if (copy_to_user(useraddr + sizeof(eeprom), data, eeprom.len))
374 ret = -EFAULT;
375
376 out:
377 kfree(data);
378 return ret;
379}
380
381static int ethtool_get_coalesce(struct net_device *dev, void __user *useraddr)
382{
383 struct ethtool_coalesce coalesce = { ETHTOOL_GCOALESCE };
384
385 if (!dev->ethtool_ops->get_coalesce)
386 return -EOPNOTSUPP;
387
388 dev->ethtool_ops->get_coalesce(dev, &coalesce);
389
390 if (copy_to_user(useraddr, &coalesce, sizeof(coalesce)))
391 return -EFAULT;
392 return 0;
393}
394
395static int ethtool_set_coalesce(struct net_device *dev, void __user *useraddr)
396{
397 struct ethtool_coalesce coalesce;
398
David S. Millerfa04ae52005-06-06 15:07:19 -0700399 if (!dev->ethtool_ops->set_coalesce)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return -EOPNOTSUPP;
401
402 if (copy_from_user(&coalesce, useraddr, sizeof(coalesce)))
403 return -EFAULT;
404
405 return dev->ethtool_ops->set_coalesce(dev, &coalesce);
406}
407
408static int ethtool_get_ringparam(struct net_device *dev, void __user *useraddr)
409{
410 struct ethtool_ringparam ringparam = { ETHTOOL_GRINGPARAM };
411
412 if (!dev->ethtool_ops->get_ringparam)
413 return -EOPNOTSUPP;
414
415 dev->ethtool_ops->get_ringparam(dev, &ringparam);
416
417 if (copy_to_user(useraddr, &ringparam, sizeof(ringparam)))
418 return -EFAULT;
419 return 0;
420}
421
422static int ethtool_set_ringparam(struct net_device *dev, void __user *useraddr)
423{
424 struct ethtool_ringparam ringparam;
425
426 if (!dev->ethtool_ops->set_ringparam)
427 return -EOPNOTSUPP;
428
429 if (copy_from_user(&ringparam, useraddr, sizeof(ringparam)))
430 return -EFAULT;
431
432 return dev->ethtool_ops->set_ringparam(dev, &ringparam);
433}
434
435static int ethtool_get_pauseparam(struct net_device *dev, void __user *useraddr)
436{
437 struct ethtool_pauseparam pauseparam = { ETHTOOL_GPAUSEPARAM };
438
439 if (!dev->ethtool_ops->get_pauseparam)
440 return -EOPNOTSUPP;
441
442 dev->ethtool_ops->get_pauseparam(dev, &pauseparam);
443
444 if (copy_to_user(useraddr, &pauseparam, sizeof(pauseparam)))
445 return -EFAULT;
446 return 0;
447}
448
449static int ethtool_set_pauseparam(struct net_device *dev, void __user *useraddr)
450{
451 struct ethtool_pauseparam pauseparam;
452
Jeff Garzike1b90c42006-07-17 12:54:40 -0400453 if (!dev->ethtool_ops->set_pauseparam)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700454 return -EOPNOTSUPP;
455
456 if (copy_from_user(&pauseparam, useraddr, sizeof(pauseparam)))
457 return -EFAULT;
458
459 return dev->ethtool_ops->set_pauseparam(dev, &pauseparam);
460}
461
462static int ethtool_get_rx_csum(struct net_device *dev, char __user *useraddr)
463{
464 struct ethtool_value edata = { ETHTOOL_GRXCSUM };
465
466 if (!dev->ethtool_ops->get_rx_csum)
467 return -EOPNOTSUPP;
468
469 edata.data = dev->ethtool_ops->get_rx_csum(dev);
470
471 if (copy_to_user(useraddr, &edata, sizeof(edata)))
472 return -EFAULT;
473 return 0;
474}
475
476static int ethtool_set_rx_csum(struct net_device *dev, char __user *useraddr)
477{
478 struct ethtool_value edata;
479
480 if (!dev->ethtool_ops->set_rx_csum)
481 return -EOPNOTSUPP;
482
483 if (copy_from_user(&edata, useraddr, sizeof(edata)))
484 return -EFAULT;
485
486 dev->ethtool_ops->set_rx_csum(dev, edata.data);
487 return 0;
488}
489
490static int ethtool_get_tx_csum(struct net_device *dev, char __user *useraddr)
491{
492 struct ethtool_value edata = { ETHTOOL_GTXCSUM };
493
494 if (!dev->ethtool_ops->get_tx_csum)
495 return -EOPNOTSUPP;
496
497 edata.data = dev->ethtool_ops->get_tx_csum(dev);
498
499 if (copy_to_user(useraddr, &edata, sizeof(edata)))
500 return -EFAULT;
501 return 0;
502}
503
504static int __ethtool_set_sg(struct net_device *dev, u32 data)
505{
506 int err;
507
508 if (!data && dev->ethtool_ops->set_tso) {
509 err = dev->ethtool_ops->set_tso(dev, 0);
510 if (err)
511 return err;
512 }
513
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700514 if (!data && dev->ethtool_ops->set_ufo) {
515 err = dev->ethtool_ops->set_ufo(dev, 0);
516 if (err)
517 return err;
518 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700519 return dev->ethtool_ops->set_sg(dev, data);
520}
521
522static int ethtool_set_tx_csum(struct net_device *dev, char __user *useraddr)
523{
524 struct ethtool_value edata;
525 int err;
526
527 if (!dev->ethtool_ops->set_tx_csum)
528 return -EOPNOTSUPP;
529
530 if (copy_from_user(&edata, useraddr, sizeof(edata)))
531 return -EFAULT;
532
533 if (!edata.data && dev->ethtool_ops->set_sg) {
534 err = __ethtool_set_sg(dev, 0);
535 if (err)
536 return err;
537 }
538
539 return dev->ethtool_ops->set_tx_csum(dev, edata.data);
540}
541
542static int ethtool_get_sg(struct net_device *dev, char __user *useraddr)
543{
544 struct ethtool_value edata = { ETHTOOL_GSG };
545
546 if (!dev->ethtool_ops->get_sg)
547 return -EOPNOTSUPP;
548
549 edata.data = dev->ethtool_ops->get_sg(dev);
550
551 if (copy_to_user(useraddr, &edata, sizeof(edata)))
552 return -EFAULT;
553 return 0;
554}
555
556static int ethtool_set_sg(struct net_device *dev, char __user *useraddr)
557{
558 struct ethtool_value edata;
559
560 if (!dev->ethtool_ops->set_sg)
561 return -EOPNOTSUPP;
562
563 if (copy_from_user(&edata, useraddr, sizeof(edata)))
564 return -EFAULT;
565
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900566 if (edata.data &&
Herbert Xu8648b302006-06-17 22:06:05 -0700567 !(dev->features & NETIF_F_ALL_CSUM))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 return -EINVAL;
569
570 return __ethtool_set_sg(dev, edata.data);
571}
572
573static int ethtool_get_tso(struct net_device *dev, char __user *useraddr)
574{
575 struct ethtool_value edata = { ETHTOOL_GTSO };
576
577 if (!dev->ethtool_ops->get_tso)
578 return -EOPNOTSUPP;
579
580 edata.data = dev->ethtool_ops->get_tso(dev);
581
582 if (copy_to_user(useraddr, &edata, sizeof(edata)))
583 return -EFAULT;
584 return 0;
585}
586
587static int ethtool_set_tso(struct net_device *dev, char __user *useraddr)
588{
589 struct ethtool_value edata;
590
591 if (!dev->ethtool_ops->set_tso)
592 return -EOPNOTSUPP;
593
594 if (copy_from_user(&edata, useraddr, sizeof(edata)))
595 return -EFAULT;
596
597 if (edata.data && !(dev->features & NETIF_F_SG))
598 return -EINVAL;
599
600 return dev->ethtool_ops->set_tso(dev, edata.data);
601}
602
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700603static int ethtool_get_ufo(struct net_device *dev, char __user *useraddr)
604{
Herbert Xu47552c42006-06-17 23:00:20 -0700605 struct ethtool_value edata = { ETHTOOL_GUFO };
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700606
607 if (!dev->ethtool_ops->get_ufo)
608 return -EOPNOTSUPP;
609 edata.data = dev->ethtool_ops->get_ufo(dev);
610 if (copy_to_user(useraddr, &edata, sizeof(edata)))
611 return -EFAULT;
612 return 0;
613}
Herbert Xu47552c42006-06-17 23:00:20 -0700614
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700615static int ethtool_set_ufo(struct net_device *dev, char __user *useraddr)
616{
617 struct ethtool_value edata;
618
619 if (!dev->ethtool_ops->set_ufo)
620 return -EOPNOTSUPP;
621 if (copy_from_user(&edata, useraddr, sizeof(edata)))
622 return -EFAULT;
623 if (edata.data && !(dev->features & NETIF_F_SG))
624 return -EINVAL;
625 if (edata.data && !(dev->features & NETIF_F_HW_CSUM))
626 return -EINVAL;
627 return dev->ethtool_ops->set_ufo(dev, edata.data);
628}
629
Herbert Xu37c31852006-06-22 03:07:29 -0700630static int ethtool_get_gso(struct net_device *dev, char __user *useraddr)
631{
632 struct ethtool_value edata = { ETHTOOL_GGSO };
633
634 edata.data = dev->features & NETIF_F_GSO;
635 if (copy_to_user(useraddr, &edata, sizeof(edata)))
636 return -EFAULT;
637 return 0;
638}
639
640static int ethtool_set_gso(struct net_device *dev, char __user *useraddr)
641{
642 struct ethtool_value edata;
643
644 if (copy_from_user(&edata, useraddr, sizeof(edata)))
645 return -EFAULT;
646 if (edata.data)
647 dev->features |= NETIF_F_GSO;
648 else
649 dev->features &= ~NETIF_F_GSO;
650 return 0;
651}
652
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653static int ethtool_self_test(struct net_device *dev, char __user *useraddr)
654{
655 struct ethtool_test test;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700656 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 u64 *data;
658 int ret;
659
660 if (!ops->self_test || !ops->self_test_count)
661 return -EOPNOTSUPP;
662
663 if (copy_from_user(&test, useraddr, sizeof(test)))
664 return -EFAULT;
665
666 test.len = ops->self_test_count(dev);
667 data = kmalloc(test.len * sizeof(u64), GFP_USER);
668 if (!data)
669 return -ENOMEM;
670
671 ops->self_test(dev, &test, data);
672
673 ret = -EFAULT;
674 if (copy_to_user(useraddr, &test, sizeof(test)))
675 goto out;
676 useraddr += sizeof(test);
677 if (copy_to_user(useraddr, data, test.len * sizeof(u64)))
678 goto out;
679 ret = 0;
680
681 out:
682 kfree(data);
683 return ret;
684}
685
686static int ethtool_get_strings(struct net_device *dev, void __user *useraddr)
687{
688 struct ethtool_gstrings gstrings;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700689 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690 u8 *data;
691 int ret;
692
693 if (!ops->get_strings)
694 return -EOPNOTSUPP;
695
696 if (copy_from_user(&gstrings, useraddr, sizeof(gstrings)))
697 return -EFAULT;
698
699 switch (gstrings.string_set) {
700 case ETH_SS_TEST:
701 if (!ops->self_test_count)
702 return -EOPNOTSUPP;
703 gstrings.len = ops->self_test_count(dev);
704 break;
705 case ETH_SS_STATS:
706 if (!ops->get_stats_count)
707 return -EOPNOTSUPP;
708 gstrings.len = ops->get_stats_count(dev);
709 break;
710 default:
711 return -EINVAL;
712 }
713
714 data = kmalloc(gstrings.len * ETH_GSTRING_LEN, GFP_USER);
715 if (!data)
716 return -ENOMEM;
717
718 ops->get_strings(dev, gstrings.string_set, data);
719
720 ret = -EFAULT;
721 if (copy_to_user(useraddr, &gstrings, sizeof(gstrings)))
722 goto out;
723 useraddr += sizeof(gstrings);
724 if (copy_to_user(useraddr, data, gstrings.len * ETH_GSTRING_LEN))
725 goto out;
726 ret = 0;
727
728 out:
729 kfree(data);
730 return ret;
731}
732
733static int ethtool_phys_id(struct net_device *dev, void __user *useraddr)
734{
735 struct ethtool_value id;
736
737 if (!dev->ethtool_ops->phys_id)
738 return -EOPNOTSUPP;
739
740 if (copy_from_user(&id, useraddr, sizeof(id)))
741 return -EFAULT;
742
743 return dev->ethtool_ops->phys_id(dev, id.data);
744}
745
746static int ethtool_get_stats(struct net_device *dev, void __user *useraddr)
747{
748 struct ethtool_stats stats;
Stephen Hemminger76fd8592006-09-08 11:16:13 -0700749 const struct ethtool_ops *ops = dev->ethtool_ops;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700750 u64 *data;
751 int ret;
752
753 if (!ops->get_ethtool_stats || !ops->get_stats_count)
754 return -EOPNOTSUPP;
755
756 if (copy_from_user(&stats, useraddr, sizeof(stats)))
757 return -EFAULT;
758
759 stats.n_stats = ops->get_stats_count(dev);
760 data = kmalloc(stats.n_stats * sizeof(u64), GFP_USER);
761 if (!data)
762 return -ENOMEM;
763
764 ops->get_ethtool_stats(dev, &stats, data);
765
766 ret = -EFAULT;
767 if (copy_to_user(useraddr, &stats, sizeof(stats)))
768 goto out;
769 useraddr += sizeof(stats);
770 if (copy_to_user(useraddr, data, stats.n_stats * sizeof(u64)))
771 goto out;
772 ret = 0;
773
774 out:
775 kfree(data);
776 return ret;
777}
778
viro@ftp.linux.org.uk0bf0519d2005-09-05 03:26:18 +0100779static int ethtool_get_perm_addr(struct net_device *dev, void __user *useraddr)
Jon Wetzela6f9a702005-08-20 17:15:54 -0700780{
781 struct ethtool_perm_addr epaddr;
782 u8 *data;
783 int ret;
784
785 if (!dev->ethtool_ops->get_perm_addr)
786 return -EOPNOTSUPP;
787
788 if (copy_from_user(&epaddr,useraddr,sizeof(epaddr)))
789 return -EFAULT;
790
791 data = kmalloc(epaddr.size, GFP_USER);
792 if (!data)
793 return -ENOMEM;
794
795 ret = dev->ethtool_ops->get_perm_addr(dev,&epaddr,data);
796 if (ret)
797 return ret;
798
799 ret = -EFAULT;
800 if (copy_to_user(useraddr, &epaddr, sizeof(epaddr)))
801 goto out;
802 useraddr += sizeof(epaddr);
803 if (copy_to_user(useraddr, data, epaddr.size))
804 goto out;
805 ret = 0;
806
807 out:
808 kfree(data);
809 return ret;
810}
811
Linus Torvalds1da177e2005-04-16 15:20:36 -0700812/* The main entry point in this file. Called from net/core/dev.c */
813
814int dev_ethtool(struct ifreq *ifr)
815{
816 struct net_device *dev = __dev_get_by_name(ifr->ifr_name);
817 void __user *useraddr = ifr->ifr_data;
818 u32 ethcmd;
819 int rc;
Stephen Hemminger81e81572005-05-29 14:14:35 -0700820 unsigned long old_features;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
Linus Torvalds1da177e2005-04-16 15:20:36 -0700822 if (!dev || !netif_device_present(dev))
823 return -ENODEV;
824
825 if (!dev->ethtool_ops)
Matthew Wilcox61a44b92007-07-31 14:00:02 -0700826 return -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700827
828 if (copy_from_user(&ethcmd, useraddr, sizeof (ethcmd)))
829 return -EFAULT;
830
Stephen Hemminger75f31232006-09-28 15:13:37 -0700831 /* Allow some commands to be done by anyone */
832 switch(ethcmd) {
Stephen Hemminger75f31232006-09-28 15:13:37 -0700833 case ETHTOOL_GDRVINFO:
Stephen Hemminger75f31232006-09-28 15:13:37 -0700834 case ETHTOOL_GMSGLVL:
Stephen Hemminger75f31232006-09-28 15:13:37 -0700835 case ETHTOOL_GCOALESCE:
836 case ETHTOOL_GRINGPARAM:
837 case ETHTOOL_GPAUSEPARAM:
838 case ETHTOOL_GRXCSUM:
839 case ETHTOOL_GTXCSUM:
840 case ETHTOOL_GSG:
841 case ETHTOOL_GSTRINGS:
Stephen Hemminger75f31232006-09-28 15:13:37 -0700842 case ETHTOOL_GTSO:
843 case ETHTOOL_GPERMADDR:
844 case ETHTOOL_GUFO:
845 case ETHTOOL_GGSO:
846 break;
847 default:
848 if (!capable(CAP_NET_ADMIN))
849 return -EPERM;
850 }
851
Stephen Hemmingere71a4782007-04-10 20:10:33 -0700852 if (dev->ethtool_ops->begin)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853 if ((rc = dev->ethtool_ops->begin(dev)) < 0)
854 return rc;
855
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -0700856 old_features = dev->features;
857
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858 switch (ethcmd) {
859 case ETHTOOL_GSET:
860 rc = ethtool_get_settings(dev, useraddr);
861 break;
862 case ETHTOOL_SSET:
863 rc = ethtool_set_settings(dev, useraddr);
864 break;
865 case ETHTOOL_GDRVINFO:
866 rc = ethtool_get_drvinfo(dev, useraddr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867 break;
868 case ETHTOOL_GREGS:
869 rc = ethtool_get_regs(dev, useraddr);
870 break;
871 case ETHTOOL_GWOL:
872 rc = ethtool_get_wol(dev, useraddr);
873 break;
874 case ETHTOOL_SWOL:
875 rc = ethtool_set_wol(dev, useraddr);
876 break;
877 case ETHTOOL_GMSGLVL:
878 rc = ethtool_get_msglevel(dev, useraddr);
879 break;
880 case ETHTOOL_SMSGLVL:
881 rc = ethtool_set_msglevel(dev, useraddr);
882 break;
883 case ETHTOOL_NWAY_RST:
884 rc = ethtool_nway_reset(dev);
885 break;
886 case ETHTOOL_GLINK:
887 rc = ethtool_get_link(dev, useraddr);
888 break;
889 case ETHTOOL_GEEPROM:
890 rc = ethtool_get_eeprom(dev, useraddr);
891 break;
892 case ETHTOOL_SEEPROM:
893 rc = ethtool_set_eeprom(dev, useraddr);
894 break;
895 case ETHTOOL_GCOALESCE:
896 rc = ethtool_get_coalesce(dev, useraddr);
897 break;
898 case ETHTOOL_SCOALESCE:
899 rc = ethtool_set_coalesce(dev, useraddr);
900 break;
901 case ETHTOOL_GRINGPARAM:
902 rc = ethtool_get_ringparam(dev, useraddr);
903 break;
904 case ETHTOOL_SRINGPARAM:
905 rc = ethtool_set_ringparam(dev, useraddr);
906 break;
907 case ETHTOOL_GPAUSEPARAM:
908 rc = ethtool_get_pauseparam(dev, useraddr);
909 break;
910 case ETHTOOL_SPAUSEPARAM:
911 rc = ethtool_set_pauseparam(dev, useraddr);
912 break;
913 case ETHTOOL_GRXCSUM:
914 rc = ethtool_get_rx_csum(dev, useraddr);
915 break;
916 case ETHTOOL_SRXCSUM:
917 rc = ethtool_set_rx_csum(dev, useraddr);
918 break;
919 case ETHTOOL_GTXCSUM:
920 rc = ethtool_get_tx_csum(dev, useraddr);
921 break;
922 case ETHTOOL_STXCSUM:
923 rc = ethtool_set_tx_csum(dev, useraddr);
924 break;
925 case ETHTOOL_GSG:
926 rc = ethtool_get_sg(dev, useraddr);
927 break;
928 case ETHTOOL_SSG:
929 rc = ethtool_set_sg(dev, useraddr);
930 break;
931 case ETHTOOL_GTSO:
932 rc = ethtool_get_tso(dev, useraddr);
933 break;
934 case ETHTOOL_STSO:
935 rc = ethtool_set_tso(dev, useraddr);
936 break;
937 case ETHTOOL_TEST:
938 rc = ethtool_self_test(dev, useraddr);
939 break;
940 case ETHTOOL_GSTRINGS:
941 rc = ethtool_get_strings(dev, useraddr);
942 break;
943 case ETHTOOL_PHYS_ID:
944 rc = ethtool_phys_id(dev, useraddr);
945 break;
946 case ETHTOOL_GSTATS:
947 rc = ethtool_get_stats(dev, useraddr);
948 break;
Jon Wetzela6f9a702005-08-20 17:15:54 -0700949 case ETHTOOL_GPERMADDR:
950 rc = ethtool_get_perm_addr(dev, useraddr);
951 break;
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700952 case ETHTOOL_GUFO:
953 rc = ethtool_get_ufo(dev, useraddr);
954 break;
955 case ETHTOOL_SUFO:
956 rc = ethtool_set_ufo(dev, useraddr);
957 break;
Herbert Xu37c31852006-06-22 03:07:29 -0700958 case ETHTOOL_GGSO:
959 rc = ethtool_get_gso(dev, useraddr);
960 break;
961 case ETHTOOL_SGSO:
962 rc = ethtool_set_gso(dev, useraddr);
963 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700964 default:
Matthew Wilcox61a44b92007-07-31 14:00:02 -0700965 rc = -EOPNOTSUPP;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700966 }
YOSHIFUJI Hideaki4ec93ed2007-02-09 23:24:36 +0900967
Stephen Hemmingere71a4782007-04-10 20:10:33 -0700968 if (dev->ethtool_ops->complete)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700969 dev->ethtool_ops->complete(dev);
Stephen Hemmingerd8a33ac2005-05-29 14:13:47 -0700970
971 if (old_features != dev->features)
972 netdev_features_change(dev);
973
Linus Torvalds1da177e2005-04-16 15:20:36 -0700974 return rc;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700975}
976
977EXPORT_SYMBOL(dev_ethtool);
978EXPORT_SYMBOL(ethtool_op_get_link);
Jon Wetzela6f9a702005-08-20 17:15:54 -0700979EXPORT_SYMBOL_GPL(ethtool_op_get_perm_addr);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700980EXPORT_SYMBOL(ethtool_op_get_sg);
981EXPORT_SYMBOL(ethtool_op_get_tso);
982EXPORT_SYMBOL(ethtool_op_get_tx_csum);
983EXPORT_SYMBOL(ethtool_op_set_sg);
984EXPORT_SYMBOL(ethtool_op_set_tso);
985EXPORT_SYMBOL(ethtool_op_set_tx_csum);
Jon Mason69f6a0f2005-05-29 20:27:24 -0700986EXPORT_SYMBOL(ethtool_op_set_tx_hw_csum);
Michael Chan6460d942007-07-14 19:07:52 -0700987EXPORT_SYMBOL(ethtool_op_set_tx_ipv6_csum);
Ananda Rajue89e9cf2005-10-18 15:46:41 -0700988EXPORT_SYMBOL(ethtool_op_set_ufo);
989EXPORT_SYMBOL(ethtool_op_get_ufo);