472 lines
9.2 KiB
C
472 lines
9.2 KiB
C
/*
|
|
* Copyright (c) 1997-2010, 2012-2015 Wind River Systems, Inc.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
*
|
|
* @brief Semaphore kernel services.
|
|
*/
|
|
|
|
#include <microkernel.h>
|
|
#include <toolchain.h>
|
|
#include <sections.h>
|
|
|
|
#include <micro_private.h>
|
|
|
|
/**
|
|
*
|
|
* @brief Update value of semaphore structure
|
|
*
|
|
* This routine updates the value of the semaphore by 0 or more units, then
|
|
* gives the semaphore to any waiting tasks that can now be satisfied.
|
|
*
|
|
* @param n Number of additional times semaphore has been given.
|
|
* @param sema Semaphore structure to update.
|
|
*
|
|
* @return N/A
|
|
*/
|
|
void _k_sem_struct_value_update(int n, struct _k_sem_struct *S)
|
|
{
|
|
struct k_args *A, *X, *Y;
|
|
|
|
#ifdef CONFIG_OBJECT_MONITOR
|
|
S->count += n;
|
|
#endif
|
|
|
|
S->level += n;
|
|
A = S->waiters;
|
|
Y = NULL;
|
|
while (A && S->level) {
|
|
X = A->next;
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Comm == _K_SVC_SEM_WAIT_REQUEST
|
|
|| A->Comm == _K_SVC_SEM_WAIT_REPLY_TIMEOUT)
|
|
#else
|
|
if (A->Comm == _K_SVC_SEM_WAIT_REQUEST)
|
|
#endif
|
|
{
|
|
S->level--;
|
|
if (Y) {
|
|
Y->next = X;
|
|
} else {
|
|
S->waiters = X;
|
|
}
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.timer) {
|
|
_k_timeout_cancel(A);
|
|
A->Comm = _K_SVC_SEM_WAIT_REPLY;
|
|
} else {
|
|
#endif
|
|
A->Time.rcode = RC_OK;
|
|
_k_state_bit_reset(A->Ctxt.task, TF_SEMA);
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
}
|
|
#endif
|
|
} else if (A->Comm == _K_SVC_SEM_GROUP_WAIT_REQUEST) {
|
|
S->level--;
|
|
A->Comm = _K_SVC_SEM_GROUP_WAIT_READY;
|
|
GETARGS(Y);
|
|
*Y = *A;
|
|
SENDARGS(Y);
|
|
Y = A;
|
|
} else {
|
|
Y = A;
|
|
}
|
|
A = X;
|
|
}
|
|
}
|
|
|
|
void _k_sem_group_wait(struct k_args *R)
|
|
{
|
|
struct k_args *A = R->Ctxt.args;
|
|
|
|
FREEARGS(R);
|
|
if (--(A->args.s1.nsem) == 0) {
|
|
_k_state_bit_reset(A->Ctxt.task, TF_LIST);
|
|
}
|
|
}
|
|
|
|
void _k_sem_group_wait_cancel(struct k_args *A)
|
|
{
|
|
struct _k_sem_struct *S = (struct _k_sem_struct *)A->args.s1.sema;
|
|
struct k_args *X = S->waiters;
|
|
struct k_args *Y = NULL;
|
|
|
|
while (X && (X->priority <= A->priority)) {
|
|
if (X->Ctxt.args == A->Ctxt.args) {
|
|
if (Y) {
|
|
Y->next = X->next;
|
|
} else {
|
|
S->waiters = X->next;
|
|
}
|
|
if (X->Comm == _K_SVC_SEM_GROUP_WAIT_REQUEST
|
|
|| X->Comm == _K_SVC_SEM_GROUP_WAIT_READY) {
|
|
if (X->Comm == _K_SVC_SEM_GROUP_WAIT_READY) {
|
|
/* obtain struct k_args of waiting task */
|
|
|
|
struct k_args *waitTaskArgs = X->Ctxt.args;
|
|
|
|
/*
|
|
* Determine if the wait cancellation request is being
|
|
* processed after the state of the 'waiters' packet state
|
|
* has been updated to _K_SVC_SEM_GROUP_WAIT_READY, but before
|
|
* the _K_SVC_SEM_GROUP_WAIT_READY packet has been processed.
|
|
* This will occur if a _K_SVC_SEM_GROUP_WAIT_TIMEOUT
|
|
* timer expiry occurs between the update of the packet state
|
|
* and the processing of the WAITMRDY packet.
|
|
*/
|
|
if (unlikely(waitTaskArgs->args.s1.sema ==
|
|
ENDLIST)) {
|
|
waitTaskArgs->args.s1.sema = A->args.s1.sema;
|
|
} else {
|
|
_k_sem_struct_value_update(1, S);
|
|
}
|
|
}
|
|
|
|
_k_sem_group_wait(X);
|
|
} else {
|
|
FREEARGS(X); /* ERROR */
|
|
}
|
|
FREEARGS(A);
|
|
return;
|
|
}
|
|
|
|
Y = X;
|
|
X = X->next;
|
|
}
|
|
A->next = X;
|
|
if (Y) {
|
|
Y->next = A;
|
|
} else {
|
|
S->waiters = A;
|
|
}
|
|
}
|
|
|
|
void _k_sem_group_wait_accept(struct k_args *A)
|
|
{
|
|
struct _k_sem_struct *S = (struct _k_sem_struct *)A->args.s1.sema;
|
|
struct k_args *X = S->waiters;
|
|
struct k_args *Y = NULL;
|
|
|
|
while (X && (X->priority <= A->priority)) {
|
|
if (X->Ctxt.args == A->Ctxt.args) {
|
|
if (Y) {
|
|
Y->next = X->next;
|
|
} else {
|
|
S->waiters = X->next;
|
|
}
|
|
if (X->Comm == _K_SVC_SEM_GROUP_WAIT_READY) {
|
|
_k_sem_group_wait(X);
|
|
} else {
|
|
FREEARGS(X); /* ERROR */
|
|
}
|
|
FREEARGS(A);
|
|
return;
|
|
}
|
|
|
|
Y = X;
|
|
X = X->next;
|
|
}
|
|
/* ERROR */
|
|
}
|
|
|
|
void _k_sem_group_wait_timeout(struct k_args *A)
|
|
{
|
|
ksem_t *L;
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.timer) {
|
|
FREETIMER(A->Time.timer);
|
|
}
|
|
#endif
|
|
|
|
L = A->args.s1.list;
|
|
while (*L != ENDLIST) {
|
|
struct k_args *R;
|
|
|
|
GETARGS(R);
|
|
R->priority = A->priority;
|
|
R->Comm =
|
|
((*L == A->args.s1.sema) ?
|
|
_K_SVC_SEM_GROUP_WAIT_ACCEPT : _K_SVC_SEM_GROUP_WAIT_CANCEL);
|
|
R->Ctxt.args = A;
|
|
R->args.s1.sema = *L++;
|
|
SENDARGS(R);
|
|
}
|
|
}
|
|
|
|
void _k_sem_group_ready(struct k_args *R)
|
|
{
|
|
struct k_args *A = R->Ctxt.args;
|
|
|
|
if (A->args.s1.sema == ENDLIST) {
|
|
A->args.s1.sema = R->args.s1.sema;
|
|
A->Comm = _K_SVC_SEM_GROUP_WAIT_TIMEOUT;
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.timer) {
|
|
_k_timeout_cancel(A);
|
|
} else
|
|
#endif
|
|
_k_sem_group_wait_timeout(A);
|
|
}
|
|
FREEARGS(R);
|
|
}
|
|
|
|
void _k_sem_wait_reply(struct k_args *A)
|
|
{
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.timer) {
|
|
FREETIMER(A->Time.timer);
|
|
}
|
|
if (A->Comm == _K_SVC_SEM_WAIT_REPLY_TIMEOUT) {
|
|
REMOVE_ELM(A);
|
|
A->Time.rcode = RC_TIME;
|
|
} else
|
|
#endif
|
|
A->Time.rcode = RC_OK;
|
|
_k_state_bit_reset(A->Ctxt.task, TF_SEMA);
|
|
}
|
|
|
|
void _k_sem_wait_reply_timeout(struct k_args *A)
|
|
{
|
|
_k_sem_wait_reply(A);
|
|
}
|
|
|
|
void _k_sem_group_wait_request(struct k_args *A)
|
|
{
|
|
struct _k_sem_struct *S = (struct _k_sem_struct *)A->args.s1.sema;
|
|
struct k_args *X = S->waiters;
|
|
struct k_args *Y = NULL;
|
|
|
|
while (X && (X->priority <= A->priority)) {
|
|
if (X->Ctxt.args == A->Ctxt.args) {
|
|
if (Y) {
|
|
Y->next = X->next;
|
|
} else {
|
|
S->waiters = X->next;
|
|
}
|
|
if (X->Comm == _K_SVC_SEM_GROUP_WAIT_CANCEL) {
|
|
_k_sem_group_wait(X);
|
|
} else {
|
|
FREEARGS(X); /* ERROR */
|
|
}
|
|
FREEARGS(A);
|
|
return;
|
|
}
|
|
|
|
Y = X;
|
|
X = X->next;
|
|
}
|
|
A->next = X;
|
|
if (Y) {
|
|
Y->next = A;
|
|
} else {
|
|
S->waiters = A;
|
|
}
|
|
_k_sem_struct_value_update(0, S);
|
|
}
|
|
|
|
void _k_sem_group_wait_any(struct k_args *A)
|
|
{
|
|
ksem_t *L;
|
|
|
|
L = A->args.s1.list;
|
|
A->args.s1.sema = ENDLIST;
|
|
A->args.s1.nsem = 0;
|
|
|
|
if (*L == ENDLIST) {
|
|
return;
|
|
}
|
|
|
|
while (*L != ENDLIST) {
|
|
struct k_args *R;
|
|
|
|
GETARGS(R);
|
|
R->priority = _k_current_task->priority;
|
|
R->Comm = _K_SVC_SEM_GROUP_WAIT_REQUEST;
|
|
R->Ctxt.args = A;
|
|
R->args.s1.sema = *L++;
|
|
SENDARGS(R);
|
|
(A->args.s1.nsem)++;
|
|
}
|
|
|
|
A->Ctxt.task = _k_current_task;
|
|
_k_state_bit_set(_k_current_task, TF_LIST);
|
|
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.ticks != TICKS_NONE) {
|
|
if (A->Time.ticks == TICKS_UNLIMITED) {
|
|
A->Time.timer = NULL;
|
|
} else {
|
|
A->Comm = _K_SVC_SEM_GROUP_WAIT_TIMEOUT;
|
|
_k_timeout_alloc(A);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
void _k_sem_wait_request(struct k_args *A)
|
|
{
|
|
struct _k_sem_struct *S;
|
|
uint32_t Sid;
|
|
|
|
Sid = A->args.s1.sema;
|
|
S = (struct _k_sem_struct *)Sid;
|
|
|
|
if (S->level) {
|
|
S->level--;
|
|
A->Time.rcode = RC_OK;
|
|
} else if (A->Time.ticks != TICKS_NONE) {
|
|
A->Ctxt.task = _k_current_task;
|
|
A->priority = _k_current_task->priority;
|
|
_k_state_bit_set(_k_current_task, TF_SEMA);
|
|
INSERT_ELM(S->waiters, A);
|
|
#ifdef CONFIG_SYS_CLOCK_EXISTS
|
|
if (A->Time.ticks == TICKS_UNLIMITED) {
|
|
A->Time.timer = NULL;
|
|
} else {
|
|
A->Comm = _K_SVC_SEM_WAIT_REPLY_TIMEOUT;
|
|
_k_timeout_alloc(A);
|
|
}
|
|
#endif
|
|
return;
|
|
} else {
|
|
A->Time.rcode = RC_FAIL;
|
|
}
|
|
}
|
|
|
|
int task_sem_take(ksem_t sema, int32_t timeout)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_WAIT_REQUEST;
|
|
A.Time.ticks = timeout;
|
|
A.args.s1.sema = sema;
|
|
KERNEL_ENTRY(&A);
|
|
return A.Time.rcode;
|
|
}
|
|
|
|
ksem_t task_sem_group_take(ksemg_t group, int32_t timeout)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_GROUP_WAIT_ANY;
|
|
A.priority = _k_current_task->priority;
|
|
A.Time.ticks = timeout;
|
|
A.args.s1.list = group;
|
|
KERNEL_ENTRY(&A);
|
|
return A.args.s1.sema;
|
|
}
|
|
|
|
void _k_sem_signal(struct k_args *A)
|
|
{
|
|
uint32_t Sid = A->args.s1.sema;
|
|
struct _k_sem_struct *S = (struct _k_sem_struct *)Sid;
|
|
|
|
_k_sem_struct_value_update(1, S);
|
|
}
|
|
|
|
void _k_sem_group_signal(struct k_args *A)
|
|
{
|
|
ksem_t *L = A->args.s1.list;
|
|
|
|
while ((A->args.s1.sema = *L++) != ENDLIST) {
|
|
_k_sem_signal(A);
|
|
}
|
|
}
|
|
|
|
void task_sem_give(ksem_t sema)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_SIGNAL;
|
|
A.args.s1.sema = sema;
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
void task_sem_group_give(ksemg_t group)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_GROUP_SIGNAL;
|
|
A.args.s1.list = group;
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
FUNC_ALIAS(isr_sem_give, fiber_sem_give, void);
|
|
|
|
void isr_sem_give(ksem_t sema)
|
|
{
|
|
nano_isr_stack_push(&_k_command_stack,
|
|
(uint32_t)sema | KERNEL_CMD_SEMAPHORE_TYPE);
|
|
}
|
|
|
|
void _k_sem_reset(struct k_args *A)
|
|
{
|
|
uint32_t Sid = A->args.s1.sema;
|
|
struct _k_sem_struct *S = (struct _k_sem_struct *)Sid;
|
|
|
|
S->level = 0;
|
|
}
|
|
|
|
void _k_sem_group_reset(struct k_args *A)
|
|
{
|
|
ksem_t *L = A->args.s1.list;
|
|
|
|
while ((A->args.s1.sema = *L++) != ENDLIST) {
|
|
_k_sem_reset(A);
|
|
}
|
|
}
|
|
|
|
void task_sem_reset(ksem_t sema)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_RESET;
|
|
A.args.s1.sema = sema;
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
void task_sem_group_reset(ksemg_t group)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_GROUP_RESET;
|
|
A.args.s1.list = group;
|
|
KERNEL_ENTRY(&A);
|
|
}
|
|
|
|
void _k_sem_inquiry(struct k_args *A)
|
|
{
|
|
struct _k_sem_struct *S;
|
|
uint32_t Sid;
|
|
|
|
Sid = A->args.s1.sema;
|
|
S = (struct _k_sem_struct *)Sid;
|
|
A->Time.rcode = S->level;
|
|
}
|
|
|
|
int task_sem_count_get(ksem_t sema)
|
|
{
|
|
struct k_args A;
|
|
|
|
A.Comm = _K_SVC_SEM_INQUIRY;
|
|
A.args.s1.sema = sema;
|
|
KERNEL_ENTRY(&A);
|
|
return A.Time.rcode;
|
|
}
|