G-sensor驱动分析

重力传感器代码分析

重力传感器驱动的功能,主要是向HAL层提供IOCTRL接口,并通过input设备上报数据。芯片实际数据的读取是采用i2c协议读取原始数据,并且作为i2c设备挂载在系统上工作的。

1、调用关系

采用模块化的编程方式,一下介绍函数的调用关系。

module_init(aac_MMAxxxxx_init);

module_exit(aac_MMAxxxxx_exit);

模块中定义了驱动初始化和退出函数,具体实现如下

static int __init aac_MMAxxxxxFC_init(void)

{

int ret;

if ((ret = i2c_add_driver(&aac_MMAxxxxxFC_i2c_driver))) {

printk(KERN_WARNING "aac_MMAxxxxxFC_init failed. /n");

return ret;

}

return ret;

}

static void  __exit aac_MMAxxxxxFC_exit(void)

{

i2c_del_driver(&aac_MMAxxxxxFC_i2c_driver);

}

调用i2c_add_driver函数将aac_MMAxxxxxFC_i2c_driver驱动添加实现了初始化函数,exit函数则调用i2c_del_driver函数将aac_MMAxxxxxFC_i2c_driver驱动删除。

对于aac_MMAxxxxxFC_i2c_driver驱动结构体,由6个参数实现该结构体。Driver用模块名来填充,probe、remove、suspend、resume分别用相对应的函数来填充。Id_table用aac_ MMAxxxxxFC_id来填充索引表。实现代码如下:

static struct i2c_driver aac_MMAxxxxxFC_i2c_driver = {

.driver = {

.name = MMAxxxxx_MODULE_NAME,

},

.probe = aac_MMAxxxxxFC_probe,

.remove = aac_MMAxxxxxFC_remove,

.id_table   = aac_MMAxxxxxFC_id,

#ifndef CONFIG_HAS_EARLYSUSPEND

.suspend  = mmaxxxxx_suspend,

.resume   = mmaxxxxx_resume,

#endif

};

1.1、   对于结构体中索引表aac_MMAxxxxxFC_id数组,具体实现如下:

static const struct i2c_device_id aac_MMAxxxxxFC_id[] = {

{ MMAxxxxx_MODULE_NAME, 0 },

{ }

};

1.2、   MMAxxxxx_MODULE_NAME表示驱动名。在头文件中定义了具体名字"mmaxxxxx"。

1.3、   aac_MMAxxxxxFC_probe函数是i2c驱动寻找设备的经典实现,这里将具体分析下实现过程。实现思路是首先注册i2c功能函数类型,然后分配misc设备空间并注册,接下来分配输入设备空间并注册,注意将misc设备获取数据传给input设备数据中。最后创建工作队列,实现位置信息数据处理。

具体代码如下:

1.3.1调用i2c_check_functionality,函数返回我们需要类型的i2c适配器

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

ret = -ENODEV;

goto exit0;

}

并且将入口函数参数client赋值给静态全局变量g_client

g_client = client;

1.3.2调用kzalloc函数给空结构体赋值,空结构体的意义在于寻址。调用sysfs_create_group函数将设备体创建到mmaxxxxx_attr_group组,主要为调试使用,关于具体调试文件系统将在文章后面章节介绍。调用misc_register函数注册misc设备mmaxxxxx_misc_device。

mmaxxxxx_misc_data = kzalloc(sizeof(struct mmaxxxxx_data), GFP_KERNEL);

if (!mmaxxxxx_misc_data) {

ret = -ENOMEM;

goto exit1;

}

//init sysfs entry

ret = sysfs_create_group(&client->dev.kobj, &mmaxxxxx_attr_group);

if (ret)

goto exit2;

//misc_register

ret = misc_register(&mmaxxxxx_misc_device);

if (ret < 0) {

dev_err(&client->dev, "mmaxxxxx_device register failed/n");

goto exit3;

}

1.3.3调用input_allocate_device函数给input设备分配空间

