input模型之加速传感器(accelerometer)驱动浅析

来源:互联网 发布:asp 防sql注入 编辑:程序博客网 时间:2024/05/29 00:30

input模型之accelerometer驱动浅析

这个驱动可是看了好长时间了,现在总结一下,只是个人理解,如有错误欢迎指正,废话就不说了啊,现在开始了

 

一:硬件的加载

分心硬件驱动之前首先需要把硬件和驱动加载到系统中,也就是系统在启动的时候加载这些东东(我的这个代码是基于x86架构的,硬件是加速传感器:属于input设备,即accelerometer sensor)

Kernel/arch/x86/platform/intel-mid/device_libs/platform_lsm303dlh_acc.c

 

static struct i2c_board_info __initdatalsm303dlh_acc_i2c_board_info = {

    I2C_BOARD_INFO("LSMAC303:00", 0x19),

};

 

static int __initlsm303dlh_acc_platform_init(void)

{

    return i2c_register_board_info(5, &lsm303dlh_acc_i2c_board_info, 1);

}

module_init(lsm303dlh_acc_platform_init);

 

i2c_board_info是要注册的设备信息,其中"LSMAC303:00"是设备的名称,0x19是设备的地址,最后通过系统函数i2c_register_board_info来注册。那么已经加载的内核是如何和驱动匹配的呢,是很俗的方法就是通过名字来匹配的。

 

二:驱动浅析

具体的驱动分析之前我们先大致了解一下input设备驱动的原理(个人见解,勿喷)


上图为输入子系统的运行结构,如上图所示,输入子系统又分为设备驱动程序和事件驱动程序,事件驱动程序是负责和用户空间的交互程序(具体一点就是上报input_event给用户空间),而设备驱动程序是负责底层输入设备的管理及与底层输入设备的通信,后获得的数据后,上报数据时就交给时间驱动程序来处理

 

首先我们先找到驱动的具体位置,本例的位置是在:

Kernel/drivers/input/misc/lsm303dlhc_acc.c

static struct i2c_driverlsm303dlhc_acc_driver = {

.driver = {

.owner = THIS_MODULE,

.name = LSM303DLHC_ACC_DEV_NAME,

  },

.probe =lsm303dlhc_acc_probe,

.remove =lsm303dlhc_acc_remove,

.suspend =lsm303dlhc_acc_suspend,

.resume =lsm303dlhc_acc_resume,

.id_table =lsm303dlhc_acc_id,

};

 

static int __init lsm303dlhc_acc_init(void)

{

pr_info("%saccelerometer driver: init\n",

LSM303DLHC_ACC_DEV_NAME);

returni2c_add_driver(&lsm303dlhc_acc_driver);

}

 

static void __exitlsm303dlhc_acc_exit(void)

{

 

pr_info("%saccelerometer driver exit\n",

LSM303DLHC_ACC_DEV_NAME);

 

i2c_del_driver(&lsm303dlhc_acc_driver);

return;

}

 

module_init(lsm303dlhc_acc_init);

module_exit(lsm303dlhc_acc_exit);

我们从下往上看代码首先是调用函数module_init来初始化驱动模块,调用的是lsm303dlhc_acc_init, lsm303dlhc_acc_init函数就是初始化具体的我们的加速传感器驱动的,调用系统函数i2c_add_driver把具体的驱动加入到系统中,而驱动结构体的初始化如代码所示,我们这里看一下

* @class: What kind of i2c device weinstantiate (for detect)

 * @attach_adapter: Callback for bus addition(deprecated)

 * @probe: Callback for device binding加载驱动是需要调用的函数

 * @remove: Callback for device unbinding

 * @shutdown: Callback for device shutdown

 * @suspend: Callback for device suspend

 * @resume: Callback for device resume

 * @alert: Alert callback, for example for theSMBus alert protocol

 * @command: Callback for bus-wide signaling(optional)

 * @driver: Device driver model driver

 * @id_table: List of I2C devices supported bythis driver

 * @detect: Callback for device detection

 * @address_list: The I2C addresses to probe(for detect)

 * @clients: List of detected clients wecreated (for i2c-core use only)

