無線網(wǎng)卡驅(qū)動(dòng)可以借助PCI,SDIO,UART,USB來用于總線通訊。MAC層可以用軟件來實(shí)現(xiàn),也可以用硬件來實(shí)現(xiàn)。一般有mac和phy組成。構(gòu)圖:
mac:一般用于數(shù)據(jù)的過濾
phy: 操作數(shù)據(jù)實(shí)際的收發(fā)。
wifi架構(gòu)圖
以USB接口的WIFI模塊進(jìn)行分析:
1、從USB總線的角度去看,它是USB設(shè)備
2、從Linux設(shè)備的分類上看,它又是網(wǎng)絡(luò)設(shè)備
3、從WIFI本身的角度去看,它又有自己獨(dú)特的功能及屬性,因此它又是一個(gè)私有的設(shè)備
只要抓住這三條線索深入去分析驅(qū)動(dòng),整個(gè)WIFI驅(qū)動(dòng)框架就會(huì)浮現(xiàn)在你眼前。
首先,現(xiàn)在我們先從USB設(shè)備開始,要寫一個(gè)USB設(shè)備驅(qū)動(dòng),那么大致步驟如下:
1、需要針對(duì)該設(shè)備定義一個(gè)USB驅(qū)動(dòng),對(duì)應(yīng)到代碼中即定義一個(gè)usb_driver結(jié)構(gòu)體變量。
2、填充該設(shè)備的usb_driver結(jié)構(gòu)體成員變量
3、將該驅(qū)動(dòng)注冊(cè)到USB子系統(tǒ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è)備和媒介都可以是虛擬的。
下面對(duì)這四層做一個(gè)較為詳細(xì)解釋:
網(wǎng)絡(luò)協(xié)議接口層最主要的功能是給上層協(xié)議提供了透明的數(shù)據(jù)包發(fā)送和接收接口。當(dāng)上層 ARP 或 IP 協(xié) 議需要發(fā)送數(shù)據(jù) 包時(shí),它將調(diào)用網(wǎng)絡(luò)協(xié)議 接口層的dev_queue_xmit()函數(shù)發(fā)送該數(shù)據(jù)包,同時(shí)需傳遞給該函數(shù)一個(gè)指向 struct sk_buff 數(shù)據(jù)結(jié)構(gòu)的指針。dev_queue_xmit()函數(shù)的原型為:
dev_queue_xmit (struct sk_buff * skb );
同樣地,上層對(duì)數(shù)據(jù)包的接收也通過向 netif_rx()函數(shù)傳遞一個(gè) struct sk_buff 數(shù)據(jù)結(jié)構(gòu)的指針來完成。netif_rx()函數(shù)的原型為:
int netif_rx(struct sk_buff *skb);
sk_buff 結(jié)構(gòu)體非常重要,它的含義為“套接字緩沖區(qū)”,用于在 Linux 網(wǎng)絡(luò)子系統(tǒng)中的各層之間傳遞數(shù)據(jù),是 Linux 網(wǎng)絡(luò)子系統(tǒng)數(shù)據(jù)傳遞的“中樞神經(jīng)”。當(dāng)發(fā)送數(shù)據(jù)包時(shí),Linux 內(nèi)核的網(wǎng)絡(luò)處理模塊必須建立一個(gè)包含要傳輸?shù)臄?shù)據(jù)包的 sk_buff,然后將 sk_buff 遞交給下層,各層在 sk_buff 中添加不同的協(xié)議頭直至交給網(wǎng)絡(luò)設(shè)備發(fā)送。同樣地,當(dāng)網(wǎng)絡(luò)設(shè)備從網(wǎng)絡(luò)媒介上接收到數(shù)據(jù)包后,它必須將接收到的數(shù)據(jù)轉(zhuǎn)換為 sk_buff 數(shù)據(jù)結(jié)構(gòu)并傳遞給上層,各層剝?nèi)ハ鄳?yīng)的協(xié)議頭直至交給用戶。
其針對(duì)sk_buff的成員變量,操作(分配,釋放,指針移動(dòng))等是這一層的核心內(nèi)容。這一層是屬于協(xié)議層通用的。驅(qū)動(dòng)代碼不需要改動(dòng)。
設(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)體,包含網(wǎng)絡(luò)設(shè)備的屬性描述和操作接口。當(dāng)我們編寫網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)程序時(shí),只需要了解其中的一部分。比如網(wǎng)卡設(shè)備名,硬件信息,接口信息(MTU等),設(shè)備操作函數(shù)(ioctl,open,stop,poll等)。
net_device 結(jié)構(gòu)體的成員(屬性和函數(shù)指針)需要被設(shè)備驅(qū)動(dòng)功能層的具體數(shù)值和函數(shù)賦予。對(duì)于具體的設(shè)備,工程師應(yīng)該編寫設(shè)備驅(qū)動(dòng)功能層的函數(shù),這些函數(shù)形如 xx_open()、xxx_stop()、xxx_get_stats()、xxx_tx_timeout()、xxx_poll()等。
由于網(wǎng)絡(luò)數(shù)據(jù)包的接收可由中斷引發(fā),設(shè)備驅(qū)動(dòng)功能層中另一個(gè)主體部分將是中斷處理函數(shù),它負(fù)責(zé)讀取硬件上接收的數(shù)據(jù)包并傳送給上層協(xié)議,可能包含xxx_interrupt()和 xxx_rx()函數(shù),前者完成中斷類型判斷等基本的工作,后者則需完成數(shù)據(jù)包的生成和遞交上層等復(fù)雜工作。
對(duì)于特定的設(shè)備,我們還可以定義其相關(guān)私有數(shù)據(jù)和操作,并封裝為一個(gè)私有信息結(jié)構(gòu)體xxx_private,讓其指針被賦值給net_device 的priv成員。xxx_private結(jié)構(gòu)體中可包含設(shè)備特殊的屬性和操作、自旋鎖與信號(hào)量、定時(shí)器以及統(tǒng)計(jì)信息等,由開發(fā)者自定義。
網(wǎng)絡(luò)設(shè)備與媒介層直接對(duì)應(yīng)于實(shí)際的硬件設(shè)備。為了給設(shè)備的物理配置和寄存器操作一個(gè)更一般的描述,我們可以定義一組宏和一組訪問設(shè)備內(nèi)部寄存器的函數(shù),具體的宏和函數(shù)與特定的硬件緊密相關(guān)。主要是配置底層硬件,寄存器的定義和寄存器的讀寫函數(shù)。
從網(wǎng)卡驅(qū)動(dòng)實(shí)現(xiàn)的主要功能的角度來講,wifi驅(qū)動(dòng)還需要以下的代碼實(shí)現(xiàn):
網(wǎng)絡(luò)設(shè)備驅(qū)動(dòng)的注冊(cè)與注銷:這個(gè)是網(wǎng)絡(luò)設(shè)備通用的驅(qū)動(dòng)框架。
網(wǎng)絡(luò)設(shè)備的打開與釋放:
int xxx_open(struct net_device *dev)
{
/* 申請(qǐng)端口、IRQ 等,類似于 fops->open */
ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev); ... netif_start_queue(dev); ... }
int xxx_release(struct net_device *dev)
{
/* 釋放端口、IRQ 等,類似于 fops->close */
free_irq(dev->irq, dev); ... netif_stop_queue(dev); /* can't transmit any more */
...
}
數(shù)據(jù)發(fā)送流程:
int xxx_tx(struct sk_buff *skb, struct net_device *dev)
{ int len;
char *data, shortpkt[ETH_ZLEN]; /* 獲得有效數(shù)據(jù)指針和長(zhǎng)度 */ data = skb->data; len = skb->len; if (len < ETH_ZLEN)
{ /* 如果幀長(zhǎng)小于以太網(wǎng)幀最小長(zhǎng)度,補(bǔ) 0 */ memset(shortpkt, 0, ETH_ZLEN);
memcpy(shortpkt, skb->data, skb->len); len = ETH_ZLEN;
data = shortpkt;
}
dev->trans_start = jiffies; /* 記錄發(fā)送時(shí)間戳 */ /* 設(shè)置硬件寄存器讓硬件把數(shù)據(jù)包發(fā)送出去 */ xxx_hw_tx(data, len, dev);
...
}
當(dāng)數(shù)據(jù)傳輸超時(shí)時(shí),意味著當(dāng)前的發(fā)送操作失敗,此時(shí),數(shù)據(jù)包發(fā)送超時(shí)處理函數(shù) xxx_tx_ timeout()將被調(diào)用。這個(gè)函數(shù)需要調(diào)用 Linux 內(nèi)核提供的 netif_wake_queue()函數(shù)重新啟動(dòng)設(shè)備發(fā)送隊(duì)列。
void xxx_tx_timeout(struct net_device *dev)
{ ... netif_wake_queue(dev); /* 重新啟動(dòng)設(shè)備發(fā)送隊(duì)列 */
}
數(shù)據(jù)接收流程:
網(wǎng)絡(luò)設(shè)備接收數(shù)據(jù)的主要方法是由中斷引發(fā)設(shè)備的中斷處理函數(shù),中斷處理函數(shù)判斷中斷類型,如果為接收中斷,則讀取接收到的數(shù)據(jù),分配 sk_buffer 數(shù)據(jù)結(jié)構(gòu)和數(shù)據(jù)緩沖區(qū),將接收到的數(shù)據(jù)復(fù)制到數(shù)據(jù)緩沖區(qū),并調(diào)用 netif_rx()函數(shù)將 sk_buffer 傳遞給上層協(xié)議。
比較簡(jiǎn)單的處理方式是中斷方式:
static void xxx_interrupt(int irq, void *dev_id, struct pt_regs
*regs)
{ ... switch (status &ISQ_EVENT_MASK)
{
case ISQ_RECEIVER_EVENT:
/* 獲取數(shù)據(jù)包 */
xxx_rx(dev); break;
/* 其他類型的中斷 */
}
}
static void xxx_rx(struct xxx_device *dev)
{ ... length = get_rev_len (...);
/* 分配新的套接字緩沖區(qū) */
skb = dev_alloc_skb(length + 2);
skb_reserve(skb, 2); /* 對(duì)齊 */
skb->dev = dev;
/* 讀取硬件上接收到的數(shù)據(jù) */
insw(ioaddr + RX_FRAME_PORT, skb_put(skb, length), length >> 1); if (length &1)
skb->data[length - 1] = inw(ioaddr + RX_FRAME_PORT);
/* 獲取上層協(xié)議類型 */
skb->protocol = eth_type_trans(skb, dev);
/* 把數(shù)據(jù)包交給上層 */
netif_rx(skb);
/* 記錄接收時(shí)間戳 */
dev->last_rx = jiffies; ... }
復(fù)雜一些的是poll的接收方式:
static int xxx_poll(struct net_device *dev, int *budget)
{
int npackets = 0, quota = min(dev->quota, *budget);
struct sk_buff *skb;
struct xxx_priv *priv = netdev_priv(dev);
struct xxx_packet *pkt; while (npackets < quota && priv->rx_queue)
{ /*從隊(duì)列中取出數(shù)據(jù)包*/ pkt = xxx_dequeue_buf(dev); /*接下來的處理,和中斷觸發(fā)的數(shù)據(jù)包接收一致*/ skb = dev_alloc_skb(pkt->datalen + 2); if (!skb)
{ ... continue;
}
skb_reserve(skb, 2);
memcpy(skb_put(skb, pkt->datalen), pkt->data, pkt->datalen);
skb->dev = dev;
skb->protocol = eth_type_trans(skb, dev); /*調(diào)用 netif_receive_skb 而不是 net_rx 將數(shù)據(jù)包交給上層協(xié)議*/ netif_receive_skb(skb); /*更改統(tǒng)計(jì)數(shù)據(jù) */ priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
xxx_release_buffer(pkt);
} /* 處理完所有數(shù)據(jù)包*/ *budget -= npackets;
dev->quota -= npackets; if (!priv->rx_queue)
{
netif_rx_complete(dev);
xxx_enable_rx_int (...); /* 再次使能網(wǎng)絡(luò)設(shè)備的接收中斷 */ return 0;
} return 1;
}
雖然 NAPI 兼容的設(shè)備驅(qū)動(dòng)以 poll 方式接收數(shù)據(jù)包,但是仍然需要首次數(shù)據(jù)包接收中斷來觸發(fā) poll 過程。與數(shù)據(jù)包的中斷接收方式不同的是,以輪詢方式接收數(shù)據(jù)包時(shí),當(dāng)?shù)谝淮沃袛喟l(fā)生后,中斷處理程序要禁止設(shè)備的數(shù)據(jù)包接收中斷,如下:
static void xxx_poll_interrupt(int irq, void *dev_id, struct
pt_regs *regs)
{ switch (status &ISQ_EVENT_MASK)
{
case ISQ_RECEIVER_EVENT: ... /* 獲取數(shù)據(jù)包 */
xxx_disable_rx_int(...); /* 禁止接收中斷 */
netif_rx_schedule(dev); break; ... /* 其他類型的中斷 */
}
}
還剩下一些網(wǎng)絡(luò)連接狀態(tài),參數(shù)設(shè)置和統(tǒng)計(jì)數(shù)據(jù)的函數(shù)需要實(shí)現(xiàn),這部分不再多做說明。
WIFI設(shè)備本身私有的功能及屬性,如自身的配置及初始化、建立與用戶空間的交互接口、自身功能的實(shí)現(xiàn)等。
1、自身的配置及初始化,比如xxx_read_chip_info(); xxx_chip_configure(); xxx_hal_init();
2、主要是在proc和sys文件系統(tǒng)上建立與用戶空間的交互接口,比如xxx_drv_proc_init();xxx_ndev_notifier_register();
3、wifi自身的一些功能實(shí)現(xiàn),比如掃描接入和功耗,電源管理相關(guān)的實(shí)現(xiàn)。
3、教程網(wǎng):http://www.downmusicas.net
企業(yè)客服QQ:58053012
網(wǎng)際迅聯(lián)(北京)科技有限公司
|
聯(lián)系我們
|
微信掃一掃 手機(jī)訪問 |