버스 장치 드라이버 모델

소개 버스 장치 드라이버 모델 1.1

원본 : HTTPS : //blog.csdn.net/21cnbao/article/details/73864762

1.1.1 곤경
다음과 같은 간단한 카드에 우리는 ABC라는 이름은, 그것은 CPU에 걸릴 필요가 구상 메모리 버스 (즉 CPUx 가정), 당신은 (등 핀 다리를 방해), 데이터 및 제어 버스를 해결해야합니다.


그런 다음 카드 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
 
#elif 정의 (BOARD_C)
#DEFINE ABC_BASE 0x120000
#DEFINE ABC_IRQ 10
...
#endif 다음

이 될 수 물론이 작업을 수행하지만 10000 다른 보드가있는 경우, 당신은 이렇게 코드를 작성, IFDEF 만 배해야합니다 및 건식 벽체의 명확한 의미를 발견했다. 다양한 리눅스 제품 적응, 세계의 다양한 하드웨어 기능의 적응을 고려하는 방법을 정말 말할 수없는 ABC와 보드 많은.

그래서, 정말 #ifdef와 만 시간을 이동, 문제를 해결할 수 있습니까? 정말이 없습니다. 하나 개의 회로 보드를 두 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, ...)
{
    경우 (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 있습니다 때문이 아니라, 당신은 대자, 또는 어떤 다른를 블랙 핀된다. 당신 어떤 외부 보드 눈사태, 궁지, ABC 자신의 모든 숙박.

아무 관계, 이들 보드 레벨의 상호 정보가 없기 때문에, 왜 그 안에 코드를 넣어 드라이브? 기본적으로, 우리는 자연 코드는 크로스 플랫폼해야한다, 그래서 ABC 누가, 변경되지 않습니다, 말할 수 있습니다. 따라서, 우리는 "#DEFINE의 ABC_BASE 0x100000에서, #은 ABC_IRQ 정의 믿고 10" 드라이브에 나타납니다 이러한 코드는,이다 "잘못된 장소와 잘못된 적으로 잘못된 전쟁을 싸웠다." 그것은 올바른 위치에 배치, 우리는 코드, 우리가 있어야 쓰기되지 않았다 "하늘로 하늘을 놓아 먼지 먼지 가자를." 우리는 진정으로 신뢰, 아마이 좋아 :


높은 응집력과 낮은 커플 링의 소프트웨어 엔지니어링 강조. 콘택트 더 가깝게 더 높은 응집력 내의 모듈의 요소 경우 상기 모듈 간의 결합은 하부의 결합을 닫지 않는다. 따라서, 높은 결합, 낮은 커플 링은 단단히 외부 함께 내부 잡아 알을 멀리 갈 강조했다. 보드 수준의 상호 정보를 구동하기 위해, 분명히 나가야 속하는.

1.1.3 비스타는
서로 다른 소프트웨어 모듈에 표시되도록 현재 보드 레벨 상호 접속 정보 및 구동 서로 분리하고있다. 궁극적으로 드라이브의 기본 주소, 인터럽트 번호 및 기타 보드 레벨의 정보를 제거해야하기 때문에 궁극적으로, 그러나, 그들은 여전히 일부 연결이. 촬영하는 방법, 이것은 큰 문제입니다.

한 가지 방법은, "인터럽트 번호가 몇 기지 주소 하는가?", "당신의 어머니 이름?"각 보드를 요구하는 전세계 모든 운전의 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