1 /*
2 * plca.c - netlink implementation of plca command
3 *
4 * Implementation of "ethtool --show-plca <dev>" and
5 * "ethtool --set-plca <dev> ..."
6 */
7
8 #include <errno.h>
9 #include <string.h>
10 #include <stdio.h>
11
12 #include "../internal.h"
13 #include "../common.h"
14 #include "netlink.h"
15 #include "bitset.h"
16 #include "parser.h"
17
18 /* PLCA_GET_CFG */
19
plca_get_cfg_reply_cb(const struct nlmsghdr * nlhdr,void * data)20 int plca_get_cfg_reply_cb(const struct nlmsghdr *nlhdr, void *data)
21 {
22 const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
23 DECLARE_ATTR_TB_INFO(tb);
24 struct nl_context *nlctx = data;
25 bool silent;
26 int idv = 255;
27 int err_ret;
28 int val;
29 int ret;
30
31 silent = nlctx->is_dump || nlctx->is_monitor;
32 err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
33 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
34 if (ret < 0)
35 return err_ret;
36
37 nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
38 if (!dev_ok(nlctx))
39 return err_ret;
40
41 if (silent)
42 putchar('\n');
43
44 printf("PLCA settings for %s:\n", nlctx->devname);
45
46 // check if PLCA is enabled
47 printf("\tEnabled: ");
48
49 if (!tb[ETHTOOL_A_PLCA_ENABLED]) {
50 printf("not supported");
51 } else {
52 val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_ENABLED]);
53 printf(val ? "Yes" : "No");
54 }
55 putchar('\n');
56
57 // get node ID
58 printf("\tlocal node ID: ");
59
60 if (!tb[ETHTOOL_A_PLCA_NODE_ID]) {
61 printf("not supported");
62 } else {
63 idv = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_ID]);
64 printf("%u (%s)", idv,
65 idv == 0 ? "coordinator" :
66 idv == 255 ? "unconfigured" : "follower");
67 }
68 putchar('\n');
69
70 // get node count
71 printf("\tNode count: ");
72 if (!tb[ETHTOOL_A_PLCA_NODE_CNT]) {
73 printf("not supported");
74 } else {
75 val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_NODE_CNT]);
76 printf("%u", val);
77
78 // The node count is ignored by follower nodes. However, it can
79 // be pre-set to enable fast coordinator role switchover.
80 // Therefore, on a follower node we still wanto to show it,
81 // indicating it is not currently used.
82 if (tb[ETHTOOL_A_PLCA_NODE_ID] && idv != 0)
83 printf(" (ignored)");
84 }
85 putchar('\n');
86
87 // get TO timer (transmit opportunity timer)
88 printf("\tTO timer: ");
89 if (!tb[ETHTOOL_A_PLCA_TO_TMR]) {
90 printf("not supported");
91 } else {
92 val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_TO_TMR]);
93 printf("%u BT", val);
94 }
95 putchar('\n');
96
97 // get burst count
98 printf("\tBurst count: ");
99 if (!tb[ETHTOOL_A_PLCA_BURST_CNT]) {
100 printf("not supported");
101 } else {
102 val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_CNT]);
103 printf("%u (%s)", val,
104 val > 0 ? "enabled" : "disabled");
105 }
106 putchar('\n');
107
108 // get burst timer
109 printf("\tBurst timer: ");
110 if (!tb[ETHTOOL_A_PLCA_BURST_TMR]) {
111 printf("not supported");
112 } else {
113 val = mnl_attr_get_u32(tb[ETHTOOL_A_PLCA_BURST_TMR]);
114 printf("%u BT", val);
115 }
116 putchar('\n');
117
118 return MNL_CB_OK;
119 }
120
121
nl_plca_get_cfg(struct cmd_context * ctx)122 int nl_plca_get_cfg(struct cmd_context *ctx)
123 {
124 struct nl_context *nlctx = ctx->nlctx;
125 struct nl_socket *nlsk = nlctx->ethnl_socket;
126 int ret;
127
128 if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_GET_CFG, true))
129 return -EOPNOTSUPP;
130
131 if (ctx->argc > 0) {
132 fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
133 *ctx->argp);
134 return 1;
135 }
136
137 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PLCA_GET_CFG,
138 ETHTOOL_A_PLCA_HEADER, 0);
139
140 if (ret < 0)
141 return ret;
142
143 return nlsock_send_get_request(nlsk, plca_get_cfg_reply_cb);
144 }
145
146 /* PLCA_SET_CFG */
147
148 static const struct param_parser set_plca_params[] = {
149 {
150 .arg = "enable",
151 .type = ETHTOOL_A_PLCA_ENABLED,
152 .handler = nl_parse_u8bool,
153 .min_argc = 1,
154 },
155 {
156 .arg = "node-id",
157 .type = ETHTOOL_A_PLCA_NODE_ID,
158 .handler = nl_parse_direct_u32,
159 .min_argc = 1,
160 },
161 {
162 .arg = "node-cnt",
163 .type = ETHTOOL_A_PLCA_NODE_CNT,
164 .handler = nl_parse_direct_u32,
165 .min_argc = 1,
166 },
167 {
168 .arg = "to-tmr",
169 .type = ETHTOOL_A_PLCA_TO_TMR,
170 .handler = nl_parse_direct_u32,
171 .min_argc = 1,
172 },
173 {
174 .arg = "burst-cnt",
175 .type = ETHTOOL_A_PLCA_BURST_CNT,
176 .handler = nl_parse_direct_u32,
177 .min_argc = 1,
178 },
179 {
180 .arg = "burst-tmr",
181 .type = ETHTOOL_A_PLCA_BURST_TMR,
182 .handler = nl_parse_direct_u32,
183 .min_argc = 1,
184 },
185 {}
186 };
187
nl_plca_set_cfg(struct cmd_context * ctx)188 int nl_plca_set_cfg(struct cmd_context *ctx)
189 {
190 struct nl_context *nlctx = ctx->nlctx;
191 struct nl_msg_buff *msgbuff;
192 struct nl_socket *nlsk;
193 int ret;
194
195 if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_SET_CFG, false))
196 return -EOPNOTSUPP;
197 if (!ctx->argc) {
198 fprintf(stderr,
199 "ethtool (--set-plca-cfg): parameters missing\n");
200 return 1;
201 }
202
203 nlctx->cmd = "--set-plca-cfg";
204 nlctx->argp = ctx->argp;
205 nlctx->argc = ctx->argc;
206 nlctx->devname = ctx->devname;
207 nlsk = nlctx->ethnl_socket;
208 msgbuff = &nlsk->msgbuff;
209
210 ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_PLCA_SET_CFG,
211 NLM_F_REQUEST | NLM_F_ACK);
212 if (ret < 0)
213 return 2;
214 if (ethnla_fill_header(msgbuff, ETHTOOL_A_PLCA_HEADER,
215 ctx->devname, 0))
216 return -EMSGSIZE;
217
218 ret = nl_parser(nlctx, set_plca_params, NULL, PARSER_GROUP_NONE, NULL);
219 if (ret < 0)
220 return 1;
221
222 ret = nlsock_sendmsg(nlsk, NULL);
223 if (ret < 0)
224 return 76;
225 ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
226 if (ret == 0)
227 return 0;
228 else
229 return nlctx->exit_code ?: 76;
230 }
231
232 /* PLCA_GET_STATUS */
233
plca_get_status_reply_cb(const struct nlmsghdr * nlhdr,void * data)234 int plca_get_status_reply_cb(const struct nlmsghdr *nlhdr, void *data)
235 {
236 const struct nlattr *tb[ETHTOOL_A_PLCA_MAX + 1] = {};
237 DECLARE_ATTR_TB_INFO(tb);
238 struct nl_context *nlctx = data;
239 bool silent;
240 int err_ret;
241 int ret;
242 u8 val;
243
244 silent = nlctx->is_dump || nlctx->is_monitor;
245 err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
246 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
247 if (ret < 0)
248 return err_ret;
249
250 nlctx->devname = get_dev_name(tb[ETHTOOL_A_PLCA_HEADER]);
251 if (!dev_ok(nlctx))
252 return err_ret;
253
254 if (silent)
255 putchar('\n');
256
257 printf("PLCA status of %s:\n", nlctx->devname);
258
259 // check whether the Open Alliance TC14 standard memory map is supported
260 printf("\tStatus: ");
261
262 if (!tb[ETHTOOL_A_PLCA_STATUS]) {
263 printf("not supported");
264 } else {
265 val = mnl_attr_get_u8(tb[ETHTOOL_A_PLCA_STATUS]);
266 printf(val ? "on" : "off");
267 }
268 putchar('\n');
269
270 return MNL_CB_OK;
271 }
272
273
nl_plca_get_status(struct cmd_context * ctx)274 int nl_plca_get_status(struct cmd_context *ctx)
275 {
276 struct nl_context *nlctx = ctx->nlctx;
277 struct nl_socket *nlsk = nlctx->ethnl_socket;
278 int ret;
279
280 if (netlink_cmd_check(ctx, ETHTOOL_MSG_PLCA_GET_STATUS, true))
281 return -EOPNOTSUPP;
282
283 if (ctx->argc > 0) {
284 fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
285 *ctx->argp);
286 return 1;
287 }
288
289 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_PLCA_GET_STATUS,
290 ETHTOOL_A_PLCA_HEADER, 0);
291
292 if (ret < 0)
293 return ret;
294
295 return nlsock_send_get_request(nlsk, plca_get_status_reply_cb);
296 }
297