国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

這篇具有很好參考價(jià)值的文章主要介紹了I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

引言

??網(wǎng)絡(luò)驅(qū)動(dòng)是 linux 里面驅(qū)動(dòng)三巨頭之一,linux 下的網(wǎng)絡(luò)功能非常強(qiáng)大,嵌入式 linux 中也常常用到網(wǎng)絡(luò)功能。前面我們已經(jīng)講過了字符設(shè)備驅(qū)動(dòng)和塊設(shè)備驅(qū)動(dòng),本章我們就來學(xué)習(xí)一下 linux 里面的網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)。

一、Linux網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的結(jié)構(gòu)

??網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序的體系結(jié)構(gòu)分為4層,依次為網(wǎng)絡(luò)協(xié)議驅(qū)動(dòng)層、網(wǎng)絡(luò)設(shè)備接口層、設(shè)備驅(qū)動(dòng)功能層、網(wǎng)絡(luò)設(shè)備與媒介層。

??(1)網(wǎng)絡(luò)協(xié)議接口層向網(wǎng)絡(luò)層協(xié)議提供統(tǒng)一的數(shù)據(jù)包收發(fā)接口,不論上層協(xié)議是ARP還是IP,都通過dev_queue_xmit函數(shù)發(fā)送數(shù)據(jù)、并通過netif_rx函數(shù)接收數(shù)據(jù)。這一層的存在使得上層協(xié)議獨(dú)立于具體的設(shè)備。

??(2)網(wǎng)絡(luò)設(shè)備接口層向協(xié)議接口層提供統(tǒng)一的用于描述具體網(wǎng)絡(luò)設(shè)備屬性和操作的結(jié)構(gòu)體net_device,該結(jié)構(gòu)體是設(shè)備驅(qū)動(dòng)功能層中的各函數(shù)的容器。實(shí)際上,網(wǎng)絡(luò)設(shè)備接口層從宏觀上規(guī)劃了具體操作硬件的設(shè)備驅(qū)動(dòng)功能層的結(jié)構(gòu)。

??(3)設(shè)備驅(qū)動(dòng)功能層的各函數(shù)是網(wǎng)絡(luò)設(shè)備接口層net_device數(shù)據(jù)結(jié)構(gòu)的具體成員,是驅(qū)使網(wǎng)絡(luò)設(shè)備硬件完成相應(yīng)動(dòng)作的程序,它通過hard_start_xmit函數(shù)啟動(dòng)發(fā)送操作,并通過網(wǎng)絡(luò)設(shè)備上的中斷觸發(fā)接收操作。

??(4)網(wǎng)絡(luò)設(shè)備與媒介層是完成數(shù)據(jù)包發(fā)送和接收的物理實(shí)體,包括網(wǎng)絡(luò)適配器和具體的傳輸媒介,網(wǎng)絡(luò)適配器被設(shè)備驅(qū)動(dòng)功能層中的函數(shù)在物理上驅(qū)動(dòng),對(duì)于linux系統(tǒng)而言,網(wǎng)絡(luò)設(shè)備和媒介都可以是虛擬的。

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

??在設(shè)計(jì)具體的網(wǎng)絡(luò)驅(qū)動(dòng)程序時(shí),我們需要完成的主要工作是編寫設(shè)備驅(qū)動(dòng)功能層的相關(guān)函數(shù)以填充net_device數(shù)據(jù)結(jié)構(gòu)的內(nèi)容并將net_device注冊(cè)入內(nèi)核。

二、網(wǎng)絡(luò)協(xié)議接口層

??網(wǎng)絡(luò)協(xié)議接口層最主要的功能是給上層協(xié)議提供透明的數(shù)據(jù)包發(fā)送和接收接口。當(dāng)上層ARP或IP需要發(fā)送數(shù)據(jù)包時(shí),它將調(diào)用網(wǎng)絡(luò)接口層的dev_queue_xmit函數(shù)發(fā)送改數(shù)據(jù)包。同樣地,上層對(duì)數(shù)據(jù)包的接收也通過向netif_rx函數(shù)傳遞struct sk_buff數(shù)據(jù)結(jié)構(gòu)的指針來完成。

1、dev_queue_xmit 函數(shù)

此函數(shù)用于將網(wǎng)絡(luò)數(shù)據(jù)發(fā)送出去,函數(shù)定義在 include/linux/netdevice.h 中,函數(shù)原型如下:

static inline int dev_queue_xmit(struct sk_buff *skb)

函數(shù)參數(shù)和返回值含義如下:

??skb:要發(fā)送的數(shù)據(jù),這是一個(gè) sk_buff 結(jié)構(gòu)體指針,sk_buff 是 Linux 網(wǎng)絡(luò)驅(qū)動(dòng)中一個(gè)非常重要的結(jié)構(gòu)體,網(wǎng)絡(luò)數(shù)據(jù)就是以 sk_buff 保存的,各個(gè)協(xié)議層在 sk_buff 中添加自己的協(xié)議頭,最終由底層驅(qū)動(dòng)講 sk_buff 中的數(shù)據(jù)發(fā)送出去。網(wǎng)絡(luò)數(shù)據(jù)的接收過程恰好相反,網(wǎng)絡(luò)底層驅(qū)動(dòng)將接收到的原始數(shù)據(jù)打包成 sk_buff,然后發(fā)送給上層協(xié)議,上層會(huì)取掉相應(yīng)的頭部,然后將最終的數(shù)據(jù)發(fā)送給用戶。

??返回值:0 發(fā)送成功,負(fù)值發(fā)送失敗。

2、netif_rx 函數(shù)

??上層接收數(shù)據(jù)的話使用 netif_rx 函數(shù),但是最原始的網(wǎng)絡(luò)數(shù)據(jù)一般是通過輪詢、中斷或 NAPI的方式來接收。netif_rx 函數(shù)定義在 net/core/dev.c 中,函數(shù)原型如下:

int netif_rx(struct sk_buff *skb)

函數(shù)參數(shù)和返回值含義如下:
??skb:保存接收數(shù)據(jù)的 sk_buff。

??返回值:NET_RX_SUCCESS 成功,NET_RX_DROP 數(shù)據(jù)包丟棄。

