rtems的网络驱动移植,首先要知道如何将设备初始化函数放入初始化过程中,如何将这个网卡设备注册到系统中。这涉及到rtems的系统初始化,接下来就具体分析初始化过程:
系统初始化第一阶段:
这部分代码主要是用汇编写的,属于bsp的一部分,然后rtems进入基于c代码的初始化第二阶段,但在进入第二阶段之前,必须要有一个可靠的运行环境,这就是第一阶段的汇编代码的工作,总结如下:
- 屏蔽中断
- 初始化cpu工作模式
- 建立内核堆栈
- 对bss段清零
- 建立基本的内存布局
最开始的代码位于/rtems/c/src/lib/libbsp/pc386(这里选择对应的处理器)/start/start.s
函数调用如下图所示:
大致的执行流程如下:
- 屏蔽中断
- 读取grub传递的multiboot_info,存放到boot_multiboot_info结构中
- 跳转到_load_segment处,加载与pc硬件相关的全局描述符和中断描述符
- 跳转到_establish,建立rtems内核栈空间
- 清除BSS
- 调用initvideo函数进行显示初始化
- 调用checkcputypesetcr0查找cpu类型
- 设定调用参数,调用第一个c函数boot_card
系统初始化第二阶段:
这一阶段主要是初始化内核组件和驱动程序做准备,与bsp联系紧密,主要工作如下:
- 初始化rtems_cpu_table结构体的cpu_table全局变量
- 初始化rtems_configuration_table结构体的configuration全局变量
- 设置rtems workspace区域
- 初始化中断和异常管理 初始化pci bios interface
执行流程:
boot_card函数首先更新cpu配置表和rtems配置表,然后调用bsp_start函数,该函数主要完成以下工作:
- 计算1ms时间指令的loop值
- 进一步更新rtems的cpu配置表
- 制定rtems的workspace的起始地址并分配空间
- 初始化pci bios interface
执行完bsp_start函数后,进入第三阶段,开始执行rtems_initialize_executive_early函数。
系统初始化第三阶段:
这一阶段的工作量相对于前两个阶段要大很多,其最终目的是完成多任务切换,并切换到用户提供的任务,这部分主要工作是:
- 初始化rtems核心层和系统服务层的功能组件
- 初始化驱动程序
- 进行多任务初始化
初始化管理器组件介绍:
当板级支持包bsp完成硬件级的基本初始化工作后,把控制权交给初始化管理器,初始化管理器的工作主要是负责启动和关闭rtems,启动rtems包括创建并且启动所有的配置好的初始化任务,并且初始化rtems系统使用到的设备驱动程序。
初始化rtems:
初始化管理器的主要操作体现在rtems_initialize_executive函数的实现机制,如果不使用该机制,也可以采用rtems_initialize_executive_early函数和rtems_initialize_executive_late函数的实现机制初始化。在rtems只能使用一种方法初始化系统化,不能重复初始化。
初始化所有的驱动程序:
这分成四个步骤,在具体执行初始化驱动程序之前,需要执行钩子函数(predriver_hook),然后执行IO_initialize_all_drivers()来完成具体的驱动程序初始化过程。
具体代码如下:
bootcard.c
/**
* @file
*
* @ingroup bsp_bootcard
*
* @brief Standard system startup.
*
* This is the C entry point for ALL RTEMS BSPs. It is invoked
* from the assembly language initialization file usually called
* start.S. It provides the framework for the BSP initialization
* sequence. For the basic flow of initialization see RTEMS C User's Guide,
* Initialization Manager.
*
* This style of initialization ensures that the C++ global
* constructors are executed after RTEMS is initialized.
* Thanks to Chris Johns <cjohns@plessey.com.au> for the idea
* to move C++ global constructors into the first task.
*/
/*
* COPYRIGHT (c) 1989-2014.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#include <bsp/bootcard.h>
#include <rtems.h>
#include <rtems/sysinit.h>
/*
* At most a single pointer to the cmdline for those target
* short on memory and not supporting a command line.
*/
const char *bsp_boot_cmdline;
RTEMS_SYSINIT_ITEM(
bsp_work_area_initialize,
RTEMS_SYSINIT_BSP_WORK_AREAS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
RTEMS_SYSINIT_ITEM(
bsp_start,
RTEMS_SYSINIT_BSP_START,
RTEMS_SYSINIT_ORDER_MIDDLE
);
RTEMS_SYSINIT_ITEM(
bsp_predriver_hook,
RTEMS_SYSINIT_BSP_PRE_DRIVERS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
/*
* This is the initialization framework routine that weaves together
* calls to RTEMS and the BSP in the proper sequence to initialize
* the system while maximizing shared code and keeping BSP code in C
* as much as possible.
*/
void boot_card(
const char *cmdline
)
{
rtems_interrupt_level bsp_isr_level;
/*
* Make sure interrupts are disabled.
*/
(void) bsp_isr_level;
rtems_interrupt_local_disable( bsp_isr_level );
bsp_boot_cmdline = cmdline;
rtems_initialize_executive();
/***************************************************************
***************************************************************
* APPLICATION RUNS NOW!!! We will not return to here!!! *
***************************************************************
***************************************************************/
}
其中包含了三个初始化函数的调用:
RTEMS_SYSINIT_ITEM(
bsp_work_area_initialize,
RTEMS_SYSINIT_BSP_WORK_AREAS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
RTEMS_SYSINIT_ITEM(
bsp_start,
RTEMS_SYSINIT_BSP_START,
RTEMS_SYSINIT_ORDER_MIDDLE
);
RTEMS_SYSINIT_ITEM(
bsp_predriver_hook,
RTEMS_SYSINIT_BSP_PRE_DRIVERS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
boot_card函数中,包含rtems_initialize_executive函数,这是初始化管理函数,位于exinit.c文件中:
exinit.c
/**
* @file
*
* @brief Initialization Manager
*
* @ingroup ClassicRTEMS
*/
/*
* COPYRIGHT (c) 1989-2014.
* On-Line Applications Research Corporation (OAR).
*
* The license and distribution terms for this file may be
* found in the file LICENSE in this distribution or at
* http://www.rtems.org/license/LICENSE.
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/system.h>
#include <rtems/config.h>
#include <rtems/extensionimpl.h>
#include <rtems/init.h>
#include <rtems/ioimpl.h>
#include <rtems/sysinit.h>
#include <rtems/score/sysstate.h>
#include <rtems/score/apimutex.h>
#include <rtems/score/copyrt.h>
#include <rtems/score/heap.h>
#include <rtems/score/interr.h>
#include <rtems/score/isr.h>
#include <rtems/score/priority.h>
#include <rtems/score/schedulerimpl.h>
#include <rtems/score/smpimpl.h>
#include <rtems/score/timecounter.h>
#include <rtems/score/threadimpl.h>
#include <rtems/score/todimpl.h>
#include <rtems/score/wkspace.h>
const char _Copyright_Notice[] =
"COPYRIGHT (c) 1989-2008.\n\
On-Line Applications Research Corporation (OAR).\n";
static Objects_Information *
_Internal_Objects[ OBJECTS_INTERNAL_CLASSES_LAST + 1 ];
static Objects_Information *_RTEMS_Objects[ OBJECTS_RTEMS_CLASSES_LAST + 1 ];
static Objects_Information *_POSIX_Objects[ OBJECTS_POSIX_CLASSES_LAST + 1 ];
Objects_Information ** const
_Objects_Information_table[ OBJECTS_APIS_LAST + 1 ] = {
NULL,
&_Internal_Objects[ 0 ],
&_RTEMS_Objects[ 0 ],
&_POSIX_Objects[ 0 ]
};
API_Mutex_Control *_RTEMS_Allocator_Mutex;
API_Mutex_Control *_Once_Mutex;
static void rtems_initialize_data_structures(void)
{
/*
* Dispatching and interrupts are disabled until the end of the
* initialization sequence. This prevents an inadvertent context
* switch before the executive is initialized.
*
* WARNING: Interrupts should have been disabled by the BSP and
* are disabled by boot_card().
*/
/*
* Initialize any target architecture specific support as early as possible
*/
_CPU_Initialize();
_Thread_Dispatch_initialization();
_ISR_Handler_initialization();
_API_Mutex_Initialization( 2 );
_API_Mutex_Allocate( &_RTEMS_Allocator_Mutex );
_API_Mutex_Allocate( &_Once_Mutex );
_Thread_Handler_initialization();
_Scheduler_Handler_initialization();
_SMP_Handler_initialize();
}
RTEMS_LINKER_ROSET( _Sysinit, rtems_sysinit_item );
RTEMS_SYSINIT_ITEM(
rtems_initialize_data_structures,
RTEMS_SYSINIT_DATA_STRUCTURES,
RTEMS_SYSINIT_ORDER_MIDDLE
);
/*
* No threads should be created before this point!!!
* _Thread_Executing and _Thread_Heir are not set.
*
* At this point all API extensions are in place. After the call to
* _Thread_Create_idle() _Thread_Executing and _Thread_Heir will be set.
*
* Scheduling can properly occur afterwards as long as we avoid dispatching.
*/
RTEMS_SYSINIT_ITEM(
_Thread_Create_idle,
RTEMS_SYSINIT_IDLE_THREADS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
/* Initialize I/O drivers.
*
* Driver Manager note:
* All drivers may not be registered yet. Drivers will dynamically
* be initialized when registered in level 2,3 and 4.
*/
RTEMS_SYSINIT_ITEM(
_IO_Initialize_all_drivers,
RTEMS_SYSINIT_DEVICE_DRIVERS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
void rtems_initialize_executive(void)
{
const volatile rtems_sysinit_item *cur = RTEMS_LINKER_SET_BEGIN(_Sysinit );
const volatile rtems_sysinit_item *end = RTEMS_LINKER_SET_END( _Sysinit );
/* Invoke the registered system initialization handlers */
while ( cur != end ) {
( *cur->handler )();
++cur;
}
_System_state_Set( SYSTEM_STATE_UP );
_SMP_Request_start_multitasking();
_Thread_Start_multitasking();
/*******************************************************************
*******************************************************************
*******************************************************************
****** APPLICATION RUNS HERE ******
****** THE FUNCTION NEVER RETURNS ******
*******************************************************************
*******************************************************************
*******************************************************************/
}
在该文件中,包含了IO_initialize_all_drivers()函数的调用:
RTEMS_SYSINIT_ITEM(
_IO_Initialize_all_drivers,
RTEMS_SYSINIT_DEVICE_DRIVERS,
RTEMS_SYSINIT_ORDER_MIDDLE
);
IO_initialize_all_drivers()函数的定义在io.c文件中
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/ioimpl.h>
bool _IO_All_drivers_initialized;
void _IO_Initialize_all_drivers( void )
{
rtems_device_major_number major;
_IO_All_drivers_initialized = true;
for ( major=0 ; major < _IO_Number_of_drivers ; major ++ )
(void) rtems_io_initialize( major, 0, NULL );
}
可以看到,主要是利用for循环rtems_io_initialize函数进行初始化,其中major就是driver的注册的数量,查找rtems_io_initialize函数的实现,在ioInitialize.c文件中:
ioInitialize.c
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <rtems/ioimpl.h>
rtems_status_code rtems_io_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *argument
)
{
rtems_device_driver_entry callout;
if ( major >= _IO_Number_of_drivers )
return RTEMS_INVALID_NUMBER;
callout = _IO_Driver_address_table[major].initialization_entry;
return callout ? callout(major, minor, argument) : RTEMS_SUCCESSFUL;
}
从该函数的实现可以看出,其实驱动函数的初始化就是调用_IO_Driver_address_table这个表中驱动设备的初始化入口成员函数,也就是initialization_entry,现在就比较明朗了,只要查找_IO_Driver_address_table表,然后向其中添加设备注册即可。该表位于confdefs.h文件中。
rtems_driver_address_table
_IO_Driver_address_table[ CONFIGURE_MAXIMUM_DRIVERS ] = {
#ifdef CONFIGURE_BSP_PREREQUISITE_DRIVERS
CONFIGURE_BSP_PREREQUISITE_DRIVERS,
#endif
#ifdef CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS
CONFIGURE_APPLICATION_PREREQUISITE_DRIVERS,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER
CONSOLE_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER
CLOCK_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER
RTC_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_WATCHDOG_DRIVER
WATCHDOG_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_STUB_DRIVER
DEVNULL_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_ZERO_DRIVER
DEVZERO_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_IDE_DRIVER
IDE_CONTROLLER_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_ATA_DRIVER
ATA_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_FRAME_BUFFER_DRIVER
FRAME_BUFFER_DRIVER_TABLE_ENTRY,
#endif
#ifdef CONFIGURE_APPLICATION_EXTRA_DRIVERS
CONFIGURE_APPLICATION_EXTRA_DRIVERS,
#endif
#ifdef CONFIGURE_APPLICATION_NEEDS_NULL_DRIVER
NULL_DRIVER_TABLE_ENTRY
#elif !defined(CONFIGURE_APPLICATION_NEEDS_CONSOLE_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_CLOCK_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_RTC_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_STUB_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_ZERO_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_IDE_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_ATA_DRIVER) && \
!defined(CONFIGURE_APPLICATION_NEEDS_FRAME_BUFFER_DRIVER) && \
!defined(CONFIGURE_APPLICATION_EXTRA_DRIVERS)
NULL_DRIVER_TABLE_ENTRY
#endif
};
可以看到这个table包含了很多外设的宏entry入口,比如console、clock等,接下来以console串口的entry举例,CONSOLE_DRIVER_TABLE_ENTRY,该宏定义位于console.h中:
#define CONSOLE_DRIVER_TABLE_ENTRY \
{ console_initialize, console_open, console_close, \
console_read, console_write, console_control }
发现宏中包含了很多console函数的声明:
console_initialize:
rtems_device_driver console_initialize(
rtems_device_major_number major,
rtems_device_minor_number minor,
void *arg
);
参数major:设备驱动的主号码
参数minor:设备驱动的次号码
当返回RTEMS_SUCCESSFUL表示初始化成功
这些函数的实现基本都是在具体的处理器和bsp代码中。所以可移植性很强。只要保证接口函数相同即可。
最后在rtems_initialize_executive函数中,执行_Thread_Start_multitasking();开始多任务模式,rtems的系统初始化工作就结束了。