13d2b847fSMichal Kubecek // SPDX-License-Identifier: GPL-2.0-only 23d2b847fSMichal Kubecek 33d2b847fSMichal Kubecek #include "netlink.h" 43d2b847fSMichal Kubecek #include "common.h" 5*80660219SOleksij 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; 14*80660219SOleksij Rempel int sqi; 15*80660219SOleksij 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 }, 26*80660219SOleksij Rempel [ETHTOOL_A_LINKSTATE_SQI] = { .type = NLA_REJECT }, 27*80660219SOleksij Rempel [ETHTOOL_A_LINKSTATE_SQI_MAX] = { .type = NLA_REJECT }, 283d2b847fSMichal Kubecek }; 293d2b847fSMichal Kubecek 30*80660219SOleksij Rempel static int linkstate_get_sqi(struct net_device *dev) 31*80660219SOleksij Rempel { 32*80660219SOleksij Rempel struct phy_device *phydev = dev->phydev; 33*80660219SOleksij Rempel int ret; 34*80660219SOleksij Rempel 35*80660219SOleksij Rempel if (!phydev) 36*80660219SOleksij Rempel return -EOPNOTSUPP; 37*80660219SOleksij Rempel 38*80660219SOleksij Rempel mutex_lock(&phydev->lock); 39*80660219SOleksij Rempel if (!phydev->drv || !phydev->drv->get_sqi) 40*80660219SOleksij Rempel ret = -EOPNOTSUPP; 41*80660219SOleksij Rempel else 42*80660219SOleksij Rempel ret = phydev->drv->get_sqi(phydev); 43*80660219SOleksij Rempel mutex_unlock(&phydev->lock); 44*80660219SOleksij Rempel 45*80660219SOleksij Rempel return ret; 46*80660219SOleksij Rempel } 47*80660219SOleksij Rempel 48*80660219SOleksij Rempel static int linkstate_get_sqi_max(struct net_device *dev) 49*80660219SOleksij Rempel { 50*80660219SOleksij Rempel struct phy_device *phydev = dev->phydev; 51*80660219SOleksij Rempel int ret; 52*80660219SOleksij Rempel 53*80660219SOleksij Rempel if (!phydev) 54*80660219SOleksij Rempel return -EOPNOTSUPP; 55*80660219SOleksij Rempel 56*80660219SOleksij Rempel mutex_lock(&phydev->lock); 57*80660219SOleksij Rempel if (!phydev->drv || !phydev->drv->get_sqi_max) 58*80660219SOleksij Rempel ret = -EOPNOTSUPP; 59*80660219SOleksij Rempel else 60*80660219SOleksij Rempel ret = phydev->drv->get_sqi_max(phydev); 61*80660219SOleksij Rempel mutex_unlock(&phydev->lock); 62*80660219SOleksij Rempel 63*80660219SOleksij Rempel return ret; 64*80660219SOleksij Rempel } 65*80660219SOleksij 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); 78*80660219SOleksij Rempel 79*80660219SOleksij Rempel ret = linkstate_get_sqi(dev); 80*80660219SOleksij Rempel if (ret < 0 && ret != -EOPNOTSUPP) 81*80660219SOleksij Rempel return ret; 82*80660219SOleksij Rempel 83*80660219SOleksij Rempel data->sqi = ret; 84*80660219SOleksij Rempel 85*80660219SOleksij Rempel ret = linkstate_get_sqi_max(dev); 86*80660219SOleksij Rempel if (ret < 0 && ret != -EOPNOTSUPP) 87*80660219SOleksij Rempel return ret; 88*80660219SOleksij Rempel 89*80660219SOleksij Rempel data->sqi_max = ret; 90*80660219SOleksij Rempel 913d2b847fSMichal Kubecek ethnl_ops_complete(dev); 923d2b847fSMichal Kubecek 933d2b847fSMichal Kubecek return 0; 943d2b847fSMichal Kubecek } 953d2b847fSMichal Kubecek 963d2b847fSMichal Kubecek static int linkstate_reply_size(const struct ethnl_req_info *req_base, 973d2b847fSMichal Kubecek const struct ethnl_reply_data *reply_base) 983d2b847fSMichal Kubecek { 99*80660219SOleksij Rempel struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 100*80660219SOleksij Rempel int len; 101*80660219SOleksij Rempel 102*80660219SOleksij Rempel len = nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */ 1033d2b847fSMichal Kubecek + 0; 104*80660219SOleksij Rempel 105*80660219SOleksij Rempel if (data->sqi != -EOPNOTSUPP) 106*80660219SOleksij Rempel len += nla_total_size(sizeof(u32)); 107*80660219SOleksij Rempel 108*80660219SOleksij Rempel if (data->sqi_max != -EOPNOTSUPP) 109*80660219SOleksij Rempel len += nla_total_size(sizeof(u32)); 110*80660219SOleksij Rempel 111*80660219SOleksij Rempel return len; 1123d2b847fSMichal Kubecek } 1133d2b847fSMichal Kubecek 1143d2b847fSMichal Kubecek static int linkstate_fill_reply(struct sk_buff *skb, 1153d2b847fSMichal Kubecek const struct ethnl_req_info *req_base, 1163d2b847fSMichal Kubecek const struct ethnl_reply_data *reply_base) 1173d2b847fSMichal Kubecek { 1183d2b847fSMichal Kubecek struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base); 1193d2b847fSMichal Kubecek 1203d2b847fSMichal Kubecek if (data->link >= 0 && 1213d2b847fSMichal Kubecek nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link)) 1223d2b847fSMichal Kubecek return -EMSGSIZE; 1233d2b847fSMichal Kubecek 124*80660219SOleksij Rempel if (data->sqi != -EOPNOTSUPP && 125*80660219SOleksij Rempel nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI, data->sqi)) 126*80660219SOleksij Rempel return -EMSGSIZE; 127*80660219SOleksij Rempel 128*80660219SOleksij Rempel if (data->sqi_max != -EOPNOTSUPP && 129*80660219SOleksij Rempel nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max)) 130*80660219SOleksij Rempel return -EMSGSIZE; 131*80660219SOleksij Rempel 1323d2b847fSMichal Kubecek return 0; 1333d2b847fSMichal Kubecek } 1343d2b847fSMichal Kubecek 1353d2b847fSMichal Kubecek const struct ethnl_request_ops ethnl_linkstate_request_ops = { 1363d2b847fSMichal Kubecek .request_cmd = ETHTOOL_MSG_LINKSTATE_GET, 1373d2b847fSMichal Kubecek .reply_cmd = ETHTOOL_MSG_LINKSTATE_GET_REPLY, 1383d2b847fSMichal Kubecek .hdr_attr = ETHTOOL_A_LINKSTATE_HEADER, 1393d2b847fSMichal Kubecek .max_attr = ETHTOOL_A_LINKSTATE_MAX, 1403d2b847fSMichal Kubecek .req_info_size = sizeof(struct linkstate_req_info), 1413d2b847fSMichal Kubecek .reply_data_size = sizeof(struct linkstate_reply_data), 1423d2b847fSMichal Kubecek .request_policy = linkstate_get_policy, 1433d2b847fSMichal Kubecek 1443d2b847fSMichal Kubecek .prepare_data = linkstate_prepare_data, 1453d2b847fSMichal Kubecek .reply_size = linkstate_reply_size, 1463d2b847fSMichal Kubecek .fill_reply = linkstate_fill_reply, 1473d2b847fSMichal Kubecek }; 148