科创园

科创园地,分享技术知识,为科技助力发展,贡献一己之力。
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

linux驱动之platform总线

Posted on 2013-02-20 10:09  科创园  阅读(1090)  评论(0编辑  收藏  举报

在linux2.6内核驱动中,为设备实现一套中断处理机制提供了如下两个步骤:

1.向内核注册中断处理;2.实现中断处理函数

int request_irq(unsigned int irq,void (*handler)(int, void*, struct pt_regs *),unsigned long flags,const char *devname,void *dev_id)       //注册中断;返回0表示成功,或者返回一个错误码

参数含义

unsigned int irq          中断号

void (*handler)(int,void *,struct pt_regs *)    中断处理函数

unsigned long flags     与中断管理有关的各种选项;如IRQF_DISABLED,IRQF_SHARED

const char * devname                    设备名

void *dev_id                             共享中断时使用

注意1 共享设备就是将不同的设备挂接到同一个中断信号线上,liunx对共享的支持主要是用在PCI设备服务。且在共享中断时,dev_id必须唯一指定。

注意2  共享中断的处理程序中,不能使用 disable_irq(unsigned int irq)   因为如果使用了这个函数,共享中断信线的其它设备将同样无法使用中断,也就无法正常工作了。

handler中断处理程序

中断处理程序就是普通的C代码。特别之处在于中断处理程序是在中断上下文中运行的,它的行为受到某些限制

1. 不能向用户空间发送或接受数据   2. 不能使用可能引起阻塞的函数  3. 不能使用可能引起调度的函数

中断处理函数一个实例如下

 1 void short_sh_interrupt(int irq, void *dev_id, struct pt_regs *regs) 
 2 { 
 3   /* 判断是否是本设备产生了中断(为什么要做这样的检测?    共享中断) */    
 4    value = inb(short_base); 
 5    if (!(value & 0x80)) return; 
 6    /* 清除中断位(如果设备支持自动清除,则不需要这步) */ 
 7    outb(value & 0x7F, short_base); 
 8    /* 中断处理,通常是数据接收*/ 
 9    。。。。。。。。。 
10     /* 唤醒等待数据的进程*/ 
11     wake_up_interruptible(&short_queue); 
12 }

释放中断函数

void free_irq(unsigned int irq, void *dev_id)   //当设备不再需要使用中断时(通常在驱动卸载时), 应当把它们返还给系统

附s3c24xx按键中断驱动程序源码:

View Code
  1 #include <linux/module.h>
  2 #include <linux/kernel.h>
  3 #include <linux/fs.h>
  4 #include <linux/init.h>
  5 #include <linux/delay.h>
  6 #include <linux/poll.h>
  7 #include <linux/irq.h>
  8 #include <asm/irq.h>
  9 #include <linux/interrupt.h>
 10 #include <asm/uaccess.h>
 11 #include <mach/regs-gpio.h>
 12 #include <mach/hardware.h>
 13 #include <linux/platform_device.h>
 14 #include <linux/cdev.h>
 15 #include <linux/miscdevice.h>
 16 #include <linux/sched.h>
 17 #include <linux/gpio.h>
 18 
 19 #define DEVICE_NAME     "buttons"
 20 
 21 struct button_irq_desc {
 22     int irq;
 23     int pin;
 24     int pin_setting;
 25     int number;
 26     char *name; 
 27 };
 28 
 29 //#define       DEBUG
 30 #undef  DEBUG
 31 #define QIUSHI
 32 #define START_BUTTON    8
 33 
 34 #ifdef  QIUSHI
 35 static struct button_irq_desc button_irqs [] = {
 36     {IRQ_EINT0, S3C2410_GPF(0),  S3C2410_GPF0_EINT0, 0, "KEY0"},
 37     {IRQ_EINT1, S3C2410_GPF(1),  S3C2410_GPF1_EINT1, 1, "KEY1"},
 38     {IRQ_EINT2, S3C2410_GPF(2),  S3C2410_GPF2_EINT2, 2, "KEY2"},
 39     {IRQ_EINT3, S3C2410_GPF(3),  S3C2410_GPF3_EINT3, 3, "KEY3"},
 40     {IRQ_EINT4, S3C2410_GPF(4),  S3C2410_GPF4_EINT4, 4, "KEY4"},
 41     {IRQ_EINT5, S3C2410_GPF(5),  S3C2410_GPF5_EINT5, 5, "KEY5"},
 42     {IRQ_EINT6, S3C2410_GPF(6),  S3C2410_GPF6_EINT6, 6, "KEY6"},
 43     {IRQ_EINT8, S3C2410_GPG(0),  S3C2410_GPG0_EINT8, 7, "KEY7"},
 44     {IRQ_EINT9, S3C2410_GPG(1),  S3C2410_GPG1_EINT9, 8, "KEY8"},
 45 };
 46 #else
 47 static struct button_irq_desc button_irqs [] = {
 48     {IRQ_EINT8 , S3C2410_GPG(0) ,  S3C2410_GPG0_EINT8  , 0, "KEY0"},
 49     {IRQ_EINT11, S3C2410_GPG(3) ,  S3C2410_GPG3_EINT11 , 1, "KEY1"},
 50     {IRQ_EINT13, S3C2410_GPG(5) ,  S3C2410_GPG5_EINT13 , 2, "KEY2"},
 51     {IRQ_EINT14, S3C2410_GPG(6) ,  S3C2410_GPG6_EINT14 , 3, "KEY3"},
 52     {IRQ_EINT15, S3C2410_GPG(7) ,  S3C2410_GPG7_EINT15 , 4, "KEY4"},
 53     {IRQ_EINT19, S3C2410_GPG(11),  S3C2410_GPG11_EINT19, 5, "KEY5"},
 54 };
 55 #endif
 56 
 57 static volatile char key_values [] = {'0', '0', '0', '0', '0', '0', '0', '0', '0'};
 58 
 59 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 60 
 61 static volatile int ev_press = 0;
 62 
 63 
 64 static irqreturn_t buttons_interrupt(int irq, void *dev_id)
 65 {
 66     struct button_irq_desc *button_irqs = (struct button_irq_desc *)dev_id;
 67     int down;
 68 
 69     // udelay(0);
 70     down = !s3c2410_gpio_getpin(button_irqs->pin);
 71         
 72 #ifdef  DEBUG   
 73     printk( "%s: new=%d, old=%d\n", __FUNCTION__ , down,  (key_values[button_irqs->number] & 1));
 74 #endif
 75 
 76     if (down != (key_values[button_irqs->number] & 1)) { // Changed
 77 
 78         key_values[button_irqs->number] = '0' + down;
 79         
 80         ev_press = 1;
 81         wake_up_interruptible(&button_waitq);
 82     }
 83     return IRQ_RETVAL(IRQ_HANDLED);
 84 }
 85 
 86 
 87 static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
 88 {
 89     int i;
 90     int err = 0;
 91     
 92     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
 93         if (button_irqs[i].irq < 0) {
 94                 continue;
 95         }
 96         err = request_irq(button_irqs[i].irq, buttons_interrupt, IRQ_TYPE_EDGE_BOTH, 
 97                           button_irqs[i].name, (void *)&button_irqs[i]);
 98         if (err)
 99         {
100             printk( "%s: err=%d, but=%d\n", __FUNCTION__, err, i);
101             break;
102         }
103     }
104 /*错误处理*/
105     if (err) {
106         i--;
107         for (; i >= 0; i--) {
108             if (button_irqs[i].irq < 0) {
109                 continue;
110             }
111             disable_irq(button_irqs[i].irq);
112             free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
113         }
114         return -EBUSY;
115     }
116 
117     ev_press = 1;
118     
119     return 0;
120 }
121 
122 
123 static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
124 {
125     int i;
126     
127     for (i = 0; i < sizeof(button_irqs)/sizeof(button_irqs[0]); i++) {
128         if (button_irqs[i].irq < 0) {
129             continue;
130         }
131         free_irq(button_irqs[i].irq, (void *)&button_irqs[i]);
132     }
133 
134     return 0;
135 }
136 
137 
138 static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
139 {
140     unsigned long err;
141 
142     if (!ev_press) {
143         if (filp->f_flags & O_NONBLOCK)
144             return -EAGAIN;
145         else
146             wait_event_interruptible(button_waitq, ev_press);
147     }
148     
149     ev_press = 0;
150 
151     err = copy_to_user(buff, (const void *)key_values, min(sizeof(key_values), count));
152 
153     return err ? -EFAULT : min(sizeof(key_values), count);
154 }
155 
156 /*poll做两件事?poll_wait指明使用的等待队列,返回掩码*/
157 
158 static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
159 {
160     unsigned int mask = 0;
161     poll_wait(file, &poll_wait, wait);
162     if (ev_press)  /*按下evpress为1,有数据刻可读*/
163         mask |= POLLIN | POLLRDNORM;
164     return mask;
165 }
166 
167 
168 
169 
170 static int s3c2410_buttons_ioctl(
171         struct inode *inode, 
172         struct file *file, 
173         unsigned int cmd, 
174         unsigned long arg)
175 {       int down;
176         down = s3c2410_gpio_getpin(button_irqs[START_BUTTON].pin);
177         return down;
178 }
179 
180 
181 static struct file_operations dev_fops = {
182     .owner   =   THIS_MODULE,
183     .open    =   s3c24xx_buttons_open,
184     .release =   s3c24xx_buttons_close, 
185     .read    =   s3c24xx_buttons_read,
186     .poll    =   s3c24xx_buttons_poll,
187     .ioctl  = s3c2410_buttons_ioctl,
188 };
189 
190 
191 static struct miscdevice misc = {
192         .minor = MISC_DYNAMIC_MINOR,
193         .name = DEVICE_NAME,
194         .fops = &dev_fops,
195 };
196 
197 static int __init dev_init(void)
198 {
199         int ret;
200 
201         ret = misc_register(&misc);
202 
203         printk (DEVICE_NAME"\tinitialized\n");
204 
205         return ret;
206 }
207 
208 static void __exit dev_exit(void)
209 {
210         misc_deregister(&misc);
211 }
212 
213 module_init(dev_init);
214 module_exit(dev_exit);
215 MODULE_LICENSE("GPL");
216 MODULE_AUTHOR("QiuShi Inc.");

 

