chiark / gitweb /
udev - link-setup - expose ID_NET_DRIVER
[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 ifreq ifr;
68         struct ethtool_drvinfo ecmd;
69         int r;
70
71         zero(ecmd);
72         ecmd.cmd = ETHTOOL_GDRVINFO;
73
74         zero(ifr);
75         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
76         ifr.ifr_data = (void *)&ecmd;
77
78         r = ioctl(fd, SIOCETHTOOL, &ifr);
79         if (r < 0)
80                 return -errno;
81
82         *ret = strdup(ecmd.driver);
83         if (!*ret)
84                 return -ENOMEM;
85
86         return 0;
87 }
88
89 int ethtool_set_speed(int fd, const char *ifname, unsigned int speed, Duplex duplex)
90 {
91         struct ifreq ifr;
92         struct ethtool_cmd ecmd;
93         bool need_update = false;
94         int r;
95
96         if (speed == 0 && duplex == _DUP_INVALID)
97                 return 0;
98
99         zero(ecmd);
100         ecmd.cmd = ETHTOOL_GSET;
101
102         zero(ifr);
103         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
104         ifr.ifr_data = (void *)&ecmd;
105
106         r = ioctl(fd, SIOCETHTOOL, &ifr);
107         if (r < 0)
108                 return -errno;
109
110         if (ethtool_cmd_speed(&ecmd) != speed) {
111                 ethtool_cmd_speed_set(&ecmd, speed);
112                 need_update = true;
113         }
114
115         switch (duplex) {
116                 case DUP_HALF:
117                         if (ecmd.duplex != DUPLEX_HALF) {
118                                 ecmd.duplex = DUPLEX_HALF;
119                                 need_update = true;
120                         }
121                         break;
122                 case DUP_FULL:
123                         if (ecmd.duplex != DUPLEX_FULL) {
124                                 ecmd.duplex = DUPLEX_FULL;
125                                 need_update = true;
126                         }
127                         break;
128                 default:
129                         break;
130         }
131
132         if (need_update) {
133                 ecmd.cmd = ETHTOOL_SSET;
134
135                 r = ioctl(fd, SIOCETHTOOL, &ifr);
136                 if (r < 0)
137                         return -errno;
138         }
139
140         return 0;
141 }
142
143 int ethtool_set_wol(int fd, const char *ifname, WakeOnLan wol) {
144         struct ifreq ifr;
145         struct ethtool_wolinfo ecmd;
146         bool need_update = false;
147         int r;
148
149         if (wol == _WOL_INVALID)
150                 return 0;
151
152         zero(ecmd);
153         ecmd.cmd = ETHTOOL_GWOL;
154
155         zero(ifr);
156         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
157         ifr.ifr_data = (void *)&ecmd;
158
159         r = ioctl(fd, SIOCETHTOOL, &ifr);
160         if (r < 0)
161                 return -errno;
162
163         switch (wol) {
164                 case WOL_PHY:
165                         if (ecmd.wolopts != WAKE_PHY) {
166                                 ecmd.wolopts = WAKE_PHY;
167                                 need_update = true;
168                         }
169                         break;
170                 case WOL_MAGIC:
171                         if (ecmd.wolopts != WAKE_MAGIC) {
172                                 ecmd.wolopts = WAKE_MAGIC;
173                                 need_update = true;
174                         }
175                         break;
176                 case WOL_OFF:
177                         if (ecmd.wolopts != 0) {
178                                 ecmd.wolopts = 0;
179                                 need_update = true;
180                         }
181                         break;
182                 default:
183                         break;
184         }
185
186         if (need_update) {
187                 ecmd.cmd = ETHTOOL_SWOL;
188
189                 r = ioctl(fd, SIOCETHTOOL, &ifr);
190                 if (r < 0)
191                         return -errno;
192         }
193
194         return 0;
195 }