显示标签为“键盘”的博文。显示所有博文
显示标签为“键盘”的博文。显示所有博文

星期日, 十二月 30, 2007

我写的spi按键的驱动程序

由于工作需要,学着写了工作以来第一个真正能用的基于spi通信的按键程序,至于为什么,优缺点之类的就不是我要考虑的了,因为硬件工程师已经给设计成这样了,所以我只管写就好了

下面是arm主程序:
主要是参考了网上大家的一些方法,至于很多东西自己也不是很清楚,毕竟做linux驱动才几个月,还有就是在选择按键码的部分,后来对应得键码换了好多次,所以注释和代码不一致,这部分是为了对应单片机所以一改再改,反正最后能用最重要,键码的修改也比较简单,放在这就当个备份吧,呵呵

/********************************************************************/
/********************************************************************/
/*********Description: SPI keyboard driver ***************/
/*********Author: Kenshinxf <xuefengwang101@gmail.com> ***************/
/*********Date: 2007/11/12 **************/
/********************************************************************/
/********************************************************************/

#include <linux/kernel.h>
#include <linux/miscdevice.h>
#include <linux/sched.h>
#include <linux/poll.h>
#include <linux/spinlock.h>
#include <linux/irq.h>
#include <linux/input.h>

#include <asm/irq.h>
#include <asm/io.h>

#include <asm/hardware.h>

#include <linux/module.h>
#include <linux/config.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/iobuf.h>
#include <linux/highmem.h>
#include <linux/in.h>
#include <linux/vmalloc.h>
#include <asm/io.h>
#include <linux/errno.h>
#include <linux/tqueue.h>
#include <linux/wait.h>
#include <linux/interrupt.h>
#include <asm/irq.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/keyboard.h>

#ifdef CONFIG_DEVFS_FS
#include <linux/devfs_fs_kernel.h>
#endif
#include <linux/kbd_kern.h>

devfs_handle_t MegaKbd_devfs_dir;
//#define spi_debug

void input_register_device (struct input_dev *);

void input_unregister_device (struct input_dev *);

void input_register_handler (struct input_handler *);
void input_unregister_handler (struct input_handler *);

int input_open_device (struct input_handle *);
void input_close_device (struct input_handle *);

devfs_handle_t input_register_minor (char *name, int minor, int minor_base);
void input_unregister_minor (devfs_handle_t handle);

void input_event (struct input_dev *dev, unsigned int type, unsigned int code,
int value);

#define input_report_key(a,b,c) input_event(a, EV_KEY, b, !!(c))

#define DEVICE_NAME "MegaKbd"
#define MegaKbd_MAJOR 232

#define U8 unsigned char
#define U32 unsigned int

#define rSPCON0 (*(volatile unsigned long *)r_SPCON0) /*SPI control Register */
#define rSPSTA0 (*(volatile unsigned long *)r_SPSTA0) /*SPI status Register */
#define rSPPIN0 (*(volatile unsigned long *)r_SPPIN0) /* SPI pin control Register */
#define rSPPRE0 (*(volatile unsigned long *)r_SPPRE0) /*SPI Baud Rate Prescaler Register */
#define rSPTDAT0 (*(volatile unsigned long *)r_SPTDAT0) /*SPI Tx Data Register */
#define rSPRDAT0 (*(volatile unsigned long *)r_SPRDAT0) /*SPI Rx Data Register */

#define rGPECON (*(volatile unsigned long *)r_GPECON) /*Configure the pins of port E */
#define rGPEUP (*(volatile unsigned long *)r_GPEUP) /*Pull-up disable register for port E */
#define rGPGCON (*(volatile unsigned long *)r_GPGCON) /*Configure the pins of port G */
#define rGPGUP (*(volatile unsigned long *)r_GPGUP) /*Pull-up disable register for port G */
#define rGPGDAT (*(volatile unsigned long *)r_GPGDAT) /*The data register for port G */
unsigned long r_SPCON0, r_SPSTA0, r_SPPIN0, r_SPPRE0, r_SPTDAT0, r_SPRDAT0;
unsigned long r_GPECON, r_GPEUP;
unsigned long r_GPGCON, r_GPGUP, r_GPGDAT;

static struct input_dev *button_dev;

static void spi_poll_done (void);
void spi_tx_data (U8 data);

static int keycode = 0;
static DECLARE_WAIT_QUEUE_HEAD (key_wait);

/*address_map*/
int
address_map (void)
{
//SPI registers
r_SPCON0 = ioremap (0x59000000, 4);
r_SPSTA0 = ioremap (0x59000004, 4);
r_SPPIN0 = ioremap (0x59000008, 4);
r_SPPRE0 = ioremap (0x5900000C, 4);
r_SPTDAT0 = ioremap (0x59000010, 4);
r_SPRDAT0 = ioremap (0x59000014, 4);
//I/O registers
r_GPECON = ioremap (0x56000040, 4);
r_GPEUP = ioremap (0x56000048, 4);
r_GPGCON = ioremap (0x56000060, 4);
r_GPGUP = ioremap (0x56000068, 4);
r_GPGDAT = ioremap (0x56000064, 4);


return 0;
}

/**********************************************************/
static void spi_poll_done (void)
{
int j = 0;

while (!(rSPSTA0 & 0x01))
{
j++;
if (j >= 5000)
{
printk ("SPI state poll failed\n");
break;
}
} //endwhile
}

/**********************************************************/
void spi_tx_data (U8 data)
{
spi_poll_done ();
rSPTDAT0 = data; //transmit data
spi_poll_done ();
}

/**********************************************************/
void Init_SPI (void)
{
int i, config, value;
#ifdef spi_debug
printk ("************************\n*");
printk ("spi open program begin..\n");
printk ("now,GPE AND GPG port init...\n");
#endif

rSPPRE0 = 0xff;

#ifdef spi_debug
value = rSPPRE0;
printk ("rSPPRE0=0x%x\n", value);
#endif

for (i = 0; i < 10; i++)
{
rSPTDAT0 = 0xff;
}
rGPECON = (rGPECON & 0xf03fffff) | 0x0a800000; //GPECON-11,12,13=10
#ifdef spi_debug
value = rGPECON;
printk ("GPECON=0x%x\n", value);
#endif
rGPEUP |= 0x3800; //GPE11,12,13 pull-up disable

#ifdef spi_debug
value = rGPEUP;
printk ("GPEUP=0x%x\n", value);
#endif

rGPGCON = (rGPGCON & 0xffffffde) | 0x00000032;
#ifdef spi_debug
value = rGPGCON;
printk ("rGPGCON=0x%x\n", value);
#endif

rGPGUP |= 0x4; //GPG2 pull-up disable;
#ifdef spi_debug
value = rGPGUP;
printk ("rGPGUP=0x%x\n", value);
#endif

//select the chip
set_gpio_ctrl (GPIO_G2 | GPIO_PULLUP_EN | GPIO_MODE_OUT);
write_gpio_bit (GPIO_G2, 0);

#ifdef spi_debug
printk ("GPE AND GPG port init end!\n");
printk ("**************************\n");
#endif

}

/*********************Drive********************************/