platform总线是linux2.6内核后加入的一条虚拟总线,由两部分组成:platform_device和platform_driver;优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,提高可可移植性。

结构体平台设备 struct platform_device

View Code
 1 struct platform_device {
 2     const char    * name;
 3     int        id;
 4     struct device    dev;
 5     u32        num_resources;
 6     struct resource    * resource;
 7 
 8     const struct platform_device_id    *id_entry;
 9 
10     /* arch specific additions */
11     struct pdev_archdata    archdata;
12 };

struct platform_device*platform_device_alloc(const char *name, int id)  //平台设备分配函数

int platform_device_add(struct platform_device *pdev)   平台设备注册函数

平台设备资源 结构体 struct resource

1 struct resource {
2     resource_size_t start;   //资源的起始物理地址
3     resource_size_t end;    //资源的结束物理地址
4     const char *name;       //资源的名称
5     unsigned long flags;     //资源的类型;常用有MEM,IO,IRQ等
6     struct resource *parent, *sibling, *child;  //资源的链表指针
7 };
设备资源分配实例:
View Code
 1 static struct resource s3c_wdt_resource1 = { 
 2 
 3          .start = 0x44100000, 
 4 
 5          .end   = 0x44200000, 
 6 
 7          .flags = IORESOURCE_MEM, 
 8 
 9    } 
