PCI驅動程式實現(linux)

2021-04-26 01:41:36 字數 4541 閱讀 4566

1.關鍵資料結構

pci 裝置上有三種位址空間:pci的i/o空間、pci的儲存空間和pci的配置空間。cpu可以訪問pci裝置上的所有位址空間,其中i/o空間和儲存空間提供給裝置驅動程式使用,而配置空間則由linux核心中的pci初始化**使用。核心在啟動時負責對所有pci裝置進行初始化,配置好所有的pci裝置,包括中斷號以及i/o基址,並在檔案/proc/pci中列出所有找到的pci裝置,以及這些裝置的引數和屬性。

linux驅動程式通常使用結構(struct)來表示一種裝置,而結構體中的變數則代表某一具體裝置,該變數存放了與該裝置相關的所有資訊。好的驅動程式都應該能驅動多個同種裝置,每個裝置之間用次裝置號進行區分,如果採用結構資料來代表所有能由該驅動程式驅動的裝置,那麼就可以簡單地使用陣列下標來表示次裝置號。

在pci驅動程式中,下面幾個關鍵資料結構起著非常核心的作用: 

pci_driver

這個資料結構在檔案include/linux/pci.h裡,這是linux核心版本2.4之後為新型的pci裝置驅動程式所新增的,其中最主要的是用於識別裝置的id_table結構,以及用於檢測裝置的函式probe( )和解除安裝裝置的函式remove( ):

struct pci_driver ;

pci_dev

這個資料結構也在檔案include/linux/pci.h裡,它詳細描述了乙個pci裝置幾乎所有的硬體資訊,包括廠商id、裝置id、各種資源等:

struct pci_dev ;

2.基本框架

在用模組方式實現pci裝置驅動程式時,通常至少要實現以下幾個部分:初始化裝置模組、裝置開啟模組、資料讀寫和控制模組、中斷處理模組、裝置釋放模組、裝置解除安裝模組。下面給出乙個典型的pci裝置驅動程式的基本框架,從中不難體會到這幾個關鍵模組是如何組織起來的。

/* 指明該驅動程式適用於哪一些pci裝置 */

static struct pci_device_id demo_pci_tbl __initdata = ,

};/* 對特定pci裝置進行描述的資料結構 */

struct demo_card

/* 中斷處理模組 */

static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)

/* 裝置檔案操作介面 */

static struct file_operations demo_fops = ;

/* 裝置模組資訊 */

static struct pci_driver demo_pci_driver = ;

static int __init demo_init_module (void)

static void __exit demo_cleanup_module (void)

/* 載入驅動程式模組入口 */

module_init(demo_init_module);

/* 解除安裝驅動程式模組入口 */

module_exit(demo_cleanup_module);

上面這段**給出了乙個典型的pci裝置驅動程式的框架,是一種相對固定的模式。需要注意的是,同載入和解除安裝模組相關的函式或資料結構都要在前面加上 __init、__exit等標誌符,以使同普通函式區分開來。構造出這樣乙個框架之後,接下去的工作就是如何完成框架內的各個功能模組了。

3.初始化裝置模組

在linux系統下,想要完成對乙個pci裝置的初始化,需要完成以下工作:

* 檢查pci匯流排是否被linux核心支援;

* 檢查裝置是否插在匯流排插槽上,如果在的話則儲存它所占用的插槽的位置等資訊。

* 讀出配置頭中的資訊提供給驅動程式使用。 

當linux核心啟動並完成對所有pci裝置進行掃瞄、登入和分配資源等初始化操作的同時,會建立起系統中所有pci裝置的拓撲結構,此後當pci驅動程式需要對裝置進行初始化時,一般都會呼叫如下的**:

static int __init demo_init_module (void)

return 0;

}驅動程式首先呼叫函式pci_present( )檢查pci匯流排是否已經被linux核心支援,如果系統支援pci匯流排結構,這個函式的返回值為0,如果驅動程式在呼叫這個函式時得到了乙個非0的返回值,那麼驅動程式就必須得中止自己的任務了。在2.4以前的核心中,需要手工呼叫pci_find_device( )函式來查詢pci裝置,但在2.4以後更好的辦法是呼叫pci_register_driver( )函式來註冊pci裝置的驅動程式,此時需要提供乙個pci_driver結構,在該結構中給出的probe探測例程將負責完成對硬體的檢測工作。

