• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    公众号

S3C2440LCD驱动(FrameBuffer)实例开发二(转)

原作者: [db:作者] 来自: [db:来源] 收藏 邀请

开发板自带的LCD驱动是基于platform总线写的,所以如果要使其它的LCD能够在自己的开发板上跑起来,那么就先了解platform驱动的架构,下面简单记录下自己看platform驱动时体会,简单的说platform是一种虚拟总线,那么它也是一条总线,所以它分为3个部分,platform_bus,platform_device,platform_driver。在platform_device向platform_bus注册设备,platform_driver向platform_bus注册驱动,注册后在platform_bus中会有一条device链表和driver链表,platform_bus中match函数将匹配两者的名字,如果相同那就把驱动和设备进行绑定。Linux platform driver机制和传统的device driver机制(通过driver_register进行注册)相比,一个明显的优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动中使用这些资源时通过platform device提供的标准结构进行申请并使用。这样提高了驱动和资源的独立性,并且具有较好的可移植性和安全性(这些标准接口是安全的)。下面举一个简单platform驱动的例子来分析platform_device和platform_driver是怎么联系,platform_driver是怎么使用platform_device提供硬件信息的。

led_dev.c

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <linux/module.h>
#include <linux/version.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/interrupt.h>
#include <linux/list.h>
#include <linux/timer.h>
#include <linux/init.h>
#include <linux/serial_core.h>
#include <linux/platform_device.h>
 
 
/* 分配/设置/注册一个platform_device */
 
static struct resource led_resource[] = {
    [0] = {
        .start = 0x56000010,             /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
        .end   = 0x56000010 + 8 - 1,
        .flags = IORESOURCE_MEM,         /* 标识led控制器io端口*/
    },
    [1] = {
        .start = 5,                      /* LED1 */
        .end   = 5,
        .flags = IORESOURCE_IRQ,         /* 标识LED中断 */
    }
 
};
/* 必须提供realease函数,可以不实现 */
static void led_release(struct device * dev)
{
}
 
 
static struct platform_device led_dev = {
    .name         = "myled",   /* 设备名 */
    .id       = -1,            /* 一般设为-1,表示同样名字的设备只有一个 */
    .num_resources    = ARRAY_SIZE(led_resource), /*  资源数量*/
    .resource     = led_resource,
    .dev = {
        .release = led_release, /* 引用上面定义的资源 */
    },
};
 
static int led_dev_init(void)
{
    platform_device_register(&led_dev);/* 注册平台设备 */
    return 0;
}
 
static void led_dev_exit(void)
{
    platform_device_unregister(&led_dev);/*  注销平台设备*/
}
 
module_init(led_dev_init);
module_exit(led_dev_exit);
 
MODULE_LICENSE("GPL");

 

Led_drv.c

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
#include <linux/fs.h>
#include <linux/interrupt.h
#include <linux/irq.h>
#include <linux/sched.h>
#include <linux/pm.h>
#include <linux/sysctl.h>
#include <linux/proc_fs.h>
#include <linux/delay.h>
#include <linux/platform_device.h>
#include <linux/input.h>
#include <linux/irq.h>
#include <asm/uaccess.h>
#include <asm/io.h>
 
static int major;
static struct class *cls;
static volatile unsigned long *gpio_con;
static volatile unsigned long *gpio_dat;
static int pin;
 
static int led_open(struct inode *inode, struct file *file)
{
    //printk("first_drv_open\n");
    /* 配置为输出 */
    *gpio_con &= ~(0x3<<(pin*2));
    *gpio_con |= (0x1<<(pin*2));
    return 0;  
}
 
static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos)
{
    int val;
 
    //printk("first_drv_write\n");
 
    copy_from_user(&val, buf, count); //    copy_to_user();
 
    if (val == 1)
    {
        // 点灯
        *gpio_dat &= ~(1<<pin);
    }
    else
    {
        // 灭灯
        *gpio_dat |= (1<<pin);
    }
     
    return 0;
}
static struct file_operations led_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   led_open,    
    .write  =   led_write,    
};
static int led_probe(struct platform_device *pdev)
{
    struct resource     *res;
    /* 分配/设置/注册一个platform_driver */
    /* 根据platform_device的资源进行ioremap */
    res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    gpio_con = ioremap(res->start, res->end - res->start + 1);
    gpio_dat = gpio_con + 1;
 
    res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
    pin = res->start;
 
    /* 注册字符设备驱动程序 */
    printk("led_probe, found led\n");
 
    /* 注册设备,生成设备文件*/
    major = register_chrdev(0, "myled", &led_fops);
    cls = class_create(THIS_MODULE, "myled");
    class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */
    return 0;
}
static int led_remove(struct platform_device *pdev)
{
    /* 卸载字符设备驱动程序 */
    /* iounmap */
    printk("led_remove, remove led\n");
    class_device_destroy(cls, MKDEV(major, 0));
    class_destroy(cls);
    unregister_chrdev(major, "myled");
    iounmap(gpio_con);
    return 0;
}
struct platform_driver led_drv = {
    .probe      = led_probe,
    .remove     = led_remove,
    .driver     = {
        .name   = "myled",
    }
};
static int led_drv_init(void)
{
    platform_driver_register(&led_drv);  /* 注册平台驱动 */
    return 0;
}
static void led_drv_exit(void)
{
    platform_driver_unregister(&led_drv); /* 注销平台驱动 */
}
module_init(led_drv_init);
module_exit(led_drv_exit);
MODULE_LICENSE("GPL");

 

这就是一个简单的platform驱动,这两个文件我们完全可以写道一个文件中去实现,现在看下由一个文件如何实现这个驱动:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
 
