(原创文章,转载请注明出处,谢谢。)
驱动编译运行,呵呵,跑起来了,欣喜之余,要看看还有什么问题没有解决,还有什么吸取的经验。首先:
1.DM9000的特性没有完全支持;
* 没有操作DM9000的eeprom部分;
* 没有调试dm9000的phy部分;
* dm9000支持发送两个队列,我们只使用了一个;
* dm9000发送采取的是查询策略,而非中断策略。效率低下。
2.QEMU 仿真 DM9000 有些问题,尚未确认。
3.延时函数。
我们一个问题一个问题说:
DM9000的EEPROM的支持问题,由于不影响大局可以暂时放一样,但是听rickleaf和我交流时,所这一块仿真时有问题。即在QEMU MINI2440 上不能正常工作,目前还不能确认是QEMU的问题还是驱动本省的问题,这个问题姑且放一放。
DM9000的内部的PHY和DM9000的MAC层已经无缝的结合在一起,两个自动协商,不要对其做匹配,即MAC工作在100Mbps下,phy肯定也工作在100Mbps下,MAC工作在全双工下,phy也工作在全双工下。这不用烦心。我实际的读过phy的寄存器,不是很成功,可以考虑是QEMU的仿真问题。
DM9000支持的两个发送队列的问题,我写得驱动只用了一个,没有使用第二个。这个问题,动手做过实验。发现程序并不稳定,我参考了Linux的驱动,在QEMU上仿真时,一段时候后就完蛋了。由于没有实际的开发板验证,实在是不知道是代码自身的问题,还是QEMU的问题。以下是改成双缓冲的 发送代码,在 dm9000_softc_t中增加了两个字段,txPktCnt,用于表示当前队列中发送包的数量。txPktLen表示队列中第二包的长度。
/* Send packet */
关于发送采用中断模式也是有同样的问题,我也做了实验,在QEMU上运行时也有不稳定的问题,但代码层面我的确找不到问题了。请高手们多多指正。我改来改去,发现查询方式最稳定,呵呵,那就用查询方式发布吧。
/* * Driver transmit daemon */最有关于一个udelay函数的问题,
udelay函数RTEMS没有提供该函数的实现,需要自己撰写。我就偷懒写了个for 循环的延迟。这样做是不对的。由于没有实际的硬件,QEMU执行ARM的代码完全不像实际那样。所以靠指令实现精确的1us的延迟,是不太可能。实际中使用应采用定时器精确测量多少条循环指令可以实现1us的延迟。这样才能做出较好的udelay来。
下面和大家深入地讨论一些问题:
查看代码,发现 mbuf 的相关宏 MGETHDR、MGET 使用 splimp()、splimp、splx()三个函数保护其临界区域。然而,在 rtems_bsdnet_internal.h 中这三个的宏定义为:
#define splnet() 0
#define splimp() 0
#define splx(_s) do { (_s) = 0; } while(0)
都是为空的。并且网络中的许多操作都是依靠这几个锁来实现临界区域的保护。这三个宏这么定义,显然什么保护都没有。照理说,不保护,那么在多任务情况下,那是得不到正确结果的,然而我们实际使用中却能得到正确结果,这又是为什么呢?
阅读RTEMS官方的文档 networking.pdf 可以找到答案。他们提供了一个 rtems_bsdnet_semaphore_obtain() 和 rtems_bsdnet_semaphore_release() 进行这个级别的互斥。用于解决网络内部的数据结构一致性的问题。
这里还有最后问题:为什么要这样设计呢?这样设计的优缺点在哪里?
这个问题,我想了很久,这个答案可能会仁者见仁智者见智,姑且抛砖引玉吧。这么设计首先不像uC/OS-II这样的操作系统也不像Linux这样的操作系统,动不动就关中断,我们都知道 RTOS 的最重要的一个性能指标就是实时性。其中对中断的响应延迟时间是个非常重要的指标。RTEMS这样的设计,最明显的就是不用关中断,即使关了中断,时间也非常的短,直接的好处就是响应中断延迟小。自然实时性就会好。
这么设计的优点就是实时性好。除去这个优点,我们看看缺点在哪里。首先,网络的接收、发送、协议栈的运转是三个任务。他们之间不能同时运行。这会对网络的性能有比较大的影响,尤其是我们的系统中有1个以上的网络驱动,假设有两个网口,那么就有5个任务。5个任务抢占调度,在一个网口有大数据量发送时,必然会影响第二个网口的性能。当然这也不是一成不变的,如果MAC支持DMA链,可以在DMA链上多放一些缓冲区,可以对网络瞬时来的数据做一定的缓冲。对性能也会有比较大的改善。但仅对于DM9000这种没有DMA的芯片来说,对性能多多少少还是有些影响吧,特别是对突发访问有影响。(我手头没有条件做测试,不然肯定会做个实验,在实际板子上验证我的思路。)
讲到这里,我想起来了,看到Linux里DM9000发送时,使用两个发送缓冲区,发送采用中断触发。看看他的临界区域的保护,两个开关中断的自旋锁之间竟然放着数据包的拷贝过程(蓝色字体标识的)。这个在实时操作系统中是难以想象的,关中断时间长不说,占用总线资源时间也长。这对RTOS来说是个噩梦。所以我考虑来考虑去还是用了查询方法,也就是基于实时性的考虑,虽然那样写在实际中DM9000的工作效率并不高。但可以使用一个队列元素,采用中断触发,作为一些性能改善。如果一些朋友有更好的建议,更好的解释和方法,恳请一定赐教!!!谢谢。
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 */
writeb(DM9000_MWCMD, db->io_addr);
(db->outblk)(db->io_data, skb->data, skb->len);
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) {
/* 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 */
dev->trans_start = jiffies; /* save the time stamp */
} else {
/* Second packet */
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;
}
(原创文章,转载请注明出处,谢谢。)