162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci
362306a36Sopenharmony_ci#include "netlink.h"
462306a36Sopenharmony_ci#include "common.h"
562306a36Sopenharmony_ci#include "bitset.h"
662306a36Sopenharmony_ci
762306a36Sopenharmony_cistruct debug_req_info {
862306a36Sopenharmony_ci	struct ethnl_req_info		base;
962306a36Sopenharmony_ci};
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_cistruct debug_reply_data {
1262306a36Sopenharmony_ci	struct ethnl_reply_data		base;
1362306a36Sopenharmony_ci	u32				msg_mask;
1462306a36Sopenharmony_ci};
1562306a36Sopenharmony_ci
1662306a36Sopenharmony_ci#define DEBUG_REPDATA(__reply_base) \
1762306a36Sopenharmony_ci	container_of(__reply_base, struct debug_reply_data, base)
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ciconst struct nla_policy ethnl_debug_get_policy[] = {
2062306a36Sopenharmony_ci	[ETHTOOL_A_DEBUG_HEADER]	=
2162306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
2262306a36Sopenharmony_ci};
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int debug_prepare_data(const struct ethnl_req_info *req_base,
2562306a36Sopenharmony_ci			      struct ethnl_reply_data *reply_base,
2662306a36Sopenharmony_ci			      const struct genl_info *info)
2762306a36Sopenharmony_ci{
2862306a36Sopenharmony_ci	struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
2962306a36Sopenharmony_ci	struct net_device *dev = reply_base->dev;
3062306a36Sopenharmony_ci	int ret;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	if (!dev->ethtool_ops->get_msglevel)
3362306a36Sopenharmony_ci		return -EOPNOTSUPP;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci	ret = ethnl_ops_begin(dev);
3662306a36Sopenharmony_ci	if (ret < 0)
3762306a36Sopenharmony_ci		return ret;
3862306a36Sopenharmony_ci	data->msg_mask = dev->ethtool_ops->get_msglevel(dev);
3962306a36Sopenharmony_ci	ethnl_ops_complete(dev);
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	return 0;
4262306a36Sopenharmony_ci}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_cistatic int debug_reply_size(const struct ethnl_req_info *req_base,
4562306a36Sopenharmony_ci			    const struct ethnl_reply_data *reply_base)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	const struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
4862306a36Sopenharmony_ci	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci	return ethnl_bitset32_size(&data->msg_mask, NULL, NETIF_MSG_CLASS_COUNT,
5162306a36Sopenharmony_ci				   netif_msg_class_names, compact);
5262306a36Sopenharmony_ci}
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_cistatic int debug_fill_reply(struct sk_buff *skb,
5562306a36Sopenharmony_ci			    const struct ethnl_req_info *req_base,
5662306a36Sopenharmony_ci			    const struct ethnl_reply_data *reply_base)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	const struct debug_reply_data *data = DEBUG_REPDATA(reply_base);
5962306a36Sopenharmony_ci	bool compact = req_base->flags & ETHTOOL_FLAG_COMPACT_BITSETS;
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	return ethnl_put_bitset32(skb, ETHTOOL_A_DEBUG_MSGMASK, &data->msg_mask,
6262306a36Sopenharmony_ci				  NULL, NETIF_MSG_CLASS_COUNT,
6362306a36Sopenharmony_ci				  netif_msg_class_names, compact);
6462306a36Sopenharmony_ci}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci/* DEBUG_SET */
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ciconst struct nla_policy ethnl_debug_set_policy[] = {
6962306a36Sopenharmony_ci	[ETHTOOL_A_DEBUG_HEADER]	=
7062306a36Sopenharmony_ci		NLA_POLICY_NESTED(ethnl_header_policy),
7162306a36Sopenharmony_ci	[ETHTOOL_A_DEBUG_MSGMASK]	= { .type = NLA_NESTED },
7262306a36Sopenharmony_ci};
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_cistatic int
7562306a36Sopenharmony_ciethnl_set_debug_validate(struct ethnl_req_info *req_info,
7662306a36Sopenharmony_ci			 struct genl_info *info)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	const struct ethtool_ops *ops = req_info->dev->ethtool_ops;
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	return ops->get_msglevel && ops->set_msglevel ? 1 : -EOPNOTSUPP;
8162306a36Sopenharmony_ci}
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_cistatic int
8462306a36Sopenharmony_ciethnl_set_debug(struct ethnl_req_info *req_info, struct genl_info *info)
8562306a36Sopenharmony_ci{
8662306a36Sopenharmony_ci	struct net_device *dev = req_info->dev;
8762306a36Sopenharmony_ci	struct nlattr **tb = info->attrs;
8862306a36Sopenharmony_ci	bool mod = false;
8962306a36Sopenharmony_ci	u32 msg_mask;
9062306a36Sopenharmony_ci	int ret;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	msg_mask = dev->ethtool_ops->get_msglevel(dev);
9362306a36Sopenharmony_ci	ret = ethnl_update_bitset32(&msg_mask, NETIF_MSG_CLASS_COUNT,
9462306a36Sopenharmony_ci				    tb[ETHTOOL_A_DEBUG_MSGMASK],
9562306a36Sopenharmony_ci				    netif_msg_class_names, info->extack, &mod);
9662306a36Sopenharmony_ci	if (ret < 0 || !mod)
9762306a36Sopenharmony_ci		return ret;
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	dev->ethtool_ops->set_msglevel(dev, msg_mask);
10062306a36Sopenharmony_ci	return 1;
10162306a36Sopenharmony_ci}
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ciconst struct ethnl_request_ops ethnl_debug_request_ops = {
10462306a36Sopenharmony_ci	.request_cmd		= ETHTOOL_MSG_DEBUG_GET,
10562306a36Sopenharmony_ci	.reply_cmd		= ETHTOOL_MSG_DEBUG_GET_REPLY,
10662306a36Sopenharmony_ci	.hdr_attr		= ETHTOOL_A_DEBUG_HEADER,
10762306a36Sopenharmony_ci	.req_info_size		= sizeof(struct debug_req_info),
10862306a36Sopenharmony_ci	.reply_data_size	= sizeof(struct debug_reply_data),
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	.prepare_data		= debug_prepare_data,
11162306a36Sopenharmony_ci	.reply_size		= debug_reply_size,
11262306a36Sopenharmony_ci	.fill_reply		= debug_fill_reply,
11362306a36Sopenharmony_ci
11462306a36Sopenharmony_ci	.set_validate		= ethnl_set_debug_validate,
11562306a36Sopenharmony_ci	.set			= ethnl_set_debug,
11662306a36Sopenharmony_ci	.set_ntf_cmd		= ETHTOOL_MSG_DEBUG_NTF,
11762306a36Sopenharmony_ci};
118