diff options
author | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2018-12-14 06:57:18 +0300 |
---|---|---|
committer | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2018-12-14 06:57:18 +0300 |
commit | 6440eb3ff4725cdc9d6d04fb9e5f7753d994f64b (patch) | |
tree | eafeccb37368c5f9876826965cb5616d77aa1e18 | |
parent | 67b622564b37786ca600b4cd0427cb32c0b704f1 (diff) |
Receiving works fine
-rw-r--r-- | clunet.c | 396 | ||||
-rw-r--r-- | clunet.h | 10 |
2 files changed, 288 insertions, 118 deletions
@@ -1,6 +1,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> +#include <linux/cdev.h> #include <linux/gpio.h> #include <linux/interrupt.h> #include <linux/mutex.h> @@ -32,14 +33,20 @@ module_param(clunet_t, byte, 0); MODULE_PARM_DESC(clunet_t,"CLUNET bit 0 length (default 64us)"); static unsigned int irqNumber = 0xffff; static struct class* clunet_class = NULL; -//static struct device* clunet_device = NULL; +static struct device* clunet_device = NULL; +static struct cdev clunet_bus_cdev; static int clunet_bus_major = 0; +static int clunet_bus_number_opens = 0; +static struct file* opened_files[MAX_OPENED_FILES]; +static DECLARE_WAIT_QUEUE_HEAD(wq_data); + static u64 last_rising_time = 0; static u64 last_falling_time = 0; static u8 clunetReadingState = CLUNET_READING_STATE_IDLE; -static u8 clunetReadingCurrentByte; -static u8 clunetReadingCurrentBit; -volatile char dataToRead[CLUNET_READ_BUFFER_SIZE]; +static u8 clunetReadingCurrentByte = 0; +static u8 clunetReadingCurrentBit = 0; +static u8 dataToRead[CLUNET_READ_BUFFER_SIZE]; +static u8 recvPriority = 0; static u8 check_crc(const char* data, const uint8_t size) { @@ -59,120 +66,247 @@ static u8 check_crc(const char* data, const uint8_t size) return crc; } -static void clunet_data_received(const uint8_t src_address, const uint8_t dst_address, const uint8_t command, char* data, const uint8_t size) +static void clunet_data_received(const uint8_t prio, const uint8_t src_address, const uint8_t dst_address, const uint8_t command, char* data, const uint8_t size) { - u8 i; - char buffer[512]; - buffer[0] = 0; - if (size > 0) strcat(buffer, ":"); - for (i = 0; i < size && i < 128; i++) - sprintf(1 + buffer + i*3, " %02X", data[i]); - printk(KERN_DEBUG "CLUNET received from %02X to %02X cmd %02X size %d%s\n", src_address, dst_address, command, size, buffer); + int i; + char* buffer; + char* b; + buffer = kmalloc(2 + 3 + 3 + 3 + size * 2 + 1 + 1, GFP_ATOMIC); // 'P SR DS CM DATA\n0' + if (!buffer) + { + printk(KERN_ERR "CLUNET: can't allocatate memory"); + return; + } + sprintf(buffer, "%d %02X %02X %02X ", prio, src_address, dst_address, command); + for (i = 0; i < size; i++) + sprintf(buffer + 2 + 3 + 3 + 3 + i*2, "%02X", data[i]); + strcat(buffer, "\n"); + printk(KERN_DEBUG "CLUNET received: %s", buffer); + b = buffer; + while (*b) + { + for (i = 0; i < clunet_bus_number_opens; i++) + { + ((struct cfile_t*)opened_files[i]->private_data)->receiver_buffer[ + ((struct cfile_t*)opened_files[i]->private_data)->receiver_write_pos++ + % RECEIVER_BUFFER_SIZE] = *b; + } + b++; + }; + kfree(buffer); + wake_up_interruptible(&wq_data); } static irq_handler_t clunet_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs) { - u64 now = ktime_to_us(ktime_get_boottime()); - u8 value = gpio_get_value(receive_pin); - if (value && last_falling_time > 0) // high - { - /* Линия свободна, пробуем запланировать отправку */ - //if (clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) - // clunet_start_send(); - - uint64_t ticks = now - last_falling_time; // Вычислим время сигнала - - /* Если кто-то долго жмёт линию (время >= 6.5Т) - это инициализация */ - if (ticks >= (CLUNET_INIT_T + CLUNET_1_T) / 2) - clunetReadingState = CLUNET_READING_STATE_PRIO1; - - /* Иначе если недолго, то смотрим на этап */ - else - switch (clunetReadingState) - { - - /* Чтение данных */ - case CLUNET_READING_STATE_DATA: - - /* Если бит значащий (время > 2Т), то установим его в приемном буфере */ - if (ticks > (CLUNET_0_T + CLUNET_1_T) / 2) - dataToRead[clunetReadingCurrentByte] |= (1 << clunetReadingCurrentBit); - - /* Инкрементируем указатель бита, и при полной обработке всех 8 бит в байте выполним: */ - if (++clunetReadingCurrentBit & 8) - { - - /* Проверка на окончание чтения пакета */ - if ((++clunetReadingCurrentByte > CLUNET_OFFSET_SIZE) && (clunetReadingCurrentByte > dataToRead[CLUNET_OFFSET_SIZE] + CLUNET_OFFSET_DATA)) - { - - clunetReadingState = CLUNET_READING_STATE_IDLE; - - /* Проверяем CRC, при успехе начнем обработку принятого пакета */ - if (!check_crc((char*)dataToRead, clunetReadingCurrentByte)) - clunet_data_received ( - dataToRead[CLUNET_OFFSET_SRC_ADDRESS], - dataToRead[CLUNET_OFFSET_DST_ADDRESS], - dataToRead[CLUNET_OFFSET_COMMAND], - (char*)(dataToRead + CLUNET_OFFSET_DATA), - dataToRead[CLUNET_OFFSET_SIZE] - ); - else - printk(KERN_WARNING "CLUNET CRC error: from %02X to %02X cmd %02X size %d\n", - dataToRead[CLUNET_OFFSET_SRC_ADDRESS], - dataToRead[CLUNET_OFFSET_DST_ADDRESS], - dataToRead[CLUNET_OFFSET_COMMAND], - dataToRead[CLUNET_OFFSET_SIZE]); - } - - /* Иначе если пакет не прочитан и буфер не закончился - подготовимся к чтению следующего байта */ - else if (clunetReadingCurrentByte < CLUNET_READ_BUFFER_SIZE) - { - clunetReadingCurrentBit = 0; - dataToRead[clunetReadingCurrentByte] = 0; - } - - /* Иначе - нехватка приемного буфера -> игнорируем пакет */ - else - { - clunetReadingState = CLUNET_READING_STATE_IDLE; - printk(KERN_ERR "CLUNET out of revc buffer\n"); - } - } - break; - - /* Получение приоритета (младший бит), клиенту он не нужен */ - case CLUNET_READING_STATE_PRIO2: - clunetReadingCurrentByte = clunetReadingCurrentBit = 0; - dataToRead[0] = 0; - - /* Получение приоритета (старший бит), клиенту он не нужен */ - case CLUNET_READING_STATE_PRIO1: - clunetReadingState++; - - } - } else { - uint64_t ticks = now - last_rising_time; // Idle time - if (clunetReadingState != CLUNET_READING_STATE_IDLE - && ticks >= CLUNET_IDLE_TIMEOUT_T) // Timeout - { - clunetReadingState = CLUNET_READING_STATE_IDLE; - printk(KERN_WARNING "CLUNET recv timeout\n"); - } - } + u64 now = ktime_to_us(ktime_get_boottime()); + u8 value = gpio_get_value(receive_pin); + if (value && last_falling_time > 0) // high + { + /* Линия свободна, пробуем запланировать отправку */ + //if (clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) + // clunet_start_send(); + + uint64_t ticks = now - last_falling_time; // Вычислим время сигнала + + /* Если кто-то долго жмёт линию (время >= 6.5Т) - это инициализация */ + if (ticks >= (CLUNET_INIT_T + CLUNET_1_T) / 2) + clunetReadingState = CLUNET_READING_STATE_PRIO1; + + /* Иначе если недолго, то смотрим на этап */ + else + switch (clunetReadingState) + { + + /* Чтение данных */ + case CLUNET_READING_STATE_DATA: + + /* Если бит значащий (время > 2Т), то установим его в приемном буфере */ + if (ticks > (CLUNET_0_T + CLUNET_1_T) / 2) + dataToRead[clunetReadingCurrentByte] |= (1 << clunetReadingCurrentBit); + + /* Инкрементируем указатель бита, и при полной обработке всех 8 бит в байте выполним: */ + if (++clunetReadingCurrentBit & 8) + { + + /* Проверка на окончание чтения пакета */ + if ((++clunetReadingCurrentByte > CLUNET_OFFSET_SIZE) && (clunetReadingCurrentByte > dataToRead[CLUNET_OFFSET_SIZE] + CLUNET_OFFSET_DATA)) + { + + clunetReadingState = CLUNET_READING_STATE_IDLE; + + /* Проверяем CRC, при успехе начнем обработку принятого пакета */ + if (!check_crc((char*)dataToRead + CLUNET_OFFSET_SRC_ADDRESS, clunetReadingCurrentByte - CLUNET_OFFSET_SRC_ADDRESS)) + clunet_data_received ( + recvPriority, + dataToRead[CLUNET_OFFSET_SRC_ADDRESS], + dataToRead[CLUNET_OFFSET_DST_ADDRESS], + dataToRead[CLUNET_OFFSET_COMMAND], + (char*)(dataToRead + CLUNET_OFFSET_DATA), + dataToRead[CLUNET_OFFSET_SIZE] + ); + else + printk(KERN_WARNING "CLUNET CRC error: prio %d from %02X to %02X cmd %02X size %d\n", + recvPriority, + dataToRead[CLUNET_OFFSET_SRC_ADDRESS], + dataToRead[CLUNET_OFFSET_DST_ADDRESS], + dataToRead[CLUNET_OFFSET_COMMAND], + dataToRead[CLUNET_OFFSET_SIZE]); + } + + /* Иначе если пакет не прочитан и буфер не закончился - подготовимся к чтению следующего байта */ + else if (clunetReadingCurrentByte < CLUNET_READ_BUFFER_SIZE) + { + clunetReadingCurrentBit = 0; + dataToRead[clunetReadingCurrentByte] = 0; + } + + /* Иначе - нехватка приемного буфера -> игнорируем пакет */ + else + { + clunetReadingState = CLUNET_READING_STATE_IDLE; + printk(KERN_ERR "CLUNET out of revc buffer\n"); + } + } + break; - if (value) - last_rising_time = now; - else - last_falling_time = now; - //printk(KERN_INFO "CLUNET: IRQ! %d\n", gpio_get_value(receive_pin)); + /* Получение приоритета (младший бит), клиенту он не нужен */ + case CLUNET_READING_STATE_PRIO2: + /* Если бит значащий (время > 2Т), то установим его в приемном буфере */ + clunetReadingState++; + if (ticks > (CLUNET_0_T + CLUNET_1_T) / 2) + recvPriority |= 1; + recvPriority++; + clunetReadingCurrentByte = CLUNET_OFFSET_SRC_ADDRESS; + clunetReadingCurrentBit = 0; + dataToRead[CLUNET_OFFSET_SRC_ADDRESS] = 0; + break; + + /* Получение приоритета (старший бит), клиенту он не нужен */ + case CLUNET_READING_STATE_PRIO1: + clunetReadingState++; + /* Если бит значащий (время > 2Т), то установим его в приемном буфере */ + if (ticks > (CLUNET_0_T + CLUNET_1_T) / 2) + recvPriority = 1 << 1; + else + recvPriority = 0; + } + } else { + uint64_t ticks = now - last_rising_time; // Idle time + if (clunetReadingState != CLUNET_READING_STATE_IDLE + && ticks >= CLUNET_IDLE_TIMEOUT_T) // Timeout + { + clunetReadingState = CLUNET_READING_STATE_IDLE; + printk(KERN_WARNING "CLUNET recv timeout\n"); + } + } + + if (value) + last_rising_time = now; + else + last_falling_time = now; + //printk(KERN_INFO "CLUNET: IRQ! %d\n", gpio_get_value(receive_pin)); return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly } +/** @brief The device open function that is called each time the device is opened + * This will only increment the clunet_bus_number_opens counter in this case. + * @param inodep A pointer to an inode object (defined in linux/fs.h) + * @param filep A pointer to a file object (defined in linux/fs.h) + */ +static int clunet_dev_open(struct inode *inodep, struct file *filep) +{ + if (clunet_bus_number_opens >= MAX_OPENED_FILES) + return -EMFILE; + filep->private_data = kmalloc(sizeof(struct cfile_t), GFP_KERNEL); + if (!filep->private_data) + return -ENOMEM; + memset(filep->private_data, 0, sizeof(struct cfile_t)); + ((struct cfile_t*)filep->private_data)->id = clunet_bus_number_opens; + filep->f_pos = 0; + opened_files[clunet_bus_number_opens] = filep; + clunet_bus_number_opens++; + return 0; +} + +/** @brief This function is called whenever device is being read from user space i.e. data is + * being sent from the device to the user. In this case is uses the copy_to_user() function to + * send the buffer string to the user and captures any errors. + * @param filep A pointer to a file object (defined in linux/fs.h) + * @param buffer The pointer to the buffer to which this function writes the data + * @param len The length of the b + * @param offset The offset if required + */ +static ssize_t clunet_dev_read(struct file *filep, char *buffer, size_t len, loff_t *offset) +{ + ssize_t r; + r = 0; + while (len) + { + if (((struct cfile_t*)filep->private_data)->receiver_write_pos - (*offset) > RECEIVER_BUFFER_SIZE) + return -ENOTRECOVERABLE; + if (*offset == ((struct cfile_t*)filep->private_data)->receiver_write_pos) + { + if (r) return r; + if (filep->f_flags & O_NONBLOCK) + return -EAGAIN; + if (wait_event_interruptible(wq_data, (*offset != ((struct cfile_t*)filep->private_data)->receiver_write_pos))) + return -ERESTARTSYS; + } + if (*offset == ((struct cfile_t*)filep->private_data)->receiver_write_pos) + break; + if (put_user(((struct cfile_t*)filep->private_data)->receiver_buffer[*offset % RECEIVER_BUFFER_SIZE], buffer)) + return -EFAULT; + buffer++; + r++; + (*offset)++; + len--; + } + return r; +} + +/** @brief This function is called whenever the device is being written to from user space i.e. + * data is sent to the device from the user. The data is copied to the message[] array in this + * LKM using the sprintf() function along with the length of the string. + * @param filep A pointer to a file object + * @param buffer The buffer to that contains the string to write to the device + * @param len The length of the array of data that is being passed in the const char buffer + * @param offset The offset if required + */ +static ssize_t clunet_dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) +{ + return 0; +} + +/** @brief The device release function that is called whenever the device is closed/released by + * the userspace program + * @param inodep A pointer to an inode object (defined in linux/fs.h) + * @param filep A pointer to a file object (defined in linux/fs.h) + */ +static int clunet_dev_release(struct inode *inodep, struct file *filep) +{ + int id; + clunet_bus_number_opens--; + id = ((struct cfile_t*)filep->private_data)->id; + opened_files[id] = opened_files[clunet_bus_number_opens]; + ((struct cfile_t*)opened_files[id]->private_data)->id = id; + kfree(filep->private_data); + return 0; +} + +static struct file_operations fops = +{ + .open = clunet_dev_open, + .read = clunet_dev_read, + .write = clunet_dev_write, + .release = clunet_dev_release, +}; + static int __init clunet_init(void) { int r; - dev_t dev; + dev_t dev; + memset(opened_files, 0, sizeof(opened_files)); gpio_free(receive_pin); r = gpio_request(receive_pin, "clunet_reciver"); if (r) @@ -205,35 +339,61 @@ static int __init clunet_init(void) } // Register the device class clunet_class = class_create(THIS_MODULE, CLASS_NAME); - if (IS_ERR(clunet_class)) + if (clunet_class == NULL) { - printk(KERN_ERR "clunet: failed to register device class\n"); + printk(KERN_ERR "CLUNET: failed to register device class\n"); clunet_free(); return -1; } r = alloc_chrdev_region(&dev, 0, 1, DEVICE_NAME_BUS); - if (r < 0) { - printk(KERN_ERR "clunet: failed to allocate chrdev region\n"); - return -1; - } + if (r) + { + printk(KERN_ERR "CLUNET: failed to allocate chrdev region: %d\n", r); + clunet_free(); + return -1; + } clunet_bus_major = MAJOR(dev); + clunet_device = device_create(clunet_class, NULL, dev, NULL, DEVICE_NAME_BUS); + if (clunet_device == NULL) + { + printk(KERN_ERR "CLUNET: failed to create /dev/%s\n", DEVICE_NAME_BUS); + clunet_free(); + return -1; + } + cdev_init(&clunet_bus_cdev, &fops); + clunet_bus_cdev.owner = THIS_MODULE; + clunet_bus_cdev.ops = &fops; + r = cdev_add(&clunet_bus_cdev, dev, 1); + if (r) + { + printk(KERN_ERR "CLUNET: failed to add chrdev: %d\n", r); + clunet_free(); + return -1; + } printk(KERN_INFO "CLUNET: started, major number %d\n", clunet_bus_major); return 0; } static void clunet_free(void) { - if (irqNumber != 0xffff) - free_irq(irqNumber, NULL); - gpio_free(receive_pin); - gpio_free(transmit_pin); + if (clunet_class && clunet_bus_major) + { + cdev_del(&clunet_bus_cdev); + device_destroy(clunet_class, MKDEV(clunet_bus_major, 0)); // remove the device + } + if (clunet_bus_major) + { + unregister_chrdev_region(MKDEV(clunet_bus_major, 0), 1); + } if (clunet_class) { class_unregister(clunet_class); // unregister the device class class_destroy(clunet_class); // remove the device class } - if (clunet_bus_major) - unregister_chrdev_region(MKDEV(clunet_bus_major, 0), 1); + if (irqNumber != 0xffff) + free_irq(irqNumber, NULL); + gpio_free(receive_pin); + gpio_free(transmit_pin); } static void __exit clunet_exit(void) @@ -31,8 +31,18 @@ #define CLUNET_OFFSET_COMMAND 2 #define CLUNET_OFFSET_SIZE 3 #define CLUNET_OFFSET_DATA 4 + #define CLUNET_BROADCAST_ADDRESS 255 +#define MAX_OPENED_FILES 64 +#define RECEIVER_BUFFER_SIZE 4096 + +struct cfile_t { + int id; + char receiver_buffer[RECEIVER_BUFFER_SIZE]; + u64 receiver_write_pos; +}; + static int clunet_init(void); static void clunet_free(void); |