`
javatome
  • 浏览: 824606 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

Linux那些事儿 之 我是PCI(5)初始化(二)

 
阅读更多

.initcall2.init子节中的两个函数已经见识过了,该轮到.initcall3.init子节里的了,就是上边儿表中的acpi_pci_init和pci_access_init,这两个又是谁先谁后那?acpi_pci_init在drivers/pci/pci-acpi.c文件里,而pci_access_init 在arch/i386/pci/init.c文件里,它俩根本就不在同一个目录下面,所以前边儿判断pcibus_class_init和pci_driver_init的顺序的技巧并不适用,那有什么方法可以让咱们找出它们的顺序?看看王冉怎么说:“昨天是五一劳动节,可是全国都在放大假绝大多数人不劳动。可见,庆祝一件事的最好的方法就是不去做这件事。譬如,庆祝世界杯的最好的方式就是不去参加世界杯——中国队几乎一直都是这么做的。再譬如,庆祝情人节的最好的方式就是不去找情人——于是,很多中国的男人把情人节的前一天(2月13日)过成了情人节。”按他这说法,认清这俩函数之间顺序的最好方法就是不去管它们的顺序,俺可以点兵点将的随便点一个出来先说,不过作为一个很清楚自己责任和使命的80后,俺还是决定去发掘一下它们的顺序。

其实这个问题可以转化为arch/i386/pci下面的Makefile和drivers/pci下面的Makefile谁先谁后的问题,往大的方面说,就是内核是怎么构建的,也就是kbuild的问题。内核里的Makefile主要有三种:第一种是根目录里的Makefile,它虽然只有一个,但地位远远凌驾于其它Makefile之上,里面定义了所有与体系结构无关的变量和目标;第二种是arch/*/Makefile,看到arch就知道它是与特定体系结构相关的,它包含在根目录下的Makefile中,为kbuild提供体系结构的特定信息,而它里面又包含了arch/*/下面各级子目录的那些Makefile;第三种就是密密麻麻躲在drivers/等各个子目录下边儿的那些Makefile了。而kbuild构建内核的过程中,是首先从根目录Makefile开始执行,从中获得与体系结构无关的变量和依赖关系,并同时从arch/*/Makefile中获得体系结构特定的变量等信息,用来扩展根目录Makefile所提供的变量。此时kbuild已经拥有了构建内核需要的所有变量和目标,然后,Make进入各个子目录,把部分变量传递给子目录里的Makefile,子目录Makefile根据配置信息决定编译哪些源文件,从而构建出一个需要编译的文件列表。然后,然后还有很漫长的路,你编译内核要耗多久,它就有多漫长,不过说到这儿前面问题的答案就已经浮出水面了,很明显,arch/i386/pci下面的Makefile是处在drivers/pci下面的Makefile前面的,也就是说,pci_access_init处在acpi_pci_init前面,所以接下来先看看pci_access_init。
5 /* arch_initcall has too random ordering, so call the initializers
6 in the right sequence from here. */
7 static __init int pci_access_init(void)
8 {
9 int type __maybe_unused = 0;
10
11 #ifdef CONFIG_PCI_DIRECT
12 type = pci_direct_probe();
13 #endif
14 #ifdef CONFIG_PCI_MMCONFIG
15 pci_mmcfg_init(type);
16 #endif
17 if (raw_pci_ops)
18 return 0;
19 #ifdef CONFIG_PCI_BIOS
20 pci_pcbios_init();
21 #endif
22 /*
23 * don't check for raw_pci_ops here because we want pcbios as last
24 * fallback, yet it's needed to run first to set pcibios_last_bus
25 * in case legacy PCI probing is used. otherwise detecting peer busses
26 * fails.
27 */
28 #ifdef CONFIG_PCI_DIRECT
29 pci_direct_init(type);
30 #endif
31 if (!raw_pci_ops)
32 printk(KERN_ERR
33 "PCI: Fatal: No config space access function found/n");
34
35 return 0;
36 }
37 arch_initcall(pci_access_init);
这个函数的主要工作是根据你配置内核时做的那道单项选择题“PCI access mode”来进行一些初始化,那几对儿#ifdef…#endif就是专为你的答案准备的,你可能注意到它们里面只有DIRECT、MMCONFIG、BIOS,没有ANY啊,那选ANY的时候咋办?如果仔细看了下前面贴的arch/i386/Kconfig里的与PCI access mode有关的那一段你就知道,选ANY就相当于其它三个一块儿选了。
9行,定义一个变量type,问题不在type,在__maybe_unused。__maybe_unused在内核里定义为__attribute__((unused)),而__attribute__也不是遇到一次两次了。Linux内核代码使用了大量的GNU C扩展,以至于GNU C成为能够编译内核的唯一编译器,GNU C的这些扩展对代码优化、目标代码布局、安全检查等方面也提供了很强的支持。__attribute__就是这些扩展中的一个,它主要被用来声明一些特殊的属性,这些属性被用来指示编译器进行特定方面的优化和更仔细的代码检查。GNU C支持十几个属性,讲USB Core时已经遇到的有section、packed、aligned等,这里的unused也是这些属性中的一个,它用于函数和变量,表示该函数或变量可能不使用,可以在编译时防止编译器产生“变量未使用”这样的警告信息。这里用在type身上,就是说type虽然是在这儿定义了,但未必会用到。什么时候用不到它?你单选BIOS的时候,内核在20行调用了pci_pcbios_init之后就跑到31行去了,这个过程没type什么事儿。
11行,如果选了DIRECT,就调用arch/i386/pci/direct.c里的pci_direct_probe函数
268 int __init pci_direct_probe(void)
269 {
270 struct resource *region, *region2;
271
272 if ((pci_probe & PCI_PROBE_CONF1) == 0)
273 goto type2;
274 region = request_region(0xCF8, 8, "PCI conf1");
275 if (!region)
276 goto type2;
277
278 if (pci_check_type1())
279 return 1;
280 release_resource(region);
281
282 type2:
283 if ((pci_probe & PCI_PROBE_CONF2) == 0)
284 return 0;
285 region = request_region(0xCF8, 4, "PCI conf2");
286 if (!region)
287 return 0;
288 region2 = request_region(0xC000, 0x1000, "PCI conf2");
289 if (!region2)
290 goto fail2;
291
292 if (pci_check_type2()) {
293 printk(KERN_INFO "PCI: Using configuration type 2/n");
294 raw_pci_ops = &pci_direct_conf2;
295 return 2;
296 }
297
298 release_resource(region2);
299 fail2:
300 release_resource(region);
301 return 0;
302 }
因为Direct方式又分为了conf1和conf2两种,所以pci_direct_probe函数要进行一下检测,判断要使用哪一种,如果你在内核参数里没有强行指定是conf1还是conf2的话,它会首先检测一下conf1,如果成了就直接返回不再检测conf2了,如果conf1不成,再去检测conf2。conf2现在一般都不会再用了,从PCI Spec v2.2那时候开始就已经将它给扔到高粱地里去了,这里还留着它主要是为了兼容很久很久以前的一些老主板,所以下面只会针对conf1去说事儿。
要搞清楚pci_direct_probe函数的检测过程,得明白这么几点。第一个就是struct resource。处理器和设备进行交流主要是通过两个空间,I/O空间或者内存空间,某些处理器两个空间都有,某些却只有一个内存空间,这两个空间的差别引出了设备那些寄存器映射方式的差别,映射到处理器的I/O空间时,就成了I/O端口,映射到内存空间时,就成I/O内存,不过不管是I/O端口还是I/O内存,在linux里都属于宝贵的I/O资源,都要使用include/linux/ioport.h文件里的struct resource结构来描述。
13 /*
14 * Resources are tree-like, allowing
15 * nesting etc..
16 */
17 struct resource {
18 resource_size_t start;
19 resource_size_t end;
20 const char *name;
21 unsigned long flags;
22 struct resource *parent, *sibling, *child;
23 };
每一个struct resource结构体都表示了一段独立的连续地址区间,name代表了这段区间也就是这个资源的名称,start和end描述了它的地址范围,parent、sibling和child用来维持一个资源的树形结构,child指向它的第一个子区间,同一资源的所有子区间通过sibling形成一个单向链表,且都通过parent来指向它们的父区间。flags描述了一个资源的种类和属性,同样都在ioport.h里定义,比如
39 #define IORESOURCE_IO 0x00000100 /* Resource type */
40 #define IORESOURCE_MEM 0x00000200
41 #define IORESOURCE_IRQ 0x00000400
42 #define IORESOURCE_DMA 0x00000800
分别代表了IO,Memory,中断,DMA四种资源类型,对应了/proc下面的ioports,iomem,interrupt,dma四个文件。不过不管是哪一种资源,只要是资源,在内核里就都是要先提出申请,得到批准后才能使用的,这点儿不能拿咱们的生活经验往上套,就说前档子上海某区的那桩高压线事件吧,22万伏高压线能不能入地,占不占你地盘儿不是你小区平头小百姓说了算的,是由xx公司和防暴警察说了算的,他们不用去获得你的批准就照样占用你小区的资源,将高压线铁塔矗立到你旁边儿,管你什么绿地不绿地辐射不辐射,当然,他们虽然没有得到老百姓的批准,但有没有得到其它什么批准就谁也不知道了。内核里就不一样,什么都得放到明处,你想使用哪种资源就要明确的提出申请,申请的函数对于I/O端口来说是request_region,对于I/O内存来说就是request_mem_region,如果你的申请得到批准了,它们就会返回一个struct resource结构体,如果没有得到批准,返回的就是一个冷冰冰的NULL。
就比如说已经说过的UHCI吧,UHCI的spec规定,UHCI的那些寄存器智能映射到I/O空间,使用request_region函数申请成功之后,look一下/proc/ioports文件
localhost:~ # cat /proc/ioports
0000-001f : dma1
0020-0021 : pic1
0cf8-0cff : PCI conf1
(此处省略若干行)
bca0-bcbf : 0000:00:1d.2
bca0-bcbf : uhci_hcd
bcc0-bcdf : 0000:00:1d.1
bcc0-bcdf : uhci_hcd
bce0-bcff : 0000:00:1d.0
bce0-bcff : uhci_hcd
c000-cfff : PCI Bus #10
cc00-ccff : 0000:10:0d.0
d000-dfff : PCI Bus #0e
dcc0-dcdf : 0000:0e:00.1
dcc0-dcdf : e1000
dce0-dcff : 0000:0e:00.0
dce0-dcff : e1000
e000-efff : PCI Bus #0c
e800-e8ff : 0000:0c:00.1
e800-e8ff : qla2xxx
ec00-ecff : 0000:0c:00.0
ec00-ecff : qla2xxx
fc00-fc0f : 0000:00:1f.1
fc00-fc07 : ide0
就可以看到里面的“uhci_hcd”。对于pci_direct_probe函数来说,在274行也使用了request_region(0xCF8, 8, "PCI conf1")来申请一个起始地址为0cf8-0cff 的I/O端口资源,上面/proc/ioports文件里的“PCI conf1也反映出了这一点。
关于struct resource,剩下的一点疑问是,它描述的是物理地址?还是逻辑地址?还是其它的什么地址?绕来绕去的地址种类有很多,有些可能就根本没什么大的区别,就像一块儿走在T台上的芙蓉姐姐和杨二车那姆一样,俺看了很久愣是没看出谁是谁又有啥区别,但你不能就这么说她们是一个样儿,所以还是挨个儿看一下,希望不会被绕进去。
与咱们最贴近的是一个“用户虚拟地址”,是用户空间所能看到的地址,每个进程都有这么一个虚拟地址空间。然后是耳熟能详的“物理地址”,在处理器和系统内存之间使用的地址。接着是“总线地址”,是处理器在总线上所看到的地址。第四个是“内核逻辑地址”,这些地址组成了常规的内核地址空间,映射了大部分乃至全部的系统主内存,被视为物理地址使用,在大多数的体系结构中,逻辑地址及其所关联的物理地址之间的区别,仅仅在于一个常数的偏移量,在拥有大量内存的32位机上,仅通过逻辑地址未必能够寻址所有的物理内存。最后一个是“内核虚拟地址”,和逻辑地址的区别在于前者不一定会直接映射到物理地址,所有的逻辑地址都可看成是内核虚拟地址。
对内核来说,总线地址、逻辑地址都可以当物理地址来用,其实对于CPU来说,总线地址就相当于物理地址,为外设分配地址就是来分配总线地址,分配得到的地址一般都不能直接使用,还要通过ioremap映射为内核虚拟地址之后使用专门的接口来访问。所以你说struct resource描述的是逻辑地址也对,物理地址也没什么不妥。
关于pci_direct_probe函数,要明白的第二点就是调用request_region时使用的参数0xCF8。凡是PCI设备不都是有个配置寄存器组么,要访问这些寄存器,不是通过I/O端口就是通过I/O内存。如果通过I/O内存的方式,将它们统统映射到内存空间,那一千种设备需要多大空间?一万种那?大量的内存将被占用,从而不能用在革命最需要的地方,所以说这很不符合节约型社会的要求。那就只有通过I/O端口了,不过I/O端口是非常稀缺的,X86上只有64K这么多,将每个配置寄存器都映射到I/O空间显然也是不现实的,现实一点的做法是为所有设备的配置寄存器组都采用相同的I/O端口,CPU则通过这个统一的I/O端口向主桥发出命令,再由各个PCI桥添加一些附加条件来间接的完成具体的读写操作。对于32位的X86架构来说,有两个32位的I/O端口用于这个目的,就是地址端口0cf8和数据端口0cfc。访问某个设备的配置寄存器时,CPU向地址端口写入目标的地址,然后通过数据端口读写数据。写入地址端口0cf8里的是一个包括总线号、设备号、功能号以及配置寄存器地址在内的综合地址。

最高位为1
保留不用(7位)
总线号(8位)
设备号(5位)
功能号(3位)
寄存器地址(8位)最低两位为0
这里的总线号、设备号、功能号就是上面说的所谓的附加条件,寄存器地址指的就是该寄存器在配置寄存器组里的偏移,最低两位为0那是因为配置寄存器组里的那些寄存器都是双字为单位的,内核在include/linux/pci_regs.h里定义了大量的宏来表示这个偏移地址。
知道了0cf8和0cff这两个统一的I/O端口,也使用request_region成功的申请到了端口资源,然后就可以使用这两个I/O端口与各种PCI设备眉来眼去了么?事情没这么简单,你是申请到了0cf8和0cff,但这并不说明系统里就有PCI总线了,所以还要不可避免地做一下验证,对于conf1这个验证使用的是arch/i386/pci/direct.c里的pci_check_type1,这也是咱们要明白的第三点。
217 static int __init pci_check_type1(void)
218 {
219 unsigned long flags;
220 unsigned int tmp;
221 int works = 0;
222
223 local_irq_save(flags);
224
225 outb(0x01, 0xCFB);
226 tmp = inl(0xCF8);
227 outl(0x80000000, 0xCF8);
228 if (inl(0xCF8) == 0x80000000 && pci_sanity_check(&pci_direct_conf1)) {
229 works = 1;
230 }
231 outl(tmp, 0xCF8);
232 local_irq_restore(flags);
233
234 return works;
235 }
不过这个要明白的第三点还真是让人不怎么明白,在#¥%×……的做了一番验证后(#¥%×……所代表的意义对俺来说至今还是个秘密,不过有一点是明确的,如果0cf8确实是主桥的地址端口,则读时候,返回的肯定是先前写到里边儿的值,所以inl(0xCF8) == 0x80000000就是判断0cf8是不是真的是当作主桥的地址端口用的。如果不相等,则肯定不是,conf1将不起作用,如果相等了,才说明0cf8极有可能就是主桥的地址端口,但也有可能只是凑巧,所以要接着调用pci_sanity_check做更深入的验证),又调用了pci_sanity_check函数,使用pci_direct_conf1做进一步的验证。
179 /*
180 * Before we decide to use direct hardware access mechanisms, we try to do some
181 * trivial checks to ensure it at least _seems_ to be working -- we just test
182 * whether bus 00 contains a host bridge (this is similar to checking
183 * techniques used in XFree86, but ours should be more reliable since we
184 * attempt to make use of direct access hints provided by the PCI BIOS).
185 *
186 * This should be close to trivial, but it isn't, because there are buggy
187 * chipsets (yes, you guessed it, by Intel and Compaq) that have no class ID.
188 */
189 static int __init pci_sanity_check(struct pci_raw_ops *o)
190 {
191 u32 x = 0;
192 int devfn;
193
194 if (pci_probe & PCI_NO_CHECKS)
195 return 1;
196 /* Assume Type 1 works for newer systems.
197 This handles machines that don't have anything on PCI Bus 0. */
198 if (dmi_get_year(DMI_BIOS_DATE) >= 2001)
199 return 1;
200
201 for (devfn = 0; devfn < 0x100; devfn++) {
202 if (o->read(0, 0, devfn, PCI_CLASS_DEVICE, 2, &x))
203 continue;
204 if (x == PCI_CLASS_BRIDGE_HOST || x == PCI_CLASS_DISPLAY_VGA)
205 return 1;
206
207 if (o->read(0, 0, devfn, PCI_VENDOR_ID, 2, &x))
208 continue;
209 if (x == PCI_VENDOR_ID_INTEL || x == PCI_VENDOR_ID_COMPAQ)
210 return 1;
211 }
212
213 DBG(KERN_WARNING "PCI: Sanity check failed/n");
214 return 0;
215 }
参数里的struct pci_raw_ops结构体定义在include/linux/pci.h里
294 struct pci_raw_ops {
295 int (*read)(unsigned int domain, unsigned int bus, unsigned int devfn,
296 int reg, int len, u32 *val);
297 int (*write)(unsigned int domain, unsigned int bus, unsigned int devfn,
298 int reg, int len, u32 val);
299 };
和那个著名的file_operations属于近亲关系,采用类似的手法,封装了对PCI设备配置寄存器的读写操作。PCI设备的访问方式有BIOS、DIRECT的conf1和conf2、MMConfig多种,因此对应的struct pci_raw_ops结构体也就有多个,与conf1相依相生的就是direct.c中的pci_direct_conf1
10 /*
11 * Functions for accessing PCI configuration space with type 1 accesses
12 */
13
14 #define PCI_CONF1_ADDRESS(bus, devfn, reg) /
15 (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3))
16
17 int pci_conf1_read(unsigned int seg, unsigned int bus,
18 unsigned int devfn, int reg, int len, u32 *value)
19 {
20 unsigned long flags;
21
22 if ((bus > 255) || (devfn > 255) || (reg > 255)) {
23 *value = -1;
24 return -EINVAL;
25 }
26
27 spin_lock_irqsave(&pci_config_lock, flags);
28
29 outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
30
31 switch (len) {
32 case 1:
33 *value = inb(0xCFC + (reg & 3));
34 break;
35 case 2:
36 *value = inw(0xCFC + (reg & 2));
37 break;
38 case 4:
39 *value = inl(0xCFC);
40 break;
41 }
42
43 spin_unlock_irqrestore(&pci_config_lock, flags);
44
45 return 0;
46 }
47
48 int pci_conf1_write(unsigned int seg, unsigned int bus,
49 unsigned int devfn, int reg, int len, u32 value)
50 {
51 unsigned long flags;
52
53 if ((bus > 255) || (devfn > 255) || (reg > 255))
54 return -EINVAL;
55
56 spin_lock_irqsave(&pci_config_lock, flags);
57
58 outl(PCI_CONF1_ADDRESS(bus, devfn, reg), 0xCF8);
59
60 switch (len) {
61 case 1:
62 outb((u8)value, 0xCFC + (reg & 3));
63 break;
64 case 2:
65 outw((u16)value, 0xCFC + (reg & 2));
66 break;
67 case 4:
68 outl((u32)value, 0xCFC);
69 break;
70 }
71
72 spin_unlock_irqrestore(&pci_config_lock, flags);
73
74 return 0;
75 }
76
77 #undef PCI_CONF1_ADDRESS
78
79 struct pci_raw_ops pci_direct_conf1 = {
80 .read = pci_conf1_read,
81 .write = pci_conf1_write,
82 };
14行,PCI_CONF1_ADDRESS宏用来计算向地址端口0cf8中写入的目标地址值,要注意,devfn表示的不仅仅是设备号,还有功能号,前面说从逻辑的角度讲,功能又可以称之为逻辑设备,那么设备号和功能号合起来也可以叫做逻辑设备号。另外,虽然各个配置寄存器都是以双字为单位,但它们都可以按照字节、16位字、32位双字来读写,所以reg的最低两位并不一定为0,为了满足向地址端口0cf8的构成,得通过reg & ~3将最低两位置为0。
22行,几乎每个函数都有的例行检查,总线号只有8位,逻辑设备号5加3也有8位,寄存器地址同样也只有8位,所以它们都不能够大于255。
27行,要操作公用的端口0cf8和0cfc了,所以得加把锁。社会经验不成文法则第2008条:占用公用资源时请加把锁。别让别人瞅见了。
29行,计算出目标设备和寄存器地址,写入端口0cf8。
31行,len为读取的字节数,因为对配置寄存器可以按照字节、16位字、32位双字来读写,所以switch有三个case,因为配置寄存器都是little-endian的,所以case 1时0xCFC + (reg & 3),case 2时0xCFC + (reg & 2)。
48行,write和read的区别只是in和out的区别,飘过。
现在回过头看看pci_sanity_check都利用pci_direct_conf1干了些什么,首先判断是不是设置了PCI_NO_CHECKS。PCI_NO_CHECKS意思是说不再用去验证了, conf1是一个值得信赖的好同志,是可以为革命服务的。在解析内核参数的时候,如果发现用户指明了使用conf1或者conf2,就会同时设置PCI_NO_CHECKS。
然后会使用for循环遍历所有可能的功能,也就是逻辑设备,去寻觅那个唯一的主桥。先是分别使用conf1去读它们的PCI_CLASS_DEVICE寄存器,如果读到的值为PCI_CLASS_BRIDGE_HOST和PCI_CLASS_DISPLAY_VGA之一,就说明主桥是存在的,如果两个都不是,需要再进一步去读PCI_VENDOR_ID寄存器,如果读到的值为PCI_VENDOR_ID_INTEL或者PCI_VENDOR_ID_COMPAQ,同样可以说明主桥是存在的,并且是Intel或者Compaq制造的,因为这两家的一些产品没有class ID,自然也读出来的PCI_CLASS_DEVICE寄存器值是trivial的。由此看来,for的过程就是一个寻寻觅觅的过程,如果众里寻它千百度,主桥仍然不在灯火阑珊处,并不就说明主桥不存在,还有种可能是conf1根本就没起作用。
本来pci_sanity_check这样就已经perfect了,但是在一年多以前,一个小伙儿提出了抗议,他发现在Horus系统上(采用了Newisys为AMD设计的Horus芯片,从而能够在服务器中使用高达32个AMD Opteron处理器),使用pci_sanity_check验证conf1验证总是失败,因为PCI总线0上啥也没有,于是他就添了196~199这么几行专门针对这种情况进行处理。原话是这样的:
Horus systems don’t have anything on bus 0 which makes the Type 1 sanity checks fail. Use the DMI BIOS year to check for newer systems and always assume Type 1 works on them. I used 2001 as an pretty arbitary cutoff year.
这里涉及到一个新概念,DMI(Desktop Management Interface),而看到DMI,你就不能不想到SMBIOS(System Management BIOS)。SMBIOS是主板厂商显示序列号、电池型号、网卡型号等等系统管理信息时所必须遵守的一套规范,DMI就是用来访问、收集这些信息的接口,简单点说,SMBIOS和DMI之间的关系就是规范和访问接口的关系。DMI信息存储在BIOS ROM的一段数据区里,江湖人称MIFD(Management Information Format Database),启动的时候,BIOS会将它们拷贝到内存里,这样,查询CPU、内存等在内的系统配置信息时就不用再进入BIOS。
这么说吧,你在网上凭借自己的三寸不烂之舌获得了一个mm的信任,偷偷远程登陆到她的机子上,想查看一下她电脑的一些系统信息然后再神通广大的样子告诉她从而显示自己是多么的powerful,这时咋办?一是打电话或者网上直接问她,这太弱了点,她会直接给你说“有病啊你”,二就是通过DMI去查,在linux上,可以通过dmidecode,执行dmidecode –t type就可以得尝所愿了,type代表的是获取信息的类型,不过这年头儿好像mm用linux的不多,linux社区里可是严重的男女比例失衡,搞linux搞内核很难会博得ppmm的崇拜,可要有心理准备啊同志们。
分享到:
评论

相关推荐

    Linux那些事儿 之 我是PCI

    Linux那些事儿 之 我是PCI,我做的一个集合,方便浏览

    Linux那些事儿之我是PCI1

    原文为blog.csdn.net/fudan_abc 上的《linux 那些事儿之我是PCI》,有闲情逸致的或者有批评建议的可以第 1 页 共 43

    电子-Linux那些事儿之我是PCI.rar

    电子-Linux那些事儿之我是PCI.rar,DIYDIY资料

    Linux那些事儿

    Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 Linux那些事儿之我是Sysfs 今天本人将9个单独的文档整理出来,做成了一个单独的文档,配有书签,更加方便读者阅读。

    Linux那些事儿1-9合集

    由复旦fudan_abc写的,风趣的文笔,深入浅出地讲解了Linux内核相关模块,主要涉及了USB相关的模块,但是...Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Block层 linux那些事儿之我是Sysfs

    Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI

    极力推荐。...Linux那些事儿之我是PCI.pdf Linux那些事儿之我是SCSI硬盘.pdf 注: 之前有人已经上传了《Linux那些事儿 系列》,其已经包含了:hub,sysfs,UHCI,usb core,U盘,此处,先感谢原作者。

    Linux那些事儿之我是EHCI主机控制器.pdf

    Linux那些事儿之 系列 之 我是EHCI主机控制器 很不错的教程。通俗易懂。 全系列: Linux那些事儿 系列之2 Block+EHCI+PCI+SCSI http://download.csdn.net/source/1369471

    linux的那些事儿全集

    Linux那些事儿之我是Block层 Linux那些事儿之我是EHCI...Linux那些事儿之我是PCI Linux那些事儿之我是SCSI硬盘 Linux那些事儿之我是Sysfs Linux那些事儿之我是UHCI Linux那些事儿之我是USB core Linux那些事儿之我是U盘

    linux那些事儿(EHCI Block SCSI Sysfs PCI USB U 盘 UHCI Hub)

    Linux那些事儿之我是EHCI主机控制器.pdf ...Linux那些事儿之我是PCI.pdf Linux那些事儿之我是USB core.pdf Linux 那些事儿之我是U 盘.pdf Linux那些事儿之我是Hub.pdf Linux那些事儿之我是UHCI.pdf

    linux那些事全集

    Linux那些事儿之我是U盘 Linux那些事儿之我是USB_core Linux那些事儿之我是UHCI ...Linux那些事儿之我是PCI Linux那些事儿之我是Hub Linux那些事儿之我是EHCI主机控制器 Linux那些事儿之我是Block层

    Linux那些事儿之我是XXX全集.rar

    Linux那些事儿之我是XXX全集 包含USB core U盘 UHCI PCI SCSI硬盘 Block Hub EHCI 。 想学驱动的童鞋,不妨看看。该书主要是进行源代码的分析

    Linux那些事儿.rar

    包括:Linux那些Linux那些事儿之我是SCSI硬盘,Linux那些事儿之我是Block层,Linux那些事儿之我是EHCI主机控制器,Linux那些事儿之我是HUB,Linux那些事儿之我是PCI,Linux那些事儿之我是Sysfs,Linux那些事儿之我是...

    PCI设备BAR空间的初始化

    PCI设备BAR空间的初始化详细的中文说明文档。合适PCI开发的硬件和软件人员作为参考

    linux那些事儿之我是USB.zip

    里面包含Linux那些事的九个文档,Block层,ECHI主机控制,HUB,PCI,SCSI硬盘,Sysfs,UHCI,USB+core,U盘等九个文档,内容详细,而且全面都有书签,适合系统学习!

    linux哪些事儿之我是usbcore echi pci u盘 sysfs

    linux哪些事儿之我是usbcore echi pci u盘 sysfs linux哪些事儿之我是usbcore echi pci u盘 sysfs

    PCI设备初始化

    系统刚上电时,CPU从0xbfc0.0000开始执行。这个地址在Rom空间中,在完成TLB,Cache,UART等初始化后,CPU就将代码拷到0x8010....主要的初始化工作:PCI设备的扫描、空间映射、资源分配都是initmips()函数所完成的。

    PCI设备初始化编成的过程

    这里以PowerPC上的PCI设备为例,介绍PCI设备初始化编程的过程,其他CPU的PCI基本框架是类似的. PowerPC的PCI控制器符合PCI2.2标准,以下使我解读的VxWorks系统PCI设备程序代码分析主要执行过程

    pci_linux.rar_PCI 打印机 linux_PCI 驱动例程_linux PCI_linux下pci_pci/qui

    pci 在linux系统下的驱动程序:支持PCI子系统初始化例程

Global site tag (gtag.js) - Google Analytics