diff options
-rw-r--r-- | io-module/mtcdt.c | 4 | ||||
-rw-r--r-- | io-module/mts_io.c | 251 |
2 files changed, 255 insertions, 0 deletions
diff --git a/io-module/mtcdt.c b/io-module/mtcdt.c index 0b7dba1..d945a43 100644 --- a/io-module/mtcdt.c +++ b/io-module/mtcdt.c @@ -270,6 +270,10 @@ static struct attribute *mtcdt_platform_attributes[] = { &dev_attr_radio_power.attr, &dev_attr_radio_reset.attr, + &dev_attr_radio_reset_backoffs.attr, + &dev_attr_radio_reset_backoff_index.attr, + &dev_attr_radio_reset_backoff_seconds.attr, + &dev_attr_led_status.attr, &dev_attr_led_cd_gpio.attr, &dev_attr_led_sig1_gpio.attr, diff --git a/io-module/mts_io.c b/io-module/mts_io.c index 1c9a5b6..995fb1d 100644 --- a/io-module/mts_io.c +++ b/io-module/mts_io.c @@ -40,6 +40,7 @@ #include <linux/spi/spi.h> #include <linux/i2c/at24.h> #include <linux/kmod.h> +#include <linux/ctype.h> #include <linux/io.h> #include <linux/module.h> @@ -90,6 +91,18 @@ static struct gpio_pin *gpio_pins; static DEFINE_MUTEX(mts_io_mutex); +static unsigned int *timings_data = NULL; +static unsigned int timings_data_size = 0; +static unsigned int timings_data_index = 0; +static time_t timings_data_stop_seconds = 0; +static struct timer_list radio_reset_timer; +static volatile int radio_reset_timer_is_start = 0; +static struct timer_list radio_reset_available_timer; +static volatile int radio_reset_available_timer_is_start = 0; +static time_t time_now_secs(); +static void radio_reset_available_timer_callback(unsigned long data); +static void radio_reset_timer_callback(unsigned long data); + /* generic GPIO support */ #include "gpio.c" @@ -267,6 +280,30 @@ static ssize_t mts_attr_store_radio_reset(struct device *dev, return -EINVAL; } + /* check reset timings is enabled */ + if (NULL != timings_data) { + /* check reset timer is started */ + if (radio_reset_timer_is_start == 1) { + log_info("radio reset timer is running. \n"); + return count; + } + + /* check reset timer available is started */ + if (radio_reset_available_timer_is_start == 1) { + del_timer(&radio_reset_available_timer); + radio_reset_available_timer_is_start = 0; + } + + /* reset timer not started, start it */ + mod_timer(&radio_reset_timer, jiffies + msecs_to_jiffies((timings_data[timings_data_index]) * 1000)); + //log_info("radio reset timer is start = [%d]\n", time_now_secs()); + /* save timings_data_stop_seconds */ + timings_data_stop_seconds = timings_data[timings_data_index] + time_now_secs(); + radio_reset_timer_is_start = 1; + } + + log_info("radio is reset\n"); + pin = gpio_pin_by_name("RADIO_RESET"); if (!pin) { @@ -294,6 +331,211 @@ static DEVICE_ATTR_MTS(dev_attr_radio_reset, "radio-reset", static DEVICE_ATTR_MTS(dev_attr_radio_power, "radio-power", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); +/* backoff-timers */ +static time_t time_now_secs() +{ + struct timespec ts = current_kernel_time(); + return ts.tv_sec; +} + +static void radio_reset_available_timer_callback( unsigned long data ) +{ + /* do your timer stuff here */ + //log_info("radio_reset_available_timer_callback\n"); + //log_info("radio reset available timer is stop = [%d]\n", time_now_secs()); + + /* zero timings_data_index */ + timings_data_index = 0; + //log_info("timings data index is zero = [%d]\n", timings_data_index); + radio_reset_available_timer_is_start = 0; +} + +static void radio_reset_timer_callback( unsigned long data ) +{ + /* do your timer stuff here */ + //log_info("radio_reset_timer_callback\n"); + //log_info("radio reset timer is stop = [%d]\n", time_now_secs()); + + /* increment timings_data_index */ + timings_data_index++; + if(timings_data_index >= timings_data_size) { + timings_data_index = timings_data_size-1; + } + + //log_info("timings data index = [%d]\n", timings_data_index); + + /* reset available timer not started, start it */ + mod_timer(&radio_reset_available_timer, jiffies + msecs_to_jiffies((timings_data[timings_data_index]) * 1000)); + //log_info("radio reset available timer is start = [%d]\n", time_now_secs()); + radio_reset_available_timer_is_start = 1; + radio_reset_timer_is_start = 0; +} + +static ssize_t mts_attr_store_radio_reset_backoffs(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + char *timings_data_str = NULL; + const char delimiter[] = " "; + char * pch = NULL; + unsigned int size = 0; + + /* free previous timings_data */ + if (NULL != timings_data) { + /* stop timers */ + del_timer(&radio_reset_timer); + del_timer(&radio_reset_available_timer); + timings_data_index = 0; + radio_reset_timer_is_start = 0; + radio_reset_available_timer_is_start = 0; + + //log_info("free previous timings_data\n"); + kfree(timings_data); + timings_data = NULL; + timings_data_size = 0; + } + + /* make a copy */ + if( NULL == (timings_data_str = kmalloc(strlen(buf), GFP_KERNEL)) ){ + log_error("can`t allocate memory\n"); + return -EINVAL; + } + + memcpy(timings_data_str, buf, strlen(buf)); + + /* get number of tokens */ + while (NULL != (pch = strsep (&timings_data_str, delimiter))) { + int value = 0; + sscanf(pch, "%d", &value); + if (value > 0){ + size++; + if (NULL == timings_data) { + /* make alloc */ + if (NULL == (timings_data = kmalloc(sizeof(unsigned int), GFP_KERNEL))) { + log_error("can`t allocate memory\n"); + goto free; + } + } else { + /* make realloc */ + if (NULL == (timings_data = krealloc(timings_data, size * sizeof(unsigned int), GFP_KERNEL))) { + log_error("can`t allocate memory\n"); + goto free; + } + } + /* save timings data */ + sscanf(pch, "%d", &timings_data[size-1]); + } + } + + timings_data_size = size; + + if (NULL != timings_data_str) { + /* free timings_data_str */ + kfree(timings_data_str); + } + return count; + +free: + if (NULL != timings_data_str) { + /* free timings_data_str */ + kfree(timings_data_str); + } + + if (NULL != timings_data) { + kfree(timings_data); + timings_data = NULL; + timings_data_size = 0; + } + return -EINVAL; +} + +static ssize_t mts_attr_store_radio_reset_backoffs_index(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int value; + int err; + + if (sscanf(buf, "%d", &value) != 1) { + return -EINVAL; + } + + if ((value < 0) || (value >= timings_data_size)) { + log_error("incorrect data\n"); + return -EINVAL; + } + + /* stop timers */ + del_timer(&radio_reset_timer); + del_timer(&radio_reset_available_timer); + radio_reset_timer_is_start = 0; + radio_reset_available_timer_is_start = 0; + timings_data_index = value; + + return count; +} + +static ssize_t mts_attr_show_radio_reset_backoffs(struct device *dev, + struct device_attribute *attr, char *buf) +{ + int ret = 0; + size_t i = 0; + + if (NULL != timings_data) { + for(i = 0; i < timings_data_size; ++i) { + ret += sprintf(buf += strlen(buf), "%d ", timings_data[i]); + } + } + + if (ret > 0) { + ret -= 1; + } + + return ret; +} + +static ssize_t mts_attr_show_radio_reset_backoff_index(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t value; + + if (strcmp(attr->attr.name, "radio-reset-backoff-index") == 0) { + value = sprintf(buf, "%d", timings_data_index); + } + else { + log_error("attribute '%s' not found", attr->attr.name); + value = -1; + } + + return value; +} + +static ssize_t mts_attr_show_radio_reset_backoff_seconds(struct device *dev, + struct device_attribute *attr, char *buf) +{ + ssize_t value; + + if (strcmp(attr->attr.name, "radio-reset-backoff-seconds") == 0) { + if (radio_reset_timer_is_start == 1) { + value = sprintf(buf, "%d", (timings_data_stop_seconds - time_now_secs())); + } else { + value = sprintf(buf, "%d", 0); + } + } else { + log_error("attribute '%s' not found", attr->attr.name); + value = -1; + } + + return value; +} + +static DEVICE_ATTR_MTS(dev_attr_radio_reset_backoffs, "radio-reset-backoffs", + mts_attr_show_radio_reset_backoffs, mts_attr_store_radio_reset_backoffs); + +static DEVICE_ATTR_MTS(dev_attr_radio_reset_backoff_index, "radio-reset-backoff-index", + mts_attr_show_radio_reset_backoff_index, mts_attr_store_radio_reset_backoffs_index); + +static DEVICE_ATTR_RO_MTS(dev_attr_radio_reset_backoff_seconds, "radio-reset-backoff-seconds", + mts_attr_show_radio_reset_backoff_seconds); + /* shared gpio-based LEDs */ static DEVICE_ATTR_MTS(dev_attr_led_status, "led-status", mts_attr_show_gpio_pin, mts_attr_store_gpio_pin); @@ -621,11 +863,20 @@ static int __init mts_io_init(void) // start the reset handler reset_callback(NULL); + /* init timers */ + setup_timer(&radio_reset_timer, radio_reset_timer_callback, 0); + setup_timer(&radio_reset_available_timer, radio_reset_available_timer_callback, 0); + return 0; } static void __exit mts_io_exit(void) { + /* delete radio_reset_timer */ + del_timer(&radio_reset_timer); + /* delete radio_reset_available_timer */ + del_timer(&radio_reset_available_timer); + cancel_delayed_work_sync(&reset_work); cleanup(); |