struct sk_buff {
	...
	unsigned char *head;
	unsigned char *data;
	unsigned char *tail;
	unsigned char *end;
	...
}

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

??sk_buff結(jié)構(gòu)體中,尤其注意的是,head和end指向緩沖區(qū)的頭部和尾部,而data和tail指向?qū)嶋H數(shù)據(jù)的頭部和尾部。

??針對(duì)sk_buff,內(nèi)核提供了一系列的操作與管理函數(shù),我們簡單看一些常見的 API 函數(shù):

3、分配 sk_buff

??要使用 sk_buff 必須先分配,首先來看一下 alloc_skb 這個(gè)函數(shù),此函數(shù)定義在include/linux/skbuff.h 中,函數(shù)原型如下:

static inline struct sk_buff *alloc_skb(unsigned int size,gfp_t priority)

函數(shù)參數(shù)和返回值含義如下:

??size:要分配的大小,也就是 skb 數(shù)據(jù)段大小。

??priority:內(nèi)存分配優(yōu)先級(jí),為 GFP MASK 宏,比如 GFP_KERNEL、GFP_ATOMIC 等。

??返回值:分配成功的話就返回申請(qǐng)到的 sk_buff 首地址,失敗的話就返回 NULL。

??在網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)中常常使用 netdev_alloc_skb 來為某個(gè)設(shè)備申請(qǐng)一個(gè)用于接收的 skb_buff,此函數(shù)也定義在 include/linux/skbuff.h 中,函數(shù)原型如下:

static inline struct sk_buff *netdev_alloc_skb(struct net_device *dev,unsigned int length)

函數(shù)參數(shù)和返回值含義如下:

??dev:要給哪個(gè)設(shè)備分配 sk_buff。

??length:要分配的大小。

??返回值:分配成功的話就返回申請(qǐng)到的 sk_buff 首地址,失敗的話就返回 NULL。

??注意的是,該函數(shù)內(nèi)存分配優(yōu)先級(jí)設(shè)置為GFP_ATOMIC。原因是該函數(shù)經(jīng)常在設(shè)備驅(qū)動(dòng)的接收中斷里被調(diào)用。

4、釋放 sk_buff

??當(dāng)使用完成以后就要釋放掉 sk_buff,釋放函數(shù)可以使用 kfree_skb,函數(shù)定義在include/linux/skbuff.c 中,函數(shù)原型如下:

void kfree_skb(struct sk_buff *skb)

函數(shù)參數(shù)和返回值含義如下:

??skb:要釋放的 sk_buff。

??返回值:無。

??對(duì)于網(wǎng)絡(luò)設(shè)備而言最好使用如下所示釋放函數(shù):

void dev_kfree_skb (struct sk_buff *skb)

??函數(shù)只要一個(gè)參數(shù) skb,就是要釋放的 sk_buff。

5、skb_put、skb_push、sbk_pull 和 skb_reserve

這四個(gè)函數(shù)用于變更 sk_buff,先來看一下 skb_put 函數(shù),此函數(shù)用于在尾部擴(kuò)展 skb_buff的數(shù)據(jù)區(qū),也就將 skb_buff 的 tail 后移 n 個(gè)字節(jié),從而導(dǎo)致 skb_buff 的 len 增加 n 個(gè)字節(jié),原型如下:

unsigned char *skb_put(struct sk_buff *skb, unsigned int len)

函數(shù)參數(shù)和返回值含義如下:

??skb:要操作的 sk_buff。

??len:要增加多少個(gè)字節(jié)。

??返回值:擴(kuò)展出來的那一段數(shù)據(jù)區(qū)首地址。

skb_put 操作之前和操作之后的數(shù)據(jù)區(qū)如下圖所示:

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

skb_push 函數(shù)用于在頭部擴(kuò)展 skb_buff 的數(shù)據(jù)區(qū),函數(shù)原型如下所示:

unsigned char *skb_push(struct sk_buff *skb, unsigned int len)

函數(shù)參數(shù)和返回值含義如下:

??skb:要操作的 sk_buff。

??len:要增加多少個(gè)字節(jié)。

??返回值:擴(kuò)展完成以后新的數(shù)據(jù)區(qū)首地址。

skb_push 操作之前和操作之后的數(shù)據(jù)區(qū)如下圖所示:

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

sbk_pull 函數(shù)用于從 sk_buff 的數(shù)據(jù)區(qū)起始位置刪除數(shù)據(jù),函數(shù)原型如下所示:

unsigned char *skb_pull(struct sk_buff *skb, unsigned int len)

函數(shù)參數(shù)和返回值含義如下:

??skb:要操作的 sk_buff。

??len:要?jiǎng)h除的字節(jié)數(shù)。

??返回值:刪除以后新的數(shù)據(jù)區(qū)首地址。

skb_pull 操作之前和操作之后的數(shù)據(jù)區(qū)如下圖所示:

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

sbk_reserve 函數(shù)用于調(diào)整緩沖區(qū)的頭部大小,方法很簡單講 skb_buff 的 data 和 tail 同時(shí)后移 n 個(gè)字節(jié)即可,函數(shù)原型如下所示:

static inline void skb_reserve(struct sk_buff *skb, int len)

函數(shù)參數(shù)和返回值含義如下:

??skb:要操作的 sk_buff。

??len:要增加的緩沖區(qū)頭部大小。

??返回值:無。

三、網(wǎng)絡(luò)設(shè)備接口層

??網(wǎng)絡(luò)設(shè)備接口層的主要功能是千變?nèi)f化的網(wǎng)絡(luò)設(shè)備定義統(tǒng)一、抽象的數(shù)據(jù)結(jié)構(gòu)net_device 結(jié)構(gòu)體,以不變應(yīng)萬變,實(shí)現(xiàn)多種硬件在軟件層次上的統(tǒng)一。

??net_device 結(jié)構(gòu)體在內(nèi)核中指代一個(gè)網(wǎng)絡(luò)設(shè)備,網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序只需要填充net_device 的具體成員并注冊(cè) net_device 即可實(shí)現(xiàn)硬件操作函數(shù)與內(nèi)核的掛接。

net_device 是一個(gè)龐大的結(jié)構(gòu)體,介紹一些關(guān)鍵成員:

