且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

[arm驱动]从零开始写个platform平台总线

更新时间:2022-09-18 21:45:59

 首先要知道platform是总线中的一种,要实现platform总线先要从总线开始下手。

第一阶段:先创建一个mybus总线和为测试match匹配而写的device和driver

1、先创建一个mybus的bus

structbus_type mybustype = {
.name = "mybus",/*创建的总线目录为/sys/bus/mybus
*/
};
EXPORT_SYMBOL(mybustype);///proc/kallsyms
staticint__init bustest_init(void){
intret;
printk("*****%s****\n", __FUNCTION__);
ret = bus_register(&mybustype);
if(ret < 0){
printk("bus_register fail\n");
returnret;
}
return0;
}

2、一般总线都需要一些属性的描述,也提供获取版本信息的外部接口


/************声明一个总线(bus)的总线的属性(attribute)对象*******************/
charbusversion[SIZE] = "mybus";
staticssize_t mybusattrshow(structbus_type * bus, char* buf){//提供外部访问设备属性的接口
printk("*****%s****\n", __FUNCTION__);
returnsnprintf(buf, SIZE, "%s\n", busversion);
}
staticssize_t mybusattrstore(structbus_type *bus, constchar* buf, size_tcount){//提供外部访问设备属性的接口
printk("*****%s****\n", __FUNCTION__);
returnsnprintf(busversion, SIZE, "%s", buf);
}
BUS_ATTR(busversion, S_IRUGO|S_IWUGO, mybusattrshow, mybusattrstore);
//生成bus_attribute对象bus_attr_busversion
/************声明一个总线(bus)的总线的属性(attribute)对象*******************/

3、创建bus属性对象的文件

staticint__init bustest_init(void){
intret;
printk("*****%s****\n", __FUNCTION__);
ret = bus_register(&mybustype);
if(ret < 0){
printk("bus_register fail\n");
returnret;
}
ret = bus_create_file(&mybustype, &bus_attr_busversion);//创建属性文件
if(ret < 0)
{
returnret;
}
return0;
}

4、给总线完善匹配match函数

staticintmybus_match(structdevice * dev, structdevice_driver * drv){
printk("*****%s****\n", __FUNCTION__);
return1;//先默认返回匹配成功
}


   测试

   在mybus总线上创建driver和device

   device.c

extern struct bus_type  mybustype;
void mydevicetest_release(structdevice *dev){
printk("*******%s*******\n", __FUNCTION__);
}
struct device mydevicetest_device = {
.bus_id = "mybusname",//在2.6.3版本中被init_name代替
/*static int my_match(struct device *dev, struct device_driver *driver)
{
return !strncmp(dev->bus_id, driver->name, strlen(driver->name));
}
*/
.bus = &mybustype,
.release = mydevicetest_release,
};
static int __init mydevicetest_init(void){
intret;
printk("*******%s*******\n", __FUNCTION__);
if(IS_ERR(&mydevicetest_device)){
returnPTR_ERR(&mydevicetest_device);
}
ret = device_register(&mydevicetest_device);
if(ret < 0){
printk("mydevicetest_init fail\n");
return ret;
}
return0;
}

   driver.c

static int mydevice_driver_probe    (structdevice * dev){
printk("*****%s*****\n", __FUNCTION__);
return0;
}
static int mydevice_driver_remove(structdevice * dev){
printk("*****%s*****\n", __FUNCTION__);
return0;
}
extern structbus_type  mybustype;
struct device_driver mydevice_driver = {
.name = "mybusname",//对应的驱动名称在/sys/bus/mybus/driver/mybusname
.owner = THIS_MODULE,
.bus = &mybustype,
.probe = mydevice_driver_probe,
.remove= mydevice_driver_remove,
};
static int__init drivertest_init(void)
{
int ret;
ret = driver_register(&mydevice_driver);
if(ret < 0)return ret;
return0;
}

   完整代码下载地址http://pan.baidu.com/s/1pJsIFcn,对应压缩包mybusone

   测试结果,看出总线没问题,match强行返回1,可以probe        

$ insmod kodir/bustest.ko                                                     
*****bustest_init****                                                         
$ insmod kodir/devicetest.ko                                                  
*******mydevicetest_init*******                                               
$ insmod kodir/drivertest.ko                                                  
*****mybus_match****                                                          
*****mydevice_driver_probe*****                                               
$

