Platform机制

来源:互联网 发布:python exit status 2 编辑:程序博客网 时间:2024/06/04 14:50

Platform机制

Platform总线Linux中一种虚拟总线。platform机制的本身使用并不复杂,由两部分组成:platform_device和platform_driver
Platform 驱动与传统的设备驱动模型相比,优势在于platform机制将设备本身的资源注册进内核,由内核统一管理,在驱动程序使用这些资源时使用统一的接口,这样提高了程序的可移植性
通过platform机制开发底层设备驱动的流程:
定义platform_device->注册platform_device->定义platform_driver->注册platform_driver
Platform机制使用了bus driver device模型,device是硬件相关代码,driver是比较稳定的代码,类似输入子系统里面,input.c是提供给上层的统一接口,buttons.c是硬件相关代码<-分离->evdev.c是纯软件比较稳定的代码。


1 分析drivers\base\platform.c

platform总线的注册在drivers\base\init.c中调用
void __init driver_init(void){/* These are the core pieces */devtmpfs_init();devices_init();buses_init();classes_init();firmware_init();hypervisor_init();/* These are also core pieces, but must come after the * core core pieces. */platform_bus_init();system_bus_init();cpu_dev_init();memory_dev_init();}
int __init platform_bus_init(void){int error;early_platform_cleanup();error = device_register(&platform_bus); // 总线也是一个设备if (error)return error;error =  bus_register(&platform_bus_type); // 注册platform总线if (error)device_unregister(&platform_bus);return error;}


platform机制是基于Linux总线设备驱动模型实现的
struct bus_type platform_bus_type = {.name= "platform",.dev_attrs= platform_dev_attrs, // 总线属性.match= platform_match, // 用于匹配platform device和platform driver.uevent= platform_uevent,.pm= &platform_dev_pm_ops, // 电源管理相关};
struct device platform_bus = {.init_name= "platform",};


static struct device_attribute platform_dev_attrs[] = {__ATTR_RO(modalias),__ATTR_NULL,};#define __ATTR_RO(_name) { \.attr= { .name = __stringify(_name), .mode = 0444 },\.show= _name##_show,\}static ssize_t modalias_show(struct device *dev, struct device_attribute *a,     char *buf){struct platform_device*pdev = to_platform_device(dev);int len = snprintf(buf, PAGE_SIZE, "platform:%s\n", pdev->name);return (len >= PAGE_SIZE) ? (PAGE_SIZE - 1) : len;}

根据name来匹配
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);/* Attempt an OF style match first */if (of_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);}

当环境变化时调用(设备拔插等)