static void megakbd_irq (int irq, void *dev_id, struct pt_regs *reg)
{
int i = 0;
int down = 0, keycode = 0;
int config;
char string;
#ifdef spi_debug
printk ("**************************\n");
printk ("\nThis is MegaKbd irq service\n");
printk ("SPI polling TX/RX Test...\n");
#endif
rSPCON0 = (1 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | (0 << 0);
#ifdef spi_debug
config = (int) rSPCON0;
printk ("rSPCON0=0x%x\n", config);
#endif
//polling,en-sck,master,low,format A,nomal
rSPPIN0 = (0 << 2) | (1 << 1) | (0 << 0);
//Multi Master error detect disable,reserved,release
#ifdef spi_debug
config = (int) rSPPIN0;
printk ("rSPPIN0=0x%x\n", config);
#endif
spi_tx_data (0xff);
#ifdef spi_debug
string = rSPTDAT0;
printk ("transmit char=0x%x\n", string);
printk ("transmit char=%c\n", string);

string = rSPRDAT0;
printk ("receive char=%c\n", string);
printk ("receive char=0x%x\n", string);
#endif

keycode = rSPRDAT0;
#ifdef spi_debug
printk ("\nkeycode is %d\n", keycode);
#endif
switch (keycode)
{
case 255:
break;
/*
case 12: //1 down
keycode = KEY_1; //2;
down = 1;
break;
case 11: //1 up
keycode = KEY_1; //2;
break;
*/
case 42: //2 down
keycode = KEY_2; //3;
down = 1;
break;
case 41: //2 up
keycode = KEY_2; //3;
break;
/*
case 72: //3 down
keycode = KEY_3; //4;
down = 1;
break;
case 71: //3 up
keycode = KEY_3; //4;
break;
*/
//key #


case 9: //4 down
keycode = KEY_ENTER; //5;
down = 1;
break;
case 8: //4 up
keycode = KEY_ENTER; //5;
break;

case 39: //5 down
keycode = KEY_9; //6;
down = 1;
break;
case 38: //5 up
keycode = KEY_9; //6;
break;

case 69: //6 down
keycode = KEY_3; //7;
down = 1;
break;
case 68: //6 up
keycode = KEY_3; //7;
break;
case 6: //7 down
keycode = KEY_0; //8;
down = 1;
break;
case 5: //7 up
keycode = KEY_0; //8;
break;
case 36: //8 down
keycode = KEY_8; //9;
down = 1;
break;
case 35: //8 up
keycode = KEY_8; //9;
break;
case 66: //9 down
keycode = KEY_2; //10;
down = 1;
break;
case 65: //9 up
keycode = KEY_2; //10;
break;
case 33: //0 down
keycode = KEY_7; //11;
down = 1;
break;
case 32: //0 up
keycode = KEY_7; //11;
break;

//key *
case 3: //asterisk down shift+8
keycode = KEY_KPASTERISK; //55;
// keycode = KEY_LEFTSHIFT;
down = 1;
// input_report_key(&button_dev, keycode, down);
// input_sync(&button_dev);
// keycode = KEY_8;
// keycode = KEY_KPASTERISK; //55;
// down = 1;
break;
case 2: //asterisk up 8+shift
keycode = KEY_KPASTERISK; //55;
// keycode = KEY_8;
// input_report_key(&button_dev, keycode, down);
// input_sync(&button_dev);
// keycode = KEY_LEFTSHIFT;
// keycode = KEY_KPASTERISK; //55;
break;

case 63: //numbersign down shift+3
keycode = KEY_1;
// keycode = KEY_LEFTSHIFT;
down = 1;
// input_report_key(&button_dev, keycode, down);
// input_sync(&button_dev);
// keycode = KEY_3;
// keycode = KEY_KPPLUS;
// down = 1;
break;
case 62: //numbersign up 3+shift
keycode = KEY_1;
// input_report_key(&button_dev, keycode, down);
// input_sync(&button_dev);
// keycode = KEY_LEFTSHIFT;
// keycode = KEY_KPPLUS;
break;

case 27: //enter down
keycode = KEY_ENTER; //28;
down = 1;
break;
case 26: //enter up
keycode = KEY_ENTER; //28;
break;
case 57: //esc down
keycode = KEY_BACKSPACE; //1;
down = 1;
break;
case 56: //esc up
keycode = KEY_BACKSPACE; //1;
break;
case 15: //backspace down
keycode = KEY_S;
down = 1;
break;
case 14: //backspace up
keycode = KEY_S;
break;
case 75: //left down
keycode = KEY_LEFT;
down = 1;
break;
case 74: //left up
keycode = KEY_LEFT;
break;
case 60: //right down
keycode = KEY_L;
down = 1;
break;
case 59: //right up
keycode = KEY_L;
break;
case 30: //up down
keycode = KEY_UP;
down = 1;
break;
case 29: //up up
keycode = KEY_UP;
break;
/*
case 45: //down down
keycode = KEY_4;
down = 1;
break;
case 44: //down up
keycode = KEY_4;
break;
*/
case 24: //f1 down
keycode = KEY_F1;
down = 1;
break;
case 23: //f1 up
keycode = KEY_F1;
break;
case 54: //f2 down
keycode = KEY_6;
down = 1;
break;
case 53: //f2 up
keycode = KEY_6;
break;
case 21: //f3 down
keycode = KEY_F3;
down = 1;
break;
case 20: //f3 up
keycode = KEY_F3;
break;
case 51: //f4 down
keycode = KEY_5;
down = 1;
break;
case 50: //f4 up
keycode = KEY_5;
break;
case 18: //f5 down
keycode = KEY_F5;
down = 1;
break;
case 17: //f5 up
keycode = KEY_F5;
break;
case 48: //f6 down
keycode = KEY_4;
down = 1;
break;
case 47: //f6 up
keycode = KEY_4;
break;

default:;
}
#ifdef spi_debug
printk("keycode=0x%x\n",keycode);
#endif
if (keycode)
{
input_report_key (&button_dev, keycode, down);
// input_sync(&button_dev);
// printk("*********************[%d]", keycode);
}

// printk ("\nkeycode=[0x%x]\n", keycode);
// printk ("\nkeycode=[%d]\n", keycode);

handle_scancode (keycode, down);
tasklet_schedule (&keyboard_tasklet);
wake_up_interruptible (&key_wait);
}

//Read for megaKbd
static ssize_t megaKbd_read (struct file *file, char *buf, size_t count, loff_t * offset)
{
static int key;
key = keycode;
copy_to_user (buf, &key, sizeof key);
return sizeof keycode;

}


//Write to megaKbd
ssize_t megaKbd_write (struct file *file, char *buf, size_t count, loff_t * offset)
{

int i = 0;
int config;
int string;
#ifdef spi_debug
printk ("SPI polling TX/RX Test...\n");
printk ("Connet SPIMOSI0 into SPIMISO\n");
#endif
rSPCON0 = (1 << 5) | (1 << 4) | (1 << 3) | (0 << 2) | (0 << 1) | (0 << 0);
// config = (int) rSPCON0;
// printk ("rSPCON0=0x%x\n", config);

//polling,en-sck,master,low,format A,nomal
rSPPIN0 = (0 << 2) | (1 << 1) | (0 << 0);

//Multi Master error detect disable,reserved,release
// config = (int) rSPPIN0;
// printk ("rSPPIN0=0x%x\n", config);
spi_tx_data (*buf);
string = rSPTDAT0;
#ifdef spi_debug
printk ("\ntransmit char=%c\n", string);
printk ("transmit char=0x%x\n", string);
#endif
string = rSPRDAT0;
#ifdef spi_debug
printk (" receive char=%c\n\n\n", string);
printk (" receive char=0x%x\n\n\n", string);
printk ("****************\n");
printk ("****************\n");
printk ("****************\n");
printk ("****************\n");
printk ("****************\n");
printk ("****************\n");
#endif
return 0;

}

/**********************************************************/
//Open the megaKbd device
static int megaKbd_open (struct inode *inode, struct file *file)
{
return 0;
}

/**********************************************************/
//Close the megaKbd device
static int megaKbd_close (struct inode *inode, struct file *file)
{
return 0;
}

/**********************************************************/
static struct file_operations megaKbd_fops = {
owner:THIS_MODULE,
llseek:no_llseek,
read:megaKbd_read,
write:megaKbd_write,
open:megaKbd_open,
release:megaKbd_close,
};

static int __init HW_kbd_init (void)
{
unsigned int result;

// set_external_irq (IRQ_EINT8, EXT_FALLING_EDGE, GPIO_PULLUP_DIS);
set_external_irq (IRQ_EINT8, EXT_RISING_EDGE, GPIO_PULLUP_DIS);
disable_irq (IRQ_EINT8);
enable_irq (IRQ_EINT8);
result = request_irq (IRQ_EINT8, &megakbd_irq, SA_INTERRUPT, DEVICE_NAME, &megakbd_irq);
if (result)
{
printk ("Can't get assigned irq %d,result=%d\n", IRQ_EINT8, result);
return result;
}
}

static int mega_kbd_translate (U8 scancode, U8 * keycode, char raw_mode)
{
int Result = 1;
printk ("my transleate\n");
*keycode = (scancode & 0x7f);
return 1;
}


static int __init megaKbd_code (void)
{

/*
button_dev = input_allocate_device();
if (!button_dev) {
printk(KERN_ERR "input: not enough memory for input device\n");
return -ENOMEM;
}
*/
// input_register_device(&button_dev);
button_dev->name = "Spi Keyboard";
// button_dev->phys = "input/input0";
button_dev->evbit[0] = BIT (EV_KEY);
// button_dev->keybit[LONG(KEY_NUMLOCK)] = BIT(KEY_NUMLOCK);
set_bit (KEY_0, button_dev->keybit);
set_bit (KEY_1, button_dev->keybit);
set_bit (KEY_2, button_dev->keybit);
set_bit (KEY_3, button_dev->keybit);
set_bit (KEY_4, button_dev->keybit);
set_bit (KEY_5, button_dev->keybit);
set_bit (KEY_6, button_dev->keybit);
set_bit (KEY_7, button_dev->keybit);
set_bit (KEY_8, button_dev->keybit);
set_bit (KEY_9, button_dev->keybit);

set_bit (KEY_F1, button_dev->keybit);
set_bit (KEY_F2, button_dev->keybit);
set_bit (KEY_F3, button_dev->keybit);
set_bit (KEY_F4, button_dev->keybit);
set_bit (KEY_F5, button_dev->keybit);
set_bit (KEY_F6, button_dev->keybit);

set_bit (KEY_ENTER, button_dev->keybit);
set_bit (KEY_ESC, button_dev->keybit);
set_bit (KEY_UP, button_dev->keybit);
set_bit (KEY_DOWN, button_dev->keybit);
set_bit (KEY_LEFT, button_dev->keybit);
set_bit (KEY_RIGHT, button_dev->keybit);

set_bit (KEY_BACKSPACE, button_dev->keybit);
set_bit (KEY_LEFTSHIFT, button_dev->keybit);
set_bit (KEY_KPASTERISK, button_dev->keybit);
set_bit (KEY_KPPLUS, button_dev->keybit);

input_register_device (&button_dev);
return 0;
}


/**********************************************************/
// Install the megaKbd driver
static int __init megaKbd_init (void)
{
unsigned int result;

//Init Spi Device
address_map ();
Init_SPI ();

result = HW_kbd_init ();
if (result)
return result;

//k_translate = mega_kbd_translate;

megaKbd_code ();
result = register_chrdev (MegaKbd_MAJOR, DEVICE_NAME, &megaKbd_fops);
if (result < 0)
{
printk (DEVICE_NAME "cannot register maor number\n");
return result;
}

//Register device
MegaKbd_devfs_dir = devfs_mk_dir (NULL, "MegaKbd", NULL);

devfs_register (MegaKbd_devfs_dir, DEVICE_NAME, DEVFS_FL_AUTO_DEVNUM, 0, 0, S_IFCHR | S_IRUGO | S_IWUGO, &megaKbd_fops, NULL);
printk ("megaKbd driver installed OK\n");

return 0;
}

/**********************************************************/
// Remove the megaKbd driver
static void megaKbd_exit (void)
{
//Disable Interrupt
// disable_irq (IRQ_EINT8);
free_irq (IRQ_EINT8, NULL);

devfs_unregister (MegaKbd_devfs_dir);
input_unregister_device (&button_dev);
printk ("megaKbd driver uninstalled OK\n");
}

module_init (megaKbd_init);
module_exit (megaKbd_exit);

MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Kenshinxf <xuefengwang101@gmail.com>");
MODULE_DESCRIPTION ("keyboard driver with spi S3C2410 board");

本来说只写arm就好了,谁知道最后单片机也得我写,真是。。。。

那就写吧,说写,其实就是改改一个师哥以前的单片机程序,不知道其他人为什么就不会呢?自己也没搞过单片机,工作后就直接arm了,其实还是比较简单的

下面是单片机程序:

/**key.c***/

#include <mega88.h>
#include "key.h"

#define INT PORTC.2
unsigned char ScanState = 0;
unsigned char KeyDelay[25];
unsigned char temp0;
bit flag0 = 0;

void SendKeyChar(unsigned char KeyValue)
{
PINC.5 = 1;
//SpiPutChar(KeyValue);
//I2cSendChar(KeyValue);
temp0 = SPSR;
SPDR = KeyValue;
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
INT = 1;
flag0 = 1;
}

// Timer 0 overflow interrupt service routine
interrupt [TIM0_OVF] void timer0_ovf_isr(void)
{
TCNT0=0x80;
switch(ScanState)
{
case 0:
if(Col1Row1 == ROW1)
{
if(Col1Row1 == 0)
{
if(++KeyDelay[0] == 7)
{
KeyDelay[0] = 0;
if(ColRow11)
{
ColRow11 = 0;
SendKeyChar(1);
}
}
}
}
else
{
if(Col1Row1 == 0) SendKeyChar(2);
else
{
KeyDelay[0] = 0;
ColRow11 = 1;
SendKeyChar(3);
}
}
Col1Row1 = ROW1;
if(Col1Row2 == ROW2)
{
if(Col1Row2 == 0)
{
if(++KeyDelay[1] == 7)
{
KeyDelay[1] = 0;
if(ColRow12)
{
ColRow12 = 0;
SendKeyChar(4);
}
}
}
}
else
{
if(Col1Row2 == 0) SendKeyChar(5);
else
{
KeyDelay[1] = 0;
ColRow12 = 1;
SendKeyChar(6);
}
}
Col1Row2 = ROW2;
if(Col1Row3 == ROW3)
{
if(Col1Row3 == 0)
{
if(++KeyDelay[2] == 7)
{
KeyDelay[2] = 0;
if(ColRow13)
{
ColRow13 = 0;
SendKeyChar(7);
}
}
}
}
else
{
if(Col1Row3 == 0) SendKeyChar(8);
else
{
KeyDelay[2] = 0;
ColRow13 = 1;
SendKeyChar(9);
}
}
Col1Row3 = ROW3;
if(Col1Row4 == ROW4)
{
if(Col1Row4 == 0)
{
if(++KeyDelay[3] == 7)
{
KeyDelay[3] = 0;
if(ColRow14)
{
ColRow14 = 0;
SendKeyChar(10);
}
}
}
}
else
{
if(Col1Row4 == 0) SendKeyChar(11);
else
{
KeyDelay[3] = 0;
ColRow14 = 1;
SendKeyChar(12);
}
}
Col1Row4 = ROW4;
if(Col1Row5 == ROW5)
{
if(Col1Row5 == 0)
{
if(++KeyDelay[4] == 7)
{
KeyDelay[4] = 0;
if(ColRow15)
{
ColRow15 = 0;
SendKeyChar(13);
}
}
}
}
else
{
if(Col1Row5 == 0) SendKeyChar(14);
else
{
KeyDelay[4] = 0;
ColRow15 = 1;
SendKeyChar(15);
}
}
Col1Row5 = ROW5;
COL1 = 0;
COL2 = 1;
break;
case 1:
if(Col2Row1 == ROW1)
{
if(Col2Row1 == 0)
{
if(++KeyDelay[5] == 7)
{
KeyDelay[5] = 0;
if(ColRow21)
{
ColRow21 = 0;
SendKeyChar(16);
}
}
}
}
else
{
if(Col2Row1 == 0) SendKeyChar(17);
else
{
KeyDelay[5] = 0;
ColRow21 = 1;
SendKeyChar(18);
}
}
Col2Row1 = ROW1;
if(Col2Row2 == ROW2)
{
if(Col2Row2 == 0)
{
if(++KeyDelay[6] == 7)
{
KeyDelay[6] = 0;
if(ColRow22)
{
ColRow22 = 0;
SendKeyChar(19);
}
}
}
}
else
{
if(Col2Row2 == 0) SendKeyChar(20);
else
{
KeyDelay[6] = 0;
ColRow22 = 1;
SendKeyChar(21);
}
}
Col2Row2 = ROW2;
if(Col2Row3 == ROW3)
{
if(Col2Row3 == 0)
{
if(++KeyDelay[7] == 7)
{
KeyDelay[7] = 0;
if(ColRow23)
{
ColRow23 = 0;
SendKeyChar(22);
}
}
}
}
else
{
if(Col2Row3 == 0) SendKeyChar(23);
else
{
KeyDelay[7] = 0;
ColRow23 = 1;
SendKeyChar(24);
}
}
Col2Row3 = ROW3;
if(Col2Row4 == ROW4)
{
if(Col2Row4 == 0)
{
if(++KeyDelay[8] == 7)
{
KeyDelay[8] = 0;
if(ColRow24)
{
ColRow24 = 0;
SendKeyChar(25);
}
}
}
}
else
{
if(Col2Row4 == 0) SendKeyChar(26);
else
{
KeyDelay[8] = 0;
ColRow24 = 1;
SendKeyChar(27);
}
}
Col2Row4 = ROW4;
if(Col2Row5 == ROW5)
{
if(Col2Row5 == 0)
{
if(++KeyDelay[9] == 7)
{
KeyDelay[9] = 0;
if(ColRow25)
{
ColRow25 = 0;
SendKeyChar(28);
}
}
}
}
else
{
if(Col2Row5 == 0) SendKeyChar(29);
else
{
KeyDelay[9] = 0;
ColRow25 = 1;
SendKeyChar(30);
}
}
Col2Row5 = ROW5;
COL2 = 0;
COL3 = 1;
break;
case 2:
if(Col3Row1 == ROW1)
{
if(Col3Row1 == 0)
{
if(++KeyDelay[10] == 7)
{
KeyDelay[10] = 0;
if(ColRow31)
{
ColRow31 = 0;
SendKeyChar(31);
}
}
}
}
else
{
if(Col3Row1 == 0) SendKeyChar(32);
else
{
KeyDelay[10] = 0;
ColRow31 = 1;
SendKeyChar(33);
}
}
Col3Row1 = ROW1;
if(Col3Row2 == ROW2)
{
if(Col3Row2 == 0)
{
if(++KeyDelay[11] == 7)
{
KeyDelay[11] = 0;
if(ColRow32)
{
ColRow32 = 0;
SendKeyChar(34);
}
}
}
}
else
{
if(Col3Row2 == 0) SendKeyChar(35);
else
{
KeyDelay[11] = 0;
ColRow32 = 1;
SendKeyChar(36);
}
}
Col3Row2 = ROW2;
if(Col3Row3 == ROW3)
{
if(Col3Row3 == 0)
{
if(++KeyDelay[12] == 7)
{
KeyDelay[12] = 0;
if(ColRow33)
{
ColRow33 = 0;
SendKeyChar(37);
}
}
}
}
else
{
if(Col3Row3 == 0) SendKeyChar(38);
else
{
KeyDelay[12] = 0;
ColRow33 = 1;
SendKeyChar(39);
}
}
Col3Row3 = ROW3;
if(Col3Row4 == ROW4)
{
if(Col3Row4 == 0)
{
if(++KeyDelay[13] == 7)
{
KeyDelay[13] = 0;
if(ColRow34)
{
ColRow34 = 0;
SendKeyChar(40);
}
}
}
}
else
{
if(Col3Row4 == 0) SendKeyChar(41);
else
{
KeyDelay[13] = 0;
ColRow34 = 1;
SendKeyChar(42);
}
}
Col3Row4 = ROW4;
if(Col3Row5 == ROW5)
{
if(Col3Row5 == 0)
{
if(++KeyDelay[14] == 7)
{
KeyDelay[14] = 0;
if(ColRow35)
{
ColRow35 = 0;
SendKeyChar(43);
}
}
}
}
else
{
if(Col3Row5 == 0) SendKeyChar(44);
else
{
KeyDelay[14] = 0;
ColRow35 = 1;
SendKeyChar(45);
}
}
Col3Row5 = ROW5;
COL3 = 0;
COL4 = 1;
break;
case 3:
if(Col4Row1 == ROW1)
{
if(Col4Row1 == 0)
{
if(++KeyDelay[15] == 7)
{
KeyDelay[15] = 0;
if(ColRow41)
{
ColRow41 = 0;
SendKeyChar(46);
}
}
}
}
else
{
if(Col4Row1 == 0) SendKeyChar(47);
else
{
KeyDelay[15] = 0;
ColRow41 = 1;
SendKeyChar(48);
}
}
Col4Row1 = ROW1;
if(Col4Row2 == ROW2)
{
if(Col4Row2 == 0)
{
if(++KeyDelay[16] == 7)
{
KeyDelay[16] = 0;
if(ColRow42)
{
ColRow42 = 0;
SendKeyChar(49);
}
}
}
}
else
{
if(Col4Row2 == 0) SendKeyChar(50);
else
{
KeyDelay[16] = 0;
ColRow42 = 1;
SendKeyChar(51);
}
}
Col4Row2 = ROW2;
if(Col4Row3 == ROW3)
{
if(Col4Row3 == 0)
{
if(++KeyDelay[17] == 7)
{
KeyDelay[17] = 0;
if(ColRow43)
{
ColRow43 = 0;
SendKeyChar(52);
}
}
}
}
else
{
if(Col4Row3 == 0) SendKeyChar(53);
else
{
KeyDelay[17] = 0;
ColRow43 = 1;
SendKeyChar(54);
}
}
Col4Row3 = ROW3;
if(Col4Row4 == ROW4)
{
if(Col4Row4 == 0)
{
if(++KeyDelay[18] == 7)
{
KeyDelay[18] = 0;
if(ColRow44)
{
ColRow44 = 0;
SendKeyChar(55);
}
}
}
}
else
{
if(Col4Row4 == 0) SendKeyChar(56);
else
{
KeyDelay[18] = 0;
ColRow44 = 1;
SendKeyChar(57);
}
}
Col4Row4 = ROW4;
if(Col4Row5 == ROW5)
{
if(Col4Row5 == 0)
{
if(++KeyDelay[19] == 7)
{
KeyDelay[19] = 0;
if(ColRow45)
{
ColRow45 = 0;
SendKeyChar(58);
}
}
}
}
else
{
if(Col4Row5 == 0) SendKeyChar(59);
else
{
KeyDelay[19] = 0;
ColRow45 = 1;
SendKeyChar(60);
}
}
Col4Row5 = ROW5;
COL4 = 0;
COL5 = 1;
break;
case 4:
if(Col5Row1 == ROW1)
{
if(Col5Row1 == 0)
{
if(++KeyDelay[20] == 7)
{
KeyDelay[20] = 0;
if(ColRow51)
{
ColRow51 = 0;
SendKeyChar(61);
}
}
}
}
else
{
if(Col5Row1 == 0) SendKeyChar(62);
else
{
KeyDelay[20] = 0;
ColRow51 = 1;
SendKeyChar(63);
}
}
Col5Row1 = ROW1;
if(Col5Row2 == ROW2)
{
if(Col5Row2 == 0)
{
if(++KeyDelay[21] == 7)
{
KeyDelay[21] = 0;
if(ColRow52)
{
ColRow52 = 0;
SendKeyChar(64);
}
}
}
}
else
{
if(Col5Row2 == 0) SendKeyChar(65);
else
{
KeyDelay[21] = 0;
ColRow52 = 1;
SendKeyChar(66);
}
}
Col5Row2 = ROW2;
if(Col5Row3 == ROW3)
{
if(Col5Row3 == 0)
{
if(++KeyDelay[22] == 7)
{
KeyDelay[22] = 0;
if(ColRow53)
{
ColRow53 = 0;
SendKeyChar(67);
}
}
}
}
else
{
if(Col5Row3 == 0) SendKeyChar(68);
else
{
KeyDelay[22] = 0;
ColRow53 = 1;
SendKeyChar(69);
}
}
Col5Row3 = ROW3;
if(Col5Row4 == ROW4)
{
if(Col5Row4 == 0)
{
if(++KeyDelay[23] == 7)
{
KeyDelay[23] = 0;
if(ColRow54)
{
ColRow54 = 0;
SendKeyChar(70);
}
}
}
}
else
{
if(Col5Row4 == 0) SendKeyChar(71);
else
{
KeyDelay[23] = 0;
ColRow54 = 1;
SendKeyChar(72);
}
}
Col5Row4 = ROW4;
if(Col5Row5 == ROW5)
{
if(Col5Row5 == 0)
{
if(++KeyDelay[24] == 7)
{
KeyDelay[24] = 0;
if(ColRow55)
{
ColRow55 = 0;
SendKeyChar(73);
}
}
}
}
else
{
if(Col5Row5 == 0) SendKeyChar(74);
else
{
KeyDelay[24] = 0;
ColRow55 = 1;
SendKeyChar(75);
}
}
Col5Row5 = ROW5;
COL5 = 0;
COL1 = 1;
break;
default:
break;
}
ScanState++;
if(ScanState == 5) ScanState = 0;

}


// Declare your global variables here


void main(void)
{
#pragma optsize-
CLKPR=0x80;
CLKPR=0x00;
#ifdef _OPTIMIZE_SIZE_
#pragma optsize+
#endif


PORTB=0x00;
DDRB=0x11;


PORTC=0x00;
DDRC=0x24;


PORTD=0x1F;
DDRD=0x60;
// Timer/Counter 0 initialization
// Clock source: System Clock
// Clock value: 7.813 kHz
// Mode: Normal top=FFh
// OC0A output: Disconnected
// OC0B output: Disconnected TCCR0A=0x00;
TCCR0B=0x05;
TCNT0=0x80;
OCR0A=0x00;
OCR0B=0x00;


// Timer/Counter 1 initialization
// Clock source: System Clock
// Clock value: Timer 1 Stopped
// Mode: Normal top=FFFFh
// OC1A output: Discon.
// OC1B output: Discon.
// Noise Canceler: Off
// Input Capture on Falling Edge
// Timer 1 Overflow Interrupt: Off
// Input Capture Interrupt: Off
// Compare A Match Interrupt: Off
// Compare B Match Interrupt: Off
TCCR1A=0x00;
TCCR1B=0x00;
TCNT1H=0x00;
TCNT1L=0x00;
ICR1H=0x00;
ICR1L=0x00;
OCR1AH=0x00;
OCR1AL=0x00;
OCR1BH=0x00;
OCR1BL=0x00;


// Timer/Counter 2 initialization
// Clock source: System Clock
// Clock value: Timer 2 Stopped
// Mode: Normal top=FFh
// OC2A output: Disconnected
// OC2B output: Disconnected
ASSR=0x00;
TCCR2A=0x00;
TCCR2B=0x00;
TCNT2=0x00;
OCR2A=0x00;
OCR2B=0x00;


// External Interrupt(s) initialization
// INT0: Off
// INT1: Off
// Interrupt on any change on pins PCINT0-7: Off
// Interrupt on any change on pins PCINT8-14: Off
// Interrupt on any change on pins PCINT16-23: Off
EICRA=0x00;
EIMSK=0x00;
PCICR=0x00;


// Timer/Counter 0 Interrupt(s) initialization
TIMSK0=0x01;
// Timer/Counter 1 Interrupt(s) initialization
TIMSK1=0x00;
// Timer/Counter 2 Interrupt(s) initialization
TIMSK2=0x00;


// Analog Comparator initialization
// Analog Comparator: Off
// Analog Comparator Input Capture by Timer/Counter 1: Off
ACSR=0x80;
ADCSRB=0x00;


// SPI initialization
// SPI Type: Slave
// SPI Clock Rate: 125.000 kHz
// SPI Clock Phase: Cycle Half
// SPI Clock Polarity: Low
// SPI Data Order: MSB First
SPCR=0x42;
SPSR=0x00;


// Global enable interrupts
#asm("sei")


while (1)
{
if(flag0)
{
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
flag0 = 0;
INT = 0;
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
INT = 1;
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
#asm("nop")
INT = 0;
}
};
}


/***key.h****/

#define COL1 DDRB.0
#define COL2 DDRB.1
#define COL3 DDRD.7
#define COL4 DDRD.6
#define COL5 DDRD.5

#define ROW1 PIND.0
#define ROW2 PIND.1
//#define KEY1 PIND.2
//#define KEY2 PIND.3
#define ROW3 PIND.2
#define ROW4 PIND.3
#define ROW5 PINB.6

bit Col1Row1 = 1;
bit Col1Row2 = 1;
bit Col1Row3 = 1;
bit Col1Row4 = 1;
bit Col1Row5 = 1;

bit Col2Row1 = 1;
bit Col2Row2 = 1;
bit Col2Row3 = 1;
bit Col2Row4 = 1;
bit Col2Row5 = 1;

bit Col3Row1 = 1;
bit Col3Row2 = 1;
bit Col3Row3 = 1;
bit Col3Row4 = 1;
bit Col3Row5 = 1;

bit Col4Row1 = 1;
bit Col4Row2 = 1;
bit Col4Row3 = 1;
bit Col4Row4 = 1;
bit Col4Row5 = 1;

bit Col5Row1 = 1;
bit Col5Row2 = 1;
bit Col5Row3 = 1;
bit Col5Row4 = 1;
bit Col5Row5 = 1;

bit ColRow11 = 1;
bit ColRow12 = 1;
bit ColRow13 = 1;
bit ColRow14 = 1;
bit ColRow15 = 1;

bit ColRow21 = 1;
bit ColRow22 = 1;
bit ColRow23 = 1;
bit ColRow24 = 1;
bit ColRow25 = 1;

bit ColRow31 = 1;
bit ColRow32 = 1;
bit ColRow33 = 1;
bit ColRow34 = 1;
bit ColRow35 = 1;

bit ColRow41 = 1;
bit ColRow42 = 1;
bit ColRow43 = 1;
bit ColRow44 = 1;
bit ColRow45 = 1;

bit ColRow51 = 1;
bit ColRow52 = 1;
bit ColRow53 = 1;
bit ColRow54 = 1;
bit ColRow55 = 1;

Read More...

星期二, 十一月 13, 2007

输入子系统--event层分析『转』

#####################################################################################
早前曾研究了一下输入子系统的原理,给人的感觉是输入子系统很复杂.但其实内核开发者在这方面已经做得很完善了,
输入子系统虽然错综复杂,但是只要我们领会了输入子系统的一些设计思想后,我们要使用它并非难事.

以下以内核自带的gpio_keys驱动为例,介绍输入子系统的使用.
主要的原因是gpio_keys驱动比较简单易懂,另外不是没个人都有触摸屏,但键盘的话相信每一块开发板上都配有吧^_^

按照以前的习惯,先从下到上的研究底层驱动是如何提交输入事件的:
#####################################################################################

drivers/input/keyboard/gpio_keys.c:

static int __devinit gpio_keys_probe(struct platform_device *pdev)
{
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input;
int i, error;

input = input_allocate_device();//申请input_dev结构
if (!input)
return -ENOMEM;

platform_set_drvdata(pdev, input);//把input_dev结构放好(以后方便调用)

input->evbit[0] = BIT(EV_KEY);//目前event的类型不操作32,所以你会看到对于evbit数组的操作都是对evbit[0]中的位来进行操作.

input->name = pdev->name;
input->phys = "gpio-keys/input0";
input->dev.parent = &pdev->dev;

input->id.bustype = BUS_HOST;
input->id.vendor = 0x0001;
input->id.product = 0x0001;
input->id.version = 0x0100;

for (i = 0; i <>nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
int irq = gpio_to_irq(button->gpio);
unsigned int type = button->type ?: EV_KEY;

set_irq_type(irq, IRQ_TYPE_EDGE_BOTH);

/* 根据用户所指定的gpio_keys来申请中断和注册中断处理函数*/
error = request_irq(irq, gpio_keys_isr, IRQF_SAMPLE_RANDOM,
button->desc ? button->desc : "gpio_keys",
pdev);
if (error) {
printk(KERN_ERR "gpio-keys: unable to claim irq %d; error %d\n",
irq, error);
goto fail;
}

input_set_capability(input, type, button->code);
}

error = input_register_device(input);//注册输入设备,并和对应的handler处理函数挂钩
if (error) {
printk(KERN_ERR "Unable to register gpio-keys input device\n");
goto fail;
}

return 0;

fail:
for (i = i - 1; i >= 0; i--)
free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);

input_free_device(input);

return error;
}


