311 lines
7.7 KiB
C
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);
|