/* 调用时机:当网卡有数据需要发送的时候,该函数被调用 */
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中可以存放两个数据包,但是同一时间只能有一个数据包处于发送状态。