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