嵌入式开发记录-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);
View Code

 

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); 
}
View Code

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; 
}

 

posted @ 2020-07-05 12:02  笑不出花的旦旦  阅读(226)  评论(0)    收藏  举报