]>
Commit | Line | Data |
---|---|---|
a5010333 TG |
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" | |
5fde13d7 TG |
32 | #include "conf-parser.h" |
33 | ||
2c5859af | 34 | static const char* const duplex_table[_DUP_MAX] = { |
5fde13d7 TG |
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 | ||
2c5859af | 42 | static const char* const wol_table[_WOL_MAX] = { |
5fde13d7 TG |
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"); | |
a5010333 TG |
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); | |
ece174c5 | 57 | if (fd < 0) |
a5010333 | 58 | return -errno; |
a5010333 TG |
59 | |
60 | *ret = fd; | |
61 | ||
62 | return 0; | |
63 | } | |
64 | ||
aedca892 | 65 | int ethtool_get_driver(int *fd, const char *ifname, char **ret) { |
61f3af4f LP |
66 | struct ethtool_drvinfo ecmd = { |
67 | .cmd = ETHTOOL_GDRVINFO | |
68 | }; | |
69 | struct ifreq ifr = { | |
70 | .ifr_data = (void*) &ecmd | |
71 | }; | |
72 | char *d; | |
847a8a5f TG |
73 | int r; |
74 | ||
aedca892 TG |
75 | if (*fd < 0) { |
76 | r = ethtool_connect(fd); | |
f647962d MS |
77 | if (r < 0) |
78 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); | |
aedca892 TG |
79 | } |
80 | ||
847a8a5f | 81 | strscpy(ifr.ifr_name, IFNAMSIZ, ifname); |
847a8a5f | 82 | |
aedca892 | 83 | r = ioctl(*fd, SIOCETHTOOL, &ifr); |
847a8a5f TG |
84 | if (r < 0) |
85 | return -errno; | |
86 | ||
61f3af4f LP |
87 | d = strdup(ecmd.driver); |
88 | if (!d) | |
847a8a5f TG |
89 | return -ENOMEM; |
90 | ||
61f3af4f | 91 | *ret = d; |
847a8a5f TG |
92 | return 0; |
93 | } | |
94 | ||
61087906 | 95 | int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex) { |
6c0519c0 TG |
96 | struct ethtool_cmd ecmd = { |
97 | .cmd = ETHTOOL_GSET | |
98 | }; | |
99 | struct ifreq ifr = { | |
100 | .ifr_data = (void*) &ecmd | |
101 | }; | |
0a2c2294 | 102 | bool need_update = false; |
a5010333 TG |
103 | int r; |
104 | ||
5fde13d7 | 105 | if (speed == 0 && duplex == _DUP_INVALID) |
a5010333 TG |
106 | return 0; |
107 | ||
aedca892 TG |
108 | if (*fd < 0) { |
109 | r = ethtool_connect(fd); | |
f647962d MS |
110 | if (r < 0) |
111 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); | |
aedca892 TG |
112 | } |
113 | ||
a5010333 | 114 | strscpy(ifr.ifr_name, IFNAMSIZ, ifname); |
a5010333 | 115 | |
aedca892 | 116 | r = ioctl(*fd, SIOCETHTOOL, &ifr); |
a5010333 TG |
117 | if (r < 0) |
118 | return -errno; | |
119 | ||
120 | if (ethtool_cmd_speed(&ecmd) != speed) { | |
121 | ethtool_cmd_speed_set(&ecmd, speed); | |
122 | need_update = true; | |
123 | } | |
124 | ||
5fde13d7 TG |
125 | switch (duplex) { |
126 | case DUP_HALF: | |
a5010333 TG |
127 | if (ecmd.duplex != DUPLEX_HALF) { |
128 | ecmd.duplex = DUPLEX_HALF; | |
129 | need_update = true; | |
130 | } | |
5fde13d7 TG |
131 | break; |
132 | case DUP_FULL: | |
a5010333 TG |
133 | if (ecmd.duplex != DUPLEX_FULL) { |
134 | ecmd.duplex = DUPLEX_FULL; | |
135 | need_update = true; | |
136 | } | |
5fde13d7 TG |
137 | break; |
138 | default: | |
139 | break; | |
a5010333 TG |
140 | } |
141 | ||
142 | if (need_update) { | |
143 | ecmd.cmd = ETHTOOL_SSET; | |
144 | ||
aedca892 | 145 | r = ioctl(*fd, SIOCETHTOOL, &ifr); |
a5010333 TG |
146 | if (r < 0) |
147 | return -errno; | |
148 | } | |
149 | ||
150 | return 0; | |
151 | } | |
152 | ||
aedca892 | 153 | int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) { |
6c0519c0 TG |
154 | struct ethtool_wolinfo ecmd = { |
155 | .cmd = ETHTOOL_GWOL | |
156 | }; | |
157 | struct ifreq ifr = { | |
158 | .ifr_data = (void*) &ecmd | |
159 | }; | |
0a2c2294 | 160 | bool need_update = false; |
a5010333 TG |
161 | int r; |
162 | ||
5fde13d7 | 163 | if (wol == _WOL_INVALID) |
a5010333 TG |
164 | return 0; |
165 | ||
aedca892 TG |
166 | if (*fd < 0) { |
167 | r = ethtool_connect(fd); | |
f647962d MS |
168 | if (r < 0) |
169 | return log_warning_errno(r, "link_config: could not connect to ethtool: %m"); | |
aedca892 TG |
170 | } |
171 | ||
a5010333 | 172 | strscpy(ifr.ifr_name, IFNAMSIZ, ifname); |
a5010333 | 173 | |
aedca892 | 174 | r = ioctl(*fd, SIOCETHTOOL, &ifr); |
a5010333 TG |
175 | if (r < 0) |
176 | return -errno; | |
177 | ||
5fde13d7 TG |
178 | switch (wol) { |
179 | case WOL_PHY: | |
180 | if (ecmd.wolopts != WAKE_PHY) { | |
181 | ecmd.wolopts = WAKE_PHY; | |
182 | need_update = true; | |
183 | } | |
184 | break; | |
185 | case WOL_MAGIC: | |
186 | if (ecmd.wolopts != WAKE_MAGIC) { | |
187 | ecmd.wolopts = WAKE_MAGIC; | |
188 | need_update = true; | |
189 | } | |
190 | break; | |
191 | case WOL_OFF: | |
192 | if (ecmd.wolopts != 0) { | |
193 | ecmd.wolopts = 0; | |
194 | need_update = true; | |
195 | } | |
196 | break; | |
197 | default: | |
198 | break; | |
199 | } | |
a5010333 TG |
200 | |
201 | if (need_update) { | |
202 | ecmd.cmd = ETHTOOL_SWOL; | |
203 | ||
aedca892 | 204 | r = ioctl(*fd, SIOCETHTOOL, &ifr); |
a5010333 TG |
205 | if (r < 0) |
206 | return -errno; | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } |