2010/02/10追記:下の日記が,ちゃんと動く版です〜
http://d.hatena.ne.jp/masami256/20100208/1265644385
最近だらだらと自作カーネルを書いているんですが,
PCIについて,OSDevとかこちらを読んで,現状分かったことを忘れないうちにメモっときます.
PCIでどんなデバイスがあるかを調べるには,CONFIG_ADDRESS(アドレス:0x0cf8)とCONFIG_DATA(アドレス:0xcfc)の2個を使って,
検索をするようです.
CONFIG_ADDRESSは32bitで,各ビットの意味はこんな感じです.
31 : イネーブルビット
30 - 24: 予約済み
23 - 16: バス番号
15 - 11: デバイス番号
10 - 8 : 機能番号
7 - 2 : レジスタのアクセス
1 - 0 : 0で固定
CONFIG_ADDRESSはデータを読み込むというよりも,書き込みがメインです.
CONFIG_ADDRESSはどんなデータをCONFIG_DATAから読み出したいかを指定するためのモノという感じでしょうか.
CONFIG_DATAからデータを読み込む前に,31bit目のイネーブルビットを1にしてあげる必要があります.
PCIデバイスはバス番号(0〜255)とデバイス番号(0〜31)の組み合わせで探していけます.
と,いうことで,こんな感じでfor文を回してみました.
void find_pci_device(void) { int bus, dev; printk("start find_pci_device\n"); for (bus = 0; bus < PCI_BUS_MAX; bus++) { for (dev = 0; dev < PCI_DEVICE_MAX; dev++) { read_pci_data(bus, dev); } } }
read_pci_data()は以下のコードです.
static u_int32_t read_pci_data(u_int8_t bus, u_int8_t dev) { u_int32_t data; struct pci_configuration_register reg; memset(®, 0, sizeof(reg)); reg.bus_num = bus; reg.dev_num = dev; reg.enable_bit = 1;
struct pci_configuration_registerはCONFIG_ADDRESSの構造を,cの構造体にしただけです.
バス番号とデバイス番号,それにイネーブルビットの値を設定します.
// write data to CONFIG_ADDRESS.
write_pci_config_address(®);
そうしたら,CONFIG_ADDRESSにデータを書き込みます.
CONFIG_ADDRESSにデータを書き込んでるのは以下のコードになります.
static void write_pci_config_address(struct pci_configuration_register *reg) { u_int32_t data; data = (reg->enable_bit << 31) | (reg->reserved << 24) | (reg->bus_num << 16) | (reg->dev_num << 11) | (reg->func_num << 8) | (reg->reg_num << 2) | reg->bit0; outl(PCI_CONFIG_ADDRESS, data); }
CONFIG_ADDRESSに書き込むデータは32bitなので,構造体のデータを1つの32bitのデータにまとめてたうえで書き込みを行います.
outl()はアセンブラのoutl命令を実行する関数です.
data = inl(CONFIG_DATA_1); if (data != 0xffffffff) {
inl()もアセンブラのinl命令を実行する関数です.
0xcfcを読み込んだ結果が0xffffffffの場合は,デバイスが存在しないということになります.
CONFIG_ADDRESSでレジスタ番号を0にしてCONFIG_DATAを読み込むと,32bitの上位16bitがベンダーID,下位16bitがデバイス番号になります.
u_int32_t class;
reg.enable_bit = 0;
write_pci_config_address(®);
この処理が必要なのか良く分かってませんが(´・ω`・)エッ? イネーブルビットを0にしてます.
reg.enable_bit = 1; reg.reg_num = 0x8; write_pci_config_address(®); class = inl(CONFIG_DATA_1); printk("Found Device: Vender is 0x%x : Device is 0x%x : Class is 0x%x\n", data & 0xffff, (data >> 16) & 0xffff, class);
こんどは,レジスタ番号に0x08を指定(デバイス番号,バス番号は変更なし)して,再度CONFIG_ADDRESSに書き込みを行います.
詳細な意味は,OS-Wikiを参照してもらうとして・・・
デバイスのクラスコードを読み出します.
0x08で読み出した場合,32bit中の下位8bitがリビジョンID,上位24bitがクラスコードになってるんですが,
今のところは一緒くたにして表示してます.
} reg.enable_bit = 0; write_pci_config_address(®); return 0; }
最後にイネーブルビットを0にして終了です.
このコードをEeePCで動かしたら,こんな感じになりました.