struct i2c_driver{

unsigned int class;

int (*attach_adapter)(struct i2c_adapter *)__deprecated;

int (*probe)(struct i2c_client *, const structi2c_device_id *);

int (*remove)(struct i2c_client *);

void (*shutdown)(struct i2c_client *);

int (*suspend)(struct i2c_client *, pm_message_tmesg);

int (*resume)(struct i2c_client *);

void (*alert)(struct i2c_client *, unsigned int data);

int (*command)(struct i2c_client *client, unsigned intcmd, void *arg);

struct device_driver driver;

const struct i2c_device_id *id_table;

int (*detect)(struct i2c_client *, structi2c_board_info *);

const unsigned short *address_list;

struct list_head clients;

};

具体的需要自己看,我只标记了主要的函数

下面就看一下具体驱动程序中的prope函数

static intlsm303dlhc_acc_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

//设备状态信息结构体

struct lsm303dlhc_acc_status *stat;   1

u32 smbus_func = I2C_FUNC_SMBUS_BYTE_DATA |

I2C_FUNC_SMBUS_WORD_DATA |I2C_FUNC_SMBUS_I2C_BLOCK ;

int err = -1;

dev_info(&client->dev, "probestart.\n");//系统函数报告信息

//申请内存空间

stat = kzalloc(sizeof(struct lsm303dlhc_acc_status),GFP_KERNEL);

if (stat == NULL) {

err = -ENOMEM;

dev_err(&client->dev,

"failed to allocate memory for moduledata: "

"%d\n", err);

goto exit_check_functionality_failed;

}

/* Support for both I2C and SMBUS adapter interfaces.*/

stat->use_smbus = 0;

//检测设备是不是i2c设备

if (!i2c_check_functionality(client->adapter,I2C_FUNC_I2C)) {

dev_warn(&client->dev, "clientnot i2c capable\n");

if (i2c_check_functionality(client->adapter,smbus_func)){

stat->use_smbus = 1;

dev_warn(&client->dev, "clientusing SMBUS\n");

} else {

err = -ENODEV;

dev_err(&client->dev, "clientnor SMBUS capable\n");

goto exit_check_functionality_failed;

}

}

       //初始化互斥锁,打开互斥锁

mutex_init(&stat->lock);

mutex_lock(&stat->lock);

 

       //把设备结构体赋给stat

stat->client = client;

//把stat赋给client中的device中的device_private中driver_date(空指针类型)

i2c_set_clientdata(client, stat);

 

stat->pdata = kmalloc(sizeof(*stat->pdata),GFP_KERNEL);

if (stat->pdata == NULL) {

err = -ENOMEM;

dev_err(&client->dev,

"failed to allocate memory for pdata:%d\n",

err);

goto err_mutexunlock;

}

 

       //给stat中的平台信息platform_data赋值

if (client->dev.platform_data == NULL) {

default_lsm303dlhc_acc_pdata.gpio_int1 =int1_gpio;

default_lsm303dlhc_acc_pdata.gpio_int2 =int2_gpio;

memcpy(stat->pdata,&default_lsm303dlhc_acc_pdata,

sizeof(*stat->pdata));

dev_info(&client->dev, "usingdefault plaform_data\n");

} else {

memcpy(stat->pdata,client->dev.platform_data,

sizeof(*stat->pdata));

}

 

       //验证设备的平台信息

err = lsm303dlhc_acc_validate_pdata(stat);

if (err < 0) {

dev_err(&client->dev, "failedto validate platform data\n");

goto exit_kfree_pdata;

}

 

       //下面是对平台信息的一些处理

