最近学习了一本书《linux 设备驱动开发详解》——宋宝华老师写的,不过买的竟然是第二版的,里面是关于处理器s3c6410的,本来看这书的第一版是讲s3c2410,后来发现其实内容差不多了。
学习了一下里面第2篇,设备驱动的核心理论,涉及很多,基础的知识需要反复的推敲才能明白。
主要内容:
1、模块加载函数
static int __init combine_init(void) { //注册设备号和设备名 int result; dev_t dev_num= MKDEV(combine_major,0); //dev_t是cdev结构体的成员,定义了设备号,MAJOR(dev_t),MINOR(dev_t)可以分别获得主设备号和次设备号 char dev_name[] = "combine"; if(combine_major) { result = register_chrdev_region(dev_num, 1, dev_name); //int register_chrdev_region(dev_t from,unsigned count,const char *name); //第一个参数:设备号;第二个参数:连续分配的设备号;第三个参数:设备名} else { result = alloc_chrdev_region(&dev_num,0,1,dev_name); //int alloc_chrdev_region(dev_t *dev,unsigned baseminor,unsigned count,const char *name); combine_major = MAJOR(dev_num); } if(result < 0) { printk("combine:unable to get major %d\n",combine_major);return result; } //关联设备结构体cdev和文件操作结构体fops cdev_init(&combine_cdev, &combine_fops); //static struct cdev combine_cdev; //void cdev_init(struct cdev *,struct file_operation *); result = cdev_add(&combine_cdev, dev_num, 1); //int cdev_add(struct cdev *,dev_t,unsigned); if(result <0 )//加上函数的出错处理 { printk("combine:unable to add cdev\n"); } printk("combine device installed \nwith major %d,%s\n",combine_major,dev_name); return 0; }
static void __exit combine_exit(void) { cdev_del(&combine_cdev); //void cdev_del(struct cdev *); unregister_chrdev_region(MKDEV(combine_major,0), 1); //void unregister_chrdev_region(dev_t from,unsigned count); printk("combine device uninstalled\n");}
static struct file_operations combine_fops = { .owner = THIS_MODULE, .open = combine_open, .release = combine_release, .read = combine_read, .write = combine_write, .ioctl = combine_ioctl, };
中断屏蔽local_irq_disable(),local_irq_enable(),
自旋锁spinlock_t,
信号量struct semaphore sem,
完成量struct completion,
互斥体struct mutex来解决并发与竞态问题。
4、阻塞操作
当应用程序进行read(),write()等系统调用时,若设备的资源不能获取,而用户又希望以阻塞的方式访问设备,驱动程序应在设备驱动的xxx_read(),xxx_write()等操作中将进程阻塞直到资源可以获取,此后应用程序的read(),write()等调用才返回。
用于设备阻塞操作可以使用等待队列wait_queue_head_t my_queue;
将事件加入等待队列wait_event_interrupt(my_queue,condition);
将事件从等待队列中唤醒wake_up_interrupt(&my_queue);
或者利用unsigned int poll(struct file *filp,struct poll_table *wait);查询是否可对设备进行无阻塞访问
struct unsigned int xxx_poll(struct file *filp,poll_table *wait) { unsigned int mask=0; struct xxx_dev *dev = filp->private_data;//获取设备结构体指针 poll_wait(filp,my_queue,wait); //这个函数加了之后再read中就不用再wait_event_interrupt, //因为在这里已经实现了阻塞,一旦资源可用就可以在read中实现无阻塞访问 //像read中wait_event_interrupt一样被wake_up_interrupt唤醒 if(...)//可读 mask |= POLLIN | POLLRDNORM;//标示数据可获得 if(...)//可写 mask |= POLLOUT | POLLWRNORM;//标示数据可写入 return mask;<pre name="code" class="cpp">}
5、linux中断编程
<pre name="code" class="html">struct key_irq_desc { unsigned int irq; int pin; int pin_setting; int number; char *name; }; static struct key_irq_desc key_irqs[] = { {IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "key1"}, {IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 1, "key2"}, {IRQ_EINT13, S3C2410_GPG(5), S3C2410_GPG5_EINT13, 2, "key3"}, {IRQ_EINT14, S3C2410_GPG(6), S3C2410_GPG6_EINT14, 3, "key4"}, {IRQ_EINT15, S3C2410_GPG(7), S3C2410_GPG7_EINT15, 4, "key5"}, {IRQ_EINT19, S3C2410_GPG(11),S3C2410_GPG11_EINT19, 5, "key6"}, }; static volatile int key_values[] = {0,0,0,0,0,0}; static DECLARE_WAIT_QUEUE_HEAD(key_waitq); static volatile int ev_press = 0; static void my_tasklet_func(unsigned long data) { printk("key do tasklet\n"); } static DECLARE_TASKLET(my_tasklet,my_tasklet_func,0); static irqreturn_t key_interrupt(int irq,void *dev_id)//中断顶半部函数 { struct key_irq_desc *key_irqs = (struct key_irq_desc *)dev_id; int up = s3c2410_gpio_getpin(key_irqs->pin); printk("<1>up=%d\n",up); if(up)//up 按下去是0,没按是1 key_values[key_irqs->number] = (key_irqs->number + 1) + 0x80; else key_values[key_irqs->number] += 1; ev_press = 1; wake_up_interruptible(&key_waitq); tasklet_schedule(&my_tasklet);//在顶半部函数中调度执行底半部函数 return IRQ_RETVAL(IRQ_HANDLED); } static int combine_open(struct inode *inode,struct file *file) { int i,err; for(i = 0;i < sizeof(key_irqs) / sizeof(key_irqs[0]);i++) { s3c2410_gpio_cfgpin(key_irqs[i].pin,key_irqs[i].pin_setting); err = request_irq(key_irqs[i].irq, key_interrupt, 0, key_irqs[i].name, (void *)&key_irqs[i]); //int request_irq(unsigned int irq,irq_handler_t handler,unsigned long irqflags,const char *devname,void *dev_id); //handler是向系统登记的中断处理顶半部函数,dev_id是传给他的参数 set_irq_type(key_irqs[i].irq,IRQ_TYPE_EDGE_BOTH); if(err) break; } if(err) { i--; for(;i >= 0;i--) { disable_irq(key_irqs[i].irq); free_irq(key_irqs[i].irq, (void *)&key_irqs[i]); //void free_irq(unsigned int irq,void *dev_id); }return -EBUSY;}return 0;}
6、下面是完整的按键与蜂鸣器结合的字符驱动函数,资源上传于:http://download.csdn.net/detail/luckywang1103/6537111
<pre name="code" class="cpp">/********************************************************************************************* * File: conbine.c * Author: luckywang * Desc: 组合各种驱动,驱动6个按键,和beep驱动,但是现在没有将两者结合起来,只是各自独立驱动 * History: 2013.11.09 *********************************************************************************************/ #include <linux/init.h> #include <linux/module.h> #include <linux/errno.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/input.h> #include <linux/serio.h> #include <linux/delay.h> #include <linux/clk.h> #include <linux/miscdevice.h> #include <linux/gpio.h> #include <asm/io.h> #include <linux/irq.h> #include <asm/irq.h> #include <asm/uaccess.h> #include <mach/regs-clock.h> #include <plat/regs-timer.h> #include <mach/regs-gpio.h> #include <linux/cdev.h> #include <linux/sched.h> #include <mach/hardware.h> #include <linux/platform_device.h> #include <linux/interrupt.h> #include <linux/wait.h> static int combine_major = 0; static struct cdev combine_cdev; #define BEEP_MAGIC 'k' #define BEEP_START_CMD _IO(BEEP_MAGIC,1) #define BEEP_STOP_CMD _IO(BEEP_MAGIC,2) struct key_irq_desc { unsigned int irq; int pin; int pin_setting; int number; char *name; }; static struct key_irq_desc key_irqs[] = { {IRQ_EINT8, S3C2410_GPG(0), S3C2410_GPG0_EINT8, 0, "key1"}, {IRQ_EINT11, S3C2410_GPG(3), S3C2410_GPG3_EINT11, 1, "key2"}, {IRQ_EINT13, S3C2410_GPG(5), S3C2410_GPG5_EINT13, 2, "key3"}, {IRQ_EINT14, S3C2410_GPG(6), S3C2410_GPG6_EINT14, 3, "key4"}, {IRQ_EINT15, S3C2410_GPG(7), S3C2410_GPG7_EINT15, 4, "key5"}, {IRQ_EINT19, S3C2410_GPG(11),S3C2410_GPG11_EINT19,5, "key6"}, }; static volatile int key_values[] = {0,0,0,0,0,0}; static DECLARE_WAIT_QUEUE_HEAD(key_waitq); static volatile int ev_press = 0; static void my_tasklet_func(unsigned long data) { printk("key do tasklet\n"); } static DECLARE_TASKLET(my_tasklet,my_tasklet_func,0); static irqreturn_t key_interrupt(int irq,void *dev_id) { struct key_irq_desc *key_irqs = (struct key_irq_desc *)dev_id; int up = s3c2410_gpio_getpin(key_irqs->pin); printk("<1>up=%d\n",up); if(up)//up 按下去是0,没按是1 key_values[key_irqs->number] = (key_irqs->number + 1) + 0x80; else key_values[key_irqs->number] += 1; ev_press = 1; wake_up_interruptible(&key_waitq); tasklet_schedule(&my_tasklet); return IRQ_RETVAL(IRQ_HANDLED); } static int combine_open(struct inode *inode,struct file *file) { int i,err; for(i = 0;i < sizeof(key_irqs) / sizeof(key_irqs[0]);i++) { s3c2410_gpio_cfgpin(key_irqs[i].pin,key_irqs[i].pin_setting); err = request_irq(key_irqs[i].irq, key_interrupt, 0, key_irqs[i].name, (void *)&key_irqs[i]); set_irq_type(key_irqs[i].irq,IRQ_TYPE_EDGE_BOTH); if(err) break; } if(err) { i--; for(;i >= 0;i--) { disable_irq(key_irqs[i].irq); free_irq(key_irqs[i].irq, (void *)&key_irqs[i]); } return -EBUSY; } return 0; } static int combine_release(struct inode *inode,struct file *file) { int i; for(i = 0;i < sizeof(key_irqs) / sizeof(key_irqs[i]);i++) { disable_irq(key_irqs[i].irq); free_irq(key_irqs[i].irq,(void *)&key_irqs[i]); } return 0; } static int combine_read(struct file *filp,char __user *buff,size_t count,loff_t *offp) { unsigned long err; if(!ev_press) { if(filp->f_flags & O_NONBLOCK) return -EAGAIN; else wait_event_interruptible(key_waitq, ev_press); } ev_press = 0; err = copy_to_user(buff,(const void *)key_values,min(sizeof(key_values),count)); memset((void *)key_values,0,sizeof(key_values)); return err ? -EFAULT : min(sizeof(key_values),count); } static int combine_write(struct file *file,const char __user *buff,size_t count,loff_t *offp) { return 0; } void beep_start(void) { s3c2410_gpio_pullup(S3C2410_GPB(0),1); s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); s3c2410_gpio_setpin(S3C2410_GPB(0),1); } void beep_stop(void) { s3c2410_gpio_cfgpin(S3C2410_GPB(0), S3C2410_GPIO_OUTPUT); s3c2410_gpio_setpin(S3C2410_GPB(0),0); } static int combine_ioctl(struct inode *inode,struct file *file,unsigned int cmd,unsigned long arg) { switch(cmd) { case BEEP_START_CMD: beep_start(); break; case BEEP_STOP_CMD: beep_stop(); break; default: break; } return 0; } static struct file_operations combine_fops = { .owner = THIS_MODULE, .open = combine_open, .release = combine_release, .read = combine_read, .write = combine_write, .ioctl = combine_ioctl, }; static int __init combine_init(void) { //注册设备号和设备名 int result; dev_t dev_num= MKDEV(combine_major,0); char dev_name[] = "combine"; if(combine_major) { result = register_chrdev_region(dev_num, 1, dev_name); } else { result = alloc_chrdev_region(&dev_num,0,1,dev_name); combine_major = MAJOR(dev_num); } if(result < 0) { printk("combine:unable to get major %d\n",combine_major); return result; } //关联设备结构体cdev和文件操作结构体fops cdev_init(&combine_cdev, &combine_fops); result = cdev_add(&combine_cdev, dev_num, 1); if(result <0 )//加上函数的出错处理 { printk("combine:unable to add cdev\n"); } printk("combine device installed \nwith major %d,%s\n",combine_major,dev_name); return 0; } static void __exit combine_exit(void) { //printk("combine driver exit\n"); cdev_del(&combine_cdev); unregister_chrdev_region(MKDEV(combine_major,0), 1); printk("combine device uninstalled\n"); } module_init(combine_init); module_exit(combine_exit); MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("luckywang"); MODULE_DESCRIPTION("s3c2440 combine driver"); MODULE_VERSION("V0.1");
测试程序
<p></p><p><span style="font-size:14px;"></span></p><pre name="code" class="cpp">/********************************************************************************************* * File: combine_test.c * Author: luckkywang * Desc: combine test code * History:2013.11.10 *********************************************************************************************/ #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <linux/ioctl.h> #include<errno.h> #define BEEP_MAGIC 'k' #define BEEP_START_CMD _IO (BEEP_MAGIC, 1) #define BEEP_STOP_CMD _IO (BEEP_MAGIC, 2) int main() { int i = 0; int key_values[] = {0,0,0,0,0,0}; int dev_fd; int ret; dev_fd = open("/dev/combine",O_RDWR | O_NONBLOCK); if ( dev_fd == -1 ) { printf("Cann't open file /dev/beep\n"); exit(1); } while(1) { ret = read(dev_fd,key_values,sizeof(key_values)); if(ret != sizeof(key_values)) { if(errno != EAGAIN) perror("read key\n"); continue; } else { for(i = 0;i < 6;i++) { printf("K%d %s,key_value=0x%02x\n",i+1,(key_values[i] & 0x80) ? "released" : key_values[i] ? "pressed down":"",key_values[i]); } key_values[i] = 0; } } /*printf("Start beep\n"); ioctl (dev_fd, BEEP_START_CMD,0); getchar(); ioctl (dev_fd, BEEP_STOP_CMD,0); printf("Stop beep and Close device\n");*/ close(dev_fd); return 0; }