Tip:你可以通过cat /sys/bus/mybus/busversion 查看版本号,也可以echo "..." > /sys/bus/mybus/busversion修改版本号,对应函数会调mybusattrshow和mybusattrstore

第二阶段,从上面的driver和device的注册可以看出,driver和device都要预先知道总线的对象的名称extern structbus_type  mybustype;

   我们可以在bus中封装对应的driver和device的函数

   修改bus.c

//注释掉EXPORT_SYMBOL(mybustype);同时封装device和driver注册注销函数
//EXPORT_SYMBOL(mybustype);///proc/kallsyms
//添加下面封装device和driver注册注销函数
int mybus_device_register(struct device *dev){
    dev->bus = &mybustype;
    return device_register(dev);
}
EXPORT_SYMBOL(mybus_device_register);
void mybus_device_unregister(struct device *dev){
    dev->bus = &mybustype;
    device_unregister(dev);
}
EXPORT_SYMBOL(mybus_device_unregister);
int  mybus_driver_register(struct device_driver *driver){
    driver->bus = &mybustype;
    return driver_register(driver);
}
EXPORT_SYMBOL(mybus_driver_register);
void  mybus_driver_unregister(struct device_driver *driver){
    driver->bus = &mybustype;
    driver_unregister(driver);
}
EXPORT_SYMBOL(mybus_driver_unregister);

  修改device.c

//添加mybus.c中封装的device注册注销相关函数mybus_device_register,mybus_device_unregister
extern int mybus_device_register(struct device * dev);
extern void mybus_device_unregister(struct device * dev);
static int __init mydevicetest_init(void){
    //..............
    ret = mybus_device_register(&mydevicetest_device);
    //............
}
static void __exit mydevicetest_exit(void){
    //.............
mybus_device_unregister(&mydevicetest_device);
}

  对应的在driver.c中

static int __init drivertest_init(void)
{
    //...........
    ret = mybus_driver_register(&mydevice_driver);
//........
}
static void __exit drivertest_exit(void){
    mybus_driver_unregister(&mydevice_driver);
}

     完整代码下载地址http://pan.baidu.com/s/1pJsIFcn,对应压缩包mybustwo

第三阶段:将mybus.c中两对注册注销函数封装成mybus.h,同时自定义一个mybus_device和mybus_driver结构体

   mybus.h

#ifndef _BUSTEST_H_
#define _BUSTEST_H_
#include <linux/device.h>
#define to_mybus_device(x) container_of((x), struct mybus_device, dev)
#define to_mybus_driver(x) container_of((x), struct mybus_driver, driver)
struct mybus_device{
    const char *name;
    struct device dev;
};
int mybus_device_register(struct mybus_device *dev);
void mybus_device_unregister(struct mybus_device *dev);
struct mybus_driver{
    int (*probe)(struct mybus_device *);//提供外部来定义
    int (*remove)(struct mybus_device *);//提供外部来定义
    struct device_driver driver;
};
int  mybus_driver_register(struct mybus_driver *driver);
void  mybus_driver_unregister(struct mybus_driver *driver);
#endif

   修改mybus.c的match规则

static int  mybus_match(struct device * dev, struct device_driver * drv){
    struct mybus_device *pdev = container_of(dev, struct mybus_device, dev);
    printk("*****%s****\n", __FUNCTION__);
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
    //return 1;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            
    return !strncmp(pdev->name, drv->name, strlen(drv->name));
}


   完成mybus_driver的probe,和macth函数

