13d2b847fSMichal Kubecek // SPDX-License-Identifier: GPL-2.0-only 23d2b847fSMichal Kubecek 33d2b847fSMichal Kubecek #include "netlink.h" 43d2b847fSMichal Kubecek #include "common.h" 580660219SOleksij Rempel #include <linux/phy.h> 63d2b847fSMichal Kubecek 73d2b847fSMichal Kubecek struct linkstate_req_info { 83d2b847fSMichal Kubecek struct ethnl_req_info base; 93d2b847fSMichal Kubecek }; 103d2b847fSMichal Kubecek 113d2b847fSMichal Kubecek struct linkstate_reply_data { 123d2b847fSMichal Kubecek struct ethnl_reply_data base; 133d2b847fSMichal Kubecek int link; 1480660219SOleksij Rempel int sqi; 1580660219SOleksij Rempel int sqi_max; 163d2b847fSMichal Kubecek }; 173d2b847fSMichal Kubecek 183d2b847fSMichal Kubecek #define LINKSTATE_REPDATA(__reply_base) \ 193d2b847fSMichal Kubecek container_of(__reply_base, struct linkstate_reply_data, base) 203d2b847fSMichal Kubecek 213d2b847fSMichal Kubecek static const struct nla_policy 223d2b847fSMichal Kubecek linkstate_get_policy[ETHTOOL_A_LINKSTATE_MAX + 1] = { 233d2b847fSMichal Kubecek [ETHTOOL_A_LINKSTATE_UNSPEC] = { .type = NLA_REJECT }, 243d2b847fSMichal Kubecek [ETHTOOL_A_LINKSTATE_HEADER] = { .type = NLA_NESTED }, 253d2b847fSMichal Kubecek [ETHTOOL_A_LINKSTATE_LINK] = { .type = NLA_REJECT }, 2680660219SOleksij Rempel [ETHTOOL_A_LINKSTATE_SQI] = { .type = NLA_REJECT }, 2780660219SOleksij Rempel [ETHTOOL_A_LINKSTATE_SQI_MAX] = { .type = NLA_REJECT }, 283d2b847fSMichal Kubecek }; 293d2b847fSMichal Kubecek 3080660219SOleksij Rempel static int linkstate_get_sqi(struct net_device *dev) 3180660219SOleksij Rempel { 3280660219SOleksij Rempel struct phy_device *phydev = dev->phydev; 3380660219SOleksij Rempel int ret; 3480660219SOleksij Rempel 3580660219SOleksij Rempel if (!phydev) 3680660219SOleksij Rempel return -EOPNOTSUPP; 3780660219SOleksij Rempel 3880660219SOleksij Rempel mutex_lock(&phydev->lock); 3980660219SOleksij Rempel if (!phydev->drv || !phydev->drv->get_sqi) 4080660219SOleksij Rempel ret = -EOPNOTSUPP; 4180660219SOleksij Rempel else 4280660219SOleksij Rempel ret = phydev->drv->get_sqi(phydev); 4380660219SOleksij Rempel mutex_unlock(&phydev->lock); 4480660219SOleksij Rempel 4580660219SOleksij Rempel return ret; 4680660219SOleksij Rempel } 4780660219SOleksij Rempel 4880660219SOleksij Rempel static int linkstate_get_sqi_max(struct net_device *dev) 4980660219SOleksij Rempel { 5080660219SOleksij Rempel struct phy_device *phydev = dev->phydev; 5180660219SOleksij Rempel int ret; 5280660219SOleksij Rempel 5380660219SOleksij Rempel if (!phydev) 5480660219SOleksij Rempel return -EOPNOTSUPP; 5580660219SOleksij Rempel 5680660219SOleksij Rempel mutex_lock(&phydev->lock); 5780660219SOleksij Rempel if (!phydev->drv || !phydev->drv->get_sqi_max) 5880660219SOleksij Rempel ret = -EOPNOTSUPP; 5980660219SOleksij Rempel else 6080660219SOleksij Rempel ret = phydev->drv->get_sqi_max(phydev); 6180660219SOleksij Rempel mutex_unlock(&phydev->lock); 6280660219SOleksij Rempel 6380660219SOleksij Rempel return ret; 6480660219SOleksij Rempel } 6580660219SOleksij Rempel 663d2b847fSMichal Kubecek static int linkstate_prepare_data(const struct ethnl_req_info *req_base, 673d2b847fSMichal Kubecek struct ethnl_reply_data *reply_base, 683d2b847fSMichal Kubecek struct genl_info *info) 693d2b847fSMichal Kubecek { 703d2b847fSMichal Kubecek struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 713d2b847fSMichal Kubecek struct net_device *dev = reply_base->dev; 723d2b847fSMichal Kubecek int ret; 733d2b847fSMichal Kubecek 743d2b847fSMichal Kubecek ret = ethnl_ops_begin(dev); 753d2b847fSMichal Kubecek if (ret < 0) 763d2b847fSMichal Kubecek return ret; 773d2b847fSMichal Kubecek data->link = __ethtool_get_link(dev); 7880660219SOleksij Rempel 7980660219SOleksij Rempel ret = linkstate_get_sqi(dev); 8080660219SOleksij Rempel if (ret < 0 && ret != -EOPNOTSUPP) 81*1ae71d99SMichal Kubecek goto out; 8280660219SOleksij Rempel data->sqi = ret; 8380660219SOleksij Rempel 8480660219SOleksij Rempel ret = linkstate_get_sqi_max(dev); 8580660219SOleksij Rempel if (ret < 0 && ret != -EOPNOTSUPP) 86*1ae71d99SMichal Kubecek goto out; 8780660219SOleksij Rempel data->sqi_max = ret; 8880660219SOleksij Rempel 89*1ae71d99SMichal Kubecek ret = 0; 90*1ae71d99SMichal Kubecek out: 913d2b847fSMichal Kubecek ethnl_ops_complete(dev); 92*1ae71d99SMichal Kubecek return ret; 933d2b847fSMichal Kubecek } 943d2b847fSMichal Kubecek 953d2b847fSMichal Kubecek static int linkstate_reply_size(const struct ethnl_req_info *req_base, 963d2b847fSMichal Kubecek const struct ethnl_reply_data *reply_base) 973d2b847fSMichal Kubecek { 9880660219SOleksij Rempel struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 9980660219SOleksij Rempel int len; 10080660219SOleksij Rempel 10180660219SOleksij Rempel len = nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */ 1023d2b847fSMichal Kubecek + 0; 10380660219SOleksij Rempel 10480660219SOleksij Rempel if (data->sqi != -EOPNOTSUPP) 10580660219SOleksij Rempel len += nla_total_size(sizeof(u32)); 10680660219SOleksij Rempel 10780660219SOleksij Rempel if (data->sqi_max != -EOPNOTSUPP) 10880660219SOleksij Rempel len += nla_total_size(sizeof(u32)); 10980660219SOleksij Rempel 11080660219SOleksij Rempel return len; 1113d2b847fSMichal Kubecek } 1123d2b847fSMichal Kubecek 1133d2b847fSMichal Kubecek static int linkstate_fill_reply(struct sk_buff *skb, 1143d2b847fSMichal Kubecek const struct ethnl_req_info *req_base, 1153d2b847fSMichal Kubecek const struct ethnl_reply_data *reply_base) 1163d2b847fSMichal Kubecek { 1173d2b847fSMichal Kubecek struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 1183d2b847fSMichal Kubecek 1193d2b847fSMichal Kubecek if (data->link >= 0 && 1203d2b847fSMichal Kubecek nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link)) 1213d2b847fSMichal Kubecek return -EMSGSIZE; 1223d2b847fSMichal Kubecek 12380660219SOleksij Rempel if (data->sqi != -EOPNOTSUPP && 12480660219SOleksij Rempel nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI, data->sqi)) 12580660219SOleksij Rempel return -EMSGSIZE; 12680660219SOleksij Rempel 12780660219SOleksij Rempel if (data->sqi_max != -EOPNOTSUPP && 12880660219SOleksij Rempel nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max)) 12980660219SOleksij Rempel return -EMSGSIZE; 13080660219SOleksij Rempel 1313d2b847fSMichal Kubecek return 0; 1323d2b847fSMichal Kubecek } 1333d2b847fSMichal Kubecek 1343d2b847fSMichal Kubecek const struct ethnl_request_ops ethnl_linkstate_request_ops = { 1353d2b847fSMichal Kubecek .request_cmd = ETHTOOL_MSG_LINKSTATE_GET, 1363d2b847fSMichal Kubecek .reply_cmd = ETHTOOL_MSG_LINKSTATE_GET_REPLY, 1373d2b847fSMichal Kubecek .hdr_attr = ETHTOOL_A_LINKSTATE_HEADER, 1383d2b847fSMichal Kubecek .max_attr = ETHTOOL_A_LINKSTATE_MAX, 1393d2b847fSMichal Kubecek .req_info_size = sizeof(struct linkstate_req_info), 1403d2b847fSMichal Kubecek .reply_data_size = sizeof(struct linkstate_reply_data), 1413d2b847fSMichal Kubecek .request_policy = linkstate_get_policy, 1423d2b847fSMichal Kubecek 1433d2b847fSMichal Kubecek .prepare_data = linkstate_prepare_data, 1443d2b847fSMichal Kubecek .reply_size = linkstate_reply_size, 1453d2b847fSMichal Kubecek .fill_reply = linkstate_fill_reply, 1463d2b847fSMichal Kubecek }; 147