驱动层-input驱动

来源:互联网 发布:java方法无返回值 编辑:程序博客网 时间:2024/04/29 17:52

问题:MDrv_IR_Input_Init在哪里使用的?


一、驱动层编写了Mdrv_ir.c和一个map.c文件,mdrv是驱动文件,map.c是一个供mdrv驱动调用的一个键值映射表。

关于input驱动的基本知识可以参照博客:

linux驱动之input驱动

输入子系统设备驱动层实现原理
     在Linux中,Input设备用input_dev结构体描述,定义在input.h中。设备的驱动只需按照如下步骤就可实现了。
     1).在驱动模块加载函数中设置Input设备支持input子系统的哪些事件; 
     2).将Input设备注册到input子系统中; 
     3).在Input设备发生输入操作时(如:键盘被按下/抬起、触摸屏被触摸/抬起/移动、鼠标被移动/单击/抬起时等),提交所发生的事件及对应的键值/坐标等状态。


驱动设计流程如下图所示

 input子系统驱动设计流程

比如驱动中

在模块初始化函数MDrv_IR_Input_Init中做了以下工作:

(1)根据宏定义选择map文件名和要注册的input设备名以及一个vendor_id

    char *map_name = RC_MAP_TV;
    char *input_name = "TV IR Receiver";
    __u16 vendor_id = 0x0019;

(2)根据Linux内核版本定义设备类型

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 20)
    struct rc_dev *dev;
#elif LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 35)
    struct input_dev *dev;
#else
    struct input_dev *dev;
    IR_KEYTAB_TYPE *ir_codes = ir_codes_tv;
#endif

(3)为一个遥控器结构体分配内存

ir = kzalloc(sizeof(struct mstar_ir), GFP_KERNEL);

(4)分配一个输入设备

#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 20)
dev = rc_allocate_device();
#else
    dev = input_allocate_device();
#endif

(5)前面为ir分配了内存,这里初始化这个input设备。

这里的vendor_id及productid之类的可以用来区分是哪个设备,比如在Android的native层加载和这个设备相关的C++程序的时候,可以通过这些id来区分。

// init input device
    ir->dev = dev;

  dev->driver_name = MDRV_NAME_IR;
    dev->map_name = map_name;
    dev->driver_type = RC_DRIVER_IR_RAW;
    dev->input_name = input_name;
    dev->input_phys = "/dev/ir";
    dev->input_id.bustype = BUS_I2C;
    dev->input_id.vendor = vendor_id;
    dev->input_id.product = 0x0001;
    dev->input_id.version = 1;
    dev->allowed_protos = RC_TYPE_ALL;

(6)注册一个输入设备
     err = rc_register_device(dev);

(7)如果注册有问题就释放内存

if (err)
    {
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 20)
        rc_free_device(dev);
#else
        input_free_device(dev);
#endif
        kfree(ir);
        return err;
    }

(8)这个是对设备结构体进行原子操作

    // No auto-repeat.
#if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 0, 20)
    clear_bit(EV_REP, ir->dev->input_dev->evbit);
#else
    clear_bit(EV_REP, ir->dev->evbit);
#endif

原子整数操作描述void set_bit(int nr,void *addr)原子地设置addr所指对象的第nr位void clear_bit(int nr,void *addr)原子地清空addr所指对象的第nr位void change_bit(int nr,void *addr)原子地翻转addr所指对象的第nr位int test_and_set_bit(int nr,void *addr)原子地设置addr所指对象的第nr位,并返回原先的值int test_and_clear_bit(int nr,void *addr)原子地清空addr所指对象的第nr位,并返回原先的值int test_and_change_bit(int nr,void *addr)原子地翻转addr所指对象的第nr位,并返回原先的值int test_bit(int nr,void *addr)原子地返回addr所指对象的第nr位(9)调用初始化key_completion结构体

init_completion(&key_completion);

(10)创建一个workqueue队列并执行这个任务

key_dispatch_workqueue = create_workqueue("keydispatch_wq");

