バスデバイスドライバモデル

バスデバイスドライバモデル1.1を導入

オリジナルます。https://blog.csdn.net/21cnbao/article/details/73864762

1.1.1ダイアー・ストレイツ
私たちはABCと呼ばれる名前を想定次のような単純なカード上には、それがメモリバス(そのCPUXと仮定して)CPUに取る必要がある、あなたは、データおよび制御バスに対処する必要がある(となど、ピン足を中断)。


そして、カードABCの内部にはドライブ、ABC我々は、割り込み番号やその他の情報のベースアドレスを定義する必要があります。回路基板上のCPUと仮定xは、ABCのアドレス0X100000は、割り込み番号は10です。:私たちは、マクロは以下のように定義されていることを前提とし

ます。#define ABC_BASE 0X100000
の#define ABC_interrupt 10

:およびアプリケーションの初期化を完了し、中断したメッセージを送信するためにコードを書く

の#define ABC_BASE 0X100000
の#define ABC_IRQ 10
 
int型abc_send(...)
{
        ABC_BASE + REG_X WRITEL( 、1);
        WRITEL(ABC_BASE + REG_Y、を0x3);
        ...
}
 
INT abc_init(...)
{
        request_irq(ABC_IRQ、...);
}
このコードの問題は、その再度ボードため、ABC_BASEとABC_IRQありますそれはコードも変更する必要があります、もはや同じではありません。一部のプログラマは、私はそれを行うことができると言う:
#ifdefのBOARD_A
の#define ABC_BASE 0X100000
の#define ABC_IRQ 10
 
#elifの定義された(BOARD_B)
の#define ABC_BASE 0x110000
ABC_IRQ 20の#define
 
(BOARD_C)に定義の#elif
の#define ABC_BASE 0x120000
の#define ABC_IRQ 10
...
#endifの

はもちろん、これを行うにはできますが、あなたは10000別のボードを持っている場合、あなたはそのコードを書く、とIFDEF万回を持っていますそして、乾式壁の明確な感覚を発見しました。さまざまなLinux製品の適応、世界のさまざまなハードウェア機能の適応が考慮して、どのように本当に言うことができなかったABC、とボード多くの。

だから、それは本当にの#ifdef万回を行っている、問題を解決することができるのだろうか?本当にそうではありません。1枚の回路基板は、2枚のABCカード、完全に胴着を持っていると仮定します。この定義は何ですか?


BOARD_Aの#ifdef
の#define ABC1_BASE 0X100000
の#define ABC1_IRQ 10
の#define ABC2_BASE 0x101000
の#define ABC2_IRQ 11
 
#elifの定義された(BOARD_B)
の#define ABC1_BASE 0x110000
の#define ABC1_IRQ 20
...
#endifの

これを行う場合は、abc_send()とabc_init()とどのように修正するには?これは、次のとおりです。
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)。
    }そうであれば(ID == 1){
            WRITEL(ABC2_BASE + REG_X、1)。
            WRITEL(ABC2_BASE + REG_Y、0x3の);
    }
    ...
}
    }他に(== ID 1。){IF
            WRITEL(ABC2_BASE + REG_X ,. 1);
            WRITEL(ABC2_BASE + REG_Y、0x3の);
    }
    ...
}

どんなに君変更は、このコードは実際にはすでに悲惨であり、あなたももうそれを立ちます。私たちは、このような偉大なカップルの導入「エラーの正しい場所で正しいコードは、」有罪ことができなかったので、なぜ私たちは、そのような苦境に陥りました。

1.1.2ロスト反射
我々が行わ致命的なミスは、ドライバは、クロスプラットフォームであることができない原因に駆動コードに結合されたボード・レベルの配線情報、ということです。

我々はそれについて考えるに切り替え、ABC-駆動本当のデューティがABCネットワークカードのトランシーバのプロセスを完了することで、どのように、このプロセスでは、それは本当にあなたが半分のダイムを持っているものCPUに接続されていますか?そして、それを行うには半分のダイムでボードの電源をオン?

答えは実際には何の関係もありません!ABCカード、あなたはTIのARMありませんので、あなたはゴッドソンあるか、何が違うのBlackfinプロセッサされています。何任意の社外取締役雪崩、追いつめ、ABC自分ですべての滞在。

その後、何の関係、これらのボードレベルの相互接続情報がないので、なぜそれ内のコードを配置するドライブ?基本的に、我々はそれが自然のコードは、クロスプラットフォームである必要がありますので、ABCと、誰が、変更されない、と言うことができます。したがって、私たちは「の#define ABC_BASE 0X100000、#はABC_IRQ定義することを信じている 10」 ドライブに表示され、そのようなコードは、ある「間違った場所、間違った敵に、間違った戦争を戦いました。」それが正しい位置に配置されていない、と私たちはコードを書く、我々は「塵ダストを手放す、天国に天国を手放す。」必要があります 私たちは、おそらくこのような、真に信頼:


高凝集および低カップリング上のソフトウェア工学強調。より密接内のモジュールの接触要素の場合、その高い凝集性、モジュール間の結合は、より低いその結合を閉じません。そのため、高い凝集、低カップリングはしっかりと卵を離れて行くの外側、内側に一緒に保持するために強調しました。どうやらから抜け出す必要があります属し、ボードレベルの相互接続情報を駆動します。

1.1.3 vistaが
、彼らは別のソフトウェアモジュールに表示されるようになりましたボードレベルの相互接続情報と運転を分離し、お互いされています。最終的には、ドライブのベースアドレス、割り込み番号及びその他のボード・レベルの情報を削除する必要があり、ので最終的に、しかし、彼らはまだ、いくつかの接続を持っています。取るためにどのように、これは大きな問題があります。

一つの方法は、「割り込み番号は、いくつかのあなたのベースアドレスであるのか?」、「あなたのお母さんの名前は?」各ボードを求めて世界中駆動のABCである。これは、まだ深刻な夫婦です。ドライブがまだボードを知っている必要があるのでボードは、法律があるか、持っているABC、ではありません。これは、ボードと直結でもあります。

 


可不可以有另外一种方法,我们维护一个共同的类似数据库的东西,板子上有什么网卡,基地址中断号是什么,都统一在一个地方维护。然后,驱动问一个统一的地方,通过一个统一的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);

}

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

おすすめ

転載: www.cnblogs.com/-glb/p/11183486.html