├── .gitignore ├── LICENSE ├── README.md ├── SConscript ├── docs ├── cmux_basic.md ├── cmux_port.md └── figures │ ├── cmux_course.png │ ├── cmux_data_store.png │ ├── cmux_frame.png │ ├── modify.png │ ├── prepare_modify.png │ ├── private control.png │ └── protocol_stack.png ├── inc ├── cmux.h └── gsm │ └── cmux_chat.h ├── sample └── cmux_sample_gsm.c └── src ├── cmux.c ├── cmux_utils.c └── gsm ├── cmux_chat.c └── cmux_gsm.c /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Object files 5 | *.o 6 | *.ko 7 | *.obj 8 | *.elf 9 | 10 | # Linker output 11 | *.ilk 12 | *.map 13 | *.exp 14 | 15 | # Precompiled Headers 16 | *.gch 17 | *.pch 18 | 19 | # Libraries 20 | *.lib 21 | *.a 22 | *.la 23 | *.lo 24 | 25 | # Shared objects (inc. Windows DLLs) 26 | *.dll 27 | *.so 28 | *.so.* 29 | *.dylib 30 | 31 | # Executables 32 | *.exe 33 | *.out 34 | *.app 35 | *.i*86 36 | *.x86_64 37 | *.hex 38 | 39 | # Debug files 40 | *.dSYM/ 41 | *.su 42 | *.idb 43 | *.pdb 44 | 45 | # Kernel Module Compile Results 46 | *.mod* 47 | *.cmd 48 | .tmp_versions/ 49 | modules.order 50 | Module.symvers 51 | Mkfile.old 52 | dkms.conf 53 | 54 | # folder 55 | /.vscode 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CMUX 2 | 3 | ## 1. 简介 4 | 5 | CMUX(Connection Multiplexing ),即连接(串口)多路复用,其功能主要在一个真实的物理通道上虚拟多个通道,每个虚拟通道上的连接和数据通讯可独立进行。 6 | 7 | CMUX 软件包常用于蜂窝模块串口复用功能(PPP + AT 模式),以及串口硬件资源受限的设备。 8 | 9 | CMUX 软件包特点如下: 10 | 11 | - 支持通过真实串口虚拟出多个串口; 12 | - 支持所有基于 GSM0707/ 0710 协议的蜂窝模块; 13 | - 支持无缝接入 PPP 功能; 14 | 15 | 目前 CMUX 的 GSM 功能支持 Luat Air720, SIM7600, SIM800C 模块,后续会接入更多蜂窝模块。 16 | 17 | ### 1.1 框架图 18 | 19 | ![](docs/figures/cmux_frame.png) 20 | 21 | - CMUX 是一种类似于传输层的协议,用户使用时无法感知该层;数据传输依赖一个真实串口传输,cmux 层负责解析数据用以分发到不同的 virtual uart ;从而实现一个真实串口虚拟出多个 UART 的目的 22 | 23 | - CMUX 在应用场景中多用于 UART, 如有必要也可以支持 SPI 方式 24 | 25 | ### 1.2 目录结构 26 | ```shell 27 | cmux 28 | ├───docs 29 | │ └───figures // 文档使用图片 30 | ├───inc // 头文件 31 | │ │───gsm 32 | │ │ └─── cmux_chat.h 33 | │ └─── cmux.h 34 | ├───sample // 示例文件 35 | │ └─── cmux_sample_gsm.c 36 | ├───src // 源码文件 37 | │ ├───gsm 38 | │ │ ├─── cmux_chat.c 39 | │ │ └─── cmux_gsm.c 40 | │ ├─── cmux_utils.c 41 | │ └─── cmux.c 42 | ├───LICENSE // 软件包许可证 43 | ├───README.md // 软件包使用说明 44 | └───SConscript // RT-Thread 默认的构建脚本 45 | ``` 46 | 47 | ### 1.3 许可证 48 | 49 | cmux 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。 50 | 51 | ## 2. 获取方式 52 | 53 | ​ **CMUX 软件包相关配置选项介绍** 54 | 55 | ```c 56 | [*] cmux protocol for rt-thread. ---> 57 | [ ] using cmux debug feature (NEW) 58 | [*] using for gsm0710 protocol (NEW) 59 | (5) set cmux max frame list length (NEW) 60 | (uart2) the real cmux serial prot (NEW) 61 | (3) the number of cmux modem port (NEW) 62 | Version (latest) ---> 63 | ``` 64 | 65 | - **using cmux debug feature:** 开启调试日志功能 66 | - **using for gsm0710 protocol:** 打开以支持蜂窝模块 67 | - **set cmux max frame list length:** 设置虚拟端口的 frame 链的最大长度 68 | - **the real cmux serial prot:** cmux 使用的真实串口名称 69 | - **the number of cmux modem port:** 蜂窝模块支持的虚拟串口数量 70 | - **the command for cmux function:** 进入 cmux 模式的命令 71 | - **Version:** 软件包版本号 72 | 73 | ## 3. 使用方式 74 | 75 | cmux 软件包初始化函数如下所示: 76 | 77 | **cmux 功能启动函数,该函数自动调用** 78 | 79 | ```c 80 | int cmux_sample(void) 81 | { 82 | rt_err_t result; 83 | 84 | /* find cmux object through the actual serial name < the actual serial has been related in the cmux.c file > */ 85 | sample = cmux_object_find(CMUX_DEPEND_NAME); 86 | if (sample == RT_NULL) 87 | { 88 | result = -RT_ERROR; 89 | LOG_E("Can't find %s", CMUX_DEPEND_NAME); 90 | goto end; 91 | } 92 | /* startup the cmux receive thread, attach control chananel and open it */ 93 | result = cmux_start(sample); 94 | if (result != RT_EOK) 95 | { 96 | LOG_E("cmux sample start error. Can't find %s", CMUX_DEPEND_NAME); 97 | goto end; 98 | } 99 | LOG_I("cmux sample (%s) start successful.", CMUX_DEPEND_NAME); 100 | 101 | /* attach AT function into cmux */ 102 | result = cmux_attach(sample, CMUX_AT_PORT, CMUX_AT_NAME, RT_DEVICE_FLAG_DMA_RX, RT_NULL); 103 | if (result != RT_EOK) 104 | { 105 | LOG_E("cmux attach (%s) failed.", CMUX_AT_NAME); 106 | goto end; 107 | } 108 | LOG_I("cmux object channel (%s) attach successful.", CMUX_AT_NAME); 109 | 110 | /* attach PPP function into cmux */ 111 | result = cmux_attach(sample, CMUX_PPP_PORT, CMUX_PPP_NAME, RT_DEVICE_FLAG_DMA_RX, RT_NULL); 112 | if (result != RT_EOK) 113 | { 114 | LOG_E("cmux attach %s failed.", CMUX_PPP_NAME); 115 | goto end; 116 | } 117 | LOG_I("cmux object channel (%s) attach successful.", CMUX_PPP_NAME); 118 | end: 119 | return RT_EOK; 120 | } 121 | #ifdef CMUX_ATUO_INITIZATION 122 | // 自动初始化 123 | INIT_APP_EXPORT(cmux_sample_start); 124 | #endif 125 | // 命令导出到MSH( cmux_sample_start 变更为cmux_start ) 126 | MSH_CMD_EXPORT_ALIAS(cmux_sample_start, cmux_start, a sample of cmux function); 127 | ``` 128 | 129 | * 模块进入 cmux 模式,注册 AT 和 PPP 接口到 CMUX 上; 130 | 131 | 132 | 133 | 模块上电后,自动初始化流程如下: 134 | 135 | ```shell 136 | \ | / 137 | - RT - Thread Operating System 138 | / | \ 4.0.2 build Apr 14 2020 139 | 2006 - 2019 Copyright by rt-thread team 140 | lwIP-2.0.2 initialized! 141 | [I/sal.skt] Socket Abstraction Layer initialize success. 142 | [I/ppp.dev] ppp_device(pp) register successfully. 143 | [I/cmux] cmux rely on (uart3) init successful. 144 | [I/cmux.air720] cmux has been control uart3. 145 | msh >[W/chat] timeout 146 | [I/cmux.sample] cmux sample (uart3) start successful. 147 | [I/cmux.sample] cmux object channel (cmux_at) attach successful. 148 | [I/cmux.sample] cmux object channel (cmux_ppp) attach successful. 149 | ``` 150 | 151 | 设备上电初始化完成,模块提示模块已经进入 CMUX 模式,可以使用已经注册的虚拟串口。 152 | 153 | 154 | 155 | 你也可以同时使用 PPP_DEVICE 提供 ppp 功能,效果如下: 156 | 157 | ```shell 158 | \ | / 159 | - RT - Thread Operating System 160 | / | \ 4.0.2 build Apr 14 2020 161 | 2006 - 2019 Copyright by rt-thread team 162 | lwIP-2.0.2 initialized! 163 | [I/sal.skt] Socket Abstraction Layer initialize success. 164 | [I/ppp.dev] ppp_device(pp) register successfully. 165 | [I/cmux] cmux rely on (uart3) init successful. 166 | [I/cmux.air720] cmux has been control uart3. 167 | msh >[I/cmux.sample] cmux sample (uart3) start successful. 168 | [I/cmux.sample] cmux object channel (cmux_at) attach successful. 169 | [I/cmux.sample] cmux object channel (cmux_ppp) attach successful. 170 | msh >ppp_start 171 | [I/ppp.dev] ppp_device connect successfully. 172 | ping www.baidu.com 173 | 60 bytes from 39.156.66.14 icmp_seq=0 ttl=49 time=126 ms 174 | 60 bytes from 39.156.66.14 icmp_seq=1 ttl=49 time=129 ms 175 | 60 bytes from 39.156.66.14 icmp_seq=2 ttl=49 time=111 ms 176 | 60 bytes from 39.156.66.14 icmp_seq=3 ttl=49 time=111 ms 177 | msh >ready 178 | msh >[D/main] cmux control channel has been open. 179 | [D/main] write data : 9 180 | [D/main] 15 ,Recieve 181 | +CSQ: 29,99 182 | 183 | [D/main] 6 ,Recieve 184 | OK 185 | ``` 186 | 在使用 PPP 功能的同时,可以使用虚拟出的 cmux_at 串口同时读取模块信号强度值,具体可以参考[<使用文档>](./docs/cmux_port.md) 187 | 188 | ## 4. 注意事项 189 | 190 | * 使用 PPP 功能详情参考 [PPP_DEVICE](https://github.com/RT-Thread-packages/ppp_device) 191 | * 只有在虚拟串口注册到 rt_device 框架后才能通过 rt_device_find 找到虚拟串口,要注意先后顺序 192 | * 虚拟串口 attach 后并不能直接使用,必须通过 rt_device_open 打开后才能使用,符合 rt_device 的操作流程 193 | * 只有进入 cmux 的命令,没有退出 cmux 的命令;所以说,只能通信模块硬重启,而不能软重启,使用时候要注意 194 | 195 | ## 5. 联系方式 196 | 197 | 联系人:xiangxistu 198 | 199 | Email: liuxianliang@rt-thread.com 200 | 201 | 202 | 203 | 对 CMUX 有疑惑,或者对 CMUX 感兴趣的开发者欢迎入群详细了解。 204 | 205 | QQ群:749347156 [<传送门>](https://jq.qq.com/?_wv=1027&k=5KcuPGI) 206 | 207 | ## 6. 相关文档 208 | CMUX 详细介绍:[<文档>](./docs/cmux_basic.md) 209 | 210 | 通过 CMUX 使用 PPP_DEVICE 文档:[<使用文档>](./docs/cmux_port.md) 211 | 212 | ## 7. 致谢 213 | 214 | 感谢网友 @ya-jeks 在 github 上的 gsmmux 参考代码 -------------------------------------------------------------------------------- /SConscript: -------------------------------------------------------------------------------- 1 | from building import * 2 | 3 | cwd = GetCurrentDir() 4 | path = [cwd + '/inc'] 5 | path += [cwd + '/inc/gsm'] 6 | src = Glob('src/*.c') 7 | 8 | src += Glob('sample/cmux_sample_gsm.c') 9 | 10 | if GetDepend(['CMUX_USING_GSM']): 11 | src += Glob('src/gsm/*.c') 12 | 13 | if GetDepend(['PKG_USING_PPP_DEVICE']): 14 | SrcRemove(src, "src/gsm/cmux_chat.c") 15 | 16 | group = DefineGroup('cmux', src, depend = ['PKG_USING_CMUX'], CPPPATH = path) 17 | 18 | Return('group') 19 | -------------------------------------------------------------------------------- /docs/cmux_basic.md: -------------------------------------------------------------------------------- 1 | ## 1. CMUX 的实现原理 2 | 3 | GSM 07.10 协议支持 4 | 5 | cmux_course 6 | 7 | ​ 以上是一次 CMUX 建立,通话和销毁的基本流程,因为牵扯到真实串口到虚拟串口的交互,所以可以在一个物理串口上同时实现多个功能。在 PPP 拨号启用时,仍然可以调用 AT 命令,也可以使用 Modem 功能同时支持数据通话。 8 | 9 | ## 2. CMUX 协议 10 | 11 | ​ 多路复用协议提供在单个物理通信通道之上虚拟出多个并行的逻辑通信通道的能力,一般应用于TE(Terminal Equipment)与MS(Mobile Station)之间,TE相当于智能手机的AP端,MS相当于智能手机的MODEM端。多路复用协议的实现效果如图: 12 | 13 |  14 | 15 | > 实际使用中,TE 端的 MUX 向 MS 端的 MUX 发起通道建立请求,设置通道参数等,是主动的一方; 16 | > 17 | > MS端的MUX等待TE端的服务请求,根据自身能力提供相应服务。 18 | 19 | 1. **启动CMUX服务** :向模块发送 AT + CMUX 命令 20 | 2. **建立DLC服务** :建立数据连接 21 | 3. **数据服务** 22 | 4. 功耗控制 23 | 5. **释放DLC服务** 24 | 6. **关闭服务** 25 | 7. 控制服务 26 | 27 | #### 2.1 CMUX 数据格式 28 | 29 | | Flag | Address | Control | Length | Information | FCS | Flag | 30 | | ----------- | ------- | ------- | :--------: | ----------- | ------ | ----------- | 31 | | 0xF9(basic) | 地址域 | 控制域 | 数据域长度 | 实际数据域 | 校验域 | 0xF9(basic) | 32 | 33 | **地址域:** 34 | 35 | | Bit No. | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 36 | | ------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 37 | | Signal | EA | C/R | DLCI | DLCI | DLCI | DLCI | DLCI | DLCI | 38 | 39 | **控制域:** 40 | 41 | | Frame Type | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 备注 | 42 | | --------------------------------------------- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | ---- | 43 | | SABM (Set Asynchronous Balanced Mode) | 1 | 1 | 1 | 1 | P/F | 1 | 0 | 0 | | 44 | | UA (Unnumbered Acknowledgement) | 1 | 1 | 0 | 0 | P/F | 1 | 1 | 0 | | 45 | | DM (Disconnected Mode) | 1 | 1 | 1 | 1 | P/F | 0 | 0 | 0 | | 46 | | DISC (Disconnect) | 1 | 1 | 0 | 0 | P/F | 0 | 1 | 0 | | 47 | | UIH(Unnumbered Information with Header check) | 1 | 1 | 1 | 1 | P/F | 1 | 1 | 1 | | 48 | | UI (Unnumbered Information) | 1 | 1 | 0 | 0 | P/F | 0 | 0 | 0 | 可选 | 49 | 50 | 1. SABM SABM命令帧,异步平衡模式 51 | 2. UA UA回应帧,对SABM和DISC这两个命令帧的确认 52 | 3. DM 如果在链路断开状态,收到DISC命令,就应该发一个DM作为响应 53 | 4. DISC DISC命令用于终止通道 54 | 5. UIH UIH命令帧/响应帧,相对于 UI 帧只对地址域,控制域和长度域校验 55 | 6. UI UI命令帧/响应帧 56 | 57 | #### 2.2 基础介绍 58 | 59 | 1. CMUX 不同帧类型介绍 60 | 61 | ```shell 62 | // bits: Poll/final, Command/Response, Extension 63 | #define CMUX_CONTROL_PF 16 64 | #define CMUX_ADDRESS_CR 2 65 | #define CMUX_ADDRESS_EA 1 66 | // the types of the frames 67 | #define CMUX_FRAME_SABM 47 68 | #define CMUX_FRAME_UA 99 69 | #define CMUX_FRAME_DM 15 70 | #define CMUX_FRAME_DISC 67 71 | #define CMUX_FRAME_UIH 239 72 | #define CMUX_FRAME_UI 3 73 | // the types of the control channel commands 74 | #define CMUX_C_CLD 193 75 | #define CMUX_C_TEST 33 76 | #define CMUX_C_MSC 225 77 | #define CMUX_C_NSC 17 78 | // basic mode flag for frame start and end 79 | #define CMUX_HEAD_FLAG (unsigned char)0xF9 80 | ``` 81 | -------------------------------------------------------------------------------- /docs/cmux_port.md: -------------------------------------------------------------------------------- 1 | # 功能使用 2 | 3 | 在 CMUX 中使用 PPP_DEVICE 与直接使用 PPP_DEVICE 的使用差别不大;使用最新版本的 PPP_DEVICE 可以做到 cmux 的兼容效果,无论是打开 cmux 功能均可以正常使用 PPP_DEVICE。 4 | 5 | 6 | 7 | 如果使用低版本的 PPP_DEVICE ,务必注意一下几点。 8 | 9 | 1. 相对于只使用 PPP_DEVICE,CMUX + PPP_DEVICE 组合需要使用 NOT_NEED 设置 10 | 11 | 出现这个问题的原因是因为 chat 功能相对简单,匹配回复的字符串时容易受到超时判定的制约,导致提前退出,匹配失败。关闭匹配字符串功能,可以防止错误判断;后面会优化 chat 方法。 12 | 13 | ![modify](./figures/modify.png) 14 | 15 | 2. rst_cmd 是考虑到模块仍在 ppp 状态需要先退出 ppp 功能而考虑的 16 | 17 | 在使用 cmux + ppp_device 组合时,ppp 拨号的前提就是模块已经进入 cmux 模式,无需发送 rst_cmd 命令 18 | 19 | ![modify](./figures/prepare_modify.png) 20 | 21 | 3. 示例中的 ready 功能是测试命令,代码如下 22 | 23 | ```c 24 | #define DBG_TAG "ready" 25 | #define DBG_LVL DBG_LOG 26 | #include 27 | 28 | static rt_uint8_t flag = 0; 29 | struct rt_device *device = RT_NULL; 30 | rt_err_t result; 31 | rt_size_t size; 32 | rt_uint8_t buffer[20]; 33 | rt_size_t count = 0; 34 | 35 | static rt_err_t recieve_invkoen(rt_device_t dev, rt_size_t size) 36 | { 37 | RT_ASSERT(dev != RT_NULL); 38 | flag = 1; 39 | return RT_EOK; 40 | } 41 | 42 | int cmux_ctl_command(void *parameter) 43 | { 44 | device = rt_device_find("cmux_at"); 45 | if(device == RT_NULL) 46 | { 47 | LOG_E("Sorry, can't find cmux control channel."); 48 | } 49 | else 50 | { 51 | result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX); 52 | if(result != RT_EOK) 53 | { 54 | LOG_E("Sorry, can't open cmux control channel."); 55 | } 56 | LOG_D("cmux control channel has been open."); 57 | } 58 | rt_device_set_rx_indicate(device, recieve_invkoen); 59 | 60 | size = rt_device_write(device, 0, "AT+CSQ\r\n", sizeof("AT+CSQ\r\n")); 61 | LOG_D("write data : %d", size); 62 | 63 | while(count++ < 500) 64 | { 65 | size = rt_device_read(device, 0, buffer, 20); 66 | if(size != 0) 67 | { 68 | buffer[size] = '\0'; 69 | LOG_D("%d ,Recieve %s", size, buffer); 70 | flag = 0; 71 | } 72 | } 73 | count = 0; 74 | return RT_EOK; 75 | } 76 | 77 | int ready(void *para) 78 | { 79 | rt_thread_t read_thread = RT_NULL; 80 | 81 | read_thread = rt_thread_create("read", 82 | (void (*)(void *parameter))cmux_ctl_command, 83 | RT_NULL, 84 | 2048, 85 | 25, 86 | 20); 87 | rt_thread_startup(read_thread); 88 | } 89 | MSH_CMD_EXPORT(ready, ready); 90 | ``` 91 | 92 | 还在考虑直接使用 at_client 功能,敬请期待。 93 | 94 | 4. 模块进入 cmux 后,无法通过命令主动退出,所以进入 cmux 需要特别注意;在 cmux_gsm.c 中修改逻辑 95 | 96 | ![private control](./figures/private control.png) 97 | 98 | * 模块已经进入 cmux 状态,无需使用 AT 命令进入 cmux 状态,可以不调用 modem_chat 99 | * 模块状态未进入 cmux 状态,但是此次模块无任何响应,可以添加电源控制,重启设备。蜂窝模块重启时间较长,建议添加合适时间的延时,以提升系统效率 -------------------------------------------------------------------------------- /docs/figures/cmux_course.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/cmux_course.png -------------------------------------------------------------------------------- /docs/figures/cmux_data_store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/cmux_data_store.png -------------------------------------------------------------------------------- /docs/figures/cmux_frame.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/cmux_frame.png -------------------------------------------------------------------------------- /docs/figures/modify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/modify.png -------------------------------------------------------------------------------- /docs/figures/prepare_modify.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/prepare_modify.png -------------------------------------------------------------------------------- /docs/figures/private control.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/private control.png -------------------------------------------------------------------------------- /docs/figures/protocol_stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RT-Thread-packages/cmux/2f2b563b682b72f3ff071b6a67a415a831f2e796/docs/figures/protocol_stack.png -------------------------------------------------------------------------------- /inc/cmux.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2020, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-15 xiangxistu the first version 9 | */ 10 | 11 | #ifndef __CMUX_H__ 12 | #define __CMUX_H__ 13 | 14 | #include 15 | 16 | #ifdef __cplusplus 17 | extern "C" { 18 | #endif 19 | 20 | //#define CMUX_DEBUG 21 | 22 | /* CMUX using long frame mode by default */ 23 | #define CMUX_RECV_READ_MAX 2048 24 | 25 | #ifndef CMUX_BUFFER_SIZE 26 | #define CMUX_BUFFER_SIZE (CMUX_RECV_READ_MAX * 2) 27 | #endif 28 | 29 | #define CMUX_SW_VERSION "1.1.0" 30 | #define CMUX_SW_VERSION_NUM 0x10100 31 | 32 | struct cmux_buffer 33 | { 34 | rt_uint8_t data[CMUX_BUFFER_SIZE]; /* cmux data buffer */ 35 | rt_uint8_t *read_point; 36 | rt_uint8_t *write_point; 37 | rt_uint8_t *end_point; 38 | int flag_found; /* the flag whether you find cmux frame */ 39 | }; 40 | 41 | struct cmux_frame 42 | { 43 | rt_uint8_t channel; /* the frame channel */ 44 | rt_uint8_t control; /* the type of frame */ 45 | int data_length; /* frame length */ 46 | rt_uint8_t *data; /* the point for cmux data */ 47 | }; 48 | 49 | struct frame 50 | { 51 | struct cmux_frame *frame; /* the point for cmux frame */ 52 | 53 | rt_slist_t frame_list; /* slist for different virtual serial */ 54 | }; 55 | 56 | struct cmux_vcoms 57 | { 58 | struct rt_device device; /* virtual device */ 59 | 60 | rt_slist_t flist; /* head of frame_list */ 61 | 62 | rt_uint16_t frame_index; /* the length of flist */ 63 | 64 | rt_uint8_t link_port; /* link port id */ 65 | 66 | rt_bool_t frame_using_status; /* This is designed for long frame when we read data; the flag will be "1" when long frame haven't reading done */ 67 | 68 | struct cmux_frame *frame; 69 | 70 | rt_size_t length; 71 | 72 | rt_uint8_t *data; 73 | }; 74 | 75 | struct cmux 76 | { 77 | struct rt_device *dev; /* device object */ 78 | const struct cmux_ops *ops; /* cmux device ops interface */ 79 | struct cmux_buffer *buffer; /* cmux buffer */ 80 | struct cmux_frame *frame; /* cmux frame point */ 81 | rt_thread_t recv_tid; /* receive thread point */ 82 | rt_uint8_t vcom_num; /* the cmux port number */ 83 | struct cmux_vcoms *vcoms; /* array */ 84 | 85 | struct rt_event *event; /* internal communication */ 86 | 87 | rt_slist_t list; /* cmux list */ 88 | 89 | void *user_data; /* reserve */ 90 | }; 91 | 92 | struct cmux_ops 93 | { 94 | rt_err_t (*start) (struct cmux *obj); 95 | rt_err_t (*stop) (struct cmux *obj); 96 | rt_err_t (*control) (struct cmux *obj, int cmd, void *arg); 97 | }; 98 | 99 | 100 | 101 | /* those function is opening for external file */ 102 | rt_err_t cmux_init(struct cmux *object, const char *dev_name, rt_uint8_t vcom_num, void *user_data); 103 | rt_err_t cmux_start(struct cmux *object); 104 | rt_err_t cmux_stop(struct cmux *object); 105 | rt_err_t cmux_attach(struct cmux *object, int port, const char *alias_name, rt_uint16_t flags, void *user_data); 106 | rt_err_t cmux_detach(struct cmux *object, const char *alias_name); 107 | void cmux_at_cmd_cfg(uint8_t mode, uint8_t subset, uint32_t port_speed, uint32_t N1, uint32_t T1, uint32_t N2, 108 | uint32_t T2, uint32_t T3, uint32_t k); 109 | 110 | /* cmux_utils */ 111 | rt_uint8_t cmux_frame_check(const rt_uint8_t *input, int length); 112 | struct cmux *cmux_object_find(const char *name); 113 | 114 | #ifdef __cplusplus 115 | } 116 | #endif 117 | 118 | #endif /* __CMUX_H__ */ 119 | -------------------------------------------------------------------------------- /inc/gsm/cmux_chat.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 xiaofan 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2019-09-19 xiaofan the first version 9 | * 2020-04-13 xiangxistu transplant into cmux 10 | */ 11 | 12 | #ifndef __CMUX_CHAT_H__ 13 | #define __CMUX_CHAT_H__ 14 | 15 | #include 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #define MODEM_CHAT_RESP_LIST(F) \ 23 | F(MODEM_CHAT_RESP_OK, "OK"), \ 24 | F(MODEM_CHAT_RESP_ERROR, "ERROR") \ 25 | 26 | #define DEFINE_MODEM_RESP_ID_TABLE(id, s) id 27 | 28 | enum { 29 | MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_ID_TABLE), 30 | MODEM_CHAT_RESP_MAX, 31 | MODEM_CHAT_RESP_NOT_NEED, 32 | }; 33 | 34 | struct modem_chat_data { 35 | const char* transmit; 36 | rt_uint8_t expect; // use CHAT_RESP_xxx 37 | rt_uint8_t retries; 38 | rt_uint8_t timeout; // second 39 | rt_bool_t ignore_cr; // ignore CR character if it is RT_TRUE 40 | }; 41 | 42 | rt_err_t modem_chat(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len); 43 | 44 | #ifdef __cplusplus 45 | } 46 | #endif 47 | 48 | #endif /* __CMUX_CHAT_H__ */ 49 | -------------------------------------------------------------------------------- /sample/cmux_sample_gsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2020, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-15 xiangxistu the first version 9 | */ 10 | 11 | #include 12 | #include 13 | 14 | #define CMUX_PPP_NAME "cmux_ppp" 15 | #define CMUX_PPP_PORT 1 16 | 17 | #define CMUX_AT_NAME "cmux_at" 18 | #define CMUX_AT_PORT 2 19 | 20 | #define DBG_TAG "cmux.sample" 21 | 22 | #ifdef CMUX_DEBUG 23 | #define DBG_LVL DBG_LOG 24 | #else 25 | #define DBG_LVL DBG_INFO 26 | #endif 27 | 28 | #include 29 | 30 | struct cmux *sample = RT_NULL; 31 | int cmux_sample(void) 32 | { 33 | rt_err_t result; 34 | 35 | /* find cmux object through the actual serial name < the actual serial has been related in the cmux.c file > */ 36 | sample = cmux_object_find(CMUX_DEPEND_NAME); 37 | if (sample == RT_NULL) 38 | { 39 | result = -RT_ERROR; 40 | LOG_E("Can't find %s", CMUX_DEPEND_NAME); 41 | goto end; 42 | } 43 | /* startup the cmux receive thread, attach control chananel and open it */ 44 | result = cmux_start(sample); 45 | if (result != RT_EOK) 46 | { 47 | LOG_E("cmux sample start error. Can't find %s", CMUX_DEPEND_NAME); 48 | goto end; 49 | } 50 | LOG_I("cmux sample (%s) start successful.", CMUX_DEPEND_NAME); 51 | 52 | /* attach AT function into cmux */ 53 | result = cmux_attach(sample, CMUX_AT_PORT, CMUX_AT_NAME, RT_DEVICE_FLAG_DMA_RX, RT_NULL); 54 | if (result != RT_EOK) 55 | { 56 | LOG_E("cmux attach (%s) failed.", CMUX_AT_NAME); 57 | goto end; 58 | } 59 | LOG_I("cmux object channel (%s) attach successful.", CMUX_AT_NAME); 60 | 61 | /* attach PPP function into cmux */ 62 | result = cmux_attach(sample, CMUX_PPP_PORT, CMUX_PPP_NAME, RT_DEVICE_FLAG_DMA_RX, RT_NULL); 63 | if (result != RT_EOK) 64 | { 65 | LOG_E("cmux attach %s failed.", CMUX_PPP_NAME); 66 | goto end; 67 | } 68 | LOG_I("cmux object channel (%s) attach successful.", CMUX_PPP_NAME); 69 | end: 70 | return RT_EOK; 71 | } 72 | #ifdef CMUX_ATUO_INITIZATION 73 | INIT_APP_EXPORT(cmux_sample); 74 | #endif 75 | MSH_CMD_EXPORT_ALIAS(cmux_sample, cmux_start, a sample of cmux function); 76 | -------------------------------------------------------------------------------- /src/cmux.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2020, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-15 xiangxistu the first version 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | // bits: Poll/final, Command/Response, Extension 16 | #define CMUX_CONTROL_PF 16 17 | #define CMUX_ADDRESS_CR 2 18 | #define CMUX_ADDRESS_EA 1 19 | // the types of the frames 20 | #define CMUX_FRAME_SABM 47 21 | #define CMUX_FRAME_UA 99 22 | #define CMUX_FRAME_DM 15 23 | #define CMUX_FRAME_DISC 67 24 | #define CMUX_FRAME_UIH 239 25 | #define CMUX_FRAME_UI 3 26 | // the types of the control channel commands 27 | #define CMUX_C_CLD 193 28 | #define CMUX_C_TEST 33 29 | #define CMUX_C_MSC 225 30 | #define CMUX_C_NSC 17 31 | // basic mode flag for frame start and end 32 | #define CMUX_HEAD_FLAG (unsigned char)0xF9 33 | 34 | #define CMUX_DHCL_MASK 63 /* DLCI number is port number, 63 is the mask of DLCI; C/R bit is 1 when we send data */ 35 | #define CMUX_DATA_MASK 127 /* when data length is out of 127( 0111 1111 ), we must use two bytes to describe data length in the cmux frame */ 36 | #define CMUX_HIGH_DATA_MASK 32640 /* 32640 (‭ 0111 1111 1000 0000 ‬), the mask of high data bits */ 37 | 38 | #define CMUX_COMMAND_IS(command, type) ((type & ~CMUX_ADDRESS_CR) == command) 39 | #define CMUX_PF_ISSET(frame) ((frame->control & CMUX_CONTROL_PF) == CMUX_CONTROL_PF) 40 | #define CMUX_FRAME_IS(type, frame) ((frame->control & ~CMUX_CONTROL_PF) == type) 41 | 42 | #define min(a, b) ((a) <= (b) ? (a) : (b)) 43 | 44 | /* increases buffer pointer by one and wraps around if necessary */ 45 | #define INC_BUF_POINTER(buf, p) \ 46 | (p)++; \ 47 | if ((p) == (buf)->end_point) \ 48 | (p) = (buf)->data; 49 | 50 | /* Tells, how many chars are saved into the buffer */ 51 | #define cmux_buffer_length(buff) (((buff)->read_point > (buff)->write_point) ? (CMUX_BUFFER_SIZE - ((buff)->read_point - (buff)->write_point)) : ((buff)->write_point - (buff)->read_point)) 52 | 53 | /* Tells, how much free space there is in the buffer */ 54 | #define cmux_buffer_free(buff) (((buff)->read_point > (buff)->write_point) ? ((buff)->read_point - (buff)->write_point) : (CMUX_BUFFER_SIZE - ((buff)->write_point - (buff)->read_point))) 55 | 56 | #define CMUX_THREAD_STACK_SIZE (CMUX_RECV_READ_MAX + 1536) 57 | #define CMUX_THREAD_PRIORITY 8 58 | 59 | #define CMUX_RECIEVE_RESET 0 60 | #define CMUX_RECIEVE_BEGIN 1 61 | #define CMUX_RECIEVE_PROCESS 2 62 | #define CMUX_RECIEVE_END 3 63 | 64 | #define CMUX_EVENT_RX_NOTIFY 1 /* serial incoming a byte */ 65 | #define CMUX_EVENT_CHANNEL_OPEN 2 66 | #define CMUX_EVENT_CHANNEL_CLOSE 4 67 | #define CMUX_EVENT_CHANNEL_OPEN_REQ 8 68 | #define CMUX_EVENT_CHANNEL_CLOSE_REQ 16 69 | #define CMUX_EVENT_FUNCTION_EXIT 32 70 | 71 | #define DBG_TAG "cmux" 72 | 73 | #ifdef CMUX_DEBUG 74 | #define DBG_LVL DBG_LOG 75 | #else 76 | #define DBG_LVL DBG_INFO 77 | #endif 78 | #include 79 | 80 | static rt_size_t cmux_send_data(struct rt_device *dev, int port, rt_uint8_t type, const char *data, int length); 81 | static rt_slist_t cmux_list = RT_SLIST_OBJECT_INIT(cmux_list); 82 | /* only one cmux object can be created */ 83 | static struct cmux *_g_cmux = RT_NULL; 84 | 85 | /** 86 | * Get the cmux object that your used device. 87 | * 88 | * @param name the name of your select device. 89 | * 90 | * @return struct cmux object point 91 | */ 92 | struct cmux *cmux_object_find(const char *name) 93 | { 94 | struct cmux *cmux = RT_NULL; 95 | struct rt_slist_node *node = RT_NULL; 96 | rt_base_t level; 97 | 98 | level = rt_hw_interrupt_disable(); 99 | rt_slist_for_each(node, &cmux_list) 100 | { 101 | cmux = rt_slist_entry(node, struct cmux, list); 102 | if (rt_strncmp(cmux->dev->parent.name, name, RT_NAME_MAX) == 0) 103 | { 104 | rt_hw_interrupt_enable(level); 105 | return cmux; 106 | } 107 | }; 108 | 109 | rt_hw_interrupt_enable(level); 110 | return RT_NULL; 111 | } 112 | 113 | /** 114 | * Receive callback function , send CMUX_EVENT_RX_NOTIFY event when uart acquire data 115 | * 116 | * @param dev the point of device driver structure, uart structure 117 | * @param size the indication callback function need this parameter 118 | * 119 | * @return RT_EOK 120 | */ 121 | static rt_err_t cmux_rx_ind(rt_device_t dev, rt_size_t size) 122 | { 123 | RT_ASSERT(dev != RT_NULL); 124 | struct cmux *cmux = RT_NULL; 125 | 126 | cmux = _g_cmux; 127 | 128 | /* when receive data from uart , send event to wake up receive thread */ 129 | rt_event_send(cmux->event, CMUX_EVENT_RX_NOTIFY); 130 | 131 | return RT_EOK; 132 | } 133 | 134 | /** 135 | * invoke callback function 136 | * 137 | * @param cmux cmux object 138 | * @param port the number of virtual channel 139 | * @param size the size of we receive from cmux 140 | * 141 | * @return the point of rt_device_write fucntion or RT_NULL 142 | */ 143 | void cmux_vcom_isr(struct cmux *cmux, rt_uint8_t port, rt_size_t size) 144 | { 145 | /* when receive data, we should notify user immediately. */ 146 | if (cmux->vcoms[port].device.rx_indicate != RT_NULL) 147 | { 148 | cmux->vcoms[port].device.rx_indicate(&cmux->vcoms[port].device, size); 149 | } 150 | else 151 | { 152 | LOG_W("channel[%02d] haven appended data, please set rx_indicate and clear receive data.", port); 153 | } 154 | } 155 | 156 | /** 157 | * allocate buffer for cmux object receive 158 | * 159 | * @param RT_NULL 160 | * 161 | * @return the point of struct cmux_buffer 162 | */ 163 | static struct cmux_buffer *cmux_buffer_init() 164 | { 165 | struct cmux_buffer *buff = RT_NULL; 166 | buff = rt_malloc(sizeof(struct cmux_buffer)); 167 | if (buff == RT_NULL) 168 | { 169 | return RT_NULL; 170 | } 171 | rt_memset(buff, 0, sizeof(struct cmux_buffer)); 172 | buff->read_point = buff->data; 173 | buff->write_point = buff->data; 174 | buff->end_point = buff->data + CMUX_BUFFER_SIZE; 175 | return buff; 176 | } 177 | 178 | /** 179 | * destroy buffer for cmux object receive 180 | * 181 | * @param frame the point of cmux_frame 182 | * 183 | * @return RT_NULL 184 | */ 185 | static void cmux_frame_destroy(struct cmux_frame *frame) 186 | { 187 | if ((frame->data_length > 0) && frame->data) 188 | { 189 | rt_free(frame->data); 190 | } 191 | if (frame) 192 | { 193 | rt_free(frame); 194 | } 195 | } 196 | 197 | /** 198 | * initial virtual serial for different channel, initial slist for channel 199 | * 200 | * @param cmux cmux object 201 | * @param channel the number of virtual serial 202 | * 203 | * @return RT_NULL 204 | */ 205 | static void vcoms_cmux_frame_init(struct cmux *cmux, int channel) 206 | { 207 | rt_base_t level; 208 | 209 | level = rt_hw_interrupt_disable(); 210 | rt_slist_init(&cmux->vcoms[channel].flist); 211 | rt_hw_interrupt_enable(level); 212 | cmux->vcoms[channel].frame_index = 0; 213 | 214 | LOG_D("init cmux data channel(%d) list.", channel); 215 | } 216 | 217 | /** 218 | * push cmux frame data into slist for different channel virtual serial 219 | * 220 | * @param cmux cmux object 221 | * @param channel the number of virtual serial 222 | * @param frame the point of frame data 223 | * 224 | * @return RT_EOK successful 225 | * RT_ENOMEM allocate memory failed 226 | */ 227 | static rt_err_t cmux_frame_push(struct cmux *cmux, int channel, struct cmux_frame *frame) 228 | { 229 | rt_base_t level; 230 | struct frame *frame_new = RT_NULL; 231 | rt_uint16_t frame_len = cmux->vcoms[channel].frame_index; 232 | 233 | if (frame_len <= CMUX_MAX_FRAME_LIST_LEN) 234 | { 235 | frame_new = rt_malloc(sizeof(struct frame)); 236 | if (frame_new == RT_NULL) 237 | { 238 | LOG_E("can't malloc to record data address."); 239 | return -RT_ENOMEM; 240 | } 241 | else 242 | { 243 | rt_memset(frame_new, 0, sizeof(struct frame)); 244 | 245 | frame_new->frame = frame; 246 | rt_slist_init(&frame_new->frame_list); 247 | } 248 | 249 | level = rt_hw_interrupt_disable(); 250 | rt_slist_append(&cmux->vcoms[channel].flist, &frame_new->frame_list); 251 | rt_hw_interrupt_enable(level); 252 | 253 | #ifdef CMUX_DEBUG 254 | LOG_HEX("CMUX_RX", 32, frame->data, frame->data_length); 255 | #endif 256 | 257 | LOG_D("new message (len:%d) for channel (%d) is append, Message total: %d.", frame_new->frame->data_length, channel, ++cmux->vcoms[channel].frame_index); 258 | 259 | return RT_EOK; 260 | } 261 | LOG_E("malloc failed, the message for channel(%d) is long than CMUX_MAX_FRAME_LIST_LEN(%d).", channel, CMUX_MAX_FRAME_LIST_LEN); 262 | return -RT_ENOMEM; 263 | } 264 | 265 | /** 266 | * pop cmux frame data from slist for different channel virtual serial 267 | * 268 | * @param cmux cmux object 269 | * @param channel the number of virtual serial 270 | * 271 | * @return frame_data successful 272 | * RT_NULL no message on the slist 273 | */ 274 | static struct cmux_frame *cmux_frame_pop(struct cmux *cmux, int channel) 275 | { 276 | rt_base_t level; 277 | struct frame *frame = RT_NULL; 278 | struct cmux_frame *frame_data = RT_NULL; 279 | struct rt_slist_node *frame_list_find = RT_NULL; 280 | rt_slist_t *frame_list = RT_NULL; 281 | 282 | frame_list = &cmux->vcoms[channel].flist; 283 | 284 | frame_list_find = rt_slist_first(frame_list); 285 | if (frame_list_find != RT_NULL) 286 | { 287 | frame = rt_container_of(frame_list_find, struct frame, frame_list); 288 | frame_data = frame->frame; 289 | 290 | level = rt_hw_interrupt_disable(); 291 | rt_slist_remove(frame_list, frame_list_find); 292 | rt_hw_interrupt_enable(level); 293 | 294 | LOG_D("A message (len:%d) for channel (%d) has been used, Message remain: %d.", frame_data->data_length, channel, --cmux->vcoms[channel].frame_index); 295 | rt_free(frame); 296 | } 297 | 298 | return frame_data; 299 | } 300 | 301 | /** 302 | * write data into cmux buffer 303 | * 304 | * @param buff the buffer of cmux object 305 | * @param input the point of data 306 | * @param count the length of data 307 | * 308 | * @return length the length of write into cmux buffer 309 | * 310 | */ 311 | rt_size_t cmux_buffer_write(struct cmux_buffer *buff, rt_uint8_t *input, rt_size_t count) 312 | { 313 | int c = buff->end_point - buff->write_point; 314 | 315 | count = min(count, cmux_buffer_free(buff)); 316 | if (count > c) 317 | { 318 | rt_memcpy(buff->write_point, input, c); 319 | rt_memcpy(buff->data, input + c, count - c); 320 | buff->write_point = buff->data + (count - c); 321 | } 322 | else 323 | { 324 | rt_memcpy(buff->write_point, input, count); 325 | buff->write_point += count; 326 | if (buff->write_point == buff->end_point) 327 | buff->write_point = buff->data; 328 | } 329 | 330 | return count; 331 | } 332 | 333 | /** 334 | * parse buffer for searching cmux frame 335 | * 336 | * @param buffer the cmux buffer for cmux object 337 | * 338 | * @return frame successful 339 | * RT_NULL no frame in the buffer 340 | */ 341 | static struct cmux_frame *cmux_frame_parse(struct cmux_buffer *buffer) 342 | { 343 | int end; 344 | int length_needed = 5; /* channel, type, length, fcs, flag */ 345 | rt_uint8_t *data = RT_NULL; 346 | rt_uint8_t fcs = 0xFF; 347 | struct cmux_frame *frame = RT_NULL; 348 | 349 | extern rt_uint8_t cmux_crctable[256]; 350 | 351 | /* Find start flag */ 352 | while (!buffer->flag_found && cmux_buffer_length(buffer) > 0) 353 | { 354 | if (*buffer->read_point == CMUX_HEAD_FLAG) 355 | buffer->flag_found = 1; 356 | INC_BUF_POINTER(buffer, buffer->read_point); 357 | } 358 | if (!buffer->flag_found) /* no frame started */ 359 | return RT_NULL; 360 | 361 | /* skip empty frames (this causes troubles if we're using DLC 62) */ 362 | while (cmux_buffer_length(buffer) > 0 && (*buffer->read_point == CMUX_HEAD_FLAG)) 363 | { 364 | INC_BUF_POINTER(buffer, buffer->read_point); 365 | } 366 | 367 | if (cmux_buffer_length(buffer) >= length_needed) 368 | { 369 | data = buffer->read_point; 370 | frame = (struct cmux_frame *)rt_malloc(sizeof(struct cmux_frame)); 371 | frame->data = RT_NULL; 372 | 373 | frame->channel = ((*data & 0xFC) >> 2); 374 | fcs = cmux_crctable[fcs ^ *data]; 375 | INC_BUF_POINTER(buffer, data); 376 | 377 | frame->control = *data; 378 | fcs = cmux_crctable[fcs ^ *data]; 379 | INC_BUF_POINTER(buffer, data); 380 | 381 | frame->data_length = (*data & 254) >> 1; 382 | fcs = cmux_crctable[fcs ^ *data]; 383 | /* frame data length more than 127 bytes */ 384 | if ((*data & 1) == 0) 385 | { 386 | INC_BUF_POINTER(buffer,data); 387 | frame->data_length += (*data*128); 388 | fcs = cmux_crctable[fcs^*data]; 389 | length_needed++; 390 | LOG_D("len_need: %d, frame_data_len: %d.", length_needed, frame->data_length); 391 | } 392 | length_needed += frame->data_length; 393 | if (cmux_buffer_length(buffer) < length_needed) 394 | { 395 | cmux_frame_destroy(frame); 396 | return RT_NULL; 397 | } 398 | INC_BUF_POINTER(buffer, data); 399 | /* extract data */ 400 | if (frame->data_length > 0) 401 | { 402 | frame->data = (unsigned char *)rt_malloc(frame->data_length); 403 | if (frame->data != RT_NULL) 404 | { 405 | end = buffer->end_point - data; 406 | if (frame->data_length > end) 407 | { 408 | rt_memcpy(frame->data, data, end); 409 | rt_memcpy(frame->data + end, buffer->data, frame->data_length - end); 410 | data = buffer->data + (frame->data_length - end); 411 | } 412 | else 413 | { 414 | rt_memcpy(frame->data, data, frame->data_length); 415 | data += frame->data_length; 416 | if (data == buffer->end_point) 417 | data = buffer->data; 418 | } 419 | if (CMUX_FRAME_IS(CMUX_FRAME_UI, frame)) 420 | { 421 | for (end = 0; end < frame->data_length; end++) 422 | fcs = cmux_crctable[fcs ^ (frame->data[end])]; 423 | } 424 | } 425 | else 426 | { 427 | LOG_E("Out of memory, when allocating space for frame data."); 428 | frame->data_length = 0; 429 | } 430 | } 431 | /* check FCS */ 432 | if (cmux_crctable[fcs ^ (*data)] != 0xCF) 433 | { 434 | LOG_W("Dropping frame: FCS doesn't match. Remain size: %d", cmux_buffer_length(buffer)); 435 | cmux_frame_destroy(frame); 436 | buffer->flag_found = 0; 437 | return cmux_frame_parse(buffer); 438 | } 439 | else 440 | { 441 | /* check end flag */ 442 | INC_BUF_POINTER(buffer, data); 443 | if (*data != CMUX_HEAD_FLAG) 444 | { 445 | LOG_W("Dropping frame: End flag not found. Instead: %d.", *data); 446 | cmux_frame_destroy(frame); 447 | buffer->flag_found = 0; 448 | return cmux_frame_parse(buffer); 449 | } 450 | else 451 | { 452 | } 453 | INC_BUF_POINTER(buffer, data); 454 | } 455 | buffer->read_point = data; 456 | } 457 | return frame; 458 | } 459 | 460 | /** 461 | * save data from serial, push frame into slist and invoke callback function 462 | * 463 | * @param device the point of device driver structure, ppp_device structure 464 | * @param buf the address of receive data from uart 465 | * @param len the length of receive data 466 | */ 467 | static void cmux_recv_processdata(struct cmux *cmux, rt_uint8_t *buf, rt_size_t len) 468 | { 469 | rt_size_t count = len; 470 | struct cmux_frame *frame = RT_NULL; 471 | 472 | cmux_buffer_write(cmux->buffer, buf, count); 473 | 474 | while ((frame = cmux_frame_parse(cmux->buffer)) != RT_NULL) 475 | { 476 | /* distribute different data */ 477 | if ((CMUX_FRAME_IS(CMUX_FRAME_UI, frame) || CMUX_FRAME_IS(CMUX_FRAME_UIH, frame))) 478 | { 479 | LOG_D("this is UI or UIH frame from channel(%d).", frame->channel); 480 | if (frame->channel > 0) 481 | { 482 | /* receive data from logical channel, distribution them */ 483 | cmux_frame_push(cmux, frame->channel, frame); 484 | cmux_vcom_isr(cmux, frame->channel, frame->data_length); 485 | } 486 | else 487 | { 488 | /* control channel command */ 489 | LOG_W("control channel command haven't support."); 490 | cmux_frame_destroy(frame); 491 | } 492 | } 493 | else 494 | { 495 | switch ((frame->control & ~CMUX_CONTROL_PF)) 496 | { 497 | case CMUX_FRAME_UA: 498 | LOG_D("This is UA frame for channel(%d).", frame->channel); 499 | 500 | break; 501 | case CMUX_FRAME_DM: 502 | LOG_D("This is DM frame for channel(%d).", frame->channel); 503 | 504 | break; 505 | case CMUX_FRAME_SABM: 506 | LOG_D("This is SABM frame for channel(%d).", frame->channel); 507 | 508 | break; 509 | case CMUX_FRAME_DISC: 510 | LOG_D("This is DISC frame for channel(%d).", frame->channel); 511 | 512 | break; 513 | } 514 | cmux_frame_destroy(frame); 515 | } 516 | } 517 | } 518 | 519 | /** 520 | * assemble general data in the format of cmux 521 | * 522 | * @param dev actual serial device 523 | * @param port the number of virtual serial 524 | * @param type the format of cmux frame 525 | * @param data general data 526 | * @param length the length of general data 527 | * 528 | * @return length 529 | */ 530 | static rt_size_t cmux_send_data(struct rt_device *dev, int port, rt_uint8_t type, const char *data, int length) 531 | { 532 | /* flag, EA=1 C port, frame type, data_length 1-2 */ 533 | rt_uint8_t prefix[5] = {CMUX_HEAD_FLAG, CMUX_ADDRESS_EA | CMUX_ADDRESS_CR, 0, 0, 0}; 534 | rt_uint8_t postfix[2] = {0xFF, CMUX_HEAD_FLAG}; 535 | int c, prefix_length = 4; 536 | 537 | /* EA=1, Command, let's add address */ 538 | prefix[1] = prefix[1] | ((CMUX_DHCL_MASK & port) << 2); 539 | /* cmux control field */ 540 | prefix[2] = type; 541 | 542 | if (length > CMUX_DATA_MASK) 543 | { 544 | prefix_length = 5; 545 | prefix[3] = ((CMUX_DATA_MASK & length) << 1); 546 | prefix[4] = (CMUX_HIGH_DATA_MASK & length) >> 7; 547 | } 548 | else 549 | { 550 | prefix[3] = 1 | (length << 1); 551 | } 552 | /* CRC checksum */ 553 | postfix[0] = cmux_frame_check(prefix + 1, prefix_length - 1); 554 | 555 | c = rt_device_write(dev, 0, prefix, prefix_length); 556 | if (c != prefix_length) 557 | { 558 | LOG_E("Couldn't write the whole prefix to the serial port for the virtual port %d. Wrote only %d bytes.", port, c); 559 | return 0; 560 | } 561 | if (length > 0) 562 | { 563 | c = rt_device_write(dev, 0, data, length); 564 | if (length != c) 565 | { 566 | LOG_E("Couldn't write all data to the serial port from the virtual port %d. Wrote only %d bytes.", port, c); 567 | return 0; 568 | } 569 | } 570 | c = rt_device_write(dev, 0, postfix, 2); 571 | if (c != 2) 572 | { 573 | LOG_E("Couldn't write the whole postfix to the serial port for the virtual port %d. Wrote only %d bytes.", port, c); 574 | return 0; 575 | } 576 | #ifdef CMUX_DEBUG 577 | LOG_HEX("CMUX_TX", 32, (const rt_uint8_t *)data, length); 578 | #endif 579 | return length; 580 | } 581 | 582 | /** 583 | * Receive thread , store serial data 584 | * 585 | * @param cmux the point of cmux object structure 586 | * 587 | * @return RT_EOK we shouldn't let the receive thread return data, receive thread need keep alive all the time 588 | */ 589 | static int cmux_recv_thread(struct cmux *cmux) 590 | { 591 | rt_uint32_t event; 592 | rt_size_t len; 593 | rt_uint8_t buffer[CMUX_RECV_READ_MAX]; 594 | 595 | rt_event_control(cmux->event, RT_IPC_CMD_RESET, RT_NULL); 596 | 597 | while (1) 598 | { 599 | rt_event_recv(cmux->event, CMUX_EVENT_RX_NOTIFY, RT_EVENT_FLAG_OR | RT_EVENT_FLAG_CLEAR, RT_WAITING_FOREVER, &event); 600 | if (event & CMUX_EVENT_RX_NOTIFY) 601 | { 602 | do 603 | { 604 | len = rt_device_read(cmux->dev, 0, buffer, CMUX_RECV_READ_MAX); 605 | if (len) 606 | { 607 | cmux_recv_processdata(cmux, buffer, len); 608 | } 609 | 610 | } while (len); 611 | } 612 | } 613 | 614 | return RT_EOK; 615 | } 616 | 617 | /** 618 | * cmux object init function 619 | * 620 | * @param object the point of cmux object 621 | * @param name the actual serial name 622 | * @param vcom_num the channel of virtual channel 623 | * @param user_data private data 624 | * 625 | * @return RT_EOK successful 626 | * RT_ENOMEM allocate memory failed 627 | */ 628 | rt_err_t cmux_init(struct cmux *object, const char *name, rt_uint8_t vcom_num, void *user_data) 629 | { 630 | static rt_uint8_t count = 1; 631 | char tmp_name[RT_NAME_MAX] = {0}; 632 | rt_base_t level; 633 | 634 | if (_g_cmux == RT_NULL) 635 | { 636 | _g_cmux = object; 637 | } 638 | else 639 | { 640 | RT_ASSERT(!_g_cmux); 641 | } 642 | 643 | object->dev = rt_device_find(name); 644 | if (object->dev == RT_NULL) 645 | { 646 | LOG_E("cmux can't find %s.", name); 647 | return RT_EOK; 648 | } 649 | 650 | object->vcom_num = vcom_num; 651 | object->vcoms = rt_malloc(vcom_num * sizeof(struct cmux_vcoms)); 652 | rt_memset(object->vcoms, 0, vcom_num * sizeof(struct cmux_vcoms)); 653 | if (object->vcoms == RT_NULL) 654 | { 655 | LOG_E("cmux vcoms malloc failed."); 656 | return -RT_ENOMEM; 657 | } 658 | 659 | object->buffer = cmux_buffer_init(); 660 | if (object->buffer == RT_NULL) 661 | { 662 | LOG_E("cmux buffer malloc failed."); 663 | return -RT_ENOMEM; 664 | } 665 | 666 | rt_snprintf(tmp_name, sizeof(tmp_name), "cmux%d", count); 667 | object->event = rt_event_create(tmp_name, RT_IPC_FLAG_FIFO); 668 | if (object->event == RT_NULL) 669 | { 670 | LOG_E("cmux event malloc failed."); 671 | return -RT_ENOMEM; 672 | } 673 | 674 | object->user_data = user_data; 675 | 676 | level = rt_hw_interrupt_disable(); 677 | 678 | rt_slist_init(&object->list); 679 | rt_slist_append(&cmux_list, &object->list); 680 | 681 | rt_hw_interrupt_enable(level); 682 | 683 | rt_snprintf(tmp_name, sizeof(tmp_name), "cmux%d", count); 684 | object->recv_tid = rt_thread_create(tmp_name, 685 | (void (*)(void *parameter))cmux_recv_thread, 686 | object, 687 | CMUX_THREAD_STACK_SIZE, 688 | CMUX_THREAD_PRIORITY, 689 | 20); 690 | if (object->recv_tid == RT_NULL) 691 | { 692 | LOG_E("cmux receive thread create failed."); 693 | return -RT_ERROR; 694 | } 695 | 696 | LOG_I("cmux rely on (%s) init successful.", name); 697 | return RT_EOK; 698 | } 699 | 700 | /** 701 | * start cmux function 702 | * 703 | * @param object the point of cmux object 704 | * 705 | * @return the result 706 | */ 707 | rt_err_t cmux_start(struct cmux *object) 708 | { 709 | rt_err_t result = 0; 710 | struct rt_device *device = RT_NULL; 711 | 712 | /* uart transfer into cmux */ 713 | rt_device_set_rx_indicate(object->dev, cmux_rx_ind); 714 | 715 | if (object->ops->start != RT_NULL) 716 | { 717 | result = object->ops->start(object); 718 | if (result != RT_EOK) 719 | return result; 720 | } 721 | 722 | if (object->recv_tid != RT_NULL) 723 | { 724 | result = rt_thread_startup(object->recv_tid); 725 | if (result != RT_EOK) 726 | { 727 | LOG_D("cmux receive thread startup failed."); 728 | return result; 729 | } 730 | } 731 | 732 | /* attach cmux control channel into rt-thread device */ 733 | cmux_attach(object, 0, "cmux_ctl", RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX, RT_NULL); 734 | 735 | device = rt_device_find("cmux_ctl"); 736 | result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX); 737 | if (result != RT_EOK) 738 | { 739 | LOG_E("cmux control channel open failed."); 740 | } 741 | 742 | return result; 743 | } 744 | 745 | /** 746 | * stop cmux function 747 | * 748 | * @param object the point of cmux object 749 | * 750 | * @return the result 751 | */ 752 | rt_err_t cmux_stop(struct cmux *object) 753 | { 754 | if (object->ops->stop != RT_NULL) 755 | { 756 | object->ops->stop(object); 757 | } 758 | 759 | /* we should send CMUX_FRAME_DM frame, close cmux control connect channel */ 760 | cmux_send_data(object->dev, 0, CMUX_FRAME_DISC | CMUX_CONTROL_PF, RT_NULL, 0); 761 | 762 | return RT_EOK; 763 | } 764 | 765 | /** 766 | * control cmux function 767 | * 768 | * @param object the point of cmux object 769 | * 770 | * @return RT_ENOSYS haven't support control function 771 | */ 772 | rt_err_t cmux_control(struct cmux *object, int cmd, void *args) 773 | { 774 | RT_ASSERT(object != RT_NULL); 775 | 776 | return -RT_ENOSYS; 777 | } 778 | 779 | /** 780 | * initialize virtual channel, and open it 781 | * 782 | * @param dev the point of virtual device 783 | * @param oflag the open flag of rt_device 784 | * 785 | * @return the result 786 | */ 787 | static rt_err_t cmux_vcom_open(rt_device_t dev, rt_uint16_t oflag) 788 | { 789 | rt_err_t result = RT_EOK; 790 | struct cmux *object = RT_NULL; 791 | struct cmux_vcoms *vcom = (struct cmux_vcoms *)dev; 792 | 793 | RT_ASSERT(dev != RT_NULL); 794 | 795 | object = _g_cmux; 796 | 797 | /* establish virtual connect channel */ 798 | cmux_send_data(object->dev, (int)vcom->link_port, CMUX_FRAME_SABM | CMUX_CONTROL_PF, RT_NULL, 0); 799 | 800 | return result; 801 | } 802 | 803 | /** 804 | * close virtual channel 805 | * 806 | * @param dev the point of virtual device 807 | * 808 | * @return the result 809 | */ 810 | static rt_err_t cmux_vcom_close(rt_device_t dev) 811 | { 812 | rt_err_t result = RT_EOK; 813 | struct cmux *object = RT_NULL; 814 | struct cmux_vcoms *vcom = (struct cmux_vcoms *)dev; 815 | 816 | object = _g_cmux; 817 | 818 | cmux_send_data(object->dev, (int)vcom->link_port, CMUX_FRAME_DISC | CMUX_CONTROL_PF, RT_NULL, 0); 819 | 820 | return result; 821 | } 822 | 823 | /** 824 | * write data into virtual channel 825 | * 826 | * @param dev the point of virtual device 827 | * @param pos offset 828 | * @param buffer the data you want to send 829 | * @param size the length of buffer 830 | * 831 | * @return the result 832 | */ 833 | static rt_size_t cmux_vcom_write(struct rt_device *dev, 834 | rt_off_t pos, 835 | const void *buffer, 836 | rt_size_t size) 837 | { 838 | struct cmux *cmux = RT_NULL; 839 | struct cmux_vcoms *vcom = (struct cmux_vcoms *)dev; 840 | rt_size_t len; 841 | cmux = _g_cmux; 842 | 843 | /* use virtual serial, we can write data into actual serial directly. */ 844 | len = cmux_send_data(cmux->dev, (int)vcom->link_port, CMUX_FRAME_UIH, buffer, size); 845 | return len; 846 | } 847 | 848 | /** 849 | * write data into virtual channel 850 | * 851 | * @param dev the point of virtual device 852 | * @param pos offset 853 | * @param buffer the buffer you want to store 854 | * @param size the length of buffer 855 | * 856 | * @return the result 857 | */ 858 | static rt_size_t cmux_vcom_read(struct rt_device *dev, 859 | rt_off_t pos, 860 | void *buffer, 861 | rt_size_t size) 862 | { 863 | struct cmux_vcoms *vcom = (struct cmux_vcoms *)dev; 864 | 865 | struct cmux *cmux = RT_NULL; 866 | rt_bool_t using_status = 0; 867 | 868 | cmux = _g_cmux; 869 | using_status = vcom->frame_using_status; 870 | 871 | /* The previous frame has been transmitted finish. */ 872 | if (!using_status) 873 | { 874 | /* support fifo, we using the first frame */ 875 | vcom->frame = cmux_frame_pop(cmux, (int)vcom->link_port); 876 | vcom->length = 0; 877 | vcom->data = RT_NULL; 878 | 879 | /* can't find frame */ 880 | if (vcom->frame == RT_NULL) 881 | { 882 | return 0; 883 | } 884 | 885 | if (size >= vcom->frame->data_length) 886 | { 887 | int data_len = vcom->frame->data_length; 888 | rt_memcpy(buffer, vcom->frame->data, data_len); 889 | cmux_frame_destroy(vcom->frame); 890 | 891 | return data_len; 892 | } 893 | else 894 | { 895 | vcom->data = vcom->frame->data; 896 | vcom->frame_using_status = 1; 897 | rt_memcpy(buffer, vcom->data, size); 898 | vcom->data = vcom->data + size; 899 | vcom->length = vcom->length + size; 900 | 901 | return size; 902 | } 903 | } 904 | else 905 | { 906 | /* transmit the rest of frame */ 907 | if (vcom->length + size >= vcom->frame->data_length) 908 | { 909 | size_t read_len; 910 | rt_memcpy(buffer, vcom->data, vcom->frame->data_length - vcom->length); 911 | vcom->frame_using_status = 0; 912 | 913 | if (vcom->frame->data_length - vcom->length >= 0) { 914 | read_len = vcom->frame->data_length - vcom->length; 915 | } 916 | cmux_frame_destroy(vcom->frame); 917 | return read_len; 918 | } 919 | else 920 | { 921 | rt_memcpy(buffer, vcom->data, size); 922 | vcom->data = vcom->data + size; 923 | vcom->length = vcom->length + size; 924 | 925 | return size; 926 | } 927 | } 928 | } 929 | 930 | /* virtual serial ops */ 931 | #ifdef RT_USING_DEVICE_OPS 932 | const struct rt_device_ops cmux_device_ops = 933 | { 934 | RT_NULL, 935 | cmux_vcom_open, 936 | cmux_vcom_close, 937 | cmux_vcom_read, 938 | cmux_vcom_write, 939 | RT_NULL, 940 | }; 941 | #endif 942 | 943 | /** 944 | * attach cmux into cmux object 945 | * 946 | * @param object the point of cmux object 947 | * @param link_port the channel of virtual serial 948 | * @param alias_name the name of virtual name 949 | * @param flag the type of virtual serial 950 | * @param user_data private data 951 | * 952 | * @return RT_EOK execute successful 953 | * 954 | */ 955 | rt_err_t cmux_attach(struct cmux *object, int link_port, const char *alias_name, rt_uint16_t flags, void *user_data) 956 | { 957 | RT_ASSERT(object != RT_NULL); 958 | struct rt_device *device = RT_NULL; 959 | 960 | if(link_port >= object->vcom_num) 961 | { 962 | LOG_E("PORT[%02d] attach failed, please increase CMUX_PORT_NUMBER in the env.", link_port); 963 | return -RT_EINVAL; 964 | } 965 | 966 | device = &object->vcoms[link_port].device; 967 | device->type = RT_Device_Class_Char; 968 | device->rx_indicate = RT_NULL; 969 | device->tx_complete = RT_NULL; 970 | 971 | #ifdef RT_USING_DEVICE_OPS 972 | device->ops = &cmux_device_ops; 973 | #else 974 | device->init = RT_NULL; 975 | device->open = cmux_vcom_open; 976 | device->close = cmux_vcom_close; 977 | device->read = cmux_vcom_read; 978 | device->write = cmux_vcom_write; 979 | device->control = RT_NULL; 980 | #endif 981 | 982 | object->vcoms[link_port].link_port = (rt_uint8_t)link_port; 983 | 984 | vcoms_cmux_frame_init(object, link_port); 985 | 986 | /* interrupt mode or dma mode is meaningless, because we don't have buffer for vcom */ 987 | if (flags & RT_DEVICE_FLAG_INT_RX) 988 | rt_device_register(device, alias_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_INT_RX); 989 | else 990 | rt_device_register(device, alias_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STREAM | RT_DEVICE_FLAG_DMA_RX); 991 | 992 | return RT_EOK; 993 | } 994 | 995 | /** 996 | * detach cmux from object 997 | * 998 | * @param object the point of cmux object 999 | * @param alias_name the name of virtual name 1000 | * 1001 | * @return RT_EOK execute successful 1002 | * -RT_ERROR error 1003 | */ 1004 | rt_err_t cmux_detach(struct cmux *object, const char *alias_name) 1005 | { 1006 | rt_device_t device = RT_NULL; 1007 | 1008 | device = rt_device_find(alias_name); 1009 | if (device->open_flag & RT_DEVICE_OFLAG_OPEN) 1010 | { 1011 | LOG_E("You should close vcom (%s) firstly.", device->parent.name); 1012 | return -RT_ERROR; 1013 | } 1014 | cmux_vcom_close(device); 1015 | 1016 | return RT_EOK; 1017 | } 1018 | -------------------------------------------------------------------------------- /src/cmux_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2020, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-15 xiangxistu the first version 9 | */ 10 | 11 | #include 12 | 13 | /* reversed, 8-bit, poly=0x07 */ 14 | const rt_uint8_t cmux_crctable[256] = { 15 | 0x00, 0x91, 0xE3, 0x72, 0x07, 0x96, 0xE4, 0x75, 16 | 0x0E, 0x9F, 0xED, 0x7C, 0x09, 0x98, 0xEA, 0x7B, 17 | 0x1C, 0x8D, 0xFF, 0x6E, 0x1B, 0x8A, 0xF8, 0x69, 18 | 0x12, 0x83, 0xF1, 0x60, 0x15, 0x84, 0xF6, 0x67, 19 | 0x38, 0xA9, 0xDB, 0x4A, 0x3F, 0xAE, 0xDC, 0x4D, 20 | 0x36, 0xA7, 0xD5, 0x44, 0x31, 0xA0, 0xD2, 0x43, 21 | 0x24, 0xB5, 0xC7, 0x56, 0x23, 0xB2, 0xC0, 0x51, 22 | 0x2A, 0xBB, 0xC9, 0x58, 0x2D, 0xBC, 0xCE, 0x5F, 23 | 0x70, 0xE1, 0x93, 0x02, 0x77, 0xE6, 0x94, 0x05, 24 | 0x7E, 0xEF, 0x9D, 0x0C, 0x79, 0xE8, 0x9A, 0x0B, 25 | 0x6C, 0xFD, 0x8F, 0x1E, 0x6B, 0xFA, 0x88, 0x19, 26 | 0x62, 0xF3, 0x81, 0x10, 0x65, 0xF4, 0x86, 0x17, 27 | 0x48, 0xD9, 0xAB, 0x3A, 0x4F, 0xDE, 0xAC, 0x3D, 28 | 0x46, 0xD7, 0xA5, 0x34, 0x41, 0xD0, 0xA2, 0x33, 29 | 0x54, 0xC5, 0xB7, 0x26, 0x53, 0xC2, 0xB0, 0x21, 30 | 0x5A, 0xCB, 0xB9, 0x28, 0x5D, 0xCC, 0xBE, 0x2F, 31 | 0xE0, 0x71, 0x03, 0x92, 0xE7, 0x76, 0x04, 0x95, 32 | 0xEE, 0x7F, 0x0D, 0x9C, 0xE9, 0x78, 0x0A, 0x9B, 33 | 0xFC, 0x6D, 0x1F, 0x8E, 0xFB, 0x6A, 0x18, 0x89, 34 | 0xF2, 0x63, 0x11, 0x80, 0xF5, 0x64, 0x16, 0x87, 35 | 0xD8, 0x49, 0x3B, 0xAA, 0xDF, 0x4E, 0x3C, 0xAD, 36 | 0xD6, 0x47, 0x35, 0xA4, 0xD1, 0x40, 0x32, 0xA3, 37 | 0xC4, 0x55, 0x27, 0xB6, 0xC3, 0x52, 0x20, 0xB1, 38 | 0xCA, 0x5B, 0x29, 0xB8, 0xCD, 0x5C, 0x2E, 0xBF, 39 | 0x90, 0x01, 0x73, 0xE2, 0x97, 0x06, 0x74, 0xE5, 40 | 0x9E, 0x0F, 0x7D, 0xEC, 0x99, 0x08, 0x7A, 0xEB, 41 | 0x8C, 0x1D, 0x6F, 0xFE, 0x8B, 0x1A, 0x68, 0xF9, 42 | 0x82, 0x13, 0x61, 0xF0, 0x85, 0x14, 0x66, 0xF7, 43 | 0xA8, 0x39, 0x4B, 0xDA, 0xAF, 0x3E, 0x4C, 0xDD, 44 | 0xA6, 0x37, 0x45, 0xD4, 0xA1, 0x30, 0x42, 0xD3, 45 | 0xB4, 0x25, 0x57, 0xC6, 0xB3, 0x22, 0x50, 0xC1, 46 | 0xBA, 0x2B, 0x59, 0xC8, 0xBD, 0x2C, 0x5E, 0xCF}; 47 | 48 | rt_uint8_t cmux_frame_check(const rt_uint8_t *input, int length) 49 | { 50 | rt_uint8_t fcs = 0xFF; 51 | int i; 52 | for (i = 0; i < length; i++) 53 | { 54 | fcs = cmux_crctable[fcs ^ input[i]]; 55 | } 56 | return (0xFF - fcs); 57 | } 58 | -------------------------------------------------------------------------------- /src/gsm/cmux_chat.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019 xiaofan 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2019-09-19 xiaofan the first version 9 | * 2020-04-13 xiangxistu transplant into cmux 10 | */ 11 | 12 | #include "cmux_chat.h" 13 | #define DBG_TAG "cmux.chat" 14 | 15 | #ifdef CMUX_DEBUG 16 | #define DBG_LVL DBG_LOG 17 | #else 18 | #define DBG_LVL DBG_INFO 19 | #endif 20 | 21 | #include 22 | 23 | #define CHAT_READ_BUF_MAX 16 24 | 25 | /** 26 | * In order to match response, we need a string search algorithm 27 | * KMP and AC algorithm both are good choice, But we need the code 28 | * is simple, readable and use lower RAM/ROM. 29 | * So We use a simplified search algorithm, this alg is like the KMP. 30 | * Specifically we assume the failure vecotor is [-1, 0, 0, 0, ...] 31 | * This assuming is not work for all pattern string. Fortunately, 32 | * it's work for this scene. 33 | */ 34 | 35 | #define DEFINE_MODEM_RESP_STRDATA_TABLE(id, str) [id] = str 36 | #define DEFINE_MODEM_RESP_STRLEN_TABLE(id, str) [id] = (sizeof(str)-1) 37 | 38 | 39 | static char *resp_strdata[] = 40 | { 41 | MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_STRDATA_TABLE) 42 | }; 43 | 44 | static rt_uint8_t resp_strlen[] = 45 | { 46 | MODEM_CHAT_RESP_LIST(DEFINE_MODEM_RESP_STRLEN_TABLE) 47 | }; 48 | 49 | #define CHAT_DATA_FMT "" 50 | #define CHAT_DATA_STR(data) (data)->transmit, resp2str((data)->expect), (data)->retries, (data)->timeout 51 | 52 | static const char* resp2str(rt_uint8_t resp_id) 53 | { 54 | if (resp_id == MODEM_CHAT_RESP_NOT_NEED) 55 | return "(not need)"; 56 | RT_ASSERT(resp_id < MODEM_CHAT_RESP_MAX); 57 | return resp_strdata[resp_id]; 58 | } 59 | 60 | /* only one device support */ 61 | static struct rt_completion rx_comp_p; 62 | 63 | static rt_uint8_t resp_match(rt_uint8_t resp_id, rt_uint8_t state, char ch) 64 | { 65 | while (1) 66 | { 67 | if (resp2str(resp_id)[state] == ch) 68 | return state + 1; 69 | if (state == 0) 70 | return 0; 71 | state = 0; 72 | }; 73 | } 74 | 75 | static rt_bool_t resp_matched(rt_uint8_t resp_id, rt_uint8_t state) 76 | { 77 | return state == resp_strlen[resp_id]; 78 | } 79 | 80 | /** 81 | * chat_rx_ind, callback function if serial recieve data 82 | * 83 | * @param device the point of device driver structure, uart structure 84 | * @param size the indication callback function need this parameter 85 | * 86 | * @return RT_EOK 87 | */ 88 | static rt_err_t chat_rx_ind(rt_device_t device, rt_size_t size) 89 | { 90 | rt_completion_done(&rx_comp_p); 91 | return RT_EOK; 92 | } 93 | 94 | /** 95 | * chat_read_until, waitting for recieve data from serial 96 | * 97 | * @param serial the point of device driver structure, uart structure 98 | * @param buffer the buffer is waitting for recieve uart data from ppp 99 | * @param size the max length of CHAT_READ_BUF_MAX 100 | * @param stop the max of tick time 101 | * 102 | * @return >=0: the length of read data 103 | * <0 : rt_device_read failed 104 | */ 105 | static rt_size_t chat_read_until(rt_device_t serial, void *buffer, rt_size_t size, rt_tick_t stop) 106 | { 107 | rt_size_t rdlen; 108 | rt_tick_t wait; 109 | 110 | rt_completion_init(&rx_comp_p); 111 | rdlen = rt_device_read(serial, 0, buffer, size); 112 | if (rdlen) 113 | return rdlen; 114 | 115 | wait = stop - rt_tick_get(); 116 | if (wait > RT_TICK_MAX / 2) 117 | return 0; 118 | 119 | rt_completion_wait(&rx_comp_p, wait); 120 | return rt_device_read(serial, 0, buffer, size); 121 | } 122 | 123 | /** 124 | * modem_flush_rx , clear data what is in the rx buffer 125 | * 126 | * @param serial the point of device driver structure, uart structure 127 | */ 128 | static void modem_flush_rx(rt_device_t serial) 129 | { 130 | char rdbuf[CHAT_READ_BUF_MAX] = {0}; 131 | 132 | while (rt_device_read(serial, 0, rdbuf, CHAT_READ_BUF_MAX)); 133 | } 134 | 135 | /** 136 | * modem_chat_once , send an order to control modem 137 | * 138 | * @param serial the point of device driver structure, uart structure 139 | * @param data the AT command 140 | * 141 | * @return =0: modem_chat_once successful 142 | * <0: modem_chat_once failed 143 | */ 144 | static rt_err_t modem_chat_once(rt_device_t serial, const struct modem_chat_data *data) 145 | { 146 | rt_uint8_t resp_state[MODEM_CHAT_RESP_MAX] = { 0 }, resp; 147 | rt_tick_t stop = rt_tick_get() + data->timeout*RT_TICK_PER_SECOND; 148 | rt_size_t rdlen, pos; 149 | char rdbuf[CHAT_READ_BUF_MAX]; 150 | 151 | if (data->transmit) 152 | { 153 | LOG_D(CHAT_DATA_FMT " transmit --> modem", CHAT_DATA_STR(data)); 154 | rt_device_write(serial, 0, data->transmit, rt_strlen(data->transmit)); 155 | if (data->ignore_cr == RT_FALSE) 156 | rt_device_write(serial, 0, "\r", 1); 157 | } 158 | 159 | if (data->expect == MODEM_CHAT_RESP_NOT_NEED) 160 | { 161 | rt_thread_mdelay(1000*data->timeout); 162 | return RT_EOK; 163 | } 164 | 165 | do 166 | { 167 | rdlen = chat_read_until(serial, rdbuf, CHAT_READ_BUF_MAX, stop); 168 | for (pos = 0; pos < rdlen; pos++) 169 | { 170 | for (resp = 0; resp < MODEM_CHAT_RESP_MAX; resp++) 171 | { 172 | resp_state[resp] = resp_match(resp, resp_state[resp], rdbuf[pos]); 173 | if (resp_matched(resp, resp_state[resp])) 174 | { 175 | if (resp == data->expect) 176 | return RT_EOK; 177 | 178 | LOG_W(CHAT_DATA_FMT" not matched, got: %s", CHAT_DATA_STR(data), resp2str(resp)); 179 | #ifndef PKG_USING_CMUX 180 | return -RT_ERROR; 181 | #else 182 | continue; 183 | #endif 184 | } 185 | } 186 | } 187 | } while ( stop - rt_tick_get() < RT_TICK_MAX / 2); 188 | LOG_W(CHAT_DATA_FMT" timeout", CHAT_DATA_STR(data)); 189 | return -RT_ETIMEOUT; 190 | } 191 | 192 | /** 193 | * modem_chat_cmux , init modem or turn modem into cmux type 194 | * 195 | * @param serial the point of device driver structure, uart structure 196 | * @param data the AT command, it is the address of chat strcuture, a collection of AT command 197 | * @param len the length of this collection of AT command 198 | * 199 | * @return =0: modem_chat_cmux successful 200 | * <0: modem_chat_cmux failed 201 | */ 202 | static rt_err_t modem_chat_cmux(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len) 203 | { 204 | rt_err_t err = RT_EOK; 205 | rt_size_t i; 206 | rt_uint8_t retry_time; 207 | 208 | for (i = 0; i < len; i++) 209 | { 210 | LOG_D(CHAT_DATA_FMT" running", CHAT_DATA_STR(&data[i])); 211 | for (retry_time = 0; retry_time < data[i].retries; retry_time++) 212 | { 213 | modem_flush_rx(serial); 214 | err = modem_chat_once(serial, &data[i]); 215 | if (err == RT_EOK) 216 | break; 217 | } 218 | if (err != RT_EOK) 219 | { 220 | LOG_E(CHAT_DATA_FMT" fail", CHAT_DATA_STR(&data[i])); 221 | break; 222 | } 223 | LOG_D(CHAT_DATA_FMT" success", CHAT_DATA_STR(&data[i])); 224 | } 225 | return err; 226 | } 227 | 228 | /** 229 | * modem_chat , a function for cmux, it will set rx_indicate 230 | * 231 | * @param device the point of device driver structure, uart structure 232 | * @param data the AT command, it is the address of chat strcuture, a collection of AT command 233 | * @param len the length of this collection of AT command 234 | * 235 | * @return =0: modem_chat successful 236 | * <0: modem_chat failed 237 | */ 238 | rt_err_t modem_chat(rt_device_t serial, const struct modem_chat_data *data, rt_size_t len) 239 | { 240 | rt_err_t (*old_rx_ind)(rt_device_t dev, rt_size_t size) = RT_NULL; 241 | 242 | rt_err_t err = RT_EOK; 243 | 244 | rt_completion_init(&rx_comp_p); 245 | old_rx_ind = serial->rx_indicate; 246 | rt_device_set_rx_indicate(serial, chat_rx_ind); 247 | 248 | LOG_D("(%s) has control by modem_chat.", serial->parent.name); 249 | err = modem_chat_cmux(serial, data, len); 250 | if (err != RT_EOK) 251 | { 252 | LOG_E("chat failed"); 253 | } 254 | 255 | serial->rx_indicate = old_rx_ind; 256 | LOG_D("(%s) has control by cmux.", serial->parent.name); 257 | return err; 258 | } 259 | -------------------------------------------------------------------------------- /src/gsm/cmux_gsm.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2006-2020, RT-Thread Development Team 3 | * 4 | * SPDX-License-Identifier: Apache-2.0 5 | * 6 | * Change Logs: 7 | * Date Author Notes 8 | * 2020-04-15 xiangxistu the first version 9 | */ 10 | 11 | #include 12 | 13 | #ifdef PKG_USING_PPP_DEVICE 14 | #include 15 | #else 16 | #include 17 | #endif 18 | 19 | #define DBG_TAG "cmux.gsm" 20 | #ifdef CMUX_DEBUG 21 | #define DBG_LVL DBG_LOG 22 | #else 23 | #define DBG_LVL DBG_INFO 24 | #endif 25 | #include 26 | 27 | /** 28 | * +CMUX=[,[,[,[,[,[,[,[,]]]]]]]] 29 | * port_speed: 30 | * 1 9600 31 | 2 19200 32 | 3 38400 33 | 4 57600 34 | 5 115200 35 | 6 230400 36 | 7 460800 37 | 8 921600 38 | * 39 | */ 40 | #ifndef CMUX_CMD 41 | #define CMUX_CMD "AT+CMUX=0,0,5,2048,20,3,30,10,2" 42 | #endif 43 | 44 | static struct cmux *gsm = RT_NULL; 45 | static char cmux_cmd[64] = { CMUX_CMD }; 46 | 47 | static struct modem_chat_data cmd[] = 48 | { 49 | {"AT", MODEM_CHAT_RESP_OK, 10, 1, RT_FALSE}, 50 | {cmux_cmd, MODEM_CHAT_RESP_OK, 5, 1, RT_FALSE}, 51 | }; 52 | 53 | /** 54 | * configuration the AT+CMUX command parameter 55 | * 56 | * default @see CMUX_CMD 57 | * 58 | * @param mode 0 Basic option, 1 Advanced option 59 | * @param subset 0 UIH frames used only, 1 UI frames used only, 2 I frames used only 60 | * @param port_speed serial port speed, like 115200 61 | * @param N1 maximum frame size 62 | * @param T1 acknowledgement timer in units of ten milliseconds 63 | * @param N2 maximum number of re-transmissions 64 | * @param T2 response timer for the multiplexer control channel in units of ten milliseconds, T2 must be longer than T1 65 | * @param T3 wake up response timer in seconds 66 | * @param k window size, for Advanced operation with Error Recovery options 67 | */ 68 | void cmux_at_cmd_cfg(uint8_t mode, uint8_t subset, uint32_t port_speed, uint32_t N1, uint32_t T1, uint32_t N2, 69 | uint32_t T2, uint32_t T3, uint32_t k) 70 | { 71 | RT_ASSERT(T2 > T1); 72 | switch(port_speed) 73 | { 74 | case 9600: port_speed = 1; break; 75 | case 19200: port_speed = 2; break; 76 | case 38400: port_speed = 3; break; 77 | case 57600: port_speed = 4; break; 78 | case 115200: port_speed = 5; break; 79 | case 230400: port_speed = 6; break; 80 | case 460800: port_speed = 7; break; 81 | case 921600: port_speed = 8; break; 82 | default: RT_ASSERT("Not support port speed" && 0); 83 | } 84 | 85 | rt_snprintf(cmux_cmd, sizeof(cmux_cmd), "AT+CMUX=%d,%d,%d,%d,%d,%d,%d,%d,%d", mode, subset, port_speed, N1, T1, N2, T2, 86 | T3, k); 87 | } 88 | 89 | static rt_err_t cmux_at_command(struct rt_device *device) 90 | { 91 | /* private control, you can add power control */ 92 | 93 | // rt_thread_mdelay(5000); 94 | return modem_chat(device, cmd, sizeof(cmd) / sizeof(cmd[0])); 95 | } 96 | 97 | static rt_err_t cmux_gsm_start(struct cmux *obj) 98 | { 99 | rt_err_t result = 0; 100 | struct rt_device *device = RT_NULL; 101 | 102 | device = obj->dev; 103 | /* using DMA mode first */ 104 | result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_DMA_RX); 105 | /* result interrupt mode when DMA mode not supported */ 106 | if (result == -RT_EIO) 107 | { 108 | result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX); 109 | } 110 | if(result != RT_EOK) 111 | { 112 | LOG_E("cmux can't open %s.", device->parent.name); 113 | goto _end; 114 | } 115 | LOG_I("cmux has been control %s.", device->parent.name); 116 | 117 | result = cmux_at_command(device); 118 | if(result != RT_EOK) 119 | { 120 | LOG_E("cmux start failed."); 121 | goto _end; 122 | } 123 | 124 | _end: 125 | return result; 126 | } 127 | const struct cmux_ops cmux_ops = 128 | { 129 | cmux_gsm_start, 130 | RT_NULL, 131 | RT_NULL 132 | }; 133 | 134 | int cmux_gsm_init(void) 135 | { 136 | gsm = rt_malloc(sizeof(struct cmux)); 137 | rt_memset(gsm, 0, sizeof(struct cmux)); 138 | 139 | gsm->ops = &cmux_ops; 140 | 141 | return cmux_init(gsm, CMUX_DEPEND_NAME, CMUX_PORT_NUMBER, RT_NULL); 142 | } 143 | INIT_COMPONENT_EXPORT(cmux_gsm_init); 144 | --------------------------------------------------------------------------------