提到input_dev结构,以下谈一下我对于它的理解:
struct input_dev {

void *private;

const char *name;
const char *phys;
const char *uniq;
struct input_id id;

/*
* 根据各种输入信号的类型来建立类型为unsigned long 的数组,
* 数组的每1bit代表一种信号类型,
* 内核中会对其进行置位或清位操作来表示时间的发生和被处理.
*/

unsigned long evbit[NBITS(EV_MAX)];
unsigned long keybit[NBITS(KEY_MAX)];
unsigned long relbit[NBITS(REL_MAX)];
unsigned long absbit[NBITS(ABS_MAX)];
unsigned long mscbit[NBITS(MSC_MAX)];
unsigned long ledbit[NBITS(LED_MAX)];
unsigned long sndbit[NBITS(SND_MAX)];
unsigned long ffbit[NBITS(FF_MAX)];
unsigned long swbit[NBITS(SW_MAX)];

.........................................
};

/**
* input_set_capability - mark device as capable of a certain event
* @dev: device that is capable of emitting or accepting event
* @type: type of the event (EV_KEY, EV_REL, etc...)
* @code: event code
*
* In addition to setting up corresponding bit in appropriate capability
* bitmap the function also adjusts dev->evbit.
*/

/* 记录本设备对于哪些事件感兴趣(对其进行处理)*/
void input_set_capability(struct input_dev *dev, unsigned int type, unsigned int code)
{
switch (type) {
case EV_KEY:
__set_bit(code, dev->keybit);//比如按键,应该对哪些键值的按键进行处理(对于其它按键不予理睬)
break;

case EV_REL:
__set_bit(code, dev->relbit);
break;

case EV_ABS:
__set_bit(code, dev->absbit);
break;

case EV_MSC:
__set_bit(code, dev->mscbit);
break;

case EV_SW:
__set_bit(code, dev->swbit);
break;

case EV_LED:
__set_bit(code, dev->ledbit);
break;

case EV_SND:
__set_bit(code, dev->sndbit);
break;

case EV_FF:
__set_bit(code, dev->ffbit);
break;

default:
printk(KERN_ERR
"input_set_capability: unknown type %u (code %u)\n",
type, code);
dump_stack();
return;
}

__set_bit(type, dev->evbit);//感觉和前面重复了(前面一经配置过一次了)
}
EXPORT_SYMBOL(input_set_capability);


