1 /*
2  * Copyright (C) 2016 YunOS Project. All rights reserved.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #include <ble_os.h>
17 #include <string.h>
18 #include <misc/util.h>
19 
20 #define BT_DBG_ENABLED IS_ENABLED(CONFIG_BLUETOOTH_DEBUG_CORE)
21 
22 #include <bluetooth/log.h>
23 #include "mbox.h"
24 
25 struct timer *g_timer_list;
26 
27 #if defined(__cplusplus)
28 extern "C"
29 {
30 #endif
31 
k_mbox_new(k_mbox_t ** mb,int size)32 int k_mbox_new(k_mbox_t **mb, int size)
33 {
34     k_mbox_t *mbox;
35     UNUSED(size);
36 
37     mbox = (k_mbox_t *)malloc(sizeof(k_mbox_t));
38 
39     if (mbox == NULL) {
40         return ERR_MEM;
41     }
42 
43     memset(mbox, 0, sizeof(k_mbox_t));
44 
45     mbox->first = mbox->last = 0;
46     k_sem_init(&mbox->not_empty, 0, 1);
47     k_sem_init(&mbox->not_full, 0, 1);
48     k_sem_init(&mbox->mutex, 1, 1);
49     mbox->wait_send = 0;
50 
51     *mb = mbox;
52     BT_DBG("k_mbox_new: mbox 0x%lx\n", mbox);
53     return ERR_OK;
54 }
55 
56 /*-----------------------------------------------------------------------------------*/
57 /*
58   Deallocates a mailbox. If there are messages still present in the
59   mailbox when the mailbox is deallocated, it is an indication of a
60   programming error in lwIP and the developer should be notified.
61 */
k_mbox_free(k_mbox_t * mb)62 void k_mbox_free(k_mbox_t *mb)
63 {
64     if (mb != NULL) {
65         k_mbox_t *mbox = mb;
66 
67         k_sem_take(&mbox->mutex, K_FOREVER);
68 
69         k_sem_delete(&mbox->not_empty);
70         k_sem_delete(&mbox->not_full);
71         k_sem_delete(&mbox->mutex);
72         BT_DBG("sys_mbox_free: mbox 0x%lx\n", mbox);
73         free(mbox);
74     }
75 }
76 
77 /*-----------------------------------------------------------------------------------*/
78 /*
79     void sys_mbox_post(k_mbox_t *mbox, void *msg)
80 
81     Posts the "msg" to the mailbox. This function have to block until  the "msg" is really posted.
82 */
k_mbox_post(k_mbox_t * mb,void * msg)83 void k_mbox_post(k_mbox_t *mb, void *msg)
84 {
85     u8_t first;
86     k_mbox_t *mbox;
87 
88     if (NULL == mb) {
89         BT_ERR("invaild mbox");
90         return;
91     }
92 
93     mbox = mb;
94 
95     k_sem_take(&mbox->mutex, K_FOREVER);
96 
97     BT_DBG("sys_mbox_post: mbox %p msg %p\n", (void *)mbox, (void *)msg);
98 
99     while ((mbox->last + 1) >= (mbox->first + K_MBOX_SIZE)) {
100         mbox->wait_send++;
101         k_sem_give(&mbox->mutex);
102         k_sem_take(&mbox->not_full, K_FOREVER);
103         k_sem_take(&mbox->mutex, K_FOREVER);
104         mbox->wait_send--;
105     }
106 
107     mbox->msgs[mbox->last % K_MBOX_SIZE] = msg;
108 
109     if (mbox->last == mbox->first) {
110         first = 1;
111     } else {
112         first = 0;
113     }
114 
115     mbox->last++;
116 
117     if (first) {
118         k_sem_give(&mbox->not_empty);
119     }
120 
121     k_sem_give(&mbox->mutex);
122 }
123 
124 /*
125     err_t k_mbox_trypost(k_mbox_t *mbox, void *msg)
126 
127     Try to post the "msg" to the mailbox. Returns ERR_MEM if this one  is full, else, ERR_OK if the "msg" is posted.
128 */
k_mbox_trypost(k_mbox_t * mb,void * msg)129 int k_mbox_trypost(k_mbox_t *mb, void *msg)
130 {
131     u8_t first;
132     k_mbox_t *mbox;
133 
134     if (NULL == mb) {
135         BT_ERR("invaild mbox");
136         return ERR_MEM;
137     }
138 
139     mbox = mb;
140 
141     k_sem_take(&mbox->mutex, K_FOREVER);
142 
143     BT_DBG("k_mbox_trypost: mbox %p msg %p\n", (void *)mbox, (void *)msg);
144 
145     if ((mbox->last + 1) >= (mbox->first + K_MBOX_SIZE)) {
146         k_sem_give(&mbox->mutex);
147         return ERR_MEM;
148     }
149 
150     mbox->msgs[mbox->last % K_MBOX_SIZE] = msg;
151 
152     if (mbox->last == mbox->first) {
153         first = 1;
154     } else {
155         first = 0;
156     }
157 
158     mbox->last++;
159 
160     if (first) {
161         k_sem_give(&mbox->not_empty);
162     }
163 
164     k_sem_give(&mbox->mutex);
165     return ERR_OK;
166 }
167 
168 /*-----------------------------------------------------------------------------------*/
169 /*
170   Blocks the thread until a message arrives in the mailbox, but does
171   not block the thread longer than "timeout" milliseconds (similar to
172   the sys_arch_sem_wait() function). The "msg" argument is a result
173   parameter that is set by the function (i.e., by doing "*msg =
174   ptr"). The "msg" parameter maybe NULL to indicate that the message
175   should be dropped.
176 
177   The return values are the same as for the sys_arch_sem_wait() function:
178   Number of milliseconds spent waiting or SYS_ARCH_TIMEOUT if there was a
179   timeout.
180 
181   Note that a function with a similar name, sys_mbox_fetch(), is
182   implemented by lwIP.
183 */
k_mbox_fetch(k_mbox_t * mb,void ** msg,uint32_t timeout)184 int k_mbox_fetch(k_mbox_t *mb, void **msg, uint32_t timeout)
185 {
186     uint32_t time_needed = 0;
187     k_mbox_t *mbox;
188 
189     if (NULL == mb) {
190         BT_ERR("invaild mbox");
191         return ERR_MEM;
192     }
193 
194     mbox = mb;
195     BT_DBG("mbox %p\n", mbox);
196 
197     /* The mutex lock is quick so we don't bother with the timeout
198      stuff here. */
199     k_sem_take(&mbox->mutex, K_FOREVER);
200 
201     BT_DBG("mutex taken\n");
202 
203     while (mbox->first == mbox->last) {
204         k_sem_give(&mbox->mutex);
205 
206         /* We block while waiting for a mail to arrive in the mailbox. We
207            must be prepared to timeout. */
208         if (timeout != 0) {
209             time_needed = k_sem_take(&mbox->not_empty, timeout);
210 
211             if (time_needed == SYS_ARCH_TIMEOUT) {
212                 return SYS_ARCH_TIMEOUT;
213             }
214         } else {
215             k_sem_take(&mbox->not_empty, K_FOREVER);
216         }
217 
218         k_sem_take(&mbox->mutex, K_FOREVER);
219     }
220 
221     if (msg != NULL) {
222         BT_DBG("sys_mbox_fetch: mbox %p msg %p\n", (void *)mbox, *msg);
223         *msg = mbox->msgs[mbox->first % K_MBOX_SIZE];
224     } else {
225         BT_DBG("sys_mbox_fetch: mbox %p, null msg\n", (void *)mbox);
226     }
227 
228     mbox->first++;
229 
230     if (mbox->wait_send) {
231         k_sem_give(&mbox->not_full);
232     }
233 
234     k_sem_give(&mbox->mutex);
235 
236     return time_needed;
237 }
238 
239 /*
240     uint32_t sys_arch_mbox_tryfetch(k_mbox_t *mbox, void **msg)
241 
242     similar to sys_arch_mbox_fetch, however if a message is not  present in the mailbox,
243     it immediately returns with the code  SYS_MBOX_EMPTY.
244 */
k_mbox_tryfetch(k_mbox_t * mb,void ** msg)245 int k_mbox_tryfetch(k_mbox_t *mb, void **msg)
246 {
247     k_mbox_t *mbox;
248 
249     if (NULL == mb) {
250         BT_ERR("invaild mbox");
251         return ERR_MEM;
252     }
253 
254     mbox = mb;
255 
256     k_sem_take(&mbox->mutex, K_FOREVER);
257 
258     if (mbox->first == mbox->last) {
259         k_sem_give(&mbox->mutex);
260         return SYS_MBOX_EMPTY;
261     }
262 
263     if (msg != NULL) {
264         BT_DBG("k_mbox_tryfetch: mbox %p msg %p\n", (void *)mbox, *msg);
265         *msg = mbox->msgs[mbox->first % K_MBOX_SIZE];
266     } else {
267         BT_DBG("k_mbox_tryfetch: mbox %p, null msg\n", (void *)mbox);
268     }
269 
270     mbox->first++;
271 
272     if (mbox->wait_send) {
273         k_sem_give(&mbox->not_full);
274     }
275 
276     k_sem_give(&mbox->mutex);
277 
278     return 0;
279 }
280 
281 #if defined(__cplusplus)
282 }
283 #endif
284