xref: /openbmc/linux/net/ethtool/module.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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