mini2440网卡驱动DM9000

http://liu1227787871.blog.163.com/blog/#m=0&t=1&c=fks_084071083082080067086080080095086086084064082085087074083

(五)mini2440网卡驱动DM9000之dm9000_stop  

static int  dm9000_stop(struct net_device *ndev){

board_info_t *db = netdev_priv(ndev);

 if (netif_msg_ifdown(db))//检测ndev是否停止
  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);/*clear carrier*/

 /* free interrupt */
 free_irq(ndev->irq, ndev);

 dm9000_shutdown(ndev);

 return 0;
}

注:关于netif的函数在netdevice.h中,以后要认真读一下,目前还不会分析。只知道大概的意思


(六)mini2440网卡驱动DM9000之dm9000_start_xmit  

/* 调用时机:当网卡有数据需要发送的时候,该函数被调用 */

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 1;

 spin_lock_irqsave(&db->lock, flags);/*详见注视*/

 /* Move data to DM9000 TX RAM ,详见注释*/
 writeb(DM9000_MWCMD, db->io_addr);/*MWCMD:/*将io_addr写入寄存器MWCMD (Memory data write

                                                                                    command with addressincrement Register)中;进行

                                                                                    这个command操作后,向io_data写入的数据会传输到

                                                                                    dm9000内部TX SRAM中;
                                                                                                                      */

 (db->outblk)(db->io_data, skb->data, skb->len);      /*将内核网络套接字结构体sk_buff的数据skb->data写入

                                                                                    db->io_data中*/
 dev->stats.tx_bytes += skb->len;                              /*写完后将结构体net_device成员stats.tx_bytes加上刚刚

                                                                                   发送的字节数*/

 db->tx_pkt_cnt++;
 /* TX control: First packet immediately send, second packet queue */
 if (db->tx_pkt_cnt == 1) {
  /* Set TX length to DM9000 */
  iow(db, DM9000_TXPLL, skb->len);
  iow(db, DM9000_TXPLH, skb->len >> 8);

  /* Issue TX polling command */
  iow(db, DM9000_TCR, TCR_TXREQ); /* Cleared after TX complete *//*这个命令之后TX Buffer中的数据被发送出去*/

  dev->trans_start = jiffies; /* save the time stamp */
 } else {
/*如果发送的是第二个数据包(表明队列中此时有包发送),则将其加入队列中:将skb->len和

skb->ip_summed(控制校验操作)赋值给board_info_t中有关队列的相关成员。调用函数netif_stop_queue(dev),通

知内核现在queue已满,不能再将发送数据传到队列中,注:第二个包的发送将在tx_done中实现,这是因为当第一个数据发送完之后会产生一个中断,在中断中如果判断出事发送完成产生的中断,则会调用tx_done对应的函数,对这个函数的分析在中断函数分析中有详细讲解*/ 

  db->queue_pkt_len = skb->len;
  netif_stop_queue(dev);
 }

 spin_unlock_irqrestore(&db->lock, flags);/*释放自旋锁*/

 /* free this SKB */
 dev_kfree_skb(skb);

 return 0;
}

(1)锁机制:spin_lock(spinlock_t *my_spinlock)--->试图获取锁my_spinlock,如果锁已被别的进程持有,必须等待并不断测试锁的状态直到锁被释放。锁释放后可立即获取。

                       spin_lock_irqsave(spinlock_t *my_spinlock,unsigned long flags)--->与spin_lock()功能类似。额外的它自动屏蔽本地中断,并将CPU的当前状态寄存器值保存到变量flags中。

        二者的区别所在: spin_lock_irqsave屏蔽了本地中断,更加安全,如果用 spin_lock的话可能会照成死锁。比如:进程A与中断运行在同一块CPU上,并且进程A与中断处理程序都spin_lock(&lock)试图获取锁。那么好让我们看看会发生什么事情,进程A正在运行,并且获取了锁,此时产生中断的话,进程A会被置位TASK_INTERRUPT状态(即进程A会被中断),但是它还持有锁啊,它还没有释放锁呢,可是中断处理程序中也要试图获取锁啊,获取不了怎么办?那就忙等待。这样进程A无法运行,中断处理函数也无法运行,就产生了死锁。而使用spin_lock_irqsave的话,由于屏蔽了本地中断,所以就不发生死锁。

