一、实现硬件操作函数
一般的,我写驱动的时候,我会先确定一些基本的硬件操作函数能够使用。如LED驱动我要实现三个操作:配置、开灯和关灯,所以我先要实现这几个硬件操作函数。
其实这些在我介绍IO内存时已经实现了(5th_mm_2/3rd/test.c),我只是稍作了一点修改,改了一下内存的数据类型,其实没什么大出入。
/*5th_mm_4/1st/test.c*/
1 #include
2 #include
3
4 #include
5 #include
6
7 unsigned long virt, phys;
8 unsigned long gpecon, gpedat, gpeup; //其实我就改了这里的数据类型,其实都是用来存放地址
9 unsigned long reg; //没有多大的影响。
10 struct resource *led_resource;
11
12 void s3c_led_config(void) //还将函数的名字改成好听点
13 {
14 reg = ioread32(gpecon);
15 reg &= ~(3 << 24);
16 reg |= (1 << 24);
17 iowrite32(reg, gpecon);
18
19 reg = ioread32(gpeup);
20 reg &= ~(3 << 12);
21 iowrite32(reg, gpeup);
22 }
23
24 void s3c_led_on(void)
25 {
26 reg = ioread32(gpedat);
27 reg &= ~(1 << 12);
28 iowrite32(reg, gpedat);
29 }
30
31 void s3c_led_off(void)
32 {
33 reg = ioread32(gpedat);
34 reg |= (1 << 12);
35 iowrite32(reg, gpedat);
36 }
37
38 void init_led_device(void)
39 {
40 phys = 0x56000000;
41 virt = (unsigned long)ioremap(phys, 0x0c);
42
43 gpecon = virt + 0x40;
44 gpedat = virt + 0x44;
45 gpeup = virt + 0x48;
46 }
47
48 static int __init test_init(void) //模块初始化函数
49 {
50 init_led_device();
51
52 led_resource = request_mem_region(phys, 0x0c, "LED_MEM");
53 if(NULL == led_resource){
54 printk("request mem error!\n");
55 return - ENOMEM;
56 }
57
58 s3c_led_config();
59 s3c_led_on();
60 printk("hello led!\n");
61 return 0;
62 }
63
64 static void __exit test_exit(void) //模块卸载函数
65 {
66 if(NULL != led_resource){
67 s3c_led_off();
68 iounmap((void *)virt);
69 release_mem_region(phys, 0x0c);
70 }
71 printk("bye\n");
72 }
73
74 module_init(test_init);
75 module_exit(test_exit);
76
77 MODULE_LICENSE("GPL");
78 MODULE_AUTHOR("xoao bai");
79 MODULE_VERSION("v0.1");
至于验证我就不做了,效果还是一样,加载模块灯亮,卸载模块灯灭。
二、面向对象思想——定义一个LED的结构体
上面的函数中,一大堆的全局变量实在让人看起来不舒服。我在第三章字符设备的文中介绍过,把这些变量定义在一个结构体中,方便以后引用,如函数传参。
/*5th_mm_4/2nd/test.c*/
7 struct _led_t{
8 //hardware obb
9 unsigned long virt, phys;
10 unsigned long gpecon, gpedat, gpeup;
11 unsigned long reg;
12 struct resource *led_resource;
13