1 // SPDX-License-Identifier: GPL-2.0-only
2 /* Copyright (c) 2020, The Linux Foundation. All rights reserved. */
3 
4 #define pr_fmt(fmt) "cnss_genl: " fmt
5 
6 #include <linux/err.h>
7 #include <linux/module.h>
8 #include <linux/delay.h>
9 #include <net/netlink.h>
10 #include <net/genetlink.h>
11 
12 #include "main.h"
13 #include "debug.h"
14 
15 #define ICNSS_GENL_FAMILY_NAME "cnss-genl"
16 #define ICNSS_GENL_MCAST_GROUP_NAME "cnss-genl-grp"
17 #define ICNSS_GENL_VERSION 1
18 #define ICNSS_GENL_DATA_LEN_MAX (15 * 1024)
19 #define ICNSS_GENL_STR_LEN_MAX 16
20 
21 enum {
22 	ICNSS_GENL_ATTR_MSG_UNSPEC,
23 	ICNSS_GENL_ATTR_MSG_TYPE,
24 	ICNSS_GENL_ATTR_MSG_FILE_NAME,
25 	ICNSS_GENL_ATTR_MSG_TOTAL_SIZE,
26 	ICNSS_GENL_ATTR_MSG_SEG_ID,
27 	ICNSS_GENL_ATTR_MSG_END,
28 	ICNSS_GENL_ATTR_MSG_DATA_LEN,
29 	ICNSS_GENL_ATTR_MSG_DATA,
30 	__ICNSS_GENL_ATTR_MAX,
31 };
32 
33 #define ICNSS_GENL_ATTR_MAX (__ICNSS_GENL_ATTR_MAX - 1)
34 
35 enum {
36 	ICNSS_GENL_CMD_UNSPEC,
37 	ICNSS_GENL_CMD_MSG,
38 	__ICNSS_GENL_CMD_MAX,
39 };
40 
41 #define ICNSS_GENL_CMD_MAX (__ICNSS_GENL_CMD_MAX - 1)
42 
43 static struct nla_policy icnss_genl_msg_policy[ICNSS_GENL_ATTR_MAX + 1] = {
44 	[ICNSS_GENL_ATTR_MSG_TYPE] = { .type = NLA_U8 },
45 	[ICNSS_GENL_ATTR_MSG_FILE_NAME] = { .type = NLA_NUL_STRING,
46 					   .len = ICNSS_GENL_STR_LEN_MAX },
47 	[ICNSS_GENL_ATTR_MSG_TOTAL_SIZE] = { .type = NLA_U32 },
48 	[ICNSS_GENL_ATTR_MSG_SEG_ID] = { .type = NLA_U32 },
49 	[ICNSS_GENL_ATTR_MSG_END] = { .type = NLA_U8 },
50 	[ICNSS_GENL_ATTR_MSG_DATA_LEN] = { .type = NLA_U32 },
51 	[ICNSS_GENL_ATTR_MSG_DATA] = { .type = NLA_BINARY,
52 				      .len = ICNSS_GENL_DATA_LEN_MAX },
53 };
54 
icnss_genl_process_msg(struct sk_buff * skb,struct genl_info * info)55 static int icnss_genl_process_msg(struct sk_buff *skb, struct genl_info *info)
56 {
57 	return 0;
58 }
59 
60 static struct genl_ops icnss_genl_ops[] = {
61 	{
62 		.cmd = ICNSS_GENL_CMD_MSG,
63 		.doit = icnss_genl_process_msg,
64 	},
65 };
66 
67 static struct genl_multicast_group icnss_genl_mcast_grp[] = {
68 	{
69 		.name = ICNSS_GENL_MCAST_GROUP_NAME,
70 	},
71 };
72 
73 static struct genl_family icnss_genl_family = {
74 	.id = 0,
75 	.hdrsize = 0,
76 	.name = ICNSS_GENL_FAMILY_NAME,
77 	.version = ICNSS_GENL_VERSION,
78 	.maxattr = ICNSS_GENL_ATTR_MAX,
79 	.policy = icnss_genl_msg_policy,
80 	.module = THIS_MODULE,
81 	.ops = icnss_genl_ops,
82 	.n_ops = ARRAY_SIZE(icnss_genl_ops),
83 	.mcgrps = icnss_genl_mcast_grp,
84 	.n_mcgrps = ARRAY_SIZE(icnss_genl_mcast_grp),
85 };
86 
icnss_genl_send_data(u8 type,char * file_name,u32 total_size,u32 seg_id,u8 end,u32 data_len,u8 * msg_buff)87 static int icnss_genl_send_data(u8 type, char *file_name, u32 total_size,
88 				u32 seg_id, u8 end, u32 data_len, u8 *msg_buff)
89 {
90 	struct sk_buff *skb = NULL;
91 	void *msg_header = NULL;
92 	int ret = 0;
93 	char filename[ICNSS_GENL_STR_LEN_MAX + 1];
94 
95 	icnss_pr_dbg("type: %u, file_name %s, total_size: %x, seg_id %u, end %u, data_len %u\n",
96 		     type, file_name, total_size, seg_id, end, data_len);
97 
98 	if (!file_name)
99 		strlcpy(filename, "default", sizeof(filename));
100 	else
101 		strlcpy(filename, file_name, sizeof(filename));
102 
103 	skb = genlmsg_new(NLMSG_HDRLEN +
104 			  nla_total_size(sizeof(type)) +
105 			  nla_total_size(strlen(filename) + 1) +
106 			  nla_total_size(sizeof(total_size)) +
107 			  nla_total_size(sizeof(seg_id)) +
108 			  nla_total_size(sizeof(end)) +
109 			  nla_total_size(sizeof(data_len)) +
110 			  nla_total_size(data_len), GFP_KERNEL);
111 	if (!skb)
112 		return -ENOMEM;
113 
114 	msg_header = genlmsg_put(skb, 0, 0,
115 				 &icnss_genl_family, 0,
116 				 ICNSS_GENL_CMD_MSG);
117 	if (!msg_header) {
118 		ret = -ENOMEM;
119 		goto fail;
120 	}
121 
122 	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_TYPE, type);
123 	if (ret < 0)
124 		goto fail;
125 	ret = nla_put_string(skb, ICNSS_GENL_ATTR_MSG_FILE_NAME, filename);
126 	if (ret < 0)
127 		goto fail;
128 	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_TOTAL_SIZE, total_size);
129 	if (ret < 0)
130 		goto fail;
131 	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_SEG_ID, seg_id);
132 	if (ret < 0)
133 		goto fail;
134 	ret = nla_put_u8(skb, ICNSS_GENL_ATTR_MSG_END, end);
135 	if (ret < 0)
136 		goto fail;
137 	ret = nla_put_u32(skb, ICNSS_GENL_ATTR_MSG_DATA_LEN, data_len);
138 	if (ret < 0)
139 		goto fail;
140 	ret = nla_put(skb, ICNSS_GENL_ATTR_MSG_DATA, data_len, msg_buff);
141 	if (ret < 0)
142 		goto fail;
143 
144 	genlmsg_end(skb, msg_header);
145 	ret = genlmsg_multicast(&icnss_genl_family, skb, 0, 0, GFP_KERNEL);
146 	if (ret < 0)
147 		icnss_pr_err("Fail to send genl msg: %d\n", ret);
148 
149 	return ret;
150 fail:
151 	icnss_pr_err("Fail to generate genl msg: %d\n", ret);
152 	if (skb)
153 		nlmsg_free(skb);
154 	return ret;
155 }
156 
icnss_genl_send_msg(void * buff,u8 type,char * file_name,u32 total_size)157 int icnss_genl_send_msg(void *buff, u8 type, char *file_name, u32 total_size)
158 {
159 	int ret = 0;
160 	u8 *msg_buff = buff;
161 	u32 remaining = total_size;
162 	u32 seg_id = 0;
163 	u32 data_len = 0;
164 	u8 end = 0;
165 	u8 retry;
166 
167 	icnss_pr_dbg("type: %u, total_size: %x\n", type, total_size);
168 
169 	while (remaining) {
170 		if (remaining > ICNSS_GENL_DATA_LEN_MAX) {
171 			data_len = ICNSS_GENL_DATA_LEN_MAX;
172 		} else {
173 			data_len = remaining;
174 			end = 1;
175 		}
176 
177 		for (retry = 0; retry < 2; retry++) {
178 			ret = icnss_genl_send_data(type, file_name, total_size,
179 						   seg_id, end, data_len,
180 						   msg_buff);
181 			if (ret >= 0)
182 				break;
183 			msleep(100);
184 		}
185 
186 		if (ret < 0) {
187 			icnss_pr_err("fail to send genl data, ret %d\n", ret);
188 			return ret;
189 		}
190 
191 		remaining -= data_len;
192 		msg_buff += data_len;
193 		seg_id++;
194 	}
195 
196 	return ret;
197 }
198 
icnss_genl_init(void)199 int icnss_genl_init(void)
200 {
201 	int ret = 0;
202 
203 	ret = genl_register_family(&icnss_genl_family);
204 	if (ret != 0)
205 		icnss_pr_err("genl_register_family fail: %d\n", ret);
206 
207 	return ret;
208 }
209 
icnss_genl_exit(void)210 void icnss_genl_exit(void)
211 {
212 	genl_unregister_family(&icnss_genl_family);
213 }
214