From fc4b2a554d07e566f0b404c85c30aaa4db27d537 Mon Sep 17 00:00:00 2001 From: Noel Eck Date: Fri, 1 Jun 2018 14:59:39 -0700 Subject: [PATCH] Platform_extender: FT4222 library refactor Updates to make the FT4222 platform extender more usable. Previous implementation opened libft4222.so and loaded symbols as needed. This implementation removes dynamic loading of libft4222 in favor of creating a new shared libary which links against libft4222.so. The dynamic loading is now done in mraa.c. One C method is exposed in libmraa-platform-ft4222.so for finding/initializing an FT4222: mraa_platform_t mraa_usb_platform_extender(mraa_board_t* board); Mraa.c attempts to open this platform library and calls the mraa_usb_platform_extender method. If an ftdi4222 is connected, the user gets added IO from the extender. If no FT4222 device is connected, continue as normal. * Create a new platform library for the FT4222 * Expose only 1 C method from the library - mraa_usb_platform_extender * libmraa-platform-ft4222.so contains CXX code (as well as previous C code). All *allocs have been removed in favor of global C++ stl containers. * Previously, the FT4222 would only initialize correctly if 2 ftdi devices existed. Now, initialize FT4222 devices based on the device id. * Many fixes for various problems with the FT4222 * Added unit test for platform extender (minimal functionality w/o hw) * Updated to FindFtd4222.cmake module to handle standard arguments * Removed CMAKE_C_FLAGS addition of -DFTDID2XX and -DFTDI4222 since these are NOT used anywhere in source. * Building the FTDI4222 shim requires libft4222.h which requires ftd2xx.h. Updated CMakeLists.txt to require both when building the shim. Signed-off-by: Noel Eck --- cmake/modules/FindFtd2xx.cmake | 19 +- cmake/modules/FindFtd4222.cmake | 19 +- docker-compose.yaml | 1 + include/mraa_internal.h | 7 - include/mraa_internal_types.h | 12 + src/CMakeLists.txt | 14 +- src/mraa.c | 31 +- src/usb/CMakeLists.txt | 13 +- src/usb/ft4222/CMakeLists.txt | 19 + src/usb/ft4222/ftdi_ft4222.cxx | 1451 +++++++++++++++++ .../usb/ft4222/ftdi_ft4222.hpp | 21 +- src/usb/ftdi_ft4222.c | 1161 ------------- src/usb/usb.c | 70 - tests/unit/CMakeLists.txt | 13 +- .../platform_extender/platform_extender.cxx | 31 + 15 files changed, 1591 insertions(+), 1291 deletions(-) create mode 100644 src/usb/ft4222/CMakeLists.txt create mode 100644 src/usb/ft4222/ftdi_ft4222.cxx rename include/usb/ftdi_ft4222.h => src/usb/ft4222/ftdi_ft4222.hpp (70%) delete mode 100644 src/usb/ftdi_ft4222.c delete mode 100644 src/usb/usb.c create mode 100644 tests/unit/platform_extender/platform_extender.cxx diff --git a/cmake/modules/FindFtd2xx.cmake b/cmake/modules/FindFtd2xx.cmake index 40fedcf..0732589 100644 --- a/cmake/modules/FindFtd2xx.cmake +++ b/cmake/modules/FindFtd2xx.cmake @@ -27,11 +27,11 @@ else (LIBFTD2XX_LIBRARIES AND LIBFTD2XX_INCLUDE_DIRS) /usr/local/include /opt/local/include /sw/include - ) + ) SET(FTD2XX_LIBNAME ftd2xx) IF(WIN32) - SET(FTD2XX_LIBNAME ftd2xx.lib) + SET(FTD2XX_LIBNAME ftd2xx.lib) ENDIF(WIN32) find_library(LIBFTD2XX_LIBRARY @@ -42,16 +42,16 @@ else (LIBFTD2XX_LIBRARIES AND LIBFTD2XX_INCLUDE_DIRS) /usr/local/lib /opt/local/lib /sw/lib - ) + ) if(LIBFTD2XX_INCLUDE_DIR) - set(LIBFTD2XX_INCLUDE_DIRS + set(LIBFTD2XX_INCLUDE_DIRS ${LIBFTD2XX_INCLUDE_DIR} - ) + ) endif(LIBFTD2XX_INCLUDE_DIR) set(LIBFTD2XX_LIBRARIES ${LIBFTD2XX_LIBRARY} - ) + ) if (LIBFTD2XX_INCLUDE_DIRS AND LIBFTD2XX_LIBRARIES) set(LIBFTD2XX_FOUND TRUE) @@ -59,14 +59,17 @@ else (LIBFTD2XX_LIBRARIES AND LIBFTD2XX_INCLUDE_DIRS) if (LIBFTD2XX_FOUND) if (NOT libftd2xx_FIND_QUIETLY) - message(STATUS "Found libftd2xx: ${LIBFTD2XX_LIBRARIES}") + message(STATUS "Found libftd2xx: ${LIBFTD2XX_LIBRARIES}") endif (NOT libftd2xx_FIND_QUIETLY) else (LIBFTD2XX_FOUND) if (libftd2xx_FIND_REQUIRED) - message(FATAL_ERROR "Could not find libftd2xx") + message(FATAL_ERROR "Could not find libftd2xx") endif (libftd2xx_FIND_REQUIRED) endif (LIBFTD2XX_FOUND) + find_package_handle_standard_args(Ftd2xx + REQUIRED_VARS LIBFTD2XX_INCLUDE_DIRS) + # show the LIBFTD2XX_INCLUDE_DIRS and LIBFTD2XX_LIBRARIES variables only in the advanced view mark_as_advanced(LIBFTD2XX_INCLUDE_DIRS LIBFTD2XX_LIBRARIES) diff --git a/cmake/modules/FindFtd4222.cmake b/cmake/modules/FindFtd4222.cmake index ea49aac..bd86fc1 100644 --- a/cmake/modules/FindFtd4222.cmake +++ b/cmake/modules/FindFtd4222.cmake @@ -28,11 +28,11 @@ else (LIBFT4222_LIBRARIES AND LIBFT4222_INCLUDE_DIRS) /usr/local/include /opt/local/include /sw/include - ) + ) SET(FTD4222_LIBNAME ft4222) IF(WIN32) - SET(FTD4222_LIBNAME LibFT4222.lib) + SET(FTD4222_LIBNAME LibFT4222.lib) ENDIF(WIN32) find_library(LIBFT4222_LIBRARY @@ -43,16 +43,16 @@ else (LIBFT4222_LIBRARIES AND LIBFT4222_INCLUDE_DIRS) /usr/local/lib /opt/local/lib /sw/lib - ) + ) if(LIBFT4222_INCLUDE_DIR) - set(LIBFT4222_INCLUDE_DIRS + set(LIBFT4222_INCLUDE_DIRS ${LIBFT4222_INCLUDE_DIR} - ) + ) endif(LIBFT4222_INCLUDE_DIR) set(LIBFT4222_LIBRARIES ${LIBFT4222_LIBRARY} - ) + ) if (LIBFT4222_INCLUDE_DIRS AND LIBFT4222_LIBRARIES) set(LIBFT4222_FOUND TRUE) @@ -60,14 +60,17 @@ else (LIBFT4222_LIBRARIES AND LIBFT4222_INCLUDE_DIRS) if (LIBFT4222_FOUND) if (NOT LIBFT4222_FIND_QUIETLY) - message(STATUS "Found LIBFT4222: ${LIBFT4222_LIBRARIES}") + message(STATUS "Found LIBFT4222: ${LIBFT4222_LIBRARIES}") endif (NOT LIBFT4222_FIND_QUIETLY) else (LIBFT4222_FOUND) if (LIBFT4222_FIND_REQUIRED) - message(FATAL_ERROR "Could not find LIBFT4222") + message(FATAL_ERROR "Could not find LIBFT4222") endif (LIBFT4222_FIND_REQUIRED) endif (LIBFT4222_FOUND) + find_package_handle_standard_args(Ftd4222 + REQUIRED_VARS LIBFT4222_INCLUDE_DIRS LIBFT4222_LIBRARIES) + # show the LIBFT4222_INCLUDE_DIRS and LIBFT4222_LIBRARIES variables only in the advanced view mark_as_advanced(LIBFT4222_INCLUDE_DIRS LIBFT4222_LIBRARIES) diff --git a/docker-compose.yaml b/docker-compose.yaml index d8161a9..b99cac2 100644 --- a/docker-compose.yaml +++ b/docker-compose.yaml @@ -108,6 +108,7 @@ services: ftdi4442: extends: all environment: + - USBPLAT=ON - FTDI4222=ON command: bash -c "./scripts/run-cmake.sh && make -Cbuild" diff --git a/include/mraa_internal.h b/include/mraa_internal.h index b9cb129..6efa412 100644 --- a/include/mraa_internal.h +++ b/include/mraa_internal.h @@ -79,13 +79,6 @@ mraa_platform_t mraa_mips_platform(); */ mraa_platform_t mraa_mock_platform(); -/** - * runtime detect running usb platform extender - * - * @return mraa_platform_t of the detected platform extender - */ -mraa_platform_t mraa_usb_platform_extender(mraa_board_t* board); - /** * runtime detect iio subsystem * diff --git a/include/mraa_internal_types.h b/include/mraa_internal_types.h index fb34a43..3900fbe 100644 --- a/include/mraa_internal_types.h +++ b/include/mraa_internal_types.h @@ -497,3 +497,15 @@ typedef struct { uint8_t iio_device_count; /**< IIO device count */ } mraa_iio_info_t; #endif + +/** + * Function pointer typedef for use with platform extender libraries. + * Currently only the FT42222. + * + * @param board Pointer to valid board structure. If a mraa_board_t + * is initialized, it will be added to board->sub_platform + * + * @return MRAA_SUCCESS if a valid subplaform has been initialized, + * otherwise return MRAA_ERROR_PLATFORM_NOT_INITIALISED + */ +typedef mraa_result_t (*fptr_add_platform_extender)(mraa_board_t* board); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6dcf406..1e9b97c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -176,22 +176,26 @@ endif() if (USBPLAT) message (STATUS "INFO - Adding USB platforms") + # USBPLAT is used in mraa.c to decide whether to attempt to load sub-platforms set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DUSBPLAT=1") + # Loading an FTDI platform extender library using dlopen requires + # mraa to link against libdl. Mraa does NOT link directly to the + # FT4222 shim library. + set (mraa_LIBS ${mraa_LIBS} dl) if (FTDID2xx) find_package (Ftd2xx) if (${LIBFTD2XX_FOUND}) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFTDID2XX=1") set (mraa_LIBS ${mraa_LIBS} ${LIBFTD2XX_LIBRARIES}) else () message (SEND_ERROR "Enabled FTDID2xx support but library not found") endif () endif () + # The FTDI4222 shim includes libft4222.h (which includes ftd2xx.h), make sure + # both exist if building the FTDI4222 shim library if (FTDI4222) + find_package (Ftd2xx REQUIRED) find_package (Ftd4222) - if (${LIBFT4222_FOUND}) - set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DFTDI4222=1") - set (mraa_LIBS ${mraa_LIBS} dl) - else () + if (NOT ${LIBFT4222_FOUND}) message (SEND_ERROR "Enabled FTDI4222 support but library not found") endif () endif () diff --git a/src/mraa.c b/src/mraa.c index 631b38e..bc06443 100644 --- a/src/mraa.c +++ b/src/mraa.c @@ -29,6 +29,7 @@ #endif #include +#include #include #include #include @@ -195,18 +196,21 @@ imraa_init() } #if defined(USBPLAT) - // Now detect sub platform, note this is not an else since we could be in - // an error case and fall through to MRAA_ERROR_PLATFORM_NOT_INITIALISED - if (plat != NULL) { - mraa_platform_t usb_platform_type = mraa_usb_platform_extender(plat); - // if we have no known platform just replace usb platform with platform - if (plat->platform_type == MRAA_UNKNOWN_PLATFORM && usb_platform_type != MRAA_UNKNOWN_PLATFORM) { - plat->platform_type = usb_platform_type; - } - } - if (plat == NULL) { - printf("mraa: FATAL error, failed to initialise platform\n"); - return MRAA_ERROR_PLATFORM_NOT_INITIALISED; + syslog(LOG_NOTICE, "Searching for USB plaform extender libraries..."); + /* If a usb platform lib is present, attempt to load and look for + * necessary symbols for adding extended I/O */ + void* usblib = dlopen("libmraa-platform-ft4222.so", RTLD_LAZY); + if (usblib) + { + syslog(LOG_NOTICE, "Found USB platform extender library: libmraa-platform-ft4222.so"); + syslog(LOG_NOTICE, "Detecting FT4222 subplatforms..."); + fptr_add_platform_extender add_ft4222_platform = + (fptr_add_platform_extender)dlsym(usblib, "mraa_usb_platform_extender"); + + /* If this method exists, call it to add a subplatform */ + syslog(LOG_NOTICE, "Detecting FT4222 subplatforms complete, found %i subplatform/s", + ((add_ft4222_platform != NULL) && (add_ft4222_platform(plat) == MRAA_SUCCESS)) + ? 1 : 0); } #endif @@ -273,7 +277,8 @@ mraa_deinit() free(plat->adv_func); } mraa_board_t* sub_plat = plat->sub_platform; - if (sub_plat != NULL) { + /* No alloc's in an FTDI_FT4222 platform structure */ + if ((sub_plat != NULL) && (sub_plat->platform_type != MRAA_FTDI_FT4222)) { if (sub_plat->pins != NULL) { free(sub_plat->pins); } diff --git a/src/usb/CMakeLists.txt b/src/usb/CMakeLists.txt index e021c5d..b7a15eb 100644 --- a/src/usb/CMakeLists.txt +++ b/src/usb/CMakeLists.txt @@ -1,12 +1 @@ -set (mraa_LIB_USB_SRCS_NOAUTO ${PROJECT_SOURCE_DIR}/src/usb/usb.c) - -if (FTDI4222) - message (INFO " - FTDI4222") - set (mraa_LIB_USB_SRCS_NOAUTO ${mraa_LIB_USB_SRCS_NOAUTO} - ${PROJECT_SOURCE_DIR}/src/usb/ftdi_ft4222.c - ) -endif () - -set (mraa_LIB_PLAT_SRCS_NOAUTO ${mraa_LIB_PLAT_SRCS_NOAUTO} - ${mraa_LIB_USB_SRCS_NOAUTO} PARENT_SCOPE) - +add_subdirectory(ft4222) diff --git a/src/usb/ft4222/CMakeLists.txt b/src/usb/ft4222/CMakeLists.txt new file mode 100644 index 0000000..8f53512 --- /dev/null +++ b/src/usb/ft4222/CMakeLists.txt @@ -0,0 +1,19 @@ +if (FTDI4222) + set(target_name mraa-platform-ft4222) + add_library(${target_name} SHARED ftdi_ft4222.cxx ftdi_ft4222.hpp) + target_link_libraries(${target_name} ${LIBFT4222_LIBRARIES}) + install(TARGETS ${target_name} DESTINATION ${LIB_INSTALL_DIR}) + + set_target_properties( + ${target_name} + PROPERTIES + SOVERSION ${mraa_VERSION_MAJOR} + VERSION ${mraa_VERSION_STRING} + ) + if (MSYS) + # Under MSYS we have to put our generated DLL into bin, otherwise it's not picked up + install(TARGETS ${target_name} DESTINATION ${CMAKE_INSTALL_BINDIR}) + else () + install(TARGETS ${target_name} DESTINATION ${LIB_INSTALL_DIR}) + endif () +endif() diff --git a/src/usb/ft4222/ftdi_ft4222.cxx b/src/usb/ft4222/ftdi_ft4222.cxx new file mode 100644 index 0000000..3a36f40 --- /dev/null +++ b/src/usb/ft4222/ftdi_ft4222.cxx @@ -0,0 +1,1451 @@ +/* + * Author: Henry Bruce + * Copyright (c) 2015 Intel Corporation. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + + +#include "ftdi_ft4222.hpp" +#include "ftd2xx.h" +#include "libft4222.h" +#include "linux/i2c-dev.h" +#include "types.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace ft4222 +{ +#define gpioPinsPerFt4222 4 + +/* These devices have configurable addresses which leave it up to makers of + * development expander boards to either set these addresses or provide a means + * to set them (jumpers or solder). The dev boards for both the PCA9555 and + * PCA9672 have a slave address of 010 0xxx (where xxx is configurable). + */ +#define PCA9672_ADDR 0x20 +#define PCA9555_ADDR 0x27 +#define PCA9555_INPUT_REG 0 +#define PCA9555_OUTPUT_REG 2 +#define PCA9555_POLARITY_REG 4 +#define PCA9555_DIRECTION_REG 6 +#define PCA9545_ADDR 0x70 +#define PCA9672_PINS 8 +#define PCA9555_PINS 16 +#define PCA9545_BUSSES 4 +#define GPIO_PORT_IO_RESET GPIO_PORT2 +#define GPIO_PORT_IO_INT GPIO_PORT3 +#define MAX_IO_EXPANDER_PINS PCA9555_PINS + +/* GPIO expander types */ +typedef enum { IO_EXP_NONE, IO_EXP_PCA9672, IO_EXP_PCA9555 } ft4222_io_exp_type; + +/* GPIO types */ +typedef enum { + GPIO_TYPE_BUILTIN, + GPIO_TYPE_PCA9672, + GPIO_TYPE_PCA9555, + GPIO_TYPE_UNKNOWN = 99 +} ft4222_gpio_type; + +/* GPIO expander interrupt monitor */ +struct gpio_expander_intmon { + pthread_t thread; + pthread_mutex_t mutex; + mraa_boolean_t should_stop; + mraa_boolean_t is_interrupt_detected[MAX_IO_EXPANDER_PINS]; + int num_active_pins; +}; + +/* At present, no c++11 in mraa, so create a lock guard */ +class lock_guard +{ + public: + lock_guard(pthread_mutex_t& mtx) : _mtx(mtx) + { + if (pthread_mutex_lock(&_mtx)) + syslog(LOG_ERR, "FT4222 error creating lock on mutex"); + } + virtual ~lock_guard() + { + if (pthread_mutex_unlock(&_mtx)) + syslog(LOG_ERR, "FT4222 error creating lock on mutex"); + } + + private: + pthread_mutex_t& _mtx; +}; + +/* Each FT4222 device is represented by 1 Ftdi_4222_Shim instance */ +class Ftdi_4222_Shim +{ + public: + Ftdi_4222_Shim(); + + virtual ~Ftdi_4222_Shim(); + + static mraa_board_t* init_and_setup_mraa_board(); + + bool init_next_free_ftdi4222_device(); + bool setup_io(); + bool init_ftdi_gpios(); + int ft4222_detect_i2c_switch(); + + uint32_t ftdi_device_id; + uint8_t pca9672DirectionMask; + union { + uint16_t word; + uint8_t bytes[2]; + } pca9555OutputValue; + union { + uint16_t word; + uint8_t bytes[2]; + } pca9555DirectionValue; + + gpio_expander_intmon gpio_mon; + std::vector pinDirection; + + FT_HANDLE h_gpio; + FT_HANDLE h_i2c; + FT_HANDLE h_spi; + mraa_i2c_mode_t mraa_i2c_mode; + uint32_t ft4222_i2c_speed(); + pthread_mutex_t mtx_ft4222; + int cur_i2c_bus; + ft4222_io_exp_type exp_type; + + private: + /** + * Function detects known I2C I/O expanders and returns the number of GPIO pins + * on expander + */ + int _detect_io_expander(); + + /* Collection to store the board info for 1 FT4222 device */ + mraa_board_t _board; + /* Collection to store pin info for 1 FT4222 device */ + std::vector _pins; + /* Struct to store the function table info for 1 FT4222 device */ + mraa_adv_func_t _adv_func_table; +}; + +Ftdi_4222_Shim::Ftdi_4222_Shim() +: ftdi_device_id(0), pca9672DirectionMask(0), gpio_mon(), pinDirection(4, GPIO_INPUT), h_gpio(NULL), + h_i2c(NULL), h_spi(NULL), mraa_i2c_mode(MRAA_I2C_FAST), cur_i2c_bus(0), exp_type(IO_EXP_NONE), + _board(), _adv_func_table() +{ + pca9555OutputValue.word = 0; + pca9555DirectionValue.word = 0; + + /* Initialize mutexes */ + pthread_mutexattr_t attr_rec; + pthread_mutexattr_init(&attr_rec); + pthread_mutexattr_settype(&attr_rec, PTHREAD_MUTEX_RECURSIVE); + if (pthread_mutex_init(&mtx_ft4222, &attr_rec) != 0) { + syslog(LOG_ERR, "Failed to setup FT HW device mutex for FT4222 access"); + throw std::runtime_error("Failed to setup FT HW device mutex for FT4222 access"); + } +} + +Ftdi_4222_Shim::~Ftdi_4222_Shim() +{ + if (ftdi_device_id) + syslog(LOG_NOTICE, "Closing FTDI FT4222 device id: 0x%08x", ftdi_device_id); + + if (h_gpio) + FT4222_UnInitialize(h_gpio); + if (h_i2c) + FT4222_UnInitialize(h_i2c); + if (h_spi) + FT4222_UnInitialize(h_spi); + + if (h_gpio) + FT_Close(h_gpio); + if (h_i2c) + FT_Close(h_i2c); + if (h_spi) + FT_Close(h_spi); +} + +/* Global collections for handling multiple shims */ +std::vector _ftdi_shims; +/* Provide a map of i2c bus index to shim pointer */ +std::map _i2c_bus_to_shim; +/* Provide a map of gpio bus index to shim pointer */ +std::map _gpio_pin_to_shim; + +uint32_t +Ftdi_4222_Shim::ft4222_i2c_speed() +{ + static std::map mode2kHz; + mode2kHz[MRAA_I2C_STD] = 100; + mode2kHz[MRAA_I2C_FAST] = 400; + mode2kHz[MRAA_I2C_HIGH] = 3400; + return mode2kHz[mraa_i2c_mode]; +} + +Ftdi_4222_Shim* +ShimFromI2cBus(int bus) +{ + std::map::iterator it = _i2c_bus_to_shim.find(bus); + if (it == _i2c_bus_to_shim.end()) { + std::ostringstream oss; + oss << "{"; + for (std::map::const_iterator it = _i2c_bus_to_shim.begin(); + it != _i2c_bus_to_shim.end();) { + oss << it->first; + if (++it != _i2c_bus_to_shim.end()) + oss << ", "; + } + oss << "}"; + syslog(LOG_ERR, "Ftdi_4222_Shim lookup failed for I2C bus: %d, valid busses are: %s", bus, + oss.str().c_str()); + return NULL; + } + + return it->second; +} + +Ftdi_4222_Shim* +ShimFromGpioPin(int pin) +{ + std::map::iterator it = _gpio_pin_to_shim.find(pin); + if (it == _gpio_pin_to_shim.end()) { + std::ostringstream oss; + oss << "{"; + for (std::map::const_iterator it = _gpio_pin_to_shim.begin(); + it != _gpio_pin_to_shim.end();) { + oss << it->first; + if (++it != _gpio_pin_to_shim.end()) + oss << ", "; + } + oss << "}"; + syslog(LOG_ERR, "Ftdi_4222_Shim lookup failed for pin: %d, valid pins are: %s", pin, + oss.str().c_str()); + return NULL; + } + + return it->second; +} + +mraa_board_t* +Ftdi_4222_Shim::init_and_setup_mraa_board() +{ + /* The Ftdi_4222_Shim constructor can throw */ + try { + /* Add a shim object to the collection */ + _ftdi_shims.push_back(Ftdi_4222_Shim()); + } catch (...) { + syslog(LOG_ERR, "Failed to create an Ftdi_4222_Shim instance..."); + return NULL; + } + + /* Attempt to initialize an FTDI4222 device and setup I/O for use by mraa */ + if (_ftdi_shims.back().init_next_free_ftdi4222_device() && _ftdi_shims.back().setup_io()) { + return &_ftdi_shims.back()._board; + } else { + _ftdi_shims.pop_back(); + return NULL; + } +} + + +void +ft4222_sleep_ms(unsigned long mseconds) +{ + struct timespec sleepTime = {}; + // Number of seconds + sleepTime.tv_sec = mseconds / 1000; + // Convert fractional seconds to nanoseconds + sleepTime.tv_nsec = (mseconds % 1000) * 1000000; + // Iterate nanosleep in a loop until the total sleep time is the original + // value of the seconds parameter + while ((nanosleep(&sleepTime, &sleepTime) != 0) && (errno == EINTR)) + ; +} + + +bool +Ftdi_4222_Shim::init_next_free_ftdi4222_device() +{ + DWORD numDevs = 0; + + if (FT_CreateDeviceInfoList(&numDevs) != FT_OK) { + syslog(LOG_ERR, "FT_CreateDeviceInfoList failed"); + return false; + } + syslog(LOG_NOTICE, "FT_GetDeviceInfoList returned %d devices", numDevs); + + FT_DEVICE_LIST_INFO_NODE devInfo[numDevs]; + if (FT_GetDeviceInfoList(&devInfo[0], &numDevs) != FT_OK) { + syslog(LOG_ERR, "FT_GetDeviceInfoList failed"); + return false; + } + + int first_4222_ndx = -1; + int ftdi_mode = -1; + /* Look for FT_DEVICE_4222H_0 devices. Print out all devices found. */ + for (DWORD i = 0; i < numDevs; i++) { + /* Log info for debugging */ + syslog(LOG_NOTICE, " FTDI ndx: %02d id: 0x%08x %s description: '%s'", i, devInfo[i].ID, + (devInfo[i].Flags & 0x2) ? "High-speed USB" : "Full-speed USB", &devInfo[i].Description[0]); + + /* If this FTDI device ID is already assigned to an Ftdi_4222_Shim, + * then log and skip */ + for (std::vector::const_iterator it = _ftdi_shims.begin(); + (it != _ftdi_shims.end()) && ((*it).ftdi_device_id != 0); ++it) { + if ((*it).ftdi_device_id == devInfo[i].ID) { + syslog(LOG_NOTICE, " FTDI ndx: %02d id: 0x%08x already initialized, skipping...", + i, devInfo[i].ID); + continue; + } + } + + /* FTDI_4222 mode 3 provides 2 devices */ + if ((first_4222_ndx == -1) && (devInfo[i].Type == FT_DEVICE_4222H_0) && + ((i + 1 < numDevs) && (devInfo[i].ID == devInfo[i + 1].ID))) { + first_4222_ndx = i; + ftdi_mode = 3; + } + /* FTDI_4222 mode 0 provides 1 device */ + else if ((first_4222_ndx == -1) && (devInfo[i].Type == FT_DEVICE_4222H_0)) { + first_4222_ndx = i; + ftdi_mode = 0; + } + } + + /* Was a usable 4222 found? */ + if (first_4222_ndx == -1) { + syslog(LOG_ERR, "No FT4222 (mode 0 or 3) devices found."); + return false; + } + + syslog(LOG_NOTICE, "FTDI 4222 device found at ndx: %02d, mode %d (%s)", first_4222_ndx, ftdi_mode, + ftdi_mode == 0 ? "SPI/I2C" : ftdi_mode == 3 ? "SPI/I2C and GPIO" : "Unknown mode"); + + /* Both modes provide a SPI/I2C at the first ndx */ + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as SPI/I2C", first_4222_ndx); + + /* Setup I2c */ + if (devInfo[first_4222_ndx].LocId == 0) { + syslog(LOG_ERR, "No I2C controller for FTDI_4222 device"); + return false; + } + + if (FT_OpenEx(reinterpret_cast(devInfo[first_4222_ndx].LocId), FT_OPEN_BY_LOCATION, &h_i2c) != FT_OK) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as SPI/I2C device - FAILED to open", first_4222_ndx + 1); + return false; + } + + // Tell the FT4222 to be an I2C Master by default on init. + if (FT4222_I2CMaster_Init(h_i2c, ft4222_i2c_speed()) != FT4222_OK) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as SPI/I2C device - FAILED to " + "initialize", + first_4222_ndx + 1); + return false; + } + + if (FT4222_I2CMaster_Reset(h_i2c) != FT4222_OK) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as SPI/I2C device - FAILED to reset", + first_4222_ndx + 1); + return false; + } + + /* Mode 3 adds 1 GPIO device, setup GPIO */ + if (ftdi_mode == 3) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as GPIO device", first_4222_ndx + 1); + + DWORD locationIdGpio = devInfo[first_4222_ndx + 1].LocId; + if (locationIdGpio == 0) { + syslog(LOG_ERR, "No GPIO controller for FTDI_4222 device"); + return false; + } + + if (FT_OpenEx((PVOID)(uintptr_t) locationIdGpio, FT_OPEN_BY_LOCATION, &h_gpio) != FT_OK) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as GPIO device - FAILED to open", + first_4222_ndx + 1); + return false; + } + + if (!init_ftdi_gpios()) { + syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as GPIO device - FAILED to initialize", + first_4222_ndx + 1); + return false; + } + } + + /* Save off this FTDI device ID */ + ftdi_device_id = devInfo[first_4222_ndx].ID; + + syslog(LOG_NOTICE, "FTDI FT4222 device initialization completed successfully"); + return true; +} + +int +ft4222_i2c_read_internal(Ftdi_4222_Shim& shim, uint8_t addr, uint8_t* data, int length) +{ + uint16 bytesRead = 0; + uint8 controllerStatus = 0; + + /* If a read fails, check the I2C controller status, reset the controller. + * + * In some cases a master read will return FT4222_OK but leaves the bus in + * an error state. + * */ + FT4222_STATUS sts_rd = FT4222_I2CMaster_Read(shim.h_i2c, addr, data, length, &bytesRead); + + if ((sts_rd != FT4222_OK) || ((FT4222_I2CMaster_GetStatus(shim.h_i2c, &controllerStatus) != FT4222_OK) || + (controllerStatus & 0x2))) { + FT4222_I2CMaster_GetStatus(shim.h_i2c, &controllerStatus); + + syslog(LOG_ERR, "FT4222_I2CMaster_Read failed for address 0x%02x with code 0x%02x I2C " + "controller status: 0x%02x", + addr, sts_rd, controllerStatus); + FT4222_I2CMaster_Reset(shim.h_i2c); + return 0; + } + + return bytesRead; +} + +int +ft4222_i2c_write_internal(Ftdi_4222_Shim& shim, uint8_t addr, uint8_t* data, int bytesToWrite) +{ + uint16 bytesWritten = 0; + uint8 controllerStatus; + + /* If a write fails, check the I2C controller status, reset the controller, + * return 0? */ + if (FT4222_I2CMaster_Write(shim.h_i2c, addr, data, bytesToWrite, &bytesWritten) != FT4222_OK) { + FT4222_I2CMaster_GetStatus(shim.h_i2c, &controllerStatus); + + syslog(LOG_ERR, "FT4222_I2CMaster_Write failed address %#02x. Code %d", addr, controllerStatus); + FT4222_I2CMaster_Reset(shim.h_i2c); + bytesWritten = 0; + } + + if (bytesWritten != bytesToWrite) + syslog(LOG_ERR, "FT4222_I2CMaster_Write wrote %u of %u bytes.", bytesWritten, bytesToWrite); + + return bytesWritten; +} +mraa_result_t +ft4222_i2c_select_bus(int bus) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(bus); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + if (bus > 0 && bus != shim->cur_i2c_bus) { + syslog(LOG_NOTICE, "ft4222_i2c_select_bus switching to bus %d", bus); + uint8_t data; + if (bus == 0) + data = 0; + else + data = 1 << (bus - 1); + + if (ft4222_i2c_write_internal(*shim, PCA9545_ADDR, &data, 1) == 1) + shim->cur_i2c_bus = bus; + else + return MRAA_ERROR_UNSPECIFIED; + } + return MRAA_SUCCESS; +} + +int +ft4222_i2c_context_read(mraa_i2c_context dev, uint8_t* data, int length) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + int bytes_read = 0; + if (ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) + bytes_read = ft4222_i2c_read_internal(*shim, dev->addr, data, length); + return bytes_read; +} + +int +ft4222_i2c_context_write(mraa_i2c_context dev, uint8_t* data, int length) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + int bytes_written = 0; + if (ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) + bytes_written = ft4222_i2c_write_internal(*shim, dev->addr, data, length); + return bytes_written; +} + +int +Ftdi_4222_Shim::_detect_io_expander() +{ + uint8_t data = 0; + if (ft4222_i2c_read_internal(*this, PCA9672_ADDR, &data, 1) == 1) { + syslog(LOG_ERR, "Detected I/O expander: PCA9672 with %d I/O pins", PCA9672_PINS); + exp_type = IO_EXP_PCA9672; + return PCA9672_PINS; + } else { + uint8_t reg = PCA9555_INPUT_REG; + ft4222_i2c_write_internal(*this, PCA9555_ADDR, ®, 1); + if (ft4222_i2c_read_internal(*this, PCA9555_ADDR, &data, 1) == 1) { + syslog(LOG_ERR, "Detected I/O expander: PCA9555 with %d I/O pins", PCA9555_PINS); + exp_type = IO_EXP_PCA9555; + reg = PCA9555_OUTPUT_REG; + + ft4222_i2c_write_internal(*this, PCA9555_ADDR, ®, 1); + ft4222_i2c_read_internal(*this, PCA9555_ADDR, &pca9555OutputValue.bytes[0], 2); + + reg = PCA9555_DIRECTION_REG; + + ft4222_i2c_write_internal(*this, PCA9555_ADDR, ®, 1); + ft4222_i2c_read_internal(*this, PCA9555_ADDR, &pca9555DirectionValue.bytes[0], 2); + return PCA9555_PINS; + } + } + exp_type = IO_EXP_NONE; + return 0; +} + +ft4222_gpio_type +ft4222_get_gpio_type(int pin) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(pin); + if (!shim) + return GPIO_TYPE_UNKNOWN; + + if (pin < gpioPinsPerFt4222) { + return GPIO_TYPE_BUILTIN; + } else + switch (shim->exp_type) { + case IO_EXP_PCA9672: + return GPIO_TYPE_PCA9672; + case GPIO_TYPE_PCA9555: + return GPIO_TYPE_PCA9555; + default: + return GPIO_TYPE_UNKNOWN; + } +} + +mraa_result_t +ftdi_ft4222_set_internal_gpio_dir(Ftdi_4222_Shim& shim, int physical_pin, GPIO_Dir direction) +{ + /* Update the direction for this pin */ + shim.pinDirection[physical_pin] = direction; + + if (!shim.init_ftdi_gpios()) + return MRAA_ERROR_UNSPECIFIED; + + return MRAA_SUCCESS; +} + +mraa_result_t +ft4222_gpio_set_pca9672_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_UNSPECIFIED; + + uint8_t mask = 1 << dev->phy_pin; + switch (dir) { + case MRAA_GPIO_IN: { + shim->pca9672DirectionMask |= mask; + int bytes_written = + ft4222_i2c_write_internal(*shim, PCA9672_ADDR, &shim->pca9672DirectionMask, 1); + return bytes_written == 1 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; + } + case MRAA_GPIO_OUT: { + shim->pca9672DirectionMask &= (~mask); + return MRAA_SUCCESS; + } + default: + return MRAA_ERROR_UNSPECIFIED; + } +} + + +mraa_result_t +ft4222_gpio_set_pca9555_dir(mraa_gpio_context dev, mraa_gpio_dir_t dir) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_UNSPECIFIED; + + uint16_t mask = 1 << (dev->phy_pin - gpioPinsPerFt4222); + switch (dir) { + case MRAA_GPIO_IN: + shim->pca9555DirectionValue.word |= mask; + break; + case MRAA_GPIO_OUT: + shim->pca9555DirectionValue.word &= (~mask); + break; + default: { + syslog(LOG_ERR, "Invalid gpio direction: 0x%08x", dir); + return MRAA_ERROR_UNSPECIFIED; + } + } + uint8_t buf[3] = { PCA9555_DIRECTION_REG, shim->pca9555DirectionValue.bytes[0], + shim->pca9555DirectionValue.bytes[1] }; + + int bytes_written = ft4222_i2c_write_internal(*shim, PCA9555_ADDR, &buf[0], sizeof(buf)); + return bytes_written == sizeof(buf) ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; +} + +mraa_result_t +ftdi_ft4222_set_internal_gpio_trigger(int pin, GPIO_Trigger trigger) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + FT4222_STATUS ft4222Status = + FT4222_GPIO_SetInputTrigger(shim->h_gpio, static_cast(pin), trigger); + if (ft4222Status == FT4222_OK) + return MRAA_SUCCESS; + else { + syslog(LOG_ERR, "FT4222_GPIO_SetInputTrigger failed with FT4222_STATUS: 0x%04x", ft4222Status); + return MRAA_ERROR_UNSPECIFIED; + } +} + +// Function detects known I2C switches and returns the number of busses. +// On startup switch is disabled so default bus will be integrated i2c bus. +int +Ftdi_4222_Shim::ft4222_detect_i2c_switch() +{ + uint8_t data = 0; + if (ft4222_i2c_read_internal(*this, PCA9545_ADDR, &data, 1) == 1) { + data = 0; + return ft4222_i2c_write_internal(*this, PCA9545_ADDR, &data, 1) == 1 ? PCA9545_BUSSES : 0; + } + return 0; +} + +/******************* I2C functions *******************/ + +mraa_result_t +i2c_init_bus_replace(mraa_i2c_context dev) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + // Tell the FT4222 to be an I2C Master. + FT4222_STATUS ft4222Status = FT4222_I2CMaster_Init(shim->h_i2c, shim->ft4222_i2c_speed()); + if (FT4222_OK != ft4222Status) { + syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!", ft4222Status); + return MRAA_ERROR_NO_RESOURCES; + } + + // Reset the I2CM registers to a known state. + ft4222Status = FT4222_I2CMaster_Reset(shim->h_i2c); + if (FT4222_OK != ft4222Status) { + syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!", ft4222Status); + return MRAA_ERROR_NO_RESOURCES; + } + + syslog(LOG_NOTICE, "I2C interface enabled GPIO0 and GPIO1 will be unavailable."); + dev->handle = shim->h_i2c; + + // Don't use file descriptors + dev->fh = -1; + + // Advertise minimal i2c support as per + // https://www.kernel.org/doc/Documentation/i2c/functionality + dev->funcs = I2C_FUNC_I2C; + return MRAA_SUCCESS; +} + +mraa_result_t +i2c_set_frequency_replace(mraa_i2c_context dev, mraa_i2c_mode_t mode) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + /* Save off this speed */ + shim->mraa_i2c_mode = mode; + + return FT4222_I2CMaster_Init(shim->h_i2c, shim->ft4222_i2c_speed()) == FT4222_OK ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; +} + +mraa_result_t +i2c_address_replace(mraa_i2c_context dev, uint8_t addr) +{ + dev->addr = (int) addr; + return MRAA_SUCCESS; +} + +int +i2c_read_replace(mraa_i2c_context dev, uint8_t* data, int length) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + int bytes_read = ft4222_i2c_read_internal(*shim, dev->addr, data, length); + + return bytes_read; +} + +int +i2c_read_byte_replace(mraa_i2c_context dev) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + uint8_t data = 0; + int bytes_read = ft4222_i2c_context_read(dev, &data, 1); + return bytes_read == 1 ? data : -1; +} + +int +i2c_read_byte_data_replace(mraa_i2c_context dev, uint8_t command) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + uint8_t data; + int bytes_read = 0; + + uint16 bytesWritten = ft4222_i2c_context_write(dev, &command, 1); + + if (bytesWritten == 1) + bytes_read = ft4222_i2c_context_read(dev, &data, 1); + + if (bytes_read == 1) { + return (int) data; + } + return -1; +} + +int +i2c_read_word_data_replace(mraa_i2c_context dev, uint8_t command) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + union { + uint8_t bytes[2]; + uint16_t word; + } buf = {}; + int bytes_read = 0; + + int bytes_written = ft4222_i2c_context_write(dev, &command, 1); + if (bytes_written == 1) + bytes_read = ft4222_i2c_context_read(dev, &buf.bytes[0], 2); + + if (bytes_read == 2) + return (int) buf.word; + + return -1; +} + +int +i2c_read_bytes_data_replace(mraa_i2c_context dev, uint8_t command, uint8_t* data, int length) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + int bytes_read = 0; + int bytes_written = ft4222_i2c_context_write(dev, &command, 1); + if (bytes_written == 1) + bytes_read = ft4222_i2c_context_read(dev, data, length); + return bytes_read; +} + +mraa_result_t +i2c_write_replace(mraa_i2c_context dev, const uint8_t* data, int bytesToWrite) +{ + Ftdi_4222_Shim* shim = ShimFromI2cBus(dev->busnum); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + uint16 bytesWritten = ft4222_i2c_context_write(dev, (uint8_t*) data, bytesToWrite); + return bytesToWrite == bytesWritten ? MRAA_SUCCESS : MRAA_ERROR_INVALID_HANDLE; +} + +/* No mutex needed */ +mraa_result_t +i2c_write_byte_replace(mraa_i2c_context dev, uint8_t data) +{ + mraa_result_t status = i2c_write_replace(dev, &data, 1); + return status; +} + +/* No mutex needed */ +mraa_result_t +i2c_write_byte_data_replace(mraa_i2c_context dev, const uint8_t data, const uint8_t command) +{ + uint8_t buf[2] = { command, data }; + mraa_result_t status = i2c_write_replace(dev, &buf[0], 2); + return status; +} + +/* No mutex needed */ +mraa_result_t +i2c_write_word_data_replace(mraa_i2c_context dev, const uint16_t data, const uint8_t command) +{ + uint8_t buf[3] = { command, (uint8_t) data, (uint8_t)(data >> 8) }; + mraa_result_t status = i2c_write_replace(dev, &buf[0], 3); + return status; +} + +mraa_result_t i2c_stop_replace(mraa_i2c_context /*dev*/) +{ + return MRAA_SUCCESS; +} + +// /******************* GPIO functions *******************/ +// +mraa_result_t +gpio_init_internal_replace(mraa_gpio_context dev, int pin) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + /* Keep the subplatform index as pin. Example: 516 */ + /* And the phy_pin as the local index to pin. Example: 516 - 512 = 4 */ + dev->pin = pin; + dev->phy_pin = pin & (~MRAA_SUB_PLATFORM_MASK); + + if (pin < 2) { + syslog(LOG_NOTICE, "Closing I2C interface to enable GPIO%d", pin); + + /* Replace with call to SPI init when SPI is fully implemented */ + FT4222_STATUS ft4222Status = + FT4222_SPIMaster_Init(shim->h_gpio, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_HIGH, CLK_LEADING, 0x01); + if (FT4222_OK != ft4222Status) { + syslog(LOG_ERR, "Failed to close I2C interface and start SPI (error %d)!", ft4222Status); + return MRAA_ERROR_NO_RESOURCES; + } + } + return MRAA_SUCCESS; +} + +mraa_result_t gpio_mode_replace(mraa_gpio_context /*dev*/, mraa_gpio_mode_t /*mode*/) +{ + return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; +} + +mraa_result_t +gpio_edge_mode_replace(mraa_gpio_context dev, mraa_gpio_edge_t mode) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + mraa_result_t result = MRAA_SUCCESS; + + switch (ft4222_get_gpio_type(dev->phy_pin)) { + case GPIO_TYPE_BUILTIN: + switch (mode) { + case MRAA_GPIO_EDGE_NONE: + case MRAA_GPIO_EDGE_BOTH: + result = ftdi_ft4222_set_internal_gpio_trigger( + dev->phy_pin, static_cast(GPIO_TRIGGER_RISING | GPIO_TRIGGER_FALLING)); + break; + case MRAA_GPIO_EDGE_RISING: + result = ftdi_ft4222_set_internal_gpio_trigger(dev->phy_pin, GPIO_TRIGGER_RISING); + break; + case MRAA_GPIO_EDGE_FALLING: + result = ftdi_ft4222_set_internal_gpio_trigger(dev->phy_pin, GPIO_TRIGGER_FALLING); + break; + default: + result = MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; + break; + } + break; + case GPIO_TYPE_PCA9672: + case GPIO_TYPE_PCA9555: + break; + default: + result = MRAA_ERROR_INVALID_RESOURCE; + break; + } + + return result; +} + +mraa_result_t +ft4222_i2c_read_io_expander(Ftdi_4222_Shim& shim, uint16_t* value) +{ + int bytes_read = 0; + uint8_t reg = PCA9555_INPUT_REG; + + switch (shim.exp_type) { + case IO_EXP_PCA9672: + bytes_read = ft4222_i2c_read_internal(shim, PCA9672_ADDR, (uint8_t*) value, 1); + break; + case GPIO_TYPE_PCA9555: + if (ft4222_i2c_write_internal(shim, PCA9555_ADDR, ®, 1) == 1) + bytes_read = ft4222_i2c_read_internal(shim, PCA9555_ADDR, (uint8_t*) value, 2); + break; + default:; + } + return bytes_read > 0 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; +} + +int +gpio_read_replace(mraa_gpio_context dev) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return -1; + + lock_guard lock(shim->mtx_ft4222); + + switch (ft4222_get_gpio_type(dev->phy_pin)) { + case GPIO_TYPE_BUILTIN: { + BOOL value; + FT4222_STATUS ft4222Status = + FT4222_GPIO_Read(shim->h_gpio, static_cast(dev->phy_pin), &value); + if (FT4222_OK != ft4222Status) { + syslog(LOG_ERR, "FT4222_GPIO_Read failed (error %d)!", ft4222Status); + return -1; + } + return value; + } + case GPIO_TYPE_PCA9672: + case GPIO_TYPE_PCA9555: { + uint16_t mask = 1 << (dev->phy_pin - gpioPinsPerFt4222); + uint16_t value; + mraa_result_t res = ft4222_i2c_read_io_expander(*shim, &value); + return res == MRAA_SUCCESS ? (value & mask) == mask : -1; + } + default: + return -1; + } +} + +mraa_result_t +gpio_write_replace_wrapper(mraa_gpio_context dev, int write_value, bool require_atomic = true) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + if (require_atomic) + pthread_mutex_lock(&shim->mtx_ft4222); + + mraa_result_t result = MRAA_SUCCESS; + + switch (ft4222_get_gpio_type(dev->phy_pin)) { + case GPIO_TYPE_BUILTIN: { + FT4222_STATUS ft4222Status = + FT4222_GPIO_Write(shim->h_gpio, static_cast(dev->phy_pin), write_value); + if (FT4222_OK != ft4222Status) { + syslog(LOG_ERR, "FT4222_GPIO_Write to pin %i failed (error %d)!", + dev->phy_pin, + ft4222Status); + result = MRAA_ERROR_UNSPECIFIED; + } + } break; + case GPIO_TYPE_PCA9672: { + uint8_t mask = 1 << dev->phy_pin; + uint8_t value; + int bytes_written = 0; + int bytes_read = ft4222_i2c_read_internal(*shim, PCA9672_ADDR, &value, 1); + if (bytes_read == 1) { + if (write_value == 1) + value = value | mask | shim->pca9672DirectionMask; + else + value &= (~mask); + bytes_written = ft4222_i2c_write_internal(*shim, PCA9672_ADDR, &value, 1); + } + result = bytes_written == 1 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; + } break; + case GPIO_TYPE_PCA9555: { + uint16_t mask = 1 << (dev->phy_pin - gpioPinsPerFt4222); + if (write_value) + shim->pca9555OutputValue.word |= mask; + else + shim->pca9555OutputValue.word &= (~mask); + uint8_t buf[3] = { PCA9555_OUTPUT_REG, shim->pca9555OutputValue.bytes[0], + shim->pca9555OutputValue.bytes[1] }; + int bytes_written = ft4222_i2c_write_internal(*shim, PCA9555_ADDR, &buf[0], sizeof(buf)); + result = bytes_written == sizeof(buf) ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; + } break; + default: + result = MRAA_ERROR_INVALID_RESOURCE; + break; + } + + if (require_atomic) + pthread_mutex_unlock(&shim->mtx_ft4222); + + return result; +} + +mraa_result_t +gpio_write_replace(mraa_gpio_context dev, int write_value) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + /* Default to an atomic gpio_write_replace call */ + return gpio_write_replace_wrapper(dev, write_value); +} + +mraa_result_t +gpio_dir_replace(mraa_gpio_context dev, mraa_gpio_dir_t dir) +{ + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + switch (ft4222_get_gpio_type(dev->phy_pin)) { + case GPIO_TYPE_BUILTIN: + switch (dir) { + case MRAA_GPIO_IN: + return ftdi_ft4222_set_internal_gpio_dir(*shim, dev->phy_pin, GPIO_INPUT); + case MRAA_GPIO_OUT: + return ftdi_ft4222_set_internal_gpio_dir(*shim, dev->phy_pin, GPIO_OUTPUT); + case MRAA_GPIO_OUT_HIGH: + if (ftdi_ft4222_set_internal_gpio_dir(*shim, dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 1, false); + case MRAA_GPIO_OUT_LOW: + if (ftdi_ft4222_set_internal_gpio_dir(*shim, dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 0, false); + default: + return MRAA_ERROR_INVALID_PARAMETER; + } + case GPIO_TYPE_PCA9672: + switch (dir) { + case MRAA_GPIO_IN: + case MRAA_GPIO_OUT: + return ft4222_gpio_set_pca9672_dir(dev, dir); + case MRAA_GPIO_OUT_HIGH: + if (ft4222_gpio_set_pca9672_dir(dev, dir) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 1, false); + case MRAA_GPIO_OUT_LOW: + if (ft4222_gpio_set_pca9672_dir(dev, dir) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 0, false); + default: + return MRAA_ERROR_INVALID_PARAMETER; + } + case GPIO_TYPE_PCA9555: + switch (dir) { + case MRAA_GPIO_IN: + case MRAA_GPIO_OUT: + return ft4222_gpio_set_pca9555_dir(dev, dir); + case MRAA_GPIO_OUT_HIGH: + if (ft4222_gpio_set_pca9555_dir(dev, dir) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 1, false); + case MRAA_GPIO_OUT_LOW: + if (ft4222_gpio_set_pca9555_dir(dev, dir) != MRAA_SUCCESS) + return MRAA_ERROR_UNSPECIFIED; + return gpio_write_replace_wrapper(dev, 0, false); + default: + return MRAA_ERROR_INVALID_PARAMETER; + } + default: + return MRAA_ERROR_INVALID_RESOURCE; + } +} + +mraa_boolean_t +ft4222_has_internal_gpio_triggered(Ftdi_4222_Shim& shim, int physical_pin) +{ + uint16 num_events = 0; + FT4222_GPIO_GetTriggerStatus(shim.h_gpio, static_cast(physical_pin), &num_events); + + if (num_events > 0) { + int i; + uint16 num_events_read; + GPIO_Trigger event; + for (i = 0; i < num_events; ++i) { + FT4222_GPIO_ReadTriggerQueue(shim.h_gpio, static_cast(physical_pin), &event, + 1, &num_events_read); + } + return TRUE; + } else + return FALSE; +} + +// INT pin of i2c PCA9672 GPIO expander is connected to FT4222 GPIO #3 +// We use INT to detect any expander GPIO level change +void* +ft4222_gpio_monitor(void* arg) +{ + Ftdi_4222_Shim* shim = static_cast(arg); + + uint16_t prev_value = 0; + ft4222_i2c_read_io_expander(*shim, &prev_value); + while (!shim->gpio_mon.should_stop) { + { + lock_guard lock(shim->mtx_ft4222); + if (ft4222_has_internal_gpio_triggered(*shim, GPIO_PORT_IO_INT)) { + uint16_t value; + if (ft4222_i2c_read_io_expander(*shim, &value) == MRAA_SUCCESS) { + uint16_t change_value = prev_value ^ value; + int i; + for (i = 0; i < MAX_IO_EXPANDER_PINS; ++i) { + uint16_t mask = 1 << i; + shim->gpio_mon.is_interrupt_detected[i] = (change_value & mask); + } + prev_value = value; + } + } + } + ft4222_sleep_ms(10); + } + return NULL; +} + +void +ft4222_gpio_monitor_add_pin(Ftdi_4222_Shim& shim) +{ + if (shim.gpio_mon.num_active_pins == 0) { + pthread_mutexattr_t attr_rec; + pthread_mutexattr_init(&attr_rec); + pthread_mutexattr_settype(&attr_rec, PTHREAD_MUTEX_RECURSIVE); + if (pthread_mutex_init(&shim.gpio_mon.mutex, &attr_rec) != 0) { + syslog(LOG_ERR, "Failed to setup GPIO monitor mutex for FT4222 access"); + throw std::runtime_error("Failed to setup GPIO monitor mutex for FT4222 access"); + } + + pthread_create(&shim.gpio_mon.thread, NULL, ft4222_gpio_monitor, &shim); + } + shim.gpio_mon.num_active_pins++; +} + +void +ft4222_gpio_monitor_remove_pin(Ftdi_4222_Shim& shim) +{ + shim.gpio_mon.num_active_pins--; + if (shim.gpio_mon.num_active_pins == 0) { + shim.gpio_mon.should_stop = TRUE; + pthread_join(shim.gpio_mon.thread, NULL); + pthread_mutex_destroy(&shim.gpio_mon.mutex); + } +} + +mraa_boolean_t +ft4222_gpio_monitor_is_interrupt_detected(Ftdi_4222_Shim& shim, int pin) +{ + mraa_boolean_t is_interrupt_detected = FALSE; + if (shim.gpio_mon.is_interrupt_detected[pin]) { + shim.gpio_mon.is_interrupt_detected[pin] = FALSE; + is_interrupt_detected = TRUE; + } + return is_interrupt_detected; +} + +mraa_result_t +gpio_interrupt_handler_init_replace(mraa_gpio_context dev) +{ + while (dev->isr_thread_terminating) { + ft4222_sleep_ms(10); + } + + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + lock_guard lock(shim->mtx_ft4222); + + std::string extra; + switch (ft4222_get_gpio_type(dev->phy_pin)) { + case GPIO_TYPE_BUILTIN: + /* Make sure this pin is an input */ + // ftdi_ft4222_set_internal_gpio_dir(*shim, dev->phy_pin, GPIO_INPUT); + extra += "(FT4222 GPIO pin)"; + break; + case GPIO_TYPE_PCA9672: + case GPIO_TYPE_PCA9555: + /* Make sure pin is an input */ + ftdi_ft4222_set_internal_gpio_dir(*shim, GPIO_PORT_IO_INT, GPIO_INPUT); + ftdi_ft4222_set_internal_gpio_trigger(GPIO_PORT_IO_INT, GPIO_TRIGGER_FALLING); + ft4222_gpio_monitor_add_pin(*shim); + extra += "(FT4222 expander GPIO pin)"; + break; + default: + return MRAA_ERROR_INVALID_RESOURCE; + } + syslog(LOG_NOTICE, "ISR added for pin: %d physical_pin: %d %s", dev->pin, dev->phy_pin, extra.c_str()); + + return MRAA_SUCCESS; +} + +/* This method is running in a cancelable thread which can go away at any time, + * likewise, the isr_thread_terminating check might never happen and the GPIO + * monitor can have problems - not sure of the best fix for this... most likely + * the best fix would be to NOT support the I/O expanders. */ +mraa_result_t +gpio_wait_interrupt_replace(mraa_gpio_context dev) +{ + + Ftdi_4222_Shim* shim = ShimFromGpioPin(dev->phy_pin); + if (!shim) + return MRAA_ERROR_NO_RESOURCES; + + + mraa_boolean_t interrupt_detected = FALSE; + ft4222_gpio_type gpio_type = GPIO_TYPE_BUILTIN; + + { + lock_guard lock(shim->mtx_ft4222); + gpio_type = ft4222_get_gpio_type(dev->phy_pin); + } + + while (!dev->isr_thread_terminating && !interrupt_detected) { + + switch (gpio_type) { + case GPIO_TYPE_BUILTIN: { + lock_guard lock(shim->mtx_ft4222); + interrupt_detected = ft4222_has_internal_gpio_triggered(*shim, dev->phy_pin); + } break; + case GPIO_TYPE_PCA9672: + case GPIO_TYPE_PCA9555: { + lock_guard lock(shim->mtx_ft4222); + /* Expander pin indexing starts past the FT4222 GPIO pins */ + interrupt_detected = + ft4222_gpio_monitor_is_interrupt_detected(*shim, dev->phy_pin - gpioPinsPerFt4222); + } break; + default:; + } + ft4222_sleep_ms(10); + } + if (dev->isr_thread_terminating) { + lock_guard lock(shim->mtx_ft4222); + ft4222_gpio_monitor_remove_pin(*shim); + } + + return MRAA_SUCCESS; +} + +void +ft4222_populate_i2c_func_table(mraa_adv_func_t* func_table) +{ + func_table->i2c_init_bus_replace = &i2c_init_bus_replace; + func_table->i2c_set_frequency_replace = &i2c_set_frequency_replace; + func_table->i2c_address_replace = &i2c_address_replace; + func_table->i2c_read_replace = &i2c_read_replace; + func_table->i2c_read_byte_replace = &i2c_read_byte_replace; + func_table->i2c_read_byte_data_replace = &i2c_read_byte_data_replace; + func_table->i2c_read_word_data_replace = &i2c_read_word_data_replace; + func_table->i2c_read_bytes_data_replace = &i2c_read_bytes_data_replace; + func_table->i2c_write_replace = &i2c_write_replace; // Used in 3 places + func_table->i2c_write_byte_replace = &i2c_write_byte_replace; // No mutex needed + func_table->i2c_write_byte_data_replace = &i2c_write_byte_data_replace; + func_table->i2c_write_word_data_replace = &i2c_write_word_data_replace; + func_table->i2c_stop_replace = &i2c_stop_replace; +} + +void +ft4222_populate_gpio_func_table(mraa_adv_func_t* func_table) +{ + func_table->gpio_init_internal_replace = &gpio_init_internal_replace; + func_table->gpio_mode_replace = &gpio_mode_replace; + func_table->gpio_edge_mode_replace = &gpio_edge_mode_replace; + func_table->gpio_dir_replace = &gpio_dir_replace; + func_table->gpio_read_replace = &gpio_read_replace; + func_table->gpio_write_replace = &gpio_write_replace; // 6 + func_table->gpio_interrupt_handler_init_replace = &gpio_interrupt_handler_init_replace; + func_table->gpio_wait_interrupt_replace = &gpio_wait_interrupt_replace; +} + +/* Store mraa_board_t subplatform allocated by this library */ +char ftdi_4222_platform_name[] = "FTDI FT4222"; +bool +Ftdi_4222_Shim::setup_io() +{ + int numI2cGpioExpanderPins = _detect_io_expander(); + int numUsbGpio = gpioPinsPerFt4222 + numI2cGpioExpanderPins; + int numI2cBusses = 1 + ft4222_detect_i2c_switch(); + int numUsbPins = numUsbGpio + 2 * (numI2cBusses - 1); // Add SDA and SCL for each i2c switch bus + mraa_pincapabilities_t pinCapsI2c = (mraa_pincapabilities_t){ 1, 0, 0, 0, 0, 1, 0, 0 }; + mraa_pincapabilities_t pinCapsI2cGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 1, 0, 0 }; + mraa_pincapabilities_t pinCapsGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 0, 0, 0 }; + + _board.platform_name = &ftdi_4222_platform_name[0]; + _board.platform_type = MRAA_FTDI_FT4222; + _board.phy_pin_count = numUsbPins; + _board.gpio_count = numUsbGpio; + + int bus = 0; + _board.i2c_bus_count = numI2cBusses; + _board.def_i2c_bus = bus; + _board.i2c_bus[bus].bus_id = bus; + + /* Save off an I2C bus-to-shim value */ + _i2c_bus_to_shim[bus] = this; + + // I2c pins (these are virtual, entries are required to configure i2c layer) + // We currently assume that GPIO 0/1 are reserved for i2c operation + _pins.push_back(mraa_pininfo_t()); + strncpy(&_pins.back().name[0], "IGPIO0/SCL0", MRAA_PIN_NAME_SIZE); + _pins.back().capabilities = pinCapsI2cGpio; + _pins.back().gpio.pinmap = 512 + _pins.size() - 1; + _pins.back().gpio.mux_total = 0; + _pins.back().i2c.mux_total = 0; + _board.i2c_bus[bus].scl = _pins.size() - 1; + /* Save off a GPIO ndx-to-shim value */ + _gpio_pin_to_shim[_pins.size() - 1] = this; + + _pins.push_back(mraa_pininfo_t()); + strncpy(&_pins.back().name[0], "IGPIO1/SDA0", MRAA_PIN_NAME_SIZE); + _pins.back().capabilities = pinCapsI2cGpio; + _pins.back().gpio.pinmap = 512 + _pins.size() - 1; + _pins.back().gpio.mux_total = 0; + _pins.back().i2c.mux_total = 0; + _board.i2c_bus[bus].sda = _pins.size() - 1; + /* Save off a GPIO ndx-to-shim value */ + _gpio_pin_to_shim[_pins.size() - 1] = this; + + // FTDI4222 gpio + _pins.push_back(mraa_pininfo_t()); + strncpy(&_pins.back().name[0], "INT-GPIO2", MRAA_PIN_NAME_SIZE); + _pins.back().capabilities = pinCapsGpio; + _pins.back().gpio.pinmap = 512 + _pins.size() - 1; + _pins.back().gpio.mux_total = 0; + /* Save off a GPIO ndx-to-shim value */ + _gpio_pin_to_shim[_pins.size() - 1] = this; + + _pins.push_back(mraa_pininfo_t()); + strncpy(&_pins.back().name[0], "INT-GPIO3", MRAA_PIN_NAME_SIZE); + _pins.back().capabilities = pinCapsGpio; + _pins.back().gpio.pinmap = 512 + _pins.size() - 1; + _pins.back().gpio.mux_total = 0; + /* Save off a GPIO ndx-to-shim value */ + _gpio_pin_to_shim[_pins.size() - 1] = this; + + + // Virtual gpio pins on i2c I/O expander. + for (int i = 0; i < numI2cGpioExpanderPins; ++i) { + _pins.push_back(mraa_pininfo_t()); + snprintf(&_pins.back().name[0], MRAA_PIN_NAME_SIZE, "EXP-GPIO%d", i); + _pins.back().capabilities = pinCapsGpio; + _pins.back().gpio.pinmap = 512 + _pins.size() - 1; + _pins.back().gpio.mux_total = 0; + /* Save off a GPIO ndx-to-shim value */ + _gpio_pin_to_shim[_pins.size() - 1] = this; + } + + // Now add any extra i2c buses behind i2c switch + for (bus = 1; bus < numI2cBusses; ++bus) { + _board.i2c_bus[bus].bus_id = bus; + _pins.back().i2c.mux_total = 0; + snprintf(&_pins.back().name[0], MRAA_PIN_NAME_SIZE, "SDA%d", bus); + _pins.back().capabilities = pinCapsI2c; + _board.i2c_bus[bus].sda = _pins.size() - 1; + + _pins.push_back(mraa_pininfo_t()); + snprintf(&_pins.back().name[0], MRAA_PIN_NAME_SIZE, "SCL%d", bus); + _pins.back().capabilities = pinCapsI2c; + _pins.back().i2c.mux_total = 0; + _board.i2c_bus[bus].scl = _pins.size() - 1; + + _pins.push_back(mraa_pininfo_t()); + } + /* The board pins points to the shim pins vector */ + _board.pins = &_pins[0]; + + _board.adv_func = &_adv_func_table; + + ft4222_populate_i2c_func_table(_board.adv_func); + ft4222_populate_gpio_func_table(_board.adv_func); + + /* Success, return the sub platform */ + return true; +} + +bool +Ftdi_4222_Shim::init_ftdi_gpios() +{ + /* Setting the direction requires a re-init, which means the + * previous resources should also be un-initialized first */ + if (h_gpio) + FT4222_UnInitialize(h_gpio); + + /* Use GPIO2 as GPIO (not suspend output) */ + return (FT4222_SetSuspendOut(h_gpio, 0) == FT4222_OK) && + /* Use GPIO3 as GPIO (not interrupt for I/O expander) */ + (FT4222_SetWakeUpInterrupt(h_gpio, 0) == FT4222_OK) && + (FT4222_GPIO_Init(h_gpio, &pinDirection[0]) == FT4222_OK); +} + +} /* namespace ft4222 */ + +/* One C method exposed for loading a platform */ +mraa_result_t +mraa_usb_platform_extender(mraa_board_t* board) +{ + /* If no board provided return unknown */ + if (board == NULL) + return MRAA_ERROR_PLATFORM_NOT_INITIALISED; + + /* Attempt to initialize an FT4222 shim and assign it as a sub-platform */ + board->sub_platform = ft4222::Ftdi_4222_Shim::init_and_setup_mraa_board(); + + /* No sub-platform returned, drop out */ + if (board->sub_platform == NULL) + return MRAA_ERROR_PLATFORM_NOT_INITIALISED; + + syslog(LOG_NOTICE, "Added subplatform of type: %s", board->sub_platform->platform_name); + + return MRAA_SUCCESS; +} diff --git a/include/usb/ftdi_ft4222.h b/src/usb/ft4222/ftdi_ft4222.hpp similarity index 70% rename from include/usb/ftdi_ft4222.h rename to src/usb/ft4222/ftdi_ft4222.hpp index f7db914..fbac3ee 100644 --- a/include/usb/ftdi_ft4222.h +++ b/src/usb/ft4222/ftdi_ft4222.hpp @@ -1,3 +1,6 @@ +#ifndef _EXPORT_USERS_NECK_MRAA_FT4222_SRC_USB_FT4222_FTDI_FT4222_HPP +#define _EXPORT_USERS_NECK_MRAA_FT4222_SRC_USB_FT4222_FTDI_FT4222_HPP + /* * Author: Henry Bruce * Copyright (c) 2015 Intel Corporation. @@ -26,16 +29,22 @@ #ifdef __cplusplus extern "C" { -#endif #include #include "mraa_internal.h" -mraa_result_t mraa_ftdi_ft4222_init(); -mraa_result_t mraa_ftdi_ft4222_get_version(unsigned int* versionChip, unsigned int* versionLib); -mraa_board_t* mraa_ftdi_ft4222(); -void *libft4222_lib; +/** + * Attempt to initialize a mraa_board_t for a UMFT4222EV module + * + * @param board Pointer to valid board structure. If a mraa_board_t + * is initialized, it will be added to board->sub_platform + * + * @return MRAA_SUCCESS if a valid subplaform has been initialized, + * otherwise return MRAA_ERROR_PLATFORM_NOT_INITIALISED + */ +mraa_result_t mraa_usb_platform_extender(mraa_board_t* board); -#ifdef __cplusplus } #endif + +#endif diff --git a/src/usb/ftdi_ft4222.c b/src/usb/ftdi_ft4222.c deleted file mode 100644 index a4dd5c8..0000000 --- a/src/usb/ftdi_ft4222.c +++ /dev/null @@ -1,1161 +0,0 @@ -/* - * Author: Henry Bruce - * Copyright (c) 2015 Intel Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include -#include -#include -#include -#include "linux/i2c-dev.h" -#include "common.h" -#include "ftd2xx.h" -#include "libft4222.h" -#include "usb/ftdi_ft4222.h" - -#define PLATFORM_NAME "FTDI FT4222" -#define I2CM_ERROR(status) (((status) &0x02) != 0) -#define PCA9672_ADDR 0x20 -#define PCA9555_ADDR 0x27 -#define PCA9555_INPUT_REG 0 -#define PCA9555_OUTPUT_REG 2 -#define PCA9555_POLARITY_REG 4 -#define PCA9555_DIRECTION_REG 6 -#define PCA9545_ADDR 0x70 -#define PCA9672_PINS 8 -#define PCA9555_PINS 16 -#define PCA9545_BUSSES 4 -#define GPIO_PORT_IO_RESET GPIO_PORT2 -#define GPIO_PORT_IO_INT GPIO_PORT3 -#define MAX_IO_EXPANDER_PINS PCA9555_PINS - -typedef enum {IO_EXP_NONE, IO_EXP_PCA9672, IO_EXP_PCA9555} ft4222_io_exp_type; -typedef enum {GPIO_TYPE_BUILTIN, GPIO_TYPE_PCA9672, GPIO_TYPE_PCA9555, GPIO_TYPE_UNKNOWN=99} ft4222_gpio_type; - -static pthread_mutex_t ft4222_lock; -static FT_HANDLE ftHandleGpio = (FT_HANDLE) NULL; //GPIO Handle -static FT_HANDLE ftHandleI2c = (FT_HANDLE) NULL; //I2C/SPI Handle -static FT_HANDLE ftHandleSpi = (FT_HANDLE) NULL; //I2C/SPI Handle -static GPIO_Dir pinDirection[] = {GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT, GPIO_OUTPUT}; -static uint8_t pca9672DirectionMask = 0; -static uint16_t pca9555OutputValue = 0; -static uint16_t pca9555DirectionValue = 0; -static int bus_speed = 400; -static int numFt4222GpioPins = 4; -static int numI2cGpioExpanderPins = 8; -static int currentI2cBus = 0; -static ft4222_io_exp_type gpio_expander_chip; -static mraa_boolean_t libft4222_load_success = TRUE; - -FT_STATUS (*dl_FT_GetDeviceInfoList)(FT_DEVICE_LIST_INFO_NODE*, LPDWORD); -FT_STATUS (*dl_FT_CreateDeviceInfoList)(LPDWORD); -FT_STATUS (*dl_FT_OpenEx)(PVOID, DWORD, FT_HANDLE*); -FT4222_STATUS (*dl_FT4222_GetVersion)(FT_HANDLE, FT4222_Version*); -FT4222_STATUS (*dl_FT4222_I2CMaster_Write)(FT_HANDLE, uint16, uint8*, uint16, uint16*); -FT4222_STATUS (*dl_FT4222_I2CMaster_Reset)(FT_HANDLE); -FT4222_STATUS (*dl_FT4222_I2CMaster_Read)(FT_HANDLE, uint16, uint8*, uint16, uint16*); -FT4222_STATUS (*dl_FT4222_I2CMaster_Init)(FT_HANDLE, uint32); -FT4222_STATUS (*dl_FT4222_I2CMaster_GetStatus)(FT_HANDLE, uint8*); -FT4222_STATUS (*dl_FT4222_GPIO_Init)(FT_HANDLE, GPIO_Dir[4]); -FT4222_STATUS (*dl_FT4222_GPIO_GetTriggerStatus)(FT_HANDLE, GPIO_Port, uint16*); -FT4222_STATUS (*dl_FT4222_GPIO_ReadTriggerQueue)(FT_HANDLE, GPIO_Port, GPIO_Trigger*, uint16, uint16*); -FT4222_STATUS (*dl_FT4222_GPIO_Read)(FT_HANDLE, GPIO_Port, BOOL*); -FT4222_STATUS (*dl_FT4222_GPIO_SetInputTrigger)(FT_HANDLE, GPIO_Port, GPIO_Trigger); -FT4222_STATUS (*dl_FT4222_GPIO_Write)(FT_HANDLE, GPIO_Port, BOOL); -FT4222_STATUS (*dl_FT4222_SetWakeUpInterrupt)(FT_HANDLE, BOOL); -FT4222_STATUS (*dl_FT4222_SetSuspendOut)(FT_HANDLE, BOOL); -FT4222_STATUS (*dl_FT4222_SPIMaster_Init)(FT_HANDLE, FT4222_SPIMode, FT4222_SPIClock, FT4222_SPICPOL, FT4222_SPICPHA, uint8); - -static void -mraa_ftdi_ft4222_sleep_ms(unsigned long mseconds) -{ - struct timespec sleepTime; - - sleepTime.tv_sec = mseconds / 1000; // Number of seconds - sleepTime.tv_nsec = (mseconds % 1000) * 1000000; // Convert fractional seconds to nanoseconds - - // Iterate nanosleep in a loop until the total sleep time is the original - // value of the seconds parameter - while ((nanosleep(&sleepTime, &sleepTime) != 0) && (errno == EINTR)) - ; -} - -void * -mraa_ftdi_ft4222_dlsym(const char *symbol) -{ - void *func = dlsym(libft4222_lib, symbol); - if (func == NULL) { - syslog(LOG_ERR, "%s", dlerror()); - libft4222_load_success = FALSE; - } - return func; -} - - -mraa_result_t -mraa_ftdi_ft4222_init() -{ - mraa_result_t mraaStatus = MRAA_ERROR_NO_RESOURCES; - FT_DEVICE_LIST_INFO_NODE* devInfo = NULL; - FT_STATUS ftStatus; - DWORD numDevs = 0; - - dl_FT_GetDeviceInfoList = mraa_ftdi_ft4222_dlsym("FT_GetDeviceInfoList"); - dl_FT_CreateDeviceInfoList = mraa_ftdi_ft4222_dlsym("FT_CreateDeviceInfoList"); - dl_FT4222_GetVersion = mraa_ftdi_ft4222_dlsym("FT4222_GetVersion"); - dl_FT4222_I2CMaster_Write = mraa_ftdi_ft4222_dlsym("FT4222_I2CMaster_Write"); - dl_FT4222_I2CMaster_Reset = mraa_ftdi_ft4222_dlsym("FT4222_I2CMaster_Reset"); - dl_FT4222_I2CMaster_Read = mraa_ftdi_ft4222_dlsym("FT4222_I2CMaster_Read"); - dl_FT4222_I2CMaster_Init = mraa_ftdi_ft4222_dlsym("FT4222_I2CMaster_Init"); - dl_FT4222_I2CMaster_GetStatus = mraa_ftdi_ft4222_dlsym("FT4222_I2CMaster_GetStatus"); - dl_FT4222_GPIO_Init = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_Init"); - dl_FT4222_GPIO_GetTriggerStatus = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_GetTriggerStatus"); - dl_FT4222_GPIO_ReadTriggerQueue = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_ReadTriggerQueue"); - dl_FT4222_GPIO_Read = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_Read"); - dl_FT4222_GPIO_SetInputTrigger = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_SetInputTrigger"); - dl_FT4222_GPIO_Write = mraa_ftdi_ft4222_dlsym("FT4222_GPIO_Write"); - dl_FT4222_SetWakeUpInterrupt = mraa_ftdi_ft4222_dlsym("FT4222_SetWakeUpInterrupt"); - dl_FT4222_SetSuspendOut = mraa_ftdi_ft4222_dlsym("FT4222_SetSuspendOut"); - dl_FT4222_SPIMaster_Init = mraa_ftdi_ft4222_dlsym("FT4222_SPIMaster_Init"); - dl_FT_OpenEx = mraa_ftdi_ft4222_dlsym("FT_OpenEx"); - - if (!libft4222_load_success) { - syslog(LOG_ERR, "Failed to find all symbols for FTDI4222 support"); - goto init_exit; - } - - ftStatus = dl_FT_CreateDeviceInfoList(&numDevs); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_CreateDeviceInfoList failed: error code %d\n", ftStatus); - goto init_exit; - } - - devInfo = calloc((size_t) numDevs, sizeof(FT_DEVICE_LIST_INFO_NODE)); - if (devInfo == NULL) { - syslog(LOG_ERR, "FT4222 allocation failure.\n"); - goto init_exit; - } - - ftStatus = dl_FT_GetDeviceInfoList(devInfo, &numDevs); - syslog(LOG_NOTICE, "FT_GetDeviceInfoList returned %d devices\n", numDevs); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_GetDeviceInfoList failed (error code %d)\n", (int) ftStatus); - goto init_exit; - } - - int first_4222_ndx = -1; - int ftdi_mode = -1; - /* Look for FT_DEVICE_4222H_0 devices. Print out all devices found. */ - for (int i = 0; i < numDevs; i++) - { - /* Log info for debugging */ - syslog(LOG_NOTICE, " FTDI ndx: %02d id: 0x%08x %s description: '%s'\n", - i, - devInfo[i].ID, - (devInfo[i].Flags & 0x2) ? "High-speed USB" : "Full-speed USB", - devInfo[i].Description); - - /* FTDI_4222 mode 3 provides 2 devices */ - if ((first_4222_ndx == -1) && (devInfo[i].Type == FT_DEVICE_4222H_0) && - ((i + 1 < numDevs) && (devInfo[i].ID == devInfo[i + 1].ID))) { - first_4222_ndx = i; - ftdi_mode = 3; - } - /* FTDI_4222 mode 0 provides 1 device */ - else if ((first_4222_ndx == -1) && (devInfo[i].Type == FT_DEVICE_4222H_0)) { - first_4222_ndx = i; - ftdi_mode = 0; - } - } - - /* Was a usable 4222 found? */ - if (first_4222_ndx == -1) { - syslog(LOG_ERR, "No FT4222 (mode 0 or 3) devices found.\n"); - goto init_exit; - } - - syslog(LOG_NOTICE, "FTDI 4222 device found at ndx: %02d, mode %d\n", first_4222_ndx, ftdi_mode); - - /* Both modes provide a SPI/I2C at the first ndx */ - syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as SPI/I2C\n", first_4222_ndx); - - /* Setup I2c */ - DWORD locationIdI2c = devInfo[first_4222_ndx].LocId; - if (locationIdI2c == 0) { - syslog(LOG_ERR, "No I2C controller for FTDI_4222 device\n"); - goto init_exit; - } - - ftStatus = dl_FT_OpenEx((PVOID)(uintptr_t) locationIdI2c, FT_OPEN_BY_LOCATION, &ftHandleI2c); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_OpenEx failed (error %d)\n", (int) ftStatus); - goto init_exit; - } - - // Tell the FT4222 to be an I2C Master by default on init. - FT4222_STATUS ft4222Status = dl_FT4222_I2CMaster_Init(ftHandleI2c, bus_speed); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!\n", ft4222Status); - goto init_exit; - } - - ft4222Status = dl_FT4222_I2CMaster_Reset(ftHandleI2c); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!\n", ft4222Status); - goto init_exit; - } - - /* Mode 3 adds 1 GPIO device */ - if (ftdi_mode == 3) { - syslog(LOG_NOTICE, "FTDI ndx: %02d initializing as GPIO device\n", first_4222_ndx + 1); - - /* Setup I2c */ - - // FIXME: Assumes just one physical FTDI device present - DWORD locationIdGpio = devInfo[first_4222_ndx + 1].LocId; - - - if (locationIdGpio == 0) { - syslog(LOG_ERR, "No GPIO controller for FTDI_4222 device\n"); - goto init_exit; - } - - - ftStatus = dl_FT_OpenEx((PVOID)(uintptr_t) locationIdGpio, FT_OPEN_BY_LOCATION, &ftHandleGpio); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT_OpenEx failed (error %d)\n", (int) ftStatus); - goto init_exit; - } - - dl_FT4222_SetSuspendOut(ftHandleGpio, 0); - dl_FT4222_SetWakeUpInterrupt(ftHandleGpio, 0); - ftStatus = dl_FT4222_GPIO_Init(ftHandleGpio, pinDirection); - if (ftStatus != FT_OK) { - syslog(LOG_ERR, "FT4222_GPIO_Init failed (error %d)\n", (int) ftStatus); - mraaStatus = MRAA_ERROR_NO_RESOURCES; - goto init_exit; - } - } - - mraaStatus = MRAA_SUCCESS; - -init_exit: - if (devInfo != NULL) - free(devInfo); - if (mraaStatus == MRAA_SUCCESS) - syslog(LOG_NOTICE, "mraa_ftdi_ft4222_init completed successfully\n"); - return mraaStatus; -} - - -mraa_result_t -mraa_ftdi_ft4222_get_version(unsigned int* versionChip, unsigned int* versionLib) -{ - if (ftHandleI2c != NULL) { - FT4222_Version ft4222Version; - FT4222_STATUS ft4222Status = dl_FT4222_GetVersion(ftHandleI2c, &ft4222Version); - if (FT4222_OK == ft4222Status) { - *versionChip = (unsigned int) ft4222Version.chipVersion; - *versionLib = (unsigned int) ft4222Version.dllVersion; - syslog(LOG_NOTICE, "FT4222_GetVersion %08X %08X\n", *versionChip, *versionLib); - return MRAA_SUCCESS; - } else { - syslog(LOG_ERR, "FT4222_GetVersion failed (error %d)\n", (int) ft4222Status); - return MRAA_ERROR_NO_RESOURCES; - } - } else { - syslog(LOG_ERR, "Bad FT4222 handle\n"); - return MRAA_ERROR_INVALID_HANDLE; - } -} - - -/******************* Private I2C functions *******************/ - -static int -mraa_ftdi_ft4222_i2c_read_internal(FT_HANDLE handle, uint8_t addr, uint8_t* data, int length) -{ - uint16 bytesRead = 0; - uint8 controllerStatus; - - /* If a read fails, check the I2C controller status, reset the controller, return 0? */ - if(dl_FT4222_I2CMaster_Read(handle, addr, data, length, &bytesRead) != FT4222_OK) { - dl_FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus); - - syslog(LOG_ERR, "FT4222_I2CMaster_Read failed for address %#02x. Code %d", addr, controllerStatus); - dl_FT4222_I2CMaster_Reset(handle); - bytesRead = 0; - } - - return bytesRead; -} - -static int -mraa_ftdi_ft4222_i2c_write_internal(FT_HANDLE handle, uint8_t addr, const uint8_t* data, int bytesToWrite) -{ - uint16 bytesWritten = 0; - uint8 controllerStatus; - - /* If a write fails, check the I2C controller status, reset the controller, return 0? */ - if(dl_FT4222_I2CMaster_Write(handle, addr, (uint8_t*) data, bytesToWrite, &bytesWritten) - != FT4222_OK) { - dl_FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus); - - syslog(LOG_ERR, "FT4222_I2CMaster_Write failed address %#02x. Code %d\n", addr, controllerStatus); - dl_FT4222_I2CMaster_Reset(handle); - bytesWritten = 0; - } - - if (bytesWritten != bytesToWrite) - syslog(LOG_ERR, "FT4222_I2CMaster_Write wrote %u of %u bytes.\n", bytesWritten, bytesToWrite); - - return bytesWritten; -} - - -// Function detects known I2C I/O expanders and returns the number of GPIO pins on expander -static int -mraa_ftdi_ft4222_detect_io_expander() -{ - uint8_t data; - if (mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &data, 1) == 1) { - gpio_expander_chip = IO_EXP_PCA9672; - return PCA9672_PINS; - } else { - uint8_t reg = PCA9555_INPUT_REG; - mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, ®, 1); - if (mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9555_ADDR, &data, 1) == 1) { - gpio_expander_chip = IO_EXP_PCA9555; - reg = PCA9555_OUTPUT_REG; - mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, ®, 1); - mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9555_ADDR, (uint8_t*)&pca9555OutputValue, 2); - reg = PCA9555_DIRECTION_REG; - mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, ®, 1); - mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9555_ADDR, (uint8_t*)&pca9555DirectionValue, 2); - return PCA9555_PINS; - } - } - gpio_expander_chip = IO_EXP_NONE; - return 0; -} - - -static ft4222_gpio_type -mraa_ftdi_ft4222_get_gpio_type(int pin) -{ - if (pin < numFt4222GpioPins) { - return GPIO_TYPE_BUILTIN; - } else switch (gpio_expander_chip) { - case IO_EXP_PCA9672: - return GPIO_TYPE_PCA9672; - case GPIO_TYPE_PCA9555: - return GPIO_TYPE_PCA9555; - default: - return GPIO_TYPE_UNKNOWN; - } -} - - -static mraa_result_t -ftdi_ft4222_set_internal_gpio_dir(int pin, GPIO_Dir direction) -{ - pinDirection[pin] = direction; - if (dl_FT4222_GPIO_Init(ftHandleGpio, pinDirection) != FT4222_OK) - return MRAA_ERROR_UNSPECIFIED; - else - return MRAA_SUCCESS; -} - - -static mraa_result_t -mraa_ftdi_ft4222_gpio_set_pca9672_dir(int pin, mraa_gpio_dir_t dir) -{ - uint8_t mask = 1 << pin; - switch (dir) { - case MRAA_GPIO_IN: - pca9672DirectionMask |= mask; - int bytes_written = mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9672_ADDR, &pca9672DirectionMask, 1); - return bytes_written == 1 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; - case MRAA_GPIO_OUT: - pca9672DirectionMask &= (~mask); - return MRAA_SUCCESS; - default: - return MRAA_ERROR_UNSPECIFIED; - } -} - - -static mraa_result_t -mraa_ftdi_ft4222_gpio_set_pca9555_dir(int pin, mraa_gpio_dir_t dir) -{ - uint16_t mask = 1 << pin; - switch (dir) { - case MRAA_GPIO_IN: - pca9555DirectionValue |= mask; - break; - case MRAA_GPIO_OUT: - pca9555DirectionValue &= (~mask); - break; - default: - return MRAA_ERROR_UNSPECIFIED; - } - uint8_t buf[3]; - buf[0] = PCA9555_DIRECTION_REG; - buf[1] = (uint8_t)(pca9555DirectionValue & 0xFF); - buf[2] = (uint8_t)(pca9555DirectionValue >> 8); - pthread_mutex_lock(&ft4222_lock); - int bytes_written = mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, buf, sizeof(buf)); - pthread_mutex_unlock(&ft4222_lock); - return bytes_written == sizeof(buf) ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; -} - - - -static mraa_result_t -ftdi_ft4222_set_internal_gpio_trigger(int pin, GPIO_Trigger trigger) -{ - FT4222_STATUS ft4222Status = dl_FT4222_GPIO_SetInputTrigger(ftHandleGpio, pin, trigger); - if (ft4222Status == FT4222_OK) - return MRAA_SUCCESS; - else - return MRAA_ERROR_UNSPECIFIED; -} - - - - -// Function detects known I2C switches and returns the number of busses. -// On startup switch is disabled so default bus will be integrated i2c bus. -static int -mraa_ftdi_ft4222_detect_i2c_switch() -{ - uint8_t data; - if(mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1) { - data = 0; - return mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1 ? PCA9545_BUSSES : 0; - } - return 0; -} - - -static mraa_result_t -mraa_ftdi_ft4222_i2c_select_bus(int bus) -{ - if (bus > 0 && bus != currentI2cBus) { - syslog(LOG_NOTICE, "mraa_ftdi_ft4222_i2c_select_bus switching to bus %d", bus); - uint8_t data; - if (bus == 0) - data = 0; - else - data = 1 << (bus-1); - if (mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9545_ADDR, &data, 1) == 1) - currentI2cBus = bus; - else - return MRAA_ERROR_UNSPECIFIED; - } - return MRAA_SUCCESS; -} - -static int -mraa_ftdi_ft4222_i2c_context_read(mraa_i2c_context dev, uint8_t* data, int length) -{ - int bytes_read = 0; - if (mraa_ftdi_ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) - bytes_read = mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, data, length); - return bytes_read; -} - -static int -mraa_ftdi_ft4222_i2c_context_write(mraa_i2c_context dev, uint8_t* data, int length) -{ - int bytes_written = 0; - if (mraa_ftdi_ft4222_i2c_select_bus(dev->busnum) == MRAA_SUCCESS) - bytes_written = mraa_ftdi_ft4222_i2c_write_internal(dev->handle, dev->addr, data, length); - return bytes_written; -} - - - -/******************* I2C functions *******************/ - -static mraa_result_t -mraa_ftdi_ft4222_i2c_init_bus_replace(mraa_i2c_context dev) -{ - // Tell the FT4222 to be an I2C Master. - FT4222_STATUS ft4222Status = dl_FT4222_I2CMaster_Init(ftHandleI2c, bus_speed); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Init failed (error %d)!\n", ft4222Status); - return MRAA_ERROR_NO_RESOURCES; - } - - // Reset the I2CM registers to a known state. - ft4222Status = dl_FT4222_I2CMaster_Reset(ftHandleI2c); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_I2CMaster_Reset failed (error %d)!\n", ft4222Status); - return MRAA_ERROR_NO_RESOURCES; - } - - syslog(LOG_NOTICE, "I2C interface enabled GPIO0 and GPIO1 will be unavailable.\n"); - dev->handle = ftHandleI2c; - dev->fh = -1; // We don't use file descriptors - dev->funcs = I2C_FUNC_I2C; // Advertise minimal i2c support as per - // https://www.kernel.org/doc/Documentation/i2c/functionality - return MRAA_SUCCESS; -} - - -static mraa_result_t -mraa_ftdi_ft4222_i2c_frequency(mraa_i2c_context dev, mraa_i2c_mode_t mode) -{ - switch (mode) { - case MRAA_I2C_STD: /**< up to 100Khz */ - bus_speed = 100; - break; - case MRAA_I2C_FAST: /**< up to 400Khz */ - bus_speed = 400; - break; - case MRAA_I2C_HIGH: /**< up to 3.4Mhz */ - bus_speed = 3400; - break; - } - return dl_FT4222_I2CMaster_Init(ftHandleI2c, bus_speed) == FT4222_OK ? MRAA_SUCCESS : MRAA_ERROR_NO_RESOURCES; -} - - -static mraa_result_t -mraa_ftdi_ft4222_i2c_address(mraa_i2c_context dev, uint8_t addr) -{ - dev->addr = (int) addr; - return MRAA_SUCCESS; -} - - -static int -mraa_ftdi_ft4222_i2c_read(mraa_i2c_context dev, uint8_t* data, int length) -{ - pthread_mutex_lock(&ft4222_lock); - int bytes_read = mraa_ftdi_ft4222_i2c_read_internal(dev->handle, dev->addr, data, length); - pthread_mutex_unlock(&ft4222_lock); - return bytes_read; -} - -static int -mraa_ftdi_ft4222_i2c_read_byte(mraa_i2c_context dev) -{ - uint8_t data; - pthread_mutex_lock(&ft4222_lock); - int bytes_read = mraa_ftdi_ft4222_i2c_context_read(dev, &data, 1); - pthread_mutex_unlock(&ft4222_lock); - return bytes_read == 1 ? data : -1; -} - -static int -mraa_ftdi_ft4222_i2c_read_byte_data(mraa_i2c_context dev, uint8_t command) -{ - uint8_t data; - int bytes_read = 0; - pthread_mutex_lock(&ft4222_lock); - uint16 bytesWritten = mraa_ftdi_ft4222_i2c_context_write(dev, &command, 1); - if (bytesWritten == 1) - bytes_read = mraa_ftdi_ft4222_i2c_context_read(dev, &data, 1); - pthread_mutex_unlock(&ft4222_lock); - if (bytes_read == 1) { - return (int) data; - } - return -1; -} - -static int -mraa_ftdi_ft4222_i2c_read_word_data(mraa_i2c_context dev, uint8_t command) -{ - union i2c_read_buf { - uint8_t bytes[2]; - uint16_t word; - } buf; - int bytes_read = 0; - - pthread_mutex_lock(&ft4222_lock); - int bytes_written = mraa_ftdi_ft4222_i2c_context_write(dev, &command, 1); - if (bytes_written == 1) - bytes_read = mraa_ftdi_ft4222_i2c_context_read(dev, buf.bytes, 2); - pthread_mutex_unlock(&ft4222_lock); - - if (bytes_read == 2) { - return (int) buf.word; - } - - return -1; -} - -static int -mraa_ftdi_ft4222_i2c_read_bytes_data(mraa_i2c_context dev, uint8_t command, uint8_t* data, int length) -{ - int bytes_read = 0; - pthread_mutex_lock(&ft4222_lock); - int bytes_written = mraa_ftdi_ft4222_i2c_context_write(dev, &command, 1); - if (bytes_written == 1) - bytes_read = mraa_ftdi_ft4222_i2c_context_read(dev, data, length); - pthread_mutex_unlock(&ft4222_lock); - return bytes_read; -} - -static mraa_result_t -mraa_ftdi_ft4222_i2c_write(mraa_i2c_context dev, const uint8_t* data, int bytesToWrite) -{ - pthread_mutex_lock(&ft4222_lock); - uint16 bytesWritten = mraa_ftdi_ft4222_i2c_context_write(dev, (uint8_t*)data, bytesToWrite); - pthread_mutex_unlock(&ft4222_lock); - return bytesToWrite == bytesWritten ? MRAA_SUCCESS : MRAA_ERROR_INVALID_HANDLE; -} - - -static mraa_result_t -mraa_ftdi_ft4222_i2c_write_byte(mraa_i2c_context dev, uint8_t data) -{ - mraa_result_t status = mraa_ftdi_ft4222_i2c_write(dev, &data, 1); - return status; -} - -static mraa_result_t -mraa_ftdi_ft4222_i2c_write_byte_data(mraa_i2c_context dev, const uint8_t data, const uint8_t command) -{ - uint8_t buf[2]; - buf[0] = command; - buf[1] = data; - mraa_result_t status = mraa_ftdi_ft4222_i2c_write(dev, buf, 2); - return status; -} - -static mraa_result_t -mraa_ftdi_ft4222_i2c_write_word_data(mraa_i2c_context dev, const uint16_t data, const uint8_t command) -{ - uint8_t buf[3]; - buf[0] = command; - buf[1] = (uint8_t) data; - buf[2] = (uint8_t)(data >> 8); - mraa_result_t status = mraa_ftdi_ft4222_i2c_write(dev, buf, 3); - return status; -} - -static mraa_result_t -mraa_ftdi_ft4222_i2c_stop(mraa_i2c_context dev) -{ - return MRAA_SUCCESS; -} - -/******************* GPIO functions *******************/ - -static mraa_result_t -mraa_ftdi_ft4222_gpio_init_internal_replace(mraa_gpio_context dev, int pin) -{ - dev->phy_pin = (pin < numFt4222GpioPins) ? pin : pin - numFt4222GpioPins; - if (pin < 2) { - syslog(LOG_NOTICE, "Closing I2C interface to enable GPIO%d\n", pin); - - /* Replace with call to SPI init when SPI is fully implemented */ - FT4222_STATUS ft4222Status = dl_FT4222_SPIMaster_Init(ftHandleSpi, SPI_IO_SINGLE, CLK_DIV_4, CLK_IDLE_HIGH, CLK_LEADING, 0x01); - if (FT4222_OK != ft4222Status){ - syslog(LOG_ERR, "Failed to close I2C interface and start SPI (error %d)!\n", ft4222Status); - return MRAA_ERROR_NO_RESOURCES; - } - } - return MRAA_SUCCESS; -} - -static mraa_result_t -mraa_ftdi_ft4222_gpio_mode_replace(mraa_gpio_context dev, mraa_gpio_mode_t mode) -{ - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; -} - -static mraa_result_t -mraa_ftdi_ft4222_gpio_edge_mode_replace(mraa_gpio_context dev, mraa_gpio_edge_t mode) -{ - switch (mraa_ftdi_ft4222_get_gpio_type(dev->pin)) { - case GPIO_TYPE_BUILTIN: - switch (mode) { - case MRAA_GPIO_EDGE_NONE: - return ftdi_ft4222_set_internal_gpio_trigger(dev->pin, 0); - case MRAA_GPIO_EDGE_BOTH: - return ftdi_ft4222_set_internal_gpio_trigger(dev->pin, GPIO_TRIGGER_RISING | GPIO_TRIGGER_FALLING); - case MRAA_GPIO_EDGE_RISING: - return ftdi_ft4222_set_internal_gpio_trigger(dev->pin, GPIO_TRIGGER_RISING); - case MRAA_GPIO_EDGE_FALLING: - return ftdi_ft4222_set_internal_gpio_trigger(dev->pin, GPIO_TRIGGER_FALLING); - default: - return MRAA_ERROR_FEATURE_NOT_IMPLEMENTED; - } - break; - case GPIO_TYPE_PCA9672: - case GPIO_TYPE_PCA9555: - return MRAA_SUCCESS; - default: - return MRAA_ERROR_INVALID_RESOURCE; - } - -} - -static mraa_result_t -mraa_ftdi_ft4222_i2c_read_io_expander(uint16_t* value) -{ - int bytes_read = 0; - uint8_t reg = PCA9555_INPUT_REG; - pthread_mutex_lock(&ft4222_lock); - switch (gpio_expander_chip) { - case IO_EXP_PCA9672: - bytes_read = mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, (uint8_t*)value, 1); - break; - case GPIO_TYPE_PCA9555: - if (mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, ®, 1) == 1) - bytes_read = mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9555_ADDR, (uint8_t*)value, 2); - break; - default:; - } - pthread_mutex_unlock(&ft4222_lock); - return bytes_read > 0 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; -} - -static int -mraa_ftdi_ft4222_gpio_read_replace(mraa_gpio_context dev) -{ - switch (mraa_ftdi_ft4222_get_gpio_type(dev->pin)) { - case GPIO_TYPE_BUILTIN: - { - BOOL value; - FT4222_STATUS ft4222Status = dl_FT4222_GPIO_Read(ftHandleGpio, dev->phy_pin, &value); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_GPIO_Read failed (error %d)!\n", ft4222Status); - return -1; - } - return value; - } - case GPIO_TYPE_PCA9672: - case GPIO_TYPE_PCA9555: - { - uint16_t mask = 1 << dev->phy_pin; - uint16_t value; - mraa_result_t res = mraa_ftdi_ft4222_i2c_read_io_expander(&value); - return res == MRAA_SUCCESS ? (value & mask) == mask : -1; - } - default: - return -1; - } -} - - -static mraa_result_t -mraa_ftdi_ft4222_gpio_write_replace(mraa_gpio_context dev, int write_value) -{ - switch (mraa_ftdi_ft4222_get_gpio_type(dev->pin)) { - case GPIO_TYPE_BUILTIN: - { - FT4222_STATUS ft4222Status = dl_FT4222_GPIO_Write(ftHandleGpio, dev->phy_pin, write_value); - if (FT4222_OK != ft4222Status) { - syslog(LOG_ERR, "FT4222_GPIO_Write failed (error %d)!\n", ft4222Status); - return MRAA_ERROR_UNSPECIFIED; - } - return MRAA_SUCCESS; - } - case GPIO_TYPE_PCA9672: - { - uint8_t mask = 1 << dev->phy_pin; - uint8_t value; - int bytes_written = 0; - pthread_mutex_lock(&ft4222_lock); - int bytes_read = mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &value, 1); - if (bytes_read == 1) { - if (write_value == 1) - value = value | mask | pca9672DirectionMask; - else - value &= (~mask); - bytes_written = mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9672_ADDR, &value, 1); - } - pthread_mutex_unlock(&ft4222_lock); - return bytes_written == 1 ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; - } - case GPIO_TYPE_PCA9555: - { - uint16_t mask = 1 << dev->phy_pin; - if (write_value) - pca9555OutputValue |= mask; - else - pca9555OutputValue &= (~mask); - uint8_t buf[3]; - buf[0] = PCA9555_OUTPUT_REG; - buf[1] = (uint8_t)(pca9555OutputValue & 0xFF); - buf[2] = (uint8_t)(pca9555OutputValue >> 8); - pthread_mutex_lock(&ft4222_lock); - int bytes_written = mraa_ftdi_ft4222_i2c_write_internal(ftHandleI2c, PCA9555_ADDR, buf, sizeof(buf)); - pthread_mutex_unlock(&ft4222_lock); - return bytes_written == sizeof(buf) ? MRAA_SUCCESS : MRAA_ERROR_UNSPECIFIED; - } - default: - return MRAA_ERROR_INVALID_RESOURCE; - } -} - - -static mraa_result_t -mraa_ftdi_ft4222_gpio_dir_replace(mraa_gpio_context dev, mraa_gpio_dir_t dir) -{ - switch (mraa_ftdi_ft4222_get_gpio_type(dev->pin)) { - case GPIO_TYPE_BUILTIN: - switch (dir) { - case MRAA_GPIO_IN: - return ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_INPUT); - case MRAA_GPIO_OUT: - return ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT); - case MRAA_GPIO_OUT_HIGH: - if (ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 1); - case MRAA_GPIO_OUT_LOW: - if (ftdi_ft4222_set_internal_gpio_dir(dev->phy_pin, GPIO_OUTPUT) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 0); - default: - return MRAA_ERROR_INVALID_PARAMETER; - } - case GPIO_TYPE_PCA9672: - switch (dir) { - case MRAA_GPIO_IN: - case MRAA_GPIO_OUT: - return mraa_ftdi_ft4222_gpio_set_pca9672_dir(dev->phy_pin, dir); - case MRAA_GPIO_OUT_HIGH: - if (mraa_ftdi_ft4222_gpio_set_pca9672_dir(dev->phy_pin, dir) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 1); - case MRAA_GPIO_OUT_LOW: - if (mraa_ftdi_ft4222_gpio_set_pca9672_dir(dev->phy_pin, dir) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 0); - default: - return MRAA_ERROR_INVALID_PARAMETER; - } - case GPIO_TYPE_PCA9555: - switch (dir) { - case MRAA_GPIO_IN: - case MRAA_GPIO_OUT: - return mraa_ftdi_ft4222_gpio_set_pca9555_dir(dev->phy_pin, dir); - case MRAA_GPIO_OUT_HIGH: - if (mraa_ftdi_ft4222_gpio_set_pca9555_dir(dev->phy_pin, dir) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 1); - case MRAA_GPIO_OUT_LOW: - if (mraa_ftdi_ft4222_gpio_set_pca9555_dir(dev->phy_pin, dir) != MRAA_SUCCESS) - return MRAA_ERROR_UNSPECIFIED; - return mraa_ftdi_ft4222_gpio_write_replace(dev, 0); - default: - return MRAA_ERROR_INVALID_PARAMETER; - } - default: - return MRAA_ERROR_INVALID_RESOURCE; - } -} - -static mraa_boolean_t -mraa_ftdi_ft4222_has_internal_gpio_triggered(int pin) -{ - uint16 num_events = 0; - dl_FT4222_GPIO_GetTriggerStatus(ftHandleGpio, pin, &num_events); - if (num_events > 0) { - int i; - uint16 num_events_read; - GPIO_Trigger event; - for (i = 0; i < num_events; ++i) - dl_FT4222_GPIO_ReadTriggerQueue(ftHandleGpio, pin, &event, 1, &num_events_read); - return TRUE; - } else - return FALSE; -} - -static struct { - pthread_t thread; - pthread_mutex_t mutex; - mraa_boolean_t should_stop; - mraa_boolean_t is_interrupt_detected[MAX_IO_EXPANDER_PINS]; - int num_active_pins; -} gpio_monitor = {0}; - -// INT pin of i2c PCA9672 GPIO expander is connected to FT4222 GPIO #3 -// We use INT to detect any expander GPIO level change -static void* -mraa_ftdi_ft4222_gpio_monitor(void *arg) -{ - uint16_t prev_value = 0; - mraa_ftdi_ft4222_i2c_read_io_expander(&prev_value); - while (!gpio_monitor.should_stop) { - mraa_boolean_t gpio_activity_detected = mraa_ftdi_ft4222_has_internal_gpio_triggered(GPIO_PORT_IO_INT); - if (gpio_activity_detected) { - uint16_t value; - if (mraa_ftdi_ft4222_i2c_read_io_expander(&value) == MRAA_SUCCESS) { - uint16_t change_value = prev_value ^ value; - int i; - pthread_mutex_lock(&gpio_monitor.mutex); - for (i = 0; i < MAX_IO_EXPANDER_PINS; ++i) { - uint16_t mask = 1 << i; - gpio_monitor.is_interrupt_detected[i] = (change_value & mask) ? 1 : 0; - } - pthread_mutex_unlock(&gpio_monitor.mutex); - prev_value = value; - } - } - mraa_ftdi_ft4222_sleep_ms(20); - } - return NULL; -} - - -static void -mraa_ftdi_ft4222_gpio_monitor_add_pin(int pin) -{ - if (gpio_monitor.num_active_pins == 0) { - pthread_mutex_init(&gpio_monitor.mutex, NULL); - pthread_create(&gpio_monitor.thread, NULL, mraa_ftdi_ft4222_gpio_monitor, NULL); - } - pthread_mutex_lock(&gpio_monitor.mutex); - gpio_monitor.num_active_pins++; - pthread_mutex_unlock(&gpio_monitor.mutex); -} - - -static void -mraa_ftdi_ft4222_gpio_monitor_remove_pin(int pin) -{ - pthread_mutex_lock(&gpio_monitor.mutex); - gpio_monitor.num_active_pins--; - if (gpio_monitor.num_active_pins == 0) { - pthread_mutex_unlock(&gpio_monitor.mutex); - gpio_monitor.should_stop = TRUE; - pthread_join(gpio_monitor.thread, NULL); - pthread_mutex_destroy(&gpio_monitor.mutex); - } else - pthread_mutex_unlock(&gpio_monitor.mutex); -} - - -static mraa_boolean_t -mraa_ftdi_ft4222_gpio_monitor_is_interrupt_detected(int pin) -{ - mraa_boolean_t is_interrupt_detected = FALSE; - pthread_mutex_lock(&gpio_monitor.mutex); - if (gpio_monitor.is_interrupt_detected[pin]) { - gpio_monitor.is_interrupt_detected[pin] = FALSE; - is_interrupt_detected = TRUE; - } - pthread_mutex_unlock(&gpio_monitor.mutex); - return is_interrupt_detected; -} - -static mraa_result_t -mraa_ftdi_ft4222_gpio_interrupt_handler_init_replace(mraa_gpio_context dev) -{ - switch (mraa_ftdi_ft4222_get_gpio_type(dev->pin)) { - case GPIO_TYPE_BUILTIN: - mraa_ftdi_ft4222_has_internal_gpio_triggered(dev->phy_pin); - break; - case GPIO_TYPE_PCA9672: - case GPIO_TYPE_PCA9555: - ftdi_ft4222_set_internal_gpio_dir(GPIO_PORT_IO_INT, GPIO_INPUT); - ftdi_ft4222_set_internal_gpio_trigger(GPIO_PORT_IO_INT, GPIO_TRIGGER_FALLING); - mraa_ftdi_ft4222_has_internal_gpio_triggered(GPIO_PORT_IO_INT); - mraa_ftdi_ft4222_gpio_monitor_add_pin(dev->phy_pin); - break; - default: - return MRAA_ERROR_INVALID_RESOURCE; - } - return MRAA_SUCCESS; -} - -static mraa_result_t -mraa_ftdi_ft4222_gpio_wait_interrupt_replace(mraa_gpio_context dev) -{ - mraa_ftdi_ft4222_gpio_read_replace(dev); - ft4222_gpio_type gpio_type = mraa_ftdi_ft4222_get_gpio_type(dev->pin); - mraa_boolean_t interrupt_detected = FALSE; - - while (!dev->isr_thread_terminating && !interrupt_detected) { - switch (gpio_type) { - case GPIO_TYPE_BUILTIN: - interrupt_detected = mraa_ftdi_ft4222_has_internal_gpio_triggered(dev->phy_pin); - break; - case GPIO_TYPE_PCA9672: - case GPIO_TYPE_PCA9555: - interrupt_detected = mraa_ftdi_ft4222_gpio_monitor_is_interrupt_detected(dev->phy_pin); - break; - default:; - } - if (!interrupt_detected) - mraa_ftdi_ft4222_sleep_ms(20); - } - if (dev->isr_thread_terminating) - mraa_ftdi_ft4222_gpio_monitor_remove_pin(dev->phy_pin); - return MRAA_SUCCESS; -} - -static void -mraa_ftdi_ft4222_populate_i2c_func_table(mraa_adv_func_t* func_table) -{ - func_table->i2c_init_bus_replace = &mraa_ftdi_ft4222_i2c_init_bus_replace; - func_table->i2c_set_frequency_replace = &mraa_ftdi_ft4222_i2c_frequency; - func_table->i2c_address_replace = &mraa_ftdi_ft4222_i2c_address; - func_table->i2c_read_replace = &mraa_ftdi_ft4222_i2c_read; - func_table->i2c_read_byte_replace = &mraa_ftdi_ft4222_i2c_read_byte; - func_table->i2c_read_byte_data_replace = &mraa_ftdi_ft4222_i2c_read_byte_data; - func_table->i2c_read_word_data_replace = &mraa_ftdi_ft4222_i2c_read_word_data; - func_table->i2c_read_bytes_data_replace = &mraa_ftdi_ft4222_i2c_read_bytes_data; - func_table->i2c_write_replace = &mraa_ftdi_ft4222_i2c_write; - func_table->i2c_write_byte_replace = &mraa_ftdi_ft4222_i2c_write_byte; - func_table->i2c_write_byte_data_replace = &mraa_ftdi_ft4222_i2c_write_byte_data; - func_table->i2c_write_word_data_replace = &mraa_ftdi_ft4222_i2c_write_word_data; - func_table->i2c_stop_replace = &mraa_ftdi_ft4222_i2c_stop; -} - -static void -mraa_ftdi_ft4222_populate_gpio_func_table(mraa_adv_func_t* func_table) -{ - func_table->gpio_init_internal_replace = &mraa_ftdi_ft4222_gpio_init_internal_replace; - func_table->gpio_mode_replace = &mraa_ftdi_ft4222_gpio_mode_replace; - func_table->gpio_edge_mode_replace = &mraa_ftdi_ft4222_gpio_edge_mode_replace; - func_table->gpio_dir_replace = &mraa_ftdi_ft4222_gpio_dir_replace; - func_table->gpio_read_replace = &mraa_ftdi_ft4222_gpio_read_replace; - func_table->gpio_write_replace = &mraa_ftdi_ft4222_gpio_write_replace; - func_table->gpio_interrupt_handler_init_replace = &mraa_ftdi_ft4222_gpio_interrupt_handler_init_replace; - func_table->gpio_wait_interrupt_replace = &mraa_ftdi_ft4222_gpio_wait_interrupt_replace; -} - - -mraa_board_t* -mraa_ftdi_ft4222() -{ - mraa_pininfo_t* pins = NULL; - mraa_adv_func_t* func_table = NULL; - mraa_board_t* sub_plat = (mraa_board_t*) calloc(1, sizeof(mraa_board_t)); - - if (sub_plat == NULL) - goto mraa_ftdi_ft4222_fail; - - numI2cGpioExpanderPins = mraa_ftdi_ft4222_detect_io_expander(); - int pinIndex = 0; - int numUsbGpio = numFt4222GpioPins + numI2cGpioExpanderPins; - int numI2cBusses = 1 + mraa_ftdi_ft4222_detect_i2c_switch(); - int numUsbPins = numUsbGpio + 2 * (numI2cBusses-1); // Add SDA and SCL for each i2c switch bus - mraa_pincapabilities_t pinCapsI2c = (mraa_pincapabilities_t){ 1, 0, 0, 0, 0, 1, 0, 0 }; - mraa_pincapabilities_t pinCapsI2cGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 1, 0, 0 }; - mraa_pincapabilities_t pinCapsGpio = (mraa_pincapabilities_t){ 1, 1, 0, 0, 0, 0, 0, 0 }; - - sub_plat->platform_name = PLATFORM_NAME; - sub_plat->phy_pin_count = numUsbPins; - sub_plat->gpio_count = numUsbGpio; - pins = (mraa_pininfo_t*) calloc(numUsbPins,sizeof(mraa_pininfo_t)); - - if (pins == NULL) - goto mraa_ftdi_ft4222_fail; - - sub_plat->pins = pins; - - int bus = 0; - sub_plat->i2c_bus_count = numI2cBusses; - sub_plat->def_i2c_bus = bus; - sub_plat->i2c_bus[bus].bus_id = bus; - - // I2c pins (these are virtual, entries are required to configure i2c layer) - // We currently assume that GPIO 0/1 are reserved for i2c operation - strncpy(sub_plat->pins[pinIndex].name, "IGPIO0/SCL0", MRAA_PIN_NAME_SIZE); - sub_plat->pins[pinIndex].capabilities = pinCapsI2cGpio; - sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - sub_plat->pins[pinIndex].i2c.mux_total = 0; - sub_plat->i2c_bus[bus].scl = pinIndex; - pinIndex++; - strncpy(sub_plat->pins[pinIndex].name, "IGPIO1/SDA0", MRAA_PIN_NAME_SIZE); - sub_plat->pins[pinIndex].capabilities = pinCapsI2cGpio; - sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - sub_plat->pins[pinIndex].i2c.mux_total = 0; - sub_plat->i2c_bus[bus].sda = pinIndex; - pinIndex++; - - // FTDI4222 gpio - strncpy(sub_plat->pins[pinIndex].name, "INT-GPIO2", MRAA_PIN_NAME_SIZE); - sub_plat->pins[pinIndex].capabilities = pinCapsGpio; - sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - pinIndex++; - strncpy(sub_plat->pins[pinIndex].name, "INT-GPIO3", MRAA_PIN_NAME_SIZE); - sub_plat->pins[pinIndex].capabilities = pinCapsGpio; - sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - pinIndex++; - - // Virtual gpio pins on i2c I/O expander. - int i; - for (i = 0; i < numI2cGpioExpanderPins; ++i) { - snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "EXP-GPIO%d", i); - sub_plat->pins[pinIndex].capabilities = pinCapsGpio; - sub_plat->pins[pinIndex].gpio.pinmap = pinIndex; - sub_plat->pins[pinIndex].gpio.mux_total = 0; - pinIndex++; - } - - // Now add any extra i2c buses behind i2c switch - for (bus = 1; bus < numI2cBusses; ++bus) { - sub_plat->i2c_bus[bus].bus_id = bus; - sub_plat->pins[pinIndex].i2c.mux_total = 0; - snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "SDA%d", bus); - sub_plat->pins[pinIndex].capabilities = pinCapsI2c; - sub_plat->i2c_bus[bus].sda = pinIndex; - pinIndex++; - snprintf(sub_plat->pins[pinIndex].name, MRAA_PIN_NAME_SIZE, "SCL%d", bus); - sub_plat->pins[pinIndex].capabilities = pinCapsI2c; - sub_plat->pins[pinIndex].i2c.mux_total = 0; - sub_plat->i2c_bus[bus].scl = pinIndex; - pinIndex++; - } - - // Set override functions - func_table = (mraa_adv_func_t*) calloc(1, sizeof(mraa_adv_func_t)); - if (func_table == NULL) - goto mraa_ftdi_ft4222_fail; - - mraa_ftdi_ft4222_populate_i2c_func_table(func_table); - mraa_ftdi_ft4222_populate_gpio_func_table(func_table); - - sub_plat->adv_func = func_table; - - if (pthread_mutex_init(&ft4222_lock, NULL) != 0) { - syslog(LOG_ERR, "Could not create mutex for FT4222 access"); - goto mraa_ftdi_ft4222_fail; - } - - /* Success, return the sub platform */ - return sub_plat; - -mraa_ftdi_ft4222_fail: - if (!func_table) free(func_table); - if (!pins) free(pins); - if (!sub_plat) free(sub_plat); - - return NULL; -} - diff --git a/src/usb/usb.c b/src/usb/usb.c deleted file mode 100644 index 363bfd1..0000000 --- a/src/usb/usb.c +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Author: Henry Bruce - * Copyright (c) 2015 Intel Corporation. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#include -#include - -#include "mraa_internal.h" -#ifdef FTDI4222 -#include "usb/ftdi_ft4222.h" -#endif - - -mraa_platform_t -mraa_usb_platform_extender(mraa_board_t* board) -{ - mraa_board_t* sub_plat = NULL; - mraa_platform_t platform_type = MRAA_UNKNOWN_PLATFORM; - -#ifdef FTDI4222 - libft4222_lib = dlopen("libft4222.so", RTLD_LAZY); - if (!libft4222_lib) { - syslog(LOG_WARNING, "libft4222.so not found, skipping"); - } else { - if (mraa_ftdi_ft4222_init() == MRAA_SUCCESS) { - unsigned int versionChip, versionLib; - if (mraa_ftdi_ft4222_get_version(&versionChip, &versionLib) == MRAA_SUCCESS) { - // TODO: Add ft4222 version checks - platform_type = MRAA_FTDI_FT4222; - } - } - } -#endif - switch (platform_type) { -#ifdef FTDI4222 - case MRAA_FTDI_FT4222: - sub_plat = mraa_ftdi_ft4222(); - break; -#endif - default: - // this is not an error but more that we didn't find a USB platform extender we recognise - syslog(LOG_DEBUG, "Unknown USB Platform Extender, currently not supported by MRAA"); - } - - if (sub_plat != NULL) { - sub_plat->platform_type = platform_type; - board->sub_platform = sub_plat; - } - return platform_type; -} diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index f29e6b6..1db9421 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -20,9 +20,20 @@ target_link_libraries(api_common_hpp_unit GTest::GTest GTest::Main mraa) target_include_directories(api_common_hpp_unit PRIVATE "${CMAKE_SOURCE_DIR}/api") gtest_add_tests(api_common_hpp_unit "" AUTO) +if (FTDI4222) + # Unit tests - Test platform extenders (as much as possible) + add_executable(platform_extender platform_extender/platform_extender.cxx) + target_link_libraries(platform_extender GTest::GTest GTest::Main mraa-platform-ft4222 dl) + target_include_directories(platform_extender PRIVATE "${PROJECT_SOURCE_DIR}/api" + "${PROJECT_SOURCE_DIR}/api/mraa" + "${PROJECT_SOURCE_DIR}/include") + gtest_add_tests(platform_extender "" AUTO) +endif () + # Add a custom target for unit tests add_custom_target(tests-unit ALL DEPENDS api_common_h_unit api_common_hpp_unit - COMMENT "mraa unit test collection") + platform_extender + COMMENT "mraa unit test collection") diff --git a/tests/unit/platform_extender/platform_extender.cxx b/tests/unit/platform_extender/platform_extender.cxx new file mode 100644 index 0000000..ef69fda --- /dev/null +++ b/tests/unit/platform_extender/platform_extender.cxx @@ -0,0 +1,31 @@ +#include + +#include "gtest/gtest.h" +#include "mraa/common.h" +#include "mraa_internal_types.h" + +/* MRAA test fixture */ +class plaform_extender : public ::testing::Test {}; + +/* Not much to test w/o hw - test a no-init case. + * Note, w/o a link to libmraa, this binary does NOT initialize mraa + * with the loader constructor attribute. This is intended. */ +TEST_F(plaform_extender, test_no_init) +{ + mraa_board_t plat = {}; + + void* usblib = dlopen("libmraa-platform-ft4222.so", RTLD_LAZY); + ASSERT_NE((void*)NULL, usblib) + << "Failed to load libmraa-platform-ft4222.so, reason (" << dlerror() + << ")"; + + fptr_add_platform_extender add_ft4222_platform = + (fptr_add_platform_extender)dlsym(usblib, "mraa_usb_platform_extender"); + + ASSERT_NE((void*)NULL, add_ft4222_platform) << "Symbol 'add_ft4222_platform' " + << "does not exist in libmraa-platform-ft4222.so"; + + ASSERT_EQ(MRAA_ERROR_PLATFORM_NOT_INITIALISED, add_ft4222_platform(&plat)) + << "Initialization returned a valid platform. Make sure no Ft4222 " + << "device is connected"; +}