/* *File Name : devadc.c *Function : based on arm linu s3c2440 adc driver *Type : standard char driver *Time : 2010-3-8 *Author:CQUPT-SSW_feiyang */ #include <linux/module.h> /*EXPORT_SYMBOL_GPL ())*/ #include <linux/moduleparam.h> /*module_param(variable, type, perm); */ #include <linux/init.h>/*module_init(init_function); module_exit(cleanup_function); */ #include <linux/kdev_t.h>/*dev_t*/ #include <linux/fs.h>/* everything... */ #include <linux/cdev.h>/*struct cdev *cdev_alloc(void); */ #include <linux/device.h>/*struct class*/ #include <asm/uaccess.h> /* copy_*_user access_ok*/ #include <asm-arm/plat-s3c/regs-adc.h> #include <linux/wait.h> #include <asm/semaphore.h> #include <asm-arm/arch-s3c2410/map.h> /*ADCCON就是S3C2410_PA_ADC*/ #include<asm/io.h>/*ioemap*/ #include <asm/hardware/clock.h> #include <linux/sched.h>/*request_irq()*/ #include <linux/interrupt.h>/*irqreturn_t*/ #include <asm/signal.h> #include<linux/ioctl.h> #include "devadc.h"
struct adc_dev adc_dev; static int adc_major = 0,adc_minor = 0; static struct class *adc_class; static void __iomem *adc_addr;/* 物理地址映射成虚拟地址后的基地址指针 */ static struct clk *adc_clock; /* 时钟 */
static irqreturn_t adc_irq(int irq, void *dev_id, struct pt_regs *sirq) { /*保证了应用程序读取一次这里就读取AD转换的值一次, 避免应用程序读取一次后发生多次中断多次读取AD转换值*/ if(!adc_dev.ev_adc) { /*读取AD转换后的值保存到全局变量adc_data中,S3C2410_ADCDAT0定义在regs-adc.h中, 这里为什么要与上一个0x3ff,很简单,因为AD转换后的数据是保存在ADCDAT0的第0-9位, 所以与上0x3ff(即:1111111111)后就得到第0-9位的数据,多余的位就都为0*/ adc_dev.data = ioread32(adc_addr + S3C2410_ADCDAT0) & 0x3ff; // PDEBUG("I Am Here\n");
/*将可读标识为1,并唤醒等待队列*/ adc_dev.ev_adc = 1; wake_up_interruptible(&adc_dev.adcq); } return IRQ_HANDLED; } static int adc_open(struct inode *inode, struct file *filp) { int ret;
init_MUTEX(&adc_dev.sem); init_waitqueue_head(&adc_dev.adcq); adc_dev.channel = 0xff; adc_dev.data = 0;
ret = request_irq(IRQ_ADC, adc_irq, SA_INTERRUPT, DEV_NAME, NULL);//申请ADC中断
if (ret) { /*错误处理*/ printk(KERN_WARNING "IRQ %d error %d\n", IRQ_ADC, ret); return -EINVAL; } adc_addr = ioremap(S3C2410_PA_ADC, 0x20); if(adc_addr == NULL) { printk(KERN_WARNING"Iomaped fail!\n"); goto maped_err; } adc_clock = clk_get(NULL, "adc"); if (!adc_clock) { PDEBUG("failed to get adc clock source\n"); goto clk_err; } clk_enable(adc_clock); PDEBUG( "adc opened\n"); return 0; clk_err: clk_disable(adc_clock); clk_put(adc_clock); maped_err: iounmap(adc_addr); return -1; } /*设置ADC控制寄存器,开启AD转换*/ static void start_adc(void) { unsigned int tmp; tmp = (1<<14) | (255<<6) | (adc_dev.channel&(0x0038));/*选择通道和设置时钟*/
writel(tmp, adc_addr + S3C2410_ADCCON); /*AD预分频器使能、模拟输入通道设为AIN0*/ tmp = readl(adc_addr + S3C2410_ADCCON); tmp = tmp | (1 << 0); /* 0 1 00000011 000 0 0 1 */ writel(tmp, adc_addr + S3C2410_ADCCON); /*AD转换开始*/ // PDEBUG("I Am Here\n tmp = %d\n",tmp);
} static int adc_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg) { int err = 0; /*拒绝错误命令*/ if (_IOC_TYPE(cmd) != ADC_IOC_MAGIC) return -ENOTTY; if (_IOC_NR(cmd) > ADC_IOC_MAXNR) return -ENOTTY; /* * the direction is a bitmask, and VERIFY_WRITE catches R/W * transfers. `Type' is user-oriented, while * access_ok is kernel-oriented, so the concept of "read" and * "write" is reversed */ if (_IOC_DIR(cmd) & _IOC_READ) err = !access_ok(VERIFY_WRITE, (void __user *)arg, _IOC_SIZE(cmd)); else if (_IOC_DIR(cmd) & _IOC_WRITE) err = !access_ok(VERIFY_READ, (void __user *)arg, _IOC_SIZE(cmd)); if (err) return -EFAULT; switch(cmd) { case ADC_IOCSCHANNEL : switch(arg) { case 0: adc_dev.channel = (0<<5) ; return 0; case 1: adc_dev.channel = (1<<3); return 0; case 2: adc_dev.channel = (1<<4); return 0; case 3: adc_dev.channel = (3<<3); return 0; default: return -EINVAL; } return 0; default: return -EINVAL; } }
static ssize_t adc_read(struct file *filp, char __user *buffer, size_t count, loff_t *ppos) { ssize_t retval; /*试着获取信号量(即:加锁)*/ if (down_trylock(&adc_dev.sem)) { return -EBUSY; } if(!adc_dev.ev_adc) { if(filp->f_flags & O_NONBLOCK) { /*应用程序若采用非阻塞方式读取则返回错误*/ return -EAGAIN; } else/*以阻塞方式进行读取*/ { /*设置ADC控制寄存器,开启AD转换*/ start_adc(); /*使等待队列进入睡眠*/ wait_event_interruptible(adc_dev.adcq, adc_dev.ev_adc); } } adc_dev.ev_adc = 0;//中断标识//第一次把这个忘了清零,故不能进入中断程序 /*将读取到的AD转换后的值发往到上层应用程序*/ if(copy_to_user(buffer, (char *)&adc_dev.data, sizeof(adc_dev.data))) { retval = -EFAULT; goto out; } up(&adc_dev.sem); return sizeof(adc_dev.data);//昨晚上犯的错误,居然return 0
out: up(&adc_dev.sem); return retval; } /*ADC设备驱动的关闭接口函数*/ static int adc_release(struct inode *inode, struct file *filp) { free_irq(IRQ_ADC, NULL); /*释放中断 特别注意中断应该在文件关闭时释放,即当进程结束时,应该释放中断,不该再拥有,否则再次读文件时会发生中断错误!huanggang大师的没有考虑到此*/ PDEBUG("File Closed!\n"); return 0; } static struct file_operations adc_fops = { .owner = THIS_MODULE, .open = adc_open, .read = adc_read, .release = adc_release, .ioctl = adc_ioctl, }; static int __init adc_init(void) //模块初始化
{ int result; dev_t dev; PDEBUG("Debug Opend!\n"); //我写来测试调试是否打开了的
if (adc_major) { dev = MKDEV(adc_major,adc_minor); result = register_chrdev_region(dev, 1, DEV_NAME);//此处为静态分配主设备号
} else { result = alloc_chrdev_region(&dev, adc_minor, 1, DEV_NAME);//动态分配主设备号,此设备号为0
adc_major = MAJOR(dev);//获取主设备号的宏
} if (result < 0) { printk(KERN_WARNING "ADC: can't get major %d\n", adc_major); return result; }else { printk(KERN_WARNING"Registered ok \nADC_major = %d\n", adc_major); } cdev_init(&adc_dev.cdev, &adc_fops) ;//初始化最先定义的字符设备结构
(adc_dev.cdev).owner = THIS_MODULE; (adc_dev.cdev).ops = &adc_fops; result = cdev_add(&adc_dev.cdev, MKDEV(adc_major,adc_minor), 1);//通知内核有该结构注册,注意考虑失败 if(result < 0){ printk(KERN_WARNING"cdev_add failed!\n"); goto setup_err; } adc_class = class_create(THIS_MODULE, DEV_NAME);//creat自己的设备节点
if(IS_ERR(adc_class)) { printk(KERN_WARNING"Err:faile in adc_class!\n"); return -1; } /*创建设备节点,名字为DEVICE_NAME ,主设备号用上面动态生成的dev*/ class_device_create(adc_class, NULL, MKDEV(adc_major,adc_minor), NULL, DEV_NAME); printk(KERN_WARNING"Leds Module Initialed!\n"); return 0; setup_err: unregister_chrdev_region(MKDEV(adc_major,adc_minor), 1); return -1; } static void __exit adc_exit(void) { unregister_chrdev_region(MKDEV(adc_major, adc_minor ), 1);//销毁设备号 cdev_del(&adc_dev.cdev);//销毁字符设备结构
class_device_destroy(adc_class, MKDEV(adc_major, adc_minor )); //销毁注册的类
class_destroy(adc_class); if(adc_clock)//释放时钟和io内存映射 { clk_disable(adc_clock); clk_put(adc_clock); iounmap(adc_addr); } printk(KERN_WARNING"ADC Module exit!\n"); } module_init(adc_init); module_exit(adc_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_DESCRIPTION("ADC DRVER"); MODULE_AUTHOR("gufeiyang@2010-03-5");
|