1 // SPDX-License-Identifier: GPL-2.0-only
4 #include <linux/ethtool_netlink.h>
10 struct ethnl_req_info base;
13 struct plca_reply_data {
14 struct ethnl_reply_data base;
15 struct phy_plca_cfg plca_cfg;
16 struct phy_plca_status plca_st;
19 // Helpers ------------------------------------------------------------------ //
21 #define PLCA_REPDATA(__reply_base) \
22 container_of(__reply_base, struct plca_reply_data, base)
24 static void plca_update_sint(int *dst, const struct nlattr *attr,
30 *dst = nla_get_u32(attr);
34 // PLCA get configuration message ------------------------------------------- //
36 const struct nla_policy ethnl_plca_get_cfg_policy[] = {
37 [ETHTOOL_A_PLCA_HEADER] =
38 NLA_POLICY_NESTED(ethnl_header_policy),
41 static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
42 struct ethnl_reply_data *reply_base,
43 const struct genl_info *info)
45 struct plca_reply_data *data = PLCA_REPDATA(reply_base);
46 struct net_device *dev = reply_base->dev;
47 const struct ethtool_phy_ops *ops;
50 // check that the PHY device is available and connected
56 // note: rtnl_lock is held already by ethnl_default_doit
57 ops = ethtool_phy_ops;
58 if (!ops || !ops->get_plca_cfg) {
63 ret = ethnl_ops_begin(dev);
67 memset(&data->plca_cfg, 0xff,
68 sizeof_field(struct plca_reply_data, plca_cfg));
70 ret = ops->get_plca_cfg(dev->phydev, &data->plca_cfg);
71 ethnl_ops_complete(dev);
77 static int plca_get_cfg_reply_size(const struct ethnl_req_info *req_base,
78 const struct ethnl_reply_data *reply_base)
80 return nla_total_size(sizeof(u16)) + /* _VERSION */
81 nla_total_size(sizeof(u8)) + /* _ENABLED */
82 nla_total_size(sizeof(u32)) + /* _NODE_CNT */
83 nla_total_size(sizeof(u32)) + /* _NODE_ID */
84 nla_total_size(sizeof(u32)) + /* _TO_TIMER */
85 nla_total_size(sizeof(u32)) + /* _BURST_COUNT */
86 nla_total_size(sizeof(u32)); /* _BURST_TIMER */
89 static int plca_get_cfg_fill_reply(struct sk_buff *skb,
90 const struct ethnl_req_info *req_base,
91 const struct ethnl_reply_data *reply_base)
93 const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
94 const struct phy_plca_cfg *plca = &data->plca_cfg;
96 if ((plca->version >= 0 &&
97 nla_put_u16(skb, ETHTOOL_A_PLCA_VERSION, plca->version)) ||
98 (plca->enabled >= 0 &&
99 nla_put_u8(skb, ETHTOOL_A_PLCA_ENABLED, !!plca->enabled)) ||
100 (plca->node_id >= 0 &&
101 nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_ID, plca->node_id)) ||
102 (plca->node_cnt >= 0 &&
103 nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_CNT, plca->node_cnt)) ||
104 (plca->to_tmr >= 0 &&
105 nla_put_u32(skb, ETHTOOL_A_PLCA_TO_TMR, plca->to_tmr)) ||
106 (plca->burst_cnt >= 0 &&
107 nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_CNT, plca->burst_cnt)) ||
108 (plca->burst_tmr >= 0 &&
109 nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_TMR, plca->burst_tmr)))
115 // PLCA set configuration message ------------------------------------------- //
117 const struct nla_policy ethnl_plca_set_cfg_policy[] = {
118 [ETHTOOL_A_PLCA_HEADER] =
119 NLA_POLICY_NESTED(ethnl_header_policy),
120 [ETHTOOL_A_PLCA_ENABLED] = NLA_POLICY_MAX(NLA_U8, 1),
121 [ETHTOOL_A_PLCA_NODE_ID] = NLA_POLICY_MAX(NLA_U32, 255),
122 [ETHTOOL_A_PLCA_NODE_CNT] = NLA_POLICY_RANGE(NLA_U32, 1, 255),
123 [ETHTOOL_A_PLCA_TO_TMR] = NLA_POLICY_MAX(NLA_U32, 255),
124 [ETHTOOL_A_PLCA_BURST_CNT] = NLA_POLICY_MAX(NLA_U32, 255),
125 [ETHTOOL_A_PLCA_BURST_TMR] = NLA_POLICY_MAX(NLA_U32, 255),
129 ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
131 struct net_device *dev = req_info->dev;
132 const struct ethtool_phy_ops *ops;
133 struct nlattr **tb = info->attrs;
134 struct phy_plca_cfg plca_cfg;
138 // check that the PHY device is available and connected
142 ops = ethtool_phy_ops;
143 if (!ops || !ops->set_plca_cfg)
146 memset(&plca_cfg, 0xff, sizeof(plca_cfg));
147 plca_update_sint(&plca_cfg.enabled, tb[ETHTOOL_A_PLCA_ENABLED], &mod);
148 plca_update_sint(&plca_cfg.node_id, tb[ETHTOOL_A_PLCA_NODE_ID], &mod);
149 plca_update_sint(&plca_cfg.node_cnt, tb[ETHTOOL_A_PLCA_NODE_CNT], &mod);
150 plca_update_sint(&plca_cfg.to_tmr, tb[ETHTOOL_A_PLCA_TO_TMR], &mod);
151 plca_update_sint(&plca_cfg.burst_cnt, tb[ETHTOOL_A_PLCA_BURST_CNT],
153 plca_update_sint(&plca_cfg.burst_tmr, tb[ETHTOOL_A_PLCA_BURST_TMR],
158 ret = ops->set_plca_cfg(dev->phydev, &plca_cfg, info->extack);
159 return ret < 0 ? ret : 1;
162 const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
163 .request_cmd = ETHTOOL_MSG_PLCA_GET_CFG,
164 .reply_cmd = ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
165 .hdr_attr = ETHTOOL_A_PLCA_HEADER,
166 .req_info_size = sizeof(struct plca_req_info),
167 .reply_data_size = sizeof(struct plca_reply_data),
169 .prepare_data = plca_get_cfg_prepare_data,
170 .reply_size = plca_get_cfg_reply_size,
171 .fill_reply = plca_get_cfg_fill_reply,
173 .set = ethnl_set_plca,
174 .set_ntf_cmd = ETHTOOL_MSG_PLCA_NTF,
177 // PLCA get status message -------------------------------------------------- //
179 const struct nla_policy ethnl_plca_get_status_policy[] = {
180 [ETHTOOL_A_PLCA_HEADER] =
181 NLA_POLICY_NESTED(ethnl_header_policy),
184 static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
185 struct ethnl_reply_data *reply_base,
186 const struct genl_info *info)
188 struct plca_reply_data *data = PLCA_REPDATA(reply_base);
189 struct net_device *dev = reply_base->dev;
190 const struct ethtool_phy_ops *ops;
193 // check that the PHY device is available and connected
199 // note: rtnl_lock is held already by ethnl_default_doit
200 ops = ethtool_phy_ops;
201 if (!ops || !ops->get_plca_status) {
206 ret = ethnl_ops_begin(dev);
210 memset(&data->plca_st, 0xff,
211 sizeof_field(struct plca_reply_data, plca_st));
213 ret = ops->get_plca_status(dev->phydev, &data->plca_st);
214 ethnl_ops_complete(dev);
219 static int plca_get_status_reply_size(const struct ethnl_req_info *req_base,
220 const struct ethnl_reply_data *reply_base)
222 return nla_total_size(sizeof(u8)); /* _STATUS */
225 static int plca_get_status_fill_reply(struct sk_buff *skb,
226 const struct ethnl_req_info *req_base,
227 const struct ethnl_reply_data *reply_base)
229 const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
230 const u8 status = data->plca_st.pst;
232 if (nla_put_u8(skb, ETHTOOL_A_PLCA_STATUS, !!status))
238 const struct ethnl_request_ops ethnl_plca_status_request_ops = {
239 .request_cmd = ETHTOOL_MSG_PLCA_GET_STATUS,
240 .reply_cmd = ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
241 .hdr_attr = ETHTOOL_A_PLCA_HEADER,
242 .req_info_size = sizeof(struct plca_req_info),
243 .reply_data_size = sizeof(struct plca_reply_data),
245 .prepare_data = plca_get_status_prepare_data,
246 .reply_size = plca_get_status_reply_size,
247 .fill_reply = plca_get_status_fill_reply,