//input_allocate

input = input_allocate_device();

if (!input) {

ret = -ENOMEM;

printk("input device allocate failed/n");

goto exit4;

}

对input各个属性项目填充,name、phys表示映射的物理端口、id.bustype、id.vendor、id.product、

input->name = "mmaxxxxx";

input->phys = "mmaxxxxx/input0";

input->id.bustype = BUS_HOST;

input->id.vendor = 0x0001;

input->id.product = 0x0001;

input->id.version = 0x0100;

//evbit选择了事件类型,absbit表示了绝对值的数据

input->evbit[0] = BIT(EV_ABS);

input->absbit[0] = BIT(ABS_X) | BIT(ABS_Y) | BIT(ABS_Z);

//将input赋值给全局变量g_input_dev设备。

g_input_dev = input;

ret = input_register_device(g_input_dev);

if (ret) {

printk("unable to register input polled device /n");

goto exit5;

}

//把misc设备数据赋值给输入设备系统数据

input_set_drvdata(g_input_dev, mmaxxxxx_misc_data);

1.3.4调用init_MUTEX函数将信号量申请为互斥体信号,在分别在mmaxxxxx_misc_ioctl、asensor_thread、mmaxxxxx_resume函数中调用,确保时间能够安全赋值。

然后创建工作队列asensor_wq,调用INIT_DELAYED_WORK函数asensor_thread数据处理函数添加到工作任务asensor_delayed_work当中。注意,此时工作队列并没有开始工作,需要mmaxxxxx_misc_ioctl接到HAL层的控制信息时才打开端口,进行工作。

init_MUTEX(&sem_thread);

asensor_wq = create_workqueue("MMAxxxxxFC_workqueue");

if (!asensor_wq) {

printk("can‘t create a workqueue/n");

ret = -1;

goto exit6;

}

INIT_DELAYED_WORK(&asensor_delayed_work, asensor_thread);

1.4、   aac_MMAxxxxxFC_remove函数是作为probe函数的反过程实现的,主要是取消工作队列和释放相关资源。

具体代码实现如下

ret = cancel_delayed_work(&asensor_delayed_work);

if(ret == 0){

flush_workqueue(asensor_wq);

}

destroy_workqueue(asensor_wq);

asensor_wq = NULL;

#ifdef CONFIG_HAS_EARLYSUSPEND

unregister_early_suspend(&early_suspend);

#endif

//clean input

mmaxxxxx_input_cleanup();

//sysfs

sysfs_remove_group(&client->dev.kobj, &mmaxxxxx_attr_group);

//misc

misc_deregister(&mmaxxxxx_misc_device);

1.5、   mmaxxxxx_suspend和mmaxxxxx_resume函数主要是睡眠和唤醒时作用,关掉外设电源并释放占用的相关资源。唤醒时实现反过程。

static int mmaxxxxx_suspend(struct i2c_client *ic, pm_message_t mesg)

{

printk("########mmaxxxxx_suspend");

cancel_delayed_work_sync(&asensor_delayed_work);

aac_MMAxxxxxFC_close_client();

return 0;

}

static int mmaxxxxx_resume(struct i2c_client *ic)

{

printk("########mmaxxxxx_resume");

aac_MMAxxxxxFC_init_client();

down_interruptible(&sem_thread);

time_delay.tv_sec = 0;

time_delay.tv_usec = read_interval;

up(&sem_thread);

if(1 == thread_flag)

{

printk("########resume!/n");

queue_delayed_work(asensor_wq, &asensor_delayed_work, timeval_to_jiffies(&time_delay));

}

return 0;

}

2.     特殊处理函数

2.1 misc控制函数

主要作用是处理HAL层的IOCTL命令,起到打开、关闭的任务。

首先定义了混杂设备结构体mmaxxxxx_misc_device,该结构体体由3个field组成,第一个表示misc设备的此设备号,第二个为misc设备的名字,第三个为misc操作结构体。操作结构体由我们自行定义。