static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
{
int i;
struct platform_device *pdev = dev_id;
struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
struct input_dev *input = platform_get_drvdata(pdev);

for (i = 0; i <>nbuttons; i++) {
struct gpio_keys_button *button = &pdata->buttons[i];
int gpio = button->gpio;

if (irq == gpio_to_irq(gpio)) {//判断哪个键被按了?
unsigned int type = button->type ?: EV_KEY;
int state = (gpio_get_value(gpio) ? 1 : 0) ^ button->active_low;//记录按键状态

input_event(input, type, button->code, !!state);//汇报输入事件
input_sync(input);//等待输入事件处理完成
}
}

return IRQ_HANDLED;
}


/*
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;

if (type > EV_MAX || !test_bit(type, dev->evbit))//首先判断该事件类型是否有效且为该设备所接受
return;

add_input_randomness(type, code, value);

switch (type) {

case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event)
dev->event(dev, type, code, value);
break;

case SYN_REPORT:
if (dev->sync)
return;
dev->sync = 1;
break;
}
break;

case EV_KEY:
/*
* 这里需要满足几个条件:
* 1: 键值有效(不超出定义的键值的有效范围)
* 2: 键值为设备所能接受(属于该设备所拥有的键值范围)
* 3: 按键状态改变了
*/

