嵌入式学习9--platform驱动模型

1.驱动模型

platform驱动模型由bus、device、driver组成,bus已经是由内核做好的,我们使用这个模型主要是需要将device和driver添加进去,并让其能够正确匹配进行工作,使用驱动模型是为了让程序运行得更加稳定,将驱动程序分开编写,也便于维护和修改。

流程主要是将不同的设备的driver和device分别添加进链表,再用设备名去匹配,如果有相同的设备名就连接起来,然后调用相关操作,完成驱动工作。

/*driver注册过程*/

int platform_driver_register(struct platform_driver *drv)
{

     /*设置driver参数*/
    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);  //将设置后的结构体进行注册
}

int driver_register(struct device_driver *drv)
{

    .......

    other = driver_find(drv->name, drv->bus);  //用设备名判断是否有重复的设备,有就不注册
    if (other) {
        printk(KERN_ERR "Error: Driver '%s' is already registered, "
            "aborting...\n", drv->name);
        return -EBUSY;
    }

    ret = bus_add_driver(drv);  //将驱动加入BUS,实质是将驱动加入链表

    ret = driver_add_groups(drv, drv->groups);  //创建sysfs接口,使得上层可以与硬件进行交互
    .......
}

int bus_add_driver(struct device_driver *drv)
{

    ......

    /*重要操作主要是这部分,在这里会将驱动添加进入链表

     * 并且还会创建sysfs接口,使得上层可以与硬件进行交互

     */
    klist_init(&priv->klist_devices, NULL, NULL);
    priv->driver = drv;
    drv->p = priv;
    priv->kobj.kset = bus->p->drivers_kset;
    error = kobject_init_and_add(&priv->kobj, &driver_ktype, NULL,
                     "%s", drv->name);
    klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);
    module_add_driver(drv->owner, drv);

    error = driver_create_file(drv, &driver_attr_uevent);
    error = driver_add_attrs(bus, drv);

    kobject_uevent(&priv->kobj, KOBJ_ADD);
    return 0;
}


/*device注册过程*/

int platform_device_register(struct platform_device *pdev)
{
    device_initialize(&pdev->dev);
    arch_setup_pdev_archdata(pdev);
    return platform_device_add(pdev);
}

int platform_device_add(struct platform_device *pdev)
{

    .......

    /*设置设备名,用于匹配*/

    if (pdev->id != -1)
        dev_set_name(&pdev->dev, "%s.%d", pdev->name,  pdev->id);
    else
        dev_set_name(&pdev->dev, "%s", pdev->name);

    /*将设置的资源、name等内容进行设置*/

    for (i = 0; i < pdev->num_resources; i++) {
        struct resource *p, *r = &pdev->resource[i];

        if (r->name == NULL)
            r->name = dev_name(&pdev->dev);

        p = r->parent;
        if (!p) {
            if (resource_type(r) == IORESOURCE_MEM)
                p = &iomem_resource;
            else if (resource_type(r) == IORESOURCE_IO)
                p = &ioport_resource;
        }

    /*关键步骤就是add,本质是将设置好的结构体加入到链表中*/

    ret = device_add(&pdev->dev);
    if (ret == 0)
        return ret;

    return ret;
}

在将driver和device加入链表后,程序运行时会通过设备来进行匹配

在platform_match函数实现中最后都会调用strcmp来对设备名进行配对

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);
    if (of_driver_match_device(dev, drv))
        return 1;
    if (pdrv->id_table)
        return platform_match_id(pdrv->id_table, pdev) != NULL;
    return (strcmp(pdev->name, drv->name) == 0);
}

2.实际操作

匹配完成后就可以使程序正常运行,至于其他的操作和不用platform驱动模型的编写方式类似,也是通过file_operations结构体进行设备注册,然后操作硬件完成相关功能,以下为用platform驱动模型编写的LED驱动

----------------device部分------------------

#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <mach/devices.h>
#include <linux/wait.h>
#include <linux/gpio.h>
#include <linux/input.h>

#define LED_DEV_NAME "led_dev"
#define LED_PHY_START 0xC001E000
#define LED_PHY_END   LED_PHY_START + 0x1000 - 1
#define LED_PHY_NUM   13

void led_release(struct device *led_dev)
{
    printk(KERN_WARNING "led_dev_release\n");
}

/*创建资源结构体,将硬件相关的资源添加,如IO、中断资源*/

static struct resource led_resource[] = {
    [0] = {
        .start = LED_PHY_START,
        .end = LED_PHY_END,
        .flags = IORESOURCE_MEM,
    },
    [1] = {
        .start = LED_PHY_NUM,
        .end   = LED_PHY_NUM,
        .flags = IORESOURCE_IRQ,
    },
};

static struct platform_device led_dev = {
        .name = LED_DEV_NAME,
        .num_resources = ARRAY_SIZE(led_resource),
        .resource = led_resource,
        .dev = {
            .release = led_release,
        },
};

static int __init led_dev_init(void)
{
    return platform_device_register(&led_dev);
}

