1 // SPDX-License-Identifier: GPL-2.0-only
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
);
27 static const struct nla_policy mt753x_nl_cmd_policy[] = {
28 [MT753X_ATTR_TYPE_MESG] = { .type = NLA_STRING },
29 [MT753X_ATTR_TYPE_PHY] = { .type = NLA_S32 },
30 [MT753X_ATTR_TYPE_REG] = { .type = NLA_S32 },
31 [MT753X_ATTR_TYPE_VAL] = { .type = NLA_S32 },
32 [MT753X_ATTR_TYPE_DEV_NAME] = { .type = NLA_S32 },
33 [MT753X_ATTR_TYPE_DEV_ID] = { .type = NLA_S32 },
34 [MT753X_ATTR_TYPE_DEVAD] = { .type = NLA_S32 },
38 static const struct genl_ops mt753x_nl_ops
[] = {
40 .cmd
= MT753X_CMD_REQUEST
,
41 .doit
= mt753x_nl_response
,
42 // .policy = mt753x_nl_cmd_policy,
43 .flags
= GENL_ADMIN_PERM
,
45 .cmd
= MT753X_CMD_READ
,
46 .doit
= mt753x_nl_response
,
47 // .policy = mt753x_nl_cmd_policy,
48 .flags
= GENL_ADMIN_PERM
,
50 .cmd
= MT753X_CMD_WRITE
,
51 .doit
= mt753x_nl_response
,
52 // .policy = mt753x_nl_cmd_policy,
53 .flags
= GENL_ADMIN_PERM
,
57 static struct genl_family mt753x_nl_family
= {
58 .name
= MT753X_GENL_NAME
,
59 .version
= MT753X_GENL_VERSION
,
60 .maxattr
= MT753X_NR_ATTR_TYPE
,
62 .n_ops
= ARRAY_SIZE(mt753x_nl_ops
),
65 static int mt753x_nl_list_devs(char *buff
, int size
)
67 struct gsw_mt753x
*gsw
;
71 memset(buff
, 0, size
);
75 list_for_each_entry(gsw
, &mt753x_devs
, list
) {
76 len
= snprintf(buf
, sizeof(buf
),
77 "id: %d, model: %s, node: %s\n",
78 gsw
->id
, gsw
->name
, gsw
->dev
->of_node
->name
);
79 strncat(buff
, buf
, size
- total
);
88 static int mt753x_nl_prepare_reply(struct genl_info
*info
, u8 cmd
,
89 struct sk_buff
**skbp
)
97 msg
= genlmsg_new(NLMSG_GOODSIZE
, GFP_KERNEL
);
101 /* Construct send-back message header */
102 reply
= genlmsg_put(msg
, info
->snd_portid
, info
->snd_seq
,
103 &mt753x_nl_family
, 0, cmd
);
113 static int mt753x_nl_send_reply(struct sk_buff
*skb
, struct genl_info
*info
)
115 struct genlmsghdr
*genlhdr
= nlmsg_data(nlmsg_hdr(skb
));
116 void *reply
= genlmsg_data(genlhdr
);
118 /* Finalize a generic netlink message (update message header) */
119 genlmsg_end(skb
, reply
);
121 /* reply to a request */
122 return genlmsg_reply(skb
, info
);
125 static s32
mt753x_nl_get_s32(struct genl_info
*info
, enum mt753x_attr attr
,
130 na
= info
->attrs
[attr
];
132 return nla_get_s32(na
);
137 static int mt753x_nl_get_u32(struct genl_info
*info
, enum mt753x_attr attr
,
142 na
= info
->attrs
[attr
];
144 *val
= nla_get_u32(na
);
151 static struct gsw_mt753x
*mt753x_nl_parse_find_gsw(struct genl_info
*info
)
153 struct gsw_mt753x
*gsw
;
157 na
= info
->attrs
[MT753X_ATTR_TYPE_DEV_ID
];
159 gsw_id
= nla_get_s32(na
);
161 gsw
= mt753x_get_gsw(gsw_id
);
163 gsw
= mt753x_get_first_gsw();
165 gsw
= mt753x_get_first_gsw();
171 static int mt753x_nl_get_swdevs(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
173 struct sk_buff
*rep_skb
= NULL
;
177 ret
= mt753x_nl_list_devs(dev_info
, sizeof(dev_info
));
179 pr_info("No switch registered\n");
183 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_REPLY
, &rep_skb
);
187 ret
= nla_put_string(rep_skb
, MT753X_ATTR_TYPE_MESG
, dev_info
);
191 return mt753x_nl_send_reply(rep_skb
, info
);
200 static int mt753x_nl_reply_read(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
202 struct sk_buff
*rep_skb
= NULL
;
207 phy
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_PHY
, -1);
208 devad
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_DEVAD
, -1);
209 reg
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_REG
, -1);
214 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_READ
, &rep_skb
);
220 value
= gsw
->mii_read(gsw
, phy
, reg
);
222 value
= gsw
->mmd_read(gsw
, phy
, devad
, reg
);
224 value
= mt753x_reg_read(gsw
, reg
);
227 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_REG
, reg
);
231 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_VAL
, value
);
235 return mt753x_nl_send_reply(rep_skb
, info
);
244 static int mt753x_nl_reply_write(struct genl_info
*info
, struct gsw_mt753x
*gsw
)
246 struct sk_buff
*rep_skb
= NULL
;
251 phy
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_PHY
, -1);
252 devad
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_DEVAD
, -1);
253 reg
= mt753x_nl_get_s32(info
, MT753X_ATTR_TYPE_REG
, -1);
255 if (mt753x_nl_get_u32(info
, MT753X_ATTR_TYPE_VAL
, &value
))
261 ret
= mt753x_nl_prepare_reply(info
, MT753X_CMD_WRITE
, &rep_skb
);
267 gsw
->mii_write(gsw
, phy
, reg
, value
);
269 gsw
->mmd_write(gsw
, phy
, devad
, reg
, value
);
271 mt753x_reg_write(gsw
, reg
, value
);
274 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_REG
, reg
);
278 ret
= nla_put_s32(rep_skb
, MT753X_ATTR_TYPE_VAL
, value
);
282 return mt753x_nl_send_reply(rep_skb
, info
);
291 static const enum mt753x_attr mt753x_nl_cmd_read_attrs
[] = {
295 static const enum mt753x_attr mt753x_nl_cmd_write_attrs
[] = {
296 MT753X_ATTR_TYPE_REG
,
300 static const struct mt753x_nl_cmd_item mt753x_nl_cmds
[] = {
302 .cmd
= MT753X_CMD_REQUEST
,
303 .require_dev
= false,
304 .process
= mt753x_nl_get_swdevs
306 .cmd
= MT753X_CMD_READ
,
308 .process
= mt753x_nl_reply_read
,
309 .required_attrs
= mt753x_nl_cmd_read_attrs
,
310 .nr_required_attrs
= ARRAY_SIZE(mt753x_nl_cmd_read_attrs
),
312 .cmd
= MT753X_CMD_WRITE
,
314 .process
= mt753x_nl_reply_write
,
315 .required_attrs
= mt753x_nl_cmd_write_attrs
,
316 .nr_required_attrs
= ARRAY_SIZE(mt753x_nl_cmd_write_attrs
),
320 static int mt753x_nl_response(struct sk_buff
*skb
, struct genl_info
*info
)
322 struct genlmsghdr
*hdr
= nlmsg_data(info
->nlhdr
);
323 const struct mt753x_nl_cmd_item
*cmditem
= NULL
;
324 struct gsw_mt753x
*gsw
= NULL
;
325 u32 sat_req_attrs
= 0;
328 for (i
= 0; i
< ARRAY_SIZE(mt753x_nl_cmds
); i
++) {
329 if (hdr
->cmd
== mt753x_nl_cmds
[i
].cmd
) {
330 cmditem
= &mt753x_nl_cmds
[i
];
336 pr_info("mt753x-nl: unknown cmd %u\n", hdr
->cmd
);
340 for (i
= 0; i
< cmditem
->nr_required_attrs
; i
++) {
341 if (info
->attrs
[cmditem
->required_attrs
[i
]])
345 if (sat_req_attrs
!= cmditem
->nr_required_attrs
) {
346 pr_info("mt753x-nl: missing required attr(s) for cmd %u\n",
351 if (cmditem
->require_dev
) {
352 gsw
= mt753x_nl_parse_find_gsw(info
);
354 pr_info("mt753x-nl: failed to find switch dev\n");
359 ret
= cmditem
->process(info
, gsw
);
366 int __init
mt753x_nl_init(void)
370 ret
= genl_register_family(&mt753x_nl_family
);
372 pr_info("mt753x-nl: genl_register_family_with_ops failed\n");
379 void __exit
mt753x_nl_exit(void)
381 genl_unregister_family(&mt753x_nl_family
);