簡介
自己寫驅動流程
- APP:open、read、write
- 驅動層:
- 實作相應的open、read、write函數
- 使用file_operation結構挂接自己的相關函數
- 使用chrdev_register注冊
- 初始化
- 退出等等
現在有了子系統結構,核心層已經有人寫好,我們隻需要實作兩層即可:裝置層、操作層
裝置層實作步驟:
- 配置設定input_dev
- 設定結構體
- 注冊結構體
- 硬體相關操作
裝置層
實作可參考核心參考示例代碼
gpio_keys.c
,
gpio_keys.c linux-2.6.22.6\drivers\input\keyboard
- 配置設定
input_dev
結構
先定義一個
結構,檢視例程怎樣配置設定一個結構input_dev
static struct input_dev *buttons_dev;
buttons_dev=input_allocate_device();
-
設定結構
先介紹一個小函數
先看下該結構裡面有什麼東西
struct input_dev {
void *private;
const char *name;
const char *phys;
const char *uniq;
struct input_id id;
unsigned long evbit[NBITS(EV_MAX)];//表示能産生哪些事件
/*
#define EV_SYN 0x00 //同步事件
#define EV_KEY 0x01 //按鍵事件
#define EV_REL 0x02 //相對位移事件 滑鼠
#define EV_ABS 0x03 //絕對位移 觸摸屏
#define EV_MSC 0x04 //下面先不介紹
······
#define EV_MAX 0x1f
*/
unsigned long keybit[NBITS(KEY_MAX)]; //表示能産生哪些按鍵
unsigned long relbit[NBITS(REL_MAX)]; //表示能産生哪些相對位移x坐标y坐标滾輪
unsigned long absbit[NBITS(ABS_MAX)]; //表示能産生哪些絕對位移事件
unsigned long mscbit[NBITS(MSC_MAX)]; //以下暫時先不介紹
unsigned long ledbit[NBITS(LED_MAX)];
······
struct list_head h_list;
struct list_head node;
};
設定該結構相關的成員
/* 2、設定結構體 */
/* 2.1、能産生按鍵類事件 */
set_bit(EV_KEY,buttons_dev->evbit);
/* 2.2、設定能産生按鍵類中哪些事件 :字母、數字、符号等等*/
/* 開發闆有四個按鍵這裡規定為 L、S、ENTER、LEFTSHIFT左邊的shift */
set_bit(KEY_L,buttons_dev->keybit);
set_bit(KEY_S,buttons_dev->keybit);
set_bit(KEY_ENTER,buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);
- 注冊
input_register_device(buttons_dev);
- 硬體相關操作
/* 4、硬體相關操作 */
/* 4.1、定時器初始化*/
init_timer(&button_timer);
button_timer.expires=;
button_timer.function=button_timer_func;
add_timer(&button_timer);
/* 4.2、注冊中斷 */
for(count=;count<;count++)
{
request_irq(mykey[count].eint,button_irq,IRQT_BOTHEDGE,mykey[count].name,&mykey[count]);
}
驅動代碼
/*
基于S3C2440按鍵輸入子系統驅動代碼
參考linux-2.6.22.6\drivers\input\keyboard\gpio_keys.c
*/
#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 <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <linux/gpio_keys.h>
#include <asm/gpio.h>
static struct input_dev *buttons_dev;
static struct timer_list button_timer;
//定義按鍵資訊結構體
struct key_info{
unsigned int eint;
unsigned int pins;
unsigned int pin_val;
char name[];
};
struct key_info *key_info_id=NULL;
//按鍵數組初始化
struct key_info mykey[]=
{
{IRQ_EINT0, S3C2410_GPF0, KEY_L, "S2"},
{IRQ_EINT2, S3C2410_GPF2, KEY_S, "S3"},
{IRQ_EINT11,S3C2410_GPG3, KEY_ENTER, "S4"},
{IRQ_EINT19,S3C2410_GPG11,KEY_LEFTSHIFT,"S5"},
};
//按鍵中斷處理函數
static irqreturn_t button_irq(int irq,void *dev_id)
{
key_info_id=(struct key_info *)dev_id ; //等待隊列控制條件置真
mod_timer(&button_timer,jiffies+HZ/);
return IRQ_HANDLED;
}
//定時器逾時處理函數
static void button_timer_func(unsigned long data)
{
struct key_info *cdev_keyinfo = key_info_id;
unsigned int val; //存儲按鍵電平
if(!key_info_id) //過濾第一個定時器中斷
return;
val=s3c2410_gpio_getpin (cdev_keyinfo->pins); //讀管腳電平
if(val)
{
/* 按鍵沒有按下,用0表示 */
input_event(buttons_dev,EV_KEY,cdev_keyinfo->pin_val,);
//事件上報完畢後,發送事件同步事件
input_sync(buttons_dev);
}else
{
/* 按鍵沒有按下,用1表示 */
input_event(buttons_dev,EV_KEY,cdev_keyinfo->pin_val,);
//事件上報完畢後,發送事件同步事件
input_sync(buttons_dev);
}
}
static int __init button_init(void)
{
unsigned char count=;
/* 1、配置設定一個input_dev結構體 */
buttons_dev=input_allocate_device();
/* 2、設定結構體 */
/* 2.1、能産生按鍵類事件 */
set_bit(EV_KEY,buttons_dev->evbit);
/* 2.2、設定能産生按鍵類中哪些事件 :字母、數字、符号等等*/
/* 開發闆有四個按鍵這裡規定為 L、S、ENTER、LEFTSHIFT左邊的shift */
set_bit(KEY_L,buttons_dev->keybit);
set_bit(KEY_S,buttons_dev->keybit);
set_bit(KEY_ENTER,buttons_dev->keybit);
set_bit(KEY_LEFTSHIFT,buttons_dev->keybit);
/* 3、注冊*/
input_register_device(buttons_dev);
/* 4、硬體相關操作 */
/* 4.1、定時器初始化*/
init_timer(&button_timer);
button_timer.expires=;
button_timer.function=button_timer_func;
add_timer(&button_timer);
/* 4.2、注冊中斷 */
for(count=;count<;count++)
{
request_irq(mykey[count].eint,button_irq,IRQT_BOTHEDGE,mykey[count].name,&mykey[count]);
}
return ;
}
static void __exit button_exit(void)
{
unsigned char count=;
/* 删除定時器 */
del_timer(&button_timer);
/* 登出裝置 */
input_unregister_device(buttons_dev);
/* 釋放中斷 */
for(count=;count<;count++)
{
free_irq(mykey[count].eint,&mykey[count]);
}
/* 釋放申請的空間 */
input_free_device(buttons_dev);
}
module_init(button_init);
module_exit(button_exit);
MODULE_LICENSE("GPL");
安裝驅動
安裝驅動前、後變化
[root@JZ2440 inputsys]# ls /dev/event*
/dev/event
[root@JZ2440 inputsys]# insmod input_btn.ko
input: Unspecified device as /class/input/input2
[root@JZ2440 inputsys]# ls /dev/event*
/dev/event /dev/event1
[root@JZ2440 inputsys]# ls /dev/event* -l
crw-rw---- root root , Dec : /dev/event
crw-rw---- root root , Dec : /dev/event1
我們可以看到,安裝驅動前隻有一個裝置,安裝按鍵驅動後多了一個
event0
裝置,無疑該裝置就是我們剛才編寫的按鍵驅動裝置。檢視裝置編号,主裝置号
event1
測裝置号
13
65
.這是怎麼得來的呢?
能夠獲得子系統下的裝置編号,證明被輸入子系統識别認可了,執行
;時,注冊函數會将我們的按鍵加入裝置隊列,并與
input_register_device(buttons_dev)
中的
handler
進行比對,比對一緻,才能調用
id_table
函數進行裝置的注冊。我們先看下
connect
中的
evdev.c
是什麼
id_table
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,
};
在注釋中解釋允許所有裝置識别,是以我們的按鍵能夠被
evdev_ids
識别
evdev
static const struct input_device_id evdev_ids[] = {
{ .driver_info = }, /* Matches all devices */
{ }, /* Terminating zero entry */
};
接下來就進入 connect
函數進行裝置的注冊
static int evdev_connect(struct input_handler *handler, struct input_dev *dev,
const struct input_device_id *id)
{
struct evdev *evdev;
struct class_device *cdev;
dev_t devt;
int minor;
int error;
for (minor = ; minor < EVDEV_MINORS && evdev_table[minor]; minor++);
if (minor == EVDEV_MINORS) {
printk(KERN_ERR "evdev: no more free evdev devices\n");
return -ENFILE;
}
evdev = kzalloc(sizeof(struct evdev), GFP_KERNEL);
if (!evdev)
return -ENOMEM;
INIT_LIST_HEAD(&evdev->client_list);
init_waitqueue_head(&evdev->wait);
evdev->exist = ;
evdev->minor = minor;
evdev->handle.dev = dev;
evdev->handle.name = evdev->name;
evdev->handle.handler = handler;
evdev->handle.private = evdev;
sprintf(evdev->name, "event%d", minor);
evdev_table[minor] = evdev;
devt = MKDEV(INPUT_MAJOR, EVDEV_MINOR_BASE + minor),
cdev = class_device_create(&input_class, &dev->cdev, devt,
dev->cdev.dev, evdev->name);
if (IS_ERR(cdev)) {
error = PTR_ERR(cdev);
goto err_free_evdev;
}
/* temporary symlink to keep userspace happy */
error = sysfs_create_link(&input_class.subsys.kobj,
&cdev->kobj, evdev->name);
if (error)
goto err_cdev_destroy;
error = input_register_handle(&evdev->handle);
if (error)
goto err_remove_link;
return ;
err_remove_link:
sysfs_remove_link(&input_class.subsys.kobj, evdev->name);
err_cdev_destroy:
class_device_destroy(&input_class, devt);
err_free_evdev:
kfree(evdev);
evdev_table[minor] = NULL;
return error;
}
這裡
connect
函數主要功能是,該
evdev
裝置最大支援
32
個裝置,當有裝置注冊時将在
32
個次裝置号中尋找沒有配置設定的裝置号,當找到合适的次裝置号,就将合成裝置的裝置号,即主裝置号
13
,次裝置号為
64
+挑選出的次裝置号,裝置命名為
event
+挑選出的次裝置号。
測試
測試很簡單
cat /dev/tty1
依次按下L、S、ENTER鍵,螢幕顯示ls即可
怎樣實作鍵盤那種按住不放能列印一串字元的效果
//在裝置結構體的支援事件類型添加重複事件即可
set_bit(EV_REP,buttons_dev->evbit);