static void __exit led_dev_exit(void)
{
    platform_device_unregister(&led_dev);

module_init(led_dev_init);
module_exit(led_dev_exit);


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("6818 led dev");
 

----------------driver部分------------------

#include <linux/module.h>
#include <linux/init.h>
#include <linux/err.h>
#include <linux/miscdevice.h>
#include <linux/irq.h>
#include <linux/io.h>
#include <linux/device.h>
#include <linux/wait.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/cdev.h>
#include <mach/devices.h>

#define LED_DEV_NAME "led_dev"

unsigned long int led_major = 0;
unsigned long int led_minor = 0;

static dev_t led_dev_num = 0;
static struct cdev led_dev_cdev;
static struct class *led_dev_class;
static struct device *led_dev_device;
static struct resource *led_dev_mem_res;
static struct resource *led_dev_irq_res;
static struct resource *led_dev_request_res;
static void __iomem *io_map;

static void led_set(void __iomem *led_reg, int led_num)
{
    int temp;

    printk(KERN_WARNING "led_set\n");

    switch(led_num){
    case 7:
        temp = readl(led_reg+0x20);
        temp &= ~(0x3 << 26);
        writel(temp, led_reg+0x20);

        temp = readl(led_reg+0x04);
        temp |= (0x1 << 13);
        writel(temp, led_reg+0x04);

        temp = readl(led_reg+0x00);
        temp &= ~(0x1 << 13);
        writel(temp, led_reg+0x00);
        break;
        
    case 8:
        break;
    case 9:
        break;
    case 10:
        break;
    default:
        break;
    }
}

static int led_open(struct inode *inode, struct file *filp)
{
    printk(KERN_WARNING "led_open\n");

    led_set(io_map, 7);
    
    return 0;
}

static ssize_t led_read(struct file *filp, char __user *usr, size_t size, loff_t *offset)
{
    return 0;
}

static ssize_t led_write(struct file *filp, const char __user *usr, size_t size, loff_t *offset)
{
    return 0;
}

static int led_release(struct inode *inode, struct file *filp)
{
    int temp;
    
    printk(KERN_WARNING "led_release\n");

    temp = readl(io_map+0x00);
    temp |= (0x1 << 13);
    writel(temp, io_map+0x00);
    
    return 0;
}

static struct file_operations fops = {
    .owner = THIS_MODULE,
    .open  = led_open,
    .read  = led_read,
    .write = led_write,
    .release = led_release,
};

static int led_probe(struct platform_device *led_dev)
{
    int ret = 0;

    printk(KERN_WARNING "led_probe\n");
    
    led_dev_num = MKDEV(led_major, led_minor);

    if(led_major)
        ret = register_chrdev_region(led_dev_num, 1, LED_DEV_NAME);
    else
        ret = alloc_chrdev_region(&led_dev_num, 0, 1, LED_DEV_NAME);
    
    cdev_init(&led_dev_cdev, &fops);

    ret = cdev_add(&led_dev_cdev, led_dev_num, 1);
    
    led_dev_class = class_create(THIS_MODULE, LED_DEV_NAME);

    led_dev_device = device_create(led_dev_class, NULL, led_dev_num, NULL, LED_DEV_NAME);

    /*通过device的资源结构体获取硬件配置,此为获取IO资源*/

    led_dev_mem_res = platform_get_resource(led_dev, IORESOURCE_MEM, 0);

     /*通过device的资源结构体获取硬件配置,此为获取中断资源*/

    led_dev_irq_res = platform_get_resource(led_dev, IORESOURCE_IRQ, 0);

    /*将获得的资源申请出来以便使用*/

    led_dev_request_res = request_mem_region(led_dev_mem_res->start, led_dev_mem_res->end - led_dev_mem_res->start + 1, "led7");

     /*将申请到的资源进行映射,因为不能直接操作硬件地址,需要映射为虚拟地址后再操作*/

    io_map = ioremap(led_dev_mem_res->start, led_dev_mem_res->end - led_dev_mem_res->start + 1);

    return 0;
}

int led_remove(struct platform_device *led_dev)
{
    printk(KERN_WARNING "led_remove\n");
    release_mem_region(led_dev_mem_res->start, led_dev_mem_res->end - led_dev_mem_res->start + 1);
    device_destroy(led_dev_class, led_dev_num);
    class_destroy(led_dev_class);
    cdev_del(&led_dev_cdev);
    unregister_chrdev_region(led_dev_num, 1);
    return 0;
}

static struct platform_driver led_drv = {
    .probe = led_probe,
    .remove = led_remove,
    .driver = {
        .name = LED_DEV_NAME,
    },
};

static int __init led_drv_init(void)
{
    return platform_driver_register(&led_drv);
}

static void __exit led_drv_exit(void)
{
    platform_driver_unregister(&led_drv);

module_init(led_drv_init);
module_exit(led_drv_exit);


MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("6818 led drv");

使用驱动模型编写的代码比较稳定,在硬件有变动时,可以只考虑修改device部分的代码,而driver部分也只需要修改某些和硬件相关的部分,基本框架是不用修改的,这样会减少代码的复杂度,也便于修改且能保持足够的稳定

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值