Access-B1202

    技术2022-05-20  30

    B1202B1205的小框,直接放楼道,接入设备,上行GE,或者EPON,下行4框可以混插ASLFXS板,提供VOIPASL服务,CSM主控板含VOIPDSP芯片处理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线程代替B1205HDLC通信

    FXS通信的HIP进程同样换成socket通信

    MIB通信以前由192.168.100.*来通信,现在改走localhost,每个板子的port不一样

     

    1.        编译问题

     

    因为从RTMESLINUX的更改,耗时而繁琐的工作,先把需要更改的理一下:

    B1205FXS是基于JAMFILE来管理编译的,虽然CSM是用cmake,为了减轻工作量,毕竟这个FXS进程是我一个人维护,不会影响别人的,所以在LINUX下继续坚持用JAMFILE,到时候只要添加到整个项目的脚本中即可

    先把不需要的任务统统删除,为了编译通过,先暂时增加一个undef_func.cundef_func.h,添加一些没有定义的函数以及变量,到时候调试代码过程中再去响应的*.c中添加函数和变量

    B1205RTEMS系统API,通通封装为空,为了编译通过,暂时不要理会

    下面是B1205B1202目录的对比图

     

    其中MISS目录就是为了添加的undef_func.cundef_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没仔细研究过,部门架构师之前封装了这些RTEMSLINUX的接口,直接拿过来用的,需要改动的就是参数的调整

     

    3.        cliserv_task线程

     

    debug命令行还是RTEMS的那一套机制,dbg_entry是单独起的一个调试线程,shell_add_cmdRTMES的命令行调试命令,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的实现

     

    B1205HDLC通信在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是否有板卡插入

    如果有,去读FXSCPLD寄存器,判断是否是FXS板,如果不是FXS就说明是ASL

    如果是FXSstart_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会知道的,直接KILLFXS进程

    这样整个流程几乎和B1205类似,底层通信接口也正常,上层不用改动

     

    6.        MIB通信

     

    k_fxsServerPort*是枚举的port,还是sys_info.slot_id根据来分配不同的port给不同slotFXS进程,采用的是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.        其它封装

     

    查看线程,查看memAPI重新封装一下

    FXS原来依靠UTMAC通信的接口要重新做到socket接口,数据格式要重新调整,把UTMAC的包头全部去掉

    HIPUTMAC通信接口也重新封装,一样的数据格式需要调整

    OAM进程和FXSMIB通信接口的IPPORT需要做相应更改

    ASL没有CPLD,每次通过FXSGPIOASL消息

    RTMES的一些原来用的调试API封装

    ......

     

    8.        小结

    JAMFILE的报错莫名其妙,它对;和;识别有问题;bin/jamfileLC_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中按照FXSmemmapU8来读GPIO数据完全不对,GPIO这个时候应该U32

    VCP升级后,接口API参数也不一样,而且初始化流程也变了,害惨我了

    printf打印不成功,是因为printf重定向错误了,打到另外一个终端窗口去了

    读取FXSCPLD数据需要延时,要不然数据不正确,直接板卡判断失误

    OAM进程的数据发送以前按照192.168.110.*发送,现在发送的IPPORT变换了,发送到了ASL板上去了

     


    最新回复(0)