static struct miscdevice mmaxxxxx_misc_device = {

.minor = MISC_DYNAMIC_MINOR,

.name = "mmaxxxxx",

.fops = &mmaxxxxx_misc_fops,

};

然后定义Misc操作结构体,该结构体由3个field组成,第一个表示所有者,属性固定为本模块,即THIS_MODULE。第二个表示打开函数,处理数据信息,第三个表示控制函数,处理misc设备的相关控制命令。

static const struct file_operations mmaxxxxx_misc_fops = {

.owner = THIS_MODULE,

.open =  mmaxxxxx_misc_open,

.ioctl = mmaxxxxx_misc_ioctl,

};

作为传感器输入设备,打开函数使用的也是数据流,所以定位数据没有意义。这种情况下,不能简单不声明lseek操作,因为默认方法是允许定位的。默认定位的方法是调用lseek函数在数据区往上或往下定位数据。在open方法中调用nonseekable_open()时,它会通知内核设备不支持lseek。

函数实现如下:

static int mmaxxxxx_misc_open(struct inode *inode, struct file *file)

{

int err;

err = nonseekable_open(inode, file);

if (err < 0)

return err;

file->private_data = mmaxxxxx_misc_data;

return 0;

}

这里注意的是文件的私有数据赋值对象为mmaxxxxx_misc_data,是一个空结构体变量。难道也仅仅是为了寻址么?

Ioctl函数作为misc设备核心的操作函数,主要作用是通过HAL层中相关command字的控制,给应用层提供了控制方法,最终实现设备体的状态获取,延时,激活,关闭,如匹配字不符合,则控制参数有误退出。

Ioctl函数中主要包括的控制命令为MMAxxxxx_IOCTL_GET_STATE、MMAxxxxx_IOCTL_SET_DELAY、MMAxxxxx_IOCTL_ENABLE、MMAxxxxx_IOCTL_DISABLE几个命令。

具体可参考代码。如MMAxxxxx_IOCTL_GET_STATE中主要通过copy_to_user将线程标示位赋值给参数argp,从而获取状态。其他几个具体参考代码。

static int mmaxxxxx_misc_ioctl(struct inode *inode, struct file *file,

unsigned int cmd, unsigned long arg)

{

void __user *argp = (void __user *)arg;

int32_t interval;

switch( cmd )

{

case MMAxxxxx_IOCTL_GET_STATE:

{

if(copy_to_user(argp, &thread_flag, sizeof(thread_flag)))

return -EFAULT;

//printk("MMAxxxxx_IOCTL_GET_STAT/n");

break;

}

case MMAxxxxx_IOCTL_ENABLE:

{

aac_MMAxxxxxFC_init_client();

thread_flag = 1;

down_interruptible(&sem_thread);

time_delay.tv_sec = 0;

time_delay.tv_usec = read_interval;

up(&sem_thread);

queue_delayed_work(asensor_wq, &asensor_delayed_work, timeval_to_jiffies(&time_delay));

//printk("MMAxxxxx_IOCTL_ENABLE/n");

break;

}

}

}

在MMAxxxxx_IOCTL_ENABLE控制命令下,通过原子操作定义了延时的时间,将工作任务asensor_delayed_work添加到工作队列asensor_wq中,这样就循环开始了该工作。

2.2 工作函数asensor_thread

工作函数中主要是通过i2c线读取相关的输出数据。I2c读取的方式这里不再详述,这里主要通过调用i2c_smbus_read_i2c_block_data函数,读取连续三个地址的数值,通过数据处理,根据硬件相关的贴片方式,输出正确的xyz结果。

处理过的结果用自定义的结构体保存。

struct _mmaxxxxx_data{

int  x_data;

int  y_data;

int  z_data;

};

这里有个需要处理的地方就是有些芯片灵敏度过高,可以通过滤波算法进行相关的去抖动处理。具体参考后续文章。

3   调试信息控制文件接口

