《[arm驱动]Platform设备驱动》涉及内核驱动函数二个,内核结构体一个,分析了内核驱动函数二个;可参考的相关应用程序模板或内核驱动模板零个,可参考的相关应用程序或内核驱动一个
一、关键是device程序中resource结构体
结构体一)resource结构体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程序中获取资源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内核源码
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个中断资源platform_get_irq(struct platform_device * dev, unsigned int num)
内核源码二)platform_get_irq内核源码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/*代码代换:platform_led_dev_, "platform_led"*/
#include
#include
#include
#include
#include
#include
#include //文件系统相关的函数和头文件
#include
#include //cdev结构的头文件包含
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
/*
*代码代换:platform_led_drv_, "platform_led"
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
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
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
应用测试程序
#include
#include
#include
#include
//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
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)无关性,这也是总线的初衷