if (code > KEY_MAX || !test_bit(code, dev->keybit) || !!test_bit(code, dev->key) == value)
return;

if (value == 2)
break;

change_bit(code, dev->key);//改变对应按键的状态

/* 如果你希望按键未释放的时候不断汇报按键事件的话需要以下这个(在简单的gpio_keys驱动中不需要这个,暂时不去分析) */
if (test_bit(EV_REP, dev->evbit) && dev->rep[REP_PERIOD] && dev->rep[REP_DELAY] && dev->timer.data && value) {
dev->repeat_key = code;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]));
}

break;
........................................................

if (type != EV_SYN)
dev->sync = 0;

if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
/*
* 循环调用所有处理该设备的handle(event,mouse,ts,joy等),
* 如果有进程打开了这些handle(进行读写),则调用其对应的event接口向气汇报该输入事件.
*/
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
}
EXPORT_SYMBOL(input_event);


#########################################################################
好了,下面再来研究一下event层对于input层报告的这个键盘输入事件是如何来处理的.
#########################################################################

drivers/input/evdev.c:

static struct input_handler evdev_handler = {
.event = evdev_event,
.connect = evdev_connect,
.disconnect = evdev_disconnect,
.fops = &evdev_fops,
.minor = EVDEV_MINOR_BASE,
.name = "evdev",
.id_table = evdev_ids,
};

