xref: /linux/net/ethtool/linkstate.c (revision 132db93572821ec2fdf81e354cc40f558faf7e4f)
1 // SPDX-License-Identifier: GPL-2.0-only
2 
3 #include "netlink.h"
4 #include "common.h"
5 #include <linux/phy.h>
6 
7 struct linkstate_req_info {
8 	struct ethnl_req_info		base;
9 };
10 
11 struct linkstate_reply_data {
12 	struct ethnl_reply_data		base;
13 	int				link;
14 	int				sqi;
15 	int				sqi_max;
16 };
17 
18 #define LINKSTATE_REPDATA(__reply_base) \
19 	container_of(__reply_base, struct linkstate_reply_data, base)
20 
21 static const struct nla_policy
22 linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = {
23 	[ETHTOOL_A_LINKSTATE_UNSPEC]		= { .type = NLA_REJECT },
24 	[ETHTOOL_A_LINKSTATE_HEADER]		= { .type = NLA_NESTED },
25 	[ETHTOOL_A_LINKSTATE_LINK]		= { .type = NLA_REJECT },
26 	[ETHTOOL_A_LINKSTATE_SQI]		= { .type = NLA_REJECT },
27 	[ETHTOOL_A_LINKSTATE_SQI_MAX]		= { .type = NLA_REJECT },
28 };
29 
30 static int linkstate_get_sqi(struct net_device *dev)
31 {
32 	struct phy_device *phydev = dev->phydev;
33 	int ret;
34 
35 	if (!phydev)
36 		return -EOPNOTSUPP;
37 
38 	mutex_lock(&phydev->lock);
39 	if (!phydev->drv || !phydev->drv->get_sqi)
40 		ret = -EOPNOTSUPP;
41 	else
42 		ret = phydev->drv->get_sqi(phydev);
43 	mutex_unlock(&phydev->lock);
44 
45 	return ret;
46 }
47 
48 static int linkstate_get_sqi_max(struct net_device *dev)
49 {
50 	struct phy_device *phydev = dev->phydev;
51 	int ret;
52 
53 	if (!phydev)
54 		return -EOPNOTSUPP;
55 
56 	mutex_lock(&phydev->lock);
57 	if (!phydev->drv || !phydev->drv->get_sqi_max)
58 		ret = -EOPNOTSUPP;
59 	else
60 		ret = phydev->drv->get_sqi_max(phydev);
61 	mutex_unlock(&phydev->lock);
62 
63 	return ret;
64 }
65 
66 static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
67 				  struct ethnl_reply_data *reply_base,
68 				  struct genl_info *info)
69 {
70 	struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
71 	struct net_device *dev = reply_base->dev;
72 	int ret;
73 
74 	ret = ethnl_ops_begin(dev);
75 	if (ret < 0)
76 		return ret;
77 	data->link = __ethtool_get_link(dev);
78 
79 	ret = linkstate_get_sqi(dev);
80 	if (ret < 0 && ret != -EOPNOTSUPP)
81 		goto out;
82 	data->sqi = ret;
83 
84 	ret = linkstate_get_sqi_max(dev);
85 	if (ret < 0 && ret != -EOPNOTSUPP)
86 		goto out;
87 	data->sqi_max = ret;
88 
89 	ret = 0;
90 out:
91 	ethnl_ops_complete(dev);
92 	return ret;
93 }
94 
95 static int linkstate_reply_size(const struct ethnl_req_info *req_base,
96 				const struct ethnl_reply_data *reply_base)
97 {
98 	struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
99 	int len;
100 
101 	len = nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */
102 		+ 0;
103 
104 	if (data->sqi != -EOPNOTSUPP)
105 		len += nla_total_size(sizeof(u32));
106 
107 	if (data->sqi_max != -EOPNOTSUPP)
108 		len += nla_total_size(sizeof(u32));
109 
110 	return len;
111 }
112 
113 static int linkstate_fill_reply(struct sk_buff *skb,
114 				const struct ethnl_req_info *req_base,
115 				const struct ethnl_reply_data *reply_base)
116 {
117 	struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
118 
119 	if (data->link >= 0 &&
120 	    nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link))
121 		return -EMSGSIZE;
122 
123 	if (data->sqi != -EOPNOTSUPP &&
124 	    nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI, data->sqi))
125 		return -EMSGSIZE;
126 
127 	if (data->sqi_max != -EOPNOTSUPP &&
128 	    nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max))
129 		return -EMSGSIZE;
130 
131 	return 0;
132 }
133 
134 const struct ethnl_request_ops ethnl_linkstate_request_ops = {
135 	.request_cmd		= ETHTOOL_MSG_LINKSTATE_GET,
136 	.reply_cmd		= ETHTOOL_MSG_LINKSTATE_GET_REPLY,
137 	.hdr_attr		= ETHTOOL_A_LINKSTATE_HEADER,
138 	.max_attr		= ETHTOOL_A_LINKSTATE_MAX,
139 	.req_info_size		= sizeof(struct linkstate_req_info),
140 	.reply_data_size	= sizeof(struct linkstate_reply_data),
141 	.request_policy		= linkstate_get_policy,
142 
143 	.prepare_data		= linkstate_prepare_data,
144 	.reply_size		= linkstate_reply_size,
145 	.fill_reply		= linkstate_fill_reply,
146 };
147