1 /* SPDX-License-Identifier: LGPL-2.1+ */
5 #include <linux/ethtool.h>
6 #include <linux/sockios.h>
8 #include "conf-parser.h"
9 #include "ethtool-util.h"
10 #include "extract-word.h"
12 #include "memory-util.h"
14 #include "socket-util.h"
15 #include "string-table.h"
18 static const char* const duplex_table
[_DUP_MAX
] = {
23 DEFINE_STRING_TABLE_LOOKUP(duplex
, Duplex
);
24 DEFINE_CONFIG_PARSE_ENUM(config_parse_duplex
, duplex
, Duplex
, "Failed to parse duplex setting");
26 static const char* const wol_table
[_WOL_MAX
] = {
28 [WOL_UCAST
] = "unicast",
29 [WOL_MCAST
] = "multicast",
30 [WOL_BCAST
] = "broadcast",
32 [WOL_MAGIC
] = "magic",
33 [WOL_MAGICSECURE
] = "secureon",
37 DEFINE_STRING_TABLE_LOOKUP(wol
, WakeOnLan
);
38 DEFINE_CONFIG_PARSE_ENUM(config_parse_wol
, wol
, WakeOnLan
, "Failed to parse WakeOnLan setting");
40 static const char* const port_table
[] = {
41 [NET_DEV_PORT_TP
] = "tp",
42 [NET_DEV_PORT_AUI
] = "aui",
43 [NET_DEV_PORT_MII
] = "mii",
44 [NET_DEV_PORT_FIBRE
] = "fibre",
45 [NET_DEV_PORT_BNC
] = "bnc",
48 DEFINE_STRING_TABLE_LOOKUP(port
, NetDevPort
);
49 DEFINE_CONFIG_PARSE_ENUM(config_parse_port
, port
, NetDevPort
, "Failed to parse Port setting");
51 static const char* const netdev_feature_table
[_NET_DEV_FEAT_MAX
] = {
52 [NET_DEV_FEAT_GSO
] = "tx-generic-segmentation",
53 [NET_DEV_FEAT_GRO
] = "rx-gro",
54 [NET_DEV_FEAT_LRO
] = "rx-lro",
55 [NET_DEV_FEAT_TSO
] = "tx-tcp-segmentation",
56 [NET_DEV_FEAT_TSO6
] = "tx-tcp6-segmentation",
59 static const char* const ethtool_link_mode_bit_table
[] = {
60 [ETHTOOL_LINK_MODE_10baseT_Half_BIT
] = "10baset-half",
61 [ETHTOOL_LINK_MODE_10baseT_Full_BIT
] = "10baset-full",
62 [ETHTOOL_LINK_MODE_100baseT_Half_BIT
] = "100baset-half",
63 [ETHTOOL_LINK_MODE_100baseT_Full_BIT
] = "100baset-full",
64 [ETHTOOL_LINK_MODE_1000baseT_Half_BIT
] = "1000baset-half",
65 [ETHTOOL_LINK_MODE_1000baseT_Full_BIT
] = "1000baset-full",
66 [ETHTOOL_LINK_MODE_Autoneg_BIT
] = "autonegotiation",
67 [ETHTOOL_LINK_MODE_TP_BIT
] = "tp",
68 [ETHTOOL_LINK_MODE_AUI_BIT
] = "aui",
69 [ETHTOOL_LINK_MODE_MII_BIT
] = "mii",
70 [ETHTOOL_LINK_MODE_FIBRE_BIT
] = "fibre",
71 [ETHTOOL_LINK_MODE_BNC_BIT
] = "bnc",
72 [ETHTOOL_LINK_MODE_10000baseT_Full_BIT
] = "10000baset-full",
73 [ETHTOOL_LINK_MODE_Pause_BIT
] = "pause",
74 [ETHTOOL_LINK_MODE_Asym_Pause_BIT
] = "asym-pause",
75 [ETHTOOL_LINK_MODE_2500baseX_Full_BIT
] = "2500basex-full",
76 [ETHTOOL_LINK_MODE_Backplane_BIT
] = "backplane",
77 [ETHTOOL_LINK_MODE_1000baseKX_Full_BIT
] = "1000basekx-full",
78 [ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT
] = "10000basekx4-full",
79 [ETHTOOL_LINK_MODE_10000baseKR_Full_BIT
] = "10000basekr-full",
80 [ETHTOOL_LINK_MODE_10000baseR_FEC_BIT
] = "10000baser-fec",
81 [ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT
] = "20000basemld2-full",
82 [ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT
] = "20000basekr2-full",
83 [ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT
] = "40000basekr4-full",
84 [ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT
] = "40000basecr4-full",
85 [ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT
] = "40000basesr4-full",
86 [ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT
] = "40000baselr4-full",
87 [ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT
] = "56000basekr4-full",
88 [ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT
] = "56000basecr4-full",
89 [ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT
] = "56000basesr4-full",
90 [ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT
] = "56000baselr4-full",
91 [ETHTOOL_LINK_MODE_25000baseCR_Full_BIT
] = "25000basecr-full",
92 [ETHTOOL_LINK_MODE_25000baseKR_Full_BIT
] = "25000basekr-full",
93 [ETHTOOL_LINK_MODE_25000baseSR_Full_BIT
] = "25000basesr-full",
94 [ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT
] = "50000basecr2-full",
95 [ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT
] = "50000basekr2-full",
96 [ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT
] = "100000basekr4-full",
97 [ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT
] = "100000basesr4-full",
98 [ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT
] = "100000basecr4-full",
99 [ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT
] = "100000baselr4-er4-full",
100 [ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT
] = "50000basesr2-full",
101 [ETHTOOL_LINK_MODE_1000baseX_Full_BIT
] = "1000basex-full",
102 [ETHTOOL_LINK_MODE_10000baseCR_Full_BIT
] = "10000basecr-full",
103 [ETHTOOL_LINK_MODE_10000baseSR_Full_BIT
] = "10000basesr-full",
104 [ETHTOOL_LINK_MODE_10000baseLR_Full_BIT
] = "10000baselr-full",
105 [ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT
] = "10000baselrm-full",
106 [ETHTOOL_LINK_MODE_10000baseER_Full_BIT
] = "10000baseer-full",
107 [ETHTOOL_LINK_MODE_2500baseT_Full_BIT
] = "2500baset-full",
108 [ETHTOOL_LINK_MODE_5000baseT_Full_BIT
] = "5000baset-full",
109 [ETHTOOL_LINK_MODE_FEC_NONE_BIT
] = "fec-none",
110 [ETHTOOL_LINK_MODE_FEC_RS_BIT
] = "fec-rs",
111 [ETHTOOL_LINK_MODE_FEC_BASER_BIT
] = "fec-baser",
113 /* Make sure the array is large enough to fit all bits */
114 assert_cc((ELEMENTSOF(ethtool_link_mode_bit_table
)-1) / 32 < N_ADVERTISE
);
116 DEFINE_STRING_TABLE_LOOKUP(ethtool_link_mode_bit
, enum ethtool_link_mode_bit_indices
);
118 int ethtool_connect(int *ret
) {
121 assert_return(ret
, -EINVAL
);
123 fd
= socket_ioctl_fd();
132 int ethtool_get_driver(int *fd
, const char *ifname
, char **ret
) {
133 struct ethtool_drvinfo ecmd
= {
134 .cmd
= ETHTOOL_GDRVINFO
137 .ifr_data
= (void*) &ecmd
143 r
= ethtool_connect(fd
);
145 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
148 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
150 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
154 d
= strdup(ecmd
.driver
);
162 int ethtool_set_speed(int *fd
, const char *ifname
, unsigned speed
, Duplex duplex
) {
163 struct ethtool_cmd ecmd
= {
167 .ifr_data
= (void*) &ecmd
169 bool need_update
= false;
172 if (speed
== 0 && duplex
== _DUP_INVALID
)
176 r
= ethtool_connect(fd
);
178 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
181 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
183 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
187 if (ethtool_cmd_speed(&ecmd
) != speed
) {
188 ethtool_cmd_speed_set(&ecmd
, speed
);
194 if (ecmd
.duplex
!= DUPLEX_HALF
) {
195 ecmd
.duplex
= DUPLEX_HALF
;
200 if (ecmd
.duplex
!= DUPLEX_FULL
) {
201 ecmd
.duplex
= DUPLEX_FULL
;
210 ecmd
.cmd
= ETHTOOL_SSET
;
212 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
220 int ethtool_set_wol(int *fd
, const char *ifname
, WakeOnLan wol
) {
221 struct ethtool_wolinfo ecmd
= {
225 .ifr_data
= (void*) &ecmd
227 bool need_update
= false;
230 if (wol
== _WOL_INVALID
)
234 r
= ethtool_connect(fd
);
236 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
239 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
241 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
247 if (ecmd
.wolopts
!= WAKE_PHY
) {
248 ecmd
.wolopts
= WAKE_PHY
;
253 if (ecmd
.wolopts
!= WAKE_UCAST
) {
254 ecmd
.wolopts
= WAKE_UCAST
;
259 if (ecmd
.wolopts
!= WAKE_MCAST
) {
260 ecmd
.wolopts
= WAKE_MCAST
;
265 if (ecmd
.wolopts
!= WAKE_BCAST
) {
266 ecmd
.wolopts
= WAKE_BCAST
;
271 if (ecmd
.wolopts
!= WAKE_ARP
) {
272 ecmd
.wolopts
= WAKE_ARP
;
277 if (ecmd
.wolopts
!= WAKE_MAGIC
) {
278 ecmd
.wolopts
= WAKE_MAGIC
;
282 case WOL_MAGICSECURE
:
283 if (ecmd
.wolopts
!= WAKE_MAGICSECURE
) {
284 ecmd
.wolopts
= WAKE_MAGICSECURE
;
289 if (ecmd
.wolopts
!= 0) {
299 ecmd
.cmd
= ETHTOOL_SWOL
;
301 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
309 static int get_stringset(int fd
, struct ifreq
*ifr
, int stringset_id
, struct ethtool_gstrings
**gstrings
) {
310 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
312 struct ethtool_sset_info info
;
316 .cmd
= ETHTOOL_GSSET_INFO
,
317 .sset_mask
= UINT64_C(1) << stringset_id
,
323 ifr
->ifr_data
= (void *) &buffer
.info
;
325 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
329 if (!buffer
.info
.sset_mask
)
332 len
= buffer
.info
.data
[0];
334 strings
= malloc0(sizeof(struct ethtool_gstrings
) + len
* ETH_GSTRING_LEN
);
338 strings
->cmd
= ETHTOOL_GSTRINGS
;
339 strings
->string_set
= stringset_id
;
342 ifr
->ifr_data
= (void *) strings
;
344 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
348 *gstrings
= TAKE_PTR(strings
);
353 static int find_feature_index(struct ethtool_gstrings
*strings
, const char *feature
) {
356 for (i
= 0; i
< strings
->len
; i
++) {
357 if (streq((char *) &strings
->data
[i
* ETH_GSTRING_LEN
], feature
))
364 int ethtool_set_features(int *fd
, const char *ifname
, int *features
) {
365 _cleanup_free_
struct ethtool_gstrings
*strings
= NULL
;
366 struct ethtool_sfeatures
*sfeatures
;
367 int block
, bit
, i
, r
;
368 struct ifreq ifr
= {};
371 r
= ethtool_connect(fd
);
373 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
376 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
378 r
= get_stringset(*fd
, &ifr
, ETH_SS_FEATURES
, &strings
);
380 return log_warning_errno(r
, "ethtool: could not get ethtool features for %s", ifname
);
382 sfeatures
= alloca0(sizeof(struct ethtool_sfeatures
) + DIV_ROUND_UP(strings
->len
, 32U) * sizeof(sfeatures
->features
[0]));
383 sfeatures
->cmd
= ETHTOOL_SFEATURES
;
384 sfeatures
->size
= DIV_ROUND_UP(strings
->len
, 32U);
386 for (i
= 0; i
< _NET_DEV_FEAT_MAX
; i
++) {
388 if (features
[i
] != -1) {
390 r
= find_feature_index(strings
, netdev_feature_table
[i
]);
392 log_warning_errno(r
, "ethtool: could not find feature: %s", netdev_feature_table
[i
]);
399 sfeatures
->features
[block
].valid
|= 1 << bit
;
402 sfeatures
->features
[block
].requested
|= 1 << bit
;
404 sfeatures
->features
[block
].requested
&= ~(1 << bit
);
408 ifr
.ifr_data
= (void *) sfeatures
;
410 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
412 return log_warning_errno(r
, "ethtool: could not set ethtool features for %s", ifname
);
417 static int get_glinksettings(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**g
) {
419 struct ethtool_link_settings req
;
420 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
422 .req
.cmd
= ETHTOOL_GLINKSETTINGS
,
424 struct ethtool_link_usettings
*u
;
428 /* The interaction user/kernel via the new API requires a small ETHTOOL_GLINKSETTINGS
429 handshake first to agree on the length of the link mode bitmaps. If kernel doesn't
430 agree with user, it returns the bitmap length it is expecting from user as a negative
431 length (and cmd field is 0). When kernel and user agree, kernel returns valid info in
432 all fields (ie. link mode length > 0 and cmd is ETHTOOL_GLINKSETTINGS). Based on
433 https://github.com/torvalds/linux/commit/3f1ac7a700d039c61d8d8b99f28d605d489a60cf
436 ifr
->ifr_data
= (void *) &ecmd
;
438 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
442 if (ecmd
.req
.link_mode_masks_nwords
>= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
445 ecmd
.req
.link_mode_masks_nwords
= -ecmd
.req
.link_mode_masks_nwords
;
447 ifr
->ifr_data
= (void *) &ecmd
;
449 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
453 if (ecmd
.req
.link_mode_masks_nwords
<= 0 || ecmd
.req
.cmd
!= ETHTOOL_GLINKSETTINGS
)
456 u
= new(struct ethtool_link_usettings
, 1);
460 *u
= (struct ethtool_link_usettings
) {
465 memcpy(u
->link_modes
.supported
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
467 offset
+= ecmd
.req
.link_mode_masks_nwords
;
468 memcpy(u
->link_modes
.advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
470 offset
+= ecmd
.req
.link_mode_masks_nwords
;
471 memcpy(u
->link_modes
.lp_advertising
, &ecmd
.link_mode_data
[offset
], 4 * ecmd
.req
.link_mode_masks_nwords
);
478 static int get_gset(int fd
, struct ifreq
*ifr
, struct ethtool_link_usettings
**u
) {
479 struct ethtool_link_usettings
*e
;
480 struct ethtool_cmd ecmd
= {
485 ifr
->ifr_data
= (void *) &ecmd
;
487 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
491 e
= new(struct ethtool_link_usettings
, 1);
495 *e
= (struct ethtool_link_usettings
) {
496 .base
.cmd
= ETHTOOL_GSET
,
497 .base
.link_mode_masks_nwords
= 1,
498 .base
.speed
= ethtool_cmd_speed(&ecmd
),
499 .base
.duplex
= ecmd
.duplex
,
500 .base
.port
= ecmd
.port
,
501 .base
.phy_address
= ecmd
.phy_address
,
502 .base
.autoneg
= ecmd
.autoneg
,
503 .base
.mdio_support
= ecmd
.mdio_support
,
505 .link_modes
.supported
[0] = ecmd
.supported
,
506 .link_modes
.advertising
[0] = ecmd
.advertising
,
507 .link_modes
.lp_advertising
[0] = ecmd
.lp_advertising
,
515 static int set_slinksettings(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
517 struct ethtool_link_settings req
;
518 __u32 link_mode_data
[3 * ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NU32
];
523 if (u
->base
.cmd
!= ETHTOOL_GLINKSETTINGS
|| u
->base
.link_mode_masks_nwords
<= 0)
527 ecmd
.req
.cmd
= ETHTOOL_SLINKSETTINGS
;
529 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.supported
, 4 * ecmd
.req
.link_mode_masks_nwords
);
531 offset
+= ecmd
.req
.link_mode_masks_nwords
;
532 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
534 offset
+= ecmd
.req
.link_mode_masks_nwords
;
535 memcpy(&ecmd
.link_mode_data
[offset
], u
->link_modes
.lp_advertising
, 4 * ecmd
.req
.link_mode_masks_nwords
);
537 ifr
->ifr_data
= (void *) &ecmd
;
539 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
546 static int set_sset(int fd
, struct ifreq
*ifr
, const struct ethtool_link_usettings
*u
) {
547 struct ethtool_cmd ecmd
= {
552 if (u
->base
.cmd
!= ETHTOOL_GSET
|| u
->base
.link_mode_masks_nwords
<= 0)
555 ecmd
.supported
= u
->link_modes
.supported
[0];
556 ecmd
.advertising
= u
->link_modes
.advertising
[0];
557 ecmd
.lp_advertising
= u
->link_modes
.lp_advertising
[0];
559 ethtool_cmd_speed_set(&ecmd
, u
->base
.speed
);
561 ecmd
.duplex
= u
->base
.duplex
;
562 ecmd
.port
= u
->base
.port
;
563 ecmd
.phy_address
= u
->base
.phy_address
;
564 ecmd
.autoneg
= u
->base
.autoneg
;
565 ecmd
.mdio_support
= u
->base
.mdio_support
;
566 ecmd
.eth_tp_mdix
= u
->base
.eth_tp_mdix
;
567 ecmd
.eth_tp_mdix_ctrl
= u
->base
.eth_tp_mdix_ctrl
;
569 ifr
->ifr_data
= (void *) &ecmd
;
571 r
= ioctl(fd
, SIOCETHTOOL
, ifr
);
578 /* If autonegotiation is disabled, the speed and duplex represent the fixed link
579 * mode and are writable if the driver supports multiple link modes. If it is
580 * enabled then they are read-only. If the link is up they represent the negotiated
581 * link mode; if the link is down, the speed is 0, %SPEED_UNKNOWN or the highest
582 * enabled speed and @duplex is %DUPLEX_UNKNOWN or the best enabled duplex mode.
584 int ethtool_set_glinksettings(
588 uint32_t advertise
[static N_ADVERTISE
],
592 _cleanup_free_
struct ethtool_link_usettings
*u
= NULL
;
593 struct ifreq ifr
= {};
596 if (autonegotiation
!= AUTONEG_DISABLE
&& memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
597 log_info("ethtool: autonegotiation is unset or enabled, the speed and duplex are not writable.");
602 r
= ethtool_connect(fd
);
604 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
607 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
609 r
= get_glinksettings(*fd
, &ifr
, &u
);
611 r
= get_gset(*fd
, &ifr
, &u
);
613 return log_warning_errno(r
, "ethtool: Cannot get device settings for %s : %m", ifname
);
617 u
->base
.speed
= DIV_ROUND_UP(speed
, 1000000);
619 if (duplex
!= _DUP_INVALID
)
620 u
->base
.duplex
= duplex
;
622 if (port
!= _NET_DEV_PORT_INVALID
)
625 if (autonegotiation
>= 0)
626 u
->base
.autoneg
= autonegotiation
;
628 if (!memeqzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
)) {
629 u
->base
.autoneg
= AUTONEG_ENABLE
;
630 memcpy(&u
->link_modes
.advertising
, advertise
, sizeof(uint32_t) * N_ADVERTISE
);
631 memzero((uint8_t*) &u
->link_modes
.advertising
+ sizeof(uint32_t) * N_ADVERTISE
,
632 ETHTOOL_LINK_MODE_MASK_MAX_KERNEL_NBYTES
- sizeof(uint32_t) * N_ADVERTISE
);
635 if (u
->base
.cmd
== ETHTOOL_GLINKSETTINGS
)
636 r
= set_slinksettings(*fd
, &ifr
, u
);
638 r
= set_sset(*fd
, &ifr
, u
);
640 return log_warning_errno(r
, "ethtool: Cannot set device settings for %s : %m", ifname
);
645 int ethtool_set_channels(int *fd
, const char *ifname
, netdev_channels
*channels
) {
646 struct ethtool_channels ecmd
= {
647 .cmd
= ETHTOOL_GCHANNELS
650 .ifr_data
= (void*) &ecmd
653 bool need_update
= false;
657 r
= ethtool_connect(fd
);
659 return log_warning_errno(r
, "ethtool: could not connect to ethtool: %m");
662 strscpy(ifr
.ifr_name
, IFNAMSIZ
, ifname
);
664 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
668 if (channels
->rx_count_set
&& ecmd
.rx_count
!= channels
->rx_count
) {
669 ecmd
.rx_count
= channels
->rx_count
;
673 if (channels
->tx_count_set
&& ecmd
.tx_count
!= channels
->tx_count
) {
674 ecmd
.tx_count
= channels
->tx_count
;
678 if (channels
->other_count_set
&& ecmd
.other_count
!= channels
->other_count
) {
679 ecmd
.other_count
= channels
->other_count
;
683 if (channels
->combined_count_set
&& ecmd
.combined_count
!= channels
->combined_count
) {
684 ecmd
.combined_count
= channels
->combined_count
;
689 ecmd
.cmd
= ETHTOOL_SCHANNELS
;
691 r
= ioctl(*fd
, SIOCETHTOOL
, &ifr
);
699 int config_parse_channel(const char *unit
,
700 const char *filename
,
703 unsigned section_line
,
709 netdev_channels
*channels
= data
;
719 r
= safe_atou32(rvalue
, &k
);
721 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to parse channel value, ignoring: %s", rvalue
);
726 log_syntax(unit
, LOG_ERR
, filename
, line
, -EINVAL
, "Invalid %s value, ignoring: %s", lvalue
, rvalue
);
730 if (streq(lvalue
, "RxChannels")) {
731 channels
->rx_count
= k
;
732 channels
->rx_count_set
= true;
733 } else if (streq(lvalue
, "TxChannels")) {
734 channels
->tx_count
= k
;
735 channels
->tx_count_set
= true;
736 } else if (streq(lvalue
, "OtherChannels")) {
737 channels
->other_count
= k
;
738 channels
->other_count_set
= true;
739 } else if (streq(lvalue
, "CombinedChannels")) {
740 channels
->combined_count
= k
;
741 channels
->combined_count_set
= true;
747 int config_parse_advertise(const char *unit
,
748 const char *filename
,
751 unsigned section_line
,
757 uint32_t *advertise
= data
;
767 if (isempty(rvalue
)) {
768 /* Empty string resets the value. */
769 memzero(advertise
, sizeof(uint32_t) * N_ADVERTISE
);
774 _cleanup_free_
char *w
= NULL
;
775 enum ethtool_link_mode_bit_indices mode
;
777 r
= extract_first_word(&p
, &w
, NULL
, 0);
781 log_syntax(unit
, LOG_ERR
, filename
, line
, r
, "Failed to split advertise modes '%s', ignoring: %m", rvalue
);
787 mode
= ethtool_link_mode_bit_from_string(w
);
788 /* We reuse the kernel provided enum which does not contain negative value. So, the cast
789 * below is mandatory. Otherwise, the check below always passes and access an invalid address. */
790 if ((int) mode
< 0) {
791 log_syntax(unit
, LOG_ERR
, filename
, line
, 0, "Failed to parse advertise mode, ignoring: %s", w
);
795 advertise
[mode
/ 32] |= 1UL << (mode
% 32);