Commit 676deab9 authored by Federico Vaga's avatar Federico Vaga

Merge branch 'release/v1.1.2'

parents 2f091d9c 4ef3400a
Pipeline #1903 failed with stages
......@@ -7,11 +7,23 @@
Changelog
=========
1.1.1 - 2021-07-06
==================
Changed
-------
- doc: improve documentation
Fixed
-----
- sw: Linux device identifier double release
- sw: avoid C+ mangling
1.1.1 - 2020-10-27
==================
Bugfix
------
Fixed
-----
- sw: fix a NULL pointer dereference
1.1.0 - 2020-10-26
......
# include parent_common.mk for buildsystem's defines
# use absolute path for REPO_PARENT
# REPO_PARENT ?=
# -include $(REPO_PARENT)/parent_common.mk
LINUX ?= /lib/modules/$(shell uname -r)/build
SPHINXOPTS = -Drelease=$(shell git describe) -Dversion=$(shell git describe | cut -d "-" -f 1 | tr -d "v")
SPHINXBUILD ?= sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
LINUX=$(LINUX) $(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
# Configuration file for the Sphinx documentation builder.
#
# This file only contains a selection of the most common options. For a full
# list see the documentation:
# https://www.sphinx-doc.org/en/master/usage/configuration.html
import os
import sys
import sphinx
# kernel-doc extension configuration for running Sphinx directly (e.g. by Read
# the Docs). In a normal build, these are supplied from the Makefile via command
# line arguments.
linux = os.getenv("LINUX", None)
if linux is None:
raise Exception("Missing LINUX environment variable")
kerneldoc_bin = os.path.join(linux, 'scripts/kernel-doc')
kerneldoc_srctree = '../../'
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
_path = os.path.join(linux, 'Documentation', 'sphinx')
sys.path.insert(0, os.path.abspath(_path))
from load_config import loadConfig
# -- Project information -----------------------------------------------------
project = 'FMC'
copyright = '2020, Federico Vaga <federico.vaga@cern.ch>'
author = 'Federico Vaga <federico.vaga@cern.ch>'
# -- General configuration ---------------------------------------------------
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'kerneldoc',
'kernel_include',
'kfigure',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The master toctree document.
master_doc = 'index'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
except ImportError:
sys.stderr.write('Warning: The Sphinx \'sphinx_rtd_theme\' HTML theme was not found. Make sure you have the theme installed to produce pretty HTML output. Falling back to the default theme.\n')
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# -----------------------------------------------------------------------------
# Since loadConfig overwrites settings from the global namespace, it has to be
# the last statement in the conf.py file
# -----------------------------------------------------------------------------
loadConfig(globals())
..
Copyright (C) 2019 CERN home.cern
SPDX-License-Identifier: CC-BY-SA-4.0
.. _fmc_device:
==================================
Use The FMC Framework From Devices
==================================
Devices requiring access to FMC information need to get the slot
structure :c:type:`struct fmc_slot` in which they are interested by
calling :c:func:`fmc_slot_get()`. This will increment the device reference
counter, so when you are done with it, remember to release it with
:c:func:`fmc_slot_put()`.
Having a valid pointer to :c:type:`struct fmc_slot` enables the device
to use the following functions :c:func:`fmc_slot_present()` and
:c:func:`fmc_slot_fru_valid()`.
As describe in :ref:`fmc_intro_i2c_dev`, FMC modules may have other I2C
devices connected on the same I2C bus used to access the EEPROM with the
FRU data. The device driver that drives[1] the FMC module may want to
create instances for those I2C devices and then use them. To achieve this
the driver must first get the slot with :c:func:`fmc_slot_get()` then
the standard I2C API can be used to create new devices; the important thing
to remember here is that the I2C address for the new device needs to be
adjusted with the FMC slot geographical address by using
:func:`fmc_slot_i2c_address()`. For example::
struct i2c_board_info info_l = { /* your values*/ };
struct i2c_client *i2cdev;
struct fmc_slot *slot;
slot = fmc_slot_get(parent_dev);
if (!slot) {
/* handle error */
}
info.addr = fmc_slot_i2c_address(info.addr, slot->ga);
i2cdev = i2c_new_device(slot->adapter, &info);
if (!i2cdev) {
/* handle error */
}
..
Copyright (C) 2019 CERN home.cern
Copyright (C) 2020 CERN home.cern
SPDX-License-Identifier: CC-BY-SA-4.0
==============
The FMC Driver
==============
.. _fmc_carrier:
=======================================
Enable FMC Framework For Carrier Boards
=======================================
......@@ -34,6 +37,7 @@ Before being able to register your device as an FMC carrier you should:
is left to the carrier driver which has full knowledge. Then, the carrier
driver informs the FMC framework about which I2C bus must be used to access
an FMC slot. This approach makes the use of the I2C bus more flexible:
- the carrier is free to connect devices on the bus
- the carrier can delegate the FMC framework to handle I2C devices on an
FMC slot.
......@@ -61,9 +65,62 @@ Once a device has been registered as an FMC carrier, automatically the FMC
framework registers a device instance for each slot. For each of them, an
I2C EEPROM instance (type 'at24c02') will be created.
If the carrier driver needs the associated :c:type:`struct fmc_carrier`
If the carrier driver needs the associated :c:type:`struct fmc_carrier <fmc_carrier>`
instance, it can obtained by using :c:func:`fmc_carrier_get()`.
This will increment the device reference counter, so when you are done with
it, remember to release it with :c:func:`fmc_carrier_put()`. But in principle
this should not be necessary because the carrier already has access to all
resources.
.. _fmc_device:
==================================
Use The FMC Framework From Devices
==================================
Devices requiring access to FMC information need to get the slot
structure :c:type:`struct fmc_slot <fmc_slot>` in which they are interested by
calling :c:func:`fmc_slot_get()`. This will increment the device reference
counter, so when you are done with it, remember to release it with
:c:func:`fmc_slot_put()`.
Having a valid pointer to :c:type:`struct fmc_slot <fmc_slot>` enables
the device to use the following functions :c:func:`fmc_slot_present()`
and :c:func:`fmc_slot_fru_valid()`.
As describe in :ref:`fmc_intro_i2c_dev`, FMC modules may have other I2C
devices connected on the same I2C bus used to access the EEPROM with the
FRU data. The device driver that drives[1] the FMC module may want to
create instances for those I2C devices and then use them. To achieve this
the driver must first get the slot with :c:func:`fmc_slot_get()` then
the standard I2C API can be used to create new devices; the important thing
to remember here is that the I2C address for the new device needs to be
adjusted with the FMC slot geographical address by using
:func:`fmc_slot_i2c_address()`. For example::
struct i2c_board_info info_l = { /* your values*/ };
struct i2c_client *i2cdev;
struct fmc_slot *slot;
slot = fmc_slot_get(parent_dev);
if (!slot) {
/* handle error */
}
info.addr = fmc_slot_i2c_address(info.addr, slot->ga);
i2cdev = i2c_new_device(slot->adapter, &info);
if (!i2cdev) {
/* handle error */
}
The FMC Driver API
==================
.. kernel-doc:: include/linux/fmc.h
.. kernel-doc:: include/uapi/linux/ipmi/fru.h
.. kernel-doc:: drivers/fmc/fmc-core.c
:export:
.. kernel-doc:: drivers/fmc/fmc-eeprom.c
:export:
......@@ -9,6 +9,36 @@
FMC User Interface
==================
The SYSFS interface
===================
The user can interact with an FMC carrier and its mezzanines through sysfs.
All attributes are described in the ABI file ``sysfs-class-fmc``
The FMC library `libfmc`
========================
If you do not want to write your own code to access the sysfs attributes, then
you could use the library `libfmc`.
This is a C library that exports the most important FMC features. All actions
from this library requires the access to an FMC carrier card, therefore you
must first use fmc_carrier_open(). This function will give you a *token* that
you use in all other functions. When you are done, you can close the carrier
access with fmc_carrier_close().::
struct fmc_tkn *tkn;
tkn = fmc_carrier_open(0);
/* some code */
fmc_carrier_close(tkn);
In the above example we are opening the FMC carrier number 0. You can get the
list of available FMC carrier numbers with fmc_carrier_id_list_get(). This
function allocates memory, you once you are done call
fmc_carrier_id_list_put(). The same to find all slot numbers for a
given carrier: fmc_slot_id_list_get() fmc_slot_id_list_put().
When you have an FMC carrier token, then you can start using the following
functions: fmc_carrier_name(), fmc_slot_is_present(), fmc_slot_is_fru_valid()
fmc_slot_geo_address(), fmc_slot_eeprom_type_get(), fmc_slot_eeprom_type_set()
fmc_slot_eeprom_size(), fmc_slot_eeprom_read(), fmc_slot_eeprom_write()
......@@ -6,7 +6,8 @@
.. _fmc:
.. toctree::
:maxdepth: 2
:maxdepth: 1
fmc-introduction
fmc-carrier
fmc-device
fmc-driver
fmc-user
......@@ -6,6 +6,7 @@ name: fmc
version: %{?_build_version}
Release: 1%{?dist}
Summary: Linux kernel FMC Layer support
BuildArch: noarch
Group: System/Kernel
License: GPL-2.0-or-later
......@@ -13,6 +14,7 @@ URL: https://gitlab.cern.ch/coht/fmc/
Source0: %{name}-%{version}.tar.gz
Source1: CHANGELOG
BuildRequires: dkms
Requires:dkms
%description
......
......@@ -223,15 +223,11 @@ err_ida:
*/
static void fmc_carrier_del_slot(struct fmc_slot *slot)
{
int id;
if (IS_ERR_OR_NULL(slot))
return;
id = slot->dev.id;
fmc_slot_eeprom_del(slot);
device_unregister(&slot->dev);
ida_simple_remove(&fmc_slot_ida, id);
}
/**
......
......@@ -63,6 +63,10 @@ static void fmc_slot_eeprom_init_default(struct fmc_slot *slot,
/**
* Read from EEPROM
* @slot: FMC slot instance
* @buf: destination buffer
* @offset: EEPROM offset in bytes
* @count: how many bytes to read
*/
ssize_t fmc_slot_eeprom_read(struct fmc_slot *slot,
void *buf, off_t offset, size_t count)
......
......@@ -27,11 +27,11 @@ struct fmc_slot;
#define FMC_SLOT_MAX_I2C 4
/**
/*
* According to the standard, the EEPROM form a mezzanine must be
* at address 0x50 to 0x53
*/
#define FMC_EEPROM_ADDR_SPACE (0x50)
#define FMC_EEPROM_ADDR_SPACE 0x50
/**
* Convert a Geographical Address suffix into an I2C address
......@@ -63,13 +63,15 @@ static inline uint8_t fmc_slot_i2c_address(uint8_t addr, uint8_t ga)
}
/**
* struct fmc_slot - FMC slot instance
* struct fmc_slot - FMC slot instance.
*
* @dev: FMC slot device
* @adapter: I2C bus adapter used to communicate with the slot
* @ga: Geographical Address according to FMC standard
* @lun: slot logical unit number
* @eeprom: I2C adapter used to talk with the mezzanine eeprom
* @macc: operations to access the mezzanine EEPROM
* @at24_data: data for the EEPROM driver
*
* The FMC standard does not say that all the FMC slots are on the same
* I2C bus. So, we must foresee that this is not the case and each FMC
......
......@@ -8,6 +8,11 @@
*/
#ifndef __UAPI_LINUX_IPMI_FRU_H__
#define __UAPI_LINUX_IPMI_FRU_H__
#ifdef __cplusplus
extern "C" {
#endif
#ifdef __KERNEL__
# include <linux/types.h>
# include <linux/string.h>
......@@ -120,7 +125,7 @@ static inline int fru_is_eof(struct fru_type_length *tl)
/* Public checksum verifiers */
static inline int fru_header_cksum_ok(struct fru_common_header *header)
{
uint8_t *ptr = (void *)header;
uint8_t *ptr = (uint8_t *)header;
int i, sum;
for (i = sum = 0; i < sizeof(*header); i++)
......@@ -131,7 +136,7 @@ static inline int fru_header_cksum_ok(struct fru_common_header *header)
static inline int fru_bia_cksum_ok(struct fru_board_info_area *bia)
{
uint8_t *ptr = (void *)bia;
uint8_t *ptr = (uint8_t *)bia;
int i, sum;
for (i = sum = 0; i < 8 * bia->area_len; i++)
......@@ -145,4 +150,8 @@ extern char *fru_get_product_name(struct fru_common_header *header);
extern char *fru_get_serial_number(struct fru_common_header *header);
extern char *fru_get_part_number(struct fru_common_header *header);
#ifdef __cplusplus
}
#endif
#endif /* __UAPI_LINUX_IPMI_FRU_H__ */
......@@ -7,6 +7,10 @@
#ifndef _FMC_CORE_H
#define _FMC_CORE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <stddef.h>
#include <stdbool.h>
#include <sys/types.h>
......@@ -50,4 +54,8 @@ int fmc_slot_eeprom_read(struct fmc_tkn *tkn, unsigned int slot_n,
int fmc_slot_eeprom_write(struct fmc_tkn *tkn, unsigned int slot_n,
const char *buf, size_t len, off_t offset);
#ifdef __cplusplus
}
#endif
#endif /* _FMC_CORE_H */
......@@ -214,7 +214,7 @@ static int __fmc_slot_eeprom_type_get(struct fmc_carrier *carr,
ret = __fmc_sys_read_string(carr, slot_n, "eeprom_type", str, max_len);
if (ret < 0) {
strncpy(str, "unknown", max_len);
strncpy(str, "unknown", max_len - 1);
return ret;
}
......@@ -420,7 +420,7 @@ int fmc_slot_eeprom_size(struct fmc_tkn *tkn, unsigned int slot_n,
unsigned long len;
strncpy(len_c, slot->eeprom_type + 3, sizeof(len_c));
strncpy(len_c, slot->eeprom_type + 3, sizeof(len_c) - 1);
len = strtol(len_c, NULL, 10);
if (errno == ERANGE || errno == EINVAL)
return - 1;
......
......@@ -123,7 +123,7 @@ static void print_slot(struct fmc_tkn *tkn,
int main(int argc, char *argv[])
{
unsigned int *carr_id;
int err;
int err = 0;
int i;
char opt;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment