|
- /********************************************************************/
- /*********************下面是前面申明函数的实现***********************/
- /********************************************************************/
- /**********************mini2440_buttons_init()***********************/
- static int __init mini2440_buttons_init(void)
- {
- int ret; /*设备注册的返回值*/
- /*注册设备驱动程序*/
- /*设备号,设备名,和驱动函数*/
- ret = register_chrdev(BUTTON_MAJOR,DEVICE_NAME,&mini2440_buttons_fops);
- /*对注册失败的处理*/
- if(ret < 0)
- {
- printk(DEVICE_NAME " can't register major number\n");
- return ret;
- }
- /*创建设备*/
- /*devfs_mk_cdev()函数是内核态的设备创建函数*/
- /*而mknod是用户态的设备创建函数*/
- devfs_mk_cdev(MKDEV(BUTTON_MAJOR,0),S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,DEVICE_NAME);
- printk(DEVICE_NAME " initialized\n");
- return 0;
- }
- /******************mini2440_buttons_exit()****************************/
- static void __exit mini2440_buttons_exit(void)
- {
- /*移除设备*/
- devfs_remove(DEVICE_NAME);
- /*注消设备驱动*/
- unregister_chrdev(BUTTON_MAJOR,DEVICE_NAME);
- }
- /*****************mini2440_buttons_open()******************************/
- static int mini2440_buttons_open(struct inode *inode,struct file *file)
- {
- int i; /*循环变量,因为有6个按钮*/
- int err; /*中断注册函数的返回值*/
- /*对每个按钮分别处理,用for循环来做*/
- /*具体地是要联结寄存器和相应的引脚*/
- /*联结中断号和相应的中断服务程序*/
- /*这一步类似于前面所说的驱动的注册*/
- /*我们可以成功称作中断的注册*/
- for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
- {
- /*寄存器与中断引脚的联结*/
- s3c2410_gpio_cfgpin(button_irqs[i].pin,button_irqs[i].pin_setting);
- /*中断的注册*/
- /*request_irq()函数*/
- /*要注意其输入参数*/
- /*&button_irqs[i]是该中断享有的资源*/
- /*会被传入buttons_interrupt,进行处理*/
- err = request_irq(button_irqs[i].irq,buttons_interrupt,NULL,button_irqs[i].name,(void *)&button_irqs[i]);
- /*中断类型的设置*/
- /*set_irq_type()函数*/
- /*IRQT_BOTHEDGE的中断类型代表什么样的中断呢?*/
- /*有几个非常重要的问题*/
- /*中断注册后,并设置好其中断类型之后,当有中断发生时,*/
- /*即按下某个按钮时,系统能够自动检测到有中断发生吗?*/
- /*检测到有中断发生,它能够自动辨别是几号中断吗?*/
- /*知道了是几号中断,那么它能自动调用其中断服务程序吗?*/
- /*对这几个问题的解答,够成了linux系统中断处理机制的核心*/
- set_irq_type(button_irqs[i].irq,IRQT_BOTHEDGE);
- /*注册失败的处理*/
- if(err)
- break; /*跳出循环*/
- }
- /*若有一个按钮中断注册失败*/
- /*则还需把前面注册成功的中断给拆了*/
- if(err)
- {
- i--; /*回到前面一个按钮的处理*/
- for(;i >=0; i--) /*依此拆除*/
- {
- /*使中断不起作用*/
- disable_irq(button_irqs[i].irq);
- /*释放中断资源*/
- free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
- }
- return -EBUSY; /*中断注册没成功的最终的返回值*/
- }
- return 0; /*正常返回*/
- }
- /**************************buttons_interrupt()*****************************/
- /*此中断服务程序,在每中断一次,就要对key_values数组设一下值*/
- /*并对数组可读标志位ev_press设一下值*/
- /*并唤醒在等待队列里的进程*/
- /*这是中断处理经常要做的事情*/
- /*在这里,等待队列button_waitq里经常等待的进程是数组的读取进程*/
- /*就是说,读取进程在没有读到数据的时候就一直在等待,等待按键的输入*/
- /*读取进程在等待,并不代表所有进程在等待,其它进程该干啥干啥去*/
- static irqreturn_t buttons_interrupt(int irq,void *dev_id)
- {
- /*button_irq_desc结构体变量*/
- /*对传入的资源进行处理*/
- struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
- /*获取寄存器的值*/
- /*这一步至关重要*/
- /*s3c2410_gpio_getpin()函数直接获取寄存器的值*/
- /*要注意,按一下按钮,会发生两次中断*/
- /*即按下是一次中断,放开又是一次中断*/
- int up = s3c2410_gpio_getpin(button_irqs->pin);
- /*通过电路原理图,可以知道没按下的时候,中断引脚应该是高电平*/
- /*从而寄存器的值应该是1*/
- /*变量取up也是有意义的,表示默认状态是弹起的状态*/
- /*当按下按钮的状态下,寄存器的值就应该是0*/
- /*下面对up的值进行处理*/
- /*即是要把数据经过一定的变换存入key_values数组中*/
- if(up) /*如果是弹起的状态*/
- /*那么就要在key_values数组的相应位存入很大的一个值*/
- /*同时又要能从值里辨别出是哪个按键*/
- key_values[button_irqs->number] = (button_irqs->number + 1) + 0x80;
- /*比如K1键开启的状态下,key_values[0]被置为(0+1)+0x80,即为129*/
- else /*如果按键是闭合的状态*/
- /*那么就要在key_values数组的相应位存入一个很小的数*/
- /*同时又要能从值中辨别出是哪个键*/
- key_values[button_irqs->number] = (button_irqs->number + 1);
- /*比如K1键闭合,则key_values[0]被置为(0+1),即为1*/
- /*对数组可读标志位进行设置*/
- ev_press = 1; /*表示数组已经可读了*/
- /*唤醒休眠的进程?*/
- /*button_waitq队列里存放有相应的处理进程*/
- /*如读取数组的值的进程*/
- /*要注意wake_up_interruptible()这些函数的用法*/
- wake_up_interruptible(&button_waitq);
- /*返回*/
- return IRQ_RETVAL(IRQ_HANDLED); /*?*/
- }
- /**********************mini2440_buttons_close()*****************************/
- static int mini2440_buttons_close(struct inode *inode,struct file *file)
- {
- int i; /*循环变量,要操作好几个按键*/
- /*for循环,对各个按键依此释放中断*/
- for(i = 0;i < sizeof(button_irqs)/sizeof(button_irqs[0]);i++)
- {
- /*使中断失效*/
- disable_irq(button_irqs[i].irq);
- /*释放资源*/
- free_irq(button_irqs[i].irq,(void *)&button_irqs[i]);
- }
- /*返回*/
- return 0;
- }
- /**********************mini2440_buttons_read()***************************/
- /*要注意,该read函数,只读取一次中断的值,而不是连续地读入*/
- /*要做到连续地读入,则需要做一个循环,不断地调用该read函数,但那不是驱动程序里该做的事情*/
- static int mini2440_buttons_read(struct file *filp,char __user *buff,size_t count,loff_t *offp)
- {
- unsigned long err; /*copy_to_user()函数的返回值*/
- /*如果key_values 数组里没有值,则会此进程会休眠*/
- /*一直到中断来临之后,中断服务程序会唤醒此休眠进程从而继续读取值*/
- /*key_values数组里有没有值,是靠ev_press标志位来判断的*/
- /*有值,就是1,无值,就是0*/
- /*进程等待队列的机制,是进程调度的一种方法*/
- if(!ev_press) /*标志位为0,即无数据时*/
- {
- if(filp->f_flags & O_NONBLOCK) /*??*/
- return -EAGAIN;
- else /*进程休眠,放进button_waitq等待队列*/
- /*这里,把ev_press标志位设成了休眠进程的标志位了?*/
- /*这是为了便于利用poll_wait函数*/
- /*也就是利于select函数*/
- wait_event_interruptible(button_waitq,ev_press);
- /*在中断处理函数中,此进程会被唤醒*/
- /*唤醒前,ev_press 已被置1了*/
- /*唤醒后的执行点从这里开始*/
- }
- /*下面就是标志位为1,即有数据可读的的处理情况*/
- /*那就开始往用户空间读数据呗*/
- err = copy_to_user(buff,(const void *)key_values,min(sizeof(key_values),count));
- /*copy_to_user()函数的使用*/
- /*对key_values数组清零*/
- memset((void *)key_values,0,sizeof(key_values));
- /*对标志位置0*/
- /*表示读取过了*/
- ev_press = 0;
- /*对err的处理*/
- if(err) /*读取错误*/
- return -EFAULT;
- else /*读取正确*/
- /*则返回读取到的字节数*/
- return min(sizeof(key_values),count);
- }
- /************************mini2440_buttons_poll()***********************/
- static unsigned int mini2440_buttons_poll(struct file *file,struct poll_table_struct *wait)
- {
- unsigned int mask = 0; /* */
- /*poll_wait()函数*/
- /*会监测进程队列button_waitq里的进程*/
- /*例如,如果mini2440_button_read所在的进程的标志位ev_press置为1了*/
- /*那么就不会再等待了*/
- /*这实质上就是select函数的运行机制*/
- poll_wait(file,&button_waitq,wait);
- if(ev_press)
- mask |= POLLIN | POLLRDNORM; /*??*/
- return mask;
- }
- ==================================================================================
- 下面是测试代码:
- /*按键测试程序*/
- #include /*标准输入输出头文件*/
- #include /*标准库*/
- #include /*一些宏的定义在这里*/
- #include /*设备的控制*/
- #include /*定义了一些类型*/
- #include /*状态*/
- #include /*文件控制*/
- #include /*选择?*/
- #include /*时间方面的函数*/
- #include /*有关错误方面的宏*/
- /*主函数入口*/
- int main(void)
- {
- int i; /*键盘输出时用到的循环变量*/
- int buttons_fd; /*buttons设备号*/
- int key_value[4]; /*四个按键的取值*/
- /*打开键盘设备文件*/
- buttons_fd = open("/dev/buttons",0); /*以0方式打开*/
- /*打开出错处理*/
- if(buttons_fd < 0) /*打开出错就会返回一个负值*/
- {
- perror("open device buttons"); /*perror函数?*/
- exit(1); /*返回1*/
- }
- /*for无限循环,等待用户输入*/
- /*这是很典型的程序执行方式*/
- for(;;)
- {
- fd_set rds; /*fd_set是types.h中定义的类型,实质上是int型*/
- /*rds用来存储设备号*/
- int ret; /*for循环内定义的局部变量ret*/
- FD_ZERO(&rds); /*rds初始化*/
- /*FD_ZERO是哪里定义的呢?*/
- FD_SET(buttons_fd,&rds); /*将buttons设备号赋给rds*/
- /*FD_SET是哪里定义的呢?*/
- /*使用系统调用select检查是否能够从/dev/buttons设备读取数据*/
- /*select函数是干什么的呢?*/
- ret = select(buttons_fd + 1,&rds,NULL,NULL,NULL);
- /*返回值ret*/
- /*返回值的具体意义是什么呢?*/
- /*对ret的处理*/
- if(ret < 0) /*当ret小于0*/
- {
- perror("select");
- exit(1);
- }
- if(ret == 0) /*当ret等于0*/
- {
- printf("Timeout.\n");
- }
- else /*能够读到数据*/
- if(FD_ISSET(buttons_fd,&rds)) /*??*/
- {
- /*读取键盘驱动发出的数据*/
- /*key_value和键盘驱动中定义一致*/
- int ret = read(buttons_fd,key_value,sizeof(key_value)); /*注意此处的ret和前面的ret有何不同*/
- /*注意键盘设备读取的特点*/
- /*对ret的处理*/
- if(ret != sizeof(key_value)) /*没有接收够*/
- {
- if(errno != EAGAIN) /*???*/
- perror("read buttons\n");
- continue;
- }
- else /*正确接收,则打印到标准终端*/
- {
- for(i = 0;i < 4;i++) /*最开始定义的循环变量i*/
- printf("K%d %s, key value = 0x%02x\n",i,(key_value[i] & 0x80) ? "released" : key_value[i] ? "pressed down" : "",key_value[i]);
- /*这一连串的输出,要注意格式*/
- }
- }
- }
- /*关闭设备*/
- close(buttons_fd);
- return 0; /*主函数返回*/
- }
- END!!
- 原文地址 http://hi.baidu.com/zhxust/blog/item/26b07c3ec76370f2828b13a3.html?
复制代码 |
|