blob: 81856fa1e632279f0ab9761747ad4bbf3ded1ba9 [file] [log] [blame]
Michal Kubecekf625aa92019-12-27 15:56:08 +01001// SPDX-License-Identifier: GPL-2.0-only
2
3#include "netlink.h"
4#include "common.h"
5#include "bitset.h"
6
7struct linkmodes_req_info {
8 struct ethnl_req_info base;
9};
10
11struct linkmodes_reply_data {
12 struct ethnl_reply_data base;
13 struct ethtool_link_ksettings ksettings;
14 struct ethtool_link_settings *lsettings;
15 bool peer_empty;
16};
17
18#define LINKMODES_REPDATA(__reply_base) \
19 container_of(__reply_base, struct linkmodes_reply_data, base)
20
21static const struct nla_policy
22linkmodes_get_policy[ETHTOOL_A_LINKMODES_MAX + 1] = {
23 [ETHTOOL_A_LINKMODES_UNSPEC] = { .type = NLA_REJECT },
24 [ETHTOOL_A_LINKMODES_HEADER] = { .type = NLA_NESTED },
25 [ETHTOOL_A_LINKMODES_AUTONEG] = { .type = NLA_REJECT },
26 [ETHTOOL_A_LINKMODES_OURS] = { .type = NLA_REJECT },
27 [ETHTOOL_A_LINKMODES_PEER] = { .type = NLA_REJECT },
28 [ETHTOOL_A_LINKMODES_SPEED] = { .type = NLA_REJECT },
29 [ETHTOOL_A_LINKMODES_DUPLEX] = { .type = NLA_REJECT },
30};
31
32static int linkmodes_prepare_data(const struct ethnl_req_info *req_base,
33 struct ethnl_reply_data *reply_base,
34 struct genl_info *info)
35{
36 struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
37 struct net_device *dev = reply_base->dev;
38 int ret;
39
40 data->lsettings = &data->ksettings.base;
41
42 ret = ethnl_ops_begin(dev);
43 if (ret < 0)
44 return ret;
45
46 ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
47 if (ret < 0 && info) {
48 GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
49 goto out;
50 }
51
52 data->peer_empty =
53 bitmap_empty(data->ksettings.link_modes.lp_advertising,
54 __ETHTOOL_LINK_MODE_MASK_NBITS);
55
56out:
57 ethnl_ops_complete(dev);
58 return ret;
59}
60
61static int linkmodes_reply_size(const struct ethnl_req_info *req_base,
62 const struct ethnl_reply_data *reply_base)
63{
64 const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
65 const struct ethtool_link_ksettings *ksettings = &data->ksettings;
66 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
67 int len, ret;
68
69 len = nla_total_size(sizeof(u8)) /* LINKMODES_AUTONEG */
70 + nla_total_size(sizeof(u32)) /* LINKMODES_SPEED */
71 + nla_total_size(sizeof(u8)) /* LINKMODES_DUPLEX */
72 + 0;
73 ret = ethnl_bitset_size(ksettings->link_modes.advertising,
74 ksettings->link_modes.supported,
75 __ETHTOOL_LINK_MODE_MASK_NBITS,
76 link_mode_names, compact);
77 if (ret < 0)
78 return ret;
79 len += ret;
80 if (!data->peer_empty) {
81 ret = ethnl_bitset_size(ksettings->link_modes.lp_advertising,
82 NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
83 link_mode_names, compact);
84 if (ret < 0)
85 return ret;
86 len += ret;
87 }
88
89 return len;
90}
91
92static int linkmodes_fill_reply(struct sk_buff *skb,
93 const struct ethnl_req_info *req_base,
94 const struct ethnl_reply_data *reply_base)
95{
96 const struct linkmodes_reply_data *data = LINKMODES_REPDATA(reply_base);
97 const struct ethtool_link_ksettings *ksettings = &data->ksettings;
98 const struct ethtool_link_settings *lsettings = &ksettings->base;
99 bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
100 int ret;
101
102 if (nla_put_u8(skb, ETHTOOL_A_LINKMODES_AUTONEG, lsettings->autoneg))
103 return -EMSGSIZE;
104
105 ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_OURS,
106 ksettings->link_modes.advertising,
107 ksettings->link_modes.supported,
108 __ETHTOOL_LINK_MODE_MASK_NBITS, link_mode_names,
109 compact);
110 if (ret < 0)
111 return -EMSGSIZE;
112 if (!data->peer_empty) {
113 ret = ethnl_put_bitset(skb, ETHTOOL_A_LINKMODES_PEER,
114 ksettings->link_modes.lp_advertising,
115 NULL, __ETHTOOL_LINK_MODE_MASK_NBITS,
116 link_mode_names, compact);
117 if (ret < 0)
118 return -EMSGSIZE;
119 }
120
121 if (nla_put_u32(skb, ETHTOOL_A_LINKMODES_SPEED, lsettings->speed) ||
122 nla_put_u8(skb, ETHTOOL_A_LINKMODES_DUPLEX, lsettings->duplex))
123 return -EMSGSIZE;
124
125 return 0;
126}
127
128const struct ethnl_request_ops ethnl_linkmodes_request_ops = {
129 .request_cmd = ETHTOOL_MSG_LINKMODES_GET,
130 .reply_cmd = ETHTOOL_MSG_LINKMODES_GET_REPLY,
131 .hdr_attr = ETHTOOL_A_LINKMODES_HEADER,
132 .max_attr = ETHTOOL_A_LINKMODES_MAX,
133 .req_info_size = sizeof(struct linkmodes_req_info),
134 .reply_data_size = sizeof(struct linkmodes_reply_data),
135 .request_policy = linkmodes_get_policy,
136
137 .prepare_data = linkmodes_prepare_data,
138 .reply_size = linkmodes_reply_size,
139 .fill_reply = linkmodes_fill_reply,
140};