1. 程式人生 > >網絡卡驅動流程分析

網絡卡驅動流程分析

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 = &eth_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;
}