From 65514bb432cda6172d9b2d4ae0b2f24d00d7b6bf Mon Sep 17 00:00:00 2001 From: Henry Bruce Date: Tue, 23 Feb 2016 15:17:21 -0800 Subject: [PATCH] ftdi_ft4222: Refactored GPIO expander ISR code GPIO expander ISRs were dropping interrupts as each pin had its own thread which cleared the INT signal when reading the GPIO register. Thus pending interrupts for GPIO on threads waiting to be scheduled were dropped. Now a single thread polls the GPIO register when INT goes low and sets interrupt flags for each GPIO line. The threads created by mraa_gpio_isr() now poll the appropriate interrupt flag. Signed-off-by: Henry Bruce Signed-off-by: Brendan Le Foll --- src/usb/ftdi_ft4222.c | 96 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 87 insertions(+), 9 deletions(-) diff --git a/src/usb/ftdi_ft4222.c b/src/usb/ftdi_ft4222.c index 18a3d91..04e873e 100644 --- a/src/usb/ftdi_ft4222.c +++ b/src/usb/ftdi_ft4222.c @@ -664,6 +664,89 @@ mraa_ftdi_ft4222_has_internal_gpio_triggered(int pin) return FALSE; } +static struct { + pthread_t thread; + pthread_mutex_t mutex; + mraa_boolean_t should_stop; + mraa_boolean_t is_interrupt_detected[PCA9672_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) +{ + uint8_t prev_value = 0; + pthread_mutex_lock(&ft4222_lock); + mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &prev_value, 1); + pthread_mutex_unlock(&ft4222_lock); + 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) { + uint8_t value; + pthread_mutex_lock(&ft4222_lock); + int bytes_read = mraa_ftdi_ft4222_i2c_read_internal(ftHandleI2c, PCA9672_ADDR, &value, 1); + pthread_mutex_unlock(&ft4222_lock); + if (bytes_read == 1) { + pthread_mutex_lock(&gpio_monitor.mutex); + uint8_t change_value = prev_value ^ value; + int i; + for (i = 0; i < PCA9672_PINS; ++i) { + uint8_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); + } + printf("gpio_monitor_thread stopped\n"); +} + + +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) { @@ -673,6 +756,7 @@ mraa_ftdi_ft4222_gpio_interrupt_handler_init_replace(mraa_gpio_context dev) 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); } return MRAA_SUCCESS; } @@ -684,24 +768,18 @@ mraa_ftdi_ft4222_gpio_wait_interrupt_replace(mraa_gpio_context dev) 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); } 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; - } - } + interrupt_detected = mraa_ftdi_ft4222_gpio_monitor_is_interrupt_detected(dev->phy_pin); } 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; }