diff options
author | Harsh Sharma <harsh.sharma@multitech.com> | 2022-03-24 17:05:56 -0500 |
---|---|---|
committer | John Klug <john.klug@multitech.com> | 2022-04-12 16:48:14 -0500 |
commit | 771c9199b66d0eadc7587cf61f00256df49188d2 (patch) | |
tree | 6e4ca812d27d5b35aa07eb380bd7b7e9bc46c440 | |
parent | 5e681baa71e96d1ae1de317c4ee74dfdc9b72b1c (diff) | |
download | mts-io-771c9199b66d0eadc7587cf61f00256df49188d2.tar.gz mts-io-771c9199b66d0eadc7587cf61f00256df49188d2.tar.bz2 mts-io-771c9199b66d0eadc7587cf61f00256df49188d2.zip |
Update mts-io to use gpiod for MTCAP34.9.4
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | io-module/buttons.c | 6 | ||||
-rw-r--r-- | io-module/machine/mtcap3.c | 352 | ||||
-rw-r--r-- | io-module/mts-io.c | 42 | ||||
-rw-r--r-- | io-module/mts_lora.c | 15 | ||||
-rw-r--r-- | io-module/version.h | 1 |
6 files changed, 230 insertions, 188 deletions
diff --git a/configure.ac b/configure.ac index 010f2ac..0527b8e 100644 --- a/configure.ac +++ b/configure.ac @@ -1,4 +1,4 @@ -AC_INIT([mts-io], [4.9.3]) +AC_INIT([mts-io], 'v'[m4_esyscmd_s([git describe --tags])]) AC_CONFIG_SRCDIR([util/mts_util_lora2_reset.c]) AM_INIT_AUTOMAKE AM_CONFIG_HEADER([config.h]) diff --git a/io-module/buttons.c b/io-module/buttons.c index 07ac635..2598c2e 100644 --- a/io-module/buttons.c +++ b/io-module/buttons.c @@ -177,7 +177,11 @@ static void button_worker(struct work_struct *ignored) pin = gpio_pin_by_button_name(pbutton[0]->name); if (pin) { - button_pressed = !gpio_get_value(pin->pin.gpio); + if (pin->do_gpio_desc) { + button_pressed = !gpiod_get_value(pin->desc); + } else { + button_pressed = !gpio_get_value(pin->pin.gpio); + } } if (pbutton[0]->pid > 0) { diff --git a/io-module/machine/mtcap3.c b/io-module/machine/mtcap3.c index 738244e..9a81e1c 100644 --- a/io-module/machine/mtcap3.c +++ b/io-module/machine/mtcap3.c @@ -6,80 +6,90 @@ static struct gpio_pin gpio_pins_mtcap3_0_0[] = { { .name = "ETH_RESET", .pin = { - .gpio = NXP_GPIO(1, 4), // ETH_NRST - has ext PU - .flags = GPIOF_OUT_INIT_HIGH, + .gpio = ~0U, // ETH_NRST - has ext PU + .flags = GPIOD_OUT_HIGH, .label = "eth-reset", }, - // mts-io in init leaves eth-reset asserted even though it is really - // an nrst pin, and does this across boards. This is wrong logically, - // but active_low must be 0 as legacy and for this to function properly. - .active_low = 0, + // mts-io in init leaves eth-reset asserted even though it is really + // an nrst pin, and does this across boards. This is wrong logically, + // but active_low must be 0 as legacy and for this to function properly. + .active_low = 0, + .do_gpio_desc = 1 }, { .name = "RADIO_POWER_MONITOR", .pin = { - .gpio = NXP_GPIO(4, 28), // CELL_STATUS - .flags = GPIOF_IN, + .gpio = ~0U, // CELL_STATUS + .flags = GPIOD_IN, .label = "radio-power-monitor", }, + .do_gpio_desc = 1 }, { .name = "RADIO_RESET", .pin = { - .gpio = NXP_GPIO(5, 4), // CELL_RESET - has ext PD - inverted through FET - .flags = GPIOF_OUT_INIT_LOW, + .gpio = ~0U, // CELL_RESET - has ext PD - inverted through FET + .flags = GPIOD_OUT_LOW, .label = "radio-reset", }, + .do_gpio_desc = 1 }, { .name = "RADIO_ONOFF", .pin = { - .gpio = NXP_GPIO(5, 8), // CELL_ONOFF - has ext PD - inverted through FET - .flags = GPIOF_OUT_INIT_HIGH, + .gpio = ~0U, // CELL_ONOFF - has ext PD - inverted through FET + .flags = GPIOD_OUT_HIGH, .label = "radio-onoff", }, + .do_gpio_desc = 1 }, { .name = "RADIO_POWER", .pin = { - .gpio = NXP_GPIO(5, 0), // CELL_PWR_EN - has ext PD - .flags = GPIOF_OUT_INIT_HIGH, + .gpio = ~0U, // CELL_PWR_EN - has ext PD + .flags = GPIOD_OUT_HIGH, .label = "radio-power", }, + .do_gpio_desc = 1 }, { .name = "RADIO_NETLIGHT", // LED4 is for cell network status .pin = { - .gpio = NXP_GPIO(4, 25), - .flags = GPIOF_IN, + .gpio = ~0U, + .flags = GPIOD_IN, .label = "radio-netlight", }, + .do_gpio_desc = 1 }, { .name = "DEVICE_RESET", .pin = { - .gpio = NXP_GPIO(2, 13), // SWITCH_IN - has EXT PU - .flags = GPIOF_IN, + .gpio = ~0U, // SWITCH_IN - has EXT PU + .flags = GPIOD_IN, .label = "reset", }, .active_low = 1, + .do_gpio_desc = 1 }, { .name = "LORA_RESET", .pin = { - .gpio = NXP_GPIO(1, 25), // LORA_RST - .flags = GPIOF_OUT_INIT_LOW, + .gpio = ~0U, // LORA_RST + .flags = GPIOD_OUT_LOW, .label = "lora/reset", }, + .do_gpio_desc = 1, .capability = CAPA_LORA, + .active_low = 1, }, { .name = "LORA_LBT_RESET", .pin = { - .gpio = NXP_GPIO(1, 27), // LORA_LBT_nRESET - .flags = GPIOF_OUT_INIT_LOW, + .gpio = ~0U, // LORA_LBT_nRESET + .flags = GPIOD_OUT_LOW, .label = "lora/lbt-reset", }, + .do_gpio_desc = 1, .capability = CAPA_LORA_LBT, .active_low = 1, }, @@ -87,34 +97,38 @@ static struct gpio_pin gpio_pins_mtcap3_0_0[] = { { .name = "STATUS_LED", .pin = { - .gpio = NXP_GPIO(2, 15), - .flags = GPIOF_OUT_INIT_HIGH, + .gpio = ~0U, + .flags = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, .label = "led-status", }, + .do_gpio_desc = 1 }, { .name = "LED2", .pin = { - .gpio = NXP_GPIO(3, 21), - .flags = GPIOF_OUT_INIT_LOW, + .gpio = ~0U, + .flags = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE, .label = "led-lora", }, + .do_gpio_desc = 1 }, { .name = "LED3", .pin = { - .gpio = NXP_GPIO(3, 22), - .flags = GPIOF_OUT_INIT_LOW, + .gpio = ~0U, + .flags = GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE, .label = "led-cell", }, + .do_gpio_desc = 1 }, { .name = "LED4", .pin = { - .gpio = NXP_GPIO(3, 23), - .flags = GPIOF_OUT_INIT_HIGH, + .gpio = ~0U, + .flags = GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE, .label = "led-power", }, + .do_gpio_desc = 1 }, { }, }; @@ -134,71 +148,75 @@ static struct gpio_pin gpio_pins_mtcap3_0_0[] = { /* radio control (power/reset) for mtcap3 */ static int radio_off_mtcap3(void) { - int i = 0; - int value = 0; + int i = 0; + int value = 0; struct gpio_pin *pwrmon_pin = gpio_pin_by_attr_name("radio-power-monitor"); struct gpio_pin *onoff_pin = gpio_pin_by_attr_name("radio-onoff"); struct gpio_pin *power_pin = gpio_pin_by_attr_name("radio-power"); - - if (!onoff_pin || !pwrmon_pin) { + + if (!onoff_pin || !pwrmon_pin) { return -ENODEV; } - - value = gpio_get_value(pwrmon_pin->pin.gpio); + + value = gpiod_get_value(pwrmon_pin->desc); + if(value == 0) { log_warning("cell radio is already off"); return 0; } - /* The reference manual indicates that PWRKEY is equivalent to AT+QPOWD */ + /* The reference manual indicates that PWRKEY is equivalent to AT+QPOWD */ log_info("turning off cell radio"); - // Toggle PWRKEY - logic is reversed to the module through the FET - gpio_set_value(onoff_pin->pin.gpio, 0); - msleep(EG95_PWRKEY_KEEPOUT_WAIT_MS); - gpio_set_value(onoff_pin->pin.gpio, 1); - msleep(EG95_PWRKEY_LOW_OFF_WAIT_MS); - gpio_set_value(onoff_pin->pin.gpio, 0); - - // Wait for module to indicate status - for(i=0; i<=EG95_POWER_MON_OFF_WAIT_S; i++) { - value = gpio_get_value(pwrmon_pin->pin.gpio); - - if(!value) { - break; - } - - msleep(1000); - } - - // disable power to the radio; We want to do this generally for - // battery powered MTCAP based devices. - gpio_set_value(power_pin->pin.gpio, 0); - msleep(EG95_POWER_KEEPOUT_WAIT_MS); - - if(value != 0) { - log_warning("cell radio was still on."); - } else { - log_info("cell radio has been turned off"); - } + // Toggle PWRKEY - logic is reversed to the module through the FET + gpiod_set_value(onoff_pin->desc, 0); + msleep(EG95_PWRKEY_KEEPOUT_WAIT_MS); + gpiod_set_value(onoff_pin->desc, 1); + msleep(EG95_PWRKEY_LOW_OFF_WAIT_MS); + gpiod_set_value(onoff_pin->desc, 0); + + // Wait for module to indicate status + for (i=0; i<=EG95_POWER_MON_OFF_WAIT_S; i++) { + + value = gpiod_get_value(pwrmon_pin->desc); + + if(!value) { + break; + } + + msleep(1000); + } + + // disable power to the radio; We want to do this generally for + // battery powered MTCAP based devices. + + gpiod_set_value(power_pin->desc, 0); + + msleep(EG95_POWER_KEEPOUT_WAIT_MS); + + if(value != 0) { + log_warning("cell radio was still on."); + } else { + log_info("cell radio has been turned off"); + } return 0; } static int radio_on_mtcap3(void) { - int i = 0; - int value = 0; + int i = 0; + int value = 0; struct gpio_pin *pwrmon_pin = gpio_pin_by_attr_name("radio-power-monitor"); struct gpio_pin *onoff_pin = gpio_pin_by_attr_name("radio-onoff"); struct gpio_pin *power_pin = gpio_pin_by_attr_name("radio-power"); - - if (!onoff_pin || !pwrmon_pin || !power_pin) { + + if (!onoff_pin || !pwrmon_pin || !power_pin) { return -ENODEV; } - value = gpio_get_value(pwrmon_pin->pin.gpio); + value = gpiod_get_value(pwrmon_pin->desc); if(value != 0) { log_warning("cell radio is already on"); @@ -206,46 +224,45 @@ static int radio_on_mtcap3(void) } log_info("turning on cell radio"); - - // Toggle PWRKEY - lgoic is reversed to the module through the FET - gpio_set_value(onoff_pin->pin.gpio, 0); - msleep(EG95_PWRKEY_KEEPOUT_WAIT_MS); - - // Enable power to the radio - gpio_set_value(power_pin->pin.gpio, 1); - msleep(EG95_POWER_KEEPOUT_WAIT_MS); - - gpio_set_value(onoff_pin->pin.gpio, 1); - msleep(EG95_PWRKEY_LOW_ON_WAIT_MS); - - // Wait for module to inidcate status. - // The spec says >= 10s, but MTQ code does 30; will be on the order of secs - for(i=0; i<=EG95_POWER_MON_ON_WAIT_S; i++) { - value = gpio_get_value(pwrmon_pin->pin.gpio); - - if(value) { - break; - } - - msleep(1000); - } - - // Set pwrkey high (through the FET switch); - // MTQ code does this after waiting for radio-power-monitor assert - gpio_set_value(onoff_pin->pin.gpio, 0); - - if(value == 0) { - log_warning("cell radio is still off."); - } else { - log_info("cell radio has been turned on"); - } + // Toggle PWRKEY - lgoic is reversed to the module through the FET + gpiod_set_value(onoff_pin->desc, 0); + msleep(EG95_PWRKEY_KEEPOUT_WAIT_MS); + + // Enable power to the radio + gpiod_set_value(power_pin->desc, 1); + msleep(EG95_POWER_KEEPOUT_WAIT_MS); + + gpiod_set_value(onoff_pin->desc, 1); + msleep(EG95_PWRKEY_LOW_ON_WAIT_MS); + + // Wait for module to inidcate status. + // The spec says >= 10s, but MTQ code does 30; will be on the order of secs + for(i=0; i<=EG95_POWER_MON_ON_WAIT_S; i++) { + + value = gpiod_get_value(pwrmon_pin->desc); + + if(value) { + break; + } + + msleep(1000); + } + + // Set pwrkey high (through the FET switch); + // MTQ code does this after waiting for radio-power-monitor assert + gpiod_set_value(onoff_pin->desc, 0); + + if(value == 0) { + log_warning("cell radio is still off."); + } else { + log_info("cell radio has been turned on"); + } return 0; } static ssize_t mts_attr_store_radio_power_mtcap3(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ + struct device_attribute *attr, const char *buf, size_t count) { int value; int err; @@ -253,14 +270,14 @@ static ssize_t mts_attr_store_radio_power_mtcap3(struct device *dev, return -EINVAL; } - mutex_lock(&mts_io_mutex); + mutex_lock(&mts_io_mutex); if (value == 0) { err = radio_off_mtcap3(); } else if (value == 1) { err = radio_on_mtcap3(); } else { - err = -EINVAL; - } + err = -EINVAL; + } mutex_unlock(&mts_io_mutex); if (err) { @@ -272,36 +289,34 @@ static ssize_t mts_attr_store_radio_power_mtcap3(struct device *dev, // *_radio_enable_* is here for backward compatibility static ssize_t mts_attr_store_radio_enable_mtcap3(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - // Included for legacy compatibilty. The 'radio-power' object controls - // power to the module, rather than 'enable.' The radio is always - // enabled, and 'radio-power' mirrors the state of the regulator enable, - // alleviating confusion, as well as ensuring clean shut down which - // reduce module failures. + struct device_attribute *attr, const char *buf, size_t count) { + // Included for legacy compatibilty. The 'radio-power' object controls + // power to the module, rather than 'enable.' The radio is always + // enabled, and 'radio-power' mirrors the state of the regulator enable, + // alleviating confusion, as well as ensuring clean shut down which + // reduce module failures. return count; } static ssize_t mts_attr_show_radio_enable_mtcap3(struct device *dev, struct device_attribute *attr, - char *buf) -{ + char *buf) { return sprintf(buf, "%d\n", 1); } static ssize_t mts_attr_store_radio_reset_mtcap3(struct device *dev, - struct device_attribute *attr, const char *buf, size_t count) -{ - int i; + struct device_attribute *attr, const char *buf, size_t count) { + int i; int value; struct gpio_pin *pwrmon_pin = gpio_pin_by_attr_name("radio-power-monitor"); struct gpio_pin *reset_pin = gpio_pin_by_attr_name("radio-reset"); - - if ( !pwrmon_pin || !reset_pin) { + + if ( !pwrmon_pin || !reset_pin) { return -ENODEV; } - value = gpio_get_value(pwrmon_pin->pin.gpio); + value = gpiod_get_value(pwrmon_pin->desc); + if (sscanf(buf, "%i", &value) != 1) { return -EINVAL; } @@ -310,64 +325,62 @@ static ssize_t mts_attr_store_radio_reset_mtcap3(struct device *dev, return -EINVAL; } - reset_radio_udev_discovery(); - mutex_lock(&mts_io_mutex); + reset_radio_udev_discovery(); + mutex_lock(&mts_io_mutex); + + radio_off_mtcap3(); + radio_on_mtcap3(); - radio_off_mtcap3(); - radio_on_mtcap3(); + value = gpiod_get_value(pwrmon_pin->desc); + + // Ensure that the module status indicates that it is up + if(!value) { + // Something has gone wrong - value = gpio_get_value(pwrmon_pin->pin.gpio); - - // Ensure that the module status indicates that it is up - if(!value) { - // Something has gone wrong - log_warning("cell radio not responding. Applying hard reset."); - // The manual advises against doing this as it is disorderly - // We do it here as a last resort. - gpio_set_value(reset_pin->pin.gpio, 1); - msleep(EG95_RESET_N_WAIT_MS); - gpio_set_value(reset_pin->pin.gpio, 0); - - for(i=0; i<=EG95_POWER_MON_ON_WAIT_S; i++) { - value = gpio_get_value(pwrmon_pin->pin.gpio); - - if(value) { - break; - } - - msleep(1000); - } - - if(value == 0) { - log_warning("Unable to reset radio."); - } else { - log_info("cell radio has been reset"); - } - } - else - { - log_info("cell radio has been reset"); - } + // The manual advises against doing this as it is disorderly + // We do it here as a last resort. + gpiod_set_value(reset_pin->desc, 1); + msleep(EG95_RESET_N_WAIT_MS); + gpiod_set_value(reset_pin->desc, 0); + + for(i=0; i<=EG95_POWER_MON_ON_WAIT_S; i++) { + + value = gpiod_get_value(pwrmon_pin->desc); + + if(value) { + break; + } + + msleep(1000); + } + + if(value == 0) { + log_warning("Unable to reset radio."); + } else { + log_info("cell radio has been reset"); + } + } else { + log_info("cell radio has been reset"); + } - mutex_unlock(&mts_io_mutex); + mutex_unlock(&mts_io_mutex); return count; } static ssize_t mts_attr_show_radio_power_mtcap3(struct device *dev, struct device_attribute *attr, - char *buf) -{ + char *buf) { int value; struct gpio_pin *pwrmon_pin = gpio_pin_by_attr_name("radio-power-monitor"); - - if ( !pwrmon_pin) { + + if ( !pwrmon_pin) { return -ENODEV; } - value = gpio_get_value(pwrmon_pin->pin.gpio); + value = gpiod_get_value(pwrmon_pin->desc); return sprintf(buf, "%d\n", value); } @@ -425,7 +438,7 @@ static struct attribute *mtcap3_0_0_platform_attributes[] = { &dev_attr_radio_reset_backoff_index.attr, &dev_attr_radio_reset_backoff_seconds.attr, - &dev_attr_radio_enable_mtcap3.attr, + &dev_attr_radio_enable_mtcap3.attr, // UDEV notification of radio discovery &dev_attr_radio_udev_discovery.attr, @@ -434,9 +447,8 @@ static struct attribute *mtcap3_0_0_platform_attributes[] = { }; static int -is_radio_power_attr_mtcap3(struct attribute *attr) -{ - return (attr == &dev_attr_radio_power_mtcap3.attr); +is_radio_power_attr_mtcap3(struct attribute *attr) { + return (attr == &dev_attr_radio_power_mtcap3.attr); } static struct attribute_group mtcap3_0_0_platform_attribute_group = { @@ -475,5 +487,5 @@ static struct attribute_group mtcap3_0_0_lora_attribute_group = { }; static struct attribute *mtcap3_0_0_lora_lbt_attributes[] = { - &dev_attr_lora_lbt_reset_mtcap3.attr, + &dev_attr_lora_lbt_reset_mtcap3.attr, }; diff --git a/io-module/mts-io.c b/io-module/mts-io.c index a6e5204..970e6b3 100644 --- a/io-module/mts-io.c +++ b/io-module/mts-io.c @@ -47,6 +47,8 @@ #include "at91gpio.h" #include "mts_io_module.h" #include "version.h" +#include "config.h" + #include "mts_io.h" #include "buttons.h" #include "mts_supercap.h" @@ -1410,7 +1412,7 @@ static int __init mts_io_init(void) int ret; - log_info("init: " DRIVER_VERSION); + log_info("init: " VERSION); /* We do a platform_driver_register to do a probe * of device tree and set the pinctrl/gpio settings. */ @@ -1475,11 +1477,25 @@ static int __init mts_io_init(void) for (pin = gpio_pins; *pin->name; pin++) { if (pin->capability == 0 || DEVICE_CAPA(id_eeprom.capa,pin->capability)) { - ret = gpio_request_one(pin->pin.gpio, pin->pin.flags, pin->pin.label); - if (ret) - dev_dbg(&mts_io_platform_device->dev, - "could not request pin %s (%d) but it could have already been requested under a different pin name", - pin->name, ret); + if (pin->do_gpio_desc) { + dev_dbg(&mts_io_platform_device->dev,"Request name:%s label: %s\n", pin->name, pin->pin.label); + pin->desc = devm_gpiod_get_optional(&mts_io_platform_device->dev, pin->name, pin->pin.flags); + if (IS_ERR(pin->desc)) { + log_warning( + "%s: Could not get gpio %s: Error: %ld\n", __func__, pin->name, PTR_ERR(pin->desc)); + } else { + if (pin->desc == NULL) + dev_dbg(&mts_io_platform_device->dev,"gpio_desc is null for name: %s, label: %s\n", + pin->name, pin->pin.label); + else + dev_dbg(&mts_io_platform_device->dev,"Found gpio %s\n", pin->name); + } + } else { + dev_dbg(&mts_io_platform_device->dev,"Request name:%s label: %s pin: %d\n", pin->name, pin->pin.label, pin->pin.gpio); + ret = gpio_request_one(pin->pin.gpio, pin->pin.flags, pin->pin.label); + if (ret) + log_warning("could not request pin %s (%d) but it could have already been requested under a different pin name\n", pin->name, ret); + } } } @@ -1521,9 +1537,15 @@ static void __exit mts_io_exit(void) /* delete radio_reset_available_timer */ del_timer(&radio_reset_available_timer); - for (pin = gpio_pins; *pin->name; pin++) - if (pin->capability == 0 || DEVICE_CAPA(id_eeprom.capa,pin->capability)) - gpio_free(pin->pin.gpio); + for (pin = gpio_pins; *pin->name; pin++) { + if (pin->capability == 0 || DEVICE_CAPA(id_eeprom.capa,pin->capability)) { + if (pin->do_gpio_desc) { + gpiod_put(pin->desc); + } else { + gpio_free(pin->pin.gpio); + } + } + } cleanup_buttons(); @@ -1549,5 +1571,5 @@ module_exit(mts_io_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_VERSION(DRIVER_VERSION); +MODULE_VERSION(VERSION); MODULE_LICENSE("GPL"); diff --git a/io-module/mts_lora.c b/io-module/mts_lora.c index 4cd65a7..d6cedb8 100644 --- a/io-module/mts_lora.c +++ b/io-module/mts_lora.c @@ -70,9 +70,11 @@ static ssize_t mts_attr_show_lora_gpio_pin(struct device *dev, } mutex_lock(&mts_io_mutex); - - value = gpio_get_value(pin->pin.gpio); - + if (pin->do_gpio_desc) { + value = gpiod_get_value(pin->desc); + } else { + value = gpio_get_value(pin->pin.gpio); + } mutex_unlock(&mts_io_mutex); if (value < 0) { @@ -112,8 +114,11 @@ static ssize_t mts_attr_store_lora_gpio_pin(struct device *dev, mutex_lock(&mts_io_mutex); - gpio_set_value(pin->pin.gpio, value); - + if (pin->do_gpio_desc) { + gpiod_set_value(pin->desc, value); + } else { + gpio_set_value(pin->pin.gpio, value); + } mutex_unlock(&mts_io_mutex); return count; diff --git a/io-module/version.h b/io-module/version.h index 5b6304b..20afba5 100644 --- a/io-module/version.h +++ b/io-module/version.h @@ -1,7 +1,6 @@ #ifndef __VERSION_H #define __VERSION_H -#define DRIVER_VERSION "v4.9.3" #define DRIVER_AUTHOR "Multitech Systems" #define DRIVER_DESC "MTS-IO Controller" #define DRIVER_NAME "mts-io" |