一、ADC子系統(tǒng)--創(chuàng)建設(shè)備文件 | ||||
本部分包括創(chuàng)建設(shè)備節(jié)點和在應(yīng)用層通過操作/dev目錄下的設(shè)備節(jié)點來控制設(shè)備。 | ||||
1.1、創(chuàng)建設(shè)備節(jié)點 | ||||
在第一篇文章中的ADC子系統(tǒng)核心的注冊函數(shù)中,調(diào)用了adc_dev_init();函數(shù),該函數(shù)如下: 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、初始化函數(shù)主要為創(chuàng)建設(shè)備節(jié)點做準備。 2、退出函數(shù)是初始化函數(shù)的相反過程。 在第一篇文章的ADC子系統(tǒng)核心注冊和注銷函數(shù)中,調(diào)用了dev增加設(shè)備函數(shù)adc_dev_add_device()和dev刪除函數(shù)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子系統(tǒng)核心注冊函數(shù)中,調(diào)用了adc_dev_prepare()函數(shù)實現(xiàn)增加dev操作函數(shù)集,具體內(nèi)容如下: 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、首先對設(shè)備號進行檢查。 2、計算設(shè)備號。 3、增加操作函數(shù)集,該函數(shù)集主要涉及到兩個函數(shù),一個打開函數(shù)adc_dev_open()和一個控制函數(shù)adc_dev_ioctl(),首先看下打開函數(shù): 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通過設(shè)備號獲取在adc_device_register()函數(shù)中創(chuàng)建的adc-core結(jié)構(gòu)體。 2、測試設(shè)備是否忙,如果忙,直接退出。如果不忙,設(shè)置忙標志。 3、判斷adc-core的操作函數(shù)集是否包括open函數(shù),通過第一篇文章我們知道,adc-core的函數(shù)操作集只有一個轉(zhuǎn)換函數(shù),所以此處err直接等于0,退出。 命令控制函數(shù)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釋放函數(shù)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的操作函數(shù)集是否包括release函數(shù),通過第一篇文章我們知道,adc-core的函數(shù)操作集只有一個轉(zhuǎn)換函數(shù),所以此處不執(zhí)行if語句里面內(nèi)容。 2、清除忙標志。 | ||||
1.2、應(yīng)用層測試程序 | ||||
應(yīng)用層測試程序如下: 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. //以阻塞方式打開設(shè)備文件,非阻塞時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文件系統(tǒng)和測試 | ||||
2.1、sysfs文件系統(tǒng) | ||||
在第一篇文章中的ADC子系統(tǒng)核心的注冊函數(shù)中,調(diào)用了adc_sysfs_init();函數(shù),該函數(shù)如下: 該函數(shù)增加了一個device_attribute屬性--adc_attrs,device_attribute屬性將在第三篇文章講述,adc_attrs具體內(nèi)容如下: 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.}; 由以上程序可知,該屬性成員總共有六個成員,通過這種賦值,系統(tǒng)起來后,在根文件目錄/sys/class/adc/adc0下就有相應(yīng)的節(jié)點,操作相應(yīng)的節(jié)點,就相當于調(diào)相應(yīng)的函數(shù),比如操作節(jié)點pbat,則調(diào)用函數(shù)adc_sysfs_show_pbat()。其他節(jié)點類似。 各個節(jié)點函數(shù)類似,首先都是獲取adc-core結(jié)構(gòu)體,然后根據(jù)節(jié)點內(nèi)容不同,賦值不同命令,最后調(diào)用adc-core中的函數(shù)操作集中的轉(zhuǎn)換函數(shù)實現(xiàn)AD轉(zhuǎn)換。 | ||||
2.2、應(yīng)用層測試 | ||||
應(yīng)用層測試采用命令行的方式,具體如下圖: | ||||
三、proc文件系統(tǒng)和測試 | ||||
3.1、proc文件系統(tǒng) | ||||
在ADC子系統(tǒng)注冊函數(shù)adc_device_register()中,調(diào)用了adc-proc.c中的adc_proc_add_device(adc); 函數(shù),具體內(nèi)容如下: 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()函數(shù)創(chuàng)建了函數(shù)操作函數(shù)集adc_proc_fops。 2、adc_proc_del_device()函數(shù)在ADC子系統(tǒng)注銷函數(shù)中調(diào)用,是adc_proc_add_device()函數(shù)的相反過程。 3、操作函數(shù)集adc_proc_fops涉及的函數(shù)如下: 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函數(shù)中首先獲得adc-core設(shè)備結(jié)構(gòu)體,使用single_open()函數(shù)調(diào)用dc_proc_show()函數(shù)。 2、釋放函數(shù)調(diào)用single_release()釋放設(shè)備。 3、adc_proc_show()函數(shù)中,使用adc-core的操作函數(shù)集的AD轉(zhuǎn)換函數(shù),實現(xiàn)AD轉(zhuǎn)換,也就是調(diào)用第一篇文章中設(shè)備驅(qū)動的gsc3280AdcCon()函數(shù)。 4、gsc3280AdcCon()分別實現(xiàn)5路AD的轉(zhuǎn)換。 | ||||
3.2、應(yīng)用層測試 | ||||
應(yīng)用層測試采用命令行的方式,具體如下圖: | ||||
四、總結(jié) | ||||
在調(diào)試過程中出現(xiàn)過內(nèi)存泄露的問題,經(jīng)過調(diào)試發(fā)現(xiàn)是在系統(tǒng)啟動過程中,根據(jù)模塊啟動宏的不同,初始化是有順序的。把先使用的模塊先初始化,比如subsys_initcall(gsc_adc_init);。 ADC核心使底層硬件對用戶來說是透明的,并且減少了編寫驅(qū)動程序的工作量。ADC新的驅(qū)動接口提供了更多的功能,使系統(tǒng)可以同時存在多個ADC。/dev,sysfs,proc這三種機制的實現(xiàn)使得應(yīng)用程序能靈活的使用ADC。 ADC核心代碼的組織方式值得學習,不同功能的代碼放在不同的文件中,簡單明了。 如果系統(tǒng)有另外一個AD轉(zhuǎn)換芯片,那么只需新增加設(shè)備驅(qū)動程序,在新的設(shè)備驅(qū)動程序中,使用函數(shù)adc_device_register() 注冊ADC子系統(tǒng),通過idr的id即可區(qū)分不同的ADC設(shè)備。 | ||||
原文參見:http://blog.chinaunix.net/uid-25445243-id-4011337.html | ||||