4593|0

57

帖子

0

TA的资源

一粒金砂(中级)

楼主
 

ARM-Linux驱动--DM9000网卡驱动分析(三) [复制链接]

ARM-Linux驱动--DM9000网卡驱动分析(三)


硬件平台: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网卡驱动分析(二)

下面开始看网卡设备的打开、关闭函数和操作函数





view plaincopy to clipboardprint?
01.static const struct net_device_ops dm9000_netdev_ops = {  
02.    .ndo_open       = dm9000_open,/* 打开设备函数 */  
03.    .ndo_stop       = dm9000_stop,/* 关闭设备函数 */  
04.    .ndo_start_xmit     = dm9000_start_xmit,/* 开始发送数据 */  
05.    .ndo_tx_timeout     = dm9000_timeout,/* 发送超时 */  
06.    .ndo_set_multicast_list = dm9000_hash_table,/* 设定多播列表 */  
07.    .ndo_do_ioctl       = dm9000_ioctl,/* io操作函数 */  
08.    .ndo_change_mtu     = eth_change_mtu,/* 改变MTU */  
09.    .ndo_validate_addr  = eth_validate_addr,  
10.    .ndo_set_mac_address    = eth_mac_addr,  
11.#ifdef CONFIG_NET_POLL_CONTROLLER  
12.    .ndo_poll_controller    = dm9000_poll_controller,  
13.#endif  
14.};  


1、DM9000的打开函数

由于在函数alloc_netdev_mq()中分配net_device和网卡的私有数据是一起分配的,详见函数的实现





view plaincopy to clipboardprint?
01.struct net_device *alloc_netdev_mq(int sizeof_priv, const char *name,  
02.        void (*setup)(struct net_device *), unsigned int queue_count)  
03.{  
04....................  
05.alloc_size = sizeof(struct net_device);  
06.    if (sizeof_priv) {  
07.        /* ensure 32-byte alignment of private area */  
08.        alloc_size = ALIGN(alloc_size, NETDEV_ALIGN);  
09.        alloc_size += sizeof_priv;  
10.    }  
11.    /* ensure 32-byte alignment of whole construct */  
12.    alloc_size += NETDEV_ALIGN - 1;  
13.  
14.    p = kzalloc(alloc_size, GFP_KERNEL);  
15.    if (!p) {  
16.        printk(KERN_ERR "alloc_netdev: Unable to allocate device.\n");  
17.        return NULL;  
18.    }  
19.  
20.    tx = kcalloc(queue_count, sizeof(struct netdev_queue), GFP_KERNEL);  
21.    if (!tx) {  
22.        printk(KERN_ERR "alloc_netdev: Unable to allocate "  
23.               "tx qdiscs.\n");  
24.        goto free_p;  
25.    }  
26.  
27.#ifdef CONFIG_RPS  
28.    rx = kcalloc(queue_count, sizeof(struct netdev_rx_queue), GFP_KERNEL);  
29.    if (!rx) {  
30.        printk(KERN_ERR "alloc_netdev: Unable to allocate "  
31.               "rx queues.\n");  
32.        goto free_tx;  
33.    }  
34...............  
35.}  


所以使用函数netdev_priv()函数返回的是网卡的私有数据的地址,函数的实现如下:





view plaincopy to clipboardprint?
01./**
02. *      netdev_priv - access network device private data
03. *      @dev: network device
04. *
05. * Get network device private data
06. */  
07.static inline void *netdev_priv(const struct net_device *dev)  
08.{  
09.        return (char *)dev + ALIGN(sizeof(struct net_device), NETDEV_ALIGN);  
10.}  


这样两者会同时生存和消失。

dm9000_open()函数





view plaincopy to clipboardprint?
01./*
02. *  Open the interface.
03. *  The interface is opened whenever "ifconfig" actives it.
04. */  
05.static int  
06.dm9000_open(struct net_device *dev)  
07.{  
08.        board_info_t *db = netdev_priv(dev);/* 返回board_info_t的地址 */  
09.        unsigned long irqflags = db->irq_res->flags & IRQF_TRIGGER_MASK;  
10.  
11.        if (netif_msg_ifup(db))  
12.                dev_dbg(db->dev, "enabling %s\n", dev->name);  
13.  
14.        /* If there is no IRQ type specified, default to something that
15.         * may work, and tell the user that this is a problem */  
16.  
17.        if (irqflags == IRQF_TRIGGER_NONE)  
18.                dev_warn(db->dev, "WARNING: no IRQ resource flags set.\n");  
19.  
20.        irqflags |= IRQF_SHARED;  
21.         
22.        /* 注册中断 */  
23.        if (request_irq(dev->irq, dm9000_interrupt, irqflags, dev->name, dev))  
24.                return -EAGAIN;  
25.  
26.        /* Initialize DM9000 board */  
27.        dm9000_reset(db);/* 复位DM9000 */  
28.        dm9000_init_dm9000(dev);/* 根据net_device的数据初始化DM9000 */  
29.  
30.        /* Init driver variable */  
31.        db->dbug_cnt = 0;  
32.  
33.        mii_check_media(&db->mii, netif_msg_link(db), 1);/* 检测mii接口的状态 */  
34.        netif_start_queue(dev);/* 用来告诉上层网络协定这个驱动程序还有空的缓冲区可用,请把下 一个封包送进来。*/  
35.         
36.        /*在probe函数中初始化的等待队列 INIT_DELAYED_WORK(&db->phy_poll, dm9000_poll_work);   
37.        *初始化定时器,调用等待队列*/  
38.        dm9000_schedule_poll(db);  
39.  
40.        return 0;  
41.}  



2、网卡关闭函数





view plaincopy to clipboardprint?
01./*
02. * Stop the interface.
03. * The interface is stopped when it is brought.
04. */  
05.static int  
06.dm9000_stop(struct net_device *ndev)  
07.{  
08.        board_info_t *db = netdev_priv(ndev);/* 同上,获取网卡的私有结构信息的地址 */  
09.  
10.        if (netif_msg_ifdown(db))  
11.                dev_dbg(db->dev, "shutting down %s\n", ndev->name);  
12.  
13.        cancel_delayed_work_sync(&db->phy_poll);/* 终止phy_poll队列中被延迟的任务 */  
14.  
15.        netif_stop_queue(ndev);/* 关闭发送队列 */  
16.        netif_carrier_off(ndev);/*通知该内核设备载波丢失,大部分涉及实际的物理连接的网络技术提供有一个载波状态,载波存在说明硬件存在并准备好*/  
17.  
18.        /* free interrupt */  
19.        free_irq(ndev->irq, ndev);/* 释放中断 */  
20.  
21.        dm9000_shutdown(ndev);/* 关闭DM9000网卡 */  
22.  
23.        return 0;  
24.}  




下面是调用的dm9000_shutdown(ndev)函数,该函数的功能是复位phy,配置寄存器GPR位0为1,关闭dm9000电源,配置寄存器IMR位7为1,disable中断,配置寄存器RCR,disable接收

函数如下:





view plaincopy to clipboardprint?
01.static void  
02.dm9000_shutdown(struct net_device *dev)  
03.{  
04.        board_info_t *db = netdev_priv(dev);/* 获取网卡私有信息的地址 */  
05.  
06.        /* RESET device */  
07.        dm9000_phy_write(dev, 0, MII_BMCR, BMCR_RESET); /* PHY RESET ,复位PHY*/  
08.        iow(db, DM9000_GPR, 0x01);      /* Power-Down PHY ,关闭PHY*/  
09.        iow(db, DM9000_IMR, IMR_PAR);   /* Disable all interrupt ,关闭所有的中断*/  
10.        iow(db, DM9000_RCR, 0x00);      /* Disable RX ,不再接受数据*/  
11.}  




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来发送包





view plaincopy to clipboardprint?
01./*
02. *  Hardware start transmission.
03. *  Send a packet to media from the upper layer.
04. */  
05.static int  
06.dm9000_start_xmit(struct sk_buff *skb, struct net_device *dev)  
07.{  
08.        unsigned long flags;  
09.        board_info_t *db = netdev_priv(dev);/* 获取网卡虽有信息的存储结构信息的地址 */  
10.  
11.        dm9000_dbg(db, 3, "%s:\n", __func__);  
12.  
13.        if (db->tx_pkt_cnt > 1)  
14.                return NETDEV_TX_BUSY;  
15.  
16.        spin_lock_irqsave(&db->lock, flags);/* 获得自旋锁 */  
17.  
18.        /* Move data to DM9000 TX RAM */  
19.        /*MWCMD 即 Memory data write command with address increment Register(F8H)
20.        * 根据 IO 操作模式(8-bit or 16-bit)来增加写指针 1 或 2
21.        */  
22.        writeb(DM9000_MWCMD, db->io_addr);  
23.  
24.        (db->outblk)(db->io_data, skb->data, skb->len);/* 将数据从sk_buff中copy到网卡的TX SRAM中 */  
25.         
26.        dev->stats.tx_bytes += skb->len;/* 统计发送的字节数 */  
27.         
28.        db->tx_pkt_cnt++;/* 待发送计数 */  
29.        /* TX control: First packet immediately send, second packet queue */  
30.        if (db->tx_pkt_cnt == 1) {  
31.                dm9000_send_packet(dev, skb->ip_summed, skb->len);/* 如果计数为1,直接发送 */  
32.        } else {/* 如果是第2个,则 */  
33.                /* Second packet */  
34.                db->queue_pkt_len = skb->len;  
35.                db->queue_ip_summed = skb->ip_summed;  
36.                netif_stop_queue(dev);/* 告诉上层停止发送 */  
37.        }  
38.        spin_unlock_irqrestore(&db->lock, flags);/* 解锁 */  
39.        /* free this SKB ,释放SKB*/  
40.        dev_kfree_skb(skb);  
41.  
42.        return NETDEV_TX_OK;  
43.}  


上面函数调用下面的函数 dm9000_send_packet来发送数据





view plaincopy to clipboardprint?
01.static void dm9000_send_packet(struct net_device *dev,  
02.                               int ip_summed,  
03.                               u16 pkt_len)  
04.{  
05.        board_info_t *dm = to_dm9000_board(dev);  
06.  
07.        /* The DM9000 is not smart enough to leave fragmented packets alone. */  
08.        if (dm->ip_summed != ip_summed) {  
09.                if (ip_summed == CHECKSUM_NONE)  
10.                        iow(dm, DM9000_TCCR, 0);  
11.                else  
12.                        iow(dm, DM9000_TCCR, TCCR_IP | TCCR_UDP | TCCR_TCP);  
13.                dm->ip_summed = ip_summed;  
14.        }  
15.  
16.        /* Set TX length to DM9000 */  
17.        /* 设置TX数据的长度到寄存器TXPLL和TXPLH */  
18.        iow(dm, DM9000_TXPLL, pkt_len);  
19.        iow(dm, DM9000_TXPLH, pkt_len >> 8);  
20.  
21.        /* Issue TX polling command */  
22.        /* 设置发送控制寄存器的发送请求位 */  
23.        iow(dm, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete */  
24.}  


5、下面看一下当一个数据包发送完成后的中断处理函数dm9000_tx_done





view plaincopy to clipboardprint?
01./*
02. * DM9000 interrupt handler
03. * receive the packet to upper layer, free the transmitted packet
04. */  
05.  
06.static void dm9000_tx_done(struct net_device *dev, board_info_t *db)  
07.{  
08.        int tx_status = ior(db, DM9000_NSR);    /* Got TX status */  
09.  
10.        if (tx_status & (NSR_TX2END | NSR_TX1END)) {/* 第一个或第二个数据包发送完毕 */  
11.                /* One packet sent complete */  
12.                db->tx_pkt_cnt--;/* 待发送的数据包个数减1 */  
13.                dev->stats.tx_packets++;/* 发送的数据包加1 */  
14.  
15.                if (netif_msg_tx_done(db))  
16.                        dev_dbg(db->dev, "tx done, NSR %02x\n", tx_status);  
17.  
18.                /* Queue packet check & send */  
19.                if (db->tx_pkt_cnt > 0)/* 如果还有数据包 */  
20.                        dm9000_send_packet(dev, db->queue_ip_summed,  
21.                                           db->queue_pkt_len);  
22.                netif_wake_queue(dev);/* 告诉内核,将数据包放入发生那个队列 */  
23.        }  
24.}
点赞 关注

回复
举报
您需要登录后才可以回帖 登录 | 注册

随便看看
查找数据手册?

EEWorld Datasheet 技术支持

相关文章 更多>>
关闭
站长推荐上一条 1/10 下一条

 
EEWorld订阅号

 
EEWorld服务号

 
汽车开发圈

About Us 关于我们 客户服务 联系方式 器件索引 网站地图 最新更新 手机版

站点相关: 国产芯 安防电子 汽车电子 手机便携 工业控制 家用电子 医疗电子 测试测量 网络通信 物联网

北京市海淀区中关村大街18号B座15层1530室 电话:(010)82350740 邮编:100190

电子工程世界版权所有 京B2-20211791 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号 Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved
快速回复 返回顶部 返回列表