Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

ethtool: add interface to interact with Ethernet Power Equipment

Add interface to support Power Sourcing Equipment. At current step it
provides generic way to address all variants of PSE devices as defined
in IEEE 802.3-2018 but support only objects specified for IEEE 802.3-2018 104.4
PoDL Power Sourcing Equipment (PSE).

Currently supported and mandatory objects are:
IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus
IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState
IEEE 802.3-2018 30.15.1.2.1 acPoDLPSEAdminControl

This is minimal interface needed to control PSE on each separate
ethernet port but it provides not all mandatory objects specified in
IEEE 802.3-2018.

Since "PoDL PSE" and "PSE" have similar names, but some different values
I decide to not merge them and keep separate naming schema. This should
allow as to be as close to IEEE 802.3 spec as possible and avoid name
conflicts in the future.

This implementation is connected to PHYs instead of MACs because PSE
auto classification can potentially interfere with PHY auto negotiation.
So, may be some extra PHY related initialization will be needed.

With WIP version of ethtools interaction with PSE capable link looks
as following:

$ ip l
...
5: t1l1@eth0: <BROADCAST,MULTICAST> ..
...

$ ethtool --show-pse t1l1
PSE attributs for t1l1:
PoDL PSE Admin State: disabled
PoDL PSE Power Detection Status: disabled

$ ethtool --set-pse t1l1 podl-pse-admin-control enable
$ ethtool --show-pse t1l1
PSE attributs for t1l1:
PoDL PSE Admin State: enabled
PoDL PSE Power Detection Status: delivering power

Signed-off-by: kernel test robot <lkp@intel.com>
Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de>
Reviewed-by: Bagas Sanjaya <bagasdotme@gmail.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>

authored by

Oleksij Rempel and committed by
Jakub Kicinski
18ff0bcd 5e82147d

