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
);
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
);