一、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.}
说明: 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.} 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.} | ||||
二、sysfs文件系统和测试 | ||||
2.1、sysfs文件系统 | ||||
在第一篇文章中的ADC子系统核心的注册函数中,调用了adc_sysfs_init();函数,该函数如下: 该函数增加了一个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、应用层测试 | ||||
应用层测试采用命令行的方式,具体如下图: | ||||
三、proc文件系统和测试 | ||||
3.1、proc文件系统 | ||||
在ADC子系统注册函数adc_device_register()中,调用了adc-proc.c中的adc_proc_add_device(adc); 函数,具体内容如下: 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涉及的函数如下: 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、应用层测试 | ||||
应用层测试采用命令行的方式,具体如下图: | ||||
四、总结 | ||||
在调试过程中出现过内存泄露的问题,经过调试发现是在系统启动过程中,根据模块启动宏的不同,初始化是有顺序的。把先使用的模块先初始化,比如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 | ||||