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

📄 os_cpu_c.c

📁 ucos2.8 移植到linux下的代码
💻 C
字号:
/*
 *                                                uC/OS-II
 *                                          The Real-Time Kernel
 *
 *                                               Linux Port
 *
 * File        : $Source: /proj/cvs-gfa/Micrium/Software/uCOS-II/Ports/linux-hal/os_cpu_c.c,v $
 * By          : (c) George Fankhauser, Sensaco Consulting GmbH, 
 *               Switzerland, http://www.sensaco.com  
 * Version     : $Revision: 1.1.1.1 $
 *
 * Changed by  : $Author: gfa $
 *               $Date: 2003/11/20 10:19:14 $
 *
 * $Log: os_cpu_c.c,v $ * Revision 1.1.1.1  2003/11/20 10:19:14  gfa * check in ucos II for linux *
 */

/** \file 
 * User context switching code derived from Topsy 2.0 port to Linux and Solaris
 * (see http://www.tik.ee.ethz.ch/~topsy).
 *
 * \author George Fankhauser
 */ 

#define  OS_CPU_GLOBALS
#include "includes.h"

#include <asm/sigcontext.h> // for context switching
#define __USE_GNU
#include <ucontext.h>
#undef __USE_GNU
#include <ucontext.h>   // for context switching
#include <string.h>
#include <unistd.h>   // used for syscalls:
                      // ualarm in Threads/unix/TMClock.c
		      // kill   in Topsy/unix/SyscallMsg.c
		      // getpid in Topsy/unix/SyscallMsg.c
#include <signal.h>   // used in Threads/unix/TMHal.c and 
                      // Topsy/unix/SyscallMsg.c
#include <setjmp.h>   // for contextswitches in Threads/unix/TMHal.c
#include <sys/select.h>

#include <stdio.h>    // if we want to use printf...


/**                                          TASK CREATION HOOK
 *
 * This function is called when a task is created.
 * Arguments  : ptcb   is a pointer to the task control block of the task being created.
 * Note(s)    : 1) Interrupts are disabled during this call.
 */
#if OS_CPU_HOOKS_EN > 0
void OSTaskCreateHook (OS_TCB *ptcb)
{
    ptcb = ptcb;
}
#endif

/**                                          TASK DELETION HOOK
 *
 * This function is called when a task is deleted.
 * \arg ptcb   is a pointer to the task control block of the task being deleted.
 * Note(s)    : 1) Interrupts are disabled during this call.
 */
#if OS_CPU_HOOKS_EN > 0
void OSTaskDelHook (OS_TCB *ptcb)
{
    ptcb = ptcb;       /* Prevent compiler warning */
}
#endif

/**                                           TASK SWITCH HOOK
 *
 * This function is called when a task switch is performed.  This allows 
 * you to perform other operations during a context switch.
 *
 * Note(s)    : 1) Interrupts are disabled during this call.
 *              2) It is assumed that the global pointer 'OSTCBHighRdy' points to the 
 *              TCB of the task that will be 'switched in' (i.e. the highest priority 
 *              task) and, 'OSTCBCur' points to the task being switched out (i.e. the 
 *              preempted task).
 */
#if (OS_CPU_HOOKS_EN > 0) && (OS_TASK_SW_HOOK_EN > 0)
void OSTaskSwHook (void)
{
}
#endif

/**                                           STATISTIC TASK HOOK
 *
 * This function is called every second by uC/OS-II's statistics task.  
 * This allows your application to add functionality to the statistics task.
 */
#if (OS_TASK_STAT_HOOK_EN > 0)
void OSTaskStatHook (void)
{
}
#endif

/**                                               TICK HOOK
 *
 * This function is called every tick.
 * Note(s)    : 1) Interrupts may or may not be ENABLED during this call.
 */
#if (OS_CPU_HOOKS_EN > 0) && (OS_TIME_TICK_HOOK_EN > 0)
void OSTimeTickHook (void)
{
}
#endif

/**                                     OS INITIALIZATION HOOK
 *                                            (BEGINNING)
 *
 * This function is called by OSInit() at the beginning of OSInit().
 * Note(s)    : 1) Interrupts should be disabled during this call.
 */
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookBegin (void)
{
    linuxInit(); ///< installs the syscall handler
}
#endif

/**                                     OS INITIALIZATION HOOK
 *                                               (END)
 *
 * This function is called by OSInit() at the end of OSInit().
 * Note(s)    : 1) Interrupts should be disabled during this call.
 */
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSInitHookEnd (void)
{
}
#endif

/**                                          IDLE TASK HOOK
 *
 * This function is called by the idle task.  This hook has been added to 
 * allow you to do such things as STOP the CPU to conserve power.
 * Note(s)    : 1) Interrupts are enabled during this call.
 * 
 * 		On real hardware a power saving function would be selected.
 * 		On Linux, we sleep a little to lower the load on the system
 * 		or, we block until next signal is delivered by calling select().
 * 		
 * 		If either of these methods causes problems just leave the
 * 		function empty.
 */
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION >= 251
void OSTaskIdleHook (void)
{
    // usleep(50); ///< as done in Topsy
    select(0, NULL, NULL, NULL, NULL); ///< as done in eCos
}
#endif

/**                                           OSTCBInit() HOOK
 *
 * This function is called by OS_TCBInit() after setting up most of the TCB.
 * \arg ptcb    is a pointer to the TCB of the task being created.
 * Note(s)    : 1) Interrupts may or may not be ENABLED during this call.
 */
#if OS_CPU_HOOKS_EN > 0 && OS_VERSION > 203
void OSTCBInitHook (OS_TCB *ptcb)
{
    ptcb = ptcb;     
}
#endif


/**                                 INITIALIZE A TASK'S STACK
 *
 * This function is called by either OSTaskCreate() or OSTaskCreateExt() to 
 * initialize the stack frame of the task being created. This function is highly 
 * processor specific.
 * \arg task    is a pointer to the task code
 * \arg pdata   is a pointer to a user supplied data area that will be passed to the task
 *              when the task first executes.
 * \arg ptos   is a pointer to the top of stack. It is assumed that 'ptos' points to the
 *              highest valid address on the stack.
 * \arg opt specifies options that can be used to alter the behavior of OSTaskStkInit().
 *               \see uCOS_II.H for OS_TASK_OPT_???.
 *
 * \return Always returns the location of the new top-of-stack' once the processor 
 * registers have been placed on the stack in the proper order.
 *
 * Note(s)    : Interrupts are enabled when your task starts executing. You can change 
 * this by setting this SREG to 0x00 instead. In this case, interrupts would be disabled 
 * upon task startup. The application code would be responsible for enabling interrupts 
 * at the beginning of the task code. You will need to modify OSTaskIdle() and 
 * OSTaskStat() so that they enable interrupts. Failure to do this will make your system 
 * crash!
 */
OS_STK* OSTaskStkInit (void (*task)(void* pd), void* pdata, OS_STK* ptos, INT16U opt)
{
    INT32U*    stk;
    ucontext_t uc;
    INT32U     sigsize = 20 + sizeof(uc);

    opt = opt;            /* 'opt' is not used, prevent warning  */
    
    getcontext(&uc);
    
    stk = (INT32U*)((int)ptos - sigsize);
    uc.uc_link = NULL; ///< no successor
    uc.uc_mcontext.gregs[REG_EBP] = (int)stk;
    uc.uc_stack.ss_sp = (void*)(((int)stk) - (OS_TASK_DEF_STK_SIZE) + sigsize); ///< base address
    uc.uc_stack.ss_size = OS_TASK_DEF_STK_SIZE - sigsize;

    makecontext(&uc, (void*)task, 1, pdata);
    memcpy(stk, &uc, sizeof(uc));
    
    return ((OS_STK *)stk);
}


/** A new thread is started
 */
void OSStartHighRdy(void) 
{
	OSTaskSwHook();
	OSRunning = TRUE;

	// real work goes here...		
	{
	ucontext_t* ucp;

	ucp = (struct ucontext*)(OSTCBHighRdy->OSTCBStkPtr);
	setcontext(ucp);
	// not reached
	}
}

/** called by OSIntExit()
 *
 * typically, after signal is handled, a potentially new thread is picked and 
 * resumed
 */
void OSIntCtxSw(void) 
{
	// on Linux this is the same as OSCtxSw; no special treats for hw/sw ints
	OSCtxSw();
}

/** 
 * This procedure is called whenever a thread sleeps voluntarily, or, a syscall is made and the
 * the thread is not the highest priority thread anymore after rescheduling.
 *
 * This procedure is called via software interrupt (on Linux synthetic targets: signal and
 * handler).
 *
 * \todo setcontext is a user space implementation of calling sigprocmask and then restoring
 * registers; should try (ugly) hack using sigreturn which is a real syscall
 */
void OSCtxSw(void) 
{	
	struct ucontext* uc = (struct ucontext*)OSTCBHighRdy->OSTCBStkPtr;

	// at this point, registers are already saved on stack
	OSTaskSwHook();

	OSTCBCur = OSTCBHighRdy;
	OSPrioCur = OSPrioHighRdy;

	//if (uc->uc_mcontext.fpregs == 0) {
	  //  fprintf(stderr, "ctx sw: uc->uc_mcontext.fpregs == 0\n");
	    //uc->uc_mcontext.fpregs = (fpregset_t)0xbffff6cc;
	//}
	setcontext(uc);
}


/**
 * \arg signo Signal number
 * \arg info Signal info
 * \arg uc User context
 */
void OSCtxSwSigHandler(int signo, siginfo_t* info, /*struct ucontext* */ void* uc)
{
	/** Linux specific module variable of current signal context, i.e. interrupted thread
 	 */	
	OSTCBCur->OSTCBStkPtr = uc; //stk;

	OSCtxSw();
}
	

/** \todo maybe wrong for linux port?
 * adjust sp  if nesting == 1 ???? maybe if there is a seperate interrupt stack
 * linux user space port does not have a special isr stack frame layout	
 */
void OSTickISR(void) 
{
	///< register context already saved in sig handler
	OSIntEnter();
	if (OSIntNesting == 1) {
		//OSTCBCur->OSTCBStkPtr = $SP;
		//asm("mov %%esp, %0" : "=g"(OSTCBCur->OSTCBStkPtr) : );
	}
	OSTimeTick(); ///< adjusts counters and ready bitmaps
	OSIntExit();  ///< reschedules if necessary
	OSIntCtxSw(); ///< restore context and jump to highest prio task
}

/**
 * Periodic signal handler that adjusts timers
 * On Linux this is not very precise but for most applications sufficient
 *
 * Pick a value between 10 and 1000 Hz for the timer
 *
 * \todo this handler maybe executed during a restore (i.e. setcontext())
 * call. This is a problem with the setcontext() user space implementation in
 * Linux i386. Most SysV and Linux RISC systems don't have this. The tests
 * below are an indication that this situatiuon has occured; the SIGALRM
 * is then aborted...
 *
 * \todo If this is not fixed by adding a Linux syscall for get/setcontext, a user level
 * implementation with an 'clock interrupt' lock should be used
 * 
 * \arg signo Signal number
 * \arg info Signal info
 * \arg uc User context
 */
void OSTimeTickSigHandler(int signo, siginfo_t* info, /*struct ucontext* */ void* uc) 
{
	if ((((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP] >= (unsigned int)setcontext) && 
	    (((ucontext_t*)uc)->uc_mcontext.gregs[REG_EIP] < (unsigned int)(setcontext + 110))) {
	    	//fprintf(stderr, "sig timer: thread interrupted in setcontext\n");
		return;
	}
	if (((ucontext_t*)uc)->uc_mcontext.fpregs == 0) {
	    	//fprintf(stderr, "sig timer: uc->uc_mcontext.fpregs == 0\n");
	    	return;
	} 
	OSTCBCur->OSTCBStkPtr = uc; //stk;
	OSTickISR();
	// restore context
}

/**
 * Kick the periodic clock; this must be done \e after microC/OS-II is initialised, 
 * typically at the end of main()
 * 
 * Either SIGALRM or SIGVTALRM can be used
 * 
 * \attention This must be called by the user in the first task
 */
void linuxInitInt()
{
    ualarm(1000000/OS_TICKS_PER_SEC, 1000000/OS_TICKS_PER_SEC); ///< periodic mode

    // alternative
    //setitimer(ITIMER_VIRTUAL, 1000000/OS_TICKS_PER_SEC); ///< SIGVTALRM  time spent by the 
    							  ///< process in User Mode
}

/**
 * Setup of Linux specific stuff such as stacks, signals, handlers, mask etc.
 *
 * This is called inside the HAL by OSInitHookBegin()
 */
void linuxInit() 
{   
    struct sigaction act;
    sigset_t mask;
             
    sigemptyset(&mask);
    act.sa_sigaction = OSTimeTickSigHandler;
    act.sa_flags = SA_SIGINFO;// | SA_ONSTACK;  
    act.sa_mask = mask;
    sigaction(SIGALRM, &act, NULL);
    sigaction(SIGVTALRM, &act, NULL);
  
    sigemptyset(&mask);
    act.sa_sigaction = OSCtxSwSigHandler;
    act.sa_flags = SA_SIGINFO;// | SA_ONSTACK;  
    act.sa_mask = mask;
    sigaction(SIGUSR1, &act, NULL);
}

⌨️ 快捷键说明

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