網(wǎng)絡設備#
網(wǎng)絡驅(qū)動結(jié)構(gòu)##
從上到下劃分4層:
- 網(wǎng)絡協(xié)議接口層
使上層協(xié)議獨立于具體設備
- 網(wǎng)絡設備接口層
向協(xié)議接口層提供統(tǒng)一的用于描述具體網(wǎng)絡設別屬性和操作的結(jié)構(gòu)體 net_device
- 設備驅(qū)動功能層(提供實際功能)
net_device 的具體成員
- 網(wǎng)絡設備與媒介層
完成數(shù)據(jù)包發(fā)送和接收的物理實體
網(wǎng)絡設備初始化##
- 硬件準備工作溪窒,檢查設備是否存在和硬件資源
- 對 net_device 數(shù)據(jù)和函數(shù)指針初始化
- 獲得設備的私有信息指針并初始化各成員值
網(wǎng)絡設備打開與釋放###
open
使能設備硬件資源盗誊,申請IO區(qū)域、中斷和DMA通道
調(diào)用Linux內(nèi)核提供的 netif_start_queue() 函數(shù),激活設備發(fā)送隊列
release
調(diào)用內(nèi)核提供的 netif_stop_queue() 函數(shù)
釋放資源
數(shù)據(jù)發(fā)送流程###
- 獲取sk_buff數(shù)據(jù)包的數(shù)據(jù)和長度
- 有效長度小于以太網(wǎng)沖突檢測所要求數(shù)據(jù)幀最小長度ETH_ZLEN赵辕,則補0
- 設置硬件寄存器,驅(qū)動設備進行數(shù)據(jù)發(fā)送
數(shù)據(jù)接收流程###
- 設備接收數(shù)據(jù)的主要方法是由中斷引發(fā)中斷處理函數(shù)
- NAPI兼容的驅(qū)動咖熟,需先disable interrupt再調(diào)用NAPI接收pkt
網(wǎng)絡狀態(tài)###
- 網(wǎng)絡硬件電路可以檢測網(wǎng)絡連接是否正常
- 驅(qū)動程序可采取一定手段檢測報告鏈路狀態(tài),中斷或定時
網(wǎng)絡設備驅(qū)動流程##
struct net_device_stats(struct net_device *dev)
{
...
return &dev->stats;
}
static int xxx_config(struct net_device *dev, struct ifmap *map)
{
if (netif_running(dev))
return -EBUSY;
/* 假設不允許改變IO地址 */
if (map->base_addr != dev->base_addr) {
printk("xxx: Can't change I/O address\n");
return -EOPNOTSUPP;
}
/* 假設允許改變 IRQ */
if (map->irq != dev->irq)
dev->irq = map->irq;
return 0;
}
static int set_mac_address(struct net_device *dev, void *addr)
{
if (netif_running(dev))
return -EBUSY;
xxx_set_mac(dev, addr);
return 0;
}
static int xxx_open(struct net_device *dev)
{
struct xxx_priv *priv = netdev_priv(dev);
...
priv->timer.expires = jiffies + 3*Hz;
priv->timer.data = (unsigned long)dev;
/* timer handler */
priv->timer.function = &xxx_timer;
add_timer(&priv->timer);
...
}
static void xxx_timer(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
u16 link;
...
if (!(dev->flags & IFF_UP))
goto set_timer;
/* 獲得物理連接狀態(tài) */
if (link = xxx_chk_link(dev)) {
if (!(dev->flags & IFF_RUNNING)) {
/* 通知內(nèi)核 link up */
netif_carrier_on(dev);
dev->flags |= IFF_RUNNING;
printk("%s: link up\n", dev->name);
}
} else {
if (dev->flags & IFF_RUNNING) {
/* 通知內(nèi)核 link down */
netif_carrier_off(dev);
dev->flags &= ~IFF_RUNNING;
printk("%s: link down\n", dev->name);
}
}
set_timer:
priv->timer.expires = jiffies + 1*Hz;
priv->timer.data = (unsigned long)dev;
/* timer handler */
priv->timer.function = &xxx_timer;
add_timer(&priv->timer);
}
#ifdef NAPI
static int xxx_poll(struct napi_struct *napi, int budget)
{
int npackets = 0;
struct sk_buff *skb;
struct xxx_priv *priv = container_of(napi, struct xxx_priv, napi);
struct xxx_packet *pkt;
while ((npackets < budget) && priv->rx_queue) {
/* 從隊列中取出數(shù)據(jù)包 */
pkt = xxx_dequeue_buf(dev);
/* 接下來的處理和中斷觸發(fā)接收一致 */
skb = dev_alloc_skb(length + 2);
...
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)計數(shù)據(jù) */
priv->stats.rx_packets++;
priv->stats.rx_bytes += pkt->datalen;
xxx_release_buffer(pkt);
npackets++;
}
if (npackets < budget) {
napi_complete(napi);
/* 再次啟動網(wǎng)絡設備的接收中斷 */
xxx_enable_rx_int(...);
}
return npackets;
}
static void xxx_interrupt(int irq, void *dev_id)
{
struct net_device *dev = dev_id;
...
switch (status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT:
/* 禁止接收中斷 */
xxx_disable_rx_int(...);
/* 獲取數(shù)據(jù)包 */
napi_schedule(&priv->napi);
break;
case ISQ_TRANSMITTER_EVENT:
dev->stats.tx_packets++;
netif_wake_queue(dev);
/* 檢查硬件錯誤標志 */
if ((status & (TX_OK | TX_LOST_CRS | TX_SQE_ERROR | TX_LATE_COL | TX_16_COL)) != TX_OK) {
/* 錯誤統(tǒng)計 */
if ((status & TX_OK) == 0)
dev->stats.tx_errors++;
if (status & TX_LOST_CRS)
dev->stats.tx_carrier_errors++;
...
}
break;
case ISQ_RX_MISS_EVENT:
...
break;
...
}
}
#else
static void xxx_rx(struct xxx_device *dev)
{
...
length = get_rev_len(...);
skb = dev_alloc_skb(length + 2);
/* 對齊 */
skb_reserve(skb, 2);
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);
/* 記錄接收時間戳 */
dev->last_rx = jiffies;
...
}
static void xxx_interrupt(int irq, void *dev_id)
{
...
switch (status & ISQ_EVENT_MASK) {
case ISQ_RECEIVER_EVENT:
/* 獲取數(shù)據(jù)包 */
xxx_rx(dev);
break;
...
}
}
#endif
/* 發(fā)送超時處理函數(shù) */
void xxx_tx_timeout(struct net_device *dev)
{
...
netif_wake_queue(dev);
...
dev->stats.tx_errors++;
}
int xxx_tx(struct sk_buff *skb, struct net_device *dev)
{
int len;
char *data, shortpkt[ETH_ZLEN];
/* 發(fā)送隊列未滿胃惜,可以發(fā)送 */
if (xxx_send_available(...)) {
/* 有效數(shù)據(jù)指針和長度 */
data = skb->data;
len = skb->len;
if (len < ETH_ZLEN) {
/* 如果幀長小于以太網(wǎng)幀最小長度,補0 */
memset(shortpkt, 0, ETH_ZLEN);
memcpy(shortpkt, skb->data, skb->len);
len = ETH_ZLEN;
data = shortpkt;
}
/* 記錄發(fā)送時間戳 */
dev->trans_start = jiffies;
if (avail) {
xxx_hw_tx(data, len, dev);
} else {
/*
阻止上層繼續(xù)向網(wǎng)絡設備傳遞數(shù)據(jù)包哪雕;當忙于發(fā)送的數(shù)據(jù)包被發(fā)送完成后船殉,在以TX結(jié)束的中斷處理中,應該調(diào)用 netif_wake_queue() 喚醒被阻塞的上層斯嚎,以啟動它繼續(xù)向網(wǎng)絡設備傳送數(shù)據(jù)包
*/
netif_stop_queue(dev);
...
}
}
}
static int xxx_open(struct net_device *dev)
{
/* 申請端口利虫、IRQ等,類似于fops->open */
ret = request_irq(dev->irq, &xxx_interrupt, 0, dev->name, dev);
...
netif_start_queue(dev);
...
}
static int xxx_release(struct net_device *dev)
{
netif_stop_queue(dev);
...
free_irq(dev->irq, dev);
...
}
void xxx_init(struct net_device *dev)
{
struct xxx_priv *priv;
/* 檢查設備是否存在和設備所使用的硬件資源 */
xxx_hw_init();
/* 初始化 net_device 公有成員 */
ether_setup(dev);
/* 設置設備的成員函數(shù)指針 */
dev->netdev_ops = &xxx_netdev_ops;
dev->ethtool_ops = &xxx_ethtool_ops;
dev->watchdog_timeo = timeout;
/* 取得私有指針并初始化 */
priv = netdev_priv(dev);
...
}
static int xxx_register(void)
{
...
/* 分配 net_device 結(jié)構(gòu)體并賦值 */
xxx_dev = alloc_netdev(sizeof(struct xxx_priv), "sn%d", xxx_init);
if (!xxx_dev)
...
/* 注冊 net_device 結(jié)構(gòu)體 */
if ((result = register_netdev(xxx_dev)))
...
}
static void xxx_unregister(void)
{
...
/* 注銷 net_device 結(jié)構(gòu)體 */
unregister_netdev(xxx_dev);
/* 釋放 net_device 結(jié)構(gòu)體 */
free_netdev(xxx_dev);
}
總結(jié)##
- net_device 結(jié)構(gòu)體的存在將網(wǎng)絡設備進行抽象堡僻,使得設備功能層中除數(shù)據(jù)包接收以外的主體工作都由填充 net_device 的屬性和函數(shù)指針完成
- 套接字緩沖區(qū) sk_buff 是所有數(shù)據(jù)流動的載體糠惫,網(wǎng)絡設備驅(qū)動和上層協(xié)議之間也基于此結(jié)構(gòu)進行數(shù)據(jù)包交互