Linux驅動開發學習筆記

2021-05-12 09:55:44 字數 4525 閱讀 7299

一、 linux裝置驅動基礎(基於linux2.6核心)(參考《linux device drivers 3rd edition》)

linux kernel有乙個很好的特性,可以支援在執行是進行擴充套件。這意味著系統啟動執行是,我們仍然可以向kernel新增功能。這種執行時可以被新增到kernel的**稱為module(模組)。

linux kernel支援好幾種模組型別,包括裝置驅動程式。每個模組由目標**組成,不是乙個完整的可執行程式。系統執行時,我們可以通過insmod將模組連線到正在執行的核心中去。也可以使用lsmod列出已載入模組,rmmod或modprobe –r 移除模組。

linux系統將裝置分為三種基本型別:字元裝置,塊裝置,網路介面。

字元裝置是能夠像位元組流一樣被訪問的裝置,一般只能順序訪問。其操作類似檔案操作。

塊裝置上能夠容納檔案系統,可以通過檔案系統隨機訪問。其操作也類似於檔案操作。

網路介面是負責資料報的傳輸和接收的,一般無法影射到檔案系統的節點。它與核心的通訊跟前面兩種裝置不同,而是通過socket方式。在系統和驅動程式之間定義有專門的資料結構(sk_buff)進行資料的傳遞。系統裡支援對傳送資料和接收資料的快取,提供流量控制機制,提供對多協議的支援。

在編寫模組的時候,應該注意,模組僅僅被連線到核心,所以它只能呼叫由核心匯出的那些函式,而不能呼叫其他的本模組未定義的函式。

在linux kernel2.6.x下進行模組開發時,需要預先準備好「kernel tree(核心樹)」,即獲得與本系統相同的核心的源**並編譯出目標檔案。

乙個最簡單的hello world驅動例子:

hello_world.c:

#include

#include

module_license("dual bsd/gpl");

static int hello_init(void)

printk(kern_alert "hello, world/n");

return 0;

static void hello_exit(void)

printk(kern_alert "goodbye, cruel world/n");

module_init(hello_init);

module_exit(hello_exit);

makefile:

obj-m := hello.o

kerneldir ?= /lib/modules/$(shell uname -r)/build

pwd := $(shell pwd)

default:

$(make) -c $(kerneldir) m=$(pwd) modules

其中,原始檔中的module_init和module_exit指定了模組被載入時執行的初始化函式和解除安裝時執行的清理函式。另外可以使用module_param指定載入模組時可以設定的引數。makefile中的obj-m指定了使用make modules時候構造*.ko目標檔案時使用的*.o目標檔案。

二、 linux網絡卡驅動基礎(參考《linux device drivers 3rd edition》)

當乙個模組被裝載到正在執行的核心中時,它要請求資源並提供一些功能設施,比如探測其裝置和硬體位置(i/o埠和irq線)、檢測到介面時向全域性網路裝置鍊錶中插入乙個net_device資料結構。

1.       分配net_device

alloc_etherdev: 初始化乙太網對應的介面,其中呼叫了alloc_netdev()和ether_setup()初始化了許多字段。常用。

alloc_fddidev: fddi裝置。

alloc_trdev: 令牌環裝置。

net_device中包括如下幾類資訊:

全域性資訊

硬體資訊(驅動內部使用的私有資料)

介面資訊

裝置方法(操作裝置的介面函式)

工具成員

2.       進一步初始化net_device

然後,需要根據硬體的特定需求進行進一步初始化。其中包括將net_device中的很多函式指標(裝置方法)指向我們自己定義的一些函式。如:

dev->hard_start_xmit = our_netdevice_function_tx; //發包函式

dev->get_stats = our_netdevice_function_stats; //統計函式

另外,本裝置需要用到的一些私有資料(不是所有的net device都有的資料)可以存放到dev->priv欄位指向的乙個自定義結構體中。

3.       註冊net_device

初始化完net_device後,可以呼叫register_netdev()註冊網路介面(其中會呼叫dev->init來進一步初始化net_device,所以可以將最後需要初始化的內容放到dev->init中去做),這時,就可以通過呼叫驅動程式操作裝置了。

4.       open和close

通過ifconfig開啟網路介面時,會呼叫dev->open。關閉網路介面時,會呼叫dev->close。

在open中,驅動程式要請求必要的系統資源,然後呼叫核心函式netif_start_queue()啟動傳輸佇列,通知核心可以通過網路裝置傳送資料報,允許介面開始傳送(transmit)資料報。

在close中,做與open相反的操作。其中包括netif_stop_queue()函式,通知核心停止通過裝置傳送(transmit)資料報。

5.       資料報傳送(transmit)

