浏览代码

VBus: added

Currently only lpc43xx is supported.
Grissiom 10 年之前
父节点
当前提交
f7415e595e

+ 49 - 22
bsp/lpc43xx/M0/applications/application.c

@@ -33,38 +33,67 @@
 /* thread phase init */
 void rt_init_thread_entry(void *parameter)
 {
+#ifdef RT_USING_LOGTRACE
+    log_trace_init();
+    log_trace_set_device(RT_CONSOLE_DEVICE_NAME);
+#endif
+
 #ifdef RT_USING_FINSH
     /* initialize finsh */
     finsh_system_init();
     finsh_set_device(RT_CONSOLE_DEVICE_NAME);
 #endif
+
+#ifdef RT_USING_VBUS
+    rt_vbus_do_init();
+#endif
 }
+
 /*the led thread*/
 ALIGN(RT_ALIGN_SIZE)
-static rt_uint8_t led_stack[ 512 ];
+static rt_uint8_t led_stack[1024];
 static struct rt_thread led_thread;
 static void led_thread_entry(void *parameter)
 {
-    rt_uint8_t led_value = 0;
     rt_device_t led_dev;
+    rt_device_t vbus_dev;
+    rt_err_t err;
+
     rt_led_hw_init();
+
     led_dev = rt_device_find("led");
     if (led_dev == RT_NULL)
     {
-        rt_kprintf("can not find the led device!\n");
+        rt_kprintf("can not find the led device\n");
         return;
     }
+
+    vbus_dev = rt_device_find("vecho");
+    if (vbus_dev == RT_NULL)
+    {
+        rt_kprintf("can not find the vbus device\n");
+        return;
+    }
+
+    err = rt_device_open(vbus_dev, RT_DEVICE_OFLAG_RDWR);
+    if (err != RT_EOK)
+    {
+        rt_kprintf("open vbus failed: %d\n", err);
+        return;
+    }
+
     while (1)
     {
-        /* led0 on */
-        led_value = 1;
-        led_dev->write(led_dev, 1, &led_value, 1);
-        rt_thread_delay(RT_TICK_PER_SECOND / 2); /* sleep 0.5 second and switch to other thread */
-
-        /* led0 off */
-        led_value = 0;
-        led_dev->write(led_dev, 1, &led_value, 1);
-        rt_thread_delay(RT_TICK_PER_SECOND / 2);
+        rt_uint8_t led_value;
+        int len;
+
+        len = rt_device_read(vbus_dev, 0, &led_value, sizeof(led_value));
+        if (len <= 0)
+        {
+            rt_kprintf("vbus read err: %d, %d\n", len, rt_get_errno());
+        }
+
+        led_dev->write(led_dev, 1, &led_value, sizeof(led_value));
     }
 }
 
@@ -74,17 +103,15 @@ int rt_application_init(void)
     rt_err_t result;
     tid = rt_thread_create("init",
                            rt_init_thread_entry, RT_NULL,
-                           2048, RT_THREAD_PRIORITY_MAX / 3, 20);
-    if (tid != RT_NULL) rt_thread_startup(tid);
+                           2048, 3, 20);
+    if (tid != RT_NULL)
+        rt_thread_startup(tid);
+
     /* init led thread */
-    result = rt_thread_init(&led_thread,
-                            "led",
-                            led_thread_entry,
-                            RT_NULL,
-                            (rt_uint8_t *)&led_stack[0],
-                            sizeof(led_stack),
-                            20,
-                            5);
+    result = rt_thread_init(&led_thread, "led",
+                            led_thread_entry, RT_NULL,
+                            (rt_uint8_t *)&led_stack[0], sizeof(led_stack),
+                            20, 5);
     if (result == RT_EOK)
     {
         rt_thread_startup(&led_thread);

+ 2 - 0
bsp/lpc43xx/M0/applications/board.c

@@ -62,5 +62,7 @@ void rt_hw_board_init()
 
     /* setup the console device */
     rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
+
+    rt_kprintf("timer compval: %d\n", LPC_RITIMER->COMPVAL);
 }
 

+ 13 - 0
bsp/lpc43xx/M0/applications/vbus_conf.h

@@ -0,0 +1,13 @@
+#ifndef __VBUS_CONF_H__
+#define __VBUS_CONF_H__
+
+/* Number of blocks in VBus. The total size of VBus is
+ * RT_VMM_RB_BLK_NR * 64byte * 2. */
+#define RT_VMM_RB_BLK_NR     20
+
+/* We don't use the IRQ number to trigger IRQ in this BSP. */
+#define RT_VBUS_GUEST_VIRQ    0
+#define RT_VBUS_HOST_VIRQ     0
+
+#endif /* end of include guard: __VBUS_CONF_H__ */
+

+ 57 - 0
bsp/lpc43xx/M0/applications/vbus_drv.c

@@ -0,0 +1,57 @@
+#include <rtthread.h>
+
+#ifdef RT_USING_VBUS
+#include <rtdevice.h>
+#include <vbus.h>
+#include <board.h>
+
+struct rt_vbus_ring rt_vbus_rings[2] SECTION("vbus_ring");
+
+int rt_vbus_do_init(void)
+{
+    return rt_vbus_init(&rt_vbus_rings[1], &rt_vbus_rings[0]);
+}
+INIT_COMPONENT_EXPORT(rt_vbus_do_init);
+
+int rt_vbus_hw_init(void)
+{
+    NVIC_ClearPendingIRQ(M0_M4CORE_IRQn);
+    NVIC_EnableIRQ(M0_M4CORE_IRQn);
+    return 0;
+}
+
+void M4CORE_IRQHandler(void)
+{
+    LPC_CREG->M4TXEVENT = 0;
+    rt_vbus_isr(M0_M4CORE_IRQn, RT_NULL);
+}
+
+int rt_vbus_hw_eoi(int irqnr, void *param)
+{
+    /* Nothing to do here as we cleared the interrupt in IRQHandler. */
+    return 0;
+}
+
+struct rt_vbus_dev rt_vbus_chn_devx[] = {
+    {
+        .req =
+        {
+            .prio = 30,
+            .name = "vecho",
+            .is_server = 0,
+            .recv_wm.low = RT_VMM_RB_BLK_NR / 3,
+            .recv_wm.high = RT_VMM_RB_BLK_NR * 2 / 3,
+            .post_wm.low = RT_VMM_RB_BLK_NR / 3,
+            .post_wm.high = RT_VMM_RB_BLK_NR * 2 / 3,
+        }
+    },
+    {
+        .req =
+        {
+            .name = RT_NULL,
+        }
+    },
+};
+
+#endif /* RT_USING_VBUS */
+

+ 53 - 0
bsp/lpc43xx/M0/applications/vbus_hw.h

@@ -0,0 +1,53 @@
+/*
+ * VMM Bus
+ *
+ * COPYRIGHT (C) 2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2014-04-15     Grissiom     init commit
+ */
+
+#include <rtthread.h>
+#include <board.h>
+
+rt_inline void rt_vbus_tick(unsigned int target_cpu, unsigned int irqnr)
+{
+    __SEV();
+}
+
+/* Read memory barrier. */
+rt_inline void rt_vbus_smp_rmb(void)
+{
+    __DMB();
+}
+
+/* Write memory barrier. */
+rt_inline void rt_vbus_smp_wmb(void)
+{
+    __DSB();
+}
+
+/* General memory barrier. */
+rt_inline void rt_vbus_smp_mb(void)
+{
+    __DSB();
+}

+ 2 - 1
bsp/lpc43xx/M0/rtconfig.h

@@ -224,7 +224,8 @@
 #define RT_LWIP_MSKADDR3 0
 // </section>
 
-
+#define RT_USING_VBUS
+#define RT_USING_LOGTRACE
 
 // </RDTConfigurator>
 

+ 6 - 0
bsp/lpc43xx/M0/rtthread-lpc43xx.ld

@@ -8,6 +8,7 @@ MEMORY
 {
     CODE (rx) : ORIGIN = 0x1B000000, LENGTH = 0x00080000
     DATA (rw) : ORIGIN = 0x10080000, LENGTH = 0x00008000
+    AHBRAM (rw) : ORIGIN = 0x20000000, LENGTH = 0x00010000
 }
 ENTRY(Reset_Handler)
 _system_stack_size = 0x200;
@@ -95,6 +96,11 @@ SECTIONS
     } > DATA
     __bss_end = .;
 
+    .vbus_ring (NOLOAD) :
+    {
+        *(vbus_ring)
+    } > AHBRAM
+
     _end = .;
 
     /* Stabs debugging sections.  */

+ 3 - 0
bsp/lpc43xx/M0/rtthread_lpc43xx.sct

@@ -11,5 +11,8 @@ LR_IROM2 0x1B000000 0x00080000  {    ; load region size_region
   RW_IRAM2 0x10080000 0x00008000  {  ; RW data
    .ANY (+RW +ZI)
   }
+  RW_AHBRAM 0x20000000 0x00010000  {  ; RW data
+   * (vbus_ring)
+  }
 }
 

+ 8 - 0
bsp/lpc43xx/M0/vbus_local_conf.h

@@ -0,0 +1,8 @@
+#ifndef __VBUS_LOCAL_CONF_H__
+#define __VBUS_LOCAL_CONF_H__
+
+#define RT_VBUS_USING_FLOW_CONTROL
+
+#define RT_VBUS_USING_TESTS
+
+#endif /* end of include guard: __VBUS_LOCAL_CONF_H__ */

+ 56 - 21
bsp/lpc43xx/M4/applications/application.c

@@ -58,12 +58,26 @@ static void _boot_M0(void)
 /* thread phase init */
 void rt_init_thread_entry(void *parameter)
 {
+    /*
+     *register unsigned int _msp __asm("msp");
+     *register unsigned int _psp __asm("psp");
+     *rt_kprintf("msp@ %p, psp@ %p\n", _msp, _psp);
+     */
+#ifdef RT_USING_LOGTRACE
+    log_trace_init();
+    log_trace_set_device(RT_CONSOLE_DEVICE_NAME);
+#endif
+
 #ifdef RT_USING_FINSH
     /* initialize finsh */
     finsh_system_init();
     finsh_set_device(RT_CONSOLE_DEVICE_NAME);
 #endif
 
+#ifdef RT_USING_VBUS
+    rt_vbus_do_init();
+#endif
+
     _boot_M0();
 }
 
@@ -73,26 +87,49 @@ static rt_uint8_t led_stack[ 512 ];
 static struct rt_thread led_thread;
 static void led_thread_entry(void *parameter)
 {
-    rt_uint8_t led_value = 0;
+    rt_uint8_t led_value;
     rt_device_t led_dev;
+    rt_device_t vbus_dev;
+    rt_err_t err;
+
     rt_led_hw_init();
+
     led_dev = rt_device_find("led");
     if (led_dev == RT_NULL)
     {
-        rt_kprintf("can not find the led device!\n");
+        rt_kprintf("can not find the led device\n");
         return;
     }
+
+    vbus_dev = rt_device_find("vecho");
+    if (vbus_dev == RT_NULL)
+    {
+        rt_kprintf("can not find the vbus device\n");
+        return;
+    }
+
+    err = rt_device_open(vbus_dev, RT_DEVICE_OFLAG_RDWR);
+    if (err != RT_EOK)
+    {
+        rt_kprintf("open vbus failed: %d\n", err);
+        return;
+    }
+
+    led_value = 0;
     while (1)
     {
-        /* led0 on */
-        led_value = 1;
-        led_dev->write(led_dev, 0, &led_value, 1);
-        rt_thread_delay(RT_TICK_PER_SECOND / 2); /* sleep 0.5 second and switch to other thread */
-
-        /* led0 off */
-        led_value = 0;
-        led_dev->write(led_dev, 0, &led_value, 1);
-        rt_thread_delay(RT_TICK_PER_SECOND / 2);
+        int len;
+
+        led_dev->write(led_dev, 0, &led_value, sizeof(led_value));
+
+        led_value = !led_value;
+        len = rt_device_write(vbus_dev, 0, &led_value, sizeof(led_value));
+        if (len <= 0)
+        {
+            rt_kprintf("vbus write err: %d, %d\n", len, rt_get_errno());
+        }
+
+        rt_thread_delay(1000);
     }
 }
 
@@ -102,17 +139,15 @@ int rt_application_init(void)
     rt_err_t result;
     tid = rt_thread_create("init",
                            rt_init_thread_entry, RT_NULL,
-                           2048, RT_THREAD_PRIORITY_MAX / 3, 20);
-    if (tid != RT_NULL) rt_thread_startup(tid);
+                           2048, 3, 20);
+    if (tid != RT_NULL)
+        rt_thread_startup(tid);
+
     /* init led thread */
