Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-2.0-only
2 : :
3 : : #include "netlink.h"
4 : : #include "common.h"
5 : :
6 : : struct linkinfo_req_info {
7 : : struct ethnl_req_info base;
8 : : };
9 : :
10 : : struct linkinfo_reply_data {
11 : : struct ethnl_reply_data base;
12 : : struct ethtool_link_ksettings ksettings;
13 : : struct ethtool_link_settings *lsettings;
14 : : };
15 : :
16 : : #define LINKINFO_REPDATA(__reply_base) \
17 : : container_of(__reply_base, struct linkinfo_reply_data, base)
18 : :
19 : : static const struct nla_policy
20 : : linkinfo_get_policy[ETHTOOL_A_LINKINFO_MAX + 1] = {
21 : : [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT },
22 : : [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED },
23 : : [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_REJECT },
24 : : [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_REJECT },
25 : : [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT },
26 : : [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_REJECT },
27 : : [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT },
28 : : };
29 : :
30 : 0 : static int linkinfo_prepare_data(const struct ethnl_req_info *req_base,
31 : : struct ethnl_reply_data *reply_base,
32 : : struct genl_info *info)
33 : : {
34 : 0 : struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
35 : 0 : struct net_device *dev = reply_base->dev;
36 : 0 : int ret;
37 : :
38 : 0 : data->lsettings = &data->ksettings.base;
39 : :
40 [ # # ]: 0 : ret = ethnl_ops_begin(dev);
41 [ # # ]: 0 : if (ret < 0)
42 : : return ret;
43 : 0 : ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
44 [ # # ]: 0 : if (ret < 0 && info)
45 [ # # ]: 0 : GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
46 [ # # ]: 0 : ethnl_ops_complete(dev);
47 : :
48 : : return ret;
49 : : }
50 : :
51 : 0 : static int linkinfo_reply_size(const struct ethnl_req_info *req_base,
52 : : const struct ethnl_reply_data *reply_base)
53 : : {
54 : 0 : return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */
55 : : + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */
56 : : + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */
57 : : + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */
58 : : + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */
59 : 0 : + 0;
60 : : }
61 : :
62 : 0 : static int linkinfo_fill_reply(struct sk_buff *skb,
63 : : const struct ethnl_req_info *req_base,
64 : : const struct ethnl_reply_data *reply_base)
65 : : {
66 : 0 : const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
67 : :
68 [ # # # # ]: 0 : if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) ||
69 : : nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR,
70 [ # # ]: 0 : data->lsettings->phy_address) ||
71 : : nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX,
72 [ # # ]: 0 : data->lsettings->eth_tp_mdix) ||
73 : : nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
74 [ # # ]: 0 : data->lsettings->eth_tp_mdix_ctrl) ||
75 : : nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER,
76 : 0 : data->lsettings->transceiver))
77 : 0 : return -EMSGSIZE;
78 : :
79 : : return 0;
80 : : }
81 : :
82 : : const struct ethnl_request_ops ethnl_linkinfo_request_ops = {
83 : : .request_cmd = ETHTOOL_MSG_LINKINFO_GET,
84 : : .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY,
85 : : .hdr_attr = ETHTOOL_A_LINKINFO_HEADER,
86 : : .max_attr = ETHTOOL_A_LINKINFO_MAX,
87 : : .req_info_size = sizeof(struct linkinfo_req_info),
88 : : .reply_data_size = sizeof(struct linkinfo_reply_data),
89 : : .request_policy = linkinfo_get_policy,
90 : :
91 : : .prepare_data = linkinfo_prepare_data,
92 : : .reply_size = linkinfo_reply_size,
93 : : .fill_reply = linkinfo_fill_reply,
94 : : };
95 : :
96 : : /* LINKINFO_SET */
97 : :
98 : : static const struct nla_policy
99 : : linkinfo_set_policy[ETHTOOL_A_LINKINFO_MAX + 1] = {
100 : : [ETHTOOL_A_LINKINFO_UNSPEC] = { .type = NLA_REJECT },
101 : : [ETHTOOL_A_LINKINFO_HEADER] = { .type = NLA_NESTED },
102 : : [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 },
103 : : [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 },
104 : : [ETHTOOL_A_LINKINFO_TP_MDIX] = { .type = NLA_REJECT },
105 : : [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 },
106 : : [ETHTOOL_A_LINKINFO_TRANSCEIVER] = { .type = NLA_REJECT },
107 : : };
108 : :
109 : 0 : int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info)
110 : : {
111 : 0 : struct nlattr *tb[ETHTOOL_A_LINKINFO_MAX + 1];
112 : 0 : struct ethtool_link_ksettings ksettings = {};
113 : 0 : struct ethtool_link_settings *lsettings;
114 : 0 : struct ethnl_req_info req_info = {};
115 : 0 : struct net_device *dev;
116 : 0 : bool mod = false;
117 : 0 : int ret;
118 : :
119 : 0 : ret = nlmsg_parse(info->nlhdr, GENL_HDRLEN, tb,
120 : : ETHTOOL_A_LINKINFO_MAX, linkinfo_set_policy,
121 : : info->extack);
122 [ # # ]: 0 : if (ret < 0)
123 : : return ret;
124 : 0 : ret = ethnl_parse_header(&req_info, tb[ETHTOOL_A_LINKINFO_HEADER],
125 : : genl_info_net(info), info->extack, true);
126 [ # # ]: 0 : if (ret < 0)
127 : : return ret;
128 : 0 : dev = req_info.dev;
129 : 0 : ret = -EOPNOTSUPP;
130 [ # # ]: 0 : if (!dev->ethtool_ops->get_link_ksettings ||
131 [ # # ]: 0 : !dev->ethtool_ops->set_link_ksettings)
132 : 0 : goto out_dev;
133 : :
134 : 0 : rtnl_lock();
135 [ # # ]: 0 : ret = ethnl_ops_begin(dev);
136 [ # # ]: 0 : if (ret < 0)
137 : 0 : goto out_rtnl;
138 : :
139 : 0 : ret = __ethtool_get_link_ksettings(dev, &ksettings);
140 [ # # ]: 0 : if (ret < 0) {
141 [ # # ]: 0 : if (info)
142 [ # # ]: 0 : GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
143 : 0 : goto out_ops;
144 : : }
145 : 0 : lsettings = &ksettings.base;
146 : :
147 [ # # ]: 0 : ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
148 [ # # ]: 0 : ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
149 : : &mod);
150 : 0 : ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
151 [ # # ]: 0 : tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod);
152 : 0 : ret = 0;
153 [ # # ]: 0 : if (!mod)
154 : 0 : goto out_ops;
155 : :
156 : 0 : ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
157 [ # # ]: 0 : if (ret < 0)
158 [ # # ]: 0 : GENL_SET_ERR_MSG(info, "link settings update failed");
159 : : else
160 : 0 : ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
161 : :
162 : 0 : out_ops:
163 [ # # ]: 0 : ethnl_ops_complete(dev);
164 : 0 : out_rtnl:
165 : 0 : rtnl_unlock();
166 : 0 : out_dev:
167 : 0 : dev_put(dev);
168 : 0 : return ret;
169 : : }
|