(2)Tx Buffer与Rx Buffer:在DM9000内部SRAM中,地址0x0000~0x0BFF是TX Buffer, 地址0x0C00~0x3FFF是RX Buffer。在发送一个包之前,包中的有效数据必须先被存储到TX Buffer中,接下来通过设置发送控制寄存器TCR的bit[0]位来将Tx Buffer中的数据发送出去。那么如何将数据存储到Tx Buffer中呢?首先让WMCMD指向映射过来的db->io_addr,接着将要发送到TX Buffer中的数据写到db->io_addr所指向的db->io_data区域。

(2)Tx Buffer只能存放两个数据包的原因:Tx Buffer中有两个发送索引,依次为indexI和indexII。首先,如果有数据需要发送,则先通过indexI将数据写入Tx Buffer,然后设置发送控制寄存器相应的位将indexI发送。在indexI发送时,还有数据需要发送的话,就将数据通过indexII写入Tx Buffer,当indexI发送结束后,就会依次将indexII发送出去。但我们同时要明白,虽然Tx Buffer中可以存放两个数据包,但是同一时间只能有一个数据包处于发送状态。





(七)mini2440网卡驱动DM9000之dm9000_timeout  


/* 当watchdog超时时调用该函数。主要的功能是保存寄存器地址,停止队列,重启并初始化DM9000,唤醒队列,恢复寄存器地址*/

static void dm9000_timeout(struct net_device *dev)
{
 board_info_t *db = netdev_priv(dev);/*调用函数netdev_priv (dev)获取结构体board_info_t*/
 u8 reg_save;
 unsigned long flags;

 /* Save previous register address */
 reg_save = readb(db->io_addr);/*将先前要操作的寄存器地址保存在变量reg_save中*/
 spin_lock_irqsave(&db->lock, flags);

 netif_stop_queue(dev);/*调用函数netif_stop_queue(dev),通知内核现在queue已满,不能再将发送数据传到队列中*/
 dm9000_reset(db);
 dm9000_init_dm9000(dev);
 /* We can accept TX packets again */
 dev->trans_start = jiffies;
 netif_wake_queue(dev);/*调用函数netif_wake_queue(dev)唤醒队列,开始接收内核传过来的待发送数据*/

 /* Restore previous register address */
 writeb(reg_save, db->io_addr);/*将函数一开始保存的寄存器地址(保存在变量reg_save中)赋值给db->io_addr,方便以后操作寄存器*/
 spin_unlock_irqrestore(&db->lock, flags);
}


(十)mini2440网卡驱动dm9000之dm9000_rx  

/* 我们上面讲到过,当网卡接收到数据时,会产生中断,在中断处理函数中通过中断状态寄存器判断出是否是接受中断,如果是接受中断的话就使用本函数处理接收到的数据 。在具体分析本函数之前我们必须先来了解一些基本的知识,请参见注释一*/

static void
dm9000_rx(struct net_device *dev)
{
board_info_t *db = netdev_priv(dev);
struct dm9000_rxhdr rxhdr; //关于dm9000_rxhdr详见注释二
struct sk_buff *skb;
u8 rxbyte, *rdptr;
bool GoodPacket;
int RxLen;

/* Check packet ready or not */
do {
ior(db, DM9000_MRCMDX); /* 读取寄存器MRCMDX,因为地址不增加,且无返回值,所以称为虚拟读。但这行代码并非生么都                                                                      没有做,因为这行代码之后,MRCMDX寄存器地址保存在了db->io_addr中,下面要读取CRCMDX                                                                    寄存器的值,只需要读取db->io_data即可。可参见下面的 rxbyte = readb(db->io_data);  */

/* Get most updated data */
rxbyte = readb(db->io_data);   //读取寄存器MRCMDX,rxbyte定义是u8 rxbyte,一个字节,所以这次读取首字节,即接收包标志位

/* Status check: this byte must be 0 or 1 */
if (rxbyte > DM9000_PKT_RDY)  /*  DM9000_PKT_RDY 定义为0x01,而rxbyte为我们读取的第一个字节,其值只能是0x00(表示还没接                                                              收)或0x01(表示已经接收) */
               {
dev_warn(db->dev, "status check fail: %d\n", rxbyte);
iow(db, DM9000_RCR, 0x00); /* Stop Device,RCR为接收控制寄存器,最低位为零则关闭使能位,停止接收 */
iow(db, DM9000_ISR, IMR_PAR); /* Stop INT request,ISR为中断状态寄存器,没懂??? */
return;
}

if (rxbyte != DM9000_PKT_RDY)
return;

/* A packet ready now  & Get status/length */
GoodPacket = true;
writeb(DM9000_MRCMD, db->io_addr); //将MRCMD地址写入db->io_addr

(db->inblk)(db->io_data, &rxhdr, sizeof(rxhdr)); /* 一次性读入四个字节的内容到rxhdr变量 */

RxLen = le16_to_cpu(rxhdr.RxLen); //读取长度

if (netif_msg_rx_status(db)) //判断接收状态,如接收正常则进行下列操作
dev_dbg(db->dev, "RX: status %02x, length %04x\n",
rxhdr.RxStatus, RxLen); //输出状态和长度

/* Packet Status check */
if (RxLen < 0x40) 
{
GoodPacket = false;
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "RX: Bad Packet (runt)\n");
}

if (RxLen > DM9000_PKT_MAX) //检查接收到的数据包的长度是否超出数据包的最大长度
 {
dev_dbg(db->dev, "RST: RX Len:%x\n", RxLen);
}

/* rxhdr.RxStatus is identical to RSR register. RSR为接收状态寄存器,是八位寄存器,其中每一位为一时都对应着一个错误*/
if (rxhdr.RxStatus & (RSR_FOE | RSR_CE | RSR_AE |
      RSR_PLE | RSR_RWTO |
      RSR_LCS | RSR_RF)) {
GoodPacket = false;
if (rxhdr.RxStatus & RSR_FOE)  //如果发生FIFO溢出错误
{
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "fifo error\n");
dev->stats.rx_fifo_errors++;
}
if (rxhdr.RxStatus & RSR_CE)  //如果发生CRC校验错误
{
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "crc error\n");
dev->stats.rx_crc_errors++;
}
if (rxhdr.RxStatus & RSR_RF)  //如果接收到数据小于64bytes
{
if (netif_msg_rx_err(db))
dev_dbg(db->dev, "length error\n");
dev->stats.rx_length_errors++;
}
}

/* Move data from DM9000 */
if (GoodPacket
    && ((skb = dev_alloc_skb(RxLen + 4)) != NULL)) {
skb_reserve(skb, 2); //预留两个字节,具体原因参见注释三
rdptr = (u8 *) skb_put(skb, RxLen - 4); //关于函数skb_put请参见注释四

/* Read received packet from RX SRAM */

(db->inblk)(db->io_data, rdptr, RxLen); //读取数据放入rdptr所在位置,rdptr位置为skb_tail,所以这句话就是读取 RX SRAM内容到skb
dev->stats.rx_bytes += RxLen;

/* Pass to upper layer */
skb->protocol = eth_type_trans(skb, dev); //函数eth_type_trans用于从以太网数据包中提取网络协议内容,并把它放入skb结构的相应位                                                                          //置,其的源代码及分析见注释五
netif_rx(skb); //调用netif_rx将数据交给协议栈,关于netif_rx函数详见注释六
dev->stats.rx_packets++;

} else {
/* need to dump the packet's data */

(db->dumpblk)(db->io_data, RxLen);
}
} while (rxbyte == DM9000_PKT_RDY);
}

注释一:我们讲到 在DM9000内部SRAM中,地址0x0000~0x0BFF是TX Buffer, 地址0x0C00~0x3FFF是RX Buffer。显而易见,接收到的数据会存储在 RX Buffer中,其格式如下:
(七)mini2440网卡驱动dm9000之dm9000_rx - 航天 - 航天的博客
如上图所示,存储在 RX Buffer中的每个包都有四个字节的信息头,我们可以使用 MRCMDX和MRCMD寄存器来得到这些信息。第一个字节用来检查数据包是否接收到了RX Buffe中,如果这个字节是01,则意味着一个包已经接收 。如果是00,则还没有数据包被接收到 RX Buffe中。第二个字节保存接收到的数据包的一些状态信息,格式和RSR寄存器(接收状态寄存器)一样,根据这个格式,接收到的数据包能被检验是正确的还是错误的。第三个和第四个字节保存了接收的数据包的长度。这四个字节以外的其他字节就是接收包的数据。
接收函数主要完成以下几方面的工作:一是检查接收到的数据包是否正确;二是根据数据包的长度在在内核空间为数据包申请一个sk_buff;三是把数据包复制到sk_buff,填写相关域段和统计信息并把sk_buff插入相应的输入队列;四是释放数据包占用的芯片buffer。
注释二:
struct dm9000_rxhdr {
u8 RxPktReady;
u8 RxStatus;
__le16 RxLen;
} __attribute__((__packed__));
注释三: skb_reserve(skb, 2);预留两个字节的内容,为什么这样呢,让我们来看一看下图:
(七)mini2440网卡驱动dm9000之dm9000_rx - 航天 - 航天的博客
 上图两箭头之间是ip头,ip头必须是16字节对齐的,但ip却是6+6+2为14个字节,所以要预留两个字节,一边16字节对齐。
注释四:
skb_put() -- 扩展缓冲区中数据区域的大小;增加len个字节

/usr/src/linux-2.6.19/include/linux/skbuff.h
static inline unsigned char *skb_put(struct sk_buff *skb, unsigned int len)
{
----unsigned char *tmp = skb->tail;
----
SKB_LINEAR_ASSERT(skb);
----
skb->tail += len;
----
skb->len += len; 
----
if (unlikely(skb->tail>skb->end))
--------
skb_over_panic(skb, len, current_text_addr());
----
return tmp;
}


+------------+                 +------------+
|            |                 |            |
|            |                 |            |
|            |                 |            |
|            |                 |            |
|            |                 |            |
|------------|<-- tail         |------------|
<-- tail
|            |                 |            |
|            |                 |            |
|            |                 
|------------|
<-- tail + len
|            |                 |            |
+------------+                 +------------+
注释五:
/usr/src/linux-2.6.19/net/ethernet/eth.c

__be16 eth_type_trans(struct sk_buff *skb, struct net_device *dev)
{
    struct ethhdr *eth;
    unsigned char *rawp;
    skb->mac.raw = skb->data;
    skb_pull(skb, ETH_HLEN);
    eth = eth_hdr(skb);
    if (is_multicast_ether_addr(eth->h_dest)) {
        if (!compare_ether_addr(eth->h_dest, dev->broadcast))
            skb->pkt_type = PACKET_BROADCAST;
        else
            skb->pkt_type = PACKET_MULTICAST;
    }
    else if (1 /*dev->flags&IFF_PROMISC */ ) {
        if (unlikely(compare_ether_addr(eth->h_dest, dev->dev_addr)))
            skb->pkt_type = PACKET_OTHERHOST;
    }
    if (ntohs(eth->h_proto) >= 1536)
        return eth->h_proto;
    rawp = skb->data;
    if (*(unsigned short *)rawp == 0xFFFF)
        return htons(ETH_P_802_3);
    return htons(ETH_P_802_2);
}
这个函数从以太网包中抽取协议标识符(在这里是ETH_P_IP);它还要赋值skb->mac.raw,从包数据中输出硬件包头,并设置skb->pkt_type。最后一项在skb分配时缺省为 PACKET_HOST(表明包被指向这个主机),当然它也可以改为符合以太网目的地址得其它值
注释六:
int netif_rx(struct sk_buff *skb)
{
struct softnet_data *queue;
unsigned long flags;

/* if netpoll wants it, pretend we never saw it */
if (netpoll_rx(skb))
return NET_RX_DROP;

if (!skb->tstamp.tv64)
net_timestamp(skb);

/*
 * The code is rearranged so that the path is the most
 * short when CPU is congested, but is still operating.
 */
local_irq_save(flags);
queue = &__get_cpu_var(softnet_data);

__get_cpu_var(netdev_rx_stat).total++;
if (queue->input_pkt_queue.qlen <= netdev_max_backlog) {
if (queue->input_pkt_queue.qlen) {
enqueue:
__skb_queue_tail(&queue->input_pkt_queue, skb);
local_irq_restore(flags);
return NET_RX_SUCCESS;
}

napi_schedule(&queue->backlog);
goto enqueue;
}

__get_cpu_var(netdev_rx_stat).dropped++;
local_irq_restore(flags);

kfree_skb(skb);
return NET_RX_DROP;
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值