linux驱动结构pci.doc_第1页
linux驱动结构pci.doc_第2页
linux驱动结构pci.doc_第3页
linux驱动结构pci.doc_第4页
linux驱动结构pci.doc_第5页
已阅读5页,还剩40页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

linux驱动结构pci Linux设计了一个通用的数据结构resource来描述各种I/O资源(如:I/O端口、外设内存、DMA和IRQ等)。 该结构定义在include/linux/ioport.h头文件中: struct resource const char *name; unsigned long start, end; 表示资源的起始物理地址和终止物理地址。它们确定了资源的范围,也即是一个闭区间start,end unsigned long flags; 描述资源的标志 struct resource *parent, *sibling, *child; 分别指向父、兄弟、子资源的指针;Linux是以一种倒置的树形结构来管理每一类I/O资源(如:I/O端口、外设内存、DMA和IRQ)的。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource结构,而树的根结点root则描述了该类资源的整个资源空间。为什么使用树?例如,考虑一下IDE硬盘接口所使用的I/O端口地址比如说从0xf000 到 0xf00f。那么,start字段为0xf000 且end 字段为0xf00f的这样一个资源包含在树中,控制器的常规名字存放在name字段中。但是,IDE设备驱动程序需要记住另外的信息,也就是IDE链主盘使用0xf000 到 0xf007的子范围,从盘使用0xf008 到 0xf00f的子范围。为了做到这点,设备驱动程序把两个子范围对应的孩子插入到从0xf000 到 0xf00f的整个范围对应的资源下。一般来说,树中的每个节点肯定相当于父节点对应范围的一个子范围。I/O端口资源树(ioport_resource)的根节点跨越了整个I/O地址空间(从端口0到65535,即64K)。更详细可以参考:每种类的PCI设备都可以用结构类型pci_dev来描述。更为准确地说,应该是每一个PCI功能,即PCI逻辑设备都唯一地对应有一个pci_dev设备描述符。该数据结构的定义如下(include/linux/pci.h):struct pci_dev struct list_head global_list; /* 全局链表元素global_list:每一个pci_dev结构都通过该成员连接到全局pci设备链表pci_devices中*/struct list_head bus_list; /* 总线设备链表元素bus_list:每一个pci_dev结构除了链接到全局设备链表中外,还会通过这个成员连接到其所属PCI总线的设备链表中。每一条PCI总线都维护一条它自己的设备链表视图,以便描述所有连接在该PCI总线上的设备,其表头由PCI总线的pci_bus结构中的 devices成员所描述t*/struct pci_bus *bus; /* 总线指针bus:指向这个PCI设备所在的PCI总线的pci_bus结构。因此,对于桥设备而言,bus指针将指向桥设备的主总线(primary bus),也即指向桥设备所在的PCI总线*/struct pci_bus *subordinate; /* 指针subordinate:指向这个PCI设备所桥接的下级总线。这个指针成员仅对桥设备才有意义,而对于一般的非桥PCI设备而言,该指针成员总是为NULL*/void *sysdata; /* 无类型指针sysdata:指向一片特定于系统的扩展数据*/struct proc_dir_entry *procent; /* 指针procent:指向该PCI设备在proc文件系统中对应的目录项*/unsigned int devfn; /* devfn:这个PCI设备的设备功能号,也成为PCI逻辑设备号(0255)。其中bit7:3是物理设备号(取值范围031),bit2:0是功能号(取值范围07)。 */unsigned short vendor;/* vendor:这是一个16无符号整数,表示PCI设备的厂商ID*/unsigned short device;/*device:这是一个16无符号整数,表示PCI设备的设备ID */unsigned short subsystem_vendor;/* subsystem_vendor:这是一个16无符号整数,表示PCI设备的子系统厂商ID*/unsigned short subsystem_device; /* subsystem_device:这是一个16无符号整数,表示PCI设备的子系统设备ID。*/unsigned int class; /* class:32位的无符号整数,表示该PCI设备的类别,其中,bit7:0为编程接口,bit15:8为子类别代码,bit 23:16为基类别代码,bit31:24无意义。显然,class成员的低3字节刚好对应与PCI配置空间中的类代码*/u8 hdr_type; /* hdr_type:8位符号整数,表示PCI配置空间头部的类型。其中,bit71表示这是一个多功能设备,bit70表示这是一个单功能设备。Bit6:0则表示PCI配置空间头部的布局类型,值00h表示这是一个一般PCI设备的配置空间头部,值01h表示这是一个PCI-to-PCI桥的配置空间头部,值02h表示CardBus桥的配置空间头部*/u8 rom_base_reg; /* rom_base_reg:8位无符号整数,表示PCI配置空间中的ROM基地址寄存器在PCI配置空间中的位置。ROM基地址寄存器在不同类型的PCI配置空间头部的位置是不一样的,对于type 0的配置空间布局,ROM基地址寄存器的起始位置是30h,而对于PCI-to-PCI桥所用的type 1配置空间布局,ROM基地址寄存器的起始位置是38h*/struct pci_driver *driver; /* 指针driver:指向这个PCI设备所对应的驱动程序定义的pci_driver结构。每一个pci设备驱动程序都必须定义它自己的pci_driver结构来描述它自己。*/u64 dma_mask; /*dma_mask:用于DMA的总线地址掩码,一般来说,这个成员的值是0xffffffff。数据类型dma_addr_t定义在include/asm/types.h中,在x86平台上,dma_addr_t类型就是u32类型*/pci_power_t current_state; /* 当前操作状态 */ struct device dev;/* 通用的设备接口*/unsigned short vendor_compatibleDEVICE_COUNT_COMPATIBLE;unsigned short device_compatibleDEVICE_COUNT_COMPATIBLE;/*定义这个PCI设备与哪些设备相兼容!/unsigned int irq;/* 无符号的整数irq:表示这个PCI设备通过哪根IRQ输入线产生中断,一般为015之间的某个值 */struct resource resourceDEVICE_COUNT_RESOURCE; /*表示该设备可能用到的资源,包括:I/O断口区域、设备内存地址区域以及扩展ROM地址区域。*/int cfg_size;/* 配置空间的大小 */unsigned int transparent:1;/* 透明 PCI 桥 */unsigned int multifunction:1;/* 多功能设备*/ unsigned int is_enabled:1;/* pci_enable_device已经被调用*/unsigned int is_busmaster:1; /* 设备是主设备*/unsigned int no_msi:1; /* 设备不使用msi*/unsigned int block_ucfg_access:1;/* 配置空间访问形式用块的形式 */u32 saved_config_space16; /* 在挂起时保存配置空间*/struct bin_attribute *rom_attr; /* sysfs ROM入口的属性描述*/int rom_attr_enabled;/* 能显示rom 属性*/struct bin_attribute *res_attrDEVICE_COUNT_RESOURCE; /* 资源的sysfs文件*/;struct pci_bus struct list_head node;/* 链表元素node:对于PCI根总线而言,其pci_bus结构通过node成员链接到本节一开始所述的根总线链表中,根总线链表的表头由一个list_head类型的全局变量pci_root_buses所描述。而对于非根pci总线,其pci_bus结构通过node成员链接到其父总线的子总线链表children中*/struct pci_bus *parent;/*parent指针:指向该pci总线的父总线,即pci桥所在的那条总线*/struct list_head children;/*children指针:描述了这条PCI总线的子总线链表的表头。这条PCI总线的所有子总线都通过上述的node链表元素链接成一条子总线链表,而该链表的表头就由父总线的children指针所描述*/struct list_head devices;/* list of devices on this bus */struct pci_dev *self;/* devices链表头:描述了这条PCI总线的逻辑设备链表的表头。除了链接在全局PCI设备链表中之外,每一个PCI逻辑设备也通过其 pci_dev结构中的bus_list成员链入其所在PCI总线的局部设备链表中,而这个局部的总线设备链表的表头就由pci_bus结构中的 devices成员所描述*/struct resource *resourcePCI_BUS_NUM_RESOURCES;/* 资源指针数组:指向应路由到这条pci总线的地址空间资源,通常是指向对应桥设备的pci_dev结构中的资源数组resource10:7*/struct pci_ops *ops;/* 指针ops:指向一个pci_ops结构,表示这条pci总线所使用的配置空间访问函数*/void *sysdata;/* 无类型指针sysdata:指向系统特定的扩展数据*/struct proc_dir_entry *procdir;/* 指针procdir:指向该PCI总线在proc文件系统中对应的目录项*/ unsigned char number;/* number:这条PCI总线的总线编号(bus number),取值范围0255 */unsigned char primary; /* primary:表示引出这条PCI总线的“桥设备的主总线”(也即桥设备所在的PCI总线)编号,取值范围0255*/unsigned char secondary; /* secondary:表示引出这条PCI总线的桥设备的次总线号,因此secondary成员总是等于number成员的值。取值范围0255*/unsigned char subordinate; /* subordinate:这条PCI总线的下属PCI总线(Subordinate pci bus)的总线编号最大值,它应该等于引出这条PCI总线的桥设备的subordinate值*/char name48;/* name48:这条PCI总线的名字字符串*/ unsigned short bridge_ctl; /*/unsigned short pad2;/* */ struct device *bridge;/* */ struct device class_dev;/* */struct bin_attribute *legacy_io; struct bin_attribute *legacy_mem; /* */;struct pci_device_id _u32 vendor,device; /厂商和设备ID _u32 subvendor,subdevice; /子系统和设备ID _u32 class,class_mask; /类、子类、prog-if三元组 kernel_ulong_t driver_data; /驱动私有数据pci_device_id 用MODULE_DEVICE_TABLE宏到处到用户空间。CPU对外设端口物理地址的编址方式有两种:一种是IO映射方式,另一种是内存映射方式。 Linux将基于IO映射方式的和内存映射方式的IO端口统称为IO区域(IO region)。IO region仍然是一种IO资源,因此它仍然可以用resource结构类型来描述。Linux管理IO region:1) request_region()把一个给定区间的IO端口分配给一个IO设备。2) check_region()检查一个给定区间的IO端口是否空闲,或者其中一些是否已经分配给某个IO设备。3) release_region()释放以前分配给一个IO设备的给定区间的IO端口。Linux中可以通过以下辅助函数来访问IO端口:inb(),inw(),inl(),outb(),outw(),outl()“b”“w”“l”分别代表8位,16位,32位。对IO内存资源的访问注:其实对IO内存和IO端口的访问的不同函数,只是对同一函数的不同调用而已,定义在include/linux/ioport.h: #define request_region(start,n,name) _request_region(&ioport_resource, (start), (n), (name) #define request_mem_region(start,n,name) _request_region(&iomem_resource, (start), (n), (name) #define rename_region(region, newname) do (region)-name = (newname); while (0) extern struct resource * _request_region(struct resource *, resource_size_t start, resource_size_t n, const char *name);1) request_mem_region()请求分配指定的IO内存资源。2) check_mem_region()检查指定的IO内存资源是否已被占用。3) release_mem_region()释放指定的IO内存资源。其中传给函数的start address参数是内存区的物理地址(以上函数参数表已省略)。驱动开发人员可以将内存映射方式的IO端口和外设内存统一看作是IO内存资源。ioremap()用来将IO资源的物理地址映射到内核虚地址空间(3GB - 4GB)中,参数addr是指向内核虚地址的指针。Linux中可以通过以下辅助函数来访问IO内存资源:readb(),readw(),readl(),writeb(),writew(),writel()。Linux在kernel/resource.c文件中定义了全局变量ioport_resource和iomem_resource,来分别描述基于IO映射方式的整个IO端口空间和基于内存映射方式的IO内存资源空间(包括IO端口和外设内存)。1)关于IO与内存空间: 在X86处理器中存在着I/O空间的概念,I/O空间是相对于内存空间而言的,它通过特定的指令in、out来访问。端口号标识了外设的寄存器地址。Intel语法的in、out指令格式为: IN 累加器, 端口号DX OUT 端口号DX,累加器 目前,大多数嵌入式微控制器如ARM、PowerPC等中并不提供I/O空间,而仅存在内存空间。内存空间可以直接通过地址、指针来访问,程序和程序运行中使用的变量和其他数据都存在于内存空间中。 即便是在X86处理器中,虽然提供了I/O空间,如果由我们自己设计电路板,外设仍然可以只挂接在内存空间。此时,CPU可以像访问一个内存单元那样访问外设I/O端口,而不需要设立专门的I/O指令。因此,内存空间是必须的,而I/O空间是可选的。(2)inb和outb:在Linux设备驱动中,宜使用Linux内核提供的函数来访问定位于I/O空间的端口,这些函数包括: 读写字节端口(8位宽)unsigned inb(unsigned port); void outb(unsigned char byte, unsigned port); 读写字端口(16位宽)unsigned inw(unsigned port); void outw(unsigned short word, unsigned port); 读写长字端口(32位宽)unsigned inl(unsigned port); void outl(unsigned longword, unsigned port); 读写一串字节void insb(unsigned port, void *addr, unsigned long count); void outsb(unsigned port, void *addr, unsigned long count); insb()从端口port开始读count个字节端口,并将读取结果写入addr指向的内存;outsb()将addr指向的内存的count个字节连续地写入port开始的端口。 读写一串字void insw(unsigned port, void *addr, unsigned long count); void outsw(unsigned port, void *addr, unsigned long count); 读写一串长字void insl(unsigned port, void *addr, unsigned long count); void outsl(unsigned port, void *addr, unsigned long count); 上述各函数中I/O端口号port的类型高度依赖于具体的硬件平台,因此,只是写出了unsigned(3)readb和writeb:在设备的物理地址被映射到虚拟地址之后,尽管可以直接通过指针访问这些地址,但是工程师宜使用Linux内核的如下一组函数来完成设备内存映射的虚拟地址的读写,这些函数包括: 读I/O内存unsigned int ioread8(void *addr);unsigned int ioread16(void *addr);unsigned int ioread32(void *addr);与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):unsigned readb(address);unsigned readw(address);unsigned readl(address); 写I/O内存void iowrite8(u8 value, void *addr);void iowrite16(u16 value, void *addr);void iowrite32(u32 value, void *addr);与上述函数对应的较早版本的函数为(这些函数在Linux 2.6中仍然被支持):void writeb(unsigned value, address);void writew(unsigned value, address);void writel(unsigned value, address);(4)把I/O端口映射到“内存空间”:void *ioport_map(unsigned long port, unsigned int count);通过这个函数,可以把port开始的count个连续的I/O端口重映射为一段“内存空间”。然后就可以在其返回的地址上像访问I/O内存一样访问这些I/O端口。当不再需要这种映射时,需要调用下面的函数来撤消:void ioport_unmap(void *addr);实际上,分析ioport_map()的源代码可发现,所谓的映射到内存空间行为实际上是给开发人员制造的一个“假象”,并没有映射到内核虚拟地址,仅仅是为了让工程师可使用统一的I/O内存访问接口访问I/O端口。 -Linux对I/O端口资源的管理 几乎每一种外设都是通过读写设备上的寄存器来进行的。外设寄存器也称为“I/O端口”,通常包括:控制寄存器、状态寄存器和数据寄存器三大类,而且一个外设的寄存器通常被连续地编址。CPU对外设IO端口物理地址的编址方式有两种:一种是I/O映射方式(I/O-mapped),另一种是内存映射方式(Memory-mapped)。而具体采用哪一种则取决于CPU的体系结构。 有些体系结构的CPU(如,PowerPC、m68k等)通常只实现一个物理地址空间(RAM)。在这种情况下,外设 I/O端口的物理地址就被映射到CPU的单一物理地址空间中,而成为内存的一部分。此时,CPU可以象访问一个内存单元那样访问外设I/O端口,而不需要设立专门的外设I/O指令。这就是所谓的“内存映射方式”(Memory-mapped)。 而另外一些体系结构的CPU(典型地如X86)则为外设专门实现了一个单独地地址空间,称为“I/O地址空间”或者“I/O端口空间”。这是一个与CPU地RAM物理地址空间不同的地址空间,所有外设的I/O端口均在这一空间中进行编址。CPU通过设立专门的I/O指令(如X86的IN和OUT指令)来访问这一空间中的地址单元(也即 I/O端口)。这就是所谓的“I/O映射方式”(I/O-mapped)。与RAM物理地址空间相比,I/O地址空间通常都比较小,如x86 CPU的I/O空间就只有64KB(0-0xffff)。这是“I/O映射方式”的一个主要缺点。 Linux将基于I/O映射方式的或内存映射方式的I/O端口通称为“I/O区域”(I/O region)。在讨论对I/O区域的管理之前,我们首先来分析一下Linux是如何实现“I/O资源”这一抽象概念的。Linux对I/O资源的描述- Linux设计了一个通用的数据结构resource来描述各种I/O资源(如:I/O端口、外设内存、DMA和IRQ等)。该结构定义在include/linux/ioport.h头文件中:struct resource resource_size_t start; resource_size_t end; const char *name; unsigned long flags; struct resource *parent, *sibling, *child;Linux对I/O资源的管理- Linux是以一种倒置的树形结构来管理每一类I/O资源(如:I/O端口、外设内存、DMA和IRQ)的。每一类I/O资源都对应有一颗倒置的资源树,树中的每一个节点都是一个resource结构,而树的根结点root则描述了该类资源的整个资源空间。 基于上述这个思想,Linux在kernel/resource.c文件中实现了对资源的申请、释放及查找等操作。 资源的申请: request_resource() 资源的释放: release_resource() 寻找可用资源: -find_resource() 分配资源: allocate_resource()管理I/O Region资源- Linux将基于I/O映射方式的I/O端口和基于内存映射方式的I/O端口资源统称为“I/O区域”(I/O Region)。I/O Region仍然是一种I/O资源,因此它仍然可以用resource结构类型来描述。下面我们就来看看Linux是如何管理I/O Region的。 I/O Region的分配: _request_region() I/O Region的释放: _release_region() 检查指定的I/O Region是否已被占用: _check_region()管理I/O端口资源- 我们都知道,采用I/O映射方式的X86处理器为外设实现了一个单独的地址空间,也即“I/O空间”(I/O Space)或称为“I/O端口空间”,其大小是64KB(0x0000-0xffff)。Linux在其所支持的所有平台上都实现了“I/O端口空间” 这一概念。 由于I/O空间非常小,因此即使外设总线有一个单独的I/O端口空间,却也不是所有的外设都将其I/O端口(指寄存器)映射到“I/O端口空间”中。比如,大多数PCI卡都通过内存映射方式来将其I/O端口或外设内存映射到CPU的RAM物理地址空间中。而老式的ISA卡通常将其I/O端口映射到I/O端口空间中。 Linux是基于“I/O Region”这一概念来实现对I/O端口资源(I/O-mapped 或 Memory-mapped)的管理的。 资源根节点的定义: Linux在kernel/resource.c文件中定义了全局变量ioport_resource和iomem_resource,来分别描述基于I/O映射方式的整个I/O端口空间和基于内存映射方式的I/O内存资源空间(包括I/O端口和外设内存)。其定义如下:struct resource ioport_resource = .name = PCI IO, .start -= 0, .end = IO_SPACE_LIMIT, .flags -= IORESOURCE_IO,;struct resource iomem_resource = .name = PCI mem, .start -= 0, .end = -1, .flags -= IORESOURCE_MEM,; 其中,宏IO_SPACE_LIMIT表示整个I/O空间的大小,对于X86平台而言,它是0xffff(定义在include/asm-i386/io.h头文件中)。显然,I/O内存空间的大小是4GB。 对I/O端口空间的操作: 基于I/O Region的操作函数_xxx_region(),Linux在头文件include/linux/ioport.h中定义了三个对I/O端口空间进行操作的接口: request_region() 请求在I/O端口空间中分配指定范围的I/O端口资源。 check_region() 检查I/O端口空间中的指定I/O端口资源是否已被占用。 release_region() 释放I/O端口空间中的指定I/O端口资源。 对I/O内存资源的操作: 基于I/O Region的操作函数_xxx_region(),Linux在头文件include/linux/ioport.h中定义了三个对I/O内存资源进行操作的接口: request_mem_region() 请求分配指定的I/O内存资源。 check_mem_region() 检查指定的I/O内存资源是否已被占用。 release_mem_region() 释放指定的I/O内存资源。访问I/O端口空间(I/O-mapped)- 在驱动程序请求了I/O端口空间中的端口资源后,它就可以通过CPU的IO指令来读写这些I/O端口了。在读写I/O端口时要注意的一点就是,大多数平台都区分8位、16位和32位的端口,也即要注意I/O端口的宽度。 inb() outb() inw() outw() inl() outl() unsigned char inb(unsigned port); port参数指定I/O端口空间中的端口地址。在大多数平台上(如x86)它都是unsigned short类型的,其它的一些平台上则是unsigned int类型的。显然,端口地址的类型是由I/O端口空间的大小来决定的。 对I/O端口的字符串操作 除了上述这些“单发”(single-shot)的I/O操作外,某些CPU也支持对某个I/O端口进行连续的读写操作,也即对单个I/O端口读或写一系列字节、字或32位整数,这就是所谓的“字符串I/O指令”(String Instruction)。这种指令在速度上显然要比用循环来实现同样的功能要快得多。 insb() outsb() insw() outw() insl() outsl()Pausing I/O 在一些平台上(典型地如X86),对于老式总线(如ISA)上的慢速外设来说,如果CPU读写其I/O端口的速度太快,那就可能会发生丢失数据的现象。对于这个问题的解决方法就是在两次连续的I/O操作之间插入一段微小的时延,以便等待慢速外设。这就是所谓的“Pausing I/O”。 对于Pausing I/O,Linux也在io.h头文件中定义了它的I/O读写函数,而且都以XXX_p命名,比如:inb_p()、outb_p()等等。访问I/O内存资源(I/O Memory)- 尽管I/O端口空间曾一度在x86平台上被广泛使用,但是由于它非常小,因此大多数现代总线的设备都以内存映射方式(Memory-mapped)来映射它的I/O端口(指I/O寄存器)和外设内存。基于内存映射方式的I/O端口称为I/O内存资源(I/O Memory)。因为基于内存映射方式的I/O和外设内存在硬件实现上的差异对于软件来说是完全透明的,所以驱动程序开发人员可以将内存映射方式的I/O端口和外设内存统一看作是“I/O内存”资源。 I/O内存资源是在CPU的单一内存物理地址空间内进行编址的,也即它和系统RAM同处在一个物理地址空间内。因此通过CPU的访内指令就可以访问I/O内存资源。 一般来说,在系统运行时,外设的I/O内存资源的物理地址是已知的,这可以通过系统固件(如BIOS)在启动时分配得到,或者通过设备的硬连线(hardwired)得到。比如,PCI卡的I/O内存资源的物理地址访问I/O内存资源,而必须将它们映射到核心虚地址空间内(通过页表),然后才能根据映射所得到的核心虚地址范围,通过访内指令访问这些I/O内存资源。物理地址就是在系统启动时由PCI BIOS分配并写到PCI卡的配置空间中的BAR中的。而ISA卡的I/O内存资源的物理地址则是通过设备硬连线映射到640KB-1MB范围之内的。但是CPU通常并没有为这些已知的外设I/O内存资源的物理地址预定义虚拟地址范围,因为它们是在系统启动后才已知的(某种意义上讲是动态的),所以驱动程序并不能直接通过 映射I/O内存资源 Linux在io.h头文件中声明了函数ioremap(),用来将I/O内存资源的物理地址映射到核心虚地址空间(3GB-4GB)中 对于X86体系结构ioremap()定义在/usr/src/linux-/include/asm-i386/io.h中 读写I/O内存资源 在将I/O内存资源的物理地址映射成核心虚地址后,理论上讲我们就可以象读写RAM那样直接读写I/O内存资源了。但是,由于在某些平台上,对 I/O内存和系统内存有不同的访问处理,因此为了确保跨平台的兼容性,Linux实现了一系列读写I/O内存资源的函数,这些函数在不同的平台上有不同的实现。但在x86平台上,读写I/O内存与读写RAM无任何差别。如下所示(include/asm-i386/io.h): readb() readw() readl() writeb() writew() writel() memset_io() memcpy_fromio() memcpytoio()显然,在x86平台上访问I/O内存资源与访问系统主存RAM是无差别的。但是为了保证驱动程序的跨平台的可移植性,我们应该使用上面的函数来访问I/O内存资源,而不应该通过指向核心虚地址的指针来访问。本文来自CSDN博客,转载请标明出处:/wucongdonglai/archive/2011/01/14/6138345.aspx一、PCI简介 PCI是一种外设总线规范。我们先来看一下什么是总线:总线是一种传输信号的路径或信道。典型情况是,总线是连接于一个或多个导体的电气连线,总线上连接的所有设备可在同一时间收到所有的传输内容。总线由电气接口和编程接口组成。本文讨论Linux 下的设备驱动,所以,重点关注编程接口。 PCI是Peripheral Component Interconnect(外围设备互联)的简称,是普遍使用在桌面及更大型的计算机上的外设总线。PCI架构被设计为ISA标准的替代品,他有三个主要目标:获得在计算机和外设之间传输数据时更好的性能;尽可能的平台无关;简化往系统中添加和删除外设的工作。二、PCI寻址 从目前开始,我想尽可能通过一些实际的例子来说明问题,而减少理论方面的问题的描述,因为,相关的理论的东西,能在其他地方找到。 我们先来看一个例子,我的计算机装有1G的RAM,1G以后的物理内存地址空间都是外部设备IO在系统内存地址空间上的映射。/proc/iomem描述了系统中所有的设备I/O在内存地址空间上的映射。我们来看地址从1G开始的第一个设备在/proc/iomem中是怎么描述的: 40000000-400003ff : 0000:00:1f.1 这是个PCI设备,40000000-400003ff是他所映射的内存地址空间,占据了内存地址空间的1024bytes的位置,而0000:00:1f.1则是个PCI外设的地址,以冒号和逗号分隔为4个部分:第一个16位表示域;第二个8位表示一个总线编号,28-256,故每个域最多能有256个总线;第三个5位表示一个设备号,每个总线最多能挂载32个设备;最后是3位,表示功能号,每个设备最多能有8种功能,也就是最多能够对应8个逻辑设备,每种功能都唯一的对应一个pci_dev结构体。 注:因为PCI规范允许单个系统拥有高达256个总线,但对于大型系统而言,这是不够的,所以,引入了域的概念。 由此,我们能得出上述的PCI设备的地址是0号域0号总线上的31号设备上的1号功能。那上述的这个PCI设备到底是什么呢?下面是我的计算机上的lspci命令的输出: 00:00.0 Host bridge: Intel Corporation 82845 845 (Brookdale) Chipset Host Bridge (rev 04) 00:01.0 PCI bridge: Intel Corporation 82845 845 (Brookdale) Chipset AGP Bridge(rev 04) 00:1d.0 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #1) (rev 02) 00:1d.1 USB Controller: Intel Corporation 82801CA/CAM USB (Hub #2) (rev 02) 00:1e.0 PCI bridge: Intel Corporation 82801 Mobile PCI Bridge (rev 42) 00:1f.0 ISA bridge: Intel Corporation 82801CAM ISA Bridge (LPC) (rev 02) 00:1f.1 IDE interface: Intel Corporation 82801CAM IDE U100 (rev 02) 00:1f.3 SMBus: Intel Corporation 82801CA/CAM SMBus Controller (rev 02) 00:1f.5 Multimedia audio controller:Intel Corporation 82801CA/CAM AC97 Audio Controller (rev 02) 00:1f.6 Modem: Intel Corporation 82801CA/CAM AC97 Modem Controller (rev 02) 01:00.0 VGA compatible controller: nVidia Corporation NV17 GeForce4 420 Go(rev a3) 02:00.0 FireWire (IEEE 1394): VIA Technologies, Inc. IEEE 1394 Host Controller(rev 46) 02:01.0 Ethernet controller: Realtek Semiconductor Co., Ltd. RTL-8139/8139C/8139C+(rev 10) 02:04.0 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01) 02:04.1 CardBus bridge: O2 Micro, Inc. OZ6933 Cardbus Controller (rev 01) lspci没有标明域,但对于一台PC而言,一般只有一个域,即0号域。通过这个输出我们能看到他是个IDE interface。由上述的输出能看到,我的计算机上共有3个PCI总线(0号,1号,2号)。在单个系统上,插入多个总线是通过桥(bridge)来完成的,桥是一种用来连接总线的特别PCI外设。所以,PCI系统的整体布局组织为树型,我们能通过上面的lspci输出,来画出我的计算机上的PCI系统的树型结构:0号总线 (主桥)-00:01(PCI桥) |-00:1d(USB号控制器,提供了0、1这两个逻辑设备,即功能) |-00:1e:0(PCI桥) |-00:1f.多功能卡(提供了0 ISA bridge、1 IDE interface、3SMBus、5 Multimedia audio controller、6 Modem这5个逻辑设备,即功能)1号总线(主桥)-01:00.0 VG

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论