add tai_sff

Signed-off-by: Wataru Ishida <ishida@nel-america.com>
This commit is contained in:
Wataru Ishida
2020-02-03 15:11:37 -08:00
committed by Wataru Ishida
parent 29892bb73f
commit 684ed69d3a
11 changed files with 1183 additions and 0 deletions

3
.gitmodules vendored
View File

@@ -4,3 +4,6 @@
[submodule "tai_mux/oopt-tai"]
path = tai_mux/oopt-tai
url = https://github.com/Telecominfraproject/oopt-tai.git
[submodule "tai_sff/oopt-tai"]
path = tai_sff/oopt-tai
url = https://github.com/Telecominfraproject/oopt-tai

View File

@@ -15,6 +15,7 @@ build instructions. The repo currently contains the following components:
* tai_ac400 - The TAI adapter code for the Acacia AC400 module
* tai_mux - A TAI adapter which multiplexes access to multiple, possible different, TAI adapters.
* tai_sff - A TAI adapter code for SFF transceiver
For more information about:
* TAI: https://github.com/Telecominfraproject/oopt-tai

3
tai_sff/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
*.so
test/test
build

201
tai_sff/LICENSE Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

74
tai_sff/Makefile Normal file
View File

@@ -0,0 +1,74 @@
PROG := libtai.so
ifndef TAI_DIR
TAI_DIR := oopt-tai
endif
ifndef TAI_LIB_DIR
TAI_LIB_DIR := $(TAI_DIR)/tools/lib
endif
ifndef TAI_FRAMEWORK_DIR
TAI_FRAMEWORK_DIR := $(TAI_DIR)/tools/framework
endif
ifndef TAI_META_LIBRARY
TAI_META_LIBRARY := $(TAI_DIR)/meta/libmetatai.so
endif
ifndef TAI_DOCKER_CMD
TAI_DOCKER_CMD := bash
endif
ifndef TAI_SFF_BUILDDIR
TAI_SFF_BUILDDIR := build
endif
CFLAGS := -std=c++17 -g3 -shared -fPIC -DTAI_EXPOSE_PLATFORM -fno-gnu-unique
INCLUDES := -I $(TAI_DIR)/inc -I $(TAI_DIR)/meta -I $(TAI_LIB_DIR) -I $(TAI_FRAMEWORK_DIR) -include sff.hpp
BUILDDIR := $(TAI_SFF_BUILDDIR)
SOURCES := $(wildcard *.cpp $(TAI_LIB_DIR)/*.cpp $(TAI_FRAMEWORK_DIR)/*.cpp)
HEADERS := $(wildcard *.hpp $(TAI_LIB_DIR)/*.hpp $(TAI_FRAMEWORK_DIR)/*.hpp)
OBJECTS := $(addprefix $(BUILDDIR)/,$(SOURCES:%.cpp=%.o))
DEPS := $(addprefix $(BUILDDIR)/,$(SOURCES:%.cpp=%.d))
.PHONY = all meta test cmd docker docker-image bash clean clean-all
all: $(PROG)
$(PROG): $(TAI_META_LIBRARY) $(OBJECTS) $(HEADERS)
$(CXX) $(CFLAGS) $(INCLUDES) -shared $(OBJECTS) -o $@ -ldl -lpthread -L $(dir $(TAI_META_LIBRARY)) -lmetatai
$(TAI_META_LIBRARY): $(wildcard custom_attrs/*)
TAI_META_CUSTOM_FILES="$(abspath $(wildcard custom_attrs/*))" $(MAKE) -C $(@D)
meta: $(TAI_META_LIBRARY)
$(BUILDDIR)/%.o: %.cpp Makefile
mkdir -p $(@D)
$(CXX) $(CFLAGS) $(INCLUDES) -c $< -MMD -MP -MF $(BUILDDIR)/$*.d -o $@
-include $(DEPS)
test:
$(MAKE) -C tests
cmd:
TAI_DOCKER_CMD="$(TAI_DOCKER_CMD)" TAI_DOCKER_RUN_OPTION="--privileged --net=host -it --rm" TAI_DOCKER_MOUNT="`pwd`:/data" $(MAKE) -C $(TAI_DIR) $@
docker:
TAI_DOCKER_CMD="make" $(MAKE) cmd
docker-image:
$(MAKE) -C $(TAI_DIR) $@
bash:
$(MAKE) cmd
clean:
$(RM) -r $(BUILDDIR) $(TARGET)
clean-all: clean
$(MAKE) -C $(TAI_DIR) clean

82
tai_sff/README.md Normal file
View File

@@ -0,0 +1,82 @@
SFF TAI implementation
===
SFF TAI library ( `libtai-sff.so` ) is a TAI library to support SFF transceivers
like SFP, QSFP+, QSFP28. It uses the [optoe](https://github.com/opencomputeproject/oom/tree/master/optoe) driver to
access EEPROM in the transceivers.
The location used to identify the transceiver is the sysfs directory which the optoe driver creates.
```
> list
module: /sys/bus/i2c/devices/18-0050 0x1000000000009
hostif: 0 0x2000000000900
netif: 0 0x3000000000900
netif: 1 0x3000000000901
netif: 2 0x3000000000902
netif: 3 0x3000000000903
module: /sys/bus/i2c/devices/19-0050 not present
module: /sys/bus/i2c/devices/20-0050 not present
module: /sys/bus/i2c/devices/21-0050 not present
module: /sys/bus/i2c/devices/22-0050 0x1000000000001
hostif: 0 0x2000000000100
netif: 0 0x3000000000100
netif: 1 0x3000000000101
netif: 2 0x3000000000102
netif: 3 0x3000000000103
module: /sys/bus/i2c/devices/23-0050 not present
module: /sys/bus/i2c/devices/24-0050 not present
module: /sys/bus/i2c/devices/25-0050 not present
module: /sys/bus/i2c/devices/26-0050 not present
module: /sys/bus/i2c/devices/27-0050 0x1000000000005
hostif: 0 0x2000000000500
netif: 0 0x3000000000500
netif: 1 0x3000000000501
netif: 2 0x3000000000502
netif: 3 0x3000000000503
module: /sys/bus/i2c/devices/28-0050 not present
module: /sys/bus/i2c/devices/29-0050 not present
module: /sys/bus/i2c/devices/30-0050 not present
module: /sys/bus/i2c/devices/31-0050 not present
module: /sys/bus/i2c/devices/32-0050 not present
module: /sys/bus/i2c/devices/33-0050 not present
module: /sys/bus/i2c/devices/34-0050 not present
module: /sys/bus/i2c/devices/35-0050 not present
module: /sys/bus/i2c/devices/36-0050 not present
module: /sys/bus/i2c/devices/37-0050 not present
module: /sys/bus/i2c/devices/38-0050 not present
module: /sys/bus/i2c/devices/39-0050 not present
module: /sys/bus/i2c/devices/40-0050 not present
module: /sys/bus/i2c/devices/41-0050 not present
module: /sys/bus/i2c/devices/42-0050 not present
module: /sys/bus/i2c/devices/43-0050 not present
module: /sys/bus/i2c/devices/44-0050 not present
module: /sys/bus/i2c/devices/45-0050 not present
module: /sys/bus/i2c/devices/46-0050 not present
module: /sys/bus/i2c/devices/47-0050 not present
module: /sys/bus/i2c/devices/48-0050 not present
module: /sys/bus/i2c/devices/49-0050 not present
>
> module /sys/bus/i2c/devices/18-0050
module(/sys/bus/i2c/devices/18-0050)> get power
3.258300
module(/sys/bus/i2c/devices/18-0050)> get temp
25.535156
module(/sys/bus/i2c/devices/18-0050)> netif 0
module(/sys/bus/i2c/devices/18-0050)/netif(0)> get current-output-power
1.388393
module(/sys/bus/i2c/devices/18-0050)/netif(0)> get current-input-power
1.428273
module(/sys/bus/i2c/devices/18-0050)/netif(0)>
```
### HOW TO BUILD
```
$ git submodule update --init
$ make docker-image
$ make docker
```
### Licensing
`libtai-sff.so` is licensed under the Apache License, Version 2.0. See LICENSE for the full license text.

1
tai_sff/oopt-tai Submodule

Submodule tai_sff/oopt-tai added at 4b9066fd57

227
tai_sff/sff.cpp Normal file
View File

@@ -0,0 +1,227 @@
#include "sff.hpp"
#include "logger.hpp"
#include <glob.h>
namespace tai::sff {
static const std::string SYSFS_I2C_DIR = "/sys/bus/i2c/devices";
Platform::Platform(const tai_service_method_table_t * services) : tai::framework::Platform(services) {
if ( services == nullptr || services->module_presence == nullptr ) {
return;
}
glob_t pglob;
auto ret = glob((SYSFS_I2C_DIR + "/*-0050").c_str(), 0, nullptr, &pglob);
if ( ret != 0 ) {
globfree(&pglob);
TAI_ERROR("glob failed");
throw Exception(TAI_STATUS_FAILURE);
}
for ( int i = 0; i < pglob.gl_pathc; i++ ) {
auto loc = std::string(pglob.gl_pathv[i]);
auto eeprom = std::ifstream(loc + "/eeprom");
if ( !eeprom ) {
continue;
}
auto fsm = std::make_shared<sff::FSM>(loc, services);
if ( fsm->start() < 0 ) {
TAI_ERROR("failed to start FSM for module %s", loc.c_str());
throw Exception(TAI_STATUS_FAILURE);
}
m_fsms[loc] = fsm;
}
globfree(&pglob);
}
tai_status_t Platform::create(tai_object_type_t type, tai_object_id_t module_id, uint32_t count, const tai_attribute_t *list, tai_object_id_t *id) {
std::shared_ptr<tai::framework::BaseObject> obj;
try {
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
{
tai::framework::Location loc;
for ( auto i = 0; i < count; i++ ) {
if ( list[i].id == TAI_MODULE_ATTR_LOCATION ) {
loc = tai::framework::Location(list[i].value.charlist.list, list[i].value.charlist.count);
break;
}
}
if ( loc == "" ) {
return TAI_STATUS_MANDATORY_ATTRIBUTE_MISSING;
}
S_FSM fsm;
if ( m_services != nullptr && m_services->module_presence != nullptr ) {
auto it = m_fsms.find(loc);
if ( it == m_fsms.end() ) {
return TAI_STATUS_INVALID_PARAMETER;
}
fsm = std::dynamic_pointer_cast<sff::FSM>(m_fsms[loc]);
if ( !fsm->is_present() ) {
TAI_ERROR("module is not present: %s", loc.c_str());
return TAI_STATUS_FAILURE;
}
} else {
auto it = m_fsms.find(loc);
if ( it != m_fsms.end() ) {
TAI_ERROR("FSM already exists for module: %s", loc.c_str());
return TAI_STATUS_ITEM_ALREADY_EXISTS;
}
fsm = std::make_shared<sff::FSM>(loc, m_services);
m_fsms[loc] = fsm;
if ( fsm->start() < 0 ) {
TAI_ERROR("failed to start FSM for module %s", loc.c_str());
return TAI_STATUS_FAILURE;
}
}
auto m = std::make_shared<Module>(count, list, fsm);
if ( fsm->set_module(m) < 0 ) {
TAI_ERROR("failed to set module to FSM for module %s", loc.c_str());
return TAI_STATUS_FAILURE;
}
obj = m;
}
break;
case TAI_OBJECT_TYPE_NETWORKIF:
case TAI_OBJECT_TYPE_HOSTIF:
{
auto t = static_cast<tai_object_type_t>(module_id >> OBJECT_TYPE_SHIFT);
if ( t != TAI_OBJECT_TYPE_MODULE ) {
return TAI_STATUS_INVALID_OBJECT_ID;
}
auto it = m_objects.find(module_id);
if ( it == m_objects.end() ) {
return TAI_STATUS_UNINITIALIZED;
}
auto module = std::dynamic_pointer_cast<Module>(it->second);
if ( type == TAI_OBJECT_TYPE_NETWORKIF ) {
auto netif = std::make_shared<NetIf>(module, count, list);
module->fsm()->set_netif(netif, (netif->id() & 0xff));
obj = netif;
} else {
auto hostif = std::make_shared<HostIf>(module, count, list);
module->fsm()->set_hostif(hostif, (hostif->id() & 0xff));
obj = hostif;
}
}
break;
default:
return TAI_STATUS_NOT_SUPPORTED;
}
} catch (tai_status_t e) {
return e;
} catch (...) {
return TAI_STATUS_FAILURE;
}
auto oid = obj->id();
auto it = m_objects.find(oid);
if ( it != m_objects.end() ) {
return TAI_STATUS_ITEM_ALREADY_EXISTS;
}
m_objects[oid] = obj;
*id = oid;
return TAI_STATUS_SUCCESS;
}
tai_object_type_t Platform::get_object_type(tai_object_id_t id) {
auto it = m_objects.find(id);
if ( it == m_objects.end() ) {
return TAI_OBJECT_TYPE_NULL;
}
auto type = static_cast<tai_object_type_t>(id >> OBJECT_TYPE_SHIFT);
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
case TAI_OBJECT_TYPE_NETWORKIF:
case TAI_OBJECT_TYPE_HOSTIF:
return type;
}
return TAI_OBJECT_TYPE_NULL;
}
tai_object_id_t Platform::get_module_id(tai_object_id_t id) {
auto it = m_objects.find(id);
if ( it == m_objects.end() ) {
return TAI_NULL_OBJECT_ID;
}
auto type = static_cast<tai_object_type_t>(id >> OBJECT_TYPE_SHIFT);
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
return id;
case TAI_OBJECT_TYPE_NETWORKIF:
case TAI_OBJECT_TYPE_HOSTIF:
{
auto idx = ((id >> 8) & 0xff);
auto module_id = static_cast<tai_object_id_t>(uint64_t(TAI_OBJECT_TYPE_MODULE) << OBJECT_TYPE_SHIFT | idx);
auto it = m_objects.find(module_id);
if ( it == m_objects.end() ) {
return TAI_NULL_OBJECT_ID;
}
return module_id;
}
}
return TAI_OBJECT_TYPE_NULL;
}
tai_status_t attribute_getter(tai_attribute_t* const attribute, void* user) {
auto ctx = reinterpret_cast<context*>(user);
return ctx->fsm->get(ctx->type, ctx->oid, attribute);
}
tai_status_t attribute_setter(const tai_attribute_t* const attribute, FSMState* state, void* user) {
auto ctx = reinterpret_cast<context*>(user);
return ctx->fsm->set(ctx->type, ctx->oid, attribute, state);
}
static const tai_attribute_value_t default_tai_module_num_network_interfaces = {
.u32 = SFF_NUM_NETIF,
};
static const tai_attribute_value_t default_tai_module_num_host_interfaces = {
.u32 = SFF_NUM_HOSTIF,
};
using M = AttributeInfo<TAI_OBJECT_TYPE_MODULE>;
using N = AttributeInfo<TAI_OBJECT_TYPE_NETWORKIF>;
using H = AttributeInfo<TAI_OBJECT_TYPE_HOSTIF>;
// sadly 'auto' can't be used here
template <> const AttributeInfoMap<TAI_OBJECT_TYPE_MODULE> Config<TAI_OBJECT_TYPE_MODULE>::m_info {
sff::M(TAI_MODULE_ATTR_LOCATION),
sff::M(TAI_MODULE_ATTR_VENDOR_NAME)
.set_getter(&sff::attribute_getter),
sff::M(TAI_MODULE_ATTR_VENDOR_PART_NUMBER)
.set_getter(&sff::attribute_getter),
sff::M(TAI_MODULE_ATTR_VENDOR_SERIAL_NUMBER)
.set_getter(&sff::attribute_getter),
sff::M(TAI_MODULE_ATTR_NUM_NETWORK_INTERFACES)
.set_default(&tai::sff::default_tai_module_num_network_interfaces),
sff::M(TAI_MODULE_ATTR_NUM_HOST_INTERFACES)
.set_default(&tai::sff::default_tai_module_num_host_interfaces),
sff::M(TAI_MODULE_ATTR_OPER_STATUS),
sff::M(TAI_MODULE_ATTR_TEMP)
.set_getter(&sff::attribute_getter),
sff::M(TAI_MODULE_ATTR_POWER)
.set_getter(&sff::attribute_getter),
sff::M(TAI_MODULE_ATTR_NOTIFY),
};
template <> const AttributeInfoMap<TAI_OBJECT_TYPE_NETWORKIF> Config<TAI_OBJECT_TYPE_NETWORKIF>::m_info {
sff::N(TAI_NETWORK_INTERFACE_ATTR_INDEX),
sff::N(TAI_NETWORK_INTERFACE_ATTR_CURRENT_OUTPUT_POWER)
.set_getter(&sff::attribute_getter),
sff::N(TAI_NETWORK_INTERFACE_ATTR_CURRENT_INPUT_POWER)
.set_getter(&sff::attribute_getter),
sff::N(TAI_NETWORK_INTERFACE_ATTR_NOTIFY),
};
template <> const AttributeInfoMap<TAI_OBJECT_TYPE_HOSTIF> Config<TAI_OBJECT_TYPE_HOSTIF>::m_info {
sff::H(TAI_HOST_INTERFACE_ATTR_INDEX),
};
}

113
tai_sff/sff.hpp Normal file
View File

@@ -0,0 +1,113 @@
#ifndef __SFF_HPP__
#define __SFF_HPP__
#include "platform.hpp"
#include "logger.hpp"
#include "sff_fsm.hpp"
#include <sys/timerfd.h>
#include <cmath>
namespace tai::sff {
using namespace tai::framework;
const uint8_t OBJECT_TYPE_SHIFT = 48;
class Platform : public tai::framework::Platform {
public:
Platform(const tai_service_method_table_t * services);
tai_status_t create(tai_object_type_t type, tai_object_id_t module_id, uint32_t attr_count, const tai_attribute_t * const attr_list, tai_object_id_t *id);
tai_status_t remove(tai_object_id_t id) {
return TAI_STATUS_NOT_SUPPORTED;
}
tai_object_type_t get_object_type(tai_object_id_t id);
tai_object_id_t get_module_id(tai_object_id_t id);
};
struct context {
S_FSM fsm;
tai_object_type_t type;
tai_object_id_t oid;
};
template<tai_object_type_t T>
class Object : public tai::framework::Object<T> {
public:
Object(uint32_t count, const tai_attribute_t *list, S_FSM fsm) : m_context{fsm, T}, tai::framework::Object<T>(count, list, fsm, reinterpret_cast<void*>(&m_context)) {}
tai_object_id_t id() const {
return m_context.oid;
}
protected:
context m_context;
};
class Module : public Object<TAI_OBJECT_TYPE_MODULE> {
public:
Module(uint32_t count, const tai_attribute_t *list, S_FSM fsm) : m_fsm(fsm), Object(count, list, fsm) {
auto loc = fsm->location();
std::ifstream ifs(loc + "/port_name");
if ( !ifs ) {
throw Exception(TAI_STATUS_ITEM_NOT_FOUND);
}
std::string buf;
ifs >> buf;
int i = -1;
std::sscanf(buf.c_str(), "port%d", &i);
if ( i < 0 ) {
TAI_ERROR("failed to parse port_name: %s", buf.c_str());
throw Exception(TAI_STATUS_ITEM_NOT_FOUND);
}
m_context.oid = static_cast<tai_object_id_t>(uint64_t(TAI_OBJECT_TYPE_MODULE) << OBJECT_TYPE_SHIFT | i);
}
S_FSM fsm() {
return m_fsm;
}
private:
S_FSM m_fsm;
};
class NetIf : public Object<TAI_OBJECT_TYPE_NETWORKIF> {
public:
NetIf(S_Module module, uint32_t count, const tai_attribute_t *list) : Object(count, list, module->fsm()) {
int index = -1;
for ( auto i = 0; i < count; i++ ) {
if ( list[i].id == TAI_NETWORK_INTERFACE_ATTR_INDEX ) {
index = list[i].value.u32;
break;
}
}
if ( index < 0 ) {
throw Exception(TAI_STATUS_MANDATORY_ATTRIBUTE_MISSING);
}
m_context.oid = static_cast<tai_object_id_t>(uint64_t(TAI_OBJECT_TYPE_NETWORKIF) << OBJECT_TYPE_SHIFT | (module->id() & 0xff) << 8 | index);
}
};
class HostIf : public Object<TAI_OBJECT_TYPE_HOSTIF> {
public:
HostIf(S_Module module, uint32_t count, const tai_attribute_t *list) : Object(count, list, module->fsm()) {
int index = -1;
for ( auto i = 0; i < count; i++ ) {
if ( list[i].id == TAI_HOST_INTERFACE_ATTR_INDEX ) {
index = list[i].value.u32;
break;
}
}
if ( index < 0 ) {
throw Exception(TAI_STATUS_MANDATORY_ATTRIBUTE_MISSING);
}
m_context.oid = static_cast<tai_object_id_t>(uint64_t(TAI_OBJECT_TYPE_HOSTIF) << OBJECT_TYPE_SHIFT | (module->id() & 0xff) << 8 | index);
}
};
};
#ifdef TAI_EXPOSE_PLATFORM
using tai::sff::Platform;
#endif
#endif // __SFF_HPP__

393
tai_sff/sff_fsm.cpp Normal file
View File

@@ -0,0 +1,393 @@
#include "sff_fsm.hpp"
namespace tai::sff {
static const std::string to_string(FSMState s) {
switch (s) {
case FSM_STATE_INIT:
return "init";
case FSM_STATE_WAITING_CONFIGURATION:
return "waiting-configuration";
case FSM_STATE_READY:
return "ready";
case FSM_STATE_END:
return "end";
}
return "unknown";
}
// trim from start (in place)
static inline void ltrim(std::string &s) {
s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
return !std::isspace(ch);
}));
}
// trim from end (in place)
static inline void rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) {
return !std::isspace(ch);
}).base(), s.end());
}
// trim from both ends (in place)
static inline void trim(std::string &s) {
ltrim(s);
rtrim(s);
}
FSM::FSM(Location loc, const tai_service_method_table_t* services) : m_loc(loc), m_services(services), m_module(nullptr), m_netif{}, m_hostif{}, m_no_transit(false) {
m_eeprom = std::ifstream(loc + "/eeprom");
if ( !m_eeprom ) {
TAI_ERROR("failed to open eeprom");
throw Exception(TAI_STATUS_ITEM_NOT_FOUND);
}
}
bool FSM::configured() {
return m_module != nullptr;
}
// this can be called only once during the lifecycle of this FSM
// remove is not considered yet
// returns 0 on success. otherwise -1
int FSM::set_module(S_Module module) {
if ( m_module != nullptr || module == nullptr ) {
return -1;
}
m_module = module;
return 0;
}
// returns 0 on success. otherwise -1
// remove is not considered yet
int FSM::set_netif(S_NetIf netif, int index) {
if ( index < 0 || index >= SFF_NUM_NETIF ) {
return -1;
}
if ( m_netif[index] != nullptr || netif == nullptr ) {
return -1;
}
m_netif[index] = netif;
return 0;
}
// returns 0 on success. otherwise -1
// remove is not considered yet
int FSM::set_hostif(S_HostIf hostif, int index) {
if ( index < 0 || index >= SFF_NUM_HOSTIF ) {
return -1;
}
if ( m_hostif[index] != nullptr || hostif == nullptr ) {
return -1;
}
m_hostif[index] = hostif;
return 0;
}
tai_status_t FSM::remove_module() {
if ( m_module == nullptr ) {
return TAI_STATUS_ITEM_NOT_FOUND;
}
for ( int i = 0; i < SFF_NUM_NETIF; i++ ) {
if ( m_netif[i] != nullptr ) {
TAI_WARN("can't remove a module before removing its sibling netifs");
return TAI_STATUS_OBJECT_IN_USE;
}
}
for ( int i = 0; i < SFF_NUM_HOSTIF; i++ ) {
if ( m_hostif[i] != nullptr ) {
TAI_WARN("can't remove a module before removing its sibling hostifs");
return TAI_STATUS_OBJECT_IN_USE;
}
}
transite(FSM_STATE_END);
while(true) {
auto s = get_state();
if ( s == FSM_STATE_END ) {
break;
}
usleep(100000);
}
m_module = nullptr;
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::remove_netif(int index) {
if ( index < 0 || index >= SFF_NUM_NETIF || m_netif[index] == nullptr ) {
return TAI_STATUS_ITEM_NOT_FOUND;
}
m_netif[index] = nullptr;
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::remove_hostif(int index) {
if ( index < 0 || index >= SFF_NUM_HOSTIF || m_hostif[index] == nullptr ) {
return TAI_STATUS_ITEM_NOT_FOUND;
}
m_hostif[index] = nullptr;
return TAI_STATUS_SUCCESS;
}
fsm_state_change_callback FSM::state_change_cb() {
return std::bind(&FSM::_state_change_cb, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);
}
FSMState FSM::_state_change_cb(FSMState current, FSMState next, void* user) {
if ( m_module != nullptr ) {
tai_attribute_t oper;
oper.id = TAI_MODULE_ATTR_OPER_STATUS;
if ( next == FSM_STATE_READY ) {
oper.value.s32 = TAI_MODULE_OPER_STATUS_READY;
} else {
oper.value.s32 = TAI_MODULE_OPER_STATUS_INITIALIZE;
}
auto& config = m_module->config();
config.set_readonly(oper);
m_module->notify(TAI_MODULE_ATTR_NOTIFY, {
TAI_MODULE_ATTR_OPER_STATUS,
});
}
TAI_INFO("%s -> %s", to_string(current).c_str(), to_string(next).c_str());
return next;
}
bool FSM::is_present() {
char buf;
m_eeprom.read(&buf, 1);
return m_eeprom.good();
}
fsm_callback FSM::cb(FSMState state) {
switch (state) {
case FSM_STATE_INIT:
return std::bind(&FSM::_init_cb, this, std::placeholders::_1, std::placeholders::_2);
case FSM_STATE_WAITING_CONFIGURATION:
return std::bind(&FSM::_waiting_configuration_cb, this, std::placeholders::_1, std::placeholders::_2);
case FSM_STATE_READY:
return std::bind(&FSM::_ready_cb, this, std::placeholders::_1, std::placeholders::_2);
}
return nullptr;
}
// wait eeprom get readable
FSMState FSM::_init_cb(FSMState current, void* user) {
fd_set fs;
auto evfd = get_event_fd();
itimerspec interval = {{1,0}, {0,1}};
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(tfd, 0, &interval, NULL);
FSMState next;
bool first = true, prev;
while (true) {
FD_ZERO(&fs);
FD_SET(tfd, &fs);
FD_SET(evfd, &fs);
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
if (FD_ISSET(evfd, &fs)) {
uint64_t r;
read(evfd, &r, sizeof(uint64_t));
next = next_state();
if ( next == FSM_STATE_END ) {
goto ret;
}
}
if (FD_ISSET(tfd, &fs)) {
uint64_t r;
read(tfd, &r, sizeof(uint64_t));
auto present = is_present();
if ( first || (present != prev) ) {
first = false;
if ( m_services != nullptr && m_services->module_presence != nullptr ) {
m_services->module_presence(present, const_cast<char*>(m_loc.c_str()));
}
}
prev = present;
if ( present ) {
next = FSM_STATE_WAITING_CONFIGURATION;
goto ret;
}
}
}
ret:
close(tfd);
return next;
}
// wait module get created ( check by configured() )
FSMState FSM::_waiting_configuration_cb(FSMState current, void* user) {
fd_set fs;
auto evfd = get_event_fd();
itimerspec interval = {{1,0}, {0,1}};
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(tfd, 0, &interval, NULL);
FSMState next;
while (true) {
FD_ZERO(&fs);
FD_SET(evfd, &fs);
FD_SET(tfd, &fs);
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
if (FD_ISSET(evfd, &fs)) {
uint64_t r;
read(evfd, &r, sizeof(uint64_t));
next = next_state();
goto ret;
}
if (FD_ISSET(tfd, &fs)) {
uint64_t r;
read(tfd, &r, sizeof(uint64_t));
if ( configured() && !m_no_transit ) {
return FSM_STATE_READY;
}
}
}
ret:
close(tfd);
return next;
}
FSMState FSM::_ready_cb(FSMState current, void* user) {
fd_set fs;
auto evfd = get_event_fd();
itimerspec interval = {{10,0}, {0,1}}; // 10s PM interval
auto tfd = timerfd_create(CLOCK_REALTIME, 0);
timerfd_settime(tfd, 0, &interval, NULL);
FSMState next;
while (true) {
FD_ZERO(&fs);
FD_SET(evfd, &fs);
FD_SET(tfd, &fs);
select(FD_SETSIZE, &fs, NULL, NULL, NULL);
if (FD_ISSET(evfd, &fs)) {
uint64_t r;
read(evfd, &r, sizeof(uint64_t));
next = next_state();
goto ret;
}
if (FD_ISSET(tfd, &fs)) {
uint64_t r;
read(tfd, &r, sizeof(uint64_t));
for ( int i = 0; i < SFF_NUM_NETIF; i++ ) {
if ( m_netif[i] != nullptr ) {
m_netif[i]->notify(TAI_NETWORK_INTERFACE_ATTR_NOTIFY, {
TAI_NETWORK_INTERFACE_ATTR_CURRENT_INPUT_POWER,
TAI_NETWORK_INTERFACE_ATTR_CURRENT_OUTPUT_POWER,
});
}
}
if ( m_module != nullptr ) {
m_module->notify(TAI_MODULE_ATTR_NOTIFY, {
TAI_MODULE_ATTR_TEMP,
TAI_MODULE_ATTR_POWER,
});
}
}
}
ret:
close(tfd);
return next;
}
tai_status_t FSM::eeprom_get_str(int address, int size, tai_attribute_t* const attr) {
m_eeprom.seekg(address);
auto p = std::make_unique<char[]>(size);
auto buf = p.get();
m_eeprom.read(buf, size);
std::string s(buf, size);
trim(s);
auto v = attr->value.charlist.count;
attr->value.charlist.count = s.size() + 1;
if ( v < (s.size() + 1)) {
return TAI_STATUS_BUFFER_OVERFLOW;
}
std::strncpy(attr->value.charlist.list, s.c_str(), v);
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::eeprom_get_temp(int address, int size, tai_attribute_t* const attr) {
m_eeprom.seekg(address);
auto p = std::make_unique<char[]>(size);
auto buf = p.get();
m_eeprom.read(buf, size);
auto temp = static_cast<int>(buf[0] * 256 + buf[1]);
if ( temp > 0x7FFF ) {
temp -= 65536;
}
attr->value.flt = static_cast<float>(temp)/256;
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::eeprom_get_voltage(int address, int size, tai_attribute_t* const attr) {
m_eeprom.seekg(address);
auto p = std::make_unique<char[]>(size);
auto buf = p.get();
m_eeprom.read(buf, size);
auto temp = static_cast<int>(buf[0] * 256 + buf[1]);
attr->value.flt = static_cast<float>(temp)/10000;
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::eeprom_get_power_dbm(int address, int size, tai_attribute_t* const attr) {
m_eeprom.seekg(address);
auto p = std::make_unique<char[]>(size);
auto buf = p.get();
m_eeprom.read(buf, size);
auto tmp = static_cast<float>(static_cast<int>(buf[0] * 256 + buf[1]))/10000;
if ( tmp < 0.001 ) {
attr->value.flt = -30; // by convention, -30dBm is the lowest legal value (OOM)
} else {
attr->value.flt = 10 * std::log10(tmp);
}
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::get(tai_object_type_t type, tai_object_id_t oid, tai_attribute_t* const attr) {
switch (type) {
case TAI_OBJECT_TYPE_MODULE:
switch (attr->id) {
case TAI_MODULE_ATTR_VENDOR_NAME:
return eeprom_get_str(148, 16, attr);
case TAI_MODULE_ATTR_VENDOR_PART_NUMBER:
return eeprom_get_str(168, 16, attr);
case TAI_MODULE_ATTR_VENDOR_SERIAL_NUMBER:
return eeprom_get_str(196, 16, attr);
case TAI_MODULE_ATTR_TEMP:
return eeprom_get_temp(22, 2, attr);
case TAI_MODULE_ATTR_POWER:
return eeprom_get_voltage(26, 2, attr);
default:
return TAI_STATUS_NOT_SUPPORTED;
}
case TAI_OBJECT_TYPE_NETWORKIF:
{
auto index = static_cast<int>(oid & 0xff);
switch (attr->id) {
case TAI_NETWORK_INTERFACE_ATTR_CURRENT_INPUT_POWER:
return eeprom_get_power_dbm(34 + index*2, 2, attr);
case TAI_NETWORK_INTERFACE_ATTR_CURRENT_OUTPUT_POWER:
return eeprom_get_power_dbm(50 + index*2, 2, attr);
default:
return TAI_STATUS_NOT_SUPPORTED;
}
}
default:
return TAI_STATUS_NOT_SUPPORTED;
}
return TAI_STATUS_SUCCESS;
}
tai_status_t FSM::set(tai_object_type_t type, tai_object_id_t oid, const tai_attribute_t* const attribute, FSMState* state) {
return TAI_STATUS_NOT_SUPPORTED;
}
};

85
tai_sff/sff_fsm.hpp Normal file
View File

@@ -0,0 +1,85 @@
#ifndef __SFF_FSM_HPP__
#define __SFF_FSM_HPP__
#include "fsm.hpp"
#include <fstream>
#include <cstdio>
#include <atomic>
namespace tai::sff {
using namespace tai::framework;
// Total number of modules
const uint8_t SFF_NUM_MODULE = 4;
// The number of network interface which one module has
const uint8_t SFF_NUM_NETIF = 4;
// The number of host interface which one module has
const uint8_t SFF_NUM_HOSTIF = 1;
class Module;
class NetIf;
class HostIf;
using S_Module = std::shared_ptr<Module>;
using S_NetIf = std::shared_ptr<NetIf>;
using S_HostIf = std::shared_ptr<HostIf>;
class FSM : public tai::framework::FSM {
// requirements to inherit tai::FSM
public:
bool configured();
private:
fsm_state_change_callback state_change_cb();
fsm_callback cb(FSMState state);
public:
FSM(Location loc, const tai_service_method_table_t* services);
int set_module(S_Module module);
int set_netif(S_NetIf netif, int index);
int set_hostif(S_HostIf hostif, int index);
tai_status_t remove_module();
tai_status_t remove_netif(int index);
tai_status_t remove_hostif(int index);
Location location() {
return m_loc;
}
bool is_present();
tai_status_t get(tai_object_type_t type, tai_object_id_t oid, tai_attribute_t* const attribute);
tai_status_t set(tai_object_type_t type, tai_object_id_t oid, const tai_attribute_t* const attribute, FSMState* state);
private:
FSMState _state_change_cb(FSMState current, FSMState next, void* user);
FSMState _init_cb(FSMState current, void* user);
FSMState _waiting_configuration_cb(FSMState current, void* user);
FSMState _ready_cb(FSMState current, void* user);
S_Module m_module;
S_NetIf m_netif[SFF_NUM_NETIF];
S_HostIf m_hostif[SFF_NUM_HOSTIF];
std::atomic<bool> m_no_transit;
const tai_service_method_table_t* m_services;
const Location m_loc;
tai_status_t eeprom_get_str(int address, int size, tai_attribute_t* const attr);
tai_status_t eeprom_get_temp(int address, int size, tai_attribute_t* const attr);
tai_status_t eeprom_get_voltage(int address, int size, tai_attribute_t* const attr);
tai_status_t eeprom_get_power_dbm(int address, int size, tai_attribute_t* const attr);
std::ifstream m_eeprom;
};
using S_FSM = std::shared_ptr<FSM>;
};
#endif