-    result = rt_thread_init(&led_thread,
-                            "led",
-                            led_thread_entry,
-                            RT_NULL,
-                            (rt_uint8_t *)&led_stack[0],
-                            sizeof(led_stack),
-                            20,
-                            5);
+    result = rt_thread_init(&led_thread, "led",
+                            led_thread_entry, RT_NULL,
+                            (rt_uint8_t *)&led_stack[0], sizeof(led_stack),
+                            20, 5);
     if (result == RT_EOK)
     {
         rt_thread_startup(&led_thread);

+ 1 - 0
bsp/lpc43xx/M4/applications/board.c

@@ -65,6 +65,7 @@ void rt_hw_board_init()
 
     /* setup the console device */
     rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
+    rt_kprintf("timer compval: %d\n", SystemCoreClock / RT_TICK_PER_SECOND - 1);
 
 #if LPC_EXT_SDRAM == 1
     lpc_sdram_hw_init();

+ 13 - 0
bsp/lpc43xx/M4/applications/vbus_conf.h

@@ -0,0 +1,13 @@
+#ifndef __VBUS_CONF_H__
+#define __VBUS_CONF_H__
+
+/* Number of blocks in VBus. The total size of VBus is
+ * RT_VMM_RB_BLK_NR * 64byte * 2. */
+#define RT_VMM_RB_BLK_NR     20
+
+/* We don't use the IRQ number to trigger IRQ in this BSP. */
+#define RT_VBUS_GUEST_VIRQ    0
+#define RT_VBUS_HOST_VIRQ     0
+
+#endif /* end of include guard: __VBUS_CONF_H__ */
+

+ 57 - 0
bsp/lpc43xx/M4/applications/vbus_drv.c

@@ -0,0 +1,57 @@
+#include <rtthread.h>
+
+#ifdef RT_USING_VBUS
+#include <rtdevice.h>
+#include <vbus.h>
+#include <board.h>
+
+struct rt_vbus_ring rt_vbus_rings[2] SECTION("vbus_ring");
+
+int rt_vbus_do_init(void)
+{
+    return rt_vbus_init(&rt_vbus_rings[0], &rt_vbus_rings[1]);
+}
+INIT_COMPONENT_EXPORT(rt_vbus_do_init);
+
+int rt_vbus_hw_init(void)
+{
+    NVIC_ClearPendingIRQ(M0CORE_IRQn);
+    NVIC_EnableIRQ(M0CORE_IRQn);
+    return 0;
+}
+
+void M0CORE_IRQHandler(void)
+{
+    LPC_CREG->M0TXEVENT = 0;
+    rt_vbus_isr(M0CORE_IRQn, RT_NULL);
+}
+
+int rt_vbus_hw_eoi(int irqnr, void *param)
+{
+    /* Nothing to do here as we cleared the interrupt in IRQHandler. */
+    return 0;
+}
+
+struct rt_vbus_dev rt_vbus_chn_devx[] = {
+    {
+        .req =
+        {
+            .prio = 30,
+            .name = "vecho",
+            .is_server = 1,
+            .recv_wm.low = RT_VMM_RB_BLK_NR / 3,
+            .recv_wm.high = RT_VMM_RB_BLK_NR * 2 / 3,
+            .post_wm.low = RT_VMM_RB_BLK_NR / 3,
+            .post_wm.high = RT_VMM_RB_BLK_NR * 2 / 3,
+        }
+    },
+    {
+        .req =
+        {
+            .name = RT_NULL,
+        }
+    },
+};
+
+#endif /* RT_USING_VBUS */
+

+ 53 - 0
bsp/lpc43xx/M4/applications/vbus_hw.h

@@ -0,0 +1,53 @@
+/*
+ * VMM Bus
+ *
+ * COPYRIGHT (C) 2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2014-04-15     Grissiom     init commit
+ */
+
+#include <rtthread.h>
+#include <board.h>
+
+rt_inline void rt_vbus_tick(unsigned int target_cpu, unsigned int irqnr)
+{
+    __SEV();
+}
+
+/* Read memory barrier. */
+rt_inline void rt_vbus_smp_rmb(void)
+{
+    __DMB();
+}
+
+/* Write memory barrier. */
+rt_inline void rt_vbus_smp_wmb(void)
+{
+    __DSB();
+}
+
+/* General memory barrier. */
+rt_inline void rt_vbus_smp_mb(void)
+{
+    __DSB();
+}

+ 2 - 1
bsp/lpc43xx/M4/rtconfig.h

@@ -223,7 +223,8 @@
 #define RT_LWIP_MSKADDR3 0
 // </section>
 
-
+#define RT_USING_VBUS
+#define RT_USING_LOGTRACE
 
 // </RDTConfigurator>
 

+ 7 - 0
bsp/lpc43xx/M4/rtthread-lpc43xx.ld

@@ -9,6 +9,7 @@ MEMORY
     CODE (rx) : ORIGIN = 0x1A000000, LENGTH = 0x00080000
     M0CODE (rx) : ORIGIN = 0x1B000000, LENGTH = 0x00080000
     DATA (rw) : ORIGIN = 0x10000000, LENGTH = 0x00008000
+    AHBRAM (rw) : ORIGIN = 0x20000000, LENGTH = 0x00010000
 }
 ENTRY(Reset_Handler)
 _system_stack_size = 0x200;
@@ -97,6 +98,12 @@ SECTIONS
     } > DATA
     __bss_end = .;
 
