Skip to content
Snippets Groups Projects
libfmc.c 14.3 KiB
Newer Older
Federico Vaga's avatar
Federico Vaga committed
/*
 * SPDX-License-Identifier: LGPL-3.0-or-later
 * Copyright (C) 2020 CERN (www.cern.ch)
 * Author: Federico Vaga <federico.vaga@cern.ch>
 */

#include <stdlib.h>
Federico Vaga's avatar
Federico Vaga committed
#include <stdio.h>
#include <stdbool.h>
#include <stddef.h>
#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>
Federico Vaga's avatar
Federico Vaga committed
#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
#define MAX_SYS_LEN 32

struct fmc_slot {
	unsigned int id;
	unsigned long flags;
	unsigned int ga;
	char eeprom_type[MAX_SYS_LEN];
};

struct fmc_carrier {
	unsigned int id;
	char name[MAX_SYS_LEN];
	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];
}

Federico Vaga's avatar
Federico Vaga committed
/**
 * 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];
	errno = FMC_ERR_SLOT_NODEV;
	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';
		}
	}
	return ret;
}

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;
	ret = sscanf(buf, "%u", val);
	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) {
		strncpy(str, "unknown", max_len);
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;
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);
		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
 */
Federico Vaga's avatar
Federico Vaga committed
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;
	}

Federico Vaga's avatar
Federico Vaga committed
	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);
	if (ret >= 0)
Federico Vaga's avatar
Federico Vaga committed
		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
 */
Federico Vaga's avatar
Federico Vaga committed
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;
	}

Federico Vaga's avatar
Federico Vaga committed
	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;
}

/**
 * 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],
				 "/sys/class/fmc/fmc-carrier-%u",
				 &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);
}