1 /*
2 * Copyright (c) 2009-2022 Huawei Technologies Co., Ltd. All rights reserved.
3 *
4 * UniProton is licensed under Mulan PSL v2.
5 * You can use this software according to the terms and conditions of the Mulan PSL v2.
6 * You may obtain a copy of Mulan PSL v2 at:
7 * http://license.coscl.org.cn/MulanPSL2
8 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
9 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
10 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
11 * See the Mulan PSL v2 for more details.
12 * Create: 2009-12-22
13 * Description: 信号量模块
14 */
15 #include "prt_sem_external.h"
16 #include "prt_asm_cpu_external.h"
17
18 /* 核内信号量最大个数 */
19 OS_SEC_BSS U16 g_maxSem;
20
21 /*
22 * 描述:把当前运行任务挂接到信号量链表上
23 */
OsSemPendListPut(struct TagSemCb * semPended,U32 timeOut)24 OS_SEC_L0_TEXT void OsSemPendListPut(struct TagSemCb *semPended, U32 timeOut)
25 {
26 struct TagTskCb *curTskCb = NULL;
27 struct TagTskCb *runTsk = RUNNING_TASK;
28 struct TagListObject *pendObj = &runTsk->pendList;
29
30 OsTskReadyDel((struct TagTskCb *)runTsk);
31
32 runTsk->taskSem = (void *)semPended;
33
34 TSK_STATUS_SET(runTsk, OS_TSK_PEND);
35 /* 根据唤醒方式挂接此链表,同优先级再按FIFO子顺序插入 */
36 if (semPended->semMode == SEM_MODE_PRIOR) {
37 LIST_FOR_EACH(curTskCb, &semPended->semList, struct TagTskCb, pendList) {
38 if (curTskCb->priority > runTsk->priority) {
39 ListTailAdd(pendObj, &curTskCb->pendList);
40 goto TIMER_ADD;
41 }
42 }
43 }
44 /* 如果到这里,说明是FIFO方式;或者是优先级方式且挂接首个节点或者挂接尾节点 */
45 ListTailAdd(pendObj, &semPended->semList);
46 TIMER_ADD:
47 // timer超时链表添加
48 if (timeOut != OS_WAIT_FOREVER) {
49 /* 如果不是永久等待则将任务挂到计时器链表中,设置OS_TSK_TIMEOUT是为了判断是否等待超时 */
50 OsTskTimerAdd((struct TagTskCb *)runTsk, timeOut);
51
52 TSK_STATUS_SET(runTsk, OS_TSK_TIMEOUT);
53 }
54
55 }
56
57 /*
58 * 描述:从非空信号量链表上摘首个任务放入到ready队列
59 */
OsSemPendListGet(struct TagSemCb * semPended)60 OS_SEC_L0_TEXT struct TagTskCb *OsSemPendListGet(struct TagSemCb *semPended)
61 {
62 struct TagTskCb *taskCb = GET_TCB_PEND(LIST_FIRST(&(semPended->semList)));
63
64 ListDelete(LIST_FIRST(&(semPended->semList)));
65 /* 如果阻塞的任务属于定时等待的任务时候,去掉其定时等待标志位,并将其从去除 */
66 if (TSK_STATUS_TST(taskCb, OS_TSK_TIMEOUT)) {
67 OS_TSK_DELAY_LOCKED_DETACH(taskCb);
68 }
69
70 // 必须先去除 OS_TSK_TIMEOUT 态,再入队[睡眠时是先出ready队,再置OS_TSK_TIMEOUT态]
71 TSK_STATUS_CLEAR(taskCb, OS_TSK_TIMEOUT | OS_TSK_PEND);
72 taskCb->taskSem = NULL;
73 /* 如果去除信号量阻塞位后,该任务不处于阻塞态则将该任务挂入就绪队列并触发任务调度 */
74 if (!TSK_STATUS_TST(taskCb, OS_TSK_SUSPEND)) {
75 OsTskReadyAddBgd(taskCb);
76 }
77
78 return taskCb;
79 }
80
OsSemPendParaCheck(U32 timeout)81 OS_SEC_L0_TEXT U32 OsSemPendParaCheck(U32 timeout)
82 {
83 if (timeout == 0) {
84 return OS_ERRNO_SEM_UNAVAILABLE;
85 }
86
87 /* 如果锁任务的情况下 */
88 if (OS_TASK_LOCK_DATA != 0) {
89 return OS_ERRNO_SEM_PEND_IN_LOCK;
90 }
91 return OS_OK;
92 }
93
OsSemPendNotNeedSche(struct TagSemCb * semPended,struct TagTskCb * runTsk)94 OS_SEC_L0_TEXT bool OsSemPendNotNeedSche(struct TagSemCb *semPended, struct TagTskCb *runTsk)
95 {
96 // 信号量获取成功
97 if (semPended->semCount > 0) {
98 semPended->semCount--;
99 semPended->semOwner = runTsk->taskPid;
100
101 return TRUE;
102 }
103 return FALSE;
104 }
105
106 /*
107 * 描述:指定信号量的P操作
108 */
PRT_SemPend(SemHandle semHandle,U32 timeout)109 OS_SEC_L0_TEXT U32 PRT_SemPend(SemHandle semHandle, U32 timeout)
110 {
111 uintptr_t intSave;
112 U32 ret;
113 struct TagTskCb *runTsk = NULL;
114 struct TagSemCb *semPended = NULL;
115
116 if (semHandle >= (SemHandle)g_maxSem) {
117 return OS_ERRNO_SEM_INVALID;
118 }
119
120 semPended = GET_SEM(semHandle);
121
122 intSave = OsIntLock();
123 if (semPended->semStat == OS_SEM_UNUSED) {
124 OsIntRestore(intSave);
125 return OS_ERRNO_SEM_INVALID;
126 }
127
128 /* 如果是中断的情况 */
129 if (OS_INT_ACTIVE) {
130 OsIntRestore(intSave);
131 return OS_ERRNO_SEM_PEND_INTERR;
132 }
133
134 runTsk = (struct TagTskCb *)RUNNING_TASK;
135
136 if (OsSemPendNotNeedSche(semPended, runTsk) == TRUE) {
137 OsIntRestore(intSave);
138 return OS_OK;
139 }
140
141 ret = OsSemPendParaCheck(timeout);
142 if (ret != OS_OK) {
143 OsIntRestore(intSave);
144 return ret;
145 }
146 /* 把当前任务挂接在信号量链表上 */
147 OsSemPendListPut(semPended, timeout);
148 if (timeout != OS_WAIT_FOREVER) {
149 /* 触发任务调度 */
150 OsTskSchedule();
151
152 /* 判断是否是等待信号量超时 */
153 if (TSK_STATUS_TST(runTsk, OS_TSK_TIMEOUT)) {
154 TSK_STATUS_CLEAR(runTsk, OS_TSK_TIMEOUT);
155 OsIntRestore(intSave);
156 return OS_ERRNO_SEM_TIMEOUT;
157 }
158 } else {
159 /* 恢复ps的快速切换 */
160 OsTskScheduleFastPs(intSave);
161 }
162
163 OsIntRestore(intSave);
164 return OS_OK;
165 }
166
OsSemPostSchePre(struct TagSemCb * semPosted)167 OS_SEC_ALW_INLINE INLINE void OsSemPostSchePre(struct TagSemCb *semPosted)
168 {
169 struct TagTskCb *resumedTask = NULL;
170
171 resumedTask = OsSemPendListGet(semPosted);
172 semPosted->semOwner = resumedTask->taskPid;
173 }
174
OsSemPostErrorCheck(struct TagSemCb * semPosted,SemHandle semHandle)175 OS_SEC_ALW_INLINE INLINE U32 OsSemPostErrorCheck(struct TagSemCb *semPosted, SemHandle semHandle)
176 {
177 (void)semHandle;
178 /* 检查信号量控制块是否UNUSED,排除大部分错误场景 */
179 if (semPosted->semStat == OS_SEM_UNUSED) {
180 return OS_ERRNO_SEM_INVALID;
181 }
182
183 /* post计数型信号量的错误场景, 释放计数型信号量且信号量计数大于最大计数 */
184 if ((semPosted)->maxSemCount <= (semPosted)->semCount) {
185 return OS_ERRNO_SEM_OVERFLOW;
186 }
187 /* post同步二进制信号量或者不存在上述错误场景 */
188 return OS_OK;
189 }
190
191 /*
192 * 描述:指定信号量的V操作
193 */
PRT_SemPost(SemHandle semHandle)194 OS_SEC_L0_TEXT U32 PRT_SemPost(SemHandle semHandle)
195 {
196 U32 ret;
197 uintptr_t intSave;
198 struct TagSemCb *semPosted = NULL;
199
200 if (semHandle >= (SemHandle)g_maxSem) {
201 return OS_ERRNO_SEM_INVALID;
202 }
203
204 semPosted = GET_SEM(semHandle);
205 intSave = OsIntLock();
206 /* 检查post信号量时的错误场景 */
207 ret = OsSemPostErrorCheck(semPosted, semHandle);
208 if (ret != OS_OK) {
209 OsIntRestore(intSave);
210 return ret;
211 }
212
213 /* 如果有任务阻塞在信号量上,就激活信号量阻塞队列上的首个任务 */
214 if (!ListEmpty(&semPosted->semList)) {
215 OsSemPostSchePre(semPosted);
216 /* 相当于快速切换+中断恢复 */
217 OsTskScheduleFastPs(intSave);
218 } else {
219 /* 释放信号量,把持有信号量的任务ID设置为OS_THREAD_ID_INVALID */
220 semPosted->semCount++;
221 semPosted->semOwner = OS_INVALID_OWNER_ID;
222 }
223
224 OsIntRestore(intSave);
225 return OS_OK;
226 }
227