فهرست منبع

First version

ZYH 7 سال پیش
والد
کامیت
9d25837283
23فایلهای تغییر یافته به همراه3346 افزوده شده و 54 حذف شده
  1. 0 52
      .gitignore
  2. 76 2
      README.md
  3. 21 0
      SConscript
  4. 0 0
      docs/CSharp-api.md
  5. 0 0
      docs/CSharp-client.md
  6. 18 0
      docs/README.md
  7. 0 0
      docs/api.md
  8. 0 0
      docs/python-api.md
  9. 0 0
      docs/python-client.md
  10. 82 0
      docs/quick-start.md
  11. 0 0
      docs/service.md
  12. 6 0
      docs/version.md
  13. 887 0
      examples/rdbd_file.c
  14. 369 0
      examples/rdbd_shell.c
  15. 78 0
      inc/rdbd.h
  16. 107 0
      inc/rdbd_service.h
  17. 51 0
      inc/rdbd_service_manager.h
  18. 45 0
      inc/usb_rdbd.h
  19. 125 0
      src/rdbd.c
  20. 192 0
      src/rdbd_service.c
  21. 699 0
      src/rdbd_service_base.c
  22. 235 0
      src/rdbd_service_manager.c
  23. 355 0
      src/usb_rdbd.c

+ 0 - 52
.gitignore

@@ -1,52 +0,0 @@
-# Prerequisites
-*.d
-
-# Object files
-*.o
-*.ko
-*.obj
-*.elf
-
-# Linker output
-*.ilk
-*.map
-*.exp
-
-# Precompiled Headers
-*.gch
-*.pch
-
-# Libraries
-*.lib
-*.a
-*.la
-*.lo
-
-# Shared objects (inc. Windows DLLs)
-*.dll
-*.so
-*.so.*
-*.dylib
-
-# Executables
-*.exe
-*.out
-*.app
-*.i*86
-*.x86_64
-*.hex
-
-# Debug files
-*.dSYM/
-*.su
-*.idb
-*.pdb
-
-# Kernel Module Compile Results
-*.mod*
-*.cmd
-.tmp_versions/
-modules.order
-Module.symvers
-Mkfile.old
-dkms.conf

+ 76 - 2
README.md