成員 說明
name 網(wǎng)絡(luò)設(shè)備的名字
mem_end 共享內(nèi)存結(jié)束地址
mem_start 共享內(nèi)存起始地址
base_addr 網(wǎng)絡(luò)設(shè)備 I/O 地址
irq 網(wǎng)絡(luò)設(shè)備的中斷號(hào)
netdev_ops 網(wǎng)絡(luò)設(shè)備的操作集函數(shù),類似字符設(shè)備中的 file_operations
ethtool_ops 網(wǎng)絡(luò)管理工具相關(guān)函數(shù)集,用戶空間網(wǎng)絡(luò)管理工具會(huì)調(diào)用此結(jié)構(gòu)體中的相關(guān)函數(shù)獲取網(wǎng)卡狀態(tài)或者配置網(wǎng)卡
header_ops 頭部的相關(guān)操作函數(shù)集,比如創(chuàng)建、解析、緩沖等
dma 網(wǎng)絡(luò)設(shè)備所使用的 DMA 通道
mtu 網(wǎng)絡(luò)最大傳輸單元
last_rx 最后接收的數(shù)據(jù)包時(shí)間戳,記錄的是 jiffies
dev_addr 當(dāng)前分配的 MAC 地址,可以通過軟件修改
_rx 接收隊(duì)列
num_rx_queues 接收隊(duì)列數(shù)量,調(diào)用 register_netdev 時(shí)會(huì)分配指定數(shù)量的接收隊(duì)列
real_num_rx_queues 當(dāng)前活動(dòng)的隊(duì)列數(shù)量
_tx 發(fā)送隊(duì)列
num_tx_queues 發(fā)送隊(duì)列數(shù)量,通過 alloc_netdev_mq 函數(shù)分配指定數(shù)量的發(fā)送隊(duì)列
real_num_tx_queues 當(dāng)前有效的發(fā)送隊(duì)列數(shù)量
trans_start 最后的數(shù)據(jù)包發(fā)送的時(shí)間戳,記錄的是 jiffies
phydev 對(duì)應(yīng)的 PHY 設(shè)備

1、申請(qǐng) net_device

編寫網(wǎng)絡(luò)驅(qū)動(dòng)的時(shí)候首先要申請(qǐng) net_device,使用 alloc_netdev 函數(shù)來申請(qǐng) net_device,這是一個(gè)宏,宏定義如下:

#define alloc_netdev(sizeof_priv, name, name_assign_type, setup) \
??alloc_netdev_mqs(sizeof_priv, name, name_assign_type, setup, 1, 1)

可以看出 alloc_netdev 的本質(zhì)是 alloc_netdev_mqs 函數(shù),此函數(shù)原型如下:

struct net_device * alloc_netdev_mqs ( int sizeof_priv, const char *name,
??????????????????void (*setup) (struct net_device *)),
??????????????????unsigned int txqs, unsigned int rxqs);

函數(shù)參數(shù)和返回值含義如下:

??sizeof_priv:私有數(shù)據(jù)塊大小。

??name:設(shè)備名字。

??setup:回調(diào)函數(shù),初始化設(shè)備的設(shè)備后調(diào)用此函數(shù)。

??txqs:分配的發(fā)送隊(duì)列數(shù)量。

??rxqs:分配的接收隊(duì)列數(shù)量。

??返回值:如果申請(qǐng)成功的話就返回申請(qǐng)到的 net_device 指針,失敗的話就返回 NULL。

??事實(shí)上網(wǎng)絡(luò)設(shè)備有多種,大家不要以為就只有以太網(wǎng)一種。Linux 內(nèi)核內(nèi)核支持的網(wǎng)絡(luò)接口有很多,比如光纖分布式數(shù)據(jù)接口(FDDI)、以太網(wǎng)設(shè)備(Ethernet)、紅外數(shù)據(jù)接口(InDA)、高性能并行接口(HPPI)、CAN 網(wǎng)絡(luò)等。內(nèi)核針對(duì)不同的網(wǎng)絡(luò)設(shè)備在 alloc_netdev 的基礎(chǔ)上提供了一層封裝,比如我們本章講解的以太網(wǎng),針對(duì)以太網(wǎng)封裝的 net_device 申請(qǐng)函數(shù)是 alloc_etherdev,這也是一個(gè)宏,內(nèi)容如下:

#define alloc_etherdev(sizeof_priv)??alloc_etherdev_mq(sizeof_priv, 1)
#define alloc_etherdev_mq(sizeof_priv, count)??alloc_etherdev_mqs(sizeof_priv, count, count)

??可以看出,alloc_etherdev 最終依靠的是 alloc_etherdev_mqs 函數(shù),此函數(shù)就是對(duì)alloc_netdev_mqs 的簡單封裝,函數(shù)內(nèi)容如下:

struct net_device *alloc_etherdev_mqs(int sizeof_priv, unsigned int txqs, unsigned int rxqs) 
{ 
	return alloc_netdev_mqs(sizeof_priv, "eth%d", NET_NAME_UNKNOWN, ether_setup, txqs, rxqs);
}

??調(diào)用 alloc_netdev_mqs 來申請(qǐng) net_device,注意這里設(shè)置網(wǎng)卡的名字為“eth%d”,這是格式化字符串,大家進(jìn)入開發(fā)板的 linux 系統(tǒng)以后看到的“eth0”、“eth1”這樣的網(wǎng)卡名字就是從這里來的。同樣的,這里設(shè)置了以太網(wǎng)的 setup 函數(shù)為 ether_setup,不同的網(wǎng)絡(luò)設(shè)備其 setup函數(shù)不同,比如 CAN 網(wǎng)絡(luò)里面 setup 函數(shù)就是 can_setup。

ether_setup 函數(shù)會(huì)對(duì) net_device 做初步的初始化,函數(shù)內(nèi)容如下所示:

void ether_setup(struct net_device *dev) 
{ 
	dev->header_ops = &eth_header_ops; 
	dev->type = ARPHRD_ETHER; 
	dev->hard_header_len = ETH_HLEN; 
	dev->mtu = ETH_DATA_LEN; 
	dev->addr_len = ETH_ALEN; 
	dev->tx_queue_len = 1000; /* Ethernet wants good queues */
	dev->flags = IFF_BROADCAST|IFF_MULTICAST;
	dev->priv_flags |= IFF_TX_SKB_SHARING;
	
	eth_broadcast_addr(dev->broadcast);
}

