openharmony_kernel_liteos_m/components/signal/los_signal.c

442 lines
12 KiB
C

/*
* Copyright (c) 2022-2022 Huawei Device Co., Ltd. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* 3. Neither the name of the copyright holder nor the names of its contributors may be used
* to endorse or promote products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "los_signal.h"
#include "securec.h"
#include "los_config.h"
#include "los_interrupt.h"
#include "los_task.h"
#include "los_sched.h"
#include "los_debug.h"
#include "los_memory.h"
#include "los_context.h"
#include "los_arch_context.h"
UINTPTR OsSignalTaskContextRestore(VOID)
{
#if (LOSCFG_KERNEL_SIGNAL == 1)
LosTaskCB *task = OsCurrTaskGet();
OsSigCB *sigCB = (OsSigCB *)task->sig;
UINTPTR sp;
if ((sigCB == NULL) || (sigCB->sigRestoreSP == NULL)) {
return 0;
}
sp = (UINTPTR)sigCB->sigRestoreSP;
sigCB->sigRestoreSP = NULL;
return sp;
#else
return 0;
#endif
}
#if (LOSCFG_KERNEL_SIGNAL == 1)
STATIC LOS_DL_LIST g_waitSignalList;
STATIC VOID SignalDefaultHandler(INT32 signo)
{
PRINTK("signal default handler, signo = %d\n", signo);
}
STATIC UINT32 AddSigInfoToList(OsSigCB *sigCB, siginfo_t *info)
{
OsSigInfoNode *tmpInfo = NULL;
BOOL findFlag = FALSE;
LOS_DL_LIST_FOR_EACH_ENTRY(tmpInfo, &sigCB->sigInfoList, OsSigInfoNode, node) {
if (tmpInfo->info.si_signo == info->si_signo) {
findFlag = TRUE;
break;
}
}
if (findFlag == FALSE) {
tmpInfo = (OsSigInfoNode *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(OsSigInfoNode));
if (tmpInfo == NULL) {
return LOS_NOK;
}
LOS_ListAdd(&sigCB->sigInfoList, &tmpInfo->node);
}
(VOID)memcpy_s(&tmpInfo->info, sizeof(siginfo_t), info, sizeof(siginfo_t));
return LOS_OK;
}
STATIC VOID DeleteSigInfoFromList(OsSigCB *sigCB, INT32 sigNo)
{
OsSigInfoNode *tmpInfo = NULL;
BOOL findFlag = FALSE;
LOS_DL_LIST_FOR_EACH_ENTRY(tmpInfo, &sigCB->sigInfoList, OsSigInfoNode, node) {
if (tmpInfo->info.si_signo == sigNo) {
LOS_ListDelete(&tmpInfo->node);
findFlag = TRUE;
break;
}
}
if (findFlag == TRUE) {
(VOID)memcpy_s(&sigCB->sigInfo, sizeof(siginfo_t), &tmpInfo->info, sizeof(siginfo_t));
(VOID)LOS_MemFree(OS_SYS_MEM_ADDR, tmpInfo);
}
}
STATIC VOID SignalHandle(LosTaskCB *task, BOOL cleanStatus)
{
UINT32 intSave;
OsSigCB *sigCB = NULL;
intSave = LOS_IntLock();
sigCB = task->sig;
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return;
}
while (sigCB->sigPendFlag & sigCB->sigSetFlag) {
UINT32 sigFlag = sigCB->sigPendFlag & sigCB->sigSetFlag;
INT32 sigNo = LOS_SIGNAL_SUPPORT_MAX - CLZ(sigFlag) + 1;
DeleteSigInfoFromList(sigCB, sigNo);
SIG_HANDLER handler = sigCB->sigHandlers[sigNo - 1];
sigCB->sigPendFlag &= ~LOS_SIGNAL_MASK(sigNo);
LOS_IntRestore(intSave);
if (handler != NULL) {
handler(sigNo);
}
intSave = LOS_IntLock();
if (cleanStatus == TRUE) {
task->taskStatus &= ~OS_TASK_FLAG_SIGNAL;
}
}
LOS_IntRestore(intSave);
}
STATIC VOID SignalEntry(INT32 sigNo)
{
(void)sigNo;
LosTaskCB *task = OsCurrTaskGet();
OsSigCB *sigCB = (OsSigCB *)task->sig;
SignalHandle(task, FALSE);
task->stackPointer = sigCB->sigSaveSP;
sigCB->sigSaveSP = NULL;
sigCB->sigRestoreSP = task->stackPointer;
task->taskStatus &= ~OS_TASK_FLAG_SIGNAL;
LOS_Schedule();
}
STATIC VOID SignalSend(LosTaskCB *task, INT32 sigNo)
{
UINT32 intSave;
OsSigCB *sigCB = NULL;
sigset_t mask = LOS_SIGNAL_MASK(sigNo);
intSave = LOS_IntLock();
sigCB = task->sig;
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return;
}
if (!(sigCB->sigPendFlag & mask)) {
sigCB->sigPendFlag |= mask;
}
if (task == OsCurrTaskGet()) {
task->taskStatus |= OS_TASK_FLAG_SIGNAL;
LOS_IntRestore(intSave);
if (!OS_INT_ACTIVE) {
SignalHandle(task, TRUE);
}
} else {
if (sigCB->sigStatus & OS_SIGNAL_STATUS_WAIT) {
if (sigCB->sigWaitFlag & LOS_SIGNAL_MASK(sigNo)) {
DeleteSigInfoFromList(sigCB, sigNo);
OsSchedTaskWake(task);
task->taskStatus |= OS_TASK_FLAG_SIGNAL;
}
} else if (!(task->taskStatus & OS_TASK_FLAG_SIGNAL)) {
task->taskStatus |= OS_TASK_FLAG_SIGNAL;
sigCB->sigSaveSP = task->stackPointer;
sigCB->sigRestoreSP = NULL;
task->stackPointer = ArchSignalContextInit(task->stackPointer, (VOID *)task->topOfStack,
(UINTPTR)SignalEntry, sigNo);
}
LOS_IntRestore(intSave);
LOS_Schedule();
}
}
STATIC OsSigCB *SignalCBInit(LosTaskCB *task)
{
OsSigCB *sigCB = NULL;
UINT32 i;
if (task->sig == NULL) {
sigCB = (OsSigCB *)LOS_MemAlloc(OS_SYS_MEM_ADDR, sizeof(OsSigCB));
if (sigCB == NULL) {
return NULL;
}
(VOID)memset_s(sigCB, sizeof(OsSigCB), 0, sizeof(OsSigCB));
LOS_ListInit(&sigCB->sigInfoList);
for (i = 0; i <= LOS_SIGNAL_SUPPORT_MAX; i++) {
sigCB->sigHandlers[i] = SignalDefaultHandler;
}
task->sig = (VOID *)sigCB;
} else {
sigCB = (OsSigCB *)task->sig;
}
return sigCB;
}
SIG_HANDLER LOS_SignalSet(INT32 sigNo, SIG_HANDLER handler)
{
UINT32 intSave;
SIG_HANDLER old = NULL;
LosTaskCB *task = OsCurrTaskGet();
OsSigCB *sigCB = NULL;
if (task == NULL) {
return SIG_ERR;
}
if (!OS_SIGNAL_VALID(sigNo)) {
return SIG_ERR;
}
intSave = LOS_IntLock();
sigCB = SignalCBInit(task);
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return SIG_ERR;
}
old = sigCB->sigHandlers[sigNo - 1]; /* signal number from 1, but index from 0 */
if (handler == SIG_IGN) {
sigCB->sigHandlers[sigNo - 1] = NULL;
sigCB->sigSetFlag &= ~LOS_SIGNAL_MASK(sigNo);
} else if (handler == SIG_DFL) {
sigCB->sigHandlers[sigNo - 1] = SignalDefaultHandler;
sigCB->sigSetFlag |= LOS_SIGNAL_MASK(sigNo);
} else {
sigCB->sigHandlers[sigNo - 1] = handler;
sigCB->sigSetFlag |= LOS_SIGNAL_MASK(sigNo);
}
LOS_IntRestore(intSave);
return old;
}
UINT32 LOS_SignalMask(INT32 how, const sigset_t *set, sigset_t *oldSet)
{
UINT32 intSave;
LosTaskCB *task = OsCurrTaskGet();
OsSigCB *sigCB = NULL;
if (task == NULL) {
return LOS_ERRNO_SIGNAL_CAN_NOT_CALL;
}
intSave = LOS_IntLock();
sigCB = SignalCBInit(task);
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_NO_MEMORY;
}
if (oldSet != NULL) {
*oldSet = sigCB->sigSetFlag;
}
if (set == NULL) {
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_INVALID;
}
switch (how) {
case SIG_BLOCK:
sigCB->sigSetFlag &= ~*set;
break;
case SIG_SETMASK:
sigCB->sigSetFlag = *set;
break;
case SIG_UNBLOCK:
sigCB->sigSetFlag |= *set;
break;
default:
PRINT_ERR("The error parameter how = %d\n", how);
break;
}
LOS_IntRestore(intSave);
return LOS_OK;
}
STATIC INLINE UINT32 SignalTimedWait(LosTaskCB *task, const sigset_t *set, UINT32 timeout, UINT32 *intSave)
{
OsSigCB *sigCB = (OsSigCB *)task->sig;
INT32 sigNo;
if (timeout == 0) {
LOS_IntRestore(*intSave);
return LOS_ERRNO_SIGNAL_INVALID;
}
if (OS_INT_ACTIVE) {
LOS_IntRestore(*intSave);
return LOS_ERRNO_SIGNAL_PEND_INTERR;
}
sigCB->sigWaitFlag |= *set;
sigCB->sigStatus |= OS_SIGNAL_STATUS_WAIT;
OsSchedTaskWait(&g_waitSignalList, timeout);
LOS_IntRestore(*intSave);
LOS_Schedule();
*intSave = LOS_IntLock();
task->taskStatus &= ~OS_TASK_FLAG_SIGNAL;
sigCB->sigStatus &= ~OS_SIGNAL_STATUS_WAIT;
sigCB->sigWaitFlag = 0;
if (task->taskStatus & OS_TASK_STATUS_TIMEOUT) {
task->taskStatus &= ~OS_TASK_STATUS_TIMEOUT;
LOS_IntRestore(*intSave);
return LOS_ERRNO_SIGNAL_TIMEOUT;
}
sigNo = sigCB->sigInfo.si_signo;
sigCB->sigPendFlag &= ~LOS_SIGNAL_MASK(sigNo);
return sigNo;
}
UINT32 LOS_SignalWait(const sigset_t *set, siginfo_t *info, UINT32 timeout)
{
UINT32 intSave;
LosTaskCB *task = OsCurrTaskGet();
OsSigCB *sigCB = NULL;
sigset_t sigFlag;
INT32 sigNo;
if ((set == NULL) || (*set == 0)) {
return LOS_ERRNO_SIGNAL_INVALID;
}
if (task == NULL) {
return LOS_ERRNO_SIGNAL_CAN_NOT_CALL;
}
intSave = LOS_IntLock();
sigCB = SignalCBInit(task);
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_NO_MEMORY;
}
sigFlag = sigCB->sigPendFlag & *set;
if (sigFlag) {
sigCB->sigPendFlag ^= sigFlag;
sigNo = LOS_SIGNAL_SUPPORT_MAX - CLZ(sigFlag) + 1;
DeleteSigInfoFromList(sigCB, sigNo);
} else {
sigNo = SignalTimedWait(task, set, timeout, &intSave);
if (sigNo > LOS_SIGNAL_SUPPORT_MAX) {
LOS_IntRestore(intSave);
return sigNo;
}
}
if (info != NULL) {
(VOID)memcpy_s(info, sizeof(siginfo_t), &sigCB->sigInfo, sizeof(siginfo_t));
}
LOS_IntRestore(intSave);
return sigNo;
}
UINT32 LOS_SignalSend(UINT32 taskID, INT32 sigNo)
{
siginfo_t info;
UINT32 intSave;
OsSigCB *sigCB = NULL;
LosTaskCB *task = NULL;
if (taskID > LOSCFG_BASE_CORE_TSK_LIMIT) {
return LOS_ERRNO_SIGNAL_INVALID;
}
if (!OS_SIGNAL_VALID(sigNo)) {
return LOS_ERRNO_SIGNAL_INVALID;
}
info.si_signo = sigNo;
info.si_code = SI_USER;
info.si_value.sival_ptr = NULL;
intSave = LOS_IntLock();
task = OS_TCB_FROM_TID(taskID);
sigCB = SignalCBInit(task);
if (sigCB == NULL) {
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_NO_MEMORY;
}
if (!(sigCB->sigSetFlag & LOS_SIGNAL_MASK(sigNo))) { /* the signal has not been set */
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_NO_SET;
}
UINT32 ret = AddSigInfoToList(sigCB, &info);
if (ret != LOS_OK) {
LOS_IntRestore(intSave);
return LOS_ERRNO_SIGNAL_NO_MEMORY;
}
LOS_IntRestore(intSave);
/* send signal to this thread */
SignalSend(task, sigNo);
return LOS_OK;
}
UINT32 OsSignalInit(VOID)
{
LOS_ListInit(&g_waitSignalList);
return LOS_OK;
}
#endif