Bus device driver model

Introducing bus device driver model 1.1

Original: https: //blog.csdn.net/21cnbao/article/details/73864762

1.1.1 dire straits
on the following simple card we envision a name called ABC, it needs to take in a CPU (assuming that CPUx) memory bus, you need to address, data and control bus (and interrupt pin feet, etc.).


Then drive in the inside of the card ABC, ABC we need to define the base address of the interrupt number and other information. Suppose CPU x above the circuit board, ABC's address 0x100000, interrupt number is 10. We assume that the macro is defined as:

#define ABC_BASE 0x100000
#define ABC_interrupt 10

and write the code to send messages to complete the application initialization and interrupt:

#define ABC_BASE 0x100000
#define ABC_IRQ 10
 
int abc_send (...)
{
        writel (ABC_BASE + REG_X ,. 1);
        writel (ABC_BASE + REG_Y, 0x3);
        ...
}
 
int abc_init (...)
{
        the request_irq (ABC_IRQ, ...);
}
this code issue is that, once again for a board, ABC_BASE and ABC_IRQ it is no longer the same, the code will also need to change. Some programmers say I can do it:
#ifdef BOARD_A
#define ABC_BASE 0x100000
#define ABC_IRQ 10
 
#elif defined (BOARD_B)
#define ABC_BASE 0x110000
ABC_IRQ 20 #define
 
#elif defined (BOARD_C)
#define ABC_BASE 0x120000
#define ABC_IRQ 10
...
#endif

to do this of course can be, but if you have 10000 different board, you have to ifdef ten thousand times, so writing code and found a clear sense of drywall. Taking into account the various linux product adaptation, adaptation of various hardware features of the world, how many a board with ABC, who really could not say.

So, is it really #ifdef go ten thousand times, it will be able to solve the problem? Really it is not. Suppose one circuit board has two ABC card, the bodice completely. Is this definition?


BOARD_A #ifdef
#define ABC1_BASE 0x100000
#define ABC1_IRQ 10
#define ABC2_BASE 0x101000
#define ABC2_IRQ 11
 
#elif defined (BOARD_B)
#define ABC1_BASE 0x110000
#define ABC1_IRQ 20
...
#endif

If you do this, abc_send () and abc_init () and how to modify? Is this:
int abc1_send (...)
{
        writel (ABC1_BASE + REG_X,. 1);
        writel(ABC1_BASE + REG_Y, 0x3);
        ...
}
 
int abc1_init(...)
{
        request_irq(ABC1_IRQ,...);
}
 
int abc2_send(...)
{
        writel(ABC2_BASE + REG_X, 1);
        writel(ABC2_BASE + REG_Y, 0x3);
        ...
}
 
int abc2_init(...)
{
        request_irq(ABC2_IRQ,...);
}

还是这样?

int abc_send(int id, ...)
{
    if (id == 0) {
            writel(ABC1_BASE + REG_X, 1);
            writel(ABC1_BASE + REG_Y, 0x3);
    } else if (id == 1) {
            writel(ABC2_BASE + REG_X, 1);
            writel (ABC2_BASE + REG_Y, 0x3);
    }
    ...
}
    } the else IF (== ID. 1) {
            writel (ABC2_BASE + REG_X,. 1);
            writel (ABC2_BASE + REG_Y, 0x3);
    }
    ...
}

matter how you modification, this code really are already miserable, and you even stand it anymore. Why did we fall into such a predicament, because we could not guilty "to the correct code in the correct location of the error," the introduction of such a great couple.

1.1.2 Lost reflection
fatal mistake we made is that the board-level interconnection information, coupled into the driven code causing the driver can not be cross-platform.

We switched to think about it, ABC-driven real duty is to complete the ABC network card transceiver processes, how, in this process, and it is really connected to what CPU you have half dime? And then turn on the board which is half a dime to do with it?

The answer is really nothing to do! ABC card, not because you are TI's ARM, you are Godson, or are you Blackfin any different. What you any outside board avalanche, cornered, ABC yourself all stays.

Since there is no relationship, then these board-level interconnection information, why drive to put code inside it? Basically, we can say that, ABC and who will not change, so it's natural code should be cross-platform. Therefore, we believe that "#define ABC_BASE 0x100000, # define ABC_IRQ 10" such a code, which appears in the drive, are "in the wrong place and the wrong enemy, fought the wrong war." It has not been placed in the correct position, and we write code, we must "let go of the heaven to heaven, let go dust dust." We truly trust, probably like this:


Software Engineering emphasis on high cohesion and low coupling. If the contact elements of the module within a more closely, its higher cohesiveness; linkages between the module does not close the lower its coupling. Therefore, high cohesion, low coupling stressed to firmly hold together inside, outside go away eggs. For driving, the board-level interconnection information, apparently belonging should get out of.

