<form id="bpvnp"></form>

            <address id="bpvnp"><listing id="bpvnp"><meter id="bpvnp"></meter></listing></address>

                <address id="bpvnp"><nobr id="bpvnp"><menuitem id="bpvnp"></menuitem></nobr></address>

                    技术文摘

                    GSC3280的ADC子系统驱动模型(二)












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

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

                    1.1、创建设备节点

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

                    1.  void __init adc_dev_init(void)

                    2.  {

                    3.      int err;

                    4.   

                    5.      err = alloc_chrdev_region(&adc_devt, 0, ADC_DEV_MAX, "adc");

                    6.      if (err < 0) {

                    7.          DBG("!!!!!!alloc chrdev region error!!!!!!\n");

                    8.          printk(KERN_ERR "%s: failed to allocate char dev region\n", __FILE__);

                    9.      }

                    10.}


                    对应退出函数,调用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. 

                    13.void adc_dev_del_device(struct adc_core_dev *adc)

                    14.{

                    15.    if (adc->dev.devt)

                    16.        cdev_del(&adc->char_dev);

                    17.}


                    在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.   

                    9.  void adc_dev_prepare(struct adc_core_dev *adc)

                    10.{

                    11.    if (!adc_devt) {

                    12.        DBG("!!!!!!adc_devt = 0!!!!!!\n");

                    13.        return;

                    14.    }

                    15.    if (adc->id >= ADC_DEV_MAX) {

                    16.        DBG("!!!!!!adc dev prepare error,id too many!!!!!!\n");

                    17.        pr_debug("%s: too many ADC devices\n", adc->name);

                    18.        return;

                    19.    }

                    20.    adc->dev.devt = MKDEV(MAJOR(adc_devt), adc->id);

                    21.    cdev_init(&adc->char_dev, &adc_dev_fops);

                    22.    adc->char_dev.owner = adc->owner;

                    23.}


                    说明:

                    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.   

                    6.      if (test_and_set_bit_lock(ADC_DEV_BUSY, &adc->flags))

                    7.          return -EBUSY;

                    8.      file->private_data = adc;

                    9.      err = adc->ops->open ? adc->ops->open(adc->dev.parent) : 0;

                    10.    if (err == 0) {

                    11.        return 0;

                    12.    }

                    13.    /* something has gone wrong */

                    14.    clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);

                    15.    return err;

                    16.}


                    说明:

                    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. 

                    13.    err = mutex_lock_interruptible(&adc->ops_lock);

                    14.    if (err)

                    15.        return err;

                    16.    if ((_IOC_TYPE(cmd) != ADC_DEV_IOC_MAGIC) || (_IOC_NR(cmd) > ADC_DEV_IOC_MAXNR)) {

                    17.        err = -ENOTTY;

                    18.        goto exit;

                    19.    }

                    20.    switch(cmd) {

                    21.    case ADC_DEV_CON_PBAT:

                    22.        adc_cmd = CMD_AD_CON_PBAT;

                    23.        break;

                    24.    case ADC_DEV_CON_CHX:

                    25.        if (get_user(channel, p)) {

                    26.            err = -EFAULT;

                    27.            goto exit;

                    28.        }

                    29.        switch (channel) {

                    30.        case 0:

                    31.            adc_cmd = CMD_AD_CON_CH0;

                    32.            break;

                    33.        case 1:

                    34.            adc_cmd = CMD_AD_CON_CH1;

                    35.            break;

                    36.        case 2:

                    37.            adc_cmd = CMD_AD_CON_CH2;

                    38.            break;

                    39.        case 3:

                    40.            adc_cmd = CMD_AD_CON_CH3;

                    41.            break;

                    42.        }

                    43.        break;

                    44.    default:

                    45.        err = -ENOTTY;

                    46.        goto exit;

                    47.    }

                    48.    DBG("adc_cmd = 0x%x\n", adc_cmd);

                    49.    put_user(adc->ops->convert(adc_cmd), p);

                    50. 

                    51.exit:

                    52.    mutex_unlock(&adc->ops_lock);

                    53.    return err;

                    54.}

                    说明:
                    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.   

                    5.      if (adc->ops->release)

                    6.          adc->ops->release(adc->dev.parent);

                    7.      clear_bit_unlock(ADC_DEV_BUSY, &adc->flags);

                    8.      return 0;

                    9.  }


                    说明:

                    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.   

                    7.  #define ADC_DEV_IOC_MAGIC            'a'

                    8.  #define ADC_DEV_IOC_MAXNR            2

                    9.  #define ADC_DEV_CON_PBAT            _IOR(ADC_DEV_IOC_MAGIC, 0, int)

                    10.#define ADC_DEV_CON_CHX            _IOWR(ADC_DEV_IOC_MAGIC, 1, int)

                    11.int main(int argc, char **argv)

                    12.{

                    13.    //unsigned char buff[2] = {0};

                    14.    unsigned int idCmd = 0;

                    15.    int fd = 0, ret = 0, data = 0;

                    16.    

                    17.    //以阻塞方式打开设备文件,非阻塞时flags=O_NONBLOCK

                    18.    fd = open("/dev/adc0", 0);

                    19.    if (fd < 0) {

                    20.        printf("Open ADC Device Faild!\n");

                    21.        exit(1);

                    22.    }

                    23.    while(1) {

                    24.        data = 1;

                    25.        idCmd = ADC_DEV_CON_CHX;

                    26.        ret = ioctl(fd, idCmd, &data);

                    27.        if (ret != 0) {

                    28.            printf("Read ADC Device Faild!\n");

                    29.            break;

                    30.        } else {

                    31.            //data = buff[0] | (buff[1] << 8);

                    32.            printf("Read ADC value is: %d\n", data);

                    33.        }

                    34.        for (ret = 0; ret < 6553600; ret++)

                    35.            ; 

                    36.    }

                    37.    close(fd);

                    38.    return 0;

                    39.}

                    说明:
                    1、首先打开设备
                    2、设置通道1和采集通道命令,调用ioctl实现AD采集
                    3、如果采集正确,打印数据
                    4、延时后,循环再一次采集数据,具体测试图如下:


                    应用层测试程序.png







                    二、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.   

                    9.  static ssize_t

                    10.adc_sysfs_show_pbat(struct device *dev, struct device_attribute *attr,

                    11.        char *buf)

                    12.{

                    13.    int result = 0;

                    14.    struct adc_core_dev *adc = to_adc_device(dev);

                    15. 

                    16.    result = adc->ops->convert(CMD_AD_CON_PBAT);

                    17.    printk(KERN_INFO "pbat = %d\n", result);

                    18.    sprintf(buf, "%d\n", result);

                    19.    return 0;

                    20.}

                    21. 

                    22.static ssize_t

                    23.adc_sysfs_show_ch0(struct device *dev, struct device_attribute *attr,

                    24.        char *buf)

                    25.{

                    26.    int result = 0;

                    27.    struct adc_core_dev *adc = to_adc_device(dev);

                    28. 

                    29.    result = adc->ops->convert(CMD_AD_CON_CH0);

                    30.    printk(KERN_INFO "ch0 = %d\n", result);

                    31.    sprintf(buf, "%d\n", result);

                    32.    return 0;

                    33.}

                    34. 

                    35.static ssize_t

                    36.adc_sysfs_show_ch1(struct device *dev, struct device_attribute *attr,

                    37.        char *buf)

                    38.{

                    39.    int result = 0;

                    40.    struct adc_core_dev *adc = to_adc_device(dev);

                    41. 

                    42.    result = adc->ops->convert(CMD_AD_CON_CH1);

                    43.    printk(KERN_INFO "ch1 = %d\n", result);

                    44.    sprintf(buf, "%d\n", result);

                    45.    return 0;

                    46.}

                    47. 

                    48.static ssize_t

                    49.adc_sysfs_show_ch2(struct device *dev, struct device_attribute *attr,

                    50.        char *buf)

                    51.{

                    52.    int result = 0;

                    53.    struct adc_core_dev *adc = to_adc_device(dev);

                    54. 

                    55.    result = adc->ops->convert(CMD_AD_CON_CH2);

                    56.    printk(KERN_INFO "ch2 = %d\n", result);

                    57.    sprintf(buf, "%d\n", result);

                    58.    return 0;

                    59.}

                    60. 

                    61.static ssize_t

                    62.adc_sysfs_show_ch3(struct device *dev, struct device_attribute *attr,

                    63.        char *buf)

                    64.{

                    65.    int result = 0;

                    66.    struct adc_core_dev *adc = to_adc_device(dev);

                    67. 

                    68.    result = adc->ops->convert(CMD_AD_CON_CH3);

                    69.    printk(KERN_INFO "ch3 = %d\n", result);

                    70.    sprintf(buf, "%d\n", result);

                    71.    return 0;

                    72.}

                    73. 

                    74.static struct device_attribute adc_attrs[] = {

                    75.    __ATTR(name, S_IRUGO, adc_sysfs_show_name, NULL),

                    76.    __ATTR(pbat, S_IRUGO, adc_sysfs_show_pbat, NULL),

                    77.    __ATTR(ch0, S_IRUGO, adc_sysfs_show_ch0, NULL),

                    78.    __ATTR(ch1, S_IRUGO, adc_sysfs_show_ch1, NULL),

                    79.    __ATTR(ch2, S_IRUGO, adc_sysfs_show_ch2, NULL),

                    80.    __ATTR(ch3, S_IRUGO, adc_sysfs_show_ch3, NULL),

                    81.    { },

                    82.};


                    由以上程序可知,该属性成员总共有六个成员,通过这种赋值,系统起来后,在根文件目录/sys/class/adc/adc0下就有相应的节点,操作相应的节点,就相当于调相应的函数,比如操作节点pbat,则调用函数adc_sysfs_show_pbat()。其他节点类似。

                    各个节点函数类似,首先都是获取adc-core结构体,然后根据节点内容不同,赋值不同命令,最后调用adc-core中的函数操作集中的转换函数实现AD转换。







                    2.2、应用层测试

                    应用层测试采用命令行的方式,具体如下图:

                    应用层测试程序2.png







                    三、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.   

                    8.  void adc_proc_add_device(struct adc_core_dev *adc)

                    9.  {

                    10.    if (adc->id == 0)

                    11.        proc_create_data("driver/adc", 0, NULL, &adc_proc_fops, adc);

                    12.}

                    13. 

                    14.void adc_proc_del_device(struct adc_core_dev *adc)

                    15.{

                    16.    if (adc->id == 0)

                    17.        remove_proc_entry("driver/adc", NULL);

                    18.}


                    说明:

                    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. 

                    21.static int adc_proc_open(struct inode *inode, struct file *file)

                    22.{

                    23.    int ret;

                    24.    struct adc_core_dev *adc = PDE(inode)->data;

                    25. 

                    26.    if (!try_module_get(THIS_MODULE)) {

                    27.        DBG("!!!!!!try_module_get error!!!!!!\n");

                    28.        return -ENODEV;

                    29.    }

                    30.    ret = single_open(file, adc_proc_show, adc);

                    31.    if (ret)

                    32.        module_put(THIS_MODULE);

                    33.    return ret;

                    34.}

                    35. 

                    36.static int adc_proc_release(struct inode *inode, struct file *file)

                    37.{

                    38.    int res = single_release(inode, file);

                    39.    module_put(THIS_MODULE);

                    40.    return res;

                    41.}


                    说明:       

                    1、open函数中首先获得adc-core设备结构体,使用single_open()函数调用dc_proc_show()函数。

                    2、释放函数调用single_release()释放设备。

                    3、adc_proc_show()函数中,使用adc-core的操作函数集的AD转换函数,实现AD转换,也就是调用第一篇文章中设备驱动的gsc3280AdcCon()函数。

                     4、gsc3280AdcCon()分别实现5路AD的转换。







                    3.2、应用层测试

                    应用层测试采用命令行的方式,具体如下图:

                    应用层测试程序3.png







                    四、总结

                    在调试过程中出现过内存泄露的问题,经过调试发现是在系统启动过程中,根据??槠舳甑牟煌?,初始化是有顺序的。把先使用的??橄瘸跏蓟?,比如subsys_initcall(gsc_adc_init);。        

                    ADC核心使底层硬件对用户来说是透明的,并且减少了编写驱动程序的工作量。ADC新的驱动接口提供了更多的功能,使系统可以同时存在多个ADC。/dev,sysfs,proc这三种机制的实现使得应用程序能灵活的使用ADC。

                    ADC核心代码的组织方式值得学习,不同功能的代码放在不同的文件中,简单明了。

                    如果系统有另外一个AD转换芯片,那么只需新增加设备驱动程序,在新的设备驱动程序中,使用函数adc_device_register() 注册ADC子系统,通过idr的id即可区分不同的ADC设备。




                    原文参见:http://blog.chinaunix.net/uid-25445243-id-4011337.html

















                     
                    彩神ll【中国】股份有限公司