最近樓主比較苦逼啊,主管佈置了一道訪問pci的做業,這個做業使用io方式還能夠很是浪地將全部的東西都給讀取出來,雖然不能讀取出pci-e設備的全部信息,可是仍是能夠將256位的其餘東西給讀出來的。linux
本文將先從io訪問模式進行對pci訪問的設置,在這裏我所使用的包含了dos和linux,這樣能夠看到這2個的系統代碼的不一樣。ios
PCI總線是一種高性能32位或者64位的多路複用地址或者數據行的總線。至關於現實生活中的公路,是所謂的信號通道。能夠在這上面傳輸數據、控制信號等等。編程
做用:高度集成外圍控制其、外圍插件和處理器/內存系統之間的互連機制。c#
若是要訪問PCI 設備,首先要肯定PCI設備在系統中的物理鏈接狀況。描述這個鏈接狀況的數據是「總線號」、「設備號」和「功能號」。一個系統能夠有256個PCI總線,每一個總線上能夠有32個設備,每一個設備能夠具備8個功能(每一個功能做爲一個PCI設備)。當這三個數據肯定的時候,就能夠在系統中惟一肯定一個PCI 設備。佈局
8~10:功能位. 有時候,一個pci設備對應多個功能.將每一個功能單元分離出來,對應一個獨立的pci device
11~15位:設備號 對應該pci總線上的設備序號
16~23位:總線號 根總線的總線號爲0.每遍歷到下層總線,總線號+1性能
在上圖的總線結構中,ethernet設備和pci-pci bridge的同類型資源空間必需要是pci bus0的一個子集操作系統
例如,pci bus 0的I/O端口資源是0x00CC~0x01CC. Ethernet設備的I/O範圍的是0x00CC~0x0xE0.那麼pci-pci bridge的I/O端口範圍就必需要在0x0xE0~0x01CC之間.插件
一樣,SCSI和VIDEO同類型資源必需要是pci_bus1的子集.pci bus1上有一個pci橋,對應的資源也就是它所連橋上的資源.即pci_bus->self.也就是說,下層總線的資源是它上層總線資源的子集。上層總線資源是下層總線資源的父集。code
其實,每一個PCI設備的資源地始地址都是由操做系統設置的.在x86上,都由bios設置好了.blog
PCI配置空間是一塊容量爲256字節並具備特定記錄結構或模型的地址空間,經過配置空間,咱們能夠了解該PCI設備的一些配置狀況,進而控制該設備,除主總線橋之外的全部PCI設備都必須事先配置空間.
配置空間的前64個字節叫頭標區,頭標區又分紅兩個部分,第一部分爲前16個字節,在各類類型的設備中定義都是同樣的,其餘字節隨各設備支持的功能不一樣而有所不一樣,位於偏移0EH的投標類型字段規定了是何種佈局,目前有三種頭標類型,頭標類型1用於PCI-PCI橋,頭標類型2用於PCI-CARDBUS橋,頭標類型0用於其餘PCI設備,下圖爲頭標類型0的頭標區佈局。
頭標區中有5個字段涉及設備的識別。
偏移:00H。該字段用以標明設備的製造者。一個有效的供應商標識由PCI SIG來分配,以保證它的惟一性。0FFFFH是該字段的無效值。
偏移:02H。用以標明特定的設備,具體代碼由供應商來分配。
偏移:08H。用來指定一個設備特有的版本識別代碼,其值由供應商提供,能夠是0。
偏移:0EH。該字段有兩個做用,一是用來表示配置空間頭標區第二部分的佈局類型;二是用以指定設備是否包含多功能。位7用來標識一個多功能設備,位7爲0代表是單功能設備,位7爲1代表是多功能設備。位0-位6代表頭標區類型。
偏移:09H。標識設備的整體功能和特定的寄存器級編程接口。該字節分三部分,每部分佔一個字節,第一部分是基本分類代碼,位於偏移0BH,第二部分叫子分類代碼,位於偏移0AH處,第三部分用於標識一個特定的寄存器級編程接口。
在dos下申請相關的接口就能夠獲得io口,經過cf8和cfc的模式進行讀取遍歷pci設備。
#include <stdio.h> typedef unsigned long DWORD; typedef unsigned int WORD; #define MK_PDI(bus,dev,func) (WORD)((bus<<8)|(dev<<3)|(func)) #define MK_PCIaddr(bus,dev,func) (DWORD)(0xf8000000L|(DWORD)MK_PDI(bus,dev,func)<<8) #define PCI_CONFIG_ADDRESS 0xCF8 #define PCI_CONFIG_DATA 0xCFC DWORD inpd(int inport) { DWORD data; asm mov dx,inport; asm lea bx,data; __emit__( 0x66,0x50, 0x66,0xED, 0x66,0x89,0x07, 0x66,0x58); return data; } void outpd(int outport,DWORD addr) { asm mov dx,outport; asm lea bx,addr; __emit__( 0x66,0x50, 0x66,0x8B,0x07, 0x66,0xEF, 0x66,0x58); } DWORD GetData(DWORD addr) { DWORD data; outpd(PCI_CONFIG_ADDRESS,addr); data = inpd(PCI_CONFIG_DATA); return data; } int main() { int bus,dev,func; DWORD addr,addr1,addr2,addr3; DWORD data,data1,data2,data3; printf("Bus#\tDev#\tFunc#"); printf("\n"); for (bus = 0; bus <= 0x63; ++bus) { for (dev = 0; dev <= 0x1F; ++dev) { for (func = 0; func <= 0x7; ++func) { addr = MK_PCIaddr(bus,dev,func); data = GetData(addr); if((WORD)data!=0xFFFF) { printf("%2.2x\t%2.2x\t%2.2x\t",bus,dev,func); printf("\n"); } } } } return 0; }
在linux系統下就很簡單了,直接看代碼吧!
#include<stdio.h> #include<stdlib.h> #include<unistd.h> #include<sys/io.h> #define PCI_MAX_BUS 255 #define PCI_MAX_DEV 31 #define PCI_MAX_FUN 7 #define PCI_BASE_ADDR 0x80000000L #define CONFIG_ADDR 0xcf8 #define CONFIG_DATA 0xcfc typedef unsigned long DWORD; typedef unsigned int WORD; typedef unsigned long DWORD; int main() { WORD bus,dev,fun; DWORD addr,data; int ret; printf("bus#\tdev#\tfun#\t"); printf("\n"); ret = iopl(3); if(ret < 0) { perror("iopl set error"); return -1; } for(bus = 0; bus <= PCI_MAX_BUS; bus++) for(dev = 0; dev <= PCI_MAX_DEV; dev++) for(fun = 0; fun <= PCI_MAX_FUN; fun++) { addr = PCI_BASE_ADDR|(bus << 16)|(dev << 11)|(fun << 8); outl(addr,CONFIG_ADDR); data = inl(CONFIG_DATA); if((data != 0xffffffff)&&(data != 0)) { printf("%2x\t%2x\t%2x",bus,dev,fun); printf("\n"); } } ret = iopl(0); if(ret < 0){ perror("iopl set error"); return -1; } return 0; }
今天暫時到這裏,下一章將介紹什麼是mmio,如何實現的!歡迎關注!