这里通过static int g_print = 0来实现是否输出打印信息,介绍相关知识之前,需要先了解linux内核中sys文件系统的介绍。sysfs 属性的功能只能靠阅读源代码来理解。在内核中, sysfs 属性一般是由 __ATTR 系列的宏来声明的,如对设备的使用 DEVICE_ATTR ,对总线使用 BUS_ATTR ,对驱动使用 DRIVER_ATTR ,对类别(class)使用  CLASS_ATTR, 这四个高级的宏来自于 <include/linux/device.h>, 都是以更低层的来自 <include/linux/sysfs.h> 中的 __ATTR/__ATRR_RO 宏实现;因此我们在内核源码树中相应位置 drivers/scsi/ 找到这几个宏的使用情况,可以得到在 drivers/scsi/scsi_sysfs.c 中。

下面通过代码介绍DEVICE_ATTR的添加过程。

3.1定义控制变量

static int g_print = 0;

3.2定义mmaxxxxx_show_print函数

功能主要是将g_print打印到内存当中。

static ssize_t mmaxxxxx_show_print(struct device *dev,

struct device_attribute *attr, char *buf)

{

return sprintf(buf, "%d/n", g_print);

}

3.3定义mmaxxxxx_store_print函数

功能主要是获取buf中存在的控制值。

static ssize_t mmaxxxxx_store_print(struct device *dev,

struct device_attribute *attr, char *buf,size_t count)

{

unsigned long val = simple_strtoul(buf, NULL, 10);

//adjust_light(val);

//ggg=val;

g_print = val;

return count;

}

3.4填充设备属性DEVICE_ATTR

static DEVICE_ATTR(print, S_IWUSR | S_IRUGO,mmaxxxxx_show_print, mmaxxxxx_store_print);

其中有四个参数,分别表示是称、权限位、读函数、写函数。

3.5属性数组mmaxxxxx_attributes

主要是填充设备属性位置。

static struct attribute *mmaxxxxx_attributes[] = {

&dev_attr_print.attr,

NULL

};

3.6将属性数组加到属性组(group)里。

static const struct attribute_group mmaxxxxx_attr_group = {

.attrs = mmaxxxxx_attributes,

};

至此完成了属性组的添加工作,通过adb连接去硬件系统中对应的文件为sys/devices/i2c-0/x-xxxx/print,x-xxxx对应的是芯片的地址线。

4    总结

文章中采用标准模块化得方法,调用内核函数,将i2c模块挂载到内核系统当中,并通过misc设备留接口给上层提供调用。在模块工作过程中,通过i2c读函数获取了实时的位置信息,并通过input设备将数据上报给用户层。

时间: 2024-08-05 14:59:06

G-sensor驱动分析的相关文章

Linux SD/MMC/SDIO驱动分析_转

转自:Linux SD/MMC/SDIO驱动分析    https://www.cnblogs.com/cslunatic/p/3678045.html#3053341 一.SD/MMC/SDIO概念区分 SD(SecureDigital)与 MMC(MultimediaCard) SD 是一种 flash memory card 的标准,也就是一般常见的 SD 记忆卡,而 MMC 则是较早的一种记忆卡标准,目前已经被 SD 标准所取代.在维基百科上有相当详细的 SD/MMC 规格说明:[htt

20150311 NandFlash驱动分析

20150311 NandFlash驱动分析 2015-03-11 李海沿 一.结构体详解 MTD体系结构: 在linux中提供了MTD(Memory Technology Device,内存技术设备)系统来建立Flash针对linux的统一.抽象的接口 引入MTD后,linux系统中的Flash设备驱动及接口可分为4层: 设备节点 MTD设备层 MTD原始设备层 硬件驱动层 硬件驱动层:Flash硬件驱动层负责底层硬件设备实际的读.写.擦除,Linux MTD设备的NAND型Flash驱动位于

linux串口驱动分析

