Linux平台设备驱动 - 按键设备驱动
2024-09-20 来源:cnblogs
之前的一篇博客简单介绍了平台设备驱动模型(http://www.cnblogs.com/ape-ming/p/5107641.html),现在就根据那篇博客所列出来的模板把上一篇博客(http://www.cnblogs.com/ape-ming/p/5110996.html)的例程改成平台设备驱动模型。
一、平台设备
根据模板首先要写一个平台设备加载函数:
1 /*
2 * 函数名 : button_device_init
3 * 函数功能: 设备加载
4 */
5
6 static int __init button_device_init(void)
7 {
8 int ret = 0;
9
10 /* 注册平台设备 */
11 platform_device_register(&button_platform_device);
12 return ret;
13 }
在这个函数里面调用platform_device_register()对设备进行注册。这个时候就需要给定一个平台设备结构体button_platform_device:
1 static struct platform_device button_platform_device =
2 {
3 .name = 'button_dev',
4 .id = 0,
5 .num_resources = ARRAY_SIZE(button_resource),
6 .resource = button_resource,
7 };
根据模型在这个结构体里面需指定了设备资源button_resource:
1 static struct resource button_resource[] =
2 {
3 [0] =
4 {
5 .start = IRQ_EINT(0),
6 .end = IRQ_EINT(3),
7 .flags = IORESOURCE_IRQ,
8 },
9 [1] =
10 {
11 .start = (resource_size_t)S3C64XX_GPNDAT,
12 .end = (resource_size_t)S3C64XX_GPNDAT,
13 .flags = IORESOURCE_MEM,
14 },
15 };
数组第一个元素指定了设备的中断号为IRQ_EINT(0)到IRQ_EINT(3),第二个元素指定了设备的IO资源。
二、平台驱动
平台驱动也要先写一个平台驱动加载函数:
1 /*
2 * 函数名 : button_driver_init
3 * 函数功能: 驱动加载
4 */
5
6 static int __init button_driver_init(void)
7 {
8 int ret = 0;
9 ret = platform_driver_register(&button_platform_driver);
10 return ret;
11 }
在这里面完成了平台驱动的注册,接下来就要有一个平台驱动的结构体button_platform_driver:
1 static struct platform_driver button_platform_driver =
2 {
3 .probe = button_platform_probe,
4 .remove = button_platform_remove,
5 .driver =
6 {
7 .owner = THIS_MODULE,
8 .name = 'button_dev',
9 },
10 };
probe成员所指定的函数就是平台设备与驱动配置之后要执行的第一个函数,匹配的条件就是driver里面的name成员是不是和上面平台设备结构体里面的name成员一致。
1 /*
2 * 函数名 : button_platform_probe
3 * 函数功能: 匹配驱动与设备
4 */
5
6 static int button_platform_probe(struct platform_device *button_device)
7 {
8 int ret = 0;
9 int i = 0;
10 int num = 0;
11 struct resource* irq_resource;
12
13 /* 注册混杂设备驱动 */
14 ret = misc_register(&misc);
15 if(ret)
16 {
17 printk('can't register miscdevn');
18 return ret;
19 }
20
21 /* 填充数组 */
22 irq_resource = platform_get_resource(button_device,IORESOURCE_IRQ,0);
23 for(num = 0,i = irq_resource->start;i <= irq_resource->end;i++,num++)
24 {
25 button_irq[num].irq = i;
26 }
27 mem_resource = platform_get_resource(button_device,IORESOURCE_MEM,0);
28
29 /* 申请外部中断 */
30 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++)
31 {
32 ret = request_irq(button_irq[i].irq,button_interrupt,IRQF_TRIGGER_FALLING,button_irq[i].name,(void*)&button_irq[i]);
33 if(ret != 0)
34 {
35 printk('request_irq failuren');
36 }
37 }
38
39 /* 初始化工作队列 */
40 INIT_WORK(&button_work,do_buttons);
41
42 /* 初始化内核定时器 */
43 init_timer(&button_time);
44 button_time.expires = jiffies + HZ/10; //100ms
45 button_time.function = button_do_time;
46 add_timer(&button_time);
47
48 return ret;
49 }
在button_platform_probe()函数中完成混杂设备驱动的注册、用platform_get_resource()获取设备的资源、申请外部中断、初始化工作队列、初始化内核定时器。其实就是把混杂设备驱动模型里面的设备注册函数和open函数所做的工作全部放到button_platform_probe()函数里面完成。之后的操作就跟混杂设备模型编写的按键驱动例程基本一样了。
这里总结一下:平台设备驱动只是一个框架,其归根到底还是采用混杂设备驱动模型(或字符设备等)的方式进行驱动程序的编写。但是采用平台设备驱动的方式使得板级代码和驱动代码分离开来,在同一类型的驱动中只需要通过相应的函数获取设备资源和数据而不必要去修改驱动代码。
完整代码:
1 /*
2 * 文件名 : button_device.c
3 * 功能描述: 通过外部中断实现按键驱动程序,平台设备驱动方式
4 * 驱动模型: platform
5 * 设备节点: /dev/buttons6410
6 * MCU : S3C6410
7 * 端口连接: KEY0-GPN0 KEY1-GPN1 KEY2-GPN2 KEY3-GPN3
8 */
9
10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 18 #include 19 #include 20 #include 21 #include 22 #include 23 24 25 26 static struct resource button_resource[] = 27 { 28 [0] = 29 { 30 .start = IRQ_EINT(0), 31 .end = IRQ_EINT(3), 32 .flags = IORESOURCE_IRQ, 33 }, 34 [1] = 35 { 36 .start = (resource_size_t)S3C64XX_GPNDAT, 37 .end = (resource_size_t)S3C64XX_GPNDAT, 38 .flags = IORESOURCE_MEM, 39 }, 40 }; 41 42 static struct platform_device button_platform_device = 43 { 44 .name = 'button_dev', 45 .id = 0, 46 .num_resources = ARRAY_SIZE(button_resource), 47 .resource = button_resource, 48 }; 49 50 /* 51 * 函数名 : button_device_init 52 * 函数功能: 设备加载 53 */ 54 55 static int __init button_device_init(void) 56 { 57 int ret = 0; 58 59 /* 注册平台设备 */ 60 platform_device_register(&button_platform_device); 61 return ret; 62 } 63 64 /* 65 * 函数名 : button_device_exit 66 * 函数功能: 设备卸载 67 */ 68 69 static void __exit button_device_exit(void) 70 { 71 /* 注销平台设备*/ 72 platform_device_unregister(&button_platform_device); 73 } 74 75 module_init(button_device_init); 76 module_exit(button_device_exit); 77 MODULE_LICENSE('GPL'); 1 /* 2 * 文件名 : button_driver.c 3 * 功能描述: 通过外部中断实现按键驱动程序,平台设备驱动方式 4 * 驱动模型: platform 5 * 设备节点: /dev/buttons6410 6 * MCU : S3C6410 7 * 端口连接: KEY0-GPN0 KEY1-GPN1 KEY2-GPN2 KEY3-GPN3 8 */ 9 10 #include 11 #include 12 #include 13 #include 14 #include 15 #include 16 #include 17 #include 18 #include 19 #include 20 #include 21 #include 22 #include 23 #include 24 #include 25 26 #include 27 #include 28 #include 29 30 #include 31 #include 32 #include 33 34 35 volatile int isKey_Pressed = 0; // 按键按下标志 36 struct work_struct button_work; //定义工作队列 37 struct timer_list button_time; 38 struct resource* mem_resource; 39 struct button_irqs 40 { 41 unsigned int irq; 42 int id; 43 char* name; 44 }; 45 46 static struct button_irqs button_irq[] = 47 { 48 {0,0,'KEY0'}, 49 {0,1,'KEY1'}, 50 {0,2,'KEY2'}, 51 {0,3,'KEY3'}, 52 }; 53 54 /* 初始化等待队列 */ 55 DECLARE_WAIT_QUEUE_HEAD(q_buttons); 56 57 static volatile int button_press[4] = {0}; 58 59 /* 60 * 函数名 : do_buttons 61 * 函数功能: 工作队列处理函数,处理按键工作 62 */ 63 static void do_buttons(struct work_struct *work) 64 { 65 mod_timer(&button_time,jiffies + HZ/10); 66 } 67 68 /* 69 * 函数名 : button_interrupt 70 * 函数功能: 外部中断服务程序 71 */ 72 static irqreturn_t button_interrupt(int irq, void *dev_id) 73 { 74 struct button_irqs *button_id = (struct button_irqs*)dev_id; 75 switch(button_id->id) 76 { 77 case 0: 78 case 1: 79 case 2: 80 case 3: 81 schedule_work(&button_work); 82 break; 83 default: break; 84 } 85 return IRQ_HANDLED; 86 } 87 88 /* 89 * 函数名 : button_do_time 90 * 函数功能: 内核定时器服务程序 91 */ 92 93 static void button_do_time(unsigned long arg) 94 { 95 int i = 0; 96 unsigned short tmp = 0; 97 tmp = readw(mem_resource->start) & 0x000F; 98 switch(tmp) 99 { 100 case 0x0E: 101 button_press[0] = 1; 102 break; 103 case 0x0D: 104 button_press[1] = 1; 105 break; 106 case 0x0B: 107 button_press[2] = 1; 108 break; 109 case 0x07: 110 button_press[3] = 1; 111 break; 112 } 113 for(i = 0;i < sizeof(button_press)/sizeof(button_press[0]);i++) 114 { 115 if(button_press[i] == 0) 116 continue; 117 118 isKey_Pressed = 1; 119 120 /* 唤醒等待队列 */ 121 wake_up_interruptible(&q_buttons); 122 break; 123 } 124 125 } 126 127 128 /* 129 * 函数名 : button_open 130 * 函数功能: 文件打开 131 */ 132 static int button_open(struct inode *node, struct file *filp) 133 { 134 return 0; 135 } 136 137 /* 138 * 函数名 : button_read 139 * 函数功能: 文件读 140 */ 141 static ssize_t button_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 142 { 143 ssize_t ret = 0; 144 size_t size = min(count,sizeof(button_press)); 145 146 /* 等待队列唤醒 */ 147 wait_event_interruptible(q_buttons,isKey_Pressed); 148 isKey_Pressed = 0; 149 150 if(copy_to_user((void*)buffer,(const void*)&button_press,size)) 151 { 152 ret = -1; 153 printk('copy to user failuren'); 154 } 155 else 156 { 157 memset((void*)&button_press,0,sizeof(button_press)); 158 ret = size; 159 } 160 return ret; 161 } 162 163 164 static struct file_operations fops = 165 { 166 .owner = THIS_MODULE, 167 .open = button_open, 168 .read = button_read, 169 }; 170 171 static struct miscdevice misc = 172 { 173 .minor = MISC_DYNAMIC_MINOR, 174 .name = 'buttons6410', 175 .fops = &fops, 176 }; 177 178 /* 179 * 函数名 : button_platform_probe 180 * 函数功能: 匹配驱动与设备 181 */ 182 183 static int button_platform_probe(struct platform_device *button_device) 184 { 185 int ret = 0; 186 int i = 0; 187 int num = 0; 188 struct resource* irq_resource; 189 190 /* 注册混杂设备驱动 */ 191 ret = misc_register(&misc); 192 if(ret) 193 { 194 printk('can't register miscdevn'); 195 return ret; 196 } 197 198 /* 填充数组 */ 199 irq_resource = platform_get_resource(button_device,IORESOURCE_IRQ,0); 200 for(num = 0,i = irq_resource->start;i <= irq_resource->end;i++,num++) 201 { 202 button_irq[num].irq = i; 203 } 204 mem_resource = platform_get_resource(button_device,IORESOURCE_MEM,0); 205 206 /* 申请外部中断 */ 207 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++) 208 { 209 ret = request_irq(button_irq[i].irq,button_interrupt,IRQF_TRIGGER_FALLING,button_irq[i].name,(void*)&button_irq[i]); 210 if(ret != 0) 211 { 212 printk('request_irq failuren'); 213 } 214 } 215 216 /* 初始化工作队列 */ 217 INIT_WORK(&button_work,do_buttons); 218 219 /* 初始化内核定时器 */ 220 init_timer(&button_time); 221 button_time.expires = jiffies + HZ/10; //100ms 222 button_time.function = button_do_time; 223 add_timer(&button_time); 224 225 return ret; 226 } 227 228 /* 229 * 函数名 : button_platform_remove 230 * 函数功能: 删除设备 231 */ 232 233 static int button_platform_remove(struct platform_device *button_device) 234 { 235 int ret = 0; 236 int i = 0; 237 238 /* 释放中断 */ 239 for(i = 0;i < sizeof(button_irq)/sizeof(button_irq[0]);i++) 240 { 241 free_irq(button_irq[i].irq,(void*)&button_irq[i]); 242 } 243 244 /* 释放内核定时器 */ 245 del_timer(&button_time); 246 247 /* 卸载混杂设备驱动 */ 248 misc_deregister(&misc); 249 250 return ret; 251 } 252 253 static struct platform_driver button_platform_driver = 254 { 255 .probe = button_platform_probe, 256 .remove = button_platform_remove, 257 .driver = 258 { 259 .owner = THIS_MODULE, 260 .name = 'button_dev', 261 }, 262 }; 263 264 /* 265 * 函数名 : button_driver_init 266 * 函数功能: 驱动加载 267 */ 268 269 static int __init button_driver_init(void) 270 { 271 int ret = 0; 272 ret = platform_driver_register(&button_platform_driver); 273 return ret; 274 } 275 276 /* 277 * 函数名 : button_driver_exit 278 * 函数功能: 驱动卸载 279 */ 280 281 static void __exit button_driver_exit(void) 282 { 283 platform_driver_unregister(&button_platform_driver); 284 } 285 286 module_init(button_driver_init); 287 module_exit(button_driver_exit); 288 MODULE_LICENSE('GPL');
- Linux Kernel之flush_cache_all在ARM平台下是如何实现的
- Linux设备驱动开发 - 平台设备驱动
- S3C6410嵌入式应用平台构建(四)——linux-3.14.4移植到OK6410-(初步启动)
- S3C6410嵌入式应用平台构建(五)——linux-3.14.4移植到OK6410-(Nand分区问题)
- S3C6410嵌入式应用平台构建(六)——linux-3.14.4移植到OK6410-(Yaffs2文件系统移植)
- S3C6410嵌入式应用平台构建(六)——linux-3.14.4移植到OK6410-(Yaffs2文件制作)
- ARM平台linux内核Notes 1
- ARM平台linux内核Notes 2
- 嵌入式linux平台上Eclipse C++开发环境的建立
- Linux帧缓冲设备驱动程序框架及图形界面GUI的移植