diff --git a/Kernel/Makefile b/Kernel/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..bf36e7f57193878db21d084a61da47abe669c9a5 --- /dev/null +++ b/Kernel/Makefile @@ -0,0 +1,7 @@ +obj-m := kernel_M.o +KVERSION = $(shell uname -r) +all: + make -C /lib/modules/$(KVERSION)/build M=$(PWD) modules + +clean: + make -C /lib/modules/$(KVERSION)/build M=$(PWD) clean \ No newline at end of file diff --git a/Kernel/kernel_M.c b/Kernel/kernel_M.c new file mode 100644 index 0000000000000000000000000000000000000000..484766e8501e7694a9fb9df849fac08d45a2e008 --- /dev/null +++ b/Kernel/kernel_M.c @@ -0,0 +1,277 @@ +#include <linux/init.h> +#include <linux/module.h> + +#include <linux/kernel.h> +#include <linux/fs.h> // For file_operations +#include <linux/uaccess.h> // For copy_from_user +#include <linux/cdev.h> // For cdev +#include <linux/device.h> // For device_create, class_create +#include <linux/slab.h> // For kmalloc, kfree + +#include <linux/crc32.h> // Include CRC32 + +#include <linux/gpio.h> // GPIO support +#include <linux/delay.h> // Delay functions +#include <linux/jiffies.h> // Time measurement + +#include <linux/kthread.h> // Kernel threads +#include <linux/sched.h> // Task scheduling + +#define DEVICE_NAME "packet_receiver" +#define CLASS_NAME "packet_class" + +// cat /sys/kernel/debug/gpio +#define GPIO_DATA 17 // GPIO for data transmission +#define GPIO_CLOCK 27 // GPIO for clock control + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Me :)"); +MODULE_DESCRIPTION("A kernel module to receive packets from userspace."); +MODULE_VERSION("1.7"); + +// The size of the receive buffer +#define RECV_BUF_SIZE 512 + +// Global variables for device number, class, device. +static dev_t dev_number; +static struct class *packet_class = NULL; +static struct cdev packet_cdev; + +// Store last valid data packet +static char last_valid_packet[256] = {0}; + +// Time of last transmission +static struct task_struct *transmit_thread; + +// Flag to control loop in the thread +static int keep_sending = 0; + +// This buffer will temporarily store data from userspace +static char *recv_buffer; + +// Forward declarations for file operations +static int packet_open(struct inode *inode, struct file *file); //not used +static int packet_release(struct inode *inode, struct file *file); //not used +static ssize_t packet_read(struct file *filp, char __user *buf, size_t len, loff_t *offset); //not used +static ssize_t packet_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset); +static unsigned long calculate_crc32(const char *data, size_t len); +static int transmit_loop(void *data); // Kernel thread function + +// File operations structure +static struct file_operations fops = { + .owner = THIS_MODULE, + .open = packet_open, //not used + .read = packet_read, //not used + .write = packet_write, + .release = packet_release, //not used +}; + +// CRC32 Calculation Function +// uses the same polynomial as zlib’s standard CRC32 +static u32 calculate_crc32(const char *data, size_t len) { + return crc32(0, data, len); +} + +// ====================== Device Open ====================== +static int packet_open(struct inode *inode, struct file *file) { + printk(KERN_INFO "packet_receiver: Device opened.\n"); + return 0; +} + +// ====================== Kernel Thread for Continuous Sending ====================== +static int transmit_loop(void *data) { + while (!kthread_should_stop()) { + if (keep_sending && strlen(last_valid_packet) > 0) { + // Wait for clock signal + if (gpio_get_value(GPIO_CLOCK) == 1) { + printk(KERN_INFO "packet_receiver: Clock HIGH -> Pausing transmission.\n"); + msleep(100); // Small sleep to avoid busy looping + continue; + } + + // Transmit the last valid packet + for (size_t i = 0; i < strlen(last_valid_packet); i++) { + gpio_set_value(GPIO_DATA, 1); + msleep(1); + gpio_set_value(GPIO_DATA, 0); + msleep(1); + } + + printk(KERN_INFO "packet_receiver: Sent data to GPIO %d\n", GPIO_DATA); + + // Wait 3x the transmission time before sending again + msleep(strlen(last_valid_packet) * 3); + } else { + msleep(100); // Wait if no valid packet + } + } + return 0; +} + +// ====================== Device Read ====================== +static ssize_t packet_read(struct file *filp, char __user *buf, size_t len, loff_t *offset) { + printk(KERN_INFO "packet_receiver: Read called.\n"); + return 0; +} + +// ====================== Device Write ====================== +static ssize_t packet_write(struct file *filp, const char __user *buf, size_t len, loff_t *offset) { + // Bound the incoming size to the size of the buffer + size_t to_copy = min(len, (size_t)RECV_BUF_SIZE - 1); + + // Clear the buffer before copying data into it (for safety) + memset(recv_buffer, 0, RECV_BUF_SIZE); + + // Copy data from user space into recv_buffer + if (copy_from_user(recv_buffer, buf, to_copy) != 0) { + printk(KERN_ERR "packet_receiver: copy_from_user failed.\n"); + return -EFAULT; + } + + // Ensure null-termination + recv_buffer[to_copy] = '\0'; + + unsigned int sender_hex = 0; + int value_id_0 = -1, value_id_1 = -1; + char value_str_0[64] = {0}; + char value_str_1[64] = {0}; + unsigned long crc_val = 0; + + printk(KERN_INFO "packet_receiver: Parsed fields [matches=%d]:\n", matches); + printk(KERN_INFO " sender=0x%X\n", sender_hex); + printk(KERN_INFO " value_id_0=%d value_0=\"%s\"\n", value_id_0, value_str_0); + printk(KERN_INFO " value_id_1=%d value_1=\"%s\"\n", value_id_1, value_str_1); + printk(KERN_INFO " crc=0x%lX\n", crc_val); + + // Prepare the payload for CRC verification + char crc_buffer[256]; + snprintf(crc_buffer, sizeof(crc_buffer), "SENDER=0x%x VALUE_ID=%d VALUE=%s VALUE_ID=%d VALUE=%s", + sender_hex, value_id_0, value_str_0, value_id_1, value_str_1); + + // Calculate CRC32 for the received data excluding the CRC field + unsigned long computed_crc = calculate_crc32(crc_buffer, strlen(crc_buffer)); + printk(KERN_INFO " computed_crc=0x%lX\n", computed_crc); + + // Check CRC32 before transmission + if (computed_crc != crc_val) { + printk(KERN_ERR "packet_receiver: CRC32 mismatch! Data rejected.\n"); + return -EIO; + } + + // Store the valid packet + strcpy(last_valid_packet, crc_buffer); + keep_sending = 1; + + printk(KERN_INFO "packet_receiver: Sent data to GPIO %d\n", GPIO_DATA); + + // Return the number of bytes written + return len; +} + +// ====================== Device Release ====================== +static int packet_release(struct inode *inode, struct file *file) { + printk(KERN_INFO "packet_receiver: Device closed.\n"); + return 0; +} + +// ====================== Module Init ====================== +static int __init packet_init(void) { + int ret; + + // Allocate a device number dynamically + ret = alloc_chrdev_region(&dev_number, 0, 1, DEVICE_NAME); + if (ret < 0) { + printk(KERN_ERR "packet_receiver: Failed to allocate major/minor.\n"); + return ret; + } + printk(KERN_INFO "packet_receiver: Registered with major=%d, minor=%d\n", + MAJOR(dev_number), MINOR(dev_number)); + + // Initialize the cdev structure + cdev_init(&packet_cdev, &fops); + packet_cdev.owner = THIS_MODULE; + + // Add the cdev to the kernel + ret = cdev_add(&packet_cdev, dev_number, 1); + if (ret < 0) { + printk(KERN_ERR "packet_receiver: Unable to add cdev.\n"); + unregister_chrdev_region(dev_number, 1); + return ret; + } + + // Create a class for udev + packet_class = class_create(CLASS_NAME); + if (IS_ERR(packet_class)) { + printk(KERN_ERR "packet_receiver: Failed to create class.\n"); + cdev_del(&packet_cdev); + unregister_chrdev_region(dev_number, 1); + return PTR_ERR(packet_class); + } + + // Create a device node /dev/packet_receiver + if (IS_ERR(device_create(packet_class, NULL, dev_number, NULL, DEVICE_NAME))) { + printk(KERN_ERR "packet_receiver: Failed to create device.\n"); + class_destroy(packet_class); + cdev_del(&packet_cdev); + unregister_chrdev_region(dev_number, 1); + return -1; + } + + // Allocate memory for receive buffer + recv_buffer = kmalloc(RECV_BUF_SIZE, GFP_KERNEL); + if (!recv_buffer) { + printk(KERN_ERR "packet_receiver: Failed to allocate recv_buffer.\n"); + device_destroy(packet_class, dev_number); + class_destroy(packet_class); + cdev_del(&packet_cdev); + unregister_chrdev_region(dev_number, 1); + return -ENOMEM; + } + + gpio_request(GPIO_DATA, "packet_data"); + gpio_request(GPIO_CLOCK, "packet_clock"); + gpio_direction_output(GPIO_DATA, 0); + gpio_direction_input(GPIO_CLOCK); // Clock is an input signal + + // Start the transmission thread + transmit_thread = kthread_run(transmit_loop, NULL, "packet_transmitter"); + if (IS_ERR(transmit_thread)) { + printk(KERN_ERR "packet_receiver: Failed to create transmit_thread.\n"); + kfree(recv_buffer); + device_destroy(packet_class, dev_number); + class_destroy(packet_class); + cdev_del(&packet_cdev); + unregister_chrdev_region(dev_number, 1); + return PTR_ERR(transmit_thread); + } + + printk(KERN_INFO "packet_receiver: Module loaded.\n"); + return 0; +} + +// ====================== Module Exit ====================== +static void __exit packet_exit(void) { + + // Stop the transmission thread + kthread_stop(transmit_thread); + + // Free recv_buffer + kfree(recv_buffer); + + // Remove device node + device_destroy(packet_class, dev_number); + // Destroy class + class_destroy(packet_class); + // Delete cdev + cdev_del(&packet_cdev); + // Free device numbers + unregister_chrdev_region(dev_number, 1); + // Free GPIOs + gpio_free(GPIO_DATA); + gpio_free(GPIO_CLOCK); + + printk(KERN_INFO "packet_receiver: Module unloaded.\n"); +} + +module_init(packet_init); +module_exit(packet_exit);