+    .vbus_ring (NOLOAD) :
+    {
+        *(vbus_ring)
+    } > AHBRAM
+
+
     .text.M0CODE :
     {
         *(M0_CODE)

+ 3 - 0
bsp/lpc43xx/M4/rtthread_lpc43xx.sct

@@ -11,6 +11,9 @@ LR_IROM1 0x1A000000 0x00080000  {    ; load region size_region
   RW_IRAM1 0x10000000 0x00008000  {  ; RW data
    .ANY (+RW +ZI)
   }
+  RW_AHBRAM 0x20000000 0x00010000  {  ; RW data
+   * (vbus_ring)
+  }
 }
 
 LR_IROM2 0x1B000000 0x00080000  {

+ 8 - 0
bsp/lpc43xx/M4/vbus_local_conf.h

@@ -0,0 +1,8 @@
+#ifndef __VBUS_LOCAL_CONF_H__
+#define __VBUS_LOCAL_CONF_H__
+
+#define RT_VBUS_USING_FLOW_CONTROL
+
+#define RT_VBUS_USING_TESTS
+
+#endif /* end of include guard: __VBUS_LOCAL_CONF_H__ */

+ 29 - 0
components/vbus/SConscript

@@ -0,0 +1,29 @@
+# RT-Thread building script for component
+
+import SCons, os
+from building import *
+
+group = []
+if not GetDepend(['RT_USING_VBUS']):
+    Return('group')
+
+cwd = GetCurrentDir()
+src = Glob('*.c')
+
+for c, f in [['RT_USING_VBUS_RFS', 'utilities/rfs.c'],
+             ['RT_USING_VBUS_RSHELL', 'utilities/rshell.c'],
+             ]:
+    if GetDepend(c):
+        src += Glob(f)
+
+with open(os.path.join(Dir('#').get_abspath(), 'vbus_local_conf.h'), 'r') as f:
+    cpp = SCons.cpp.PreProcessor()
+    cpp.process_contents(f.read())
+    if 'RT_VBUS_USING_TESTS' in cpp.cpp_namespace:
+        src += Glob('tests/*.c')
+
+CPPPATH = [cwd, os.path.join(cwd, 'share_hdr')]
+
+group = DefineGroup('VBus', src, depend = ['RT_USING_VBUS'], CPPPATH = CPPPATH)
+
+Return('group')

+ 275 - 0
components/vbus/prio_queue.c

@@ -0,0 +1,275 @@
+/*
+ * Priority Queue
+ *
+ * COPYRIGHT (C) 2013, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2013-11-04     Grissiom     add comment
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include "prio_queue.h"
+
+struct rt_prio_queue_item {
+    struct rt_prio_queue_item *next;
+    /* data follows */
+};
+
+static void _do_push(struct rt_prio_queue *que,
+                     rt_uint8_t prio,
+                     struct rt_prio_queue_item *item)
+{
+    if (que->head[prio] == RT_NULL)
+    {
+        que->head[prio] = item;
+        que->bitmap |= 1 << prio;
+    }
+    else
+    {
+        RT_ASSERT(que->tail[prio]);
+        que->tail[prio]->next = item;
+    }
+    que->tail[prio] = item;
+}
+
+static struct rt_prio_queue_item* _do_pop(struct rt_prio_queue *que)
+{
+    int ffs;
+    struct rt_prio_queue_item *item;
+
+    ffs = __rt_ffs(que->bitmap);
+    if (ffs == 0)
+        return RT_NULL;
+    ffs--;
+
+    item = que->head[ffs];
+    RT_ASSERT(item);
+
+    que->head[ffs] = item->next;
+    if (que->head[ffs] == RT_NULL)
+    {
+        que->bitmap &= ~(1 << ffs);
+    }
+
+    return item;
+}
+
+rt_err_t rt_prio_queue_init(struct rt_prio_queue *que,
+                            const char *name,
+                            void *buf,
+                            rt_size_t bufsz,
+                            rt_size_t itemsz)
+{
+    RT_ASSERT(que);
+
+    rt_memset(que, 0, sizeof(*que));
+
+    rt_list_init(&(que->suspended_pop_list));
+
+    rt_mp_init(&que->pool, name, buf, bufsz,
+               sizeof(struct rt_prio_queue_item) + itemsz);
+
+    que->item_sz = itemsz;
+
+    return RT_EOK;
+}
+
+void rt_prio_queue_detach(struct rt_prio_queue *que)
+{
+    /* wake up all suspended pop threads, push thread is suspended on mempool.
+     */
+    while (!rt_list_isempty(&(que->suspended_pop_list)))
+    {
+        rt_thread_t thread;
+
+        /* disable interrupt */
+        rt_ubase_t temp = rt_hw_interrupt_disable();
+
+        /* get next suspend thread */
+        thread = rt_list_entry(que->suspended_pop_list.next, struct rt_thread, tlist);
+        /* set error code to RT_ERROR */
+        thread->error = -RT_ERROR;
+
+        rt_thread_resume(thread);
+
+        /* enable interrupt */
+        rt_hw_interrupt_enable(temp);
+    }
+    rt_mp_detach(&que->pool);
+}
+
+#ifdef RT_USING_HEAP
+struct rt_prio_queue* rt_prio_queue_create(const char *name,
+                                           rt_size_t item_nr,
+                                           rt_size_t item_sz)
+{
+    struct rt_prio_queue *que;
+    rt_size_t bufsz;
+
+    bufsz = item_nr * (sizeof(struct rt_prio_queue_item)
+                       + item_sz
+                       + sizeof(void*));
+
+    RT_ASSERT(item_nr);
+
+    que = rt_malloc(sizeof(*que) + bufsz);
+    if (!que)
+        return RT_NULL;
+
+    rt_prio_queue_init(que, name, que+1, bufsz, item_sz);
+
+    return que;
+}
+
+void rt_prio_queue_delete(struct rt_prio_queue *que)
+{
+    rt_prio_queue_detach(que);
+    rt_free(que);
+}
+#endif
+
+rt_err_t rt_prio_queue_push(struct rt_prio_queue *que,
+                            rt_uint8_t prio,
+                            void *data,
+                            rt_int32_t timeout)
+{
+    rt_ubase_t level;
+    struct rt_prio_queue_item *item;
+
+    RT_ASSERT(que);
+
+    if (prio >= RT_PRIO_QUEUE_PRIO_MAX)
+        return -RT_ERROR;
+
+    item = rt_mp_alloc(&que->pool, timeout);
+    if (item == RT_NULL)
+        return -RT_ENOMEM;
+
+    rt_memcpy(item+1, data, que->item_sz);
+    item->next = RT_NULL;
+
+    level = rt_hw_interrupt_disable();
+
+    _do_push(que, prio, item);
+
+    if (!rt_list_isempty(&(que->suspended_pop_list)))
+    {
+        rt_thread_t thread;
+
+        /* get thread entry */
+        thread = rt_list_entry(que->suspended_pop_list.next,
+                               struct rt_thread,
+                               tlist);
+        /* resume it */
+        rt_thread_resume(thread);
+        rt_hw_interrupt_enable(level);
+
+        /* perform a schedule */
+        rt_schedule();
+
+        return RT_EOK;
+    }
+
+    rt_hw_interrupt_enable(level);
+
+    return RT_EOK;
+}
+
+rt_err_t rt_prio_queue_pop(struct rt_prio_queue *que,
+                           void *data,
+                           rt_int32_t timeout)
+{
+    rt_ubase_t level;
+    struct rt_prio_queue_item *item;
+
+    RT_ASSERT(que);
+    RT_ASSERT(data);
+
+    level = rt_hw_interrupt_disable();
+    for (item = _do_pop(que);
+         item == RT_NULL;
+         item = _do_pop(que))
+    {
+        rt_thread_t thread;
+
+        if (timeout == 0)
+        {
+            rt_hw_interrupt_enable(level);
+            return -RT_ETIMEOUT;
+        }
+
+        RT_DEBUG_NOT_IN_INTERRUPT;
+
+        thread = rt_thread_self();
+        thread->error = RT_EOK;
+        rt_thread_suspend(thread);
+
+        rt_list_insert_before(&(que->suspended_pop_list), &(thread->tlist));
+
+        if (timeout > 0)
+        {
+            rt_timer_control(&(thread->thread_timer),
+                             RT_TIMER_CTRL_SET_TIME,
+                             &timeout);
+            rt_timer_start(&(thread->thread_timer));
+        }
+
+        rt_hw_interrupt_enable(level);
+
+        rt_schedule();
+
+        /* thread is waked up */
+        if (thread->error != RT_EOK)
+            return thread->error;
+        level = rt_hw_interrupt_disable();
+    }
+
+    rt_hw_interrupt_enable(level);
+
+    rt_memcpy(data, item+1, que->item_sz);
+    rt_mp_free(item);
+
+    return RT_EOK;
+}
+
+void rt_prio_queue_dump(struct rt_prio_queue *que)
+{
+    int level = 0;
+
+    rt_kprintf("bitmap: %08x\n", que->bitmap);
+    for (level = 0; level < RT_PRIO_QUEUE_PRIO_MAX; level++)
+    {
+        struct rt_prio_queue_item *item;
+
+        rt_kprintf("%2d: ", level);
+        for (item = que->head[level];
+             item;
+             item = item->next)
+        {
+            rt_kprintf("%p, ", item);
+        }
+        rt_kprintf("\n");
+    }
+}
+

+ 44 - 0
components/vbus/prio_queue.h

@@ -0,0 +1,44 @@
+#ifndef __PRIO_QUEUE_H__
+#define __PRIO_QUEUE_H__
+
+#include <rtthread.h>
+
+#define RT_PRIO_QUEUE_PRIO_MAX  32
+
+struct rt_prio_queue_item;
+
+struct rt_prio_queue {
+    rt_uint32_t bitmap;
+    struct rt_prio_queue_item *head[RT_PRIO_QUEUE_PRIO_MAX];
+    struct rt_prio_queue_item *tail[RT_PRIO_QUEUE_PRIO_MAX];
+    /* push thread suspend on the mempool, not queue */
+    rt_list_t suspended_pop_list;
+    rt_size_t item_sz;
+
+    struct rt_mempool pool;
+};
+
+rt_err_t rt_prio_queue_init(struct rt_prio_queue *que,
+                            const char *name,
+                            void *buf,
+                            rt_size_t bufsz,
+                            rt_size_t itemsz);
+void rt_prio_queue_detach(struct rt_prio_queue *que);
+
+rt_err_t rt_prio_queue_push(struct rt_prio_queue *que,
+                            rt_uint8_t prio,
+                            void *data,
+                            rt_int32_t timeout);
+rt_err_t rt_prio_queue_pop(struct rt_prio_queue *que,
+                           void *data,
+                           rt_int32_t timeout);
+#ifdef RT_USING_HEAP
+struct rt_prio_queue* rt_prio_queue_create(const char *name,
+                                           rt_size_t item_nr,
+                                           rt_size_t item_sz);
+void rt_prio_queue_delete(struct rt_prio_queue *que);
+#endif
+
+void rt_prio_queue_dump(struct rt_prio_queue *que);
+
+#endif /* end of include guard: __PRIO_QUEUE_H__ */

+ 70 - 0
components/vbus/rt_watermark_queue.c

@@ -0,0 +1,70 @@
+/*
+ * Water Gauge
+ *
+ * COPYRIGHT (C) 2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2014-04-16     Grissiom     first version
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#include "rt_watermark_queue.h"
+
+void rt_wm_que_set_mark(struct rt_watermark_queue *wg,
+                             unsigned int low, unsigned int high)
+{
+    RT_ASSERT(low <= high);
+
+    wg->high_mark = high;
+    wg->low_mark = low;
+}
+
+void rt_wm_que_init(struct rt_watermark_queue *wg,
+                         unsigned int low, unsigned int high)
+{
+    rt_wm_que_set_mark(wg, low, high);
+    rt_list_init(&wg->suspended_threads);
+    wg->level = 0;
+}
+
+void rt_wm_que_dump(struct rt_watermark_queue *wg)
+{
+    struct rt_list_node *node;
+
+    rt_kprintf("wg %p: low: %d, high: %d, cur: %d\n",
+               wg, wg->low_mark, wg->high_mark, wg->level);
+    rt_kprintf("thread suspend:");
+    for (node = wg->suspended_threads.next;
+         node != &wg->suspended_threads;
+         node = node->next)
+    {
+        rt_thread_t thread;
+
+        thread = rt_list_entry(wg->suspended_threads.next,
+                               struct rt_thread,
+                               tlist);
+        rt_kprintf(" %.*s", RT_NAME_MAX, thread->name);
+    }
+    rt_kprintf("\n");
+}

+ 148 - 0
components/vbus/rt_watermark_queue.h

@@ -0,0 +1,148 @@
+/*
+ * Thread queue with water mark
+ *
+ * COPYRIGHT (C) 2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2014-04-16     Grissiom     first version
+ */
+
+struct rt_watermark_queue
+{
+    /* Current water level. */
+    unsigned int level;
+    unsigned int high_mark;
+    unsigned int low_mark;
+    rt_list_t suspended_threads;
+};
+
+/** Init the struct rt_watermark_queue.
+ */
+void rt_wm_que_init(struct rt_watermark_queue *wg,
+                    unsigned int low, unsigned int high);
+void rt_wm_que_set_mark(struct rt_watermark_queue *wg,
+                        unsigned int low, unsigned int high);
+void rt_wm_que_dump(struct rt_watermark_queue *wg);
+
+/* Water marks are often used in performance critical places. Benchmark shows
+ * inlining functions will have 10% performance gain in some situation(for
+ * example, VBus). So keep the inc/dec compact and inline. */
+
+/** Increase the water level.
+ *
+ * It should be called in the thread that want to raise the water level. If the
+ * current level is above the high mark, the thread will be suspended up to
+ * @timeout ticks.
+ *
+ * @return RT_EOK if water level increased successfully. -RT_EFULL on @timeout
+ * is zero and the level is above water mark. -RT_ETIMEOUT if timeout occurred.
+ */
+rt_inline rt_err_t rt_wm_que_inc(struct rt_watermark_queue *wg,
+                                 int timeout)
+{
+    rt_base_t ilvl;
+
+    /* Assert as early as possible. */
+    if (timeout != 0)
+    {
+        RT_DEBUG_IN_THREAD_CONTEXT;
+    }
+
+    ilvl = rt_hw_interrupt_disable();
+
+    while (wg->level > wg->high_mark)
+    {
+        rt_thread_t thread;
+
+        if (timeout == 0)
+        {
+            rt_hw_interrupt_enable(ilvl);
+            return -RT_EFULL;
+        }
+
+        thread = rt_thread_self();
+        thread->error = RT_EOK;
+        rt_thread_suspend(thread);
+        rt_list_insert_after(&wg->suspended_threads, &thread->tlist);
+        if (timeout > 0)
+        {
+            rt_timer_control(&(thread->thread_timer),
+                             RT_TIMER_CTRL_SET_TIME,
+                             &timeout);
+            rt_timer_start(&(thread->thread_timer));
+        }
+        rt_hw_interrupt_enable(ilvl);
+        rt_schedule();
+        if (thread->error != RT_EOK)
+            return thread->error;
+
+        ilvl = rt_hw_interrupt_disable();
+    }
+
+    wg->level++;
+
+    if (wg->level == 0)
+    {
+        wg->level = ~0;
+    }
+
+    rt_hw_interrupt_enable(ilvl);
+
+    return RT_EOK;
+}
+
+/** Decrease the water level.
+ *
+ * It should be called by the consumer that drain the water out. If the water
+ * level reached low mark, all the thread suspended in this queue will be waken
+ * up. It's safe to call this function in interrupt context.
+ */
+rt_inline void rt_wm_que_dec(struct rt_watermark_queue *wg)
+{
+    int need_sched = 0;
+    rt_base_t ilvl;
+
+    if (wg->level == 0)
+        return;
+
+    ilvl = rt_hw_interrupt_disable();
+    wg->level--;
+    if (wg->level == wg->low_mark)
+    {
+        /* There should be spaces between the low mark and high mark, so it's
+         * safe to resume all the threads. */
+        while (!rt_list_isempty(&wg->suspended_threads))
+        {
+            rt_thread_t thread;
+
+            thread = rt_list_entry(wg->suspended_threads.next,
+                                   struct rt_thread,
+                                   tlist);
+            rt_thread_resume(thread);
+            need_sched = 1;
+        }
+    }
+    rt_hw_interrupt_enable(ilvl);
+
+    if (need_sched)
+        rt_schedule();
+}

+ 79 - 0
components/vbus/share_hdr/vbus_api.h

@@ -0,0 +1,79 @@
+#ifndef __VBUS_API_H__
+#define __VBUS_API_H__
+
+#include "vbus_conf.h"
+
+#define RT_VBUS_CHANNEL_NR   32
+
+#define RT_VBUS_BLK_HEAD_SZ  4
+#define RT_VBUS_MAX_PKT_SZ   (256 - RT_VBUS_BLK_HEAD_SZ)
+
+#ifndef __ASSEMBLY__
+#include <stddef.h> /* For size_t */
+
+struct rt_vbus_blk
+{
+    unsigned char id;
+    unsigned char qos;
+    unsigned char len;
+    unsigned char reserved;
+    unsigned char data[60];
+} __attribute__((packed));
+
+struct rt_vbus_ring
+{
+    volatile size_t put_idx;
+    volatile size_t get_idx;
+    /* whether the writer is blocked on this ring. For RTT, it means the
+     * central writer thread is waiting. For Linux, it means there are some
+     * threads waiting for space to write.
+     *
+     * Note that we don't record whether there are reading thread blocked. When
+     * there is new data, the other side will always be waked up. */
+    volatile unsigned int blocked;
+    struct rt_vbus_blk blks[RT_VMM_RB_BLK_NR];
+};
+
+enum
+{
+    RT_VBUS_CHN0_CMD_ENABLE,
+    RT_VBUS_CHN0_CMD_DISABLE,
+    RT_VBUS_CHN0_CMD_SET,
+    RT_VBUS_CHN0_CMD_ACK,
+    RT_VBUS_CHN0_CMD_NAK,
+    /* If the recieving side reached high water mark. It has the right to
+     * suspend the channel. All the server/client should know about this
+     * command but the one that does not implement flow control could ignore
+     * this command. */
+    RT_VBUS_CHN0_CMD_SUSPEND,
+    RT_VBUS_CHN0_CMD_RESUME,
+    RT_VBUS_CHN0_CMD_MAX,
+};
+
+enum rt_vbus_chn_status
+{
+    /* initial state, available for reuse */
+    RT_VBUS_CHN_ST_AVAILABLE,
+    /* ACK DISABLE send(CS) or received(CS), but not ready for reuse.(the
+     * channel is not closed by this end) */
+    RT_VBUS_CHN_ST_CLOSED,
+    /* ENABLE send(client) or received(server) */
+    RT_VBUS_CHN_ST_ESTABLISHING,
+    /* ACK SET send(C) or received(S) */
+    RT_VBUS_CHN_ST_ESTABLISHED,
+    /* Channel suspended by flow control. */
+    RT_VBUS_CHN_ST_SUSPEND,
+    /* DISABLE received(CS) */
+    RT_VBUS_CHN_ST_CLOSING,
+};
+#endif
+
+#undef BUILD_ASSERT
+/* borrowed from http://lxr.linux.no/linux+v2.6.26.5/include/linux/kernel.h#L494 */
+#define BUILD_ASSERT(condition) ((void)sizeof(char[1 - 2*!(condition)]))
+
+/* max length of a channel name, including the \0 */
+#define RT_VBUS_CHN_NAME_MAX   16
+
+#endif /* end of include guard: __VBUS_API_H__ */
+

+ 1370 - 0
components/vbus/vbus.c

@@ -0,0 +1,1370 @@
+/*
+ * VMM Bus
+ *
+ * COPYRIGHT (C) 2013-2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2013-11-04     Grissiom     add comment
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include "vbus.h"
+#include "prio_queue.h"
+#include "vbus_hw.h"
+
+//#define RT_VBUS_STATISTICS
+
+#define RT_VBUS_RB_LOW_TICK   (RT_VMM_RB_BLK_NR * 2 / 3)
+#define RT_VBUS_RB_TICK_STEP  (100)
+
+#ifndef RT_USING_LOGTRACE
+/* console could be run on vbus. If we log on it, there will be oops. */
+#define vbus_debug(...)
+#define vbus_verbose(...)
+#define vbus_info(...)
+#define vbus_error(...)
+#else // have RT_USING_LOGTRACE
+#include <log_trace.h>
+
+#if defined(log_session_lvl)
+/* Define log_trace_session as const so the compiler could optimize some log
+ * out. */
+const static struct log_trace_session _lgs = {
+    .id  = {.name = "vbus"},
+    .lvl = LOG_TRACE_LEVEL_VERBOSE,
+};
+
+#define vbus_debug(fmt, ...)   log_session_lvl(&_lgs, LOG_TRACE_LEVEL_DEBUG,   fmt, ##__VA_ARGS__)
+#define vbus_verbose(fmt, ...) log_session_lvl(&_lgs, LOG_TRACE_LEVEL_VERBOSE, fmt, ##__VA_ARGS__)
+#define vbus_info(fmt, ...)    log_session_lvl(&_lgs, LOG_TRACE_LEVEL_INFO,    fmt, ##__VA_ARGS__)
+#define vbus_error(fmt, ...)    log_session_lvl(&_lgs, LOG_TRACE_LEVEL_ERROR,    fmt, ##__VA_ARGS__)
+#else
+static struct log_trace_session _lgs = {
+    .id  = {.name = "vbus"},
+    .lvl = LOG_TRACE_LEVEL_VERBOSE,
+};
+#define vbus_debug(fmt, ...)   log_session(&_lgs, LOG_TRACE_DEBUG""fmt, ##__VA_ARGS__)
+#define vbus_verbose(fmt, ...) log_session(&_lgs, LOG_TRACE_VERBOSE""fmt, ##__VA_ARGS__)
+#define vbus_info(fmt, ...)    log_session(&_lgs, LOG_TRACE_INFO""fmt, ##__VA_ARGS__)
+#define vbus_error(fmt, ...)    log_session(&_lgs, LOG_TRACE_ERROR""fmt, ##__VA_ARGS__)
+#endif
+#endif // RT_USING_LOGTRACE
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(ar)     (sizeof(ar)/sizeof(ar[0]))
+#endif
+
+struct rt_vbus_ring *RT_VBUS_OUT_RING;
+struct rt_vbus_ring *RT_VBUS_IN_RING;
+
+const char *rt_vbus_chn_st2str[] = {
+    "available",
+    "closed",
+    "establishing",
+    "established",
+    "suspended",
+    "closing",
+};
+
+const char *rt_vbus_sess_st2str[] = {
+    "available",
+    "listening",
+    "establishing",
+};
+
+const char *rt_vbus_cmd2str[] = {
+    "ENABLE",
+    "DISABLE",
+    "SET",
+    "ACK",
+    "NAK",
+    "SUSPEND",
+    "RESUME",
+};
+
+static char* dump_cmd_pkt(unsigned char *dp, size_t dsize);
+
+/* 4 bytes for the head */
+#define LEN2BNR(len)    ((len + RT_VBUS_BLK_HEAD_SZ \
+                          + sizeof(struct rt_vbus_blk) - 1) \
+                         / sizeof(struct rt_vbus_blk))
+
+rt_inline void _ring_add_get_bnr(struct rt_vbus_ring *ring,
+                                 rt_size_t bnr)
+{
+    int nidx = ring->get_idx + bnr;
+
+    if (nidx >= RT_VMM_RB_BLK_NR)
+    {
+        nidx -= RT_VMM_RB_BLK_NR;
+    }
+    rt_vbus_smp_wmb();
+    ring->get_idx = nidx;
+}
+
+rt_inline int _bus_ring_space_nr(struct rt_vbus_ring *rg)
+{
+    int delta;
+
+    rt_vbus_smp_rmb();
+    delta = rg->get_idx - rg->put_idx;
+
+    if (delta > 0)
+    {
+        /* Put is behind the get. */
+        return delta - 1;
+    }
+    else
+    {
+        /* delta is negative. */
+        return RT_VMM_RB_BLK_NR + delta - 1;
+    }
+}
+
+struct rt_vbus_pkg {
+    rt_uint8_t id;
+    rt_uint8_t prio;
+    rt_uint8_t finished;
+    rt_uint8_t len;
+    const void *data;
+};
+
+/* chn0 is always connected */
+static enum rt_vbus_chn_status _chn_status[RT_VBUS_CHANNEL_NR];
+
+rt_inline int _chn_connected(unsigned char chnr)
+{
+    return _chn_status[chnr] == RT_VBUS_CHN_ST_ESTABLISHED ||
+           _chn_status[chnr] == RT_VBUS_CHN_ST_SUSPEND;
+}
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+#include <rt_watermark_queue.h>
+struct rt_watermark_queue _chn_wm_que[RT_VBUS_CHANNEL_NR];
+void rt_vbus_set_post_wm(unsigned char chnr, unsigned int low, unsigned int high)
+{
+    RT_ASSERT((0 < chnr) && (chnr < ARRAY_SIZE(_chn_wm_que)));
+    rt_wm_que_set_mark(&_chn_wm_que[chnr], low, high);
+}
+
+/* Threads suspended by the flow control of other side. */
+rt_list_t _chn_suspended_threads[RT_VBUS_CHANNEL_NR];
+
+struct
+{
+    unsigned int level;
+    unsigned int high_mark;
+    unsigned int low_mark;
+    /* The suspend command does not have ACK. So if the other side still
+     * sending pkg after SUSPEND, warn it again. Also use it as a flag that
+     * tell me whether are we dropping from the high mark or not when reaching
+     * the low mark. */
+    unsigned int last_warn;
+} _chn_recv_wm[RT_VBUS_CHANNEL_NR];
+
+void rt_vbus_set_recv_wm(unsigned char chnr, unsigned int low, unsigned int high)
+{
+    RT_ASSERT((0 < chnr) && (chnr < ARRAY_SIZE(_chn_recv_wm)));
+    _chn_recv_wm[chnr].low_mark = low;
+    _chn_recv_wm[chnr].high_mark = high;
+}
+#else
+void rt_vbus_set_recv_wm(unsigned char chnr, unsigned int low, unsigned int high)
+{}
+void rt_vbus_set_post_wm(unsigned char chnr, unsigned int low, unsigned int high)
+{}
+#endif
+
+struct {
+    rt_vbus_event_listener indicate;
+    void *ctx;
+} _vbus_rx_indi[RT_VBUS_EVENT_ID_MAX][RT_VBUS_CHANNEL_NR];
+
+void rt_vbus_register_listener(unsigned char chnr,
+                               enum rt_vbus_event_id eve,
+                               rt_vbus_event_listener indi,
+                               void *ctx)
+{
+    RT_ASSERT(chnr != 0 && chnr < RT_VBUS_CHANNEL_NR);
+    RT_ASSERT(eve < sizeof(_vbus_rx_indi)/sizeof(_vbus_rx_indi[0]));
+
+    _vbus_rx_indi[eve][chnr].indicate = indi;
+    _vbus_rx_indi[eve][chnr].ctx = ctx;
+}
+
+static void _vbus_indicate(enum rt_vbus_event_id eve, unsigned char chnr)
+{
+    RT_ASSERT(eve < sizeof(_vbus_rx_indi)/sizeof(_vbus_rx_indi[0]));
+
+    if (_vbus_rx_indi[eve][chnr].indicate)
+        _vbus_rx_indi[eve][chnr].indicate(_vbus_rx_indi[eve][chnr].ctx);
+}
+
+#define _BUS_OUT_THRD_STACK_SZ  2048
+#define _BUS_OUT_THRD_PRIO      8
+#define _BUS_OUT_PKG_NR         RT_VMM_RB_BLK_NR
+
+static struct rt_thread _bus_out_thread;
+static rt_uint8_t _bus_out_thread_stack[_BUS_OUT_THRD_STACK_SZ];
+struct rt_prio_queue *_bus_out_que;
+
+static void _bus_out_entry(void *param)
+{
+    struct rt_vbus_pkg dpkg;
+
+    _bus_out_que = rt_prio_queue_create("vbus",
+                                        _BUS_OUT_PKG_NR,
+                                        sizeof(struct rt_vbus_pkg));
+
+    if (!_bus_out_que)
+    {
+        rt_kprintf("could not create vmm bus queue\n");
+        return;
+    }
+
+    while (rt_prio_queue_pop(_bus_out_que, &dpkg,
+                             RT_WAITING_FOREVER) == RT_EOK)
+    {
+        int sp;
+        rt_uint32_t nxtidx;
+        const int dnr = LEN2BNR(dpkg.len);
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+        rt_wm_que_dec(&_chn_wm_que[dpkg.id]);
+#endif
+
+        if (!_chn_connected(dpkg.id))
+            continue;
+
+        sp = _bus_ring_space_nr(RT_VBUS_OUT_RING);
+
+        vbus_debug("vmm bus out"
+                   "(data: %p, len: %d, prio: %d, id: %d)\n",
+                   dpkg.data, dpkg.len, dpkg.prio, dpkg.id);
+
+        /* wait for enough space */
+        while (sp < dnr)
+        {
+            rt_ubase_t lvl = rt_hw_interrupt_disable();
+
+            RT_VBUS_OUT_RING->blocked = 1;
+            rt_vbus_smp_wmb();
+
+            /* kick the guest, hoping this could force it do the work */
+            rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
+
+            rt_thread_suspend(rt_thread_self());
+            rt_schedule();
+
+            RT_VBUS_OUT_RING->blocked = 0;
+
+            rt_hw_interrupt_enable(lvl);
+
+            sp = _bus_ring_space_nr(RT_VBUS_OUT_RING);
+        }
+
+        nxtidx = RT_VBUS_OUT_RING->put_idx + dnr;
+
+        RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].id  = dpkg.id;
+        RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].qos = dpkg.prio;
+        RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].len = dpkg.len;
+
+        if (nxtidx >= RT_VMM_RB_BLK_NR)
+        {
+            unsigned int tailsz;
+
+            tailsz = (RT_VMM_RB_BLK_NR - RT_VBUS_OUT_RING->put_idx)
+                * sizeof(RT_VBUS_OUT_RING->blks[0]) - RT_VBUS_BLK_HEAD_SZ;
+
+            /* the remaining block is sufficient for the data */
+            if (tailsz > dpkg.len)
+                tailsz = dpkg.len;
+
+            rt_memcpy(&RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].data,
+                      dpkg.data, tailsz);
+            rt_memcpy(&RT_VBUS_OUT_RING->blks[0],
+                      ((char*)dpkg.data)+tailsz,
+                      dpkg.len - tailsz);
+
+            rt_vbus_smp_wmb();
+            RT_VBUS_OUT_RING->put_idx = nxtidx - RT_VMM_RB_BLK_NR;
+        }
+        else
+        {
+            rt_memcpy(&RT_VBUS_OUT_RING->blks[RT_VBUS_OUT_RING->put_idx].data,
+                      dpkg.data, dpkg.len);
+
+            rt_vbus_smp_wmb();
+            RT_VBUS_OUT_RING->put_idx = nxtidx;
+        }
+
+        rt_vbus_smp_wmb();
+        rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
+
+        if (dpkg.finished)
+        {
+            _vbus_indicate(RT_VBUS_EVENT_ID_TX, dpkg.id);
+        }
+    }
+    RT_ASSERT(0);
+}
+
+void rt_vbus_resume_out_thread(void)
+{
+    rt_thread_resume(&_bus_out_thread);
+    rt_schedule();
+}
+
+rt_err_t rt_vbus_post(rt_uint8_t id,
+                      rt_uint8_t prio,
+                      const void *data,
+                      rt_size_t size,
+                      rt_int32_t timeout)
+{
+    rt_err_t err = RT_EOK;
+    struct rt_vbus_pkg pkg;
+    unsigned int putsz;
+    const unsigned char *dp;
+
+    if (!_bus_out_que)
+    {
+        rt_kprintf("post (data: %p, size: %d, timeout: %d) "
+                   "to bus before initialition\n",
+                   data, size, timeout);
+        return -RT_ERROR;
+    }
+
+    if (id >= RT_VBUS_CHANNEL_NR)
+        return -RT_ERROR;
+
+    if (timeout != 0)
+    {
+        RT_DEBUG_IN_THREAD_CONTEXT;
+    }
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    while (_chn_status[id] == RT_VBUS_CHN_ST_SUSPEND)
+    {
+        rt_thread_t thread;
+
+        if (timeout == 0)
+        {
+            return -RT_EFULL;
+        }
+
+        thread = rt_thread_self();
+        thread->error = RT_EOK;
+        /* We only touch the _chn_suspended_threads in thread, so lock the
+         * scheduler is enough. */
+        rt_enter_critical();
+        rt_thread_suspend(thread);
+
+        rt_list_insert_after(&_chn_suspended_threads[id], &thread->tlist);
+        if (timeout > 0)
+        {
+            rt_timer_control(&(thread->thread_timer),
+                             RT_TIMER_CTRL_SET_TIME,
+                             &timeout);
+            rt_timer_start(&(thread->thread_timer));
+        }
+        /* rt_exit_critical will do schedule on need. */
+        rt_exit_critical();
+
+        if (thread->error != RT_EOK)
+            return thread->error;
+    }
+#endif
+
+    if (_chn_status[id] != RT_VBUS_CHN_ST_ESTABLISHED)
+        return -RT_ERROR;
+
+    dp       = data;
+    pkg.id   = id;
+    pkg.prio = prio;
+    for (putsz = 0; size; size -= putsz)
+    {
+        pkg.data = dp;
+
+        if (size > RT_VBUS_MAX_PKT_SZ)
+        {
+            putsz = RT_VBUS_MAX_PKT_SZ;
+            pkg.finished = 0;
+        }
+        else
+        {
+            putsz = size;
+            pkg.finished = 1;
+        }
+
+        pkg.len = putsz;
+        dp += putsz;
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+        err = rt_wm_que_inc(&_chn_wm_que[id], timeout);
+        if (err != RT_EOK)
+            break;
+#endif
+
+        vbus_debug("post (data: %p(%d), size: %d, finshed: %d, timeout: %d)\n",
+                   pkg.data, ((unsigned char*)pkg.data)[0],
+                   pkg.len, pkg.finished, timeout);
+
+        err = rt_prio_queue_push(_bus_out_que, prio, &pkg, timeout);
+        if (err != RT_EOK)
+            break;
+    }
+
+    return err;
+}
+
+struct rt_completion _chn0_post_cmp;
+
+void _chn0_tx_listener(void *p)
+{
+    rt_completion_done(&_chn0_post_cmp);
+}
+
+/* Posts in channel0 should be sync. */
+static rt_err_t _chn0_post(const void *data,
+                               rt_size_t size,
+                               int timeout)
+{
+    rt_err_t err;
+
+    rt_completion_init(&_chn0_post_cmp);
+    err = rt_vbus_post(0, 0, data, size, timeout);
+    if (err != RT_EOK)
+        return err;
+    return rt_completion_wait(&_chn0_post_cmp, timeout);
+}
+
+#define _BUS_IN_THRD_STACK_SZ  1024
+#define _BUS_IN_THRD_PRIO      (_BUS_OUT_THRD_PRIO+1)
+#if (_BUS_IN_THRD_PRIO == RT_THREAD_PRIORITY_MAX)
+#error "_BUS_OUT_THRD_PRIO too low"
+#endif
+
+static struct rt_thread _bus_in_thread;
+static rt_uint8_t _bus_in_thread_stack[_BUS_OUT_THRD_STACK_SZ];
+static struct rt_semaphore _bus_in_sem;
+static struct rt_event     _bus_in_event;
+/* {head, tail} */
+#define _IN_ACT_HEAD 0
+#define _IN_ACT_TAIL 1
+static struct rt_vbus_data *_bus_in_action[RT_VBUS_CHANNEL_NR][2];
+#ifdef RT_VBUS_STATISTICS
+static unsigned int _bus_in_action_nr[RT_VBUS_CHANNEL_NR];
+#endif
+
+static void rt_vbus_notify_chn(unsigned char chnr, rt_err_t err)
+{
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    /* TODO: get rid of this */
+    /* Protect the list. */
+    rt_enter_critical();
+    while (!rt_list_isempty(&_chn_suspended_threads[chnr]))
+    {
+        rt_thread_t thread;
+
+        thread = rt_list_entry(_chn_suspended_threads[chnr].next,
+                               struct rt_thread,
+                               tlist);
+        thread->error = err;
+        rt_thread_resume(thread);
+    }
+    rt_exit_critical();
+#endif
+    rt_event_send(&_bus_in_event, 1 << chnr);
+}
+
+static void rt_vbus_notify_set(rt_uint32_t set)
+{
+    rt_event_send(&_bus_in_event, set);
+}
+
+rt_err_t rt_vbus_listen_on(rt_uint8_t chnr,
+                           rt_int32_t timeout)
+{
+    rt_uint32_t notuse;
+
+    if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR || !_chn_connected(chnr))
+        return -RT_EIO;
+
+    return rt_event_recv(&_bus_in_event, 1 << chnr,
+                         RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR,
+                         timeout, &notuse);
+}
+
+void rt_vbus_data_push(unsigned int id, struct rt_vbus_data *act)
+{
+    rt_ubase_t lvl;
+
+    RT_ASSERT(0 < id && id < RT_VBUS_CHANNEL_NR);
+
+    lvl = rt_hw_interrupt_disable();
+
+    if (_bus_in_action[id][_IN_ACT_HEAD] == RT_NULL)
+    {
+        _bus_in_action[id][_IN_ACT_HEAD] = act;
+        _bus_in_action[id][_IN_ACT_TAIL] = act;
+    }
+    else
+    {
+        _bus_in_action[id][_IN_ACT_TAIL]->next = act;
+        _bus_in_action[id][_IN_ACT_TAIL] = act;
+    }
+
+#ifdef RT_VBUS_STATISTICS
+    _bus_in_action_nr[id]++;
+#endif
+
+    rt_hw_interrupt_enable(lvl);
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    _chn_recv_wm[id].level++;
+    if (_chn_recv_wm[id].level == 0)
+        _chn_recv_wm[id].level = ~0;
+    if (_chn_recv_wm[id].level > _chn_recv_wm[id].high_mark &&
+        _chn_recv_wm[id].level > _chn_recv_wm[id].last_warn)
+    {
+        unsigned char buf[2];
+
+        buf[0] = RT_VBUS_CHN0_CMD_SUSPEND;
+        buf[1] = id;
+        vbus_debug("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
+        _chn0_post(buf, sizeof(buf), RT_WAITING_FOREVER);
+        /* Warn the other side in 100 more pkgs. */
+        _chn_recv_wm[id].last_warn = _chn_recv_wm[id].level + 100;
+    }
+#endif
+}
+
+struct rt_vbus_data* rt_vbus_data_pop(unsigned int id)
+{
+    struct rt_vbus_data *act;
+    rt_ubase_t lvl;
+
+    RT_ASSERT(0 < id && id < RT_VBUS_CHANNEL_NR);
+
+    lvl = rt_hw_interrupt_disable();
+
+    act = _bus_in_action[id][_IN_ACT_HEAD];
+    if (act)
+    {
+        _bus_in_action[id][_IN_ACT_HEAD] = act->next;
+    }
+
+    rt_hw_interrupt_enable(lvl);
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    if (_chn_recv_wm[id].level != 0)
+    {
+        _chn_recv_wm[id].level--;
+        if (_chn_recv_wm[id].level <= _chn_recv_wm[id].low_mark &&
+            _chn_recv_wm[id].last_warn > _chn_recv_wm[id].low_mark)
+        {
+            unsigned char buf[2];
+
+            buf[0] = RT_VBUS_CHN0_CMD_RESUME;
+            buf[1] = id;
+            vbus_debug("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
+            _chn0_post(buf, sizeof(buf), RT_WAITING_FOREVER);
+            _chn_recv_wm[id].last_warn = 0;
+        }
+    }
+#endif
+    return act;
+}
+
+/* dump cmd that is not start with ACK/NAK */
+static size_t __dump_naked_cmd(char *dst, size_t lsize,
+                               unsigned char *dp, size_t dsize)
+{
+    size_t len;
+    if (dp[0] == RT_VBUS_CHN0_CMD_DISABLE ||
+        dp[0] == RT_VBUS_CHN0_CMD_SUSPEND ||
+        dp[0] == RT_VBUS_CHN0_CMD_RESUME)
+    {
+        len = rt_snprintf(dst, lsize, "%s %d",
+                          rt_vbus_cmd2str[dp[0]], dp[1]);
+    }
+    else if (dp[0] == RT_VBUS_CHN0_CMD_ENABLE)
+    {
+        len = rt_snprintf(dst, lsize, "%s %s",
+                          rt_vbus_cmd2str[dp[0]], dp+1);
+    }
+    else if (dp[0] < RT_VBUS_CHN0_CMD_MAX)
+    {
+        len = rt_snprintf(dst, lsize, "%s %s %d",
+                          rt_vbus_cmd2str[dp[0]],
+                          dp+1, dp[2+rt_strlen((char*)dp+1)]);
+    }
+    else
+    {
+        len = rt_snprintf(dst, lsize, "(invalid)%d %d",
+                          dp[0], dp[1]);
+    }
+    return len;
+}
+
+static char _cmd_dump_buf[64];
+static char* dump_cmd_pkt(unsigned char *dp, size_t dsize)
+{
+    size_t len;
+
+    if (dp[0] == RT_VBUS_CHN0_CMD_ACK || dp[0] == RT_VBUS_CHN0_CMD_NAK )
+    {
+        len = rt_snprintf(_cmd_dump_buf, sizeof(_cmd_dump_buf),
+                          "%s ", rt_vbus_cmd2str[dp[0]]);
+        len += __dump_naked_cmd(_cmd_dump_buf+len, sizeof(_cmd_dump_buf)-len,
+                                dp+1, dsize-1);
+    }
+    else
+    {
+        len = __dump_naked_cmd(_cmd_dump_buf, sizeof(_cmd_dump_buf),
+                               dp, dsize);
+    }
+
+    if (len > sizeof(_cmd_dump_buf) - 1)
+        len = sizeof(_cmd_dump_buf) - 1;
+
+    _cmd_dump_buf[len] = '\0';
+    return _cmd_dump_buf;
+}
+
+static rt_err_t _chn0_echo_with(rt_uint8_t prefix,
+                                rt_uint32_t dsize,
+                                unsigned char *dp)
+{
+    rt_err_t err;
+    unsigned char *resp;
+
+    resp = rt_malloc(dsize+1);
+    if (!resp)
+        return -RT_ENOMEM;
+    *resp = prefix;
+    rt_memcpy(resp+1, dp, dsize);
+    vbus_verbose("%s --> remote\n", dump_cmd_pkt(resp, dsize+1));
+
+    err = _chn0_post(resp, dsize+1, RT_WAITING_FOREVER);
+
+    rt_free(resp);
+
+    return err;
+}
+
+static rt_err_t _chn0_nak(rt_uint32_t dsize, unsigned char *dp)
+{
+    return _chn0_echo_with(RT_VBUS_CHN0_CMD_NAK, dsize, dp);
+}
+
+static rt_err_t _chn0_ack(rt_uint32_t dsize, unsigned char *dp)
+{
+    return _chn0_echo_with(RT_VBUS_CHN0_CMD_ACK, dsize, dp);
+}
+
+enum _vbus_session_st
+{
+    SESSIOM_AVAILABLE,
+    SESSIOM_LISTENING,
+    SESSIOM_ESTABLISHING,
+};
+
+struct rt_vbus_conn_session
+{
+    /* negative value means error */
+    int chnr;
+    enum _vbus_session_st st;
+    struct rt_completion cmp;
+    struct rt_vbus_request *req;
+};
+
+static struct rt_vbus_conn_session _sess[RT_VBUS_CHANNEL_NR/2];
+
+static int _sess_find(const unsigned char *name,
+                      enum _vbus_session_st st)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(_sess); i++)
+    {
+        if (_sess[i].st == st && _sess[i].req->name &&
+            rt_strcmp(_sess[i].req->name, (char*)name) == 0)
+            break;
+    }
+    return i;
+}
+
+static int _chn0_actor(unsigned char *dp, size_t dsize)
+{
+    if (*dp != RT_VBUS_CHN0_CMD_SUSPEND && *dp != RT_VBUS_CHN0_CMD_RESUME)
+        vbus_verbose("local <-- %s\n", dump_cmd_pkt(dp, dsize));
+
+    switch (*dp)
+    {
+    case RT_VBUS_CHN0_CMD_ENABLE:
+        {
+            int i, chnr;
+            rt_err_t err;
+            unsigned char *resp;
+
+            i = _sess_find(dp+1, SESSIOM_LISTENING);
+            if (i == ARRAY_SIZE(_sess))
+            {
+                _chn0_nak(dsize, dp);
+                break;
+            }
+
+            for (chnr = 0; chnr < ARRAY_SIZE(_chn_status); chnr++)
+            {
+                if (_chn_status[chnr] == RT_VBUS_CHN_ST_AVAILABLE)
+                    break;
+            }
+            if (chnr == ARRAY_SIZE(_chn_status))
+            {
+                _chn0_nak(dsize, dp);
+                break;
+            }
+
+            resp = rt_malloc(dsize + 1);
+            if (!resp)
+                break;
+
+            *resp = RT_VBUS_CHN0_CMD_SET;
+            rt_memcpy(resp+1, dp+1, dsize-1);
+            resp[dsize] = chnr;
+
+            rt_vbus_set_recv_wm(chnr, _sess[i].req->recv_wm.low, _sess[i].req->recv_wm.high);
+            rt_vbus_set_post_wm(chnr, _sess[i].req->post_wm.low, _sess[i].req->post_wm.high);
+
+            vbus_verbose("%s --> remote\n", dump_cmd_pkt(resp, dsize+1));
+            err = _chn0_post(resp, dsize+1, RT_WAITING_FOREVER);
+
+            if (err == RT_EOK)
+            {
+                _sess[i].st   = SESSIOM_ESTABLISHING;
+                vbus_debug("set sess %d st: %s\n", i,
+                           rt_vbus_sess_st2str[_sess[i].st]);
+                _sess[i].chnr = chnr;
+                _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHING;
+            }
+            rt_free(resp);
+        }
+        break;
+    case RT_VBUS_CHN0_CMD_SET:
+        {
+            int i, chnr;
+
+            i = _sess_find(dp+1, SESSIOM_ESTABLISHING);
+            if (i == ARRAY_SIZE(_sess))
+            {
+                vbus_verbose("drop spurious packet\n");
+                break;
+            }
+
+            chnr = dp[1+rt_strlen((const char*)dp+1)+1];
+
+            if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
+            {
+                vbus_verbose("SET wrong chnr %d\n", chnr);
+                break;
+            }
+            if (_chn_status[chnr] != RT_VBUS_CHN_ST_AVAILABLE)
+            {
+                _chn0_nak(dsize, dp);
+                vbus_verbose("SET wrong chnr status %d, %s\n",
+                             chnr, rt_vbus_chn_st2str[_chn_status[chnr]]);
+                break;
+            }
+
+            rt_vbus_set_recv_wm(chnr, _sess[i].req->recv_wm.low, _sess[i].req->recv_wm.high);
+            rt_vbus_set_post_wm(chnr, _sess[i].req->post_wm.low, _sess[i].req->post_wm.high);
+
+            if (_chn0_ack(dsize, dp) >= 0)
+            {
+                _sess[i].chnr = chnr;
+                _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
+                vbus_debug("chn %d %s\n", chnr,
+                           rt_vbus_chn_st2str[_chn_status[chnr]]);
+                rt_completion_done(&_sess[i].cmp);
+            }
+        }
+        break;
+    case RT_VBUS_CHN0_CMD_ACK:
+        if (dp[1] == RT_VBUS_CHN0_CMD_SET)
+        {
+            int i, chnr;
+
+            i = _sess_find(dp+2, SESSIOM_ESTABLISHING);
+            if (i == ARRAY_SIZE(_sess))
+                /* drop that spurious packet */
+                break;
+
+            chnr = dp[1+rt_strlen((const char*)dp+2)+2];
+
+            _sess[i].chnr = chnr;
+            _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
+            vbus_debug("chn %d %s\n", chnr,
+                       rt_vbus_chn_st2str[_chn_status[chnr]]);
+            rt_completion_done(&_sess[i].cmp);
+        }
+        else if (dp[1] == RT_VBUS_CHN0_CMD_DISABLE)
+        {
+            unsigned char chnr = dp[2];
+
+            if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
+                break;
+
+            /* We could only get here by sending DISABLE command, which is
+             * initiated by the rt_vbus_close_chn. */
+            _chn_status[chnr] = RT_VBUS_CHN_ST_AVAILABLE;
+
+            _vbus_indicate(RT_VBUS_EVENT_ID_DISCONN, chnr);
+            /* notify the thread that the channel has been closed */
+            rt_vbus_notify_chn(chnr, -RT_ERROR);
+        }
+        else
+        {
+            vbus_info("invalid ACK for %d\n", dp[1]);
+        }
+        break;
+    case RT_VBUS_CHN0_CMD_DISABLE:
+        {
+            unsigned char chnr = dp[1];
+
+            if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
+                break;
+
+            _chn_status[chnr] = RT_VBUS_CHN_ST_CLOSING;
+
+            _chn0_ack(dsize, dp);
+
+            _vbus_indicate(RT_VBUS_EVENT_ID_DISCONN, chnr);
+            /* notify the thread that the channel has been closed */
+            rt_vbus_notify_chn(chnr, -RT_ERROR);
+        }
+        break;
+    case RT_VBUS_CHN0_CMD_SUSPEND:
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+        {
+            unsigned char chnr = dp[1];
+
+            if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
+                break;
+
+            if (_chn_status[chnr] != RT_VBUS_CHN_ST_ESTABLISHED)
+                break;
+
+            _chn_status[chnr] = RT_VBUS_CHN_ST_SUSPEND;
+        }
+#endif
+        break;
+    case RT_VBUS_CHN0_CMD_RESUME:
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+        {
+            unsigned char chnr = dp[1];
+
+            if (chnr == 0 || chnr >= RT_VBUS_CHANNEL_NR)
+                break;
+
+            if (_chn_status[chnr] != RT_VBUS_CHN_ST_SUSPEND)
+                break;
+
+            _chn_status[chnr] = RT_VBUS_CHN_ST_ESTABLISHED;
+
+            /* Protect the list. */
+            rt_enter_critical();
+            while (!rt_list_isempty(&_chn_suspended_threads[chnr]))
+            {
+                rt_thread_t thread;
+
+                thread = rt_list_entry(_chn_suspended_threads[chnr].next,
+                                       struct rt_thread,
+                                       tlist);
+                rt_thread_resume(thread);
+            }
+            rt_exit_critical();
+        }
+#endif
+        break;
+    case RT_VBUS_CHN0_CMD_NAK:
+        if (dp[1] == RT_VBUS_CHN0_CMD_ENABLE)
+        {
+            int i;
+
+            i = _sess_find(dp+2, SESSIOM_ESTABLISHING);
+            if (i == ARRAY_SIZE(_sess))
+                /* drop that spurious packet */
+                break;
+
+            _sess[i].chnr = -RT_EIO;
+            rt_completion_done(&_sess[i].cmp);
+        }
+        else if (dp[1] == RT_VBUS_CHN0_CMD_SET)
+        {
+            vbus_info("NAK for %d not implemented\n", dp[1]);
+        }
+        else
+        {
+            vbus_info("invalid NAK for %d\n", dp[1]);
+        }
+        break;
+    default:
+        /* just ignore the invalid cmd */
+        vbus_info("drop unknown cmd %d on chn0\n", *dp);
+        break;
+    };
+
+    return RT_EOK;
+}
+
+int rt_vbus_request_chn(struct rt_vbus_request *req,
+                        int timeout)
+{
+    int i, chnr, err;
+	size_t plen = rt_strlen(req->name) + 2;
+	unsigned char *pbuf;
+    rt_ubase_t lvl;
+
+    lvl = rt_hw_interrupt_disable();
+    for (i = 0; i < ARRAY_SIZE(_sess); i++)
+    {
+        if (_sess[i].st == SESSIOM_AVAILABLE)
+            break;
+    }
+    if (i == ARRAY_SIZE(_sess))
+    {
+        rt_hw_interrupt_enable(lvl);
+        return -RT_ERROR;
+    }
+
+    rt_completion_init(&_sess[i].cmp);
+    _sess[i].req = req;
+
+    if (req->is_server)
+    {
+        _sess[i].st = SESSIOM_LISTENING;
+        rt_hw_interrupt_enable(lvl);
+
+        vbus_debug("request listening %s on %d\n", req->name, i);
+
+        /* always wait on the condition */
+        err = RT_EOK;
+        goto _waitforcmp;
+    }
+
+	pbuf = rt_malloc(plen);
+	if (!pbuf)
+    {
+        rt_hw_interrupt_enable(lvl);
+        return -RT_ENOMEM;
+    }
+
+    _sess[i].st = SESSIOM_ESTABLISHING;
+    rt_hw_interrupt_enable(lvl);
+
+    pbuf[0] = RT_VBUS_CHN0_CMD_ENABLE;
+    rt_memcpy(pbuf+1, req->name, plen-1);
+    vbus_verbose("%s --> remote\n", dump_cmd_pkt(pbuf, plen));
+
+	err = _chn0_post(pbuf, plen, RT_WAITING_FOREVER);
+    rt_free(pbuf);
+
+_waitforcmp:
+    if (err == RT_EOK)
+        err = rt_completion_wait(&_sess[i].cmp, timeout);
+
+    vbus_debug("request wait cmp done %d, chnr %d\n", err, _sess[i].chnr);
+
+    if (err)
+    {
+        /* cleanup the mass when the wait is time out but we have done some job
+         */
+        if (_sess[i].st == SESSIOM_ESTABLISHING)
+            _chn_status[_sess[i].chnr] = RT_VBUS_CHN_ST_AVAILABLE;
+        chnr = err;
+        goto Out;
+    }
+
+    RT_ASSERT(_sess[i].chnr != 0);
+
+    chnr = _sess[i].chnr;
+
+Out:
+    /* detach the sess as we finished the job */
+    _sess[i].st = SESSIOM_AVAILABLE;
+    _sess[i].req = RT_NULL;
+
+    return chnr;
+}
+
+void rt_vbus_close_chn(unsigned char chnr)
+{
+    void *p;
+    rt_err_t err;
+    unsigned char buf[2];
+
+    buf[0] = RT_VBUS_CHN0_CMD_DISABLE;
+    buf[1] = chnr;
+
+    RT_ASSERT(0 < chnr && chnr < RT_VBUS_CHANNEL_NR);
+
+    if (_chn_status[chnr] == RT_VBUS_CHN_ST_CLOSED ||
+        _chn_status[chnr] == RT_VBUS_CHN_ST_CLOSING)
+    {
+        _chn_status[chnr] = RT_VBUS_CHN_ST_AVAILABLE;
+        return;
+    }
+
+    if (!_chn_connected(chnr))
+        return;
+
+    _chn_status[chnr] = RT_VBUS_CHN_ST_CLOSING;
+    vbus_info("%s --> remote\n", dump_cmd_pkt(buf, sizeof(buf)));
+    err = _chn0_post(&buf, sizeof(buf), RT_WAITING_FOREVER);
+    if (err == RT_EOK)
+        /* wait for the ack */
+        rt_vbus_listen_on(chnr, 10 * RT_TICK_PER_SECOND);
+
+    /* cleanup the remaining data */
+    for (p = rt_vbus_data_pop(chnr); p; p = rt_vbus_data_pop(chnr))
+        rt_free(p);
+    /* FIXME: there is a chance that there are some data left on the send
+     * buffer. So if we connect other channel with the same number immediately,
+     * the new channel will receive some garbage data. However, this is highly
+     * un-probable. */
+}
+
+#ifdef RT_VBUS_STATISTICS
+static unsigned int _total_data_sz;
+#endif
+
+static void _bus_in_entry(void *param)
+{
+    rt_sem_init(&_bus_in_sem, "vbus", 0, RT_IPC_FLAG_FIFO);
+    rt_event_init(&_bus_in_event, "vbus", RT_IPC_FLAG_FIFO);
+    rt_memset(_bus_in_action, 0, sizeof(_bus_in_action));
+
+    while (rt_sem_take(&_bus_in_sem,
+                       RT_WAITING_FOREVER) == RT_EOK)
+    {
+        rt_uint32_t event_set = 0;
+
+        /* while(not empty) */
+        while (RT_VBUS_IN_RING->get_idx != RT_VBUS_IN_RING->put_idx)
+        {
+            unsigned int id, nxtidx;
+            rt_size_t size;
+            struct rt_vbus_data *act;
+
+            rt_vbus_smp_rmb();
+            size = RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].len;
+            id = RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].id;
+
+            vbus_debug("vmm bus in: chnr %d, size %d\n", id, size);
+
+            /* Suspended channel can still recv data. */
+            if (id > RT_VBUS_CHANNEL_NR || !_chn_connected(id))
+            {
+                vbus_error("drop on invalid chn %d\n", id);
+                /* drop the invalid packet */
+                _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
+                continue;
+            }
+
+            if (id == 0)
+            {
+                if (size > 60)
+                    vbus_error("too big(%d) packet on chn0\n", size);
+                else
+                    _chn0_actor(RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, size);
+                _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
+                continue;
+            }
+
+#ifdef RT_VBUS_STATISTICS
+            _total_data_sz += size;
+#endif
+
+            act = rt_malloc(sizeof(*act) + size);
+            if (act == RT_NULL)
+            {
+                //vbus_error("drop on OOM (%d, %d)\n", id, size);
+                /* drop the packet on malloc fall */
+                _ring_add_get_bnr(RT_VBUS_IN_RING, LEN2BNR(size));
+                continue;
+            }
+
+            act->size = size;
+            act->next = RT_NULL;
+
+            nxtidx = RT_VBUS_IN_RING->get_idx + LEN2BNR(size);
+            if (nxtidx >= RT_VMM_RB_BLK_NR)
+            {
+                unsigned int tailsz;
+
+                tailsz = (RT_VMM_RB_BLK_NR - RT_VBUS_IN_RING->get_idx)
+                          * sizeof(RT_VBUS_IN_RING->blks[0]) - RT_VBUS_BLK_HEAD_SZ;
+
+                /* the remaining block is sufficient for the data */
+                if (tailsz > size)
+                    tailsz = size;
+
+                rt_memcpy(act+1, &RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, tailsz);
+                rt_memcpy((char*)(act+1) + tailsz, &RT_VBUS_IN_RING->blks[0], size - tailsz);
+
+                /* It shall make sure the CPU has finished reading the item
+                 * before it writes the new tail pointer, which will erase the
+                 * item. */
+                rt_vbus_smp_wmb();
+                RT_VBUS_IN_RING->get_idx = nxtidx - RT_VMM_RB_BLK_NR;
+            }
+            else
+            {
+                rt_memcpy(act+1, &RT_VBUS_IN_RING->blks[RT_VBUS_IN_RING->get_idx].data, size);
+
+                rt_vbus_smp_wmb();
+                RT_VBUS_IN_RING->get_idx = nxtidx;
+            }
+
+            rt_vbus_data_push(id, act);
+            _vbus_indicate(RT_VBUS_EVENT_ID_RX, id);
+            event_set |= 1 << id;
+
+            if (RT_VBUS_IN_RING->blocked)
+                rt_vbus_tick(0, RT_VBUS_GUEST_VIRQ);
+        }
+
+        if (event_set != 0)
+            rt_vbus_notify_set(event_set);
+    }
+    RT_ASSERT(0);
+}
+
+void rt_vbus_isr(int irqnr, void *param)
+{
+    if (RT_VBUS_OUT_RING->blocked)
+        rt_vbus_resume_out_thread();
+
+    rt_sem_release(&_bus_in_sem);
+    rt_vbus_hw_eoi(irqnr, param);
+}
+
+int rt_vbus_init(void *outr, void *inr)
+{
+    int i;
+
+#ifdef RT_USING_LOGTRACE
+    log_trace_register_session(&_lgs);
+#endif
+
+    if (outr > inr)
+    {
+        RT_ASSERT((char*)outr - (char*)inr >= sizeof(struct rt_vbus_ring));
+    }
+    else
+    {
+        RT_ASSERT((char*)inr - (char*)outr >= sizeof(struct rt_vbus_ring));
+    }
+
+    RT_VBUS_OUT_RING = outr;
+    RT_VBUS_IN_RING  = inr;
+
+    rt_memset(RT_VBUS_OUT_RING, 0, sizeof(*RT_VBUS_OUT_RING));
+    rt_memset(RT_VBUS_IN_RING,  0, sizeof(*RT_VBUS_IN_RING));
+    _chn_status[0] = RT_VBUS_CHN_ST_ESTABLISHED;
+    for (i = 1; i < ARRAY_SIZE(_chn_status); i++)
+    {
+        _chn_status[i] = RT_VBUS_CHN_ST_AVAILABLE;
+    }
+    for (i = 0; i < ARRAY_SIZE(_sess); i++)
+    {
+        _sess[i].req = RT_NULL;
+        _sess[i].st  = SESSIOM_AVAILABLE;
+    }
+    _vbus_rx_indi[RT_VBUS_EVENT_ID_TX][0].indicate = _chn0_tx_listener;
+    _vbus_rx_indi[RT_VBUS_EVENT_ID_TX][0].ctx = RT_NULL;
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    for (i = 0; i < ARRAY_SIZE(_chn_wm_que); i++)
+    {
+        rt_wm_que_init(&_chn_wm_que[i],
+                       RT_VMM_RB_BLK_NR / 3,
+                       RT_VMM_RB_BLK_NR * 2 / 3);
+    }
+    /* Channel 0 has the full channel. */
+    rt_wm_que_set_mark(&_chn_wm_que[0], 0, ~0);
+
+    for (i = 0; i < ARRAY_SIZE(_chn_suspended_threads); i++)
+    {
+        rt_list_init(&_chn_suspended_threads[i]);
+    }
+
+    for (i = 1; i < ARRAY_SIZE(_chn_recv_wm); i++)
+    {
+        rt_vbus_set_recv_wm(i,
+                            RT_VMM_RB_BLK_NR / 3,
+                            RT_VMM_RB_BLK_NR * 2 / 3);
+        _chn_recv_wm[i].level = 0;
+        _chn_recv_wm[i].last_warn = 0;
+    }
+    /* Channel 0 has the full channel. Don't suspend it. */
+    _chn_recv_wm[0].low_mark = 0;
+    _chn_recv_wm[0].high_mark = ~0;
+    _chn_recv_wm[0].level = 0;
+    _chn_recv_wm[0].last_warn = 0;
+#endif
+
+    rt_thread_init(&_bus_out_thread, "vbusout",
+                   _bus_out_entry, RT_NULL,
+                   _bus_out_thread_stack, sizeof(_bus_out_thread_stack),
+                   _BUS_OUT_THRD_PRIO, 20);
+    rt_thread_startup(&_bus_out_thread);
+
+    rt_thread_init(&_bus_in_thread, "vbusin",
+                   _bus_in_entry, RT_NULL,
+                   _bus_in_thread_stack, sizeof(_bus_in_thread_stack),
+                   _BUS_IN_THRD_PRIO, 20);
+
+
+    rt_thread_startup(&_bus_in_thread);
+
+    rt_vbus_hw_init();
+
+    rt_kprintf("VBus loaded: %d out blocks, %d in blocks\n",
+               RT_VMM_RB_BLK_NR, RT_VMM_RB_BLK_NR);
+
+    rt_vbus_chnx_init();
+
+    return 0;
+}
+
+void rt_vbus_rb_dump(void)
+{
+    rt_kprintf("OUT ring:(%s blocked)\n", RT_VBUS_OUT_RING->blocked ? "is" : "not");
+    rt_kprintf("put idx: %8x, get idx: %8x\n",
+               RT_VBUS_OUT_RING->put_idx, RT_VBUS_OUT_RING->get_idx);
+    rt_kprintf("space: %d\n", _bus_ring_space_nr(RT_VBUS_OUT_RING));
+
+
+    rt_kprintf("IN ring:(%s blocked)\n", RT_VBUS_IN_RING->blocked ? "is" : "not");
+    rt_kprintf("put idx: %8x, get idx: %8x\n",
+               RT_VBUS_IN_RING->put_idx, RT_VBUS_IN_RING->get_idx);
+    rt_kprintf("space: %d\n", _bus_ring_space_nr(RT_VBUS_IN_RING));
+}
+
+void rt_vbus_chn_dump(void)
+{
+    int i;
+    rt_kprintf("vbus channel status:\n");
+    for (i = 0; i < ARRAY_SIZE(_chn_status); i++)
+    {
+        rt_kprintf("%2d:%s\n", i, rt_vbus_chn_st2str[_chn_status[i]]);
+    }
+}
+
+void rt_vbus_sess_dump(void)
+{
+    int i;
+
+    rt_kprintf("vbus conn session:\n");
+    for (i = 0; i < ARRAY_SIZE(_sess); i++)
+    {
+        rt_kprintf("%2d(%s):%s\n", i, _sess[i].req ? _sess[i].req->name : "",
+                   rt_vbus_sess_st2str[_sess[i].st]);
+    }
+}
+
+void rt_vbus_que_dump(void)
+{
+    rt_kprintf("out que:\n");
+    rt_prio_queue_dump(_bus_out_que);
+}
+
+unsigned int rt_vbus_total_data_sz(void)
+{
+#ifdef RT_VBUS_STATISTICS
+    return _total_data_sz;
+#else
+    return (unsigned int)-1;
+#endif
+}
+
+void rt_vbus_data_pkt_dump(void)
+{
+    int i;
+
+    for (i = 0; i < ARRAY_SIZE(_bus_in_action); i++)
+    {
+        struct rt_vbus_data *dp;
+
+#ifdef RT_VBUS_STATISTICS
+        rt_kprintf("%2d %4d: ", i, _bus_in_action_nr[i]);
+#else
+        rt_kprintf("%2d: ", i);
+#endif
+        for (dp = _bus_in_action[i][_IN_ACT_HEAD];
+             dp;
+             dp = dp->next)
+        {
+            rt_kprintf("%p(%d) -> ", dp, dp->size);
+        }
+        rt_kprintf(" nil\n");
+    }
+}
+
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+void rt_vbus_chm_wm_dump(void)
+{
+    int i;
+
+    rt_kprintf("post wm:\n");
+    for (i = 0; i < ARRAY_SIZE(_chn_wm_que); i++)
+        rt_wm_que_dump(&_chn_wm_que[i]);
+
+    rt_kprintf("recv wm:\n");
+    rt_kprintf("     low,     high,      cur,  last warn\n");
+    for (i = 0; i < ARRAY_SIZE(_chn_recv_wm); i++)
+    {
+        rt_kprintf("%8x, %8x, %8x, %8x\n",
+                   _chn_recv_wm[i].low_mark, _chn_recv_wm[i].high_mark,
+                   _chn_recv_wm[i].level, _chn_recv_wm[i].last_warn);
+    }
+}
+#endif
+
+#ifdef RT_USING_FINSH
+#include <finsh.h>
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_rb_dump,    vbrb, dump vbus ringbuffer status);
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_chn_dump,  vbchn, dump vbus channel status);
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_sess_dump, vbses, dump vbus session status);
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_que_dump,  vbque, dump vbus out queue status);
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_total_data_sz,  vbtsz, total in data);
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_data_pkt_dump,  vbdq, dump the data queue);
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+FINSH_FUNCTION_EXPORT_ALIAS(rt_vbus_chm_wm_dump, vbwm, dump vbus water mark status);
+#endif
+#endif
+

+ 195 - 0
components/vbus/vbus.h

@@ -0,0 +1,195 @@
+#ifndef __VBUS_H__
+#define __VBUS_H__
+/*
+ * VBus
+ *
+ * COPYRIGHT (C) 2013-2014, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2014-06-09     Grissiom     version 2.0.2; add comment
+ */
+
+#include "vbus_local_conf.h"
+#include <vbus_api.h>
+
+int rt_vbus_init(void *outr, void *inr);
+
+void rt_vbus_resume_out_thread(void);
+
+/** Post data on channel.
+ *
+ * @param chnr the channel number
+ * @param prio the priority of the data
+ * @param datap pointer to the actual data
+ * @param size number of byte of the data
+ * @param timeout the value used in the blocking API
+ *
+ * Note: rt_vbus_post is an asynchronous function that when it returns, the
+ * @datap and @size is recorded in the post queue at least but there is no
+ * guarantee that the data is copied into the ring buffer. To avoid data
+ * corruption, you need to wait on the RT_VBUS_EVENT_ID_TX event.
+ *
+ * However, if you just post static data such as static string, there is no
+ * need to wait.
+ *
+ * @sa rt_vbus_register_listener .
+ */
+rt_err_t rt_vbus_post(rt_uint8_t chnr,
+                      rt_uint8_t prio,
+                      const void *datap,
+                      rt_size_t size,
+                      rt_int32_t timeout);
+
+struct rt_vbus_data {
+    /* Number of bytes in current data package. */
+    unsigned char size;
+    /* Used internally in VBus. Don't modify this field as it may corrupt the
+     * receive queue. */
+    struct rt_vbus_data *next;
+    /* Data follows the struct */
+};
+
+struct rt_vbus_wm_cfg {
+	unsigned int low, high;
+};
+
+struct rt_vbus_request {
+	unsigned char prio;
+	const char *name;
+	int is_server;
+	struct rt_vbus_wm_cfg recv_wm, post_wm;
+};
+
+/** Request a channel.
+ *
+ * @return channel number. Negative if error happened.
+ */
+int rt_vbus_request_chn(struct rt_vbus_request *req, int timeout);
+
+/** Close channel @chnr */
+void rt_vbus_close_chn(unsigned char chnr);
+
+/** Set the water mark level for posting into the channel @chnr. */
+void rt_vbus_set_post_wm(unsigned char chnr, unsigned int low, unsigned int high);
+/** Set the water mark level for receiving from the channel @chnr. */
+void rt_vbus_set_recv_wm(unsigned char chnr, unsigned int low, unsigned int high);
+
+typedef void (*rt_vbus_event_listener)(void *ctx);
+
+enum rt_vbus_event_id {
+    /* On a packet received in channel. */
+    RT_VBUS_EVENT_ID_RX,
+    /* On the data of rt_vbus_post has been written to the ring buffer. */
+    RT_VBUS_EVENT_ID_TX,
+    /* On the channel has been closed. */
+    RT_VBUS_EVENT_ID_DISCONN,
+    RT_VBUS_EVENT_ID_MAX,
+};
+
+/** Register callback @indi on the event @eve on the @chnr.
+ *
+ * @ctx will passed to @indi on calling the @indi.
+ */
+void rt_vbus_register_listener(unsigned char chnr,
+                               enum rt_vbus_event_id eve,
+                               rt_vbus_event_listener indi,
+                               void *ctx);
+
+/** Listen on any events happen on the @chnr for @timeout ticks.
+ *
+ * This function blocks until events occur or timeout happened.
+ */
+rt_err_t rt_vbus_listen_on(rt_uint8_t chnr,
+                           rt_int32_t timeout);
+
+/** Push a data package into the receive queue of the channel @chnr. */
+void rt_vbus_data_push(unsigned int chnr,
+                       struct rt_vbus_data *data);
+/** Pop a data package from the receive queue of the channel @chnr.
+ *
+ * The actual data is following the struct rt_vbus_data. After using it, it
+ * should be freed by rt_free.
+ */
+struct rt_vbus_data* rt_vbus_data_pop(unsigned int chnr);
+
+struct rt_vbus_dev
+{
+    /* Runtime infomations. */
+    rt_uint8_t chnr;
+    struct rt_vbus_data *act;
+    rt_size_t pos;
+
+    /* There will be a request for each channel. So no need to seperate them so
+     * clearly. */
+    struct rt_vbus_request req;
+};
+
+rt_err_t rt_vbus_chnx_init(void);
+/** Get the corresponding channel number from the VBus device @dev. */
+rt_uint8_t rt_vbus_get_chnnr(rt_device_t dev);
+/** Register a call back on the other side disconnect the channel.
+ *
+ * @sa rt_vbus_register_listener .
+ */
+void rt_vbus_chnx_register_disconn(rt_device_t dev,
+                                   rt_vbus_event_listener indi,
+                                   void *ctx);
+
+/* Commands for the device control interface. */
+#define VBUS_IOCRECV_WM      0xD1
+#define VBUS_IOCPOST_WM      0xD2
+/** Configure event listener */
+#define VBUS_IOC_LISCFG      0xD3
+
+struct rt_vbus_dev_liscfg
+{
+    enum rt_vbus_event_id event;
+    rt_vbus_event_listener listener;
+    void *ctx;
+};
+
+int rt_vbus_shell_start(void);
+#ifdef RT_USING_VBUS_RFS
+int dfs_rfs_init(void);
+#endif
+
+/** VBus hardware init function.
+ *
+ * BSP should implement this function to initialize the interrupts etc.
+ */
+int rt_vbus_hw_init(void);
+
+/** VBus ISR function.
+ *
+ * BSP should call this function when the interrupt from other core is
+ * triggered. @param is not used by VBus and will pass to rt_vbus_hw_eoi.
+ */
+void rt_vbus_isr(int irqnr, void *param);
+
+/** VBus End Of Interrupt function.
+ *
+ * This function will be called when VBus finished the ISR handling. BSP should
+ * define this function to clear the interrupt flag etc.
+ */
+int rt_vbus_hw_eoi(int irqnr, void *param);
+
+#endif /* end of include guard: __VBUS_H__ */

+ 286 - 0
components/vbus/vbus_chnx.c

@@ -0,0 +1,286 @@
+/*
+ * Channel on VMM Bus
+ *
+ * COPYRIGHT (C) 2013, Shanghai Real-Thread Technology Co., Ltd
+ *
+ *  This file is part of RT-Thread (http://www.rt-thread.org)
+ *
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2013-11-04     Grissiom     add comment
+ */
+
+#include <rthw.h>
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include "vbus.h"
+
+static void _rx_indicate(void *ctx)
+{
+    rt_device_t dev = ctx;
+
+    if (dev->rx_indicate)
+        dev->rx_indicate(dev, 0);
+}
+
+static void _tx_complete(void *ctx)
+{
+    rt_device_t dev = ctx;
+
+    if (dev->tx_complete)
+        dev->tx_complete(dev, 0);
+}
+
+static rt_err_t _open(rt_device_t dev, rt_uint16_t oflag)
+{
+    int chnr;
+    struct rt_vbus_dev *vdev = dev->user_data;
+
+    if (vdev->chnr)
+        return RT_EOK;
+
+    /* FIXME: request the same name for twice will crash */
+    chnr = rt_vbus_request_chn(&vdev->req, RT_WAITING_FOREVER);
+    if (chnr < 0)
+        return chnr;
+
+    vdev->chnr = chnr;
+    rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_RX, _rx_indicate, dev);
+    rt_vbus_register_listener(chnr, RT_VBUS_EVENT_ID_TX, _tx_complete, dev);
+
+    return RT_EOK;
+}
+
+static rt_err_t _close(rt_device_t dev)
+{
+    struct rt_vbus_dev *vdev = dev->user_data;
+
+    RT_ASSERT(vdev->chnr != 0);
+
+    rt_vbus_close_chn(vdev->chnr);
+    vdev->chnr = 0;
+
+    return RT_EOK;
+}
+
+static rt_size_t _read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+    rt_size_t outsz = 0;
+    struct rt_vbus_dev *vdev = dev->user_data;
+
+    RT_ASSERT(vdev->chnr != 0);
+
+    if (vdev->act == RT_NULL)
+    {
+        vdev->act = rt_vbus_data_pop(vdev->chnr);
+        vdev->pos = 0;
+    }
+
+    while (1)
+    {
+        rt_err_t err;
+
+        while (vdev->act)
+        {
+            rt_size_t cpysz;
+
+            if (size - outsz > vdev->act->size - vdev->pos)
+                cpysz = vdev->act->size - vdev->pos;
+            else
+                cpysz = size - outsz;
+
+            rt_memcpy((char*)buffer + outsz, ((char*)(vdev->act+1)) + vdev->pos, cpysz);
+            vdev->pos += cpysz;
+
+            outsz += cpysz;
+            if (outsz == size)
+            {
+                return outsz;
+            }
+            else if (outsz > size)
+                RT_ASSERT(0);
+
+            /* free old and get new */
+            rt_free(vdev->act);
+            vdev->act = rt_vbus_data_pop(vdev->chnr);
+            vdev->pos = 0;
+        }
+
+        /* TODO: We don't want to touch the rx_indicate here. But this lead to
+         * some duplication. Maybe we should find a better way to handle this.
+         */
+        if (rt_interrupt_get_nest() == 0)
+        {
+            err = rt_vbus_listen_on(vdev->chnr, RT_WAITING_FOREVER);
+        }
+        else
+        {
+            err = rt_vbus_listen_on(vdev->chnr, 0);
+        }
+        if (err != RT_EOK)
+        {
+            rt_set_errno(err);
+            return outsz;
+        }
+        vdev->act = rt_vbus_data_pop(vdev->chnr);
+        vdev->pos = 0;
+    }
+}
+
+static rt_size_t _write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+    rt_err_t err;
+    struct rt_vbus_dev *vdev = dev->user_data;
+
+    RT_ASSERT(vdev->chnr != 0);
+
+    if (rt_interrupt_get_nest() == 0)
+    {
+        /* Thread context. */
+        err = rt_vbus_post(vdev->chnr, vdev->req.prio,
+                           buffer, size, RT_WAITING_FOREVER);
+    }
+    else
+    {
+        /* Interrupt context. */
+        err = rt_vbus_post(vdev->chnr, vdev->req.prio,
+                           buffer, size, 0);
+    }
+
+    if (err)
+    {
+        rt_set_errno(err);
+        return 0;
+    }
+
+    return size;
+}
+
+rt_err_t  _control(rt_device_t dev, rt_uint8_t cmd, void *args)
+{
+    RT_ASSERT(dev);
+
+    switch (cmd) {
+    case VBUS_IOC_LISCFG: {
+        struct rt_vbus_dev *vdev = dev->user_data;
+        struct rt_vbus_dev_liscfg *liscfg = args;
+
+        RT_ASSERT(vdev->chnr != 0);
+        if (!liscfg)
+            return -RT_ERROR;
+
+        rt_vbus_register_listener(vdev->chnr, liscfg->event,
+                                  liscfg->listener, liscfg->ctx);
+        return RT_EOK;
+    }
+        break;
+#ifdef RT_VBUS_USING_FLOW_CONTROL
+    case VBUS_IOCRECV_WM: {
+        struct rt_vbus_dev *vdev = dev->user_data;
+        struct rt_vbus_wm_cfg *cfg;
+
+        RT_ASSERT(vdev->chnr != 0);
+
+        if (!args)
+            return -RT_ERROR;
+
+        cfg = (struct rt_vbus_wm_cfg*)args;
+        if (cfg->low > cfg->high)
+            return -RT_ERROR;
+
+        rt_vbus_set_recv_wm(vdev->chnr, cfg->low, cfg->high);
+        return RT_EOK;
+    }
+        break;
+    case VBUS_IOCPOST_WM: {
+        struct rt_vbus_dev *vdev = dev->user_data;
+        struct rt_vbus_wm_cfg *cfg;
+
+        RT_ASSERT(vdev->chnr != 0);
+
+        if (!args)
+            return -RT_ERROR;
+
+        cfg = (struct rt_vbus_wm_cfg*)args;
+        if (cfg->low > cfg->high)
+            return -RT_ERROR;
+
+        rt_vbus_set_post_wm(vdev->chnr, cfg->low, cfg->high);
+        return RT_EOK;
+    }
+        break;
+#endif
+    default:
+        break;
+    };
+
+    return -RT_ENOSYS;
+}
+
+rt_uint8_t rt_vbus_get_chnnr(rt_device_t dev)
+{
+    struct rt_vbus_dev *vdev;
+
+    RT_ASSERT(dev);
+
+    vdev = dev->user_data;
+
+    return vdev->chnr;
+}
+
+void rt_vbus_chnx_register_disconn(rt_device_t dev,
+                                   rt_vbus_event_listener indi,
+                                   void *ctx)
+{
+    struct rt_vbus_dev *vdev = dev->user_data;
+
+    RT_ASSERT(vdev->chnr != 0);
+
+    if (vdev)
+        rt_vbus_register_listener(vdev->chnr, RT_VBUS_EVENT_ID_DISCONN,
+                                  indi, ctx);
+}
+
+#define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
+
+extern struct rt_vbus_dev rt_vbus_chn_devx[];
+static struct rt_device _devx[32];
+
+rt_err_t rt_vbus_chnx_init(void)
+{
+    int i;
+    struct rt_vbus_dev *p;
+
+    for (i = 0,                   p = rt_vbus_chn_devx;
+         i < ARRAY_SIZE(_devx) && p->req.name;
+         i++,                     p++)
+    {
+        _devx[i].type      = RT_Device_Class_Char;
+        _devx[i].open      = _open;
+        _devx[i].close     = _close;
+        _devx[i].read      = _read;
+        _devx[i].write     = _write;
+        _devx[i].control   = _control;
+        _devx[i].user_data = p;
+        rt_device_register(&_devx[i], p->req.name, RT_DEVICE_FLAG_RDWR);
+    }
+
+    return RT_EOK;
+}