chiark / gitweb /
18d44fc9689631ae75c6ab7bb655c8b8c4301956
[elogind.git] / src / udev / net / ethtool-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4  This file is part of systemd.
5
6  Copyright (C) 2013 Tom Gundersen <teg@jklm.no>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <sys/ioctl.h>
23 #include <net/if.h>
24 #include <linux/ethtool.h>
25 #include <linux/sockios.h>
26
27 #include "ethtool-util.h"
28
29 #include "strxcpyx.h"
30 #include "util.h"
31 #include "log.h"
32 #include "conf-parser.h"
33
34 static const char* const duplex_table[] = {
35         [DUP_FULL] = "full",
36         [DUP_HALF] = "half"
37 };
38
39 DEFINE_STRING_TABLE_LOOKUP(duplex, Duplex);
40 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex, duplex, Duplex, "Failed to parse duplex setting");
41
42 static const char* const wol_table[] = {
43         [WOL_PHY] = "phy",
44         [WOL_MAGIC] = "magic",
45         [WOL_OFF] = "off"
46 };
47
48 DEFINE_STRING_TABLE_LOOKUP(wol, WakeOnLan);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol, wol, WakeOnLan, "Failed to parse WakeOnLan setting");
50
51 int ethtool_connect(int *ret) {
52         int fd;
53
54         assert_return(ret, -EINVAL);
55
56         fd = socket(PF_INET, SOCK_DGRAM, 0);
57         if (fd < 0) {
58                 return -errno;
59         }
60
61         *ret = fd;
62
63         return 0;
64 }
65
66 int ethtool_get_driver(int fd, const char *ifname, char **ret) {
67         struct ethtool_drvinfo ecmd = {
68                 .cmd = ETHTOOL_GDRVINFO
69         };
70         struct ifreq ifr = {
71                 .ifr_data = (void*) &ecmd
72         };
73         char *d;
74         int r;
75
76         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
77
78         r = ioctl(fd, SIOCETHTOOL, &ifr);
79         if (r < 0)
80                 return -errno;
81
82         d = strdup(ecmd.driver);
83         if (!d)
84                 return -ENOMEM;
85
86         *ret = d;
87         return 0;
88 }
89
90 int ethtool_set_speed(int fd, const char *ifname, unsigned int speed, Duplex duplex)
91 {
92         struct ifreq ifr;
93         struct ethtool_cmd ecmd;
94         bool need_update = false;
95         int r;
96
97         if (speed == 0 && duplex == _DUP_INVALID)
98                 return 0;
99
100         zero(ecmd);
101         ecmd.cmd = ETHTOOL_GSET;
102
103         zero(ifr);
104         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
105         ifr.ifr_data = (void *)&ecmd;
106
107         r = ioctl(fd, SIOCETHTOOL, &ifr);
108         if (r < 0)
109                 return -errno;
110
111         if (ethtool_cmd_speed(&ecmd) != speed) {
112                 ethtool_cmd_speed_set(&ecmd, speed);
113                 need_update = true;
114         }
115
116         switch (duplex) {
117                 case DUP_HALF:
118                         if (ecmd.duplex != DUPLEX_HALF) {
119                                 ecmd.duplex = DUPLEX_HALF;
120                                 need_update = true;
121                         }
122                         break;
123                 case DUP_FULL:
124                         if (ecmd.duplex != DUPLEX_FULL) {
125                                 ecmd.duplex = DUPLEX_FULL;
126                                 need_update = true;
127                         }
128                         break;
129                 default:
130                         break;
131         }
132
133         if (need_update) {
134                 ecmd.cmd = ETHTOOL_SSET;
135
136                 r = ioctl(fd, SIOCETHTOOL, &ifr);
137                 if (r < 0)
138                         return -errno;
139         }
140
141         return 0;
142 }
143
144 int ethtool_set_wol(int fd, const char *ifname, WakeOnLan wol) {
145         struct ifreq ifr;
146         struct ethtool_wolinfo ecmd;
147         bool need_update = false;
148         int r;
149
150         if (wol == _WOL_INVALID)
151                 return 0;
152
153         zero(ecmd);
154         ecmd.cmd = ETHTOOL_GWOL;
155
156         zero(ifr);
157         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
158         ifr.ifr_data = (void *)&ecmd;
159
160         r = ioctl(fd, SIOCETHTOOL, &ifr);
161         if (r < 0)
162                 return -errno;
163
164         switch (wol) {
165                 case WOL_PHY:
166                         if (ecmd.wolopts != WAKE_PHY) {
167                                 ecmd.wolopts = WAKE_PHY;
168                                 need_update = true;
169                         }
170                         break;
171                 case WOL_MAGIC:
172                         if (ecmd.wolopts != WAKE_MAGIC) {
173                                 ecmd.wolopts = WAKE_MAGIC;
174                                 need_update = true;
175                         }
176                         break;
177                 case WOL_OFF:
178                         if (ecmd.wolopts != 0) {
179                                 ecmd.wolopts = 0;
180                                 need_update = true;
181                         }
182                         break;
183                 default:
184                         break;
185         }
186
187         if (need_update) {
188                 ecmd.cmd = ETHTOOL_SWOL;
189
190                 r = ioctl(fd, SIOCETHTOOL, &ifr);
191                 if (r < 0)
192                         return -errno;
193         }
194
195         return 0;
196 }