接續
http://tw.myblog.yahoo.com/blue-comic/article?mid=912




  驅動程式通過pci_module_init向內核註冊自己(我們有時會看到pci_register_driver函數,其實他們是同一個,在內核代碼中會看到,只是個簡單的#define):
            pci_module_init(&pci_driver);
    呼叫函數後,如果pci_device_id陣列中標識的設備存在於系統中,並且該設備恰好還沒有驅動程式,則該驅動程式會被安裝。下面我們來看從8139too驅動代碼中裁剪的pci設備初始化代碼:
pci_driver.h:
/* pci_driver.h
* helinqiang@hotmail.com
* 2006-3-5
*/
#ifndef PCI_DRIVER_H
#define PCI_DRIVER_H
#include   //for struct pci_device_id
#include            //for MODULE_DEVICE_TABLE
#include
            
//for struct pci_driver
#define DRV_NAME    "8139too"
#define DRV_VERSION "0.9.27"
#define RTL8139_DRIVER_NAME   DRV_NAME " Fast Ethernet driver " DRV_VERSION
typedef enum{
    RTL8139 = 0,
    RTL8129,
}board_t;
static struct pci_device_id rtl8139_pci_tbl[] = {
    {0x10ec, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x10ec, 0x8138, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1113, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1500, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x4033, 0x1360, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1186, 0x1300, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1186, 0x1340, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x13d1, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1259, 0xa117, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1259, 0xa11e, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x14ea, 0xab06, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x14ea, 0xab07, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x11db, 0x1234, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1432, 0x9130, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x02ac, 0x1012, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x018a, 0x0106, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x126c, 0x1211, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x1743, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
    {0x021b, 0x8139, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
#ifdef CONFIG_SH_SECUREEDGE5410
    /* Bogus 8139 silicon reports 8129 without external PROM :-( */
    {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8139 },
#endif
#ifdef CONFIG_8139TOO_8129
    {0x10ec, 0x8129, PCI_ANY_ID, PCI_ANY_ID, 0, 0, RTL8129 },
#endif
/* some crazy cards report invalid vendor ids like
     * 0x0001 here.  The other ids are valid and constant,
     * so we simply don’t match on the main vendor id.
     */
    {PCI_ANY_ID, 0x8139, 0x10ec, 0x8139, 0, 0, RTL8139 },
    {PCI_ANY_ID, 0x8139, 0x1186, 0x1300, 0, 0, RTL8139 },
    {PCI_ANY_ID, 0x8139, 0x13d1, 0xab06, 0, 0, RTL8139 },
    {0,}
};
MODULE_DEVICE_TABLE(pci, rtl8139_pci_tbl);
static int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id);
static void __devexit rtl8139_remove_one(struct pci_dev *pdev);
static struct pci_driver rtl8139_pci_driver = {
    .name       = DRV_NAME,
    .id_table   = rtl8139_pci_tbl,
    .probe      = rtl8139_init_one,
    .remove     = __devexit_p(rtl8139_remove_one),
};
#endif //PCI_DRIVER_H
pci_driver.c:
/* pci_driver.c
* helinqiang@hotmail.com
* 2006-3-5
*/
#include "pci_driver.h"
#include
MODULE_AUTHOR("Linqiang He, Hangzhou China");
MODULE_LICENSE("Dual BSD/GPL");
static int __init rtl8139_init_module(void)
{
    /* when we’re a module, we always print a version message,
     * even if no 8139 board is found.
     */
#ifdef MODULE
    printk (KERN_INFO RTL8139_DRIVER_NAME "n");
#endif
    return pci_module_init(&rtl8139_pci_driver);
}
static void __exit rtl8139_cleanup_module (void)
{
    pci_unregister_driver(&rtl8139_pci_driver);
}
module_init(rtl8139_init_module);
module_exit(rtl8139_cleanup_module);
int __devinit rtl8139_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
    //這裏可插入各種調試代碼,下文會有專門描述。
    return 0;
}
void __devexit rtl8139_remove_one (struct pci_dev *pdev)
{
}
    註冊驅動程式成功後,rtl8139_init_one會被呼叫,在這個函數中,我們能通過插入一些列印輸出語句看到PCI的設置位址空間和I/O位址區域的一些情況。
    首先,插入以下語句:
            u16 vendor, device;
            pci_read_config_word(pdev, 0, &vendor);
            pci_read_config_word(pdev, 2, &device);
            printk(KERN_INFO "%x, %xn", vendor, device);
    這段代碼讀取了網卡設備的設置位址空間的前四位元,他正好是設備的廠商號和設備號。下面是輸出:
            Mar  9 21:44:39 localhost kernel: 10ec, 8139
    10ec8139就是我的網卡的廠商號和設備號了。
    再插入下列代碼:
            u32 addr1,addr2,addr3, addr4,addr5,addr6;
            pci_read_config_dword(pdev, 16, &addr1);
            pci_read_config_dword(pdev, 20, &addr2);
            pci_read_config_dword(pdev, 24, &addr3);
            pci_read_config_dword(pdev, 28, &addr4);
            pci_read_config_dword(pdev, 32, &addr5);
            pci_read_config_dword(pdev, 36, &addr6);
           
printk(KERN_INFO "%x,%x,%x,%x,%x,%xn",addr1, addr2, addr3,addr4,addr5,addr6);
    這段代碼讀取網卡設備的6I/O位址區域的址始位置。下面是輸出:
    Mar  9 21:55:06 localhost kernel: 3401,e0000800,0,0,0,0
    可見,該設備只使用了前兩個I/O位址區域,分別標識他的I/O埠區域和記憶體位址空間。
    另外,在這裏,還可直接列印出網卡的MAC位址。不再詳述。


著上文給出的原始碼,我們能在rtl8139_init_one中插入一些不同的調試代碼,觀察設備驅動模組在內核中的一些動作。
8139too 網卡設備的設備記憶體的頭6個位元組存放的是該網卡的48位元的MAC位址,我們能通過訪問設備記憶體得到這個MAC位址。下面通過在
rtl8139_init_one在插入代碼,以四種不同方式訪問設備記憶體。第一種是通過訪問I/O記憶體實現,後三種則是通過訪問I/O埠的形式實現。
第一種:
unsigned long mmio_start, addr1, addr2;
void __iomem *ioaddr;
mmio_start = pci_resource_start( pdev, 1);
//ioaddr = pci_iomap(pdev, 1, 0);


Ioaddr = ioremap(mmio_start, pci_resource_len(pdev,1));
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "mmio start: %lXn", mmio_start);
printk(KERN_INFO "ioaddr: %pn", ioaddr);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lXn",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16 ) & 0xFF,
(addr1 >> 24 ) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
運行結果:
Mar 10 22:34:56 localhost kernel: mmio start: E0000800
Mar 10 22:34:56 localhost kernel: ioaddr: f8aa6800
Mar 10 22:34:56 localhost kernel: 00.02.3F.AC.41.9D
第二種:
unsigned long pio_start, pio_len, addr1, addr2;
void __iomem *ioaddr;
pio_start = pci_resource_start( pdev, 0);
pio_len = pci_resource_len (pdev, 0);
ioaddr = ioport_map(pio_start, pio_len);
addr1 = ioread32( ioaddr );
addr2 = ioread32( ioaddr + 4 );
printk(KERN_INFO "pio start: %lXn", pio_start);
printk(KERN_INFO "ioaddr: %pn", ioaddr);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lXn",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16 ) & 0xFF,
(addr1 >> 24 ) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
運行結果:
Mar 10 22:30:52 localhost kernel: pio start: 3400
Mar 10 22:30:52 localhost kernel: ioaddr: 00013400
Mar 10 22:30:52 localhost kernel: 00.02.3F.AC.41.9D
第三種:
unsigned long pio_start, addr1, addr2;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inl( pio_start );
addr2 = inl( pio_start + 4 );
printk(KERN_INFO "port io start: %lXn", pio_start);
printk(KERN_INFO "%02lX.%02lX.%02lX.%02lX.%02lX.%02lXn",
(addr1) & 0xFF,
(addr1 >> 8) & 0xFF,
(addr1 >> 16) & 0xFF,
(addr1 >> 24) & 0xFF,
(addr2) & 0xFF,
(addr2 >> 8) & 0xFF );
運行結果:
Mar 10 22:36:18 localhost kernel: port io start: 3400
Mar 10 22:36:18 localhost kernel: 00.02.3F.AC.41.9D
第四種:
unsigned long pio_start;
u8 addr1, addr2, addr3, addr4, addr5, addr6;
pio_start = pci_resource_start( pdev, 0 );
addr1 = inb( pio_start );
addr2 = inb( pio_start + 1 );
addr3 = inb( pio_start + 2 );
addr4 = inb( pio_start + 3 );
addr5 = inb( pio_start + 4 );
addr6 = inb( pio_start + 5 );
printk(KERN_INFO "port io start: %lXn", pio_start);
printk(KERN_INFO "%02X.%02X.%02X.%02X.%02X.%02Xn",
addr1, addr2, addr3, addr4, addr5, addr6 );
運行結果:
Mar 10 22:37:19 localhost kernel: port io start: 3400
Mar 10 22:37:19 localhost kernel: 00.02.3F.AC.41.9D


 


