本篇日志记录了我在爱立信实习中参与的一个项目:基站监测与调试工具的开发。
WoD工具分为两部分,一部分是在DSP侧,实现在基站freescale的板子上,提供基站端的流量信息/数据信息/signal状态/内存信息等;另一部分是PC侧,负责监视。控制基站与显示,记录数据结果。两部分之间通过socket通信,在自己设计的数据包传输协议基础上交换信息。(在外场测试中,没有PC侧的应用,DSP侧应将各种信息或数据记录在文件中存储,需要时用脚本解析)。
相关代码主要为om_message.c
在DSP0的app.c中,appstart()调用om_init(),这个函数创建了几个低优先级的进程(为了保证基站端物理层处理的实时性,WoD相关的进程优先级都会设为最低的):
void om_init(void)
{
om_pool_id = s_create_pool(0, (OSADDRESS)&om_pool_buf[krn_get_core_id()][0], OM_POOL_BUF_SIZE, omPoolSizes);
pid_wod_send = create_process(OS_PRI_PROC, "wodSendProc", wodSendProc,
(OSADDRESS)4096, WOD_SEND_PRIORITY, 0, 0, 0, 0, 0);
start(pid_wod_send);
pid_wod_recv = create_process(OS_PRI_PROC, "wodRecvProc", wodRecvProc,
(OSADDRESS)4096, WOD_RECV_PRIORITY, 0, 0, 0, 0, 0);
start(pid_wod_recv);
pid_log_to_file = create_process(OS_PRI_PROC,"logToFileProc",logToFileProc,
(OSADDRESS)4096, WOD_LOG_TO_FILE_PRIORITY, 0,0,0,0,0);
start(pid_log_to_file);
om_debug_log_init();
}
下面以通信最基本的send和recv操作来记录进程键通信的用法:
简洁起见,以记录小数据和天线数据(大量)到文件这两个功能为例,涉及到的操作有文件的建立,关闭和写入。
send进程等待信号并判断sig的action,根据action的类别来封装message各个字段内容。除了包头(dspID,coreID,fileID,packetID等)外,payload通过
memcpy(p + 9, sig->send_req.sendbuf, sig->send_req.sendlength);
来具体赋值,及sig中传入了sendbuf中有效信息,如文件名,天线数据等。PS:天线数据因为过大,是从bufpointer开始copy过来的。之后通过socket发送
err = ipcom_sendto(sockcli, sendbuf, sendsize, 0, (struct Ip_sockaddr *)&serveraddr, sizeof(serveraddr));
free_buf(&sig);
OS_PROCESS(wodSendProc)
{
S32 err;
union SIGNAL *sig;
static SIGSELECT sigsel[] = {1,SIG_SEND_REQ};
U32 * p;
struct msgHead* msg_p;
U8 sendbuf[MAX_WOD_SEND_LENGTH];
#pragma align sendbuf 8
U32 sendsize;
Ip_fd_set readset;
Ip_fd sockcli;
sockcli = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0);
IP_FD_ZERO(&readset);
IP_FD_SET(sockcli, &readset);
for(;;)
{
sig = receive(sigsel);
switch(sig->sig_no)
{
case SIG_SEND_REQ:
switch(sig->send_req.Action)
{
case ATL_CMD_READ_MEMORY_RSP:
msg_p = (struct msgHead*)&sendbuf[0];
p = (U32 *)&sendbuf[0];
msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id
msg_p->FLIMode = om_swap32(krn_get_core_id());
msg_p->Action = om_swap32(sig->send_req.Action);
msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) - 2);
*(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN);
memcpy(p+9, sig->send_req.sendbuf, sig->send_req.sendlength);
sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32);
break;
case ATL_CMD_FILE_OPEN:
case ATL_CMD_FILE_CLOSE:
msg_p = (struct msgHead*)&sendbuf[0];
p = (U32 *)&sendbuf[0];
msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id
msg_p->FLIMode = om_swap32(krn_get_core_id());
msg_p->Action = om_swap32(sig->send_req.Action);
msg_p->FLIdx = om_swap16(sig->send_req.fileIdx);
msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) -2 );
*(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN);
memcpy(p + 9, sig->send_req.sendbuf, sig->send_req.sendlength);
sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32);
break;
case ATL_CMD_DATA_WRITE:
msg_p = (struct msgHead*)&sendbuf[0];
p = (U32 *)&sendbuf[0];
msg_p->FLIType = om_swap32(pfm_get_dsp_id()); // device id
msg_p->FLIMode = om_swap32(krn_get_core_id());
msg_p->Action = om_swap32(sig->send_req.Action);
msg_p->FLIdx = om_swap16(sig->send_req.fileIdx);
msg_p->PacketIdx = om_swap16(sig->send_req.packetIdx);
msg_p->Size = om_swap32(9 + 1 + sig->send_req.sendlength/sizeof(U32) -2 );
*(p+9+sig->send_req.sendlength/sizeof(U32)) = om_swap32(END_TOKEN);
memcpy(p+9, sig->send_req.sendbufpointer, sig->send_req.sendlength);
sendsize = sig->send_req.sendlength + sizeof(struct msgHead) + sizeof(U32);
break;
default:
break;
}
break;
default:
break;
}
err = ipcom_sendto(sockcli, sendbuf, sendsize, 0, (struct Ip_sockaddr *)&serveraddr, sizeof(serveraddr));
free_buf(&sig);
}
ipcom_socketclose(sockcli);
}
recv进程从socket读出请求(recvfrom),然后根据包头中的action字段实施具体操作,如果请求的是从内存中绝对地址读出N个字节的数据,
1.动态分配一个sigsigsend = s_alloc(om_pool_id,sizeof(struct SigSendReq), SIG_SEND_REQ);
2.拷贝内存数据到sigsend的buf中去memcpy(&sigsend->send_req.sendbuf[0], (char*) p, size);
3.将信号传递给send进程send(&sigsend,pid_wod_send);
错误处理要注意,如果分配了sig但是由于异常导致没有send出去,一定要判断异常并free掉这个sendsig,如果send成功,在send进程中处理完这个sig后也要free掉这块内存。
OS_PROCESS(wodRecvProc)
{
S32 err;
union SIGNAL *sig;
Ip_fd sockcli;
struct Ip_sockaddr_in clisockaddr;
struct Ip_sockaddr_in fromsockaddr;
S32 socklen = sizeof(fromsockaddr);
Ip_fd_set readset;
U32 * p;
struct msgHead* msg_p;
U8 recvbuf[MAX_WOD_SEND_LENGTH];
U32 memoryAddr;
U32 readSize;
/* create socket */
sockcli = ipcom_socket(IP_AF_INET, IP_SOCK_DGRAM, 0);
/* bind address */
clisockaddr.sin_family = IP_AF_INET;
clisockaddr.sin_port = IP_PORT;
clisockaddr.sin_addr.s_addr = 0;
err = ipcom_bind(sockcli, (struct Ip_sockaddr*)&clisockaddr, sizeof(clisockaddr));
IP_FD_ZERO(&readset);
IP_FD_SET(sockcli, &readset);
for(;;)
{
/* receive data */
err = ipcom_recvfrom(sockcli, (U8*)recvbuf, sizeof(recvbuf), 0, (struct Ip_sockaddr *)&fromsockaddr, &socklen);
if (err >0)
{
msg_p = (struct msgHead*)&recvbuf[0];
switch(msg_p->Action)
{
case ATL_CMD_READ_MEMORY_REQ: // read memory
p = (U32 * )&recvbuf[0];
memoryAddr = *(p + 9);
readSize = *(p + 10);
om_read_memory(memoryAddr,readSize);
break;
}
}
}
ipcom_socketclose(sockcli);
}
void om_read_memory(U32 addr, U32 len)
{
union SIGNAL* sigsend;
U32 p;
U32 size;
p = addr;
size = len * sizeof(U32);
if (size > MAX_SIG_SEND_REQ_LENGTH)
{
return;
}
sigsend = s_alloc(om_pool_id,sizeof(struct SigSendReq), SIG_SEND_REQ);
sigsend->send_req.Action = ATL_CMD_READ_MEMORY_RSP;
sigsend->send_req.sendlength = size;
if (sigsend->send_req.sendlength > MAX_SIG_SEND_REQ_LENGTH)
{
free_buf(&sigsend);
return;
}
memcpy(&sigsend->send_req.sendbuf[0], (char*) p, size);
send(&sigsend,pid_wod_send);
return;
}
目前看来,逻辑比较简单,问题在于处理天线端口上大量数据的时候,常常一次数据要分成不同的packet来发送,而且数据传输比较麻烦,单独开辟一个进程并利用DMA搬数首先,创建两个sem
semWodDMA = create_sem(0);
在DMA开始之前和之后,查看定时状态,确定数据的有效性
//get current subframe and radio frame , timing check
deadline.sfn = deadlineRFC;
deadline.subframe = deadlineSFC;
status = l1IfTimingCheck(l1GetTiming(), &deadline);
if(status == SUCCESS) //data is valid
{
outputFlag = FAILURE;
if((sig->send_data_req.dataType == PUS_CRCERR_ANT_FREQ_DATA)
|| (sig->send_data_req.dataType == PUS_AN_DTX_ANT_FREQ_DATA)
|| (sig->send_data_req.dataType == PUC_AN_DTX_ANT_FREQ_DATA)
|| (sig->send_data_req.dataType == DL_ANT_FREQ_DATA)
|| (sig->send_data_req.dataType == SRS_ERR_ANT_FREQ_DATA)) //data, config, scale
{
data_p = gWodDataFileLogOut[sig->send_data_req.dataType][log_file_para.SFC].addr;
data_size = gWodDataFileLogOut[sig->send_data_req.dataType][log_file_para.SFC].len;
cfg_p = gWodDataCfgAddr[sig->send_data_req.dataType][log_file_para.RFC & 1][log_file_para.SFC].addr;
cfg_size = gWodDataCfgAddr[sig->send_data_req.dataType][log_file_para.RFC & 1][log_file_para.SFC].len;
scale_p = gWodDataScaleAddr[sig->send_data_req.dataType][log_file_para.SFC].addr;
scale_size = gWodDataScaleAddr[sig->send_data_req.dataType][log_file_para.SFC].len;
/* cache invalidate */
DCACHE_INVALIDATE((INT8*)data_p, ALIGN_SIZE(data_size, POOL_ALIGNED_256));
outputFlag = carryWodData(data_p,data_size,cfg_p,cfg_size,scale_p,scale_size,&WodObifDataBuffer[0],WOD_DATA_DMA_REF_INFO,DmaWodDataCallback);
}
//启动DMA后,等待sem //wait semaphore
if(outputFlag == SUCCESS)
{
wait_sem(semWodDMA);
}
DCACHE_FLUSH(&WodObifDataBuffer[0], ALIGN_SIZE(sizeof(WodObifDataBuffer), POOL_ALIGNED_256));
//若确定数据timing和size有效,则进行open,write,close
status = omFileIdFind(&file_id);
status = om_msg_file_open(file_id + dsp_file_id_offset,0,&log_file_para);
//...
}
考虑到多个DSP可能同时调用logdatatofile,所以需要在建立文件ID时加锁
INT32 omFileIdFind(U32* file_id)
{
static U32 fileIdx = 0;
LOCK();
*file_id = fileIdx;
fileIdx++;
if(fileIdx == TOTAL_FILE_ID)
{
fileIdx = 0;
}
UNLOCK();
return SUCCESS;
}
注意,由于天线数据数量过大,memcpy到sig的sendbuf中不太现实,可以利用一个全局的buf暂存DMA转移过来的数据
S8 WodObifDataBuffer[MAX_TRACE_DATA_LEN + MAX_TRACE_CFG_LEN + MAX_TRACE_SCALE_LEN];
S32 om_msg_data_write(U32 FLIdx, U32 packetIdx, L1_Log* log_parm, U32 len, void* buffer)
{
union SIGNAL *sig;
sig = s_alloc(om_pool_id,sizeof(struct SigSendReq),SIG_SEND_REQ);
sig->send_req.Action = ATL_CMD_DATA_WRITE;
sig->send_req.fileIdx = (U16)FLIdx;
sig->send_req.packetIdx = (U16)packetIdx;
sig->send_req.sendbufpointer = (U8*)buffer;
sig->send_req.sendlength = len;
if(sig->send_req.sendlength > MAX_SIG_SEND_REQ_LENGTH)
{
free_buf(&sig);
return FAILURE;
}
send(&sig,pid_wod_send);
return SUCCESS;
}
这里直接将sendbufpointer赋值为这个全局地址即可。p = (S8*)&WodObifDataBuffer[0];
status = om_msg_data_write(file_id + dsp_file_id_offset,packet_idx[file_id],&log_file_para,MAX_SIG_SEND_REQ_LENGTH,p);
假如sendbufpointer为局部变量地址,则执行相关代码后会直接导致DSP崩溃。。。。