版權聲明:本文為博主原創文章,未經博主允許不得轉載。 在上一節分析輸入子系統內的intput_handler軟體處理部分後,接下來我們開始寫input_dev驅動 本節目標: 實現鍵盤驅動,讓開發板的4個按鍵代表鍵盤中的L、S、空格鍵、回車鍵 1.先來介紹以下幾個結構體使用和函數,下麵代碼中會用到 ...
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
在上一節分析輸入子系統內的intput_handler軟體處理部分後,接下來我們開始寫input_dev驅動
本節目標:
實現鍵盤驅動,讓開發板的4個按鍵代表鍵盤中的L、S、空格鍵、回車鍵
1.先來介紹以下幾個結構體使用和函數,下麵代碼中會用到
1)input_dev驅動設備結構體中常用成員如下:
struct input_dev { void *private; const char *name; //設備名字 const char *phys; //文件路徑,比如 input/buttons const char *uniq; struct input_id id; unsigned long evbit[NBITS(EV_MAX)]; //表示支持哪類事件,常用有以下幾種事件(可以多選) //EV_SYN 同步事件,當使用input_event()函數後,就要使用這個上報個同步事件 //EV_KEY 鍵盤事件 //EV_REL (relative)相對坐標事件,比如滑鼠 //EV_ABS (absolute)絕對坐標事件,比如搖桿、觸摸屏感應 //EV_MSC 其他事件,功能 //EV_LED LED燈事件 //EV_SND (sound)聲音事件 //EV_REP 重覆鍵盤按鍵事件 //(內部會定義一個定時器,若有鍵盤按鍵事件一直按下/鬆開,就重覆定時,時間一到就上報事件) //EV_FF 受力事件 //EV_PWR 電源事件 //EV_FF_STATUS 受力狀態事件 unsigned long keybit[NBITS(KEY_MAX)]; //存放支持的鍵盤按鍵值 //鍵盤變數定義在:include/linux/input.h, 比如: KEY_L(按鍵L) unsigned long relbit[NBITS(REL_MAX)]; //存放支持的相對坐標值 unsigned long absbit[NBITS(ABS_MAX)]; //存放支持的絕對坐標值 unsigned long mscbit[NBITS(MSC_MAX)]; //存放支持的其它事件,也就是功能 unsigned long ledbit[NBITS(LED_MAX)]; //存放支持的各種狀態LED unsigned long sndbit[NBITS(SND_MAX)]; //存放支持的各種聲音 unsigned long ffbit[NBITS(FF_MAX)]; //存放支持的受力設備 unsigned long swbit[NBITS(SW_MAX)]; //存放支持的開關功能 ... ...
2)函數如下:
struct input_dev *input_allocate_device(void); //向內核中申請一個input_dev設備,然後返回這個設備 input_unregister_device(struct input_dev *dev); //卸載/sys/class/input目錄下的input_dev這個類設備, 一般在驅動出口函數寫 input_free_device(struct input_dev *dev); //釋放input_dev這個結構體, 一般在驅動出口函數寫 set_bit(nr,p); //設置某個結構體成員p裡面的某位等於nr,支持這個功能 /* 比如: set_bit(EV_KEY,buttons_dev->evbit); //設置input_dev結構體buttons_dev->evbit支持EV_KEY set_bit(KEY_S,buttons_dev->keybit); //設置input_dev結構體buttons_dev->keybit支持按鍵”S” */ void input_event(struct input_dev *dev, unsigned int type, unsigned int code, int value); //上報事件 // input_dev *dev :要上報哪個input_dev驅動設備的事件 // type : 要上報哪類事件, 比如按鍵事件,則填入: EV_KEY // code: 對應的事件里支持的哪個變數,比如按下按鍵L則填入: KEY_L //value:對應的變數里的數值,比如鬆開按鍵則填入1,鬆開按鍵則填入0
input_sync(struct input_dev *dev); //同步事件通知
為什麼使用了input_event()上報事件函數,就要使用這個函數?
因為input_event()函數只是個事件函數,所以需要這個input_sync()同步事件函數來通知系統,然後系統才會知道
input_sync()代碼如下:
static inline void input_sync(struct input_dev *dev) { input_event(dev, EV_SYN, SYN_REPORT, 0); //就是上報同步事件,告訴內核:input_event()事件執行完畢 }
2.然後開始寫代碼
1)向內核申請input_dev結構體
2)設置input_dev的成員
3)註冊input_dev 驅動設備
4)初始化定時器和中斷
5)寫中斷服務函數
6)寫定時器超時函數
7)在出口函數中 釋放中斷函數,刪除定時器,卸載釋放驅動
具體代碼如下(都加了註釋):
#include <linux/module.h> #include <linux/version.h> #include <linux/init.h> #include <linux/fs.h> #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/sched.h> #include <linux/pm.h> #include <linux/sysctl.h> #include <linux/proc_fs.h> #include <linux/delay.h> #include <linux/platform_device.h> #include <linux/input.h> #include <linux/irq.h> #include <linux/gpio_keys.h> #include <asm/gpio.h> struct input_dev *buttons_dev; // 定義一個input_dev結構體 static struct ping_desc *buttons_id; //保存dev_id,在定時器中用 static struct timer_list buttons_timer; //定時器結構體 struct ping_desc{ unsigned char *name; //中斷設備名稱 int pin_irq; //按鍵的外部中斷標誌位 unsigned int pin; //引腳 unsigned int irq_ctl; //觸發中斷狀態: IRQ_TYPE_EDGE_BOTH unsigned int button; //dev_id,對應鍵盤的 L , S, 空格, enter }; // KEY1 -> L // KEY2 -> S // KEY3 -> 空格 // KEY4 -> enter static struct ping_desc buttons_desc[5]= { {"s1", IRQ_EINT0, S3C2410_GPF0, IRQ_TYPE_EDGE_BOTH,KEY_L}, {"s2", IRQ_EINT2, S3C2410_GPF2, IRQ_TYPE_EDGE_BOTH,KEY_S}, {"s3", IRQ_EINT11, S3C2410_GPG3 , IRQ_TYPE_EDGE_BOTH,KEY_SPACE}, {"s4", IRQ_EINT19, S3C2410_GPG11,IRQ_TYPE_EDGE_BOTH,KEY_ENTER}, }; /*5. 寫中斷服務函數*/ static irqreturn_t buttons_irq (int irq, void *dev_id) //中斷服務函數 { buttons_id=(struct ping_desc *)dev_id; //保存當前的dev_id mod_timer(&buttons_timer, jiffies+HZ/100 ); //更新定時器值 10ms return 0; } /*6.寫定時器超時函數*/ void buttons_timer_function(unsigned long i) { int val; val=s3c2410_gpio_getpin(buttons_id->pin); //獲取是什麼電平 if(val) //高電平,鬆開 { /*上報事件*/ input_event(buttons_dev,EV_KEY,buttons_id->button, 0); //上報EV_KEY類型,button按鍵,0(沒按下) input_sync(buttons_dev); // 上傳同步事件,告訴系統有事件出現 } else //低電平,按下 { /*上報事件*/ input_event(buttons_dev, EV_KEY, buttons_id->button, 1); //上報EV_KEY類型,button按鍵,1(按下) input_sync(buttons_dev); // 上傳同步事件,告訴系統有事件出現 } } static int buttons_init(void) //入口函數 { int i; buttons_dev=input_allocate_device(); //1.向內核 申請input_dev結構體 /*2.設置input_dev , */ set_bit(EV_KEY,buttons_dev->evbit); //支持鍵盤事件 set_bit(EV_REP,buttons_dev->evbit); //支持鍵盤重覆按事件 set_bit(KEY_L,buttons_dev->keybit); //支持按鍵 L set_bit(KEY_S,buttons_dev->keybit); //支持按鍵 S set_bit(KEY_SPACE,buttons_dev->keybit); //支持按鍵 空格 set_bit(KEY_ENTER,buttons_dev->keybit); //支持按鍵 enter /*3.註冊input_dev */ input_register_device(buttons_dev); /*4. 初始化硬體:初始化定時器和中斷*/ // KEY1 -> L // KEY2 -> S // KEY3 -> 空格 // KEY4 -> enter init_timer(&buttons_timer); buttons_timer.function=buttons_timer_function; add_timer(&buttons_timer); for(i=0;i<4;i++) request_irq(buttons_desc[i].pin_irq, buttons_irq, buttons_desc[i].irq_ctl, buttons_desc[i].name, &buttons_desc[i]); return 0; } static int buttons_exit(void) //出口函數 { /*7.釋放中斷函數,刪除定時器,卸載釋放驅動*/ int i; for(i=0;i<4;i++) free_irq(buttons_desc[i].pin_irq,&buttons_desc[i]); //釋放中斷函數 del_timer(&buttons_timer); //刪除定時器 input_unregister_device(buttons_dev); //卸載類下的驅動設備 input_free_device(buttons_dev); //釋放驅動結構體 return 0; } module_init(buttons_init); module_exit(buttons_exit); MODULE_LICENSE("GPL v2");
3.測試運行:
掛載鍵盤驅動後, 如下圖,可以通過 ls -l /dev/event* 命令查看已掛載的設備節點:
在上一節輸入子系統里分析到:輸入子系統的主設備號為13,其中event驅動本身的此設備號是從64開始的,所以我們的鍵盤驅動的此設備號=64+1
3.1測試運行有兩種,一種是直接打開/dev/tyy1,第二種是使用exec命令
(exec命令詳解入口地址: http://www.cnblogs.com/lifexy/p/7553228.html)
方法1:
cat /dev/tty1 //就會通過tty_io.c來訪問鍵盤驅動
方法2:
exec 0</dev/tty1
3.2 調試:
若測試不成功,板子又在QT下進行的:
1)可以使用vi命令,在記事本中按按鍵試
2)或者刪除/etc/init.d/rcS 裡面有關QT自啟動的命令,然後重啟
若板子沒在QT下進行,也無法測試成功:
1)可以使用hexdump命令來調試代碼
(hexdump命令調試代碼詳解地址:http://www.cnblogs.com/lifexy/p/7553550.html)