您的位置:首页 > 其它

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设备。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: