⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 sys_arch.c

📁 lwip在ucos上的移植代码
💻 C
字号:
/*
*********************************************************************************************************
*                                              lwIP TCP/IP Stack
*                                    	 port for uC/OS-II RTOS on TIC6711 DSK
*
* File : sys_arch.c
* By   : ZengMing @ DEP,Tsinghua University,Beijing,China
* Reference: YangYe's source code for SkyEye project
*********************************************************************************************************
*/

#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/mem.h"

#include "arch/sys_arch.h" 

static OS_MEM *pQueueMem;

static char pcQueueMemoryPool[MAX_QUEUES * sizeof(TQ_DESCR)];

struct sys_timeouts lwip_timeouts[LWIP_TASK_MAX];
struct sys_timeouts null_timeouts;	//sys_arch_timeouts必须不能返回空,可以返回此变量

OS_STK LWIP_TASK_STK[LWIP_TASK_MAX][LWIP_STK_SIZE];


/*-----------------------------------------------------------------------------------*/
/* This func should be called first in lwip task!
* sys_int必须在tcpip协议栈任务tcpip_thread创建前被调用
 * -------------------------------------------------		*/
void sys_init(void)
{
    u8_t i;
    u8_t   ucErr;
    //init mem used by sys_mbox_t //use ucosII functions
    //为消息队列创建内存分区
    pQueueMem = OSMemCreate( (void*)pcQueueMemoryPool, MAX_QUEUES, sizeof(TQ_DESCR), &ucErr );
    //init lwip task prio offset
    //curr_prio_offset = 0;
    //init lwip_timeouts for every lwip task
    //初始化lwip定时事件表,具体实现参考下面章节
    for(i=0;i<LWIP_TASK_MAX;i++){
    	lwip_timeouts[i].next = NULL;
    }
    null_timeouts.next = NULL;//别忘了它哦
}


/*-----------------------------------------------------------------------------------*/
//建立并返回一个信号量
sys_sem_t sys_sem_new(u8_t count)
{
    sys_sem_t pSem;
    pSem = OSSemCreate((u16_t)count );
    return pSem;
}


/*-----------------------------------------------------------------------------------*/
//删除信号量
void sys_sem_free(sys_sem_t sem)
{
    u8_t     ucErr;
    (void)OSSemDel((OS_EVENT *)sem, OS_DEL_NO_PEND, &ucErr );
}

/*-----------------------------------------------------------------------------------*/
//释放信号量
void sys_sem_signal(sys_sem_t sem)
{
    OSSemPost((OS_EVENT *)sem );
}


/**
 * 函数名称 : sys_arch_sem_wait
 * 功能描述 : 请求由参数sem指定的信号量并阻塞线程,并进行超时时间转换
 * 入口参数 : <sem>[in] sem指定要发送的信号
 * : <timeout>[in] 指定等待的最长时间(单位为毫秒)。为0,线程会一直被阻塞直至收到指定的信号;非0,指定线程最长等待时
 * : 间
 * 出口参数 : - 0: 在指定时间内等到指定信号
 * : - SYS_ARCH_TIMEOUT: 在指定时间内没有等到指定信号
 */
u32_t sys_arch_sem_wait(sys_sem_t sem, u32_t timeout)
{
  u8_t err;
  u32_t ucos_timeout = 0;

	//把ms转换为ucos的时钟节拍
  if(timeout != 0){
  	ucos_timeout = (timeout * OS_TICKS_PER_SEC) / 1000;
  	if(ucos_timeout < 1)
  		ucos_timeout = 1;
  	else if(ucos_timeout > 65535) //ucOS only support u16_t pend
  		ucos_timeout = 65535;
  }
  	
  OSSemPend ((OS_EVENT *)sem,(u16_t)ucos_timeout, (u8_t *)&err);
  
  if(err == OS_NO_ERR)
  	return 0;	// only when timeout!
  else
  	return SYS_ARCH_TIMEOUT;
}


/*
 *	邮箱用于消息传递,用户即可以将其实现为一个队列,允许多条消息投递到这个邮箱,也可以
 *	每次只允许投递一个消息,这两种方式LwIP都可以正常运作。不过,前者更加有效。需要用户
 *	特别注意的是――投递到邮箱中的消息只能是一个指针。
 *
 *		既然使用消息队列能提高性能,我们就用ucos的消息队列来实现邮箱传递,而ucos的消息队列
 *	传递的消息正好也是一个指针。:-)
 *
 *		千杯不醉		2007.1.28
 */
/**
 *					关于ucos的消息队列
 *
 *		ucos的消息队列需要定义三个数据结构,一,事件控制块。二,队列控制块。三,消息内存——存放消息指针。
 *
 *
 */
sys_mbox_t sys_mbox_new(void)
{
    u8_t       ucErr;
    PQ_DESCR    pQDesc;
    //从消息队列内存分区中得到一个内存块
    pQDesc = OSMemGet( pQueueMem, &ucErr );
    if( ucErr == OS_NO_ERR ) {   
    //创建一个消息队列,从队列控制块链表中申请一个控制块
        pQDesc->pQ = OSQCreate( &(pQDesc->pvQEntries[0]), MAX_QUEUE_ENTRIES );       
        if( pQDesc->pQ != NULL ) {
            return pQDesc;
        }
    } 
    return SYS_MBOX_NULL;
}

/*-----------------------------------------------------------------------------------*/
void
sys_mbox_free(sys_mbox_t mbox)
{
    u8_t     ucErr;
    
    //clear OSQ EVENT,
    OSQFlush( mbox->pQ );
    //del OSQ EVENT
    (void)OSQDel( mbox->pQ, OS_DEL_NO_PEND, &ucErr);
    //put mem back to mem queue,别忘了取消数组所占用的内存
    ucErr = OSMemPut( pQueueMem, mbox );
}

/*-----------------------------------------------------------------------------------*/

/**
 *	在ucos中,如果OSQPost(OS_EVENT *pevent, void *msg)中的msg==NULL 会返回一条OS_ERR_POST_NULL_PTR
 *  错误.而在lwip中会调用sys_mbox_post(mbox,NULL)发送一条空消息,我们在本函数中把NULL变成一个常量指针0xffffffff.
 */
const void *pvNullPointer = (const void *)0xffffffff;
void
sys_mbox_post(sys_mbox_t mbox, void *msg)
{
		u8_t ucErr;
    if( !msg ) 
			msg = (void*)&pvNullPointer;
			
		/*
		 *	如果OSQPost()返回OS_Q_FULL之外的异常标志,表明程序编写有问题。
		 */
    ucErr = OSQPost( mbox->pQ, msg);
    //lwip没有对消息投递错误做任何处理
    //TODO:如果消息队列满则等待100ms再进行投递
   /* while(ucErr == OS_Q_FULL)
	    OSTimeDlyHMSM(0, 0, 0, 100);
	    */
}

/*-----------------------------------------------------------------------------------*/
u32_t 
sys_arch_mbox_fetch(sys_mbox_t mbox, void **msg, u32_t timeout)
{
    u8_t     ucErr;
    u32_t ucos_timeout = 0;
	/*
	 *	在lwip中 ,timeout的单位是ms,在ucosII ,timeout 的单位是timer tick 
	 *	所以要进行单位转换
   */
   //进行单位转换
  if(timeout != 0){
  ucos_timeout = (timeout * OS_TICKS_PER_SEC)/1000;
  if(ucos_timeout < 1)
  	ucos_timeout = 1;
  else if(ucos_timeout > 65535)	//ucOS only support u16_t timeout
  	ucos_timeout = 65535;
  }  
    //如果data!=NULL就返回消息指针,
  if(msg != NULL){
    *msg = OSQPend( mbox->pQ, (u16_t)ucos_timeout, &ucErr );        
  }else{
    //just discard return value if msg==NULL
    OSQPend(mbox->pQ,(u16_t)ucos_timeout,&ucErr);
  }
   //这里修改了ucos中的OSQPend系统调用,
//原来的void  *OSQPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)
// err的返回值只有两种:收到消息就返回OS_NO_ERR,超时则返回OS_TIMEOUT
//这里先将err从8位数据改变成了16位数据 OSQPend(*pevent,timeout, INT16U *err)
//重新定义了OS_TIMEOUT
//在ucos中原有#define OS_TIMEOUT 20
//改为 #define  OS_TIMEOUT  -1
//err返回值的意义也改变了,如果超时返回OS_TIMEOUT
// 如果收到消息,则返回OSTCBCur->OSTCBDly修改部分代码如下
//if (msg != (void *)0) { /* Did we get a message?  */
// OSTCBCur->OSTCBMsg = (void *)0;
// OSTCBCur->OSTCBStat     = OS_STAT_RDY;
// OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
// *err = OSTCBCur->OSTCBDly;// zhangzs @2003.12.12
//    OS_EXIT_CRITICAL();
// return (msg);     /* Return message received */
//    }
//关于ucos的OSTBCur->OSTCBDly的含义请查阅ucos的书籍

//我看完全没有必要,超时返回SYS_ARCH_TIMEOUT,没有超时干脆返回0.
  if( ucErr == OS_TIMEOUT ) {
        return SYS_ARCH_TIMEOUT;
    } else {
    /*
     *	参看sys_mbox_post()函数,由于当消息为空时投递的是pvNullPointer,所以此时要做的是把消息
     *	还原回来
     */
      if(*msg == (void*)&pvNullPointer ) 
	  		*msg = NULL;
      return 0;
    }
}



/*----------------------------------------------------------------------*/
//函数sys_arch_timeouts返回对应于当前任务的指向定时事件链表的起始指针.该指针存在lwip_timeouts[MAX_LWIP_TASKS]中.
//此函数不能返回0,所以不成功就返回一个空的sys_arch_timeouts指针
struct 
sys_timeouts * sys_arch_timeouts(void)
{
  u8_t curr_prio;
  s16_t offset;
  
  OS_TCB curr_task_pcb;
  
  null_timeouts.next = NULL;
  
  OSTaskQuery(OS_PRIO_SELF,&curr_task_pcb);
  curr_prio = curr_task_pcb.OSTCBPrio;
  //也可以如下实现
  //curr_prio = OSTCBCur->OSTCBPrio;
  
  //获取当前任务的优先级
  offset = curr_prio - LWIP_START_PRIO;

  /*if(curr_prio == TCPIP_THREAD_PRIO) 
  		return &lwip_timeouts[LWIP_TASK_MAX];
  else if(offset >= 0 && offset < LWIP_TASK_MAX)
  		return &lwip_timeouts[offset];
  else return &null_timeouts;*/
  
  /*
   *	由于进程要与lwip_timeouts一一对应,所以必须与sys_thread_new分配的进程一致
   */
   if(offset >= 0 && offset < LWIP_TASK_MAX)
  		return &lwip_timeouts[offset];
  else return &null_timeouts;
}


/**
 *	移植说明一文所说的,实现多线程就能够获得的LwIP的完整功能,指的就是TCPIP、SLIP、PPP协议支持。
 *	当我们只需要TCPIP时,一个单线程的LwIP已经足够.
 *	
 *	另外,在这里还需要特别交待的是,使用LwIP接口函数建立的应用层线程(或者任务)与LwIP内部线程
 *	(TCPIP、SLIP、PPP)完全不同。应用层线程就是一个普通的uCOS任务,优先级低于LwIP线程。它使用
 *	OSTaskCreate()(或OSTaskCreateExt())函数建立。如果使用sys_thread_new()函数建立,它就会占用
 *	非常宝贵的线程资源,包括优先级号以及下文将要阐述的sys_timeouts数组资源。
 */
 /**
  *		入口参数prio理解为要创建进程的优先级。系统中TCPIP_THREAD_PRIO设置为5.
  *
  *		千杯不醉		2007.1.28
  */
sys_thread_t sys_thread_new(void (* thread)(void *arg), void *arg, int prio)
{  
  s8_t offset;
  offset = (s8_t)(prio - LWIP_START_PRIO);
  if(offset >= 0 && offset < LWIP_TASK_MAX)
  {
  	OSTaskCreate(thread, (void *)arg, &LWIP_TASK_STK[offset][LWIP_STK_SIZE-1], prio);
		return prio;
  }else {
		//printf(" lwip task prio out of range ! error! ");
		return 0;
  }
}







⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -