硬件平臺:FL2440(s3c2440)內核版本:2.6.35主機平臺:Ubuntu11.04內核版本:2.6.39交叉編譯器:arm-linuc-gcc4.3.2原" />

亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

ARM-Linux驅動--DM9000網卡驅動分析(三)

系統 2156 0
<style type="text/css"> <!-- @page { margin: 0.79in } P { margin-bottom: 0.08in } PRE.cjk { font-family: "AR PL UMing HK", monospace } A:link { so-language: zxx } --> </style>

硬件平臺: 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

ARM-Linux驅動--DM9000網卡驅動分析(三)

上圖可以看出 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);/* 告訴內核,將數據包放入發生那個隊列 */
            }
    }
    
        

    更多查看
    ARM-Linux驅動--DM9000網卡驅動分析(四)



ARM-Linux驅動--DM9000網卡驅動分析(三)


更多文章、技術交流、商務合作、聯系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯系: 360901061

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

【本文對您有幫助就好】

您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會非常 感謝您的哦!!!

發表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 国产一精品一aⅴ一免费 | 日本一级看片免费播放 | 天天拍天天操 | 国产激情一区二区三区四区 | 最新中文字幕一区 | 国产系列欧美系列日韩系列在线 | 91国语精品自产拍在线观看一 | 欧美伦理一区 | 色综合天天综久久久噜噜噜久久〔 | 久久国产这里只有精品 | 中文字幕一区久久久久 | 四虎影视紧急入口地址大全 | 一区二区三区精品国产欧美 | 国内视频在线 | 日本免费不卡一区 | 欧美日韩一区二区三区久久 | 久久草在线播放 | 久久艹免费视频 | 亚洲欧洲日产国码二区首页 | 亚洲精品香蕉一区二区在线观看 | 天天射综合 | 亚洲精品国产一区二区三区四区 | 免费观看成人碰视频公开 | 日本中文字幕一区二区有码在线 | 国产或人精品日本亚洲77美色 | 性欧美日韩 | 精品久久国产视频 | 久久免费视频99 | 久久久久国产精品 | 校园春色男人天堂 | 精品啪啪| 日韩一区精品 | 日本不卡一区二区三区 最新 | 伊人免费视频网 | 中文字幕一区二区日产乱码 | 日韩深夜 | 五月月色开心婷婷久久合 | 激情在线网站 | 国产精品久久久久秋霞影视 | 国产精品五月天 | 免费乱人伦 |