//总线macth成功之后,device的device_driver得到初始化
//然后将device_driver 中的probe将dev的地址交给mybus_drv_probe 中的_dev
//因为register 中使得driver->driver.probe = mybus_drv_probe;
static int mybus_drv_probe(struct device *_dev){
    struct mybus_driver *drv = to_mybus_driver(_dev->driver);//找到子驱动的mybus_driver
    struct mybus_device *dev = to_mybus_device(_dev);//找到子驱动的mybus_device
    printk("*****%s****\n", __FUNCTION__);
    return drv->probe(dev);//做自驱动中的事情
}
//同上
static int mybus_drv_remove(struct device *_dev){
    struct mybus_driver *drv = to_mybus_driver(_dev->driver);
    struct mybus_device *dev = to_mybus_device(_dev);
    printk("*****%s****\n", __FUNCTION__);
    return drv->remove(dev);
}
int  mybus_driver_register(struct mybus_driver *driver){
    driver->driver.bus = &mybustype;
    if (driver->probe)
        driver->driver.probe = mybus_drv_probe;
    if (driver->remove)
        driver->driver.remove = mybus_drv_remove;
    printk("*****%s****\n", __FUNCTION__);
    return driver_register(&driver->driver);
}
EXPORT_SYMBOL(mybus_driver_register);
void  mybus_driver_unregister(struct mybus_driver *driver){
    driver->driver.bus = &mybustype;
    printk("*****%s****\n", __FUNCTION__);
    driver_unregister(&driver->driver);
}
EXPORT_SYMBOL(mybus_driver_unregister);

   完整代码下载地址http://pan.baidu.com/s/1pJsIFcn,对应压缩包mybusthree

第四阶段,上面程序有缺陷:不符合我们日常的面向对象思想


/*
参考了device_register
int device_register(struct device *dev){
device_initialize(dev);    
return device_add(dev);}
*/
/******************将子设备添加到父设备中***************/
int mybus_device_add(struct mybus_device*pdev)
{
    int ret = 0;
    if (!pdev)
        return -EINVAL;
    if (!pdev->dev.parent)
        pdev->dev.parent = &mybus_parentdevice;
    pdev->dev.bus = &mybustype;
    ret = device_add(&pdev->dev);
    if (ret == 0)
        return ret;
    return ret;
}
/******************将子设备添加到父设备中***************/
int mybus_device_register(struct mybus_device *pdev){
    printk("*****%s****\n", __FUNCTION__);
    //dev->dev.bus = &mybustype;
    //return device_register(&dev->dev);
    device_initialize(&pdev->dev);
    return mybus_device_add(pdev);
}
EXPORT_SYMBOL(mybus_device_register);
/*
参考了
void device_unregister(struct device * dev)
{
    device_del(dev);
    put_device(dev);
}
*/
void mybus_device_unregister(struct mybus_device *pdev){
    printk("*****%s****\n", __FUNCTION__);
    //dev->dev.bus = &mybustype;
    //device_unregister(&dev->dev);
    if (pdev)
        device_del(&pdev->dev);
    if (pdev)
        put_device(&pdev->dev);//重新device计数
}
EXPORT_SYMBOL(mybus_device_unregister);

   其他文件(如device.c,driver.c)不变

     完整代码下载地址http://pan.baidu.com/s/1pJsIFcn,对应压缩包mybusfour

第五阶段:给mybus_device添加资源,同时添加设备id

   在mybus.h中添加resource    

struct mybus_device{
    const char *name;
    struct device dev;
    u32     id;
    u32     num_resources;
    struct resource * resource;
};

   在mybus.c中实现将mybus_device


