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"; +}