pse-pd.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // SPDX-License-Identifier: GPL-2.0-only
  2. //
  3. // ethtool interface for Ethernet PSE (Power Sourcing Equipment)
  4. // and PD (Powered Device)
  5. //
  6. // Copyright (c) 2022 Pengutronix, Oleksij Rempel <kernel@pengutronix.de>
  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_phy),
  26. };
  27. static int pse_get_pse_attributes(struct phy_device *phydev,
  28. struct netlink_ext_ack *extack,
  29. struct pse_reply_data *data)
  30. {
  31. if (!phydev) {
  32. NL_SET_ERR_MSG(extack, "No PHY found");
  33. return -EOPNOTSUPP;
  34. }
  35. if (!phydev->psec) {
  36. NL_SET_ERR_MSG(extack, "No PSE is attached");
  37. return -EOPNOTSUPP;
  38. }
  39. memset(&data->status, 0, sizeof(data->status));
  40. return pse_ethtool_get_status(phydev->psec, extack, &data->status);
  41. }
  42. static int pse_prepare_data(const struct ethnl_req_info *req_base,
  43. struct ethnl_reply_data *reply_base,
  44. const struct genl_info *info)
  45. {
  46. struct pse_reply_data *data = PSE_REPDATA(reply_base);
  47. struct net_device *dev = reply_base->dev;
  48. struct nlattr **tb = info->attrs;
  49. struct phy_device *phydev;
  50. int ret;
  51. ret = ethnl_ops_begin(dev);
  52. if (ret < 0)
  53. return ret;
  54. phydev = ethnl_req_get_phydev(req_base, tb[ETHTOOL_A_PSE_HEADER],
  55. info->extack);
  56. if (IS_ERR(phydev))
  57. return -ENODEV;
  58. ret = pse_get_pse_attributes(phydev, info->extack, data);
  59. ethnl_ops_complete(dev);
  60. return ret;
  61. }
  62. static int pse_reply_size(const struct ethnl_req_info *req_base,
  63. const struct ethnl_reply_data *reply_base)
  64. {
  65. const struct pse_reply_data *data = PSE_REPDATA(reply_base);
  66. const struct pse_control_status *st = &data->status;
  67. int len = 0;
  68. if (st->podl_admin_state > 0)
  69. len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */
  70. if (st->podl_pw_status > 0)
  71. len += nla_total_size(sizeof(u32)); /* _PODL_PSE_PW_D_STATUS */
  72. if (st->c33_admin_state > 0)
  73. len += nla_total_size(sizeof(u32)); /* _C33_PSE_ADMIN_STATE */
  74. if (st->c33_pw_status > 0)
  75. len += nla_total_size(sizeof(u32)); /* _C33_PSE_PW_D_STATUS */
  76. if (st->c33_pw_class > 0)
  77. len += nla_total_size(sizeof(u32)); /* _C33_PSE_PW_CLASS */
  78. if (st->c33_actual_pw > 0)
  79. len += nla_total_size(sizeof(u32)); /* _C33_PSE_ACTUAL_PW */
  80. if (st->c33_ext_state_info.c33_pse_ext_state > 0) {
  81. len += nla_total_size(sizeof(u32)); /* _C33_PSE_EXT_STATE */
  82. if (st->c33_ext_state_info.__c33_pse_ext_substate > 0)
  83. /* _C33_PSE_EXT_SUBSTATE */
  84. len += nla_total_size(sizeof(u32));
  85. }
  86. if (st->c33_avail_pw_limit > 0)
  87. /* _C33_AVAIL_PSE_PW_LIMIT */
  88. len += nla_total_size(sizeof(u32));
  89. if (st->c33_pw_limit_nb_ranges > 0)
  90. /* _C33_PSE_PW_LIMIT_RANGES */
  91. len += st->c33_pw_limit_nb_ranges *
  92. (nla_total_size(0) +
  93. nla_total_size(sizeof(u32)) * 2);
  94. return len;
  95. }
  96. static int pse_put_pw_limit_ranges(struct sk_buff *skb,
  97. const struct pse_control_status *st)
  98. {
  99. const struct ethtool_c33_pse_pw_limit_range *pw_limit_ranges;
  100. int i;
  101. pw_limit_ranges = st->c33_pw_limit_ranges;
  102. for (i = 0; i < st->c33_pw_limit_nb_ranges; i++) {
  103. struct nlattr *nest;
  104. nest = nla_nest_start(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_RANGES);
  105. if (!nest)
  106. return -EMSGSIZE;
  107. if (nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MIN,
  108. pw_limit_ranges->min) ||
  109. nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_LIMIT_MAX,
  110. pw_limit_ranges->max)) {
  111. nla_nest_cancel(skb, nest);
  112. return -EMSGSIZE;
  113. }
  114. nla_nest_end(skb, nest);
  115. pw_limit_ranges++;
  116. }
  117. return 0;
  118. }
  119. static int pse_fill_reply(struct sk_buff *skb,
  120. const struct ethnl_req_info *req_base,
  121. const struct ethnl_reply_data *reply_base)
  122. {
  123. const struct pse_reply_data *data = PSE_REPDATA(reply_base);
  124. const struct pse_control_status *st = &data->status;
  125. if (st->podl_admin_state > 0 &&
  126. nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE,
  127. st->podl_admin_state))
  128. return -EMSGSIZE;
  129. if (st->podl_pw_status > 0 &&
  130. nla_put_u32(skb, ETHTOOL_A_PODL_PSE_PW_D_STATUS,
  131. st->podl_pw_status))
  132. return -EMSGSIZE;
  133. if (st->c33_admin_state > 0 &&
  134. nla_put_u32(skb, ETHTOOL_A_C33_PSE_ADMIN_STATE,
  135. st->c33_admin_state))
  136. return -EMSGSIZE;
  137. if (st->c33_pw_status > 0 &&
  138. nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_D_STATUS,
  139. st->c33_pw_status))
  140. return -EMSGSIZE;
  141. if (st->c33_pw_class > 0 &&
  142. nla_put_u32(skb, ETHTOOL_A_C33_PSE_PW_CLASS,
  143. st->c33_pw_class))
  144. return -EMSGSIZE;
  145. if (st->c33_actual_pw > 0 &&
  146. nla_put_u32(skb, ETHTOOL_A_C33_PSE_ACTUAL_PW,
  147. st->c33_actual_pw))
  148. return -EMSGSIZE;
  149. if (st->c33_ext_state_info.c33_pse_ext_state > 0) {
  150. if (nla_put_u32(skb, ETHTOOL_A_C33_PSE_EXT_STATE,
  151. st->c33_ext_state_info.c33_pse_ext_state))
  152. return -EMSGSIZE;
  153. if (st->c33_ext_state_info.__c33_pse_ext_substate > 0 &&
  154. nla_put_u32(skb, ETHTOOL_A_C33_PSE_EXT_SUBSTATE,
  155. st->c33_ext_state_info.__c33_pse_ext_substate))
  156. return -EMSGSIZE;
  157. }
  158. if (st->c33_avail_pw_limit > 0 &&
  159. nla_put_u32(skb, ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT,
  160. st->c33_avail_pw_limit))
  161. return -EMSGSIZE;
  162. if (st->c33_pw_limit_nb_ranges > 0 &&
  163. pse_put_pw_limit_ranges(skb, st))
  164. return -EMSGSIZE;
  165. return 0;
  166. }
  167. static void pse_cleanup_data(struct ethnl_reply_data *reply_base)
  168. {
  169. const struct pse_reply_data *data = PSE_REPDATA(reply_base);
  170. kfree(data->status.c33_pw_limit_ranges);
  171. }
  172. /* PSE_SET */
  173. const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = {
  174. [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy_phy),
  175. [ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] =
  176. NLA_POLICY_RANGE(NLA_U32, ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED,
  177. ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED),
  178. [ETHTOOL_A_C33_PSE_ADMIN_CONTROL] =
  179. NLA_POLICY_RANGE(NLA_U32, ETHTOOL_C33_PSE_ADMIN_STATE_DISABLED,
  180. ETHTOOL_C33_PSE_ADMIN_STATE_ENABLED),
  181. [ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT] = { .type = NLA_U32 },
  182. };
  183. static int
  184. ethnl_set_pse_validate(struct phy_device *phydev, struct genl_info *info)
  185. {
  186. struct nlattr **tb = info->attrs;
  187. if (IS_ERR_OR_NULL(phydev)) {
  188. NL_SET_ERR_MSG(info->extack, "No PHY is attached");
  189. return -EOPNOTSUPP;
  190. }
  191. if (!phydev->psec) {
  192. NL_SET_ERR_MSG(info->extack, "No PSE is attached");
  193. return -EOPNOTSUPP;
  194. }
  195. if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] &&
  196. !pse_has_podl(phydev->psec)) {
  197. NL_SET_ERR_MSG_ATTR(info->extack,
  198. tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL],
  199. "setting PoDL PSE admin control not supported");
  200. return -EOPNOTSUPP;
  201. }
  202. if (tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL] &&
  203. !pse_has_c33(phydev->psec)) {
  204. NL_SET_ERR_MSG_ATTR(info->extack,
  205. tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL],
  206. "setting C33 PSE admin control not supported");
  207. return -EOPNOTSUPP;
  208. }
  209. return 0;
  210. }
  211. static int
  212. ethnl_set_pse(struct ethnl_req_info *req_info, struct genl_info *info)
  213. {
  214. struct nlattr **tb = info->attrs;
  215. struct phy_device *phydev;
  216. int ret;
  217. phydev = ethnl_req_get_phydev(req_info, tb[ETHTOOL_A_PSE_HEADER],
  218. info->extack);
  219. ret = ethnl_set_pse_validate(phydev, info);
  220. if (ret)
  221. return ret;
  222. if (tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]) {
  223. unsigned int pw_limit;
  224. pw_limit = nla_get_u32(tb[ETHTOOL_A_C33_PSE_AVAIL_PW_LIMIT]);
  225. ret = pse_ethtool_set_pw_limit(phydev->psec, info->extack,
  226. pw_limit);
  227. if (ret)
  228. return ret;
  229. }
  230. /* These values are already validated by the ethnl_pse_set_policy */
  231. if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] ||
  232. tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]) {
  233. struct pse_control_config config = {};
  234. if (tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL])
  235. config.podl_admin_control = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]);
  236. if (tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL])
  237. config.c33_admin_control = nla_get_u32(tb[ETHTOOL_A_C33_PSE_ADMIN_CONTROL]);
  238. /* pse_ethtool_set_config() will do nothing if the config
  239. * is zero
  240. */
  241. ret = pse_ethtool_set_config(phydev->psec, info->extack,
  242. &config);
  243. if (ret)
  244. return ret;
  245. }
  246. /* Return errno or zero - PSE has no notification */
  247. return ret;
  248. }
  249. const struct ethnl_request_ops ethnl_pse_request_ops = {
  250. .request_cmd = ETHTOOL_MSG_PSE_GET,
  251. .reply_cmd = ETHTOOL_MSG_PSE_GET_REPLY,
  252. .hdr_attr = ETHTOOL_A_PSE_HEADER,
  253. .req_info_size = sizeof(struct pse_req_info),
  254. .reply_data_size = sizeof(struct pse_reply_data),
  255. .prepare_data = pse_prepare_data,
  256. .reply_size = pse_reply_size,
  257. .fill_reply = pse_fill_reply,
  258. .cleanup_data = pse_cleanup_data,
  259. .set = ethnl_set_pse,
  260. /* PSE has no notification */
  261. };