Newer
Older
/*
* SPDX-License-Identifier: LGPL-3.0-or-later
* Copyright (C) 2020 CERN (www.cern.ch)
* Author: Federico Vaga <federico.vaga@cern.ch>
*/
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <assert.h>
#include <glob.h>
#include <errno.h>
#include <string.h>
#include <libgen.h>
#include <fmc/core.h>
#ifndef GIT_VERSION
#define GIT_VERSION "n/a"
#endif
static const char *libfmc_version = GIT_VERSION;
const char *libfmc_version_full = "libfmc version:" GIT_VERSION;
#ifndef BIT
#define BIT(_n) (1 << _n)
#endif
#ifndef MIN
#define MIN(_a,_b) (((_a) < (_b)) ? (_a) : (_b))
#endif
#define FMC_SLOT_PRESENT BIT(0)
#define FMC_SLOT_FRU_VALID BIT(1)
#define MAX_PATH_LEN 128
struct fmc_slot {
unsigned int id;
unsigned long flags;
unsigned int ga;
char eeprom_type[MAX_SYS_LEN];
};
struct fmc_carrier {
unsigned int id;
struct fmc_slot **slot;
unsigned int n_slots;
};
static const char *fmc_error_string[] = {
[FMC_ERR_SLOT_NODEV - __FMC_ERR_MIN] = "Slot number not found",
[FMC_ERR_NOPRES - __FMC_ERR_MIN] = "FMC Module not present",
};
/**
* It returns the error message associated to the given error code
* @param[in] err error code
*/
const char *fmc_strerror(int err)
{
if (err < __FMC_ERR_MIN || err > __FMC_ERR_MAX)
return strerror(err);
return fmc_error_string[err - __FMC_ERR_MIN];
}
/**
* Return the FMC library version
* @return return the FMC library version as string
*/
const char *fmc_version_get(void)
{
return libfmc_version;
}
static struct fmc_slot *__fmc_slot_get(struct fmc_carrier *carrier,
unsigned int slot_n)
{
int i;
for (i = 0; i < carrier->n_slots; ++i)
if (carrier->slot[i]->id == slot_n)
return carrier->slot[i];
return NULL;
}
static bool fmc_slot_is_flag_set(struct fmc_tkn *tkn, unsigned int slot_n,
unsigned int flag)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
struct fmc_slot *slot = __fmc_slot_get(carrier, slot_n);
if (!slot)
return false;
return (slot->flags & flag);
}
static int __fmc_sys_read_string(struct fmc_carrier *carr,
unsigned int slot_n,
const char *name,
char *str,
unsigned int max_len)
{
char path[MAX_PATH_LEN];
int fd;
int ret;
snprintf(path, MAX_PATH_LEN,
"/sys/class/fmc/fmc-carrier-%u/fmc-slot-%u.%u/%s",
carr->id, carr->id, slot_n, name);
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = read(fd, str, max_len);
close(fd);
if (ret > 0) {
char *n = strrchr(str, '\n');
if (n) {
*n = '\0';
}
}
static int __fmc_sys_write_string(struct fmc_carrier *carr,
unsigned int slot_n,
const char *name,
const char *str,
unsigned int max_len)
{
char path[MAX_PATH_LEN];
int fd;
int ret;
snprintf(path, MAX_PATH_LEN,
"/sys/class/fmc/fmc-carrier-%u/fmc-slot-%u.%u/%s",
carr->id, carr->id, slot_n, name);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = write(fd, str, max_len);
close(fd);
return ret;
}
static int __fmc_sys_read_int(struct fmc_carrier *carr,
unsigned int slot_n,
const char *name,
unsigned int *val)
{
char buf[MAX_SYS_LEN];
int ret;
ret = __fmc_sys_read_string(carr, slot_n, name, buf, MAX_SYS_LEN);
if (ret < 0)
return ret;
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
if (ret != 1)
return -1;
return 0;
}
static bool __fmc_slot_is_present(struct fmc_carrier *carr,
unsigned int slot_n)
{
int err;
unsigned int val;
err = __fmc_sys_read_int(carr, slot_n, "present", &val);
if (err < 0)
return false;
return val;
}
static bool __fmc_slot_is_fru_valid(struct fmc_carrier *carr,
unsigned int slot_n)
{
int err;
unsigned int val;
err = __fmc_sys_read_int(carr, slot_n, "fru_valid", &val);
if (err < 0)
return false;
return val;
}
static int __fmc_slot_ga_get(struct fmc_carrier *carr,
unsigned int slot_n,
unsigned int *ga)
{
int err;
err = __fmc_sys_read_int(carr, slot_n, "fmc_ga", ga);
if (err)
*ga = 0xFF;
return err;
}
static int __fmc_slot_eeprom_type_get(struct fmc_carrier *carr,
unsigned int slot_n,
char *str, size_t max_len)
{
int ret;
ret = __fmc_sys_read_string(carr, slot_n, "eeprom_type", str, max_len);
if (ret < 0) {
return ret;
}
return 0;
}
static int __fmc_slot_eeprom_type_set(struct fmc_carrier *carr,
unsigned int slot_n,
const char *str, size_t max_len)
{
int ret = __fmc_sys_write_string(carr, slot_n, "eeprom_type",
str, max_len);
return ret < 0 ? -1 : 0;
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
static int __fmc_carrier_type_get(struct fmc_carrier *carr,
char *buf, size_t max_len)
{
char carrier_type[32];
char path[sizeof("/sys/class/fmc/fmc-carrier-/device") + 4];
ssize_t ret;
ret = snprintf(path, sizeof(path),
"/sys/class/fmc/fmc-carrier-%u/device", carr->id);
if (ret < 0)
goto err_path;
ret = readlink(path, carrier_type, sizeof(carrier_type));
if (ret < 0)
goto err_link;
if ((size_t)ret >= sizeof(carrier_type))
ret = sizeof(carrier_type) - 1;
carrier_type[ret] = '\0';
strncpy(buf, basename(carrier_type), max_len);
return 0;
err_link:
err_path:
strncpy(buf, "unknown", max_len);
return -1;
}
static int fmc_carrier_slots_init(struct fmc_carrier *carr)
{
char pattern[MAX_PATH_LEN];
glob_t g;
int err, i;
snprintf(pattern, MAX_PATH_LEN, "/sys/class/fmc/fmc-carrier-%u/fmc-slot-%u.*",
carr->id, carr->id);
err = glob(pattern, 0, NULL, &g);
if (err == GLOB_NOMATCH)
goto err_glob;
carr->n_slots = g.gl_pathc;
carr->slot = calloc(carr->n_slots, sizeof(struct fmc_slot *));
if (!carr->slot) {
errno = ENOMEM;
goto err_alloc;
}
__fmc_carrier_type_get(carr, carr->name, MAX_SYS_LEN);
for (i = 0; i < carr->n_slots; i++) {
carr->slot[i] = malloc(sizeof(*carr->slot[i]));
memset(carr->slot[i], 0, sizeof(*carr->slot[i]));
sscanf(basename(g.gl_pathv[i]), "fmc-slot-%*d.%u",
&carr->slot[i]->id);
__fmc_slot_ga_get(carr, carr->slot[i]->id, &carr->slot[i]->ga);
__fmc_slot_eeprom_type_get(carr, carr->slot[i]->id,
carr->slot[i]->eeprom_type,
MAX_SYS_LEN);
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
if (__fmc_slot_is_present(carr, carr->slot[i]->id))
carr->slot[i]->flags |= FMC_SLOT_PRESENT;
if (__fmc_slot_is_fru_valid(carr, carr->slot[i]->id))
carr->slot[i]->flags |= FMC_SLOT_FRU_VALID;
}
globfree(&g);
return 0;
err_alloc:
globfree(&g);
err_glob:
return -1;
}
static void fmc_carrier_slots_cleanup(struct fmc_carrier *carr)
{
int i;
for (i = 0; i < carr->n_slots; i++)
free(carr->slot[i]);
free(carr->slot);
}
/**
* Check whether the an FMC mezzanine is present or not
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
*
* @return true if present; false if not present or error
*/
bool fmc_slot_is_present(struct fmc_tkn *tkn, unsigned int slot_n)
{
return fmc_slot_is_flag_set(tkn, slot_n, FMC_SLOT_PRESENT);
}
/**
* Check whether the an FMC mezzanine is present or not
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
*
* @return true if the FRU is valid; false if the FRU is not valid or error
*/
bool fmc_slot_is_fru_valid(struct fmc_tkn *tkn, unsigned int slot_n)
{
return fmc_slot_is_flag_set(tkn, slot_n, FMC_SLOT_FRU_VALID);
}
/**
* Get the slot geographical address
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[out] ga: geographical address
*
* @return on success 0, otherwise -1 and errono is appropriately set
*/
int fmc_slot_geo_address(struct fmc_tkn *tkn, unsigned int slot_n,
unsigned int *ga)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
struct fmc_slot *slot = __fmc_slot_get(carrier, slot_n);
if (!slot)
return -1;
*ga = slot->ga;
return 0;
}
/**
* Get the FMC EEPROM type
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[out] buf: EEPROM type string
* @param[in] count: maximum number of characters in buf
*
* @return on success 0, otherwise -1 and errono is appropriately set
*/
int fmc_slot_eeprom_type_get(struct fmc_tkn *tkn, unsigned int slot_n,
char *buf, size_t count)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
struct fmc_slot *slot = __fmc_slot_get(carrier, slot_n);
if (!slot)
return -1;
strncpy(buf, slot->eeprom_type, MIN(count, MAX_SYS_LEN));
return 0;
}
/**
* Set the FMC EEPROM type
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[in] buf: EEPROM type string
* @param[in] count: maximum number of characters in buf
*
* @return on success 0, otherwise -1 and errono is appropriately set
*/
int fmc_slot_eeprom_type_set(struct fmc_tkn *tkn, unsigned int slot_n,
const char *buf, size_t count)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
struct fmc_slot *slot = __fmc_slot_get(carrier, slot_n);
int err;
if (!slot)
return -1;
err = __fmc_slot_eeprom_type_set(carrier, slot_n, buf, count);
__fmc_slot_eeprom_type_get(carrier, slot_n, slot->eeprom_type, count);
return err;
}
/**
* Get the FMC EEPROM size
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[out] size: EEPROM size in Bytes
*
* @return on success 0, otherwise -1 and errono is appropriately set
*/
int fmc_slot_eeprom_size(struct fmc_tkn *tkn, unsigned int slot_n,
unsigned int *size)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
struct fmc_slot *slot = __fmc_slot_get(carrier, slot_n);
char len_c[8];
unsigned long len;
strncpy(len_c, slot->eeprom_type + 3, sizeof(len_c));
len = strtol(len_c, NULL, 10);
if (errno == ERANGE || errno == EINVAL)
return - 1;
*size = (len * 1024) / 8;
return 0;
}
/**
* Read FMC EEPROM
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[out] buf: EEPROM content
* @param[in] len: number of Bytes to read
* @param[in] offset: start reading from this EEPROM offset
*
* @return on success the number of Bytes read, otherwise -1 and errono is
* appropriately set
*/
int fmc_slot_eeprom_read(struct fmc_tkn *tkn, unsigned int slot_n,
char *buf, size_t len, off_t offset)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
char path[MAX_PATH_LEN];
int fd;
int ret;
if (!fmc_slot_is_present(tkn, slot_n)) {
errno = FMC_ERR_NOPRES;
return -1;
}
snprintf(path, MAX_PATH_LEN,
"/sys/class/fmc/fmc-carrier-%u/fmc-slot-%u.%u/fru_eeprom/eeprom",
carrier->id, carrier->id, slot_n);
fd = open(path, O_RDONLY);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
ret = read(fd, buf, len);
close(fd);
return ret;
}
/**
* Write FMC EEPROM
* @param[in] tkn: FMC carrier token
* @param[in] slot_n: FMC slot number (LUN)
* @param[in] buf: EEPROM content
* @param[in] len: number of Bytes to read
* @param[in] offset: start writing from this EEPROM offset
*
* @return on success the number of Bytes worte, otherwise -1 and errono is
* appropriately set
*/
int fmc_slot_eeprom_write(struct fmc_tkn *tkn, unsigned int slot_n,
const char *buf, size_t len, off_t offset)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
char path[MAX_PATH_LEN];
int fd;
int ret;
if (!fmc_slot_is_present(tkn, slot_n)) {
errno = FMC_ERR_NOPRES;
return -1;
}
snprintf(path, MAX_PATH_LEN,
"/sys/class/fmc/fmc-carrier-%u/fmc-slot-%u.%u/fru_eeprom/eeprom",
carrier->id, carrier->id, slot_n);
fd = open(path, O_WRONLY);
if (fd < 0)
return -1;
ret = lseek(fd, offset, SEEK_SET);
if (ret > 0)
ret = write(fd, buf, len);
close(fd);
return ret;
}
/**
* Get FMC carrier name
* @param[in] tkn: FMC carrier token
* @param[out] buf: EEPROM content
* @param[in] count: maximum number of characters in buf
*
* @return on success 0, otherwise -1 and errono is appropriately set
*/
int fmc_carrier_name(struct fmc_tkn *tkn, char *buf, size_t count)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
strncpy(buf, carrier->name, count);
return 0;
}
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
/**
* Open an FMC carrier
*
* @param[in] carrier_id: carrier identifier
* @return on success a valid pointer, otherwise NULL and errono is
* appropriately set
*/
struct fmc_tkn *fmc_carrier_open(unsigned int carrier_id)
{
struct fmc_carrier *carr;
int ret;
carr = malloc(sizeof(*carr));
if (!carr)
return NULL;
memset(carr, 0, sizeof(*carr));
carr->id = carrier_id;
ret = fmc_carrier_slots_init(carr);
if (ret < 0)
goto err_slots;
return (struct fmc_tkn *)carr;
err_slots:
free(carr);
return NULL;
}
/**
* Close a previously opened FMC carrier
*
* @param[in] tkn: FMC carrier token
*/
void fmc_carrier_close(struct fmc_tkn *tkn)
{
if (tkn) {
struct fmc_carrier *carr = (struct fmc_carrier *)tkn;
fmc_carrier_slots_cleanup(carr);
free(carr);
}
}
/**
* Identifiers list of available carrier
* @return on success a list of identifiers terminated by
* FMC_ID_INVALID
*/
unsigned int *fmc_carrier_id_list_get(void)
{
glob_t g;
int err, i;
unsigned int *id_list;
err = glob("/sys/class/fmc/fmc-carrier-*", 0, NULL, &g);
if (err == GLOB_NOMATCH)
goto err_glob;
id_list = calloc(g.gl_pathc + 1, sizeof(unsigned int));
if (!id_list)
goto err_alloc;
for (i = 0; i < g.gl_pathc; i++) {
int ret = sscanf(g.gl_pathv[i],
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
&id_list[i]);
if (ret != 1)
goto err_scanf;
}
id_list[i] = FMC_ID_INVALID;
globfree(&g);
return id_list;
err_scanf:
free(id_list);
err_alloc:
globfree(&g);
err_glob:
return NULL;
}
/**
* Identifiers list of available slots on a given carrier
* @return on success a list of identifiers terminated by
* FMC_ID_INVALID
*/
unsigned int *fmc_slot_id_list_get(struct fmc_tkn *tkn)
{
struct fmc_carrier *carrier = (struct fmc_carrier *)tkn;
unsigned int *id_list;
int i;
id_list = calloc(carrier->n_slots + 1, sizeof(unsigned int));
if (!id_list)
goto err_alloc;
for (i = 0; i < carrier->n_slots; ++i)
id_list[i] = carrier->slot[i]->id;
id_list[i] = FMC_ID_INVALID;
return id_list;
err_alloc:
return NULL;
}
static void fmc_id_list_put(unsigned int *id_list)
{
free(id_list);
}
void fmc_carrier_id_list_put(unsigned int *id_list)
{
fmc_id_list_put(id_list);
}
void fmc_slot_id_list_put(unsigned int *id_list)
{
fmc_id_list_put(id_list);
}