1.1.3 vista
now board-level interconnection information and driving has been separated, and each other so that they appear in different software modules. Ultimately, however, they still have some connection, because, ultimately have to remove the drive base address, interrupt number and other board-level information. How to take, this is a big problem.

One method is the ABC of driving all over the world asking each board, "Does your base address, interrupt number is a few?", "Your mother name?" This is still a serious couple. Because the drive still have to know the board is not ABC, which the board has, how there is a law. It is also in the board and direct coupling.

 


可不可以有另外一种方法,我们维护一个共同的类似数据库的东西,板子上有什么网卡,基地址中断号是什么,都统一在一个地方维护。然后,驱动问一个统一的地方,通过一个统一的API来获取即好?

基于这样的想法,Linux把设备驱动分为了总线、设备和驱动三个实体,总线是上图中的统一纽带,设备是上图中的板级互连信息,这三个实体完成的职责分别如下:


我们把所有的板子互连信息填入设备端,然后让设备端向总线注册告知总线自己的存在,总线上面自然关联了这些设备,并进一步间接关联了设备的板级连接信息。比如arch/blackfin/mach-bf533/boards/ip0x.c这块板子有2个DM9000的网卡,它是这样注册的:

static struct resource dm9000_resource1[] = {
    {
        .start = 0x20100000,
        .end   = 0x20100000 + 1,
        .flags = IORESOURCE_MEM
    },{
        .start = 0x20100000 + 2,
        .end   = 0x20100000 + 3,
        .flags = IORESOURCE_MEM
    },{
        .start = IRQ_PF15,
        .end   = IRQ_PF15,
        .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE
    }
};
 
static struct resource dm9000_resource2[] = {
    {
        .start = 0x20200000,
        .end   = 0x20200000 + 1,
        .flags = IORESOURCE_MEM
    }…
};
 

static struct platform_device dm9000_device1 = {
    .name           = "dm9000",
    .id             = 0,
    .num_resources  = ARRAY_SIZE(dm9000_resource1),
    .resource       = dm9000_resource1,
};
 

static struct platform_device dm9000_device2 = {
    .name           = "dm9000",
    .id             = 1,
    .num_resources  = ARRAY_SIZE(dm9000_resource2),
    .resource       = dm9000_resource2,
};
 
static struct platform_device *ip0x_devices[] __initdata = {
    &dm9000_device1,
    &dm9000_device2,

};
 
static int __init ip0x_init(void)
{
    platform_add_devices(ip0x_devices, ARRAY_SIZE(ip0x_devices));
    …
}

这样platform的总线这个统一纽带上,自然就知道板子上面有2个DM9000的网卡。一旦DM9000的驱动也被注册,由于platform总线已经关联了设备,驱动自然可以根据已经存在的DM9000设备信息,获知如下的内存基地址、中断等信息了:

static struct resource dm9000_resource1[] = {
    {
        .start = 0x20100000,
        .end   = 0x20100000 + 1,
        .flags = IORESOURCE_MEM
    },{
        .start = 0x20100000 + 2,
        .end   = 0x20100000 + 3,
        .flags = IORESOURCE_MEM
    },{
        .start = IRQ_PF15,
        .end   = IRQ_PF15,
        .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE
    }
}

总线存在的目的,则是把这些驱动和这些设备,一一配对的匹配在一起。如下图,某个电路板子上有2个ABC,1个DEF,1个HIJ设备,以及分别1个的ABC、DEF、HIJ驱动,那么总线,就是让2个ABC设备和1个ABC驱动匹配,DEF设备和驱动一对一匹配,HIJ设备和驱动一对一匹配。


驱动本身,则可以用最简单的API取出设备端填入的互连信息,看一下drivers/net/ethernet/davicom/dm9000.c的dm9000_probe()代码:

static int dm9000_probe(struct platform_device *pdev)
{
    …
db->addr_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
db->data_res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
db->irq_res  = platform_get_resource(pdev, IORESOURCE_IRQ, 0);

}

这样,板级互连信息,再也不会闯入驱动,而驱动,看起来也没有和设备之间直接耦合,因为它调用的都是总线级别的标准API:platform_get_resource()。总线里面有个match()函数,来完成哪个设备由哪个驱动来服务的职责,比如对于挂在内存上的platform总线而言,它的匹配类似(最简单的匹配方法就是设备和驱动的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);
 
        /* 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);

}

这就是总线设备驱动模型引入的背景,下一篇将结合代码深入研究总线设备驱动模型。

Guess you like

Origin www.cnblogs.com/-glb/p/11183486.html