From 7770b505f3d29b303dc5ac48f549e8aacddf9116 Mon Sep 17 00:00:00 2001 From: Lauchmelder Date: Wed, 15 Jun 2022 18:26:46 +0200 Subject: [PATCH] driver now kinda works --- Makefile | 2 +- src/checks.c | 13 +++++---- src/interface.c | 76 ++++++++++++++++++++++++++++++++++++++++++++++++ src/interface.h | 42 +++++++++++++++++++++++++++ src/io.c | 77 +++++++++++++++---------------------------------- src/lcd.c | 22 +++++++++++++- src/lcd.h | 10 ++++--- src/proc.c | 58 +++++++++++++++++++++++++++++++++++++ 8 files changed, 235 insertions(+), 65 deletions(-) create mode 100644 src/interface.c create mode 100644 src/interface.h create mode 100644 src/proc.c diff --git a/Makefile b/Makefile index fd7cc99..ece9770 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ obj-m = lcd.o -lcd-y = src/lcd.o src/io.o src/checks.o +lcd-y = src/lcd.o src/io.o src/checks.o src/interface.o src/proc.o KVERSION = $(shell uname -r) diff --git a/src/checks.c b/src/checks.c index 3267c70..97fee1d 100644 --- a/src/checks.c +++ b/src/checks.c @@ -1,6 +1,7 @@ #include "checks.h" #include +#include #include "lcd.h" @@ -16,12 +17,14 @@ int lcd_is_valid_gpio(int pin) int lcd_assign_gpio(int pin) { - if(lcd_is_valid_gpio(pin)) - return pin; + if(!lcd_is_valid_gpio(pin)) + { + printk(KERN_WARNING "%s: GPIO pin %d is invalid. Using %d instead\n", THIS_MODULE->name, pin, sub_pin); + pin = sub_pin++; + } - - printk(KERN_WARNING "%s: GPIO pin %d is invalid. Using %d instead\n", THIS_MODULE->name, pin, sub_pin); - return sub_pin++; + // gpio_request(pin, "no label"); + return pin; } int lcd_check_duplicates(const int* used_pins) diff --git a/src/interface.c b/src/interface.c new file mode 100644 index 0000000..0dfe591 --- /dev/null +++ b/src/interface.c @@ -0,0 +1,76 @@ +#include "interface.h" +#include "lcd.h" + +#include +#include + +static void lcd_send_helper(const struct lcd_gpio_config* config, uint8_t data); + +void lcd_power_on(const struct lcd_gpio_config* config) +{ + gpio_set_value(config->power, 1); + gpio_set_value(config->rs, 0); + gpio_set_value(config->rw, 0); + gpio_set_value(config->enable, 0); + + gpio_set_value(config->power, 0); + + // 50ms power cycle + mdelay(50); + lcd_screen_init(config); +} + +void lcd_power_off(const struct lcd_gpio_config* config) +{ + gpio_set_value(config->power, 1); +} + +void lcd_send_command(const struct lcd_gpio_config* config, uint8_t command) +{ + gpio_set_value(config->rw, 0); + gpio_set_value(config->rs, 0); + + lcd_send_helper(config, command); +} + +void lcd_send_data(const struct lcd_gpio_config* config, uint8_t data) +{ + gpio_set_value(config->rw, 0); + gpio_set_value(config->rs, 1); + + lcd_send_helper(config, data); +} + +static void lcd_send_helper(const struct lcd_gpio_config* config, uint8_t data) +{ + gpio_set_value(config->enable, 1); + + gpio_set_value(config->data[3], (data >> 7) & 0x1); + gpio_set_value(config->data[2], (data >> 6) & 0x1); + gpio_set_value(config->data[1], (data >> 5) & 0x1); + gpio_set_value(config->data[0], (data >> 4) & 0x1); + + udelay(1000); + gpio_set_value(config->enable, 0); + + mdelay(10); + gpio_set_value(config->enable, 1); + + gpio_set_value(config->data[3], (data >> 3) & 0x1); + gpio_set_value(config->data[2], (data >> 2) & 0x1); + gpio_set_value(config->data[1], (data >> 1) & 0x1); + gpio_set_value(config->data[0], (data >> 0) & 0x1); + + udelay(1000); + gpio_set_value(config->enable, 0); + mdelay(10); +} + +void lcd_screen_init(const struct lcd_gpio_config* config) +{ + lcd_send_command(config, (LCD_FUNC_SET | LCD_FOUR_BIT) >> 4); + lcd_send_command(config, LCD_FUNC_SET | LCD_TWO_ROWS | LCD_FONT_5_7); + lcd_send_command(config, LCD_DISPLAY_SWITCH | LCD_DISPLAY_ON | LCD_CURSOR_ON | LCD_CURSOR_BLINK); + lcd_send_command(config, LCD_SCREEN_CLEAR); + lcd_send_command(config, LCD_INPUT_SET | LCD_LEFT_TO_RIGHT); +} \ No newline at end of file diff --git a/src/interface.h b/src/interface.h new file mode 100644 index 0000000..7327bde --- /dev/null +++ b/src/interface.h @@ -0,0 +1,42 @@ +#ifndef _LCD_INTERFACE_H +#define _LCD_INTERFACE_H + +#include + +#define LCD_SCREEN_CLEAR 0x01 +#define LCD_CURSOR_RETURN 0x02 +#define LCD_INPUT_SET 0x04 +#define LCD_DISPLAY_SWITCH 0x08 +#define LCD_SHIFT_MODE 0x10 +#define LCD_FUNC_SET 0x20 + +#define LCD_FOUR_BIT 0x00 +#define LCD_EIGHT_BIT 0x10 +#define LCD_ONE_ROW 0x00 +#define LCD_TWO_ROWS 0x08 +#define LCD_FONT_5_7 0x00 +#define LCD_FONT_5_10 0x04 + +#define LCD_DISPLAY_OFF 0x00 +#define LCD_DISPLAY_ON 0x04 +#define LCD_CURSOR_OFF 0x00 +#define LCD_CURSOR_ON 0x02 +#define LCD_CURSOR_STATIC 0x00 +#define LCD_CURSOR_BLINK 0x01 + +#define LCD_RIGHT_TO_LEFT 0x00 +#define LCD_LEFT_TO_RIGHT 0x02 +#define LCD_NO_SHIFT 0x00 +#define LCD_SHIFT 0x01 + +struct lcd_gpio_config; + +void lcd_power_on(const struct lcd_gpio_config* config); +void lcd_power_off(const struct lcd_gpio_config* config); + +void lcd_send_command(const struct lcd_gpio_config* config, uint8_t command); +void lcd_send_data(const struct lcd_gpio_config* config, uint8_t data); + +void lcd_screen_init(const struct lcd_gpio_config* config); + +#endif \ No newline at end of file diff --git a/src/io.c b/src/io.c index b363f24..62fd15a 100644 --- a/src/io.c +++ b/src/io.c @@ -4,8 +4,7 @@ #include #include #include "lcd.h" - -static bool advance_ptr(const char* base, ssize_t size, char** ptr); +#include "interface.h" loff_t lcd_llseek(struct file* filp, loff_t where, int whence) { @@ -20,58 +19,26 @@ ssize_t lcd_read(struct file* filp, char __user* buf, size_t count, loff_t* off) ssize_t lcd_write(struct file* filp, const char __user* buf, size_t count, loff_t* off) { struct lcd_dev* dev = (struct lcd_dev*)filp->private_data; - char* kern_buf = NULL; - char* cmd_start = NULL; - char* cmd_end = NULL; - ssize_t retval = 0; - struct lcd_gpio_config* config; + struct lcd_gpio_config* cfg = &dev->config; + char* data = (char*)kmalloc(count, GFP_KERNEL); + char* ptr; - if(down_interruptible(&dev->sem)) - return retval; - - kern_buf = (char*)kmalloc(count, GFP_KERNEL); - if(kern_buf == NULL) + printk(KERN_DEBUG "%s: test %d\n", THIS_MODULE->name, count); + + if(copy_from_user((void*)data, (const void*)buf, count) != 0) { - printk(KERN_ERR "%s: Failed to allocate buffer (%d bytes), aborting write.\n", THIS_MODULE->name, count); - retval = -ENOMEM; + printk(KERN_WARNING "%s: Failed to copy some bytes from user.\n", THIS_MODULE->name); goto out; } - if(copy_from_user(kern_buf, buf, count)) + for(ptr = data; *ptr != '\0' && *ptr != '\n'; ptr++) { - printk(KERN_ERR "%s: Failed to copy data from user, aborting write.\n", THIS_MODULE->name); - retval = -EFAULT; - kfree(kern_buf); - goto out; + lcd_send_data(cfg, (uint8_t)*ptr); } - cmd_start = kern_buf; - cmd_end = kern_buf; - - config = &dev->config; - - while(advance_ptr(kern_buf, count, &cmd_end)) - { - *cmd_end = '\0'; - - if(strcmp(cmd_start, "0") == 0) - gpio_set_value(config->power, 0); - else if(strcmp(cmd_start, "1") == 0) { - printk(KERN_DEBUG "%s: Setting output of gpio %d to 1", THIS_MODULE->name, config->power); - gpio_set_value(config->power, 1); - } - else - printk(KERN_WARNING "%s: Unrecognized command \"%s\"\n", THIS_MODULE->name, cmd_start); - - cmd_start = cmd_end + 1; - } - - retval = count; - kfree(kern_buf); - out: - up(&dev->sem); - return retval; + kfree(data); + return count; } long lcd_ioctl(struct file* filp, unsigned int cmd, unsigned long arg) @@ -83,8 +50,14 @@ int lcd_open(struct inode* inode, struct file* filp) { struct lcd_dev* dev; dev = container_of(inode->i_cdev, struct lcd_dev, dev); + + if(down_trylock(&dev->sem)) + return -EACCES; + filp->private_data = (void*)dev; + lcd_power_on(&dev->config); + printk(KERN_DEBUG "%s: Opened device\n", THIS_MODULE->name); return 0; } @@ -93,17 +66,13 @@ int lcd_release(struct inode* inode, struct file* filp) { struct lcd_dev* dev; dev = (struct lcd_dev*)filp->private_data; - + + lcd_power_off(&dev->config); + + up(&dev->sem); + printk(KERN_DEBUG "%s: Released device\n", THIS_MODULE->name); return 0; } -static bool advance_ptr(const char* base, ssize_t size, char** ptr) -{ - while(*ptr - base < size && **ptr != '\n') - (*ptr)++; - - return *ptr - base < size; -} - #endif \ No newline at end of file diff --git a/src/lcd.c b/src/lcd.c index 38ad89b..c60eb3e 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -3,6 +3,7 @@ #include #include #include +#include #include "lcd.h" #include "checks.h" @@ -57,6 +58,19 @@ static struct file_operations lcd_fops = .release = lcd_release }; +extern int lcd_proc_open (struct inode*, struct file*); +extern ssize_t lcd_proc_read (struct file*, char __user*, size_t, loff_t*); +extern loff_t lcd_proc_lseek (struct file*, loff_t, int); +extern int lcd_proc_release (struct inode*, struct file*); + +static struct proc_ops lcd_pops = +{ + .proc_open = lcd_proc_open, + .proc_read = lcd_proc_read, + .proc_lseek = lcd_proc_lseek, + .proc_release = lcd_proc_release +}; + static int __init lcd_init_module(void) { int result = 0; @@ -91,12 +105,17 @@ static int __init lcd_init_module(void) return result; } + gpio_set_value(lcddev.config.power, 1); + + lcddev.proc_dir = proc_create_data("lcd", 0, NULL, &lcd_pops, (void*)&lcddev); + printk(KERN_INFO "%s: Module loaded!\n", THIS_MODULE->name); return result; } static void __exit lcd_exit_module(void) { + proc_remove(lcddev.proc_dir); lcd_exit_gpio(); lcd_exit_cdev(); unregister_chrdev_region(lcddev.devno, 1); @@ -131,8 +150,9 @@ static int __init lcd_init_gpio(void) if(result < 0) return result; - for(i = 0; i < 8; i++) + for(i = 0; i < 8; i++) { gpio_direction_output(lcddev.used_pins[i], 0); + } return result; } diff --git a/src/lcd.h b/src/lcd.h index 2467f1e..437b5d8 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -4,15 +4,16 @@ #include #include #include +#include #define LCD_MAJOR 0 #define LCD_MINOR 0 #define LCD_PIN_POWER 14 -#define LCD_PIN_REGISTER_SELECT 11 -#define LCD_PIN_READ_WRITE 9 -#define LCD_PIN_ENABLE 10 -#define LCD_PIN_DATA { 26, 19, 13, 06 } +#define LCD_PIN_REGISTER_SELECT 22 +#define LCD_PIN_READ_WRITE 27 +#define LCD_PIN_ENABLE 17 +#define LCD_PIN_DATA { 26, 19, 13, 6 } struct lcd_gpio_config { @@ -28,6 +29,7 @@ struct lcd_dev struct semaphore sem; struct lcd_gpio_config config; int* used_pins; + struct proc_dir_entry* proc_dir; }; #endif \ No newline at end of file diff --git a/src/proc.c b/src/proc.c new file mode 100644 index 0000000..bc96bdb --- /dev/null +++ b/src/proc.c @@ -0,0 +1,58 @@ +#include +#include +#include + +#include "lcd.h" + +int lcd_proc_open(struct inode* inode, struct file* filp) +{ + struct lcd_dev* dev = PDE_DATA(inode); + filp->private_data = (void*)dev; + + return 0; +} + +ssize_t lcd_proc_read(struct file* filp, char __user* buf, size_t count, off_t* offset) +{ + struct lcd_dev* dev = (struct lcd_dev*)filp->private_data; + + char* output; + size_t len = 0; + + if(*offset > 0) + return 0; + + if(count < 512) + return 0; + + output = (char*)kmalloc(512, GFP_KERNEL); + if(output == NULL) + return 0; + + len += sprintf(output + len, "Device maj %u min %u\n", MAJOR(dev->devno), MINOR(dev->devno)); + len += sprintf(output + len, "=== PIN CONFIGURATION ===\n"); + + len += sprintf(output + len, "Power : %d\n", dev->config.power); + len += sprintf(output + len, "RS : %d\n", dev->config.rs); + len += sprintf(output + len, "R/W : %d\n", dev->config.rw); + len += sprintf(output + len, "Enable: %d\n", dev->config.enable); + len += sprintf(output + len, "D4-D7 : [%d, %d, %d, %d]\n", dev->config.data[0], dev->config.data[1], dev->config.data[2], dev->config.data[3]); + + if(copy_to_user(buf, output, len) != 0) + { + printk(KERN_WARNING "%s: Failed to copy some bytes to /proc\n", THIS_MODULE->name); + } + + *offset += len; + return len; +} + +extern loff_t lcd_proc_lseek (struct file* filp, loff_t offset, int whence) +{ + return 0; +} + +int lcd_proc_release(struct inode* inode, struct file* filp) +{ + return 0; +} \ No newline at end of file