+449 -1
+59
Documentation/networking/ethtool-netlink.rst
··· 220 220 ``ETHTOOL_MSG_PHC_VCLOCKS_GET`` get PHC virtual clocks info 221 221 ``ETHTOOL_MSG_MODULE_SET`` set transceiver module parameters 222 222 ``ETHTOOL_MSG_MODULE_GET`` get transceiver module parameters 223 + ``ETHTOOL_MSG_PSE_SET`` set PSE parameters 224 + ``ETHTOOL_MSG_PSE_GET`` get PSE parameters 223 225 ===================================== ================================= 224 226 225 227 Kernel to userspace: ··· 262 260 ``ETHTOOL_MSG_STATS_GET_REPLY`` standard statistics 263 261 ``ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY`` PHC virtual clocks info 264 262 ``ETHTOOL_MSG_MODULE_GET_REPLY`` transceiver module parameters 263 + ``ETHTOOL_MSG_PSE_GET_REPLY`` PSE parameters 265 264 ======================================== ================================= 266 265 267 266 ``GET`` requests are sent by userspace applications to retrieve device ··· 1629 1626 1630 1627 For CMIS modules, low power mode is forced by the host according to table 6-12 1631 1628 in revision 5.0 of the specification. 1629 + 1630 + PSE_GET 1631 + ======= 1632 + 1633 + Gets PSE attributes. 1634 + 1635 + Request contents: 1636 + 1637 + ===================================== ====== ========================== 1638 + ``ETHTOOL_A_PSE_HEADER`` nested request header 1639 + ===================================== ====== ========================== 1640 + 1641 + Kernel response contents: 1642 + 1643 + ====================================== ====== ============================= 1644 + ``ETHTOOL_A_PSE_HEADER`` nested reply header 1645 + ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` u32 Operational state of the PoDL 1646 + PSE functions 1647 + ``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` u32 power detection status of the 1648 + PoDL PSE. 1649 + ====================================== ====== ============================= 1650 + 1651 + When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` attribute identifies 1652 + the operational state of the PoDL PSE functions. The operational state of the 1653 + PSE function can be changed using the ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` 1654 + action. This option is corresponding to ``IEEE 802.3-2018`` 30.15.1.1.2 1655 + aPoDLPSEAdminState. Possible values are: 1656 + 1657 + .. kernel-doc:: include/uapi/linux/ethtool.h 1658 + :identifiers: ethtool_podl_pse_admin_state 1659 + 1660 + When set, the optional ``ETHTOOL_A_PODL_PSE_PW_D_STATUS`` attribute identifies 1661 + the power detection status of the PoDL PSE. The status depend on internal PSE 1662 + state machine and automatic PD classification support. This option is 1663 + corresponding to ``IEEE 802.3-2018`` 30.15.1.1.3 aPoDLPSEPowerDetectionStatus. 1664 + Possible values are: 1665 + 1666 + .. kernel-doc:: include/uapi/linux/ethtool.h 1667 + :identifiers: ethtool_podl_pse_pw_d_status 1668 + 1669 + PSE_SET 1670 + ======= 1671 + 1672 + Sets PSE parameters. 1673 + 1674 + Request contents: 1675 + 1676 + ====================================== ====== ============================= 1677 + ``ETHTOOL_A_PSE_HEADER`` nested request header 1678 + ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` u32 Control PoDL PSE Admin state 1679 + ====================================== ====== ============================= 1680 + 1681 + When set, the optional ``ETHTOOL_A_PODL_PSE_ADMIN_CONTROL`` attribute is used 1682 + to control PoDL PSE Admin functions. This option is implementing 1683 + ``IEEE 802.3-2018`` 30.15.1.2.1 acPoDLPSEAdminControl. See 1684 + ``ETHTOOL_A_PODL_PSE_ADMIN_STATE`` for supported values. 1632 1685 1633 1686 Request translation 1634 1687 ===================
+58
drivers/net/pse-pd/pse_core.c
··· 254 254 return psec; 255 255 } 256 256 EXPORT_SYMBOL_GPL(of_pse_control_get); 257 + 258 + /** 259 + * pse_ethtool_get_status - get status of PSE control 260 + * @psec: PSE control pointer 261 + * @extack: extack for reporting useful error messages 262 + * @status: struct to store PSE status 263 + */ 264 + int pse_ethtool_get_status(struct pse_control *psec, 265 + struct netlink_ext_ack *extack, 266 + struct pse_control_status *status) 267 + { 268 + const struct pse_controller_ops *ops; 269 + int err; 270 + 271 + ops = psec->pcdev->ops; 272 + 273 + if (!ops->ethtool_get_status) { 274 + NL_SET_ERR_MSG(extack, 275 + "PSE driver does not support status report"); 276 + return -EOPNOTSUPP; 277 + } 278 + 279 + mutex_lock(&psec->pcdev->lock); 280 + err = ops->ethtool_get_status(psec->pcdev, psec->id, extack, status); 281 + mutex_unlock(&psec->pcdev->lock); 282 + 283 + return err; 284 + } 285 + EXPORT_SYMBOL_GPL(pse_ethtool_get_status); 286 + 287 + /** 288 + * pse_ethtool_set_config - set PSE control configuration 289 + * @psec: PSE control pointer 290 + * @extack: extack for reporting useful error messages 291 + * @config: Configuration of the test to run 292 + */ 293 + int pse_ethtool_set_config(struct pse_control *psec, 294 + struct netlink_ext_ack *extack, 295 + const struct pse_control_config *config) 296 + { 297 + const struct pse_controller_ops *ops; 298 + int err; 299 + 300 + ops = psec->pcdev->ops; 301 + 302 + if (!ops->ethtool_set_config) { 303 + NL_SET_ERR_MSG(extack, 304 + "PSE driver does not configuration"); 305 + return -EOPNOTSUPP; 306 + } 307 + 308 + mutex_lock(&psec->pcdev->lock); 309 + err = ops->ethtool_set_config(psec->pcdev, psec->id, extack, config); 310 + mutex_unlock(&psec->pcdev->lock); 311 + 312 + return err; 313 + } 314 + EXPORT_SYMBOL_GPL(pse_ethtool_set_config);
+62
include/linux/pse-pd/pse.h
··· 9 9 #include <linux/list.h> 10 10 #include <uapi/linux/ethtool.h> 11 11 12 + struct phy_device; 13 + struct pse_controller_dev; 14 + 15 + /** 16 + * struct pse_control_config - PSE control/channel configuration. 17 + * 18 + * @admin_cotrol: set PoDL PSE admin control as described in 19 + * IEEE 802.3-2018 30.15.1.2.1 acPoDLPSEAdminControl 20 + */ 21 + struct pse_control_config { 22 + enum ethtool_podl_pse_admin_state admin_cotrol; 23 + }; 24 + 25 + /** 26 + * struct pse_control_status - PSE control/channel status. 27 + * 28 + * @podl_admin_state: operational state of the PoDL PSE 29 + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState 30 + * @podl_pw_status: power detection status of the PoDL PSE. 31 + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: 32 + */ 33 + struct pse_control_status { 34 + enum ethtool_podl_pse_admin_state podl_admin_state; 35 + enum ethtool_podl_pse_pw_d_status podl_pw_status; 36 + }; 37 + 38 + /** 39 + * struct pse_controller_ops - PSE controller driver callbacks 40 + * 41 + * @ethtool_get_status: get PSE control status for ethtool interface 42 + * @ethtool_set_config: set PSE control configuration over ethtool interface 43 + */ 44 + struct pse_controller_ops { 45 + int (*ethtool_get_status)(struct pse_controller_dev *pcdev, 46 + unsigned long id, struct netlink_ext_ack *extack, 47 + struct pse_control_status *status); 48 + int (*ethtool_set_config)(struct pse_controller_dev *pcdev, 49 + unsigned long id, struct netlink_ext_ack *extack, 50 + const struct pse_control_config *config); 51 + }; 52 + 12 53 struct module; 13 54 struct device_node; 14 55 struct of_phandle_args; ··· 92 51 struct pse_control *of_pse_control_get(struct device_node *node); 93 52 void pse_control_put(struct pse_control *psec); 94 53 54 + int pse_ethtool_get_status(struct pse_control *psec, 55 + struct netlink_ext_ack *extack, 56 + struct pse_control_status *status); 57 + int pse_ethtool_set_config(struct pse_control *psec, 58 + struct netlink_ext_ack *extack, 59 + const struct pse_control_config *config); 60 + 95 61 #else 96 62 97 63 static inline struct pse_control *of_pse_control_get(struct device_node *node) ··· 108 60 109 61 static inline void pse_control_put(struct pse_control *psec) 110 62 { 63 + } 64 + 65 + int pse_ethtool_get_status(struct pse_control *psec, 66 + struct netlink_ext_ack *extack, 67 + struct pse_control_status *status) 68 + { 69 + return -ENOTSUPP; 70 + } 71 + 72 + int pse_ethtool_set_config(struct pse_control *psec, 73 + struct netlink_ext_ack *extack, 74 + const struct pse_control_config *config) 75 + { 76 + return -ENOTSUPP; 111 77 } 112 78 113 79 #endif
+45
include/uapi/linux/ethtool.h
··· 737 737 }; 738 738 739 739 /** 740 + * enum ethtool_podl_pse_admin_state - operational state of the PoDL PSE 741 + * functions. IEEE 802.3-2018 30.15.1.1.2 aPoDLPSEAdminState 742 + * @ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN: state of PoDL PSE functions are 743 + * unknown 744 + * @ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED: PoDL PSE functions are disabled 745 + * @ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED: PoDL PSE functions are enabled 746 + */ 747 + enum ethtool_podl_pse_admin_state { 748 + ETHTOOL_PODL_PSE_ADMIN_STATE_UNKNOWN = 1, 749 + ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, 750 + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED, 751 + }; 752 + 753 + /** 754 + * enum ethtool_podl_pse_pw_d_status - power detection status of the PoDL PSE. 755 + * IEEE 802.3-2018 30.15.1.1.3 aPoDLPSEPowerDetectionStatus: 756 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN: PoDL PSE 757 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED: "The enumeration “disabled” is 758 + * asserted true when the PoDL PSE state diagram variable mr_pse_enable is 759 + * false" 760 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING: "The enumeration “searching” is 761 + * asserted true when either of the PSE state diagram variables 762 + * pi_detecting or pi_classifying is true." 763 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING: "The enumeration “deliveringPower” 764 + * is asserted true when the PoDL PSE state diagram variable pi_powered is 765 + * true." 766 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP: "The enumeration “sleep” is asserted 767 + * true when the PoDL PSE state diagram variable pi_sleeping is true." 768 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE: "The enumeration “idle” is asserted true 769 + * when the logical combination of the PoDL PSE state diagram variables 770 + * pi_prebiased*!pi_sleeping is true." 771 + * @ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR: "The enumeration “error” is asserted 772 + * true when the PoDL PSE state diagram variable overload_held is true." 773 + */ 774 + enum ethtool_podl_pse_pw_d_status { 775 + ETHTOOL_PODL_PSE_PW_D_STATUS_UNKNOWN = 1, 776 + ETHTOOL_PODL_PSE_PW_D_STATUS_DISABLED, 777 + ETHTOOL_PODL_PSE_PW_D_STATUS_SEARCHING, 778 + ETHTOOL_PODL_PSE_PW_D_STATUS_DELIVERING, 779 + ETHTOOL_PODL_PSE_PW_D_STATUS_SLEEP, 780 + ETHTOOL_PODL_PSE_PW_D_STATUS_IDLE, 781 + ETHTOOL_PODL_PSE_PW_D_STATUS_ERROR, 782 + }; 783 + 784 + /** 740 785 * struct ethtool_gstrings - string set for data tagging 741 786 * @cmd: Command number = %ETHTOOL_GSTRINGS 742 787 * @string_set: String set ID; one of &enum ethtool_stringset
+16
include/uapi/linux/ethtool_netlink.h
··· 49 49 ETHTOOL_MSG_PHC_VCLOCKS_GET, 50 50 ETHTOOL_MSG_MODULE_GET, 51 51 ETHTOOL_MSG_MODULE_SET, 52 + ETHTOOL_MSG_PSE_GET, 53 + ETHTOOL_MSG_PSE_SET, 52 54 53 55 /* add new constants above here */ 54 56 __ETHTOOL_MSG_USER_CNT, ··· 96 94 ETHTOOL_MSG_PHC_VCLOCKS_GET_REPLY, 97 95 ETHTOOL_MSG_MODULE_GET_REPLY, 98 96 ETHTOOL_MSG_MODULE_NTF, 97 + ETHTOOL_MSG_PSE_GET_REPLY, 99 98 100 99 /* add new constants above here */ 101 100 __ETHTOOL_MSG_KERNEL_CNT, ··· 864 861 /* add new constants above here */ 865 862 __ETHTOOL_A_MODULE_CNT, 866 863 ETHTOOL_A_MODULE_MAX = (__ETHTOOL_A_MODULE_CNT - 1) 864 + }; 865 + 866 + /* Power Sourcing Equipment */ 867 + enum { 868 + ETHTOOL_A_PSE_UNSPEC, 869 + ETHTOOL_A_PSE_HEADER, /* nest - _A_HEADER_* */ 870 + ETHTOOL_A_PODL_PSE_ADMIN_STATE, /* u32 */ 871 + ETHTOOL_A_PODL_PSE_ADMIN_CONTROL, /* u32 */ 872 + ETHTOOL_A_PODL_PSE_PW_D_STATUS, /* u32 */ 873 + 874 + /* add new constants above here */ 875 + __ETHTOOL_A_PSE_CNT, 876 + ETHTOOL_A_PSE_MAX = (__ETHTOOL_A_PSE_CNT - 1) 867 877 }; 868 878 869 879 /* generic netlink info */
+2 -1
net/ethtool/Makefile
··· 7 7 ethtool_nl-y := netlink.o bitset.o strset.o linkinfo.o linkmodes.o \ 8 8 linkstate.o debug.o wol.o features.o privflags.o rings.o \ 9 9 channels.o coalesce.o pause.o eee.o tsinfo.o cabletest.o \ 10 - tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o 10 + tunnels.o fec.o eeprom.o stats.o phc_vclocks.o module.o \ 11 + pse-pd.o
+1
net/ethtool/common.h
··· 46 46 int __ethtool_get_ts_info(struct net_device *dev, struct ethtool_ts_info *info); 47 47 48 48 extern const struct ethtool_phy_ops *ethtool_phy_ops; 49 + extern const struct ethtool_pse_ops *ethtool_pse_ops; 49 50 50 51 int ethtool_get_module_info_call(struct net_device *dev, 51 52 struct ethtool_modinfo *modinfo);
+17
net/ethtool/netlink.c
··· 286 286 [ETHTOOL_MSG_STATS_GET] = &ethnl_stats_request_ops, 287 287 [ETHTOOL_MSG_PHC_VCLOCKS_GET] = &ethnl_phc_vclocks_request_ops, 288 288 [ETHTOOL_MSG_MODULE_GET] = &ethnl_module_request_ops, 289 + [ETHTOOL_MSG_PSE_GET] = &ethnl_pse_request_ops, 289 290 }; 290 291 291 292 static struct ethnl_dump_ctx *ethnl_dump_context(struct netlink_callback *cb) ··· 1023 1022 .doit = ethnl_set_module, 1024 1023 .policy = ethnl_module_set_policy, 1025 1024 .maxattr = ARRAY_SIZE(ethnl_module_set_policy) - 1, 1025 + }, 1026 + { 1027 + .cmd = ETHTOOL_MSG_PSE_GET, 1028 + .doit = ethnl_default_doit, 1029 + .start = ethnl_default_start, 1030 + .dumpit = ethnl_default_dumpit, 1031 + .done = ethnl_default_done, 1032 + .policy = ethnl_pse_get_policy, 1033 + .maxattr = ARRAY_SIZE(ethnl_pse_get_policy) - 1, 1034 + }, 1035 + { 1036 + .cmd = ETHTOOL_MSG_PSE_SET, 1037 + .flags = GENL_UNS_ADMIN_PERM, 1038 + .doit = ethnl_set_pse, 1039 + .policy = ethnl_pse_set_policy, 1040 + .maxattr = ARRAY_SIZE(ethnl_pse_set_policy) - 1, 1026 1041 }, 1027 1042 }; 1028 1043
+4
net/ethtool/netlink.h
··· 345 345 extern const struct ethnl_request_ops ethnl_stats_request_ops; 346 346 extern const struct ethnl_request_ops ethnl_phc_vclocks_request_ops; 347 347 extern const struct ethnl_request_ops ethnl_module_request_ops; 348 + extern const struct ethnl_request_ops ethnl_pse_request_ops; 348 349 349 350 extern const struct nla_policy ethnl_header_policy[ETHTOOL_A_HEADER_FLAGS + 1]; 350 351 extern const struct nla_policy ethnl_header_policy_stats[ETHTOOL_A_HEADER_FLAGS + 1]; ··· 384 383 extern const struct nla_policy ethnl_phc_vclocks_get_policy[ETHTOOL_A_PHC_VCLOCKS_HEADER + 1]; 385 384 extern const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1]; 386 385 extern const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1]; 386 + extern const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1]; 387 + extern const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1]; 387 388 388 389 int ethnl_set_linkinfo(struct sk_buff *skb, struct genl_info *info); 389 390 int ethnl_set_linkmodes(struct sk_buff *skb, struct genl_info *info); ··· 405 402 int ethnl_tunnel_info_dumpit(struct sk_buff *skb, struct netlink_callback *cb); 406 403 int ethnl_set_fec(struct sk_buff *skb, struct genl_info *info); 407 404 int ethnl_set_module(struct sk_buff *skb, struct genl_info *info); 405 + int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info); 408 406 409 407 extern const char stats_std_names[__ETHTOOL_STATS_CNT][ETH_GSTRING_LEN]; 410 408 extern const char stats_eth_phy_names[__ETHTOOL_A_STATS_ETH_PHY_CNT][ETH_GSTRING_LEN];
+185
net/ethtool/pse-pd.c
··· 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 <kernel@pengutronix.de> 7 + // 8 + 9 + #include "common.h" 10 + #include "linux/pse-pd/pse.h" 11 + #include "netlink.h" 12 + #include <linux/ethtool_netlink.h> 13 + #include <linux/ethtool.h> 14 + #include <linux/phy.h> 15 + 16 + struct pse_req_info { 17 + struct ethnl_req_info base; 18 + }; 19 + 20 + struct pse_reply_data { 21 + struct ethnl_reply_data base; 22 + struct pse_control_status status; 23 + }; 24 + 25 + #define PSE_REPDATA(__reply_base) \ 26 + container_of(__reply_base, struct pse_reply_data, base) 27 + 28 + /* PSE_GET */ 29 + 30 + const struct nla_policy ethnl_pse_get_policy[ETHTOOL_A_PSE_HEADER + 1] = { 31 + [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 32 + }; 33 + 34 + static int pse_get_pse_attributes(struct net_device *dev, 35 + struct netlink_ext_ack *extack, 36 + struct pse_reply_data *data) 37 + { 38 + struct phy_device *phydev = dev->phydev; 39 + 40 + if (!phydev) { 41 + NL_SET_ERR_MSG(extack, "No PHY is attached"); 42 + return -EOPNOTSUPP; 43 + } 44 + 45 + if (!phydev->psec) { 46 + NL_SET_ERR_MSG(extack, "No PSE is attached"); 47 + return -EOPNOTSUPP; 48 + } 49 + 50 + memset(&data->status, 0, sizeof(data->status)); 51 + 52 + return pse_ethtool_get_status(phydev->psec, extack, &data->status); 53 + } 54 + 55 + static int pse_prepare_data(const struct ethnl_req_info *req_base, 56 + struct ethnl_reply_data *reply_base, 57 + struct genl_info *info) 58 + { 59 + struct pse_reply_data *data = PSE_REPDATA(reply_base); 60 + struct net_device *dev = reply_base->dev; 61 + int ret; 62 + 63 + ret = ethnl_ops_begin(dev); 64 + if (ret < 0) 65 + return ret; 66 + 67 + ret = pse_get_pse_attributes(dev, info->extack, data); 68 + 69 + ethnl_ops_complete(dev); 70 + 71 + return ret; 72 + } 73 + 74 + static int pse_reply_size(const struct ethnl_req_info *req_base, 75 + const struct ethnl_reply_data *reply_base) 76 + { 77 + const struct pse_reply_data *data = PSE_REPDATA(reply_base); 78 + const struct pse_control_status *st = &data->status; 79 + int len = 0; 80 + 81 + if (st->podl_admin_state > 0) 82 + len += nla_total_size(sizeof(u32)); /* _PODL_PSE_ADMIN_STATE */ 83 + if (st->podl_pw_status > 0) 84 + len += nla_total_size(sizeof(u32)); /* _PODL_PSE_PW_D_STATUS */ 85 + 86 + return len; 87 + } 88 + 89 + static int pse_fill_reply(struct sk_buff *skb, 90 + const struct ethnl_req_info *req_base, 91 + const struct ethnl_reply_data *reply_base) 92 + { 93 + const struct pse_reply_data *data = PSE_REPDATA(reply_base); 94 + const struct pse_control_status *st = &data->status; 95 + 96 + if (st->podl_admin_state > 0 && 97 + nla_put_u32(skb, ETHTOOL_A_PODL_PSE_ADMIN_STATE, 98 + st->podl_admin_state)) 99 + return -EMSGSIZE; 100 + 101 + if (st->podl_pw_status > 0 && 102 + nla_put_u32(skb, ETHTOOL_A_PODL_PSE_PW_D_STATUS, 103 + st->podl_pw_status)) 104 + return -EMSGSIZE; 105 + 106 + return 0; 107 + } 108 + 109 + const struct ethnl_request_ops ethnl_pse_request_ops = { 110 + .request_cmd = ETHTOOL_MSG_PSE_GET, 111 + .reply_cmd = ETHTOOL_MSG_PSE_GET_REPLY, 112 + .hdr_attr = ETHTOOL_A_PSE_HEADER, 113 + .req_info_size = sizeof(struct pse_req_info), 114 + .reply_data_size = sizeof(struct pse_reply_data), 115 + 116 + .prepare_data = pse_prepare_data, 117 + .reply_size = pse_reply_size, 118 + .fill_reply = pse_fill_reply, 119 + }; 120 + 121 + /* PSE_SET */ 122 + 123 + const struct nla_policy ethnl_pse_set_policy[ETHTOOL_A_PSE_MAX + 1] = { 124 + [ETHTOOL_A_PSE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy), 125 + [ETHTOOL_A_PODL_PSE_ADMIN_CONTROL] = 126 + NLA_POLICY_RANGE(NLA_U32, ETHTOOL_PODL_PSE_ADMIN_STATE_DISABLED, 127 + ETHTOOL_PODL_PSE_ADMIN_STATE_ENABLED), 128 + }; 129 + 130 + static int pse_set_pse_config(struct net_device *dev, 131 + struct netlink_ext_ack *extack, 132 + struct nlattr **tb) 133 + { 134 + struct phy_device *phydev = dev->phydev; 135 + struct pse_control_config config = {}; 136 + 137 + /* Optional attribute. Do not return error if not set. */ 138 + if (!tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]) 139 + return 0; 140 + 141 + /* this values are already validated by the ethnl_pse_set_policy */ 142 + config.admin_cotrol = nla_get_u32(tb[ETHTOOL_A_PODL_PSE_ADMIN_CONTROL]); 143 + 144 + if (!phydev) { 145 + NL_SET_ERR_MSG(extack, "No PHY is attached"); 146 + return -EOPNOTSUPP; 147 + } 148 + 149 + if (!phydev->psec) { 150 + NL_SET_ERR_MSG(extack, "No PSE is attached"); 151 + return -EOPNOTSUPP; 152 + } 153 + 154 + return pse_ethtool_set_config(phydev->psec, extack, &config); 155 + } 156 + 157 + int ethnl_set_pse(struct sk_buff *skb, struct genl_info *info) 158 + { 159 + struct ethnl_req_info req_info = {}; 160 + struct nlattr **tb = info->attrs; 161 + struct net_device *dev; 162 + int ret; 163 + 164 + ret = ethnl_parse_header_dev_get(&req_info, tb[ETHTOOL_A_PSE_HEADER], 165 + genl_info_net(info), info->extack, 166 + true); 167 + if (ret < 0) 168 + return ret; 169 + 170 + dev = req_info.dev; 171 + 172 + rtnl_lock(); 173 + ret = ethnl_ops_begin(dev); 174 + if (ret < 0) 175 + goto out_rtnl; 176 + 177 + ret = pse_set_pse_config(dev, info->extack, tb); 178 + ethnl_ops_complete(dev); 179 + out_rtnl: 180 + rtnl_unlock(); 181 + 182 + ethnl_parse_header_dev_put(&req_info); 183 + 184 + return ret; 185 + }