2、刪除 net_device

當(dāng)我們注銷網(wǎng)絡(luò)驅(qū)動(dòng)的時(shí)候需要釋放掉前面已經(jīng)申請(qǐng)到的 net_device,釋放函數(shù)為free_netdev,函數(shù)原型如下:

void free_netdev(struct net_device *dev)

函數(shù)參數(shù)和返回值含義如下:

??dev:要釋放掉的 net_device 指針。

??返回值:無。

3、注冊(cè) net_device

net_device 申請(qǐng)并初始化完成以后就需要向內(nèi)核注冊(cè) net_device,要用到函數(shù) register_netdev,函數(shù)原型如下:

int register_netdev(struct net_device *dev)

函數(shù)參數(shù)和返回值含義如下:

??dev:要注冊(cè)的 net_device 指針。

??返回值:0 注冊(cè)成功,負(fù)值 注冊(cè)失敗。

4、注銷 net_device

既然有注冊(cè),那么必然有注銷,注銷 net_device 使用函數(shù) unregister_netdev,函數(shù)原型如下:

void unregister_netdev(struct net_device *dev)

函數(shù)參數(shù)和返回值含義如下:

??dev:要注銷的 net_device 指針。

??返回值:無。

四、網(wǎng)絡(luò) NAPI 處理機(jī)制

??NAPI 是一種高效的網(wǎng)絡(luò)處理技術(shù)。NAPI 的核心思想就是不全部采用中斷來讀取網(wǎng)絡(luò)數(shù)據(jù),而是采用中斷來喚醒數(shù)據(jù)接收服務(wù)程序,在接收服務(wù)程序中采用 POLL 的方法來輪詢處理數(shù)據(jù)。這種方法的好處就是可以提高短數(shù)據(jù)包的接收效率,減少中斷處理的時(shí)間。目前 NAPI 已經(jīng)在 Linux 的網(wǎng)絡(luò)驅(qū)動(dòng)中得到了大量的應(yīng)用,NXP 官方編寫的網(wǎng)絡(luò)驅(qū)動(dòng)都是采用的 NAPI 機(jī)制。

1、初始化 NAPI

首先要初始化一個(gè) napi_struct 實(shí)例,使用 netif_napi_add 函數(shù),此函數(shù)定義在 net/core/dev.c中,函數(shù)原型如下:

void netif_napi_add(struct net_device *dev, struct napi_struct *napi,int (*poll)(struct napi_struct *, int), int weight)

函數(shù)參數(shù)和返回值含義如下:

??dev:每個(gè) NAPI 必須關(guān)聯(lián)一個(gè)網(wǎng)絡(luò)設(shè)備,此參數(shù)指定 NAPI 要關(guān)聯(lián)的網(wǎng)絡(luò)設(shè)備。

??napi:要初始化的 NAPI 實(shí)例。

??poll:NAPI 所使用的輪詢函數(shù),非常重要,一般在此輪詢函數(shù)中完成網(wǎng)絡(luò)數(shù)據(jù)接收的工作。

??weight:NAPI 默認(rèn)權(quán)重(weight),一般為 NAPI_POLL_WEIGHT。

??返回值:無。

2、刪除 NAPI

如果要?jiǎng)h除 NAPI,使用 netif_napi_del 函數(shù)即可,函數(shù)原型如下:

void netif_napi_del(struct napi_struct *napi)

函數(shù)參數(shù)和返回值含義如下:

??napi:要?jiǎng)h除的 NAPI。

??返回值:無。

3、使能 NAPI

初始化完 NAPI 以后,必須使能才能使用,使用函數(shù) napi_enable,函數(shù)原型如下:

inline void napi_enable(struct napi_struct *n)

函數(shù)參數(shù)和返回值含義如下:

??n:要使能的 NAPI。

??返回值:無。

4、關(guān)閉 NAPI

關(guān)閉 NAPI 使用 napi_disable 函數(shù)即可,函數(shù)原型如下:

void napi_disable(struct napi_struct *n)

函數(shù)參數(shù)和返回值含義如下:

??n:要關(guān)閉的 NAPI。

??返回值:無。

5、檢查 NAPI 是否可以進(jìn)行調(diào)度

使用 napi_schedule_prep 函數(shù)檢查 NAPI 是否可以進(jìn)行調(diào)度,函數(shù)原型如下:

inline bool napi_schedule_prep(struct napi_struct *n)

函數(shù)參數(shù)和返回值含義如下:

??n:要檢查的 NAPI。

??返回值:如果可以調(diào)度就返回真,如果不可調(diào)度就返回假。

6、NAPI 調(diào)度

如果可以調(diào)度的話就進(jìn)行調(diào)度,使用__napi_schedule 函數(shù)完成 NAPI 調(diào)度,函數(shù)原型如下:

void __napi_schedule(struct napi_struct *n)

函數(shù)參數(shù)和返回值含義如下:

??n:要調(diào)度的 NAPI。

??返回值:無。

??我們也可以使用 napi_schedule 函數(shù)來一次完成 napi_schedule_prep 和__napi_schedule 這兩個(gè)函數(shù)的工作,napi_schedule 函數(shù)內(nèi)容如下所示:

static inline void napi_schedule(struct napi_struct *n) 
{ 
	if (napi_schedule_prep(n))
		__napi_schedule(n);
}

??從示例代碼可以看出,napi_schedule 函數(shù)就是對(duì)napi_schedule_prep和__napi_schedule 的簡單封裝,一次完成判斷和調(diào)度。

7、NAPI 處理完成

NAPI 處理完成以后需要調(diào)用 napi_complete 函數(shù)來標(biāo)記 NAPI 處理完成,函數(shù)原型如下:

inline void napi_complete(struct napi_struct *n)

函數(shù)參數(shù)和返回值含義如下:

??n:處理完成的 NAPI。

??返回值:無。

五、設(shè)備驅(qū)動(dòng)功能層