if (stat->pdata->init) {

err = stat->pdata->init();

if (err < 0) {

dev_err(&client->dev, "initfailed: %d\n", err);

goto err_pdata_init;

              }

       }

 

       if(stat->pdata->gpio_int1 >= 0){

              stat->irq1 =gpio_to_irq(stat->pdata->gpio_int1);

              pr_info("%s: %s has set irq1to irq: %d, "

                                                 "mappedon gpio:%d\n",

                     LSM303DLHC_ACC_DEV_NAME,__func__, stat->irq1,

                                                 stat->pdata->gpio_int1);

       }

 

       if(stat->pdata->gpio_int2 >= 0){

              stat->irq2 =gpio_to_irq(stat->pdata->gpio_int2);

              pr_info("%s: %s has set irq2to irq: %d, "

                                                 "mappedon gpio:%d\n",

                     LSM303DLHC_ACC_DEV_NAME, __func__,stat->irq2,

stat->pdata->gpio_int2);

       }

 

       memset(stat->resume_state, 0,ARRAY_SIZE(stat->resume_state));

 

       stat->resume_state[RES_CTRL_REG1] =(ALL_ZEROES |

                                   LSM303DLHC_ACC_ENABLE_ALL_AXES);

       stat->resume_state[RES_CTRL_REG4] =(ALL_ZEROES | CTRL_REG4_BDU_ENABLE);

 

       //给设备加电

       err =lsm303dlhc_acc_device_power_on(stat);

       if (err < 0) {

              dev_err(&client->dev,"power on failed: %d\n", err);

              goto err_pdata_init;

       }

 

       atomic_set(&stat->enabled, 1);

 

       err =lsm303dlhc_acc_update_fs_range(stat, stat->pdata->fs_range);

       if (err < 0) {

              dev_err(&client->dev,"update_fs_range failed\n");

              goto  err_power_off;

       }

 

       err = lsm303dlhc_acc_update_odr(stat,stat->pdata->poll_interval);

       if (err < 0) {

              dev_err(&client->dev,"update_odr failed\n");

              goto  err_power_off;

       }

      

       //input设备的初始化,也就是本文讲到的重点

       err = lsm303dlhc_acc_input_init(stat);

       if (err < 0) {

              dev_err(&client->dev,"input init failed\n");

              goto err_power_off;

       }

 

 

       //创建一些sys系统文件接口

       err =create_sysfs_interfaces(&client->dev);

       if (err < 0) {

              dev_err(&client->dev,

                "device LSM303DLHC_ACC_DEV_NAME sysfs register failed\n");

              goto err_input_cleanup;

       }

 

 

       lsm303dlhc_acc_device_power_off(stat);

 

下面看一下input设备的初始化



首先看INIT_DELAYED_WORK()这个函数,执行lsm303dlhc_acc_input_work_func函数的时机和频率,也就是上报数据的时机和频率

里面有个函数体lsm303dlhc_acc_input_work_func


我们这个函数的功能是获得加速器的XYZ的值并上报数据

lsm303dlhc_acc_get_acceleration_data函数是获得xyz的值,lsm303dlhc_acc_report_values就是用来上报数据的,它的操作就是交给evdev即事件驱动程序来处理了


第一条语句产生了ABS_X事件,即设备在x轴的相对移动,经过evdev处理之后,evdev产生的每个时间包都是如下格式

/kernel/include/uapi/linux/input.h

struct input_event{

       struct timeval time;

       __u16 type;

       __u16 code;

       __s32 value;

};

第二条第三条语句也都是如此input_sync()时表示三个事件报告都已做完

 

stat->input_dev->open= lsm303dlhc_acc_input_open;

       stat->input_dev->close =lsm303dlhc_acc_input_close;

这两句是入口函数,这些函数直接对应相应的系统调用,用户应用程序通过sys/中的某些属性文件节点调用


如上图,是我们的板子上的具体的该加速设备的设备文件input0,属性文件enable就是给用户空间用来控制input设备的打开和关闭的,往enable文件写1就打开设备,对应上面的lsm303dlhc_acc_input_open,写0就关闭设备,对应上面的lsm303dlhc_acc_input_close。

(顺序有点乱啊)

下面接着看,就该

/* register device withinput core */(该函数的作用)

intinput_register_device(struct input_dev *dev)

