blob: 397ebe550cfe6fdd2eeda11ba6843250826d1875 [file] [log] [blame]
developerfd40db22021-04-29 10:08:25 +08001/*
2 * switch_netlink.c: switch(netlink) set API
3 *
4 * Author: Sirui Zhao <Sirui.Zhao@mediatek.com>
5 */
6#include <stdio.h>
7#include <string.h>
8#include <stdlib.h>
9#include <errno.h>
10#include <sys/types.h>
11#include <sys/socket.h>
12#include <netlink/netlink.h>
13#include <netlink/genl/genl.h>
14#include <netlink/genl/family.h>
15#include <netlink/genl/ctrl.h>
16
17#include "switch_netlink.h"
18
19static struct nl_sock *user_sock;
20static struct nl_cache *cache;
21static struct genl_family *family;
22static struct nlattr *attrs[MT753X_ATTR_TYPE_MAX + 1];
23
24static int wait_handler(struct nl_msg *msg, void *arg)
25{
26 int *finished = arg;
27
28 *finished = 1;
29 return NL_STOP;
30}
31
32static int list_swdevs(struct nl_msg *msg, void *arg)
33{
34 struct mt753x_attr *val = arg;
35 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
36
37 if (nla_parse(attrs, MT753X_ATTR_TYPE_MAX, genlmsg_attrdata(gnlh, 0),
38 genlmsg_attrlen(gnlh, 0), NULL) < 0)
39 goto done;
40
41 if (gnlh->cmd == MT753X_CMD_REPLY) {
42 if (attrs[MT753X_ATTR_TYPE_MESG]) {
43 val->dev_info =
developerbe40a9e2024-03-07 21:44:26 +080044 nla_get_string(attrs[MT753X_ATTR_TYPE_MESG]);
developerfd40db22021-04-29 10:08:25 +080045 printf("register switch dev:\n%s", val->dev_info);
developerbe40a9e2024-03-07 21:44:26 +080046 } else {
developerfd40db22021-04-29 10:08:25 +080047 fprintf(stderr, "ERROR:No switch dev now\n");
48 goto done;
49 }
50 } else
51 goto done;
52 return 0;
53done:
54 return NL_SKIP;
55}
56
57static int construct_attrs(struct nl_msg *msg, void *arg)
58{
59 struct mt753x_attr *val = arg;
60 int type = val->type;
61
62 if (val->dev_id > -1)
63 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_DEV_ID, val->dev_id);
64
65 if (val->op == 'r') {
66 if (val->phy_dev != -1)
developerbe40a9e2024-03-07 21:44:26 +080067 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_PHY_DEV,
68 val->phy_dev);
developerfd40db22021-04-29 10:08:25 +080069 if (val->port_num >= 0)
70 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_PHY, val->port_num);
71 NLA_PUT_U32(msg, type, val->reg);
72 } else if (val->op == 'w') {
73 if (val->phy_dev != -1)
developerbe40a9e2024-03-07 21:44:26 +080074 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_PHY_DEV,
75 val->phy_dev);
developerfd40db22021-04-29 10:08:25 +080076 if (val->port_num >= 0)
77 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_PHY, val->port_num);
78 NLA_PUT_U32(msg, type, val->reg);
79 NLA_PUT_U32(msg, MT753X_ATTR_TYPE_VAL, val->value);
80 } else {
81 printf("construct_attrs_message\n");
82 NLA_PUT_STRING(msg, type, "hello");
83 }
84 return 0;
85
86nla_put_failure:
87 return -1;
88}
89
90static int spilt_attrs(struct nl_msg *msg, void *arg)
91{
92 struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
93 struct mt753x_attr *val = arg;
94 char *str;
95
96 if (nla_parse(attrs, MT753X_ATTR_TYPE_MAX, genlmsg_attrdata(gnlh, 0),
97 genlmsg_attrlen(gnlh, 0), NULL) < 0)
98 goto done;
99
100 if ((gnlh->cmd == MT753X_CMD_WRITE) || (gnlh->cmd == MT753X_CMD_READ)) {
101 if (attrs[MT753X_ATTR_TYPE_MESG]) {
102 str = nla_get_string(attrs[MT753X_ATTR_TYPE_MESG]);
103 printf(" %s\n", str);
104 if (!strncmp(str, "No", 2))
105 goto done;
106 }
107 if (attrs[MT753X_ATTR_TYPE_REG]) {
developerbe40a9e2024-03-07 21:44:26 +0800108 val->reg = nla_get_u32(attrs[MT753X_ATTR_TYPE_REG]);
developerfd40db22021-04-29 10:08:25 +0800109 }
110 if (attrs[MT753X_ATTR_TYPE_VAL]) {
developerbe40a9e2024-03-07 21:44:26 +0800111 val->value = nla_get_u32(attrs[MT753X_ATTR_TYPE_VAL]);
developerfd40db22021-04-29 10:08:25 +0800112 }
developerbe40a9e2024-03-07 21:44:26 +0800113 } else
developerfd40db22021-04-29 10:08:25 +0800114 goto done;
115
116 return 0;
117done:
118 return NL_SKIP;
119}
120
developerbe40a9e2024-03-07 21:44:26 +0800121static int mt753x_request_callback(int cmd,
122 int (*spilt)(struct nl_msg *, void *),
developerfd40db22021-04-29 10:08:25 +0800123 int (*construct)(struct nl_msg *, void *),
124 void *arg)
125{
126 struct nl_msg *msg;
127 struct nl_cb *callback = NULL;
128 int finished;
129 int flags = 0;
130 int err;
131
developerbe40a9e2024-03-07 21:44:26 +0800132 /* Allocate an netllink message buffer */
developerfd40db22021-04-29 10:08:25 +0800133 msg = nlmsg_alloc();
134 if (!msg) {
135 fprintf(stderr, "Failed to allocate netlink message\n");
136 exit(1);
137 }
138 if (!construct) {
139 if (cmd == MT753X_CMD_REQUEST)
140 flags |= NLM_F_REQUEST;
141 else
142 flags |= NLM_F_DUMP;
143 }
144 genlmsg_put(msg, NL_AUTO_PID, NL_AUTO_SEQ, genl_family_get_id(family),
145 0, flags, cmd, 0);
146
developerbe40a9e2024-03-07 21:44:26 +0800147 /* Fill attaribute of netlink message by construct function */
developerfd40db22021-04-29 10:08:25 +0800148 if (construct) {
149 err = construct(msg, arg);
150 if (err < 0) {
151 fprintf(stderr, "attributes error\n");
152 goto nal_put_failure;
153 }
154 }
155
developerbe40a9e2024-03-07 21:44:26 +0800156 /* Allocate an new callback handler */
developerfd40db22021-04-29 10:08:25 +0800157 callback = nl_cb_alloc(NL_CB_CUSTOM);
158 if (!callback) {
159 fprintf(stderr, "Failed to allocate callback handler\n");
160 exit(1);
161 }
162
developerbe40a9e2024-03-07 21:44:26 +0800163 /* Send netlink message */
developerfd40db22021-04-29 10:08:25 +0800164 err = nl_send_auto_complete(user_sock, msg);
165 if (err < 0) {
166 fprintf(stderr, "nl_send_auto_complete failied:%d\n", err);
167 goto out;
168 }
169 finished = 0;
170 if (spilt)
171 nl_cb_set(callback, NL_CB_VALID, NL_CB_CUSTOM, spilt, arg);
172
173 if (construct)
174 nl_cb_set(callback, NL_CB_ACK, NL_CB_CUSTOM, wait_handler,
175 &finished);
176 else
177 nl_cb_set(callback, NL_CB_FINISH, NL_CB_CUSTOM, wait_handler,
178 &finished);
179
developerbe40a9e2024-03-07 21:44:26 +0800180 /* Receive message from kernel request */
developerfd40db22021-04-29 10:08:25 +0800181 err = nl_recvmsgs(user_sock, callback);
182 if (err < 0)
183 goto out;
184
developerbe40a9e2024-03-07 21:44:26 +0800185 /* Wait until an ACK is received for the latest not yet acknowledge */
developerfd40db22021-04-29 10:08:25 +0800186 if (!finished)
187 err = nl_wait_for_ack(user_sock);
188out:
189 if (callback)
190 nl_cb_put(callback);
191
192nal_put_failure:
193 nlmsg_free(msg);
194 return err;
195}
196
197void mt753x_netlink_free(void)
198{
199 if (family)
200 nl_object_put((struct nl_object *)family);
201 if (cache)
202 nl_cache_free(cache);
203 if (user_sock)
204 nl_socket_free(user_sock);
205 user_sock = NULL;
206 cache = NULL;
207 family = NULL;
208}
209
210int mt753x_netlink_init(const char *name)
211{
212 int ret;
213
214 user_sock = NULL;
215 cache = NULL;
216 family = NULL;
217
developerbe40a9e2024-03-07 21:44:26 +0800218 /* Allocate an new netlink socket */
developerfd40db22021-04-29 10:08:25 +0800219 user_sock = nl_socket_alloc();
220 if (!user_sock) {
221 fprintf(stderr, "Failed to create user socket\n");
222 goto err;
223 }
developerbe40a9e2024-03-07 21:44:26 +0800224 /* Connetct the genl controller */
developerfd40db22021-04-29 10:08:25 +0800225 if (genl_connect(user_sock)) {
226 fprintf(stderr, "Failed to connetct to generic netlink\n");
227 goto err;
228 }
developerbe40a9e2024-03-07 21:44:26 +0800229 /* Allocate an new nl_cache */
developerfd40db22021-04-29 10:08:25 +0800230 ret = genl_ctrl_alloc_cache(user_sock, &cache);
231 if (ret < 0) {
232 fprintf(stderr, "Failed to allocate netlink cache\n");
233 goto err;
234 }
235
236 if (name == NULL)
237 return -EINVAL;
238
developerbe40a9e2024-03-07 21:44:26 +0800239 /* Look up generic netlik family by "mt753x" in the provided cache */
developerfd40db22021-04-29 10:08:25 +0800240 family = genl_ctrl_search_by_name(cache, name);
241 if (!family) {
developerbe40a9e2024-03-07 21:44:26 +0800242 /* printf("switch(mt753x) API not be prepared\n"); */
developerfd40db22021-04-29 10:08:25 +0800243 goto err;
244 }
245 return 0;
246err:
247 mt753x_netlink_free();
248 return -EINVAL;
249}
250
251void mt753x_list_swdev(struct mt753x_attr *arg, int cmd)
252{
253 int err;
254
255 err = mt753x_request_callback(cmd, list_swdevs, NULL, arg);
256 if (err < 0)
257 fprintf(stderr, "mt753x list dev error\n");
258}
259
260static int mt753x_request(struct mt753x_attr *arg, int cmd)
261{
262 int err;
263
264 err = mt753x_request_callback(cmd, spilt_attrs, construct_attrs, arg);
265 if (err < 0) {
266 fprintf(stderr, "mt753x deal request error\n");
267 return err;
268 }
269 return 0;
270}
271
272static int phy_operate_netlink(char op, struct mt753x_attr *arg,
273 unsigned int port_num, unsigned int phy_dev,
274 unsigned int offset, unsigned int *value)
275{
276 int ret = 0;
277 struct mt753x_attr *attr = arg;
278
279 attr->port_num = port_num;
280 attr->phy_dev = phy_dev;
281 attr->reg = offset;
282 attr->value = -1;
283 attr->type = MT753X_ATTR_TYPE_REG;
284
developerbe40a9e2024-03-07 21:44:26 +0800285 switch (op) {
286 case 'r':
287 attr->op = 'r';
288 ret = mt753x_request(attr, MT753X_CMD_READ);
289 *value = attr->value;
290 break;
291 case 'w':
292 attr->op = 'w';
293 attr->value = *value;
294 ret = mt753x_request(attr, MT753X_CMD_WRITE);
295 break;
296 default:
297 break;
developerfd40db22021-04-29 10:08:25 +0800298 }
299
300 return ret;
301}
302
303int reg_read_netlink(struct mt753x_attr *arg, unsigned int offset,
304 unsigned int *value)
305{
306 int ret;
307
308 ret = phy_operate_netlink('r', arg, -1, -1, offset, value);
309 return ret;
310}
311
312int reg_write_netlink(struct mt753x_attr *arg, unsigned int offset,
313 unsigned int value)
314{
315 int ret;
316
317 ret = phy_operate_netlink('w', arg, -1, -1, offset, &value);
318 return ret;
319}
320
321int phy_cl22_read_netlink(struct mt753x_attr *arg, unsigned int port_num,
322 unsigned int phy_addr, unsigned int *value)
323{
324 int ret;
325
326 ret = phy_operate_netlink('r', arg, port_num, -1, phy_addr, value);
327 return ret;
328}
329
330int phy_cl22_write_netlink(struct mt753x_attr *arg, unsigned int port_num,
331 unsigned int phy_addr, unsigned int value)
332{
333 int ret;
334
335 ret = phy_operate_netlink('w', arg, port_num, -1, phy_addr, &value);
336 return ret;
337}
338
339int phy_cl45_read_netlink(struct mt753x_attr *arg, unsigned int port_num,
340 unsigned int phy_dev, unsigned int phy_addr,
341 unsigned int *value)
342{
343 int ret;
344
345 ret = phy_operate_netlink('r', arg, port_num, phy_dev, phy_addr, value);
346 return ret;
347}
348
349int phy_cl45_write_netlink(struct mt753x_attr *arg, unsigned int port_num,
350 unsigned int phy_dev, unsigned int phy_addr,
351 unsigned int value)
352{
353 int ret;
354
developerbe40a9e2024-03-07 21:44:26 +0800355 ret =
356 phy_operate_netlink('w', arg, port_num, phy_dev, phy_addr, &value);
developerfd40db22021-04-29 10:08:25 +0800357 return ret;
358}
359
360void dump_extend_phy_reg(struct mt753x_attr *arg, int port_no, int from,
developerbe40a9e2024-03-07 21:44:26 +0800361 int to, int is_local, int page_no)
developerfd40db22021-04-29 10:08:25 +0800362{
developerbe40a9e2024-03-07 21:44:26 +0800363 unsigned int temp = 0;
364 int r31 = 0;
365 int i = 0;
developerfd40db22021-04-29 10:08:25 +0800366
developerbe40a9e2024-03-07 21:44:26 +0800367 if (is_local == 0) {
368 printf("\n\nGlobal Register Page %d\n", page_no);
369 printf("===============");
370 r31 |= 0 << 15; /* global */
371 r31 |= ((page_no & 0x7) << 12); /* page no */
372 phy_cl22_write_netlink(arg, port_no, 31, r31); /* select global page x */
373 for (i = 16; i < 32; i++) {
374 if (i % 8 == 0)
375 printf("\n");
376 phy_cl22_read_netlink(arg, port_no, i, &temp);
377 printf("%02d: %04X ", i, temp);
378 }
379 } else {
380 printf("\n\nLocal Register Port %d Page %d\n", port_no,
381 page_no);
382 printf("===============");
383 r31 |= 1 << 15; /* Local */
384 r31 |= ((page_no & 0x7) << 12); /* Page no */
385 phy_cl22_write_netlink(arg, port_no, 31, r31); /* Select global page x */
386 for (i = 16; i < 32; i++) {
387 if (i % 8 == 0)
388 printf("\n");
389 phy_cl22_read_netlink(arg, port_no, i, &temp);
390 printf("%02d: %04X ", i, temp);
391 }
392 }
393 printf("\n");
developerfd40db22021-04-29 10:08:25 +0800394}
395
396int phy_dump_netlink(struct mt753x_attr *arg, int phy_addr)
397{
398 int i;
399 int ret;
developerbe40a9e2024-03-07 21:44:26 +0800400 unsigned int offset, value, phy_base = 0;
401
402 if (chip_name == 0x8855)
403 phy_base = 6;
developerfd40db22021-04-29 10:08:25 +0800404
405 if (phy_addr == 32) {
developerbe40a9e2024-03-07 21:44:26 +0800406 /* Dump all phy register */
developerfd40db22021-04-29 10:08:25 +0800407 for (i = 0; i < 5; i++) {
408 printf("\n[Port %d]=============", i);
409 for (offset = 0; offset < 16; offset++) {
410 if (offset % 8 == 0)
411 printf("\n");
developerbe40a9e2024-03-07 21:44:26 +0800412 ret =
413 phy_cl22_read_netlink(arg, phy_base + i,
414 offset, &value);
developerfd40db22021-04-29 10:08:25 +0800415 printf("%02d: %04X ", offset, value);
416 }
417 }
418 } else {
419 printf("\n[Port %d]=============", phy_addr);
420 for (offset = 0; offset < 16; offset++) {
421 if (offset % 8 == 0)
422 printf("\n");
developerbe40a9e2024-03-07 21:44:26 +0800423 ret =
424 phy_cl22_read_netlink(arg, phy_addr, offset,
425 &value);
developerfd40db22021-04-29 10:08:25 +0800426 printf("%02d: %04X ", offset, value);
427 }
428 }
429 printf("\n");
developerbe40a9e2024-03-07 21:44:26 +0800430 for (offset = 0; offset < 5; offset++) { /* Global register page 0~4 */
431 if (phy_addr == 32) /* Dump all phy register */
developerfd40db22021-04-29 10:08:25 +0800432 dump_extend_phy_reg(arg, 0, 16, 31, 0, offset);
433 else
434 dump_extend_phy_reg(arg, phy_addr, 16, 31, 0, offset);
435 }
436
developerbe40a9e2024-03-07 21:44:26 +0800437 if (phy_addr == 32) { /* Dump all phy register */
438 for (offset = 0; offset < 5; offset++) { /* Local register port 0-port4 */
439 dump_extend_phy_reg(arg, phy_base + offset, 16, 31, 1, 0); /* Dump local page 0 */
440 dump_extend_phy_reg(arg, phy_base + offset, 16, 31, 1, 1); /* Dump local page 1 */
441 dump_extend_phy_reg(arg, phy_base + offset, 16, 31, 1, 2); /* Dump local page 2 */
442 dump_extend_phy_reg(arg, phy_base + offset, 16, 31, 1, 3); /* Dump local page 3 */
developerfd40db22021-04-29 10:08:25 +0800443 }
444 } else {
developerbe40a9e2024-03-07 21:44:26 +0800445 dump_extend_phy_reg(arg, phy_addr, 16, 31, 1, 0); /* Dump local page 0 */
446 dump_extend_phy_reg(arg, phy_addr, 16, 31, 1, 1); /* Dump local page 1 */
447 dump_extend_phy_reg(arg, phy_addr, 16, 31, 1, 2); /* Dump local page 2 */
448 dump_extend_phy_reg(arg, phy_addr, 16, 31, 1, 3); /* Dump local page 3 */
developerfd40db22021-04-29 10:08:25 +0800449 }
450 return ret;
451}