??net_device結(jié)構(gòu)體的成員(屬性和net_device_ops結(jié)構(gòu)體中的函數(shù)指針)需要被設(shè)備驅(qū)動(dòng)功能層賦予具體的數(shù)值和函數(shù)。對(duì)于具體的設(shè)備xxx,應(yīng)該編寫相應(yīng)的設(shè)備驅(qū)動(dòng)功能層函數(shù),這些函數(shù)如xxx_open()、xxx_stop()等。

??net_device_ops 結(jié)構(gòu)體定義在 include/linux/netdevice.h 文件中,net_device_ops 結(jié)構(gòu)體里面都是一些以“ndo_”開頭的函數(shù),這些函數(shù)就需要網(wǎng)絡(luò)驅(qū)動(dòng)編寫人員去實(shí)現(xiàn),不需要全部都實(shí)現(xiàn),根據(jù)實(shí)際驅(qū)動(dòng)情況實(shí)現(xiàn)其中一部分即可。結(jié)構(gòu)體內(nèi)容如下所示(結(jié)構(gòu)體比較大,這里有縮減):

struct net_device_ops { 
	int (*ndo_init)(struct net_device *dev);
	void (*ndo_uninit)(struct net_device *dev);
	int (*ndo_open)(struct net_device *dev);
	int (*ndo_stop)(struct net_device *dev);
	netdev_tx_t (*ndo_start_xmit) (struct sk_buff *skb, struct net_device *dev);
	u16 (*ndo_select_queue)(struct net_device *dev, struct sk_buff *skb,void *accel_priv,select_queue_fallback_t fallback);
	void (*ndo_change_rx_flags)(struct net_device *dev,int flags);
	void (*ndo_set_rx_mode)(struct net_device *dev);
	int (*ndo_set_mac_address)(struct net_device *dev,void *addr);
	int (*ndo_validate_addr)(struct net_device *dev);
	int (*ndo_do_ioctl)(struct net_device *dev,struct ifreq *ifr, int cmd);
	int (*ndo_set_config)(struct net_device *dev,struct ifmap *map);
	int (*ndo_change_mtu)(struct net_device *dev,int new_mtu);
	int (*ndo_neigh_setup)(struct net_device *dev,
	struct neigh_parms *);
	void (*ndo_tx_timeout) (struct net_device *dev);
	......
	#ifdef CONFIG_NET_POLL_CONTROLLER
		void (*ndo_poll_controller)(struct net_device *dev);
		int (*ndo_netpoll_setup)(struct net_device *dev,
		struct netpoll_info *info);
		void (*ndo_netpoll_cleanup)(struct net_device *dev);
	#endif
	......
	int (*ndo_set_features)(struct net_device *dev,netdev_features_t features);
	......
};

??fec_probe 函數(shù)設(shè)置了網(wǎng)卡驅(qū)動(dòng)的 net_dev_ops 操作集為 fec_netdev_ops,fec_netdev_ops 內(nèi)容如下:

static const struct net_device_ops fec_netdev_ops = { 
	.ndo_open = fec_enet_open, 
	.ndo_stop = fec_enet_close, 
	.ndo_start_xmit = fec_enet_start_xmit, 
	.ndo_select_queue = fec_enet_select_queue, 
	.ndo_set_rx_mode = set_multicast_list, 
	.ndo_change_mtu = eth_change_mtu, 
	ndo_validate_addr = eth_validate_addr, 
	ndo_tx_timeout = fec_timeout,
	.ndo_set_mac_address = fec_set_mac_address,
	.ndo_do_ioctl = fec_enet_ioctl,
	#ifdef CONFIG_NET_POLL_CONTROLLER
		.ndo_poll_controller = fec_poll_controller,
	#endif
		.ndo_set_features = fec_set_features,
};

1、fec_enet_open 函數(shù)簡析

打開一個(gè)網(wǎng)卡的時(shí)候 fec_enet_open 函數(shù)就會(huì)執(zhí)行,函數(shù)源碼如下所示(限于篇幅原因,有省略):

static int fec_enet_open(struct net_device *ndev)
{ 
	struct fec_enet_private *fep = netdev_priv(ndev);
	const struct platform_device_id *id_entry = platform_get_device_id(fep->pdev);
	int ret; 
	pinctrl_pm_select_default_state(&fep->pdev->dev);
	ret = fec_enet_clk_enable(ndev, true);
	if (ret)
		return ret;

	/* I should reset the ring buffers here, but I don't yet know
	* a simple way to do that.
	*/

	ret = fec_enet_alloc_buffers(ndev);
	if (ret)
		goto err_enet_alloc;

	/* Init MAC prior to mii bus probe */
	fec_restart(ndev);

	/* Probe and connect to PHY when open the interface */
	ret = fec_enet_mii_probe(ndev);
	if (ret)
		goto err_enet_mii_probe;

	napi_enable(&fep->napi);
	phy_start(fep->phy_dev);
	netif_tx_start_all_queues(ndev);

	......

	return 0;

	err_enet_mii_probe:
		fec_enet_free_buffers(ndev);
	err_enet_alloc:
		fep->miibus_up_failed = true;
		if (!fep->mii_bus_share)
			pinctrl_pm_select_sleep_state(&fep->pdev->dev);
	return ret;
}

第 9 行,調(diào)用 fec_enet_clk_enable 函數(shù)使能 enet 時(shí)鐘。
第 17 行,調(diào)用 fec_enet_alloc_buffers 函數(shù)申請(qǐng)環(huán)形緩沖區(qū) buffer,此函數(shù)里面會(huì)調(diào)用fec_enet_alloc_rxq_buffers 和 fec_enet_alloc_txq_buffers 這兩個(gè)函數(shù)分別實(shí)現(xiàn)發(fā)送隊(duì)列和接收隊(duì)列緩沖區(qū)的申請(qǐng)。
第 22 行,重啟網(wǎng)絡(luò),一般連接狀態(tài)改變、傳輸超時(shí)或者配置網(wǎng)絡(luò)的時(shí)候都會(huì)調(diào)用 fec_restart函數(shù)。
第 25 行,打開網(wǎng)卡的時(shí)候調(diào)用 fec_enet_mii_probe 函數(shù)來探測并連接對(duì)應(yīng)的 PHY 設(shè)備。
第 29 行,調(diào)用 napi_enable 函數(shù)使能 NAPI 調(diào)度。
第 30 行,調(diào)用 phy_start 函數(shù)開啟 PHY 設(shè)備。
第 31 行,調(diào)用 netif_tx_start_all_queues 函數(shù)來激活發(fā)送隊(duì)列。