@@ -1,2 +1,76 @@
-# rdb
-RT-Thread Debug Bridge
+# RT-Thread Debug Bridge
+
+## 1. 介绍
+
+*RT-Thread Debug Bridge* (以下简称*RDB*)是一个在 RT-Thread 上,基于 USB/TCP等可靠通信协议的远程调试桥。
+可用于PC与运行有RT-Thread的设备进行可靠通信的应用层协议框架
+
+### 1.1 目录结构
+
+| 名称 | 说明 |
+| ---- | ---- |
+| docs  | 文档目录 |
+| examples | 例子目录,并有相应的一些说明 |
+| inc  | 头文件目录 |
+| src  | 源代码目录 |
+
+### 1.2 许可证
+
+*RT-Thread Debug Bridge* 遵循 **GPLv2** 许可,详见 `LICENSE` 文件。
+
+### 1.3 依赖
+
+- 基本依赖USB Device,依赖DFS Posix libc
+- 文件传输依赖dstr
+- shell 依赖finsh组件
+- 需要有足够的FD数量
+
+## 2. 启用RDB
+
+### 2.1 开启USB Device(需要对应BSP中含有USB驱动)
+
+在ENV menuconfig中开启USB Device:
+
+```bash
+RT-Thread Components  --->
+    Device Drivers  --->
+        Using USB  --->
+            -*- Using USB device
+            (4096) usb thread stack size
+            (0x0483) USB Vendor ID
+            (0x0010) USB Product ID
+            [ ]   Enable composite device
+            Device type (Using custom class by register interface)  --->
+```
+
+其中 VID PID可以自行设置,但要求在Windows系统中从未枚举过。
+
+VID建议使用芯片厂商ID 避免一些麻烦 [VID列表](http://www.linux-usb.org/usb.ids)
+
+### 2.2 选中RDB-SRC 软件包
+
+使用 RDB 需要在 RT-Thread 的包管理器中选择它,具体路径如下:
+
+```bash
+RT-Thread online packages  --->
+    tools packages  --->
+        [*] rdb:RT-Thread Debug Bridge package for rt-thread  --->
+            --- rdb:RT-Thread Debug Bridge package for rt-thread
+            [*]   Enable example for rdb push/pull (NEW)
+            [ ]   Enable example for rdb shell (NEW)
+                  Version (latest)  --->
+```
+
+各项示例功能的开启与关闭按需配置。
+
+然后让 RT-Thread 的包管理器自动更新,或者使用 `pkgs --update` 命令更新包到 BSP 中。
+
+## 3. 注意事项
+
+在通信带宽满载的情况下同时使用多种功能会出现某一功能假死现象。在实时性要求较高的场所不建议使用大带宽功能,如文件传输。
+
+## 4. 其他参考文档
+
+- [快速上手指南](docs/quick-start.md)
+- [rdb服务开发文档](docs/service.md)
+- [更多文档](docs/README.md)

+ 21 - 0
SConscript

@@ -0,0 +1,21 @@
+Import('RTT_ROOT')
+Import('rtconfig')
+from building import *
+
+cwd = GetCurrentDir()
+src = ['src/rdbd.c', 'src/rdbd_service.c', 'src/rdbd_service_manager.c', 'src/rdbd_service_base.c']
+
+if GetDepend(['PKGS_USING_USB_RDBD']):
+    src += ['src/usb_rdbd.c']
+
+if GetDepend(['PKGS_USING_RDBD_FILE']):
+    src += ['examples/rdbd_file.c']
+
+if GetDepend(['PKGS_USING_RDBD_SHELL']):
+    src += ['examples/rdbd_shell.c']
+
+CPPPATH = [cwd + '/inc']
+
+group = DefineGroup('rdb', src, depend = ['PKG_USING_RDB'], CPPPATH = CPPPATH)
+
+Return('group')

+ 0 - 0
docs/CSharp-api.md


+ 0 - 0
docs/CSharp-client.md


+ 18 - 0
docs/README.md

@@ -0,0 +1,18 @@
+# 文档
+
+## 软件包地址
+
+- https://github.com/RT-Thread-packages/rdb
+
+## 文档列表
+
+|文件名                             |描述|
+|:-----                             |:----|
+|[version.md](version.md)           |版本信息|
+|[quick-start.md](quick-start.md)   |快速上手指南|
+|[api.md](api.md)                   |API文档|
+|[service.md](service.md)           |rdb 服务开发文档|
+|[python-client.md](python-client.md)|python上位机开发文档|
+|[python-api.md](python-api.md)     |python上位机API文档|
+|[CSharp-client.md](CSharp-client.md)|CSharp上位机开发文档|
+|[CSharp-api.md](CSharp-api.md)     |CSharp上位机API文档|

+ 0 - 0
docs/api.md


+ 0 - 0
docs/python-api.md


+ 0 - 0
docs/python-client.md


+ 82 - 0
docs/quick-start.md

@@ -0,0 +1,82 @@
+# 快速上手指南
+
+## 1. sample1 - rdb shell
+
+### 1.1 简介
+
+rdb shell是一个用于finsh/msh通信的rdb服务
+
+### 1.2 RT-Thread端
+
+#### 1.2.1 开启rdb shell
+
+在env中配置开启rdb并启用rdb shell的示例
+
+```bash
+RT-Thread online packages  --->
+    tools packages  --->
+        [*] rdb:RT-Thread Debug Bridge package for rt-thread  --->
+            --- rdb:RT-Thread Debug Bridge package for rt-thread
+            [ ]   Enable example for rdb push/pull (NEW)
+            [*]   Enable example for rdb shell (NEW)
+                  Version (latest)  --->
+```
+
+#### 1.2.2 编译并下载工程
+
+由于bsp情况复杂 此处不再多做具体bsp的编译烧录说明
+
+### 1.3 通过USB连接PC与设备
+
+### 1.4 PC端
+
+#### 1.4.1 请确认PC环境
+
+- Windows7 SP1 或更高版本
+- 已安装.NET Framework 4.6.1 或更高版本
+- 若为Windows 7 请安装对应驱动 驱动中的设备路径需要修改,请参阅驱动下的readme.md文件
+- Windows 8 或更高版本的系统不需要手动安装驱动,若无法正常识别,请确认硬件问题或考虑安装MSDN版本的Windows系统
+
+#### 1.4.3 启动shell控制台
+
+在env 中执行
+
+```bash
+rdb list
+rdb connect 0
+rdb shell
+```
+
+此时rdb会启动一个telent的putty客户端 并自动连接上设备
+
+其中 rdb connect 0中的0为设备序号 当PC端接入了多个rdb设备时 通过设备号区分.
+
+在rdb connect成功之后,不需要运行多次 connect命令
+
+#### 1.4.4 杀死 rdb服务
+
+当rdb服务异常时 使用如下命令杀死 rdb服务
+
+```bash
+rdb kill
+```
+
+## 2 sample2 - rdb push/pull
+
+rdb push/pull 是一个用于文件传输的rdb服务
+
+使用流程与rdb shell相同此处不再赘述
+
+### 2.1 相关命令如下
+
+```bash
+rdb push LOCALPATH REMOTEPATH
+rdb pull REMOTEPATH [LOCALPATH]
+```
+
+LOCALPATH 为本地路径 可以为目录也可以为文件
+REMOTEPATH 为设备端路径 可以为目录也可以为文件
+
+### 2.2 注意事项
+
+当传输大文件时会占满带宽,造成其他服务假死。

+ 0 - 0
docs/service.md


+ 6 - 0
docs/version.md

@@ -0,0 +1,6 @@
+# 版本和修订 #
+
+| Date       | Version   |  Author    | Note  |
+| --------   | :-----:   | :----      | :---- |
+| 2018-04-28 | v1.0.0      | ZYH     | 初始版本 |
+|            |           |            | |

+ 887 - 0
examples/rdbd_file.c

@@ -0,0 +1,887 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rdbd.h>
+#include "rtdevice.h"
+#include <stdio.h>
+#include <string.h>
+//#include <dirent.h>
+#include <dstr.h>
+#include <rdbd_service.h>
+#include <rdbd_service_manager.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <libc.h>
+#include <dfs_select.h>
+#include <dfs_posix.h>
+//select dfs
+//select libc
+//select dstr v0.2.0
+#define RDBD_FILE_HASH_FNV_SEED           0x811C9DC5
+
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "RDBD FILE"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#define RDBD_FILE_CTRL_NEW_FILE         0x40
+#define RDBD_FILE_CTRL_WRITE_FILE       0x41
+#define RDBD_FILE_CTRL_WRITE_DONE       0x42
+#define RDBD_FILE_CTRL_LIST_DIR         0x43
+#define RDBD_FILE_CTRL_READ_FILE        0x44
+#define RDBD_FILE_CTRL_RM_FILE          0x45
+#define RDBD_FTLE_CTRL_GET_HASH         0x46
+#define RDBD_FTLE_CTRL_DIR_SYNC_START   0x47
+#define RDBD_FTLE_CTRL_DIR_SYNC_STOP    0x48
+#define RDBD_FTLE_CTRL_CHECK_PATH       0x49
+
+#define RDBD_FTLE_CTRL_PATH_IS_DIR       0x01
+#define RDBD_FTLE_CTRL_PATH_IS_FILE      0x02
+#define RDBD_FTLE_CTRL_PATH_NONE         0x00
+
+#define RDBD_FILE_READ_BLOCK_SIZE   4096
+
+#define RDBD_FILE_HEADER_SIZE       (sizeof(struct rdbd_file_control_msg_header))
+
+#define RDBD_FILE_MSG_SIZE(x)       (RDBD_FILE_MSG_MSG(x)->header.pathlength + RDBD_FILE_MSG_MSG(x)->header.length + RDBD_FILE_HEADER_SIZE)
+#define RDBD_FILE_MSG_RAW(x)        ((rt_uint8_t *)x)
+#define RDBD_FILE_MSG_MSG(x)        ((rdbd_file_ctrl_msg_t)x)
+
+struct rdbd_file_control_msg_header
+{
+    rt_uint16_t command;
+    rt_uint16_t pathlength;
+    rt_uint32_t offset;
+    rt_uint32_t length;
+};
+
+struct rdbd_file_session
+{
+    rt_list_t list;
+    char * path;
+    int flag;
+    int fd;
+};
+
+static struct rdbd_file_session * create_session(rt_list_t * session_list, char * path, int flag)
+{
+    struct rdbd_file_session * session;
+    if(session_list )
+    session = rt_calloc(sizeof(struct rdbd_file_session), 1);
+    if(RT_NULL == session_list || RT_NULL == path)
+    {
+        LOG_E("Invalid args %s,%d", __FUNCTION__, __LINE__);
+        return RT_NULL;
+    }
+    session->fd = -1;
+    session->flag = flag;
+    session->path = rt_strdup(path);
+    if(RT_NULL == session->path)
+    {
+        LOG_E("No memory create session");
+        goto _error;
+    }
+    session->fd = open(session->path, session->flag);
+    if(session->fd < 0)
+    {
+        LOG_E("Open file %s error", session->path);
+        goto _error;
+    }
+    rt_list_insert_before(session_list, &session->list);
+    return session;
+    
+_error:
+    if(session)
+    {
+        if(session->path)
+        {
+            rt_free(session->path);
+        }
+        if(session->fd >= 0)
+        {
+            close(session->fd);
+        }
+        free(session);
+    }
+    return RT_NULL;
+}
+
+static struct rdbd_file_session * find_session(rt_list_t * session_list, char * path)
+{
+    rt_list_t * node;
+    if(RT_NULL == session_list || RT_NULL == path)
+    {
+        LOG_E("Invalid args %s,%d", __FUNCTION__, __LINE__);
+        return RT_NULL;
+    }
+    
+    for(node = session_list->next; 
+        node != session_list; 
+        node = node->next)
+    {
+        if(strcmp(path, rt_list_entry(node, struct rdbd_file_session, list)->path) == 0)
+        {
+            return rt_list_entry(node, struct rdbd_file_session, list);
+        }
+    }
+    return RT_NULL;
+}
+
+static int delete_session(struct rdbd_file_session * session)
+{
+    if(RT_NULL == session)
+    {
+        LOG_E("Invalid args %s,%d", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    fsync(session->fd);
+    close(session->fd);
+    rt_free(session->path);
+    rt_list_remove(&session->list);
+    rt_free(session);
+    return 0;
+}
+
+static int read_session(struct rdbd_file_session * session, off_t offset, rt_uint8_t * buffer, rt_size_t len)
+{
+    if(RT_NULL == session || RT_NULL == buffer)
+    {
+        LOG_E("Invalid args %s,%d", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    if((session->flag & 0xF) == O_WRONLY)
+    {
+        LOG_E("Invalid read %s,%d", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    LOG_I("seek to %d", offset);
+    
+    lseek(session->fd, offset, SEEK_SET);
+    
+    LOG_I("Read fd %d, 0x%08X %d", session->fd, buffer, len);
+    return read(session->fd, buffer, len);
+}
+
+static int write_session(struct rdbd_file_session * session, off_t offset, const rt_uint8_t * buffer, rt_size_t len)
+{
+    if(RT_NULL == session || RT_NULL == buffer)
+    {
+        LOG_E("Invalid args %s,%d", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    if((session->flag & 0xF) == O_RDONLY)
+    {
+        LOG_E("Invalid write %s,%d", __FUNCTION__, __LINE__);
+        return -1;
+    }
+    lseek(session->fd, offset, SEEK_SET);
+    return write(session->fd, buffer, len);
+}
+
+
+
+
+struct rdbd_file_control_msg
+{
+    struct rdbd_file_control_msg_header header;
+    rt_uint8_t msg[RT_UINT16_MAX];
+};
+typedef struct rdbd_file_control_msg * rdbd_file_ctrl_msg_t;
+
+struct file_request_send
+{
+    rt_list_t list;
+    char * msg;
+    int msg_pos;
+    int request_file_send;
+};
+
+static rt_uint32_t rdbd_file_calc_hash(const char * filename);
+static rt_dstr_t * get_file_list(const char *pathname);
+static int file_service_request_send(rt_list_t * header, struct rdbd_file_control_msg * msg, int request_file_send);
+static int file_service_delete_request(struct file_request_send * request);
+static void file_service_thread_entry(void * arg)
+{
+    struct rdbd_service * file_service= (struct rdbd_service *)arg;
+    fd_set readset, writeset;
+    int result = 0;
+    int res = 0;
+    int max_fd = file_service->in_pipe_read_fd + 1;
+    rt_list_t request_write_list;
+    rt_list_t session_list;
+    struct file_request_send * request;
+    rdbd_file_ctrl_msg_t msg = RT_NULL;
+    int msg_pos = 0;
+    rt_dstr_t * file_list;
+    char * path;
+    struct rdbd_file_session * session = RT_NULL;
+    rt_list_init(&request_write_list);
+    rt_list_init(&session_list);
+    
+    while(1)
+    {
+        FD_ZERO(&readset);
+        FD_ZERO(&writeset);
+        FD_SET(file_service->in_pipe_read_fd, &readset);
+        if(!rt_list_isempty(&request_write_list)) //need write data to pc
+        {
+            FD_SET(file_service->out_pipe_write_fd, &writeset);
+            if(file_service->out_pipe_write_fd > (max_fd - 1))
+            {
+                max_fd = file_service->out_pipe_write_fd + 1;
+            }
+        }
+        res = select(max_fd, &readset, &writeset, RT_NULL, RT_NULL);
+        if(res > 0)
+        {
+            if(FD_ISSET(file_service->in_pipe_read_fd, &readset))
+            {
+                if(msg == RT_NULL)
+                {
+                    msg = rt_calloc(RDBD_FILE_HEADER_SIZE, 1);
+                    msg_pos = read(file_service->in_pipe_read_fd, RDBD_FILE_MSG_RAW(msg), RDBD_FILE_HEADER_SIZE);
+                    if(msg_pos < 0)
+                    {
+                        msg_pos = 0;
+                    }
+                }
+                else if(msg_pos < RDBD_FILE_HEADER_SIZE)//continue read header
+                {
+                    result = read(file_service->in_pipe_read_fd,
+                                                    RDBD_FILE_MSG_RAW(msg) + msg_pos, 
+                                                    RDBD_FILE_HEADER_SIZE - msg_pos);//read header
+                    if(result > 0)
+                    {
+                        msg_pos += result;
+                    }
+                }
+                else if(msg_pos < (RDBD_FILE_MSG_SIZE(msg)))//read body
+                {
+                    if(msg_pos == RDBD_FILE_HEADER_SIZE)
+                    {
+                        msg = rt_realloc(msg, RDBD_FILE_MSG_SIZE(msg));//realloc
+                    }
+                    result = read(file_service->in_pipe_read_fd,
+                                                    RDBD_FILE_MSG_RAW(msg) + msg_pos, 
+                                                    RDBD_FILE_MSG_SIZE(msg) - msg_pos);//read body
+                    if(result > 0)
+                    {
+                        msg_pos += result;
+                    }
+                    if(msg_pos == RDBD_FILE_MSG_SIZE(msg))//read done
+                    {
+                        path = rt_calloc(1,msg->header.pathlength + 1);
+                        if(RT_NULL == path)
+                        {
+                            LOG_E("no memory");
+                            RT_ASSERT(0);
+                        }
+                        rt_strncpy(path, (const char *)msg->msg, msg->header.pathlength);
+                        LOG_D("path: %s", path);
+                        path[msg->header.pathlength] = '\0';
+                        if(RDBD_FILE_CTRL_NEW_FILE == msg->header.command || RDBD_FILE_CTRL_WRITE_FILE == msg->header.command)//Done
+                        {
+                            while (1) //replace '\\' to '/'
+                            {
+                                char *p;
+                                p = strchr(path, '\\');
+                                if (p)
+                                {
+                                    *p = '/';
+                                }
+                                else
+                                {
+                                    break;
+                                }
+                            }
+                            {
+                                char *begin, *end;
+                                begin = path;
+                                char *_path;
+                                while (1)
+                                {
+                                    end = strchr(begin, '/'); //find '\\' since begin
+                                    if (end == NULL) //if not find '\\' finsh
+                                    {
+                                        break;
+                                    }
+                                    if (begin == end) //if begin is '\\'
+                                    {
+                                        begin += 1;
+                                        continue;
+                                    }
+                                    _path = rt_malloc(end - path + 1);
+                                    if (_path == RT_NULL) //malloc fail
+                                    {
+                                        break;//???
+                                    }
+                                    strncpy(_path, path, end - path); //copy path
+                                    _path[end - path] = '\0';
+                                    {
+                                        if (access(_path, 0) != 0) //dir not exist
+                                        {
+                                            if (mkdir(_path, 0777) != 0) //create fail
+                                            {
+                                                LOG_E("mkdir %s failed", _path);
+                                                free(_path);
+                                                _path = RT_NULL;
+                                                break;
+                                            }
+                                        }
+                                    }
+                                    free(_path);
+                                    _path = RT_NULL;
+                                    begin = end + 1; //next
+                                }
+                            }
+                            
+                            switch(msg->header.command)
+                            {
+                            case RDBD_FILE_CTRL_NEW_FILE:
+                                session = create_session(&session_list, path, O_CREAT|O_TRUNC|O_WRONLY);
+                                if(session == RT_NULL)
+                                {
+                                    LOG_E("Create file error %s", path);
+                                    RT_ASSERT(0);
+                                }
+                                break;
+                            case RDBD_FILE_CTRL_WRITE_FILE:
+                                session = find_session(&session_list, path);
+                                if(session == RT_NULL)
+                                {
+                                    LOG_E("Please open the file %s first", path);
+                                    RT_ASSERT(0);
+                                }
+                                break;
+                            default:
+                                RT_ASSERT(0);
+                            }
+                            LOG_D("Offset %d", msg->header.offset);
+                            LOG_D("Write %d", msg->header.length);
+                            if(msg->header.length)
+                            {
+                                if(write_session(session, msg->header.offset, &msg->msg[msg->header.pathlength], msg->header.length) != msg->header.length)
+                                {
+                                    LOG_W("Space not enought");
+                                }
+                            }
+                            session = RT_NULL;
+                            //end of function release mem
+                        }
+                        else if(RDBD_FILE_CTRL_WRITE_DONE == msg->header.command)
+                        {
+                            session = find_session(&session_list, path);
+                            if(session == RT_NULL)
+                            {
+                                LOG_W("Not need release session %s", path);
+                                RT_ASSERT(0);
+                            }
+                            delete_session(session);
+                        }
+                        else if(RDBD_FILE_CTRL_READ_FILE == msg->header.command)
+                        {
+                            msg = rt_realloc(msg, msg->header.pathlength + RDBD_FILE_READ_BLOCK_SIZE + RDBD_FILE_HEADER_SIZE);
+                            if(msg == RT_NULL)
+                            {
+                                LOG_E("No memory");
+                                RT_ASSERT(0);
+                            }
+                            session = create_session(&session_list, path, O_RDONLY);
+                            if(session == RT_NULL)
+                            {
+                                LOG_E("Open file error %s", path);
+                                RT_ASSERT(0);
+                            }
+                            
+                            msg->header.offset = 0;
+                            msg->header.length = read_session(session, msg->header.offset, &msg->msg[msg->header.pathlength], RDBD_FILE_READ_BLOCK_SIZE);
+                            file_service_request_send(&request_write_list, msg, 1);
+                            msg = RT_NULL;
+                        }
+                        else if(RDBD_FILE_CTRL_RM_FILE == msg->header.command)
+                        {
+                            remove(path);
+                        }
+                        else if(RDBD_FILE_CTRL_LIST_DIR == msg->header.command)
+                        {
+                            file_list = get_file_list(path);
+                            msg->header.offset = 0;
+                            msg->header.pathlength = 0;
+                            msg->header.length = rt_dstr_strlen(file_list);
+                            msg = rt_realloc(msg, RDBD_FILE_MSG_SIZE(msg));
+                            memcpy(msg->msg, file_list->str, msg->header.length);
+                            file_service_request_send(&request_write_list, msg, 0);
+                            rt_dstr_del(file_list);
+                            file_list = RT_NULL;
+                            msg = RT_NULL;
+                        }
+                        else if(RDBD_FTLE_CTRL_GET_HASH == msg->header.command)
+                        {
+                            msg->header.length = 4;
+                            msg->header.offset = 0;
+                            msg = rt_realloc(msg, RDBD_FILE_MSG_SIZE(msg));
+                            {
+                                rt_uint32_t hash = rdbd_file_calc_hash(path);
+                                memcpy(&msg->msg[msg->header.length], &hash, 4);
+                            }
+                            file_service_request_send(&request_write_list, msg, 0);
+                            msg = RT_NULL;
+                        }
+                        else if(RDBD_FTLE_CTRL_DIR_SYNC_START == msg->header.command)
+                        {
+                        //TO DO
+                        }
+                        else if(RDBD_FTLE_CTRL_DIR_SYNC_STOP == msg->header.command)
+                        {
+                        //TO DO
+                        }
+                        else if(RDBD_FTLE_CTRL_CHECK_PATH == msg->header.command)
+                        {
+                            msg->header.length = 1;
+                            msg->header.offset = 0;
+                            msg = rt_realloc(msg, RDBD_FILE_MSG_SIZE(msg));
+                            {
+                                struct stat filestat;
+                                if (stat(path, &filestat) == -1)
+                                {
+                                    msg->msg[msg->header.pathlength] = RDBD_FTLE_CTRL_PATH_NONE;
+                                }
+                                else
+                                {
+                                    if((filestat.st_mode & S_IFMT) == S_IFDIR)
+                                    {
+                                        msg->msg[msg->header.pathlength] = RDBD_FTLE_CTRL_PATH_IS_DIR;
+                                    }
+                                    else
+                                    {
+                                        msg->msg[msg->header.pathlength] = RDBD_FTLE_CTRL_PATH_IS_FILE;
+                                    }
+                                }
+                            }
+                            file_service_request_send(&request_write_list, msg, 0);
+                            msg = RT_NULL;
+                        }
+
+                        if(RT_NULL != path)
+                        {
+                            rt_free(path);
+                            path = RT_NULL;
+                        }
+                        if(RT_NULL != msg)
+                        {
+                            rt_free(msg);
+                            msg = RT_NULL;
+                        }
+                    }
+                }
+            }
+            if(!rt_list_isempty(&request_write_list)) //need write data to pc
+            {
+                if(FD_ISSET(file_service->out_pipe_write_fd, &writeset))
+                {
+                    request = rt_list_entry(request_write_list.next, struct file_request_send, list);
+                    result = write(file_service->out_pipe_write_fd,
+                                                request->msg + request->msg_pos, RDBD_FILE_MSG_SIZE(request->msg) - request->msg_pos);
+                    if(result > 0)
+                    {
+                        request->msg_pos += result;
+                    }
+                    if(request->msg_pos == RDBD_FILE_MSG_SIZE(request->msg))
+                    {
+                        request->msg_pos = 0;
+                        if(request->request_file_send && 0 != RDBD_FILE_MSG_MSG(request->msg)->header.length)
+                        {
+                            RDBD_FILE_MSG_MSG(request->msg)->header.offset += RDBD_FILE_MSG_MSG(request->msg)->header.length;
+                            path = rt_calloc(1,RDBD_FILE_MSG_MSG(request->msg)->header.pathlength + 1);
+                            if(RT_NULL == path)
+                            {
+                                LOG_E("No memory");
+                                RT_ASSERT(0);
+                            }
+                            rt_strncpy(path, (const char *)RDBD_FILE_MSG_MSG(request->msg)->msg, RDBD_FILE_MSG_MSG(request->msg)->header.pathlength);
+                            path[RDBD_FILE_MSG_MSG(request->msg)->header.pathlength] = '\0';
+                            
+                            request->msg = rt_realloc(request->msg, RDBD_FILE_MSG_MSG(request->msg)->header.pathlength + RDBD_FILE_READ_BLOCK_SIZE + RDBD_FILE_HEADER_SIZE);
+                            if(request->msg == RT_NULL)
+                            {
+                                LOG_E("No memory");
+                                RT_ASSERT(0);
+                            }
+                            session = find_session(&session_list, path);
+                            if(session == RT_NULL)
+                            {
+                                LOG_E("Please open the file %s first", path);
+                                RT_ASSERT(0);
+                            }
+                            free(path);
+                            path = RT_NULL;
+                            RDBD_FILE_MSG_MSG(request->msg)->header.length = read_session(session, RDBD_FILE_MSG_MSG(request->msg)->header.offset, &RDBD_FILE_MSG_MSG(request->msg)->msg[RDBD_FILE_MSG_MSG(request->msg)->header.pathlength], RDBD_FILE_READ_BLOCK_SIZE);
+                            if(RDBD_FILE_MSG_MSG(request->msg)->header.length == 0)
+                            {
+                                RDBD_FILE_MSG_MSG(request->msg)->header.command = RDBD_FILE_CTRL_WRITE_DONE;
+                                request->request_file_send = 0;
+                                delete_session(session);
+                                session = RT_NULL;
+                            }
+                        }
+                        else
+                        {
+                            file_service_delete_request(request);
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static int start(void * args);
+static int stop(void * args);
+static int resume(void * args);
+static int suspend(void * args);
+static struct rdbd_service_control_ops control_ops = 
+{
+    start,
+    stop,
+    resume,
+    suspend,
+};
+
+static int file_service_request_send(rt_list_t * header, struct rdbd_file_control_msg * msg, int request_file_send)
+{
+    struct file_request_send * request;
+    if(RT_NULL == header || RT_NULL == msg)
+    {
+        LOG_E("Write request arg error");
+        return -1;
+    }
+    request = rt_malloc(sizeof(struct file_request_send));
+    if(RT_NULL == request)
+    {
+        LOG_E("No memory request send to file");
+        return -1;
+    }
+    request->msg = (char *)msg;
+    request->msg_pos = 0;
+    request->request_file_send = request_file_send;
+    rt_list_insert_before(header, &request->list);
+    return 0;
+}
+
+static int file_service_delete_request(struct file_request_send * request)
+{
+    if(RT_NULL == request)
+    {
+        LOG_E("Delete request arg error");
+        return -1;
+    }
+    if(RT_NULL != request->msg)
+    {
+        rt_free(request->msg);
+    }
+    rt_list_remove(&request->list);
+    rt_free(request);
+    return 0;
+}
+
+int rdbd_file_service_init(void)
+{
+    struct rdbd_service * file_service;
+    rdbd_t usbrdbd = rdbd_find("usb");
+    if(RT_NULL == usbrdbd)
+    {
+        LOG_E("rdbd usb find error");
+        return -1;
+    }
+
+    file_service = rdbd_create_service(RDBD_SERVICE_ID_FILE, "file", &control_ops, RT_NULL, "filein", 1024,"fileout", 1024, RDBD_SERVICE_FLAG_WR|RDBD_SERVICE_FLAG_RD);
+    if(RT_NULL == file_service)
+    {
+        LOG_E("file_service create error");
+        goto _error;
+    }
+    LOG_I("Service %s created :", file_service->name);
+    LOG_I("in_pipe_path %s", file_service->in_pipe_path);
+    LOG_I("out_pipe_path %s", file_service->out_pipe_path);
+    LOG_I("service_id %d", file_service->service_id);
+    LOG_I("status %d", file_service->status);
+
+    rdbd_service_install(usbrdbd, file_service);
+    
+    rdbd_service_control(file_service, RDBD_SERVICE_START, file_service);
+    
+    return 0;
+
+_error:
+    if(RT_NULL != usbrdbd)
+    {
+        //TO DO
+    }
+    return -1;
+}
+INIT_APP_EXPORT(rdbd_file_service_init);
+
+static int start(void * args)
+{
+    struct rdbd_service * file_service = (struct rdbd_service *)args;
+
+    if(RT_NULL == file_service)
+    {
+         LOG_E("Start up service error, args is null");
+         return -1;
+    }
+    
+    if(file_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        file_service->in_pipe_read_fd = open(file_service->in_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(file_service->in_pipe_read_fd < 0)
+        {
+            LOG_E("Start up service %s error open in pipe failed",file_service->name);
+            return -1;
+        }
+    }
+
+    if(file_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        file_service->out_pipe_write_fd = open(file_service->out_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(file_service->out_pipe_write_fd < 0)
+        {
+            LOG_E("Start up service %s error open out pipe failed",file_service->name);
+            goto _error;
+        }
+    }
+    
+    
+    if(file_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        file_service->in_pipe_write_fd = open(file_service->in_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(file_service->in_pipe_write_fd < 0)
+        {
+            LOG_E("Start up transfer error open in pipe failed",file_service->name);
+            return -1;
+        }
+    }
+
+    if(file_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        file_service->out_pipe_read_fd = open(file_service->out_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(file_service->out_pipe_read_fd < 0)
+        {
+            LOG_E("Start up transfer error open out pipe failed",file_service->name);
+            goto _error;
+        }
+    }
+
+    file_service->service_thread = rt_thread_create(file_service->name,
+                                                    file_service_thread_entry,
+                                                    file_service,
+                                                    2048,
+                                                    21,
+                                                    20);
+    if(RT_NULL != file_service->service_thread)
+    {
+        if(RT_EOK != rt_thread_startup(file_service->service_thread))
+        {
+            rt_thread_delete(file_service->service_thread);
+            file_service->service_thread = RT_NULL;
+            goto _error;
+        }
+    }
+    else
+    {
+        goto _error;
+    }
+    return 0;
+    
+_error:
+    stop(args);
+    return -1;
+}
+
+static int stop(void * args)
+{
+    struct rdbd_service * file_service = (struct rdbd_service *)args;
+
+    if(RT_NULL == file_service)
+    {
+         LOG_E("Stop service error, args is null");
+         return -1;
+    }
+
+    if(RT_NULL != file_service->service_thread)
+    {
+        if(RT_EOK != rt_thread_delete(file_service->service_thread))
+        {
+            LOG_E("Delete thread %s failed", file_service->service_thread->name);
+            return -1;
+        }
+        file_service->service_thread = RT_NULL;
+    }
+    
+    if(file_service->in_pipe_read_fd >= 0)
+    {
+        if(close(file_service->in_pipe_read_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", file_service->in_pipe_read_fd);
+            return -1;
+        }
+        file_service->in_pipe_read_fd = -1;
+    }
+    
+    if(file_service->in_pipe_write_fd >= 0)
+    {
+        if(close(file_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", file_service->in_pipe_write_fd);
+            return -1;
+        }
+        file_service->in_pipe_write_fd = -1;
+    }
+    
+    if(file_service->in_pipe_write_fd >= 0)
+    {
+        if(close(file_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", file_service->in_pipe_write_fd);
+            return -1;
+        }
+        file_service->in_pipe_write_fd = -1;
+    }
+    
+    if(file_service->out_pipe_write_fd >= 0)
+    {
+        if(close(file_service->out_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", file_service->out_pipe_write_fd);
+            return -1;
+        }
+        file_service->out_pipe_write_fd = -1;
+    }
+    
+    return 0;
+}
+
+static int resume(void * args)
+{
+    //Not need
+    return 0;
+}
+static int suspend(void * args)
+{
+    //Not need
+    return 0;
+}
+
+static rt_dstr_t * get_file_list(const char *pathname)
+{
+    rt_dstr_t * list = RT_NULL, *fullpath = RT_NULL, *temp_list = RT_NULL;
+    struct dirent * dir = NULL;
+    DIR * root_dir = NULL;
+    struct stat filestat;
+
+    list = rt_dstr_new("");
+    root_dir = opendir(pathname);
+    if(root_dir == NULL)
+    {
+        return list;
+    }
+    while(1)
+    {
+        dir = readdir(root_dir);
+        if(dir == NULL)
+        {
+            break;
+        }
+        if (strncmp(dir->d_name, ".", 1) == 0)
+			continue; /* skip hide file*/
+        if(strcmp("/", pathname) == 0)
+        {
+            fullpath = rt_dstr_sprintf(fullpath, "/%s", dir->d_name);
+        }
+        else
+        {
+            fullpath = rt_dstr_sprintf(fullpath, "%s/%s", pathname, dir->d_name);
+        }
+        if (stat(fullpath->str, &filestat) == -1)
+        {
+            LOG_E("cannot access the file %s", fullpath->str);
+            return list;
+        }
+        if ((filestat.st_mode & S_IFMT) == S_IFDIR)
+        {
+            temp_list = get_file_list(fullpath->str);
+            list = rt_dstr_append_printf(list, "%s", temp_list->str);
+            rt_dstr_del(temp_list);
+            temp_list = RT_NULL;
+        }
+        else
+        {
+            list = rt_dstr_append_printf(list, "%s\n", fullpath->str);
+        }
+        rt_dstr_del(fullpath);
+        fullpath = RT_NULL;
+    }
+    closedir(root_dir);
+    return list;
+}
+
+int list_dir(void)
+{
+    int i = 0;
+    rt_dstr_t * list = get_file_list("/");
+    while(list->str[i] != '\0')
+    {
+        rt_kprintf("%c",list->str[i++]);
+    }
+    
+    rt_dstr_del(list);
+    return 0;
+}
+MSH_CMD_EXPORT(list_dir, list dir);
+
+/* hash a single byte */
+static rt_uint32_t fnv1a_r(unsigned char oneByte, rt_uint32_t hash)
+{
+    return (oneByte ^ hash) * 0x01000193; // 0x01000193 = 16777619
+}
+
+static rt_uint32_t rdbd_file_calc_hash(const char * filename)
+{
+    FILE * fp = NULL;
+    char ch;
+    uint32_t hash = RDBD_FILE_HASH_FNV_SEED;
+    fp = fopen(filename, "r");
+    if(fp == NULL)
+    {
+        LOG_W("%s not found!", filename);
+        return 0;
+    }
+    while(!feof(fp))
+    {
+        ch = fgetc(fp);
+        hash = fnv1a_r(ch, hash);
+    }
+    fclose(fp);
+    return hash;
+}
+

+ 369 - 0
examples/rdbd_shell.c

@@ -0,0 +1,369 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include "rtthread.h"
+#include "rdbd.h"
+#include "rdbd_service.h"
+#include "rdbd_service_manager.h"
+#include "rthw.h"
+#include <fcntl.h>
+#include <sys/time.h>
+#include <dfs_select.h>
+#include <libc.h>
+#include <dfs_posix.h>
+#define PKGS_USING_USB_RDBD
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "RDBD Shell"
+#define DBG_LEVEL         DBG_WARNING
+#define DBG_COLOR
+#include <rtdbg.h>
+
+
+static int start(void * args);
+static int stop(void * args);
+static int resume(void * args);
+static int suspend(void * args);
+static struct rdbd_service_control_ops control_ops = 
+{
+    start,
+    stop,
+    resume,
+    suspend,
+};
+
+extern rt_uint32_t finsh_get_echo(void);
+extern void finsh_set_echo(rt_uint32_t echo);
+char drop_buffer[128];
+static rt_err_t rt_shell_service_device_init(struct rt_device *dev)
+{
+    ((void)dev);
+    return RT_EOK;
+}
+
+static rt_err_t rt_shell_service_device_open(struct rt_device *dev, rt_uint16_t oflag)
+{
+    dev->open_flag = oflag & 0xff;
+    return RT_EOK;
+}
+
+static rt_err_t rt_shell_service_device_close(struct rt_device *dev)
+{
+    ((void)dev);
+    return RT_EOK;
+}
+
+static rt_size_t rt_shell_service_device_read(rt_device_t dev, rt_off_t pos, void* buffer, rt_size_t size)
+{
+    struct rdbd_service * shell_service = (struct rdbd_service *)dev->user_data;
+    fd_set readset;
+    int res = 0;
+    int max_fd = shell_service->in_pipe_read_fd + 1;
+    //if(RDBD_SERVICE_STATUS_RUNNING == shell_service->status)
+    {
+        FD_ZERO(&readset);
+        FD_SET(shell_service->in_pipe_read_fd, &readset);
+        if(finsh_get_echo())
+        {
+            finsh_set_echo(0);
+        }
+        res = select(max_fd, &readset, RT_NULL, RT_NULL, RT_NULL);
+        if(res > 0)
+        {
+            if(FD_ISSET(shell_service->in_pipe_read_fd, &readset))
+            {
+                return read(shell_service->in_pipe_read_fd, buffer, size);
+            }
+        }
+    }
+    return 0;
+}
+
+
+static rt_size_t rt_shell_service_device_write(rt_device_t dev, rt_off_t pos, const void* buffer, rt_size_t size)
+{
+    struct rdbd_service * shell_service = (struct rdbd_service *)dev->user_data;
+    fd_set writeset;
+    int res = 0;
+    int max_fd = shell_service->out_pipe_write_fd + 1;
+    char * returnBuffer = RT_NULL;
+    int written_size = 0;
+    int result = size;
+    int n_size = 0;
+    int i;
+    for(i = 0; i < size; i++)
+    {
+        if(((char *)buffer)[i] == '\n')
+        {
+            n_size ++;
+        }
+    }
+    returnBuffer = rt_malloc(size + n_size);
+    for(i = 0; i < size; i++)
+    {
+        if(((char *)buffer)[i] == '\n')
+        {
+            returnBuffer[written_size] = '\r';
+            written_size++;
+        }
+        returnBuffer[written_size] = ((char *)buffer)[i];
+        written_size++;
+    }
+    size = written_size;
+    written_size = 0;
+    while(size)
+    {
+        if(RDBD_SERVICE_STATUS_RUNNING == shell_service->status)
+        {
+            FD_ZERO(&writeset);
+            FD_SET(shell_service->out_pipe_write_fd, &writeset);
+            res = select(max_fd, RT_NULL, &writeset, RT_NULL, RT_NULL);
+            if(res > 0)
+            {
+                if(FD_ISSET(shell_service->out_pipe_write_fd, &writeset))
+                {
+                    written_size = write(shell_service->out_pipe_write_fd, returnBuffer + written_size, size);
+                    if(written_size > 0)
+                    {
+                        size -= written_size;
+                    }
+                }
+            }
+        }
+        else
+        {
+            break;
+        }
+    }
+    free(returnBuffer);
+    return result;
+}
+
+#ifdef RT_USING_DEVICE_OPS
+const static struct rt_device_ops shell_ops = 
+{
+    rt_shell_service_device_init,
+    rt_shell_service_device_open,
+    rt_shell_service_device_close,
+    rt_shell_service_device_read,
+    rt_shell_service_device_write,
+    RT_NULL
+};
+#endif
+
+static int start(void * args)
+{
+    struct rdbd_service * shell_service = (struct rdbd_service *)args;
+
+    if(RT_NULL == shell_service)
+    {
+         LOG_E("Start up service error, args is null");
+         return -1;
+    }
+    
+    if(shell_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        shell_service->in_pipe_read_fd = open(shell_service->in_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(shell_service->in_pipe_read_fd < 0)
+        {
+            LOG_E("Start up service %s error open in pipe failed",shell_service->name);
+            return -1;
+        }
+    }
+
+    if(shell_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        shell_service->out_pipe_write_fd = open(shell_service->out_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(shell_service->out_pipe_write_fd < 0)
+        {
+            LOG_E("Start up service %s error open out pipe failed",shell_service->name);
+            goto _error;
+        }
+    }
+    
+    
+    if(shell_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        shell_service->in_pipe_write_fd = open(shell_service->in_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(shell_service->in_pipe_write_fd < 0)
+        {
+            LOG_E("Start up transfer error open in pipe failed",shell_service->name);
+            return -1;
+        }
+    }
+
+    if(shell_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        shell_service->out_pipe_read_fd = open(shell_service->out_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(shell_service->out_pipe_read_fd < 0)
+        {
+            LOG_E("Start up transfer error open out pipe failed",shell_service->name);
+            goto _error;
+        }
+    }
+    return 0;
+    
+_error:
+    if(shell_service->in_pipe_read_fd >= 0)
+    {
+        close(shell_service->in_pipe_read_fd);
+        shell_service->in_pipe_read_fd = -1;
+    }
+    if(shell_service->in_pipe_write_fd >= 0)
+    {
+        close(shell_service->in_pipe_write_fd);
+        shell_service->in_pipe_write_fd = -1;
+    }
+    
+    if(shell_service->out_pipe_read_fd >= 0)
+    {
+        close(shell_service->out_pipe_read_fd);
+        shell_service->out_pipe_read_fd = -1;
+    }
+    if(shell_service->out_pipe_write_fd >= 0)
+    {
+        close(shell_service->out_pipe_write_fd);
+        shell_service->out_pipe_write_fd = -1;
+    }
+    return -1;
+}
+
+static int stop(void * args)
+{
+    struct rdbd_service * shell_service = (struct rdbd_service *)args;
+    
+    if(RT_NULL == shell_service)
+    {
+         LOG_E("Stop service error, args is null");
+         return -1;
+    }
+    
+    if(shell_service->in_pipe_read_fd >= 0)
+    {
+        if(close(shell_service->in_pipe_read_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", shell_service->in_pipe_read_fd);
+            return -1;
+        }
+        shell_service->in_pipe_read_fd = -1;
+    }
+    
+    if(shell_service->in_pipe_write_fd >= 0)
+    {
+        if(close(shell_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", shell_service->in_pipe_write_fd);
+            return -1;
+        }
+        shell_service->in_pipe_write_fd = -1;
+    }
+    
+    if(shell_service->in_pipe_write_fd >= 0)
+    {
+        if(close(shell_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", shell_service->in_pipe_write_fd);
+            return -1;
+        }
+        shell_service->in_pipe_write_fd = -1;
+    }
+    
+    if(shell_service->out_pipe_write_fd >= 0)
+    {
+        if(close(shell_service->out_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", shell_service->out_pipe_write_fd);
+            return -1;
+        }
+        shell_service->out_pipe_write_fd = -1;
+    }
+    
+    return 0;
+}
+
+static int resume(void * args)
+{
+    //Not need
+    return 0;
+}
+static int suspend(void * args)
+{
+    //Not need
+    return 0;
+}
+
+
+int rdbd_shell_service_init(void)
+{
+    struct rdbd_service * shell_service;
+    rt_device_t shell_device = rt_calloc(sizeof(struct rt_thread), 1);
+    rdbd_t usbrdbd = rdbd_find("usb");
+    if(RT_NULL == usbrdbd)
+    {
+        LOG_E("rdbd usb find error");
+        return -1;
+    }
+
+    shell_service = rdbd_create_service(RDBD_SERVICE_ID_SHELL, "shell", &control_ops, RT_NULL, "shellin", 128,"shellout", 128, RDBD_SERVICE_FLAG_WR|RDBD_SERVICE_FLAG_RD);
+    if(RT_NULL == shell_service)
+    {
+        LOG_E("shell_service create error");
+        goto _error;
+    }
+    LOG_I("Service %s created :", shell_service->name);
+    LOG_I("in_pipe_path %s", shell_service->in_pipe_path);
+    LOG_I("out_pipe_path %s", shell_service->out_pipe_path);
+    LOG_I("service_id %d", shell_service->service_id);
+    LOG_I("status %d", shell_service->status);
+    shell_device->type       = RT_Device_Class_Char;
+#ifdef RT_USING_DEVICE_OPS
+    device->ops         = &shell_ops;
+#else
+    shell_device->init       = rt_shell_service_device_init;
+    shell_device->open       = rt_shell_service_device_open;
+    shell_device->close      = rt_shell_service_device_close;
+    shell_device->read       = rt_shell_service_device_read;
+    shell_device->write      = rt_shell_service_device_write;
+    shell_device->control    = RT_NULL;
+#endif
+    shell_device->user_data  = shell_service;
+    shell_service->user_data = shell_device;
+    rt_device_register(shell_device, "rdbdsh", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX);
+    rdbd_service_install(usbrdbd, shell_service);
+    
+    rt_console_set_device("rdbdsh");
+
+    // /* add non-block flag */
+    // ioctl(libc_stdio_get_console(), F_SETFL, (void *) (dev_old_flag | O_NONBLOCK));
+    /* set tcp shell device for console */
+    libc_stdio_set_console("rdbdsh", O_RDWR);
+    rdbd_service_control(shell_service, RDBD_SERVICE_START, shell_service);
+    
+    return 0;
+
+_error:
+    if(RT_NULL != usbrdbd)
+    {
+        //TO DO
+    }
+    return -1;
+}
+INIT_APP_EXPORT(rdbd_shell_service_init);

+ 78 - 0
inc/rdbd.h

@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#ifndef __RDBD_H__
+#define __RDBD_H__
+#include <rtthread.h>
+#include <rtdevice.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define RDBD_STATUS_CONNECTED        (1)
+#define RDBD_STATUS_DISCONNECTED     (0)
+
+
+typedef struct rdbd_msg * rdbd_msg_t;
+typedef struct rdbd * rdbd_t;
+struct rdbd_header
+{
+    rt_uint8_t source;
+    rt_uint32_t msg_len : 24;
+};
+
+struct rdbd_msg
+{
+    struct rdbd_header header;
+    rt_uint8_t msg[RT_UINT16_MAX];
+};
+
+struct rdbd_transfer_ops
+{
+    int (* read)(void * buffer, size_t size, void (* callback)(void * context, int size), void * context);
+    int (* write)(const void * buffer, size_t size,void (* callback)(void * context, int size), void * context);
+};
+
+struct rdbd
+{
+    rt_list_t list;
+    int status;
+    char * name;
+    struct rdbd_transfer_ops * private_transfer_ops;
+    rt_list_t service_list;
+};
+
+extern rdbd_t rdbd_find(const char * rdbd_name);
+extern int rdbd_register_transfer_ops(rdbd_t rdbd, struct rdbd_transfer_ops * ops);
+
+extern rdbd_t rdbd_create(const char * rdbd_name);
+extern int rdbd_delete(rdbd_t rdbd);
+extern int rdbd_get_status(rdbd_t rdbd);
+
+#define RDBD_MSG(x)      ((rdbd_msg_t)x)
+#define RDBD_RAW_MSG(x)  ((char *)x)
+#define RDBD_MSG_LEN(x)  (RDBD_MSG(x)->header.msg_len + sizeof(struct rdbd_header))
+
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 107 - 0
inc/rdbd_service.h

@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#ifndef __RDBD_SERVICE_H__
+#define __RDBD_SERVICE_H__
+#include <rtthread.h>
+#include <rdbd.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+///<summer>
+///default Service id for rdbd service, you can used service id 20-255
+///</summer>
+#define RDBD_SERVICE_ID_CONTROL      (0U)
+#define RDBD_SERVICE_ID_FILE         (1U)
+#define RDBD_SERVICE_ID_SHELL        (2U)
+#define RDBD_SERVICE_ID_RTI          (3U)
+#define RDBD_SERVICE_ID_TCP_DUMP     (4U)
+#define RDBD_SERVICE_ID_OTA          (5U)
+
+#define RDBD_SERVICE_FLAG_NONE  0
+#define RDBD_SERVICE_FLAG_RD    1
+#define RDBD_SERVICE_FLAG_WR    2
+
+enum rdbd_service_status
+{
+    RDBD_SERVICE_STATUS_RUNNING = 1,
+    RDBD_SERVICE_STATUS_STOP = 2,
+    RDBD_SERVICE_STATUS_SUSPENDED = 3
+};
+
+struct rdbd_service_control_ops
+{
+    int (*start)(void * args);
+    int (*stop)(void * args);
+    int (* resume)(void * args);
+    int (* suspend)(void * args);
+};
+
+struct rdbd_request_write
+{
+    rt_list_t list;
+    struct rdbd_msg * msg;
+    int msg_pos;
+};
+
+struct rdbd_service
+{
+    rt_list_t list;
+    char * name;
+    rdbd_t rdbd;
+    int status;
+    char * in_pipe_path;
+    char * out_pipe_path;
+
+    int in_pipe_read_fd;
+    int in_pipe_write_fd;
+    int out_pipe_read_fd;
+    int out_pipe_write_fd;
+    rt_thread_t service_thread;
+    struct rdbd_service_control_ops * control_ops;
+    void * user_data;
+    struct rdbd_msg * msg;
+    int msg_pos;
+
+    rt_list_t request_write_list;
+    rt_uint8_t service_id;
+    rt_uint8_t flag;
+};
+extern struct rdbd_service * rdbd_create_service(rt_uint8_t service_id,
+                                                const char * name,
+                                                struct rdbd_service_control_ops * control_ops, 
+                                                void * user_data, 
+                                                const char * in_pipe_name,
+                                                rt_uint32_t in_buf_size,
+                                                const char * out_pipe_name,
+                                                rt_uint32_t out_buf_size,
+                                                rt_uint8_t flag);
+
+extern int rdbd_service_request_write(struct rdbd_service * service, struct rdbd_msg * msg);
+
+extern int rdbd_service_request_delete(struct rdbd_request_write * request);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 51 - 0
inc/rdbd_service_manager.h

@@ -0,0 +1,51 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#ifndef __RDBD_SERVICE_MANAGER_H__
+#define __RDBD_SERVICE_MANAGER_H__
+
+#include <rtthread.h>
+#include <rdbd_service.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum rdbd_service_control_cmd
+{
+    RDBD_SERVICE_START = 1,
+    RDBD_SERVICE_STOP = 2,
+    RDBD_SERVICE_SUSPEND = 3,
+    RDBD_SERVICE_RESUME = 4,
+    RDBD_SERVICE_GET_STATUS = 5,
+};
+
+extern int rdbd_service_control(struct rdbd_service * service, int cmd, void * args);
+extern int rdbd_service_install(rdbd_t rdbd, struct rdbd_service * service);
+extern int rdbd_service_uninstall(struct rdbd_service * service);
+extern struct rdbd_service * rdbd_service_find(rdbd_t rdbd, const char * name);
+extern struct rdbd_service * rdbd_service_get(rdbd_t rdbd, rt_uint8_t service_id);
+#ifdef __cplusplus
+}
+#endif
+
+#endif

+ 45 - 0
inc/usb_rdbd.h

@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#ifndef __USB_RDBD_H__
+#define __USB_RDBD_H__
+#include <rdbd.h>
+#ifdef __cplusplus
+extern "C" {
+#endif
+struct usb_rdbd_descriptor
+{
+#ifdef RT_USB_DEVICE_COMPOSITE
+    struct uiad_descriptor iad_desc;
+#endif
+    struct uinterface_descriptor intf_desc;
+    struct uendpoint_descriptor ep_out_desc;
+    struct uendpoint_descriptor ep_in_desc;
+};
+typedef struct usb_rdbd_descriptor* usb_rdbd_desc_t;
+
+#define RDBD_CMD_GET_SERVICE_LIST      0x80
+#define RDBD_CMD_GET_SERVICE_PIPE_ADDR 0x81
+#ifdef __cplusplus
+}
+#endif
+#endif

+ 125 - 0
src/rdbd.c

@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rdbd.h>
+#include <rtthread.h>
+#include <string.h>
+#include <rdbd_service_manager.h>
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "RDBD"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+static rt_list_t rdbd_list;
+
+int rdbd_init(void)
+{
+    rt_memset(&rdbd_list, 0, sizeof(rdbd_list));
+    rt_list_init(&rdbd_list);
+    return 0;
+}
+INIT_PREV_EXPORT(rdbd_init);
+
+rdbd_t rdbd_create(const char * rdbd_name)
+{
+    rdbd_t rdbd = rt_calloc(sizeof(struct rdbd),1);
+    if(RT_NULL == rdbd)
+    {
+        LOG_E("rdbd %s create failed,no memory", rdbd_name);
+        return RT_NULL;
+    } 
+    rdbd->name = rt_strdup(rdbd_name);
+    if(RT_NULL == rdbd->name)
+    {
+        LOG_E("rdbd %s create failed,no memory", rdbd_name);
+        goto _error;
+    }
+    rdbd->status = RDBD_STATUS_DISCONNECTED;
+    rt_list_init(&rdbd->service_list);
+    rt_list_insert_before(&rdbd_list, &rdbd->list);
+    LOG_I("Create rdbd %s", rdbd->name);
+    return rdbd;
+
+_error:
+    if(RT_NULL != rdbd)
+    {
+        if(RT_NULL != rdbd->name)
+        {
+            rt_free(rdbd->name);
+            rdbd->name = RT_NULL;
+        }
+        rt_free(rdbd);
+        rdbd = RT_NULL;
+    }
+    return RT_NULL;
+}
+
+int rdbd_delete(rdbd_t rdbd)
+{
+    int result = 0;
+    //STOP all and uninstall
+    while(!rt_list_isempty(&rdbd->service_list))
+    {
+        result += rdbd_service_control(rt_list_entry(rdbd->service_list.next,struct rdbd_service, list), RDBD_SERVICE_STOP, rt_list_entry(rdbd->service_list.next,struct rdbd_service, list));
+        result += rdbd_service_uninstall(rt_list_entry(rdbd->service_list.next,struct rdbd_service, list));
+    }
+    LOG_I("Delete rdb %s", rdbd->name);
+    rt_free(rdbd->name);
+    rdbd->name = RT_NULL;
+    rt_free(rdbd);
+    rdbd = RT_NULL;
+    return result;
+}
+
+int rdbd_get_status(rdbd_t rdbd)
+{
+    return rdbd->status;
+}
+
+rdbd_t rdbd_find(const char * rdbd_name)
+{
+    rt_list_t * l;
+    rdbd_t rdbd;
+    for(l = rdbd_list.next;
+        l != &rdbd_list; 
+        l = l->next)
+    {
+        rdbd = rt_list_entry(l, struct rdbd, list);
+        if(0 == strcmp(rdbd->name, rdbd_name))
+        {
+            return rdbd;
+        }
+    }
+    return RT_NULL;
+}
+
+int rdbd_register_transfer_ops(rdbd_t rdbd, struct rdbd_transfer_ops * ops)
+{
+    if(RT_NULL == ops)
+    {
+        LOG_E("Register transfer ops error");
+        return -1;
+    }
+    rdbd->private_transfer_ops = ops;
+    return 0;
+}
+

+ 192 - 0
src/rdbd_service.c

@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rdbd_service.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#undef DBG_ENABLE
+#ifdef PKGS_USING_RDBD_SHELL
+#undef DBG_ENABLE
+#endif
+#define DBG_SECTION_NAME  "RDBD Service"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+
+struct rdbd_service * rdbd_create_service(rt_uint8_t service_id,
+                                                const char * name,
+                                                struct rdbd_service_control_ops * control_ops, 
+                                                void * user_data, 
+                                                const char * in_pipe_name,
+                                                rt_uint32_t in_buf_size,
+                                                const char * out_pipe_name,
+                                                rt_uint32_t out_buf_size,
+                                                rt_uint8_t flag)
+{
+    struct rdbd_service * service = RT_NULL;
+    rt_pipe_t * inpipe = RT_NULL;
+    rt_pipe_t * outpipe = RT_NULL;
+    
+    if(RT_NULL == name)
+    {
+        LOG_E("Service create failed,name is null");
+        return RT_NULL;
+    }
+    if(RT_NULL == control_ops)
+    {
+        LOG_E("Service %s create failed,control_ops is null", name);
+        return RT_NULL;
+    }
+    if((flag & RDBD_SERVICE_FLAG_RD) && ((RT_NULL == in_pipe_name) || (0 == in_buf_size)))
+    {
+        LOG_E("Service %s create failed,service is readable but in_pipe_name is null or in_buf_size is 0", name);
+        return RT_NULL;
+    }
+
+    if((flag & RDBD_SERVICE_FLAG_WR) && ((RT_NULL == out_pipe_name) || (0 == out_buf_size)))
+    {
+        LOG_E("Service %s create failed,service is writeable but out_buf_size is null", name);
+        return RT_NULL;
+    }
+
+    service = calloc(sizeof(struct rdbd_service), 1);
+    if(RT_NULL == service)
+    {
+        LOG_E("Service %s create failed,no memory", name);
+        return RT_NULL;
+    }
+
+    service->name = rt_strdup(name);
+    if(RT_NULL == service->name)
+    {
+        LOG_E("Service %s create failed,no memory", name);
+        goto _error;
+    }
+
+    if(flag & RDBD_SERVICE_FLAG_RD)
+    {
+        inpipe = rt_pipe_create(in_pipe_name, in_buf_size);
+        if(RT_NULL == inpipe)
+        {
+            LOG_E("Service %s create failed,no memory", name);
+            goto _error;
+        }
+        LOG_I("Create pipe %s size %d", in_pipe_name, in_buf_size);
+        service->in_pipe_path = calloc(strlen("/dev/") + strlen(in_pipe_name) + 1, 1);
+        if(RT_NULL == service->in_pipe_path)
+        {
+            LOG_E("Service %s create failed,no memory", name);
+            goto _error;
+        }
+        sprintf(service->in_pipe_path, "/dev/%s", in_pipe_name);
+    }
+
+    if(flag & RDBD_SERVICE_FLAG_WR)
+    {
+        outpipe = rt_pipe_create(out_pipe_name, out_buf_size);
+        if(RT_NULL == outpipe)
+        {
+            LOG_E("Service %s create failed,no memory", name);
+            goto _error;
+        }
+        LOG_I("Create pipe %s size %d", out_pipe_name, out_buf_size);
+        service->out_pipe_path = calloc(strlen("/dev/") + strlen(out_pipe_name) + 1, 1);
+        if(RT_NULL == service->out_pipe_path)
+        {
+            LOG_E("Service %s create failed,no memory", name);
+            goto _error;
+        }
+        sprintf(service->out_pipe_path, "/dev/%s", out_pipe_name);
+    }
+
+    service->control_ops = control_ops;
+    service->user_data = user_data;
+    service->service_id = service_id;
+    service->status = RDBD_SERVICE_STATUS_STOP;
+    service->flag = flag;
+    rt_list_init(&service->request_write_list);
+    return service;
+
+_error:
+    if(RT_NULL != service)
+    {
+        if(RT_NULL != service->name)
+        {
+            free(service->name);
+            service->name = RT_NULL;
+        }
+        
+        if(RT_NULL != service->in_pipe_path)
+        {
+            free(service->in_pipe_path);
+            service->in_pipe_path = RT_NULL;
+        }
+
+        if(RT_NULL != service->out_pipe_path)
+        {
+            free(service->out_pipe_path);
+            service->out_pipe_path = RT_NULL;
+        }
+
+        free(service);
+        service = RT_NULL;
+    }
+    return RT_NULL;
+}
+
+int rdbd_service_request_write(struct rdbd_service * service, struct rdbd_msg * msg)
+{
+    struct rdbd_request_write * request;
+    if(RT_NULL == service || RT_NULL == msg)
+    {
+        LOG_E("Write request arg error");
+        return -1;
+    }
+    request = rt_malloc(sizeof(struct rdbd_request_write));
+    if(RT_NULL == request)
+    {
+        LOG_E("No memory request write to %s", service->name);
+        return -1;
+    }
+    request->msg = msg;
+    request->msg_pos = 0;
+    rt_list_insert_before(&service->request_write_list, &request->list);
+    return 0;
+}
+
+int rdbd_service_request_delete(struct rdbd_request_write * request)
+{
+    if(RT_NULL == request)
+    {
+        LOG_E("Delete request arg error");
+        return -1;
+    }
+    if(RT_NULL != request->msg)
+    {
+        rt_free(request->msg);
+    }
+    rt_list_remove(&request->list);
+    rt_free(request);
+    return 0;
+}

+ 699 - 0
src/rdbd_service_base.c

@@ -0,0 +1,699 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rdbd.h>
+#include <rdbd_service.h>
+#include <rdbd_service_manager.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <dfs_select.h>
+#include <unistd.h>
+#define RDB_RINGBUFF_SIZE   2048
+
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "RDBD Service"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+static void hex_dump(const char *name, const char *buf, rt_size_t size)
+{
+    #define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
+    #define WIDTH_SIZE           16
+
+    rt_size_t i, j;
+
+    for (i = 0; i < size; i += WIDTH_SIZE)
+    {
+        LOG_I("%s: %04X-%04X: ", name, i, i + WIDTH_SIZE);
+        for (j = 0; j < WIDTH_SIZE; j++)
+        {
+            if (i + j < size)
+            {
+                LOG_RAW("%02X ", buf[i + j]);
+            }
+            else
+            {
+                LOG_RAW("   ");
+            }
+            if ((j + 1) % 8 == 0)
+            {
+                LOG_RAW(" ");
+            }
+        }
+        LOG_RAW("  ");
+        for (j = 0; j < WIDTH_SIZE; j++)
+        {
+            if (i + j < size)
+            {
+                LOG_RAW("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
+            }
+        }
+        LOG_RAW("\n");
+    }
+}
+static int start(void * args);
+static int stop(void * args);
+static int resume(void * args);
+static int suspend(void * args);
+static struct rdbd_service_control_ops control_ops = 
+{
+    start,
+    stop,
+    resume,
+    suspend,
+};
+
+
+extern struct rdbd_transfer_ops usb_rdb_transfer_ops;
+int rdbd_base_service_init(void)
+{
+    struct rdbd_service * base_service;
+    rdbd_t usbrdbd = rdbd_create("usb");
+    if(RT_NULL == usbrdbd)
+    {
+        LOG_E("rdbd create error");
+        return -1;
+    }
+
+    rdbd_register_transfer_ops(usbrdbd, &usb_rdb_transfer_ops);
+    
+    base_service = rdbd_create_service(RDBD_SERVICE_ID_CONTROL, "base", &control_ops, RT_NULL, "basein", RDB_RINGBUFF_SIZE,"baseout", RDB_RINGBUFF_SIZE, RDBD_SERVICE_FLAG_WR|RDBD_SERVICE_FLAG_RD);
+    if(RT_NULL == base_service)
+    {
+        LOG_E("base_service create error");
+        goto _error;
+    }
+    LOG_I("Service %s created :", base_service->name);
+    LOG_I("in_pipe_path %s", base_service->in_pipe_path);
+    LOG_I("out_pipe_path %s", base_service->out_pipe_path);
+    LOG_I("service_id %d", base_service->service_id);
+    LOG_I("status %d", base_service->status);
+
+    rdbd_service_install(usbrdbd, base_service);
+    
+    rdbd_service_control(base_service, RDBD_SERVICE_START, base_service);
+    
+//    rdbd_delete(usbrdbd);
+
+    return 0;
+
+_error:
+    if(RT_NULL != usbrdbd)
+    {
+        //TO DO
+    }
+    return -1;
+}
+INIT_COMPONENT_EXPORT(rdbd_base_service_init);
+static int rdbd_service_base_response(struct rdbd_service * base_service, char * request, int request_size, char ** response, int * response_size);
+
+static void base_service_thread_entry(void * arg)
+{
+    struct rdbd_service * base_service= (struct rdbd_service *)arg;
+    struct rdbd_service * service;
+    struct rdbd_request_write * request;
+    char * response = RT_NULL;
+    int response_size = 0;
+    fd_set readset, writeset;
+    int res = 0;
+    struct timeval timeout;
+    rt_list_t * node;
+    int max_fd = base_service->in_pipe_read_fd + 1;
+    int result = 0;
+    while(1)
+    {
+        FD_ZERO(&readset);
+        FD_ZERO(&writeset);
+        
+        FD_SET(base_service->in_pipe_read_fd, &readset);
+        if(!rt_list_isempty(&base_service->request_write_list)) //need write data to pc
+        {
+            FD_SET(base_service->out_pipe_write_fd, &writeset);
+            if(base_service->out_pipe_write_fd > (max_fd - 1))
+            {
+                max_fd = base_service->out_pipe_write_fd + 1;
+            }
+        }
+
+        for(node = base_service->rdbd->service_list.next; 
+            node != &base_service->rdbd->service_list; 
+            node = node->next)
+        {
+            service = rt_list_entry(node, struct rdbd_service, list);
+            if(service == base_service || service->status != RDBD_SERVICE_STATUS_RUNNING)
+            {
+                continue;
+            }
+            if(service->flag & RDBD_SERVICE_FLAG_WR)
+            {
+                FD_SET(service->out_pipe_read_fd, &readset);
+                if(service->out_pipe_read_fd > (max_fd - 1))
+                {
+                    max_fd = service->out_pipe_read_fd + 1;
+                }
+            }
+            if((service->flag & RDBD_SERVICE_FLAG_RD) && !rt_list_isempty(&service->request_write_list)) // need write data to service
+            {
+                FD_SET(service->in_pipe_write_fd, &writeset);
+                if(service->in_pipe_write_fd > (max_fd - 1))
+                {
+                    max_fd = service->in_pipe_write_fd + 1;
+                }
+            }
+        }
+
+        timeout.tv_sec=0;
+        timeout.tv_usec=500000; //timeout 500ms
+        res = select(max_fd, &readset, &writeset, RT_NULL, &timeout);
+        if(res == 0)//timeout
+        {
+            //LOG_I("res %d", res);
+        }
+        else if(res > 0)//select successful
+        {
+            if(FD_ISSET(base_service->in_pipe_read_fd, &readset))//pc data in
+            {
+                if(base_service->msg == RT_NULL)
+                {
+                    base_service->msg = rt_calloc(sizeof(struct rdbd_header), 1);
+                    base_service->msg_pos = read(base_service->in_pipe_read_fd,
+                                                    RDBD_RAW_MSG(base_service->msg), 
+                                                    sizeof(struct rdbd_header));//read header
+                    if(base_service->msg_pos < 0)
+                    {
+                        base_service->msg_pos = 0;
+                    }
+                    if(base_service->msg->header.msg_len == 0)
+                    {
+                        //drop
+                        free(base_service->msg);
+                        base_service->msg = RT_NULL;
+                    }
+                }
+                else if(base_service->msg_pos < sizeof(struct rdbd_header))//continue read header
+                {
+                    result = read(base_service->in_pipe_read_fd,
+                                    RDBD_RAW_MSG(base_service->msg) + base_service->msg_pos, 
+                                    sizeof(struct rdbd_header) - base_service->msg_pos);//read header
+                    if(result > 0)
+                    {
+                        base_service->msg_pos += result;
+                    }
+                }
+                else if(base_service->msg_pos < (base_service->msg->header.msg_len + sizeof(struct rdbd_header)))//read body
+                {
+                    if(base_service->msg_pos == sizeof(struct rdbd_header))
+                    {
+                        base_service->msg = rt_realloc(base_service->msg, (base_service->msg->header.msg_len + sizeof(struct rdbd_header)));//realloc
+                    }
+                    result = read(base_service->in_pipe_read_fd,
+                                    RDBD_RAW_MSG(base_service->msg) + base_service->msg_pos, 
+                                    (base_service->msg->header.msg_len + sizeof(struct rdbd_header)) - base_service->msg_pos);//read body
+                    if(result > 0)
+                    {
+                        base_service->msg_pos += result;
+                    }
+                    if(base_service->msg_pos == (base_service->msg->header.msg_len + sizeof(struct rdbd_header)))//read done
+                    {
+                        service = rdbd_service_get(base_service->rdbd, base_service->msg->header.source);
+                        if(RT_NULL == service || service->status != RDBD_SERVICE_STATUS_RUNNING)
+                        {
+                            //drop
+                            LOG_W("Not found service %d or service not running", base_service->msg->header.source);
+                        }
+                        else if(service == base_service)
+                        {
+                            rdbd_service_base_response(base_service, (char *)base_service->msg->msg, base_service->msg->header.msg_len, &response, &response_size);
+                            free(base_service->msg);
+                            base_service->msg = RT_NULL;
+                            if(response != RT_NULL)
+                            {
+                                base_service->msg = rt_malloc(response_size + sizeof(struct rdbd_header));
+                                base_service->msg->header.source = RDBD_SERVICE_ID_CONTROL;
+                                base_service->msg->header.msg_len = response_size;
+                                rt_memcpy(base_service->msg->msg, response, response_size);
+                                free(response);
+                                response = RT_NULL;
+                                response_size = 0;
+                                rdbd_service_request_write(base_service, base_service->msg);
+                                base_service->msg = RT_NULL;
+                            }
+                            
+                        }
+                        else
+                        {
+                            rdbd_service_request_write(service, base_service->msg);
+                            base_service->msg = RT_NULL;//restart receive
+                        }
+                    }
+                }
+            }
+
+            if(!rt_list_isempty(&base_service->request_write_list))//write data to pc
+            {
+                if(FD_ISSET(base_service->out_pipe_write_fd, &writeset))
+                {
+                    request = rt_list_entry(base_service->request_write_list.next, struct rdbd_request_write, list);
+                    result = write(base_service->out_pipe_write_fd,
+                                                RDBD_RAW_MSG(request->msg) + request->msg_pos, RDBD_MSG_LEN(request->msg) - request->msg_pos);
+                    if(result > 0)
+                    {
+                        request->msg_pos += result;
+                    }
+                    if(request->msg_pos == RDBD_MSG_LEN(request->msg))
+                    {
+                        rdbd_service_request_delete(request);
+                    }
+                }
+            }
+
+            for(node = base_service->rdbd->service_list.next; 
+                node != &base_service->rdbd->service_list; 
+                node = node->next)// poll all service
+            {
+                service = rt_list_entry(node, struct rdbd_service, list);
+                if(service == base_service || service->status != RDBD_SERVICE_STATUS_RUNNING)
+                {
+                    continue;
+                }
+                if(service->flag & RDBD_SERVICE_FLAG_WR)
+                {
+                    if(FD_ISSET(service->out_pipe_read_fd, &readset))
+                    {
+                        service->msg_pos = 0;
+                        service->msg = rt_malloc(RDB_RINGBUFF_SIZE + sizeof(struct rdbd_header));
+                        service->msg->header.source = service->service_id;
+                        service->msg->header.msg_len = read(service->out_pipe_read_fd, service->msg->msg, RDB_RINGBUFF_SIZE);
+                        if(service->msg->header.msg_len <= 0)
+                        {
+                            free(service->msg);
+                        }
+                        else
+                        {
+                            service->msg = rt_realloc(service->msg, RDBD_MSG_LEN(service->msg));
+                            rdbd_service_request_write(base_service, service->msg);
+                        }
+                        service->msg = RT_NULL;
+                    }
+                }
+                if((service->flag & RDBD_SERVICE_FLAG_RD) && !rt_list_isempty(&service->request_write_list))
+                {
+                    if(FD_ISSET(service->in_pipe_write_fd, &writeset))
+                    {
+                        request = rt_list_entry(service->request_write_list.next, struct rdbd_request_write, list);
+                        if(request->msg_pos == 0)
+                        {
+                            request->msg_pos = sizeof(struct rdbd_header);
+                        }
+                        result = write(service->in_pipe_write_fd,
+                                                    RDBD_RAW_MSG(request->msg) + request->msg_pos, RDBD_MSG_LEN(request->msg) - request->msg_pos);
+                        if(result > 0)
+                        {
+                            request->msg_pos += result;
+                        }
+                        if(request->msg_pos == RDBD_MSG_LEN(request->msg))
+                        {
+                            rdbd_service_request_delete(request);//write done
+                        }
+                    }
+                }
+            }
+        }
+        else
+        {
+            RT_ASSERT(0);
+        }
+    }
+}
+
+static char transfer_read_buff[RDB_RINGBUFF_SIZE];
+static char transfer_write_buff[RDB_RINGBUFF_SIZE];
+struct rt_completion transfer_write_completion;
+
+static void transfer_read_callback(void * context, int size)
+{
+    struct rdbd_service * base_service= (struct rdbd_service *)context;
+    fd_set writeset;
+    int res = 0;
+    int max_fd = base_service->in_pipe_write_fd + 1;
+    int write_len = 0;
+    char * pwrite = transfer_read_buff;
+    rt_list_t * node;
+    if(size < 0)
+    {
+        base_service->rdbd->status = RDBD_STATUS_DISCONNECTED;
+        rdbd_service_control(base_service, RDBD_SERVICE_SUSPEND, base_service);
+        return;
+    }
+    while(size)
+    {
+        FD_ZERO(&writeset);
+        FD_SET(base_service->in_pipe_write_fd, &writeset);
+        res = select(max_fd, RT_NULL, &writeset, RT_NULL, RT_NULL);
+        if(res < 0)
+        {
+            RT_ASSERT(0);
+        }
+        else if(res > 0)
+        {
+            write_len = write(base_service->in_pipe_write_fd, pwrite, size);
+            LOG_I("Read size:%d",write_len);
+            if(write_len > 0)
+            {
+                size -= write_len;
+                pwrite += write_len;
+            }
+        }
+    }
+    if(base_service->rdbd->private_transfer_ops->read(transfer_read_buff, sizeof(transfer_read_buff), transfer_read_callback, base_service) < 0)
+    {
+        base_service->rdbd->status = RDBD_STATUS_DISCONNECTED;
+        
+        for(node = base_service->rdbd->service_list.next; 
+            node != &base_service->rdbd->service_list; 
+            node = node->next)// poll all service
+        {
+            rdbd_service_control(rt_list_entry(node, struct rdbd_service, list), RDBD_SERVICE_SUSPEND, rt_list_entry(node, struct rdbd_service, list));
+        }
+    }
+}
+
+static void transfer_write_callback(void * context, int size)
+{
+    LOG_I("Write size:%d", size);
+    rt_completion_done(&transfer_write_completion);
+}
+
+static void transfer_thread_entry(void * arg)
+{
+    struct rdbd_service * base_service= (struct rdbd_service *)arg;
+    fd_set readset;
+    int res = 0;
+    int max_fd = base_service->out_pipe_read_fd + 1;
+    int write_size = 0;
+    struct timeval timeout;
+    rt_list_t * node;
+    while(1)
+    {
+        if(base_service->rdbd->status == RDBD_STATUS_DISCONNECTED)
+        {
+            if(base_service->rdbd->private_transfer_ops->read(transfer_read_buff, sizeof(transfer_read_buff), transfer_read_callback, base_service) >= 0)
+            {
+                base_service->rdbd->status = RDBD_STATUS_CONNECTED;
+                for(node = base_service->rdbd->service_list.next; 
+                    node != &base_service->rdbd->service_list; 
+                    node = node->next)// poll all service
+                {
+                    rdbd_service_control(rt_list_entry(node, struct rdbd_service, list), RDBD_SERVICE_RESUME, rt_list_entry(node, struct rdbd_service, list));
+                }
+                continue;
+            }
+            rt_thread_delay(500);
+        }
+        else
+        {
+            FD_ZERO(&readset);
+            FD_SET(base_service->out_pipe_read_fd, &readset);
+            timeout.tv_sec=0;
+            timeout.tv_usec=500000; //timeout 500ms
+            res = select(max_fd, &readset, RT_NULL, RT_NULL, &timeout);
+            if(res < 0)
+            {
+                RT_ASSERT(0);
+            }
+            else if(res > 0)
+            {
+                if(FD_ISSET(base_service->out_pipe_read_fd, &readset))
+                {
+                    write_size = read(base_service->out_pipe_read_fd, transfer_write_buff, sizeof(transfer_write_buff));
+                    if(write_size > 0)
+                    {
+                        rt_completion_init(&transfer_write_completion);
+                        if(base_service->rdbd->private_transfer_ops->write(transfer_write_buff, write_size, transfer_write_callback, RT_NULL) >= 0)
+                        {
+                            rt_completion_wait(&transfer_write_completion, RT_WAITING_FOREVER);
+                        }
+                        else
+                        {
+                            rt_completion_wait(&transfer_write_completion, 1);
+                            base_service->rdbd->status = RDBD_STATUS_DISCONNECTED;
+                            for(node = base_service->rdbd->service_list.next; 
+                                node != &base_service->rdbd->service_list; 
+                                node = node->next)// poll all service
+                            {
+                                rdbd_service_control(rt_list_entry(node, struct rdbd_service, list), RDBD_SERVICE_SUSPEND, rt_list_entry(node, struct rdbd_service, list));
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
+
+static int start(void * args)
+{
+    struct rdbd_service * base_service = (struct rdbd_service *)args;
+    rt_thread_t tid = RT_NULL;
+
+    if(RT_NULL == base_service)
+    {
+         LOG_E("Start up service error, args is null");
+         return -1;
+    }
+    
+    if(base_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        base_service->in_pipe_read_fd = open(base_service->in_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(base_service->in_pipe_read_fd < 0)
+        {
+            LOG_E("Start up service %s error open in pipe failed",base_service->name);
+            return -1;
+        }
+    }
+
+    if(base_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        base_service->out_pipe_write_fd = open(base_service->out_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(base_service->out_pipe_write_fd < 0)
+        {
+            LOG_E("Start up service %s error open out pipe failed",base_service->name);
+            goto _error;
+        }
+    }
+    
+    
+    base_service->service_thread = rt_thread_create(base_service->name,
+                                                    base_service_thread_entry,
+                                                    base_service,
+                                                    1024,
+                                                    23,
+                                                    20);
+    if(RT_NULL != base_service->service_thread)
+    {
+        if(RT_EOK != rt_thread_startup(base_service->service_thread))
+        {
+            rt_thread_delete(base_service->service_thread);
+            base_service->service_thread = RT_NULL;
+            goto _error;
+        }
+    }
+    else
+    {
+        goto _error;
+    }
+    
+    if(base_service->flag & RDBD_SERVICE_FLAG_RD)
+    {
+        base_service->in_pipe_write_fd = open(base_service->in_pipe_path, O_WRONLY | O_NONBLOCK, 0);
+        if(base_service->in_pipe_write_fd < 0)
+        {
+            LOG_E("Start up transfer error open in pipe failed",base_service->name);
+            return -1;
+        }
+    }
+
+    if(base_service->flag & RDBD_SERVICE_FLAG_WR)
+    {
+        base_service->out_pipe_read_fd = open(base_service->out_pipe_path, O_RDONLY | O_NONBLOCK, 0);
+        if(base_service->out_pipe_read_fd < 0)
+        {
+            LOG_E("Start up transfer error open out pipe failed",base_service->name);
+            goto _error;
+        }
+    }
+    
+    tid = rt_thread_create("rdbt",
+                            transfer_thread_entry,
+                            base_service,
+                            1024,
+                            22,
+                            20);
+    if(RT_NULL != tid)
+    {
+        if(RT_EOK != rt_thread_startup(tid))
+        {
+            rt_thread_delete(tid);
+            tid = RT_NULL;
+            goto _error;
+        }
+        base_service->user_data = tid;
+        return 0;
+    }
+_error:
+    if(base_service->in_pipe_read_fd >= 0)
+    {
+        close(base_service->in_pipe_read_fd);
+        base_service->in_pipe_read_fd = -1;
+    }
+    if(base_service->in_pipe_write_fd >= 0)
+    {
+        close(base_service->in_pipe_write_fd);
+        base_service->in_pipe_write_fd = -1;
+    }
+    
+    if(base_service->out_pipe_read_fd >= 0)
+    {
+        close(base_service->out_pipe_read_fd);
+        base_service->out_pipe_read_fd = -1;
+    }
+    if(base_service->out_pipe_write_fd >= 0)
+    {
+        close(base_service->out_pipe_write_fd);
+        base_service->out_pipe_write_fd = -1;
+    }
+    base_service->user_data = RT_NULL;
+    return -1;
+}
+
+static int stop(void * args)
+{
+    struct rdbd_service * base_service = (struct rdbd_service *)args;
+    rt_thread_t tid;
+    if(RT_NULL == base_service)
+    {
+         LOG_E("Stop service error, args is null");
+         return -1;
+    }
+    
+    tid = (rt_thread_t)base_service->user_data;
+    if(RT_EOK != rt_thread_delete(tid))
+    {
+        LOG_E("Delete thread %s failed", tid->name);
+        return -1;
+    }
+    tid = RT_NULL;
+    
+    if(RT_EOK != rt_thread_delete(base_service->service_thread))
+    {
+        LOG_E("Delete thread %s failed", base_service->service_thread->name);
+        return -1;
+    }
+    base_service->service_thread = RT_NULL;
+    
+    if(base_service->in_pipe_read_fd >= 0)
+    {
+        if(close(base_service->in_pipe_read_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", base_service->in_pipe_read_fd);
+            return -1;
+        }
+        base_service->in_pipe_read_fd = -1;
+    }
+    
+    if(base_service->in_pipe_write_fd >= 0)
+    {
+        if(close(base_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", base_service->in_pipe_write_fd);
+            return -1;
+        }
+        base_service->in_pipe_write_fd = -1;
+    }
+    
+    if(base_service->in_pipe_write_fd >= 0)
+    {
+        if(close(base_service->in_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", base_service->in_pipe_write_fd);
+            return -1;
+        }
+        base_service->in_pipe_write_fd = -1;
+    }
+    
+    if(base_service->out_pipe_write_fd >= 0)
+    {
+        if(close(base_service->out_pipe_write_fd) < 0)
+        {
+            LOG_E("Close fd %d failed", base_service->out_pipe_write_fd);
+            return -1;
+        }
+        base_service->out_pipe_write_fd = -1;
+    }
+    
+    base_service->user_data = RT_NULL;
+    return 0;
+}
+
+static int resume(void * args)
+{
+    //Not need
+    return 0;
+}
+static int suspend(void * args)
+{
+    //Not need
+    return 0;
+}
+#define RDBD_SERVICE_BASE_CMD_REQUEST_SERVICE_LIST 0x00
+
+static int rdbd_service_base_response(struct rdbd_service * base_service, char * request, int request_size, char ** response, int * response_size)
+{
+    int service_list_len = 0;
+    rt_list_t * node = RT_NULL;
+    *response = RT_NULL;
+    *response_size = 0;
+    int i = 0;
+    hex_dump("request", request, request_size);
+    switch(request[0])
+    {
+    case RDBD_SERVICE_BASE_CMD_REQUEST_SERVICE_LIST:
+        service_list_len = rt_list_len(&base_service->rdbd->service_list) - 1;//skip base service
+        *response = rt_malloc(service_list_len + 2);
+        *response_size = service_list_len + 2;
+        (*response)[i++] = 0;
+        (*response)[i++] = service_list_len;
+        for(node = base_service->rdbd->service_list.next; 
+                node != &base_service->rdbd->service_list; 
+                node = node->next)// poll all service
+        {
+            if(rt_list_entry(node, struct rdbd_service, list) != base_service)//skip base service
+            {
+                (*response)[i++] = rt_list_entry(node, struct rdbd_service, list)->service_id;
+            }
+        }
+        break;
+    default:
+        break;
+    }
+    return 0;
+}

+ 235 - 0
src/rdbd_service_manager.c

@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rdbd_service_manager.h>
+#include <string.h>
+
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "RDBD Service Manager"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+
+
+int rdbd_service_control(struct rdbd_service * service, int cmd, void * args)
+{
+    int result = 0;
+    if(RT_NULL == service)
+    {
+        LOG_E("Null can not be control");
+        return -1;
+    }
+    if(RT_NULL == service->rdbd)
+    {
+        LOG_E("Service %s not be install", service->name);
+        return -1;
+    }
+    switch(cmd)
+    {
+    case RDBD_SERVICE_START:
+        LOG_I("Service %s start", service->name);
+        if(RDBD_SERVICE_STATUS_STOP != service->status)
+        {
+            LOG_W("Service %s is allready start", service->name);
+            result = -1;
+            break;
+        }
+        result = service->control_ops->start(args);
+        if(0 == result)
+        {
+            if(rdbd_get_status(service->rdbd) == RDBD_STATUS_CONNECTED)
+            {
+                result = service->control_ops->resume(RT_NULL);
+                if(0 == result)
+                {
+                    service->status = RDBD_SERVICE_STATUS_RUNNING;
+                    LOG_I("Service %s is running", service->name);
+                }
+                else
+                {
+                    service->control_ops->stop(RT_NULL);//?
+                    LOG_E("Service %s start failed!", service->name);
+                }
+            }
+            else
+            {
+                service->status = RDBD_SERVICE_STATUS_SUSPENDED;
+                LOG_I("Service %s is suspended", service->name);
+            }
+        }
+        break;
+    case RDBD_SERVICE_STOP:
+        if(RDBD_SERVICE_STATUS_STOP == service->status)
+        {
+            LOG_W("Service %s is not start", service->name);
+            result = -1;
+            break;
+        }
+        result = service->control_ops->stop(args);
+        if(0 == result)
+        {
+            LOG_I("Service %s stop", service->name);
+            service->status = RDBD_SERVICE_STATUS_STOP;
+        }
+        else
+        {
+            LOG_E("Service %s stop error!", service->name);
+        }
+        break;
+    case RDBD_SERVICE_SUSPEND:
+        if(RDBD_SERVICE_STATUS_STOP == service->status)
+        {
+            LOG_W("Service %s is not start", service->name);
+            result = -1;
+            break;
+        }
+        if(RDBD_SERVICE_STATUS_SUSPENDED == service->status)
+        {
+            LOG_W("Service %s is allready suspended", service->name);
+            result = 0;
+            break;
+        }
+        result = service->control_ops->suspend(args);
+        if(0 == result)
+        {
+            LOG_I("Service %s suspended", service->name);
+            service->status = RDBD_SERVICE_STATUS_SUSPENDED;
+        }
+        else
+        {
+            LOG_E("Service %s stop error!", service->name);
+        }
+        break;
+    case RDBD_SERVICE_RESUME:
+        if(RDBD_SERVICE_STATUS_STOP == service->status)
+        {
+            LOG_W("Service %s is not start", service->name);
+            result = -1;
+            break;
+        }
+        if(RDBD_SERVICE_STATUS_RUNNING == service->status)
+        {
+            LOG_W("Service %s is allready running", service->name);
+            result = 0;
+            break;
+        }
+        result = service->control_ops->resume(args);
+        if(0 == result)
+        {
+            LOG_I("Service %s running", service->name);
+            service->status = RDBD_SERVICE_STATUS_RUNNING;
+        }
+        else
+        {
+            LOG_E("Service %s resume error!", service->name);
+        }
+        break;
+    case RDBD_SERVICE_GET_STATUS:
+        if(RT_NULL == args)
+        {
+            LOG_E("Service %s get status args invalid", service->name);
+            result = -1;
+            break;
+        }
+        *((int *)args) = service->status;
+        break;
+    default:
+        LOG_W("Service %s control cmd invalid", service->name);
+        break;
+    }
+    return result;
+}
+
+int rdbd_service_install(rdbd_t rdbd, struct rdbd_service * service)
+{
+    if(RT_NULL == service)
+    {
+        LOG_E("Service install failed service is null");
+        return -1;
+    }
+    if(RT_NULL == rdbd)
+    {
+        LOG_E("Service %s install failed rdbd is null", service->name);
+        return -1;
+    }
+    if(RT_NULL != service->rdbd)
+    {
+        LOG_E("Service %s allready be installed to %s", service->name, service->rdbd->name);
+        return -1;
+    }
+    rt_list_insert_before(&rdbd->service_list, &service->list);
+    service->rdbd = rdbd;
+    LOG_I("Service %s installed to %s", service->name, service->rdbd->name);
+    return 0;
+}
+
+int rdbd_service_uninstall(struct rdbd_service * service)
+{
+    if(RT_NULL == service)
+    {
+        LOG_E("Uninstall failed service is null");
+        return -1;
+    }
+    if(RT_NULL == service->rdbd)
+    {
+        LOG_E("Service %s is nor be installed", service->name);
+        return -1;
+    }
+    rt_list_remove(&service->list);
+    service->rdbd = RT_NULL;
+    return 0;
+}
+
+struct rdbd_service * rdbd_service_find(rdbd_t rdbd, const char * name)
+{
+    rt_list_t * l;
+    struct rdbd_service * service;
+    for(l = rdbd->service_list.next;
+        l != &rdbd->service_list; 
+        l = l->next)
+    {
+        service = rt_list_entry(l, struct rdbd_service, list);
+        if(0 == strcmp(service->name, name))
+        {
+            return service;
+        }
+    }
+    return RT_NULL;
+}
+
+struct rdbd_service * rdbd_service_get(rdbd_t rdbd, rt_uint8_t service_id)
+{
+    rt_list_t * l;
+    struct rdbd_service * service;
+    for(l = rdbd->service_list.next;
+        l != &rdbd->service_list; 
+        l = l->next)
+    {
+        service = rt_list_entry(l, struct rdbd_service, list);
+        if(service_id == service->service_id)
+        {
+            return service;
+        }
+    }
+    return RT_NULL;
+}
+

+ 355 - 0
src/usb_rdbd.c

@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 2018, Real-Thread Information Technology Ltd
+ * All rights reserved
+ *
+ * This software is dual-licensed: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation. For the terms of this
+ * license, see <http://www.gnu.org/licenses/>.
+ *
+ * You are free to use this software under the terms of the GNU General
+ * Public License, 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.
+ *
+ * Alternatively, you can license this software under a commercial
+ * license, please send mail to business@rt-thread.com for contact. 
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2018-09-25     ZYH          the first version
+ */
+
+#include <rtthread.h>
+#ifdef PKGS_USING_USB_RDBD
+#include <rthw.h>
+#include <rtservice.h>
+#include <rtdevice.h>
+#include <drivers/usb_device.h>
+#include "rdbd.h"
+#include "usb_rdbd.h"
+#include <string.h>
+
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME  "URDBD"
+#define DBG_LEVEL         DBG_LOG
+#define DBG_COLOR
+#include <rtdbg.h>
+
+static uep_t rdbd_ep_out;
+static uep_t rdbd_ep_in;
+static ufunction_t rdbd_func = RT_NULL;
+void (* usb_rdb_write_callback)(void * context, int size) = RT_NULL;
+void * usb_rdb_write_context = RT_NULL;
+
+void (* usb_rdb_read_callback)(void * context, int size) = RT_NULL;
+void * usb_rdb_read_context = RT_NULL;
+static struct udevice_descriptor dev_desc =
+{
+    USB_DESC_LENGTH_DEVICE,     //bLength;
+    USB_DESC_TYPE_DEVICE,       //type;
+    USB_BCD_VERSION,            //bcdUSB;
+    0x00,                       //bDeviceClass;
+    0x00,                       //bDeviceSubClass;
+    0x00,                       //bDeviceProtocol;
+    0x40,                       //bMaxPacketSize0;
+    _VENDOR_ID,                 //idVendor;
+    _PRODUCT_ID,                //idProduct;
+    USB_BCD_DEVICE,             //bcdDevice;
+    USB_STRING_MANU_INDEX,      //iManufacturer;
+    USB_STRING_PRODUCT_INDEX,   //iProduct;
+    USB_STRING_SERIAL_INDEX,    //iSerialNumber;
+    USB_DYNAMIC,                //bNumConfigurations;
+};
+//FS and HS needed
+static struct usb_qualifier_descriptor dev_qualifier =
+{
+    sizeof(dev_qualifier),          //bLength
+    USB_DESC_TYPE_DEVICEQUALIFIER,  //bDescriptorType
+    0x0200,                         //bcdUSB
+    0xFF,                           //bDeviceClass
+    0x00,                           //bDeviceSubClass
+    0x00,                           //bDeviceProtocol
+    64,                             //bMaxPacketSize0
+    0x01,                           //bNumConfigurations
+    0,
+};
+
+struct usb_rdbd_descriptor _usb_rdbd_desc =
+{
+#ifdef RT_USB_DEVICE_COMPOSITE
+    /* Interface Association Descriptor */
+    USB_DESC_LENGTH_IAD,
+    USB_DESC_TYPE_IAD,
+    USB_DYNAMIC,
+    0x01,
+    0xFF,
+    0x00,
+    0x00,
+    0x00,
+#endif
+    /*interface descriptor*/
+    USB_DESC_LENGTH_INTERFACE,  //bLength;
+    USB_DESC_TYPE_INTERFACE,    //type;
+    USB_DYNAMIC,                //bInterfaceNumber;
+    0x00,                       //bAlternateSetting;
+    0x02,                       //bNumEndpoints
+    0xFF,                       //bInterfaceClass;
+    0x00,                       //bInterfaceSubClass;
+    0x00,                       //bInterfaceProtocol;
+    0x00,                       //iInterface;
+    /*endpoint descriptor*/
+    USB_DESC_LENGTH_ENDPOINT,
+    USB_DESC_TYPE_ENDPOINT,
+    USB_DYNAMIC | USB_DIR_OUT,
+    USB_EP_ATTR_BULK,
+    USB_DYNAMIC,
+    0x00,
+    /*endpoint descriptor*/
+    USB_DESC_LENGTH_ENDPOINT,
+    USB_DESC_TYPE_ENDPOINT,
+    USB_DYNAMIC | USB_DIR_IN,
+    USB_EP_ATTR_BULK,
+    USB_DYNAMIC,
+    0x00,
+};
+
+
+const static char *_ustring[] =
+{
+    "Language",
+    "RT-Thread Team.",
+    "RT-Thread Debug Bridge",
+    "32021919830108",
+    "Configuration",
+    "Interface",
+    USB_STRING_OS//must be
+};
+struct usb_os_proerty usb_rdbd_proerty[] =
+{
+    USB_OS_PROERTY_DESC(USB_OS_PROERTY_TYPE_REG_SZ, "DeviceInterfaceGUID", "{af2c84af-785c-4aba-ad24-72c5bbcd0504}"),
+};
+
+struct usb_os_function_comp_id_descriptor rdbd_usb_func_comp_id_desc =
+{
+    .bFirstInterfaceNumber = USB_DYNAMIC,
+    .reserved1          = 0x01,
+    .compatibleID       = {'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00},
+    .subCompatibleID    = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
+    .reserved2          = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
+};
+
+void rt_hex_dump(const char *name, const char *buf, rt_size_t size)
+ {
+ #define __is_print(ch)       ((unsigned int)((ch) - ' ') < 127u - ' ')
+ #define WIDTH_SIZE           16
+
+     rt_size_t i, j;
+
+     for (i = 0; i < size; i += WIDTH_SIZE)
+     {
+         rt_kprintf("[D/HEX] %s: %04X-%04X: ", name, i, i + WIDTH_SIZE);
+         for (j = 0; j < WIDTH_SIZE; j++)
+         {
+             if (i + j < size)
+             {
+                 rt_kprintf("%02X ", buf[i + j]);
+             }
+             else
+             {
+                 rt_kprintf("   ");
+             }
+             if ((j + 1) % 8 == 0)
+             {
+                 rt_kprintf(" ");
+             }
+         }
+         rt_kprintf("  ");
+         for (j = 0; j < WIDTH_SIZE; j++)
+         {
+             if (i + j < size)
+             {
+                 rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
+             }
+         }
+         rt_kprintf("\n");
+     }
+ }
+static rt_err_t _ep_out_handler(ufunction_t func, rt_size_t size)
+{
+    if(RT_NULL != usb_rdb_read_callback)
+    {
+        usb_rdb_read_callback(usb_rdb_read_context, size);
+    }
+    return RT_EOK;
+}
+static rt_err_t _ep_in_handler(ufunction_t func, rt_size_t size)
+{
+    if(RT_NULL != usb_rdb_write_callback)
+    {
+        usb_rdb_write_callback(usb_rdb_write_context, size);
+    }
+    return RT_EOK;
+}
+
+static rt_err_t _interface_handler(ufunction_t func, ureq_t setup)
+{
+    switch (setup->bRequest)
+    {
+    case 'A':
+        switch (setup->wIndex)
+        {
+        case 0x05:
+            usbd_os_proerty_descriptor_send(func, setup, usb_rdbd_proerty, sizeof(usb_rdbd_proerty) / sizeof(usb_rdbd_proerty[0]));
+            break;
+        }
+        break;
+     default:
+        LOG_E("unsupport request 0x%02X", setup->bRequest);
+        rt_usbd_ep0_set_stall(func->device);
+        break;
+    }
+    return RT_EOK;
+}
+static rt_err_t _function_enable(ufunction_t func)
+{
+    RT_ASSERT(func != RT_NULL);
+    return RT_EOK;
+}
+static rt_err_t _function_disable(ufunction_t func)
+{
+    RT_ASSERT(func != RT_NULL);
+    if(RT_NULL != usb_rdb_read_callback)
+    {
+        usb_rdb_read_callback(usb_rdb_read_context, -1);
+    }
+    return RT_EOK;
+}
+
+static struct ufunction_ops ops =
+{
+    _function_enable,
+    _function_disable,
+    RT_NULL,
+};
+
+static rt_err_t _rdbd_usb_descriptor_config(usb_rdbd_desc_t rdbd_desc, rt_uint8_t cintf_nr, rt_uint8_t device_is_hs)
+{
+#ifdef RT_USB_DEVICE_COMPOSITE
+    rdbd_desc->iad_desc.bFirstInterface = cintf_nr;
+#endif
+    rdbd_desc->ep_out_desc.wMaxPacketSize = device_is_hs ? 512 : 64;
+    rdbd_desc->ep_in_desc.wMaxPacketSize = device_is_hs ? 512 : 64;
+    rdbd_usb_func_comp_id_desc.bFirstInterfaceNumber = cintf_nr;
+    return RT_EOK;
+}
+
+static rt_err_t rt_usb_rdbd_usb_init(ufunction_t func)
+{
+    return RT_EOK;
+}
+
+ufunction_t rt_usbd_function_rdbd_usb_create(udevice_t device)
+{
+    uintf_t             rdbd_usb_intf;
+    ualtsetting_t       rdbd_usb_setting;
+    usb_rdbd_desc_t       rdbd_usb_desc;
+
+    /* parameter check */
+    RT_ASSERT(device != RT_NULL);
+
+    /* set usb device string description */
+    rt_usbd_device_set_string(device, _ustring);
+
+    /* create a cdc function */
+    rdbd_func = rt_usbd_function_new(device, &dev_desc, &ops);
+    rt_usbd_device_set_qualifier(device, &dev_qualifier);
+
+    /* create an interface object */
+    rdbd_usb_intf = rt_usbd_interface_new(device, _interface_handler);
+
+    /* create an alternate setting object */
+    rdbd_usb_setting = rt_usbd_altsetting_new(sizeof(struct usb_rdbd_descriptor));
+
+    /* config desc in alternate setting */
+    rt_usbd_altsetting_config_descriptor(rdbd_usb_setting, &_usb_rdbd_desc, (rt_off_t) & ((usb_rdbd_desc_t)0)->intf_desc);
+
+    /* configure the hid interface descriptor */
+    _rdbd_usb_descriptor_config(rdbd_usb_setting->desc, rdbd_usb_intf->intf_num, device->dcd->device_is_hs);
+
+    /* create endpoint */
+    rdbd_usb_desc = (usb_rdbd_desc_t)rdbd_usb_setting->desc;
+    rdbd_ep_out = rt_usbd_endpoint_new(&rdbd_usb_desc->ep_out_desc, _ep_out_handler);
+    rdbd_ep_in  = rt_usbd_endpoint_new(&rdbd_usb_desc->ep_in_desc, _ep_in_handler);
+
+    /* add the int out and int in endpoint to the alternate setting */
+    rt_usbd_altsetting_add_endpoint(rdbd_usb_setting, rdbd_ep_out);
+    rt_usbd_altsetting_add_endpoint(rdbd_usb_setting, rdbd_ep_in);
+
+    /* add the alternate setting to the interface, then set default setting */
+    rt_usbd_interface_add_altsetting(rdbd_usb_intf, rdbd_usb_setting);
+    rt_usbd_set_altsetting(rdbd_usb_intf, 0);
+
+    /* add the interface to the mass storage function */
+    rt_usbd_function_add_interface(rdbd_func, rdbd_usb_intf);
+
+    rt_usbd_os_comp_id_desc_add_os_func_comp_id_desc(device->os_comp_id_desc, &rdbd_usb_func_comp_id_desc);
+    /* initilize winusb */
+    rt_usb_rdbd_usb_init(rdbd_func);
+    return rdbd_func;
+}
+
+struct udclass rdbd_usb_class =
+{
+    .rt_usbd_function_create = rt_usbd_function_rdbd_usb_create
+};
+
+int rt_usbd_rdbd_usb_class_register(void)
+{
+    rt_usbd_class_register(&rdbd_usb_class);
+    return 0;
+}
+INIT_PREV_EXPORT(rt_usbd_rdbd_usb_class_register);
+
+
+static int rdbd_usb_read(void * buffer, size_t size, void (* callback)(void * context, int size), void * context)
+{
+    if (rdbd_func->device->state != USB_STATE_CONFIGURED)
+    {
+        return -1;//return disconnect
+    }
+    
+    usb_rdb_read_callback = callback;
+    usb_rdb_read_context = context;
+
+    rdbd_ep_out->request.buffer = buffer;
+    rdbd_ep_out->request.size = size;
+    rdbd_ep_out->request.req_type = UIO_REQUEST_READ_BEST;
+    rt_usbd_io_request(rdbd_func->device, rdbd_ep_out, &rdbd_ep_out->request);
+    return size;
+}
+
+static int rdbd_usb_write(const void * buffer, size_t size,void (* callback)(void * context, int size), void * context)
+{
+    if (rdbd_func->device->state != USB_STATE_CONFIGURED)
+    {
+        return -1;//return disconnect
+    }
+    usb_rdb_write_callback = callback;
+    usb_rdb_write_context = context;
+
+    rdbd_ep_in->buffer = (void *)buffer;
+    rdbd_ep_in->request.buffer = rdbd_ep_in->buffer;
+    rdbd_ep_in->request.size = size;
+    rdbd_ep_in->request.req_type = UIO_REQUEST_WRITE;
+    rt_usbd_io_request(rdbd_func->device, rdbd_ep_in, &rdbd_ep_in->request);
+    return size;
+}
+
+struct rdbd_transfer_ops usb_rdb_transfer_ops = 
+{
+    rdbd_usb_read,
+    rdbd_usb_write
+};
+
+#endif