嵌入式开发记录-day17 设备注册、驱动注册、platform总线字符设备驱动框架
1、注册驱动、设备将会把驱动的name、设备的name相互比较,系统自动调用platform_match()设备都有主设备号,和次设备号;
设备注册是注册到sys/devices/platform下, 因此可以在这个下面查看我们注册到的设备: ls /sys/devices/platform
设备注册包含的结构体
platform_device struct platform_device { const char * name; // 注册设备名称 int id; // 注册设备的ID号 struct device dev; u32 num_resources; struct resource * resource; const struct platform_device_id *id_entry; // MFD cell pointer struct mfd_cell *mfd_cell; // arch specific additions struct pdev_archdata archdata; }
在这里的注册设备都注册在platform虚拟总线下的设备,所以在arch/arm/mach-exynos/mach-itop4412.c文件下注册 注册设备、驱动,都是注册到linux内核中,添加一段链表结点到platform平台的链表上
2、设备注册方式一:将设备注册进内核里的一般流程,注册一个名称为hello_ctl的设备
1、打开文件 vim driver/char/Kconfig,并添加如下代码(可以搜索LEDS,并仿照LEDS的格式),并保存退出
config HELLO_CTL bool "Enable HELLO config" default y help Enable HELLO config
2、执行make menuconfig 选择将hello_ctl编译进内核,或者编译成模块
3、打开文件 vim arch/arm/mach-exynos/mach-itop4412.c,添加如下代码,可仿照其他代码格式(可搜索LEDS)
#ifdef CONFIG_HELLO_CTL struct platform_device s3c_device_hello_ctl = { .name = "hello", .id = -1, }; #endif
4、继续搜索,LEDS找到宏定义位置,仿照其格式添加如下代码,保存退出 `#ifdef CONFIG_HELLO_CTL &s3c_device_hello_ctl, #endif`
#ifdef CONFIG_HELLO_CTL &s3c_device_hello_ctl, #endif
5、执行make zImage编译成镜像,使用fastboot烧写到板子里
6、查看注册的设备ls /sys/devices/platform/
3、驱动注册,注册hello_ctl对应的驱动
1、所需的头文件#include <linux/platform_device.h>,在vim include/linux/platform_driver.h
2、注册驱动函数extern int platform_driver_register(struct platform_driver *);
3、卸载驱动函数extern void platform_driver_unregister(struct platform_driver *);
4、注册驱动参数
struct platform_driver * 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 (*resume)(struct platform_device *); // 挂起的驱动,又恢复运行 struct device_driver driver; // 结构体 const struct platform_device_id *id_table; }
4、驱动注册代码基本实现,hello_ctl驱动注册 `// 驱动注册所需头文件 包含结构体,注册和卸载所需的函数
#include <linux/platform_device.h> #include <linux/module.h> #include <linux/init.h> #define DRIVER_NAME "hello_ctl" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("TOPEET"); // 4、结构体函数完善 // int (*probe)(struct platform_device *); // 初始化相关动作 static int hello_probe(struct platform_device *p) { printk(KERN_EMERG "\t initialized\n"); return 0; } // int (*remove)(struct platform_device *); static int hello_remove(struct platform_device *p) { return 0; } // int (*suspend)(struct platform_device *, pm_message_t state); static int hello_suspend(struct platform_device *p) { return 0; } // int (*resume)(struct platform_device *); static int hello_resume(struct platform_device *p) { return 0; } // void (*shutdown)(struct platform_device *); static void hello_shutdown(struct platform_device *p) { } // 3、结构体数据初始化 // 注册驱动传入的结构体 struct platform_driver hello_driver = { .probe = hello_probe, // 用于初始化模块 .remove = hello_remove, // 移除模块时,执行的动作 .suspend = hello_suspend, // 模块挂起时,执行的动作 .resume = hello_resume, // 挂起的模块,恢复运行执行动作 .shutdown = hello_shutdown, .driver = { .name = DRIVER_NAME, // 驱动名称 .owner = THIS_MODULE, // 驱动所有者,THIS_MODULE宏定义 }, }; } // 2、模块初始化相关实现 // 模块的入口函数 加载模块执行动作 static int hello_init(void) { int DriverState; printk(KERN_EMERG "HELLO WORLD enter!\n"); DriverState = platform_driver_register(&hello_driver); // 注册驱动 转而执行初始化动作 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); // 注册驱动执行状态 return 0; } // 模块退出函数 卸载模块所执行 static void hello_exit(void) { printk(KERN_EMERG "HELLO WORLD exit!\n"); platform_driver_unregister(&hello_driver); // 卸载驱动函数 } //1、 入口函数module_nint module_init(hello_init); //module_exit(hello_exit); } //1、 入口函数 module_init(hello_init); module_exit(hello_exit);
5、上面注册设备,设备注册需要编译到镜像中,再将镜像烧写进内核中;module方式注册设备则不需要重新编译镜像;
6、设备注册方式二,以module方式注册设备,但是需要对模块进行加载和卸载;
#include <linux/module.h> #include <linux/init.h> #include <linux/platform_device.h> #define DRIVER_NAME "hello_ctl" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("TOPEET"); static void leds_release(struct device *dev) { printk("leds_release"); } struct platform_device platform_device_hello = { .name = "my_code_led", .id = -1, .dev = { .release = leds_release, }, }; static int hello_init(void) { printk(KERN_EMERG "HELLO WORLD enter!\n"); platform_device_register(&platform_device_hello); return 0; } static void hello_exit(void) { printk(KERN_EMERG "HELLO WORLD exit!\n"); platform_device_unregister(&platform_device_hello); } //1、 入口函数module_nint module_init(hello_init); module_exit(hello_exit);
7、在调试时候,想要获取注册设备对外提供的接口信息、主要是结构体中的成员信息:
#include <linux/platform_device.h> #include <linux/module.h> #include <linux/init.h> #define DRIVER_NAME "my_code_led" MODULE_LICENSE("Dual BSD/GPL"); MODULE_AUTHOR("TOPEET"); // int (*probe)(struct platform_device *); static int hello_probe(struct platform_device *pdv) { printk(KERN_EMERG "\t initialized\n"); printk("pdv->name is %s\n",pdv->name); printk("pdv->id is %d\n",pdv->id); pdv->dev.release(&pdv->dev); return 0; } // int (*remove)(struct platform_device *); static int hello_remove(struct platform_device *p) { return 0; } // int (*suspend)(struct platform_device *, pm_message_t state); static int hello_suspend(struct platform_device *p) { return 0; } // int (*resume)(struct platform_device *); static int hello_resume(struct platform_device *p) { return 0; } // void (*shutdown)(struct platform_device *); static void hello_shutdown(struct platform_device *p) { } // 注册驱动传入的结构体 struct platform_driver hello_driver = { .probe = hello_probe, .remove = hello_remove, .suspend = hello_suspend, .resume = hello_resume, .shutdown = hello_shutdown, .driver = { .name = DRIVER_NAME, .owner = THIS_MODULE, }, }; static int hello_init(void) { int DriverState; printk(KERN_EMERG "HELLO WORLD enter!\n"); DriverState = platform_driver_register(&hello_driver); // 注册驱动 转而执行初始化动作 printk(KERN_EMERG "\tDriverState is %d\n", DriverState); return 0; } // 卸载驱动函数 static void hello_exit(void) { printk(KERN_EMERG "HELLO WORLD exit!\n"); platform_driver_unregister(&hello_driver); }
8、补充结构
struct platform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; char *driver_override; /* Driver name to force a match */ /* MFD cell pointer */ struct mfd_cell *mfd_cell; /* arch specific additions */ struct pdev_archdata archdata; }; // 设备注册 int platform_device_register(struct platform_device *pdev) { device_initialize(&pdev->dev); arch_setup_pdev_archdata(pdev); return platform_device_add(pdev); } EXPORT_SYMBOL_GPL(platform_device_register); 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 (*resume)(struct platform_device *); struct device_driver driver; const struct platform_device_id *id_table; bool prevent_deferred_probe; }; /* * use a macro to avoid include chaining to get THIS_MODULE */ #define platform_driver_register(drv) \ __platform_driver_register(drv, THIS_MODULE) /** * __platform_driver_register - register a driver for platform-level devices * @drv: platform driver structure * @owner: owning module/driver */ int __platform_driver_register(struct platform_driver *drv, struct module *owner) { drv->driver.owner = owner; drv->driver.bus = &platform_bus_type; drv->driver.probe = platform_drv_probe; drv->driver.remove = platform_drv_remove; drv->driver.shutdown = platform_drv_shutdown; return driver_register(&drv->driver); } EXPORT_SYMBOL_GPL(__platform_driver_register); /** * platform_match - bind platform device to platform driver. * @dev: device. * @drv: driver. * * Platform device IDs are assumed to be encoded like this: * "<name><instance>", where <name> is a short description of the type of * device, like "pci" or "floppy", and <instance> is the enumerated * instance of the device, like '0' or '42'. Driver IDs are simply * "<name>". So, extract the <name> from the platform_device structure, * and compare it against the name of the driver. Return whether they match * or not. */ static int platform_match(struct device *dev, struct device_driver *drv) { struct platform_device *pdev = to_platform_device(dev); struct platform_driver *pdrv = to_platform_driver(drv); /* When driver_override is set, only bind to the matching driver */ if (pdev->driver_override) return !strcmp(pdev->driver_override, drv->name); /* Attempt an OF style match first */ if (of_driver_match_device(dev, drv)) return 1; /* Then try ACPI style match */ if (acpi_driver_match_device(dev, drv)) return 1; /* Then try to match against the id table */ if (pdrv->id_table) return platform_match_id(pdrv->id_table, pdev) != NULL; /* fall-back to driver name match */ return (strcmp(pdev->name, drv->name) == 0); }
9、获取设备资源:
设备信息描述在结构体中
struct platform_device { const char *name; int id; bool id_auto; struct device dev; u32 num_resources; struct resource *resource; const struct platform_device_id *id_entry; }; struct resource { resource_size_t start; // 物理起始地址、或者GPIO号 resource_size_t end; // 物理终止地址 const char *name; // 资源名称 unsigned long flags; // 资源类型标记 struct resource *parent, *sibling, *child; };
资源类型定义:
struct resource res[]={ [0]={ .start=0x12345, .end = 0x233, flags = IORESOURCE_IO, }, [1]={ flags = IORESOURCE_MEM, }, [2]={ flags = IORESOURCE_IRQ, }, }; // flags取值类型 #define IORESOURCE_TYPE_BITS 0x00001f00 /* Resource type */ #define IORESOURCE_IO 0x00000100 /* PCI/ISA I/O ports */ #define IORESOURCE_MEM 0x00000200 #define IORESOURCE_REG 0x00000300 /* Register offsets */ #define IORESOURCE_IRQ 0x00000400 #define IORESOURCE_DMA 0x00000800
/** * platform_get_resource - get a resource for a device * @dev: platform device * @type: resource type * @num: resource index */ struct resource *platform_get_resource(struct platform_device *dev,unsigned int type, unsigned int num)
在probe中传过来的struct platform_device *p参数获取到设备信息,根据设备信息就可以映射指定的硬件资源;也就可以访问硬件操作了;
static int hello_probe(struct platform_device *p) { struct resource *res = platform_get_resource(p,IORESOURCE_IO, index) printk(KERN_EMERG "\t initialized\n"); return 0; }

浙公网安备 33010602011771号