一、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设备。
阅读(4299) | 评论(0) | 转发(1) |