2、fec_enet_close 函數(shù)簡析

關(guān)閉網(wǎng)卡的時(shí)候 fec_enet_close 函數(shù)就會(huì)執(zhí)行,函數(shù)內(nèi)容如下:

static int fec_enet_close(struct net_device *ndev) 
{ 
	struct fec_enet_private *fep = netdev_priv(ndev);
	phy_stop(fep->phy_dev);
	if (netif_device_present(ndev))
	{  
		napi_disable(&fep->napi);
		netif_tx_disable(ndev);
		fec_stop(ndev);
	}
	phy_disconnect(fep->phy_dev);
	fep->phy_dev = NULL;
	fec_enet_clk_enable(ndev, false);
	pm_qos_remove_request(&fep->pm_qos_req);
	pinctrl_pm_select_sleep_state(&fep->pdev->dev);
	pm_runtime_put_sync_suspend(ndev->dev.parent);
	fec_enet_free_buffers(ndev);
	return 0;
}

第 5 行,調(diào)用 phy_stop 函數(shù)停止 PHY 設(shè)備。
第 8 行,調(diào)用 napi_disable 函數(shù)關(guān)閉 NAPI 調(diào)度。
第 9 行,調(diào)用 netif_tx_disable 函數(shù)關(guān)閉 NAPI 的發(fā)送隊(duì)列。
第 10 行,調(diào)用 fec_stop 函數(shù)關(guān)閉 I.MX6ULL 的 ENET 外設(shè)。
第 13 行,調(diào)用 phy_disconnect 函數(shù)斷開與 PHY 設(shè)備的連接。
第 16 行,調(diào)用 fec_enet_clk_enable 函數(shù)關(guān)閉 ENET 外設(shè)時(shí)鐘。
第 20 行,調(diào)用 fec_enet_free_buffers 函數(shù)釋放發(fā)送和接收的環(huán)形緩沖區(qū)內(nèi)存。

3、fec_enet_start_xmit 函數(shù)簡析

I.MX6ULL 的網(wǎng)絡(luò)數(shù)據(jù)發(fā)送是通過 fec_enet_start_xmit 函數(shù)來完成的,這個(gè)函數(shù)將上層傳遞過來的 sk_buff 中的數(shù)據(jù)通過硬件發(fā)送出去,函數(shù)源碼如下:

static netdev_tx_t fec_enet_start_xmit(struct sk_buff *skb,struct net_device *ndev)  
{  
	struct fec_enet_private *fep = netdev_priv(ndev);
	int entries_free;  
	unsigned short queue;  
	struct fec_enet_priv_tx_q *txq;  
	struct netdev_queue *nq;  
	int ret;  
	queue = skb_get_queue_mapping(skb);
	txq = fep->tx_queue[queue];
	nq = netdev_get_tx_queue(ndev, queue);

	if (skb_is_gso(skb))
		ret = fec_enet_txq_submit_tso(txq, skb, ndev);
	else
		ret = fec_enet_txq_submit_skb(txq, skb, ndev);
	if (ret)
		return ret;

	entries_free = fec_enet_get_free_txdesc_num(fep, txq);
	if (entries_free <= txq->tx_stop_threshold)
		netif_tx_stop_queue(nq);

	return NETDEV_TX_OK;
}

此函數(shù)的參數(shù)第一個(gè)參數(shù) skb 就是上層應(yīng)用傳遞下來的要發(fā)送的網(wǎng)絡(luò)數(shù)據(jù),第二個(gè)參數(shù)ndev 就是要發(fā)送數(shù)據(jù)的設(shè)備。

第 14 行,判斷 skb 是否為 GSO(Generic Segmentation Offload),如果是 GSO 的話就通過fec_enet_txq_submit_tso 函數(shù)發(fā)送,如果不是的話就通過 fec_enet_txq_submit_skb 發(fā)送。這里簡單講一下 TSO 和 GSO:
TSO:全稱是 TCP Segmentation Offload,利用網(wǎng)卡對(duì)大數(shù)據(jù)包進(jìn)行自動(dòng)分段處理,降低 CPU負(fù)載。
GSO:全稱是 Generic Segmentation Offload,在發(fā)送數(shù)據(jù)之前先檢查一下網(wǎng)卡是否支持 TSO,如果支持的話就讓網(wǎng)卡分段,不過不支持的話就由協(xié)議棧進(jìn)行分段處理,分段處理完成以后再交給網(wǎng)卡去發(fā)送。
第 21 行,通過 fec_enet_get_free_txdesc_num 函數(shù)獲取剩余的發(fā)送描述符數(shù)量。
第 23 行,如果剩余的發(fā)送描述符的數(shù)量小于設(shè)置的閾值(tx_stop_threshold)的話就調(diào)用函數(shù)netif_tx_stop_queu 來暫停發(fā)送,通過暫停發(fā)送來通知應(yīng)用層停止向網(wǎng)絡(luò)發(fā)送 skb,發(fā)送中斷中會(huì)重新開啟的。

4、fec_enet_interrupt 中斷服務(wù)函數(shù)簡析

前面說了 I.MX6ULL 的網(wǎng)絡(luò)數(shù)據(jù)接收采用 NAPI 框架,所以肯定要用到中斷。fec_probe 函數(shù)會(huì)初始化網(wǎng)絡(luò)中斷,中斷服務(wù)函數(shù)為 fec_enet_interrupt,函數(shù)內(nèi)容如下:

static irqreturn_t fec_enet_interrupt(int irq, void *dev_id) 
{ 
	struct net_device *ndev = dev_id; 
	struct fec_enet_private *fep = netdev_priv(ndev);
	uint int_events; 
	irqreturn_t ret = IRQ_NONE;  
	int_events = readl(fep->hwp + FEC_IEVENT);
	writel(int_events, fep->hwp + FEC_IEVENT);
	fec_enet_collect_events(fep, int_events);

	if ((fep->work_tx || fep->work_rx) && fep->link) 
	{
		ret = IRQ_HANDLED;

		if (napi_schedule_prep(&fep->napi)) 
		{
			/* Disable the NAPI interrupts */
			writel(FEC_ENET_MII, fep->hwp + FEC_IMASK);
			__napi_schedule(&fep->napi);
		}
	}

	if (int_events & FEC_ENET_MII) 
	{
		ret = IRQ_HANDLED;
		complete(&fep->mdio_done);
	}

	if (fep->ptp_clock)
		fec_ptp_check_pps_event(fep);

	return ret;
}

??可以看出中斷服務(wù)函數(shù)非常短!而且也沒有見到有關(guān)數(shù)據(jù)接收的處理過程,那是因?yàn)镮.MX6ULL 的網(wǎng)絡(luò)驅(qū)動(dòng)使用了 NAPI,具體的網(wǎng)絡(luò)數(shù)據(jù)收發(fā)是在 NAPI 的 poll 函數(shù)中完成的,中斷里面只需要進(jìn)行 napi 調(diào)度即可,這個(gè)就是中斷的上半部和下半部處理機(jī)制。

第 8 行,讀取 NENT 的中斷狀態(tài)寄存器 EIR,獲取中斷狀態(tài),
第 9 行,將第 8 行獲取到的中斷狀態(tài)值又寫入 EIR 寄存器,用于清除中斷狀態(tài)寄存器。
第 10 行,調(diào)用 fec_enet_collect_events 函數(shù)統(tǒng)計(jì)中斷信息,也就是統(tǒng)計(jì)都發(fā)生了哪些中斷。fep 中成員變量 work_tx 和 work_rx 的 bit0、bit1 和 bit2 用來做不同的標(biāo)記,work_rx 的 bit2 表示接收到數(shù)據(jù)幀,work_tx 的 bit2 表示發(fā)送完數(shù)據(jù)幀。
第 15 行,調(diào)用 napi_schedule_prep 函數(shù)檢查 NAPI 是否可以進(jìn)行調(diào)度。
第 17 行,如果使能了相關(guān)中斷就要先關(guān)閉這些中斷,向 EIMR 寄存器的 bit23 寫 1 即可關(guān)閉相關(guān)中斷。
第 18 行,調(diào)用__napi_schedule函數(shù)來啟動(dòng) NAPI 調(diào)度,這個(gè)時(shí)候 napi 的 poll 函數(shù)就會(huì)執(zhí)行,在本網(wǎng)絡(luò)驅(qū)動(dòng)中就是 fec_enet_rx_napi 函數(shù)。

__napi_schedule函數(shù)被輪詢方式驅(qū)動(dòng)的中斷程序調(diào)用,將設(shè)備的poll方法添加到網(wǎng)絡(luò)層的poll處理隊(duì)列中,排隊(duì)并且準(zhǔn)備接收數(shù)據(jù)包,最終觸發(fā)一個(gè)NET_RX_SOFTIRQ軟中斷,從而通知網(wǎng)絡(luò)層接收數(shù)據(jù)包。

I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架

fec_enet_init 函數(shù)初始化網(wǎng)絡(luò)的時(shí)候會(huì)調(diào)用 netif_napi_add 來設(shè)置 NAPI 的 poll 函數(shù)為 fec_enet_rx_napi,函數(shù)內(nèi)容如下:

static int fec_enet_rx_napi(struct napi_struct *napi, int budget)
{ 
	struct net_device *ndev = napi->dev; 
	struct fec_enet_private *fep = netdev_priv(ndev);
	int pkts; 
	pkts = fec_enet_rx(ndev, budget);

	fec_enet_tx(ndev);

	if (pkts < budget) {
		napi_complete(napi);
		writel(FEC_DEFAULT_IMASK, fep->hwp + FEC_IMASK);
	}
	return pkts;
}

第 7 行,調(diào)用 fec_enet_rx 函數(shù)進(jìn)行真正的數(shù)據(jù)接收。
第 9 行,調(diào)用 fec_enet_tx 函數(shù)進(jìn)行數(shù)據(jù)發(fā)送。
第 12 行,調(diào)用 napi_complete 函數(shù)來宣布一次輪詢結(jié)束,
第 13 行,設(shè)置 ENET 的 EIMR 寄存器,重新使能中斷。文章來源地址http://www.zghlxwxcb.cn/news/detail-415862.html

