diff options
author | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2018-12-16 04:23:37 +0300 |
---|---|---|
committer | Alexey 'Cluster' Avdyukhin <clusterrr@clusterrr.com> | 2018-12-16 04:23:37 +0300 |
commit | 0fe00227df3ae33fd2531ee31ebd9d42e7af8b47 (patch) | |
tree | 8ab806ff7265a1018131dcc6518e1bdf258427a6 | |
parent | 6440eb3ff4725cdc9d6d04fb9e5f7753d994f64b (diff) |
It works!
-rw-r--r-- | clunet.c | 330 | ||||
-rw-r--r-- | clunet.h | 18 |
2 files changed, 323 insertions, 25 deletions
@@ -23,6 +23,7 @@ static u8 receive_pin = 2; static u8 transmit_pin = 3; static u8 transmit_value = 1; static u8 clunet_t = 64; +static u8 address = 0; module_param(receive_pin, byte, 0); MODULE_PARM_DESC(receive_pin,"CLUNET receiver pin number (default 2)"); module_param(transmit_pin, byte, 0); @@ -31,15 +32,27 @@ module_param(transmit_value, byte, 0); MODULE_PARM_DESC(transmit_value,"0 = direct send logic, 1 = inverse send logic (default 1)"); module_param(clunet_t, byte, 0); MODULE_PARM_DESC(clunet_t,"CLUNET bit 0 length (default 64us)"); +module_param(address, byte, 0); +MODULE_PARM_DESC(clunet_t,"Local address (default 0)"); +static u8 clunet_sending = 0; static unsigned int irqNumber = 0xffff; static struct class* clunet_class = NULL; -static struct device* clunet_device = NULL; +static struct device* clunet_bus_device = NULL; static struct cdev clunet_bus_cdev; +static struct cdev clunet_device_cdev; static int clunet_bus_major = 0; +static int clunet_device_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 DEFINE_MUTEX(m_wait_for_line); +static struct hrtimer* data_send_timer = NULL; +static u8 clunetSendingState = CLUNET_SENDING_STATE_IDLE; +static u8 clunetSendingDataLength; +static u8 clunetSendingCurrentByte; +static u8 clunetSendingCurrentBit; +static u8 dataToSend[CLUNET_SEND_BUFFER_SIZE]; static u64 last_rising_time = 0; static u64 last_falling_time = 0; static u8 clunetReadingState = CLUNET_READING_STATE_IDLE; @@ -47,6 +60,7 @@ static u8 clunetReadingCurrentByte = 0; static u8 clunetReadingCurrentBit = 0; static u8 dataToRead[CLUNET_READ_BUFFER_SIZE]; static u8 recvPriority = 0; +static u8 sendPriority; static u8 check_crc(const char* data, const uint8_t size) { @@ -66,11 +80,26 @@ static u8 check_crc(const char* data, const uint8_t size) return crc; } +static void set_send_timer(u16 t) +{ + if (data_send_timer) + { + hrtimer_try_to_cancel(data_send_timer); + hrtimer_start(data_send_timer, ktime_set(0, t * 1000UL), HRTIMER_MODE_REL); + } +} + +static void clunet_set_line(u8 value) +{ + clunet_sending = value; + gpio_direction_output(transmit_pin, value ? transmit_value : !transmit_value); +} + 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) { - int i; + int i, p; char* buffer; - char* b; + int d_address; buffer = kmalloc(2 + 3 + 3 + 3 + size * 2 + 1 + 1, GFP_ATOMIC); // 'P SR DS CM DATA\n0' if (!buffer) { @@ -82,16 +111,23 @@ static void clunet_data_received(const uint8_t prio, const uint8_t src_address, 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) + p = 0; + while (buffer[p]) { for (i = 0; i < clunet_bus_number_opens; i++) { + d_address = ((struct cfile_t*)opened_files[i]->private_data)->device_address; + if ((d_address < 0) // bus devices + || (src_address == d_address// source address matched + && (dst_address == address // destination address is my address + || dst_address == CLUNET_BROADCAST_ADDRESS) // or broadcast address + && p >= 2 + 3 + 3) // skip 'P SR DS ' + ) ((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; + % RECEIVER_BUFFER_SIZE] = buffer[p]; } - b++; + p++; }; kfree(buffer); wake_up_interruptible(&wq_data); @@ -100,14 +136,18 @@ static void clunet_data_received(const uint8_t prio, const uint8_t src_address, 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 + u8 value = CLUNET_READING; + uint64_t ticks; + if (!value && last_falling_time > 0) // high { /* Линия свободна, пробуем запланировать отправку */ - //if (clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) - // clunet_start_send(); + if (clunetSendingState == CLUNET_SENDING_STATE_WAITING_LINE) + { + clunetSendingState = CLUNET_SENDING_STATE_INIT; + set_send_timer(CLUNET_1_T); + } - uint64_t ticks = now - last_falling_time; // Вычислим время сигнала + ticks = now - last_falling_time; // Вычислим время сигнала /* Если кто-то долго жмёт линию (время >= 6.5Т) - это инициализация */ if (ticks >= (CLUNET_INIT_T + CLUNET_1_T) / 2) @@ -132,7 +172,6 @@ static irq_handler_t clunet_irq_handler(unsigned int irq, void *dev_id, struct p /* Проверка на окончание чтения пакета */ if ((++clunetReadingCurrentByte > CLUNET_OFFSET_SIZE) && (clunetReadingCurrentByte > dataToRead[CLUNET_OFFSET_SIZE] + CLUNET_OFFSET_DATA)) { - clunetReadingState = CLUNET_READING_STATE_IDLE; /* Проверяем CRC, при успехе начнем обработку принятого пакета */ @@ -192,7 +231,7 @@ static irq_handler_t clunet_irq_handler(unsigned int irq, void *dev_id, struct p recvPriority = 0; } } else { - uint64_t ticks = now - last_rising_time; // Idle time + ticks = now - last_rising_time; // Idle time if (clunetReadingState != CLUNET_READING_STATE_IDLE && ticks >= CLUNET_IDLE_TIMEOUT_T) // Timeout { @@ -201,21 +240,128 @@ static irq_handler_t clunet_irq_handler(unsigned int irq, void *dev_id, struct p } } - if (value) + 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 } +/* Таймер */ +static enum hrtimer_restart send_timer_callback(struct hrtimer *timer) +{ + /* Если достигли фазы завершения передачи, то завершим ее и освободим передатчик */ + if (clunetSendingState == CLUNET_SENDING_STATE_DONE) + { + clunetSendingState = CLUNET_SENDING_STATE_IDLE; // Указываем, что передатчик свободен + clunet_set_line(0); // Отпускаем линию + wake_up_interruptible(&wq_data); // Wake up, i'm ready for new data! + } + /* Иначе если передачу необходимо продолжить, то сначала проверим на конфликт */ + else if (!clunet_sending && CLUNET_READING) + { + clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; // Переходим в режим ожидания линии + } + /* Все в порядке, можем продолжать */ + else + { + clunet_set_line(!clunet_sending); // Инвертируем значение сигнала + + /* Если отпустили линию, то запланируем время паузы перед следующей передачей длительностью 1Т */ + if (!clunet_sending) + { + set_send_timer(CLUNET_T); + } + /* Если прижали линию к земле, то запланируем время передачи сигнала в зависимости от текущей фазы передачи */ + /* Фаза передачи данных */ + else if (clunetSendingState == CLUNET_SENDING_STATE_DATA) + { + /* Планируем следующее прерывание в зависимости от значения бита */ + set_send_timer((dataToSend[clunetSendingCurrentByte] & (1 << clunetSendingCurrentBit)) ? CLUNET_1_T : CLUNET_0_T); + /* Если передан байт данных */ + if (++clunetSendingCurrentBit & 8) + { + /* Если не все данные отосланы */ + if (++clunetSendingCurrentByte < clunetSendingDataLength) + clunetSendingCurrentBit = 0; // начинаем передачу следующего байта с бита 0 + /* Иначе передача всех данных закончена */ + else + clunetSendingState++; // переходим к следующей фазе завершения передачи пакета + } + } + else + switch (clunetSendingState++) + { + /* Фаза инициализации передачи пакета (время 10Т) */ + case CLUNET_SENDING_STATE_INIT: + set_send_timer(CLUNET_INIT_T); + break; + /* Фаза передачи приоритета (старший бит) */ + case CLUNET_SENDING_STATE_PRIO1: + set_send_timer((sendPriority > 2) ? CLUNET_1_T : CLUNET_0_T); + break; + /* Фаза передачи приоритета (младший бит) */ + case CLUNET_SENDING_STATE_PRIO2: + set_send_timer((sendPriority & 1) ? CLUNET_0_T : CLUNET_1_T); + clunetSendingCurrentByte = clunetSendingCurrentBit = 0; // Готовим счётчики передачи данных + break; + } + } + + return HRTIMER_NORESTART; +} + +void clunet_send(const uint8_t src_address, const uint8_t dst_address, const uint8_t prio, const uint8_t command, const char* data, const uint8_t size) +{ + u8 i; + /* Если размер данных в пределах буфера передачи (максимально для протокола 250 байт) */ + if (size < (CLUNET_SEND_BUFFER_SIZE - CLUNET_OFFSET_DATA)) + { + /* Прерываем текущую передачу, если есть такая */ + if (clunetSendingState) + { + if (data_send_timer) + hrtimer_try_to_cancel(data_send_timer); + clunet_set_line(0); + } + + /* Заполняем переменные */ + sendPriority = (prio > 4) ? 4 : prio ? : 1; // Ограничим приоритет диапазоном (1 ; 4) + dataToSend[CLUNET_OFFSET_SRC_ADDRESS] = src_address; + dataToSend[CLUNET_OFFSET_DST_ADDRESS] = dst_address; + dataToSend[CLUNET_OFFSET_COMMAND] = command; + dataToSend[CLUNET_OFFSET_SIZE] = size; + + /* Копируем данные в буфер */ + for (i = 0; i < size; i++) + dataToSend[CLUNET_OFFSET_DATA + i] = data[i]; + + /* Добавляем контрольную сумму */ + dataToSend[CLUNET_OFFSET_DATA + size] = check_crc((char*)dataToSend, CLUNET_OFFSET_DATA + size); + + clunetSendingDataLength = size + (CLUNET_OFFSET_DATA + 1); + + // Если линия свободна, то запланируем передачу сразу + if (!CLUNET_READING) + { + clunetSendingState = CLUNET_SENDING_STATE_INIT; + set_send_timer(CLUNET_1_T); + } + // Иначе будем ожидать когда освободится в процедуре внешнего прерывания + else + { + clunetSendingState = CLUNET_SENDING_STATE_WAITING_LINE; + } + } +} + /** @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) { + int d_address = -1; if (clunet_bus_number_opens >= MAX_OPENED_FILES) return -EMFILE; filep->private_data = kmalloc(sizeof(struct cfile_t), GFP_KERNEL); @@ -223,6 +369,9 @@ static int clunet_dev_open(struct inode *inodep, struct file *filep) return -ENOMEM; memset(filep->private_data, 0, sizeof(struct cfile_t)); ((struct cfile_t*)filep->private_data)->id = clunet_bus_number_opens; + if (imajor(inodep) == clunet_device_major) + d_address = iminor(inodep); + ((struct cfile_t*)filep->private_data)->device_address = d_address; filep->f_pos = 0; opened_files[clunet_bus_number_opens] = filep; clunet_bus_number_opens++; @@ -256,7 +405,7 @@ static ssize_t clunet_dev_read(struct file *filep, char *buffer, size_t len, lof 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; + return -EIO; buffer++; r++; (*offset)++; @@ -275,7 +424,102 @@ static ssize_t clunet_dev_read(struct file *filep, char *buffer, size_t len, lof */ static ssize_t clunet_dev_write(struct file *filep, const char *buffer, size_t len, loff_t *offset) { - return 0; + ssize_t r; + u16 i, p, l; + u8 v; + char ch; + u8* decoded; + int d_address; + r = 0; + d_address = ((struct cfile_t*)filep->private_data)->device_address; + while (r < len) + { + if (((struct cfile_t*)filep->private_data)->transmitter_write_pos + >= TRANSMITTER_BUFFER_SIZE) + { + ((struct cfile_t*)filep->private_data)->transmitter_write_pos = 0; + return -ENOBUFS; + } + if (get_user(ch, buffer)) + return -EIO; + r++; + buffer++; + if (ch == '\n') + { + l = ((struct cfile_t*)filep->private_data)->transmitter_write_pos; + decoded = kmalloc(l / 2 + 1, GFP_KERNEL); + if (!decoded) + return -ENOMEM; + if (d_address < 0) // Bus? + { + p = 1; + decoded[0] = 0; + } else { // Device? Skip priority, source address and dst address + p = 3 * 2; + decoded[0] = 4; // Always high priority + decoded[1] = address; // Our address + decoded[2] = d_address; // Device address + } + for (i = 0; i < l; i++) + { + ch = ((struct cfile_t*)filep->private_data)->transmitter_buffer[i]; + if (ch >= '0' && ch <= '9') + v = ch - '0'; + else if (ch >= 'a' && ch <= 'f') + v = ch - 'a' + 10; + else if (ch >= 'A' && ch <= 'F') + v = ch - 'A' + 10; + else + continue; + if (p & 1) + decoded[p/2] |= v; + else + decoded[p/2] = v << 4; + p++; + } + ((struct cfile_t*)filep->private_data)->transmitter_write_pos = 0; + p >>= 1; + if (p < 4 || decoded[0] == 0 || decoded[0] > 4) // Invalid command + { + kfree(decoded); + return -EBADMSG; + } + // Only one thread can really wait for free line + if (mutex_lock_interruptible(&m_wait_for_line)) + { + kfree(decoded); + return -ERESTARTSYS; + } + // Line is busy + if (clunetSendingState != CLUNET_SENDING_STATE_IDLE) + { + if (filep->f_flags & O_NONBLOCK) + { + // Oh, we shouldn't wait for line, bye-bye + kfree(decoded); + mutex_unlock(&m_wait_for_line); + return -EBUSY; + } + // Waiting... + if (wait_event_interruptible(wq_data, clunetSendingState == CLUNET_SENDING_STATE_IDLE)) + { + kfree(decoded); + mutex_unlock(&m_wait_for_line); + return -ERESTARTSYS; + } + } + // Ok, it's time to enqueue data + clunet_send(decoded[1], decoded[2], decoded[0], decoded[3], decoded+4, p-4); + // Cleanup + kfree(decoded); + mutex_unlock(&m_wait_for_line); + break; // Stop it + } + ((struct cfile_t*)filep->private_data)->transmitter_buffer[ + ((struct cfile_t*)filep->private_data)->transmitter_write_pos++ + ] = ch; + } + return r; } /** @brief The device release function that is called whenever the device is closed/released by @@ -307,6 +551,15 @@ static int __init clunet_init(void) int r; dev_t dev; memset(opened_files, 0, sizeof(opened_files)); + data_send_timer = kmalloc(sizeof(struct hrtimer), GFP_KERNEL); + if (!data_send_timer) + { + printk(KERN_ERR "CLUNET: can't allocate memory for timer\n"); + clunet_free(); + return -1; + } + hrtimer_init(data_send_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + data_send_timer->function = send_timer_callback; gpio_free(receive_pin); r = gpio_request(receive_pin, "clunet_reciver"); if (r) @@ -324,7 +577,7 @@ static int __init clunet_init(void) clunet_free(); return -1; } - gpio_direction_output(transmit_pin, !transmit_value); + clunet_set_line(0); irqNumber = gpio_to_irq(receive_pin); r = request_irq(irqNumber, // The interrupt number requested (irq_handler_t) clunet_irq_handler, // The pointer to the handler function below @@ -353,16 +606,15 @@ static int __init clunet_init(void) return -1; } clunet_bus_major = MAJOR(dev); - clunet_device = device_create(clunet_class, NULL, dev, NULL, DEVICE_NAME_BUS); - if (clunet_device == NULL) + clunet_bus_device = device_create(clunet_class, NULL, dev, NULL, DEVICE_NAME_BUS_FILE); + if (clunet_bus_device == NULL) { - printk(KERN_ERR "CLUNET: failed to create /dev/%s\n", DEVICE_NAME_BUS); + printk(KERN_ERR "CLUNET: failed to create /dev/%s\n", DEVICE_NAME_BUS_FILE); 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) { @@ -370,17 +622,45 @@ static int __init clunet_init(void) clunet_free(); return -1; } - printk(KERN_INFO "CLUNET: started, major number %d\n", clunet_bus_major); + + r = alloc_chrdev_region(&dev, 0, 256, DEVICE_NAME_CUSTOM); + if (r) + { + printk(KERN_ERR "CLUNET: failed to allocate chrdev region (device): %d\n", r); + clunet_free(); + return -1; + } + clunet_device_major = MAJOR(dev); + cdev_init(&clunet_device_cdev, &fops); + clunet_device_cdev.owner = THIS_MODULE; + r = cdev_add(&clunet_device_cdev, dev, 256); + if (r) + { + printk(KERN_ERR "CLUNET: failed to add chrdev (device): %d\n", r); + clunet_free(); + return -1; + } + printk(KERN_INFO "CLUNET: started, major numbers: %d (bus), %d (devices)\n", clunet_bus_major, clunet_device_major); return 0; } static void clunet_free(void) { + if (data_send_timer) + { + hrtimer_try_to_cancel(data_send_timer); + kfree(data_send_timer); + } 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_device_major) + { + cdev_del(&clunet_device_cdev); + unregister_chrdev_region(MKDEV(clunet_device_major, 0), 256); + } if (clunet_bus_major) { unregister_chrdev_region(MKDEV(clunet_bus_major, 0), 1); @@ -3,8 +3,13 @@ #define CLASS_NAME "clunet" #define DEVICE_NAME_BUS "clunet_bus" +#define DEVICE_NAME_BUS_FILE "clunet" +#define DEVICE_NAME_CUSTOM "clunet_device" #define CLUNET_READ_BUFFER_SIZE 256 +#define CLUNET_SEND_BUFFER_SIZE 256 + +#define CLUNET_READING !gpio_get_value(receive_pin) #define CLUNET_T clunet_t #define CLUNET_0_T (CLUNET_T) @@ -12,6 +17,15 @@ #define CLUNET_INIT_T (10*CLUNET_T) #define CLUNET_IDLE_TIMEOUT_T (10*CLUNET_T) +#define CLUNET_PRIORITY_NOTICE 1 +/* Приоритет пакета 1 - неважное уведомление, которое вообще может быть потеряно без последствий */ +#define CLUNET_PRIORITY_INFO 2 +/* Приоритет пакета 2 - какая-то информация, не очень важная */ +#define CLUNET_PRIORITY_MESSAGE 3 +/* Приоритет пакета 3 - сообщение с какой-то важной информацией */ +#define CLUNET_PRIORITY_COMMAND 4 +/* Приоритет пакета 4 - команда, на которую нужно сразу отреагировать */ + #define CLUNET_SENDING_STATE_IDLE 0 #define CLUNET_SENDING_STATE_INIT 1 #define CLUNET_SENDING_STATE_PRIO1 2 @@ -36,11 +50,15 @@ #define MAX_OPENED_FILES 64 #define RECEIVER_BUFFER_SIZE 4096 +#define TRANSMITTER_BUFFER_SIZE 512 struct cfile_t { int id; char receiver_buffer[RECEIVER_BUFFER_SIZE]; u64 receiver_write_pos; + char transmitter_buffer[TRANSMITTER_BUFFER_SIZE]; + u16 transmitter_write_pos; + int device_address; }; static int clunet_init(void); |