queue_work(key_dispatch_workqueue, &key_dispatch_thread);

Workqueue编程接口

序号

接口函数

说明

1

create_workqueue

用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数:

@name:workqueue的名称

2

create_singlethread_workqueue

用于创建workqueue,只创建一个内核线程。输入参数:

@name:workqueue名称

3

destroy_workqueue

释放workqueue队列。输入参数:

@ workqueue_struct:需要释放的workqueue队列指针

4

schedule_work

调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue——keventd_wq输入参数:

@ work_struct:具体任务对象指针

5

schedule_delayed_work

延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,输入参数:

@work_struct:具体任务对象指针

@delay:延迟时间

6

queue_work

调度执行一个指定workqueue中的任务。输入参数:

@ workqueue_struct:指定的workqueue指针

@work_struct:具体任务对象指针

7

queue_delayed_work

延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。



在模块注销函数MDrv_IR_Input_Exit()中做了以下工作:

(1)释放workqueue队列

destroy_workqueue(key_dispatch_workqueue);

(2)注销输入设备


input_free_device(ir->dev);

(3)释放输入设备

 kfree(ir);

事件上报:

驱动程序通过在MDrv_IR_Init中注册中断实现,每次发生中断的时候就会触发_MDrv_IR_ISR函数,这样子,就能对键值进行处理了

  result = request_irq(E_FIQ_IR, _MDrv_IR_ISR, SA_INTERRUPT, "IR", NULL);->MDrv_IR_ISRParseKey->_MDrv_IR_GetKey

事件支持

Set_bit告诉input子系统它支持哪些事件 
Set_bit(EV_KEY,button_dev.evbit) 
Struct input_dev中有两个成员,一个是evbit;一个是keybit.分别用来表示设备所支持的事件类型和按键类型。


事件类型linux/input.h:

/*
 * Event types
 */
#define EV_FACTKEYREADER 0x19 自定义键值类型,键值过滤的时候可以根据键值类型来进行过滤
#define EV_SYN 0x00 同步事件
#define EV_KEY 0x01 按键事件
#define EV_REL 0x02 相对坐标
#define EV_ABS 0x03 绝对坐标
#define EV_MSC 0x04  其它
#define EV_SW 0x05
#define EV_LED 0x11  LED
#define EV_SND 0x12 声音
#define EV_REP 0x14 Repeat 
#define EV_FF 0x15 力反馈
#define EV_PWR 0x16
#define EV_FF_STATUS 0x17
#define EV_MAX 0x1f
#define EV_CNT (EV_MAX+1)
报告事件
Void input_event(struct input_dev *dev,unsigned int type,unsigned int code,int value);//报告指定type,code的输入事件
Void input_report_key(struct input_dev *dev,unsigned int code,int value);//报告键值 
Void input_report_rel(struct input_dev *dev,unsigned int code,int value);//报告相对坐标
Void input_report_abs(struct input_dev *dev,unsigned int code,int value);//报告绝对坐标
Void input_sync(struct input_dev *dev);//报告同步事件 

在触摸屏驱动设计中,一次坐标及按下状态的整个报告过程如下: 
Input_report_abs(input_dev,ABS_X,x);//X坐标 
Input_report_abs(input_dev,ABS_Y,y);//Y坐标 
Input_report_abs(input_dev,ABS_PRESSURE,pres);//压力 
input_sync(struct input_dev *dev);//同步 

释放与注销设备
Void input_free_device(struct input_dev *dev); 
Void input_unregister_device(struct input_dev *); 

在内核中,input_dev表示一个input设备,

                    input_handler表示input设备的接口

        linux内核的drivers/input/keyboard/gpio_keys.c基于input架构实现了一个通用的GPIO按键驱动。该驱动基于platform_driver架构,名为“gpio-keys”。它将硬件相关信息屏蔽在板文件platform_device的platform_data中,因此驱动可应用于各个处理器,具有良好的跨平台性。

0 0
原创粉丝点击