網絡卡驅動流程分析
1.網絡卡驅動架構分析
1.1linux網路子系統
1.2.重要資料結構
1.3.網絡卡驅動架構分析
1.1linux網路子系統
linux網路子系統可以分為System call interface(系統呼叫介面),Protocol agnostic interface(協議無關介面),Network protocols(網路協議棧),Device agnostic interface(裝置無關介面),(Device drivers)裝置驅動程式
系統呼叫介面層
為應用程式提供訪問網路子系統的統一方法。
協議無關層
提供通用的方法來使用傳輸層協議。
協議棧的實現
實現具體的網路協議
裝置無關層
協議與裝置驅動之前通訊的通用介面
裝置驅動程式
1.2.重要資料結構.
在Linux核心中,每個網絡卡都由一個net_device結構來描述,其中的一些重要成員有:
char name[IFNAMSIZ]
裝置名,如:eth%d
unsigned long base_addr
I/O 基地址
const struct net_device_ops *netdev_ops;
net_device_ops結構記錄了網絡卡所支援的操作。
static const struct net_device_ops dm9000_netdev_ops =
{
.ndo_open = dm9000_open,
.ndo_stop = dm9000_stop,
.ndo_start_xmit = dm9000_start_xmit,
.ndo_do_ioctl = dm9000_ioctl,
.ndo_validate_addr = eth_validate_addr,
.ndo_set_mac_address = eth_mac_addr,
};
Linux核心中的每個網路資料包都由一個套接字緩衝區結構struct sk_buff 描述,即一個
sk_buff結構就是一個網路包,指向sk_buff的指標通常被稱做skb。其中包含4個成員,head,data,tail,end,其中data和tail指向資料的頭和尾,head和end指向資料包的頭和尾。
1.3.網絡卡驅動架構分析
1.3.1初始化
1.3.1.1分配net_device結構-alloc_etherdev
1.3.1.2初始化net_device結構(裝置號,基地址,MAC地址,netdev_ops)
1.3.1.3初始化硬體
1.3.1.4註冊網絡卡驅動—register_netdev
1.3.2資料傳送
1.3.2.1通知上層協議,暫停向網絡卡傳送資料-netif_stop_queue
1.3.2.2將SKb的資料寫入網絡卡暫存器,傳送走。
1.3.2.3釋放skb結構-dev_kfree_skb
1.3.2.4在傳送中斷處理過程中,通知上層協議可以向網絡卡送資料-netif_wake_queue
1.3.3資料接收
1.3.3.1讀取接收狀態
1.3.3.2讀取接收到資料的長度
1.3.3.3分配skb結構-dev_alloc_skb
1.3.3.4把網絡卡暫存器讀出資料存入skb
1.3.3.5把收到的skb資料包交給協議棧處理-netif_rx
1.4迴環網絡卡驅動設計
迴環網絡卡是一個純軟體的網絡卡,可以認為是將TX與RX連線在一起的網絡卡。
範例程式碼
unsigned long bytes = 0;
unsigned long packets = 0;
//資料傳送
int loopback_xmit(struct sk_buff *skb,struct net_device *dev)
{
skb->protocol = eth_type_trans(skb,dev);//標明資料包的協議
bytes+=skb->len;//記錄傳送資料包的長度
packets++;//記錄資料包的數目
netif_rx(skb);//將收到的包送回去
return 0;
}
static struct net_device_stats *loopback_get_stats(struct net_device *dev)
{
struct net_device_stats *stats = &dev->stats;//將狀態指標指向網絡卡網絡卡描述結構的狀態
stats->tx_bytes = bytes;//位元組
stats->rx_bytes = bytes;
stats->rx_packets = packets;//資料包
stats->tx_packets = packets;//資料包
return stats;
}
struct net_device_ops loopback_ops ={
.ndo_start_xmit = loopback_xmit,
.ndo_get_stats = loopback_get_stats,
};
void static loopback_setup(struct net_device *dev)//對net_devic結構進行初始化
{
dev->mtu = (16*1024)+20+20+12;//接收包的大小
dev->flags = IFF_LOOPBACK;//設定網絡卡的標誌
dev->header_ops = ð_header_ops;//構造包的大小
dev->netdev_ops = &loopback_ops;//初始化網絡卡操作函式集結構
//dev->addr_len = ETH_ALEN;乙太網地址大小
//dev->type = ARPHRD_LOOPBACK;//環路裝置
dev->priv_flags &= ~IFF_XMIT_DST_RELEASE;
//dev->hw_features= NETIF_F_ALL_TSO | NETIF_F_UFO;
}
static __net_init int loopback_net_init(struct net *net)
{
struct net_device *dev;
dev = alloc_netdev(0,"lo%d",loopback_setup);
register_netdev(dev);
net->loopback_dev = dev;
return 0;
}
static __net_exit void loopback_net_exit(struct net *net)
{
struct net_device *dev = net->loopback_dev;
unregister_netdev(dev);
}
/* Registered in net/core/dev.c */
struct pernet_operations __net_initdata loopback_net_ops = {
.init = loopback_net_init,
.exit = loopback_net_exit,
};
2網路子系統深度分析
linux網絡卡驅動程式
linux網路子系統可以分為System call interface(系統呼叫介面),Protocol agnostic interface(協議無關介面),Network protocols(網路協議棧),Device agnostic interface(裝置無關介面),(Device drivers)裝置驅動程式
資料包傳輸流程
使用者程式UDP傳送:
建立socket,再呼叫write函式。
sock_aio_write是傳送介面,sock_aio_write呼叫do_sock_write
在呼叫 sock_sendmesg,呼叫udp_sendmsg,呼叫ip_route_output_flow(選擇路由)
udp_push_pending_fram
DM9000網絡卡驅動程式分析
DM9000採用平臺裝置編寫
DM9000的初始化
1.先分配net_device結構
2.從platform_device中獲取地址、中斷號
3.將獲取到的地址對映為虛擬地址
4.讀取晶片型別
5.設定操作函式集
6.從eeprom讀取MAC地址
7.使用register_dev註冊網絡卡結構
8.dm9000的硬體初始化在open函式中進行
8.1.request_irp()註冊中斷處理函式
8.2.dm9000_init_dm9000()包含設定片選、使能傳送、接受中斷,啟動傳送佇列
dm9000的傳送函式
dm9000_start_xmit()
netif_stop_queue,通知協議棧,暫停向驅動傳送資料
iow()寫入傳送skb數包的長度
writeb寫資料進入dm9000的資料暫存器
iow()啟動傳送向dm9000的DM9000_TCR暫存器寫資料
當傳送方完成觸發中斷
dev_kfree_skb釋放SKB
DM9000的傳送中斷
ior(db,DM9000_ISR)獲取中斷型別
判斷是否傳送中斷
dm9000_tx_done()傳送中斷函式
讀取DM9000_TCR暫存器判斷髮送是否正確
傳送正確,netif_work_queue通知協議棧可以繼續傳送資料
DM9000接收資料
接收dm9000_rx()
Dummy read空讀
讀取dm9000的的接收狀態和資料長度
分配skb,分配整個資料包
skb_reserve將資料報的data和tail的指標向後移動兩位
tail的位置是資料包的位置減4
將接收的資料填充入skb包
將skb提交到協議棧netif_rx
dm9000範例程式碼
struct int
dm9000_start_xmit(struct sk_buff *skb,struct net_device *dev)
{ board_info_t *db = netdev_priv(dev);
//通知協議棧,暫停向驅動傳送資料
netif_stop_queue(dev);
//將skb的資料寫入暫存器
iow(db,DM9000_TXPLL,skb->len);
iow(db,DM9000_TXPLH,skb->len>>8);
writeb(DM9000_MWCMD,db->io_addr);
(db->outblk)(db->io_data,skb->data,skb->len);
iow(db,DM9000_TCR,TCR_TXREQ);
//釋放skb
dev_kfree_skb(skb);
return 0;
}