static struct class *seconddrv_class;
static struct class_device  *seconddrv_class_dev;
 
volatile unsigned long *gpfcon;
volatile unsigned long *gpfdat;
static int second_drv_open(struct inode *inode, struct file *file)
{
    /*
     * K1,K2,K3,K4对应GPF1、GPF4、GPF2、GPF0
     */
 
    /* 配置GPF1、GPF4、GPF2、GPF0为输入引脚 */
    *gpfcon &= ~((0x3<<(1*2)) | (0x3<<(4*2)) | (0x3<<(2*2)) | (0x3<<(0*2)));
    return 0;
}
ssize_t second_drv_read(struct file *file, char __user *buf, size_t size, loff_t *ppos)
{
    /* 返回4个引脚的电平 */
    unsigned char key_vals[4];
    int regval;
    if (size != sizeof(key_vals))
        return -EINVAL;
 
    regval = *gpfdat;
    key_vals[0] = (regval & (1<<1)) ? 1 : 0;
    key_vals[1] = (regval & (1<<4)) ? 1 : 0;
    key_vals[2] = (regval & (1<<2)) ? 1 : 0;
    key_vals[3] = (regval & (1<<0)) ? 1 : 0;
 
    copy_to_user(buf, key_vals, sizeof(key_vals));
     
    return sizeof(key_vals);
}
static struct file_operations sencod_drv_fops = {
    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
    .open   =   second_drv_open,    
    .read   =   second_drv_read,      
};
int major;
static int second_drv_init(void)
{
    major = register_chrdev(0, "second_drv", &sencod_drv_fops);
 
    seconddrv_class = class_create(THIS_MODULE, "second_drv");
 
    seconddrv_class_dev = class_device_create(seconddrv_class, NULL, MKDEV(major, 0), NULL, "buttons"); /* /dev/buttons */
 
    gpfcon = (volatile unsigned long *)ioremap(0x56000050, 16);
    gpfdat = gpfcon + 1;
 
    return 0;
}
static void second_drv_exit(void)
{
    unregister_chrdev(major, "second_drv");
    class_device_unregister(seconddrv_class_dev);
    class_destroy(seconddrv_class);
    iounmap(gpfcon);
    return 0;
}
module_init(second_drv_init);
module_exit(second_drv_exit);
MODULE_LICENSE("GPL");

 

     由此可见,如果由platform驱动去实现led驱动将会多出很多东西,而这些多出来的就是我们如何把一个驱动程序融合到platform里面,那既然用platform驱动要多写那么多东西那问什么还要写基于platform的驱动呢?我的理解是基于以下几个原因:如果一个设备挂在总线上,其结果是配套的sysfs结点,设备电源管理都成为可能;隔离了BSP和驱动,在BSP中定义platform设备和设备使用的资源,设备的具体配置信息,而在驱动中,只要通过API去获取资源和数据,做到了板相关代码与驱动代码的分离,使得驱动具有更好的可移植性和更好的扩展性和跨平台性;假如我们要实现一个LCD驱动,那么我们只需修改BSP相关的代码,platform基本上不需修改,避免重复着轮子的可能性;基于platformplatform机制将设备本身的资源注册进内核,由内核统一管理。

    上面platform的例子还没有完全按照linux platform的标准去写,因为阅读代码可知linux platform驱动把platform_device相关的代码放在一块,然后统一进行注册!

下面记录下如何把device添加到板级文件中(最开始还是建议写成两个文件,分别编译成模块加载,因为如果是放到板级文件中那么需要重新编译内核,这个将在编译上浪费很多时间)

步骤:

1. 在/arch/arm/plat-s3c24xx/devs.c中定义led 相关的设备及资源,代码如下:

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
static struct resource led_resource[] = {
    [0] = {
        .start = 0x56000010,             /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */
        .end   = 0x56000010 + 8 - 1,
        .flags = IORESOURCE_MEM,         /* 标识led控制器io端口*/
    },
    [1] = {
        .start = 5,                      /* LED1 */
        .end   = 5,
        .flags = IORESOURCE_IRQ,         /* 标识LED中断 */
    }
 
};
EXPORT_SYMBOL(s3c_device_lcd);

 

请注意最后一行是导出led平台设备,会在mach-smdk2440.c中添加到平台设备列表中。

2. 如果还需定义led的其它信息,那么在/arch/arm/plat-s3c2410/include/mach/fb.h 为led平台设备定义一个s3c2410fb_mach_info结果体。led很简单所以没有必要再去定义,但是后面讲的LCD的平台设备需要定义!因为仅仅通过resource,驱动还无法操作LCD。

3. 不要实现,只是记录下系统还会做什么事! 在mach-smdk2440.c中将会调用platform_add_devices(),注册平台设备。

好了对platform有了一定了解后,那么看看如果将LCD驱动写成linux设备模型的platform驱动,同样给出不使用platform架构是一个完整的字符设备驱动的lcd驱动,在给出一个符合platform模型的lcd驱动,通过这两个文件的对比,就可以知道在符合platform模型的lcd驱动中可以找到LCD驱动的全部信息,也会发现基本上所有plat_form驱动出了驱动本身的东西外,框架性的东西基本上一样的,所以如果理解了一两个platform驱动,那么以后写platform驱动或看platform代码跟不以flatform写出的代码是没有什么区别的!代码如下:

 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174

鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
上一篇:
自己动手制作C#电子词典发布时间:2022-07-18
下一篇:
C++重写虚函数的代码使用注意点+全部知识点+全部例子实现发布时间:2022-07-18
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap