快捷搜索:  汽车  科技

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)struct platform_device { const char * name; u32 id; struct device dev; u32 num_resources; struct resource * resource; };备注:struct device dev;struct bus_type { char * name; struct subsystem subsys; struct kset drivers; struct kset devices; struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; int (*match)(struct device * d

什么是总线:

通俗来说就是:一个总线是处理器和一个或多个设备之间的通道。所有的设备都通过一个总线连接 比如STM32的APB2总线,在其上面挂载着相应的外设(GPIOx等外设)。

驱动程序分离/分层的概念:

回顾之前讲解的控制LED亮灭的Linux驱动程序,我们了解到,想要点亮LED主要需要两个部分:1,硬件部分:设置相应的硬件,比如:重映射某个寄存器的地址,然后设置为输出模式。2,软件部分:编写相应的驱动代码,比如注册驱动设备等。

当我们修改硬件连接,即换成其它引脚控制LED时,那么我们按照之前的方法就是重新编写整个程序,这样显得比较麻烦。此时就引入一个分离/分层的概念,通俗来讲就是,将上述所说的两部分(硬件部分和软件部分)分离出来,以后修改硬件连接时,就直接修改硬件部分,而那些通用的软件部分(所谓通用:就是不管我们的灯连接在哪个引脚,但都需要注册相应的设备,这些注册代码就通用的)就不用修改,这样就可以减少开发时间。

问题:怎么将上述分离/分层的两个部分连接起来?

方法:引入我们上述讲的“总线”,即使用一个虚拟总线挂接两个部分(注:这里的虚拟总线不是物理上真实存在的总线,是用软件代码模拟出来的总线)

简单查看内核提供的总线相关代码:

linux-2.6.22.6\Documentation\driver-model这里面有相应的描述文件

总线类型:

struct bus_type { char * name; struct subsystem subsys; struct kset drivers; struct kset devices; struct bus_attribute * bus_attrs; struct device_attribute * dev_attrs; struct driver_attribute * drv_attrs; int (*match)(struct device * dev struct device_driver * drv); int (*hotplug) (struct device *dev char **envp int num_envp char *buffer int buffer_size); int (*suspend)(struct device * dev pm_message_t state); int (*resume)(struct device * dev); };

平台设备

struct platform_device { const char * name; u32 id; struct device dev; u32 num_resources; struct resource * resource; };

备注:struct device dev;

device

struct device { struct klist klist_children; struct klist_node knode_parent; /* node in sibling list */ struct klist_node knode_driver; struct klist_node knode_bus; struct device *parent; struct kobject kobj; char bus_id[BUS_ID_SIZE]; /* position on parent bus */ struct device_type *type; unsigned is_registered:1; unsigned uevent_suppress:1; struct device_attribute uevent_attr; struct device_attribute *devt_attr; struct semaphore sem; /* semaphore to synchronize calls to * its driver. */ struct bus_type * bus; /* type of bus device is on */ struct device_driver *driver; /* which driver has allocated this device */ void *driver_data; /* data private to the driver */ void *platform_data; /* Platform specific data device core doesn't touch it */ struct dev_pm_info power; #ifdef CONFIG_NUMA int numa_node; /* NUMA node this device is close to */ #endif u64 *dma_mask; /* dma mask (if dma'able device) */ u64 coherent_dma_mask;/* Like dma_mask but for alloc_coherent mappings as not all hardware supports 64 bit addresses for consistent allocations such descriptors. */ struct list_head dma_pools; /* dma pools (if dma'ble) */ struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */ /* arch specific additions */ struct dev_archdata archdata; spinlock_t devres_lock; struct list_head devres_head; /* class_device migration path */ struct list_head node; struct class *class; dev_t devt; /* dev_t creates the sysfs "dev" */ struct attribute_group **groups; /* optional groups */ void (*release)(struct device * dev); };

平台驱动

struct platform_driver { int (*probe)(struct platform_device *); int (*remove)(struct platform_device *); void (*shutdown)(struct platform_device *); int (*suspend)(struct platform_device * pm_message_t state); int (*suspend_late)(struct platform_device * pm_message_t state); int (*resume_early)(struct platform_device *); int (*resume)(struct platform_device *); struct device_driver driver; };

备注:struct device_driver driver;

device_driver

struct device_driver { const char * name; struct bus_type * bus; struct kobject kobj; struct klist klist_devices; struct klist_node knode_bus; struct module * owner; const char * mod_name; /* used for built-in modules */ struct module_kobject * mkobj; int (*probe) (struct device * dev); int (*remove) (struct device * dev); void (*shutdown) (struct device * dev); int (*suspend) (struct device * dev pm_message_t state); int (*resume) (struct device * dev); };平台驱动与平台设备的关系

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)(1)

平台设备注册函数执行主要流程:

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)(2)

我们从上图可知,int platform_device_register(struct platform_device * pdev) 不仅向上将设备注册到总线bus上,而且最终还会查看是否有合适的驱动,如果有就调用 int device_bind_driver(struct device *dev)函数进行设备与驱动的绑定。

平台驱动注册函数执行主要流程:

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)(3)

我们从上图可知,int platform_driver_register(struct platform_driver *drv)不仅向上将驱动注册到总线bus上,而且最终还会查看是否有合适的设备。

关联:

上述讲解了相应的结构体和相关函数的主要执行过程,但引入一个问题:内核中这么多的总线,平台设备和平台驱动是怎么知道它们是属于同一个虚拟总线的?

方法:

我们可以从下图中标记的数字1就可知道,它们在执行时会绑定到同一个总线上。

嵌入式驱动开发新方向(嵌入式设备驱动程序基础笔记第17期)(4)

备注:数字1是:

pdev->dev.bus = &platform_bus_type;

