(原创文章,转载注明出处,谢谢)
这些天花了些时间把 RTEMS 4.9.5 关于 QEMU mini2440 bsp 弄了一下,弄得不好,把我遇到的问题说出来和大家分享一下。便于大家在相关问题上的继续研究。也请您不吝赐教,指出我移植中的问题。
开发环境的建立请参考:
QEMU MINI2440 的 Linux FC 下网络配置
http://blog.csdn.net/coolbacon/archive/2011/03/16/6252938.aspx
关于RTEMS MINI2440的QEMU仿真从UBOOT加载问题的研究
http://blog.csdn.net/coolbacon/archive/2011/03/20/6262776.aspx
RTEMS 4.9.5 在 QEMU MINI2440 上的移植发布啦……
http://blog.csdn.net/coolbacon/archive/2011/03/22/6269551.aspx
s3c2440的移植相对来讲,更加简单。因为s3c2410和s3c2440非常接近。可以借用gp32、smdk2410、s3c2410的文件修改生成 mini2440 的bsp。这里可以参考rickleaf写的移植记录:
http://blog.csdn.net/rickleaf/archive/2011/03/16/6254361.aspx
也可以参考我关于ARM移植的这一块的博文,关于at91sam9260的移植部分,大同小异。
这块就不细说了,想练手的朋友可以试试,如您遇到问题,欢迎找我讨论。
移植好了以后, 就开始做 dm9000 的驱动。
首先在c/src/lib/libbsp/arm/mini2440/下建立一个network/文件夹,在该文件夹下建立一个network.c的空文件。关于dm9000的驱动就写在这个文件里。然后修改c/src/lib/libbsp/arm/mini2440/Makefile.am文件。修改这个文件的目的,主要是:
在configure配置代码时,如果指定了--enable-networking 参数,即将网络代码编译进bsp;如果指定了--enable-networking ,也需要将我们编写的驱动编译进入bsp中。
我把Makefile.am文件内容贴在这里,修改的部分用红字标识出来。
##
## $Id: Makefile.am,v 1.5.2.1 2008/09/29 01:47:32 ralf Exp $
##
ACLOCAL_AMFLAGS = -I ../../../../aclocal
include $(top_srcdir)/../../../../automake/compile.am
include $(top_srcdir)/../../bsp.am
dist_project_lib_DATA = bsp_specs
include_HEADERS = include/bsp.h
include_HEADERS += smc/smc.h
include_HEADERS += ../../shared/include/tm27.h
nodist_include_HEADERS = include/bspopts.h
DISTCLEANFILES = include/bspopts.h
nodist_include_HEADERS += ../../shared/include/coverhd.h
noinst_PROGRAMS =
EXTRA_DIST = start/start.S
start.$(OBJEXT): start/start.S
$(CPPASCOMPILE) -o $@ -c $<
project_lib_DATA = start.$(OBJEXT)
dist_project_lib_DATA += startup/linkcmds
startup_SOURCES = ../../shared/bsplibc.c ../../shared/bsppost.c /
startup/bspstart.c startup/bspclean.c startup/memmap.c /
../../shared/bootcard.c ../../shared/sbrk.c /
../../shared/bsppredriverhook.c ../../shared/gnatinstallhandler.c
console_SOURCES = console/uart.c ../../shared/console.c
abort_SOURCES = ../shared/abort/abort.c
smc_SOURCES = smc/smc.c smc/smc.h
if HAS_NETWORKING
network_CPPFLAGS = -D__INSIDE_RTEMS_BSD_TCPIP_STACK__
noinst_PROGRAMS += network.rel
network_rel_SOURCES = network/network.c
network_rel_CPPFLAGS = $(AM_CPPFLAGS) $(network_CPPFLAGS)
network_rel_LDFLAGS = $(RTEMS_RELLDFLAGS)
endif
noinst_LIBRARIES = libbsp.a
libbsp_a_SOURCES = $(startup_SOURCES) $(console_SOURCES) $(abort_SOURCES) /
$(smc_SOURCES)
libbsp_a_LIBADD = ../../../libcpu/@RTEMS_CPU@/shared/arm920.rel /
../../../libcpu/@RTEMS_CPU@/@RTEMS_CPU_MODEL@/clock.rel /
../../../libcpu/@RTEMS_CPU@/@RTEMS_CPU_MODEL@/timer.rel /
../../../libcpu/@RTEMS_CPU@/@RTEMS_CPU_MODEL@/irq.rel
if HAS_NETWORKING
libbsp_a_LIBADD += network.rel
endif
include $(srcdir)/preinstall.am
include $(top_srcdir)/../../../../automake/local.am
以上修改的地方不多,但是位置不能错。
确认我们要准备以下资源:
1.手里有dm9000、s3c2440的datasheet;
2.找一份linux的源代码,从中找出dm9000的驱动代码;
3.手里有networking.pdf,RTEMS官方关于网络方面的编程指导手册。
这里我们就开始编写DM9000的驱动了。RTEMS的网络驱动不同于其他驱动,其他驱动全部是通过RTEMS的IO管理器管理的,而网络驱动是交由BSD的协议栈管理的。这是RTEMS系统中唯一一个特别的驱动。BSD的在RTEMS下的整个系统模型是:
这个图中,虚线方框内基本上就是我们驱动需要完成的目标。这个图可以这样理解:
我看到networking.pdf 手册上问了一系列的问题,问得非常好。如果回答完全,在理解以上架构下写一个驱动是没有任何问题的。这里我翻译如下:
驱动由以下函数组成:
其他函数都是辅助这些主要函数的,:)。
撰写驱动时,要理解一个重要的数据结构:struct ifnet。ifnet 结构中的几个域是由驱动提供维护的:
DM9000的自定义数据结构是:
typedef struct
{
/*
* Connection to networking code
* This entry *must* be the first in the sonic_softc structure.
*/
struct arpcom arpcom;
/*
* Interrupt vector
*/
rtems_vector_number vector;
/*
* Indicates configuration
*/
int acceptBroadcast;
/*
* Task waiting for interrupts
*/
rtems_id rxDaemonTid;
rtems_id txDaemonTid;
uint32_t (*outmbuf)(struct mbuf *);
void (*outblk)(volatile void *, int);
void (*inblk)(void *, int);
void (*rx_status)(uint16_t *, uint16_t *);
} dm9000_softc_t;
rtems_dm9000_attach 函数:
我的 rtems_dm9000_attach函数比较简单,基本上都没啥了,都是些必要的操作。我简单的做了一些注释:
int rtems_dm9000_attach (
struct rtems_bsdnet_ifconfig *config,
void *chip /* only one ethernet, so no chip number */
)
{
struct ifnet *ifp;
int mtu;
int unitnumber;
char *unitname;
/*
* Parse driver name 分析驱动的名称
*/
if ((unitnumber = rtems_bsdnet_parse_driver_name (config, &unitname)) < 0)
return 0;
/*
* Is driver free? 判断驱动是否已经被链接过了
*/
if (unitnumber != 0) {
printf ("Bad DM9000 unit number./n");
return 0;
}
ifp = &softc.arpcom.ac_if;
if (ifp->if_softc != NULL) {
printf ("Driver already in use./n");
return 0;
}
/*
* zero out the control structure 初始化数据结构 softc
*/
memset( &softc, 0, sizeof(softc) );
/* get the MAC address from the chip */
/*获取设备的MAC地址,这个MAC地址呢获取的方法我觉得各个系统是千差万别的,
我这里偷懒,就随便弄了一下
这个MAC地址这样配置后,实际的效果是:00:00:01:23:45:00
*/
softc.arpcom.ac_enaddr[0] = 0x00;
softc.arpcom.ac_enaddr[1] = 0x00;
softc.arpcom.ac_enaddr[2] = 0x01;
softc.arpcom.ac_enaddr[3] = 0x23;
softc.arpcom.ac_enaddr[4] = 0x45;
softc.arpcom.ac_enaddr[5] = 0x00;
if (config->mtu) {
mtu = config->mtu;
} else {
mtu = ETHERMTU;
}
softc.acceptBroadcast = !config->ignore_broadcast;
/*
* Set up network interface values
*/
ifp->if_softc = &softc;
ifp->if_unit = unitnumber;
ifp->if_name = unitname;
ifp->if_mtu = mtu;
ifp->if_init = dm9000_init;
ifp->if_ioctl = dm9000_ioctl;
ifp->if_start = dm9000_start;
ifp->if_output = ether_output;
ifp->if_flags = IFF_BROADCAST;
if (ifp->if_snd.ifq_maxlen == 0) {
ifp->if_snd.ifq_maxlen = ifqmaxlen;
}
/*
* Attach the interface
*/
if_attach (ifp);
ether_ifattach (ifp);
return 1;
}
dm9000_init 函数:
void dm9000_init(void *arg)
{
dm9000_softc_t *sc = arg;
struct ifnet *ifp = &sc->arpcom.ac_if;
if (sc->txDaemonTid == 0) {/*需要判断一下驱动是不是已经启动了,不然会造成比较严重的错误*/
/*初始化DM9000的硬件,硬件的初始化我不赘述,这里也不贴它们的代码了,
大家可以查看dm9000的相关文档和资料,参考linux的驱动 去理解,都是死东西,没啥好说的。 */
dm9000_init_hw(sc);
/* Start driver tasks */
sc->rxDaemonTid = rtems_bsdnet_newproc("ENrx",
4096,
dm9000_rxDaemon,
sc);
sc->txDaemonTid = rtems_bsdnet_newproc("ENtx",
4096,
dm9000_txDaemon,
sc);
/*创建并 启动用于轮询方式的网络任务 ,需要定义宏 CONFIG_NET_POLL_CONTROLLER */
#ifdef CONFIG_NET_POLL_CONTROLLER
gName = rtems_build_name('D', 'E', 'A', 'M');
rtems_task_create(gName, 1, RTEMS_MINIMUM_STACK_SIZE * 2,
RTEMS_DEFAULT_MODES,
RTEMS_DEFAULT_ATTRIBUTES,
&gID);
rtems_task_start(gID, dm9000_poll_task, 1);
#endif
} /* if txDaemonTid */
/*安装网络的中断*/
#if !defined(CONFIG_NET_POLL_CONTROLLER)
/* install the interrupt handler */
BSP_install_rtems_irq_handler(&dm9000_isr_data);
#endif
/* EMAC doesn't support promiscuous, so ignore requests
混杂模式,即DM9000像一个监听器,监视所有网络上的数据包,不管其是不是发送给自己的。没打算支持这个功能。 :)
*/
if (ifp->if_flags & IFF_PROMISC) {
printf ("Warning - DM9000 Ethernet driver"
" doesn't support Promiscuous Mode!/n");
}
/*
* Tell the world that we're running.
*/
ifp->if_flags |= IFF_RUNNING;
}
dm9000_start/dm9000_stop函数:
void dm9000_start(struct ifnet *ifp)
{
/*开始函数这样写几乎是定式,没啥好说的*/
dm9000_softc_t *sc = ifp->if_softc;
rtems_event_send(sc->txDaemonTid, START_TRANSMIT_EVENT);
ifp->if_flags |= IFF_OACTIVE;
}
void dm9000_stop (dm9000_softc_t *sc)
{
struct ifnet *ifp = &sc->arpcom.ac_if;
ifp->if_flags &= ~IFF_RUNNING;
/*
* Stop the transmitter and receiver.
*这里我只停了接收,没有停发送。其实不然:
要解释一下:
整个驱动中,我采用的策略是:发送使用查询,接收使用中断。由于发送是协议栈控制的,实际上
ifp->if_flags &= ~IFF_RUNNING;已经将发送停止了。接收是由DM9000控制的,所以要通知DM9000停止接收数据。
当然DM9000可以关闭发送,已经没有那个必要了。
*/
DM9000_iow(DM9000_RCR, 0x00); /* Disable RX */
}
(原创文章,转载注明出处,谢谢)