blob: cee89dbacb3d1ebb8163701a51ca157457d36802 [file] [log] [blame]
developerfd40db22021-04-29 10:08:25 +08001// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (c) 2018 MediaTek Inc.
4 * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>
5 */
6
7#include <linux/types.h>
8#include <linux/kernel.h>
9#include <linux/module.h>
10#include <linux/init.h>
11#include <net/genetlink.h>
12
13#include "mt753x.h"
14#include "mt753x_nl.h"
15
16struct mt753x_nl_cmd_item {
17 enum mt753x_cmd cmd;
18 bool require_dev;
19 int (*process)(struct genl_info *info, struct gsw_mt753x *gsw);
20 u32 nr_required_attrs;
21 const enum mt753x_attr *required_attrs;
22};
23
24static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info);
25
26static const struct nla_policy mt753x_nl_cmd_policy[] = {
27 [MT753X_ATTR_TYPE_MESG] = { .type = NLA_STRING },
28 [MT753X_ATTR_TYPE_PHY] = { .type = NLA_S32 },
29 [MT753X_ATTR_TYPE_REG] = { .type = NLA_S32 },
30 [MT753X_ATTR_TYPE_VAL] = { .type = NLA_S32 },
31 [MT753X_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 },
32 [MT753X_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 },
33 [MT753X_ATTR_TYPE_DEVAD] = { .type = NLA_S32 },
34};
35
36static const struct genl_ops mt753x_nl_ops[] = {
37 {
38 .cmd = MT753X_CMD_REQUEST,
39 .doit = mt753x_nl_response,
40// .policy = mt753x_nl_cmd_policy,
41 .flags = GENL_ADMIN_PERM,
42 }, {
43 .cmd = MT753X_CMD_READ,
44 .doit = mt753x_nl_response,
45// .policy = mt753x_nl_cmd_policy,
46 .flags = GENL_ADMIN_PERM,
47 }, {
48 .cmd = MT753X_CMD_WRITE,
49 .doit = mt753x_nl_response,
50// .policy = mt753x_nl_cmd_policy,
51 .flags = GENL_ADMIN_PERM,
52 },
53};
54
55static struct genl_family mt753x_nl_family = {
56 .name = MT753X_GENL_NAME,
57 .version = MT753X_GENL_VERSION,
58 .maxattr = MT753X_NR_ATTR_TYPE,
59 .ops = mt753x_nl_ops,
60 .n_ops = ARRAY_SIZE(mt753x_nl_ops),
61 .policy = mt753x_nl_cmd_policy,
62};
63
64static int mt753x_nl_list_devs(char *buff, int size)
65{
66 struct gsw_mt753x *gsw;
67 int len, total = 0;
68 char buf[80];
69
70 memset(buff, 0, size);
71
72 mt753x_lock_gsw();
73
74 list_for_each_entry(gsw, &mt753x_devs, list) {
75 len = snprintf(buf, sizeof(buf),
76 "id: %d, model: %s, node: %s\n",
77 gsw->id, gsw->name, gsw->dev->of_node->name);
78 strncat(buff, buf, size - total);
79 total += len;
80 }
81
82 mt753x_put_gsw();
83
84 return total;
85}
86
87static int mt753x_nl_prepare_reply(struct genl_info *info, u8 cmd,
88 struct sk_buff **skbp)
89{
90 struct sk_buff *msg;
91 void *reply;
92
93 if (!info)
94 return -EINVAL;
95
96 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
97 if (!msg)
98 return -ENOMEM;
99
100 /* Construct send-back message header */
101 reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,
102 &mt753x_nl_family, 0, cmd);
103 if (!reply) {
104 nlmsg_free(msg);
105 return -EINVAL;
106 }
107
108 *skbp = msg;
109 return 0;
110}
111
112static int mt753x_nl_send_reply(struct sk_buff *skb, struct genl_info *info)
113{
114 struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
115 void *reply = genlmsg_data(genlhdr);
116
117 /* Finalize a generic netlink message (update message header) */
118 genlmsg_end(skb, reply);
119
120 /* reply to a request */
121 return genlmsg_reply(skb, info);
122}
123
124static s32 mt753x_nl_get_s32(struct genl_info *info, enum mt753x_attr attr,
125 s32 defval)
126{
127 struct nlattr *na;
128
129 na = info->attrs[attr];
130 if (na)
131 return nla_get_s32(na);
132
133 return defval;
134}
135
136static int mt753x_nl_get_u32(struct genl_info *info, enum mt753x_attr attr,
137 u32 *val)
138{
139 struct nlattr *na;
140
141 na = info->attrs[attr];
142 if (na) {
143 *val = nla_get_u32(na);
144 return 0;
145 }
146
147 return -1;
148}
149
150static struct gsw_mt753x *mt753x_nl_parse_find_gsw(struct genl_info *info)
151{
152 struct gsw_mt753x *gsw;
153 struct nlattr *na;
154 int gsw_id;
155
156 na = info->attrs[MT753X_ATTR_TYPE_DEV_ID];
157 if (na) {
158 gsw_id = nla_get_s32(na);
159 if (gsw_id >= 0)
160 gsw = mt753x_get_gsw(gsw_id);
161 else
162 gsw = mt753x_get_first_gsw();
163 } else {
164 gsw = mt753x_get_first_gsw();
165 }
166
167 return gsw;
168}
169
170static int mt753x_nl_get_swdevs(struct genl_info *info, struct gsw_mt753x *gsw)
171{
172 struct sk_buff *rep_skb = NULL;
173 char dev_info[512];
174 int ret;
175
176 ret = mt753x_nl_list_devs(dev_info, sizeof(dev_info));
177 if (!ret) {
178 pr_info("No switch registered\n");
179 return -EINVAL;
180 }
181
182 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_REPLY, &rep_skb);
183 if (ret < 0)
184 goto err;
185
186 ret = nla_put_string(rep_skb, MT753X_ATTR_TYPE_MESG, dev_info);
187 if (ret < 0)
188 goto err;
189
190 return mt753x_nl_send_reply(rep_skb, info);
191
192err:
193 if (rep_skb)
194 nlmsg_free(rep_skb);
195
196 return ret;
197}
198
199static int mt753x_nl_reply_read(struct genl_info *info, struct gsw_mt753x *gsw)
200{
201 struct sk_buff *rep_skb = NULL;
202 s32 phy, devad, reg;
203 int value;
204 int ret = 0;
205
206 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
207 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
208 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
209
210 if (reg < 0)
211 goto err;
212
213 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_READ, &rep_skb);
214 if (ret < 0)
215 goto err;
216
217 if (phy >= 0) {
218 if (devad < 0)
219 value = gsw->mii_read(gsw, phy, reg);
220 else
221 value = gsw->mmd_read(gsw, phy, devad, reg);
222 } else {
223 value = mt753x_reg_read(gsw, reg);
224 }
225
226 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
227 if (ret < 0)
228 goto err;
229
230 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
231 if (ret < 0)
232 goto err;
233
234 return mt753x_nl_send_reply(rep_skb, info);
235
236err:
237 if (rep_skb)
238 nlmsg_free(rep_skb);
239
240 return ret;
241}
242
243static int mt753x_nl_reply_write(struct genl_info *info, struct gsw_mt753x *gsw)
244{
245 struct sk_buff *rep_skb = NULL;
246 s32 phy, devad, reg;
247 u32 value;
248 int ret = 0;
249
250 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
251 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
252 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
253
254 if (mt753x_nl_get_u32(info, MT753X_ATTR_TYPE_VAL, &value))
255 goto err;
256
257 if (reg < 0)
258 goto err;
259
260 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_WRITE, &rep_skb);
261 if (ret < 0)
262 goto err;
263
264 if (phy >= 0) {
265 if (devad < 0)
266 gsw->mii_write(gsw, phy, reg, value);
267 else
268 gsw->mmd_write(gsw, phy, devad, reg, value);
269 } else {
270 mt753x_reg_write(gsw, reg, value);
271 }
272
273 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
274 if (ret < 0)
275 goto err;
276
277 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
278 if (ret < 0)
279 goto err;
280
281 return mt753x_nl_send_reply(rep_skb, info);
282
283err:
284 if (rep_skb)
285 nlmsg_free(rep_skb);
286
287 return ret;
288}
289
290static const enum mt753x_attr mt753x_nl_cmd_read_attrs[] = {
291 MT753X_ATTR_TYPE_REG
292};
293
294static const enum mt753x_attr mt753x_nl_cmd_write_attrs[] = {
295 MT753X_ATTR_TYPE_REG,
296 MT753X_ATTR_TYPE_VAL
297};
298
299static const struct mt753x_nl_cmd_item mt753x_nl_cmds[] = {
300 {
301 .cmd = MT753X_CMD_REQUEST,
302 .require_dev = false,
303 .process = mt753x_nl_get_swdevs
304 }, {
305 .cmd = MT753X_CMD_READ,
306 .require_dev = true,
307 .process = mt753x_nl_reply_read,
308 .required_attrs = mt753x_nl_cmd_read_attrs,
309 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_read_attrs),
310 }, {
311 .cmd = MT753X_CMD_WRITE,
312 .require_dev = true,
313 .process = mt753x_nl_reply_write,
314 .required_attrs = mt753x_nl_cmd_write_attrs,
315 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_write_attrs),
316 }
317};
318
319static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info)
320{
321 struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
322 const struct mt753x_nl_cmd_item *cmditem = NULL;
323 struct gsw_mt753x *gsw = NULL;
324 u32 sat_req_attrs = 0;
325 int i, ret;
326
327 for (i = 0; i < ARRAY_SIZE(mt753x_nl_cmds); i++) {
328 if (hdr->cmd == mt753x_nl_cmds[i].cmd) {
329 cmditem = &mt753x_nl_cmds[i];
330 break;
331 }
332 }
333
334 if (!cmditem) {
335 pr_info("mt753x-nl: unknown cmd %u\n", hdr->cmd);
336 return -EINVAL;
337 }
338
339 for (i = 0; i < cmditem->nr_required_attrs; i++) {
340 if (info->attrs[cmditem->required_attrs[i]])
341 sat_req_attrs++;
342 }
343
344 if (sat_req_attrs != cmditem->nr_required_attrs) {
345 pr_info("mt753x-nl: missing required attr(s) for cmd %u\n",
346 hdr->cmd);
347 return -EINVAL;
348 }
349
350 if (cmditem->require_dev) {
351 gsw = mt753x_nl_parse_find_gsw(info);
352 if (!gsw) {
353 pr_info("mt753x-nl: failed to find switch dev\n");
354 return -EINVAL;
355 }
356 }
357
358 ret = cmditem->process(info, gsw);
359
360 mt753x_put_gsw();
361
362 return ret;
363}
364
developer5145ebb2022-07-14 15:16:24 +0800365int mt753x_nl_init(void)
developerfd40db22021-04-29 10:08:25 +0800366{
367 int ret;
368
369 ret = genl_register_family(&mt753x_nl_family);
370 if (ret) {
371 pr_info("mt753x-nl: genl_register_family_with_ops failed\n");
372 return ret;
373 }
374
375 return 0;
376}
377
developer5145ebb2022-07-14 15:16:24 +0800378void mt753x_nl_exit(void)
developerfd40db22021-04-29 10:08:25 +0800379{
380 genl_unregister_family(&mt753x_nl_family);
381}