drv->driver.bus = &platform_bus_type;

具体的平台总线定义如下:

struct bus_type platform_bus_type = { .name = "platform" .dev_attrs = platform_dev_attrs .match = platform_match .uevent = platform_uevent .suspend = platform_suspend .suspend_late = platform_suspend_late .resume_early = platform_resume_early .resume = platform_resume };总结:通俗来讲就是:平台设备注册函数在名为platform的总线上进行设备的注册,同时查看是否有合适的平台驱动,如有就进行绑定;平台驱动注册函数在名为platform的总线上进行驱动的注册,同时查看是否有合适的设备,如有就进行绑定。上述过程需通过match函数(.match = platform_match)。

static int platform_match(struct device * dev struct device_driver * drv) { struct platform_device *pdev = container_of(dev struct platform_device dev); return (strncmp(pdev->name drv->name BUS_ID_SIZE) == 0); }补充:(总线初始化相关调用过程)

1, start_kernel 2, rest_init 3, kernel_thread 4, kernel_init 5, do_basic_setup 6, driver_init 7, platform_bus_init 8, device_register(&platform_bus); struct device platform_bus = { .bus_id = "platform" }; 9, bus_register(&platform_bus_type); struct bus_type platform_bus_type = { .name = "platform" .dev_attrs = platform_dev_attrs .match = platform_match .uevent = platform_uevent .suspend = platform_suspend .suspend_late = platform_suspend_late .resume_early = platform_resume_early .resume = platform_resume };编写程序验证

编写一个简单的控制LED的程序,分别是两个C文件:一个编写平台设备,一个编写平台驱动

1,编写平台设备

//平台设备相关代码用于硬件相关 #include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.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 resource led_dev_resource[] = { [0] = { .start = 0x56000050 .end = 0x56000058 .name = "doubixioaohanhan" .flags = IORESOURCE_MEM } [1] = { .start = 4 .end = 4 .flags = IORESOURCE_IRQ } }; static void led_dev_release(struct device * dev) { printk("doubixiaohanhan"); } static struct platform_device led_dev = { .name = "led_bus" .id = 0 .resource = led_dev_resource .num_resources = ARRAY_SIZE(led_dev_resource) .dev ={ .release = led_dev_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");

2,编写平台驱动

#include <linux/module.h> #include <linux/kernel.h> #include <linux/fs.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/irq.h> #include <linux/platform_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/device.h> #define CLASS_NAME "doubixiaohanhan" #define OUTPUT_HIGH 1 #define OUTPUT_LOW 0 int major = 0; static struct class *led_class; static struct class_device *led_class_device; static volatile unsigned long *gpiof_con; static volatile unsigned long *gpiof_dat; static int led_pin; static int led_drv_open(struct inode * inode struct file * file) { *gpiof_con &= ~(3<<(led_pin * 2)); *gpiof_con |= (1<<(led_pin * 2)); printk("led_pin configurate output mode"); return 0; } static ssize_t led_drv_write(struct file * file const char __user * userbuf size_t count loff_t * off) { int val = 0; if( copy_from_user(&val userbuf 1)) ; if(OUTPUT_HIGH == val) { *gpiof_dat |= (1<<led_pin); } else { *gpiof_dat &= ~(1<<led_pin); } return 0; } static struct file_operations led_drv_fops={ .owner = THIS_MODULE .open = led_drv_open .write = led_drv_write }; static int led_drv_probe(struct platform_device *pdev) { struct resource *res; printk("I am doubixiaohanhan probe"); //register device driver major = register_chrdev(0 "led" &led_drv_fops); //create class and class-device led_class = class_create(THIS_MODULE CLASS_NAME); led_class_device = class_device_create(led_class NULL MKDEV(major 1) NULL "LED"); //PORT REMAP res = platform_get_resource(pdev IORESOURCE_MEM 0); gpiof_con = ioremap(res->start res->end - res->start 1); gpiof_dat = gpiof_con 1; res = platform_get_resource(pdev IORESOURCE_IRQ 0); led_pin = res->start; return 0; } static int led_drv_remove(struct platform_device *pdev) { printk("I am doubixiaohanhan remove"); iounmap(gpiof_con); class_destroy(led_class); //class_device_destroy(led_class led_class_device); class_device_destroy(led_class MKDEV(major 1)); unregister_chrdev(major "led"); return 0; } static struct platform_driver led_drv = { .probe = led_drv_probe .remove = led_drv_remove .driver = { .name = "led_bus" .owner = THIS_MODULE } }; //drv->driver.probe = platform_drv_probe; //int (*probe)(struct platform_device *); //led_drv->driver.probe=platform_drv_probe //led_drv->led_drv_probe static int led_drv_init(void) { platform_driver_register(&led_drv);//driver_register(&drv->driver); 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");

3,编写应用测试程序

#include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> //输入数字1熄灭LED //输入其它字符开启LED int main(int argc char **argv) { int fd; char pin[2] = {1 0}; int val; fd = open("/dev/LED" O_RDWR); if(fd < 0) { printf("/dev/LED can't open\n"); return ; } printf("\n"); while(1) { printf("\ninput integer select light on/off\n"); scanf("%d" &val); if(val == 1) write(fd &pin[0] 1); else write(fd &pin[1] 1); } }

4,编写Makefile

#compile regular KERN_DIR = /work/system/linux-2.6.22.6 all: make -C $(KERN_DIR) M=`pwd` modules rm -rf modules.order Module.symvers .PHONY: clean: make -C $(KERN_DIR) M=`pwd` modules clean rm -rf modules.order Module.symvers obj-m = led_dev.o obj-m = led_drv.o

然后编译测试成功。

欢迎关注“逗比小憨憨”,更多内容正在持续不断更新中。。。。。。

猜您喜欢: