Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include "netlink.h"
0004 #include "common.h"
0005 #include <linux/phy.h>
0006 
0007 struct linkstate_req_info {
0008     struct ethnl_req_info       base;
0009 };
0010 
0011 struct linkstate_reply_data {
0012     struct ethnl_reply_data         base;
0013     int                 link;
0014     int                 sqi;
0015     int                 sqi_max;
0016     bool                    link_ext_state_provided;
0017     struct ethtool_link_ext_state_info  ethtool_link_ext_state_info;
0018 };
0019 
0020 #define LINKSTATE_REPDATA(__reply_base) \
0021     container_of(__reply_base, struct linkstate_reply_data, base)
0022 
0023 const struct nla_policy ethnl_linkstate_get_policy[] = {
0024     [ETHTOOL_A_LINKSTATE_HEADER]        =
0025         NLA_POLICY_NESTED(ethnl_header_policy),
0026 };
0027 
0028 static int linkstate_get_sqi(struct net_device *dev)
0029 {
0030     struct phy_device *phydev = dev->phydev;
0031     int ret;
0032 
0033     if (!phydev)
0034         return -EOPNOTSUPP;
0035 
0036     mutex_lock(&phydev->lock);
0037     if (!phydev->drv || !phydev->drv->get_sqi)
0038         ret = -EOPNOTSUPP;
0039     else
0040         ret = phydev->drv->get_sqi(phydev);
0041     mutex_unlock(&phydev->lock);
0042 
0043     return ret;
0044 }
0045 
0046 static int linkstate_get_sqi_max(struct net_device *dev)
0047 {
0048     struct phy_device *phydev = dev->phydev;
0049     int ret;
0050 
0051     if (!phydev)
0052         return -EOPNOTSUPP;
0053 
0054     mutex_lock(&phydev->lock);
0055     if (!phydev->drv || !phydev->drv->get_sqi_max)
0056         ret = -EOPNOTSUPP;
0057     else
0058         ret = phydev->drv->get_sqi_max(phydev);
0059     mutex_unlock(&phydev->lock);
0060 
0061     return ret;
0062 };
0063 
0064 static int linkstate_get_link_ext_state(struct net_device *dev,
0065                     struct linkstate_reply_data *data)
0066 {
0067     int err;
0068 
0069     if (!dev->ethtool_ops->get_link_ext_state)
0070         return -EOPNOTSUPP;
0071 
0072     err = dev->ethtool_ops->get_link_ext_state(dev, &data->ethtool_link_ext_state_info);
0073     if (err)
0074         return err;
0075 
0076     data->link_ext_state_provided = true;
0077 
0078     return 0;
0079 }
0080 
0081 static int linkstate_prepare_data(const struct ethnl_req_info *req_base,
0082                   struct ethnl_reply_data *reply_base,
0083                   struct genl_info *info)
0084 {
0085     struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
0086     struct net_device *dev = reply_base->dev;
0087     int ret;
0088 
0089     ret = ethnl_ops_begin(dev);
0090     if (ret < 0)
0091         return ret;
0092     data->link = __ethtool_get_link(dev);
0093 
0094     ret = linkstate_get_sqi(dev);
0095     if (ret < 0 && ret != -EOPNOTSUPP)
0096         goto out;
0097     data->sqi = ret;
0098 
0099     ret = linkstate_get_sqi_max(dev);
0100     if (ret < 0 && ret != -EOPNOTSUPP)
0101         goto out;
0102     data->sqi_max = ret;
0103 
0104     if (dev->flags & IFF_UP) {
0105         ret = linkstate_get_link_ext_state(dev, data);
0106         if (ret < 0 && ret != -EOPNOTSUPP && ret != -ENODATA)
0107             goto out;
0108     }
0109 
0110     ret = 0;
0111 out:
0112     ethnl_ops_complete(dev);
0113     return ret;
0114 }
0115 
0116 static int linkstate_reply_size(const struct ethnl_req_info *req_base,
0117                 const struct ethnl_reply_data *reply_base)
0118 {
0119     struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
0120     int len;
0121 
0122     len = nla_total_size(sizeof(u8)) /* LINKSTATE_LINK */
0123         + 0;
0124 
0125     if (data->sqi != -EOPNOTSUPP)
0126         len += nla_total_size(sizeof(u32));
0127 
0128     if (data->sqi_max != -EOPNOTSUPP)
0129         len += nla_total_size(sizeof(u32));
0130 
0131     if (data->link_ext_state_provided)
0132         len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_STATE */
0133 
0134     if (data->ethtool_link_ext_state_info.__link_ext_substate)
0135         len += nla_total_size(sizeof(u8)); /* LINKSTATE_EXT_SUBSTATE */
0136 
0137     return len;
0138 }
0139 
0140 static int linkstate_fill_reply(struct sk_buff *skb,
0141                 const struct ethnl_req_info *req_base,
0142                 const struct ethnl_reply_data *reply_base)
0143 {
0144     struct linkstate_reply_data *data = LINKSTATE_REPDATA(reply_base);
0145 
0146     if (data->link >= 0 &&
0147         nla_put_u8(skb, ETHTOOL_A_LINKSTATE_LINK, !!data->link))
0148         return -EMSGSIZE;
0149 
0150     if (data->sqi != -EOPNOTSUPP &&
0151         nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI, data->sqi))
0152         return -EMSGSIZE;
0153 
0154     if (data->sqi_max != -EOPNOTSUPP &&
0155         nla_put_u32(skb, ETHTOOL_A_LINKSTATE_SQI_MAX, data->sqi_max))
0156         return -EMSGSIZE;
0157 
0158     if (data->link_ext_state_provided) {
0159         if (nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_STATE,
0160                    data->ethtool_link_ext_state_info.link_ext_state))
0161             return -EMSGSIZE;
0162 
0163         if (data->ethtool_link_ext_state_info.__link_ext_substate &&
0164             nla_put_u8(skb, ETHTOOL_A_LINKSTATE_EXT_SUBSTATE,
0165                    data->ethtool_link_ext_state_info.__link_ext_substate))
0166             return -EMSGSIZE;
0167     }
0168 
0169     return 0;
0170 }
0171 
0172 const struct ethnl_request_ops ethnl_linkstate_request_ops = {
0173     .request_cmd        = ETHTOOL_MSG_LINKSTATE_GET,
0174     .reply_cmd      = ETHTOOL_MSG_LINKSTATE_GET_REPLY,
0175     .hdr_attr       = ETHTOOL_A_LINKSTATE_HEADER,
0176     .req_info_size      = sizeof(struct linkstate_req_info),
0177     .reply_data_size    = sizeof(struct linkstate_reply_data),
0178 
0179     .prepare_data       = linkstate_prepare_data,
0180     .reply_size     = linkstate_reply_size,
0181     .fill_reply     = linkstate_fill_reply,
0182 };