GSC3280的ADC子系统驱动模型(二)
2014-03-19 11:53
127 查看
一、ADC子系统--创建设备文件
本部分包括创建设备节点和在应用层通过操作/dev目录下的设备节点来控制设备。1.1、创建设备节点
在第一篇文章中的ADC子系统核心的注册函数中,调用了adc_dev_init();函数,该函数如下:点击(此处)折叠或打开
void __init adc_dev_init(void)
{
int err;
err = alloc_chrdev_region(&adc_devt, 0,
ADC_DEV_MAX, "adc");
if (err
< 0)
{
DBG("!!!!!!alloc chrdev region error!!!!!!\n");
printk(KERN_ERR
"%s: failed to allocate char dev region\n", __FILE__);
}
}
对应退出函数,调用adc_dev_exit();函数,函数如下:
点击(此处)折叠或打开
void __exit adc_dev_exit(void)
{
if (adc_devt)
unregister_chrdev_region(adc_devt, ADC_DEV_MAX);
}
说明:
1、初始化函数主要为创建设备节点做准备。
2、退出函数是初始化函数的相反过程。
在第一篇文章的ADC子系统核心注册和注销函数中,调用了dev增加设备函数adc_dev_add_device()
和dev删除函数adc_dev_del_device(),程序如下:
点击(此处)折叠或打开
void adc_dev_add_device(struct adc_core_dev
*adc)
{
if (cdev_add(&adc->char_dev,
adc->dev.devt, 1))
{
DBG("!!!!!!cdev add error.!!!!!!\n");
printk(KERN_WARNING
"%s: failed to add char device %d:%d\n",
adc->name, MAJOR(adc_devt),
adc->id);
}
else {
pr_debug("%s: dev (%d:%d)\n", adc->name,
MAJOR(adc_devt), adc->id);
}
}
void adc_dev_del_device(struct adc_core_dev
*adc)
{
if (adc->dev.devt)
cdev_del(&adc->char_dev);
}
在ADC子系统核心注册函数中,调用了adc_dev_prepare()函数实现增加dev操作函数集,具体内容
如下:
点击(此处)折叠或打开
static const struct file_operations adc_dev_fops
= {
.owner = THIS_MODULE,
.llseek = no_llseek,
.unlocked_ioctl = adc_dev_ioctl,
.open = adc_dev_open,
.release = adc_dev_release,
};
void adc_dev_prepare(struct adc_core_dev
*adc)
{
if (!adc_devt)
{
DBG("!!!!!!adc_devt = 0!!!!!!\n");
return;
}
if (adc->id
>= ADC_DEV_MAX)
{
DBG("!!!!!!adc dev prepare error,id too many!!!!!!\n");
pr_debug("%s: too many ADC devices\n", adc->name);
return;
}
adc->dev.devt
= MKDEV(MAJOR(adc_devt), adc->id);
cdev_init(&adc->char_dev,
&adc_dev_fops);
adc->char_dev.owner
= adc->owner;
}
说明:
1、首先对设备号进行检查。
2、计算设备号。
3、增加操作函数集,该函数集主要涉及到两个函数,一个打开函数adc_dev_open()和一个控制函
数adc_dev_ioctl(),首先看下打开函数:
点击(此处)折叠或打开
static int adc_dev_open(struct inode
*inode, struct file
*file)
{
int err;
struct adc_core_dev *adc
= container_of(inode->i_cdev, struct adc_core_dev, char_dev);
if (test_and_set_bit_lock(ADC_DEV_BUSY,
&adc->flags))
return -EBUSY;
file->private_data
= adc;
err = adc->ops->open
? adc->ops->open(adc->dev.parent)
: 0;
if (err
== 0)
{
return 0;
}
/* something has gone wrong
*/
clear_bit_unlock(ADC_DEV_BUSY,
&adc->flags);
return err;
}
说明:
1、使用container_of通过设备号获取在adc_device_register()函数中创建的adc-core结构体。
2、测试设备是否忙,如果忙,直接退出。如果不忙,设置忙标志。
3、判断adc-core的操作函数集是否包括open函数,通过第一篇文章我们知道,adc-core的函数
操作集只有一个转换函数,所以此处err直接等于0,退出。
命令控制函数adc_dev_ioctl()如下:
点击(此处)折叠或打开
#define ADC_DEV_IOC_MAGIC 'a'
#define ADC_DEV_IOC_MAXNR 2
#define ADC_DEV_CON_PBAT _IOR(ADC_DEV_IOC_MAGIC, 0,
int)
#define ADC_DEV_CON_CHX _IOWR(ADC_DEV_IOC_MAGIC, 1,
int)
static long adc_dev_ioctl(struct file
*file, unsigned
int cmd, unsigned long arg)
{
int err = 0, channel
= 0;
unsigned short adc_cmd
= 0;
struct adc_core_dev
*adc = file->private_data;
void __user *argp
= (void __user
*)arg;
int __user
*p = argp;
err = mutex_lock_interruptible(&adc->ops_lock);
if (err)
return err;
if ((_IOC_TYPE(cmd)
!= ADC_DEV_IOC_MAGIC)
||
(_IOC_NR(cmd)
> ADC_DEV_IOC_MAXNR)) {
err =
-ENOTTY;
goto exit;
}
switch(cmd) {
case ADC_DEV_CON_PBAT:
adc_cmd = CMD_AD_CON_PBAT;
break;
case ADC_DEV_CON_CHX:
if (get_user(channel, p)) {
err =
-EFAULT;
goto exit;
}
switch (channel) {
case 0:
adc_cmd = CMD_AD_CON_CH0;
break;
case 1:
adc_cmd = CMD_AD_CON_CH1;
break;
case 2:
adc_cmd = CMD_AD_CON_CH2;
break;
case 3:
adc_cmd = CMD_AD_CON_CH3;
break;
}
break;
default:
err =
-ENOTTY;
goto exit;
}
DBG("adc_cmd = 0x%x\n", adc_cmd);
put_user(adc->ops->convert(adc_cmd),
p);
exit:
mutex_unlock(&adc->ops_lock);
return err;
}
说明:
1、此处共有两条命令。
2、命令控制函数首先对命令有效性进行检查。
3、第一条是测量pbat命令,直接赋命令值。
4、第二条命令是对AD转换通道进行测量,此处包括一个通道值,所以先从应用层获取通道值。
5、最后调用adc-core操作函数集中的转换函数,也就是第一篇文章中的gsc3280AdcCon()进行AD转换。
6、将转换结果发送给应用层。
dev释放函数adc_dev_release()如下:
点击(此处)折叠或打开
static int adc_dev_release(struct inode
*inode, struct file
*file)
{
struct adc_core_dev *adc
= file->private_data;
if (adc->ops->release)
adc->ops->release(adc->dev.parent);
clear_bit_unlock(ADC_DEV_BUSY,
&adc->flags);
return 0;
}
说明:
1、判断adc-core的操作函数集是否包括release函数,通过第一篇文章我们知道,adc-core的函数操作集
只有一个转换函数,所以此处不执行if语句里面内容。
2、清除忙标志。
1.2、应用层测试程序
应用层测试程序如下:点击(此处)折叠或打开
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include <linux/ioctl.h>
#define ADC_DEV_IOC_MAGIC 'a'
#define ADC_DEV_IOC_MAXNR 2
#define ADC_DEV_CON_PBAT _IOR(ADC_DEV_IOC_MAGIC, 0,
int)
#define ADC_DEV_CON_CHX _IOWR(ADC_DEV_IOC_MAGIC, 1,
int)
int main(int argc, char
**argv)
{
//unsigned char buff[2]
= {0};
unsigned int idCmd
= 0;
int fd = 0, ret
= 0, data
= 0;
//以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK
fd = open("/dev/adc0", 0);
if (fd
< 0)
{
printf("Open ADC Device Faild!\n");
exit(1);
}
while(1)
{
data = 1;
idCmd = ADC_DEV_CON_CHX;
ret = ioctl(fd, idCmd,
&data);
if (ret
!= 0)
{
printf("Read ADC Device Faild!\n");
break;
} else
{
//data
= buff[0]
| (buff[1]
<< 8);
printf("Read ADC value is: %d\n", data);
}
for (ret
= 0; ret
< 6553600; ret++)
;
}
close(fd);
return 0;
}
说明:
1、首先打开设备
2、设置通道1和采集通道命令,调用ioctl实现AD采集
3、如果采集正确,打印数据
4、延时后,循环再一次采集数据,具体测试图如下:
二、sysfs文件系统和测试
2.1、sysfs文件系统
在第一篇文章中的ADC子系统核心的注册函数中,调用了adc_sysfs_init();函数,该函数如下:点击(此处)折叠或打开
void __init adc_sysfs_init(struct
class *adc_class)
{
adc_class->dev_attrs
= adc_attrs;
}
该函数增加了一个device_attribute属性--adc_attrs,device_attribute属性将在第三篇文章讲述,
adc_attrs具体内容如下:
点击(此处)折叠或打开
#define to_adc_device(d) container_of(d, struct adc_core_dev,
dev)
/* device attributes
*/
static ssize_t
adc_sysfs_show_name(struct device
*dev, struct device_attribute
*attr, char
*buf)
{
return sprintf(buf,
"%s\n", to_adc_device(dev)->name);
}
static ssize_t
adc_sysfs_show_pbat(struct device
*dev, struct device_attribute
*attr,
char *buf)
{
int result
= 0;
struct adc_core_dev *adc
= to_adc_device(dev);
result = adc->ops->convert(CMD_AD_CON_PBAT);
printk(KERN_INFO
"pbat = %d\n", result);
sprintf(buf,
"%d\n", result);
return 0;
}
static ssize_t
adc_sysfs_show_ch0(struct device
*dev, struct device_attribute
*attr,
char *buf)
{
int result
= 0;
struct adc_core_dev *adc
= to_adc_device(dev);
result = adc->ops->convert(CMD_AD_CON_CH0);
printk(KERN_INFO
"ch0 = %d\n", result);
sprintf(buf,
"%d\n", result);
return 0;
}
static ssize_t
adc_sysfs_show_ch1(struct device
*dev, struct device_attribute
*attr,
char *buf)
{
int result
= 0;
struct adc_core_dev *adc
= to_adc_device(dev);
result = adc->ops->convert(CMD_AD_CON_CH1);
printk(KERN_INFO
"ch1 = %d\n", result);
sprintf(buf,
"%d\n", result);
return 0;
}
static ssize_t
adc_sysfs_show_ch2(struct device
*dev, struct device_attribute
*attr,
char *buf)
{
int result
= 0;
struct adc_core_dev *adc
= to_adc_device(dev);
result = adc->ops->convert(CMD_AD_CON_CH2);
printk(KERN_INFO
"ch2 = %d\n", result);
sprintf(buf,
"%d\n", result);
return 0;
}
static ssize_t
adc_sysfs_show_ch3(struct device
*dev, struct device_attribute
*attr,
char *buf)
{
int result
= 0;
struct adc_core_dev *adc
= to_adc_device(dev);
result = adc->ops->convert(CMD_AD_CON_CH3);
printk(KERN_INFO
"ch3 = %d\n", result);
sprintf(buf,
"%d\n", result);
return 0;
}
static struct device_attribute adc_attrs[]
= {
__ATTR(name, S_IRUGO, adc_sysfs_show_name,
NULL),
__ATTR(pbat, S_IRUGO, adc_sysfs_show_pbat,
NULL),
__ATTR(ch0, S_IRUGO, adc_sysfs_show_ch0,
NULL),
__ATTR(ch1, S_IRUGO, adc_sysfs_show_ch1,
NULL),
__ATTR(ch2, S_IRUGO, adc_sysfs_show_ch2,
NULL),
__ATTR(ch3, S_IRUGO, adc_sysfs_show_ch3,
NULL),
{ },
};
由以上程序可知,该属性成员总共有六个成员,通过这种赋值,系统起来后,在根文件目录
/sys/class/adc/adc0下就有相应的节点,操作相应的节点,就相当于调相应的函数,比如操作节
点pbat,则调用函数adc_sysfs_show_pbat()。其他节点类似。
各个节点函数类似,首先都是获取adc-core结构体,然后根据节点内容不同,赋值不同命令,
最后调用adc-core中的函数操作集中的转换函数实现AD转换。
2.2、应用层测试
应用层测试采用命令行的方式,具体如下图:三、proc文件系统和测试
3.1、proc文件系统
在ADC子系统注册函数adc_device_register()中,调用了adc-proc.c中的adc_proc_add_device(adc);函数,具体内容如下:
点击(此处)折叠或打开
static const struct file_operations adc_proc_fops
= {
.open = adc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = adc_proc_release,
};
void adc_proc_add_device(struct adc_core_dev
*adc)
{
if (adc->id
== 0)
proc_create_data("driver/adc", 0,
NULL,
&adc_proc_fops, adc);
}
void adc_proc_del_device(struct adc_core_dev
*adc)
{
if (adc->id
== 0)
remove_proc_entry("driver/adc",
NULL);
}
说明:
1、使用proc_create_data()函数创建了函数操作函数集adc_proc_fops。
2、adc_proc_del_device()函数在ADC子系统注销函数中调用,是adc_proc_add_device()函数的相反过程。
3、操作函数集adc_proc_fops涉及的函数如下:
点击(此处)折叠或打开
static int adc_proc_show(struct seq_file
*seq, void
*offset)
{
int result
= 0;
struct adc_core_dev *adc
= seq->private;
result = adc->ops->convert(CMD_AD_CON_PBAT);
seq_printf(seq,
"PBAT:%d\n", result);
result = adc->ops->convert(CMD_AD_CON_CH0);
seq_printf(seq,
"CH0:%d\n", result);
result = adc->ops->convert(CMD_AD_CON_CH1);
seq_printf(seq,
"CH1:%d\n", result);
result = adc->ops->convert(CMD_AD_CON_CH2);
seq_printf(seq,
"CH2:%d\n", result);
result = adc->ops->convert(CMD_AD_CON_CH3);
seq_printf(seq,
"CH3:%d\n", result);
if (adc->ops->proc)
adc->ops->proc(adc->dev.parent,
seq);
return 0;
}
static int adc_proc_open(struct inode
*inode, struct file
*file)
{
int ret;
struct adc_core_dev *adc
= PDE(inode)->data;
if (!try_module_get(THIS_MODULE))
{
DBG("!!!!!!try_module_get error!!!!!!\n");
return -ENODEV;
}
ret = single_open(file, adc_proc_show, adc);
if (ret)
module_put(THIS_MODULE);
return ret;
}
static int adc_proc_release(struct inode
*inode, struct file
*file)
{
int res = single_release(inode, file);
module_put(THIS_MODULE);
return res;
}
说明:
1、open函数中首先获得adc-core设备结构体,使用single_open()函数调用adc_proc_show()函数。
2、释放函数调用single_release()释放设备。
3、adc_proc_show()函数中,使用adc-core的操作函数集的AD转换函数,实现AD转换,也就是调用
第一篇文章中设备驱动的gsc3280AdcCon()函数。
4、gsc3280AdcCon()分别实现5路AD的转换。
3.2、应用层测试
应用层测试采用命令行的方式,具体如下图:四、总结
在调试过程中出现过内存泄露的问题,经过调试发现是在系统启动过程中,根据模块启动宏的不同,初始化是有顺序的。把先使用的模块先初始化,比如subsys_initcall(gsc_adc_init);。
ADC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。ADC新的驱动接口提供了更
多的功能,使系统可以同时存在多个ADC。 /dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用ADC。
ADC核心代码的组织方式值得学习,不同功能的代码放在不同的文件中,简单明了。
如果系统有另外一个AD转换芯片,那么只需新增加设备驱动程序,在新的设备驱动程序中,使用函数
adc_device_register() 注册ADC子系统,通过idr的id即可区分不同的ADC设备。
相关文章推荐
- GSC3280的ADC子系统驱动模型(一)
- GSC3280的ADC子系统驱动模型(三)----class的使用
- 实例:触摸屏驱动-2.用input子系统报告事件 分类: linux_内核_input模型 2013-07-10 09:39 327人阅读 评论(0) 收藏
- Linux块设备IO子系统(一) _驱动模型
- "阻塞--中断"驱动模型在i2c在子系统、uart驱动、spi子系统中的实现
- Linux内核MTD子系统二之设备驱动模型
- input子系统七之触摸屏驱动开发实例1(ADC驱动实例)
- Linux Kernel设备驱动模型之子系统注册
- 输入子系统驱动模型
- Linux Kernel设备驱动模型之子系统接口注册
- input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析
- linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析
- LINUX驱动子系统设计的粗糙模型
- linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析
- Linux设备驱动模型学习笔记(kobject,kset,subsystem子系统)
- 触摸屏驱动设计----输入子系统模型
- linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析
- linux input输入子系统分析《二》:s3c2440的ADC简单驱动实例分析
- 设备驱动-----Linux设备模型之input子系统详解
- linux驱动模型<输入子系统>