10 
11 static struct resource s3c_wdt_resource2 = { 
12 
13          .start = 20, 
14 
15          .end   = 20, 
16 
17          .flags = IORESOURCE_IRQ, 
18 
19    } 

 

struct resource *platform_get_resource(struct platform_device *dev, unsigned int type, unsigned int num)                       //获取资源分配函数

参数:

vdev: 资源所属的设备

vtype: 获取的资源类型

vnum: 获取的资源数

例:

platform_get_resource(pdev, IORESOURCE_IRQ, 0)                  //获取中断

 

结构体平台驱动 struct platform_driver

1 struct platform_driver {
2     int (*probe)(struct platform_device *);
3     int (*remove)(struct platform_device *);
4     void (*shutdown)(struct platform_device *);
5     int (*suspend)(struct platform_device *, pm_message_t state);
6     int (*resume)(struct platform_device *);
7     struct device_driver driver;
8     const struct platform_device_id *id_table;
9 };

int platform_driver_register(struct platform_driver *)     //平台驱动注册使用函数

void platform_driver_unregister(struct platform_driver *);  //平台驱动卸载使用函数

下例结合mini2440利用平台驱动模型来分析按键中断程序

plat_device.c

View Code
 1 #include <linux/module.h>
 2 #include <linux/types.h>
 3 #include <linux/fs.h>
 4 #include <linux/init.h>
 5 #include <linux/platform_device.h>
 6 #include <mach/regs-gpio.h>
 7 #include <linux/interrupt.h>
 8 #include <linux/device.h>
 9 #include <linux/io.h>
