1. 程式人生 > >PCI裝置驅動

PCI裝置驅動

為了能看到實際的執行效果,我們選擇8139too網絡卡作為示例,從該網絡卡的linux驅動程式中裁剪相關程式碼。
    一個PCI裝置的驅動程式必須要向核心中的PCI核心描述自己。同時,它也必須告訴PCI核心自己能夠驅動哪些裝置。下面,就介紹兩個相關的重要資料結構。
    struct pci_device_id {
        __u32 vendor, device;       /* Vendor and device ID or PCI_ANY_ID*/
        __u32 subvendor, subdevice; /* Subsystem ID's or PCI_ANY_ID */
        __u32 class, class_mask;    /* (class,subclass,prog-if) triplet */
        kernel_ulong_t driver_data; /* Data private to the driver */
    };
       
    struct pci_driver {
        struct list_head node;
        char *name;
        struct module *owner;
        const struct pci_device_id *id_table; //驅動所能操縱的裝置id列表。
        int (*probe)(struct pci_dev *dev, const struct pci_device_id *id); //插入新裝置
        void (*remove)(struct pci_dev *dev);   //移除裝置。
        int (*suspend)(struct pci_dev *dev, pm_message_t state);
        int (*resume)(struct pci_dev *dev);
        int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable);
        void (*shutdown) (struct pci_dev *dev);
        struct device_driver    driver;
        struct pci_dynids dynids;
    };
    pci_device_id唯一標識一個PCI裝置。它的幾個成員依次分別表示:廠商號,裝置號,子廠商號,子裝置號,類別,類別掩碼(類可分為基類,子類),私有資料。每一個PCI裝置的驅動程式都有一個pci_device_id的陣列,用於告訴PCI核心自己能夠驅動哪些裝置。8139too的驅動程式定義它的pci_device_id陣列如下:
        static struct pci_device_id rtl8139_pci_tbl[];
    該陣列被初始化為8139系列的一組網絡卡,當PCI核心得到這個陣列後,會拿陣列中的每一項跟從PCI配置空間中讀取到的資料進行比對,從而為該驅動程式找到正確的裝置。而pci_driver代表一個pci驅動程式。成員id_talbe即是指向pci_device_id陣列的指標。name是驅動程式的名字,probe完成探測工作,即拿pci_device_id陣列與核心中的資料進行比對。remove完成驅動程式的移除工作。關鍵的成員就這幾個。
    驅動程式通過pci_module_init向核心註冊自己(我們有時會看到pci_register_driver函式,其實它們是同一個,在核心程式碼中會看到,只是一個簡單的#define):
            pci_module_init(&pci_driver);
    呼叫函式後,如果pci_device_id陣列中標識的裝置存在於系統中,並且該裝置恰好還沒有驅動程式,則該驅動程式會被安裝。下面我們來看從8139too驅動程式碼中裁剪的pci裝置初始化程式碼:
pci_driver.h:

/* pci_driver.h
 *
[email protected]

 * 2006-3-5
 */
#ifndef PCI_DRIVER_H
#define PCI_DRIVER_H

#include <linux/mod_devicetable.h>  //for struct pci_device_id
#include <linux/module.h>           //for MODULE_DEVICE_TABLE
#include <linux/pci.h>              //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
 *
[email protected]

 * 2006-3-5
 */

#include "pci_driver.h"

#include <linux/init.h>

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, %x/n", vendor, device);
    這段程式碼讀取了網絡卡裝置的配置地址空間的前四位,它正好是裝置的廠商號和裝置號。下面是輸出:
            Mar  9 21:44:39 localhost kernel: 10ec, 8139
    10ec和8139就是我的網絡卡的廠商號和裝置號了。
    再插入下列程式碼:
            u32 addr1,addr2,addr3, addr4,addr5,addr6;
           

相關推薦

淺談Linux PCI裝置驅動

轉自 http://www.uml.org.cn/embeded/201205152.asp 淺談Linux PCI裝置驅動(一) 要弄清楚Linux PCI裝置驅動,首先要明白,所謂的Linux

PCI裝置驅動切換方法

在linux系統中,有時會為同一類裝置同時載入多個驅動,用於測試或者不同使用方式。例如做資料報文處理的伺服器上可能會同時載入普通的網絡卡驅動和DPDK的igb_uio驅動來使用通訊網絡卡和資料處理卡。 在這種情況下,需要一種方式能夠讓指定裝置在多個驅動間切換,從而實現同類裝

PCI裝置驅動裝置

四、PCI裝置的列舉探測過程 在核心啟動過程中,PCI裝置的探測過程是完全自動的,核心已經整合好了方法,我們無需更改,在這裡還是分析一邊程式碼作為了解。 分析之前,先看一下全部的函式呼叫關係,大致瞭解一下 pci_arch_init /* 判斷host/pci

PCI裝置驅動程式

一、PCI裝置驅動編寫 PCI匯流排是現在非常流行的計算機匯流排,學會它的驅動設計方法很重要。相信曾經想學習PCI匯流排驅動的人有這麼一個經歷,就是去看那些講解PCI匯流排驅動的書籍和資料的時候,會被裡面繁雜的內容所擊敗,又是什麼配置空間又是什麼列舉的,還沒開

Linux下PCI裝置驅動程式開發基本框架

PCI是一種廣泛採用的匯流排標準,它提供了許多優於其它匯流排標準(如EISA)的新特性,目前已經成為計算機系統中應用最為廣泛,並且最為通用的匯流排標準。Linux的核心能較好地支援PCI匯流排,本文以Intel 386體系結構為主,探討了在Linux下開發PCI裝置驅動程式的基本框架。    一、PCI匯流排

淺談Linux PCI裝置驅動(下)

我們在 淺談Linux PCI裝置驅動(上)中(以下簡稱 淺談(一) )介紹了PCI的配置暫存器組,而Linux PCI初始化就是使用了這些暫存器來進行的。後面我們會舉個例子來說明Linux PCI裝置驅動的主要工作內容(不是全部內容),這裡只做文字性的介紹而不會涉及具體程式碼的分析,因為要

淺談Linux PCI裝置驅動(上)

有學員建議寫寫PCI驅動,今天就找到一篇,文章很長,這基本上是全網對PCI講的比較詳細的部落格了,分成上下兩篇,這是上部分,未完待續。 要弄清楚Linux PCI裝置驅動,首先要明白,所謂的Linux PCI裝置驅動實際包括Linux PCI裝置驅動和裝置本身驅動兩部分。 不知道讀者

PCI子系統(三)- PCI裝置驅動

這裡拿linux/drivers/leds/leds-ss4200.c來做模板參考學習 註冊struct pci_driver static struct pci_driver nas_gpio_pci_driver = { .name = KBUILD_M

Linux下的PCI裝置驅動自動載入問題MODULE_DEVICE_TABLE

以前做驅動時,一般將驅動複製到/lib/modules/$(uname -r)/kernel/driver/目錄後,執行depmod都可以自動載入,但是客戶反映公司一款驅動無法自動載入。後經過與其它版本程式碼對比,才發現是MODULE_DEVICE_TABLE沒有設定引起的

PCI裝置驅動

為了能看到實際的執行效果,我們選擇8139too網絡卡作為示例,從該網絡卡的linux驅動程式中裁剪相關程式碼。     一個PCI裝置的驅動程式必須要向核心中的PCI核心描述自己。同時,它也必須告訴PCI核心自己能夠驅動哪些裝置。下面,就介紹兩個相關的重要資料結構。     struct pci_devic

淺談Linux PCI裝置驅動(二)

我們在 淺談Linux PCI裝置驅動(一)中(以下簡稱 淺談(一) )介紹了PCI的配置暫存器組,而Linux PCI初始化就是使用了這些暫存器來進行的。後面我們會舉個例子來說明Linux PCI裝置驅動的主要工作內容(不是全部內容),這裡只做文字性的介紹而不會涉及具體程

PCI驅動基礎 >> Linux裝置驅動程式

俗話說的好,免費是最貴,閒暇是最累的,但是我自己選的路就要負責走完; 壓力一天比一天重,當學習了理論卻不知道該如何輸出的時候,會有一種油然而生的挫敗感; 看來必須得調整自己的心態還是要調整學習方法,如何才能用最好狀態去接受新的知識; 文章目錄 [0x1

linux PCI驅動呼叫字元裝置驅動方式

上一篇文章寫了字元裝置驅動的基本結構及訪問方式,在實際應用時首先需要繫結自己的硬體裝置。本篇主要描述字元裝置驅動與PCI介面型別的裝置訪問方式(核心為2.6.24及以上的方法,測試核心為2.6.32)。 首先介紹下PCI驅動結構: //PCI裝置id描述

【DPDK】談談DPDK如何實現bypass核心的原理 其一 PCI裝置與UIO驅動

【前言】   隨著網路的高速發展,對網路的效能要求也越來越高,DPDK框架是目前的一種加速網路IO的解決方案之一,也是最為流行的一套方案。DPDK通過bypass核心協議棧與核心驅動,將驅動的工作從核心態移至使用者態,並利用polling mode的執行緒工作模式加速網路I/O使得網路IO效能出現大幅度的增長

linux裝置驅動開發學習--記憶體和IO訪問

一 I/O 埠 1. 讀寫位元組埠(8 位寬) unsigned inb(unsigned port); void outb(unsigned char byte, unsigned port); 2. 讀寫字埠(16 位寬) unsigned inw(unsigne

字元裝置驅動程式的三種寫法

驅動工程師如何去寫驅動程式? 要看原理圖。確定如何去操作硬體。 對於點亮led燈來說,確定引腳,檢視晶片手冊,確定如何去操作引腳,要設定哪些暫存器,如何設定這些暫存器才可以讓這個引腳輸出高電平或者低電平。 寫驅動程式 驅動程式起封裝作用,如何封裝。應用程式要操作硬體需要o

linux裝置驅動模型 - regmap

1. regmap介紹 regmap主要是為了方便操作暫存器而設計的,它將所有模組的暫存器(包括soc上模組的暫存器和外圍裝置的暫存器等) 抽象出來,用一套統一介面來操作暫存器 比如,如果要操作i2c裝置的暫存器,那麼就要呼叫i2c_transfer介面,要操作spi裝置的暫存

linux裝置驅動模型 - device/bus/driver

在linux驅動模型中,為了便於管理各種裝置,我們把不同裝置分別掛在他們對應的總線上,裝置對應的驅動程式也在總線上找,這樣就提出了deivce-bus-driver的模型,硬體上有許多裝置匯流排,那麼我們就在裝置模型上抽象出bus概念,相應的device就代表裝置,driver表示驅動,

linux裝置驅動模型 - sys/kobject

1. sysfs 1.1 sysfs檔案系統註冊 在系統啟動時會註冊sysfs檔案系統 (fs/sysfs/mount.c) int __init sysfs_init(void) { int err; sysfs_root = kernfs_creat

linux裝置驅動模型 - 驅動框架

linux驅動模型框架如圖所示: 1. kernfs 驅動最終是提供給使用者層使用,那麼其中的介面就是通過kernfs檔案系統來註冊,kernfs是一個通用的核心虛擬檔案系統 2. sysfs/kobject sysfs是裝置驅動檔案系統,裝置之間的各種關係會在在/