天天看點

基于JZ2440按鍵輸入子系統代碼實作

簡介

自己寫驅動流程
  • 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);
           

繼續閱讀