到了這里,關(guān)于I.MX6ULL ARM驅(qū)動(dòng)開發(fā)---網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)框架的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 018——紅外遙控模塊驅(qū)動(dòng)開發(fā)(基于HS0038和I.MX6uLL)

    018——紅外遙控模塊驅(qū)動(dòng)開發(fā)(基于HS0038和I.MX6uLL)

    目錄 一、 模塊介紹 1.1 簡介 1.2 協(xié)議 二、 驅(qū)動(dòng)代碼 三、 應(yīng)用代碼 四、 實(shí)驗(yàn) 五、 程序優(yōu)化 ????????紅外遙控被廣泛應(yīng)用于家用電器、工業(yè)控制和智能儀器系統(tǒng)中,像我們熟知的有電視機(jī)盒子遙控器、空調(diào)遙控器。紅外遙控器系統(tǒng)分為發(fā)送端和接收端,如圖下圖所示。

    2024年04月16日
    瀏覽(21)
  • 017——DS18B20驅(qū)動(dòng)開發(fā)(基于I.MX6uLL)

    017——DS18B20驅(qū)動(dòng)開發(fā)(基于I.MX6uLL)

    目錄 一、 模塊介紹 1.1 簡介 1.2 主要特點(diǎn) 1.3 存儲(chǔ)器介紹 1.4 時(shí)序 1.5 命令 1.5.1 命令大全? ? 1.5.2 命令使用 1.5.3 使用示例 1.6 原理圖 二、 驅(qū)動(dòng)程序 三、 應(yīng)用程序 四、 測試 ????????DS18B20 溫度傳感器具有線路簡單、體積小的特點(diǎn),用來測量溫度非常簡單,在一根通信線上

    2024年04月12日
    瀏覽(24)
  • I.MX6ULL開發(fā)筆記(二)——硬件外設(shè)操作

    I.MX6ULL開發(fā)筆記(二)——硬件外設(shè)操作

    在文章http://t.csdnimg.cn/EGWt9中有介紹Linux下文件目錄,那么在Linux系統(tǒng)下,RGB燈也是一個(gè)設(shè)備,所以我們需要到 /sys 目錄下去操作這個(gè)設(shè)備。 之后,我們進(jìn)入到 class 目錄,這里掛載著開發(fā)板上的外設(shè): 在這里就能看到熟悉的硬件接口了,那么我們進(jìn)入到 leds 的目錄下: 可以看

    2024年01月24日
    瀏覽(25)
  • I.MX6ull UART

    I.MX6ull UART

    ?一 簡介 UART?全稱叫做串行接口,通常也叫做 COM 接口,串行接口指的是數(shù)據(jù)一個(gè)一個(gè)的順序傳輸,通信線路簡單。使用兩條線即可實(shí)現(xiàn)雙向通信,一條用于發(fā)送,一條用于接收。串口通 信距離遠(yuǎn) ,但是速 度相對(duì)會(huì)低 ,串口是一種很常用的工業(yè)接口。I.MX6U 自帶的 UART 外設(shè)

    2024年02月09日
    瀏覽(20)
  • 使用一根網(wǎng)線,讓Ubuntu和正點(diǎn)原子I.MX6ULL開發(fā)板互相ping通

    使用一根網(wǎng)線,讓Ubuntu和正點(diǎn)原子I.MX6ULL開發(fā)板互相ping通

    準(zhǔn)備一根網(wǎng)線即可 2.1 找根網(wǎng)線將I.MX6ULL和電腦連起來 2.2 讓I.MX6ULL通電運(yùn)行起來,我這里使用的是正點(diǎn)原子版本的內(nèi)核、 2.3 進(jìn)入電腦的網(wǎng)絡(luò)連接后,按照如下步驟操作 2.4 將ip地址、子網(wǎng)掩碼、默認(rèn)網(wǎng)關(guān)設(shè)置如下,= 注意,子網(wǎng)掩碼一定要是255.255.255.0 IP地址推薦使用192.168.5.

    2024年02月19日
    瀏覽(34)
  • I.MX6ull EPIT定時(shí)器

    I.MX6ull EPIT定時(shí)器

    一 簡介 EPIT定時(shí)器是一種增強(qiáng)的周期中斷定時(shí)器,完成周期性中斷定時(shí)的功能。 具有以下特點(diǎn) ?EPIT定時(shí)器是一個(gè)32位的定時(shí)器? 時(shí)鐘源可選的向下計(jì)數(shù)器??EPIT 共有 3 個(gè)時(shí)鐘源可選擇,ipg_clk、ipg_clk_32k 和 ipg_clk_highfreq ?當(dāng)計(jì)數(shù)值和比較值相等的時(shí)候產(chǎn)生中斷 ?12 位分頻器 對(duì)

    2024年02月08日
    瀏覽(25)
  • I.MX6ull GPT高精度定時(shí)器

    I.MX6ull GPT高精度定時(shí)器

    一 簡介 GPT的全稱是General Purpose Timer,它是一個(gè)32位的向上的定時(shí)器, GPT 定時(shí)器也可以跟一個(gè)值進(jìn)行比較,當(dāng)計(jì)數(shù)器值和這個(gè)值相等的話就發(fā)生比較事件,產(chǎn)生比較中斷。GPT 定時(shí)器有一個(gè) 12 位的分頻器,可以對(duì) GPT 定時(shí)器的時(shí)鐘源進(jìn)行分頻。 分析方式 同EPTI? 它具有以下特點(diǎn)

    2024年02月08日
    瀏覽(93)
  • 【Linux 裸機(jī)篇(五)】I.MX6ULL BSP工程管理下的 Makefile編寫、鏈接腳本

    【Linux 裸機(jī)篇(五)】I.MX6ULL BSP工程管理下的 Makefile編寫、鏈接腳本

    文件夾 描述 bsp 存放驅(qū)動(dòng)文件 imx6ul 存放跟芯片有關(guān)的文件,比如 NXP 官方的 SDK庫文件 obj 存放編譯生成的.o 文件 project 存放 start.S 和 main.c 文件,也就是應(yīng)用文件 行 描述 1~7 定義了一些變量,除了第 2 行以外其它的都是跟編譯器有關(guān)的,如果使用其它編譯器的話只需要修改第

    2023年04月20日
    瀏覽(26)
  • i.MX6ULL移植NXP官方Linux內(nèi)核imx_5.4.47_2.2.0

    i.MX6ULL移植NXP官方Linux內(nèi)核imx_5.4.47_2.2.0

    系統(tǒng):Ubuntu18.04 參考資料:百問網(wǎng) IMX6ULL開發(fā)板(從零移植篇-預(yù)覽版)-V0.1,正點(diǎn)原子驅(qū)動(dòng)開發(fā)指南 開發(fā)板:100ask i.MX6ULL PRO 交叉編譯工具鏈的獲取就不寫了 打開 .bashrc 文件。 vi ~/.bashrc 。在該文件最后面添加如下(根據(jù)自己的交叉編譯工具鏈) (1)直接從官網(wǎng)下載,非常慢而

    2024年02月12日
    瀏覽(95)
  • I.MX6ULL_Linux_驅(qū)動(dòng)篇(57)linux Regmap API驅(qū)動(dòng)

    I.MX6ULL_Linux_驅(qū)動(dòng)篇(57)linux Regmap API驅(qū)動(dòng)

    我們?cè)谇懊鎸W(xué)習(xí) I2C 和 SPI 驅(qū)動(dòng)的時(shí)候,針對(duì) I2C 和 SPI 設(shè)備寄存器的操作都是通過相關(guān)的 API 函數(shù)進(jìn)行操作的。這樣 Linux 內(nèi)核中就會(huì)充斥著大量的重復(fù)、冗余代碼,但是這些本質(zhì)上都是對(duì)寄存器的操作,所以為了方便內(nèi)核開發(fā)人員統(tǒng)一訪問 I2C/SPI 設(shè)備的時(shí)候,為此引入了 Reg

    2024年03月26日
    瀏覽(23)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包