Private
Public Access
2
0

ftdi_ft4222: Added GPIO interrupt support for language bindings.

- FT4222 GPIO ISR implementation was specific to C/C++ and ignored
  language binding support. This is now fixed via adv_func updates.
- GPIO ISR code has been refactored to reduce i2c traffic when using
  I/O expanders
- Added support for built-in FT4222 GPIO interrupts

Signed-off-by: Henry Bruce <henry.bruce@intel.com>
This commit is contained in:
Henry Bruce
2016-02-04 12:27:51 -08:00
committed by Brendan Le Foll
parent d2fcb88c4b
commit 1fd4ed5736
3 changed files with 141 additions and 86 deletions

View File

@@ -41,7 +41,7 @@ typedef struct {
mraa_result_t (*gpio_mode_replace) (mraa_gpio_context dev, mraa_gpio_mode_t mode);
mraa_result_t (*gpio_mode_pre) (mraa_gpio_context dev, mraa_gpio_mode_t mode);
mraa_result_t (*gpio_mode_post) (mraa_gpio_context dev, mraa_gpio_mode_t mode);
mraa_result_t (*gpio_edge_mode_replace) (mraa_gpio_context dev, mraa_gpio_edge_t mode);
mraa_result_t (*gpio_dir_replace) (mraa_gpio_context dev, mraa_gpio_dir_t dir);
@@ -53,7 +53,8 @@ typedef struct {
mraa_result_t (*gpio_write_pre) (mraa_gpio_context dev, int value);
mraa_result_t (*gpio_write_post) (mraa_gpio_context dev, int value);
mraa_result_t (*gpio_mmap_setup) (mraa_gpio_context dev, mraa_boolean_t en);
void* (*gpio_interrupt_handler_replace) (mraa_gpio_context dev);
mraa_result_t (*gpio_interrupt_handler_init_replace) (mraa_gpio_context dev);
mraa_result_t (*gpio_wait_interrupt_replace) (mraa_gpio_context dev);
mraa_result_t (*i2c_init_pre) (unsigned int bus);
mraa_result_t (*i2c_init_bus_replace) (mraa_i2c_context dev);

View File

@@ -268,18 +268,21 @@ static void*
mraa_gpio_interrupt_handler(void* arg)
{
mraa_gpio_context dev = (mraa_gpio_context) arg;
if (IS_FUNC_DEFINED(dev, gpio_interrupt_handler_replace))
return dev->advance_func->gpio_interrupt_handler_replace(dev);
int fp = -1;
mraa_result_t ret;
// open gpio value with open(3)
char bu[MAX_SIZE];
sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
int fp = open(bu, O_RDONLY);
if (fp < 0) {
syslog(LOG_ERR, "gpio: failed to open gpio%d/value", dev->pin);
return NULL;
if (IS_FUNC_DEFINED(dev, gpio_interrupt_handler_init_replace)) {
if (dev->advance_func->gpio_interrupt_handler_init_replace(dev) != MRAA_SUCCESS)
return NULL;
} else {
// open gpio value with open(3)
char bu[MAX_SIZE];
sprintf(bu, SYSFS_CLASS_GPIO "/gpio%d/value", dev->pin);
fp = open(bu, O_RDONLY);
if (fp < 0) {
syslog(LOG_ERR, "gpio: failed to open gpio%d/value", dev->pin);
return NULL;
}
}
#ifndef HAVE_PTHREAD_CANCEL
@@ -309,11 +312,15 @@ mraa_gpio_interrupt_handler(void* arg)
#endif
for (;;) {
ret = mraa_gpio_wait_interrupt(dev->isr_value_fp
if (IS_FUNC_DEFINED(dev, gpio_wait_interrupt_replace)) {
ret = dev->advance_func->gpio_wait_interrupt_replace(dev);
} else {
ret = mraa_gpio_wait_interrupt(dev->isr_value_fp
#ifndef HAVE_PTHREAD_CANCEL
, dev->isr_control_pipe[0]
#endif
);
}
if (ret == MRAA_SUCCESS && !dev->isr_thread_terminating) {
#ifdef HAVE_PTHREAD_CANCEL
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
@@ -391,8 +398,10 @@ mraa_gpio_interrupt_handler(void* arg)
#ifdef HAVE_PTHREAD_CANCEL
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
#endif
close(dev->isr_value_fp);
dev->isr_value_fp = -1;
if (fp != -1) {
close(dev->isr_value_fp);
dev->isr_value_fp = -1;
}
#if defined(SWIGJAVA) || defined(JAVACALLBACK)
if(dev->isr == mraa_java_isr_callback) {

View File

@@ -40,7 +40,7 @@
#define PCA9672_PINS 8
#define PCA9545_BUSSES 4
#define GPIO_PORT_IO_RESET GPIO_PORT2
#define GPIO_PORT_IO_STATUS GPIO_PORT3
#define GPIO_PORT_IO_INT GPIO_PORT3
static FT_HANDLE ftHandleGpio = (FT_HANDLE) NULL; //GPIO Handle
static FT_HANDLE ftHandleI2c = (FT_HANDLE) NULL; //I2C/SPI Handle
@@ -52,6 +52,36 @@ static int numI2cGpioExpanderPins = 8;
static int numI2cSwitchBusses = 4;
static int currentI2cBus = 0;
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))
;
}
static unsigned int
mraa_ftdi_ft4222_get_tick_count_ms()
{
static unsigned int startTick = 0;
unsigned int ticks;
struct timeval now;
gettimeofday(&now, NULL);
ticks = now.tv_sec * 1000;
ticks += now.tv_usec / 1000;
if (startTick == 0)
startTick = ticks;
return ticks - startTick;
}
mraa_result_t
mraa_ftdi_ft4222_init()
{
@@ -181,7 +211,8 @@ mraa_ftdi_ft4222_i2c_read_internal(FT_HANDLE handle, uint8_t addr, uint8_t* data
{
uint16 bytesRead = 0;
uint8 controllerStatus;
// syslog(LOG_NOTICE, "FT4222_I2CMaster_Read(%#02X, %#02X)", addr, length);
syslog(LOG_NOTICE, "FT4222_I2CMaster_Read(%#02X, %#02X)", addr, length);
mraa_ftdi_ft4222_sleep_ms(1);
FT4222_STATUS ft4222Status = FT4222_I2CMaster_Read(handle, addr, data, length, &bytesRead);
ft4222Status = FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus);
if (FT4222_OK != ft4222Status || I2CM_ERROR(controllerStatus)) {
@@ -189,6 +220,7 @@ mraa_ftdi_ft4222_i2c_read_internal(FT_HANDLE handle, uint8_t addr, uint8_t* data
FT4222_I2CMaster_Reset(handle);
return 0;
}
syslog(LOG_NOTICE, "FT4222_I2CMaster_Read completed");
return bytesRead;
}
@@ -197,7 +229,7 @@ mraa_ftdi_ft4222_i2c_write_internal(FT_HANDLE handle, uint8_t addr, const uint8_
{
uint16 bytesWritten = 0;
uint8 controllerStatus;
// syslog(LOG_NOTICE, "FT4222_I2CMaster_Write(%#02X, %#02X, %d)", addr, *data, bytesToWrite);
syslog(LOG_NOTICE, "FT4222_I2CMaster_Write(%#02X, %#02X, %d)", addr, *data, bytesToWrite);
FT4222_STATUS ft4222Status = FT4222_I2CMaster_Write(handle, addr, (uint8_t*) data, bytesToWrite, &bytesWritten);
ft4222Status = FT4222_I2CMaster_GetStatus(ftHandleI2c, &controllerStatus);
if (FT4222_OK != ft4222Status || I2CM_ERROR(controllerStatus)) {
@@ -209,6 +241,7 @@ mraa_ftdi_ft4222_i2c_write_internal(FT_HANDLE handle, uint8_t addr, const uint8_
if (bytesWritten != bytesToWrite)
syslog(LOG_ERR, "FT4222_I2CMaster_Write wrote %u of %u bytes.\n", bytesWritten, bytesToWrite);
syslog(LOG_NOTICE, "FT4222_I2CMaster_Write completed");
return bytesWritten;
}
@@ -242,6 +275,17 @@ ftdi_ft4222_set_internal_gpio_dir(int pin, GPIO_Dir direction)
return MRAA_SUCCESS;
}
static mraa_result_t
ftdi_ft4222_set_internal_gpio_trigger(int pin, GPIO_Trigger trigger)
{
FT4222_STATUS ft4222Status = 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
@@ -260,6 +304,7 @@ static mraa_result_t
mraa_ftdi_ft4222_i2c_select_bus(int bus)
{
if (bus != currentI2cBus) {
syslog(LOG_NOTICE, "mraa_ftdi_ft4222_i2c_select_bus switching to bus %d", bus);
uint8_t data;
if (bus == 0)
data = 0;
@@ -292,34 +337,6 @@ mraa_ftdi_ft4222_i2c_context_write(mraa_i2c_context dev, uint8_t* data, int leng
}
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))
;
}
static unsigned int
mraa_ftdi_ft4222_get_tick_count_ms()
{
static unsigned int startTick = 0;
unsigned int ticks;
struct timeval now;
gettimeofday(&now, NULL);
ticks = now.tv_sec * 1000;
ticks += now.tv_usec / 1000;
if (startTick == 0)
startTick = ticks;
return ticks - startTick;
}
/******************* I2C functions *******************/
@@ -488,13 +505,27 @@ mraa_ftdi_ft4222_gpio_init_internal_replace(mraa_gpio_context dev, int pin)
static mraa_result_t
mraa_ftdi_ft4222_gpio_mode_replace(mraa_gpio_context dev, mraa_gpio_mode_t mode)
{
return MRAA_SUCCESS;
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)
{
return MRAA_SUCCESS;
if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) {
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;
}
} else
return MRAA_SUCCESS;
}
static int
@@ -581,49 +612,61 @@ mraa_ftdi_ft4222_gpio_dir_replace(mraa_gpio_context dev, mraa_gpio_dir_t dir)
}
}
static void*
mraa_ftdi_ft4222_gpio_interrupt_handler_replace(mraa_gpio_context dev)
static mraa_boolean_t
mraa_ftdi_ft4222_has_internal_gpio_triggered(int pin)
{
#ifdef USE_FT4222_GPIO_TRIGGER
// FIXME: Use big buffer; shouldn't be more than this many events to read
GPIO_Trigger event_buf[256];
int prev_level = mraa_ftdi_ft4222_gpio_read_replace(dev);
while (1) {
uint16 num_events = 0;
FT4222_STATUS status = FT4222_GPIO_GetTriggerStatus(ftHandleGpio, GPIO_PORT_IO_STATUS, &num_events);
if (status != FT4222_OK)
printf("FT4222_GPIO_GetTriggerStatus failed with code %d\n", status);
printf("%u: FT4222_GPIO_GetTriggerStatus Events = %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), num_events);
if (num_events > 0) {
int level = mraa_ftdi_ft4222_gpio_read_replace(dev);
uint16 num_events_read;
FT4222_GPIO_ReadTriggerQueue(ftHandleGpio, GPIO_PORT_IO_STATUS, event_buf, num_events, &num_events_read);
// printf("%u: FT4222_GPIO_ReadTriggerQueue Events= %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), num_events_read);
printf("%u: level = %d\n", mraa_ftdi_ft4222_get_tick_count_ms(), level);
if (level != prev_level) {
dev->isr(dev->isr_args);
prev_level = level;
}
uint16 num_events = 0;
FT4222_STATUS ft4222Status = 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)
FT4222_GPIO_ReadTriggerQueue(ftHandleGpio, pin, &event, 1, &num_events_read);
return TRUE;
} else
return FALSE;
}
}
mraa_ftdi_ft4222_sleep_ms(20);
// int level = mraa_ftdi_ft4222_gpio_read_replace(dev);
// printf("level = %d\n", level);
static mraa_result_t
mraa_ftdi_ft4222_gpio_interrupt_handler_init_replace(mraa_gpio_context dev)
{
if (mraa_ftdi_ft4222_is_internal_gpio(dev->pin)) {
mraa_ftdi_ft4222_has_internal_gpio_triggered(dev->phy_pin);
} else {
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);
}
#else
return MRAA_SUCCESS;
}
static mraa_result_t
mraa_ftdi_ft4222_gpio_wait_interrupt_replace(mraa_gpio_context dev)
{
int prev_level = mraa_ftdi_ft4222_gpio_read_replace(dev);
while (1) {
int level = mraa_ftdi_ft4222_gpio_read_replace(dev);
// MRAA_GPIO_EDGE_BOTH
if (level != prev_level) {
dev->isr(dev->isr_args);
prev_level = level;
mraa_boolean_t is_internal_pin = mraa_ftdi_ft4222_is_internal_gpio(dev->pin);
mraa_boolean_t interrupt_detected = FALSE;
// INT pin of i2c PCA9672 GPIO expander is connected to FT4222 GPIO #3
// We use INT to detect any expander GPIO level change
while (!dev->isr_thread_terminating && !interrupt_detected) {
if (is_internal_pin) {
interrupt_detected = mraa_ftdi_ft4222_has_internal_gpio_triggered(dev->phy_pin);
}
mraa_ftdi_ft4222_sleep_ms(100);
else {
mraa_boolean_t gpio_activity_detected = mraa_ftdi_ft4222_has_internal_gpio_triggered(GPIO_PORT_IO_INT);
if (gpio_activity_detected) {
int level = mraa_ftdi_ft4222_gpio_read_replace(dev);
if (level != prev_level) {
interrupt_detected = TRUE;
}
}
}
if (!interrupt_detected)
mraa_ftdi_ft4222_sleep_ms(20);
}
#endif
return NULL;
return MRAA_SUCCESS;
}
static void
@@ -653,7 +696,8 @@ mraa_ftdi_ft4222_populate_gpio_func_table(mraa_adv_func_t* func_table)
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_replace = &mraa_ftdi_ft4222_gpio_interrupt_handler_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;
}
@@ -752,3 +796,4 @@ mraa_ftdi_ft4222()
sub_plat->adv_func = func_table;
return sub_plat;
}