1353407d9SIdo Schimmel // SPDX-License-Identifier: GPL-2.0-only
2353407d9SIdo Schimmel
3353407d9SIdo Schimmel #include <linux/ethtool.h>
4353407d9SIdo Schimmel
5353407d9SIdo Schimmel #include "netlink.h"
6353407d9SIdo Schimmel #include "common.h"
7353407d9SIdo Schimmel #include "bitset.h"
8353407d9SIdo Schimmel
9353407d9SIdo Schimmel struct module_req_info {
10353407d9SIdo Schimmel struct ethnl_req_info base;
11353407d9SIdo Schimmel };
12353407d9SIdo Schimmel
13353407d9SIdo Schimmel struct module_reply_data {
14353407d9SIdo Schimmel struct ethnl_reply_data base;
15353407d9SIdo Schimmel struct ethtool_module_power_mode_params power;
16353407d9SIdo Schimmel };
17353407d9SIdo Schimmel
18353407d9SIdo Schimmel #define MODULE_REPDATA(__reply_base) \
19353407d9SIdo Schimmel container_of(__reply_base, struct module_reply_data, base)
20353407d9SIdo Schimmel
21353407d9SIdo Schimmel /* MODULE_GET */
22353407d9SIdo Schimmel
23353407d9SIdo Schimmel const struct nla_policy ethnl_module_get_policy[ETHTOOL_A_MODULE_HEADER + 1] = {
24353407d9SIdo Schimmel [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
25353407d9SIdo Schimmel };
26353407d9SIdo Schimmel
module_get_power_mode(struct net_device * dev,struct module_reply_data * data,struct netlink_ext_ack * extack)27353407d9SIdo Schimmel static int module_get_power_mode(struct net_device *dev,
28353407d9SIdo Schimmel struct module_reply_data *data,
29353407d9SIdo Schimmel struct netlink_ext_ack *extack)
30353407d9SIdo Schimmel {
31353407d9SIdo Schimmel const struct ethtool_ops *ops = dev->ethtool_ops;
32353407d9SIdo Schimmel
33353407d9SIdo Schimmel if (!ops->get_module_power_mode)
34353407d9SIdo Schimmel return 0;
35353407d9SIdo Schimmel
36353407d9SIdo Schimmel return ops->get_module_power_mode(dev, &data->power, extack);
37353407d9SIdo Schimmel }
38353407d9SIdo Schimmel
module_prepare_data(const struct ethnl_req_info * req_base,struct ethnl_reply_data * reply_base,const struct genl_info * info)39353407d9SIdo Schimmel static int module_prepare_data(const struct ethnl_req_info *req_base,
40353407d9SIdo Schimmel struct ethnl_reply_data *reply_base,
41*f946270dSJakub Kicinski const struct genl_info *info)
42353407d9SIdo Schimmel {
43353407d9SIdo Schimmel struct module_reply_data *data = MODULE_REPDATA(reply_base);
44353407d9SIdo Schimmel struct net_device *dev = reply_base->dev;
45353407d9SIdo Schimmel int ret;
46353407d9SIdo Schimmel
47353407d9SIdo Schimmel ret = ethnl_ops_begin(dev);
48353407d9SIdo Schimmel if (ret < 0)
49353407d9SIdo Schimmel return ret;
50353407d9SIdo Schimmel
51*f946270dSJakub Kicinski ret = module_get_power_mode(dev, data, info->extack);
52353407d9SIdo Schimmel if (ret < 0)
53353407d9SIdo Schimmel goto out_complete;
54353407d9SIdo Schimmel
55353407d9SIdo Schimmel out_complete:
56353407d9SIdo Schimmel ethnl_ops_complete(dev);
57353407d9SIdo Schimmel return ret;
58353407d9SIdo Schimmel }
59353407d9SIdo Schimmel
module_reply_size(const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)60353407d9SIdo Schimmel static int module_reply_size(const struct ethnl_req_info *req_base,
61353407d9SIdo Schimmel const struct ethnl_reply_data *reply_base)
62353407d9SIdo Schimmel {
63353407d9SIdo Schimmel struct module_reply_data *data = MODULE_REPDATA(reply_base);
64353407d9SIdo Schimmel int len = 0;
65353407d9SIdo Schimmel
66353407d9SIdo Schimmel if (data->power.policy)
67353407d9SIdo Schimmel len += nla_total_size(sizeof(u8)); /* _MODULE_POWER_MODE_POLICY */
68353407d9SIdo Schimmel
69353407d9SIdo Schimmel if (data->power.mode)
70353407d9SIdo Schimmel len += nla_total_size(sizeof(u8)); /* _MODULE_POWER_MODE */
71353407d9SIdo Schimmel
72353407d9SIdo Schimmel return len;
73353407d9SIdo Schimmel }
74353407d9SIdo Schimmel
module_fill_reply(struct sk_buff * skb,const struct ethnl_req_info * req_base,const struct ethnl_reply_data * reply_base)75353407d9SIdo Schimmel static int module_fill_reply(struct sk_buff *skb,
76353407d9SIdo Schimmel const struct ethnl_req_info *req_base,
77353407d9SIdo Schimmel const struct ethnl_reply_data *reply_base)
78353407d9SIdo Schimmel {
79353407d9SIdo Schimmel const struct module_reply_data *data = MODULE_REPDATA(reply_base);
80353407d9SIdo Schimmel
81353407d9SIdo Schimmel if (data->power.policy &&
82353407d9SIdo Schimmel nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE_POLICY,
83353407d9SIdo Schimmel data->power.policy))
84353407d9SIdo Schimmel return -EMSGSIZE;
85353407d9SIdo Schimmel
86353407d9SIdo Schimmel if (data->power.mode &&
87353407d9SIdo Schimmel nla_put_u8(skb, ETHTOOL_A_MODULE_POWER_MODE, data->power.mode))
88353407d9SIdo Schimmel return -EMSGSIZE;
89353407d9SIdo Schimmel
90353407d9SIdo Schimmel return 0;
91353407d9SIdo Schimmel }
92353407d9SIdo Schimmel
9304007961SJakub Kicinski /* MODULE_SET */
9404007961SJakub Kicinski
9504007961SJakub Kicinski const struct nla_policy ethnl_module_set_policy[ETHTOOL_A_MODULE_POWER_MODE_POLICY + 1] = {
9604007961SJakub Kicinski [ETHTOOL_A_MODULE_HEADER] = NLA_POLICY_NESTED(ethnl_header_policy),
9704007961SJakub Kicinski [ETHTOOL_A_MODULE_POWER_MODE_POLICY] =
9804007961SJakub Kicinski NLA_POLICY_RANGE(NLA_U8, ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH,
9904007961SJakub Kicinski ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO),
10004007961SJakub Kicinski };
10104007961SJakub Kicinski
10204007961SJakub Kicinski static int
ethnl_set_module_validate(struct ethnl_req_info * req_info,struct genl_info * info)10304007961SJakub Kicinski ethnl_set_module_validate(struct ethnl_req_info *req_info,
10404007961SJakub Kicinski struct genl_info *info)
10504007961SJakub Kicinski {
10604007961SJakub Kicinski const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
10704007961SJakub Kicinski struct nlattr **tb = info->attrs;
10804007961SJakub Kicinski
10904007961SJakub Kicinski if (!tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY])
11004007961SJakub Kicinski return 0;
11104007961SJakub Kicinski
11204007961SJakub Kicinski if (!ops->get_module_power_mode || !ops->set_module_power_mode) {
11304007961SJakub Kicinski NL_SET_ERR_MSG_ATTR(info->extack,
11404007961SJakub Kicinski tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY],
11504007961SJakub Kicinski "Setting power mode policy is not supported by this device");
11604007961SJakub Kicinski return -EOPNOTSUPP;
11704007961SJakub Kicinski }
11804007961SJakub Kicinski
11904007961SJakub Kicinski return 1;
12004007961SJakub Kicinski }
12104007961SJakub Kicinski
12204007961SJakub Kicinski static int
ethnl_set_module(struct ethnl_req_info * req_info,struct genl_info * info)12304007961SJakub Kicinski ethnl_set_module(struct ethnl_req_info *req_info, struct genl_info *info)
12404007961SJakub Kicinski {
12504007961SJakub Kicinski struct ethtool_module_power_mode_params power = {};
12604007961SJakub Kicinski struct ethtool_module_power_mode_params power_new;
12704007961SJakub Kicinski const struct ethtool_ops *ops;
12804007961SJakub Kicinski struct net_device *dev = req_info->dev;
12904007961SJakub Kicinski struct nlattr **tb = info->attrs;
13004007961SJakub Kicinski int ret;
13104007961SJakub Kicinski
13204007961SJakub Kicinski ops = dev->ethtool_ops;
13304007961SJakub Kicinski
13404007961SJakub Kicinski power_new.policy = nla_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
13504007961SJakub Kicinski ret = ops->get_module_power_mode(dev, &power, info->extack);
13604007961SJakub Kicinski if (ret < 0)
13704007961SJakub Kicinski return ret;
13804007961SJakub Kicinski
13904007961SJakub Kicinski if (power_new.policy == power.policy)
14004007961SJakub Kicinski return 0;
14104007961SJakub Kicinski
14204007961SJakub Kicinski ret = ops->set_module_power_mode(dev, &power_new, info->extack);
14304007961SJakub Kicinski return ret < 0 ? ret : 1;
14404007961SJakub Kicinski }
14504007961SJakub Kicinski
146353407d9SIdo Schimmel const struct ethnl_request_ops ethnl_module_request_ops = {
147353407d9SIdo Schimmel .request_cmd = ETHTOOL_MSG_MODULE_GET,
148353407d9SIdo Schimmel .reply_cmd = ETHTOOL_MSG_MODULE_GET_REPLY,
149353407d9SIdo Schimmel .hdr_attr = ETHTOOL_A_MODULE_HEADER,
150353407d9SIdo Schimmel .req_info_size = sizeof(struct module_req_info),
151353407d9SIdo Schimmel .reply_data_size = sizeof(struct module_reply_data),
152353407d9SIdo Schimmel
153353407d9SIdo Schimmel .prepare_data = module_prepare_data,
154353407d9SIdo Schimmel .reply_size = module_reply_size,
155353407d9SIdo Schimmel .fill_reply = module_fill_reply,
15604007961SJakub Kicinski
15704007961SJakub Kicinski .set_validate = ethnl_set_module_validate,
15804007961SJakub Kicinski .set = ethnl_set_module,
15904007961SJakub Kicinski .set_ntf_cmd = ETHTOOL_MSG_MODULE_NTF,
160353407d9SIdo Schimmel };
161