chiark / gitweb /
3ec245ecabc96b14b3e9582de3d61998653f109d
[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[_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         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 ethtool_cmd ecmd = {
93                 .cmd = ETHTOOL_GSET
94         };
95         struct ifreq ifr = {
96                 .ifr_data = (void*) &ecmd
97         };
98         bool need_update = false;
99         int r;
100
101         if (speed == 0 && duplex == _DUP_INVALID)
102                 return 0;
103
104         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
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 ethtool_wolinfo ecmd = {
145                 .cmd = ETHTOOL_GWOL
146         };
147         struct ifreq ifr = {
148                 .ifr_data = (void*) &ecmd
149         };
150         bool need_update = false;
151         int r;
152
153         if (wol == _WOL_INVALID)
154                 return 0;
155
156         strscpy(ifr.ifr_name, IFNAMSIZ, ifname);
157
158         r = ioctl(fd, SIOCETHTOOL, &ifr);
159         if (r < 0)
160                 return -errno;
161
162         switch (wol) {
163                 case WOL_PHY:
164                         if (ecmd.wolopts != WAKE_PHY) {
165                                 ecmd.wolopts = WAKE_PHY;
166                                 need_update = true;
167                         }
168                         break;
169                 case WOL_MAGIC:
170                         if (ecmd.wolopts != WAKE_MAGIC) {
171                                 ecmd.wolopts = WAKE_MAGIC;
172                                 need_update = true;
173                         }
174                         break;
175                 case WOL_OFF:
176                         if (ecmd.wolopts != 0) {
177                                 ecmd.wolopts = 0;
178                                 need_update = true;
179                         }
180                         break;
181                 default:
182                         break;
183         }
184
185         if (need_update) {
186                 ecmd.cmd = ETHTOOL_SWOL;
187
188                 r = ioctl(fd, SIOCETHTOOL, &ifr);
189                 if (r < 0)
190                         return -errno;
191         }
192
193         return 0;
194 }