Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/ClusterM/clunet-lkm.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexey 'Cluster' Avdyukhin <cluster@raspberrypi>2018-12-12 03:33:31 +0300
committerAlexey 'Cluster' Avdyukhin <cluster@raspberrypi>2018-12-12 03:33:31 +0300
commit67b622564b37786ca600b4cd0427cb32c0b704f1 (patch)
tree8eb83ee83b4a92045015e325213355439b6878fa
It's alive!
-rw-r--r--Makefile7
-rw-r--r--clunet.c246
-rw-r--r--clunet.h39
3 files changed, 292 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..5b0332e
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,7 @@
+obj-m := clunet.o
+
+all:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
+
+clean:
+ make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
diff --git a/clunet.c b/clunet.c
new file mode 100644
index 0000000..e354454
--- /dev/null
+++ b/clunet.c
@@ -0,0 +1,246 @@
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/timekeeping.h>
+#include <linux/ktime.h>
+#include <linux/hrtimer.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/string.h>
+#include <asm/div64.h>
+#include "clunet.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Cluster");
+MODULE_DESCRIPTION("CLUNET driver");
+
+static u8 receive_pin = 2;
+static u8 transmit_pin = 3;
+static u8 transmit_value = 1;
+static u8 clunet_t = 64;
+module_param(receive_pin, byte, 0);
+MODULE_PARM_DESC(receive_pin,"CLUNET receiver pin number (default 2)");
+module_param(transmit_pin, byte, 0);
+MODULE_PARM_DESC(transmit_pin,"CLUNET transmitter pin number (default 3)");
+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)");
+static unsigned int irqNumber = 0xffff;
+static struct class* clunet_class = NULL;
+//static struct device* clunet_device = NULL;
+static int clunet_bus_major = 0;
+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 check_crc(const char* data, const uint8_t size)
+{
+ u8 crc = 0;
+ u8 i, j;
+ for (i = 0; i < size; i++)
+ {
+ uint8_t inbyte = data[i];
+ for (j = 0 ; j < 8 ; j++)
+ {
+ uint8_t mix = (crc ^ inbyte) & 1;
+ crc >>= 1;
+ if (mix) crc ^= 0x8C;
+ inbyte >>= 1;
+ }
+ }
+ 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)
+{
+ 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);
+}
+
+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");
+ }
+ }
+
+ 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 int __init clunet_init(void)
+{
+ int r;
+ dev_t dev;
+ gpio_free(receive_pin);
+ r = gpio_request(receive_pin, "clunet_reciver");
+ if (r)
+ {
+ printk(KERN_ERR "CLUNET: receive_pin gpio_request error: %d\n", r);
+ clunet_free();
+ return -1;
+ }
+ gpio_direction_input(receive_pin);
+ gpio_free(transmit_pin);
+ r = gpio_request(transmit_pin, "clunet_transmitter");
+ if (r)
+ {
+ printk(KERN_ERR "CLUNET: transmit_pin gpio_request error: %d\n", r);
+ clunet_free();
+ return -1;
+ }
+ gpio_direction_output(transmit_pin, !transmit_value);
+ 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
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, // Interrupt on falling edge
+ "clunet_handler", // Used in /proc/interrupts to identify the owner
+ NULL); // The *dev_id for shared interrupt lines, NULL is okay
+ if (r)
+ {
+ printk(KERN_ERR "CLUNET: receive_pin request_irq error: %d\n", r);
+ clunet_free();
+ return -1;
+ }
+ // Register the device class
+ clunet_class = class_create(THIS_MODULE, CLASS_NAME);
+ if (IS_ERR(clunet_class))
+ {
+ 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;
+ }
+ clunet_bus_major = MAJOR(dev);
+ 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)
+ {
+ 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);
+}
+
+static void __exit clunet_exit(void)
+{
+ clunet_free();
+ printk(KERN_INFO "CLUNET: stopped\n");
+}
+
+module_init(clunet_init);
+module_exit(clunet_exit);
diff --git a/clunet.h b/clunet.h
new file mode 100644
index 0000000..1c46447
--- /dev/null
+++ b/clunet.h
@@ -0,0 +1,39 @@
+#ifndef _CLUNET_H_
+#define _CLUNET_H_
+
+#define CLASS_NAME "clunet"
+#define DEVICE_NAME_BUS "clunet_bus"
+
+#define CLUNET_READ_BUFFER_SIZE 256
+
+#define CLUNET_T clunet_t
+#define CLUNET_0_T (CLUNET_T)
+#define CLUNET_1_T (3*CLUNET_T)
+#define CLUNET_INIT_T (10*CLUNET_T)
+#define CLUNET_IDLE_TIMEOUT_T (10*CLUNET_T)
+
+#define CLUNET_SENDING_STATE_IDLE 0
+#define CLUNET_SENDING_STATE_INIT 1
+#define CLUNET_SENDING_STATE_PRIO1 2
+#define CLUNET_SENDING_STATE_PRIO2 3
+#define CLUNET_SENDING_STATE_DATA 4
+#define CLUNET_SENDING_STATE_DONE 5
+#define CLUNET_SENDING_STATE_WAITING_LINE 8
+
+#define CLUNET_READING_STATE_IDLE 0
+#define CLUNET_READING_STATE_INIT 1
+#define CLUNET_READING_STATE_PRIO1 2
+#define CLUNET_READING_STATE_PRIO2 3
+#define CLUNET_READING_STATE_DATA 4
+
+#define CLUNET_OFFSET_SRC_ADDRESS 0
+#define CLUNET_OFFSET_DST_ADDRESS 1
+#define CLUNET_OFFSET_COMMAND 2
+#define CLUNET_OFFSET_SIZE 3
+#define CLUNET_OFFSET_DATA 4
+#define CLUNET_BROADCAST_ADDRESS 255
+
+static int clunet_init(void);
+static void clunet_free(void);
+
+#endif \ No newline at end of file