1*1b481fc3SMaciej Żenczykowski /*
2*1b481fc3SMaciej Żenczykowski * module-eeprom.c - netlink implementation of module eeprom get command
3*1b481fc3SMaciej Żenczykowski *
4*1b481fc3SMaciej Żenczykowski * ethtool -m <dev>
5*1b481fc3SMaciej Żenczykowski */
6*1b481fc3SMaciej Żenczykowski
7*1b481fc3SMaciej Żenczykowski #include <errno.h>
8*1b481fc3SMaciej Żenczykowski #include <string.h>
9*1b481fc3SMaciej Żenczykowski #include <stdio.h>
10*1b481fc3SMaciej Żenczykowski #include <stddef.h>
11*1b481fc3SMaciej Żenczykowski
12*1b481fc3SMaciej Żenczykowski #include "../sff-common.h"
13*1b481fc3SMaciej Żenczykowski #include "../qsfp.h"
14*1b481fc3SMaciej Żenczykowski #include "../cmis.h"
15*1b481fc3SMaciej Żenczykowski #include "../internal.h"
16*1b481fc3SMaciej Żenczykowski #include "../common.h"
17*1b481fc3SMaciej Żenczykowski #include "../list.h"
18*1b481fc3SMaciej Żenczykowski #include "netlink.h"
19*1b481fc3SMaciej Żenczykowski #include "parser.h"
20*1b481fc3SMaciej Żenczykowski
21*1b481fc3SMaciej Żenczykowski #define ETH_I2C_ADDRESS_LOW 0x50
22*1b481fc3SMaciej Żenczykowski #define ETH_I2C_MAX_ADDRESS 0x7F
23*1b481fc3SMaciej Żenczykowski
24*1b481fc3SMaciej Żenczykowski struct cmd_params {
25*1b481fc3SMaciej Żenczykowski u8 dump_hex;
26*1b481fc3SMaciej Żenczykowski u8 dump_raw;
27*1b481fc3SMaciej Żenczykowski u32 offset;
28*1b481fc3SMaciej Żenczykowski u32 length;
29*1b481fc3SMaciej Żenczykowski u32 page;
30*1b481fc3SMaciej Żenczykowski u32 bank;
31*1b481fc3SMaciej Żenczykowski u32 i2c_address;
32*1b481fc3SMaciej Żenczykowski };
33*1b481fc3SMaciej Żenczykowski
34*1b481fc3SMaciej Żenczykowski static const struct param_parser getmodule_params[] = {
35*1b481fc3SMaciej Żenczykowski {
36*1b481fc3SMaciej Żenczykowski .arg = "hex",
37*1b481fc3SMaciej Żenczykowski .handler = nl_parse_u8bool,
38*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, dump_hex),
39*1b481fc3SMaciej Żenczykowski .min_argc = 1,
40*1b481fc3SMaciej Żenczykowski },
41*1b481fc3SMaciej Żenczykowski {
42*1b481fc3SMaciej Żenczykowski .arg = "raw",
43*1b481fc3SMaciej Żenczykowski .handler = nl_parse_u8bool,
44*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, dump_raw),
45*1b481fc3SMaciej Żenczykowski .min_argc = 1,
46*1b481fc3SMaciej Żenczykowski },
47*1b481fc3SMaciej Żenczykowski {
48*1b481fc3SMaciej Żenczykowski .arg = "offset",
49*1b481fc3SMaciej Żenczykowski .handler = nl_parse_direct_u32,
50*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, offset),
51*1b481fc3SMaciej Żenczykowski .min_argc = 1,
52*1b481fc3SMaciej Żenczykowski },
53*1b481fc3SMaciej Żenczykowski {
54*1b481fc3SMaciej Żenczykowski .arg = "length",
55*1b481fc3SMaciej Żenczykowski .handler = nl_parse_direct_u32,
56*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, length),
57*1b481fc3SMaciej Żenczykowski .min_argc = 1,
58*1b481fc3SMaciej Żenczykowski },
59*1b481fc3SMaciej Żenczykowski {
60*1b481fc3SMaciej Żenczykowski .arg = "page",
61*1b481fc3SMaciej Żenczykowski .handler = nl_parse_direct_u32,
62*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, page),
63*1b481fc3SMaciej Żenczykowski .min_argc = 1,
64*1b481fc3SMaciej Żenczykowski },
65*1b481fc3SMaciej Żenczykowski {
66*1b481fc3SMaciej Żenczykowski .arg = "bank",
67*1b481fc3SMaciej Żenczykowski .handler = nl_parse_direct_u32,
68*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, bank),
69*1b481fc3SMaciej Żenczykowski .min_argc = 1,
70*1b481fc3SMaciej Żenczykowski },
71*1b481fc3SMaciej Żenczykowski {
72*1b481fc3SMaciej Żenczykowski .arg = "i2c",
73*1b481fc3SMaciej Żenczykowski .handler = nl_parse_direct_u32,
74*1b481fc3SMaciej Żenczykowski .dest_offset = offsetof(struct cmd_params, i2c_address),
75*1b481fc3SMaciej Żenczykowski .min_argc = 1,
76*1b481fc3SMaciej Żenczykowski },
77*1b481fc3SMaciej Żenczykowski {}
78*1b481fc3SMaciej Żenczykowski };
79*1b481fc3SMaciej Żenczykowski
80*1b481fc3SMaciej Żenczykowski static struct list_head eeprom_page_list = LIST_HEAD_INIT(eeprom_page_list);
81*1b481fc3SMaciej Żenczykowski
82*1b481fc3SMaciej Żenczykowski struct eeprom_page_entry {
83*1b481fc3SMaciej Żenczykowski struct list_head list; /* Member of eeprom_page_list */
84*1b481fc3SMaciej Żenczykowski void *data;
85*1b481fc3SMaciej Żenczykowski };
86*1b481fc3SMaciej Żenczykowski
eeprom_page_list_add(void * data)87*1b481fc3SMaciej Żenczykowski static int eeprom_page_list_add(void *data)
88*1b481fc3SMaciej Żenczykowski {
89*1b481fc3SMaciej Żenczykowski struct eeprom_page_entry *entry;
90*1b481fc3SMaciej Żenczykowski
91*1b481fc3SMaciej Żenczykowski entry = malloc(sizeof(*entry));
92*1b481fc3SMaciej Żenczykowski if (!entry)
93*1b481fc3SMaciej Żenczykowski return -ENOMEM;
94*1b481fc3SMaciej Żenczykowski
95*1b481fc3SMaciej Żenczykowski entry->data = data;
96*1b481fc3SMaciej Żenczykowski list_add(&entry->list, &eeprom_page_list);
97*1b481fc3SMaciej Żenczykowski
98*1b481fc3SMaciej Żenczykowski return 0;
99*1b481fc3SMaciej Żenczykowski }
100*1b481fc3SMaciej Żenczykowski
eeprom_page_list_flush(void)101*1b481fc3SMaciej Żenczykowski static void eeprom_page_list_flush(void)
102*1b481fc3SMaciej Żenczykowski {
103*1b481fc3SMaciej Żenczykowski struct eeprom_page_entry *entry;
104*1b481fc3SMaciej Żenczykowski struct list_head *head, *next;
105*1b481fc3SMaciej Żenczykowski
106*1b481fc3SMaciej Żenczykowski list_for_each_safe(head, next, &eeprom_page_list) {
107*1b481fc3SMaciej Żenczykowski entry = (struct eeprom_page_entry *) head;
108*1b481fc3SMaciej Żenczykowski free(entry->data);
109*1b481fc3SMaciej Żenczykowski list_del(head);
110*1b481fc3SMaciej Żenczykowski free(entry);
111*1b481fc3SMaciej Żenczykowski }
112*1b481fc3SMaciej Żenczykowski }
113*1b481fc3SMaciej Żenczykowski
get_eeprom_page_reply_cb(const struct nlmsghdr * nlhdr,void * data)114*1b481fc3SMaciej Żenczykowski static int get_eeprom_page_reply_cb(const struct nlmsghdr *nlhdr, void *data)
115*1b481fc3SMaciej Żenczykowski {
116*1b481fc3SMaciej Żenczykowski const struct nlattr *tb[ETHTOOL_A_MODULE_EEPROM_DATA + 1] = {};
117*1b481fc3SMaciej Żenczykowski struct ethtool_module_eeprom *request = data;
118*1b481fc3SMaciej Żenczykowski DECLARE_ATTR_TB_INFO(tb);
119*1b481fc3SMaciej Żenczykowski u8 *eeprom_data;
120*1b481fc3SMaciej Żenczykowski int ret;
121*1b481fc3SMaciej Żenczykowski
122*1b481fc3SMaciej Żenczykowski ret = mnl_attr_parse(nlhdr, GENL_HDRLEN, attr_cb, &tb_info);
123*1b481fc3SMaciej Żenczykowski if (ret < 0)
124*1b481fc3SMaciej Żenczykowski return ret;
125*1b481fc3SMaciej Żenczykowski
126*1b481fc3SMaciej Żenczykowski if (!tb[ETHTOOL_A_MODULE_EEPROM_DATA])
127*1b481fc3SMaciej Żenczykowski return MNL_CB_ERROR;
128*1b481fc3SMaciej Żenczykowski
129*1b481fc3SMaciej Żenczykowski eeprom_data = mnl_attr_get_payload(tb[ETHTOOL_A_MODULE_EEPROM_DATA]);
130*1b481fc3SMaciej Żenczykowski request->data = malloc(request->length);
131*1b481fc3SMaciej Żenczykowski if (!request->data)
132*1b481fc3SMaciej Żenczykowski return MNL_CB_ERROR;
133*1b481fc3SMaciej Żenczykowski memcpy(request->data, eeprom_data, request->length);
134*1b481fc3SMaciej Żenczykowski
135*1b481fc3SMaciej Żenczykowski ret = eeprom_page_list_add(request->data);
136*1b481fc3SMaciej Żenczykowski if (ret < 0)
137*1b481fc3SMaciej Żenczykowski goto err_list_add;
138*1b481fc3SMaciej Żenczykowski
139*1b481fc3SMaciej Żenczykowski return MNL_CB_OK;
140*1b481fc3SMaciej Żenczykowski
141*1b481fc3SMaciej Żenczykowski err_list_add:
142*1b481fc3SMaciej Żenczykowski free(request->data);
143*1b481fc3SMaciej Żenczykowski return MNL_CB_ERROR;
144*1b481fc3SMaciej Żenczykowski }
145*1b481fc3SMaciej Żenczykowski
nl_get_eeprom_page(struct cmd_context * ctx,struct ethtool_module_eeprom * request)146*1b481fc3SMaciej Żenczykowski int nl_get_eeprom_page(struct cmd_context *ctx,
147*1b481fc3SMaciej Żenczykowski struct ethtool_module_eeprom *request)
148*1b481fc3SMaciej Żenczykowski {
149*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = ctx->nlctx;
150*1b481fc3SMaciej Żenczykowski struct nl_socket *nlsock;
151*1b481fc3SMaciej Żenczykowski struct nl_msg_buff *msg;
152*1b481fc3SMaciej Żenczykowski int ret;
153*1b481fc3SMaciej Żenczykowski
154*1b481fc3SMaciej Żenczykowski if (!request || request->i2c_address > ETH_I2C_MAX_ADDRESS)
155*1b481fc3SMaciej Żenczykowski return -EINVAL;
156*1b481fc3SMaciej Żenczykowski
157*1b481fc3SMaciej Żenczykowski nlsock = nlctx->ethnl_socket;
158*1b481fc3SMaciej Żenczykowski msg = &nlsock->msgbuff;
159*1b481fc3SMaciej Żenczykowski
160*1b481fc3SMaciej Żenczykowski ret = nlsock_prep_get_request(nlsock, ETHTOOL_MSG_MODULE_EEPROM_GET,
161*1b481fc3SMaciej Żenczykowski ETHTOOL_A_MODULE_EEPROM_HEADER, 0);
162*1b481fc3SMaciej Żenczykowski if (ret < 0)
163*1b481fc3SMaciej Żenczykowski return ret;
164*1b481fc3SMaciej Żenczykowski
165*1b481fc3SMaciej Żenczykowski if (ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_LENGTH,
166*1b481fc3SMaciej Żenczykowski request->length) ||
167*1b481fc3SMaciej Żenczykowski ethnla_put_u32(msg, ETHTOOL_A_MODULE_EEPROM_OFFSET,
168*1b481fc3SMaciej Żenczykowski request->offset) ||
169*1b481fc3SMaciej Żenczykowski ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_PAGE,
170*1b481fc3SMaciej Żenczykowski request->page) ||
171*1b481fc3SMaciej Żenczykowski ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_BANK,
172*1b481fc3SMaciej Żenczykowski request->bank) ||
173*1b481fc3SMaciej Żenczykowski ethnla_put_u8(msg, ETHTOOL_A_MODULE_EEPROM_I2C_ADDRESS,
174*1b481fc3SMaciej Żenczykowski request->i2c_address))
175*1b481fc3SMaciej Żenczykowski return -EMSGSIZE;
176*1b481fc3SMaciej Żenczykowski
177*1b481fc3SMaciej Żenczykowski ret = nlsock_sendmsg(nlsock, NULL);
178*1b481fc3SMaciej Żenczykowski if (ret < 0)
179*1b481fc3SMaciej Żenczykowski return ret;
180*1b481fc3SMaciej Żenczykowski return nlsock_process_reply(nlsock, get_eeprom_page_reply_cb,
181*1b481fc3SMaciej Żenczykowski (void *)request);
182*1b481fc3SMaciej Żenczykowski }
183*1b481fc3SMaciej Żenczykowski
eeprom_dump_hex(struct cmd_context * ctx)184*1b481fc3SMaciej Żenczykowski static int eeprom_dump_hex(struct cmd_context *ctx)
185*1b481fc3SMaciej Żenczykowski {
186*1b481fc3SMaciej Żenczykowski struct ethtool_module_eeprom request = {
187*1b481fc3SMaciej Żenczykowski .length = 128,
188*1b481fc3SMaciej Żenczykowski .i2c_address = ETH_I2C_ADDRESS_LOW,
189*1b481fc3SMaciej Żenczykowski };
190*1b481fc3SMaciej Żenczykowski int ret;
191*1b481fc3SMaciej Żenczykowski
192*1b481fc3SMaciej Żenczykowski ret = nl_get_eeprom_page(ctx, &request);
193*1b481fc3SMaciej Żenczykowski if (ret < 0)
194*1b481fc3SMaciej Żenczykowski return ret;
195*1b481fc3SMaciej Żenczykowski
196*1b481fc3SMaciej Żenczykowski dump_hex(stdout, request.data, request.length, request.offset);
197*1b481fc3SMaciej Żenczykowski
198*1b481fc3SMaciej Żenczykowski return 0;
199*1b481fc3SMaciej Żenczykowski }
200*1b481fc3SMaciej Żenczykowski
eeprom_parse(struct cmd_context * ctx)201*1b481fc3SMaciej Żenczykowski static int eeprom_parse(struct cmd_context *ctx)
202*1b481fc3SMaciej Żenczykowski {
203*1b481fc3SMaciej Żenczykowski struct ethtool_module_eeprom request = {
204*1b481fc3SMaciej Żenczykowski .length = 1,
205*1b481fc3SMaciej Żenczykowski .i2c_address = ETH_I2C_ADDRESS_LOW,
206*1b481fc3SMaciej Żenczykowski };
207*1b481fc3SMaciej Żenczykowski int ret;
208*1b481fc3SMaciej Żenczykowski
209*1b481fc3SMaciej Żenczykowski /* Fetch the SFF-8024 Identifier Value. For all supported standards, it
210*1b481fc3SMaciej Żenczykowski * is located at I2C address 0x50, byte 0. See section 4.1 in SFF-8024,
211*1b481fc3SMaciej Żenczykowski * revision 4.9.
212*1b481fc3SMaciej Żenczykowski */
213*1b481fc3SMaciej Żenczykowski ret = nl_get_eeprom_page(ctx, &request);
214*1b481fc3SMaciej Żenczykowski if (ret < 0)
215*1b481fc3SMaciej Żenczykowski return ret;
216*1b481fc3SMaciej Żenczykowski
217*1b481fc3SMaciej Żenczykowski switch (request.data[0]) {
218*1b481fc3SMaciej Żenczykowski #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
219*1b481fc3SMaciej Żenczykowski case SFF8024_ID_SFP:
220*1b481fc3SMaciej Żenczykowski return sff8079_show_all_nl(ctx);
221*1b481fc3SMaciej Żenczykowski case SFF8024_ID_QSFP:
222*1b481fc3SMaciej Żenczykowski case SFF8024_ID_QSFP28:
223*1b481fc3SMaciej Żenczykowski case SFF8024_ID_QSFP_PLUS:
224*1b481fc3SMaciej Żenczykowski return sff8636_show_all_nl(ctx);
225*1b481fc3SMaciej Żenczykowski case SFF8024_ID_QSFP_DD:
226*1b481fc3SMaciej Żenczykowski case SFF8024_ID_OSFP:
227*1b481fc3SMaciej Żenczykowski case SFF8024_ID_DSFP:
228*1b481fc3SMaciej Żenczykowski return cmis_show_all_nl(ctx);
229*1b481fc3SMaciej Żenczykowski #endif
230*1b481fc3SMaciej Żenczykowski default:
231*1b481fc3SMaciej Żenczykowski /* If we cannot recognize the memory map, default to dumping
232*1b481fc3SMaciej Żenczykowski * the first 128 bytes in hex.
233*1b481fc3SMaciej Żenczykowski */
234*1b481fc3SMaciej Żenczykowski return eeprom_dump_hex(ctx);
235*1b481fc3SMaciej Żenczykowski }
236*1b481fc3SMaciej Żenczykowski }
237*1b481fc3SMaciej Żenczykowski
nl_getmodule(struct cmd_context * ctx)238*1b481fc3SMaciej Żenczykowski int nl_getmodule(struct cmd_context *ctx)
239*1b481fc3SMaciej Żenczykowski {
240*1b481fc3SMaciej Żenczykowski struct cmd_params getmodule_cmd_params = {};
241*1b481fc3SMaciej Żenczykowski struct ethtool_module_eeprom request = {0};
242*1b481fc3SMaciej Żenczykowski struct nl_context *nlctx = ctx->nlctx;
243*1b481fc3SMaciej Żenczykowski int ret;
244*1b481fc3SMaciej Żenczykowski
245*1b481fc3SMaciej Żenczykowski if (netlink_cmd_check(ctx, ETHTOOL_MSG_MODULE_EEPROM_GET, false))
246*1b481fc3SMaciej Żenczykowski return -EOPNOTSUPP;
247*1b481fc3SMaciej Żenczykowski
248*1b481fc3SMaciej Żenczykowski nlctx->cmd = "-m";
249*1b481fc3SMaciej Żenczykowski nlctx->argp = ctx->argp;
250*1b481fc3SMaciej Żenczykowski nlctx->argc = ctx->argc;
251*1b481fc3SMaciej Żenczykowski nlctx->devname = ctx->devname;
252*1b481fc3SMaciej Żenczykowski ret = nl_parser(nlctx, getmodule_params, &getmodule_cmd_params, PARSER_GROUP_NONE, NULL);
253*1b481fc3SMaciej Żenczykowski if (ret < 0)
254*1b481fc3SMaciej Żenczykowski return ret;
255*1b481fc3SMaciej Żenczykowski
256*1b481fc3SMaciej Żenczykowski if (getmodule_cmd_params.dump_hex && getmodule_cmd_params.dump_raw) {
257*1b481fc3SMaciej Żenczykowski fprintf(stderr, "Hex and raw dump cannot be specified together\n");
258*1b481fc3SMaciej Żenczykowski return -EINVAL;
259*1b481fc3SMaciej Żenczykowski }
260*1b481fc3SMaciej Żenczykowski
261*1b481fc3SMaciej Żenczykowski /* When complete hex/raw dump of the EEPROM is requested, fallback to
262*1b481fc3SMaciej Żenczykowski * ioctl. Netlink can only request specific pages.
263*1b481fc3SMaciej Żenczykowski */
264*1b481fc3SMaciej Żenczykowski if ((getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) &&
265*1b481fc3SMaciej Żenczykowski !getmodule_cmd_params.page && !getmodule_cmd_params.bank &&
266*1b481fc3SMaciej Żenczykowski !getmodule_cmd_params.i2c_address) {
267*1b481fc3SMaciej Żenczykowski nlctx->ioctl_fallback = true;
268*1b481fc3SMaciej Żenczykowski return -EOPNOTSUPP;
269*1b481fc3SMaciej Żenczykowski }
270*1b481fc3SMaciej Żenczykowski
271*1b481fc3SMaciej Żenczykowski #ifdef ETHTOOL_ENABLE_PRETTY_DUMP
272*1b481fc3SMaciej Żenczykowski if (getmodule_cmd_params.page || getmodule_cmd_params.bank ||
273*1b481fc3SMaciej Żenczykowski getmodule_cmd_params.offset || getmodule_cmd_params.length)
274*1b481fc3SMaciej Żenczykowski #endif
275*1b481fc3SMaciej Żenczykowski getmodule_cmd_params.dump_hex = true;
276*1b481fc3SMaciej Żenczykowski
277*1b481fc3SMaciej Żenczykowski request.offset = getmodule_cmd_params.offset;
278*1b481fc3SMaciej Żenczykowski request.length = getmodule_cmd_params.length ?: 128;
279*1b481fc3SMaciej Żenczykowski request.page = getmodule_cmd_params.page;
280*1b481fc3SMaciej Żenczykowski request.bank = getmodule_cmd_params.bank;
281*1b481fc3SMaciej Żenczykowski request.i2c_address = getmodule_cmd_params.i2c_address ?: ETH_I2C_ADDRESS_LOW;
282*1b481fc3SMaciej Żenczykowski
283*1b481fc3SMaciej Żenczykowski if (request.page && !request.offset)
284*1b481fc3SMaciej Żenczykowski request.offset = 128;
285*1b481fc3SMaciej Żenczykowski
286*1b481fc3SMaciej Żenczykowski if (getmodule_cmd_params.dump_hex || getmodule_cmd_params.dump_raw) {
287*1b481fc3SMaciej Żenczykowski ret = nl_get_eeprom_page(ctx, &request);
288*1b481fc3SMaciej Żenczykowski if (ret < 0)
289*1b481fc3SMaciej Żenczykowski goto cleanup;
290*1b481fc3SMaciej Żenczykowski
291*1b481fc3SMaciej Żenczykowski if (getmodule_cmd_params.dump_raw)
292*1b481fc3SMaciej Żenczykowski fwrite(request.data, 1, request.length, stdout);
293*1b481fc3SMaciej Żenczykowski else
294*1b481fc3SMaciej Żenczykowski dump_hex(stdout, request.data, request.length,
295*1b481fc3SMaciej Żenczykowski request.offset);
296*1b481fc3SMaciej Żenczykowski } else {
297*1b481fc3SMaciej Żenczykowski ret = eeprom_parse(ctx);
298*1b481fc3SMaciej Żenczykowski if (ret < 0)
299*1b481fc3SMaciej Żenczykowski goto cleanup;
300*1b481fc3SMaciej Żenczykowski }
301*1b481fc3SMaciej Żenczykowski
302*1b481fc3SMaciej Żenczykowski cleanup:
303*1b481fc3SMaciej Żenczykowski eeprom_page_list_flush();
304*1b481fc3SMaciej Żenczykowski return ret;
305*1b481fc3SMaciej Żenczykowski }
306