xref: /openbmc/linux/net/ethtool/plca.c (revision f291209eca5eba0b4704fa0832af57b12dbc1a02)
18580e16cSPiergiorgio Beruto // SPDX-License-Identifier: GPL-2.0-only
28580e16cSPiergiorgio Beruto 
38580e16cSPiergiorgio Beruto #include <linux/phy.h>
48580e16cSPiergiorgio Beruto #include <linux/ethtool_netlink.h>
58580e16cSPiergiorgio Beruto 
68580e16cSPiergiorgio Beruto #include "netlink.h"
78580e16cSPiergiorgio Beruto #include "common.h"
88580e16cSPiergiorgio Beruto 
98580e16cSPiergiorgio Beruto struct plca_req_info {
108580e16cSPiergiorgio Beruto 	struct ethnl_req_info		base;
118580e16cSPiergiorgio Beruto };
128580e16cSPiergiorgio Beruto 
138580e16cSPiergiorgio Beruto struct plca_reply_data {
148580e16cSPiergiorgio Beruto 	struct ethnl_reply_data		base;
158580e16cSPiergiorgio Beruto 	struct phy_plca_cfg		plca_cfg;
168580e16cSPiergiorgio Beruto 	struct phy_plca_status		plca_st;
178580e16cSPiergiorgio Beruto };
188580e16cSPiergiorgio Beruto 
198580e16cSPiergiorgio Beruto // Helpers ------------------------------------------------------------------ //
208580e16cSPiergiorgio Beruto 
218580e16cSPiergiorgio Beruto #define PLCA_REPDATA(__reply_base) \
228580e16cSPiergiorgio Beruto 	container_of(__reply_base, struct plca_reply_data, base)
238580e16cSPiergiorgio Beruto 
248580e16cSPiergiorgio Beruto // PLCA get configuration message ------------------------------------------- //
258580e16cSPiergiorgio Beruto 
268580e16cSPiergiorgio Beruto const struct nla_policy ethnl_plca_get_cfg_policy[] = {
278580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_HEADER]		=
288580e16cSPiergiorgio Beruto 		NLA_POLICY_NESTED(ethnl_header_policy),
298580e16cSPiergiorgio Beruto };
308580e16cSPiergiorgio Beruto 
plca_update_sint(int * dst,struct nlattr ** tb,u32 attrid,bool * mod)31*8957261cSParthiban Veerasooran static void plca_update_sint(int *dst, struct nlattr **tb, u32 attrid,
32*8957261cSParthiban Veerasooran 			     bool *mod)
33*8957261cSParthiban Veerasooran {
34*8957261cSParthiban Veerasooran 	const struct nlattr *attr = tb[attrid];
35*8957261cSParthiban Veerasooran 
36*8957261cSParthiban Veerasooran 	if (!attr ||
37*8957261cSParthiban Veerasooran 	    WARN_ON_ONCE(attrid >= ARRAY_SIZE(ethnl_plca_set_cfg_policy)))
38*8957261cSParthiban Veerasooran 		return;
39*8957261cSParthiban Veerasooran 
40*8957261cSParthiban Veerasooran 	switch (ethnl_plca_set_cfg_policy[attrid].type) {
41*8957261cSParthiban Veerasooran 	case NLA_U8:
42*8957261cSParthiban Veerasooran 		*dst = nla_get_u8(attr);
43*8957261cSParthiban Veerasooran 		break;
44*8957261cSParthiban Veerasooran 	case NLA_U32:
45*8957261cSParthiban Veerasooran 		*dst = nla_get_u32(attr);
46*8957261cSParthiban Veerasooran 		break;
47*8957261cSParthiban Veerasooran 	default:
48*8957261cSParthiban Veerasooran 		WARN_ON_ONCE(1);
49*8957261cSParthiban Veerasooran 	}
50*8957261cSParthiban Veerasooran 
51*8957261cSParthiban Veerasooran 	*mod = true;
52*8957261cSParthiban Veerasooran }
53*8957261cSParthiban Veerasooran 
plca_get_cfg_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)548580e16cSPiergiorgio Beruto static int plca_get_cfg_prepare_data(const struct ethnl_req_info *req_base,
558580e16cSPiergiorgio Beruto 				     struct ethnl_reply_data *reply_base,
56f946270dSJakub Kicinski 				     const struct genl_info *info)
578580e16cSPiergiorgio Beruto {
588580e16cSPiergiorgio Beruto 	struct plca_reply_data *data = PLCA_REPDATA(reply_base);
598580e16cSPiergiorgio Beruto 	struct net_device *dev = reply_base->dev;
608580e16cSPiergiorgio Beruto 	const struct ethtool_phy_ops *ops;
618580e16cSPiergiorgio Beruto 	int ret;
628580e16cSPiergiorgio Beruto 
638580e16cSPiergiorgio Beruto 	// check that the PHY device is available and connected
648580e16cSPiergiorgio Beruto 	if (!dev->phydev) {
658580e16cSPiergiorgio Beruto 		ret = -EOPNOTSUPP;
668580e16cSPiergiorgio Beruto 		goto out;
678580e16cSPiergiorgio Beruto 	}
688580e16cSPiergiorgio Beruto 
698580e16cSPiergiorgio Beruto 	// note: rtnl_lock is held already by ethnl_default_doit
708580e16cSPiergiorgio Beruto 	ops = ethtool_phy_ops;
718580e16cSPiergiorgio Beruto 	if (!ops || !ops->get_plca_cfg) {
728580e16cSPiergiorgio Beruto 		ret = -EOPNOTSUPP;
738580e16cSPiergiorgio Beruto 		goto out;
748580e16cSPiergiorgio Beruto 	}
758580e16cSPiergiorgio Beruto 
768580e16cSPiergiorgio Beruto 	ret = ethnl_ops_begin(dev);
7728dbf774SPiergiorgio Beruto 	if (ret < 0)
788580e16cSPiergiorgio Beruto 		goto out;
798580e16cSPiergiorgio Beruto 
808580e16cSPiergiorgio Beruto 	memset(&data->plca_cfg, 0xff,
818580e16cSPiergiorgio Beruto 	       sizeof_field(struct plca_reply_data, plca_cfg));
828580e16cSPiergiorgio Beruto 
838580e16cSPiergiorgio Beruto 	ret = ops->get_plca_cfg(dev->phydev, &data->plca_cfg);
848580e16cSPiergiorgio Beruto 	ethnl_ops_complete(dev);
858580e16cSPiergiorgio Beruto 
868580e16cSPiergiorgio Beruto out:
878580e16cSPiergiorgio Beruto 	return ret;
888580e16cSPiergiorgio Beruto }
898580e16cSPiergiorgio Beruto 
plca_get_cfg_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)908580e16cSPiergiorgio Beruto static int plca_get_cfg_reply_size(const struct ethnl_req_info *req_base,
918580e16cSPiergiorgio Beruto 				   const struct ethnl_reply_data *reply_base)
928580e16cSPiergiorgio Beruto {
938580e16cSPiergiorgio Beruto 	return nla_total_size(sizeof(u16)) +	/* _VERSION */
948580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u8)) +	/* _ENABLED */
958580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u32)) +	/* _NODE_CNT */
968580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u32)) +	/* _NODE_ID */
978580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u32)) +	/* _TO_TIMER */
988580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u32)) +	/* _BURST_COUNT */
998580e16cSPiergiorgio Beruto 	       nla_total_size(sizeof(u32));	/* _BURST_TIMER */
1008580e16cSPiergiorgio Beruto }
1018580e16cSPiergiorgio Beruto 
plca_get_cfg_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)1028580e16cSPiergiorgio Beruto static int plca_get_cfg_fill_reply(struct sk_buff *skb,
1038580e16cSPiergiorgio Beruto 				   const struct ethnl_req_info *req_base,
1048580e16cSPiergiorgio Beruto 				   const struct ethnl_reply_data *reply_base)
1058580e16cSPiergiorgio Beruto {
1068580e16cSPiergiorgio Beruto 	const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
1078580e16cSPiergiorgio Beruto 	const struct phy_plca_cfg *plca = &data->plca_cfg;
1088580e16cSPiergiorgio Beruto 
1098580e16cSPiergiorgio Beruto 	if ((plca->version >= 0 &&
1108580e16cSPiergiorgio Beruto 	     nla_put_u16(skb, ETHTOOL_A_PLCA_VERSION, plca->version)) ||
1118580e16cSPiergiorgio Beruto 	    (plca->enabled >= 0 &&
1128580e16cSPiergiorgio Beruto 	     nla_put_u8(skb, ETHTOOL_A_PLCA_ENABLED, !!plca->enabled)) ||
1138580e16cSPiergiorgio Beruto 	    (plca->node_id >= 0 &&
1148580e16cSPiergiorgio Beruto 	     nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_ID, plca->node_id)) ||
1158580e16cSPiergiorgio Beruto 	    (plca->node_cnt >= 0 &&
1168580e16cSPiergiorgio Beruto 	     nla_put_u32(skb, ETHTOOL_A_PLCA_NODE_CNT, plca->node_cnt)) ||
1178580e16cSPiergiorgio Beruto 	    (plca->to_tmr >= 0 &&
1188580e16cSPiergiorgio Beruto 	     nla_put_u32(skb, ETHTOOL_A_PLCA_TO_TMR, plca->to_tmr)) ||
1198580e16cSPiergiorgio Beruto 	    (plca->burst_cnt >= 0 &&
1208580e16cSPiergiorgio Beruto 	     nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_CNT, plca->burst_cnt)) ||
1218580e16cSPiergiorgio Beruto 	    (plca->burst_tmr >= 0 &&
1228580e16cSPiergiorgio Beruto 	     nla_put_u32(skb, ETHTOOL_A_PLCA_BURST_TMR, plca->burst_tmr)))
1238580e16cSPiergiorgio Beruto 		return -EMSGSIZE;
1248580e16cSPiergiorgio Beruto 
1258580e16cSPiergiorgio Beruto 	return 0;
1268580e16cSPiergiorgio Beruto };
1278580e16cSPiergiorgio Beruto 
1288580e16cSPiergiorgio Beruto // PLCA set configuration message ------------------------------------------- //
1298580e16cSPiergiorgio Beruto 
1308580e16cSPiergiorgio Beruto const struct nla_policy ethnl_plca_set_cfg_policy[] = {
1318580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_HEADER]		=
1328580e16cSPiergiorgio Beruto 		NLA_POLICY_NESTED(ethnl_header_policy),
1338580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_ENABLED]	= NLA_POLICY_MAX(NLA_U8, 1),
1348580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_NODE_ID]	= NLA_POLICY_MAX(NLA_U32, 255),
1358580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_NODE_CNT]	= NLA_POLICY_RANGE(NLA_U32, 1, 255),
1368580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_TO_TMR]		= NLA_POLICY_MAX(NLA_U32, 255),
1378580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_BURST_CNT]	= NLA_POLICY_MAX(NLA_U32, 255),
1388580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_BURST_TMR]	= NLA_POLICY_MAX(NLA_U32, 255),
1398580e16cSPiergiorgio Beruto };
1408580e16cSPiergiorgio Beruto 
14104007961SJakub Kicinski static int
ethnl_set_plca(struct ethnl_req_info * req_info,struct genl_info * info)14204007961SJakub Kicinski ethnl_set_plca(struct ethnl_req_info *req_info, struct genl_info *info)
1438580e16cSPiergiorgio Beruto {
14404007961SJakub Kicinski 	struct net_device *dev = req_info->dev;
1458580e16cSPiergiorgio Beruto 	const struct ethtool_phy_ops *ops;
14604007961SJakub Kicinski 	struct nlattr **tb = info->attrs;
1478580e16cSPiergiorgio Beruto 	struct phy_plca_cfg plca_cfg;
1488580e16cSPiergiorgio Beruto 	bool mod = false;
1498580e16cSPiergiorgio Beruto 	int ret;
1508580e16cSPiergiorgio Beruto 
1518580e16cSPiergiorgio Beruto 	// check that the PHY device is available and connected
15204007961SJakub Kicinski 	if (!dev->phydev)
15304007961SJakub Kicinski 		return -EOPNOTSUPP;
1548580e16cSPiergiorgio Beruto 
1558580e16cSPiergiorgio Beruto 	ops = ethtool_phy_ops;
15604007961SJakub Kicinski 	if (!ops || !ops->set_plca_cfg)
15704007961SJakub Kicinski 		return -EOPNOTSUPP;
1588580e16cSPiergiorgio Beruto 
1598580e16cSPiergiorgio Beruto 	memset(&plca_cfg, 0xff, sizeof(plca_cfg));
160*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.enabled, tb, ETHTOOL_A_PLCA_ENABLED, &mod);
161*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.node_id, tb, ETHTOOL_A_PLCA_NODE_ID, &mod);
162*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.node_cnt, tb, ETHTOOL_A_PLCA_NODE_CNT, &mod);
163*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.to_tmr, tb, ETHTOOL_A_PLCA_TO_TMR, &mod);
164*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.burst_cnt, tb, ETHTOOL_A_PLCA_BURST_CNT,
1658580e16cSPiergiorgio Beruto 			 &mod);
166*8957261cSParthiban Veerasooran 	plca_update_sint(&plca_cfg.burst_tmr, tb, ETHTOOL_A_PLCA_BURST_TMR,
1678580e16cSPiergiorgio Beruto 			 &mod);
1688580e16cSPiergiorgio Beruto 	if (!mod)
16904007961SJakub Kicinski 		return 0;
1708580e16cSPiergiorgio Beruto 
1718580e16cSPiergiorgio Beruto 	ret = ops->set_plca_cfg(dev->phydev, &plca_cfg, info->extack);
17204007961SJakub Kicinski 	return ret < 0 ? ret : 1;
1738580e16cSPiergiorgio Beruto }
1748580e16cSPiergiorgio Beruto 
17504007961SJakub Kicinski const struct ethnl_request_ops ethnl_plca_cfg_request_ops = {
17604007961SJakub Kicinski 	.request_cmd		= ETHTOOL_MSG_PLCA_GET_CFG,
17704007961SJakub Kicinski 	.reply_cmd		= ETHTOOL_MSG_PLCA_GET_CFG_REPLY,
17804007961SJakub Kicinski 	.hdr_attr		= ETHTOOL_A_PLCA_HEADER,
17904007961SJakub Kicinski 	.req_info_size		= sizeof(struct plca_req_info),
18004007961SJakub Kicinski 	.reply_data_size	= sizeof(struct plca_reply_data),
18104007961SJakub Kicinski 
18204007961SJakub Kicinski 	.prepare_data		= plca_get_cfg_prepare_data,
18304007961SJakub Kicinski 	.reply_size		= plca_get_cfg_reply_size,
18404007961SJakub Kicinski 	.fill_reply		= plca_get_cfg_fill_reply,
18504007961SJakub Kicinski 
18604007961SJakub Kicinski 	.set			= ethnl_set_plca,
18704007961SJakub Kicinski 	.set_ntf_cmd		= ETHTOOL_MSG_PLCA_NTF,
18804007961SJakub Kicinski };
18904007961SJakub Kicinski 
1908580e16cSPiergiorgio Beruto // PLCA get status message -------------------------------------------------- //
1918580e16cSPiergiorgio Beruto 
1928580e16cSPiergiorgio Beruto const struct nla_policy ethnl_plca_get_status_policy[] = {
1938580e16cSPiergiorgio Beruto 	[ETHTOOL_A_PLCA_HEADER]		=
1948580e16cSPiergiorgio Beruto 		NLA_POLICY_NESTED(ethnl_header_policy),
1958580e16cSPiergiorgio Beruto };
1968580e16cSPiergiorgio Beruto 
plca_get_status_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)1978580e16cSPiergiorgio Beruto static int plca_get_status_prepare_data(const struct ethnl_req_info *req_base,
1988580e16cSPiergiorgio Beruto 					struct ethnl_reply_data *reply_base,
199f946270dSJakub Kicinski 					const struct genl_info *info)
2008580e16cSPiergiorgio Beruto {
2018580e16cSPiergiorgio Beruto 	struct plca_reply_data *data = PLCA_REPDATA(reply_base);
2028580e16cSPiergiorgio Beruto 	struct net_device *dev = reply_base->dev;
2038580e16cSPiergiorgio Beruto 	const struct ethtool_phy_ops *ops;
2048580e16cSPiergiorgio Beruto 	int ret;
2058580e16cSPiergiorgio Beruto 
2068580e16cSPiergiorgio Beruto 	// check that the PHY device is available and connected
2078580e16cSPiergiorgio Beruto 	if (!dev->phydev) {
2088580e16cSPiergiorgio Beruto 		ret = -EOPNOTSUPP;
2098580e16cSPiergiorgio Beruto 		goto out;
2108580e16cSPiergiorgio Beruto 	}
2118580e16cSPiergiorgio Beruto 
2128580e16cSPiergiorgio Beruto 	// note: rtnl_lock is held already by ethnl_default_doit
2138580e16cSPiergiorgio Beruto 	ops = ethtool_phy_ops;
2148580e16cSPiergiorgio Beruto 	if (!ops || !ops->get_plca_status) {
2158580e16cSPiergiorgio Beruto 		ret = -EOPNOTSUPP;
2168580e16cSPiergiorgio Beruto 		goto out;
2178580e16cSPiergiorgio Beruto 	}
2188580e16cSPiergiorgio Beruto 
2198580e16cSPiergiorgio Beruto 	ret = ethnl_ops_begin(dev);
22028dbf774SPiergiorgio Beruto 	if (ret < 0)
2218580e16cSPiergiorgio Beruto 		goto out;
2228580e16cSPiergiorgio Beruto 
2238580e16cSPiergiorgio Beruto 	memset(&data->plca_st, 0xff,
2248580e16cSPiergiorgio Beruto 	       sizeof_field(struct plca_reply_data, plca_st));
2258580e16cSPiergiorgio Beruto 
2268580e16cSPiergiorgio Beruto 	ret = ops->get_plca_status(dev->phydev, &data->plca_st);
2278580e16cSPiergiorgio Beruto 	ethnl_ops_complete(dev);
2288580e16cSPiergiorgio Beruto out:
2298580e16cSPiergiorgio Beruto 	return ret;
2308580e16cSPiergiorgio Beruto }
2318580e16cSPiergiorgio Beruto 
plca_get_status_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)2328580e16cSPiergiorgio Beruto static int plca_get_status_reply_size(const struct ethnl_req_info *req_base,
2338580e16cSPiergiorgio Beruto 				      const struct ethnl_reply_data *reply_base)
2348580e16cSPiergiorgio Beruto {
2358580e16cSPiergiorgio Beruto 	return nla_total_size(sizeof(u8));	/* _STATUS */
2368580e16cSPiergiorgio Beruto }
2378580e16cSPiergiorgio Beruto 
plca_get_status_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)2388580e16cSPiergiorgio Beruto static int plca_get_status_fill_reply(struct sk_buff *skb,
2398580e16cSPiergiorgio Beruto 				      const struct ethnl_req_info *req_base,
2408580e16cSPiergiorgio Beruto 				      const struct ethnl_reply_data *reply_base)
2418580e16cSPiergiorgio Beruto {
2428580e16cSPiergiorgio Beruto 	const struct plca_reply_data *data = PLCA_REPDATA(reply_base);
2438580e16cSPiergiorgio Beruto 	const u8 status = data->plca_st.pst;
2448580e16cSPiergiorgio Beruto 
2458580e16cSPiergiorgio Beruto 	if (nla_put_u8(skb, ETHTOOL_A_PLCA_STATUS, !!status))
2468580e16cSPiergiorgio Beruto 		return -EMSGSIZE;
2478580e16cSPiergiorgio Beruto 
2488580e16cSPiergiorgio Beruto 	return 0;
2498580e16cSPiergiorgio Beruto };
2508580e16cSPiergiorgio Beruto 
2518580e16cSPiergiorgio Beruto const struct ethnl_request_ops ethnl_plca_status_request_ops = {
2528580e16cSPiergiorgio Beruto 	.request_cmd		= ETHTOOL_MSG_PLCA_GET_STATUS,
2538580e16cSPiergiorgio Beruto 	.reply_cmd		= ETHTOOL_MSG_PLCA_GET_STATUS_REPLY,
2548580e16cSPiergiorgio Beruto 	.hdr_attr		= ETHTOOL_A_PLCA_HEADER,
2558580e16cSPiergiorgio Beruto 	.req_info_size		= sizeof(struct plca_req_info),
2568580e16cSPiergiorgio Beruto 	.reply_data_size	= sizeof(struct plca_reply_data),
2578580e16cSPiergiorgio Beruto 
2588580e16cSPiergiorgio Beruto 	.prepare_data		= plca_get_status_prepare_data,
2598580e16cSPiergiorgio Beruto 	.reply_size		= plca_get_status_reply_size,
2608580e16cSPiergiorgio Beruto 	.fill_reply		= plca_get_status_fill_reply,
2618580e16cSPiergiorgio Beruto };
262