]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/udev/net/ethtool-util.c
treewide: no need to negate errno for log_*_errno()
[thirdparty/systemd.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[_DUP_MAX] = {
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[_WOL_MAX] = {
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 if (*fd < 0) {
77 r = ethtool_connect(fd);
78 if (r < 0) {
79 log_warning_errno(r, "link_config: could not connect to ethtool: %m");
80 return r;
81 }
82 }
83
84 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
85
86 r = ioctl(*fd, SIOCETHTOOL, &ifr);
87 if (r < 0)
88 return -errno;
89
90 d = strdup(ecmd.driver);
91 if (!d)
92 return -ENOMEM;
93
94 *ret = d;
95 return 0;
96 }
97
98 int ethtool_set_speed(int *fd, const char *ifname, unsigned int speed, Duplex duplex)
99 {
100 struct ethtool_cmd ecmd = {
101 .cmd = ETHTOOL_GSET
102 };
103 struct ifreq ifr = {
104 .ifr_data = (void*) &ecmd
105 };
106 bool need_update = false;
107 int r;
108
109 if (speed == 0 && duplex == _DUP_INVALID)
110 return 0;
111
112 if (*fd < 0) {
113 r = ethtool_connect(fd);
114 if (r < 0) {
115 log_warning_errno(r, "link_config: could not connect to ethtool: %m");
116 return r;
117 }
118 }
119
120 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
121
122 r = ioctl(*fd, SIOCETHTOOL, &ifr);
123 if (r < 0)
124 return -errno;
125
126 if (ethtool_cmd_speed(&ecmd) != speed) {
127 ethtool_cmd_speed_set(&ecmd, speed);
128 need_update = true;
129 }
130
131 switch (duplex) {
132 case DUP_HALF:
133 if (ecmd.duplex != DUPLEX_HALF) {
134 ecmd.duplex = DUPLEX_HALF;
135 need_update = true;
136 }
137 break;
138 case DUP_FULL:
139 if (ecmd.duplex != DUPLEX_FULL) {
140 ecmd.duplex = DUPLEX_FULL;
141 need_update = true;
142 }
143 break;
144 default:
145 break;
146 }
147
148 if (need_update) {
149 ecmd.cmd = ETHTOOL_SSET;
150
151 r = ioctl(*fd, SIOCETHTOOL, &ifr);
152 if (r < 0)
153 return -errno;
154 }
155
156 return 0;
157 }
158
159 int ethtool_set_wol(int *fd, const char *ifname, WakeOnLan wol) {
160 struct ethtool_wolinfo ecmd = {
161 .cmd = ETHTOOL_GWOL
162 };
163 struct ifreq ifr = {
164 .ifr_data = (void*) &ecmd
165 };
166 bool need_update = false;
167 int r;
168
169 if (wol == _WOL_INVALID)
170 return 0;
171
172 if (*fd < 0) {
173 r = ethtool_connect(fd);
174 if (r < 0) {
175 log_warning_errno(r, "link_config: could not connect to ethtool: %m");
176 return r;
177 }
178 }
179
180 strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
181
182 r = ioctl(*fd, SIOCETHTOOL, &ifr);
183 if (r < 0)
184 return -errno;
185
186 switch (wol) {
187 case WOL_PHY:
188 if (ecmd.wolopts != WAKE_PHY) {
189 ecmd.wolopts = WAKE_PHY;
190 need_update = true;
191 }
192 break;
193 case WOL_MAGIC:
194 if (ecmd.wolopts != WAKE_MAGIC) {
195 ecmd.wolopts = WAKE_MAGIC;
196 need_update = true;
197 }
198 break;
199 case WOL_OFF:
200 if (ecmd.wolopts != 0) {
201 ecmd.wolopts = 0;
202 need_update = true;
203 }
204 break;
205 default:
206 break;
207 }
208
209 if (need_update) {
210 ecmd.cmd = ETHTOOL_SWOL;
211
212 r = ioctl(*fd, SIOCETHTOOL, &ifr);
213 if (r < 0)
214 return -errno;
215 }
216
217 return 0;
218 }