核心要傳送乙個資料報時,都會呼叫驅動中的dev->hard_start_transmit函式來將資料放入到外發佇列中。核心傳給驅動的資料報都位於乙個socket buffer(sk_buff,簡稱skb)中。skb是乙個複雜的資料結構,核心提供了很多函式操作它。但驅動中的dev->hard_start_transmit函式傳輸它時,不需要做任何修改,因為此時它已經有了完整的報頭。skb->data指向要傳輸的資料,skb->len是資料的長度(以位元組octet為單位)。

dev->hard_start_transmit需要呼叫硬體相關的函式來將資料傳送出去,如果傳輸成功,釋放skb,否則需要重新傳輸。

在傳輸過程中,如果硬體裝置的緩衝區即將耗盡,這時說明核心向裝置傳送資料報過快,這時驅動可以呼叫核心函式netif_stop_queue(),通知核心暫時停止向裝置傳送資料報。在未來某個時刻,可以通過netif_wake_queue()重新啟動佇列。(另外注意函式netif_tx_disable。)

傳輸超時:通過設定dev->watchdog_timeo的成員,可以設定傳輸超時時間,超過超時時間時,dev->tx_timeout函式會被呼叫。可以在這個函式裡重置傳輸狀態。

6.       資料報接收(receive)

接收資料比傳送資料稍微複雜一些,因為必須在原子上下文(gfp_atomic--kmalloc)中分配乙個sk_buff並傳遞給上層處理。有兩種模式接收資料報:中斷(interrupt)和輪詢(poll)。

在接收函式中,當接收到資料報時,首先通過dev_alloc_skb()函式分配skb,然後用memcpy將獲得的資料(資料報一般從硬體中獲得)拷貝到skb中(這裡有一種優化策略,是可以在資料報到達前分配skb,然後指示介面當資料報到達時直接放入skb,省去拷貝過程),然後更新net_device中的統計計數器,最後,通過netif_rx()函式將skb傳遞給上層軟體處理。

7.       關於介面中斷處理程式

介面裝置在兩種事件下產生中斷:新資料報到達,外發資料報傳輸完成。另外,當產生錯誤、連線狀態改變時也會產生中斷。

在中斷處理程式中,可以通過某種方法判斷出中斷型別,如果是新資料報到達或者外發資料報完成,需要做如下處理:

新資料報到達--中斷:呼叫接收函式。(上面可以看到,接收函式並不在net_device的裝置方法之中。)

外發資料報完成--中斷:呼叫dev_kfree_skb()函式釋放skb。

8.       不使用中斷接收資料,使用輪詢(napi)

在資料流量較大時,如果使用中斷方式接收資料報會使cpu負荷比較大。這時可以修改中斷處理程式,在收到乙個接收資料的中斷時,禁用中斷,然後啟用輪詢介面。

啟用輪詢介面時,應該呼叫netif_rx_schedule(dev)函式。這個函式會在以後的某個時間點呼叫dev->poll函式。

dev->poll函式的原型如下:

int (*poll) (struct net_device* dev, int* budget);

在這個函式中,將skb交給上層處理應該呼叫函式netif_receive_skb()而不是netif_rx()。結束輪詢時呼叫netif_rx_complete()函式。

9.       鏈路狀態

核心需要通過驅動程式了解網路鏈路是否正常。硬體能感知到線路上的載波訊號是否正常,驅動程式檢測到硬體上的載波訊號狀態發生改變時,可以通過下面的核心函式通知核心:

netif_carrier_off(dev): 通知核心載波消失,鏈路不能正常工作。

netif_carrier_on(dev): 通知核心載波出現。

10.   sk_buff詳述(參見)

11.   模組解除安裝

先呼叫unregister_netdev(),然後呼叫free_netdev()釋放net_device。

linux驅動學習筆記

1.先從最簡單的例子開始 include include module license dual bsd gpl static int hello init void printk kern alert hello,world n return 0 static void hello exit vo...

wince驅動開發學習筆記

因為課題前期調研沒做好,用的cpu板卡和資料採集卡來自兩個部門。加上買的是裸板,自己定製的os,技術支援不愛搭理。所以給的ai板卡的驅動一直裝不上,自己在鬱悶中尋找答案,就紮進了wince驅動的知識庫裡了。不管驅動裝不上是不是這個原因,學點東西總沒有壞處。做點記錄備查 首先是wince驅動的分類問題...

windows CE驅動開發學習筆記

首先是wince驅動的分類問題。按照書上講的說ce下驅動分成單體驅動和分層驅動,而看到另一種說法是本機驅動和流式驅動。經過microsun大哥的指點,把這兩種分類法分開了。在這裡引用一下 單體與分層只是從 的形式上做的分類.分層驅動 上分為pdd與mdd,一般的微軟已經實現了mdd,可能也實現了pd...