1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (c) 2018 MediaTek Inc.
4 * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>
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>
14 #include "mt753x_nl.h"
16 struct mt753x_nl_cmd_item {
19 int (*process)(struct genl_info *info, struct gsw_mt753x *gsw);
20 u32 nr_required_attrs;
21 const enum mt753x_attr *required_attrs;
24 static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info);
26 static 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 },
36 static const struct genl_ops mt753x_nl_ops[] = {
38 .cmd = MT753X_CMD_REQUEST,
39 .doit = mt753x_nl_response,
40 .policy = mt753x_nl_cmd_policy,
41 .flags = GENL_ADMIN_PERM,
43 .cmd = MT753X_CMD_READ,
44 .doit = mt753x_nl_response,
45 .policy = mt753x_nl_cmd_policy,
46 .flags = GENL_ADMIN_PERM,
48 .cmd = MT753X_CMD_WRITE,
49 .doit = mt753x_nl_response,
50 .policy = mt753x_nl_cmd_policy,
51 .flags = GENL_ADMIN_PERM,
55 static struct genl_family mt753x_nl_family = {
56 .name = MT753X_GENL_NAME,
57 .version = MT753X_GENL_VERSION,
58 .maxattr = MT753X_NR_ATTR_TYPE,
60 .n_ops = ARRAY_SIZE(mt753x_nl_ops),
63 static int mt753x_nl_list_devs(char *buff, int size)
65 struct gsw_mt753x *gsw;
69 memset(buff, 0, size);
73 list_for_each_entry(gsw, &mt753x_devs, list) {
74 len = snprintf(buf, sizeof(buf),
75 "id: %d, model: %s, node: %s\n",
76 gsw->id, gsw->name, gsw->dev->of_node->name);
77 strncat(buff, buf, size - total);
86 static int mt753x_nl_prepare_reply(struct genl_info *info, u8 cmd,
87 struct sk_buff **skbp)
95 msg = genlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
99 /* Construct send-back message header */
100 reply = genlmsg_put(msg, info->snd_portid, info->snd_seq,
101 &mt753x_nl_family, 0, cmd);
111 static int mt753x_nl_send_reply(struct sk_buff *skb, struct genl_info *info)
113 struct genlmsghdr *genlhdr = nlmsg_data(nlmsg_hdr(skb));
114 void *reply = genlmsg_data(genlhdr);
116 /* Finalize a generic netlink message (update message header) */
117 genlmsg_end(skb, reply);
119 /* reply to a request */
120 return genlmsg_reply(skb, info);
123 static s32 mt753x_nl_get_s32(struct genl_info *info, enum mt753x_attr attr,
128 na = info->attrs[attr];
130 return nla_get_s32(na);
135 static int mt753x_nl_get_u32(struct genl_info *info, enum mt753x_attr attr,
140 na = info->attrs[attr];
142 *val = nla_get_u32(na);
149 static struct gsw_mt753x *mt753x_nl_parse_find_gsw(struct genl_info *info)
151 struct gsw_mt753x *gsw;
155 na = info->attrs[MT753X_ATTR_TYPE_DEV_ID];
157 gsw_id = nla_get_s32(na);
159 gsw = mt753x_get_gsw(gsw_id);
161 gsw = mt753x_get_first_gsw();
163 gsw = mt753x_get_first_gsw();
169 static int mt753x_nl_get_swdevs(struct genl_info *info, struct gsw_mt753x *gsw)
171 struct sk_buff *rep_skb = NULL;
175 ret = mt753x_nl_list_devs(dev_info, sizeof(dev_info));
177 pr_info("No switch registered\n");
181 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_REPLY, &rep_skb);
185 ret = nla_put_string(rep_skb, MT753X_ATTR_TYPE_MESG, dev_info);
189 return mt753x_nl_send_reply(rep_skb, info);
198 static int mt753x_nl_reply_read(struct genl_info *info, struct gsw_mt753x *gsw)
200 struct sk_buff *rep_skb = NULL;
205 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
206 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
207 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
212 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_READ, &rep_skb);
218 value = gsw->mii_read(gsw, phy, reg);
220 value = gsw->mmd_read(gsw, phy, devad, reg);
222 value = mt753x_reg_read(gsw, reg);
225 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
229 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
233 return mt753x_nl_send_reply(rep_skb, info);
242 static int mt753x_nl_reply_write(struct genl_info *info, struct gsw_mt753x *gsw)
244 struct sk_buff *rep_skb = NULL;
249 phy = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_PHY, -1);
250 devad = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_DEVAD, -1);
251 reg = mt753x_nl_get_s32(info, MT753X_ATTR_TYPE_REG, -1);
253 if (mt753x_nl_get_u32(info, MT753X_ATTR_TYPE_VAL, &value))
259 ret = mt753x_nl_prepare_reply(info, MT753X_CMD_WRITE, &rep_skb);
265 gsw->mii_write(gsw, phy, reg, value);
267 gsw->mmd_write(gsw, phy, devad, reg, value);
269 mt753x_reg_write(gsw, reg, value);
272 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_REG, reg);
276 ret = nla_put_s32(rep_skb, MT753X_ATTR_TYPE_VAL, value);
280 return mt753x_nl_send_reply(rep_skb, info);
289 static const enum mt753x_attr mt753x_nl_cmd_read_attrs[] = {
293 static const enum mt753x_attr mt753x_nl_cmd_write_attrs[] = {
294 MT753X_ATTR_TYPE_REG,
298 static const struct mt753x_nl_cmd_item mt753x_nl_cmds[] = {
300 .cmd = MT753X_CMD_REQUEST,
301 .require_dev = false,
302 .process = mt753x_nl_get_swdevs
304 .cmd = MT753X_CMD_READ,
306 .process = mt753x_nl_reply_read,
307 .required_attrs = mt753x_nl_cmd_read_attrs,
308 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_read_attrs),
310 .cmd = MT753X_CMD_WRITE,
312 .process = mt753x_nl_reply_write,
313 .required_attrs = mt753x_nl_cmd_write_attrs,
314 .nr_required_attrs = ARRAY_SIZE(mt753x_nl_cmd_write_attrs),
318 static int mt753x_nl_response(struct sk_buff *skb, struct genl_info *info)
320 struct genlmsghdr *hdr = nlmsg_data(info->nlhdr);
321 const struct mt753x_nl_cmd_item *cmditem = NULL;
322 struct gsw_mt753x *gsw = NULL;
323 u32 sat_req_attrs = 0;
326 for (i = 0; i < ARRAY_SIZE(mt753x_nl_cmds); i++) {
327 if (hdr->cmd == mt753x_nl_cmds[i].cmd) {
328 cmditem = &mt753x_nl_cmds[i];
334 pr_info("mt753x-nl: unknown cmd %u\n", hdr->cmd);
338 for (i = 0; i < cmditem->nr_required_attrs; i++) {
339 if (info->attrs[cmditem->required_attrs[i]])
343 if (sat_req_attrs != cmditem->nr_required_attrs) {
344 pr_info("mt753x-nl: missing required attr(s) for cmd %u\n",
349 if (cmditem->require_dev) {
350 gsw = mt753x_nl_parse_find_gsw(info);
352 pr_info("mt753x-nl: failed to find switch dev\n");
357 ret = cmditem->process(info, gsw);
364 int __init mt753x_nl_init(void)
368 ret = genl_register_family(&mt753x_nl_family);
370 pr_info("mt753x-nl: genl_register_family_with_ops failed\n");
377 void __exit mt753x_nl_exit(void)
379 genl_unregister_family(&mt753x_nl_family);