/*************resource**************/
//请求分配指定的I/O内存资源。
static struct resource * myrequest_resource(struct resource *root, struct resource *newre)
{
    resource_size_t start = newre->start;
    resource_size_t end = newre->end;
    struct resource *tmp, **p;
    printk("*****%s****\n", __FUNCTION__);
    if (end < start)
        return root;
    if (start < root->start)
        return root;
    if (end > root->end)
        return root;
    p = &root->child;
    for (;;) {
        tmp = *p;
        if (!tmp || tmp->start > end) {
            newre->sibling = tmp;
            *p = newre;
            newre->parent = root;
            return NULL;
        }
        p = &tmp->sibling;
        if (tmp->end < start)
            continue;
        return tmp;
    }
}
/*
将resource链接到总线上,这样linux内核就可以对总线上resource对应的设备实体进行管理,这样可以resource对应的设备发生竞争。
1、linux需要管理4G物理总线的所有空间,所以挂接到总线上的形形色色的各种设备实体,这就需要链在一起。
2、内核中有两棵resource树,一棵是iomem_resource, 另一棵是ioport_resource,分别代表着两类不同性质的地址资源。两棵树的根也都是resource数据结构,不过这两个数据结构描述的并不是用于具体操作对象的地址资源,而是概念上的整个地址空间所以挂接到总线上的形形色色的各种设备实体,这就需要链在一起,因此resource结构体提供了另外3个成员:指针parent、sibling和child:分别为指向父亲、兄弟和子资源的指针。    
3、linux会坚决避免将一个已经被一个设备实体使用的总线物理地址区间段[resource->start, resource->end],再分配给另一个后来的也需要这个区间段或者区间段内部分地址的设备实体,进而避免设备之间出现对同一总线物理地址段的重复引用,而造成对唯一物理地址的设备实体二义性.
*/
int insert_resource(struct resource *parent, struct resource *newre)
{
//root->child(*pchild)指向root所有孩子中地址空间最小的一个;pchild->sibling是兄弟链表的开头,指向比自己地址空间大的兄弟。
    int result;
    struct resource *first, *next;
    write_lock(&resource_lock);
    for (;; parent = first) {
        result = 0;
        printk("*****%s****\n", __FUNCTION__);
        first = myrequest_resource(parent, newre);
        if (!first)
            goto out;
    //printk("*****%s****\n", __FUNCTION__);  
        result = -EBUSY;
        if (first == parent)
            goto out;
        if ((first->start > newre->start) || (first->end < newre->end))
            break;
        if ((first->start == newre->start) && (first->end == newre->end))
            break;
    }
    for (next = first; ; next = next->sibling) {
        /* Partial overlap? Bad, and unfixable */
        if (next->start < newre->start || next->end > newre->end)
            goto out;
        if (!next->sibling)
            break;
        if (next->sibling->start > newre->end)
            break;
    }
    result = 0;
    newre->parent = parent;
    newre->sibling = next->sibling;
    newre->child = first;
    next->sibling = NULL;
    for (next = first; next; next = next->sibling)
        next->parent = newre;
    if (parent->child == first) {
        parent->child = newre;
    } else {
        next = parent->child;
        while (next->sibling != first)
            next = next->sibling;
        next->sibling = newre;
    }
 out:
    write_unlock(&resource_lock);
    return result;
}
static int __release_resource(struct resource *old)
{
    struct resource *tmp, **p;
    p = &old->parent->child;
    for (;;) {
        tmp = *p;
        if (!tmp)
            break;
        if (tmp == old) {
            *p = tmp->sibling;
            old->parent = NULL;
            return 0;
        }
        p = &tmp->sibling;
    }
    return -EINVAL;
}
int release_resource(struct resource *old)
{
    int retval;
    write_lock(&resource_lock);
    retval = __release_resource(old);
    write_unlock(&resource_lock);
    return retval;
}
/*************resource**************/
//在mybus_device_add将resource插入总线中
int mybus_device_add(struct mybus_device*pdev)
{
                                                                                                                                                                                                                      
    int i, ret = 0;
    if (!pdev)
        return -EINVAL;
    if (!pdev->dev.parent)
        pdev->dev.parent = &mybus_parentdevice;
    pdev->dev.bus = &mybustype;
    if (pdev->id != -1)
        snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", 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;
}

   完整代码下载地址http://pan.baidu.com/s/1pJsIFcn,对应压缩包mybusfive

第六阶段:实现get_irq和get_resource,写个驱动进行测试

   mybus.c

/*************get_resource**************/
struct resource *mybus_get_resource(struct mybus_device *dev, unsigned int type, unsigned int num)
{
    int i;
    for (i = 0; i < dev->num_resources; i++) {
        struct resource *r = &dev->resource[i];
        if ((r->flags & (IORESOURCE_IO|IORESOURCE_MEM|
                 IORESOURCE_IRQ|IORESOURCE_DMA))
            == type)
            if (num-- == 0)
                return r;
    }
    return NULL;
}
EXPORT_SYMBOL_GPL(mybus_get_resource);
int mybus_get_irq(struct mybus_device *dev, unsigned int num)
{
    struct resource *r = mybus_get_resource(dev, IORESOURCE_IRQ, num);
    return r ? r->start : -ENXIO;
}
EXPORT_SYMBOL_GPL(mybus_get_irq);
/*************get_resource**************/

对应mybus.h加入

struct resource *mybus_get_resource(struct mybus_device *dev, unsigned int type, unsigned int num);
int mybus_get_irq(struct mybus_device *dev, unsigned int num);

到这里一个platform总线就算写好了

   可以拿它当platform来用了



本文转自lilin9105 51CTO博客,原文链接:http://blog.51cto.com/7071976/1406028,如需转载请自行联系原作者