1 /*
2 * module.c - netlink implementation of module commands
3 *
4 * Implementation of "ethtool --show-module <dev>" and
5 * "ethtool --set-module <dev> ..."
6 */
7
8 #include <errno.h>
9 #include <ctype.h>
10 #include <inttypes.h>
11 #include <string.h>
12 #include <stdio.h>
13 #include <stdarg.h>
14
15 #include "../internal.h"
16 #include "../common.h"
17 #include "netlink.h"
18 #include "parser.h"
19
20 /* MODULE_GET */
21
module_power_mode_policy_name(u8 val)22 static const char *module_power_mode_policy_name(u8 val)
23 {
24 switch (val) {
25 case ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH:
26 return "high";
27 case ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO:
28 return "auto";
29 default:
30 return "unknown";
31 }
32 }
33
module_power_mode_name(u8 val)34 static const char *module_power_mode_name(u8 val)
35 {
36 switch (val) {
37 case ETHTOOL_MODULE_POWER_MODE_LOW:
38 return "low";
39 case ETHTOOL_MODULE_POWER_MODE_HIGH:
40 return "high";
41 default:
42 return "unknown";
43 }
44 }
45
module_reply_cb(const struct nlmsghdr * nlhdr,void * data)46 int module_reply_cb(const struct nlmsghdr *nlhdr, void *data)
47 {
48 const struct nlattr *tb[ETHTOOL_A_MODULE_MAX + 1] = {};
49 struct nl_context *nlctx = data;
50 DECLARE_ATTR_TB_INFO(tb);
51 bool silent;
52 int err_ret;
53 int ret;
54
55 silent = nlctx->is_dump || nlctx->is_monitor;
56 err_ret = silent ? MNL_CB_OK : MNL_CB_ERROR;
57 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
58 if (ret < 0)
59 return err_ret;
60 nlctx->devname = get_dev_name(tb[ETHTOOL_A_MODULE_HEADER]);
61 if (!dev_ok(nlctx))
62 return err_ret;
63
64 if (silent)
65 print_nl();
66
67 open_json_object(NULL);
68
69 print_string(PRINT_ANY, "ifname", "Module parameters for %s:\n",
70 nlctx->devname);
71
72 if (tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]) {
73 u8 val;
74
75 val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE_POLICY]);
76 print_string(PRINT_ANY, "power-mode-policy",
77 "power-mode-policy: %s\n",
78 module_power_mode_policy_name(val));
79 }
80
81 if (tb[ETHTOOL_A_MODULE_POWER_MODE]) {
82 u8 val;
83
84 val = mnl_attr_get_u8(tb[ETHTOOL_A_MODULE_POWER_MODE]);
85 print_string(PRINT_ANY, "power-mode",
86 "power-mode: %s\n", module_power_mode_name(val));
87 }
88
89 close_json_object();
90
91 return MNL_CB_OK;
92 }
93
nl_gmodule(struct cmd_context * ctx)94 int nl_gmodule(struct cmd_context *ctx)
95 {
96 struct nl_context *nlctx = ctx->nlctx;
97 struct nl_socket *nlsk;
98 int ret;
99
100 if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_GET, true))
101 return -EOPNOTSUPP;
102 if (ctx->argc > 0) {
103 fprintf(stderr, "ethtool: unexpected parameter '%s'\n",
104 *ctx->argp);
105 return 1;
106 }
107
108 nlsk = nlctx->ethnl_socket;
109 ret = nlsock_prep_get_request(nlsk, ETHTOOL_MSG_MODULE_GET,
110 ETHTOOL_A_MODULE_HEADER, 0);
111 if (ret < 0)
112 return ret;
113
114 new_json_obj(ctx->json);
115 ret = nlsock_send_get_request(nlsk, module_reply_cb);
116 delete_json_obj();
117 return ret;
118 }
119
120 /* MODULE_SET */
121
122 static const struct lookup_entry_u8 power_mode_policy_values[] = {
123 { .arg = "high", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_HIGH },
124 { .arg = "auto", .val = ETHTOOL_MODULE_POWER_MODE_POLICY_AUTO },
125 {}
126 };
127
128 static const struct param_parser smodule_params[] = {
129 {
130 .arg = "power-mode-policy",
131 .type = ETHTOOL_A_MODULE_POWER_MODE_POLICY,
132 .handler = nl_parse_lookup_u8,
133 .handler_data = power_mode_policy_values,
134 .min_argc = 1,
135 },
136 {}
137 };
138
nl_smodule(struct cmd_context * ctx)139 int nl_smodule(struct cmd_context *ctx)
140 {
141 struct nl_context *nlctx = ctx->nlctx;
142 struct nl_msg_buff *msgbuff;
143 struct nl_socket *nlsk;
144 int ret;
145
146 if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_SET, false))
147 return -EOPNOTSUPP;
148 if (!ctx->argc) {
149 fprintf(stderr, "ethtool (--set-module): parameters missing\n");
150 return 1;
151 }
152
153 nlctx->cmd = "--set-module";
154 nlctx->argp = ctx->argp;
155 nlctx->argc = ctx->argc;
156 nlctx->devname = ctx->devname;
157 nlsk = nlctx->ethnl_socket;
158 msgbuff = &nlsk->msgbuff;
159
160 ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MODULE_SET,
161 NLM_F_REQUEST | NLM_F_ACK);
162 if (ret < 0)
163 return 2;
164 if (ethnla_fill_header(msgbuff, ETHTOOL_A_MODULE_HEADER,
165 ctx->devname, 0))
166 return -EMSGSIZE;
167
168 ret = nl_parser(nlctx, smodule_params, NULL, PARSER_GROUP_NONE, NULL);
169 if (ret < 0)
170 return 1;
171
172 ret = nlsock_sendmsg(nlsk, NULL);
173 if (ret < 0)
174 return 83;
175 ret = nlsock_process_reply(nlsk, nomsg_reply_cb, nlctx);
176 if (ret == 0)
177 return 0;
178 else
179 return nlctx->exit_code ?: 83;
180 }
181
182 /* MODULE_FW_FLASH_ACT */
183
184 static const struct param_parser flash_module_fw_params[] = {
185 {
186 .arg = "file",
187 .type = ETHTOOL_A_MODULE_FW_FLASH_FILE_NAME,
188 .handler = nl_parse_string,
189 .min_argc = 1,
190 },
191 {
192 .arg = "pass",
193 .type = ETHTOOL_A_MODULE_FW_FLASH_PASSWORD,
194 .handler = nl_parse_direct_u32,
195 .min_argc = 1,
196 },
197 {}
198 };
199
200 struct module_flash_context {
201 uint8_t breakout:1,
202 first:1;
203 };
204
module_fw_flash_ntf_cb(const struct nlmsghdr * nlhdr,void * data)205 static int module_fw_flash_ntf_cb(const struct nlmsghdr *nlhdr, void *data)
206 {
207 const struct nlattr *tb[ETHTOOL_A_MODULE_FW_FLASH_MAX + 1] = {};
208 struct module_flash_context *mfctx;
209 struct nl_context *nlctx = data;
210 DECLARE_ATTR_TB_INFO(tb);
211 u8 status = 0;
212 int ret;
213
214 mfctx = nlctx->cmd_private;
215
216 ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
217 if (ret < 0)
218 return MNL_CB_OK;
219 nlctx->devname = get_dev_name(tb[ETHTOOL_A_MODULE_FW_FLASH_HEADER]);
220 if (!dev_ok(nlctx))
221 return MNL_CB_OK;
222
223 if (tb[ETHTOOL_A_MODULE_FW_FLASH_STATUS])
224 status = mnl_attr_get_u32(tb[ETHTOOL_A_MODULE_FW_FLASH_STATUS]);
225
226 switch (status) {
227 case ETHTOOL_MODULE_FW_FLASH_STATUS_STARTED:
228 print_string(PRINT_FP, NULL,
229 "Transceiver module firmware flashing started for device %s\n",
230 nlctx->devname);
231 break;
232 case ETHTOOL_MODULE_FW_FLASH_STATUS_IN_PROGRESS:
233 if (mfctx->first) {
234 print_string(PRINT_FP, NULL,
235 "Transceiver module firmware flashing in progress for device %s\n",
236 nlctx->devname);
237 mfctx->first = 0;
238 }
239 break;
240 case ETHTOOL_MODULE_FW_FLASH_STATUS_COMPLETED:
241 print_nl();
242 print_string(PRINT_FP, NULL,
243 "Transceiver module firmware flashing completed for device %s\n",
244 nlctx->devname);
245 mfctx->breakout = 1;
246 break;
247 case ETHTOOL_MODULE_FW_FLASH_STATUS_ERROR:
248 print_nl();
249 print_string(PRINT_FP, NULL,
250 "Transceiver module firmware flashing encountered an error for device %s\n",
251 nlctx->devname);
252 mfctx->breakout = 1;
253 break;
254 default:
255 break;
256 }
257
258 if (tb[ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG]) {
259 const char *status_msg;
260
261 status_msg = mnl_attr_get_str(tb[ETHTOOL_A_MODULE_FW_FLASH_STATUS_MSG]);
262 print_string(PRINT_FP, NULL, "Status message: %s\n", status_msg);
263 }
264
265 if (tb[ETHTOOL_A_MODULE_FW_FLASH_DONE] &&
266 tb[ETHTOOL_A_MODULE_FW_FLASH_TOTAL]) {
267 uint64_t done, total;
268
269 done = attr_get_uint(tb[ETHTOOL_A_MODULE_FW_FLASH_DONE]);
270 total = attr_get_uint(tb[ETHTOOL_A_MODULE_FW_FLASH_TOTAL]);
271
272 if (total)
273 print_u64(PRINT_FP, NULL, "Progress: %"PRIu64"%\r",
274 done * 100 / total);
275 }
276
277 return MNL_CB_OK;
278 }
279
nl_flash_module_fw_cb(const struct nlmsghdr * nlhdr,void * data)280 static int nl_flash_module_fw_cb(const struct nlmsghdr *nlhdr, void *data)
281 {
282 const struct genlmsghdr *ghdr = (const struct genlmsghdr *)(nlhdr + 1);
283
284 if (ghdr->cmd != ETHTOOL_MSG_MODULE_FW_FLASH_NTF)
285 return MNL_CB_OK;
286
287 module_fw_flash_ntf_cb(nlhdr, data);
288
289 return MNL_CB_STOP;
290 }
291
nl_flash_module_fw_process_ntf(struct cmd_context * ctx)292 static int nl_flash_module_fw_process_ntf(struct cmd_context *ctx)
293 {
294 struct nl_context *nlctx = ctx->nlctx;
295 struct module_flash_context *mfctx;
296 struct nl_socket *nlsk;
297 int ret;
298
299 nlsk = nlctx->ethnl_socket;
300
301 mfctx = malloc(sizeof(struct module_flash_context));
302 if (!mfctx)
303 return -ENOMEM;
304
305 mfctx->breakout = 0;
306 mfctx->first = 1;
307 nlctx->cmd_private = mfctx;
308
309 while (!mfctx->breakout) {
310 ret = nlsock_process_reply(nlsk, nl_flash_module_fw_cb, nlctx);
311 if (ret)
312 goto out;
313 nlsk->seq++;
314 }
315
316 out:
317 free(mfctx);
318 return ret;
319 }
320
nl_flash_module_fw(struct cmd_context * ctx)321 int nl_flash_module_fw(struct cmd_context *ctx)
322 {
323 struct nl_context *nlctx = ctx->nlctx;
324 struct nl_msg_buff *msgbuff;
325 struct nl_socket *nlsk;
326 int ret;
327
328 if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_FW_FLASH_ACT, false))
329 return -EOPNOTSUPP;
330 if (!ctx->argc) {
331 fprintf(stderr, "ethtool (--flash-module-firmware): parameters missing\n");
332 return 1;
333 }
334
335 nlctx->cmd = "--flash-module-firmware";
336 nlctx->argp = ctx->argp;
337 nlctx->argc = ctx->argc;
338 nlctx->devname = ctx->devname;
339 nlsk = nlctx->ethnl_socket;
340 msgbuff = &nlsk->msgbuff;
341
342 ret = msg_init(nlctx, msgbuff, ETHTOOL_MSG_MODULE_FW_FLASH_ACT,
343 NLM_F_REQUEST | NLM_F_ACK);
344 if (ret < 0)
345 return 2;
346 if (ethnla_fill_header(msgbuff, ETHTOOL_A_MODULE_FW_FLASH_HEADER,
347 ctx->devname, 0))
348 return -EMSGSIZE;
349
350 ret = nl_parser(nlctx, flash_module_fw_params, NULL, PARSER_GROUP_NONE,
351 NULL);
352 if (ret < 0)
353 return 1;
354
355 ret = nlsock_sendmsg(nlsk, NULL);
356 if (ret < 0)
357 fprintf(stderr, "Cannot flash transceiver module firmware\n");
358 else
359 ret = nl_flash_module_fw_process_ntf(ctx);
360
361 return ret;
362 }
363