《[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.koall: 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.symversobj-m += platform_led_dev.oobj-m += platform_led_drv.o
应用测试程序
#include#include #include #include //myledint 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-gccmyled.bin:$(objs) $(myarmgcc) -o $@ $^ cp *.bin /opt/fsmini/%.o:%.c $(myarmgcc) -c -o $@ $
总结:可以看出,将device可drive分开写,驱动(driver)可以达到平台(device)无关性,这也是总线的初衷