From 684ed69d3a9d28785fc86b54eebdfbc547880eab Mon Sep 17 00:00:00 2001 From: Wataru Ishida Date: Mon, 3 Feb 2020 15:11:37 -0800 Subject: [PATCH] add tai_sff Signed-off-by: Wataru Ishida --- .gitmodules | 3 + README.md | 1 + tai_sff/.gitignore | 3 + tai_sff/LICENSE | 201 ++++++++++++++++++++++ tai_sff/Makefile | 74 +++++++++ tai_sff/README.md | 82 +++++++++ tai_sff/oopt-tai | 1 + tai_sff/sff.cpp | 227 +++++++++++++++++++++++++ tai_sff/sff.hpp | 113 +++++++++++++ tai_sff/sff_fsm.cpp | 393 ++++++++++++++++++++++++++++++++++++++++++++ tai_sff/sff_fsm.hpp | 85 ++++++++++ 11 files changed, 1183 insertions(+) create mode 100644 tai_sff/.gitignore create mode 100644 tai_sff/LICENSE create mode 100644 tai_sff/Makefile create mode 100644 tai_sff/README.md create mode 160000 tai_sff/oopt-tai create mode 100644 tai_sff/sff.cpp create mode 100644 tai_sff/sff.hpp create mode 100644 tai_sff/sff_fsm.cpp create mode 100644 tai_sff/sff_fsm.hpp diff --git a/.gitmodules b/.gitmodules index cdd0655..2ff7978 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/README.md b/README.md index 39dbc4a..45cc86f 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/tai_sff/.gitignore b/tai_sff/.gitignore new file mode 100644 index 0000000..0378430 --- /dev/null +++ b/tai_sff/.gitignore @@ -0,0 +1,3 @@ +*.so +test/test +build diff --git a/tai_sff/LICENSE b/tai_sff/LICENSE new file mode 100644 index 0000000..5c304d1 --- /dev/null +++ b/tai_sff/LICENSE @@ -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. diff --git a/tai_sff/Makefile b/tai_sff/Makefile new file mode 100644 index 0000000..38173f3 --- /dev/null +++ b/tai_sff/Makefile @@ -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 diff --git a/tai_sff/README.md b/tai_sff/README.md new file mode 100644 index 0000000..05af370 --- /dev/null +++ b/tai_sff/README.md @@ -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. diff --git a/tai_sff/oopt-tai b/tai_sff/oopt-tai new file mode 160000 index 0000000..4b9066f --- /dev/null +++ b/tai_sff/oopt-tai @@ -0,0 +1 @@ +Subproject commit 4b9066fd57b7eef147184a8ef25c8f85b0b23528 diff --git a/tai_sff/sff.cpp b/tai_sff/sff.cpp new file mode 100644 index 0000000..efbe154 --- /dev/null +++ b/tai_sff/sff.cpp @@ -0,0 +1,227 @@ +#include "sff.hpp" +#include "logger.hpp" + +#include + +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(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 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(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(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(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(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(it->second); + if ( type == TAI_OBJECT_TYPE_NETWORKIF ) { + auto netif = std::make_shared(module, count, list); + module->fsm()->set_netif(netif, (netif->id() & 0xff)); + obj = netif; + } else { + auto hostif = std::make_shared(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(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(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(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(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(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; + using N = AttributeInfo; + using H = AttributeInfo; + + // sadly 'auto' can't be used here + template <> const AttributeInfoMap Config::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 Config::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 Config::m_info { + sff::H(TAI_HOST_INTERFACE_ATTR_INDEX), + }; +} diff --git a/tai_sff/sff.hpp b/tai_sff/sff.hpp new file mode 100644 index 0000000..d91bbda --- /dev/null +++ b/tai_sff/sff.hpp @@ -0,0 +1,113 @@ +#ifndef __SFF_HPP__ +#define __SFF_HPP__ + +#include "platform.hpp" +#include "logger.hpp" +#include "sff_fsm.hpp" + +#include +#include + +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 + class Object : public tai::framework::Object { + public: + Object(uint32_t count, const tai_attribute_t *list, S_FSM fsm) : m_context{fsm, T}, tai::framework::Object(count, list, fsm, reinterpret_cast(&m_context)) {} + + tai_object_id_t id() const { + return m_context.oid; + } + + protected: + context m_context; + }; + + class Module : public Object { + 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(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 { + 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(uint64_t(TAI_OBJECT_TYPE_NETWORKIF) << OBJECT_TYPE_SHIFT | (module->id() & 0xff) << 8 | index); + } + }; + + class HostIf : public Object { + 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(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__ diff --git a/tai_sff/sff_fsm.cpp b/tai_sff/sff_fsm.cpp new file mode 100644 index 0000000..b4bb2a3 --- /dev/null +++ b/tai_sff/sff_fsm.cpp @@ -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(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(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(size); + auto buf = p.get(); + m_eeprom.read(buf, size); + auto temp = static_cast(buf[0] * 256 + buf[1]); + if ( temp > 0x7FFF ) { + temp -= 65536; + } + attr->value.flt = static_cast(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(size); + auto buf = p.get(); + m_eeprom.read(buf, size); + auto temp = static_cast(buf[0] * 256 + buf[1]); + attr->value.flt = static_cast(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(size); + auto buf = p.get(); + m_eeprom.read(buf, size); + auto tmp = static_cast(static_cast(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(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; + } + +}; diff --git a/tai_sff/sff_fsm.hpp b/tai_sff/sff_fsm.hpp new file mode 100644 index 0000000..4429b54 --- /dev/null +++ b/tai_sff/sff_fsm.hpp @@ -0,0 +1,85 @@ +#ifndef __SFF_FSM_HPP__ +#define __SFF_FSM_HPP__ + +#include "fsm.hpp" + +#include +#include +#include + +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; + using S_NetIf = std::shared_ptr; + using S_HostIf = std::shared_ptr; + + 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 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; + +}; + +#endif