linkinfo.c 4.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  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. const 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)
  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. /* LINKINFO_SET */
  63. const struct nla_policy ethnl_linkinfo_set_policy[] = {
  64. [ETHTOOL_A_LINKINFO_HEADER] =
  65. NLA_POLICY_NESTED(ethnl_header_policy),
  66. [ETHTOOL_A_LINKINFO_PORT] = { .type = NLA_U8 },
  67. [ETHTOOL_A_LINKINFO_PHYADDR] = { .type = NLA_U8 },
  68. [ETHTOOL_A_LINKINFO_TP_MDIX_CTRL] = { .type = NLA_U8 },
  69. };
  70. static int
  71. ethnl_set_linkinfo_validate(struct ethnl_req_info *req_info,
  72. struct genl_info *info)
  73. {
  74. const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
  75. if (!ops->get_link_ksettings || !ops->set_link_ksettings)
  76. return -EOPNOTSUPP;
  77. return 1;
  78. }
  79. static int
  80. ethnl_set_linkinfo(struct ethnl_req_info *req_info, struct genl_info *info)
  81. {
  82. struct ethtool_link_ksettings ksettings = {};
  83. struct ethtool_link_settings *lsettings;
  84. struct net_device *dev = req_info->dev;
  85. struct nlattr **tb = info->attrs;
  86. bool mod = false;
  87. int ret;
  88. ret = __ethtool_get_link_ksettings(dev, &ksettings);
  89. if (ret < 0) {
  90. GENL_SET_ERR_MSG(info, "failed to retrieve link settings");
  91. return ret;
  92. }
  93. lsettings = &ksettings.base;
  94. ethnl_update_u8(&lsettings->port, tb[ETHTOOL_A_LINKINFO_PORT], &mod);
  95. ethnl_update_u8(&lsettings->phy_address, tb[ETHTOOL_A_LINKINFO_PHYADDR],
  96. &mod);
  97. ethnl_update_u8(&lsettings->eth_tp_mdix_ctrl,
  98. tb[ETHTOOL_A_LINKINFO_TP_MDIX_CTRL], &mod);
  99. if (!mod)
  100. return 0;
  101. ret = dev->ethtool_ops->set_link_ksettings(dev, &ksettings);
  102. if (ret < 0) {
  103. GENL_SET_ERR_MSG(info, "link settings update failed");
  104. return ret;
  105. }
  106. return 1;
  107. }
  108. const struct ethnl_request_ops ethnl_linkinfo_request_ops = {
  109. .request_cmd = ETHTOOL_MSG_LINKINFO_GET,
  110. .reply_cmd = ETHTOOL_MSG_LINKINFO_GET_REPLY,
  111. .hdr_attr = ETHTOOL_A_LINKINFO_HEADER,
  112. .req_info_size = sizeof(struct linkinfo_req_info),
  113. .reply_data_size = sizeof(struct linkinfo_reply_data),
  114. .prepare_data = linkinfo_prepare_data,
  115. .reply_size = linkinfo_reply_size,
  116. .fill_reply = linkinfo_fill_reply,
  117. .set_validate = ethnl_set_linkinfo_validate,
  118. .set = ethnl_set_linkinfo,
  119. .set_ntf_cmd = ETHTOOL_MSG_LINKINFO_NTF,
  120. };