linux串口驱动分析 硬件资源及描写叙述 s3c2440A 通用异步接收器和发送器(UART)提供了三个独立的异步串行 I/O(SIO)port,每一个port都能够在中断模式或 DMA 模式下操作.UART 使用系统时钟能够支持最高 115.2Kbps 的波特率.每一个 UART 通道对于接收器和发送器包含了 2 个 64 位的 FIFO. 寄存器 名称 地址 在linux中的描写叙述 (2410 和 2440 处理器对内存地址映射关系同样) UART 线性控制寄存器(ULCONn) ULC

CentOS 6.6下 BCM4312 802.11b/g无线网卡驱动安装

安装基本环境: dell 1440 uname - r  2.6.32-504.el6.i686 lspci |grep Network  # BCM4312 lspci -n |grep 14e4 # 14e4:4315 ############################### hybrid-portsrc_x86_32-v5_100_82_38.tar.gz下载地址在 免费下载地址在 http://linux.linuxidc.com/ 用户名与密码都是www.linuxidc.com

atheros wifi 驱动分析

Ar6003 驱动文档摘要 1.  wmi : wireless module interface //无线模块结构 2.  bmi : bootloader message interface 3.  htc : host target communications 4.  wps:wifi protected setup 5.  CS:connection services module 6.  STA:station 7.  AP:access point Wireless applica

蓝牙驱动分析 linux

蓝牙驱动分析 这个驱动分析的是OK6410开发板自带的内核版本是linux3.0.1,所支持的wifi和蓝牙一体芯片是marvell的8688和8787.根据开发板的设计,芯片与主机之间是通过sdio协议接口通信的,所以驱动也是通过sdio的方式写的. 个人分析驱动的过程是从插入设备驱动的动作开始的. 首先每次插入设备和拔出设备驱动都会通过终端打印相应的信息,判断在sd卡槽中肯定是触发中断的,通过看硬件原理图和数据手册中的SDMMC控制器可知用于mmc的中断号分别为56和57,回到代码中.在内核

[国嵌攻略][140][触摸屏驱动分析]

触摸屏驱动分析 初始化 1.使能ADC时钟 2.将物理地址转化为虚拟地址 3.让触摸屏进入等待中断模式 4.分配输入设备结构 5.设置可能上报的事件类型和按键类型 6.为TC和ADC中断注册处理函数 7.注册输入型设备 按下处理 1.判断按下或弹起 2.如果是按下情况,那么启动XY坐标的AD转换 3.进行4次ADC转换,获取4次XY坐标值 4.计算4次采集的平均值,并上报给内核

【linux驱动分析】之dm9000驱动分析(三):sk_buff结构分析

[linux驱动分析]之dm9000驱动分析(一):dm9000原理及硬件分析 [linux驱动分析]之dm9000驱动分析(二):定义在板文件里的资源和设备以及几个宏 [linux驱动分析]之dm9000驱动分析(四):net_device结构体 [linux驱动分析]之dm9000驱动分析(五):另外几个重要的结构体 [linux驱动分析]之dm9000驱动分析(六):dm9000_init和dm9000_probe的实现 [linux驱动分析]之dm9000驱动分析(七):dm9000的卸

linux input设备驱动分析

linux input设备驱动分析 工作机制 输入设备工作机制: 输入动作–>产生中断–>CPU通过总线或者IO读取数据到缓冲区 构架层次 app //-------------------- input_event_driver //-------------------- input_core //-------------------- input_device_driver //-------------------- hardware 数据结构 输入设备对象 struct input

S3C6410 LCD驱动分析(转)

一. 理论分析1. 几个概念:FIMC :    Fully Interactive Mobile Camera (完全交互式移动摄像机)FIMD:     Fully Interactive Mobile Display (完全交互式移动显示设备)2. 设置VCLK在VIDCON0中bit[3:2]-->Select the Video Clock source =00 --> HCLK=133MHZbit[13:6] --> CLKVAL_F = 13  (这个值是在驱动中计算出来的