10 
11 /*平台资源的定义*/
12 static struct resource s3c_buttons_resource[] = {
13     [0]={
14         .start = S3C24XX_PA_GPIO,
15         .end   = S3C24XX_PA_GPIO + S3C24XX_SZ_GPIO - 1,
16         .flags = IORESOURCE_MEM,
17     },
18     
19     [1]={
20         .start = IRQ_EINT8,
21         .end   = IRQ_EINT8,
22         .flags = IORESOURCE_IRQ,
23     },
24     [2]={
25         .start = IRQ_EINT11,
26         .end   = IRQ_EINT11,
27         .flags = IORESOURCE_IRQ,
28     },
29     [3]={
30         .start = IRQ_EINT13,
31         .end   = IRQ_EINT13,
32         .flags = IORESOURCE_IRQ,
33     },
34     [4]={
35         .start = IRQ_EINT14,
36         .end   = IRQ_EINT14,
37         .flags = IORESOURCE_IRQ,
38     },
39     [5]={
40         .start = IRQ_EINT15,
41         .end   = IRQ_EINT15,
42         .flags = IORESOURCE_IRQ,
43     },
44     [6]={
45         .start = IRQ_EINT19,
46         .end   = IRQ_EINT19,
47         .flags = IORESOURCE_IRQ,
48     }
49 };
50 
51 static struct platform_device *s3c_buttons;
52 
53 
54 static int __init platform_init(void)
55 {
56 
57     s3c_buttons = platform_device_alloc("mini2440-buttons",-1);
58 
59     platform_device_add_resources(s3c_buttons,&s3c_buttons_resource,7);
60 
61     /*平台设备的注册*/
62     platform_device_add(s3c_buttons);
63 
64 
65 }
66 
67 static void __exit platform_exit(void)
68 {
69     platform_device_unregister(s3c_buttons);
70 }
71 
72 module_init(platform_init);
73 module_exit(platform_exit);
74 
75 MODULE_AUTHOR("David Xie");
76 MODULE_LICENSE("GPL");
77 MODULE_ALIAS("platform:mini2440buttons");

plat_buttons.c

