B1202是B1205的小框,直接放楼道,接入设备,上行GE,或者EPON,下行4框可以混插ASL和FXS板,提供VOIP和ASL服务,CSM主控板含VOIP的DSP芯片处理VOIP业务
支持ADSL2+接入,ADSL2+ 板卡密度为32路/卡,每框最大支持128路
支持VoIP接入,POTS板卡密度为48路/卡,每框最大支持192路
支持VDSL2接入,VDSL2板卡密度为16路/卡,每框最大支持最多64路
支持LAN接入,LAN板卡密度为24路/卡,每框最大支持96路
FXS板卡上CPU为了costdown,裁减,代码移植到CSM主控板做为linux的一个进程运行,移植的改动包括:
down_ctrl以及filesystem以及RTEMS协议栈等这些任务不需要了
B1205跑的是RTEMS系统,现在跑的是LINUX,系统相关的API全部要重新封装
因为进程后台运行,增加cliserv_task线程,方便终端调试
UTMAC不需要了,起main_thread线程代替B1205的HDLC通信
和FXS通信的HIP进程同样换成socket通信
MIB通信以前由192.168.100.*来通信,现在改走localhost,每个板子的port不一样
1. 编译问题
因为从RTMES到LINUX的更改,耗时而繁琐的工作,先把需要更改的理一下:
B1205的FXS是基于JAMFILE来管理编译的,虽然CSM是用cmake,为了减轻工作量,毕竟这个FXS进程是我一个人维护,不会影响别人的,所以在LINUX下继续坚持用JAMFILE,到时候只要添加到整个项目的脚本中即可
先把不需要的任务统统删除,为了编译通过,先暂时增加一个undef_func.c和undef_func.h,添加一些没有定义的函数以及变量,到时候调试代码过程中再去响应的*.c中添加函数和变量
B1205的RTEMS系统API,通通封装为空,为了编译通过,暂时不要理会
下面是B1205和B1202目录的对比图
其中MISS目录就是为了添加的undef_func.c和undef_func.h目录,其它没有的目录全部删除掉,当然JAMFILE中这些目录的编译选项要相应删除
另外增加一个mian入口,要不然编译不通过的
2. 系统API封装
编译通过了,就开始porting系统的API了,主要集中在utkernel.c,包括:任务(线程)消息队列事件 信号量 信号 定时器
信号量相对简单,几乎小改即可
rtems_semaphore_create
sem_init
rtems_semaphore_release
sem_post
rtems_semaphore_obtain
sem_wait
......
OS_STATUS
free_semaphore (SEMAPHORE * semaphore)
{
OS_STATUS rc;
if ( semaphore == NULL )
{
return( FAILURE );
}
// rc = (OS_STATUS) rtems_semaphore_release( semaphore->sem_id );
rc = (OS_STATUS)sem_post((sem_t *)&semaphore->sem_id);
return ( rc );
}
......
信号
这个是增加用来debug用,当crash的时候可以dump看挂掉的位置,直接添加几个信号加上backtrace
定时器函数
RTEMS拥有rtems_timer_create定时器函数,linux下这个我还没实现呢,linux下虽然可以用SIGALRM信号实现,但是如果同一进程中多次使用的定时器呢?应该需要增加一个结构,当定时器超时时来判断到底是哪个函数执行
线程
task_create就是封装的线程
alloc_task_info的目的是维护task_tables 为了get_set_task_name/get_task_id/print_tasks,为了照顾RTEMS的那一套机制
......
#if 0
/* Create task */
status = rtems_task_create( tptr->namestr,
tptr->priority,
tptr->stack_size,
tptr->preempt_option,
tptr->attribute,
&(tptr->task_id) );
if ( status == RTEMS_SUCCESSFUL )
rtems_task_start(tptr->task_id, tptr->entrypt, tptr->attribute);
else
printf("INIT ERROR: Failure creating task: %s, status = %d/n",
tptr->task_name, status );
#endif
syslog(LOG_INFO,"creating task: %s",tptr->task_name);
task_create( tptr->task_name, tptr->priority,
tptr->stack_size,
tptr->preempt_option,&(tptr->task_id),tptr->entrypt ,NULL);
......
int task_create( char *name, U32 priority, U32 stack_size,U32 preempt_option,U32 *id,void*(*entrypt)(void*) ,void *param)
{
pthread_attr_t attr;
struct sched_param s_param;
int status;
rtems_user_task_table *task;
if(disable_cli&&(strncmp(name,"cli_",4)==0))
return 0;
pthread_attr_init(&attr);
pthread_attr_setstacksize(&attr, (size_t)stack_size);
pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
task=alloc_task_info(name,entrypt,param);
if(task == NULL)
{
syslog(LOG_ERR,"alloc_task_info(%s,%p,%p) failed/n",name,entrypt,param);
return -1;
}
if ((status=pthread_create(id, &attr, task_startup, task)) != 0)
{ /* mt020.201 - Addition for destroying thread attribute object attr */
pthread_attr_destroy(&attr);
fprintf(stderr,"INIT ERROR: Failure creating task: %s, status = %d/n", name, status );
return status;
} /* mt020.201 - Addition for destroying thread attribute object attr */
pthread_attr_destroy(&attr);
task->task_id = *id;
fprintf(stderr,"creating task: %s, status = %d/n", name, status );
return 0;
}
移植的消息队列-rtems_message_queue_create,和事件-rtems_event_send没仔细研究过,部门架构师之前封装了这些RTEMS和LINUX的接口,直接拿过来用的,需要改动的就是参数的调整
3. cliserv_task线程
debug命令行还是RTEMS的那一套机制,dbg_entry是单独起的一个调试线程,shell_add_cmd是RTMES的命令行调试命令,getaline是解析输入命令,process_line调用shell_lookup_cmd来执行调试命令
cliserv_task这个线程用来起一个socket的服务器,采用select方式,后台运行的话,写一个client程序,连接到FXS进程就有一个单独的调试窗口了,fcli0.ipc就是通信用的IPC,参考socket
......
const char *pathcli[]={
"/tmp/ipc/fcli0.ipc",
"/tmp/ipc/fcli1.ipc",
"/tmp/ipc/fcli2.ipc",
"/tmp/ipc/fcli3.ipc",
};
......
task_create("clis",81,8096,SCHED_FIFO,&init_tid,cliserv_task,(void*)(pathcli[sys_info.slot_id]));
......
void dbg_entry (
void* ignore)
{
U8 line[512];
U8* dbg_prompt_s ;
struct termios term;
setvbuf(stdin, NULL, _IONBF, 0); /* Not buffered*/
#if 1
if (tcgetattr(fileno(stdin), &term) >= 0)
{
oldterm = term;
term.c_lflag &= ~(ECHO|ECHONL | ICANON | ISIG | IEXTEN);/*ECHO | */
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
if (tcsetattr(fileno(stdin), TCSADRAIN, &term) < 0)
{
fprintf(stderr, "shell:cannot set terminal attributes/n");
}
}
#endif
dbg_prompt_s = "FXS> " ;
udbg_init();
shell_add_cmd(NULL, NULL, NULL, NULL, 0); /* init the chain list*/
for (;;)
{
printf(dbg_prompt_s);
fflush(stdout);
getaline(&line); /* in target, there is a different way ? */
if ((line[0] == 0)||(line[0] == '/n'))
continue;
process_line(line);
}
}
......
4. main_thread的实现
B1205的HDLC通信在B1202中不需要了,因为在一个CPU上,通信变得更简单了,采用AF_UNIX内部的localhost通信,port不一致而已,仍然采用select方式,sys_info.slot_id是脚本带入的参数,slot0.ipc就是通信用的IPC,参考socket
HIP进程底层一样的做相应的改动,在原来收发包的地方换成,socket通信
......
const char *path[]={
"/tmp/ipc/slot0.ipc",
"/tmp/ipc/slot1.ipc",
"/tmp/ipc/slot2.ipc",
"/tmp/ipc/slot3.ipc",
};
slotx_fd = setup_server_socket_udp(path[sys_info.slot_id]);
......
void *main_thread(void *param)
{
fd_set rset;
int maxfd=0;
int rv;
qbuf_t *msgp;
while(1)
{
FD_ZERO(&rset);
if(slotx_fd != -1)
FD_SET(slotx_fd, &rset);
maxfd = max( maxfd, slotx_fd);
rv = select (maxfd + 1, &rset, NULL, NULL, NULL);
if (rv < 0)
{
syslog(LOG_INFO, "select returned error %d/n", errno);
continue;
}
if ((slotx_fd != -1) && (FD_ISSET(slotx_fd, &rset)))
{
//syslog(LOG_INFO,"receive from SBUS driver/n");
//syslog(LOG_INFO,"data------------/n");
rv = recv_msg_from_slotx(slotx_fd,rx_buffer);
if(rv)
proc_msg_from_slotx(rx_buffer,rv);
}
}
}
5. 板卡启动流程
B1202因为没有了板卡代码,所以整个流程是
当板卡插入时,HIP进程起一个定时器函数,定时去读FPGA的数据,判断每个slot是否有板卡插入
如果有,去读FXS的CPLD寄存器,判断是否是FXS板,如果不是FXS就说明是ASL板
如果是FXS,start_rmp_fxs是一个脚本,它会后台起FXS进程的,fxs_board_id是带入到脚本的参数,上面的main_thread中的sys_info.slot_id就是根据这个这个参数值
sprintf(cmd,"start_rmp_fxs %d >>/var/log/messages", fxs_board_id);
xsystem(cmd);
如果板卡拔出,同样HIP会知道的,直接KILL掉FXS进程
这样整个流程几乎和B1205类似,底层通信接口也正常,上层不用改动
6. MIB通信
k_fxsServerPort*是枚举的port,还是sys_info.slot_id根据来分配不同的port给不同slot的FXS进程,采用的是localhost,不同的port的区别的
......
switch(sys_info.slot_id)
{
case 0:
k_fxsServerPort = k_fxsServerPort0;
k_fxsSyncPort = k_fxsSyncPort0;
k_fxsAsyncPort = k_fxsAsyncPort0;
k_fxsmmlPort = k_fxsmmlPort0;
break;
case 1:
k_fxsServerPort = k_fxsServerPort1;
k_fxsSyncPort = k_fxsSyncPort1;
k_fxsAsyncPort = k_fxsAsyncPort1;
k_fxsmmlPort = k_fxsmmlPort1;
break;
case 2:
k_fxsServerPort = k_fxsServerPort2;
k_fxsSyncPort = k_fxsSyncPort2;
k_fxsAsyncPort = k_fxsAsyncPort2;
k_fxsmmlPort = k_fxsmmlPort2;
break;
case 3:
k_fxsServerPort = k_fxsServerPort3;
k_fxsSyncPort = k_fxsSyncPort3;
k_fxsAsyncPort = k_fxsAsyncPort3;
k_fxsmmlPort = k_fxsmmlPort3;
break;
default:
break;
}
......
static void
oamInitUdl(void)
{
/* Register the UDL ports. */
if (register_udl_port(OAM_QUEUE, k_fxsServerPort, k_serverPort) != RV_OK)
rtems_panic("could not register OAM server port with UDL");
if (register_udl_port(OAM_QUEUE, k_fxsSyncPort, k_syncPort) != RV_OK)
rtems_panic("could not register OAM sync port with UDL");
if (register_udl_port(OAM_QUEUE, k_fxsAsyncPort, k_asyncPort) != RV_OK)
rtems_panic("could not register OAM async port with UDL");
/* Register the UDL command handler. */
if (register_udl_cmd_handler(k_fxsServerPort, oamCommandHandler, (void *) 0)
!= RV_OK)
{
rtems_panic("could not register OAM command handler with UDL");
}
}
7. 其它封装
查看线程,查看mem的API重新封装一下
FXS原来依靠UTMAC通信的接口要重新做到socket接口,数据格式要重新调整,把UTMAC的包头全部去掉
HIP的UTMAC通信接口也重新封装,一样的数据格式需要调整
OAM进程和FXS的MIB通信接口的IP和PORT需要做相应更改
ASL没有CPLD,每次通过FXS的GPIO给ASL消息
RTMES的一些原来用的调试API封装
......
8. 小结
JAMFILE的报错莫名其妙,它对;和;识别有问题;bin/jamfile:LC_LIBS += libvcp_ctrl; 也报错,需要加一个空格在";"前
自己有unharject的文件,没想到这个文件也被别人改过了,所以编译不过啊!!
if (serv_fd = socket(AF_UNIX, SOCK_STREAM, NULL) < 0) 这行代码写得太失败了,接下来当然100%的bind错误
taskid也就是线程的ID号,因为没有封装线程ID号和线程命的函数,直接导致线程间通信不正确
第一次用IPC通信,/tmp/ipc/slot0.ipc居然自己去新建一个,可笑
HIP中按照FXS中memmap的U8来读GPIO数据完全不对,GPIO这个时候应该U32
VCP升级后,接口API参数也不一样,而且初始化流程也变了,害惨我了
printf打印不成功,是因为printf重定向错误了,打到另外一个终端窗口去了
读取FXS的CPLD数据需要延时,要不然数据不正确,直接板卡判断失误
OAM进程的数据发送以前按照192.168.110.*发送,现在发送的IP和PORT变换了,发送到了ASL板上去了