/*! Remove a task from the scheduler list. Before attempting to remove the task, the owner should ensure that all connections of this task to other objects (plumber, semaphore, etc.) are removed PRIOR to calling this function, otherwise system corruption will likely occur. \fn BOOL Task_Remove(TASK_STRUCT *pstTask_) \sa Task_Add() \param pstTask_ - pointer to the task structure to remove \return (BOOL) TRUE on success, FAIL on failure */ BOOL Task_Remove(TASK_STRUCT *pstTask_) { TASK_STRUCT *pstPrev; TASK_STRUCT *pstTarget; CS_ENTER(); // initialize the target pstPrev = pstTask_; pstTarget = pstTask_->pstNext; // Look through the circular list of tasks and find the connectors. while (pstTarget != pstTask_) { // iterate through... pstPrev = pstTarget; pstTarget = pstTarget->pstNext; } // Remove the target from the circularly linked list pstPrev->pstNext = pstTask_->pstNext; // De-initialize the "next" pointer in the task list. pstTask_->pstNext = NULL; // Set the task as uninitialized (require initialization before reuse) pstTask_->eState = TASK_UNINIT; CS_EXIT(); // Call a context switch. Task_YieldSWI(); // Will not return if the thread deletes itself. return FALSE; }
/*! Post (release) a semaphore. If there are other tasks waiting for this semaphore, the highest-priority task in the semaphore wait list will claim the semaphore and run. \fn BOOL Semaphore_Post(SEMAPHORE_STRUCT *pstSem_) \param pstSem_ - pointer to the semaphore to post */ void Semaphore_Post(SEMAPHORE_STRUCT *pstSem_) { BOOL bTaskWait = FALSE; //!! Disable scheduler + interrupts (callable from ISR) CS_ENTER(); // Nothing is pending if (pstSem_->pstTask == NULL) { // Re-initialize the semaphore if (pstSem_->usSem < pstSem_->usMax) { pstSem_->usSem++; } } else { // Wake the highest priority task pending on the semaphore Semaphore_WakeNextPending(pstSem_); bTaskWait = TRUE; } CS_EXIT(); if (bTaskWait) { Task_YieldSWI(); } }
/*! Add a task to the scheduler list \fn BOOL Task_Add(TASK_STRUCT *pstTask_) \sa Task_Remove() \param pstTask_ - pointer to the task structure to add \return (BOOL) TRUE on success, FAIL on failure */ BOOL Task_Add(TASK_STRUCT *pstTask_) { CS_ENTER(); // init the task state pstTask_->eState = TASK_READY; // If the list is empty... if (pstCurrentTask == NULL) { // Circularly linked list... pstCurrentTask = pstTask_; pstCurrentTask->pstNext = pstCurrentTask; } // The list isn't empty, insert it somewhere. else { // reconnect the links... pstTask_->pstNext = pstCurrentTask->pstNext; pstCurrentTask->pstNext = pstTask_; } CS_EXIT(); // It's all good - no maximum items in the list... return TRUE; }
/*! Signal a task to wait for a semaphore. If the semaphore is not available, the task will block and wait until the semaphore becomes available. \fn BOOL Semaphore_Pend(SEMAPHORE_STRUCT *pstSem_, USHORT usTime_) \param pstSem_ - pointer to the semaphore to pend on \param usTime_ - the time limit to wait for the semaphore. Integer number of ticks or TIME_FOREVER \return BOOL - TRUE on success, FALSE on timeout */ BOOL Semaphore_Pend(SEMAPHORE_STRUCT *pstSem_, USHORT usTime_) { BOOL bTaskWait = FALSE; BOOL bReturn = TRUE; TASK_STRUCT *pstTask; //!! Disable Scheduler !! CS_ENTER(); pstTask = Task_GetCurrentTask(); if (pstSem_->usSem != 0) { // Semaphore isn't claimed, claim it. pstSem_->usSem--; } else { // Remove the task from the ready state - blocking on semaphore pstTask->eState = TASK_BLOCKED; pstTask->usTimeLeft = usTime_; // Add the task to the semaphore's waiting list. Semaphore_AddToList(pstSem_, pstTask); bTaskWait = TRUE; } CS_EXIT(); if (bTaskWait) { // Switch tasks immediately Task_YieldSWI(); CS_ENTER(); if (pstTask->bTimeout) { // If there was a timeout waiting for the semaphore bReturn = FALSE; Semaphore_DeleteFromList(pstSem_, pstTask); pstTask->bTimeout = FALSE; // Reset the task timeout flag } CS_EXIT(); } return bReturn; }
/*! Disables the scheduler, and returns the scheduler's previous state. This is used in combination with Task_SchedulerRestore() to provide the scheduler-disabled context. \fn Task_SchedulerDisable(void) \return BOOL - the previous state of the scheduler (TRUE = ENABLED) */ BOOL Task_SchedulerDisable(void) { BOOL bReturn; CS_ENTER(); bReturn = Task_IsSchedulerEnabled(); Task_SetScheduler(FALSE); CS_EXIT(); return bReturn; }
/*! Set a task to sleep for a period of time specified in the arguments \sa Task_Tick() \fn void Task_Sleep(USHORT usTime_) \param usTime_ - the time period in RTOS ticks to sleep through */ void Task_Sleep(USHORT usTime_) { // Do this in a scheduler-disabled context CS_ENTER(); pstCurrentTask->eState = TASK_SLEEP; pstCurrentTask->usTimeLeft = usTime_; CS_EXIT(); // Call the scheduler immediately. Task_YieldSWI(); }
//--------------------------------------------------------------------------- void Notify_Wait( Notify_t *pstNotify_, bool *pbFlag_ ) { CS_ENTER(); BlockingObject_Block( (ThreadList_t*)pstNotify_, Scheduler_GetCurrentThread() ); if (pbFlag_) { *pbFlag_ = false; } CS_EXIT(); Thread_Yield(); if (pbFlag_) { *pbFlag_ = true; } }
//--------------------------------------------------------------------------- K_USHORT ATMegaUART_Read( ATMegaUART_t *pstUART_, K_USHORT usSizeIn_, K_UCHAR *pvData_ ) { // Read a string of characters of length N. Return the number of bytes // actually read. If less than the 1 length, this indicates that // the buffer is full and that the app needs to wait. K_USHORT i = 0; K_USHORT usRead = 0; K_BOOL bExit = 0; K_UCHAR *pucData = (K_UCHAR*)pvData_; for (i = 0; i < usSizeIn_; i++) { // If Tail != Head, there's data in the buffer. CS_ENTER(); if (pstUART_->ucRxTail != pstUART_->ucRxHead) { // We have room to add the byte, so add it. pucData[i] = pstUART_->pucRxBuffer[pstUART_->ucRxTail]; // Update the buffer head pointer. pstUART_->ucRxTail++; if (pstUART_->ucRxTail >= pstUART_->ucRxSize) { pstUART_->ucRxTail = 0; } usRead++; } else { // Can't do anything else - the buffer is empty bExit = 1; } CS_EXIT(); // If we have to bail because the buffer is empty, do it now. if (bExit == 1) { break; } } return usRead; }
//--------------------------------------------------------------------------- void ATMegaUART_StartTx( ATMegaUART_t *pstUART_ ) { // Send the tail byte out. K_UCHAR ucNext; CS_ENTER(); // Send the byte at the tail index UART_UDR = pstUART_->pucTxBuffer[pstUART_->ucTxTail]; // Update the tail index ucNext = (pstUART_->ucTxTail + 1); if (ucNext >= pstUART_->ucTxSize) { ucNext = 0; } pstUART_->ucTxTail = ucNext; CS_EXIT(); }
bool Notify_Wait( Notify_t *pstNotify_, K_ULONG ulWaitTimeMS_, bool *pbFlag_ ) { bool bUseTimer = false; Timer_t stNotifyTimer; CS_ENTER(); if (ulWaitTimeMS_) { bUseTimer = true; Thread_SetExpired( Scheduler_GetCurrentThread(), false ); Timer_Init( &stNotifyTimer ); Timer_Start( &stNotifyTimer, 0, ulWaitTimeMS_, TimedNotify_Callback, (void*)pstNotify_); } Block(g_pstCurrent); if (pbFlag_) { *pbFlag_ = false; } CS_EXIT(); Thread_Yield(); if (pbFlag_) { *pbFlag_ = true; } if (bUseTimer) { Timer_Stop( &stNotifyTimer ); return ( Thread_GetExpired( Scheduler_GetCurrentThread() ) == false ); } return true; }
/*! Allocates a single block of memory from the heap based on the required size (if one is available). \fn UCHAR *Heap_Alloc(USHORT usSize_) \param usSize_ - the size of the block to allocate \return pointer to the block of newly-allocated memory, or NULL on failure */ UCHAR *Heap_Alloc(USHORT usSize_) { USHORT i; BOOL bDone = FALSE; UCHAR *pucReturn = NULL; // Try to allocate the smallest possible block for(i = 0; i < HEAP_NUM_ELEMENTS; i++) { if (stHeap.astHeapElements[i].usSize > usSize_) { CS_ENTER(); if (stHeap.astHeapElements[i].bInUse == FALSE) { // Found smallest suitable block that isn't in use. // Set the in-use flag to true stHeap.astHeapElements[i].bInUse = TRUE; // Set the return value pucReturn = &(stHeap.aucPool[stHeap.astHeapElements[i].usOffset]); // Found something, bail. bDone = TRUE; } CS_EXIT(); // break on success. if (bDone) { break; } } } // Return the address of the return pucReturn; }
/*! De-allocates a previously allocated block of memory based on the address given. \fn void Heap_Free(void *pvData_) \param pvData_ - pointer to the previously-allocated memory block */ void Heap_Free(void *pvData_) { USHORT i; BOOL bDone = FALSE; UCHAR *pucData = NULL; UCHAR *pucTemp = NULL; // Set the UCHAR* version of the input void parameter pucData = (UCHAR*)pvData_; // Go through the whole list trying to find the matching element allocated for (i = 0; i < HEAP_NUM_ELEMENTS; i++) { // Only looking at allocated elements if (stHeap.astHeapElements[i].bInUse == TRUE) { CS_ENTER(); // Get the address for the element's array pucTemp = &(stHeap.aucPool[stHeap.astHeapElements[i].usOffset]); // Element addresses match, this is the match if (pucTemp == pucData) { // Deallocate the element and exit stHeap.astHeapElements[i].bInUse = FALSE; bDone = TRUE; } CS_EXIT(); // Break on success if (bDone) { break; } } } }
//--------------------------------------------------------------------------- void Notify_Signal( Notify_t *pstNotify_ ) { bool bReschedule = false; CS_ENTER(); Thread_t *pstCurrent = (Thread_t*)LinkList_GetHead((LinkListNode_t*)pstNotify_); while (pstCurrent != NULL) { BlockingObject_UnBlock( pstCurrent ); if ( !bReschedule && ( Thread_GetCurPriority( pstCurrent ) >= Thread_GetCurPriority( Scheduler_GetCurrentThread() ) ) ) { bReschedule = true; } pstCurrent = (Thread_t*)LinkList_GetHead(pstNotify_); } CS_EXIT(); if (bReschedule) { Thread_Yield(); } }
//--------------------------------------------------------------------------- K_USHORT ATMegaUART_Write( ATMegaUART_t *pstUART_, K_USHORT usSizeOut_, K_UCHAR *pvData_) { // Write a string of characters of length N. Return the number of bytes // actually written. If less than the 1 length, this indicates that // the buffer is full and that the app needs to wait. K_USHORT i = 0; K_USHORT usWritten = 0; K_UCHAR ucNext; K_BOOL bActivate = 0; K_BOOL bExit = 0; K_UCHAR *pucData = (K_UCHAR*)pvData_; // If the head = tail, we need to start sending data out the data ourselves. if (pstUART_->ucTxHead == pstUART_->ucTxTail) { bActivate = 1; } for (i = 0; i < usSizeOut_; i++) { CS_ENTER(); // Check that head != tail (we have room) ucNext = (pstUART_->ucTxHead + 1); if (ucNext >= pstUART_->ucTxSize) { ucNext = 0; } if (ucNext != pstUART_->ucTxTail) { // We have room to add the byte, so add it. pstUART_->pucTxBuffer[pstUART_->ucTxHead] = pucData[i]; // Update the buffer head pointer. pstUART_->ucTxHead = ucNext; usWritten++; } else { // Can't do anything - buffer is full bExit = 1; } CS_EXIT(); // bail if the buffer is full if (bExit == 1) { break; } } // Activate the transmission if we're currently idle if (bActivate == 1) { // We know we're idle if we get here. CS_ENTER(); if (UART_SRA & (1 << UDRE0)) { ATMegaUART_StartTx(pstUART_); } CS_EXIT(); } return usWritten; }
/*! Used to restore the state of the scheduler after performing an operation that operates in a scheduler-disabled context \fn Task_SchedulerRestore(BOOL bState_) \param bState_ - TRUE to enable the scheduler, FALSE to disable the scheduler */ void Task_SchedulerRestore(BOOL bState_) { CS_ENTER(); Task_SetScheduler(bState_); CS_EXIT(); }