static void evdev_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
struct evdev *evdev = handle->private;
struct evdev_client *client;

if (evdev->grab) {
client = evdev->grab;

do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);

kill_fasync(&client->fasync, SIGIO, POLL_IN);
} else
/* 遍厉client_list链表中的client结构(代表些打开evdev的进程(个人理解^_^)) */
list_for_each_entry(client, &evdev->client_list, node) {
/* 填充代表该输入信号的struct input_event结构(事件,类型,键码,键值) */
do_gettimeofday(&client->buffer[client->head].time);
client->buffer[client->head].type = type;
client->buffer[client->head].code = code;
client->buffer[client->head].value = value;
/* 更新写指针 */
client->head = (client->head + 1) & (EVDEV_BUFFER_SIZE - 1);

kill_fasync(&client->fasync, SIGIO, POLL_IN);//通知调用input_sync的进程:输入事件经已处理完毕(通知底层).
}

wake_up_interruptible(&evdev->wait);//唤醒睡眠在evdev->wait等待队列等待输入信息的进程(通知上层).
}

###################################################################################
好了,至此一个按键的输入事件处理完毕,现在再来从上到上的来看看用户是如何获取这个输入事件的.
###################################################################################


static const struct file_operations evdev_fops = {
.owner = THIS_MODULE,
.read = evdev_read,
.write = evdev_write,
.poll = evdev_poll,
.open = evdev_open,
.release = evdev_release,
.unlocked_ioctl = evdev_ioctl,
#ifdef CONFIG_COMPAT
.compat_ioctl = evdev_ioctl_compat,
#endif
.fasync = evdev_fasync,
.flush = evdev_flush
};


