linkinfo.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. #include "netlink.h"
  3. #include "common.h"
  4. struct linkinfo_req_info {
  5. struct ethnl_req_info base;
  6. };
  7. struct linkinfo_reply_data {
  8. struct ethnl_reply_data base;
  9. struct ethtool_link_ksettings ksettings;
  10. struct ethtool_link_settings *lsettings;
  11. };
  12. #define LINKINFO_REPDATA(__reply_base) \
  13. container_of(__reply_base, struct linkinfo_reply_data, base)
  14. const struct nla_policy ethnl_linkinfo_get_policy[] = {
  15. [ETHTOOL_A_LINKINFO_HEADER] =
  16. NLA_POLICY_NESTED(ethnl_header_policy),
  17. };
  18. static int linkinfo_prepare_data(const struct ethnl_req_info *req_base,
  19. struct ethnl_reply_data *reply_base,
  20. struct genl_info *info)
  21. {
  22. struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
  23. struct net_device *dev = reply_base->dev;
  24. int ret;
  25. data->lsettings = &data->ksettings.base;
  26. ret = ethnl_ops_begin(dev);
  27. if (ret < 0)
  28. return ret;
  29. ret = __ethtool_get_link_ksettings(dev, &data->ksettings);
  30. if (ret < 0 && info)
  31. GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
  32. ethnl_ops_complete(dev);
  33. return ret;
  34. }
  35. static int linkinfo_reply_size(const struct ethnl_req_info *req_base,
  36. const struct ethnl_reply_data *reply_base)
  37. {
  38. return nla_total_size(sizeof(u8)) /* LINKINFO_PORT */
  39. + nla_total_size(sizeof(u8)) /* LINKINFO_PHYADDR */
  40. + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX */
  41. + nla_total_size(sizeof(u8)) /* LINKINFO_TP_MDIX_CTRL */
  42. + nla_total_size(sizeof(u8)) /* LINKINFO_TRANSCEIVER */
  43. + 0;
  44. }
  45. static int linkinfo_fill_reply(struct sk_buff *skb,
  46. const struct ethnl_req_info *req_base,
  47. const struct ethnl_reply_data *reply_base)
  48. {
  49. const struct linkinfo_reply_data *data = LINKINFO_REPDATA(reply_base);
  50. if (nla_put_u8(skb, ETHTOOL_A_LINKINFO_PORT, data->lsettings->port) ||
  51. nla_put_u8(skb, ETHTOOL_A_LINKINFO_PHYADDR,
  52. data->lsettings->phy_address) ||
  53. nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX,
  54. data->lsettings->eth_tp_mdix) ||
  55. nla_put_u8(skb, ETHTOOL_A_LINKINFO_TP_MDIX_CTRL,
  56. data->lsettings->eth_tp_mdix_ctrl) ||
  57. nla_put_u8(skb, ETHTOOL_A_LINKINFO_TRANSCEIVER,
  58. data->lsettings->transceiver))
  59. return -EMSGSIZE;
  60. return 0;
  61. }
  62. const struct ethnl_request_ops ethnl_linkinfo_request_ops = {
  63. .request_cmd = ETHTOOL_MSG_LINKINFO_GET,
  64. .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY,
  65. .hdr_attr = ETHTOOL_A_LINKINFO_HEADER,
  66. .req_info_size = sizeof(struct linkinfo_req_info),
  67. .reply_data_size = sizeof(struct linkinfo_reply_data),
  68. .prepare_data = linkinfo_prepare_data,
  69. .reply_size = linkinfo_reply_size,
  70. .fill_reply = linkinfo_fill_reply,
  71. };
  72. /* LINKINFO_SET */
  73. const struct nla_policy ethnl_linkinfo_set_policy[] = {
  74. [ETHTOOL_A_LINKINFO_HEADER] =
  75. NLA_POLICY_NESTED(ethnl_header_policy),
  76. [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 },
  77. [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 },
  78. [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 },
  79. };
  80. int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info)
  81. {
  82. struct ethtool_link_ksettings ksettings = {};
  83. struct ethtool_link_settings *lsettings;
  84. struct ethnl_req_info req_info = {};
  85. struct nlattr **tb = info->attrs;
  86. struct net_device *dev;
  87. bool mod = false;
  88. int ret;
  89. ret = ethnl_parse_header_dev_get(&req_info,
  90. tb[ETHTOOL_A_LINKINFO_HEADER],
  91. genl_info_net(info), info->extack,
  92. true);
  93. if (ret < 0)
  94. return ret;
  95. dev = req_info.dev;
  96. ret = -EOPNOTSUPP;
  97. if (!dev->ethtool_ops->get_link_ksettings ||
  98. !dev->ethtool_ops->set_link_ksettings)
  99. goto out_dev;
  100. rtnl_lock();
  101. ret = ethnl_ops_begin(dev);
  102. if (ret < 0)
  103. goto out_rtnl;
  104. ret = __ethtool_get_link_ksettings(dev, &ksettings);
  105. if (ret < 0) {
  106. GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
  107. goto out_ops;
  108. }
  109. lsettings = &ksettings.base;
  110. ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
  111. ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
  112. &mod);
  113. ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
  114. tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod);
  115. ret = 0;
  116. if (!mod)
  117. goto out_ops;
  118. ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
  119. if (ret < 0)
  120. GENL_SET_ERR_MSG(info, "link settings update failed");
  121. else
  122. ethtool_notify(dev, ETHTOOL_MSG_LINKINFO_NTF, NULL);
  123. out_ops:
  124. ethnl_ops_complete(dev);
  125. out_rtnl:
  126. rtnl_unlock();
  127. out_dev:
  128. ethnl_parse_header_dev_put(&req_info);
  129. return ret;
  130. }