{

       static atomic_t input_no =ATOMIC_INIT(0);

       struct input_devres *devres = NULL;

       struct input_handler *handler;

       unsigned int packet_size;

       const char *path;

       int error;

 

       if (dev->devres_managed) {

              devres =devres_alloc(devm_input_device_unregister,

                                  sizeof(struct input_devres), GFP_KERNEL);

              if (!devres)

                     return -ENOMEM;

 

              devres->input = dev;

       }

 

       /* Every input device generatesEV_SYN/SYN_REPORT events. */

       __set_bit(EV_SYN, dev->evbit);

 

       /* KEY_RESERVED is not supposed to betransmitted to userspace. */

       __clear_bit(KEY_RESERVED,dev->keybit);

 

       /* Make sure that bitmasks not mentionedin dev->evbit are clean. */

       input_cleanse_bitmasks(dev);

 

       packet_size =input_estimate_events_per_packet(dev);

       if (dev->hint_events_per_packet <packet_size)

              dev->hint_events_per_packet =packet_size;

 

       dev->max_vals =max(dev->hint_events_per_packet, packet_size) + 2;

       dev->vals = kcalloc(dev->max_vals,sizeof(*dev->vals), GFP_KERNEL);

       if (!dev->vals) {

              error = -ENOMEM;

              goto err_devres_free;

       }

 

       /*

        *If delay and period are pre-set by the driver, then autorepeating

        *is handled by the driver itself and we don't do it in input.c.

        */

       init_timer(&dev->timer);

       if (!dev->rep[REP_DELAY] &&!dev->rep[REP_PERIOD]) {

              dev->timer.data = (long) dev;

              dev->timer.function =input_repeat_key;

              dev->rep[REP_DELAY] = 250;

              dev->rep[REP_PERIOD] = 33;

       }

 

       if (!dev->getkeycode)

              dev->getkeycode =input_default_getkeycode;

 

       if (!dev->setkeycode)

              dev->setkeycode =input_default_setkeycode;

 

       dev_set_name(&dev->dev,"input%ld",

                   (unsigned long)atomic_inc_return(&input_no) - 1);

 

       error = device_add(&dev->dev);

       if (error)

              goto err_free_vals;

 

       path = kobject_get_path(&dev->dev.kobj,GFP_KERNEL);

       pr_info("%s as %s\n",

              dev->name ? dev->name :"Unspecified device",

              path ? path : "N/A");

       kfree(path);

 

       error =mutex_lock_interruptible(&input_mutex);

       if (error)

              goto err_device_del;

 

       list_add_tail(&dev->node,&input_dev_list);

 

       //遍历handler链表,调用input_attach_handler函数与handler进行匹配

       list_for_each_entry(handler,&input_handler_list, node)

              input_attach_handler(dev,handler);

//遍历input_handler_list列表,找到与input_dev对应的input_handler,就是所谓的配对,为以后向用户空间上报数据包做准备

 

       input_wakeup_procfs_readers();

 

       mutex_unlock(&input_mutex);

 

       if (dev->devres_managed) {

              dev_dbg(dev->dev.parent,"%s: registering %s with devres.\n",

                     __func__,dev_name(&dev->dev));

              devres_add(dev->dev.parent,devres);

       }

       return 0;

 

err_device_del:

       device_del(&dev->dev);

err_free_vals:

       kfree(dev->vals);

       dev->vals = NULL;

err_devres_free:

       devres_free(devres);

       return error;

}

从下到下先是对input_dev的结构体进行初始化,最后通过device_add函数把设备添加到系统中,并生成设备文件和相关的属性文件

 

最后看一下上报的数据是如何交付给handler来处理的,看一个实例吧

input_report_abs(stat->input_dev,ABS_X, xyz[0]);//上报X轴移动的绝对值的事件

/kernle/include/lilnux/input.h


/kernel/drivers/input/input.c

input_event()- report new input event * @dev: device that generated the event

 * @type: type of the event * @code: eventcode* @value: value of the event


/kernel/drivers/input/input.c


接着看input_pass_values()函数


Input_to_handler()


最后调用handler的event()方法来处理上报的数据

/kernel/drivers/input/evdev.c


接着看event()方法的具体实现就是evdev_event


接着调用evdev_events方法

接着就是调用evdev_pass_values上报数据了


看到没?终于看到了input_event了,他就是最终我们上报的数据包了。

_pass_event(client,&event);


0 0