static int evdev_open(struct inode *inode, struct file *file)
{
struct evdev_client *client;
struct evdev *evdev;
int i = iminor(inode) - EVDEV_MINOR_BASE;
int error;

if (i >= EVDEV_MINORS)
return -ENODEV;

evdev = evdev_table[i];

if (!evdev || !evdev->exist)
return -ENODEV;

client = kzalloc(sizeof(struct evdev_client), GFP_KERNEL);
if (!client)
return -ENOMEM;

client->evdev = evdev;
/* 添加evdev_client结构到链表evdev->client_list中(好让输入事件到来的时候填写该结构并唤醒进程读取) */
list_add_tail(&client->node, &evdev->client_list);

if (!evdev->open++ && evdev->exist) {
error = input_open_device(&evdev->handle);
if (error) {
list_del(&client->node);
kfree(client);
return error;
}
}

file->private_data = client;//存放好evdev_client结构方便以后使用
return 0;
}


static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
int retval;

if (count <>//对于每次读取的数据大小是有一定的要求.
return -EINVAL;

if (client->head == client->tail && evdev->exist && (file->f_flags & O_NONBLOCK))//缓存中没有数据可读且设备是存在的,
如果设置为NONBLOCK方式来读,立即返回.
return -EAGAIN;

retval = wait_event_interruptible(evdev->wait,
client->head != client->tail || !evdev->exist);//否则等待缓存有数据可读或设备不存在(被移去)
if (retval)
return retval;

if (!evdev->exist)
return -ENODEV;

while (client->head != client->tail && retval + evdev_event_size() <= count) {//下面开始读取数据

struct input_event *event = (struct input_event *) client->buffer + client->tail;//获取缓存中的读指针

if (evdev_event_to_user(buffer + retval, event))//返回数据给用户
return -EFAULT;

client->tail = (client->tail + 1) & (EVDEV_BUFFER_SIZE - 1);//更新读指针
retval += evdev_event_size();
}

return retval;
}

呵呵,看到了吧,应用程序就是这样获取输入事件的^_^

######################################################################################################################################
本来对于gpio_keys这样的驱动程序,只要当发生按键事件的时候向上层应用程序汇报键值即可.
不过,对于一些带输出设备(例如led灯)的输入设备来说(例如键盘),上层应用程序同样可以利用event层来读取或改变其状态.
请看以下代码:
######################################################################################################################################

static ssize_t evdev_write(struct file *file, const char __user *buffer, size_t count, loff_t *ppos)
{
struct evdev_client *client = file->private_data;
struct evdev *evdev = client->evdev;
struct input_event event;
int retval = 0;

if (!evdev->exist)
return -ENODEV;

while (retval <>

if (evdev_event_from_user(buffer + retval, &event))//从用户处获取事件结构
return -EFAULT;
input_inject_event(&evdev->handle, event.type, event.code, event.value);//往底层发送事件
retval += evdev_event_size();
}

return retval;
}


/**
* input_inject_event() - send input event from input handler
* @handle: input handle to send event through
* @type: type of the event
* @code: event code
* @value: value of the event
*
* Similar to input_event() but will ignore event if device is "grabbed" and handle
* injecting event is not the one that owns the device.
*/
void input_inject_event(struct input_handle *handle, unsigned int type, unsigned int code, int value)
{
if (!handle->dev->grab || handle->dev->grab == handle)
input_event(handle->dev, type, code, value);
}
EXPORT_SYMBOL(input_inject_event);

/*
* input_event() - report new input event
* @dev: device that generated the event
* @type: type of the event
* @code: event code
* @value: value of the event
*
* This function should be used by drivers implementing various input devices
* See also input_inject_event()
*/
void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
{
struct input_handle *handle;

if (type > EV_MAX || !test_bit(type, dev->evbit))//首先判断该事件类型是否有效且为该设备所接受
return;

add_input_randomness(type, code, value);

switch (type) {

case EV_SYN:
switch (code) {
case SYN_CONFIG:
if (dev->event)
dev->event(dev, type, code, value);
break;

case SYN_REPORT:
if (dev->sync)
return;
dev->sync = 1;
break;
}
break;

.............................................................
case EV_LED:

if (code > LED_MAX || !test_bit(code, dev->ledbit) || !!test_bit(code, dev->led) == value)
return;

change_bit(code, dev->led);

if (dev->event)
dev->event(dev, type, code, value);

break;


if (type != EV_SYN)
dev->sync = 0;

if (dev->grab)
dev->grab->handler->event(dev->grab, type, code, value);
else
/*
* 循环调用所有处理该设备的handle(event,mouse,ts,joy等),
* 如果有进程打开了这些handle(进行读写),则调用其对应的event接口向气汇报该输入事件.
*/
list_for_each_entry(handle, &dev->h_list, d_node)
if (handle->open)
handle->handler->event(handle, type, code, value);
}
EXPORT_SYMBOL(input_event);

注:
鉴于简单的gpio_keys驱动中没有注册自己的event接口,当然也没有对于LED灯的处理,而event层只是简单的向上层汇报输入事件(event层也不可能帮你处理你的led设备,对吧),所以这个通过输入子系统控制LED的部分暂时不去研究.
(输出设备LED灯不属于这个输入设备gpio_key的一部分.当然,如果你想通过这个gpio_keys设备来控制led灯的话,可以修改这个gpio_keys驱动,详细可参考driver/input/keyboard目录下的驱动)

Read More...

星期四, 十一月 08, 2007

关于键盘,可能还有其他东西的牛网站

http://www.win.tue.nl/~aeb/linux/

Read More...

键盘驱动原来如此『转』

 键盘驱动原来如此
by good02xaut

闲来无事,研究一下键盘驱动。虽然前面贴过一篇《键盘体系》的介绍,那个链接好像失效。网上还是可以很容易搜到的。

从src里面查找我们需要的参考信息。首先是/drivers/char/makefile文件。keyboard.o提供了键盘的支持。还有就是键盘映射表文件。

drivers/char/keyboard.c,粗略一看,收获不是很大。查看其他kb文件。当然还有很多include目录下的文件。

得到认识:

1。键盘和网卡类似,在/dev/目录下面没有对应于任何设备节点。也就是说,键盘不需要主次设备号。

2。键盘属于tty驱动范畴。对于tty驱动,本来都是一个IN+OUT成对出现的。象串口驱动。而键盘则是通过VT成对出现。IN是键盘,OUT则是fb。

3。内核参数,console=tty0。表明使用VT为tty。console=ttyS0。表明使用串口为tty.

4。历史原因造成了键盘编码的复杂性。由于键盘存在语言差异,不同的国家键盘可以大不同。因此出现了,扫描码,键盘码,ASCII等等。

5。嵌入式场合,很少会用到键盘。但是多会用到按键。这里,我是提倡使用键盘驱动的。当然,也可以基于misc驱动。只有键盘驱动才可以成为VT。

键盘驱动的流程:
中断驱动键盘矩阵
申请中断-》中断检测=》中断处理=》获取scancode=》hand_scancode=》tasklet_schlue.。

查询键盘矩阵
初始化定时器=》定时器溢出=》扫描获取scancode=》hand_scancode=》tasklet_schelue

在S3C44B0X的公板上,有4个按键,分别接到了EINT4,EINT5,EINT6,EINT7。通过对中断21号的处理程序,设置。
按键1=》l
按键2=》s
按键3=》回车
内核启动时,console=tty0。
内核启动后,LCD上有输出。进入shell.
单击1,2,3。屏幕上>ls 命令可以正常执行。

键盘驱动就是这么简单:)