View Code
  1 #include <linux/module.h>
  2 #include <linux/types.h>
  3 #include <linux/miscdevice.h>
  4 #include <linux/fs.h>
  5 #include <linux/init.h>
  6 #include <linux/platform_device.h>
  7 #include <linux/interrupt.h>
  8 #include <linux/clk.h>
  9 #include <linux/uaccess.h>
 10 #include <linux/io.h>
 11 #include <mach/map.h>
 12 #include <mach/regs-gpio.h>
 13 #include <linux/poll.h>
 14 #include <linux/irq.h>
 15 #include <asm/unistd.h>
 16 #include <linux/device.h>
 17 
 18 
 19 static DECLARE_WAIT_QUEUE_HEAD(button_waitq);
 20 
 21 static volatile int ev_press = 0;
 22 
 23 
 24 static int key_value;
 25 static struct device     *buttons_dev;    /* platform device attached to */
 26 static struct resource    *buttons_mem;
 27 static struct resource   *buttons_irq;
 28 static void __iomem        *buttons_base;
 29 
 30 
 31 static int button_irqs[6];
 32 
 33 static irqreturn_t buttons_interrupt(int irq, void *dev_id)
 34 {
 35     int i;
 36     for(i=0; i<6; i++){
 37         if(irq == button_irqs[i]){
 38             //printk("==>interrput number:%d\n",irq);  
 39             key_value = i;
 40             ev_press =1;
 41             wake_up_interruptible(&button_waitq);        
 42         }
 43     }
 44     
 45     return IRQ_RETVAL(IRQ_HANDLED);
 46     
 47 }
 48 
 49 static int s3c24xx_buttons_open(struct inode *inode, struct file *file)
 50 {
 51     int i;
 52     int err = 0;
 53     /*注册中断*/
 54     for(i=0; i<6; i++){
 55         if (button_irqs[i] < 0) 
 56             continue;
 57                             
 58                 /*中断触发方式:上升沿触发*/
 59         err = request_irq(button_irqs[i],buttons_interrupt,IRQ_TYPE_EDGE_RISING,NULL,NULL);
 60         if(err)
 61           break;
 62     }
 63 
 64     if (err) {
 65         i--;
 66         for (; i >= 0; i--) {
 67             if (button_irqs[i] < 0) {
 68                 continue;
 69             }
 70             disable_irq(button_irqs[i]);
 71             free_irq(button_irqs[i], NULL);
 72         }
 73         return -EBUSY;
 74     }
 75 
 76     ev_press = 0;   
 77     return 0;
 78 }
 79 
 80 static int s3c24xx_buttons_close(struct inode *inode, struct file *file)
 81 {
 82     int i;    
 83     for (i=0; i<6; i++) {
 84             if (button_irqs[i] < 0) {
 85                 continue;
 86             }
 87             free_irq(button_irqs[i],NULL);
 88     }
 89     return 0;
 90 }
 91 
 92 static int s3c24xx_buttons_read(struct file *filp, char __user *buff, size_t count, loff_t *offp)
 93 {
 94     unsigned long err;
 95     if (!ev_press) {
 96         if (filp->f_flags & O_NONBLOCK)
 97             return -EAGAIN;
 98         else
 99             wait_event_interruptible(button_waitq, ev_press);
100     }    
101     ev_press = 0;
102     err = copy_to_user(buff, &key_value, sizeof(key_value));
103     return sizeof(key_value);
104 }
105 //轮询poll要做两件事:poll_wait指明使用的等待队列;返回掩码
106 static unsigned int s3c24xx_buttons_poll( struct file *file, struct poll_table_struct *wait)
107 {
108     unsigned int mask = 0;
109     poll_wait(file, &button_waitq, wait);
110     if (ev_press){
111         mask |= POLLIN | POLLRDNORM;      /*按下evpress为1,有数据刻可读*/
112     }
113     return mask;
114 }
115 
116 
117 
118 static struct file_operations mini2440buttons_fops = {
119     .owner   =   THIS_MODULE,
120     .open    =   s3c24xx_buttons_open,
121     .release =   s3c24xx_buttons_close,
122     .read    =   s3c24xx_buttons_read,
123     .poll    =   s3c24xx_buttons_poll,
124 };
125 
126 static struct miscdevice mini2440_miscdev = {
127     
128     .minor = MISC_DYNAMIC_MINOR,
129     .name ="buttons",
130     .fops = &mini2440buttons_fops,
131 };
132 
133 /* device interface */
134 static int mini2440_buttons_probe(struct platform_device *pdev)
135 {
136     struct resource *res;
137     struct device *dev;
138     int ret;
139     int size;
140     int i;
141     
142     printk("probe:%s\n", __func__);
143     dev = &pdev->dev;
144     buttons_dev = &pdev->dev;
145     
146     /*平台资源获取*/
147     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
148     if (res == NULL) {
149         dev_err(dev, "no memory resource specified\n");
150         return -ENOENT;
151     }
152     
153     size = (res->end - res->start) + 1;
154     buttons_mem = request_mem_region(res->start, size, pdev->name);
155     if (buttons_mem == NULL) {
156         dev_err(dev, "failed to get memory region\n");
157         ret = -ENOENT;
158         goto err_req;
159     }
160     
161     buttons_base = ioremap(res->start, size);
162     if (buttons_base == NULL) {
163         dev_err(dev, "failed to ioremap() region\n");
164         ret = -EINVAL;
165         goto err_req;
166     }
167     printk(KERN_DEBUG"probe: mapped buttons_base=%p\n", buttons_base);
168 
169  /*get irq number*/
170   for(i=0; i<6; i++){
171      buttons_irq = platform_get_resource(pdev,IORESOURCE_IRQ,i);
172      if(buttons_irq == NULL){
173            dev_err(dev,"no irq resource specified\n");
174           ret = -ENOENT;
175           goto err_map;
176     }
177     button_irqs[i] = buttons_irq->start;
178     //printk("button_irqs[%d]=%d\n",i,button_irqs[i]);  
179     }
180     ret = misc_register(&mini2440_miscdev);      //混杂设备注册
181         
182     return 0;
183 
184  err_map:
185     iounmap(buttons_base);
186 
187  err_req:
188     release_resource(buttons_mem);
189     kfree(buttons_mem);
190 
191     return ret;
192 }
193 
194 static int mini2440_buttons_remove(struct platform_device *dev)
195 {
196     release_resource(buttons_mem);
197     kfree(buttons_mem);
198     buttons_mem = NULL;
199 
200     iounmap(buttons_base);
201     misc_deregister(&mini2440_miscdev);
202     return 0;
203 }
204 
205 /*平台驱动定义*/
206 static struct platform_driver mini2440buttons_driver = {
207     .probe        = mini2440_buttons_probe,
208     .remove        = mini2440_buttons_remove,
209     .driver        = {
210         .owner    = THIS_MODULE,
211         .name    = "mini2440-buttons",
212     },
213 };
214 
215 static char banner[] __initdata =
216      "Mini2440 Buttons Driver\n";
217 /*
218 宏 __init作用与 __initdata作用:
219 此宏定义可知标记后的函数或数据其实是放到了特定的(代码或数据)段中。
220 标记为初始化的函数,表明该函数供在初始化期间使用。在模块装载之后,
221 模块装载就会将初始化函数扔掉。这样可以将该函数占用的内存释放出来。
222 */
223 static int __init buttons_init(void)
224 {
225     printk(banner);
226     /*平台驱动注册*/
227     platform_driver_register(&mini2440buttons_driver);
228     return 0;
229 }
230 
231 static void __exit buttons_exit(void)
232 {
233     platform_driver_unregister(&mini2440buttons_driver);
234 }
235 
236 module_init(buttons_init);
237 module_exit(buttons_exit);
238 
239 MODULE_AUTHOR("David Xie");
240 MODULE_DESCRIPTION("Mini2440 Buttons Driver");
241 MODULE_LICENSE("GPL");