#define DRIVER_VERSION "v1.1.13" #define DRIVER_AUTHOR "Multi-Tech" #define DRIVER_DESC "MTS LoRa Accessory Card" #define DRIVER_NAME "mtac-lora" #define DEBUG 0 #include #include #include #include #include #include #include #include #include static struct gpio_pin gpio_pins_mtac_lora_0_0[] = { // gpio pins for Accessory Card 1 { .name = "AP1_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-reset", }, .do_gpio_desc = 1 }, // gpio pins for Accessory Card 2 { .name = "AP2_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-reset", }, .do_gpio_desc = 1 }, { }, }; static struct gpio_pin gpio_pins_mtac_lora_1_5[] = { // gpio pins for Accessory Card 1 { .name = "AP1_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-reset", }, .do_gpio_desc = 1 }, { .name = "AP1_GPIO1", .pin = { .gpio = ~0U, .flags = GPIOD_IN, .label = "ap1-cdone", }, .do_gpio_desc = 1 }, { .name = "AP1_GPIO2", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-creset", }, .do_gpio_desc = 1 }, // gpio pins for Accessory Card 2 { .name = "AP2_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-reset", }, .do_gpio_desc = 1 }, { .name = "AP2_GPIO1", .pin = { .gpio = ~0U, .flags = GPIOD_IN, .label = "ap2-cdone", }, .do_gpio_desc = 1 }, { .name = "AP2_GPIO2", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-creset", }, .do_gpio_desc = 1 }, { }, }; static struct gpio_pin gpio_pins_mtac_lora_2g4_0_0[] = { // gpio pins for Accessory Card 1 { .name = "AP1_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-reset", }, .do_gpio_desc = 1 }, { .name = "AP1_GPIO1", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap1-boot", }, .do_gpio_desc = 1 }, // gpio pins for Accessory Card 2 { .name = "AP2_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-reset", }, .do_gpio_desc = 1 }, { .name = "AP2_GPIO1", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap2-boot", }, .do_gpio_desc = 1 }, { }, }; static struct gpio_pin gpio_pins_mtac_lora_003_0_0[] = { // gpio pins for Accessory Card 1 { .name = "AP1_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-reset", }, .do_gpio_desc = 1 }, { .name = "AP1_GPIO4", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-creset", }, .do_gpio_desc = 1 }, { .name = "AP1_LBTRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap1-lbtreset", }, .do_gpio_desc = 1 }, // gpio pins for Accessory Card 2 { .name = "AP2_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-reset", }, .do_gpio_desc = 1 }, { .name = "AP2_GPIO4", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-creset", }, .do_gpio_desc = 1 }, { .name = "AP2_LBTRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap2-lbtreset", }, .do_gpio_desc = 1 }, { }, }; static struct gpio_pin gpio_pins_mtac_lora_003_0_1[] = { // gpio pins for Accessory Card 1 { .name = "AP1_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-reset", }, .do_gpio_desc = 1 }, { .name = "AP1_GPIO4", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap1-creset", }, .do_gpio_desc = 1 }, { .name = "AP1_TBD1", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap1-lbtreset", }, .do_gpio_desc = 1 }, // gpio pins for Accessory Card 2 { .name = "AP2_NRESET", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-reset", }, .do_gpio_desc = 1 }, { .name = "AP2_GPIO4", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_HIGH, .label = "ap2-creset", }, .do_gpio_desc = 1 }, { .name = "AP2_TBD1", .pin = { .gpio = ~0U, .flags = GPIOD_OUT_LOW, .label = "ap2-lbtreset", }, .do_gpio_desc = 1 }, { }, }; ssize_t lora_show_eui(struct kobject *kobj, struct kobj_attribute *attr, char *buf) { int retval = -1; int port; int port_index; struct mts_ap_eeprom_layout *app; port = mtac_port_from_kobject(kobj); if (port < 1) { log_error("mtac_port_from_kobject returned %d", port); return -1; } port_index = port - 1; app = (struct mts_ap_eeprom_layout *)mts_ap_eeprom[port_index]; if (! strcmp(attr->attr.name, "eui")) { retval = sprintf(buf, "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X\n", app->eui[0], app->eui[1], app->eui[2], app->eui[3], app->eui[4], app->eui[5], app->eui[6], app->eui[7]); } return retval; } static char* lora_gpio_pin_name_by_attr_name(const char* name, int port) { switch (port) { case port_1: if (! strcmp(name, "reset")) { return "ap1-reset"; } else if (!strcmp(name,"lbtreset")) { return "ap1-lbtreset"; } else if (!strcmp(name,"nreset")) { return "ap1-nreset"; } else if (!strcmp(name,"cdone")) { return "ap1-cdone"; } else if (!strcmp(name,"creset")) { return "ap1-creset"; } else if (!strcmp(name,"boot")) { return "ap1-boot"; } else { log_error("attribute name [%s] is invalid for LORA in port %d", name, port); return ""; } case port_2: if (! strcmp(name, "reset")) { return "ap2-reset"; } else if (!strcmp(name,"lbtreset")) { return "ap2-lbtreset"; } else if (!strcmp(name,"nreset")) { return "ap2-nreset"; } else if (!strcmp(name,"cdone")) { return "ap2-cdone"; } else if (!strcmp(name,"creset")) { return "ap2-creset"; } else if (!strcmp(name,"boot")) { return "ap2-boot"; } else { log_error("attribute name [%s] is invalid for LORA in port %d", name, port); return ""; } } /* NOTREACHED */ return ""; } // 1 cdone/boot (optional) // 1 creset (optional) // 1 eui // 1 reset // 1 vendor-id // 1 product-id // 1 device-id // 1 hw-version // NULL #define ATTRS_SIZE_1_5 9 #define ATTRS_SIZE_2G4_0_0 8 #define ATTRS_SIZE_0_0 7 #define ATTRS_SIZE_003_0_0 9 /*dev*/ #define ATTRS_SIZE_003_0_1 9 /*dev*/ // Set the hardware version if the ROM string matches one of the valid // hardware versions. // Lengths of strings must be the same for a match, then can compare // text. Without a length check a subset of a string could be a match. #define COMPARE_AND_ASSIGN(CANDIDATE) ((hw_version_len == (sizeof(HW_VERSION_ ## CANDIDATE)-1)) && \ (strncmp(ap_eeprom->hw_version, HW_VERSION_ ## CANDIDATE, sizeof(HW_VERSION_ ## CANDIDATE)) == 0)) \ lora_hw_version = CANDIDATE; static bool lora_setup(enum ap port) { int i; int port_index = port - 1; int index = 0; int count = 0; int ret; char buf[32]; struct kobj_attribute* attr; struct attribute **attrs; int lora_hw_version; int hw_version_len; int ap_lora_attrs_size; struct mts_ap_eeprom_layout *ap_eeprom; struct kobject *subdir; ap_eeprom = (struct mts_ap_eeprom_layout *)mts_ap_eeprom[port_index]; log_info("loading LORA accessory card in port %d hw: version %s", port, ap_eeprom->hw_version); sprintf(buf, "ap%d", port); subdir = kobject_create_and_add(buf, &mts_io_platform_device->dev.kobj); if (! subdir) { log_error("kobject_create_and_add in port %d failed", port); return false; } // create the link to the apX directory this card is in // if we're in the first slot, we get plain "lora" // if we're in a different slot, we might need to use "lora-2" to differentiate if (port > 1) { for (i = 1; i < port; i++) { if (mtac_port_info[i - 1]) { if (strstr(mtac_port_info[i - 1]->product_id, PRODUCT_ID_MTAC_LORA)) { count++; } else if (strstr(mtac_port_info[i - 1]->product_id, PRODUCT_ID_MTAC_LORA_003)) { count++; } } } } if (count > 0) { sprintf(buf, "lora-%d", count + 1); } else { sprintf(buf, "lora"); } log_info("sysfs_create_link called"); ret = sysfs_create_link(subdir->parent, subdir, buf); if (ret) { log_error("failed to link [%s] to [%s], %d", buf, subdir->name, ret); } else log_debug("created link [%s] to [%s], success:%d", buf, subdir->name, ret); // hw_version string length, null character not counted. hw_version_len = strnlen(ap_eeprom->hw_version,sizeof ap_eeprom->hw_version); /* Set LoRa hardware version. */ if COMPARE_AND_ASSIGN(MTAC_LORA_0_0) else if COMPARE_AND_ASSIGN(MTAC_LORA_1_0) else if COMPARE_AND_ASSIGN(MTAC_LORA_1_5) else if COMPARE_AND_ASSIGN(MTAC_LORA_2G4_0_0) else if COMPARE_AND_ASSIGN(MTAC_LORA_003_0_0) else if COMPARE_AND_ASSIGN(MTAC_LORA_003_0_1) else { log_error("Unknown hw-version in port %d", port); return false; } if ((lora_hw_version == MTAC_LORA_0_0) || (lora_hw_version == MTAC_LORA_1_0)) { ap_lora_attrs_size = ATTRS_SIZE_0_0; mtac_set_port_pins(port_index,gpio_pins_mtac_lora_0_0,subdir); } else if ((lora_hw_version == MTAC_LORA_2G4_0_0) || (lora_hw_version == MTAC_LORA_2G4_0_0)) { ap_lora_attrs_size = ATTRS_SIZE_2G4_0_0; mtac_set_port_pins(port_index,gpio_pins_mtac_lora_2g4_0_0,subdir); } else if (lora_hw_version == MTAC_LORA_003_0_0) { ap_lora_attrs_size = ATTRS_SIZE_003_0_0; mtac_set_port_pins(port_index,gpio_pins_mtac_lora_003_0_0,subdir); } else if (lora_hw_version == MTAC_LORA_003_0_1) { ap_lora_attrs_size = ATTRS_SIZE_003_0_1; mtac_set_port_pins(port_index,gpio_pins_mtac_lora_003_0_1,subdir); } else { ap_lora_attrs_size = ATTRS_SIZE_1_5; mtac_set_port_pins(port_index,gpio_pins_mtac_lora_1_5,subdir); } attrs = kzalloc(sizeof(struct attribute*) * ap_lora_attrs_size, GFP_KERNEL); if (! attrs) { log_error("failed to allocate attribute space for port %d", port); return false; } log_debug("Allocated %d attrs @ %p",ap_lora_attrs_size,attrs); sprintf(buf, "reset"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RW); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); kfree(attrs); return false; } attr->show = mtac_attr_show_ap_gpio_pin; attr->store = mtac_attr_store_ap_gpio_pin; attrs[index++] = &attr->attr; if (lora_hw_version == MTAC_LORA_1_5) { //GPIO1 - cdone on FPGA - input to CPU sprintf(buf, "cdone"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RO); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = mtac_attr_show_ap_gpio_pin; attrs[index++] = &attr->attr; //GPIO2 reset on FPGA - output from CPU sprintf(buf, "creset"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RW); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = mtac_attr_show_ap_gpio_pin; attr->store = mtac_attr_store_ap_gpio_pin; attrs[index++] = &attr->attr; if(index >= ap_lora_attrs_size) { panic("Internal error, too many attributes on the LORA card index %d >= %d", index,ap_lora_attrs_size); } } if (lora_hw_version == MTAC_LORA_2G4_0_0) { //GPIO1 - boot on MCU controls reset into bootloader mode sprintf(buf, "boot"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RW); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = mtac_attr_show_ap_gpio_pin; attr->store = mtac_attr_store_ap_gpio_pin; attrs[index++] = &attr->attr; if(index >= ap_lora_attrs_size) { panic("Internal error, too many attributes on the LORA card index %d >= %d", index,ap_lora_attrs_size); } } if (lora_hw_version == MTAC_LORA_003_0_0 || lora_hw_version == MTAC_LORA_003_0_1) { log_debug("assigning lbtreset"); //GPIO1 - boot on MCU controls reset into bootloader mode sprintf(buf, "lbtreset"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RW); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = mtac_attr_show_ap_gpio_pin; attr->store = mtac_attr_store_ap_gpio_pin; attrs[index++] = &attr->attr; if(index >= ap_lora_attrs_size) { panic("Internal error, too many attributes on the LORA card index %d >= %d", index,ap_lora_attrs_size); } log_debug("assigning creset"); //NRESET - Resets both the 1303 and 1261 sprintf(buf, "creset"); attr = mtac_create_attribute(buf, MTS_ATTR_MODE_RW); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = mtac_attr_show_ap_gpio_pin; attr->store = mtac_attr_store_ap_gpio_pin; attrs[index++] = &attr->attr; if(index >= ap_lora_attrs_size) { panic("Internal error, too many attributes on the LORA card index %d >= %d", index,ap_lora_attrs_size); } } sprintf(buf,"eui"); attr = mtac_create_attribute("eui", MTS_ATTR_MODE_RO); if (! attr) { log_error("failed to create attribute [%s] for LORA in port %d", buf, port); attrs[index] = NULL; mtac_port_info[port_index]->attr_group.attrs = attrs; return false; } attr->show = lora_show_eui; attrs[index++] = &attr->attr; // add attributes for eeprom contents ret = mtac_add_product_info_attributes(port, attrs, &index); log_debug("Terminate addrs at index %d",index); attrs[index] = NULL; // Terminate the array. mtac_port_info[port_index]->attr_group.attrs = attrs; // attrs available for teardown. if (!ret) { log_error("failed to add product info attributes for LORA in port %d", port); return false; } log_debug("mtac_port_info[port_index=%d]->subdirs = %p mtac_port_info[port_index=%d]->attr_group=%p", port_index,mtac_port_info[port_index]->subdirs,port_index,&mtac_port_info[port_index]->attr_group); if (sysfs_create_group(mtac_port_info[port_index]->subdirs, &mtac_port_info[port_index]->attr_group)) { log_error("sysfs_create_group failed for LORA in port %d", port); return false; } return true; } static bool lora_teardown(enum ap port) { int port_index = port - 1; struct attribute **attrs = mtac_port_info[port_index]->attr_group.attrs; struct attribute *p; int i; log_info("unloading LORA accessory card in port %d", port); if(attrs) { // clean up allocated memory for attributes for(i=0;attrs[i];i++) { p = attrs[i]; if (p->name) { log_debug("Free name %s",p->name); kfree(p->name); } log_debug("Free attribute %p",p); kfree(p); } log_debug("kfree %p",attrs); kfree(attrs); } // clean up our "apX/" kobject if it exists log_debug("free up directories"); if (mtac_port_info[port_index]->subdirs) { log_debug("Free subdirs %p",mtac_port_info[port_index]->subdirs); kobject_put(mtac_port_info[port_index]->subdirs); } mtac_clear_port_pins(port_index); return true; } void set_lora_info(struct ap_info* info) { snprintf(info->product_id, 32, "%s", PRODUCT_ID_MTAC_LORA); info->setup = &lora_setup; info->teardown = &lora_teardown; info->gpio_pin_name_by_attr_name = &lora_gpio_pin_name_by_attr_name; } void set_lora_info_003(struct ap_info* info) { snprintf(info->product_id, 32, "%s", PRODUCT_ID_MTAC_LORA_003); info->setup = &lora_setup; info->teardown = &lora_teardown; info->gpio_pin_name_by_attr_name = &lora_gpio_pin_name_by_attr_name; } /* * Loop through all the slots, and set up the * mtac-lora driver for all slots. */ static int __init mtac_lora_init(void) { int slot_count = 0; int slot_count_lora = 0; int slot_count_003 = 0; log_debug("mtac_find for LORA called"); slot_count_lora = mtac_find(set_lora_info,PRODUCT_ID_MTAC_LORA); log_debug("mtac_find for MTAC-003 called"); slot_count_003 = mtac_find(set_lora_info_003,PRODUCT_ID_MTAC_LORA_003); slot_count = slot_count_lora + slot_count_003; if (slot_count < 1) { log_debug("No MTAC LORA found"); if (slot_count < 0) return slot_count; else return -ENXIO; } return 0; } /* We can only tear down our own device */ static void __exit mtac_lora_exit(void) { mtac_free(PRODUCT_ID_MTAC_LORA,lora_setup,"lora"); mtac_free(PRODUCT_ID_MTAC_LORA_003,lora_setup,"lora"); log_info("exiting"); } module_init(mtac_lora_init); module_exit(mtac_lora_exit); MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_VERSION(DRIVER_VERSION); MODULE_LICENSE("GPL");