Read More...

星期二, 十一月 06, 2007

矩阵式小键盘的linux驱动编写参考『转』

矩阵式小键盘的linux驱动编写参考
电路设计上4*5键盘, 采用9个I/O口,以中断方式(EINT0)获取键值,当然也可以采用定时查询方式等。这里提供部分矩阵键盘的linux驱动代码参考,可以应用在ARMSYS2410/2440上。
完整的键盘驱动文件可以参考driver/char/s3c2410_kbd.c文件(这个驱动采用的是专用芯片来连接键盘,不是矩阵键盘的例子,但可以参考一下该驱动的结构等信息)。

//在EINT0的中断处理中以扫描方式读取键值

static int key_interrupt(int nr, void *devid, struct pt_regs *regs)
{
int i,scannum;
unsigned short temp = 0;
unsigned char tempc0,tempc1;
int ret;

mdelay(10);
for(i = 0; i < scannum="scan_s[i];" temp =" (scannum" i =" 0;" ret =" key_ascii[i];">= 20)
printk("default key 0x%x\n", temp);
}
if ( ret != KBCTL_NODATA )
{
if ( (ret != KBCTL_AGAIN )&& (ret != KK_NONE))
handle_scancode(ret, !(ret & 0x80));
tasklet_schedule(&keyboard_tasklet);
handle_scancode(0,1);
}

mdelay(10);
tempc0 = 0;
scan_key(tempc0);
beep_off;
SRCPND = 1;
}


//列输入脚(读出键值)


#define GPE13 13
#define GPE12 12
#define GPE11 11
#define GPG2 2


//行输出脚(输出扫描码)
#define GPE15 15 //GPE
#define GPE14 14 //IICSDA and IICSCL is open drain output
#define GPB0 0
#define GPH10 10
#define GPH9 9


//读取键值程序
#define read_key(x) { \
x = 0; \
x |= (((GPEDAT >> GPE13) & 0x1) <<>> GPE12) & 0x1) <<>> GPE11) & 0x1) <<>> GPG2) & 0x1) <<>

//输出扫描码
#define scan_key(x) { \
GPHDAT = (GPHDAT | (0x3 << gpbdat =" (GPBDAT">> 2) & 0x1) << gpedat =" (GPEDAT">> 3) & 0x3) << style="color: rgb(9, 9, 247);">//初始化中断
static int __init HW_kbd_init(void)
{
int ret = -ENODEV;
int delay ;
set_external_irq(IRQ_KBD, EXT_FALLING_EDGE, GPIO_PULLUP_DIS);
key_init;
scan_key(0);
/*
* Claim the appropriate interrupts
*/

ret = request_irq(IRQ_KBD, key_interrupt, SA_INTERRUPT,
"keyboard", key_interrupt);


printk("the EXTINT0 is 0x%x\n",EXTINT0);
if (ret) {
printk(KERN_INFO "request IRQ failed (%d)\n", IRQ_KBD);
return ret;
}


return ret;
}


Read More...

星期三, 十月 31, 2007

S3C2410下的Linux键盘驱动分析

内核初始化的时候会调用chr_dev_init 之后会有这样的调用过程

0 && image.height>0){if(image.width>=510){this.width=510;this.height=image.height*510/image.width;}}" style="width: 184px; height: 239px;" src="http://docs.google.com/File?id=dcmkbdfm_40ctgbvcc2">
真正的键盘部分的初始化从kbd_init开始 kbd_init 初始化键盘状态结构体(struct kbd_struct), 这个结构体主要记录shift状态, led状态等. 将各个tty对应的struct kbd_struct全部初始化. 即每个tty都有自己的独立的struct kbd_struct 得到tty的结构体指针 键盘的硬件初始化(kbd_init) 允许键盘处理的后半部分(tasklet_enable)
调度键盘处理的后半部分(keyboard_tasklet),
keyboard_tasklet主要是处理键值(kbd_processkeycode)和处理led指示灯(kbd_leds)的动作.
kbd_processkeycode主要判断各种状态, 将扫描值映射成键值, 将其放入tty的键值队列,
以及autorepeat动作(如果键盘是处于raw mode就不会处理autorepeat, 因为控制台是希望得到键盘的原始状态).
autorepeat主要是通过一个timer来实现的,这个timer调用key_callback,这个函数又再次调用
keyboard_tasklet, 这样只要按着键就会不断有新键值进入tty的键值队列.
对于键盘处理的后半部分(keyboard_tasklet), 我们只要记住一点: 这部分操作时间可能会比较长, 所以需要与中断响应分开处理.
向电源管理模块注册(pm_register)
OK, 我们再来看看键盘的硬件初始化
kbd_init被定义成与(s3c2410_kbd_init)相同,s3c2410_kbd_init会调用HW_kbd_init,
设置键盘接口界面(这里使用的是SPI). 包括IO口控制,SPI触发模式,频率等.
写一些测试数据(SPI_WRITE)
将key_interrupt注册到EINT1.这里好像有点奇怪,怎么会注册到外部中断呢?分析可能是这个版本对应的外挂SPI键盘在有按键按下时,会产生一个中断,通知S3C2410去读SPI接口
key_interrupt
选通外挂键盘
等待外挂键盘准本就绪
写一个空数据到SPI总线上
等
待所有数据输出完成(在输出完成的同时,输入数据(即键盘的扫描值)也进入到S3C2410.
这一步其实是效率很低的,因为是在FIQ响应中,所以所有中断都被禁止,
而SPI总线的速率为99.12K,那么我们等待所有数据输出完成就需要(8/99.12)毫秒,也就是80.7uS,
而在这段时间里CPU是什么也干不了的.不过幸亏,没有几个人会用到SPI总线的外挂键盘吧.
对扫描值做一些判断与处理, 最后调度后半处理函数keyboard_tasklet

Read More...