Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1834969
  • 博文数量: 134
  • 博客积分: 2488
  • 博客等级: 大尉
  • 技术积分: 7554
  • 用 户 组: 普通用户
  • 注册时间: 2011-02-26 21:19
个人简介

1.每日自省; 2.享受人生; 3.尽力而为; 4.坚持不懈; 5.切莫急躁; 6.慎言敏行; 7.动心忍性; 8.上善若水。

文章分类

全部博文(134)

文章存档

2014年(38)

2013年(42)

2012年(15)

2011年(39)

分类: LINUX

2013-11-26 16:39:25

一、ADC子系统--创建设备文件

        本部分包括创建设备节点和在应用层通过操作/dev目录下的设备节点来控制设备。

1.1、创建设备节点

        在第一篇文章中的ADC子系统核心的注册函数中,调用了adc_dev_init();函数,该函数如下:

点击(此处)折叠或打开

  1. void __init adc_dev_init(void)
  2. {
  3.     int err;

  4.     err = alloc_chrdev_region(&adc_devt, 0, ADC_DEV_MAX, "adc");
  5.     if (err < 0) {
  6.         DBG("!!!!!!alloc chrdev region error!!!!!!\n");
  7.         printk(KERN_ERR "%s: failed to allocate char dev region\n", __FILE__);
  8.     }
  9. }

        对应退出函数,调用adc_dev_exit();函数,函数如下:

点击(此处)折叠或打开

  1. void __exit adc_dev_exit(void)
  2. {
  3.     if (adc_devt)
  4.         unregister_chrdev_region(adc_devt, ADC_DEV_MAX);
  5. }

        说明:

        1、初始化函数主要为创建设备节点做准备。

        2、退出函数是初始化函数的相反过程。

        在第一篇文章的ADC子系统核心注册和注销函数中,调用了dev增加设备函数adc_dev_add_device()

和dev删除函数adc_dev_del_device(),程序如下:

点击(此处)折叠或打开

  1. void adc_dev_add_device(struct adc_core_dev *adc)
  2. {
  3.     if (cdev_add(&adc->char_dev, adc->dev.devt, 1)) {
  4.         DBG("!!!!!!cdev add error.!!!!!!\n");
  5.         printk(KERN_WARNING "%s: failed to add char device %d:%d\n",
  6.                 adc->name, MAJOR(adc_devt), adc->id);
  7.     }
  8.     else {
  9.         pr_debug("%s: dev (%d:%d)\n", adc->name, MAJOR(adc_devt), adc->id);
  10.     }
  11. }

  12. void adc_dev_del_device(struct adc_core_dev *adc)
  13. {
  14.     if (adc->dev.devt)
  15.         cdev_del(&adc->char_dev);
  16. }

        在ADC子系统核心注册函数中,调用了adc_dev_prepare()函数实现增加dev操作函数集,具体内容

如下:

点击(此处)折叠或打开

  1. static const struct file_operations adc_dev_fops = {
  2.     .owner            = THIS_MODULE,
  3.     .llseek            = no_llseek,
  4.     .unlocked_ioctl    = adc_dev_ioctl,
  5.     .open            = adc_dev_open,
  6.     .release            = adc_dev_release,
  7. };

  8. void adc_dev_prepare(struct adc_core_dev *adc)
  9. {
  10.     if (!adc_devt) {
  11.         DBG("!!!!!!adc_devt = 0!!!!!!\n");
  12.         return;
  13.     }
  14.     if (adc->id >= ADC_DEV_MAX) {
  15.         DBG("!!!!!!adc dev prepare error,id too many!!!!!!\n");
  16.         pr_debug("%s: too many ADC devices\n", adc->name);
  17.         return;
  18.     }
  19.     adc->dev.devt = MKDEV(MAJOR(adc_devt), adc->id);
  20.     cdev_init(&adc->char_dev, &adc_dev_fops);
  21.     adc->char_dev.owner = adc->owner;
  22. }

        说明:

        1、首先对设备号进行检查。

        2、计算设备号。

        3、增加操作函数集,该函数集主要涉及到两个函数,一个打开函数adc_dev_open()和一个控制函
数adc_dev_ioctl(),首先看下打开函数:

点击(此处)折叠或打开

  1. static int adc_dev_open(struct inode *inode, struct file *file)
  2. {
  3.     int err;
  4.     struct adc_core_dev *adc = container_of(inode->i_cdev, struct adc_core_dev, char_dev);

  5.     if (test_and_set_bit_lock(ADC_DEV_BUSY, &adc->flags))
  6.         return -EBUSY;
  7.     file->private_data = adc;
  8.     err = adc->ops->open ? adc->ops->open(adc->dev.parent) : 0;
  9.     if (err == 0) {
  10.         return 0;
  11.     }
  12.     /* something has gone wrong */
  13.     clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);
  14.     return err;
  15. }

        说明:

        1、使用container_of通过设备号获取在adc_device_register()函数中创建的adc-core结构体。

        2、测试设备是否忙,如果忙,直接退出。如果不忙,设置忙标志。

        3、判断adc-core的操作函数集是否包括open函数,通过第一篇文章我们知道,adc-core的函数

操作集只有一个转换函数,所以此处err直接等于0,退出。

        命令控制函数adc_dev_ioctl()如下:

点击(此处)折叠或打开

  1. #define ADC_DEV_IOC_MAGIC            'a'
  2. #define ADC_DEV_IOC_MAXNR            2
  3. #define ADC_DEV_CON_PBAT            _IOR(ADC_DEV_IOC_MAGIC, 0, int)
  4. #define ADC_DEV_CON_CHX            _IOWR(ADC_DEV_IOC_MAGIC, 1, int)
  5. static long adc_dev_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
  6. {
  7.     int err = 0, channel = 0;
  8.     unsigned short adc_cmd = 0;
  9.     struct adc_core_dev *adc = file->private_data;
  10.     void __user *argp = (void __user *)arg;
  11.     int __user *p = argp;

  12.     err = mutex_lock_interruptible(&adc->ops_lock);
  13.     if (err)
  14.         return err;
  15.     if ((_IOC_TYPE(cmd) != ADC_DEV_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_DEV_IOC_MAXNR)) {
  16.         err = -ENOTTY;
  17.         goto exit;
  18.     }
  19.     switch(cmd) {
  20.     case ADC_DEV_CON_PBAT:
  21.         adc_cmd = CMD_AD_CON_PBAT;
  22.         break;
  23.     case ADC_DEV_CON_CHX:
  24.         if (get_user(channel, p)) {
  25.             err = -EFAULT;
  26.             goto exit;
  27.         }
  28.         switch (channel) {
  29.         case 0:
  30.             adc_cmd = CMD_AD_CON_CH0;
  31.             break;
  32.         case 1:
  33.             adc_cmd = CMD_AD_CON_CH1;
  34.             break;
  35.         case 2:
  36.             adc_cmd = CMD_AD_CON_CH2;
  37.             break;
  38.         case 3:
  39.             adc_cmd = CMD_AD_CON_CH3;
  40.             break;
  41.         }
  42.         break;
  43.     default:
  44.         err = -ENOTTY;
  45.         goto exit;
  46.     }
  47.     DBG("adc_cmd = 0x%x\n", adc_cmd);
  48.     put_user(adc->ops->convert(adc_cmd), p);

  49. exit:
  50.     mutex_unlock(&adc->ops_lock);
  51.     return err;
  52. }

        说明:

        1、此处共有两条命令。

        2、命令控制函数首先对命令有效性进行检查。

        3、第一条是测量pbat命令,直接赋命令值。

        4、第二条命令是对AD转换通道进行测量,此处包括一个通道值,所以先从应用层获取通道值。

        5、最后调用adc-core操作函数集中的转换函数,也就是第一篇文章中的gsc3280AdcCon()进行AD转换。

        6、将转换结果发送给应用层。        

        dev释放函数adc_dev_release()如下:

点击(此处)折叠或打开

  1. static int adc_dev_release(struct inode *inode, struct file *file)
  2. {
  3.     struct adc_core_dev *adc = file->private_data;

  4.     if (adc->ops->release)
  5.         adc->ops->release(adc->dev.parent);
  6.     clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);
  7.     return 0;
  8. }

        说明:

        1、判断adc-core的操作函数集是否包括release函数,通过第一篇文章我们知道,adc-core的函数操作集

只有一个转换函数,所以此处不执行if语句里面内容。

        2、清除忙标志。

1.2、应用层测试程序

        应用层测试程序如下:

点击(此处)折叠或打开

  1. #include <fcntl.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <sys/ioctl.h>
  5. #include <linux/ioctl.h>

  6. #define ADC_DEV_IOC_MAGIC            'a'
  7. #define ADC_DEV_IOC_MAXNR            2
  8. #define ADC_DEV_CON_PBAT            _IOR(ADC_DEV_IOC_MAGIC, 0, int)
  9. #define ADC_DEV_CON_CHX            _IOWR(ADC_DEV_IOC_MAGIC, 1, int)
  10. int main(int argc, char **argv)
  11. {
  12.     //unsigned char buff[2] = {0};
  13.     unsigned int idCmd = 0;
  14.     int fd = 0, ret = 0, data = 0;
  15.     
  16.     //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK
  17.     fd = open("/dev/adc0", 0);
  18.     if (fd < 0) {
  19.         printf("Open ADC Device Faild!\n");
  20.         exit(1);
  21.     }
  22.     while(1) {
  23.         data = 1;
  24.         idCmd = ADC_DEV_CON_CHX;
  25.         ret = ioctl(fd, idCmd, &data);
  26.         if (ret != 0) {
  27.             printf("Read ADC Device Faild!\n");
  28.             break;
  29.         } else {
  30.             //data = buff[0] | (buff[1] << 8);
  31.             printf("Read ADC value is: %d\n", data);
  32.         }
  33.         for (ret = 0; ret < 6553600; ret++)
  34.             ;
  35.     }
  36.     close(fd);
  37.     return 0;
  38. }

        说明:

        1、首先打开设备

        2、设置通道1和采集通道命令,调用ioctl实现AD采集

        3、如果采集正确,打印数据

        4、延时后,循环再一次采集数据,具体测试图如下:


二、sysfs文件系统和测试       

2.1、sysfs文件系统

        在第一篇文章中的ADC子系统核心的注册函数中,调用了adc_sysfs_init();函数,该函数如下:

点击(此处)折叠或打开

  1. void __init adc_sysfs_init(struct class *adc_class)
  2. {
  3.     adc_class->dev_attrs = adc_attrs;
  4. }

        该函数增加了一个device_attribute属性--adc_attrs,device_attribute属性将在第三篇文章讲述,
adc_attrs具体内容如下:

点击(此处)折叠或打开

  1. #define to_adc_device(d) container_of(d, struct adc_core_dev, dev)
  2. /* device attributes */
  3. static ssize_t
  4. adc_sysfs_show_name(struct device *dev, struct device_attribute *attr, char *buf)
  5. {
  6.     return sprintf(buf, "%s\n", to_adc_device(dev)->name);
  7. }

  8. static ssize_t
  9. adc_sysfs_show_pbat(struct device *dev, struct device_attribute *attr,
  10.         char *buf)
  11. {
  12.     int result = 0;
  13.     struct adc_core_dev *adc = to_adc_device(dev);

  14.     result = adc->ops->convert(CMD_AD_CON_PBAT);
  15.     printk(KERN_INFO "pbat = %d\n", result);
  16.     sprintf(buf, "%d\n", result);
  17.     return 0;
  18. }

  19. static ssize_t
  20. adc_sysfs_show_ch0(struct device *dev, struct device_attribute *attr,
  21.         char *buf)
  22. {
  23.     int result = 0;
  24.     struct adc_core_dev *adc = to_adc_device(dev);

  25.     result = adc->ops->convert(CMD_AD_CON_CH0);
  26.     printk(KERN_INFO "ch0 = %d\n", result);
  27.     sprintf(buf, "%d\n", result);
  28.     return 0;
  29. }

  30. static ssize_t
  31. adc_sysfs_show_ch1(struct device *dev, struct device_attribute *attr,
  32.         char *buf)
  33. {
  34.     int result = 0;
  35.     struct adc_core_dev *adc = to_adc_device(dev);

  36.     result = adc->ops->convert(CMD_AD_CON_CH1);
  37.     printk(KERN_INFO "ch1 = %d\n", result);
  38.     sprintf(buf, "%d\n", result);
  39.     return 0;
  40. }

  41. static ssize_t
  42. adc_sysfs_show_ch2(struct device *dev, struct device_attribute *attr,
  43.         char *buf)
  44. {
  45.     int result = 0;
  46.     struct adc_core_dev *adc = to_adc_device(dev);

  47.     result = adc->ops->convert(CMD_AD_CON_CH2);
  48.     printk(KERN_INFO "ch2 = %d\n", result);
  49.     sprintf(buf, "%d\n", result);
  50.     return 0;
  51. }

  52. static ssize_t
  53. adc_sysfs_show_ch3(struct device *dev, struct device_attribute *attr,
  54.         char *buf)
  55. {
  56.     int result = 0;
  57.     struct adc_core_dev *adc = to_adc_device(dev);

  58.     result = adc->ops->convert(CMD_AD_CON_CH3);
  59.     printk(KERN_INFO "ch3 = %d\n", result);
  60.     sprintf(buf, "%d\n", result);
  61.     return 0;
  62. }

  63. static struct device_attribute adc_attrs[] = {
  64.     __ATTR(name, S_IRUGO, adc_sysfs_show_name, NULL),
  65.     __ATTR(pbat, S_IRUGO, adc_sysfs_show_pbat, NULL),
  66.     __ATTR(ch0, S_IRUGO, adc_sysfs_show_ch0, NULL),
  67.     __ATTR(ch1, S_IRUGO, adc_sysfs_show_ch1, NULL),
  68.     __ATTR(ch2, S_IRUGO, adc_sysfs_show_ch2, NULL),
  69.     __ATTR(ch3, S_IRUGO, adc_sysfs_show_ch3, NULL),
  70.     { },
  71. };

        由以上程序可知,该属性成员总共有六个成员,通过这种赋值,系统起来后,在根文件目录

