OK6410之驱动分离分层机制

在linux驱动中存在着一种驱动分离分层的总线机制。

 

platform会存在/sys/bus/里面

 

其中bus总线上有关于driver的链表drv和关于device的链表dev。当你注册device或者是driver结构体的时候:

(1)将device或者driver结构体加入bus总先相应的链表中;

(2)通过.match函数和bus上的相对的链表中的结构体进行匹配

(3)若匹配成功则调用driver的.probe函数

注意这是一种机制,具体怎么使用自己可以再讨论

 

其中platform是个全局变量,为platform_bus_type,属于虚拟设备总线,通过这个总线将设备和驱动联系起来,属于Linux中bus的一种

该platform_bus_type的结构体定义如下所示(位于drivers/base):

struct bus_type platform_bus_type = {              
.name              = "platform",             //设备名称
.dev_attrs        = platform_dev_attrs,    //设备属性、含获取sys文件名,该总线会放在/sys/bus下
.match              = platform_match,   //匹配设备和驱动,匹配成功就调用driver的.probe函数
.uevent             = platform_uevent,  //消息传递,比如热插拔操作
.suspend         = platform_suspend,     //电源管理的低功耗挂起
.suspend_late          = platform_suspend_late,  
.resume_early         = platform_resume_early,
.resume           = platform_resume,    //恢复
};

接下来以/drivers/input/keybard/gpio_keys.c程序为例子

 

在驱动层init入口函数中通过platform_driver_register()来注册diver驱动

在驱动层exit出口函数中通过platform_driver_unregister()函数来注销diver驱动

static int __init gpio_keys_init(void)    //init出口函数
{
   return platform_driver_register(&gpio_keys_device_driver);   //注册driver驱动
}

static void __exit gpio_keys_exit(void)   //exit出口函数
{
   platform_driver_unregister(&gpio_keys_device_driver);   //注销driver驱动
}

其中platform_driver_register()函数具体定义如下:

int platform_driver_register(struct platform_driver *drv)
{
   drv->driver.bus = &platform_bus_type;                //(1)挂接到虚拟总线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;
   if (drv->suspend)
        drv->driver.suspend = platform_drv_suspend;
    if (drv->resume)
        drv->driver.resume = platform_drv_resume;

   return driver_register(&drv->driver);              //(2) 注册到driver目录下
}

注册driver后会调用platform结构体中的.match函数匹配相应的device。

 

接下来尝试编写led的dev.c和drv.c文件

其中dev.c是设备文件,编写与硬件相关的内容;drv.c为驱动文件,编写与驱动相关的内容。

 

首先编写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>

static struct resource led_resource[] = {               //资源数组
    [0] = {
        .start = 0x56000050,                     //led的寄存器GPFCON起始地址
        .end   = 0x56000050 + 8 - 1,     // led的寄存器GPFDAT结束地址
        .flags = IORESOURCE_MEM,      //表示地址资源
    },
    [1] = {
        .start =  5,                                   //表示GPF第几个引脚开始
        .end   = 5,                            //结束引脚
        .flags = IORESOURCE_IRQ,     //表示中断资源    
    } 
};

static void led_release(struct device * dev)       //释放函数
{}

static struct platform_device led_dev = {
    .name         = "myled",                    //对应的platform_driver驱动的名字
    .id       = -1,                                    //表示只有一个设备
    .num_resources    = ARRAY_SIZE(led_resource),        //资源数量,ARRAY_SIZE()函数:获取数量
    .resource     = led_resource,      //资源数组led_resource
    .dev = {
    .release = led_release,                 //释放函数,必须向内核提供一个release函数, 、
                                       //否则卸载时,内核找不到该函数会报错
       },
};


static int led_dev_init(void)    //入口函数,注册dev设备
{
  platform_device_register(&led_dev);
  return 0;
}

static void led_dev_exit(void) //出口函数,注销dev设备
{
  platform_device_unregister(&led_dev); 
}
module_init(led_dev_init);   //修饰入口函数
module_exit(led_dev_exit);  //修饰出口函数
MODULE_LICENSE("GPL");   //声明函数

可以看到其中比较重要的是platform_device结构体和led_resource结构体。

(1)platform_device的name是用于跟driver配对的,只有这个配对上了才会调用probe函数。

(2)resource指代的是硬件相关的资源,在probe函数中通过调用这些资源进行寄存器配置。

(3)最后调用platform_device_register()函数进行注册即可。在该函数中调用platform_device_add()函数将device加入到总线中

struct resource {
         resource_size_t start;                    //起始资源,如果是地址的话,必须是物理地址
         resource_size_t end;                      //结束资源,如果是地址的话,必须是物理地址
         const char *name;                         //资源名
         unsigned long flags;                      //资源的标志
         //比如IORESOURCE_MEM,表示地址资源, IORESOURCE_IRQ表示中断引脚... ...

         struct resource *parent, *sibling, *child;   //资源拓扑指针父、兄、子,可以构成链表
};
struct platform_device {
  const char       * name;           //设备名称,要与platform_driver的name一样,这样总线才能匹配成功
  u32          id;                   //id号,插入总线下相同name的设备编号(一个驱动可以有多个设备),如果只有一个设备填-1
  struct  device  dev;               //内嵌的具体的device结构体,其中成员platform_data,是个void *类型,可以给平台driver提供各种数据(比如:GPIO引脚等等)
  u32 num_resources;                 //资源数量,
  struct resource         * resource;    //资源结构体,保存设备的信息
};
int platform_device_add(struct platform_device *pdev)
{
    int i, ret = 0;

    if (!pdev)
        return -EINVAL;

    if (!pdev->dev.parent)
        pdev->dev.parent = &platform_bus;//父设备设置为platform_bus

    pdev->dev.bus = &platform_bus_type;//设置挂在platform总线上

    if (pdev->id != -1)
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%d", pdev->name,
             pdev->id);
    else
        strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);

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

        if (r->name == NULL)
            r->name = pdev->dev.bus_id;

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

        if (p && insert_resource(p, r)) {
            printk(KERN_ERR
                   "%s: failed to claim resource %d\n",
                   pdev->dev.bus_id, i);
            ret = -EBUSY;
            goto failed;
        }
    }

    pr_debug("Registering platform device '%s'. Parent at %s\n",
         pdev->dev.bus_id, pdev->dev.parent->bus_id);

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

failed:
    while (--i >= 0)
        if (pdev->resource[i].flags & (IORESOURCE_MEM|IORESOURCE_IO))
            release_resource(&pdev->resource[i]);
    return ret;
}

接下来开始编写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 */
	
	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");



该文件重点是probe函数中的处理。当有device和driver匹配成功时就会调用该函数。因此该函数中主要是对device中resource的利用。以后当硬件连接电路等改变时只要修改dev.c中的内容即可

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值