Files
H1502_iostate/iostate.c
2019-09-06 10:59:02 +02:00

311 lines
7.7 KiB
C

#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kthread.h>
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/fb.h>
#include <linux/interrupt.h>
#define IN 0
#define OUT 1
#ifdef TEST
#define FB_NUM 1
#else
#define FB_NUM 0
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Martijn Scheepers");
MODULE_DESCRIPTION("H1502 iostate driver.");
MODULE_VERSION("0.1");
static struct task_struct *display_update_task;
// gpio label 7x7
static char gpio_label[] = {
0x70, 0x22, 0x22, 0x20, 0x22, 0x22, 0x70, //I:
0x70, 0x8A, 0x8A, 0x88, 0x8A, 0x8A, 0x70, //O:
};
// gpio icons 7x7
static char gpio_icon[] = {
0x7C, 0x82, 0x82, 0x82, 0x82, 0x82, 0x7C, //open
0x7C, 0xFE, 0xFE, 0xFE, 0xFE, 0xFE, 0x7C //closed
};
//pin numbering gpio
#ifdef TEST
//gpio on raspberry header
static int gpio_input[] = {
2, 3, 4, 2, 3, 4, 2, 3
};
static int gpio_output[] = {
4, 3, 2, 4, 3, 2, 4, 3
};
#else
//gpio h1502
static int gpio_input[] = {
22, 23, 24, 25, 26, 27, 28, 29
};
static char* gpio_input_name[] = {
"Input1","Input2","Input3","Input4","Input5","Input6","Input7","Input8"
};
static int gpio_output[] = {
77, 76, 84, 83, 78, 79, 80, 81
};
static char* gpio_output_name[] = {
"Relay1","Relay2","Relay3","Relay4","Relay5","Relay6","Relay7","Relay8"
};
#endif
static irq_handler_t io_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs);
static void fb_put_label(struct fb_info *info, unsigned int x, unsigned int y, unsigned int val){
unsigned int i, j, bits, loc;
for (i = 0; i < 7; i++) {
bits = gpio_label[(7 * val) + i];
for (j = 0; j < 7; j++) {
loc = (x + j + info->var.xoffset) * (info->var.bits_per_pixel / 8) + (y + i + info->var.yoffset) * info->fix.line_length;
if (loc < info->fix.smem_len){
if(((bits >> (7 - j)) & 1) ) {
writeb(0xFF, info->screen_base + loc);
}
else{
writeb(0x00, info->screen_base + loc);
}
}
}
}
}
static void fb_put_icon(struct fb_info *info, unsigned int x, unsigned int y, unsigned int val){
unsigned int i, j, bits, loc;
for (i = 0; i < 7; i++) {
bits = gpio_icon[(7 * val) + i];
for (j = 0; j < 7; j++) {
loc = (x + j + info->var.xoffset) * (info->var.bits_per_pixel / 8) + (y + i + info->var.yoffset) * info->fix.line_length;
if (loc < info->fix.smem_len){
if(((bits >> (7 - j)) & 1) ) {
writeb(0xFF, info->screen_base + loc);
}
else{
writeb(0x00, info->screen_base + loc);
}
}
}
}
}
static void fb_draw_gpio(struct fb_info *info, char io, unsigned int bits){
unsigned int i;
for(i = 0; i < 8; i++){
if(io == IN){
fb_put_icon(info, (i * 8) + 8, 0, bits >> i & 0x1);
}else if(io == OUT){
fb_put_icon(info, (i * 8) + 8, 25, bits >> i & 0x1);
}
}
info->fbops->fb_sync(info);
}
static void fb_update_screen(struct fb_info *info){
unsigned int gpio_state;
int i;
gpio_state = 0x00;
for(i = 0; i < 8; i++){
gpio_state = gpio_state << 1;
gpio_state = gpio_state | !gpio_get_value(gpio_input[i]);
}
fb_put_label(info, 0, 0, IN);
fb_draw_gpio(info, IN, gpio_state);
gpio_state = 0x00;
for(i = 0; i < 8; i++){
gpio_state = gpio_state << 1;
gpio_state = gpio_state | gpio_get_value(gpio_output[i]);
}
fb_put_label(info, 0, 25, OUT);
fb_draw_gpio(info, OUT, gpio_state);
}
static void fb_clear_screen(struct fb_info *info){
memset_io(info->screen_base, 0x00, info->fix.smem_len);
info->fbops->fb_sync(info);
}
static int check_fb(struct fb_info *info){
if (!info){
printk(KERN_INFO "h1502iostate: fb%d getting registered fb_info error\n", FB_NUM);
return -ENODEV;
}
if (!info->screen_base) {
printk(KERN_INFO "h1502iostate: fb%d screen_base empty\n", FB_NUM);
return -ENXIO;
}
return 0;
}
static DECLARE_WAIT_QUEUE_HEAD(wq);
static int flag = 0;
//thread with loop for refreshing the display
static int update_display_thread(void *arg){
struct fb_info *info;
int ret;
info = registered_fb[FB_NUM];
ret = check_fb(info);
if(ret < 0){
return ret;
}
while(!kthread_should_stop()){
wait_event_interruptible(wq, flag != 0);
flag = 0;
fb_update_screen(info);
}
return 0;
}
static struct fake_dev {
int foo;
}fake_input[8], fake_output[8];
// ############################################# //
// called when kernel module starts
static int __init h1502iostate_init(void){
int ret;
unsigned int irqNumber;
struct fb_info *info;
#ifndef TEST
int i;
#endif
printk(KERN_INFO "h1502iostate: starting iostate display update\n");
info = registered_fb[FB_NUM];
ret = check_fb(info);
if(ret < 0){
return ret;
}
#ifdef TEST
//raspberry pi GPIO
gpio_request(2, "sysfs");
gpio_direction_input(2);
irqNumber = gpio_to_irq(2);
ret = request_irq(irqNumber, (irq_handler_t) io_irq_handler, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "iostate_handler", &fake_input[2]);
gpio_request(3, "sysfs");
gpio_direction_input(3);
irqNumber = gpio_to_irq(3);
ret = request_irq(irqNumber, (irq_handler_t) io_irq_handler, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "iostate_handler", &fake_input[3]);
gpio_request(4, "sysfs");
gpio_direction_input(4);
irqNumber = gpio_to_irq(4);
ret = request_irq(irqNumber, (irq_handler_t) io_irq_handler, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "iostate_handler", &fake_input[4]);
#else
//h1502
for(i = 0; i < 8; i++){
gpio_request(gpio_input[i], gpio_input_name[i]);
gpio_direction_input(gpio_input[i]);
irqNumber = gpio_to_irq(gpio_input[i]);
ret = request_irq(irqNumber, (irq_handler_t) io_irq_handler, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "iostate_handler", &fake_input[i]);
if(ret < 0){
return ret;
}
}
for(i = 0; i < 8; i++){
gpio_request(gpio_output[i], gpio_output_name[i]);
gpio_direction_output(gpio_output[i], 0);
irqNumber = gpio_to_irq(gpio_output[i]);
ret = request_irq(irqNumber, (irq_handler_t) io_irq_handler, IRQF_SHARED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "iostate_handler", &fake_output[i]);
if(ret < 0){
return ret;
}
}
#endif
fb_clear_screen(info);
fb_update_screen(info);
display_update_task = kthread_run(update_display_thread, NULL, "display_update");
if(IS_ERR(display_update_task)){
printk(KERN_ALERT "h1502iostate: failed to create the update task\n");
return PTR_ERR(display_update_task);
}
return 0;
}
// called when kernel module closes
static void __exit h1502iostate_exit(void){
static unsigned int irqNumber;
struct fb_info *info;
#ifndef TEST
int i;
#endif
int ret;
info = registered_fb[FB_NUM];
ret = check_fb(info);
if(ret < 0){
printk(KERN_ALERT "h1502iostate: failed to get framebuffer\n");
}
#ifdef TEST
irqNumber = gpio_to_irq(2);
free_irq(irqNumber, &fake_input[2]);
irqNumber = gpio_to_irq(3);
free_irq(irqNumber, &fake_input[3]);
irqNumber = gpio_to_irq(4);
free_irq(irqNumber, &fake_input[4]);
#else
for(i = 0; i < 8; i++){
irqNumber = gpio_to_irq(gpio_input[i]);
free_irq(irqNumber, &fake_input[i]);
}
for(i = 0; i < 8; i++){
irqNumber = gpio_to_irq(gpio_output[i]);
free_irq(irqNumber, &fake_output[i]);
}
#endif
if(display_update_task){
flag = 1;
kthread_stop(display_update_task);
wake_up_interruptible(&wq);
}
fb_clear_screen(info);
printk(KERN_INFO "h1502iostate: Goodbye\n");
}
static irq_handler_t io_irq_handler(unsigned int irq, void *dev_id, struct pt_regs *regs){
flag = 1;
wake_up_interruptible(&wq);
return (irq_handler_t) IRQ_HANDLED; // Announce that the IRQ has been handled correctly
}
module_init(h1502iostate_init);
module_exit(h1502iostate_exit);