static int platform_uevent(struct device *dev, struct kobj_uevent_env *env){struct platform_device*pdev = to_platform_device(dev);int rc;/* Some devices have extra OF data and an OF-style MODALIAS */rc = of_device_uevent(dev,env);if (rc != -ENODEV)return rc;// 添加值到环境buffer中add_uevent_var(env, "MODALIAS=%s%s", PLATFORM_MODULE_PREFIX,(pdev->id_entry) ? pdev->id_entry->name : pdev->name);return 0;}

2. Platform平台设备

平台设备使用Struct Platform_device来描述:
struct platform_device {    const char *name; /*设备名*/    int id; /*设备编号,配合设备名使用*/    struct device dev;    u32 num_resources;    struct resource *resource; /*设备资源*/}


2.1 Platform_device的分配使用

struct platform_device *platform_device_alloc(const char *name, int id)
参数:
name: 设备名
id: 设备id,一般为-1

2.2 注册平台设备

int platform_device_register(struct platform_device *pdev)
会调用device_add,主要操作有:
1. 把device放入bus的dev链表
2. 从bus的drv链表中取出每一个drv,用bus的match函数判断drv是否支持该dev
3. 若可以支持,调用drv的probe函数
详细可以参考:device_add流程
int platform_device_register(struct platform_device *pdev){device_initialize(&pdev->dev);return platform_device_add(pdev); // 里面会调用device_add}

2.3 平台设备资源

使用struct resource来描述:
struct resource {    resource_size_t start; //资源的起始物理地址    resource_size_t end; //资源的结束物理地址    const char *name; //资源的名称    unsigned long flags; //资源的类型,比如MEM,IO,IRQ类型    struct resource *parent, *sibling, *child; //资源链表指针}

2.4 获取资源

struct resource *platform_get_resource(struct platform_device*dev, unsigned int type, unsigned int num)
参数:
dev: 资源所属的设备
type: 获取的资源类型
num: 获取的资源数

3 Platform平台驱动

使用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 (*suspend_late)(struct platform_device *, pm_message_t state);    int (*resume_early)(struct platform_device *);    int (*resume)(struct platform_device *);    struct device_driver driver;}

3.1 平台驱动注册

int platform_driver_register(struct platform_driver *)
1. 把drv放入bus的drv链表
2. 从bus的dev链表中取出每一个dev,用bus的match函数判断该drv是否支持dev
3. 若可以支持,调用drv的probe函数

int platform_driver_register(struct platform_driver *drv){drv->driver.bus = &platform_bus_type;if (drv->probe)drv->driver.probe = platform_drv_probe;if (drv->remove)drv->driver.remove = platform_drv_remove;if (drv->shutdown)drv->driver.shutdown = platform_drv_shutdown;return driver_register(&drv->driver); // 里面会调用driver_find(drv->name, drv->bus);遍历设备}

什么时候驱动回去遍历设备,看能否处理?
注册驱动时或注册设备时都会去相互遍历匹配
驱动和设备怎么匹配呢?
根据name来匹配滴
通常会在probe函数里面创建设备节点,方便上层调用

4 实例

led_drv.c
/* 分配/设置/注册一个platform_driver */#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/fs.h>#include <linux/interrupt.h>#include <linux/irq.h>#include <linux/sched.h>#include <linux/pm.h>#include <linux/sysctl.h>#include <linux/proc_fs.h>#include <linux/delay.h>#include <linux/platform_device.h>#include <linux/input.h>#include <linux/irq.h>#include <asm/uaccess.h>#include <asm/io.h>static int major;static struct class *cls;static volatile unsigned long *gpio_con;static volatile unsigned long *gpio_dat;static int pin;static int led_open(struct inode *inode, struct file *file){//printk("first_drv_open\n");/* 配置为输出 */*gpio_con &= ~(0x3<<(pin*2));*gpio_con |= (0x1<<(pin*2));return 0;}static ssize_t led_write(struct file *file, const char __user *buf, size_t count, loff_t * ppos){int val;//printk("first_drv_write\n");copy_from_user(&val, buf, count); //copy_to_user();if (val == 1){// 点灯*gpio_dat &= ~(1<<pin);}else{// 灭灯*gpio_dat |= (1<<pin);}return 0;}static struct file_operations led_fops = {    .owner  =   THIS_MODULE,    /* 这是一个宏,推向编译模块时自动创建的__this_module变量 */    .open   =   led_open,     .write=led_write,   };static int led_probe(struct platform_device *pdev){struct resource*res;/* 根据platform_device的资源进行ioremap */res = platform_get_resource(pdev, IORESOURCE_MEM, 0);gpio_con = ioremap(res->start, res->end - res->start + 1);gpio_dat = gpio_con + 1;res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);pin = res->start;/* 注册字符设备驱动程序 */printk("led_probe, found led\n");major = register_chrdev(0, "myled", &led_fops);cls = class_create(THIS_MODULE, "myled");class_device_create(cls, NULL, MKDEV(major, 0), NULL, "led"); /* /dev/led */// 后续新的内核版本使用的是class_create()和device_create()return 0;}static int led_remove(struct platform_device *pdev){/* 卸载字符设备驱动程序 *//* iounmap */printk("led_remove, remove led\n");class_device_destroy(cls, MKDEV(major, 0));class_destroy(cls);unregister_chrdev(major, "myled");iounmap(gpio_con);return 0;}struct platform_driver led_drv = {.probe= led_probe,.remove= led_remove,.driver= {.name= "myled",}};static int led_drv_init(void){platform_driver_register(&led_drv);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");
led_dev.c
#include <linux/module.h>#include <linux/version.h>#include <linux/init.h>#include <linux/kernel.h>#include <linux/types.h>#include <linux/interrupt.h>#include <linux/list.h>#include <linux/timer.h>#include <linux/init.h>#include <linux/serial_core.h>#include <linux/platform_device.h>/* 分配/设置/注册一个platform_device */static struct resource led_resource[] = {    [0] = {        .start = 0x56000010,             /* TQ2440的LED是GPB5,6,7,8, GPBCON地址是0x56000010 */        .end   = 0x56000010 + 8 - 1,        .flags = IORESOURCE_MEM,    },    [1] = {        .start = 5,                      /* LED1 */        .end   = 5,        .flags = IORESOURCE_IRQ,    }};static void led_release(struct device * dev){}static struct platform_device led_dev = {    .name         = "myled",    .id       = -1,    .num_resources    = ARRAY_SIZE(led_resource),    .resource     = led_resource,    .dev = {     .release = led_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");




原创粉丝点击