xref: /nrf52832-nimble/rt-thread/components/vbus/vbus.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * COPYRIGHT (C) 2018, Real-Thread Information Technology Ltd
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2013-11-04     Grissiom     add comment
9  */
10 
11 #include <rthw.h>
12 #include <rtthread.h>
13 #include <rtdevice.h>
14 
15 #include "vbus.h"
16 #include "prio_queue.h"
17 #include "vbus_hw.h"
18 
19 //#define RT_VBUS_STATISTICS
20 
21 #define RT_VBUS_RB_LOW_TICK   (RT_VMM_RB_BLK_NR * 2 / 3)
22 #define RT_VBUS_RB_TICK_STEP  (100)
23 
24 #ifndef RT_USING_LOGTRACE
25 /* console could be run on vbus. If we log on it, there will be oops. */
26 #define vbus_debug(...)
27 #define vbus_verbose(...)
28 #define vbus_info(...)
29 #define vbus_error(...)
30 #else // have RT_USING_LOGTRACE
31 #include <log_trace.h>
32 
33 #if defined(log_session_lvl)
34 /* Define log_trace_session as const so the compiler could optimize some log
35  * out. */
36 const static struct log_trace_session _lgs = {
37     .id  = {.name = "vbus"},
38     .lvl = LOG_TRACE_LEVEL_VERBOSE,
39 };
40 
41 #define vbus_debug(fmt, ...)   log_session_lvl(&_lgs, LOG_TRACE_LEVEL_DEBUG,   fmt, ##__VA_ARGS__)
42 #define vbus_verbose(fmt, ...) log_session_lvl(&_lgs, LOG_TRACE_LEVEL_VERBOSE, fmt, ##__VA_ARGS__)
43 #define vbus_info(fmt, ...)    log_session_lvl(&_lgs, LOG_TRACE_LEVEL_INFO,    fmt, ##__VA_ARGS__)
44 #define vbus_error(fmt, ...)   log_session_lvl(&_lgs, LOG_TRACE_LEVEL_ERROR,    fmt, ##__VA_ARGS__)
45 #else
46 static struct log_trace_session _lgs = {
47     .id  = {.name = "vbus"},
48     .lvl = LOG_TRACE_LEVEL_VERBOSE,
49 };
50 #define vbus_debug(fmt, ...)   log_session(&_lgs, LOG_TRACE_DEBUG""fmt, ##__VA_ARGS__)
51 #define vbus_verbose(fmt, ...) log_session(&_lgs, LOG_TRACE_VERBOSE""fmt, ##__VA_ARGS__)
52 #define vbus_info(fmt, ...)    log_session(&_lgs, LOG_TRACE_INFO""fmt, ##__VA_ARGS__)
53 #define vbus_error(fmt, ...)   log_session(&_lgs, LOG_TRACE_ERROR""fmt, ##__VA_ARGS__)
54 #endif
55 #endif // RT_USING_LOGTRACE
56 
57 #ifndef ARRAY_SIZE
58 #define ARRAY_SIZE(ar)     (sizeof(ar)/sizeof(ar[0]))
59 #endif
60 
61 struct rt_vbus_ring *RT_VBUS_OUT_RING;
62 struct rt_vbus_ring *RT_VBUS_IN_RING;
63 
64 const char *rt_vbus_chn_st2str[] = {
65     "available",
66     "closed",
67     "establishing",
68     "established",
69     "suspended",
70     "closing",
71 };
72 
73 const char *rt_vbus_sess_st2str[] = {
74     "available",
75     "listening",
76     "establishing",
77 };
78 
79 const char *rt_vbus_cmd2str[] = {
80     "ENABLE",
81     "DISABLE",
82     "SET",
83     "ACK",
84     "NAK",
85     "SUSPEND",
86     "RESUME",
87 };
88 
89 static char* dump_cmd_pkt(unsigned char *dp, size_t dsize);
90 
91 /* 4 bytes for the head */
92 #define LEN2BNR(len)    ((len + RT_VBUS_BLK_HEAD_SZ \
93                           + sizeof(struct rt_vbus_blk) - 1) \
94                          / sizeof(struct rt_vbus_blk))
95 
_ring_add_get_bnr(struct rt_vbus_ring * ring,rt_size_t bnr)96 rt_inline void _ring_add_get_bnr(struct rt_vbus_ring *ring,
97                                  rt_size_t bnr)
98 {
99     int nidx = ring->get_idx + bnr;
100 
101     if (nidx >= RT_VMM_RB_BLK_NR)
102     {
103         nidx -= RT_VMM_RB_BLK_NR;
104     }
105     rt_vbus_smp_wmb();
106     ring->get_idx = nidx;
107 }
108 
_bus_ring_space_nr(struct rt_vbus_ring * rg)109 rt_inline int _bus_ring_space_nr(struct rt_vbus_ring *rg)
110 {
111     int delta;
112 
113     rt_vbus_smp_rmb();
114     delta = rg->get_idx - rg->put_idx;
115 
116     if (delta > 0)
117     {
118         /* Put is behind the get. */
119         return delta - 1;
120     }
121     else
122     {
123         /* delta is negative. */
124         return RT_VMM_RB_BLK_NR + delta - 1;
125     }
126 }
127 
128 struct rt_vbus_pkg {
129     rt_uint8_t id;
130     rt_uint8_t prio;
131     rt_uint8_t finished;
132     rt_uint8_t len;
133     const void *data;
134 };
135 
136 /* chn0 is always connected */
137 static enum rt_vbus_chn_status _chn_status[RT_VBUS_CHANNEL_NR];
138 
_chn_connected(unsigned char chnr)139 rt_inline int _chn_connected(unsigned char chnr)
140 {
141     return _chn_status[chnr] == RT_VBUS_CHN_ST_ESTABLISHED ||
142            _chn_status[chnr] == RT_VBUS_CHN_ST_SUSPEND;
143 }
144 
145 #ifdef RT_VBUS_USING_FLOW_CONTROL
146 #include <watermark_queue.h>
147 struct rt_watermark_queue _chn_wm_que[RT_VBUS_CHANNEL_NR];
rt_vbus_set_post_wm(unsigned char chnr,unsigned int low,unsigned int high)148 void rt_vbus_set_post_wm(unsigned char chnr, unsigned int low, unsigned int high)
149 {
150     RT_ASSERT((0 < chnr) && (chnr < ARRAY_SIZE(_chn_wm_que)));
151     rt_wm_que_set_mark(&_chn_wm_que[chnr], low, high);
152 }
153 
154 /* Threads suspended by the flow control of other side. */
155 rt_list_t _chn_suspended_threads[RT_VBUS_CHANNEL_NR];
156 
157 struct
158 {
159     unsigned int level;
160     unsigned int high_mark;
161     unsigned int low_mark;
162     /* The suspend command does not have ACK. So if the other side still
163      * sending pkg after SUSPEND, warn it again. Also use it as a flag that
164      * tell me whether are we dropping from the high mark or not when reaching
165      * the low mark. */
166     unsigned int last_warn;
167 } _chn_recv_wm[RT_VBUS_CHANNEL_NR];
168 
rt_vbus_set_recv_wm(unsigned char chnr,unsigned int low,unsigned int high)169 void rt_vbus_set_recv_wm(unsigned char chnr, unsigned int low, unsigned int high)
170 {
171     RT_ASSERT((0 < chnr) && (chnr < ARRAY_SIZE(_chn_recv_wm)));
172     _chn_recv_wm[chnr].low_mark = low;
173     _chn_recv_wm[chnr].high_mark = high;
174 }
175 #else
rt_vbus_set_recv_wm(unsigned char chnr,unsigned int low,unsigned int high)176 void rt_vbus_set_recv_wm(unsigned char chnr, unsigned int low, unsigned int high)
177 {}
rt_vbus_set_post_wm(unsigned char chnr,unsigned int low,unsigned int high)178 void rt_vbus_set_post_wm(unsigned char chnr, unsigned int low, unsigned int high)
179 {}
180 #endif
181 
182 struct {
183     rt_vbus_event_listener indicate;
184     void *ctx;
185 } _vbus_rx_indi[RT_VBUS_EVENT_ID_MAX][RT_VBUS_CHANNEL_NR];
186 
rt_vbus_register_listener(unsigned char chnr,enum rt_vbus_event_id eve,rt_vbus_event_listener indi,void * ctx)187 void rt_vbus_register_listener(unsigned char chnr,
188                                enum rt_vbus_event_id eve,
189                                rt_vbus_event_listener indi,
190                                void *ctx)
191 {
192     RT_ASSERT(chnr != 0 && chnr < RT_VBUS_CHANNEL_NR);
193     RT_ASSERT(eve < sizeof(_vbus_rx_indi)/sizeof(_vbus_rx_indi[0]));
194 
195     _vbus_rx_indi[eve][chnr].indicate = indi;
196     _vbus_rx_indi[eve][chnr].ctx = ctx;
197 }
198 
_vbus_indicate(enum rt_vbus_event_id eve,unsigned char chnr)199 static void _vbus_indicate(enum rt_vbus_event_id eve, unsigned char chnr)
200 {
201     RT_ASSERT(eve < sizeof(_vbus_rx_indi)/sizeof(_vbus_rx_indi[0]));
202 
203     if (_vbus_rx_indi[eve][chnr].indicate)
204         _vbus_rx_indi[eve][chnr].indicate(_vbus_rx_indi[eve][chnr].ctx);
205 }
206 
207 #define _BUS_OUT_THRD_STACK_SZ  2048
208 #define _BUS_OUT_THRD_PRIO      8
209 #define _BUS_OUT_PKG_NR         RT_VMM_RB_BLK_NR
210 
211 static struct rt_thread _bus_out_thread;
212 static rt_uint8_t _bus_out_thread_stack[_BUS_OUT_THRD_STACK_SZ];
213 struct rt_prio_queue *_bus_out_que;
214 
_bus_out_entry(void * param)215 static void _bus_out_entry(void *param)
216 {
217     struct rt_vbus_pkg dpkg;
218 
219     _bus_out_que = rt_prio_queue_create("vbus",
220                                         _BUS_OUT_PKG_NR,
221                                         sizeof(struct rt_vbus_pkg));
222 
223     if (!_bus_out_que)
224     {
225         rt_kprintf("could not create vmm bus queue\n");
226         return;
227     }
228 
229     while (rt_prio_queue_pop(_bus_out_que, &dpkg,
230                              RT_WAITING_FOREVER) == RT_EOK)
231     {
232         int sp;
233         rt_uint32_t nxtidx;
234         const int dnr = LEN2BNR(dpkg.len);
235 
236 #ifdef RT_VBUS_USING_FLOW_CONTROL
237         rt_wm_que_dec(&_chn_wm_que[dpkg.id]);
238 #endif
239 
240         if (!_chn_connected(dpkg.id))
241             continue;
242 
243         sp = _bus_ring_space_nr(RT_VBUS_OUT_RING);
244 
245         vbus_debug("vmm bus out"
246                    "(data: %p, len: %d, prio: %d, id: %d)\n",
247                    dpkg.data, dpkg.len, dpkg.prio, dpkg.id);
248 
249         /* wait for enough space */
250         while (sp < dnr)
251         {
252             rt_ubase_t lvl = rt_hw_interrupt_disable();
253 
254             RT_VBUS_OUT_RING->blocked = 1;
255             rt_vbus_smp_wmb();
256 
257             /* kick the guest, hoping this could force it do the work */
258             rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
259 
260             rt_thread_suspend(rt_thread_self());
261             rt_schedule();
262 
263             RT_VBUS_OUT_RING->blocked = 0;
264 
265             rt_hw_interrupt_enable(lvl);
266 
267             sp = _bus_ring_space_nr(RT_VBUS_OUT_RING);
268         }
269 
270         nxtidx = RT_VBUS_OUT_RING->put_idx + dnr;
271 
272         RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].id  = dpkg.id;
273         RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].qos = dpkg.prio;
274         RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].len = dpkg.len;
275 
276         if (nxtidx >= RT_VMM_RB_BLK_NR)
277         {
278             unsigned int tailsz;
279 
280             tailsz = (RT_VMM_RB_BLK_NR - RT_VBUS_OUT_RING->put_idx)
281                 * sizeof(RT_VBUS_OUT_RING->blks[0]) - RT_VBUS_BLK_HEAD_SZ;
282 
283             /* the remaining block is sufficient for the data */
284             if (tailsz > dpkg.len)
285                 tailsz = dpkg.len;
286 
287             rt_memcpy(&RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].data,
288                       dpkg.data, tailsz);
289             rt_memcpy(&RT_VBUS_OUT_RING->blks[0],
290                       ((char*)dpkg.data)+tailsz,
291                       dpkg.len - tailsz);
292 
293             rt_vbus_smp_wmb();
294             RT_VBUS_OUT_RING->put_idx = nxtidx - RT_VMM_RB_BLK_NR;
295         }
296         else
297         {
298             rt_memcpy(&RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].data,
299                       dpkg.data, dpkg.len);
300 
301             rt_vbus_smp_wmb();
302             RT_VBUS_OUT_RING->put_idx = nxtidx;
303         }
304 
305         rt_vbus_smp_wmb();
306         rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
307 
308         if (dpkg.finished)
309         {
310             _vbus_indicate(RT_VBUS_EVENT_ID_TX, dpkg.id);
311         }
312     }
313     RT_ASSERT(0);
314 }
315 
rt_vbus_resume_out_thread(void)316 void rt_vbus_resume_out_thread(void)
317 {
318     rt_thread_resume(&_bus_out_thread);
319     rt_schedule();
320 }
321 
rt_vbus_post(rt_uint8_t id,rt_uint8_t prio,const void * data,rt_size_t size,rt_int32_t timeout)322 rt_err_t rt_vbus_post(rt_uint8_t id,
323                       rt_uint8_t prio,
324                       const void *data,
325                       rt_size_t size,
326                       rt_int32_t timeout)
327 {
328     rt_err_t err = RT_EOK;
329     struct rt_vbus_pkg pkg;
330     unsigned int putsz;
331     const unsigned char *dp;
332 
333     if (!_bus_out_que)
334     {
335         rt_kprintf("post (data: %p, size: %d, timeout: %d) "
336                    "to bus before initialition\n",
337                    data, size, timeout);
338         return -RT_ERROR;
339     }
340 
341     if (id >= RT_VBUS_CHANNEL_NR)
342         return -RT_ERROR;
343 
344     if (timeout != 0)
345     {
346         RT_DEBUG_IN_THREAD_CONTEXT;
347     }
348 
349 #ifdef RT_VBUS_USING_FLOW_CONTROL
350     while (_chn_status[id] == RT_VBUS_CHN_ST_SUSPEND)
351     {
352         rt_thread_t thread;
353 
354         if (timeout == 0)
355         {
356             return -RT_EFULL;
357         }
358 
359         thread = rt_thread_self();
360         thread->error = RT_EOK;
361         /* We only touch the _chn_suspended_threads in thread, so lock the
362          * scheduler is enough. */
363         rt_enter_critical();
364         rt_thread_suspend(thread);
365 
366         rt_list_insert_after(&_chn_suspended_threads[id], &thread->tlist);
367         if (timeout > 0)
368         {
369             rt_timer_control(&(thread->thread_timer),
370                              RT_TIMER_CTRL_SET_TIME,
371                              &timeout);
372             rt_timer_start(&(thread->thread_timer));
373         }
374         /* rt_exit_critical will do schedule on need. */
375         rt_exit_critical();
376 
377         if (thread->error != RT_EOK)
378             return thread->error;
379     }
380 #endif
381 
382     if (_chn_status[id] != RT_VBUS_CHN_ST_ESTABLISHED)
383         return -RT_ERROR;
384 
385     dp       = data;
386     pkg.id   = id;
387     pkg.prio = prio;
388     for (putsz = 0; size; size -= putsz)
389     {
390         pkg.data = dp;
391 
392         if (size > RT_VBUS_MAX_PKT_SZ)
393         {
394             putsz = RT_VBUS_MAX_PKT_SZ;
395             pkg.finished = 0;
396         }
397         else
398         {
399             putsz = size;
400             pkg.finished = 1;
401         }
402 
403         pkg.len = putsz;
404         dp += putsz;
405 
406 #ifdef RT_VBUS_USING_FLOW_CONTROL
407         err = rt_wm_que_inc(&_chn_wm_que[id], timeout);
408         if (err != RT_EOK)
409             break;
410 #endif
411 
412         vbus_debug("post (data: %p(%d), size: %d, finshed: %d, timeout: %d)\n",
413                    pkg.data, ((unsigned char*)pkg.data)[0],
414                    pkg.len, pkg.finished, timeout);
415 
416         err = rt_prio_queue_push(_bus_out_que, prio, &pkg, timeout);
417         if (err != RT_EOK)
418             break;
419     }
420 
421     return err;
422 }
423 
424 struct rt_completion _chn0_post_cmp;
425 
_chn0_tx_listener(void * p)426 void _chn0_tx_listener(void *p)
427 {
428     rt_completion_done(&_chn0_post_cmp);
429 }
430 
431 /* Posts in channel0 should be sync. */
_chn0_post(const void * data,rt_size_t size,int timeout)432 static rt_err_t _chn0_post(const void *data,
433                                rt_size_t size,
434                                int timeout)
435 {
436     rt_err_t err;
437 
438     rt_completion_init(&_chn0_post_cmp);
439     err = rt_vbus_post(0, 0, data, size, timeout);
440     if (err != RT_EOK)
441         return err;
442     return rt_completion_wait(&_chn0_post_cmp, timeout);
443 }
444 
445 #define _BUS_IN_THRD_STACK_SZ  1024
446 #define _BUS_IN_THRD_PRIO      (_BUS_OUT_THRD_PRIO+1)
447 #if (_BUS_IN_THRD_PRIO == RT_THREAD_PRIORITY_MAX)
448 #error "_BUS_OUT_THRD_PRIO too low"
449 #endif
450 
451 static struct rt_thread _bus_in_thread;
452 static rt_uint8_t _bus_in_thread_stack[_BUS_OUT_THRD_STACK_SZ];
453 static struct rt_semaphore _bus_in_sem;
454 static struct rt_event     _bus_in_event;
455 /* {head, tail} */
456 #define _IN_ACT_HEAD 0
457 #define _IN_ACT_TAIL 1
458 static struct rt_vbus_data *_bus_in_action[RT_VBUS_CHANNEL_NR][2];
459 #ifdef RT_VBUS_STATISTICS
460 static unsigned int _bus_in_action_nr[RT_VBUS_CHANNEL_NR];
461 #endif
462 
rt_vbus_notify_chn(unsigned char chnr,rt_err_t err)463 static void rt_vbus_notify_chn(unsigned char chnr, rt_err_t err)
464 {
465 #ifdef RT_VBUS_USING_FLOW_CONTROL
466     /* TODO: get rid of this */
467     /* Protect the list. */
468     rt_enter_critical();
469     while (!rt_list_isempty(&_chn_suspended_threads[chnr]))
470     {
471         rt_thread_t thread;
472 
473         thread = rt_list_entry(_chn_suspended_threads[chnr].next,
474                                struct rt_thread,
475                                tlist);
476         thread->error = err;
477         rt_thread_resume(thread);
478     }
479     rt_exit_critical();
480 #endif
481     rt_event_send(&_bus_in_event, 1 << chnr);
482 }
483 
rt_vbus_notify_set(rt_uint32_t set)484 static void rt_vbus_notify_set(rt_uint32_t set)
485 {
486     rt_event_send(&_bus_in_event, set);
487 }
488 
rt_vbus_listen_on(rt_uint8_t chnr,rt_int32_t timeout)489 rt_err_t rt_vbus_listen_on(rt_uint8_t chnr,
490                            rt_int32_t timeout)
491 {
492     rt_uint32_t notuse;
493 
494     if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR || !_chn_connected(chnr))
495         return -RT_EIO;
496 
497     return rt_event_recv(&_bus_in_event, 1 << chnr,
498                          RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
499                          timeout, &notuse);
500 }
501 
rt_vbus_data_push(unsigned int id,struct rt_vbus_data * act)502 void rt_vbus_data_push(unsigned int id, struct rt_vbus_data *act)
503 {
504     rt_ubase_t lvl;
505 
506     RT_ASSERT(0 < id && id < RT_VBUS_CHANNEL_NR);
507 
508     lvl = rt_hw_interrupt_disable();
509 
510     if (_bus_in_action[id][_IN_ACT_HEAD] == RT_NULL)
511     {
512         _bus_in_action[id][_IN_ACT_HEAD] = act;
513         _bus_in_action[id][_IN_ACT_TAIL] = act;
514     }
515     else
516     {
517         _bus_in_action[id][_IN_ACT_TAIL]->next = act;
518         _bus_in_action[id][_IN_ACT_TAIL] = act;
519     }
520 
521 #ifdef RT_VBUS_STATISTICS
522     _bus_in_action_nr[id]++;
523 #endif
524 
525     rt_hw_interrupt_enable(lvl);
526 
527 #ifdef RT_VBUS_USING_FLOW_CONTROL
528     _chn_recv_wm[id].level++;
529     if (_chn_recv_wm[id].level == 0)
530         _chn_recv_wm[id].level = ~0;
531     if (_chn_recv_wm[id].level > _chn_recv_wm[id].high_mark &&
532         _chn_recv_wm[id].level > _chn_recv_wm[id].last_warn)
533     {
534         unsigned char buf[2];
535 
536         buf[0] = RT_VBUS_CHN0_CMD_SUSPEND;
537         buf[1] = id;
538         vbus_debug("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
539         _chn0_post(buf, sizeof(buf), RT_WAITING_FOREVER);
540         /* Warn the other side in 100 more pkgs. */
541         _chn_recv_wm[id].last_warn = _chn_recv_wm[id].level + 100;
542     }
543 #endif
544 }
545 
rt_vbus_data_pop(unsigned int id)546 struct rt_vbus_data* rt_vbus_data_pop(unsigned int id)
547 {
548     struct rt_vbus_data *act;
549     rt_ubase_t lvl;
550 
551     RT_ASSERT(0 < id && id < RT_VBUS_CHANNEL_NR);
552 
553     lvl = rt_hw_interrupt_disable();
554 
555     act = _bus_in_action[id][_IN_ACT_HEAD];
556     if (act)
557     {
558         _bus_in_action[id][_IN_ACT_HEAD] = act->next;
559     }
560 
561     rt_hw_interrupt_enable(lvl);
562 
563 #ifdef RT_VBUS_USING_FLOW_CONTROL
564     if (_chn_recv_wm[id].level != 0)
565     {
566         _chn_recv_wm[id].level--;
567         if (_chn_recv_wm[id].level <= _chn_recv_wm[id].low_mark &&
568             _chn_recv_wm[id].last_warn > _chn_recv_wm[id].low_mark)
569         {
570             unsigned char buf[2];
571 
572             buf[0] = RT_VBUS_CHN0_CMD_RESUME;
573             buf[1] = id;
574             vbus_debug("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
575             _chn0_post(buf, sizeof(buf), RT_WAITING_FOREVER);
576             _chn_recv_wm[id].last_warn = 0;
577         }
578     }
579 #endif
580     return act;
581 }
582 
583 /* dump cmd that is not start with ACK/NAK */
__dump_naked_cmd(char * dst,size_t lsize,unsigned char * dp,size_t dsize)584 static size_t __dump_naked_cmd(char *dst, size_t lsize,
585                                unsigned char *dp, size_t dsize)
586 {
587     size_t len;
588     if (dp[0] == RT_VBUS_CHN0_CMD_DISABLE ||
589         dp[0] == RT_VBUS_CHN0_CMD_SUSPEND ||
590         dp[0] == RT_VBUS_CHN0_CMD_RESUME)
591     {
592         len = rt_snprintf(dst, lsize, "%s %d",
593                           rt_vbus_cmd2str[dp[0]], dp[1]);
594     }
595     else if (dp[0] == RT_VBUS_CHN0_CMD_ENABLE)
596     {
597         len = rt_snprintf(dst, lsize, "%s %s",
598                           rt_vbus_cmd2str[dp[0]], dp+1);
599     }
600     else if (dp[0] < RT_VBUS_CHN0_CMD_MAX)
601     {
602         len = rt_snprintf(dst, lsize, "%s %s %d",
603                           rt_vbus_cmd2str[dp[0]],
604                           dp+1, dp[2+rt_strlen((char*)dp+1)]);
605     }
606     else
607     {
608         len = rt_snprintf(dst, lsize, "(invalid)%d %d",
609                           dp[0], dp[1]);
610     }
611     return len;
612 }
613 
614 static char _cmd_dump_buf[64];
dump_cmd_pkt(unsigned char * dp,size_t dsize)615 static char* dump_cmd_pkt(unsigned char *dp, size_t dsize)
616 {
617     size_t len;
618 
619     if (dp[0] == RT_VBUS_CHN0_CMD_ACK || dp[0] == RT_VBUS_CHN0_CMD_NAK )
620     {
621         len = rt_snprintf(_cmd_dump_buf, sizeof(_cmd_dump_buf),
622                           "%s ", rt_vbus_cmd2str[dp[0]]);
623         len += __dump_naked_cmd(_cmd_dump_buf+len, sizeof(_cmd_dump_buf)-len,
624                                 dp+1, dsize-1);
625     }
626     else
627     {
628         len = __dump_naked_cmd(_cmd_dump_buf, sizeof(_cmd_dump_buf),
629                                dp, dsize);
630     }
631 
632     if (len > sizeof(_cmd_dump_buf) - 1)
633         len = sizeof(_cmd_dump_buf) - 1;
634 
635     _cmd_dump_buf[len] = '\0';
636     return _cmd_dump_buf;
637 }
638 
_chn0_echo_with(rt_uint8_t prefix,rt_uint32_t dsize,unsigned char * dp)639 static rt_err_t _chn0_echo_with(rt_uint8_t prefix,
640                                 rt_uint32_t dsize,
641                                 unsigned char *dp)
642 {
643     rt_err_t err;
644     unsigned char *resp;
645 
646     resp = rt_malloc(dsize+1);
647     if (!resp)
648         return -RT_ENOMEM;
649     *resp = prefix;
650     rt_memcpy(resp+1, dp, dsize);
651     vbus_verbose("%s --> remote\n", dump_cmd_pkt(resp, dsize+1));
652 
653     err = _chn0_post(resp, dsize+1, RT_WAITING_FOREVER);
654 
655     rt_free(resp);
656 
657     return err;
658 }
659 
_chn0_nak(rt_uint32_t dsize,unsigned char * dp)660 static rt_err_t _chn0_nak(rt_uint32_t dsize, unsigned char *dp)
661 {
662     return _chn0_echo_with(RT_VBUS_CHN0_CMD_NAK, dsize, dp);
663 }
664 
_chn0_ack(rt_uint32_t dsize,unsigned char * dp)665 static rt_err_t _chn0_ack(rt_uint32_t dsize, unsigned char *dp)
666 {
667     return _chn0_echo_with(RT_VBUS_CHN0_CMD_ACK, dsize, dp);
668 }
669 
670 enum _vbus_session_st
671 {
672     SESSIOM_AVAILABLE,
673     SESSIOM_LISTENING,
674     SESSIOM_ESTABLISHING,
675 };
676 
677 struct rt_vbus_conn_session
678 {
679     /* negative value means error */
680     int chnr;
681     enum _vbus_session_st st;
682     struct rt_completion cmp;
683     struct rt_vbus_request *req;
684 };
685 
686 static struct rt_vbus_conn_session _sess[RT_VBUS_CHANNEL_NR/2];
687 
_sess_find(const unsigned char * name,enum _vbus_session_st st)688 static int _sess_find(const unsigned char *name,
689                       enum _vbus_session_st st)
690 {
691     int i;
692 
693     for (i = 0; i < ARRAY_SIZE(_sess); i++)
694     {
695         if (_sess[i].st == st && _sess[i].req->name &&
696             rt_strcmp(_sess[i].req->name, (char*)name) == 0)
697             break;
698     }
699     return i;
700 }
701 
_chn0_actor(unsigned char * dp,size_t dsize)702 static int _chn0_actor(unsigned char *dp, size_t dsize)
703 {
704     if (*dp != RT_VBUS_CHN0_CMD_SUSPEND && *dp != RT_VBUS_CHN0_CMD_RESUME)
705         vbus_verbose("local <-- %s\n", dump_cmd_pkt(dp, dsize));
706 
707     switch (*dp)
708     {
709     case RT_VBUS_CHN0_CMD_ENABLE:
710         {
711             int i, chnr;
712             rt_err_t err;
713             unsigned char *resp;
714 
715             i = _sess_find(dp+1, SESSIOM_LISTENING);
716             if (i == ARRAY_SIZE(_sess))
717             {
718                 _chn0_nak(dsize, dp);
719                 break;
720             }
721 
722             for (chnr = 0; chnr < ARRAY_SIZE(_chn_status); chnr++)
723             {
724                 if (_chn_status[chnr] == RT_VBUS_CHN_ST_AVAILABLE)
725                     break;
726             }
727             if (chnr == ARRAY_SIZE(_chn_status))
728             {
729                 _chn0_nak(dsize, dp);
730                 break;
731             }
732 
733             resp = rt_malloc(dsize + 1);
734             if (!resp)
735                 break;
736 
737             *resp = RT_VBUS_CHN0_CMD_SET;
738             rt_memcpy(resp+1, dp+1, dsize-1);
739             resp[dsize] = chnr;
740 
741             rt_vbus_set_recv_wm(chnr, _sess[i].req->recv_wm.low, _sess[i].req->recv_wm.high);
742             rt_vbus_set_post_wm(chnr, _sess[i].req->post_wm.low, _sess[i].req->post_wm.high);
743 
744             vbus_verbose("%s --> remote\n", dump_cmd_pkt(resp, dsize+1));
745             err = _chn0_post(resp, dsize+1, RT_WAITING_FOREVER);
746 
747             if (err == RT_EOK)
748             {
749                 _sess[i].st   = SESSIOM_ESTABLISHING;
750                 vbus_debug("set sess %d st: %s\n", i,
751                            rt_vbus_sess_st2str[_sess[i].st]);
752                 _sess[i].chnr = chnr;
753                 _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHING;
754             }
755             rt_free(resp);
756         }
757         break;
758     case RT_VBUS_CHN0_CMD_SET:
759         {
760             int i, chnr;
761 
762             i = _sess_find(dp+1, SESSIOM_ESTABLISHING);
763             if (i == ARRAY_SIZE(_sess))
764             {
765                 vbus_verbose("drop spurious packet\n");
766                 break;
767             }
768 
769             chnr = dp[1+rt_strlen((const char*)dp+1)+1];
770 
771             if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
772             {
773                 vbus_verbose("SET wrong chnr %d\n", chnr);
774                 break;
775             }
776             if (_chn_status[chnr] != RT_VBUS_CHN_ST_AVAILABLE)
777             {
778                 _chn0_nak(dsize, dp);
779                 vbus_verbose("SET wrong chnr status %d, %s\n",
780                              chnr, rt_vbus_chn_st2str[_chn_status[chnr]]);
781                 break;
782             }
783 
784             rt_vbus_set_recv_wm(chnr, _sess[i].req->recv_wm.low, _sess[i].req->recv_wm.high);
785             rt_vbus_set_post_wm(chnr, _sess[i].req->post_wm.low, _sess[i].req->post_wm.high);
786 
787             if (_chn0_ack(dsize, dp) >= 0)
788             {
789                 _sess[i].chnr = chnr;
790                 _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
791                 vbus_debug("chn %d %s\n", chnr,
792                            rt_vbus_chn_st2str[_chn_status[chnr]]);
793                 rt_completion_done(&_sess[i].cmp);
794             }
795         }
796         break;
797     case RT_VBUS_CHN0_CMD_ACK:
798         if (dp[1] == RT_VBUS_CHN0_CMD_SET)
799         {
800             int i, chnr;
801 
802             i = _sess_find(dp+2, SESSIOM_ESTABLISHING);
803             if (i == ARRAY_SIZE(_sess))
804                 /* drop that spurious packet */
805                 break;
806 
807             chnr = dp[1+rt_strlen((const char*)dp+2)+2];
808 
809             _sess[i].chnr = chnr;
810             _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
811             vbus_debug("chn %d %s\n", chnr,
812                        rt_vbus_chn_st2str[_chn_status[chnr]]);
813             rt_completion_done(&_sess[i].cmp);
814         }
815         else if (dp[1] == RT_VBUS_CHN0_CMD_DISABLE)
816         {
817             unsigned char chnr = dp[2];
818 
819             if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
820                 break;
821 
822             /* We could only get here by sending DISABLE command, which is
823              * initiated by the rt_vbus_close_chn. */
824             _chn_status[chnr] = RT_VBUS_CHN_ST_AVAILABLE;
825 
826             _vbus_indicate(RT_VBUS_EVENT_ID_DISCONN, chnr);
827             /* notify the thread that the channel has been closed */
828             rt_vbus_notify_chn(chnr, -RT_ERROR);
829         }
830         else
831         {
832             vbus_info("invalid ACK for %d\n", dp[1]);
833         }
834         break;
835     case RT_VBUS_CHN0_CMD_DISABLE:
836         {
837             unsigned char chnr = dp[1];
838 
839             if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
840                 break;
841 
842             _chn_status[chnr] = RT_VBUS_CHN_ST_CLOSING;
843 
844             _chn0_ack(dsize, dp);
845 
846             _vbus_indicate(RT_VBUS_EVENT_ID_DISCONN, chnr);
847             /* notify the thread that the channel has been closed */
848             rt_vbus_notify_chn(chnr, -RT_ERROR);
849         }
850         break;
851     case RT_VBUS_CHN0_CMD_SUSPEND:
852 #ifdef RT_VBUS_USING_FLOW_CONTROL
853         {
854             unsigned char chnr = dp[1];
855 
856             if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
857                 break;
858 
859             if (_chn_status[chnr] != RT_VBUS_CHN_ST_ESTABLISHED)
860                 break;
861 
862             _chn_status[chnr] = RT_VBUS_CHN_ST_SUSPEND;
863         }
864 #endif
865         break;
866     case RT_VBUS_CHN0_CMD_RESUME:
867 #ifdef RT_VBUS_USING_FLOW_CONTROL
868         {
869             unsigned char chnr = dp[1];
870 
871             if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
872                 break;
873 
874             if (_chn_status[chnr] != RT_VBUS_CHN_ST_SUSPEND)
875                 break;
876 
877             _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
878 
879             /* Protect the list. */
880             rt_enter_critical();
881             while (!rt_list_isempty(&_chn_suspended_threads[chnr]))
882             {
883                 rt_thread_t thread;
884 
885                 thread = rt_list_entry(_chn_suspended_threads[chnr].next,
886                                        struct rt_thread,
887                                        tlist);
888                 rt_thread_resume(thread);
889             }
890             rt_exit_critical();
891         }
892 #endif
893         break;
894     case RT_VBUS_CHN0_CMD_NAK:
895         if (dp[1] == RT_VBUS_CHN0_CMD_ENABLE)
896         {
897             int i;
898 
899             i = _sess_find(dp+2, SESSIOM_ESTABLISHING);
900             if (i == ARRAY_SIZE(_sess))
901                 /* drop that spurious packet */
902                 break;
903 
904             _sess[i].chnr = -RT_EIO;
905             rt_completion_done(&_sess[i].cmp);
906         }
907         else if (dp[1] == RT_VBUS_CHN0_CMD_SET)
908         {
909             vbus_info("NAK for %d not implemented\n", dp[1]);
910         }
911         else
912         {
913             vbus_info("invalid NAK for %d\n", dp[1]);
914         }
915         break;
916     default:
917         /* just ignore the invalid cmd */
918         vbus_info("drop unknown cmd %d on chn0\n", *dp);
919         break;
920     };
921 
922     return RT_EOK;
923 }
924 
rt_vbus_request_chn(struct rt_vbus_request * req,int timeout)925 int rt_vbus_request_chn(struct rt_vbus_request *req,
926                         int timeout)
927 {
928     int i, chnr, err;
929 	size_t plen = rt_strlen(req->name) + 2;
930 	unsigned char *pbuf;
931     rt_ubase_t lvl;
932 
933     lvl = rt_hw_interrupt_disable();
934     for (i = 0; i < ARRAY_SIZE(_sess); i++)
935     {
936         if (_sess[i].st == SESSIOM_AVAILABLE)
937             break;
938     }
939     if (i == ARRAY_SIZE(_sess))
940     {
941         rt_hw_interrupt_enable(lvl);
942         return -RT_ERROR;
943     }
944 
945     rt_completion_init(&_sess[i].cmp);
946     _sess[i].req = req;
947 
948     if (req->is_server)
949     {
950         _sess[i].st = SESSIOM_LISTENING;
951         rt_hw_interrupt_enable(lvl);
952 
953         vbus_debug("request listening %s on %d\n", req->name, i);
954 
955         /* always wait on the condition */
956         err = RT_EOK;
957         goto _waitforcmp;
958     }
959 
960 	pbuf = rt_malloc(plen);
961 	if (!pbuf)
962     {
963         rt_hw_interrupt_enable(lvl);
964         return -RT_ENOMEM;
965     }
966 
967     _sess[i].st = SESSIOM_ESTABLISHING;
968     rt_hw_interrupt_enable(lvl);
969 
970     pbuf[0] = RT_VBUS_CHN0_CMD_ENABLE;
971     rt_memcpy(pbuf+1, req->name, plen-1);
972     vbus_verbose("%s --> remote\n", dump_cmd_pkt(pbuf, plen));
973 
974 	err = _chn0_post(pbuf, plen, RT_WAITING_FOREVER);
975     rt_free(pbuf);
976 
977 _waitforcmp:
978     if (err == RT_EOK)
979         err = rt_completion_wait(&_sess[i].cmp, timeout);
980 
981     vbus_debug("request wait cmp done %d, chnr %d\n", err, _sess[i].chnr);
982 
983     if (err)
984     {
985         /* cleanup the mass when the wait is time out but we have done some job
986          */
987         if (_sess[i].st == SESSIOM_ESTABLISHING)
988             _chn_status[_sess[i].chnr] = RT_VBUS_CHN_ST_AVAILABLE;
989         chnr = err;
990         goto Out;
991     }
992 
993     RT_ASSERT(_sess[i].chnr != 0);
994 
995     chnr = _sess[i].chnr;
996 
997 Out:
998     /* detach the sess as we finished the job */
999     _sess[i].st = SESSIOM_AVAILABLE;
1000     _sess[i].req = RT_NULL;
1001 
1002     return chnr;
1003 }
1004 
rt_vbus_close_chn(unsigned char chnr)1005 void rt_vbus_close_chn(unsigned char chnr)
1006 {
1007     void *p;
1008     rt_err_t err;
1009     unsigned char buf[2];
1010 
1011     buf[0] = RT_VBUS_CHN0_CMD_DISABLE;
1012     buf[1] = chnr;
1013 
1014     RT_ASSERT(0 < chnr && chnr < RT_VBUS_CHANNEL_NR);
1015 
1016     if (_chn_status[chnr] == RT_VBUS_CHN_ST_CLOSED ||
1017         _chn_status[chnr] == RT_VBUS_CHN_ST_CLOSING)
1018     {
1019         _chn_status[chnr] = RT_VBUS_CHN_ST_AVAILABLE;
1020         return;
1021     }
1022 
1023     if (!_chn_connected(chnr))
1024         return;
1025 
1026     _chn_status[chnr] = RT_VBUS_CHN_ST_CLOSING;
1027     vbus_info("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
1028     err = _chn0_post(&buf, sizeof(buf), RT_WAITING_FOREVER);
1029     if (err == RT_EOK)
1030         /* wait for the ack */
1031         rt_vbus_listen_on(chnr, 10 * RT_TICK_PER_SECOND);
1032 
1033     /* cleanup the remaining data */
1034     for (p = rt_vbus_data_pop(chnr); p; p = rt_vbus_data_pop(chnr))
1035         rt_free(p);
1036     /* FIXME: there is a chance that there are some data left on the send
1037      * buffer. So if we connect other channel with the same number immediately,
1038      * the new channel will receive some garbage data. However, this is highly
1039      * un-probable. */
1040 }
1041 
1042 #ifdef RT_VBUS_STATISTICS
1043 static unsigned int _total_data_sz;
1044 #endif
1045 
_bus_in_entry(void * param)1046 static void _bus_in_entry(void *param)
1047 {
1048     rt_sem_init(&_bus_in_sem, "vbus", 0, RT_IPC_FLAG_FIFO);
1049     rt_event_init(&_bus_in_event, "vbus", RT_IPC_FLAG_FIFO);
1050     rt_memset(_bus_in_action, 0, sizeof(_bus_in_action));
1051 
1052     while (rt_sem_take(&_bus_in_sem,
1053                        RT_WAITING_FOREVER) == RT_EOK)
1054     {
1055         rt_uint32_t event_set = 0;
1056 
1057         /* while(not empty) */
1058         while (RT_VBUS_IN_RING->get_idx != RT_VBUS_IN_RING->put_idx)
1059         {
1060             unsigned int id, nxtidx;
1061             rt_size_t size;
1062             struct rt_vbus_data *act;
1063 
1064             rt_vbus_smp_rmb();
1065             size = RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].len;
1066             id = RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].id;
1067 
1068             vbus_debug("vmm bus in: chnr %d, size %d\n", id, size);
1069 
1070             /* Suspended channel can still recv data. */
1071             if (id > RT_VBUS_CHANNEL_NR || !_chn_connected(id))
1072             {
1073                 vbus_error("drop on invalid chn %d\n", id);
1074                 /* drop the invalid packet */
1075                 _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
1076                 continue;
1077             }
1078 
1079             if (id == 0)
1080             {
1081                 if (size > 60)
1082                     vbus_error("too big(%d) packet on chn0\n", size);
1083                 else
1084                     _chn0_actor(RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, size);
1085                 _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
1086                 continue;
1087             }
1088 
1089 #ifdef RT_VBUS_STATISTICS
1090             _total_data_sz += size;
1091 #endif
1092 
1093             act = rt_malloc(sizeof(*act) + size);
1094             if (act == RT_NULL)
1095             {
1096                 //vbus_error("drop on OOM (%d, %d)\n", id, size);
1097                 /* drop the packet on malloc fall */
1098                 _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
1099                 continue;
1100             }
1101 
1102             act->size = size;
1103             act->next = RT_NULL;
1104 
1105             nxtidx = RT_VBUS_IN_RING->get_idx + LEN2BNR(size);
1106             if (nxtidx >= RT_VMM_RB_BLK_NR)
1107             {
1108                 unsigned int tailsz;
1109 
1110                 tailsz = (RT_VMM_RB_BLK_NR - RT_VBUS_IN_RING->get_idx)
1111                           * sizeof(RT_VBUS_IN_RING->blks[0]) - RT_VBUS_BLK_HEAD_SZ;
1112 
1113                 /* the remaining block is sufficient for the data */
1114                 if (tailsz > size)
1115                     tailsz = size;
1116 
1117                 rt_memcpy(act+1, &RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, tailsz);
1118                 rt_memcpy((char*)(act+1) + tailsz, &RT_VBUS_IN_RING->blks[0], size - tailsz);
1119 
1120                 /* It shall make sure the CPU has finished reading the item
1121                  * before it writes the new tail pointer, which will erase the
1122                  * item. */
1123                 rt_vbus_smp_wmb();
1124                 RT_VBUS_IN_RING->get_idx = nxtidx - RT_VMM_RB_BLK_NR;
1125             }
1126             else
1127             {
1128                 rt_memcpy(act+1, &RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, size);
1129 
1130                 rt_vbus_smp_wmb();
1131                 RT_VBUS_IN_RING->get_idx = nxtidx;
1132             }
1133 
1134             rt_vbus_data_push(id, act);
1135             _vbus_indicate(RT_VBUS_EVENT_ID_RX, id);
1136             event_set |= 1 << id;
1137 
1138             if (RT_VBUS_IN_RING->blocked)
1139                 rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
1140         }
1141 
1142         if (event_set != 0)
1143             rt_vbus_notify_set(event_set);
1144     }
1145     RT_ASSERT(0);
1146 }
1147 
rt_vbus_isr(int irqnr,void * param)1148 void rt_vbus_isr(int irqnr, void *param)
1149 {
1150     if (RT_VBUS_OUT_RING->blocked)
1151         rt_vbus_resume_out_thread();
1152 
1153     rt_sem_release(&_bus_in_sem);
1154     rt_vbus_hw_eoi(irqnr, param);
1155 }
1156 
rt_vbus_init(void * outr,void * inr)1157 int rt_vbus_init(void *outr, void *inr)
1158 {
1159     int i;
1160 
1161 #ifdef RT_USING_LOGTRACE
1162     log_trace_register_session(&_lgs);
1163 #endif
1164 
1165     if (outr > inr)
1166     {
1167         RT_ASSERT((char*)outr - (char*)inr >= sizeof(struct rt_vbus_ring));
1168     }
1169     else
1170     {
1171         RT_ASSERT((char*)inr - (char*)outr >= sizeof(struct rt_vbus_ring));
1172     }
1173 
1174     RT_VBUS_OUT_RING = outr;
1175     RT_VBUS_IN_RING  = inr;
1176 
1177     rt_memset(RT_VBUS_OUT_RING, 0, sizeof(*RT_VBUS_OUT_RING));
1178     rt_memset(RT_VBUS_IN_RING,  0, sizeof(*RT_VBUS_IN_RING));
1179     _chn_status[0] = RT_VBUS_CHN_ST_ESTABLISHED;
1180     for (i = 1; i < ARRAY_SIZE(_chn_status); i++)
1181     {
1182         _chn_status[i] = RT_VBUS_CHN_ST_AVAILABLE;
1183     }
1184     for (i = 0; i < ARRAY_SIZE(_sess); i++)
1185     {
1186         _sess[i].req = RT_NULL;
1187         _sess[i].st  = SESSIOM_AVAILABLE;
1188     }
1189     _vbus_rx_indi[RT_VBUS_EVENT_ID_TX][0].indicate = _chn0_tx_listener;
1190     _vbus_rx_indi[RT_VBUS_EVENT_ID_TX][0].ctx = RT_NULL;
1191 
1192 #ifdef RT_VBUS_USING_FLOW_CONTROL
1193     for (i = 0; i < ARRAY_SIZE(_chn_wm_que); i++)
1194     {
1195         rt_wm_que_init(&_chn_wm_que[i],
1196                        RT_VMM_RB_BLK_NR / 3,
1197                        RT_VMM_RB_BLK_NR * 2 / 3);
1198     }
1199     /* Channel 0 has the full channel. */
1200     rt_wm_que_set_mark(&_chn_wm_que[0], 0, ~0);
1201 
1202     for (i = 0; i < ARRAY_SIZE(_chn_suspended_threads); i++)
1203     {
1204         rt_list_init(&_chn_suspended_threads[i]);
1205     }
1206 
1207     for (i = 1; i < ARRAY_SIZE(_chn_recv_wm); i++)
1208     {
1209         rt_vbus_set_recv_wm(i,
1210                             RT_VMM_RB_BLK_NR / 3,
1211                             RT_VMM_RB_BLK_NR * 2 / 3);
1212         _chn_recv_wm[i].level = 0;
1213         _chn_recv_wm[i].last_warn = 0;
1214     }
1215     /* Channel 0 has the full channel. Don't suspend it. */
1216     _chn_recv_wm[0].low_mark = 0;
1217     _chn_recv_wm[0].high_mark = ~0;
1218     _chn_recv_wm[0].level = 0;
1219     _chn_recv_wm[0].last_warn = 0;
1220 #endif
1221 
1222     rt_thread_init(&_bus_out_thread, "vbusout",
1223                    _bus_out_entry, RT_NULL,
1224                    _bus_out_thread_stack, sizeof(_bus_out_thread_stack),
1225                    _BUS_OUT_THRD_PRIO, 20);
1226     rt_thread_startup(&_bus_out_thread);
1227 
1228     rt_thread_init(&_bus_in_thread, "vbusin",
1229                    _bus_in_entry, RT_NULL,
1230                    _bus_in_thread_stack, sizeof(_bus_in_thread_stack),
1231                    _BUS_IN_THRD_PRIO, 20);
1232 
1233 
1234     rt_thread_startup(&_bus_in_thread);
1235 
1236     rt_vbus_hw_init();
1237 
1238     rt_kprintf("VBus loaded: %d out blocks, %d in blocks\n",
1239                RT_VMM_RB_BLK_NR, RT_VMM_RB_BLK_NR);
1240 
1241     rt_vbus_chnx_init();
1242 
1243     return 0;
1244 }
1245 
rt_vbus_rb_dump(void)1246 void rt_vbus_rb_dump(void)
1247 {
1248     rt_kprintf("OUT ring:(%s blocked)\n", RT_VBUS_OUT_RING->blocked ? "is" : "not");
1249     rt_kprintf("put idx: %8x, get idx: %8x\n",
1250                RT_VBUS_OUT_RING->put_idx, RT_VBUS_OUT_RING->get_idx);
1251     rt_kprintf("space: %d\n", _bus_ring_space_nr(RT_VBUS_OUT_RING));
1252 
1253 
1254     rt_kprintf("IN ring:(%s blocked)\n", RT_VBUS_IN_RING->blocked ? "is" : "not");
1255     rt_kprintf("put idx: %8x, get idx: %8x\n",
1256                RT_VBUS_IN_RING->put_idx, RT_VBUS_IN_RING->get_idx);
1257     rt_kprintf("space: %d\n", _bus_ring_space_nr(RT_VBUS_IN_RING));
1258 }
1259 
rt_vbus_chn_dump(void)1260 void rt_vbus_chn_dump(void)
1261 {
1262     int i;
1263     rt_kprintf("vbus channel status:\n");
1264     for (i = 0; i < ARRAY_SIZE(_chn_status); i++)
1265     {
1266         rt_kprintf("%2d:%s\n", i, rt_vbus_chn_st2str[_chn_status[i]]);
1267     }
1268 }
1269 
rt_vbus_sess_dump(void)1270 void rt_vbus_sess_dump(void)
1271 {
1272     int i;
1273 
1274     rt_kprintf("vbus conn session:\n");
1275     for (i = 0; i < ARRAY_SIZE(_sess); i++)
1276     {
1277         rt_kprintf("%2d(%s):%s\n", i, _sess[i].req ? _sess[i].req->name : "",
1278                    rt_vbus_sess_st2str[_sess[i].st]);
1279     }
1280 }
1281 
rt_vbus_que_dump(void)1282 void rt_vbus_que_dump(void)
1283 {
1284     rt_kprintf("out que:\n");
1285     rt_prio_queue_dump(_bus_out_que);
1286 }
1287 
rt_vbus_total_data_sz(void)1288 unsigned int rt_vbus_total_data_sz(void)
1289 {
1290 #ifdef RT_VBUS_STATISTICS
1291     return _total_data_sz;
1292 #else
1293     return (unsigned int)-1;
1294 #endif
1295 }
1296 
rt_vbus_data_pkt_dump(void)1297 void rt_vbus_data_pkt_dump(void)
1298 {
1299     int i;
1300 
1301     for (i = 0; i < ARRAY_SIZE(_bus_in_action); i++)
1302     {
1303         struct rt_vbus_data *dp;
1304 
1305 #ifdef RT_VBUS_STATISTICS
1306         rt_kprintf("%2d %4d: ", i, _bus_in_action_nr[i]);
1307 #else
1308         rt_kprintf("%2d: ", i);
1309 #endif
1310         for (dp = _bus_in_action[i][_IN_ACT_HEAD];
1311              dp;
1312              dp = dp->next)
1313         {
1314             rt_kprintf("%p(%d) -> ", dp, dp->size);
1315         }
1316         rt_kprintf(" nil\n");
1317     }
1318 }
1319 
1320 #ifdef RT_VBUS_USING_FLOW_CONTROL
rt_vbus_chm_wm_dump(void)1321 void rt_vbus_chm_wm_dump(void)
1322 {
1323     int i;
1324 
1325     rt_kprintf("post wm:\n");
1326     for (i = 0; i < ARRAY_SIZE(_chn_wm_que); i++)
1327         rt_wm_que_dump(&_chn_wm_que[i]);
1328 
1329     rt_kprintf("recv wm:\n");
1330     rt_kprintf("     low,     high,      cur,  last warn\n");
1331     for (i = 0; i < ARRAY_SIZE(_chn_recv_wm); i++)
1332     {
1333         rt_kprintf("%8x, %8x, %8x, %8x\n",
1334                    _chn_recv_wm[i].low_mark, _chn_recv_wm[i].high_mark,
1335                    _chn_recv_wm[i].level, _chn_recv_wm[i].last_warn);
1336     }
1337 }
1338 #endif
1339 
1340 #ifdef RT_USING_FINSH
1341 #include <finsh.h>
1342 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_rb_dump,    vbrb, dump vbus ringbuffer status);
1343 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_chn_dump,  vbchn, dump vbus channel status);
1344 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_sess_dump, vbses, dump vbus session status);
1345 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_que_dump,  vbque, dump vbus out queue status);
1346 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_total_data_sz,  vbtsz, total in data);
1347 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_data_pkt_dump,  vbdq, dump the data queue);
1348 #ifdef RT_VBUS_USING_FLOW_CONTROL
1349 FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_chm_wm_dump, vbwm, dump vbus water mark status);
1350 #endif
1351 #endif
1352 
1353