pse-pd.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // ethtool interface for for Ethernet PSE (Power Sourcing Equipment)
  4. // and PD (Powered Device)
  5. //
  6. // Copyright (c) 2022 Pengutronix, Oleksij Rempel <[email protected]>
  7. //
  8. #include "common.h"
  9. #include "linux/pse-pd/pse.h"
  10. #include "netlink.h"
  11. #include <linux/ethtool_netlink.h>
  12. #include <linux/ethtool.h>
  13. #include <linux/phy.h>
  14. struct pse_req_info {
  15. struct ethnl_req_info base;
  16. };
  17. struct pse_reply_data {
  18. struct ethnl_reply_data base;
  19. struct pse_control_status status;
  20. };
  21. #define PSE_REPDATA(__reply_base) \
  22. container_of(__reply_base, struct pse_reply_data, base)
  23. /* PSE_GET */
  24. const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1] = {
  25. [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
  26. };
  27. static int pse_get_pse_attributes(struct net_device *dev,
  28. struct netlink_ext_ack *extack,
  29. struct pse_reply_data *data)
  30. {
  31. struct phy_device *phydev = dev->phydev;
  32. if (!phydev) {
  33. NL_SET_ERR_MSG(extack, "No PHY is attached");
  34. return -EOPNOTSUPP;
  35. }
  36. if (!phydev->psec) {
  37. NL_SET_ERR_MSG(extack, "No PSE is attached");
  38. return -EOPNOTSUPP;
  39. }
  40. memset(&data->status, 0, sizeof(data->status));
  41. return pse_ethtool_get_status(phydev->psec, extack, &data->status);
  42. }
  43. static int pse_prepare_data(const struct ethnl_req_info *req_base,
  44. struct ethnl_reply_data *reply_base,
  45. struct genl_info *info)
  46. {
  47. struct pse_reply_data *data = PSE_REPDATA(reply_base);
  48. struct net_device *dev = reply_base->dev;
  49. int ret;
  50. ret = ethnl_ops_begin(dev);
  51. if (ret < 0)
  52. return ret;
  53. ret = pse_get_pse_attributes(dev, info ? info->extack : NULL, data);
  54. ethnl_ops_complete(dev);
  55. return ret;
  56. }
  57. static int pse_reply_size(const struct ethnl_req_info *req_base,
  58. const struct ethnl_reply_data *reply_base)
  59. {
  60. const struct pse_reply_data *data = PSE_REPDATA(reply_base);
  61. const struct pse_control_status *st = &data->status;
  62. int len = 0;
  63. if (st->podl_admin_state > 0)
  64. len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */
  65. if (st->podl_pw_status > 0)
  66. len += nla_total_size(sizeof(u32)); /* _PODL_PSE_PW_D_STATUS */
  67. return len;
  68. }
  69. static int pse_fill_reply(struct sk_buff *skb,
  70. const struct ethnl_req_info *req_base,
  71. const struct ethnl_reply_data *reply_base)
  72. {
  73. const struct pse_reply_data *data = PSE_REPDATA(reply_base);
  74. const struct pse_control_status *st = &data->status;
  75. if (st->podl_admin_state > 0 &&
  76. nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE,
  77. st->podl_admin_state))
  78. return -EMSGSIZE;
  79. if (st->podl_pw_status > 0 &&
  80. nla_put_u32(skb, ETHTOOL_A_PODL_PSE_PW_D_STATUS,
  81. st->podl_pw_status))
  82. return -EMSGSIZE;
  83. return 0;
  84. }
  85. const struct ethnl_request_ops ethnl_pse_request_ops = {
  86. .request_cmd = ETHTOOL_MSG_PSE_GET,
  87. .reply_cmd = ETHTOOL_MSG_PSE_GET_REPLY,
  88. .hdr_attr = ETHTOOL_A_PSE_HEADER,
  89. .req_info_size = sizeof(struct pse_req_info),
  90. .reply_data_size = sizeof(struct pse_reply_data),
  91. .prepare_data = pse_prepare_data,
  92. .reply_size = pse_reply_size,
  93. .fill_reply = pse_fill_reply,
  94. };
  95. /* PSE_SET */
  96. const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
  97. [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
  98. [ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] =
  99. NLA_POLICY_RANGE(NLA_U32, ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED,
  100. ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED),
  101. };
  102. static int pse_set_pse_config(struct net_device *dev,
  103. struct netlink_ext_ack *extack,
  104. struct nlattr **tb)
  105. {
  106. struct phy_device *phydev = dev->phydev;
  107. struct pse_control_config config = {};
  108. /* Optional attribute. Do not return error if not set. */
  109. if (!tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL])
  110. return 0;
  111. /* this values are already validated by the ethnl_pse_set_policy */
  112. config.admin_cotrol = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
  113. if (!phydev) {
  114. NL_SET_ERR_MSG(extack, "No PHY is attached");
  115. return -EOPNOTSUPP;
  116. }
  117. if (!phydev->psec) {
  118. NL_SET_ERR_MSG(extack, "No PSE is attached");
  119. return -EOPNOTSUPP;
  120. }
  121. return pse_ethtool_set_config(phydev->psec, extack, &config);
  122. }
  123. int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info)
  124. {
  125. struct ethnl_req_info req_info = {};
  126. struct nlattr **tb = info->attrs;
  127. struct net_device *dev;
  128. int ret;
  129. ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_PSE_HEADER],
  130. genl_info_net(info), info->extack,
  131. true);
  132. if (ret < 0)
  133. return ret;
  134. dev = req_info.dev;
  135. rtnl_lock();
  136. ret = ethnl_ops_begin(dev);
  137. if (ret < 0)
  138. goto out_rtnl;
  139. ret = pse_set_pse_config(dev, info->extack, tb);
  140. ethnl_ops_complete(dev);
  141. out_rtnl:
  142. rtnl_unlock();
  143. ethnl_parse_header_dev_put(&req_info);
  144. return ret;
  145. }