/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);

函数,具体内容如下:

点击(此处)折叠或打开

  1. static const struct file_operations adc_proc_fops = {
  2.     .open    = adc_proc_open,
  3.     .read    = seq_read,
  4.     .llseek    = seq_lseek,
  5.     .release    = adc_proc_release,
  6. };

  7. void adc_proc_add_device(struct adc_core_dev *adc)
  8. {
  9.     if (adc->id == 0)
  10.         proc_create_data("driver/adc", 0, NULL, &adc_proc_fops, adc);
  11. }

  12. void adc_proc_del_device(struct adc_core_dev *adc)
  13. {
  14.     if (adc->id == 0)
  15.         remove_proc_entry("driver/adc", NULL);
  16. }

        说明:

        1、使用proc_create_data()函数创建了函数操作函数集adc_proc_fops。

        2、adc_proc_del_device()函数在ADC子系统注销函数中调用,是adc_proc_add_device()函数的相反过程。

        3、操作函数集adc_proc_fops涉及的函数如下:

点击(此处)折叠或打开

  1. static int adc_proc_show(struct seq_file *seq, void *offset)
  2. {
  3.     int result = 0;
  4.     struct adc_core_dev *adc = seq->private;
  5.     
  6.     result = adc->ops->convert(CMD_AD_CON_PBAT);
  7.     seq_printf(seq, "PBAT:%d\n", result);
  8.     result = adc->ops->convert(CMD_AD_CON_CH0);
  9.     seq_printf(seq, "CH0:%d\n", result);
  10.     result = adc->ops->convert(CMD_AD_CON_CH1);
  11.     seq_printf(seq, "CH1:%d\n", result);
  12.     result = adc->ops->convert(CMD_AD_CON_CH2);
  13.     seq_printf(seq, "CH2:%d\n", result);
  14.     result = adc->ops->convert(CMD_AD_CON_CH3);
  15.     seq_printf(seq, "CH3:%d\n", result);
  16.     if (adc->ops->proc)
  17.         adc->ops->proc(adc->dev.parent, seq);
  18.     return 0;
  19. }

  20. static int adc_proc_open(struct inode *inode, struct file *file)
  21. {
  22.     int ret;
  23.     struct adc_core_dev *adc = PDE(inode)->data;

  24.     if (!try_module_get(THIS_MODULE)) {
  25.         DBG("!!!!!!try_module_get error!!!!!!\n");
  26.         return -ENODEV;
  27.     }
  28.     ret = single_open(file, adc_proc_show, adc);
  29.     if (ret)
  30.         module_put(THIS_MODULE);
  31.     return ret;
  32. }

  33. static int adc_proc_release(struct inode *inode, struct file *file)
  34. {
  35.     int res = single_release(inode, file);
  36.     module_put(THIS_MODULE);
  37.     return res;
  38. }

        说明:       

        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设备。

阅读(4306) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~