blob: fa8e595ee422b5dffcbdb8f654127e2e11b135bf [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);
developera48549f2022-12-29 17:34:13 +080078 if (len == strlen(buf)) {
79 strncat(buff, buf, size - total);
80 total += len;
81 }
developerfd40db22021-04-29 10:08:25 +080082 }
83
84 mt753x_put_gsw();
85
86 return total;
87}
88
89static int mt753x_nl_prepare_reply(struct genl_info *info, u8 cmd,
90 struct sk_buff **skbp)
91{
92 struct sk_buff *msg;
93 void *reply;
94
95 if (!info)
96 return -EINVAL;
97
98 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
99 if (!msg)
100 return -ENOMEM;
101
102 /* Construct send-back message header */
103 reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,
104 &mt753x_nl_family, 0, cmd);
105 if (!reply) {
106 nlmsg_free(msg);
107 return -EINVAL;
108 }
109
110 *skbp = msg;
111 return 0;
112}
113
114static int mt753x_nl_send_reply(struct sk_buff *skb, struct genl_info *info)
115{
116 struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
117 void *reply = genlmsg_data(genlhdr);
118
119 /* Finalize a generic netlink message (update message header) */
120 genlmsg_end(skb, reply);
121
122 /* reply to a request */
123 return genlmsg_reply(skb, info);
124}
125
126static s32 mt753x_nl_get_s32(struct genl_info *info, enum mt753x_attr attr,
127 s32 defval)
128{
129 struct nlattr *na;
130
131 na = info->attrs[attr];
132 if (na)
133 return nla_get_s32(na);
134
135 return defval;
136}
137
138static int mt753x_nl_get_u32(struct genl_info *info, enum mt753x_attr attr,
139 u32 *val)
140{
141 struct nlattr *na;
142
143 na = info->attrs[attr];
144 if (na) {
145 *val = nla_get_u32(na);
146 return 0;
147 }
148
149 return -1;
150}
151
152static struct gsw_mt753x *mt753x_nl_parse_find_gsw(struct genl_info *info)
153{
154 struct gsw_mt753x *gsw;
155 struct nlattr *na;
156 int gsw_id;
157
158 na = info->attrs[MT753X_ATTR_TYPE_DEV_ID];
159 if (na) {
160 gsw_id = nla_get_s32(na);
161 if (gsw_id >= 0)
162 gsw = mt753x_get_gsw(gsw_id);
163 else
164 gsw = mt753x_get_first_gsw();
165 } else {
166 gsw = mt753x_get_first_gsw();
167 }
168
169 return gsw;
170}
171
172static int mt753x_nl_get_swdevs(struct genl_info *info, struct gsw_mt753x *gsw)
173{
174 struct sk_buff *rep_skb = NULL;
175 char dev_info[512];
176 int ret;
177
178 ret = mt753x_nl_list_devs(dev_info, sizeof(dev_info));
179 if (!ret) {
180 pr_info("No switch registered\n");
181 return -EINVAL;
182 }
183
184 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_REPLY, &rep_skb);
185 if (ret < 0)
186 goto err;
187
188 ret = nla_put_string(rep_skb, MT753X_ATTR_TYPE_MESG, dev_info);
189 if (ret < 0)
190 goto err;
191
192 return mt753x_nl_send_reply(rep_skb, info);
193
194err:
195 if (rep_skb)
196 nlmsg_free(rep_skb);
197
198 return ret;
199}
200
201static int mt753x_nl_reply_read(struct genl_info *info, struct gsw_mt753x *gsw)
202{
203 struct sk_buff *rep_skb = NULL;
204 s32 phy, devad, reg;
205 int value;
206 int ret = 0;
207
208 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
209 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
210 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
211
212 if (reg < 0)
213 goto err;
214
215 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_READ, &rep_skb);
216 if (ret < 0)
217 goto err;
218
219 if (phy >= 0) {
220 if (devad < 0)
221 value = gsw->mii_read(gsw, phy, reg);
222 else
223 value = gsw->mmd_read(gsw, phy, devad, reg);
224 } else {
225 value = mt753x_reg_read(gsw, reg);
226 }
227
228 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
229 if (ret < 0)
230 goto err;
231
232 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
233 if (ret < 0)
234 goto err;
235
236 return mt753x_nl_send_reply(rep_skb, info);
237
238err:
239 if (rep_skb)
240 nlmsg_free(rep_skb);
241
242 return ret;
243}
244
245static int mt753x_nl_reply_write(struct genl_info *info, struct gsw_mt753x *gsw)
246{
247 struct sk_buff *rep_skb = NULL;
248 s32 phy, devad, reg;
249 u32 value;
250 int ret = 0;
251
252 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
253 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
254 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
255
256 if (mt753x_nl_get_u32(info, MT753X_ATTR_TYPE_VAL, &value))
257 goto err;
258
259 if (reg < 0)
260 goto err;
261
262 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_WRITE, &rep_skb);
263 if (ret < 0)
264 goto err;
265
266 if (phy >= 0) {
267 if (devad < 0)
268 gsw->mii_write(gsw, phy, reg, value);
269 else
270 gsw->mmd_write(gsw, phy, devad, reg, value);
271 } else {
272 mt753x_reg_write(gsw, reg, value);
273 }
274
275 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
276 if (ret < 0)
277 goto err;
278
279 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
280 if (ret < 0)
281 goto err;
282
283 return mt753x_nl_send_reply(rep_skb, info);
284
285err:
286 if (rep_skb)
287 nlmsg_free(rep_skb);
288
289 return ret;
290}
291
292static const enum mt753x_attr mt753x_nl_cmd_read_attrs[] = {
293 MT753X_ATTR_TYPE_REG
294};
295
296static const enum mt753x_attr mt753x_nl_cmd_write_attrs[] = {
297 MT753X_ATTR_TYPE_REG,
298 MT753X_ATTR_TYPE_VAL
299};
300
301static const struct mt753x_nl_cmd_item mt753x_nl_cmds[] = {
302 {
303 .cmd = MT753X_CMD_REQUEST,
304 .require_dev = false,
305 .process = mt753x_nl_get_swdevs
306 }, {
307 .cmd = MT753X_CMD_READ,
308 .require_dev = true,
309 .process = mt753x_nl_reply_read,
310 .required_attrs = mt753x_nl_cmd_read_attrs,
311 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_read_attrs),
312 }, {
313 .cmd = MT753X_CMD_WRITE,
314 .require_dev = true,
315 .process = mt753x_nl_reply_write,
316 .required_attrs = mt753x_nl_cmd_write_attrs,
317 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_write_attrs),
318 }
319};
320
321static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info)
322{
323 struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
324 const struct mt753x_nl_cmd_item *cmditem = NULL;
325 struct gsw_mt753x *gsw = NULL;
326 u32 sat_req_attrs = 0;
327 int i, ret;
328
329 for (i = 0; i < ARRAY_SIZE(mt753x_nl_cmds); i++) {
330 if (hdr->cmd == mt753x_nl_cmds[i].cmd) {
331 cmditem = &mt753x_nl_cmds[i];
332 break;
333 }
334 }
335
336 if (!cmditem) {
337 pr_info("mt753x-nl: unknown cmd %u\n", hdr->cmd);
338 return -EINVAL;
339 }
340
341 for (i = 0; i < cmditem->nr_required_attrs; i++) {
342 if (info->attrs[cmditem->required_attrs[i]])
343 sat_req_attrs++;
344 }
345
346 if (sat_req_attrs != cmditem->nr_required_attrs) {
347 pr_info("mt753x-nl: missing required attr(s) for cmd %u\n",
348 hdr->cmd);
349 return -EINVAL;
350 }
351
352 if (cmditem->require_dev) {
353 gsw = mt753x_nl_parse_find_gsw(info);
354 if (!gsw) {
355 pr_info("mt753x-nl: failed to find switch dev\n");
356 return -EINVAL;
357 }
358 }
359
360 ret = cmditem->process(info, gsw);
361
362 mt753x_put_gsw();
363
364 return ret;
365}
366
developer5145ebb2022-07-14 15:16:24 +0800367int mt753x_nl_init(void)
developerfd40db22021-04-29 10:08:25 +0800368{
369 int ret;
370
371 ret = genl_register_family(&mt753x_nl_family);
372 if (ret) {
373 pr_info("mt753x-nl: genl_register_family_with_ops failed\n");
374 return ret;
375 }
376
377 return 0;
378}
379
developer5145ebb2022-07-14 15:16:24 +0800380void mt753x_nl_exit(void)
developerfd40db22021-04-29 10:08:25 +0800381{
382 genl_unregister_family(&mt753x_nl_family);
383}