Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/net/ethtool/linkinfo.c
26282 views
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
const struct nla_policy ethnl_linkinfo_get_policy[] = {
20
[ETHTOOL_A_LINKINFO_HEADER] =
21
NLA_POLICY_NESTED(ethnl_header_policy),
22
};
23
24
static int linkinfo_prepare_data(const struct ethnl_req_info *req_base,
25
struct ethnl_reply_data *reply_base,
26
const struct genl_info *info)
27
{
28
struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
29
struct net_device *dev = reply_base->dev;
30
int ret;
31
32
data->lsettings = &data->ksettings.base;
33
34
ret = ethnl_ops_begin(dev);
35
if (ret < 0)
36
return ret;
37
ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
38
if (ret < 0)
39
GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
40
ethnl_ops_complete(dev);
41
42
return ret;
43
}
44
45
static int linkinfo_reply_size(const struct ethnl_req_info *req_base,
46
const struct ethnl_reply_data *reply_base)
47
{
48
return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */
49
+ nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */
50
+ nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */
51
+ nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */
52
+ nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */
53
+ 0;
54
}
55
56
static int linkinfo_fill_reply(struct sk_buff *skb,
57
const struct ethnl_req_info *req_base,
58
const struct ethnl_reply_data *reply_base)
59
{
60
const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
61
62
if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) ||
63
nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR,
64
data->lsettings->phy_address) ||
65
nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX,
66
data->lsettings->eth_tp_mdix) ||
67
nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
68
data->lsettings->eth_tp_mdix_ctrl) ||
69
nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER,
70
data->lsettings->transceiver))
71
return -EMSGSIZE;
72
73
return 0;
74
}
75
76
/* LINKINFO_SET */
77
78
const struct nla_policy ethnl_linkinfo_set_policy[] = {
79
[ETHTOOL_A_LINKINFO_HEADER] =
80
NLA_POLICY_NESTED(ethnl_header_policy),
81
[ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 },
82
[ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 },
83
[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 },
84
};
85
86
static int
87
ethnl_set_linkinfo_validate(struct ethnl_req_info *req_info,
88
struct genl_info *info)
89
{
90
const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
91
92
if (!ops->get_link_ksettings || !ops->set_link_ksettings)
93
return -EOPNOTSUPP;
94
return 1;
95
}
96
97
static int
98
ethnl_set_linkinfo(struct ethnl_req_info *req_info, struct genl_info *info)
99
{
100
struct ethtool_link_ksettings ksettings = {};
101
struct ethtool_link_settings *lsettings;
102
struct net_device *dev = req_info->dev;
103
struct nlattr **tb = info->attrs;
104
bool mod = false;
105
int ret;
106
107
ret = __ethtool_get_link_ksettings(dev, &ksettings);
108
if (ret < 0) {
109
GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
110
return ret;
111
}
112
lsettings = &ksettings.base;
113
114
ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
115
ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
116
&mod);
117
ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
118
tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod);
119
if (!mod)
120
return 0;
121
122
ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
123
if (ret < 0) {
124
GENL_SET_ERR_MSG(info, "link settings update failed");
125
return ret;
126
}
127
128
return 1;
129
}
130
131
const struct ethnl_request_ops ethnl_linkinfo_request_ops = {
132
.request_cmd = ETHTOOL_MSG_LINKINFO_GET,
133
.reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY,
134
.hdr_attr = ETHTOOL_A_LINKINFO_HEADER,
135
.req_info_size = sizeof(struct linkinfo_req_info),
136
.reply_data_size = sizeof(struct linkinfo_reply_data),
137
138
.prepare_data = linkinfo_prepare_data,
139
.reply_size = linkinfo_reply_size,
140
.fill_reply = linkinfo_fill_reply,
141
142
.set_validate = ethnl_set_linkinfo_validate,
143
.set = ethnl_set_linkinfo,
144
.set_ntf_cmd = ETHTOOL_MSG_LINKINFO_NTF,
145
};
146
147