《[arm驱动]Platform设备驱动》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序或内核驱动一个
一、关键是device程序中resource结构体
结构体一)resource结构体
1
2
3
4
5
6
7
|
struct
resource {
resource_size_t start;
//定义资源的起始地址
resource_size_t end;
//定义资源的结束地址
const
char
*name;
//定义资源的名称
unsigned
long
flags;
//定义资源的类型,例如MEM, IO ,IRQ, DMA类型
struct
resource *parent, *sibling, *child;
//资源链表指针
};
|
函数一)driver程序中获取资源
1
|
struct
resource *platform_get_resource(
struct
platform_device *dev, unsigned
int
type, unsigned
int
num)
|
参数:
dev: 资源所属的设备
type: 获取的资源类型 IORESOURCE_IO IORESOURCE_MEM IORESOURCE_IRQ IORESOURCE_DMA
num: 获取的资源数
例:platform_get_resource(pdev, IORESOURCE_IRQ, 0)获取第一个中断号
内核源码一)platform_get_resource内核源码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
platform_get_resource(
struct
platform_device *dev, unsigned
int
type,
unsigned
int
num)
{
int
i;
for
(i = 0; i < dev->num_resources; i++) {
struct
resource *r = &dev->resource[i];
//从下面两个if中看出num = 0并不等同于要取resource[0],而是获取第一个flag为type的resource
if
((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
IORESOURCE_IRQ|IORESOURCE_DMA))
== type)
if
(num-- == 0)
return
r;
}
return
NULL;
}
|
函数二)driver中获取第num+1个中断资源
1
|
platform_get_irq(
struct
platform_device * dev, unsigned
int
num)
|
内核源码二)platform_get_irq内核源码
1
2
3
4
5
6
|
int
platform_get_irq(
struct
platform_device *dev, unsigned
int
num)
{
struct
resource *r = platform_get_resource(dev, IORESOURCE_IRQ, num);
//调用了platform_get_resource
return
r ? r->start : -ENXIO;
}
|
实例一)实现一个s3c2440的platform led流水灯
platform_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
|
/*代码代换:platform_led_dev_, "platform_led"*/
#include <linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/platform_device.h>
#include <linux/fs.h>//文件系统相关的函数和头文件
#include <linux/device.h>
#include <linux/cdev.h> //cdev结构的头文件包含<linux/kdev_t.h>
static
struct
resource platform_led_dev_resource[] = {
/*
参考s3c2440芯片手册
GPFCON 0x56000050//本驱动使用,注意unsigned long 为4字节(4*8 bit),相当与ff
GPFDAT 0x56000054//本驱动使用
GPFUP 0x56000058//本驱动不使用,上拉电阻在驱动中不使用到,
*/
[0] = {
.start = 0x56000050,
.end = 0x56000050 + 8 - 1,
.flags = IORESOURCE_MEM,
},
};
static
void
platform_led_dev_release(
struct
device * dev){
printk(
"device say the device is release\n"
);
return
;
}
static
struct
platform_device platform_led_dev_device = {
//添加设备结构体
.name =
"platform_led"
,
.id = -1,
.num_resources = ARRAY_SIZE(platform_led_dev_resource),
//一定要加,因为platform_get_resource中要用到
.resource = platform_led_dev_resource,
.dev = {
.release = platform_led_dev_release,
//解决"Device 'platform_dev' does not have a release() function“问题
}
};
static
int
__init platform_led_dev_init(
void
){
platform_device_register(&platform_led_dev_device);
//注册设备到内核
return
0;
}
static
void
platform_led_dev_exit(
void
){
platform_device_unregister(&platform_led_dev_device);
//卸载设备
printk(KERN_ALERT
"good bye\n"
);
}
module_init(platform_led_dev_init);
module_exit(platform_led_dev_exit);
MODULE_LICENSE(
"GPL"
);
|
platform_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
|
/*
*代码代换:platform_led_drv_, "platform_led"
*/
#include <linux/string.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <asm/uaccess.h>
#include <asm/irq.h>
#include <asm/io.h>
#include <asm/arch/regs-gpio.h>
#include <asm/hardware.h>
#include <linux/platform_device.h>
static
struct
class
*platform_led_drv_cls;
static
volatile
unsigned
long
*gpiof_con;
//注意unsigned long 为4字节(4*8 bit),相当与ff
static
volatile
unsigned
long
*gpiof_dat;
static
int
major;
static
int
platform_led_drv_open(
struct
inode *inode,
struct
file *file)
{
printk(
"driver\tplatform_led open\n"
);
/* 配置为输出 */
*gpiof_con &= ~((0x3 << (4*2)) | (0x3 << (5*2)) | (0x3 << (6*2)));
*gpiof_con |= ((0x1 << (4*2)) | (0x1 << (5*2)) | (0x1 << (6*2) ));
return
0;
}
static
ssize_t platform_led_drv_write(
struct
file *file,
const
char
__user *buf,
size_t
count, loff_t * ppos)
{
int
val;
int
on;
copy_from_user(&val, buf, count);
on = val % 10;
val = val / 10 + 4;
if
(on == 0){
*gpiof_dat &= ~(1 << val);
}
else
{
*gpiof_dat |= (1 << val);
}
return
0;
}
static
struct
file_operations platform_led_drv_fops = {
.owner = THIS_MODULE,
/* 这是一个宏,推向编译模块时自动创建的__this_module变量 */
.open = platform_led_drv_open,
.write = platform_led_drv_write,
};
static
int
platform_led_drv_probe(
struct
platform_device *dev)
//
{
struct
resource *res;
//获得GPFCON,GPFDAT
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
printk(
"driver say the driver found the device\n"
);
gpiof_con = (
volatile
unsigned
long
*)ioremap(res->start, res->end - res->start + 1);
//将0x56000050-0x56000057进行IO映射,gpiof_con等于ioremap的首地址,占4个字节
gpiof_dat = gpiof_con + 1;
//相当与0x56000050 + 4
major = register_chrdev(0,
"platform_led"
, &platform_led_drv_fops);
/*/proc/devices*/
platform_led_drv_cls = class_create(THIS_MODULE,
"platform_led"
);
/* /sys/class/platform_led */
class_device_create(platform_led_drv_cls, NULL, MKDEV(major, 0), NULL,
"platform_led"
);
/* /dev/platform_led */
printk(
"driver say the driver probe ok\n"
);
return
0;
}
static
int
platform_led_drv_remove(
struct
platform_device *dev)
{
printk(
"driver say the device is polled out\n"
);
/* 卸载字符设备驱动程序 */
/* iounmap */
class_device_destroy(platform_led_drv_cls, MKDEV(major, 0));
class_destroy(platform_led_drv_cls);
unregister_chrdev(major,
"platform_led"
);
iounmap(gpiof_con);
return
0;
}
//platform 总线相关文件挂载/sys/bus/platform/路径下
static
struct
platform_driver platform_led_drv_driver = {
//driver是驱动的意思,相关文件在/sys/bus/platform/drivers
.probe = platform_led_drv_probe,
//注册时要执行的函数
.
remove
= platform_led_drv_remove,
//注销时会执行的函数
.driver = {
.owner = THIS_MODULE,
.name =
"platform_led"
,
//会在"/sys/bus/platform/drivers"下创建platform_dev文件夹
},
};
static
int
__init platform_led_drv_driver_init(
void
)
{
/*注册平台驱动*/
return
platform_driver_register(&platform_led_drv_driver);
}
static
void
platform_led_drv_driver_exit(
void
)
{
platform_driver_unregister(&platform_led_drv_driver);
}
module_init(platform_led_drv_driver_init);
module_exit(platform_led_drv_driver_exit);
MODULE_LICENSE(
"GPL"
);
|
上面两个c文件对于的Makefile
1
2
3
4
5
6
7
8
9
10
11
12
13
|
KERN_DIR =
/workspacearm/linux-2
.6.2.6
#platform_led_dev.ko
#platform_led_drv.ko
all:
make
-C $(KERN_DIR) M=`
pwd
` modules
cp
platform_led_dev.ko
/opt/fsmini/
cp
platform_led_drv.ko
/opt/fsmini/
clean:
make
-C $(KERN_DIR) M=`
pwd
` modules clean
rm
-rf modules.order
rm
-rf Module.symvers
obj-m += platform_led_dev.o
obj-m += platform_led_drv.o
|
应用测试程序
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
|
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
//myled
int
main(
int
argc,
char
**argv)
{
int
fd;
int
val = 1;
fd = open(
"/dev/platform_led"
, O_RDWR);
if
(fd < 0)
{
printf
(
"can't open!\n"
);
}
while
(1){
if
(val > 10){
val = val - 10;
write(fd, &val, 4);
val = val + 10;
}
else
{
val = 21;
write(fd, &val, 4);
val = 1;
}
val -=1;
write(fd, &val, 4);
sleep(1);
val += 11;
if
(val > 22 )val = 1;
}
//write(fd, &val, 4);
close(fd);
return
0;
}
|
应用程序对应的Makefile
1
2
3
4
5
6
7
8
9
|
objs := $(patsubst %c, %o, $(shell ls *.c))
myarmgcc := /workspacearm/armlinuxgcc2626/bin/arm-linux-gcc
myled.bin:$(objs)
$(myarmgcc) -o $@ $^
cp *.bin /opt/fsmini/
%.o:%.c
$(myarmgcc) -c -o $@ $<
clean:
rm -f *.bin *.o
|
总结:可以看出,将device可drive分开写,驱动(driver)可以达到平台(device)无关性,这也是总线的初衷
本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1396916,如需转载请自行联系原作者