硬件平臺: FL2440(s3c2440)
內核版本: 2.6.35
主機平臺: Ubuntu11.04
內核版本: 2.6.39
交叉編譯器: arm-linuc-gcc4.3.2
原創作品,轉載請標明出處 http://blog.csdn.net/yming0221/article/details/6615027
本文接上文
ARM-Linux 驅動 --DM9000 網卡驅動分析(一)
ARM-Linux 驅動 --DM9000 網卡驅動分析(二)
下面開始看網卡設備的打開、關閉函數和操作函數
static const struct net_device_ops dm9000_netdev_ops = { .ndo_open = dm9000_open,/* 打開設備函數 */ .ndo_stop = dm9000_stop,/* 關閉設備函數 */ .ndo_start_xmit = dm9000_start_xmit,/* 開始發送數據 */ .ndo_tx_timeout = dm9000_timeout,/* 發送超時 */ .ndo_set_multicast_list = dm9000_hash_table,/* 設定多播列表 */ .ndo_do_ioctl = dm9000_ioctl,/* io操作函數 */ .ndo_change_mtu = eth_change_mtu,/* 改變MTU */ .ndo_validate_addr = eth_validate_addr, .ndo_set_mac_address = eth_mac_addr, #ifdef CONFIG_NET_POLL_CONTROLLER .ndo_poll_controller = dm9000_poll_controller, #endif };
1 、 DM9000 的打開函數
由于在函數 alloc_netdev_mq() 中分配 net_device 和網卡的私有數據是一起分配的,詳見函數的實現
struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name, void (*setup)(struct net_device *), unsigned int queue_count) { ................... alloc_size = sizeof(struct net_device); if (sizeof_priv) { /* ensure 32-byte alignment of private area */ alloc_size = ALIGN(alloc_size, NETDEV_ALIGN); alloc_size += sizeof_priv; } /* ensure 32-byte alignment of whole construct */ alloc_size += NETDEV_ALIGN - 1; p = kzalloc(alloc_size, GFP_KERNEL); if (!p) { printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n"); return NULL; } tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL); if (!tx) { printk(KERN_ERR "alloc_netdev: Unable to allocate " "tx qdiscs.\n"); goto free_p; } #ifdef CONFIG_RPS rx = kcalloc(queue_count, sizeof(struct netdev_rx_queue), GFP_KERNEL); if (!rx) { printk(KERN_ERR "alloc_netdev: Unable to allocate " "rx queues.\n"); goto free_tx; } .............. }
所以使用函數 netdev_priv() 函數返回的是網卡的私有數據的地址,函數的實現如下:
/** * netdev_priv - access network device private data * @dev: network device * * Get network device private data */ static inline void *netdev_priv(const struct net_device *dev) { return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN); }
這樣兩者會同時生存和消失。
dm9000_open() 函數
/* * Open the interface. * The interface is opened whenever "ifconfig" actives it. */ static int dm9000_open(struct net_device *dev) { board_info_t *db = netdev_priv(dev);/* 返回board_info_t的地址 */ unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK; if (netif_msg_ifup(db)) dev_dbg(db->dev, "enabling %s\n", dev->name); /* If there is no IRQ type specified, default to something that * may work, and tell the user that this is a problem */ if (irqflags == IRQF_TRIGGER_NONE) dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n"); irqflags |= IRQF_SHARED; /* 注冊中斷 */ if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev)) return -EAGAIN; /* Initialize DM9000 board */ dm9000_reset(db);/* 復位DM9000 */ dm9000_init_dm9000(dev);/* 根據net_device的數據初始化DM9000 */ /* Init driver variable */ db->dbug_cnt = 0; mii_check_media(&db->mii, netif_msg_link(db), 1);/* 檢測mii接口的狀態 */ netif_start_queue(dev);/* 用來告訴上層網絡協定這個驅動程序還有空的緩沖區可用,請把下 一個封包送進來。*/ /*在probe函數中初始化的等待隊列 INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work); *初始化定時器,調用等待隊列*/ dm9000_schedule_poll(db); return 0; }
2 、網卡關閉函數
/* * Stop the interface. * The interface is stopped when it is brought. */ static int dm9000_stop(struct net_device *ndev) { board_info_t *db = netdev_priv(ndev);/* 同上,獲取網卡的私有結構信息的地址 */ if (netif_msg_ifdown(db)) dev_dbg(db->dev, "shutting down %s\n", ndev->name); cancel_delayed_work_sync(&db->phy_poll);/* 終止phy_poll隊列中被延遲的任務 */ netif_stop_queue(ndev);/* 關閉發送隊列 */ netif_carrier_off(ndev);/*通知該內核設備載波丟失,大部分涉及實際的物理連接的網絡技術提供有一個載波狀態,載波存在說明硬件存在并準備好*/ /* free interrupt */ free_irq(ndev->irq, ndev);/* 釋放中斷 */ dm9000_shutdown(ndev);/* 關閉DM9000網卡 */ return 0; }
下面是調用的 dm9000_shutdown(ndev) 函數,該函數的功能是復位 phy ,配置寄存器 GPR 位 0 為 1 ,關閉 dm9000 電源,配置寄存器 IMR 位 7 為 1 , disable 中斷,配置寄存器 RCR , disable 接收
函數如下 :
static void dm9000_shutdown(struct net_device *dev) { board_info_t *db = netdev_priv(dev);/* 獲取網卡私有信息的地址 */ /* RESET device */ dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET ,復位PHY*/ iow(db, DM9000_GPR, 0x01); /* Power-Down PHY ,關閉PHY*/ iow(db, DM9000_IMR, IMR_PAR); /* Disable all interrupt ,關閉所有的中斷*/ iow(db, DM9000_RCR, 0x00); /* Disable RX ,不再接受數據*/ }
3 、接下來了解一下數據的發送函數 dm9000_start_xmit
上圖可以看出
DM9000
的
SRAM
中地址
0x0000
到
0x0BFF
是
TXBuffer
,從
0x0C00
到
0x3FFF
是
RXBuffer
,包的有效數據必須提前放到
TXBuffer
緩沖區,使用端口命令來選擇
MWCMD
寄存器。最后設置
TXCR
寄存器的
bit[0]TXREQ
來自動發送包。
發送包的步驟如下
:
(
1
)檢查存儲器寬度,通過讀取
ISR
的
bit[7:6]
來確定位數
(
2
)寫數據到
TXSRAM
(
3
)寫傳輸長度到
TXPLL
和
TXPLH
寄存器
(
4
)設置
TXCR
的
bit[0]TXREQ
來發送包
/* * Hardware start transmission. * Send a packet to media from the upper layer. */ static int dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev) { unsigned long flags; board_info_t *db = netdev_priv(dev);/* 獲取網卡雖有信息的存儲結構信息的地址 */ dm9000_dbg(db, 3, "%s:\n", __func__); if (db->tx_pkt_cnt > 1) return NETDEV_TX_BUSY; spin_lock_irqsave(&db->lock, flags);/* 獲得自旋鎖 */ /* Move data to DM9000 TX RAM */ /*MWCMD 即 Memory data write command with address increment Register(F8H) * 根據 IO 操作模式(8-bit or 16-bit)來增加寫指針 1 或 2 */ writeb(DM9000_MWCMD, db->io_addr); (db->outblk)(db->io_data, skb->data, skb->len);/* 將數據從sk_buff中copy到網卡的TX SRAM中 */ dev->stats.tx_bytes += skb->len;/* 統計發送的字節數 */ db->tx_pkt_cnt++;/* 待發送計數 */ /* TX control: First packet immediately send, second packet queue */ if (db->tx_pkt_cnt == 1) { dm9000_send_packet(dev, skb->ip_summed, skb->len);/* 如果計數為1,直接發送 */ } else {/* 如果是第2個,則 */ /* Second packet */ db->queue_pkt_len = skb->len; db->queue_ip_summed = skb->ip_summed; netif_stop_queue(dev);/* 告訴上層停止發送 */ } spin_unlock_irqrestore(&db->lock, flags);/* 解鎖 */ /* free this SKB ,釋放SKB*/ dev_kfree_skb(skb); return NETDEV_TX_OK; }
上面函數調用下面的函數 dm9000_send_packet 來發送數據
static void dm9000_send_packet(struct net_device *dev, int ip_summed, u16 pkt_len) { board_info_t *dm = to_dm9000_board(dev); /* The DM9000 is not smart enough to leave fragmented packets alone. */ if (dm->ip_summed != ip_summed) { if (ip_summed == CHECKSUM_NONE) iow(dm, DM9000_TCCR, 0); else iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP); dm->ip_summed = ip_summed; } /* Set TX length to DM9000 */ /* 設置TX數據的長度到寄存器TXPLL和TXPLH */ iow(dm, DM9000_TXPLL, pkt_len); iow(dm, DM9000_TXPLH, pkt_len >> 8); /* Issue TX polling command */ /* 設置發送控制寄存器的發送請求位 */ iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */ }
5 、下面看一下當一個數據包發送完成后的中斷處理函數 dm9000_tx_done
/* * DM9000 interrupt handler * receive the packet to upper layer, free the transmitted packet */ static void dm9000_tx_done(struct net_device *dev, board_info_t *db) { int tx_status = ior(db, DM9000_NSR); /* Got TX status */ if (tx_status & (NSR_TX2END | NSR_TX1END)) {/* 第一個或第二個數據包發送完畢 */ /* One packet sent complete */ db->tx_pkt_cnt--;/* 待發送的數據包個數減1 */ dev->stats.tx_packets++;/* 發送的數據包加1 */ if (netif_msg_tx_done(db)) dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status); /* Queue packet check & send */ if (db->tx_pkt_cnt > 0)/* 如果還有數據包 */ dm9000_send_packet(dev, db->queue_ip_summed, db->queue_pkt_len); netif_wake_queue(dev);/* 告訴內核,將數據包放入發生那個隊列 */ } }
-
更多查看
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元