static int __init demo_probe(struct pci_dev *pci_dev, const struct pci_device_id *pci_id)

/* 在核心空間中動態申請記憶體 */

if ((card = kmalloc(sizeof(struct demo_card), gfp_kernel)) == null)

memset(card, 0, sizeof(*card));

/* 讀取pci配置資訊 */

card->iobase = pci_resource_start (pci_dev, 1);

card->pci_dev = pci_dev;

card->pci_id = pci_id->device;

card->irq = pci_dev->irq;

card->next = devs;

card->magic = demo_card_magic;

/* 設定成匯流排主dma模式 */ 

pci_set_master(pci_dev);

/* 申請i/o資源 */

request_region(card->iobase, 64, card_names[pci_id->driver_data]);

return 0;

}4.開啟裝置模組

在這個模組裡主要實現申請中斷、檢查讀寫模式以及申請對裝置的控制權等。在申請控制權的時候,非阻塞方式遇忙返回,否則程序主動接受排程,進入睡眠狀態,等待其它程序釋放對裝置的控制權。

static int demo_open(struct inode *inode, struct file *file)

if(file->f_mode & fmode_write)

/* 申請對裝置的控制權 */

down(&card->open_sem);

while(card->open_mode & file->f_mode) else }}

5.資料讀寫和控制資訊模組

pci裝置驅動程式可以通過demo_fops 結構中的函式demo_ioctl( ),向應用程式提供對硬體進行控制的介面。例如,通過它可以從i/o暫存器裡讀取乙個資料,併發送到使用者空間裡:

static int demo_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)

}事實上,在demo_fops裡還可以實現諸如demo_read( )、demo_mmap( )等操作,linux核心原始碼中的driver目錄裡提供了許多裝置驅動程式的源**,找那裡可以找到類似的例子。在對資源的訪問方式上,除了有i/o指令以外,還有對外設i/o記憶體的訪問。對這些記憶體的操作一方面可以通過把i/o記憶體重新對映後作為普通記憶體進行操作,另一方面也可以通過匯流排主dma (bus master dma)的方式讓裝置把資料通過dma傳送到系統記憶體中。

6.中斷處理模組

pc的中斷資源比較有限,只有0~15的中斷號,因此大部分外部裝置都是以共享的形式申請中斷號的。當中斷發生的時候,中斷處理程式首先負責對中斷進行識別,然後再做進一步的處理。

static void demo_interrupt(int irq, void *dev_id, struct pt_regs *regs)

/* 告訴裝置已經收到中斷 */

outl(status & int_mask, card->iobase + glob_sta);

spin_unlock(&card->lock);

/* 其它進一步的處理,如更新dma緩衝區指標等 */

}7.釋放裝置模組

釋放裝置模組主要負責釋放對裝置的控制權,釋放占用的記憶體和中斷等,所做的事情正好與開啟裝置模組相反:

static int demo_release(struct inode *inode, struct file *file)

8. 解除安裝裝置模組

呼叫函式pci_unregister_driver( )從linux核心中登出裝置驅動程式

linux下PCI裝置驅動程式之註冊詳解

一 識別方式 為了方便每個人的管理,搞了一套身份識別的玩意 檔案和身份證 kernel本省就像乙個社會,每個進來的裝置必須要有它獨特的名字和一些檔案。這個工作對pci來說,它是由pci device id這個結構體來進行身份資訊儲存的 17 struct pci device id 針對一些英文不好...

linux 驅動程式 高階字元驅動程式

ioctl方法 驅動程式的原型實現 int ioctl struct inode inode,struct file filp,unsigned int cmd,unsigned long arg ioctl 命令選擇 位段結構 number direction ioc read ioc write...

linux裝置驅動程式 字元裝置驅動程式

先留個 有一起學習驅動程式的加qq295699450 字元裝置驅動 這篇比較惱火。載入成功,但是讀不出來資料,有知道怎麼回事的,留個言,一起討論下 資料結構 struct scull mem struct scull dev dev 整個驅動程式 如下 include include include...