pci設備驅動中其他常用的函數(或巨集)如下所示:


l          獲取I/O資源或記憶體資源。


#define pci_resource_start(dev, bar) ((dev)->resource[(bar)].start)


#define pci_resource_end(dev, bar) ((dev)->resource[(bar)].end)


#define pci_resource_flags(dev, bar) ((dev)->resource[(bar)].flags)


#define pci_resource_len(dev, bar) \


((pci_resource_start(dev, bar) == 0 && \


pci_resource_end(dev, bar) ==       \


pci_resource_start(dev, bar)) ? 0 :    \


(pci_resource_end(dev, bar) - pci_resource_start(dev, bar) +1)


l          申請/釋放I/O或記憶體資源。


Int pci_request_regions(struct pci_dev *pdev, const char *res_name);


Void pci_release_region(stuct pci_dev *pdev);


l          獲取或設置驅動私有資料


Void * pci_get_drvdata(struct pci_dev *pdev);


Void pci_set_drvdata(struct pci_dev *pdev, void * data);


l          使能/禁止PCI設備


Int pci_enable_device(struct pci_dev *pdev);


Void pci_disable_device(struct pci_dev * dev);


l          設置為匯流排主DMA


Pci_set_master(struct pci_dev * dev);
arrow
arrow
    全站熱搜

    立你斯 發表在 痞客邦 留言(0) 人氣()