Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 
0003 #include "netlink.h"
0004 #include "common.h"
0005 
0006 struct linkinfo_req_info {
0007     struct ethnl_req_info       base;
0008 };
0009 
0010 struct linkinfo_reply_data {
0011     struct ethnl_reply_data     base;
0012     struct ethtool_link_ksettings   ksettings;
0013     struct ethtool_link_settings    *lsettings;
0014 };
0015 
0016 #define LINKINFO_REPDATA(__reply_base) \
0017     container_of(__reply_base, struct linkinfo_reply_data, base)
0018 
0019 const struct nla_policy ethnl_linkinfo_get_policy[] = {
0020     [ETHTOOL_A_LINKINFO_HEADER]     =
0021         NLA_POLICY_NESTED(ethnl_header_policy),
0022 };
0023 
0024 static int linkinfo_prepare_data(const struct ethnl_req_info *req_base,
0025                  struct ethnl_reply_data *reply_base,
0026                  struct genl_info *info)
0027 {
0028     struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
0029     struct net_device *dev = reply_base->dev;
0030     int ret;
0031 
0032     data->lsettings = &data->ksettings.base;
0033 
0034     ret = ethnl_ops_begin(dev);
0035     if (ret < 0)
0036         return ret;
0037     ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
0038     if (ret < 0 && info)
0039         GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
0040     ethnl_ops_complete(dev);
0041 
0042     return ret;
0043 }
0044 
0045 static int linkinfo_reply_size(const struct ethnl_req_info *req_base,
0046                    const struct ethnl_reply_data *reply_base)
0047 {
0048     return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */
0049         + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */
0050         + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */
0051         + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */
0052         + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */
0053         + 0;
0054 }
0055 
0056 static int linkinfo_fill_reply(struct sk_buff *skb,
0057                    const struct ethnl_req_info *req_base,
0058                    const struct ethnl_reply_data *reply_base)
0059 {
0060     const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
0061 
0062     if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) ||
0063         nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR,
0064                data->lsettings->phy_address) ||
0065         nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX,
0066                data->lsettings->eth_tp_mdix) ||
0067         nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
0068                data->lsettings->eth_tp_mdix_ctrl) ||
0069         nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER,
0070                data->lsettings->transceiver))
0071         return -EMSGSIZE;
0072 
0073     return 0;
0074 }
0075 
0076 const struct ethnl_request_ops ethnl_linkinfo_request_ops = {
0077     .request_cmd        = ETHTOOL_MSG_LINKINFO_GET,
0078     .reply_cmd      = ETHTOOL_MSG_LINKINFO_GET_REPLY,
0079     .hdr_attr       = ETHTOOL_A_LINKINFO_HEADER,
0080     .req_info_size      = sizeof(struct linkinfo_req_info),
0081     .reply_data_size    = sizeof(struct linkinfo_reply_data),
0082 
0083     .prepare_data       = linkinfo_prepare_data,
0084     .reply_size     = linkinfo_reply_size,
0085     .fill_reply     = linkinfo_fill_reply,
0086 };
0087 
0088 /* LINKINFO_SET */
0089 
0090 const struct nla_policy ethnl_linkinfo_set_policy[] = {
0091     [ETHTOOL_A_LINKINFO_HEADER]     =
0092         NLA_POLICY_NESTED(ethnl_header_policy),
0093     [ETHTOOL_A_LINKINFO_PORT]       = { .type = NLA_U8 },
0094     [ETHTOOL_A_LINKINFO_PHYADDR]        = { .type = NLA_U8 },
0095     [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL]   = { .type = NLA_U8 },
0096 };
0097 
0098 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info)
0099 {
0100     struct ethtool_link_ksettings ksettings = {};
0101     struct ethtool_link_settings *lsettings;
0102     struct ethnl_req_info req_info = {};
0103     struct nlattr **tb = info->attrs;
0104     struct net_device *dev;
0105     bool mod = false;
0106     int ret;
0107 
0108     ret = ethnl_parse_header_dev_get(&req_info,
0109                      tb[ETHTOOL_A_LINKINFO_HEADER],
0110                      genl_info_net(info), info->extack,
0111                      true);
0112     if (ret < 0)
0113         return ret;
0114     dev = req_info.dev;
0115     ret = -EOPNOTSUPP;
0116     if (!dev->ethtool_ops->get_link_ksettings ||
0117         !dev->ethtool_ops->set_link_ksettings)
0118         goto out_dev;
0119 
0120     rtnl_lock();
0121     ret = ethnl_ops_begin(dev);
0122     if (ret < 0)
0123         goto out_rtnl;
0124 
0125     ret = __ethtool_get_link_ksettings(dev, &ksettings);
0126     if (ret < 0) {
0127         GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
0128         goto out_ops;
0129     }
0130     lsettings = &ksettings.base;
0131 
0132     ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
0133     ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
0134             &mod);
0135     ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
0136             tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod);
0137     ret = 0;
0138     if (!mod)
0139         goto out_ops;
0140 
0141     ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
0142     if (ret < 0)
0143         GENL_SET_ERR_MSG(info, "link settings update failed");
0144     else
0145         ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
0146 
0147 out_ops:
0148     ethnl_ops_complete(dev);
0149 out_rtnl:
0150     rtnl_unlock();
0151 out_dev:
0152     ethnl_parse_header_dev_put(&req_info);
0153     return ret;
0154 }