From a70a31d282c3ca047bfbb0a93c2dd1be179fb55b Mon Sep 17 00:00:00 2001 From: Beichen Date: Wed, 3 Aug 2022 09:39:02 +0800 Subject: [PATCH 01/21] =?UTF-8?q?Ubiquitous/RT-Thread=5FFusion=5FXiUOS/aii?= =?UTF-8?q?t=5Fboard/xidatong-riscv64/applications/=EF=BC=9AUpdate=20the?= =?UTF-8?q?=20main=20function=20and=20add=20the=20dependency=20macro=20of?= =?UTF-8?q?=20lcd=5Ftest.c=20in=20SConscript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xidatong-riscv64/applications/SConscript | 11 ++++++++++- .../aiit_board/xidatong-riscv64/applications/main.c | 10 +++++----- 2 files changed, 15 insertions(+), 6 deletions(-) diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript index c583d3016..7bb0b8524 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript @@ -1,9 +1,18 @@ from building import * cwd = GetCurrentDir() -src = Glob('*.c') + Glob('*.cpp') +src = [ + 'main.c' +] CPPPATH = [cwd] +## 设置 lcd_test.c 的依赖宏 +if GetDepend('BSP_USING_LCD'): + src += ['lcd_test.c'] + group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) Return('group') + + + diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/main.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/main.c index 5b51e4275..cf701b92a 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/main.c +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/main.c @@ -21,11 +21,11 @@ #include #include #include -#define LED_G 12 +//#define LED_G 12 int main(void) { - rt_pin_mode(LED_G, PIN_MODE_OUTPUT); + //rt_pin_mode(LED_G, PIN_MODE_OUTPUT); rt_thread_mdelay(100); char info1[25] ={0}; char info2[25] ={0}; @@ -39,10 +39,10 @@ int main(void) #endif while(1) { - rt_pin_write(LED_G, PIN_HIGH); - rt_thread_mdelay(500); - rt_pin_write(LED_G, PIN_LOW); + //rt_pin_write(LED_G, PIN_HIGH); rt_thread_mdelay(500); + //rt_pin_write(LED_G, PIN_LOW); + //rt_thread_mdelay(500); } return 0; } From 38c2864c73431c78e85199c846beb799c7fbc746 Mon Sep 17 00:00:00 2001 From: Beichen Date: Wed, 3 Aug 2022 09:53:01 +0800 Subject: [PATCH 02/21] =?UTF-8?q?Ubiquitous/RT-Thread=5FFusion=5FXiUOS/app?= =?UTF-8?q?=5Fmatch=5Frt-thread/=EF=BC=9AAdd=20WIZnet=20software=20package?= =?UTF-8?q?=20which=20supports=20W5500=20device=20and=20update=20the=20Kco?= =?UTF-8?q?nfig?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app_match_rt-thread/Kconfig | 1 + .../app_match_rt-thread/wiznet/Kconfig | 112 + .../app_match_rt-thread/wiznet/LICENSE | 201 ++ .../app_match_rt-thread/wiznet/README.md | 164 ++ .../app_match_rt-thread/wiznet/README_ZH.md | 167 ++ .../app_match_rt-thread/wiznet/SConscript | 34 + .../app_match_rt-thread/wiznet/inc/wiz.h | 40 + .../wiznet/inc/wiz_socket.h | 116 + .../wiznet/ioLibrary/Ethernet/W5500/w5500.c | 267 ++ .../wiznet/ioLibrary/Ethernet/W5500/w5500.h | 2163 +++++++++++++++++ .../wiznet/ioLibrary/Ethernet/wizchip_conf.c | 908 +++++++ .../wiznet/ioLibrary/Ethernet/wizchip_conf.h | 661 +++++ .../ioLibrary/Ethernet/wizchip_socket.c | 931 +++++++ .../ioLibrary/Ethernet/wizchip_socket.h | 489 ++++ .../ioLibrary/Internet/DHCP/wizchip_dhcp.c | 1030 ++++++++ .../ioLibrary/Internet/DHCP/wizchip_dhcp.h | 162 ++ .../ioLibrary/Internet/DNS/wizchip_dns.c | 564 +++++ .../ioLibrary/Internet/DNS/wizchip_dns.h | 109 + .../app_match_rt-thread/wiznet/src/wiz.c | 987 ++++++++ .../wiznet/src/wiz_af_inet.c | 110 + .../wiznet/src/wiz_device.c | 83 + .../app_match_rt-thread/wiznet/src/wiz_ping.c | 259 ++ .../wiznet/src/wiz_socket.c | 1739 +++++++++++++ 23 files changed, 11297 insertions(+) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/Kconfig create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/LICENSE create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README.md create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README_ZH.md create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/SConscript create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz_socket.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_af_inet.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_device.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_ping.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_socket.c diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/Kconfig b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/Kconfig index 4bae294f6..bd708394c 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/Kconfig +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/Kconfig @@ -4,4 +4,5 @@ source "$RT_Thread_DIR/app_match_rt-thread/rw007/Kconfig" source "$RT_Thread_DIR/app_match_rt-thread/ov2640/Kconfig" source "$RT_Thread_DIR/app_match_rt-thread/hs300x/Kconfig" source "$RT_Thread_DIR/app_match_rt-thread/sx1278/Kconfig" +source "$RT_Thread_DIR/app_match_rt-thread/wiznet/Kconfig" endmenu diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/Kconfig b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/Kconfig new file mode 100644 index 000000000..018e8dace --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/Kconfig @@ -0,0 +1,112 @@ + +# Kconfig file for package wiznet +menuconfig PKG_USING_WIZNET + bool "WIZnet: WIZnet TCP/IP chips SAL framework implement" + default n + select RT_USING_PIN + select RT_USING_SPI + select RT_USING_LIBC if RT_VER_NUM < 0x40100 + select RT_USING_SAL + select RT_USING_TIMER_SOFT + +if PKG_USING_WIZNET + + config PKG_WIZNET_PATH + string + default "/packages/iot/wiznet" + + choice + prompt "WIZnet device type" + default WIZ_USING_W5500 + help + Select the wiznet type + + config WIZ_USING_W5500 + bool "W5500" + + endchoice + + config WIZNET_DEVICE_EXTERN_CONFIG + bool + default n + + if !WIZNET_DEVICE_EXTERN_CONFIG + menu "WIZnet device configure" + + config WIZ_SPI_DEVICE + string "SPI device name" + default "spi10" + + config WIZ_RST_PIN + int "Reset PIN number" + default 10 + + config WIZ_IRQ_PIN + int "IRQ PIN number" + default 11 + endmenu + endif + + config WIZ_USING_DHCP + bool "Enable alloc IP address through DHCP" + default y + + if !WIZ_USING_DHCP + + menu "WIZnet network configure" + + config WIZ_IPADDR + string "IPv4: IP address" + default 192.168.1.10 + + config WIZ_GWADDR + string "IPv4: Gateway address" + default 192.168.1.1 + + config WIZ_MSKADDR + string "IPv4: Mask address" + default 255.255.255.0 + + endmenu + + endif + + config WIZ_USING_PING + bool "Enable Ping utility" + default y + + config WIZ_DEBUG + bool "Enable debug log output" + default n + + choice + prompt "Version" + default PKG_USING_WIZNET_V110 if ((RT_VER_NUM < 0x30103) || (RT_VER_NUM = 0x40000)) + default PKG_USING_WIZNET_LATEST_VERSION + help + Select the wiznet version + + config PKG_USING_WIZNET_V200 + bool "v2.0.0" + + if ((RT_VER_NUM < 0x30103) || (RT_VER_NUM = 0x40000)) + config PKG_USING_WIZNET_V110 + bool "v1.1.0" + + config PKG_USING_WIZNET_V100 + bool "v1.0.0" + endif + + config PKG_USING_WIZNET_LATEST_VERSION + bool "latest" + + endchoice + + config PKG_WIZNET_VER + string + default "v2.0.0" if PKG_USING_WIZNET_V200 + default "v1.1.0" if PKG_USING_WIZNET_V110 + default "v1.0.0" if PKG_USING_WIZNET_V100 + default "latest" if PKG_USING_WIZNET_LATEST_VERSION + +endif diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/LICENSE b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/LICENSE new file mode 100644 index 000000000..261eeb9e9 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README.md b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README.md new file mode 100644 index 000000000..ff5bf80f7 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README.md @@ -0,0 +1,164 @@ +# WIZnet + +[中文页](README_ZH.md) | English + +## 1. Introduction + +The WIZnet software package is a porting implementation of RT-Thread based on the WIZnet official website [ioLibrary_Driver](https://github.com/Wiznet/ioLibrary_Driver) code base, and currently only supports W5500 devices. On the basis of the original code library function, this software package docks with the RT-Thread SAL socket abstraction layer, realizes the support for standard BSD Socket APIs, is perfectly compatible with a variety of software packages and network functions, and improves the compatibility of WIZnet devices. + +### 1.1 Directory structure + +The WIZnet software package directory structure is as follows: + +``` +wiznet +├───inc // RT_Thread transplant header file +├───iolibrary // WIZnet official library file +│ └───Ethernet // WIZnet official Socket APIs and WIZCHIP driver +│ │ └───W5500 // WIZCHIP driver +│ │ wizchip_conf.c // Socket configuration file +│ │ wizchip_socket.c // Socket APIs file +│ └───Internet // WIZnet official network function realization +│ │ └───DHCP // DHCP function implementation +│ └───────DNS // DNS function realization +├───src // RT_Thread transplant source code file +│ └───wiz_af_inet.c // WIZnet BSD Socket registered to SAL +│ │ wiz_device.c // WIZnet device initialization +│ │ wiz_ping.c // WIZnet device Ping command realization +│ │ wiz_socket.c // WIZnet BSD Socket APIs implementation +│ └───wiz.c // WIZnet initialization (device initialization, network initialization) +│ LICENSE // package license +│ README.md // Software package instructions +└───SConscript // RT-Thread default build script +``` + + +### 1.2 License + +The WIZnet software package complies with the Apache-2.0 license, see the LICENSE file for details. + +### 1.3 Dependency + +- RT-Thread 4.0.1+ +- SAL component +- netdev component +- SPI driver: WIZnet devices use SPI for data communication, which requires the support of the system SPI driver framework; +- PIN driver: used to handle device reset and interrupt pins; + +## 2. Get the software package + +To use the WIZnet software package, you need to select it in the RT-Thread package management. The specific path is as follows: + +```shell +WIZnet: WIZnet TCP/IP chips SAL framework implement + WIZnet device type (W5500) ---> + WIZnet device configure ---> + (spi30) SPI device name + (10) Reset PIN number + (11) IRQ PIN number + [] Enable alloc IP address through DHCP + WIZnet network configure ---> + (192.168.1.10) IPv4: IP address + (192.168.1.1) IPv4: Gateway address + (255.255.255.0) IPv4: Mask address + [] Enable Ping utility + [] Enable debug log output + Version (latest) ---> +``` + +**WIZnet device type**: Configure the supported device type (currently only supports W5500 devices) + +**WIZnet device configure**: configure the parameters of the device used + +- **SPI device name**: Configure the name of the device using SPI (note that it needs to be set to **non-SPI bus device**) + +- **Reset PIN number**: Configure the reset pin number connected to the device (modified according to the actual pin number used) + +- **IRQ PIN number**: Configure the interrupt pin number of the device connection (same as above) + +**Enable alloc IP address through DHCP**: Configure whether to use DHCP to allocate IP addresses (enabled by default) + +**WIZnet network configure**: If you do not enable the DHCP function, you need to configure the statically connected IP address, gateway and subnet mask + +**Enable Ping utility**: Configure to enable Ping command (enabled by default) + +**Enable debug log output**: Configure to enable debug log display + +**Version**: software package version selection + +## 3. Use the software package + +The initialization function of WIZnet software package is as follows: + +```c +int wiz_init(void); +``` + +This function supports component initialization. If the automatic component initialization function is enabled, the application layer does not need to call this function. The main functions of the function are: + +- Set the default MAC address; +- Device configuration and initialization (configure SPI device, configure reset and interrupt pins); +- Network configuration and initialization (DHCP allocation of IP address, configuration of socket parameters); +- Register the implemented BSD Socket APIs to the SAL socket abstraction layer to complete WIZnet device adaptation; + +Each WIZnet device needs a unique MAC address. The user can call the following function in the application layer program to set the MAC address of the WIZnet device. If this function is not called, the device will use the default MAC address. The default MAC address is `00-E0-81 -DC-53-1A` (Note: If there are devices with the same MAC address in the same LAN, it may cause the device network to be abnormal). + +```c +int wiz_set_mac(const char *mac); +``` + +After the device is powered on and initialized, the device's MAC address is successfully set, and then you can enter the command `wiz_ifconfig` in FinSH to view the device's IP address, MAC address and other network information, as shown below: + +```shell +msh />ifconfig +network interface device: W5500 (Default) ## Device name +MTU: 1472 ## Network maximum transmission unit +MAC: 00 e0 81 dc 53 1a ## Device MAC address +FLAGS: UP LINK_UP INTERNET_UP ## Device flag +ip address: 192.168.12.26 ## Device IP address +gw address: 192.168.10.1 ## Device gateway address +net mask: 255.255.0.0 ## Device subnet mask +dns server #0: 192.168.10.1 ## DNS server address 0 +dns server #1: 0.0.0.0 ## DNS server address 1 +``` + +After obtaining the IP address successfully, if the Ping command function is enabled, you can enter the command `ping + domain name address` in FinSH to test the network connection status, as shown below: + +```shell +msh />wiz_ping baidu.com +32 bytes from 220.181.57.216 icmp_seq=0 ttl=128 time=31 ticks +32 bytes from 220.181.57.216 icmp_seq=1 ttl=128 time=31 ticks +32 bytes from 220.181.57.216 icmp_seq=2 ttl=128 time=32 ticks +32 bytes from 220.181.57.216 icmp_seq=3 ttl=128 time=32 ticks +``` + +The normal test of the `ping` command indicates that the WIZnet device is successfully connected to the network, and then you can use the standard BSD Socket APIs abstracted by SAL (Socket Abstraction Layer) for network development (MQTT, HTTP, MbedTLS, NTP, Iperf, etc.), WIZnet software package The supported protocol cluster types are: the primary protocol cluster is **AF_WIZ**, and the secondary protocol cluster is **AF_INET** (for specific differences and usage, please refer to [SAL Programming Guide](https://www.rt-thread.org/document/site/submodules/rtthread-manual-doc/zh/1chapters/13-chapter_sal/) ). + +## 4. Common problems + +- Assertion problem during SPI device initialization + + ```shell + (wiz_device->parent.type == RT_Device_Class_SPIDevice) assertion failed at function:wiz_spi_init, line number:126 + ``` + + The above assertion problem occurs. The possible reason is that the name of the SPI device used by WIZnet in ENV is incorrectly filled. Please distinguish the relationship between SPI DEVICE and SPI BUS. If there is no SPI device or only SPI bus device in the BSP project, you need to manually mount the SPI device to the SPI bus in the driver and correctly configure the SPI device name used in the WIZnet software package. + +- The latest version of WIZnet software package has been supported as a server server mode (not supported before V1.1.0). + +- WIZNet software package initialization error ```[E/wiz.dev] You should attach [wiznet] into SPI bus firstly.``` error is caused by not mounting the winzet device to the SPI bus; please refer to the wiz_init function Note to solve the problem of package initialization failure. + +- When using the previous code in the RT-Thread repository, please compare ```[components/net/sal_socket/src/sal_socket.c]```, especially about the content of [PR](https://github.com/RT-Thread/rt-thread/pull/3534/files), pay attention to the content of sal_closesocket . When you always fail to apply for socket(-1), please make sure that the RT-Thread code you are using is the same as the [PR](https://github.com/RT-Thread/rt-thread/pull/3534 /files). + +- When applying for a socket, the error is ```0x22```. Note that the development branch of wiznet is in the master version or a version greater than V1.1.0. Please pay attention to the execution order of ```wiz_socket_init()```, because the ```sal_check_netdev_internet_up``` networking detection function will actively apply for a socket to determine whether the w5500 has network capabilities, and network status changes will cause ```sal_check_netdev_internet_up``` was called, causing ```0x22``` error. + + +## 5. Matters needing attention + +- When obtaining the software package, you need to pay attention to the correct configuration of the SPI device name, reset pin number and interrupt pin number used; +- After the initialization is complete, it is recommended to use the `wiz_set_mac()` function to set the device MAC address to prevent conflicts with the default MAC address; + +## 6. Contact & Thanks + +- Maintenance: RT-Thread development team +- Homepage: https://github.com/RT-Thread-packages/wiznet diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README_ZH.md b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README_ZH.md new file mode 100644 index 000000000..de52d88a6 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/README_ZH.md @@ -0,0 +1,167 @@ +# WIZnet + +中文页 | [English](README.md) + +## 1、介绍 + +WIZnet 软件包是 RT-Thread 基于 WIZnet 官网 [ioLibrary_Driver](https://github.com/Wiznet/ioLibrary_Driver) 代码库的移植实现,目前只支持 W5500 设备。该软件包在原代码库功能的基础上,对接 RT-Thread SAL 套接字抽象层,实现对标准 BSD Socket APIs 的支持,完美的兼容多种软件包和网络功能实现,提高 WIZnet 设备兼容性。 + +### 1.1 目录结构 + +WIZnet 软件包目录结构如下所示: + +``` +wiznet +├───inc // RT_Thread 移植头文件 +├───iolibrary // WIZnet 官方库文件 +│ └───Ethernet // WIZnet 官方 Socket APIs 和 WIZCHIP 驱动 +│ │ └───W5500 // WIZCHIP 驱动 +│ │ wizchip_conf.c // Socket 配置文件 +│ │ wizchip_socket.c // Socket APIs 文件 +│ └───Internet // WIZnet 官方网络功能实现 +│ │ └───DHCP // DHCP 功能实现 +│ └───────DNS // DNS 功能实现 +├───src // RT_Thread 移植源码文件 +│ └───wiz_af_inet.c // WIZnet BSD Socket 注册到 SAL +│ │ wiz_device.c // WIZnet 设备初始化 +│ │ wiz_ping.c // WIZnet 设备 Ping 命令实现 +│ │ wiz_socket.c // WIZnet BSD Socket APIs 实现 +│ └───wiz.c // WIZnet 初始化(设备初始化、网络初始化) +│ LICENSE // 软件包许可证 +│ README.md // 软件包使用说明 +└───SConscript // RT-Thread 默认的构建脚本 +``` + + +### 1.2 许可证 + +WIZnet 软件包遵循 Apache-2.0 许可,详见 LICENSE 文件。 + +### 1.3 依赖 + +- RT-Thread 4.0.1+ +- SAL 组件 +- netdev 组件 +- SPI 驱动:WIZnet 设备使用 SPI 进行数据通讯,需要系统 SPI 驱动框架支持; +- PIN 驱动:用于处理设备复位和中断引脚; + +## 2、获取软件包 + +使用 WIZnet 软件包需要在 RT-Thread 的包管理中选中它,具体路径如下: + +```shell +WIZnet: WIZnet TCP/IP chips SAL framework implement + WIZnet device type (W5500) ---> + WIZnet device configure ---> + (spi30) SPI device name + (10) Reset PIN number + (11) IRQ PIN number + [ ] Enable alloc IP address through DHCP + WIZnet network configure ---> + (192.168.1.10) IPv4: IP address + (192.168.1.1) IPv4: Gateway address + (255.255.255.0) IPv4: Mask address + [ ] Enable Ping utility + [ ] Enable debug log output + Version (latest) ---> +``` + +**WIZnet device type** :配置支持的设备类型(目前只支持 W5500 设备 ) + +**WIZnet device configure** :配置使用设备的参数 + +- **SPI device name**:配置使用 SPI 的设备名称(注意需设置为**非 SPI 总线设备**) + +- **Reset PIN number**:配置设备连接的复位引脚号(根据实际使用引脚号修改) + +- **IRQ PIN number**:配置设备连接的中断引脚号(同上) + +**Enable alloc IP address through DHCP**: 配置是否使用 DHCP 分配 IP 地址(默认开启) + +**WIZnet network configure**:如果不开启 DHCP 功能,需要配置静态连接的 IP 地址、网关和子网掩码 + +**Enable Ping utility**: 配置开启 Ping 命令 (默认开启) + +**Enable debug log output**:配置开启调试日志显示 + +**Version**:软件包版本选择 + +## 3、使用软件包 + +WIZnet 软件包初始化函数如下所示: + +```c +int wiz_init(void); +``` + +该函数支持组件初始化,如果开启组件自动初始化功能,则应用层无需在调用该函数 ,函数主要完成功能有, + +- 设置默认 MAC 地址; + +- 设备配置和初始化(配置 SPI 设备,配置复位和中断引脚); + +- 网络配置和初始化(DHCP 分配 IP 地址,配置 socket 参数 ); + +- 注册实现的 BSD Socket APIs 到 SAL 套接字抽象层中,完成 WIZnet 设备适配; + +每个 WIZnet 设备需要唯一的 MAC 地址,用户可以在应用层程序中调用如下函数设置 WIZnet 设备 MAC 地址,如果不调用该函数,设备将使用默认的 MAC 地址,默认 MAC 地址为 `00-E0-81-DC-53-1A`(注意:同一个局域网中如果存在相同 MAC 地址的设备,可能导致设备网络异常) 。 + +```c +int wiz_set_mac(const char *mac); +``` + +设备上电初始化完成,设置设备 MAC 地址成功,然后可以在 FinSH 中输入命令 `wiz_ifconfig` 查看设备 IP 地址、MAC 地址等网络信息,如下所示: + +```shell +msh />ifconfig +network interface device: W5500 (Default) ## 设备名称 +MTU: 1472 ## 网络最大传输单元 +MAC: 00 e0 81 dc 53 1a ## 设备 MAC 地址 +FLAGS: UP LINK_UP INTERNET_UP ## 设备标志 +ip address: 192.168.12.26 ## 设备 IP 地址 +gw address: 192.168.10.1 ## 设备网关地址 +net mask : 255.255.0.0 ## 设备子网掩码 +dns server #0: 192.168.10.1 ## 域名解析服务器地址0 +dns server #1: 0.0.0.0 ## 域名解析服务器地址1 +``` + +获取 IP 地址成功之后,如果开启 Ping 命令功能,可以在 FinSH 中输入命令 `ping + 域名地址` 测试网络连接状态, 如下所示: + +```shell +msh />wiz_ping baidu.com +32 bytes from 220.181.57.216 icmp_seq=0 ttl=128 time=31 ticks +32 bytes from 220.181.57.216 icmp_seq=1 ttl=128 time=31 ticks +32 bytes from 220.181.57.216 icmp_seq=2 ttl=128 time=32 ticks +32 bytes from 220.181.57.216 icmp_seq=3 ttl=128 time=32 ticks +``` + +`ping` 命令测试正常说明 WIZnet 设备网络连接成功,之后可以使用 SAL(套接字抽象层) 抽象出来的标准 BSD Socket APIs 进行网络开发(MQTT、HTTP、MbedTLS、NTP、Iperf 等),WIZnet 软件包支持的协议簇类型为:主协议簇为 **AF_WIZ**、次协议簇为 **AF_INET**(具体区别和使用方式可查看 [SAL 编程指南](https://www.rt-thread.org/document/site/submodules/rtthread-manual-doc/zh/1chapters/13-chapter_sal/) )。 + +## 4、常见问题 + +- SPI 设备初始化时断言问题 + + ```shell + (wiz_device->parent.type == RT_Device_Class_SPIDevice) assertion failed at function:wiz_spi_init, line number:126 + ``` + + 出现上述断言问题,可能原因是 ENV 中配置 WIZnet 使用的 SPI 设备名称填写不正确,请区分 SPI DEVICE 与 SPI BUS 的关系。如果 BSP 工程中没有 SPI 设备或者只有 SPI 总线设备,需要手动在驱动中挂载 SPI 设备到 SPI 总线,并正确配置 WIZnet 软件包中使用的 SPI 设备名称。 + +- WIZnet 软件包最新版本已支持作为 server 服务器模式(V1.1.0 版本之前不支持)。 + +- WIZNet 软件包初始化出现 ```[E/wiz.dev] You should attach [wiznet] into SPI bus firstly.```错误,是因为没有挂载 winzet 设备到 SPI 总线导致的;请参考 wiz_init 函数中的注释,解决软件包初始化失败的问题。 + +- 在使用 RT-Thread 仓库的既往代码时,请比对 ```[components/net/sal_socket/src/sal_socket.c]```的内容,尤其是关于此处 [PR](https://github.com/RT-Thread/rt-thread/pull/3534/files) 的内容,注意 sal_closesocket 的内容。当你总是申请 socket(-1) 失败时,请确保你所使用的 RT-Thread 的代码是与该 [PR](https://github.com/RT-Thread/rt-thread/pull/3534/files) 的意图相符合的。 + +- 当出现申请 socket 时错误为 ```0x22``` 错误,注意 wiznet 的开发分支处于 master 版本或者大于 V1.1.0 的版本。请留意 ```wiz_socket_init()``` 的执行顺序,因为 ```sal_check_netdev_internet_up``` 联网检测函数,会主动申请 socket 以判断 w5500 是否具有网络能力,而网络状态变更会导致 ```sal_check_netdev_internet_up``` 被调用,造成 ```0x22``` 错误。 + + +## 5、注意事项 + +- 获取软件包时,需要注意正确配置使用的 SPI 设备名称、复位引脚号和中断引脚号; +- 初始化完成之后,建议使用 `wiz_set_mac()` 函数设置设备 MAC 地址,防止使用默认 MAC 地址产生冲突; + +## 6、联系方式 & 感谢 + +- 维护:RT-Thread 开发团队 +- 主页:https://github.com/RT-Thread-packages/wiznet diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/SConscript b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/SConscript new file mode 100644 index 000000000..ebcfeef47 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/SConscript @@ -0,0 +1,34 @@ +from building import * + +cwd = GetCurrentDir() + +src = Split(''' +src/wiz_af_inet.c +src/wiz_device.c +src/wiz_socket.c +src/wiz.c +''') + +if GetDepend(['WIZ_USING_PING']): + src += Glob('src/wiz_ping.c') + +src += Glob('ioLibrary/Ethernet/*.c') +src += Glob('ioLibrary/Internet/DNS/*.c') + +if GetDepend(['WIZ_USING_DHCP']): + src += Glob('ioLibrary/Internet/DHCP/*.c') + +if GetDepend(['WIZ_USING_W5500']): + src += Glob('ioLibrary/Ethernet/W5500/*.c') + +CPPPATH = [ +cwd + '/inc', +cwd + '/ioLibrary', +cwd + '/ioLibrary/Ethernet', +cwd + '/ioLibrary/Ethernet/W5500', +cwd + '/ioLibrary/Internet', +] + +group = DefineGroup('WIZnet', src, depend = ['PKG_USING_WIZNET'], CPPPATH = CPPPATH) + +Return('group') diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz.h new file mode 100644 index 000000000..5c4e4bc63 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#ifndef __WIZ_H__ +#define __WIZ_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define WIZ_SW_VERSION "2.1.0" +#define WIZ_SW_VERSION_NUM 0x020100 + +#ifndef WIZ_SOCKETS_NUM +#define WIZ_SOCKETS_NUM 8 +#endif + +#ifndef WIZ_RX_MBOX_NUM +#define WIZ_RX_MBOX_NUM 10 +#endif + +/* WIZnet set chip MAC address */ +void wiz_user_config_mac(char *mac_buf, rt_uint8_t buf_len); +/* WIZnet initialize device and network */ +int wiz_init(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __WIZ_H__ */ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz_socket.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz_socket.h new file mode 100644 index 000000000..4886be0ec --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/inc/wiz_socket.h @@ -0,0 +1,116 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#ifndef __WIZ_SOCKET_H__ +#define __WIZ_SOCKET_H__ + +#include +#include +#include + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* WIZnet socket magic word */ +#define WIZ_SOCKET_MAGIC 0x3120 + +/* WIZnet Socket address family */ +#ifndef AF_WIZ +#define AF_WIZ 46 +#endif + +struct wiz_socket; +/* A callback prototype to inform about events for wiznet socket */ +typedef void (* wiz_socket_callback)(struct wiz_socket *conn, int event, uint16_t len); + +struct wiz_clnt_info +{ + int socket; + int state; + rt_slist_t list; +}; + +struct wiz_svr_info +{ + int backlog; + rt_timer_t conn_tmr; + rt_mailbox_t conn_mbox; + rt_slist_t clnt_list; +}; + +struct wiz_socket +{ + /* WIZnet socket magic word */ + uint32_t magic; + + int socket; + uint16_t port; + /* type of the WIZnet socket (TCP, UDP or RAW) */ + uint8_t type; + /* current state of the WIZnet socket */ + uint8_t state; + /* receive semaphore, received data release semaphore */ + rt_sem_t recv_notice; + rt_mutex_t recv_lock; + + /* timeout to wait for send or receive data in milliseconds */ + int32_t recv_timeout; + int32_t send_timeout; + + /* A callback function that is informed about events for this AT socket */ + wiz_socket_callback callback; + + struct sockaddr *remote_addr; + /* number of times data was received, set by event_callback() */ + uint16_t rcvevent; + /* number of times data was ACKed (free send buffer), set by event_callback() */ + uint16_t sendevent; + /* error happened for this socket, set by event_callback() */ + uint16_t errevent; + + /* server socket information */ + struct wiz_svr_info *svr_info; + +#ifdef SAL_USING_POSIX + rt_wqueue_t wait_head; +#endif +}; + +int wiz_socket(int domain, int type, int protocol); +int wiz_closesocket(int socket); +int wiz_shutdown(int socket, int how); +int wiz_listen(int socket, int backlog); +int wiz_bind(int socket, const struct sockaddr *name, socklen_t namelen); +int wiz_connect(int socket, const struct sockaddr *name, socklen_t namelen); +int wiz_accept(int socket, struct sockaddr *addr, socklen_t *addrlen); +int wiz_sendto(int socket, const void *dwiza, size_t size, int flags, const struct sockaddr *to, socklen_t tolen); +int wiz_send(int socket, const void *dwiza, size_t size, int flags); +int wiz_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen); +int wiz_recv(int socket, void *mem, size_t len, int flags); +int wiz_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen); +int wiz_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen); +struct hostent *wiz_gethostbyname(const char *name); +int wiz_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res); +void wiz_freeaddrinfo(struct addrinfo *ai); + +/* get WIZnet socket object */ +struct wiz_socket *wiz_get_socket(int socket); +/* WIZnet chip TCP/IP protocol register */ +int wiz_inet_init(void); + +#ifdef __cplusplus + } +#endif + +#endif /* __WIZ_SOCKET_H__ */ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.c new file mode 100644 index 000000000..68d4cb8be --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.c @@ -0,0 +1,267 @@ +//***************************************************************************** +// +//! \file w5500.c +//! \brief W5500 HAL Interface. +//! \version 1.0.2 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2014/05/01> V1.0.2 +//! 1. Implicit type casting -> Explicit type casting. Refer to M20140501 +//! Fixed the problem on porting into under 32bit MCU +//! Issued by Mathias ClauBen, wizwiki forum ID Think01 and bobh +//! Thank for your interesting and serious advices. +//! <2013/12/20> V1.0.1 +//! 1. Remove warning +//! 2. WIZCHIP_READ_BUF WIZCHIP_WRITE_BUF in case _WIZCHIP_IO_MODE_SPI_FDM_ +//! for loop optimized(removed). refer to M20131220 +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** +//#include +#include "w5500.h" + +#define _W5500_SPI_VDM_OP_ 0x00 +#define _W5500_SPI_FDM_OP_LEN1_ 0x01 +#define _W5500_SPI_FDM_OP_LEN2_ 0x02 +#define _W5500_SPI_FDM_OP_LEN4_ 0x03 + +#if (_WIZCHIP_ == 5500) +//////////////////////////////////////////////////// + +uint8_t WIZCHIP_READ(uint32_t AddrSel) +{ + uint8_t ret; + uint8_t spi_data[3]; + + WIZCHIP_CRITICAL_ENTER(); + WIZCHIP.CS._select(); + + AddrSel |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_); + + if(!WIZCHIP.IF.SPI._read_burst || !WIZCHIP.IF.SPI._write_burst) // byte operation + { + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0); + } + else // burst operation + { + spi_data[0] = (AddrSel & 0x00FF0000) >> 16; + spi_data[1] = (AddrSel & 0x0000FF00) >> 8; + spi_data[2] = (AddrSel & 0x000000FF) >> 0; + WIZCHIP.IF.SPI._write_burst(spi_data, 3); + } + ret = WIZCHIP.IF.SPI._read_byte(); + + WIZCHIP.CS._deselect(); + WIZCHIP_CRITICAL_EXIT(); + return ret; +} + +void WIZCHIP_WRITE(uint32_t AddrSel, uint8_t wb ) +{ + uint8_t spi_data[4]; + + WIZCHIP_CRITICAL_ENTER(); + WIZCHIP.CS._select(); + + AddrSel |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_); + + //if(!WIZCHIP.IF.SPI._read_burst || !WIZCHIP.IF.SPI._write_burst) // byte operation + if(!WIZCHIP.IF.SPI._write_burst) // byte operation + { + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0); + WIZCHIP.IF.SPI._write_byte(wb); + } + else // burst operation + { + spi_data[0] = (AddrSel & 0x00FF0000) >> 16; + spi_data[1] = (AddrSel & 0x0000FF00) >> 8; + spi_data[2] = (AddrSel & 0x000000FF) >> 0; + spi_data[3] = wb; + WIZCHIP.IF.SPI._write_burst(spi_data, 4); + } + + WIZCHIP.CS._deselect(); + WIZCHIP_CRITICAL_EXIT(); +} + +void WIZCHIP_READ_BUF (uint32_t AddrSel, uint8_t* pBuf, uint16_t len) +{ + uint8_t spi_data[3]; + uint16_t i; + + WIZCHIP_CRITICAL_ENTER(); + WIZCHIP.CS._select(); + + AddrSel |= (_W5500_SPI_READ_ | _W5500_SPI_VDM_OP_); + + if(!WIZCHIP.IF.SPI._read_burst || !WIZCHIP.IF.SPI._write_burst) // byte operation + { + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0); + for(i = 0; i < len; i++) + pBuf[i] = WIZCHIP.IF.SPI._read_byte(); + } + else // burst operation + { + spi_data[0] = (AddrSel & 0x00FF0000) >> 16; + spi_data[1] = (AddrSel & 0x0000FF00) >> 8; + spi_data[2] = (AddrSel & 0x000000FF) >> 0; + WIZCHIP.IF.SPI._write_burst(spi_data, 3); + WIZCHIP.IF.SPI._read_burst(pBuf, len); + } + + WIZCHIP.CS._deselect(); + WIZCHIP_CRITICAL_EXIT(); +} + +void WIZCHIP_WRITE_BUF(uint32_t AddrSel, uint8_t* pBuf, uint16_t len) +{ + uint8_t spi_data[3]; + uint16_t i; + + WIZCHIP_CRITICAL_ENTER(); + WIZCHIP.CS._select(); + + AddrSel |= (_W5500_SPI_WRITE_ | _W5500_SPI_VDM_OP_); + + if(!WIZCHIP.IF.SPI._write_burst) // byte operation + { + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x00FF0000) >> 16); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x0000FF00) >> 8); + WIZCHIP.IF.SPI._write_byte((AddrSel & 0x000000FF) >> 0); + for(i = 0; i < len; i++) + WIZCHIP.IF.SPI._write_byte(pBuf[i]); + } + else // burst operation + { + spi_data[0] = (AddrSel & 0x00FF0000) >> 16; + spi_data[1] = (AddrSel & 0x0000FF00) >> 8; + spi_data[2] = (AddrSel & 0x000000FF) >> 0; + WIZCHIP.IF.SPI._write_burst(spi_data, 3); + WIZCHIP.IF.SPI._write_burst(pBuf, len); + } + + WIZCHIP.CS._deselect(); + WIZCHIP_CRITICAL_EXIT(); +} + + +uint16_t getSn_TX_FSR(uint8_t sn) +{ + uint16_t val=0,val1=0; + + do + { + val1 = WIZCHIP_READ(Sn_TX_FSR(sn)); + val1 = (val1 << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_FSR(sn),1)); + if (val1 != 0) + { + val = WIZCHIP_READ(Sn_TX_FSR(sn)); + val = (val << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_FSR(sn),1)); + } + }while (val != val1); + return val; +} + + +uint16_t getSn_RX_RSR(uint8_t sn) +{ + uint16_t val=0,val1=0; + + do + { + val1 = WIZCHIP_READ(Sn_RX_RSR(sn)); + val1 = (val1 << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RSR(sn),1)); + if (val1 != 0) + { + val = WIZCHIP_READ(Sn_RX_RSR(sn)); + val = (val << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RSR(sn),1)); + } + }while (val != val1); + return val; +} + +void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len) +{ + uint16_t ptr = 0; + uint32_t addrsel = 0; + + if(len == 0) return; + ptr = getSn_TX_WR(sn); + //M20140501 : implict type casting -> explict type casting + //addrsel = (ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3); + addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_TXBUF_BLOCK(sn) << 3); + // + WIZCHIP_WRITE_BUF(addrsel,wizdata, len); + + ptr += len; + setSn_TX_WR(sn,ptr); +} + +void wiz_recv_data(uint8_t sn, uint8_t *wizdata, uint16_t len) +{ + uint16_t ptr = 0; + uint32_t addrsel = 0; + + if(len == 0) return; + ptr = getSn_RX_RD(sn); + //M20140501 : implict type casting -> explict type casting + //addrsel = ((ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3); + addrsel = ((uint32_t)ptr << 8) + (WIZCHIP_RXBUF_BLOCK(sn) << 3); + // + WIZCHIP_READ_BUF(addrsel, wizdata, len); + ptr += len; + + setSn_RX_RD(sn,ptr); +} + + +void wiz_recv_ignore(uint8_t sn, uint16_t len) +{ + uint16_t ptr = 0; + + ptr = getSn_RX_RD(sn); + ptr += len; + setSn_RX_RD(sn,ptr); +} + +#endif diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.h new file mode 100644 index 000000000..3afc16e9c --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/W5500/w5500.h @@ -0,0 +1,2163 @@ +//***************************************************************************** +// +//! \file w5500.h +//! \brief W5500 HAL Header File. +//! \version 1.0.0 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +// + +#ifndef _W5500_H_ +#define _W5500_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "wizchip_conf.h" + +/// @cond DOXY_APPLY_CODE +#if (_WIZCHIP_ == 5500) +/// @endcond + +#define _W5500_IO_BASE_ 0x00000000 + +#define _W5500_SPI_READ_ (0x00 << 2) //< SPI interface Read operation in Control Phase +#define _W5500_SPI_WRITE_ (0x01 << 2) //< SPI interface Write operation in Control Phase + +#define WIZCHIP_CREG_BLOCK 0x00 //< Common register block +#define WIZCHIP_SREG_BLOCK(N) (1+4*N) //< Socket N register block +#define WIZCHIP_TXBUF_BLOCK(N) (2+4*N) //< Socket N Tx buffer address block +#define WIZCHIP_RXBUF_BLOCK(N) (3+4*N) //< Socket N Rx buffer address block + +#define WIZCHIP_OFFSET_INC(ADDR, N) (ADDR + (N<<8)) //< Increase offset address + + +/////////////////////////////////////// +// Definition For Legacy Chip Driver // +/////////////////////////////////////// +#define IINCHIP_READ(ADDR) WIZCHIP_READ(ADDR) ///< The defined for legacy chip driver +#define IINCHIP_WRITE(ADDR,VAL) WIZCHIP_WRITE(ADDR,VAL) ///< The defined for legacy chip driver +#define IINCHIP_READ_BUF(ADDR,BUF,LEN) WIZCHIP_READ_BUF(ADDR,BUF,LEN) ///< The defined for legacy chip driver +#define IINCHIP_WRITE_BUF(ADDR,BUF,LEN) WIZCHIP_WRITE(ADDR,BUF,LEN) ///< The defined for legacy chip driver + +////////////////////////////// +//-------------------------- defgroup --------------------------------- +/** + * @defgroup W5500 W5500 + * + * @brief WHIZCHIP register defines and I/O functions of @b W5500. + * + * - @ref WIZCHIP_register : @ref Common_register_group and @ref Socket_register_group + * - @ref WIZCHIP_IO_Functions : @ref Basic_IO_function, @ref Common_register_access_function and @ref Socket_register_access_function + */ + + +/** + * @defgroup WIZCHIP_register WIZCHIP register + * @ingroup W5500 + * + * @brief WHIZCHIP register defines register group of @b W5500. + * + * - @ref Common_register_group : Common register group + * - @ref Socket_register_group : \c SOCKET n register group + */ + + +/** + * @defgroup WIZCHIP_IO_Functions WIZCHIP I/O functions + * @ingroup W5500 + * + * @brief This supports the basic I/O functions for @ref WIZCHIP_register. + * + * - Basic I/O function \n + * WIZCHIP_READ(), WIZCHIP_WRITE(), WIZCHIP_READ_BUF(), WIZCHIP_WRITE_BUF() \n\n + * + * - @ref Common_register_group access functions \n + * -# @b Mode \n + * getMR(), setMR() + * -# @b Interrupt \n + * getIR(), setIR(), getIMR(), setIMR(), getSIR(), setSIR(), getSIMR(), setSIMR(), getINTLEVEL(), setINTLEVEL() + * -# Network Information \n + * getSHAR(), setSHAR(), getGAR(), setGAR(), getSUBR(), setSUBR(), getSIPR(), setSIPR() + * -# @b Retransmission \n + * getRCR(), setRCR(), getRTR(), setRTR() + * -# @b PPPoE \n + * getPTIMER(), setPTIMER(), getPMAGIC(), getPMAGIC(), getPSID(), setPSID(), getPHAR(), setPHAR(), getPMRU(), setPMRU() + * -# ICMP packet \n + * getUIPR(), getUPORTR() + * -# @b etc. \n + * getPHYCFGR(), setPHYCFGR(), getVERSIONR() \n\n + * + * - \ref Socket_register_group access functions \n + * -# SOCKET control \n + * getSn_MR(), setSn_MR(), getSn_CR(), setSn_CR(), getSn_IMR(), setSn_IMR(), getSn_IR(), setSn_IR() + * -# SOCKET information \n + * getSn_SR(), getSn_DHAR(), setSn_DHAR(), getSn_PORT(), setSn_PORT(), getSn_DIPR(), setSn_DIPR(), getSn_DPORT(), setSn_DPORT() + * getSn_MSSR(), setSn_MSSR() + * -# SOCKET communication \n + * getSn_RXBUF_SIZE(), setSn_RXBUF_SIZE(), getSn_TXBUF_SIZE(), setSn_TXBUF_SIZE() \n + * getSn_TX_RD(), getSn_TX_WR(), setSn_TX_WR() \n + * getSn_RX_RD(), setSn_RX_RD(), getSn_RX_WR() \n + * getSn_TX_FSR(), getSn_RX_RSR(), getSn_KPALVTR(), setSn_KPALVTR() + * -# IP header field \n + * getSn_FRAG(), setSn_FRAG(), getSn_TOS(), setSn_TOS() \n + * getSn_TTL(), setSn_TTL() + */ + + + +/** + * @defgroup Common_register_group Common register + * @ingroup WIZCHIP_register + * + * @brief Common register group\n + * It set the basic for the networking\n + * It set the configuration such as interrupt, network information, ICMP, etc. + * @details + * @sa MR : Mode register. + * @sa GAR, SUBR, SHAR, SIPR + * @sa INTLEVEL, IR, IMR, SIR, SIMR : Interrupt. + * @sa _RTR_, _RCR_ : Data retransmission. + * @sa PTIMER, PMAGIC, PHAR, PSID, PMRU : PPPoE. + * @sa UIPR, UPORTR : ICMP message. + * @sa PHYCFGR, VERSIONR : etc. + */ + + + +/** + * @defgroup Socket_register_group Socket register + * @ingroup WIZCHIP_register + * + * @brief Socket register group.\n + * Socket register configures and control SOCKETn which is necessary to data communication. + * @details + * @sa Sn_MR, Sn_CR, Sn_IR, Sn_IMR : SOCKETn Control + * @sa Sn_SR, Sn_PORT, Sn_DHAR, Sn_DIPR, Sn_DPORT : SOCKETn Information + * @sa Sn_MSSR, Sn_TOS, Sn_TTL, Sn_KPALVTR, Sn_FRAG : Internet protocol. + * @sa Sn_RXBUF_SIZE, Sn_TXBUF_SIZE, Sn_TX_FSR, Sn_TX_RD, Sn_TX_WR, Sn_RX_RSR, Sn_RX_RD, Sn_RX_WR : Data communication + */ + + + + /** + * @defgroup Basic_IO_function Basic I/O function + * @ingroup WIZCHIP_IO_Functions + * @brief These are basic input/output functions to read values from register or write values to register. + */ + +/** + * @defgroup Common_register_access_function Common register access functions + * @ingroup WIZCHIP_IO_Functions + * @brief These are functions to access common registers. + */ + +/** + * @defgroup Socket_register_access_function Socket register access functions + * @ingroup WIZCHIP_IO_Functions + * @brief These are functions to access socket registers. + */ + +//------------------------------- defgroup end -------------------------------------------- +//----------------------------- W5500 Common Registers IOMAP ----------------------------- +/** + * @ingroup Common_register_group + * @brief Mode Register address(R/W)\n + * @ref MR is used for S/W reset, ping block mode, PPPoE mode and etc. + * @details Each bit of @ref MR defined as follows. + * + * + * + *
7 6 5 4 3 2 1 0
RST Reserved WOL PB PPPoE Reserved FARP Reserved
+ * - \ref MR_RST : Reset + * - \ref MR_WOL : Wake on LAN + * - \ref MR_PB : Ping block + * - \ref MR_PPPOE : PPPoE mode + * - \ref MR_FARP : Force ARP mode + */ +#define MR (_W5500_IO_BASE_ + (0x0000 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Gateway IP Register address(R/W) + * @details @ref GAR configures the default gateway address. + */ +#define GAR (_W5500_IO_BASE_ + (0x0001 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Subnet mask Register address(R/W) + * @details @ref SUBR configures the subnet mask address. + */ +#define SUBR (_W5500_IO_BASE_ + (0x0005 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Source MAC Register address(R/W) + * @details @ref SHAR configures the source hardware address. + */ +#define SHAR (_W5500_IO_BASE_ + (0x0009 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Source IP Register address(R/W) + * @details @ref SIPR configures the source IP address. + */ +#define SIPR (_W5500_IO_BASE_ + (0x000F << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Set Interrupt low level timer register address(R/W) + * @details @ref INTLEVEL configures the Interrupt Assert Time. + */ +#define INTLEVEL (_W5500_IO_BASE_ + (0x0013 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Interrupt Register(R/W) + * @details @ref IR indicates the interrupt status. Each bit of @ref IR will be still until the bit will be written to by the host. + * If @ref IR is not equal to x00 INTn PIN is asserted to low until it is x00\n\n + * Each bit of @ref IR defined as follows. + * + * + * + *
7 6 5 4 3 2 1 0
CONFLICT UNREACH PPPoE MP Reserved Reserved Reserved Reserved
+ * - \ref IR_CONFLICT : IP conflict + * - \ref IR_UNREACH : Destination unreachable + * - \ref IR_PPPoE : PPPoE connection close + * - \ref IR_MP : Magic packet + */ +#define IR (_W5500_IO_BASE_ + (0x0015 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Interrupt mask register(R/W) + * @details @ref _IMR_ is used to mask interrupts. Each bit of @ref _IMR_ corresponds to each bit of @ref IR. + * When a bit of @ref _IMR_ is and the corresponding bit of @ref IR is an interrupt will be issued. In other words, + * if a bit of @ref _IMR_ is an interrupt will not be issued even if the corresponding bit of @ref IR is \n\n + * Each bit of @ref _IMR_ defined as the following. + * + * + * + *
7 6 5 4 3 2 1 0
IM_IR7 IM_IR6 IM_IR5 IM_IR4 Reserved Reserved Reserved Reserved
+ * - \ref IM_IR7 : IP Conflict Interrupt Mask + * - \ref IM_IR6 : Destination unreachable Interrupt Mask + * - \ref IM_IR5 : PPPoE Close Interrupt Mask + * - \ref IM_IR4 : Magic Packet Interrupt Mask + */ +//M20150401 : Rename SYMBOE ( Re-define error in a compile) +//#define IMR (_W5500_IO_BASE_ + (0x0016 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +#define _IMR_ (_W5500_IO_BASE_ + (0x0016 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Socket Interrupt Register(R/W) + * @details @ref SIR indicates the interrupt status of Socket.\n + * Each bit of @ref SIR be still until @ref Sn_IR is cleared by the host.\n + * If @ref Sn_IR is not equal to x00 the n-th bit of @ref SIR is and INTn PIN is asserted until @ref SIR is x00 */ +#define SIR (_W5500_IO_BASE_ + (0x0017 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Socket Interrupt Mask Register(R/W) + * @details Each bit of @ref SIMR corresponds to each bit of @ref SIR. + * When a bit of @ref SIMR is and the corresponding bit of @ref SIR is Interrupt will be issued. + * In other words, if a bit of @ref SIMR is an interrupt will be not issued even if the corresponding bit of @ref SIR is + */ +#define SIMR (_W5500_IO_BASE_ + (0x0018 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Timeout register address( 1 is 100us )(R/W) + * @details @ref _RTR_ configures the retransmission timeout period. The unit of timeout period is 100us and the default of @ref _RTR_ is x07D0. + * And so the default timeout period is 200ms(100us X 2000). During the time configured by @ref _RTR_, W5500 waits for the peer response + * to the packet that is transmitted by \ref Sn_CR (CONNECT, DISCON, CLOSE, SEND, SEND_MAC, SEND_KEEP command). + * If the peer does not respond within the @ref _RTR_ time, W5500 retransmits the packet or issues timeout. + */ +//M20150401 : Rename SYMBOE ( Re-define error in a compile) +//#define RTR (_W5500_IO_BASE_ + (0x0019 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +#define _RTR_ (_W5500_IO_BASE_ + (0x0019 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Retry count register(R/W) + * @details @ref _RCR_ configures the number of time of retransmission. + * When retransmission occurs as many as ref _RCR_+1 Timeout interrupt is issued (@ref Sn_IR_TIMEOUT = '1'). + */ +//M20150401 : Rename SYMBOE ( Re-define error in a compile) +//#define RCR (_W5500_IO_BASE_ + (0x001B << 8) + (WIZCHIP_CREG_BLOCK << 3)) +#define _RCR_ (_W5500_IO_BASE_ + (0x001B << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PPP LCP Request Timer register in PPPoE mode(R/W) + * @details @ref PTIMER configures the time for sending LCP echo request. The unit of time is 25ms. + */ +#define PTIMER (_W5500_IO_BASE_ + (0x001C << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PPP LCP Magic number register in PPPoE mode(R/W) + * @details @ref PMAGIC configures the 4bytes magic number to be used in LCP negotiation. + */ +#define PMAGIC (_W5500_IO_BASE_ + (0x001D << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PPP Destination MAC Register address(R/W) + * @details @ref PHAR configures the PPPoE server hardware address that is acquired during PPPoE connection process. + */ +#define PHAR (_W5500_IO_BASE_ + (0x001E << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PPP Session Identification Register(R/W) + * @details @ref PSID configures the PPPoE sever session ID acquired during PPPoE connection process. + */ +#define PSID (_W5500_IO_BASE_ + (0x0024 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PPP Maximum Segment Size(MSS) register(R/W) + * @details @ref PMRU configures the maximum receive unit of PPPoE. + */ +#define PMRU (_W5500_IO_BASE_ + (0x0026 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Unreachable IP register address in UDP mode(R) + * @details W5500 receives an ICMP packet(Destination port unreachable) when data is sent to a port number + * which socket is not open and @ref IR_UNREACH bit of @ref IR becomes and @ref UIPR & @ref UPORTR indicates + * the destination IP address & port number respectively. + */ +#define UIPR (_W5500_IO_BASE_ + (0x0028 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief Unreachable Port register address in UDP mode(R) + * @details W5500 receives an ICMP packet(Destination port unreachable) when data is sent to a port number + * which socket is not open and @ref IR_UNREACH bit of @ref IR becomes and @ref UIPR & @ref UPORTR + * indicates the destination IP address & port number respectively. + */ +#define UPORTR (_W5500_IO_BASE_ + (0x002C << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief PHY Status Register(R/W) + * @details @ref PHYCFGR configures PHY operation mode and resets PHY. In addition, @ref PHYCFGR indicates the status of PHY such as duplex, Speed, Link. + */ +#define PHYCFGR (_W5500_IO_BASE_ + (0x002E << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +// Reserved (_W5500_IO_BASE_ + (0x002F << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0030 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0031 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0032 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0033 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0034 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0035 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0036 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0037 << 8) + (WIZCHIP_CREG_BLOCK << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0038 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + +/** + * @ingroup Common_register_group + * @brief chip version register address(R) + * @details @ref VERSIONR always indicates the W5500 version as @b 0x04. + */ +#define VERSIONR (_W5500_IO_BASE_ + (0x0039 << 8) + (WIZCHIP_CREG_BLOCK << 3)) + + +//----------------------------- W5500 Socket Registers IOMAP ----------------------------- +/** + * @ingroup Socket_register_group + * @brief socket Mode register(R/W) + * @details @ref Sn_MR configures the option or protocol type of Socket n.\n\n + * Each bit of @ref Sn_MR defined as the following. + * + * + * + *
7 6 5 4 3 2 1 0
MULTI/MFEN BCASTB ND/MC/MMB UCASTB/MIP6B Protocol[3] Protocol[2] Protocol[1] Protocol[0]
+ * - @ref Sn_MR_MULTI : Support UDP Multicasting + * - @ref Sn_MR_BCASTB : Broadcast block in UDP Multicasting + * - @ref Sn_MR_ND : No Delayed Ack(TCP) flag + * - @ref Sn_MR_MC : IGMP version used in UDP mulitcasting + * - @ref Sn_MR_MMB : Multicast Blocking in @ref Sn_MR_MACRAW mode + * - @ref Sn_MR_UCASTB : Unicast Block in UDP Multicating + * - @ref Sn_MR_MIP6B : IPv6 packet Blocking in @ref Sn_MR_MACRAW mode + * - Protocol + * + * + * + * + * + * + *
Protocol[3] Protocol[2] Protocol[1] Protocol[0] @b Meaning
0 0 0 0 Closed
0 0 0 1 TCP
0 0 1 0 UDP
0 1 0 0 MACRAW
+ * - @ref Sn_MR_MACRAW : MAC LAYER RAW SOCK \n + * - @ref Sn_MR_UDP : UDP + * - @ref Sn_MR_TCP : TCP + * - @ref Sn_MR_CLOSE : Unused socket + * @note MACRAW mode should be only used in Socket 0. + */ +#define Sn_MR(N) (_W5500_IO_BASE_ + (0x0000 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Socket command register(R/W) + * @details This is used to set the command for Socket n such as OPEN, CLOSE, CONNECT, LISTEN, SEND, and RECEIVE.\n + * After W5500 accepts the command, the @ref Sn_CR register is automatically cleared to 0x00. + * Even though @ref Sn_CR is cleared to 0x00, the command is still being processed.\n + * To check whether the command is completed or not, please check the @ref Sn_IR or @ref Sn_SR. + * - @ref Sn_CR_OPEN : Initialize or open socket. + * - @ref Sn_CR_LISTEN : Wait connection request in TCP mode(Server mode) + * - @ref Sn_CR_CONNECT : Send connection request in TCP mode(Client mode) + * - @ref Sn_CR_DISCON : Send closing request in TCP mode. + * - @ref Sn_CR_CLOSE : Close socket. + * - @ref Sn_CR_SEND : Update TX buffer pointer and send data. + * - @ref Sn_CR_SEND_MAC : Send data with MAC address, so without ARP process. + * - @ref Sn_CR_SEND_KEEP : Send keep alive message. + * - @ref Sn_CR_RECV : Update RX buffer pointer and receive data. + */ +#define Sn_CR(N) (_W5500_IO_BASE_ + (0x0001 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Socket interrupt register(R) + * @details @ref Sn_IR indicates the status of Socket Interrupt such as establishment, termination, receiving data, timeout).\n + * When an interrupt occurs and the corresponding bit of @ref Sn_IMR is the corresponding bit of @ref Sn_IR becomes \n + * In order to clear the @ref Sn_IR bit, the host should write the bit to \n + * + * + * + *
7 6 5 4 3 2 1 0
Reserved Reserved Reserved SEND_OK TIMEOUT RECV DISCON CON
+ * - \ref Sn_IR_SENDOK : SEND_OK Interrupt + * - \ref Sn_IR_TIMEOUT : TIMEOUT Interrupt + * - \ref Sn_IR_RECV : RECV Interrupt + * - \ref Sn_IR_DISCON : DISCON Interrupt + * - \ref Sn_IR_CON : CON Interrupt + */ +#define Sn_IR(N) (_W5500_IO_BASE_ + (0x0002 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Socket status register(R) + * @details @ref Sn_SR indicates the status of Socket n.\n + * The status of Socket n is changed by @ref Sn_CR or some special control packet as SYN, FIN packet in TCP. + * @par Normal status + * - @ref SOCK_CLOSED : Closed + * - @ref SOCK_INIT : Initiate state + * - @ref SOCK_LISTEN : Listen state + * - @ref SOCK_ESTABLISHED : Success to connect + * - @ref SOCK_CLOSE_WAIT : Closing state + * - @ref SOCK_UDP : UDP socket + * - @ref SOCK_MACRAW : MAC raw mode socket + *@par Temporary status during changing the status of Socket n. + * - @ref SOCK_SYNSENT : This indicates Socket n sent the connect-request packet (SYN packet) to a peer. + * - @ref SOCK_SYNRECV : It indicates Socket n successfully received the connect-request packet (SYN packet) from a peer. + * - @ref SOCK_FIN_WAIT : Connection state + * - @ref SOCK_CLOSING : Closing state + * - @ref SOCK_TIME_WAIT : Closing state + * - @ref SOCK_LAST_ACK : Closing state + */ +#define Sn_SR(N) (_W5500_IO_BASE_ + (0x0003 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief source port register(R/W) + * @details @ref Sn_PORT configures the source port number of Socket n. + * It is valid when Socket n is used in TCP/UDP mode. It should be set before OPEN command is ordered. + */ +#define Sn_PORT(N) (_W5500_IO_BASE_ + (0x0004 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Peer MAC register address(R/W) + * @details @ref Sn_DHAR configures the destination hardware address of Socket n when using SEND_MAC command in UDP mode or + * it indicates that it is acquired in ARP-process by CONNECT/SEND command. + */ +#define Sn_DHAR(N) (_W5500_IO_BASE_ + (0x0006 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Peer IP register address(R/W) + * @details @ref Sn_DIPR configures or indicates the destination IP address of Socket n. It is valid when Socket n is used in TCP/UDP mode. + * In TCP client mode, it configures an IP address of TCP serverbefore CONNECT command. + * In TCP server mode, it indicates an IP address of TCP clientafter successfully establishing connection. + * In UDP mode, it configures an IP address of peer to be received the UDP packet by SEND or SEND_MAC command. + */ +#define Sn_DIPR(N) (_W5500_IO_BASE_ + (0x000C << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Peer port register address(R/W) + * @details @ref Sn_DPORT configures or indicates the destination port number of Socket n. It is valid when Socket n is used in TCP/UDP mode. + * In TCP clientmode, it configures the listen port number of TCP serverbefore CONNECT command. + * In TCP Servermode, it indicates the port number of TCP client after successfully establishing connection. + * In UDP mode, it configures the port number of peer to be transmitted the UDP packet by SEND/SEND_MAC command. + */ +#define Sn_DPORT(N) (_W5500_IO_BASE_ + (0x0010 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Maximum Segment Size(Sn_MSSR0) register address(R/W) + * @details @ref Sn_MSSR configures or indicates the MTU(Maximum Transfer Unit) of Socket n. + */ +#define Sn_MSSR(N) (_W5500_IO_BASE_ + (0x0012 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +// Reserved (_W5500_IO_BASE_ + (0x0014 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief IP Type of Service(TOS) Register(R/W) + * @details @ref Sn_TOS configures the TOS(Type Of Service field in IP Header) of Socket n. + * It is set before OPEN command. + */ +#define Sn_TOS(N) (_W5500_IO_BASE_ + (0x0015 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +/** + * @ingroup Socket_register_group + * @brief IP Time to live(TTL) Register(R/W) + * @details @ref Sn_TTL configures the TTL(Time To Live field in IP header) of Socket n. + * It is set before OPEN command. + */ +#define Sn_TTL(N) (_W5500_IO_BASE_ + (0x0016 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0017 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0018 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x0019 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x001A << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x001B << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x001C << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) +// Reserved (_W5500_IO_BASE_ + (0x001D << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Receive memory size register(R/W) + * @details @ref Sn_RXBUF_SIZE configures the RX buffer block size of Socket n. + * Socket n RX Buffer Block size can be configured with 1,2,4,8, and 16 Kbytes. + * If a different size is configured, the data cannot be normally received from a peer. + * Although Socket n RX Buffer Block size is initially configured to 2Kbytes, + * user can re-configure its size using @ref Sn_RXBUF_SIZE. The total sum of @ref Sn_RXBUF_SIZE can not be exceed 16Kbytes. + * When exceeded, the data reception error is occurred. + */ +#define Sn_RXBUF_SIZE(N) (_W5500_IO_BASE_ + (0x001E << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Transmit memory size register(R/W) + * @details @ref Sn_TXBUF_SIZE configures the TX buffer block size of Socket n. Socket n TX Buffer Block size can be configured with 1,2,4,8, and 16 Kbytes. + * If a different size is configured, the data can�t be normally transmitted to a peer. + * Although Socket n TX Buffer Block size is initially configured to 2Kbytes, + * user can be re-configure its size using @ref Sn_TXBUF_SIZE. The total sum of @ref Sn_TXBUF_SIZE can not be exceed 16Kbytes. + * When exceeded, the data transmission error is occurred. + */ +#define Sn_TXBUF_SIZE(N) (_W5500_IO_BASE_ + (0x001F << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Transmit free memory size register(R) + * @details @ref Sn_TX_FSR indicates the free size of Socket n TX Buffer Block. It is initialized to the configured size by @ref Sn_TXBUF_SIZE. + * Data bigger than @ref Sn_TX_FSR should not be saved in the Socket n TX Buffer because the bigger data overwrites the previous saved data not yet sent. + * Therefore, check before saving the data to the Socket n TX Buffer, and if data is equal or smaller than its checked size, + * transmit the data with SEND/SEND_MAC command after saving the data in Socket n TX buffer. But, if data is bigger than its checked size, + * transmit the data after dividing into the checked size and saving in the Socket n TX buffer. + */ +#define Sn_TX_FSR(N) (_W5500_IO_BASE_ + (0x0020 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Transmit memory read pointer register address(R) + * @details @ref Sn_TX_RD is initialized by OPEN command. However, if Sn_MR(P[3:0]) is TCP mode(001, it is re-initialized while connecting with TCP. + * After its initialization, it is auto-increased by SEND command. + * SEND command transmits the saved data from the current @ref Sn_TX_RD to the @ref Sn_TX_WR in the Socket n TX Buffer. + * After transmitting the saved data, the SEND command increases the @ref Sn_TX_RD as same as the @ref Sn_TX_WR. + * If its increment value exceeds the maximum value 0xFFFF, (greater than 0x10000 and the carry bit occurs), + * then the carry bit is ignored and will automatically update with the lower 16bits value. + */ +#define Sn_TX_RD(N) (_W5500_IO_BASE_ + (0x0022 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Transmit memory write pointer register address(R/W) + * @details @ref Sn_TX_WR is initialized by OPEN command. However, if Sn_MR(P[3:0]) is TCP mode(001, it is re-initialized while connecting with TCP.\n + * It should be read or be updated like as follows.\n + * 1. Read the starting address for saving the transmitting data.\n + * 2. Save the transmitting data from the starting address of Socket n TX buffer.\n + * 3. After saving the transmitting data, update @ref Sn_TX_WR to the increased value as many as transmitting data size. + * If the increment value exceeds the maximum value 0xFFFF(greater than 0x10000 and the carry bit occurs), + * then the carry bit is ignored and will automatically update with the lower 16bits value.\n + * 4. Transmit the saved data in Socket n TX Buffer by using SEND/SEND command + */ +#define Sn_TX_WR(N) (_W5500_IO_BASE_ + (0x0024 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Received data size register(R) + * @details @ref Sn_RX_RSR indicates the data size received and saved in Socket n RX Buffer. + * @ref Sn_RX_RSR does not exceed the @ref Sn_RXBUF_SIZE and is calculated as the difference between + * �Socket n RX Write Pointer (@ref Sn_RX_WR)and �Socket n RX Read Pointer (@ref Sn_RX_RD) + */ +#define Sn_RX_RSR(N) (_W5500_IO_BASE_ + (0x0026 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Read point of Receive memory(R/W) + * @details @ref Sn_RX_RD is initialized by OPEN command. Make sure to be read or updated as follows.\n + * 1. Read the starting save address of the received data.\n + * 2. Read data from the starting address of Socket n RX Buffer.\n + * 3. After reading the received data, Update @ref Sn_RX_RD to the increased value as many as the reading size. + * If the increment value exceeds the maximum value 0xFFFF, that is, is greater than 0x10000 and the carry bit occurs, + * update with the lower 16bits value ignored the carry bit.\n + * 4. Order RECV command is for notifying the updated @ref Sn_RX_RD to W5500. + */ +#define Sn_RX_RD(N) (_W5500_IO_BASE_ + (0x0028 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Write point of Receive memory(R) + * @details @ref Sn_RX_WR is initialized by OPEN command and it is auto-increased by the data reception. + * If the increased value exceeds the maximum value 0xFFFF, (greater than 0x10000 and the carry bit occurs), + * then the carry bit is ignored and will automatically update with the lower 16bits value. + */ +#define Sn_RX_WR(N) (_W5500_IO_BASE_ + (0x002A << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief socket interrupt mask register(R) + * @details @ref Sn_IMR masks the interrupt of Socket n. + * Each bit corresponds to each bit of @ref Sn_IR. When a Socket n Interrupt is occurred and the corresponding bit of @ref Sn_IMR is + * the corresponding bit of @ref Sn_IR becomes When both the corresponding bit of @ref Sn_IMR and @ref Sn_IR are and the n-th bit of @ref IR is + * Host is interrupted by asserted INTn PIN to low. + */ +#define Sn_IMR(N) (_W5500_IO_BASE_ + (0x002C << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Fragment field value in IP header register(R/W) + * @details @ref Sn_FRAG configures the FRAG(Fragment field in IP header). + */ +#define Sn_FRAG(N) (_W5500_IO_BASE_ + (0x002D << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +/** + * @ingroup Socket_register_group + * @brief Keep Alive Timer register(R/W) + * @details @ref Sn_KPALVTR configures the transmitting timer of �KEEP ALIVE(KA)packet of SOCKETn. It is valid only in TCP mode, + * and ignored in other modes. The time unit is 5s. + * KA packet is transmittable after @ref Sn_SR is changed to SOCK_ESTABLISHED and after the data is transmitted or received to/from a peer at least once. + * In case of '@ref Sn_KPALVTR > 0', W5500 automatically transmits KA packet after time-period for checking the TCP connection (Auto-keepalive-process). + * In case of '@ref Sn_KPALVTR = 0', Auto-keep-alive-process will not operate, + * and KA packet can be transmitted by SEND_KEEP command by the host (Manual-keep-alive-process). + * Manual-keep-alive-process is ignored in case of '@ref Sn_KPALVTR > 0'. + */ +#define Sn_KPALVTR(N) (_W5500_IO_BASE_ + (0x002F << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + +//#define Sn_TSR(N) (_W5500_IO_BASE_ + (0x0030 << 8) + (WIZCHIP_SREG_BLOCK(N) << 3)) + + +//----------------------------- W5500 Register values ----------------------------- + +/* MODE register values */ +/** + * @brief Reset + * @details If this bit is All internal registers will be initialized. It will be automatically cleared as after S/W reset. + */ +#define MR_RST 0x80 + +/** + * @brief Wake on LAN + * @details 0 : Disable WOL mode\n + * 1 : Enable WOL mode\n + * If WOL mode is enabled and the received magic packet over UDP has been normally processed, the Interrupt PIN (INTn) asserts to low. + * When using WOL mode, the UDP Socket should be opened with any source port number. (Refer to Socket n Mode Register (@ref Sn_MR) for opening Socket.) + * @note The magic packet over UDP supported by W5500 consists of 6 bytes synchronization stream (xFFFFFFFFFFFF and + * 16 times Target MAC address stream in UDP payload. The options such like password are ignored. You can use any UDP source port number for WOL mode. + */ +#define MR_WOL 0x20 + +/** + * @brief Ping block + * @details 0 : Disable Ping block\n + * 1 : Enable Ping block\n + * If the bit is it blocks the response to a ping request. + */ +#define MR_PB 0x10 + +/** + * @brief Enable PPPoE + * @details 0 : DisablePPPoE mode\n + * 1 : EnablePPPoE mode\n + * If you use ADSL, this bit should be + */ +#define MR_PPPOE 0x08 + +/** + * @brief Enable UDP_FORCE_ARP CHECHK + * @details 0 : Disable Force ARP mode\n + * 1 : Enable Force ARP mode\n + * In Force ARP mode, It forces on sending ARP Request whenever data is sent. + */ +#define MR_FARP 0x02 + +/* IR register values */ +/** + * @brief Check IP conflict. + * @details Bit is set as when own source IP address is same with the sender IP address in the received ARP request. + */ +#define IR_CONFLICT 0x80 + +/** + * @brief Get the destination unreachable message in UDP sending. + * @details When receiving the ICMP (Destination port unreachable) packet, this bit is set as + * When this bit is Destination Information such as IP address and Port number may be checked with the corresponding @ref UIPR & @ref UPORTR. + */ +#define IR_UNREACH 0x40 + +/** + * @brief Get the PPPoE close message. + * @details When PPPoE is disconnected during PPPoE mode, this bit is set. + */ +#define IR_PPPoE 0x20 + +/** + * @brief Get the magic packet interrupt. + * @details When WOL mode is enabled and receives the magic packet over UDP, this bit is set. + */ +#define IR_MP 0x10 + + +/* PHYCFGR register value */ +#define PHYCFGR_RST ~(1<<7) //< For PHY reset, must operate AND mask. +#define PHYCFGR_OPMD (1<<6) // Configre PHY with OPMDC value +#define PHYCFGR_OPMDC_ALLA (7<<3) +#define PHYCFGR_OPMDC_PDOWN (6<<3) +#define PHYCFGR_OPMDC_NA (5<<3) +#define PHYCFGR_OPMDC_100FA (4<<3) +#define PHYCFGR_OPMDC_100F (3<<3) +#define PHYCFGR_OPMDC_100H (2<<3) +#define PHYCFGR_OPMDC_10F (1<<3) +#define PHYCFGR_OPMDC_10H (0<<3) +#define PHYCFGR_DPX_FULL (1<<2) +#define PHYCFGR_DPX_HALF (0<<2) +#define PHYCFGR_SPD_100 (1<<1) +#define PHYCFGR_SPD_10 (0<<1) +#define PHYCFGR_LNK_ON (1<<0) +#define PHYCFGR_LNK_OFF (0<<0) + +/* IMR register values */ +/** + * @brief IP Conflict Interrupt Mask. + * @details 0: Disable IP Conflict Interrupt\n + * 1: Enable IP Conflict Interrupt + */ +#define IM_IR7 0x80 + +/** + * @brief Destination unreachable Interrupt Mask. + * @details 0: Disable Destination unreachable Interrupt\n + * 1: Enable Destination unreachable Interrupt + */ +#define IM_IR6 0x40 + +/** + * @brief PPPoE Close Interrupt Mask. + * @details 0: Disable PPPoE Close Interrupt\n + * 1: Enable PPPoE Close Interrupt + */ +#define IM_IR5 0x20 + +/** + * @brief Magic Packet Interrupt Mask. + * @details 0: Disable Magic Packet Interrupt\n + * 1: Enable Magic Packet Interrupt + */ +#define IM_IR4 0x10 + +/* Sn_MR Default values */ +/** + * @brief Support UDP Multicasting + * @details 0 : disable Multicasting\n + * 1 : enable Multicasting\n + * This bit is applied only during UDP mode(P[3:0] = 010.\n + * To use multicasting, @ref Sn_DIPR & @ref Sn_DPORT should be respectively configured with the multicast group IP address & port number + * before Socket n is opened by OPEN command of @ref Sn_CR. + */ +#define Sn_MR_MULTI 0x80 + +/** + * @brief Broadcast block in UDP Multicasting. + * @details 0 : disable Broadcast Blocking\n + * 1 : enable Broadcast Blocking\n + * This bit blocks to receive broadcasting packet during UDP mode(P[3:0] = 010.\m + * In addition, This bit does when MACRAW mode(P[3:0] = 100 + */ +#define Sn_MR_BCASTB 0x40 + +/** + * @brief No Delayed Ack(TCP), Multicast flag + * @details 0 : Disable No Delayed ACK option\n + * 1 : Enable No Delayed ACK option\n + * This bit is applied only during TCP mode (P[3:0] = 001.\n + * When this bit is It sends the ACK packet without delay as soon as a Data packet is received from a peer.\n + * When this bit is It sends the ACK packet after waiting for the timeout time configured by @ref _RTR_. + */ +#define Sn_MR_ND 0x20 + +/** + * @brief Unicast Block in UDP Multicasting + * @details 0 : disable Unicast Blocking\n + * 1 : enable Unicast Blocking\n + * This bit blocks receiving the unicast packet during UDP mode(P[3:0] = 010 and MULTI = + */ +#define Sn_MR_UCASTB 0x10 + +/** + * @brief MAC LAYER RAW SOCK + * @details This configures the protocol mode of Socket n. + * @note MACRAW mode should be only used in Socket 0. + */ +#define Sn_MR_MACRAW 0x04 + +#define Sn_MR_IPRAW 0x03 /**< IP LAYER RAW SOCK */ + +/** + * @brief UDP + * @details This configures the protocol mode of Socket n. + */ +#define Sn_MR_UDP 0x02 + +/** + * @brief TCP + * @details This configures the protocol mode of Socket n. + */ +#define Sn_MR_TCP 0x01 + +/** + * @brief Unused socket + * @details This configures the protocol mode of Socket n. + */ +#define Sn_MR_CLOSE 0x00 + +/* Sn_MR values used with Sn_MR_MACRAW */ +/** + * @brief MAC filter enable in @ref Sn_MR_MACRAW mode + * @details 0 : disable MAC Filtering\n + * 1 : enable MAC Filtering\n + * This bit is applied only during MACRAW mode(P[3:0] = 100.\n + * When set as W5500 can only receive broadcasting packet or packet sent to itself. + * When this bit is W5500 can receive all packets on Ethernet. + * If user wants to implement Hybrid TCP/IP stack, + * it is recommended that this bit is set as for reducing host overhead to process the all received packets. + */ +#define Sn_MR_MFEN Sn_MR_MULTI + +/** + * @brief Multicast Blocking in @ref Sn_MR_MACRAW mode + * @details 0 : using IGMP version 2\n + * 1 : using IGMP version 1\n + * This bit is applied only during UDP mode(P[3:0] = 010 and MULTI = + * It configures the version for IGMP messages (Join/Leave/Report). + */ +#define Sn_MR_MMB Sn_MR_ND + +/** + * @brief IPv6 packet Blocking in @ref Sn_MR_MACRAW mode + * @details 0 : disable IPv6 Blocking\n + * 1 : enable IPv6 Blocking\n + * This bit is applied only during MACRAW mode (P[3:0] = 100. It blocks to receiving the IPv6 packet. + */ +#define Sn_MR_MIP6B Sn_MR_UCASTB + +/* Sn_MR value used with Sn_MR_UDP & Sn_MR_MULTI */ +/** + * @brief IGMP version used in UDP mulitcasting + * @details 0 : disable Multicast Blocking\n + * 1 : enable Multicast Blocking\n + * This bit is applied only when MACRAW mode(P[3:0] = 100. It blocks to receive the packet with multicast MAC address. + */ +#define Sn_MR_MC Sn_MR_ND + +/* Sn_MR alternate values */ +/** + * @brief For Berkeley Socket API + */ +#define SOCK_STREAM Sn_MR_TCP + +/** + * @brief For Berkeley Socket API + */ +#define SOCK_DGRAM Sn_MR_UDP + + +/* Sn_CR values */ +/** + * @brief Initialize or open socket + * @details Socket n is initialized and opened according to the protocol selected in Sn_MR(P3:P0). + * The table below shows the value of @ref Sn_SR corresponding to @ref Sn_MR.\n + * + * + * + * + * + * + *
\b Sn_MR (P[3:0]) \b Sn_SR
Sn_MR_CLOSE (000)
Sn_MR_TCP (001) SOCK_INIT (0x13)
Sn_MR_UDP (010) SOCK_UDP (0x22)
S0_MR_MACRAW (100) SOCK_MACRAW (0x02)
+ */ +#define Sn_CR_OPEN 0x01 + +/** + * @brief Wait connection request in TCP mode(Server mode) + * @details This is valid only in TCP mode (\ref Sn_MR(P3:P0) = \ref Sn_MR_TCP). + * In this mode, Socket n operates as a TCP serverand waits for connection-request (SYN packet) from any TCP client + * The @ref Sn_SR changes the state from \ref SOCK_INIT to \ref SOCKET_LISTEN. + * When a TCP clientconnection request is successfully established, + * the @ref Sn_SR changes from SOCK_LISTEN to SOCK_ESTABLISHED and the @ref Sn_IR(0) becomes + * But when a TCP clientconnection request is failed, @ref Sn_IR(3) becomes and the status of @ref Sn_SR changes to SOCK_CLOSED. + */ +#define Sn_CR_LISTEN 0x02 + +/** + * @brief Send connection request in TCP mode(Client mode) + * @details To connect, a connect-request (SYN packet) is sent to TCP serverconfigured by @ref Sn_DIPR & Sn_DPORT(destination address & port). + * If the connect-request is successful, the @ref Sn_SR is changed to @ref SOCK_ESTABLISHED and the Sn_IR(0) becomes \n\n + * The connect-request fails in the following three cases.\n + * 1. When a @b ARPTO occurs (@ref Sn_IR[3] = ) because destination hardware address is not acquired through the ARP-process.\n + * 2. When a @b SYN/ACK packet is not received and @b TCPTO (Sn_IR(3) = )\n + * 3. When a @b RST packet is received instead of a @b SYN/ACK packet. In these cases, @ref Sn_SR is changed to @ref SOCK_CLOSED. + * @note This is valid only in TCP mode and operates when Socket n acts as TCP client + */ +#define Sn_CR_CONNECT 0x04 + +/** + * @brief Send closing request in TCP mode + * @details Regardless of TCP serveror TCP client the DISCON command processes the disconnect-process (b>Active closeor Passive close.\n + * @par Active close + * it transmits disconnect-request(FIN packet) to the connected peer\n + * @par Passive close + * When FIN packet is received from peer, a FIN packet is replied back to the peer.\n + * @details When the disconnect-process is successful (that is, FIN/ACK packet is received successfully), @ref Sn_SR is changed to @ref SOCK_CLOSED.\n + * Otherwise, TCPTO occurs (\ref Sn_IR(3)='1') and then @ref Sn_SR is changed to @ref SOCK_CLOSED. + * @note Valid only in TCP mode. + */ +#define Sn_CR_DISCON 0x08 + +/** + * @brief Close socket + * @details Sn_SR is changed to @ref SOCK_CLOSED. + */ +#define Sn_CR_CLOSE 0x10 + +/** + * @brief Update TX buffer pointer and send data + * @details SEND transmits all the data in the Socket n TX buffer.\n + * For more details, please refer to Socket n TX Free Size Register (@ref Sn_TX_FSR), Socket n, + * TX Write Pointer Register(@ref Sn_TX_WR), and Socket n TX Read Pointer Register(@ref Sn_TX_RD). + */ +#define Sn_CR_SEND 0x20 + +/** + * @brief Send data with MAC address, so without ARP process + * @details The basic operation is same as SEND.\n + * Normally SEND transmits data after destination hardware address is acquired by the automatic ARP-process(Address Resolution Protocol).\n + * But SEND_MAC transmits data without the automatic ARP-process.\n + * In this case, the destination hardware address is acquired from @ref Sn_DHAR configured by host, instead of APR-process. + * @note Valid only in UDP mode. + */ +#define Sn_CR_SEND_MAC 0x21 + +/** + * @brief Send keep alive message + * @details It checks the connection status by sending 1byte keep-alive packet.\n + * If the peer can not respond to the keep-alive packet during timeout time, the connection is terminated and the timeout interrupt will occur. + * @note Valid only in TCP mode. + */ +#define Sn_CR_SEND_KEEP 0x22 + +/** + * @brief Update RX buffer pointer and receive data + * @details RECV completes the processing of the received data in Socket n RX Buffer by using a RX read pointer register (@ref Sn_RX_RD).\n + * For more details, refer to Socket n RX Received Size Register (@ref Sn_RX_RSR), Socket n RX Write Pointer Register (@ref Sn_RX_WR), + * and Socket n RX Read Pointer Register (@ref Sn_RX_RD). + */ +#define Sn_CR_RECV 0x40 + +/* Sn_IR values */ +/** + * @brief SEND_OK Interrupt + * @details This is issued when SEND command is completed. + */ +#define Sn_IR_SENDOK 0x10 + +/** + * @brief TIMEOUT Interrupt + * @details This is issued when ARPTO or TCPTO occurs. + */ +#define Sn_IR_TIMEOUT 0x08 + +/** + * @brief RECV Interrupt + * @details This is issued whenever data is received from a peer. + */ +#define Sn_IR_RECV 0x04 + +/** + * @brief DISCON Interrupt + * @details This is issued when FIN or FIN/ACK packet is received from a peer. + */ +#define Sn_IR_DISCON 0x02 + +/** + * @brief CON Interrupt + * @details This is issued one time when the connection with peer is successful and then @ref Sn_SR is changed to @ref SOCK_ESTABLISHED. + */ +#define Sn_IR_CON 0x01 + +/* Sn_SR values */ +/** + * @brief Closed + * @details This indicates that Socket n is released.\n + * When DICON, CLOSE command is ordered, or when a timeout occurs, it is changed to @ref SOCK_CLOSED regardless of previous status. + */ +#define SOCK_CLOSED 0x00 + +/** + * @brief Initiate state + * @details This indicates Socket n is opened with TCP mode.\n + * It is changed to @ref SOCK_INIT when @ref Sn_MR(P[3:0]) = 001 and OPEN command is ordered.\n + * After @ref SOCK_INIT, user can use LISTEN /CONNECT command. + */ +#define SOCK_INIT 0x13 + +/** + * @brief Listen state + * @details This indicates Socket n is operating as TCP servermode and waiting for connection-request (SYN packet) from a peer TCP client.\n + * It will change to @ref SOCK_ESTALBLISHED when the connection-request is successfully accepted.\n + * Otherwise it will change to @ref SOCK_CLOSED after TCPTO @ref Sn_IR(TIMEOUT) = '1') is occurred. + */ +#define SOCK_LISTEN 0x14 + +/** + * @brief Connection state + * @details This indicates Socket n sent the connect-request packet (SYN packet) to a peer.\n + * It is temporarily shown when @ref Sn_SR is changed from @ref SOCK_INIT to @ref SOCK_ESTABLISHED by CONNECT command.\n + * If connect-accept(SYN/ACK packet) is received from the peer at SOCK_SYNSENT, it changes to @ref SOCK_ESTABLISHED.\n + * Otherwise, it changes to @ref SOCK_CLOSED after TCPTO (@ref Sn_IR[TIMEOUT] = '1') is occurred. + */ +#define SOCK_SYNSENT 0x15 + +/** + * @brief Connection state + * @details It indicates Socket n successfully received the connect-request packet (SYN packet) from a peer.\n + * If socket n sends the response (SYN/ACK packet) to the peer successfully, it changes to @ref SOCK_ESTABLISHED. \n + * If not, it changes to @ref SOCK_CLOSED after timeout (@ref Sn_IR[TIMEOUT] = '1') is occurred. + */ +#define SOCK_SYNRECV 0x16 + +/** + * @brief Success to connect + * @details This indicates the status of the connection of Socket n.\n + * It changes to @ref SOCK_ESTABLISHED when the TCP SERVERprocessed the SYN packet from the TCP CLIENTduring @ref SOCK_LISTEN, or + * when the CONNECT command is successful.\n + * During @ref SOCK_ESTABLISHED, DATA packet can be transferred using SEND or RECV command. + */ +#define SOCK_ESTABLISHED 0x17 + +/** + * @brief Closing state + * @details These indicate Socket n is closing.\n + * These are shown in disconnect-process such as active-close and passive-close.\n + * When Disconnect-process is successfully completed, or when timeout occurs, these change to @ref SOCK_CLOSED. + */ +#define SOCK_FIN_WAIT 0x18 + +/** + * @brief Closing state + * @details These indicate Socket n is closing.\n + * These are shown in disconnect-process such as active-close and passive-close.\n + * When Disconnect-process is successfully completed, or when timeout occurs, these change to @ref SOCK_CLOSED. + */ +#define SOCK_CLOSING 0x1A + +/** + * @brief Closing state + * @details These indicate Socket n is closing.\n + * These are shown in disconnect-process such as active-close and passive-close.\n + * When Disconnect-process is successfully completed, or when timeout occurs, these change to @ref SOCK_CLOSED. + */ +#define SOCK_TIME_WAIT 0x1B + +/** + * @brief Closing state + * @details This indicates Socket n received the disconnect-request (FIN packet) from the connected peer.\n + * This is half-closing status, and data can be transferred.\n + * For full-closing, DISCON command is used. But For just-closing, CLOSE command is used. + */ +#define SOCK_CLOSE_WAIT 0x1C + +/** + * @brief Closing state + * @details This indicates Socket n is waiting for the response (FIN/ACK packet) to the disconnect-request (FIN packet) by passive-close.\n + * It changes to @ref SOCK_CLOSED when Socket n received the response successfully, or when timeout(@ref Sn_IR[TIMEOUT] = '1') is occurred. + */ +#define SOCK_LAST_ACK 0x1D + +/** + * @brief UDP socket + * @details This indicates Socket n is opened in UDP mode(@ref Sn_MR(P[3:0]) = '010').\n + * It changes to SOCK_UDP when @ref Sn_MR(P[3:0]) = '010' and @ref Sn_CR_OPEN command is ordered.\n + * Unlike TCP mode, data can be transfered without the connection-process. + */ +#define SOCK_UDP 0x22 + +#define SOCK_IPRAW 0x32 /**< IP raw mode socket */ + +/** + * @brief MAC raw mode socket + * @details This indicates Socket 0 is opened in MACRAW mode (S0_MR(P[3:0]) = 100and is valid only in Socket 0.\n + * It changes to SOCK_MACRAW when S0_MR(P[3:0] = 100and OPEN command is ordered.\n + * Like UDP mode socket, MACRAW mode Socket 0 can transfer a MAC packet (Ethernet frame) without the connection-process. + */ +#define SOCK_MACRAW 0x42 + +//#define SOCK_PPPOE 0x5F + +/* IP PROTOCOL */ +#define IPPROTO_IP 0 //< Dummy for IP +#define IPPROTO_ICMP 1 //< Control message protocol +#define IPPROTO_IGMP 2 //< Internet group management protocol +#define IPPROTO_GGP 3 //< Gateway^2 (deprecated) +#define IPPROTO_TCP 6 //< TCP +#define IPPROTO_PUP 12 //< PUP +#define IPPROTO_UDP 17 //< UDP +#define IPPROTO_IDP 22 //< XNS idp +#define IPPROTO_ND 77 //< UNOFFICIAL net disk protocol +#define IPPROTO_RAW 255 //< Raw IP packet + + +/** + * @brief Enter a critical section + * + * @details It is provided to protect your shared code which are executed without distribution. \n \n + * + * In non-OS environment, It can be just implemented by disabling whole interrupt.\n + * In OS environment, You can replace it to critical section api supported by OS. + * + * \sa WIZCHIP_READ(), WIZCHIP_WRITE(), WIZCHIP_READ_BUF(), WIZCHIP_WRITE_BUF() + * \sa WIZCHIP_CRITICAL_EXIT() + */ +#define WIZCHIP_CRITICAL_ENTER() WIZCHIP.CRIS._enter() + +#ifdef _exit +#undef _exit +#endif + +/** + * @brief Exit a critical section + * + * @details It is provided to protect your shared code which are executed without distribution. \n\n + * + * In non-OS environment, It can be just implemented by disabling whole interrupt. \n + * In OS environment, You can replace it to critical section api supported by OS. + * + * @sa WIZCHIP_READ(), WIZCHIP_WRITE(), WIZCHIP_READ_BUF(), WIZCHIP_WRITE_BUF() + * @sa WIZCHIP_CRITICAL_ENTER() + */ +#define WIZCHIP_CRITICAL_EXIT() WIZCHIP.CRIS._exit() + + +//////////////////////// +// Basic I/O Function // +//////////////////////// + +/** + * @ingroup Basic_IO_function + * @brief It reads 1 byte value from a register. + * @param AddrSel Register address + * @return The value of register + */ +uint8_t WIZCHIP_READ (uint32_t AddrSel); + +/** + * @ingroup Basic_IO_function + * @brief It writes 1 byte value to a register. + * @param AddrSel Register address + * @param wb Write data + * @return void + */ +void WIZCHIP_WRITE(uint32_t AddrSel, uint8_t wb ); + +/** + * @ingroup Basic_IO_function + * @brief It reads sequence data from registers. + * @param AddrSel Register address + * @param pBuf Pointer buffer to read data + * @param len Data length + */ +void WIZCHIP_READ_BUF (uint32_t AddrSel, uint8_t* pBuf, uint16_t len); + +/** + * @ingroup Basic_IO_function + * @brief It writes sequence data to registers. + * @param AddrSel Register address + * @param pBuf Pointer buffer to write data + * @param len Data length + */ +void WIZCHIP_WRITE_BUF(uint32_t AddrSel, uint8_t* pBuf, uint16_t len); + +///////////////////////////////// +// Common Register I/O function // +///////////////////////////////// +/** + * @ingroup Common_register_access_function + * @brief Set Mode Register + * @param (uint8_t)mr The value to be set. + * @sa getMR() + */ +#define setMR(mr) \ + WIZCHIP_WRITE(MR,mr) + + +/** + * @ingroup Common_register_access_function + * @brief Get Mode Register + * @return uint8_t. The value of Mode register. + * @sa setMR() + */ +#define getMR() \ + WIZCHIP_READ(MR) + +/** + * @ingroup Common_register_access_function + * @brief Set gateway IP address + * @param (uint8_t*)gar Pointer variable to set gateway IP address. It should be allocated 4 bytes. + * @sa getGAR() + */ +#define setGAR(gar) \ + WIZCHIP_WRITE_BUF(GAR,gar,4) + +/** + * @ingroup Common_register_access_function + * @brief Get gateway IP address + * @param (uint8_t*)gar Pointer variable to get gateway IP address. It should be allocated 4 bytes. + * @sa setGAR() + */ +#define getGAR(gar) \ + WIZCHIP_READ_BUF(GAR,gar,4) + +/** + * @ingroup Common_register_access_function + * @brief Set subnet mask address + * @param (uint8_t*)subr Pointer variable to set subnet mask address. It should be allocated 4 bytes. + * @sa getSUBR() + */ +#define setSUBR(subr) \ + WIZCHIP_WRITE_BUF(SUBR, subr,4) + + +/** + * @ingroup Common_register_access_function + * @brief Get subnet mask address + * @param (uint8_t*)subr Pointer variable to get subnet mask address. It should be allocated 4 bytes. + * @sa setSUBR() + */ +#define getSUBR(subr) \ + WIZCHIP_READ_BUF(SUBR, subr, 4) + +/** + * @ingroup Common_register_access_function + * @brief Set local MAC address + * @param (uint8_t*)shar Pointer variable to set local MAC address. It should be allocated 6 bytes. + * @sa getSHAR() + */ +#define setSHAR(shar) \ + WIZCHIP_WRITE_BUF(SHAR, shar, 6) + +/** + * @ingroup Common_register_access_function + * @brief Get local MAC address + * @param (uint8_t*)shar Pointer variable to get local MAC address. It should be allocated 6 bytes. + * @sa setSHAR() + */ +#define getSHAR(shar) \ + WIZCHIP_READ_BUF(SHAR, shar, 6) + +/** + * @ingroup Common_register_access_function + * @brief Set local IP address + * @param (uint8_t*)sipr Pointer variable to set local IP address. It should be allocated 4 bytes. + * @sa getSIPR() + */ +#define setSIPR(sipr) \ + WIZCHIP_WRITE_BUF(SIPR, sipr, 4) + +/** + * @ingroup Common_register_access_function + * @brief Get local IP address + * @param (uint8_t*)sipr Pointer variable to get local IP address. It should be allocated 4 bytes. + * @sa setSIPR() + */ +#define getSIPR(sipr) \ + WIZCHIP_READ_BUF(SIPR, sipr, 4) + +/** + * @ingroup Common_register_access_function + * @brief Set INTLEVEL register + * @param (uint16_t)intlevel Value to set @ref INTLEVEL register. + * @sa getINTLEVEL() + */ +#define setINTLEVEL(intlevel) {\ + WIZCHIP_WRITE(INTLEVEL, (uint8_t)(intlevel >> 8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(INTLEVEL,1), (uint8_t) intlevel); \ + } + + +/** + * @ingroup Common_register_access_function + * @brief Get INTLEVEL register + * @return uint16_t. Value of @ref INTLEVEL register. + * @sa setINTLEVEL() + */ +//M20150401 : Type explict declaration +/* +#define getINTLEVEL() \ + ((WIZCHIP_READ(INTLEVEL) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(INTLEVEL,1))) +*/ +#define getINTLEVEL() \ + (((uint16_t)WIZCHIP_READ(INTLEVEL) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(INTLEVEL,1))) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref IR register + * @param (uint8_t)ir Value to set @ref IR register. + * @sa getIR() + */ +#define setIR(ir) \ + WIZCHIP_WRITE(IR, (ir & 0xF0)) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref IR register + * @return uint8_t. Value of @ref IR register. + * @sa setIR() + */ +#define getIR() \ + (WIZCHIP_READ(IR) & 0xF0) +/** + * @ingroup Common_register_access_function + * @brief Set @ref _IMR_ register + * @param (uint8_t)imr Value to set @ref _IMR_ register. + * @sa getIMR() + */ +#define setIMR(imr) \ + WIZCHIP_WRITE(_IMR_, imr) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref _IMR_ register + * @return uint8_t. Value of @ref _IMR_ register. + * @sa setIMR() + */ +#define getIMR() \ + WIZCHIP_READ(_IMR_) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref SIR register + * @param (uint8_t)sir Value to set @ref SIR register. + * @sa getSIR() + */ +#define setSIR(sir) \ + WIZCHIP_WRITE(SIR, sir) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref SIR register + * @return uint8_t. Value of @ref SIR register. + * @sa setSIR() + */ +#define getSIR() \ + WIZCHIP_READ(SIR) +/** + * @ingroup Common_register_access_function + * @brief Set @ref SIMR register + * @param (uint8_t)simr Value to set @ref SIMR register. + * @sa getSIMR() + */ +#define setSIMR(simr) \ + WIZCHIP_WRITE(SIMR, simr) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref SIMR register + * @return uint8_t. Value of @ref SIMR register. + * @sa setSIMR() + */ +#define getSIMR() \ + WIZCHIP_READ(SIMR) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref _RTR_ register + * @param (uint16_t)rtr Value to set @ref _RTR_ register. + * @sa getRTR() + */ +#define setRTR(rtr) {\ + WIZCHIP_WRITE(_RTR_, (uint8_t)(rtr >> 8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(_RTR_,1), (uint8_t) rtr); \ + } + +/** + * @ingroup Common_register_access_function + * @brief Get @ref _RTR_ register + * @return uint16_t. Value of @ref _RTR_ register. + * @sa setRTR() + */ +//M20150401 : Type explict declaration +/* +#define getRTR() \ + ((WIZCHIP_READ(_RTR_) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(_RTR_,1))) +*/ +#define getRTR() \ + (((uint16_t)WIZCHIP_READ(_RTR_) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(_RTR_,1))) + + +/** + * @ingroup Common_register_access_function + * @brief Set @ref _RCR_ register + * @param (uint8_t)rcr Value to set @ref _RCR_ register. + * @sa getRCR() + */ +#define setRCR(rcr) \ + WIZCHIP_WRITE(_RCR_, rcr) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref _RCR_ register + * @return uint8_t. Value of @ref _RCR_ register. + * @sa setRCR() + */ +#define getRCR() \ + WIZCHIP_READ(_RCR_) + +//================================================== test done =========================================================== + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PTIMER register + * @param (uint8_t)ptimer Value to set @ref PTIMER register. + * @sa getPTIMER() + */ +#define setPTIMER(ptimer) \ + WIZCHIP_WRITE(PTIMER, ptimer) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PTIMER register + * @return uint8_t. Value of @ref PTIMER register. + * @sa setPTIMER() + */ +#define getPTIMER() \ + WIZCHIP_READ(PTIMER) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PMAGIC register + * @param (uint8_t)pmagic Value to set @ref PMAGIC register. + * @sa getPMAGIC() + */ +#define setPMAGIC(pmagic) \ + WIZCHIP_WRITE(PMAGIC, pmagic) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PMAGIC register + * @return uint8_t. Value of @ref PMAGIC register. + * @sa setPMAGIC() + */ +#define getPMAGIC() \ + WIZCHIP_READ(PMAGIC) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PHAR address + * @param (uint8_t*)phar Pointer variable to set PPP destination MAC register address. It should be allocated 6 bytes. + * @sa getPHAR() + */ +#define setPHAR(phar) \ + WIZCHIP_WRITE_BUF(PHAR, phar, 6) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PHAR address + * @param (uint8_t*)phar Pointer variable to PPP destination MAC register address. It should be allocated 6 bytes. + * @sa setPHAR() + */ +#define getPHAR(phar) \ + WIZCHIP_READ_BUF(PHAR, phar, 6) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PSID register + * @param (uint16_t)psid Value to set @ref PSID register. + * @sa getPSID() + */ +#define setPSID(psid) {\ + WIZCHIP_WRITE(PSID, (uint8_t)(psid >> 8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(PSID,1), (uint8_t) psid); \ + } + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PSID register + * @return uint16_t. Value of @ref PSID register. + * @sa setPSID() + */ +//uint16_t getPSID(void); +//M20150401 : Type explict declaration +/* +#define getPSID() \ + ((WIZCHIP_READ(PSID) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(PSID,1))) +*/ +#define getPSID() \ + (((uint16_t)WIZCHIP_READ(PSID) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(PSID,1))) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PMRU register + * @param (uint16_t)pmru Value to set @ref PMRU register. + * @sa getPMRU() + */ +#define setPMRU(pmru) { \ + WIZCHIP_WRITE(PMRU, (uint8_t)(pmru>>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(PMRU,1), (uint8_t) pmru); \ + } + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PMRU register + * @return uint16_t. Value of @ref PMRU register. + * @sa setPMRU() + */ +//M20150401 : Type explict declaration +/* +#define getPMRU() \ + ((WIZCHIP_READ(PMRU) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(PMRU,1))) +*/ +#define getPMRU() \ + (((uint16_t)WIZCHIP_READ(PMRU) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(PMRU,1))) + +/** + * @ingroup Common_register_access_function + * @brief Get unreachable IP address + * @param (uint8_t*)uipr Pointer variable to get unreachable IP address. It should be allocated 4 bytes. + */ +//M20150401 : Size Error of UIPR (6 -> 4) +/* +#define getUIPR(uipr) \ + WIZCHIP_READ_BUF(UIPR,uipr,6) +*/ +#define getUIPR(uipr) \ + WIZCHIP_READ_BUF(UIPR,uipr,4) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref UPORTR register + * @return uint16_t. Value of @ref UPORTR register. + */ +//M20150401 : Type explict declaration +/* +#define getUPORTR() \ + ((WIZCHIP_READ(UPORTR) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(UPORTR,1))) +*/ +#define getUPORTR() \ + (((uint16_t)WIZCHIP_READ(UPORTR) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(UPORTR,1))) + +/** + * @ingroup Common_register_access_function + * @brief Set @ref PHYCFGR register + * @param (uint8_t)phycfgr Value to set @ref PHYCFGR register. + * @sa getPHYCFGR() + */ +#define setPHYCFGR(phycfgr) \ + WIZCHIP_WRITE(PHYCFGR, phycfgr) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref PHYCFGR register + * @return uint8_t. Value of @ref PHYCFGR register. + * @sa setPHYCFGR() + */ +#define getPHYCFGR() \ + WIZCHIP_READ(PHYCFGR) + +/** + * @ingroup Common_register_access_function + * @brief Get @ref VERSIONR register + * @return uint8_t. Value of @ref VERSIONR register. + */ +#define getVERSIONR() \ + WIZCHIP_READ(VERSIONR) + +///////////////////////////////////// + +/////////////////////////////////// +// Socket N register I/O function // +/////////////////////////////////// +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_MR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)mr Value to set @ref Sn_MR + * @sa getSn_MR() + */ +#define setSn_MR(sn, mr) \ + WIZCHIP_WRITE(Sn_MR(sn),mr) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_MR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_MR. + * @sa setSn_MR() + */ +#define getSn_MR(sn) \ + WIZCHIP_READ(Sn_MR(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_CR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)cr Value to set @ref Sn_CR + * @sa getSn_CR() + */ +#define setSn_CR(sn, cr) \ + WIZCHIP_WRITE(Sn_CR(sn), cr) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_CR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_CR. + * @sa setSn_CR() + */ +#define getSn_CR(sn) \ + WIZCHIP_READ(Sn_CR(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_IR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)ir Value to set @ref Sn_IR + * @sa getSn_IR() + */ +#define setSn_IR(sn, ir) \ + WIZCHIP_WRITE(Sn_IR(sn), (ir & 0x1F)) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_IR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_IR. + * @sa setSn_IR() + */ +#define getSn_IR(sn) \ + (WIZCHIP_READ(Sn_IR(sn)) & 0x1F) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_IMR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)imr Value to set @ref Sn_IMR + * @sa getSn_IMR() + */ +#define setSn_IMR(sn, imr) \ + WIZCHIP_WRITE(Sn_IMR(sn), (imr & 0x1F)) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_IMR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_IMR. + * @sa setSn_IMR() + */ +#define getSn_IMR(sn) \ + (WIZCHIP_READ(Sn_IMR(sn)) & 0x1F) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_SR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_SR. + */ +#define getSn_SR(sn) \ + WIZCHIP_READ(Sn_SR(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_PORT register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)port Value to set @ref Sn_PORT. + * @sa getSn_PORT() + */ +#define setSn_PORT(sn, port) { \ + WIZCHIP_WRITE(Sn_PORT(sn), (uint8_t)(port >> 8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_PORT(sn),1), (uint8_t) port); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_PORT register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_PORT. + * @sa setSn_PORT() + */ +//M20150401 : Type explict declaration +/* +#define getSn_PORT(sn) \ + ((WIZCHIP_READ(Sn_PORT(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_PORT(sn),1))) +*/ +#define getSn_PORT(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_PORT(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_PORT(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_DHAR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t*)dhar Pointer variable to set socket n destination hardware address. It should be allocated 6 bytes. + * @sa getSn_DHAR() + */ +#define setSn_DHAR(sn, dhar) \ + WIZCHIP_WRITE_BUF(Sn_DHAR(sn), dhar, 6) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_MR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t*)dhar Pointer variable to get socket n destination hardware address. It should be allocated 6 bytes. + * @sa setSn_DHAR() + */ +#define getSn_DHAR(sn, dhar) \ + WIZCHIP_READ_BUF(Sn_DHAR(sn), dhar, 6) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_DIPR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t*)dipr Pointer variable to set socket n destination IP address. It should be allocated 4 bytes. + * @sa getSn_DIPR() + */ +#define setSn_DIPR(sn, dipr) \ + WIZCHIP_WRITE_BUF(Sn_DIPR(sn), dipr, 4) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_DIPR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t*)dipr Pointer variable to get socket n destination IP address. It should be allocated 4 bytes. + * @sa setSn_DIPR() + */ +#define getSn_DIPR(sn, dipr) \ + WIZCHIP_READ_BUF(Sn_DIPR(sn), dipr, 4) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_DPORT register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)dport Value to set @ref Sn_DPORT + * @sa getSn_DPORT() + */ +#define setSn_DPORT(sn, dport) { \ + WIZCHIP_WRITE(Sn_DPORT(sn), (uint8_t) (dport>>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_DPORT(sn),1), (uint8_t) dport); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_DPORT register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_DPORT. + * @sa setSn_DPORT() + */ +//M20150401 : Type explict declaration +/* +#define getSn_DPORT(sn) \ + ((WIZCHIP_READ(Sn_DPORT(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_DPORT(sn),1))) +*/ +#define getSn_DPORT(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_DPORT(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_DPORT(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_MSSR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)mss Value to set @ref Sn_MSSR + * @sa setSn_MSSR() + */ +#define setSn_MSSR(sn, mss) { \ + WIZCHIP_WRITE(Sn_MSSR(sn), (uint8_t)(mss>>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_MSSR(sn),1), (uint8_t) mss); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_MSSR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_MSSR. + * @sa setSn_MSSR() + */ +//M20150401 : Type explict declaration +/* +#define getSn_MSSR(sn) \ + ((WIZCHIP_READ(Sn_MSSR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_MSSR(sn),1))) +*/ +#define getSn_MSSR(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_MSSR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_MSSR(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_TOS register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)tos Value to set @ref Sn_TOS + * @sa getSn_TOS() + */ +#define setSn_TOS(sn, tos) \ + WIZCHIP_WRITE(Sn_TOS(sn), tos) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TOS register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of Sn_TOS. + * @sa setSn_TOS() + */ +#define getSn_TOS(sn) \ + WIZCHIP_READ(Sn_TOS(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_TTL register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)ttl Value to set @ref Sn_TTL + * @sa getSn_TTL() + */ +#define setSn_TTL(sn, ttl) \ + WIZCHIP_WRITE(Sn_TTL(sn), ttl) + + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TTL register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_TTL. + * @sa setSn_TTL() + */ +#define getSn_TTL(sn) \ + WIZCHIP_READ(Sn_TTL(sn)) + + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_RXBUF_SIZE register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)rxbufsize Value to set @ref Sn_RXBUF_SIZE + * @sa getSn_RXBUF_SIZE() + */ +#define setSn_RXBUF_SIZE(sn, rxbufsize) \ + WIZCHIP_WRITE(Sn_RXBUF_SIZE(sn),rxbufsize) + + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_RXBUF_SIZE register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_RXBUF_SIZE. + * @sa setSn_RXBUF_SIZE() + */ +#define getSn_RXBUF_SIZE(sn) \ + WIZCHIP_READ(Sn_RXBUF_SIZE(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_TXBUF_SIZE register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)txbufsize Value to set @ref Sn_TXBUF_SIZE + * @sa getSn_TXBUF_SIZE() + */ +#define setSn_TXBUF_SIZE(sn, txbufsize) \ + WIZCHIP_WRITE(Sn_TXBUF_SIZE(sn), txbufsize) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TXBUF_SIZE register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_TXBUF_SIZE. + * @sa setSn_TXBUF_SIZE() + */ +#define getSn_TXBUF_SIZE(sn) \ + WIZCHIP_READ(Sn_TXBUF_SIZE(sn)) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TX_FSR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_TX_FSR. + */ +uint16_t getSn_TX_FSR(uint8_t sn); + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TX_RD register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_TX_RD. + */ +//M20150401 : Type explict declaration +/* +#define getSn_TX_RD(sn) \ + ((WIZCHIP_READ(Sn_TX_RD(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_RD(sn),1))) +*/ +#define getSn_TX_RD(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_TX_RD(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_RD(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_TX_WR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)txwr Value to set @ref Sn_TX_WR + * @sa GetSn_TX_WR() + */ +#define setSn_TX_WR(sn, txwr) { \ + WIZCHIP_WRITE(Sn_TX_WR(sn), (uint8_t)(txwr>>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_TX_WR(sn),1), (uint8_t) txwr); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_TX_WR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_TX_WR. + * @sa setSn_TX_WR() + */ +//M20150401 : Type explict declaration +/* +#define getSn_TX_WR(sn) \ + ((WIZCHIP_READ(Sn_TX_WR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_WR(sn),1))) +*/ +#define getSn_TX_WR(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_TX_WR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_TX_WR(sn),1))) + + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_RX_RSR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_RX_RSR. + */ +uint16_t getSn_RX_RSR(uint8_t sn); + + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_RX_RD register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)rxrd Value to set @ref Sn_RX_RD + * @sa getSn_RX_RD() + */ +#define setSn_RX_RD(sn, rxrd) { \ + WIZCHIP_WRITE(Sn_RX_RD(sn), (uint8_t)(rxrd>>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_RX_RD(sn),1), (uint8_t) rxrd); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_RX_RD register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_RX_RD. + * @sa setSn_RX_RD() + */ +//M20150401 : Type explict declaration +/* +#define getSn_RX_RD(sn) \ + ((WIZCHIP_READ(Sn_RX_RD(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RD(sn),1))) +*/ +#define getSn_RX_RD(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_RX_RD(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_RD(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_RX_WR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_RX_WR. + */ +//M20150401 : Type explict declaration +/* +#define getSn_RX_WR(sn) \ + ((WIZCHIP_READ(Sn_RX_WR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_WR(sn),1))) +*/ +#define getSn_RX_WR(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_RX_WR(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_RX_WR(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_FRAG register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint16_t)frag Value to set @ref Sn_FRAG + * @sa getSn_FRAD() + */ +#define setSn_FRAG(sn, frag) { \ + WIZCHIP_WRITE(Sn_FRAG(sn), (uint8_t)(frag >>8)); \ + WIZCHIP_WRITE(WIZCHIP_OFFSET_INC(Sn_FRAG(sn),1), (uint8_t) frag); \ + } + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_FRAG register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of @ref Sn_FRAG. + * @sa setSn_FRAG() + */ +//M20150401 : Type explict declaration +/* +#define getSn_FRAG(sn) \ + ((WIZCHIP_READ(Sn_FRAG(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_FRAG(sn),1))) +*/ +#define getSn_FRAG(sn) \ + (((uint16_t)WIZCHIP_READ(Sn_FRAG(sn)) << 8) + WIZCHIP_READ(WIZCHIP_OFFSET_INC(Sn_FRAG(sn),1))) + +/** + * @ingroup Socket_register_access_function + * @brief Set @ref Sn_KPALVTR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param (uint8_t)kpalvt Value to set @ref Sn_KPALVTR + * @sa getSn_KPALVTR() + */ +#define setSn_KPALVTR(sn, kpalvt) \ + WIZCHIP_WRITE(Sn_KPALVTR(sn), kpalvt) + +/** + * @ingroup Socket_register_access_function + * @brief Get @ref Sn_KPALVTR register + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint8_t. Value of @ref Sn_KPALVTR. + * @sa setSn_KPALVTR() + */ +#define getSn_KPALVTR(sn) \ + WIZCHIP_READ(Sn_KPALVTR(sn)) + +////////////////////////////////////// + +///////////////////////////////////// +// Sn_TXBUF & Sn_RXBUF IO function // +///////////////////////////////////// +/** + * @brief Socket_register_access_function + * @brief Gets the max buffer size of socket sn passed as parameter. + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of Socket n RX max buffer size. + */ +//M20150401 : Type explict declaration +/* +#define getSn_RxMAX(sn) \ + (getSn_RXBUF_SIZE(sn) << 10) +*/ +#define getSn_RxMAX(sn) \ + (((uint16_t)getSn_RXBUF_SIZE(sn)) << 10) + +/** + * @brief Socket_register_access_function + * @brief Gets the max buffer size of socket sn passed as parameters. + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @return uint16_t. Value of Socket n TX max buffer size. + */ +//M20150401 : Type explict declaration +/* +#define getSn_TxMAX(sn) \ + (getSn_TXBUF_SIZE(sn) << 10) +*/ +#define getSn_TxMAX(sn) \ + (((uint16_t)getSn_TXBUF_SIZE(sn)) << 10) + +/** + * @ingroup Basic_IO_function + * @brief It copies data to internal TX memory + * + * @details This function reads the Tx write pointer register and after that, + * it copies the wizdata(pointer buffer) of the length of len(variable) bytes to internal TX memory + * and updates the Tx write pointer register. + * This function is being called by send() and sendto() function also. + * + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param wizdata Pointer buffer to write data + * @param len Data length + * @sa wiz_recv_data() + */ +void wiz_send_data(uint8_t sn, uint8_t *wizdata, uint16_t len); + +/** + * @ingroup Basic_IO_function + * @brief It copies data to your buffer from internal RX memory + * + * @details This function read the Rx read pointer register and after that, + * it copies the received data from internal RX memory + * to wizdata(pointer variable) of the length of len(variable) bytes. + * This function is being called by recv() also. + * + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param wizdata Pointer buffer to read data + * @param len Data length + * @sa wiz_send_data() + */ +void wiz_recv_data(uint8_t sn, uint8_t *wizdata, uint16_t len); + +/** + * @ingroup Basic_IO_function + * @brief It discard the received data in RX memory. + * @details It discards the data of the length of len(variable) bytes in internal RX memory. + * @param (uint8_t)sn Socket number. It should be 0 ~ 7. + * @param len Data length + */ +void wiz_recv_ignore(uint8_t sn, uint16_t len); + +/// @cond DOXY_APPLY_CODE +#endif +/// @endcond + +#ifdef __cplusplus +} +#endif + +#endif // _W5500_H_ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.c new file mode 100644 index 000000000..f6497ba0b --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.c @@ -0,0 +1,908 @@ +//****************************************************************************/ +//! +//! \file wizchip_conf.c +//! \brief WIZCHIP Config Header File. +//! \version 1.0.1 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2014/05/01> V1.0.1 Refer to M20140501 +//! 1. Explicit type casting in wizchip_bus_readdata() & wizchip_bus_writedata() +// Issued by Mathias ClauBen. +//! uint32_t type converts into ptrdiff_t first. And then recoverting it into uint8_t* +//! For remove the warning when pointer type size is not 32bit. +//! If ptrdiff_t doesn't support in your complier, You should must replace ptrdiff_t into your suitable pointer type. +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//*****************************************************************************/ +//A20140501 : for use the type - ptrdiff_t +#include +// + +#include "wizchip_conf.h" + +///////////// +//M20150401 : Remove ; in the default callback function such as wizchip_cris_enter(), wizchip_cs_select() and etc. +///////////// + +/** + * @brief Default function to enable interrupt. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_cris_enter(void) {}; +void wizchip_cris_enter(void) {} + +/** + * @brief Default function to disable interrupt. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_cris_exit(void) {}; +void wizchip_cris_exit(void) {} + +/** + * @brief Default function to select chip. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_cs_select(void) {}; +void wizchip_cs_select(void) {} + +/** + * @brief Default function to deselect chip. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_cs_deselect(void) {}; +void wizchip_cs_deselect(void) {} + +/** + * @brief Default function to read in direct or indirect interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ + //M20150601 : Rename the function for integrating with W5300 +//uint8_t wizchip_bus_readbyte(uint32_t AddrSel) { return * ((volatile uint8_t *)((ptrdiff_t) AddrSel)); } +iodata_t wizchip_bus_readdata(uint32_t AddrSel) { return * ((volatile iodata_t *)((ptrdiff_t) AddrSel)); } + +/** + * @brief Default function to write in direct or indirect interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//M20150601 : Rename the function for integrating with W5300 +//void wizchip_bus_writebyte(uint32_t AddrSel, uint8_t wb) { *((volatile uint8_t*)((ptrdiff_t)AddrSel)) = wb; } +void wizchip_bus_writedata(uint32_t AddrSel, iodata_t wb) { *((volatile iodata_t*)((ptrdiff_t)AddrSel)) = wb; } + +/** + * @brief Default function to read in SPI interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//uint8_t wizchip_spi_readbyte(void) {return 0;}; +uint8_t wizchip_spi_readbyte(void) {return 0;} + +/** + * @brief Default function to write in SPI interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_spi_writebyte(uint8_t wb) {}; +void wizchip_spi_writebyte(uint8_t wb) {} + +/** + * @brief Default function to burst read in SPI interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_spi_readburst(uint8_t* pBuf, uint16_t len) {}; +void wizchip_spi_readburst(uint8_t* pBuf, uint16_t len) {} + +/** + * @brief Default function to burst write in SPI interface. + * @note This function help not to access wrong address. If you do not describe this function or register any functions, + * null function is called. + */ +//void wizchip_spi_writeburst(uint8_t* pBuf, uint16_t len) {}; +void wizchip_spi_writeburst(uint8_t* pBuf, uint16_t len) {} + +/** + * @\ref _WIZCHIP instance + */ +// +//M20150401 : For a compiler didnot support a member of structure +// Replace the assignment of struct members with the assingment of array +// +/* +_WIZCHIP WIZCHIP = + { + .id = _WIZCHIP_ID_, + .if_mode = _WIZCHIP_IO_MODE_, + .CRIS._enter = wizchip_cris_enter, + .CRIS._exit = wizchip_cris_exit, + .CS._select = wizchip_cs_select, + .CS._deselect = wizchip_cs_deselect, + .IF.BUS._read_byte = wizchip_bus_readbyte, + .IF.BUS._write_byte = wizchip_bus_writebyte +// .IF.SPI._read_byte = wizchip_spi_readbyte, +// .IF.SPI._write_byte = wizchip_spi_writebyte + }; +*/ +_WIZCHIP WIZCHIP = +{ + _WIZCHIP_IO_MODE_, + _WIZCHIP_ID_ , + { + wizchip_cris_enter, + wizchip_cris_exit + }, + { + wizchip_cs_select, + wizchip_cs_deselect + }, + { + { + //M20150601 : Rename the function + //wizchip_bus_readbyte, + //wizchip_bus_writebyte + wizchip_bus_readdata, + wizchip_bus_writedata + }, + + } +}; + + +static uint8_t _DNS_[4]; // DNS server ip address +static dhcp_mode _DHCP_; // DHCP mode + +void reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void)) +{ + if(!cris_en || !cris_ex) + { + WIZCHIP.CRIS._enter = wizchip_cris_enter; + WIZCHIP.CRIS._exit = wizchip_cris_exit; + } + else + { + WIZCHIP.CRIS._enter = cris_en; + WIZCHIP.CRIS._exit = cris_ex; + } +} + +void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void)) +{ + if(!cs_sel || !cs_desel) + { + WIZCHIP.CS._select = wizchip_cs_select; + WIZCHIP.CS._deselect = wizchip_cs_deselect; + } + else + { + WIZCHIP.CS._select = cs_sel; + WIZCHIP.CS._deselect = cs_desel; + } +} + +//M20150515 : For integrating with W5300 +//void reg_wizchip_bus_cbfunc(uint8_t(*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, uint8_t wb)) +void reg_wizchip_bus_cbfunc(iodata_t(*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, iodata_t wb)) +{ + while(!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_BUS_)); + //M20150601 : Rename call back function for integrating with W5300 + /* + if(!bus_rb || !bus_wb) + { + WIZCHIP.IF.BUS._read_byte = wizchip_bus_readbyte; + WIZCHIP.IF.BUS._write_byte = wizchip_bus_writebyte; + } + else + { + WIZCHIP.IF.BUS._read_byte = bus_rb; + WIZCHIP.IF.BUS._write_byte = bus_wb; + } + */ + if(!bus_rb || !bus_wb) + { + WIZCHIP.IF.BUS._read_data = wizchip_bus_readdata; + WIZCHIP.IF.BUS._write_data = wizchip_bus_writedata; + } + else + { + WIZCHIP.IF.BUS._read_data = bus_rb; + WIZCHIP.IF.BUS._write_data = bus_wb; + } +} + +void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb)) +{ + while(!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_SPI_)); + + if(!spi_rb || !spi_wb) + { + WIZCHIP.IF.SPI._read_byte = wizchip_spi_readbyte; + WIZCHIP.IF.SPI._write_byte = wizchip_spi_writebyte; + } + else + { + WIZCHIP.IF.SPI._read_byte = spi_rb; + WIZCHIP.IF.SPI._write_byte = spi_wb; + } +} + +// 20140626 Eric Added for SPI burst operations +void reg_wizchip_spiburst_cbfunc(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(uint8_t* pBuf, uint16_t len)) +{ + while(!(WIZCHIP.if_mode & _WIZCHIP_IO_MODE_SPI_)); + + if(!spi_rb || !spi_wb) + { + WIZCHIP.IF.SPI._read_burst = wizchip_spi_readburst; + WIZCHIP.IF.SPI._write_burst = wizchip_spi_writeburst; + } + else + { + WIZCHIP.IF.SPI._read_burst = spi_rb; + WIZCHIP.IF.SPI._write_burst = spi_wb; + } +} + +int8_t ctlwizchip(ctlwizchip_type cwtype, void* arg) +{ +#if _WIZCHIP_ == W5100S || _WIZCHIP_ == W5200 || _WIZCHIP_ == W5500 + uint8_t tmp = 0; +#endif + uint8_t* ptmp[2] = {0,0}; + switch(cwtype) + { + case CW_RESET_WIZCHIP: + wizchip_sw_reset(); + break; + case CW_INIT_WIZCHIP: + if(arg != 0) + { + ptmp[0] = (uint8_t*)arg; + ptmp[1] = ptmp[0] + _WIZCHIP_SOCK_NUM_; + } + return wizchip_init(ptmp[0], ptmp[1]); + case CW_CLR_INTERRUPT: + wizchip_clrinterrupt(*((intr_kind*)arg)); + break; + case CW_GET_INTERRUPT: + *((intr_kind*)arg) = wizchip_getinterrupt(); + break; + case CW_SET_INTRMASK: + wizchip_setinterruptmask(*((intr_kind*)arg)); + break; + case CW_GET_INTRMASK: + *((intr_kind*)arg) = wizchip_getinterruptmask(); + break; + //M20150601 : This can be supported by W5200, W5500 + //#if _WIZCHIP_ > W5100 + #if (_WIZCHIP_ == W5200 || _WIZCHIP_ == W5500) + case CW_SET_INTRTIME: + setINTLEVEL(*(uint16_t*)arg); + break; + case CW_GET_INTRTIME: + *(uint16_t*)arg = getINTLEVEL(); + break; + #endif + case CW_GET_ID: + ((uint8_t*)arg)[0] = WIZCHIP.id[0]; + ((uint8_t*)arg)[1] = WIZCHIP.id[1]; + ((uint8_t*)arg)[2] = WIZCHIP.id[2]; + ((uint8_t*)arg)[3] = WIZCHIP.id[3]; + ((uint8_t*)arg)[4] = WIZCHIP.id[4]; + ((uint8_t*)arg)[5] = 0; + break; + #if _WIZCHIP_ == W5100S || _WIZCHIP_ == W5500 + case CW_RESET_PHY: + wizphy_reset(); + break; + case CW_SET_PHYCONF: + wizphy_setphyconf((wiz_PhyConf*)arg); + break; + case CW_GET_PHYCONF: + wizphy_getphyconf((wiz_PhyConf*)arg); + break; + case CW_GET_PHYSTATUS: + break; + case CW_SET_PHYPOWMODE: + return wizphy_setphypmode(*(uint8_t*)arg); + #endif + #if _WIZCHIP_ == W5100S || _WIZCHIP_ == W5200 || _WIZCHIP_ == W5500 + case CW_GET_PHYPOWMODE: + tmp = wizphy_getphypmode(); + if((int8_t)tmp == -1) return -1; + *(uint8_t*)arg = tmp; + break; + case CW_GET_PHYLINK: + tmp = wizphy_getphylink(); + if((int8_t)tmp == -1) return -1; + *(uint8_t*)arg = tmp; + break; + #endif + default: + return -1; + } + return 0; +} + + +int8_t ctlnetwork(ctlnetwork_type cntype, void* arg) +{ + + switch(cntype) + { + case CN_SET_NETINFO: + wizchip_setnetinfo((wiz_NetInfo*)arg); + break; + case CN_GET_NETINFO: + wizchip_getnetinfo((wiz_NetInfo*)arg); + break; + case CN_SET_NETMODE: + return wizchip_setnetmode(*(netmode_type*)arg); + case CN_GET_NETMODE: + *(netmode_type*)arg = wizchip_getnetmode(); + break; + case CN_SET_TIMEOUT: + wizchip_settimeout((wiz_NetTimeout*)arg); + break; + case CN_GET_TIMEOUT: + wizchip_gettimeout((wiz_NetTimeout*)arg); + break; + default: + return -1; + } + return 0; +} + +void wizchip_sw_reset(void) +{ + uint8_t gw[4], sn[4], sip[4]; + uint8_t mac[6]; +//A20150601 +#if _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_BUS_INDIR_ + uint16_t mr = (uint16_t)getMR(); + setMR(mr | MR_IND); +#endif +// + getSHAR(mac); + getGAR(gw); getSUBR(sn); getSIPR(sip); + setMR(MR_RST); + getMR(); // for delay +//A2015051 : For indirect bus mode +#if _WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_BUS_INDIR_ + setMR(mr | MR_IND); +#endif +// + setSHAR(mac); + setGAR(gw); + setSUBR(sn); + setSIPR(sip); +} + +int8_t wizchip_init(uint8_t* txsize, uint8_t* rxsize) +{ + int8_t i; +#if _WIZCHIP_ < W5200 + int8_t j; +#endif + int8_t tmp = 0; + wizchip_sw_reset(); + if(txsize) + { + tmp = 0; + //M20150601 : For integrating with W5300 + #if _WIZCHIP_ == W5300 + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + if(txsize[i] >= 64) return -1; //No use 64KB even if W5300 support max 64KB memory allocation + tmp += txsize[i]; + if(tmp > 128) return -1; + } + if(tmp % 8) return -1; + #else + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + tmp += txsize[i]; + + #if _WIZCHIP_ < W5200 //2016.10.28 peter add condition for w5100 and w5100s + if(tmp > 8) return -1; + #else + if(tmp > 16) return -1; + #endif + } + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + #if _WIZCHIP_ < W5200 //2016.10.28 peter add condition for w5100 + j = 0; + while((txsize[i] >> j != 1)&&(txsize[i] !=0)){j++;} + setSn_TXBUF_SIZE(i, j); + #else + setSn_TXBUF_SIZE(i, txsize[i]); + #endif + } + + #endif + } + + if(rxsize) + { + tmp = 0; + #if _WIZCHIP_ == W5300 + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + if(rxsize[i] >= 64) return -1; //No use 64KB even if W5300 support max 64KB memory allocation + tmp += rxsize[i]; + if(tmp > 128) return -1; + } + if(tmp % 8) return -1; + #else + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + tmp += rxsize[i]; + #if _WIZCHIP_ < W5200 //2016.10.28 peter add condition for w5100 and w5100s + if(tmp > 8) return -1; + #else + if(tmp > 16) return -1; + #endif + } + + for(i = 0 ; i < _WIZCHIP_SOCK_NUM_; i++) + { + #if _WIZCHIP_ < W5200 // add condition for w5100 + j = 0; + while((rxsize[i] >> j != 1)&&(txsize[i] !=0)){j++;} + setSn_RXBUF_SIZE(i, j); + #else + setSn_RXBUF_SIZE(i, rxsize[i]); + #endif + } + #endif + } + return 0; +} + +void wizchip_clrinterrupt(intr_kind intr) +{ + uint8_t ir = (uint8_t)intr; + uint8_t sir = (uint8_t)((uint16_t)intr >> 8); +#if _WIZCHIP_ < W5500 + ir |= (1<<4); // IK_WOL +#endif +#if _WIZCHIP_ == W5200 + ir |= (1 << 6); +#endif + +#if _WIZCHIP_ < W5200 + sir &= 0x0F; +#endif + +#if _WIZCHIP_ <= W5100S + ir |= sir; + setIR(ir); +//A20150601 : For integrating with W5300 +#elif _WIZCHIP_ == W5300 + setIR( ((((uint16_t)ir) << 8) | (((uint16_t)sir) & 0x00FF)) ); +#else + setIR(ir); +//M20200227 : For clear + //setSIR(sir); + for(ir=0; ir<8; ir++){ + if(sir & (0x01 <> 8); + sir = (uint8_t)ret; +#else + ir = getIR(); + sir = getSIR(); +#endif + +//M20150601 : For Integrating with W5300 +//#if _WIZCHIP_ < W5500 +#if _WIZCHIP_ < W5200 + ir &= ~(1<<4); // IK_WOL +#endif +#if _WIZCHIP_ == W5200 + ir &= ~(1 << 6); +#endif + ret = sir; + ret = (ret << 8) + ir; + return (intr_kind)ret; +} + +void wizchip_setinterruptmask(intr_kind intr) +{ + uint8_t imr = (uint8_t)intr; + uint8_t simr = (uint8_t)((uint16_t)intr >> 8); +#if _WIZCHIP_ < W5500 + imr &= ~(1<<4); // IK_WOL +#endif +#if _WIZCHIP_ == W5200 + imr &= ~(1 << 6); +#endif + +#if _WIZCHIP_ < W5200 + simr &= 0x0F; + imr |= simr; + setIMR(imr); +//A20150601 : For integrating with W5300 +#elif _WIZCHIP_ == W5300 + setIMR( ((((uint16_t)imr) << 8) | (((uint16_t)simr) & 0x00FF)) ); +#else + setIMR(imr); + setSIMR(simr); +#endif +} + +intr_kind wizchip_getinterruptmask(void) +{ + uint8_t imr = 0; + uint8_t simr = 0; + uint16_t ret = 0; +#if _WIZCHIP_ < W5200 + imr = getIMR(); + simr = imr & 0x0F; +//A20150601 : For integrating with W5300 +#elif _WIZCHIP_ == W5300 + ret = getIMR(); + imr = (uint8_t)(ret >> 8); + simr = (uint8_t)ret; +#else + imr = getIMR(); + simr = getSIMR(); +#endif + +#if _WIZCHIP_ < W5500 + imr &= ~(1<<4); // IK_WOL +#endif +#if _WIZCHIP_ == W5200 + imr &= ~(1 << 6); // IK_DEST_UNREACH +#endif + ret = simr; + ret = (ret << 8) + imr; + return (intr_kind)ret; +} + +int8_t wizphy_getphylink(void) +{ + int8_t tmp = PHY_LINK_OFF; +#if _WIZCHIP_ == W5100S + if(getPHYSR() & PHYSR_LNK) + tmp = PHY_LINK_ON; +#elif _WIZCHIP_ == W5200 + if(getPHYSTATUS() & PHYSTATUS_LINK) + tmp = PHY_LINK_ON; +#elif _WIZCHIP_ == W5500 + if(getPHYCFGR() & PHYCFGR_LNK_ON) + tmp = PHY_LINK_ON; + +#else + tmp = -1; +#endif + return tmp; +} + +#if _WIZCHIP_ > W5100 + +int8_t wizphy_getphypmode(void) +{ + int8_t tmp = 0; + #if _WIZCHIP_ == W5200 + if(getPHYSTATUS() & PHYSTATUS_POWERDOWN) + tmp = PHY_POWER_DOWN; + else + tmp = PHY_POWER_NORM; + #elif _WIZCHIP_ == 5500 + if((getPHYCFGR() & PHYCFGR_OPMDC_ALLA) == PHYCFGR_OPMDC_PDOWN) + tmp = PHY_POWER_DOWN; + else + tmp = PHY_POWER_NORM; + #else + tmp = -1; + #endif + return tmp; +} +#endif + +#if _WIZCHIP_ == W5100S +void wizphy_reset(void) +{ + uint16_t tmp = wiz_mdio_read(PHYMDIO_BMCR); + tmp |= BMCR_RESET; + wiz_mdio_write(PHYMDIO_BMCR, tmp); + while(wiz_mdio_read(PHYMDIO_BMCR)&BMCR_RESET){} +} + +void wizphy_setphyconf(wiz_PhyConf* phyconf) +{ + uint16_t tmp = wiz_mdio_read(PHYMDIO_BMCR); + if(phyconf->mode == PHY_MODE_AUTONEGO) + tmp |= BMCR_AUTONEGO; + else + { + tmp &= ~BMCR_AUTONEGO; + if(phyconf->duplex == PHY_DUPLEX_FULL) + { + tmp |= BMCR_DUP; + } + else + { + tmp &= ~BMCR_DUP; + } + if(phyconf->speed == PHY_SPEED_100) + { + tmp |= BMCR_SPEED; + } + else + { + tmp &= ~BMCR_SPEED; + } + } + wiz_mdio_write(PHYMDIO_BMCR, tmp); +} + +void wizphy_getphyconf(wiz_PhyConf* phyconf) +{ + uint16_t tmp = 0; + tmp = wiz_mdio_read(PHYMDIO_BMCR); + phyconf->by = PHY_CONFBY_SW; + if(tmp & BMCR_AUTONEGO) + { + phyconf->mode = PHY_MODE_AUTONEGO; + } + else + { + phyconf->mode = PHY_MODE_MANUAL; + if(tmp&BMCR_DUP) phyconf->duplex = PHY_DUPLEX_FULL; + else phyconf->duplex = PHY_DUPLEX_HALF; + if(tmp&BMCR_SPEED) phyconf->speed = PHY_SPEED_100; + else phyconf->speed = PHY_SPEED_10; + } +} + +int8_t wizphy_setphypmode(uint8_t pmode) +{ + uint16_t tmp = 0; + tmp = wiz_mdio_read(PHYMDIO_BMCR); + if( pmode == PHY_POWER_DOWN) + { + tmp |= BMCR_PWDN; + } + else + { + tmp &= ~BMCR_PWDN; + } + wiz_mdio_write(PHYMDIO_BMCR, tmp); + tmp = wiz_mdio_read(PHYMDIO_BMCR); + if( pmode == PHY_POWER_DOWN) + { + if(tmp & BMCR_PWDN) return 0; + } + else + { + if((tmp & BMCR_PWDN) != BMCR_PWDN) return 0; + } + return -1; +} + +#endif +#if _WIZCHIP_ == W5500 +void wizphy_reset(void) +{ + uint8_t tmp = getPHYCFGR(); + tmp &= PHYCFGR_RST; + setPHYCFGR(tmp); + tmp = getPHYCFGR(); + tmp |= ~PHYCFGR_RST; + setPHYCFGR(tmp); +} + +void wizphy_setphyconf(wiz_PhyConf* phyconf) +{ + uint8_t tmp = 0; + if(phyconf->by == PHY_CONFBY_SW) + tmp |= PHYCFGR_OPMD; + else + tmp &= ~PHYCFGR_OPMD; + if(phyconf->mode == PHY_MODE_AUTONEGO) + tmp |= PHYCFGR_OPMDC_ALLA; + else + { + if(phyconf->duplex == PHY_DUPLEX_FULL) + { + if(phyconf->speed == PHY_SPEED_100) + tmp |= PHYCFGR_OPMDC_100F; + else + tmp |= PHYCFGR_OPMDC_10F; + } + else + { + if(phyconf->speed == PHY_SPEED_100) + tmp |= PHYCFGR_OPMDC_100H; + else + tmp |= PHYCFGR_OPMDC_10H; + } + } + setPHYCFGR(tmp); + wizphy_reset(); +} + +void wizphy_getphyconf(wiz_PhyConf* phyconf) +{ + uint8_t tmp = 0; + tmp = getPHYCFGR(); + phyconf->by = (tmp & PHYCFGR_OPMD) ? PHY_CONFBY_SW : PHY_CONFBY_HW; + switch(tmp & PHYCFGR_OPMDC_ALLA) + { + case PHYCFGR_OPMDC_ALLA: + case PHYCFGR_OPMDC_100FA: + phyconf->mode = PHY_MODE_AUTONEGO; + break; + default: + phyconf->mode = PHY_MODE_MANUAL; + break; + } + switch(tmp & PHYCFGR_OPMDC_ALLA) + { + case PHYCFGR_OPMDC_100FA: + case PHYCFGR_OPMDC_100F: + case PHYCFGR_OPMDC_100H: + phyconf->speed = PHY_SPEED_100; + break; + default: + phyconf->speed = PHY_SPEED_10; + break; + } + switch(tmp & PHYCFGR_OPMDC_ALLA) + { + case PHYCFGR_OPMDC_100FA: + case PHYCFGR_OPMDC_100F: + case PHYCFGR_OPMDC_10F: + phyconf->duplex = PHY_DUPLEX_FULL; + break; + default: + phyconf->duplex = PHY_DUPLEX_HALF; + break; + } +} + +void wizphy_getphystat(wiz_PhyConf* phyconf) +{ + uint8_t tmp = getPHYCFGR(); + phyconf->duplex = (tmp & PHYCFGR_DPX_FULL) ? PHY_DUPLEX_FULL : PHY_DUPLEX_HALF; + phyconf->speed = (tmp & PHYCFGR_SPD_100) ? PHY_SPEED_100 : PHY_SPEED_10; +} + +int8_t wizphy_setphypmode(uint8_t pmode) +{ + uint8_t tmp = 0; + tmp = getPHYCFGR(); + if((tmp & PHYCFGR_OPMD)== 0) return -1; + tmp &= ~PHYCFGR_OPMDC_ALLA; + if( pmode == PHY_POWER_DOWN) + tmp |= PHYCFGR_OPMDC_PDOWN; + else + tmp |= PHYCFGR_OPMDC_ALLA; + setPHYCFGR(tmp); + wizphy_reset(); + tmp = getPHYCFGR(); + if( pmode == PHY_POWER_DOWN) + { + if(tmp & PHYCFGR_OPMDC_PDOWN) return 0; + } + else + { + if(tmp & PHYCFGR_OPMDC_ALLA) return 0; + } + return -1; +} +#endif + + +void wizchip_setnetinfo(wiz_NetInfo* pnetinfo) +{ + setSHAR(pnetinfo->mac); + setGAR(pnetinfo->gw); + setSUBR(pnetinfo->sn); + setSIPR(pnetinfo->ip); + _DNS_[0] = pnetinfo->dns[0]; + _DNS_[1] = pnetinfo->dns[1]; + _DNS_[2] = pnetinfo->dns[2]; + _DNS_[3] = pnetinfo->dns[3]; + _DHCP_ = pnetinfo->dhcp; +} + +void wizchip_getnetinfo(wiz_NetInfo* pnetinfo) +{ + getSHAR(pnetinfo->mac); + getGAR(pnetinfo->gw); + getSUBR(pnetinfo->sn); + getSIPR(pnetinfo->ip); + pnetinfo->dns[0]= _DNS_[0]; + pnetinfo->dns[1]= _DNS_[1]; + pnetinfo->dns[2]= _DNS_[2]; + pnetinfo->dns[3]= _DNS_[3]; + pnetinfo->dhcp = _DHCP_; +} + +int8_t wizchip_setnetmode(netmode_type netmode) +{ + uint8_t tmp = 0; +#if _WIZCHIP_ != W5500 + if(netmode & ~(NM_WAKEONLAN | NM_PPPOE | NM_PINGBLOCK)) return -1; +#else + if(netmode & ~(NM_WAKEONLAN | NM_PPPOE | NM_PINGBLOCK | NM_FORCEARP)) return -1; +#endif + tmp = getMR(); + tmp |= (uint8_t)netmode; + setMR(tmp); + return 0; +} + +netmode_type wizchip_getnetmode(void) +{ + return (netmode_type) getMR(); +} + +void wizchip_settimeout(wiz_NetTimeout* nettime) +{ + setRCR(nettime->retry_cnt); + setRTR(nettime->time_100us); +} + +void wizchip_gettimeout(wiz_NetTimeout* nettime) +{ + nettime->retry_cnt = getRCR(); + nettime->time_100us = getRTR(); +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.h new file mode 100644 index 000000000..698f82ba4 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_conf.h @@ -0,0 +1,661 @@ +//***************************************************************************** +// +//! \file wizchip_conf.h +//! \brief WIZCHIP Config Header File. +//! \version 1.0.0 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +/** + * @defgroup extra_functions 2. WIZnet Extra Functions + * + * @brief These functions is optional function. It could be replaced at WIZCHIP I/O function because they were made by WIZCHIP I/O functions. + * @details There are functions of configuring WIZCHIP, network, interrupt, phy, network information and timer. \n + * + */ + +#ifndef _WIZCHIP_CONF_H_ +#define _WIZCHIP_CONF_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/** + * @brief Select WIZCHIP. + * @todo You should select one, \b W5100, \b W5100S, \b W5200, \b W5300, \b W5500 or etc. \n\n + * ex> #define \_WIZCHIP_ W5500 + */ + +#define W5100 5100 +#define W5100S 5100+5 +#define W5200 5200 +#define W5300 5300 +#define W5500 5500 + +#ifndef _WIZCHIP_ +#define _WIZCHIP_ W5500 // W5100, W5100S, W5200, W5300, W5500 +#endif + +#define _WIZCHIP_IO_MODE_NONE_ 0x0000 +#define _WIZCHIP_IO_MODE_BUS_ 0x0100 /**< Bus interface mode */ +#define _WIZCHIP_IO_MODE_SPI_ 0x0200 /**< SPI interface mode */ +//#define _WIZCHIP_IO_MODE_IIC_ 0x0400 +//#define _WIZCHIP_IO_MODE_SDIO_ 0x0800 +// Add to +// + +#define _WIZCHIP_IO_MODE_BUS_DIR_ (_WIZCHIP_IO_MODE_BUS_ + 1) /**< BUS interface mode for direct */ +#define _WIZCHIP_IO_MODE_BUS_INDIR_ (_WIZCHIP_IO_MODE_BUS_ + 2) /**< BUS interface mode for indirect */ + +#define _WIZCHIP_IO_MODE_SPI_VDM_ (_WIZCHIP_IO_MODE_SPI_ + 1) /**< SPI interface mode for variable length data*/ +#define _WIZCHIP_IO_MODE_SPI_FDM_ (_WIZCHIP_IO_MODE_SPI_ + 2) /**< SPI interface mode for fixed length data mode*/ +#define _WIZCHIP_IO_MODE_SPI_5500_ (_WIZCHIP_IO_MODE_SPI_ + 3) /**< SPI interface mode for fixed length data mode*/ + +#if (_WIZCHIP_ == W5100) + #define _WIZCHIP_ID_ "W5100\0" +/** + * @brief Define interface mode. + * @todo you should select interface mode as chip. Select one of @ref \_WIZCHIP_IO_MODE_SPI_ , @ref \_WIZCHIP_IO_MODE_BUS_DIR_ or @ref \_WIZCHIP_IO_MODE_BUS_INDIR_ + */ +// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_DIR_ +// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_INDIR_ + #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_ + +//A20150601 : Define the unit of IO DATA. + typedef uint8_t iodata_t; +//A20150401 : Indclude W5100.h file + #include "W5100/w5100.h" + +#elif (_WIZCHIP_ == W5100S) +#define _WIZCHIP_ID_ "W5100S\0" +/** +* @brief Define interface mode. +* @todo you should select interface mode as chip. Select one of @ref \_WIZCHIP_IO_MODE_SPI_ , @ref \_WIZCHIP_IO_MODE_BUS_DIR_ or @ref \_WIZCHIP_IO_MODE_BUS_INDIR_ +*/ +// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_INDIR_ + //#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_5500_ + #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_ + +//A20150601 : Define the unit of IO DATA. + typedef uint8_t iodata_t; +//A20150401 : Indclude W5100.h file + #include "W5100S/w5100s.h" +#elif (_WIZCHIP_ == W5200) + #define _WIZCHIP_ID_ "W5200\0" +/** + * @brief Define interface mode. + * @todo you should select interface mode as chip. Select one of @ref \_WIZCHIP_IO_MODE_SPI_ or @ref \ _WIZCHIP_IO_MODE_BUS_INDIR_ + */ +#ifndef _WIZCHIP_IO_MODE_ +// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_INDIR_ + #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_ +#endif +//A20150601 : Define the unit of IO DATA. + typedef uint8_t iodata_t; + #include "W5200/w5200.h" +#elif (_WIZCHIP_ == W5500) + #define _WIZCHIP_ID_ "W5500\0" + +/** + * @brief Define interface mode. \n + * @todo Should select interface mode as chip. + * - @ref \_WIZCHIP_IO_MODE_SPI_ \n + * -@ref \_WIZCHIP_IO_MODE_SPI_VDM_ : Valid only in @ref \_WIZCHIP_ == W5500 \n + * -@ref \_WIZCHIP_IO_MODE_SPI_FDM_ : Valid only in @ref \_WIZCHIP_ == W5500 \n + * - @ref \_WIZCHIP_IO_MODE_BUS_ \n + * - @ref \_WIZCHIP_IO_MODE_BUS_DIR_ \n + * - @ref \_WIZCHIP_IO_MODE_BUS_INDIR_ \n + * - Others will be defined in future. \n\n + * ex> #define \_WIZCHIP_IO_MODE_ \_WIZCHIP_IO_MODE_SPI_VDM_ + * + */ +#ifndef _WIZCHIP_IO_MODE_ + //#define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_FDM_ + #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_SPI_VDM_ +#endif +//A20150601 : Define the unit of IO DATA. + typedef uint8_t iodata_t; + #include "W5500/w5500.h" +#elif ( _WIZCHIP_ == W5300) + #define _WIZCHIP_ID_ "W5300\0" +/** + * @brief Define interface mode. + * @todo you should select interface mode as chip. Select one of @ref \_WIZCHIP_IO_MODE_SPI_ , @ref \_WIZCHIP_IO_MODE_BUS_DIR_ or @ref \_WIZCHIP_IO_MODE_BUS_INDIR_ + */ +#ifndef _WIZCHIP_IO_MODE_ +// #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_DIR_ + #define _WIZCHIP_IO_MODE_ _WIZCHIP_IO_MODE_BUS_INDIR_ +#endif + +//A20150601 : Define the unit and bus width of IO DATA. + /** + * @brief Select the data width 8 or 16 bits. + * @todo you should select the bus width. Select one of 8 or 16. + */ + #ifndef _WIZCHIP_IO_BUS_WIDTH_ + #define _WIZCHIP_IO_BUS_WIDTH_ 8 // 16 + #endif + #if _WIZCHIP_IO_BUS_WIDTH_ == 8 + typedef uint8_t iodata_t; + #elif _WIZCHIP_IO_BUS_WIDTH_ == 16 + typedef uint16_t iodata_t; + #else + #error "Unknown _WIZCHIP_IO_BUS_WIDTH_. It should be 8 or 16." + #endif +// + #include "W5300/w5300.h" +#else + #error "Unknown defined _WIZCHIP_. You should define one of 5100, 5200, and 5500 !!!" +#endif + +#ifndef _WIZCHIP_IO_MODE_ + #error "Undefined _WIZCHIP_IO_MODE_. You should define it !!!" +#endif + +/** + * @brief Define I/O base address when BUS IF mode. + * @todo Should re-define it to fit your system when BUS IF Mode (@ref \_WIZCHIP_IO_MODE_BUS_, + * @ref \_WIZCHIP_IO_MODE_BUS_DIR_, @ref \_WIZCHIP_IO_MODE_BUS_INDIR_). \n\n + * ex> #define \_WIZCHIP_IO_BASE_ 0x00008000 + */ +#if _WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_BUS_ + #define _WIZCHIP_IO_BASE_ 0x60000000 // for 5100S IND +#elif _WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SPI_ + #define _WIZCHIP_IO_BASE_ 0x00000000 // for 5100S SPI +#endif + +#ifndef _WIZCHIP_IO_BASE_ +#define _WIZCHIP_IO_BASE_ 0x00000000 // 0x8000 +#endif + +//M20150401 : Typing Error +//#if _WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_BUS +#if _WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_BUS_ + #ifndef _WIZCHIP_IO_BASE_ + #error "You should be define _WIZCHIP_IO_BASE to fit your system memory map." + #endif +#endif + +#if _WIZCHIP_ >= W5200 + #define _WIZCHIP_SOCK_NUM_ 8 ///< The count of independant socket of @b WIZCHIP +#else + #define _WIZCHIP_SOCK_NUM_ 4 ///< The count of independant socket of @b WIZCHIP +#endif + + +/******************************************************** +* WIZCHIP BASIC IF functions for SPI, SDIO, I2C , ETC. +*********************************************************/ +/** + * @ingroup DATA_TYPE + * @brief The set of callback functions for W5500:@ref WIZCHIP_IO_Functions W5200:@ref WIZCHIP_IO_Functions_W5200 + */ +typedef struct __WIZCHIP +{ + uint16_t if_mode; ///< host interface mode + uint8_t id[7]; ///< @b WIZCHIP ID such as @b 5100, @b 5200, @b 5500, and so on. + /** + * The set of critical section callback func. + */ + struct _CRIS + { + void (*_enter) (void); ///< crtical section enter + void (*_exit) (void); ///< critial section exit + }CRIS; + /** + * The set of @ref \_WIZCHIP_ select control callback func. + */ + struct _CS + { + void (*_select) (void); ///< @ref \_WIZCHIP_ selected + void (*_deselect)(void); ///< @ref \_WIZCHIP_ deselected + }CS; + /** + * The set of interface IO callback func. + */ + union _IF + { + /** + * For BUS interface IO + */ + //M20156501 : Modify the function name for integrating with W5300 + //struct + //{ + // uint8_t (*_read_byte) (uint32_t AddrSel); + // void (*_write_byte) (uint32_t AddrSel, uint8_t wb); + //}BUS; + struct + { + iodata_t (*_read_data) (uint32_t AddrSel); + void (*_write_data) (uint32_t AddrSel, iodata_t wb); + }BUS; + + /** + * For SPI interface IO + */ + struct + { + uint8_t (*_read_byte) (void); + void (*_write_byte) (uint8_t wb); + void (*_read_burst) (uint8_t* pBuf, uint16_t len); + void (*_write_burst) (uint8_t* pBuf, uint16_t len); + }SPI; + // To be added + // + }IF; +}_WIZCHIP; + +extern _WIZCHIP WIZCHIP; + +/** + * @ingroup DATA_TYPE + * WIZCHIP control type enumration used in @ref ctlwizchip(). + */ +typedef enum +{ + CW_RESET_WIZCHIP, ///< Resets WIZCHIP by softly + CW_INIT_WIZCHIP, ///< Initializes to WIZCHIP with SOCKET buffer size 2 or 1 dimension array typed uint8_t. + CW_GET_INTERRUPT, ///< Get Interrupt status of WIZCHIP + CW_CLR_INTERRUPT, ///< Clears interrupt + CW_SET_INTRMASK, ///< Masks interrupt + CW_GET_INTRMASK, ///< Get interrupt mask + CW_SET_INTRTIME, ///< Set interval time between the current and next interrupt. + CW_GET_INTRTIME, ///< Set interval time between the current and next interrupt. + CW_GET_ID, ///< Gets WIZCHIP name. + +//D20150601 : For no modification your application code +//#if _WIZCHIP_ == W5500 + CW_RESET_PHY, ///< Resets internal PHY. Valid Only W5500 + CW_SET_PHYCONF, ///< When PHY configured by internal register, PHY operation mode (Manual/Auto, 10/100, Half/Full). Valid Only W5000 + CW_GET_PHYCONF, ///< Get PHY operation mode in internal register. Valid Only W5500 + CW_GET_PHYSTATUS, ///< Get real PHY status on operating. Valid Only W5500 + CW_SET_PHYPOWMODE, ///< Set PHY power mode as normal and down when PHYSTATUS.OPMD == 1. Valid Only W5500 +//#endif +//D20150601 : For no modification your application code +//#if _WIZCHIP_ == W5200 || _WIZCHIP_ == W5500 + CW_GET_PHYPOWMODE, ///< Get PHY Power mode as down or normal, Valid Only W5100, W5200 + CW_GET_PHYLINK ///< Get PHY Link status, Valid Only W5100, W5200 +//#endif +}ctlwizchip_type; + +/** + * @ingroup DATA_TYPE + * Network control type enumration used in @ref ctlnetwork(). + */ +typedef enum +{ + CN_SET_NETINFO, ///< Set Network with @ref wiz_NetInfo + CN_GET_NETINFO, ///< Get Network with @ref wiz_NetInfo + CN_SET_NETMODE, ///< Set network mode as WOL, PPPoE, Ping Block, and Force ARP mode + CN_GET_NETMODE, ///< Get network mode as WOL, PPPoE, Ping Block, and Force ARP mode + CN_SET_TIMEOUT, ///< Set network timeout as retry count and time. + CN_GET_TIMEOUT, ///< Get network timeout as retry count and time. +}ctlnetwork_type; + +/** + * @ingroup DATA_TYPE + * Interrupt kind when CW_SET_INTRRUPT, CW_GET_INTERRUPT, CW_SET_INTRMASK + * and CW_GET_INTRMASK is used in @ref ctlnetwork(). + * It can be used with OR operation. + */ +typedef enum +{ +#if _WIZCHIP_ == W5500 + IK_WOL = (1 << 4), ///< Wake On Lan by receiving the magic packet. Valid in W500. +#elif _WIZCHIP_ == W5300 + IK_FMTU = (1 << 4), ///< Received a ICMP message (Fragment MTU) +#endif + + IK_PPPOE_TERMINATED = (1 << 5), ///< PPPoE Disconnected + +#if _WIZCHIP_ != W5200 + IK_DEST_UNREACH = (1 << 6), ///< Destination IP & Port Unreachable, No use in W5200 +#endif + + IK_IP_CONFLICT = (1 << 7), ///< IP conflict occurred + + IK_SOCK_0 = (1 << 8), ///< Socket 0 interrupt + IK_SOCK_1 = (1 << 9), ///< Socket 1 interrupt + IK_SOCK_2 = (1 << 10), ///< Socket 2 interrupt + IK_SOCK_3 = (1 << 11), ///< Socket 3 interrupt +#if _WIZCHIP_ > W5100S + IK_SOCK_4 = (1 << 12), ///< Socket 4 interrupt, No use in 5100 + IK_SOCK_5 = (1 << 13), ///< Socket 5 interrupt, No use in 5100 + IK_SOCK_6 = (1 << 14), ///< Socket 6 interrupt, No use in 5100 + IK_SOCK_7 = (1 << 15), ///< Socket 7 interrupt, No use in 5100 +#endif + +#if _WIZCHIP_ > W5100S + IK_SOCK_ALL = (0xFF << 8) ///< All Socket interrupt +#else + IK_SOCK_ALL = (0x0F << 8) ///< All Socket interrupt +#endif +}intr_kind; + +#define PHY_CONFBY_HW 0 ///< Configured PHY operation mode by HW pin +#define PHY_CONFBY_SW 1 ///< Configured PHY operation mode by SW register +#define PHY_MODE_MANUAL 0 ///< Configured PHY operation mode with user setting. +#define PHY_MODE_AUTONEGO 1 ///< Configured PHY operation mode with auto-negotiation +#define PHY_SPEED_10 0 ///< Link Speed 10 +#define PHY_SPEED_100 1 ///< Link Speed 100 +#define PHY_DUPLEX_HALF 0 ///< Link Half-Duplex +#define PHY_DUPLEX_FULL 1 ///< Link Full-Duplex +#define PHY_LINK_OFF 0 ///< Link Off +#define PHY_LINK_ON 1 ///< Link On +#define PHY_POWER_NORM 0 ///< PHY power normal mode +#define PHY_POWER_DOWN 1 ///< PHY power down mode + + +#if _WIZCHIP_ == W5100S || _WIZCHIP_ == W5500 +/** + * @ingroup DATA_TYPE + * It configures PHY configuration when CW_SET PHYCONF or CW_GET_PHYCONF in W5500, + * and it indicates the real PHY status configured by HW or SW in all WIZCHIP. \n + * Valid only in W5500. + */ +typedef struct wiz_PhyConf_t +{ + uint8_t by; ///< set by @ref PHY_CONFBY_HW or @ref PHY_CONFBY_SW + uint8_t mode; ///< set by @ref PHY_MODE_MANUAL or @ref PHY_MODE_AUTONEGO + uint8_t speed; ///< set by @ref PHY_SPEED_10 or @ref PHY_SPEED_100 + uint8_t duplex; ///< set by @ref PHY_DUPLEX_HALF @ref PHY_DUPLEX_FULL + //uint8_t power; ///< set by @ref PHY_POWER_NORM or @ref PHY_POWER_DOWN + //uint8_t link; ///< Valid only in CW_GET_PHYSTATUS. set by @ref PHY_LINK_ON or PHY_DUPLEX_OFF + }wiz_PhyConf; +#endif + +/** + * @ingroup DATA_TYPE + * It used in setting dhcp_mode of @ref wiz_NetInfo. + */ +typedef enum +{ + NETINFO_STATIC = 1, ///< Static IP configuration by manually. + NETINFO_DHCP ///< Dynamic IP configruation from a DHCP sever +}dhcp_mode; + +/** + * @ingroup DATA_TYPE + * Network Information for WIZCHIP + */ +typedef struct wiz_NetInfo_t +{ + uint8_t mac[6]; ///< Source Mac Address + uint8_t _pad[2]; ///< avoid 'non-aligned exception' in some cpu. @20201109 + uint8_t ip[4]; ///< Source IP Address + uint8_t sn[4]; ///< Subnet Mask + uint8_t gw[4]; ///< Gateway IP Address + uint8_t dns[4]; ///< DNS server IP Address + dhcp_mode dhcp; ///< 1 - Static, 2 - DHCP +}wiz_NetInfo; + +/** + * @ingroup DATA_TYPE + * Network mode + */ +typedef enum +{ +#if _WIZCHIP_ == W5500 + NM_FORCEARP = (1<<1), ///< Force to APP send whenever udp data is sent. Valid only in W5500 +#endif + NM_WAKEONLAN = (1<<5), ///< Wake On Lan + NM_PINGBLOCK = (1<<4), ///< Block ping-request + NM_PPPOE = (1<<3), ///< PPPoE mode +}netmode_type; + +/** + * @ingroup DATA_TYPE + * Used in CN_SET_TIMEOUT or CN_GET_TIMEOUT of @ref ctlwizchip() for timeout configruation. + */ +typedef struct wiz_NetTimeout_t +{ + uint8_t retry_cnt; ///< retry count + uint16_t time_100us; ///< time unit 100us +}wiz_NetTimeout; + +/** + *@brief Registers call back function for critical section of I/O functions such as + *\ref WIZCHIP_READ, @ref WIZCHIP_WRITE, @ref WIZCHIP_READ_BUF and @ref WIZCHIP_WRITE_BUF. + *@param cris_en : callback function for critical section enter. + *@param cris_ex : callback function for critical section exit. + *@todo Describe @ref WIZCHIP_CRITICAL_ENTER and @ref WIZCHIP_CRITICAL_EXIT marco or register your functions. + *@note If you do not describe or register, default functions(@ref wizchip_cris_enter & @ref wizchip_cris_exit) is called. + */ +void reg_wizchip_cris_cbfunc(void(*cris_en)(void), void(*cris_ex)(void)); + + +/** + *@brief Registers call back function for WIZCHIP select & deselect. + *@param cs_sel : callback function for WIZCHIP select + *@param cs_desel : callback fucntion for WIZCHIP deselect + *@todo Describe @ref wizchip_cs_select and @ref wizchip_cs_deselect function or register your functions. + *@note If you do not describe or register, null function is called. + */ +void reg_wizchip_cs_cbfunc(void(*cs_sel)(void), void(*cs_desel)(void)); + +/** + *@brief Registers call back function for bus interface. + *@param bus_rb : callback function to read byte data using system bus + *@param bus_wb : callback function to write byte data using system bus + *@todo Describe @ref wizchip_bus_readbyte and @ref wizchip_bus_writebyte function + *or register your functions. + *@note If you do not describe or register, null function is called. + */ +//M20150601 : For integrating with W5300 +//void reg_wizchip_bus_cbfunc(uint8_t (*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, uint8_t wb)); +void reg_wizchip_bus_cbfunc(iodata_t (*bus_rb)(uint32_t addr), void (*bus_wb)(uint32_t addr, iodata_t wb)); + +/** + *@brief Registers call back function for SPI interface. + *@param spi_rb : callback function to read byte using SPI + *@param spi_wb : callback function to write byte using SPI + *@todo Describe \ref wizchip_spi_readbyte and \ref wizchip_spi_writebyte function + *or register your functions. + *@note If you do not describe or register, null function is called. + */ +void reg_wizchip_spi_cbfunc(uint8_t (*spi_rb)(void), void (*spi_wb)(uint8_t wb)); + +/** + *@brief Registers call back function for SPI interface. + *@param spi_rb : callback function to burst read using SPI + *@param spi_wb : callback function to burst write using SPI + *@todo Describe \ref wizchip_spi_readbyte and \ref wizchip_spi_writebyte function + *or register your functions. + *@note If you do not describe or register, null function is called. + */ +void reg_wizchip_spiburst_cbfunc(void (*spi_rb)(uint8_t* pBuf, uint16_t len), void (*spi_wb)(uint8_t* pBuf, uint16_t len)); + +/** + * @ingroup extra_functions + * @brief Controls to the WIZCHIP. + * @details Resets WIZCHIP & internal PHY, Configures PHY mode, Monitor PHY(Link,Speed,Half/Full/Auto), + * controls interrupt & mask and so on. + * @param cwtype : Decides to the control type + * @param arg : arg type is dependent on cwtype. + * @return 0 : Success \n + * -1 : Fail because of invalid \ref ctlwizchip_type or unsupported \ref ctlwizchip_type in WIZCHIP + */ +int8_t ctlwizchip(ctlwizchip_type cwtype, void* arg); + +/** + * @ingroup extra_functions + * @brief Controls to network. + * @details Controls to network environment, mode, timeout and so on. + * @param cntype : Input. Decides to the control type + * @param arg : Inout. arg type is dependent on cntype. + * @return -1 : Fail because of invalid \ref ctlnetwork_type or unsupported \ref ctlnetwork_type in WIZCHIP \n + * 0 : Success + */ +int8_t ctlnetwork(ctlnetwork_type cntype, void* arg); + + +/* + * The following functions are implemented for internal use. + * but You can call these functions for code size reduction instead of ctlwizchip() and ctlnetwork(). + */ + +/** + * @ingroup extra_functions + * @brief Reset WIZCHIP by softly. + */ +void wizchip_sw_reset(void); + +/** + * @ingroup extra_functions + * @brief Initializes WIZCHIP with socket buffer size + * @param txsize Socket tx buffer sizes. If null, initialized the default size 2KB. + * @param rxsize Socket rx buffer sizes. If null, initialized the default size 2KB. + * @return 0 : succcess \n + * -1 : fail. Invalid buffer size + */ +int8_t wizchip_init(uint8_t* txsize, uint8_t* rxsize); + +/** + * @ingroup extra_functions + * @brief Clear Interrupt of WIZCHIP. + * @param intr : @ref intr_kind value operated OR. It can type-cast to uint16_t. + */ +void wizchip_clrinterrupt(intr_kind intr); + +/** + * @ingroup extra_functions + * @brief Get Interrupt of WIZCHIP. + * @return @ref intr_kind value operated OR. It can type-cast to uint16_t. + */ +intr_kind wizchip_getinterrupt(void); + +/** + * @ingroup extra_functions + * @brief Mask or Unmask Interrupt of WIZCHIP. + * @param intr : @ref intr_kind value operated OR. It can type-cast to uint16_t. + */ +void wizchip_setinterruptmask(intr_kind intr); + +/** + * @ingroup extra_functions + * @brief Get Interrupt mask of WIZCHIP. + * @return : The operated OR vaule of @ref intr_kind. It can type-cast to uint16_t. + */ +intr_kind wizchip_getinterruptmask(void); + +//todo +#if _WIZCHIP_ > W5100 + int8_t wizphy_getphylink(void); ///< get the link status of phy in WIZCHIP. No use in W5100 + int8_t wizphy_getphypmode(void); ///< get the power mode of PHY in WIZCHIP. No use in W5100 +#endif + +#if _WIZCHIP_ == W5100S || _WIZCHIP_ == W5500 + void wizphy_reset(void); ///< Reset phy. Vailid only in W5500 +/** + * @ingroup extra_functions + * @brief Set the phy information for WIZCHIP without power mode + * @param phyconf : @ref wiz_PhyConf + */ + void wizphy_setphyconf(wiz_PhyConf* phyconf); + /** + * @ingroup extra_functions + * @brief Get phy configuration information. + * @param phyconf : @ref wiz_PhyConf + */ + void wizphy_getphyconf(wiz_PhyConf* phyconf); + /** + * @ingroup extra_functions + * @brief Get phy status. + * @param phyconf : @ref wiz_PhyConf + */ + void wizphy_getphystat(wiz_PhyConf* phyconf); + /** + * @ingroup extra_functions + * @brief set the power mode of phy inside WIZCHIP. Refer to @ref PHYCFGR in W5500, @ref PHYSTATUS in W5200 + * @param pmode Settig value of power down mode. + */ + int8_t wizphy_setphypmode(uint8_t pmode); +#endif + +/** +* @ingroup extra_functions + * @brief Set the network information for WIZCHIP + * @param pnetinfo : @ref wizNetInfo + */ +void wizchip_setnetinfo(wiz_NetInfo* pnetinfo); + +/** + * @ingroup extra_functions + * @brief Get the network information for WIZCHIP + * @param pnetinfo : @ref wizNetInfo + */ +void wizchip_getnetinfo(wiz_NetInfo* pnetinfo); + +/** + * @ingroup extra_functions + * @brief Set the network mode such WOL, PPPoE, Ping Block, and etc. + * @param pnetinfo Value of network mode. Refer to @ref netmode_type. + */ +int8_t wizchip_setnetmode(netmode_type netmode); + +/** + * @ingroup extra_functions + * @brief Get the network mode such WOL, PPPoE, Ping Block, and etc. + * @return Value of network mode. Refer to @ref netmode_type. + */ +netmode_type wizchip_getnetmode(void); + +/** + * @ingroup extra_functions + * @brief Set retry time value(@ref _RTR_) and retry count(@ref _RCR_). + * @details @ref _RTR_ configures the retransmission timeout period and @ref _RCR_ configures the number of time of retransmission. + * @param nettime @ref _RTR_ value and @ref _RCR_ value. Refer to @ref wiz_NetTimeout. + */ +void wizchip_settimeout(wiz_NetTimeout* nettime); + +/** + * @ingroup extra_functions + * @brief Get retry time value(@ref _RTR_) and retry count(@ref _RCR_). + * @details @ref _RTR_ configures the retransmission timeout period and @ref _RCR_ configures the number of time of retransmission. + * @param nettime @ref _RTR_ value and @ref _RCR_ value. Refer to @ref wiz_NetTimeout. + */ +void wizchip_gettimeout(wiz_NetTimeout* nettime); +#ifdef __cplusplus + } +#endif + +#endif // _WIZCHIP_CONF_H_ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.c new file mode 100644 index 000000000..2568ccdc7 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.c @@ -0,0 +1,931 @@ +//***************************************************************************** +// +//! \file wizchip_socket.c +//! \brief SOCKET APIs Implements file. +//! \details SOCKET APIs like as Berkeley Socket APIs. +//! \version 1.0.3 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2014/05/01> V1.0.3. Refer to M20140501 +//! 1. Implicit type casting -> Explicit type casting. +//! 2. replace 0x01 with PACK_REMAINED in recvfrom() +//! 3. Validation a destination ip in connect() & sendto(): +//! It occurs a fatal error on converting unint32 address if uint8* addr parameter is not aligned by 4byte address. +//! Copy 4 byte addr value into temporary uint32 variable and then compares it. +//! <2013/12/20> V1.0.2 Refer to M20131220 +//! Remove Warning. +//! <2013/11/04> V1.0.1 2nd Release. Refer to "20131104". +//! In sendto(), Add to clear timeout interrupt status (Sn_IR_TIMEOUT) +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** +#include "wizchip_socket.h" + +//M20150401 : Typing Error +//#define SOCK_ANY_PORT_NUM 0xC000; +#define SOCK_ANY_PORT_NUM 0xC000 + +static uint16_t sock_any_port = SOCK_ANY_PORT_NUM; +static uint16_t sock_io_mode = 0; +static uint16_t sock_is_sending = 0; + +static uint16_t sock_remained_size[_WIZCHIP_SOCK_NUM_] = {0,0,}; + +//M20150601 : For extern decleation +//static uint8_t sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,}; +uint8_t sock_pack_info[_WIZCHIP_SOCK_NUM_] = {0,}; +// + +#if _WIZCHIP_ == 5200 + static uint16_t sock_next_rd[_WIZCHIP_SOCK_NUM_] ={0,}; +#endif + +//A20150601 : For integrating with W5300 +#if _WIZCHIP_ == 5300 + uint8_t sock_remained_byte[_WIZCHIP_SOCK_NUM_] = {0,}; // set by wiz_recv_data() +#endif + + +#define CHECK_SOCKNUM() \ + do{ \ + if(sn > _WIZCHIP_SOCK_NUM_) return SOCKERR_SOCKNUM; \ + }while(0); \ + +#define CHECK_SOCKMODE(mode) \ + do{ \ + if((getSn_MR(sn) & 0x0F) != mode) return SOCKERR_SOCKMODE; \ + }while(0); \ + +#define CHECK_SOCKINIT() \ + do{ \ + if((getSn_SR(sn) != SOCK_INIT)) return SOCKERR_SOCKINIT; \ + }while(0); \ + +#define CHECK_SOCKDATA() \ + do{ \ + if(len == 0) return SOCKERR_DATALEN; \ + }while(0); \ + + + +int8_t wizchip_socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag) +{ + CHECK_SOCKNUM(); + switch(protocol) + { + case Sn_MR_TCP : + { + //M20150601 : Fixed the warning - taddr will never be NULL + /* + uint8_t taddr[4]; + getSIPR(taddr); + */ + uint32_t taddr; + getSIPR((uint8_t*)&taddr); + if(taddr == 0) return SOCKERR_SOCKINIT; + break; + } + case Sn_MR_UDP : + case Sn_MR_MACRAW : + case Sn_MR_IPRAW : + break; + #if ( _WIZCHIP_ < 5200 ) + case Sn_MR_PPPoE : + break; + #endif + default : + return SOCKERR_SOCKMODE; + } + //M20150601 : For SF_TCP_ALIGN & W5300 + //if((flag & 0x06) != 0) return SOCKERR_SOCKFLAG; + if((flag & 0x04) != 0) return SOCKERR_SOCKFLAG; +#if _WIZCHIP_ == 5200 + if(flag & 0x10) return SOCKERR_SOCKFLAG; +#endif + + if(flag != 0) + { + switch(protocol) + { + case Sn_MR_TCP: + //M20150601 : For SF_TCP_ALIGN & W5300 + #if _WIZCHIP_ == 5300 + if((flag & (SF_TCP_NODELAY|SF_IO_NONBLOCK|SF_TCP_ALIGN))==0) return SOCKERR_SOCKFLAG; + #else + if((flag & (SF_TCP_NODELAY|SF_IO_NONBLOCK))==0) return SOCKERR_SOCKFLAG; + #endif + + break; + case Sn_MR_UDP: + if(flag & SF_IGMP_VER2) + { + if((flag & SF_MULTI_ENABLE)==0) return SOCKERR_SOCKFLAG; + } + #if _WIZCHIP_ == 5500 + if(flag & SF_UNI_BLOCK) + { + if((flag & SF_MULTI_ENABLE) == 0) return SOCKERR_SOCKFLAG; + } + #endif + break; + default: + break; + } + } + wizchip_close(sn); + //M20150601 + #if _WIZCHIP_ == 5300 + setSn_MR(sn, ((uint16_t)(protocol | (flag & 0xF0))) | (((uint16_t)(flag & 0x02)) << 7) ); + #else + setSn_MR(sn, (protocol | (flag & 0xF0))); + #endif + if(!port) + { + port = sock_any_port++; + if(sock_any_port == 0xFFF0) sock_any_port = SOCK_ANY_PORT_NUM; + } + setSn_PORT(sn,port); + setSn_CR(sn,Sn_CR_OPEN); + while(getSn_CR(sn)); + //A20150401 : For release the previous sock_io_mode + sock_io_mode &= ~(1 < sn + //if( ((getSn_MR(s)& 0x0F) == Sn_MR_TCP) && (getSn_TX_FSR(s) != getSn_TxMAX(s)) ) + if( ((getSn_MR(sn)& 0x0F) == Sn_MR_TCP) && (getSn_TX_FSR(sn) != getSn_TxMAX(sn)) ) + { + uint8_t destip[4] = {0, 0, 0, 1}; + // TODO + // You can wait for completing to sending data; + // wait about 1 second; + // if you have completed to send data, skip the code of erratum 1 + // ex> wait_1s(); + // if (getSn_TX_FSR(s) == getSn_TxMAX(s)) continue; + // + //M20160503 : The socket() of close() calls close() itself again. It occures a infinite loop - close()->socket()->close()->socket()-> ~ + //socket(s,Sn_MR_UDP,0x3000,0); + //sendto(s,destip,1,destip,0x3000); // send the dummy data to an unknown destination(0.0.0.1). + setSn_MR(sn,Sn_MR_UDP); + setSn_PORTR(sn, 0x3000); + setSn_CR(sn,Sn_CR_OPEN); + while(getSn_CR(sn) != 0); + while(getSn_SR(sn) != SOCK_UDP); + sendto(sn,destip,1,destip,0x3000); // send the dummy data to an unknown destination(0.0.0.1). + }; +#endif + setSn_CR(sn,Sn_CR_CLOSE); + /* wait to process the command... */ + while( getSn_CR(sn) ); + /* clear all interrupt of the socket. */ + setSn_IR(sn, 0xFF); + //A20150401 : Release the sock_io_mode of socket n. + sock_io_mode &= ~(1< freesize) len = freesize; // check size not to exceed MAX size. + while(1) + { + freesize = getSn_TX_FSR(sn); + tmp = getSn_SR(sn); + if ((tmp != SOCK_ESTABLISHED) && (tmp != SOCK_CLOSE_WAIT)) + { + wizchip_close(sn); + return SOCKERR_SOCKSTATUS; + } + if( (sock_io_mode & (1< freesize) ) return SOCK_BUSY; + if(len <= freesize) break; + } + wiz_send_data(sn, buf, len); + #if _WIZCHIP_ == 5200 + sock_next_rd[sn] = getSn_TX_RD(sn) + len; + #endif + + #if _WIZCHIP_ == 5300 + setSn_TX_WRSR(sn,len); + #endif + + setSn_CR(sn,Sn_CR_SEND); + /* wait to process the command... */ + while(getSn_CR(sn)); + sock_is_sending |= (1 << sn); + //M20150409 : Explicit Type Casting + //return len; + return (int32_t)len; +} + + +int32_t wizchip_recv(uint8_t sn, uint8_t * buf, uint16_t len) +{ + uint8_t tmp = 0; + uint16_t recvsize = 0; +//A20150601 : For integarating with W5300 +#if _WIZCHIP_ == 5300 + uint8_t head[2]; + uint16_t mr; +#endif +// + CHECK_SOCKNUM(); + CHECK_SOCKMODE(Sn_MR_TCP); + CHECK_SOCKDATA(); + + recvsize = getSn_RxMAX(sn); + if(recvsize < len) len = recvsize; + +//A20150601 : For Integrating with W5300 +#if _WIZCHIP_ == 5300 + //sock_pack_info[sn] = PACK_COMPLETED; // for clear + if(sock_remained_size[sn] == 0) + { +#endif +// + while(1) + { + recvsize = getSn_RX_RSR(sn); + tmp = getSn_SR(sn); + if (tmp != SOCK_ESTABLISHED) + { + if(tmp == SOCK_CLOSE_WAIT) + { + if(recvsize != 0) break; + else if(getSn_TX_FSR(sn) == getSn_TxMAX(sn)) + { + wizchip_close(sn); + return SOCKERR_SOCKSTATUS; + } + } + else + { + wizchip_close(sn); + return SOCKERR_SOCKSTATUS; + } + } + if((sock_io_mode & (1< sock_remained_size[sn]) len = sock_remained_size[sn]; + recvsize = len; + if(sock_pack_info[sn] & PACK_FIFOBYTE) + { + *buf = sock_remained_byte[sn]; + buf++; + sock_pack_info[sn] &= ~(PACK_FIFOBYTE); + recvsize -= 1; + sock_remained_size[sn] -= 1; + } + if(recvsize != 0) + { + wiz_recv_data(sn, buf, recvsize); + setSn_CR(sn,Sn_CR_RECV); + while(getSn_CR(sn)); + } + sock_remained_size[sn] -= recvsize; + if(sock_remained_size[sn] != 0) + { + sock_pack_info[sn] |= PACK_REMAINED; + if(recvsize & 0x1) sock_pack_info[sn] |= PACK_FIFOBYTE; + } + else sock_pack_info[sn] = PACK_COMPLETED; + if(getSn_MR(sn) & Sn_MR_ALIGN) sock_remained_size[sn] = 0; + //len = recvsize; +#else + if(recvsize < len) len = recvsize; + wiz_recv_data(sn, buf, len); + setSn_CR(sn,Sn_CR_RECV); + while(getSn_CR(sn)); +#endif + + //M20150409 : Explicit Type Casting + //return len; + return (int32_t)len; +} + +int32_t wizchip_sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port) +{ + uint8_t tmp = 0; + uint16_t freesize = 0; + uint32_t taddr; + + CHECK_SOCKNUM(); + switch(getSn_MR(sn) & 0x0F) + { + case Sn_MR_UDP: + case Sn_MR_MACRAW: +// break; +// #if ( _WIZCHIP_ < 5200 ) + case Sn_MR_IPRAW: + break; +// #endif + default: + return SOCKERR_SOCKMODE; + } + CHECK_SOCKDATA(); + //M20140501 : For avoiding fatal error on memory align mismatched + //if(*((uint32_t*)addr) == 0) return SOCKERR_IPINVALID; + //{ + //uint32_t taddr; + taddr = ((uint32_t)addr[0]) & 0x000000FF; + taddr = (taddr << 8) + ((uint32_t)addr[1] & 0x000000FF); + taddr = (taddr << 8) + ((uint32_t)addr[2] & 0x000000FF); + taddr = (taddr << 8) + ((uint32_t)addr[3] & 0x000000FF); + //} + // + //if(*((uint32_t*)addr) == 0) return SOCKERR_IPINVALID; + if((taddr == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_IPINVALID; + if((port == 0) && ((getSn_MR(sn)&Sn_MR_MACRAW) != Sn_MR_MACRAW)) return SOCKERR_PORTZERO; + tmp = getSn_SR(sn); +//#if ( _WIZCHIP_ < 5200 ) + if((tmp != SOCK_MACRAW) && (tmp != SOCK_UDP) && (tmp != SOCK_IPRAW)) return SOCKERR_SOCKSTATUS; +//#else +// if(tmp != SOCK_MACRAW && tmp != SOCK_UDP) return SOCKERR_SOCKSTATUS; +//#endif + + setSn_DIPR(sn,addr); + setSn_DPORT(sn,port); + freesize = getSn_TxMAX(sn); + if (len > freesize) len = freesize; // check size not to exceed MAX size. + while(1) + { + freesize = getSn_TX_FSR(sn); + if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED; + if( (sock_io_mode & (1< freesize) ) return SOCK_BUSY; + if(len <= freesize) break; + }; + wiz_send_data(sn, buf, len); + + #if _WIZCHIP_ < 5500 //M20150401 : for WIZCHIP Errata #4, #5 (ARP errata) + getSIPR((uint8_t*)&taddr); + if(taddr == 0) + { + getSUBR((uint8_t*)&taddr); + setSUBR((uint8_t*)"\x00\x00\x00\x00"); + } + else taddr = 0; + #endif + +//A20150601 : For W5300 +#if _WIZCHIP_ == 5300 + setSn_TX_WRSR(sn, len); +#endif +// + setSn_CR(sn,Sn_CR_SEND); + /* wait to process the command... */ + while(getSn_CR(sn)); + while(1) + { + tmp = getSn_IR(sn); + if(tmp & Sn_IR_SENDOK) + { + setSn_IR(sn, Sn_IR_SENDOK); + break; + } + //M:20131104 + //else if(tmp & Sn_IR_TIMEOUT) return SOCKERR_TIMEOUT; + else if(tmp & Sn_IR_TIMEOUT) + { + setSn_IR(sn, Sn_IR_TIMEOUT); + //M20150409 : Fixed the lost of sign bits by type casting. + //len = (uint16_t)SOCKERR_TIMEOUT; + //break; + #if _WIZCHIP_ < 5500 //M20150401 : for WIZCHIP Errata #4, #5 (ARP errata) + if(taddr) setSUBR((uint8_t*)&taddr); + #endif + return SOCKERR_TIMEOUT; + } + //////////// + } + #if _WIZCHIP_ < 5500 //M20150401 : for WIZCHIP Errata #4, #5 (ARP errata) + if(taddr) setSUBR((uint8_t*)&taddr); + #endif + //M20150409 : Explicit Type Casting + //return len; + return (int32_t)len; +} + + + +int32_t wizchip_recvfrom(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port) +{ +//M20150601 : For W5300 +#if _WIZCHIP_ == 5300 + uint16_t mr; + uint16_t mr1; +#else + uint8_t mr; +#endif +// + uint8_t head[8]; + uint16_t pack_len=0; + + CHECK_SOCKNUM(); + //CHECK_SOCKMODE(Sn_MR_UDP); +//A20150601 +#if _WIZCHIP_ == 5300 + mr1 = getMR(); +#endif + + switch((mr=getSn_MR(sn)) & 0x0F) + { + case Sn_MR_UDP: + case Sn_MR_IPRAW: + case Sn_MR_MACRAW: + break; + #if ( _WIZCHIP_ < 5200 ) + case Sn_MR_PPPoE: + break; + #endif + default: + return SOCKERR_SOCKMODE; + } + CHECK_SOCKDATA(); + if(sock_remained_size[sn] == 0) + { + while(1) + { + pack_len = getSn_RX_RSR(sn); + if(getSn_SR(sn) == SOCK_CLOSED) return SOCKERR_SOCKCLOSED; + if( (sock_io_mode & (1< 1514) + { + wizchip_close(sn); + return SOCKFATAL_PACKLEN; + } + sock_pack_info[sn] = PACK_FIRST; + } + if(len < sock_remained_size[sn]) pack_len = len; + else pack_len = sock_remained_size[sn]; + wiz_recv_data(sn,buf,pack_len); + break; + //#if ( _WIZCHIP_ < 5200 ) + case Sn_MR_IPRAW: + if(sock_remained_size[sn] == 0) + { + wiz_recv_data(sn, head, 6); + setSn_CR(sn,Sn_CR_RECV); + while(getSn_CR(sn)); + addr[0] = head[0]; + addr[1] = head[1]; + addr[2] = head[2]; + addr[3] = head[3]; + sock_remained_size[sn] = head[4]; + //M20150401 : For Typing Error + //sock_remaiend_size[sn] = (sock_remained_size[sn] << 8) + head[5]; + sock_remained_size[sn] = (sock_remained_size[sn] << 8) + head[5]; + sock_pack_info[sn] = PACK_FIRST; + } + // + // Need to packet length check + // + if(len < sock_remained_size[sn]) pack_len = len; + else pack_len = sock_remained_size[sn]; + wiz_recv_data(sn, buf, pack_len); // data copy. + break; + //#endif + default: + wiz_recv_ignore(sn, pack_len); // data copy. + sock_remained_size[sn] = pack_len; + break; + } + setSn_CR(sn,Sn_CR_RECV); + /* wait to process the command... */ + while(getSn_CR(sn)) ; + sock_remained_size[sn] -= pack_len; + //M20150601 : + //if(sock_remained_size[sn] != 0) sock_pack_info[sn] |= 0x01; + if(sock_remained_size[sn] != 0) + { + sock_pack_info[sn] |= PACK_REMAINED; + #if _WIZCHIP_ == 5300 + if(pack_len & 0x01) sock_pack_info[sn] |= PACK_FIFOBYTE; + #endif + } + else sock_pack_info[sn] = PACK_COMPLETED; +#if _WIZCHIP_ == 5300 + pack_len = len; +#endif + // + //M20150409 : Explicit Type Casting + //return pack_len; + return (int32_t)pack_len; +} + + +int8_t ctlsocket(uint8_t sn, ctlsock_type cstype, void* arg) +{ + uint8_t tmp = 0; + CHECK_SOCKNUM(); + switch(cstype) + { + case CS_SET_IOMODE: + tmp = *((uint8_t*)arg); + if(tmp == SOCK_IO_NONBLOCK) sock_io_mode |= (1< explict type casting + //*((uint8_t*)arg) = (sock_io_mode >> sn) & 0x0001; + *((uint8_t*)arg) = (uint8_t)((sock_io_mode >> sn) & 0x0001); + // + break; + case CS_GET_MAXTXBUF: + *((uint16_t*)arg) = getSn_TxMAX(sn); + break; + case CS_GET_MAXRXBUF: + *((uint16_t*)arg) = getSn_RxMAX(sn); + break; + case CS_CLR_INTERRUPT: + if( (*(uint8_t*)arg) > SIK_ALL) return SOCKERR_ARG; + setSn_IR(sn,*(uint8_t*)arg); + break; + case CS_GET_INTERRUPT: + *((uint8_t*)arg) = getSn_IR(sn); + break; + #if _WIZCHIP_ != 5100 + case CS_SET_INTMASK: + if( (*(uint8_t*)arg) > SIK_ALL) return SOCKERR_ARG; + setSn_IMR(sn,*(uint8_t*)arg); + break; + case CS_GET_INTMASK: + *((uint8_t*)arg) = getSn_IMR(sn); + break; + #endif + default: + return SOCKERR_ARG; + } + return SOCK_OK; +} + +int8_t wizchip_setsockopt(uint8_t sn, sockopt_type sotype, void* arg) +{ + // M20131220 : Remove warning + //uint8_t tmp; + CHECK_SOCKNUM(); + switch(sotype) + { + case SO_TTL: + setSn_TTL(sn,*(uint8_t*)arg); + break; + case SO_TOS: + setSn_TOS(sn,*(uint8_t*)arg); + break; + case SO_MSS: + setSn_MSSR(sn,*(uint16_t*)arg); + break; + case SO_DESTIP: + setSn_DIPR(sn, (uint8_t*)arg); + break; + case SO_DESTPORT: + setSn_DPORT(sn, *(uint16_t*)arg); + break; +#if _WIZCHIP_ != 5100 + case SO_KEEPALIVESEND: + CHECK_SOCKMODE(Sn_MR_TCP); + #if _WIZCHIP_ > 5200 + if(getSn_KPALVTR(sn) != 0) return SOCKERR_SOCKOPT; + #endif + setSn_CR(sn,Sn_CR_SEND_KEEP); + while(getSn_CR(sn) != 0) + { + // M20131220 + //if ((tmp = getSn_IR(sn)) & Sn_IR_TIMEOUT) + if (getSn_IR(sn) & Sn_IR_TIMEOUT) + { + setSn_IR(sn, Sn_IR_TIMEOUT); + return SOCKERR_TIMEOUT; + } + } + break; + #if !( (_WIZCHIP_ == 5100) || (_WIZCHIP_ == 5200) ) + case SO_KEEPALIVEAUTO: + CHECK_SOCKMODE(Sn_MR_TCP); + setSn_KPALVTR(sn,*(uint8_t*)arg); + break; + #endif +#endif + default: + return SOCKERR_ARG; + } + return SOCK_OK; +} + +int8_t wizchip_getsockopt(uint8_t sn, sockopt_type sotype, void* arg) +{ + CHECK_SOCKNUM(); + switch(sotype) + { + case SO_FLAG: + *(uint8_t*)arg = getSn_MR(sn) & 0xF0; + break; + case SO_TTL: + *(uint8_t*) arg = getSn_TTL(sn); + break; + case SO_TOS: + *(uint8_t*) arg = getSn_TOS(sn); + break; + case SO_MSS: + *(uint16_t*) arg = getSn_MSSR(sn); + break; + case SO_DESTIP: + getSn_DIPR(sn, (uint8_t*)arg); + break; + case SO_DESTPORT: + *(uint16_t*) arg = getSn_DPORT(sn); + break; + #if _WIZCHIP_ > 5200 + case SO_KEEPALIVEAUTO: + CHECK_SOCKMODE(Sn_MR_TCP); + *(uint16_t*) arg = getSn_KPALVTR(sn); + break; + #endif + case SO_SENDBUF: + *(uint16_t*) arg = getSn_TX_FSR(sn); + break; + case SO_RECVBUF: + *(uint16_t*) arg = getSn_RX_RSR(sn); + break; + case SO_STATUS: + *(uint8_t*) arg = getSn_SR(sn); + break; + case SO_REMAINSIZE: + if(getSn_MR(sn) & Sn_MR_TCP) + *(uint16_t*)arg = getSn_RX_RSR(sn); + else + *(uint16_t*)arg = sock_remained_size[sn]; + break; + case SO_PACKINFO: + //CHECK_SOCKMODE(Sn_MR_TCP); +#if _WIZCHIP_ != 5300 + if((getSn_MR(sn) == Sn_MR_TCP)) + return SOCKERR_SOCKMODE; +#endif + *(uint8_t*)arg = sock_pack_info[sn]; + break; + default: + return SOCKERR_SOCKOPT; + } + return SOCK_OK; +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.h new file mode 100644 index 000000000..7d53aa58a --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Ethernet/wizchip_socket.h @@ -0,0 +1,489 @@ +//***************************************************************************** +// +//! \file wizchip_socket.h +//! \brief SOCKET APIs Header file. +//! \details SOCKET APIs like as berkeley socket api. +//! \version 1.0.2 +//! \date 2013/10/21 +//! \par Revision history +//! <2015/02/05> Notice +//! The version history is not updated after this point. +//! Download the latest version directly from GitHub. Please visit the our GitHub repository for ioLibrary. +//! >> https://github.com/Wiznet/ioLibrary_Driver +//! <2014/05/01> V1.0.2. Refer to M20140501 +//! 1. Modify the comment : SO_REMAINED -> PACK_REMAINED +//! 2. Add the comment as zero byte udp data reception in getsockopt(). +//! <2013/10/21> 1st Release +//! \author MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** +/** + * @defgroup WIZnet_socket_APIs 1. WIZnet socket APIs + * @brief WIZnet socket APIs are based on Berkeley socket APIs, thus it has much similar name and interface. + * But there is a little bit of difference. + * @details + * Comparison between WIZnet and Berkeley SOCKET APIs + * + * + * + * + * + * + * + * + * + * + * + * + *
API WIZnet Berkeley
socket() O O
bind() X O
listen() O O
connect() O O
accept() X O
recv() O O
send() O O
recvfrom() O O
sendto() O O
closesocket() O
close() & disconnect()
O
+ * There are @b bind() and @b accept() functions in @b Berkeley SOCKET API but, + * not in @b WIZnet SOCKET API. Because socket() of WIZnet is not only creating a SOCKET but also binding a local port number, + * and listen() of WIZnet is not only listening to connection request from client but also accepting the connection request. \n + * When you program "TCP SERVER" with Berkeley SOCKET API, you can use only one listen port. + * When the listen SOCKET accepts a connection request from a client, it keeps listening. + * After accepting the connection request, a new SOCKET is created and the new SOCKET is used in communication with the client. \n + * Following figure shows network flow diagram by Berkeley SOCKET API. + * @image html Berkeley_SOCKET.jpg "" + * But, When you program "TCP SERVER" with WIZnet SOCKET API, you can use as many as 8 listen SOCKET with same port number. \n + * Because there's no accept() in WIZnet SOCKET APIs, when the listen SOCKET accepts a connection request from a client, + * it is changed in order to communicate with the client. + * And the changed SOCKET is not listening any more and is dedicated for communicating with the client. \n + * If there're many listen SOCKET with same listen port number and a client requests a connection, + * the SOCKET which has the smallest SOCKET number accepts the request and is changed as communication SOCKET. \n + * Following figure shows network flow diagram by WIZnet SOCKET API. + * @image html WIZnet_SOCKET.jpg "" + */ +#ifndef _WIZCHIP_SOCKET_H_ +#define _WIZCHIP_SOCKET_H_ +#ifdef __cplusplus + extern "C" { +#endif + +#include "wizchip_conf.h" + +#define SOCKET uint8_t ///< SOCKET type define for legacy driver + +#define SOCK_OK 1 ///< Result is OK about socket process. +#define SOCK_BUSY 0 ///< Socket is busy on processing the operation. Valid only Non-block IO Mode. +#define SOCK_FATAL -1000 ///< Result is fatal error about socket process. + +#define SOCK_ERROR 0 +#define SOCKERR_SOCKNUM (SOCK_ERROR - 1) ///< Invalid socket number +#define SOCKERR_SOCKOPT (SOCK_ERROR - 2) ///< Invalid socket option +#define SOCKERR_SOCKINIT (SOCK_ERROR - 3) ///< Socket is not initialized or SIPR is Zero IP address when Sn_MR_TCP +#define SOCKERR_SOCKCLOSED (SOCK_ERROR - 4) ///< Socket unexpectedly closed. +#define SOCKERR_SOCKMODE (SOCK_ERROR - 5) ///< Invalid socket mode for socket operation. +#define SOCKERR_SOCKFLAG (SOCK_ERROR - 6) ///< Invalid socket flag +#define SOCKERR_SOCKSTATUS (SOCK_ERROR - 7) ///< Invalid socket status for socket operation. +#define SOCKERR_ARG (SOCK_ERROR - 10) ///< Invalid argument. +#define SOCKERR_PORTZERO (SOCK_ERROR - 11) ///< Port number is zero +#define SOCKERR_IPINVALID (SOCK_ERROR - 12) ///< Invalid IP address +#define SOCKERR_TIMEOUT (SOCK_ERROR - 13) ///< Timeout occurred +#define SOCKERR_DATALEN (SOCK_ERROR - 14) ///< Data length is zero or greater than buffer max size. +#define SOCKERR_BUFFER (SOCK_ERROR - 15) ///< Socket buffer is not enough for data communication. + +#define SOCKFATAL_PACKLEN (SOCK_FATAL - 1) ///< Invalid packet length. Fatal Error. + +/* + * SOCKET FLAG + */ +#define SF_ETHER_OWN (Sn_MR_MFEN) ///< In @ref Sn_MR_MACRAW, Receive only the packet as broadcast, multicast and own packet +#define SF_IGMP_VER2 (Sn_MR_MC) ///< In @ref Sn_MR_UDP with \ref SF_MULTI_ENABLE, Select IGMP version 2. +#define SF_TCP_NODELAY (Sn_MR_ND) ///< In @ref Sn_MR_TCP, Use to nodelayed ack. +#define SF_MULTI_ENABLE (Sn_MR_MULTI) ///< In @ref Sn_MR_UDP, Enable multicast mode. + +#if _WIZCHIP_ == 5500 + #define SF_BROAD_BLOCK (Sn_MR_BCASTB) ///< In @ref Sn_MR_UDP or @ref Sn_MR_MACRAW, Block broadcast packet. Valid only in W5500 + #define SF_MULTI_BLOCK (Sn_MR_MMB) ///< In @ref Sn_MR_MACRAW, Block multicast packet. Valid only in W5500 + #define SF_IPv6_BLOCK (Sn_MR_MIP6B) ///< In @ref Sn_MR_MACRAW, Block IPv6 packet. Valid only in W5500 + #define SF_UNI_BLOCK (Sn_MR_UCASTB) ///< In @ref Sn_MR_UDP with \ref SF_MULTI_ENABLE. Valid only in W5500 +#endif + +//A201505 : For W5300 +#if _WIZCHIP_ == 5300 + #define SF_TCP_ALIGN 0x02 ///< Valid only \ref Sn_MR_TCP and W5300, refer to \ref Sn_MR_ALIGN +#endif + +#define SF_IO_NONBLOCK 0x01 ///< Socket nonblock io mode. It used parameter in \ref socket(). + +/* + * UDP & MACRAW Packet Infomation + */ +#define PACK_FIRST 0x80 ///< In Non-TCP packet, It indicates to start receiving a packet. (When W5300, This flag can be applied) +#define PACK_REMAINED 0x01 ///< In Non-TCP packet, It indicates to remain a packet to be received. (When W5300, This flag can be applied) +#define PACK_COMPLETED 0x00 ///< In Non-TCP packet, It indicates to complete to receive a packet. (When W5300, This flag can be applied) +//A20150601 : For Integrating with W5300 +#define PACK_FIFOBYTE 0x02 ///< Valid only W5300, It indicate to have read already the Sn_RX_FIFOR. +// + +/** + * @ingroup WIZnet_socket_APIs + * @brief Open a socket. + * @details Initializes the socket with 'sn' passed as parameter and open. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param protocol Protocol type to operate such as TCP, UDP and MACRAW. + * @param port Port number to be bined. + * @param flag Socket flags as \ref SF_ETHER_OWN, \ref SF_IGMP_VER2, \ref SF_TCP_NODELAY, \ref SF_MULTI_ENABLE, \ref SF_IO_NONBLOCK and so on.\n + * Valid flags only in W5500 : @ref SF_BROAD_BLOCK, @ref SF_MULTI_BLOCK, @ref SF_IPv6_BLOCK, and @ref SF_UNI_BLOCK. + * @sa Sn_MR + * + * @return @b Success : The socket number @b 'sn' passed as parameter\n + * @b Fail :\n @ref SOCKERR_SOCKNUM - Invalid socket number\n + * @ref SOCKERR_SOCKMODE - Not support socket mode as TCP, UDP, and so on. \n + * @ref SOCKERR_SOCKFLAG - Invaild socket flag. + */ +int8_t wizchip_socket(uint8_t sn, uint8_t protocol, uint16_t port, uint8_t flag); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Close a socket. + * @details It closes the socket with @b'sn' passed as parameter. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * + * @return @b Success : @ref SOCK_OK \n + * @b Fail : @ref SOCKERR_SOCKNUM - Invalid socket number + */ +int8_t wizchip_close(uint8_t sn); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Listen to a connection request from a client. + * @details It is listening to a connection request from a client. + * If connection request is accepted successfully, the connection is established. Socket sn is used in passive(server) mode. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @return @b Success : @ref SOCK_OK \n + * @b Fail :\n @ref SOCKERR_SOCKINIT - Socket is not initialized \n + * @ref SOCKERR_SOCKCLOSED - Socket closed unexpectedly. + */ +int8_t wizchip_listen(uint8_t sn); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Try to connect a server. + * @details It requests connection to the server with destination IP address and port number passed as parameter.\n + * @note It is valid only in TCP client mode. + * In block io mode, it does not return until connection is completed. + * In Non-block io mode, it return @ref SOCK_BUSY immediately. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param addr Pointer variable of destination IP address. It should be allocated 4 bytes. + * @param port Destination port number. + * + * @return @b Success : @ref SOCK_OK \n + * @b Fail :\n @ref SOCKERR_SOCKNUM - Invalid socket number\n + * @ref SOCKERR_SOCKMODE - Invalid socket mode\n + * @ref SOCKERR_SOCKINIT - Socket is not initialized\n + * @ref SOCKERR_IPINVALID - Wrong server IP address\n + * @ref SOCKERR_PORTZERO - Server port zero\n + * @ref SOCKERR_TIMEOUT - Timeout occurred during request connection\n + * @ref SOCK_BUSY - In non-block io mode, it returned immediately\n + */ +int8_t wizchip_connect(uint8_t sn, uint8_t * addr, uint16_t port); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Try to disconnect a connection socket. + * @details It sends request message to disconnect the TCP socket 'sn' passed as parameter to the server or client. + * @note It is valid only in TCP server or client mode. \n + * In block io mode, it does not return until disconnection is completed. \n + * In Non-block io mode, it return @ref SOCK_BUSY immediately. \n + + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @return @b Success : @ref SOCK_OK \n + * @b Fail :\n @ref SOCKERR_SOCKNUM - Invalid socket number \n + * @ref SOCKERR_SOCKMODE - Invalid operation in the socket \n + * @ref SOCKERR_TIMEOUT - Timeout occurred \n + * @ref SOCK_BUSY - Socket is busy. + */ +int8_t wizchip_disconnect(uint8_t sn); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Send data to the connected peer in TCP socket. + * @details It is used to send outgoing data to the connected socket. + * @note It is valid only in TCP server or client mode. It can't send data greater than socket buffer size. \n + * In block io mode, It doesn't return until data send is completed - socket buffer size is greater than data. \n + * In non-block io mode, It return @ref SOCK_BUSY immediately when socket buffer is not enough. \n + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param buf Pointer buffer containing data to be sent. + * @param len The byte length of data in buf. + * @return @b Success : The sent data size \n + * @b Fail : \n @ref SOCKERR_SOCKSTATUS - Invalid socket status for socket operation \n + * @ref SOCKERR_TIMEOUT - Timeout occurred \n + * @ref SOCKERR_SOCKMODE - Invalid operation in the socket \n + * @ref SOCKERR_SOCKNUM - Invalid socket number \n + * @ref SOCKERR_DATALEN - zero data length \n + * @ref SOCK_BUSY - Socket is busy. + */ +int32_t wizchip_send(uint8_t sn, uint8_t * buf, uint16_t len); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Receive data from the connected peer. + * @details It is used to read incoming data from the connected socket.\n + * It waits for data as much as the application wants to receive. + * @note It is valid only in TCP server or client mode. It can't receive data greater than socket buffer size. \n + * In block io mode, it doesn't return until data reception is completed - data is filled as len in socket buffer. \n + * In non-block io mode, it return @ref SOCK_BUSY immediately when len is greater than data size in socket buffer. \n + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param buf Pointer buffer to read incoming data. + * @param len The max data length of data in buf. + * @return @b Success : The real received data size \n + * @b Fail :\n + * @ref SOCKERR_SOCKSTATUS - Invalid socket status for socket operation \n + * @ref SOCKERR_SOCKMODE - Invalid operation in the socket \n + * @ref SOCKERR_SOCKNUM - Invalid socket number \n + * @ref SOCKERR_DATALEN - zero data length \n + * @ref SOCK_BUSY - Socket is busy. + */ +int32_t wizchip_recv(uint8_t sn, uint8_t * buf, uint16_t len); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Sends datagram to the peer with destination IP address and port number passed as parameter. + * @details It sends datagram of UDP or MACRAW to the peer with destination IP address and port number passed as parameter.\n + * Even if the connectionless socket has been previously connected to a specific address, + * the address and port number parameters override the destination address for that particular datagram only. + * @note In block io mode, It doesn't return until data send is completed - socket buffer size is greater than len. + * In non-block io mode, It return @ref SOCK_BUSY immediately when socket buffer is not enough. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param buf Pointer buffer to send outgoing data. + * @param len The byte length of data in buf. + * @param addr Pointer variable of destination IP address. It should be allocated 4 bytes. + * @param port Destination port number. + * + * @return @b Success : The sent data size \n + * @b Fail :\n @ref SOCKERR_SOCKNUM - Invalid socket number \n + * @ref SOCKERR_SOCKMODE - Invalid operation in the socket \n + * @ref SOCKERR_SOCKSTATUS - Invalid socket status for socket operation \n + * @ref SOCKERR_DATALEN - zero data length \n + * @ref SOCKERR_IPINVALID - Wrong server IP address\n + * @ref SOCKERR_PORTZERO - Server port zero\n + * @ref SOCKERR_SOCKCLOSED - Socket unexpectedly closed \n + * @ref SOCKERR_TIMEOUT - Timeout occurred \n + * @ref SOCK_BUSY - Socket is busy. + */ +int32_t wizchip_sendto(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t port); + +/** + * @ingroup WIZnet_socket_APIs + * @brief Receive datagram of UDP or MACRAW + * @details This function is an application I/F function which is used to receive the data in other then TCP mode. \n + * This function is used to receive UDP and MAC_RAW mode, and handle the header as well. + * This function can divide to received the packet data. + * On the MACRAW SOCKET, the addr and port parameters are ignored. + * @note In block io mode, it doesn't return until data reception is completed - data is filled as len in socket buffer + * In non-block io mode, it return @ref SOCK_BUSY immediately when len is greater than data size in socket buffer. + * + * @param sn Socket number. It should be 0 ~ @ref \_WIZCHIP_SOCK_NUM_. + * @param buf Pointer buffer to read incoming data. + * @param len The max data length of data in buf. + * When the received packet size <= len, receives data as packet sized. + * When others, receives data as len. + * @param addr Pointer variable of destination IP address. It should be allocated 4 bytes. + * It is valid only when the first call recvfrom for receiving the packet. + * When it is valid, @ref packinfo[7] should be set as '1' after call @ref getsockopt(sn, SO_PACKINFO, &packinfo). + * @param port Pointer variable of destination port number. + * It is valid only when the first call recvform for receiving the packet. +* When it is valid, @ref packinfo[7] should be set as '1' after call @ref getsockopt(sn, SO_PACKINFO, &packinfo). + * + * @return @b Success : This function return real received data size for success.\n + * @b Fail : @ref SOCKERR_DATALEN - zero data length \n + * @ref SOCKERR_SOCKMODE - Invalid operation in the socket \n + * @ref SOCKERR_SOCKNUM - Invalid socket number \n + * @ref SOCKBUSY - Socket is busy. + */ +int32_t wizchip_recvfrom(uint8_t sn, uint8_t * buf, uint16_t len, uint8_t * addr, uint16_t *port); + + +///////////////////////////// +// SOCKET CONTROL & OPTION // +///////////////////////////// +#define SOCK_IO_BLOCK 0 ///< Socket Block IO Mode in @ref setsockopt(). +#define SOCK_IO_NONBLOCK 1 ///< Socket Non-block IO Mode in @ref setsockopt(). + +/** + * @defgroup DATA_TYPE DATA TYPE + */ + +/** + * @ingroup DATA_TYPE + * @brief The kind of Socket Interrupt. + * @sa Sn_IR, Sn_IMR, setSn_IR(), getSn_IR(), setSn_IMR(), getSn_IMR() + */ +typedef enum +{ + SIK_CONNECTED = (1 << 0), ///< connected + SIK_DISCONNECTED = (1 << 1), ///< disconnected + SIK_RECEIVED = (1 << 2), ///< data received + SIK_TIMEOUT = (1 << 3), ///< timeout occurred + SIK_SENT = (1 << 4), ///< send ok + //M20150410 : Remove the comma of last member + //SIK_ALL = 0x1F, ///< all interrupt + SIK_ALL = 0x1F ///< all interrupt +}sockint_kind; + +/** + * @ingroup DATA_TYPE + * @brief The type of @ref ctlsocket(). + */ +typedef enum +{ + CS_SET_IOMODE, ///< set socket IO mode with @ref SOCK_IO_BLOCK or @ref SOCK_IO_NONBLOCK + CS_GET_IOMODE, ///< get socket IO mode + CS_GET_MAXTXBUF, ///< get the size of socket buffer allocated in TX memory + CS_GET_MAXRXBUF, ///< get the size of socket buffer allocated in RX memory + CS_CLR_INTERRUPT, ///< clear the interrupt of socket with @ref sockint_kind + CS_GET_INTERRUPT, ///< get the socket interrupt. refer to @ref sockint_kind +#if _WIZCHIP_ > 5100 + CS_SET_INTMASK, ///< set the interrupt mask of socket with @ref sockint_kind, Not supported in W5100 + CS_GET_INTMASK ///< get the masked interrupt of socket. refer to @ref sockint_kind, Not supported in W5100 +#endif +}ctlsock_type; + + +/** + * @ingroup DATA_TYPE + * @brief The type of socket option in @ref setsockopt() or @ref getsockopt() + */ +typedef enum +{ + SO_FLAG, ///< Valid only in getsockopt(), For set flag of socket refer to flag in @ref socket(). + SO_TTL, ///< Set TTL. @ref Sn_TTL ( @ref setSn_TTL(), @ref getSn_TTL() ) + SO_TOS, ///< Set TOS. @ref Sn_TOS ( @ref setSn_TOS(), @ref getSn_TOS() ) + SO_MSS, ///< Set MSS. @ref Sn_MSSR ( @ref setSn_MSSR(), @ref getSn_MSSR() ) + SO_DESTIP, ///< Set the destination IP address. @ref Sn_DIPR ( @ref setSn_DIPR(), @ref getSn_DIPR() ) + SO_DESTPORT, ///< Set the destination Port number. @ref Sn_DPORT ( @ref setSn_DPORT(), @ref getSn_DPORT() ) +#if _WIZCHIP_ != 5100 + SO_KEEPALIVESEND, ///< Valid only in setsockopt. Manually send keep-alive packet in TCP mode, Not supported in W5100 + #if !( (_WIZCHIP_ == 5100) || (_WIZCHIP_ == 5200) ) + SO_KEEPALIVEAUTO, ///< Set/Get keep-alive auto transmission timer in TCP mode, Not supported in W5100, W5200 + #endif +#endif + SO_SENDBUF, ///< Valid only in getsockopt. Get the free data size of Socekt TX buffer. @ref Sn_TX_FSR, @ref getSn_TX_FSR() + SO_RECVBUF, ///< Valid only in getsockopt. Get the received data size in socket RX buffer. @ref Sn_RX_RSR, @ref getSn_RX_RSR() + SO_STATUS, ///< Valid only in getsockopt. Get the socket status. @ref Sn_SR, @ref getSn_SR() + SO_REMAINSIZE, ///< Valid only in getsockopt. Get the remained packet size in other then TCP mode. + SO_PACKINFO ///< Valid only in getsockopt. Get the packet information as @ref PACK_FIRST, @ref PACK_REMAINED, and @ref PACK_COMPLETED in other then TCP mode. +}sockopt_type; + +/** + * @ingroup WIZnet_socket_APIs + * @brief Control socket. + * @details Control IO mode, Interrupt & Mask of socket and get the socket buffer information. + * Refer to @ref ctlsock_type. + * @param sn socket number + * @param cstype type of control socket. refer to @ref ctlsock_type. + * @param arg Data type and value is determined according to @ref ctlsock_type. \n + * + * + * + * + * + *
@b cstype @b data type@b value
@ref CS_SET_IOMODE \n @ref CS_GET_IOMODE uint8_t @ref SOCK_IO_BLOCK @ref SOCK_IO_NONBLOCK
@ref CS_GET_MAXTXBUF \n @ref CS_GET_MAXRXBUF uint16_t 0 ~ 16K
@ref CS_CLR_INTERRUPT \n @ref CS_GET_INTERRUPT \n @ref CS_SET_INTMASK \n @ref CS_GET_INTMASK @ref sockint_kind @ref SIK_CONNECTED, etc.
+ * @return @b Success @ref SOCK_OK \n + * @b fail @ref SOCKERR_ARG - Invalid argument\n + */ +int8_t wizchip_ctlsocket(uint8_t sn, ctlsock_type cstype, void* arg); + +/** + * @ingroup WIZnet_socket_APIs + * @brief set socket options + * @details Set socket option like as TTL, MSS, TOS, and so on. Refer to @ref sockopt_type. + * + * @param sn socket number + * @param sotype socket option type. refer to @ref sockopt_type + * @param arg Data type and value is determined according to sotype. \n + * + * + * + * + * + * + * + * + * + *
@b sotype @b data type@b value
@ref SO_TTL uint8_t 0 ~ 255
@ref SO_TOS uint8_t 0 ~ 255
@ref SO_MSS uint16_t 0 ~ 65535
@ref SO_DESTIP uint8_t[4]
@ref SO_DESTPORT uint16_t 0 ~ 65535
@ref SO_KEEPALIVESEND null null
@ref SO_KEEPALIVEAUTO uint8_t 0 ~ 255
+ * @return + * - @b Success : @ref SOCK_OK \n + * - @b Fail + * - @ref SOCKERR_SOCKNUM - Invalid Socket number \n + * - @ref SOCKERR_SOCKMODE - Invalid socket mode \n + * - @ref SOCKERR_SOCKOPT - Invalid socket option or its value \n + * - @ref SOCKERR_TIMEOUT - Timeout occurred when sending keep-alive packet \n + */ +int8_t wizchip_setsockopt(uint8_t sn, sockopt_type sotype, void* arg); + +/** + * @ingroup WIZnet_socket_APIs + * @brief get socket options + * @details Get socket option like as FLAG, TTL, MSS, and so on. Refer to @ref sockopt_type + * @param sn socket number + * @param sotype socket option type. refer to @ref sockopt_type + * @param arg Data type and value is determined according to sotype. \n + * + * + * + * + * + * + * + * + * + * + * + * + * + *
@b sotype @b data type@b value
@ref SO_FLAG uint8_t @ref SF_ETHER_OWN, etc...
@ref SO_TOS uint8_t 0 ~ 255
@ref SO_MSS uint16_t 0 ~ 65535
@ref SO_DESTIP uint8_t[4]
@ref SO_DESTPORT uint16_t
@ref SO_KEEPALIVEAUTO uint8_t 0 ~ 255
@ref SO_SENDBUF uint16_t 0 ~ 65535
@ref SO_RECVBUF uint16_t 0 ~ 65535
@ref SO_STATUS uint8_t @ref SOCK_ESTABLISHED, etc..
@ref SO_REMAINSIZE uint16_t 0~ 65535
@ref SO_PACKINFO uint8_t @ref PACK_FIRST, etc...
+ * @return + * - @b Success : @ref SOCK_OK \n + * - @b Fail + * - @ref SOCKERR_SOCKNUM - Invalid Socket number \n + * - @ref SOCKERR_SOCKOPT - Invalid socket option or its value \n + * - @ref SOCKERR_SOCKMODE - Invalid socket mode \n + * @note + * The option as PACK_REMAINED and SO_PACKINFO is valid only in NON-TCP mode and after call @ref recvfrom(). \n + * When SO_PACKINFO value is PACK_FIRST and the return value of recvfrom() is zero, + * This means the zero byte UDP data(UDP Header only) received. + */ +int8_t wizchip_getsockopt(uint8_t sn, sockopt_type sotype, void* arg); + +#ifdef __cplusplus + } +#endif + +#endif // _WIZCHIP_SOCKET_H_ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.c new file mode 100644 index 000000000..60310f83f --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.c @@ -0,0 +1,1030 @@ +//***************************************************************************** +// +//! \file wizchip_dhcp.c +//! \brief DHCP APIs implement file. +//! \details Processing DHCP protocol as DISCOVER, OFFER, REQUEST, ACK, NACK and DECLINE. +//! \version 1.1.1 +//! \date 2019/10/08 +//! \par Revision history +//! <2019/10/08> compare DHCP server ip address +//! <2013/11/18> 1st Release +//! <2012/12/20> V1.1.0 +//! 1. Optimize code +//! 2. Add reg_dhcp_cbfunc() +//! 3. Add DHCP_stop() +//! 4. Integrate check_DHCP_state() & DHCP_run() to DHCP_run() +//! 5. Don't care system endian +//! 6. Add comments +//! <2012/12/26> V1.1.1 +//! 1. Modify variable declaration: dhcp_tick_1s is declared volatile for code optimization +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +#include +#include "DHCP/wizchip_dhcp.h" + +/* If you want to display debug & processing message, Define _DHCP_DEBUG_ in dhcp.h */ + +#ifdef _DHCP_DEBUG_ + #include +#endif + +/* DHCP state machine. */ +#define STATE_DHCP_INIT 0 ///< Initialize +#define STATE_DHCP_DISCOVER 1 ///< send DISCOVER and wait OFFER +#define STATE_DHCP_REQUEST 2 ///< send REQEUST and wait ACK or NACK +#define STATE_DHCP_LEASED 3 ///< ReceiveD ACK and IP leased +#define STATE_DHCP_REREQUEST 4 ///< send REQUEST for maintaining leased IP +#define STATE_DHCP_RELEASE 5 ///< No use +#define STATE_DHCP_STOP 6 ///< Stop processing DHCP + +#define DHCP_FLAGSBROADCAST 0x8000 ///< The broadcast value of flags in @ref RIP_MSG +#define DHCP_FLAGSUNICAST 0x0000 ///< The unicast value of flags in @ref RIP_MSG + +/* DHCP message OP code */ +#define DHCP_BOOTREQUEST 1 ///< Request Message used in op of @ref RIP_MSG +#define DHCP_BOOTREPLY 2 ///< Reply Message used i op of @ref RIP_MSG + +/* DHCP message type */ +#define DHCP_DISCOVER 1 ///< DISCOVER message in OPT of @ref RIP_MSG +#define DHCP_OFFER 2 ///< OFFER message in OPT of @ref RIP_MSG +#define DHCP_REQUEST 3 ///< REQUEST message in OPT of @ref RIP_MSG +#define DHCP_DECLINE 4 ///< DECLINE message in OPT of @ref RIP_MSG +#define DHCP_ACK 5 ///< ACK message in OPT of @ref RIP_MSG +#define DHCP_NAK 6 ///< NACK message in OPT of @ref RIP_MSG +#define DHCP_RELEASE 7 ///< RELEASE message in OPT of @ref RIP_MSG. No use +#define DHCP_INFORM 8 ///< INFORM message in OPT of @ref RIP_MSG. No use + +#define DHCP_HTYPE10MB 1 ///< Used in type of @ref RIP_MSG +#define DHCP_HTYPE100MB 2 ///< Used in type of @ref RIP_MSG + +#define DHCP_HLENETHERNET 6 ///< Used in hlen of @ref RIP_MSG +#define DHCP_HOPS 0 ///< Used in hops of @ref RIP_MSG +#define DHCP_SECS 0 ///< Used in secs of @ref RIP_MSG + +#define INFINITE_LEASETIME 0xffffffff ///< Infinite lease time + +#define OPT_SIZE 312 /// Max OPT size of @ref RIP_MSG +#define RIP_MSG_SIZE (236+OPT_SIZE) /// Max size of @ref RIP_MSG + +/* + * @brief DHCP option and value (cf. RFC1533) + */ +enum +{ + padOption = 0, + subnetMask = 1, + timerOffset = 2, + routersOnSubnet = 3, + timeServer = 4, + nameServer = 5, + dns = 6, + logServer = 7, + cookieServer = 8, + lprServer = 9, + impressServer = 10, + resourceLocationServer = 11, + hostName = 12, + bootFileSize = 13, + meritDumpFile = 14, + domainName = 15, + swapServer = 16, + rootPath = 17, + extentionsPath = 18, + IPforwarding = 19, + nonLocalSourceRouting = 20, + policyFilter = 21, + maxDgramReasmSize = 22, + defaultIPTTL = 23, + pathMTUagingTimeout = 24, + pathMTUplateauTable = 25, + ifMTU = 26, + allSubnetsLocal = 27, + broadcastAddr = 28, + performMaskDiscovery = 29, + maskSupplier = 30, + performRouterDiscovery = 31, + routerSolicitationAddr = 32, + staticRoute = 33, + trailerEncapsulation = 34, + arpCacheTimeout = 35, + ethernetEncapsulation = 36, + tcpDefaultTTL = 37, + tcpKeepaliveInterval = 38, + tcpKeepaliveGarbage = 39, + nisDomainName = 40, + nisServers = 41, + ntpServers = 42, + vendorSpecificInfo = 43, + netBIOSnameServer = 44, + netBIOSdgramDistServer = 45, + netBIOSnodeType = 46, + netBIOSscope = 47, + xFontServer = 48, + xDisplayManager = 49, + dhcpRequestedIPaddr = 50, + dhcpIPaddrLeaseTime = 51, + dhcpOptionOverload = 52, + dhcpMessageType = 53, + dhcpServerIdentifier = 54, + dhcpParamRequest = 55, + dhcpMsg = 56, + dhcpMaxMsgSize = 57, + dhcpT1value = 58, + dhcpT2value = 59, + dhcpClassIdentifier = 60, + dhcpClientIdentifier = 61, + endOption = 255 +}; + +/* + * @brief DHCP message format + */ +typedef struct { + uint8_t op; ///< @ref DHCP_BOOTREQUEST or @ref DHCP_BOOTREPLY + uint8_t htype; ///< @ref DHCP_HTYPE10MB or @ref DHCP_HTYPE100MB + uint8_t hlen; ///< @ref DHCP_HLENETHERNET + uint8_t hops; ///< @ref DHCP_HOPS + uint32_t xid; ///< @ref DHCP_XID This increase one every DHCP transaction. + uint16_t secs; ///< @ref DHCP_SECS + uint16_t flags; ///< @ref DHCP_FLAGSBROADCAST or @ref DHCP_FLAGSUNICAST + uint8_t ciaddr[4]; ///< @ref Request IP to DHCP sever + uint8_t yiaddr[4]; ///< @ref Offered IP from DHCP server + uint8_t siaddr[4]; ///< No use + uint8_t giaddr[4]; ///< No use + uint8_t chaddr[16]; ///< DHCP client 6bytes MAC address. Others is filled to zero + uint8_t sname[64]; ///< No use + uint8_t file[128]; ///< No use + uint8_t OPT[OPT_SIZE]; ///< Option +} RIP_MSG; + + + +uint8_t DHCP_SOCKET; // Socket number for DHCP + +uint8_t DHCP_SIP[4]; // DHCP Server IP address +uint8_t DHCP_REAL_SIP[4]; // For extract my DHCP server in a few DHCP server + +// Network information from DHCP Server +uint8_t OLD_allocated_ip[4] = {0, }; // Previous IP address +uint8_t DHCP_allocated_ip[4] = {0, }; // IP address from DHCP +uint8_t DHCP_allocated_gw[4] = {0, }; // Gateway address from DHCP +uint8_t DHCP_allocated_sn[4] = {0, }; // Subnet mask from DHCP +uint8_t DHCP_allocated_dns[4] = {0, }; // DNS address from DHCP + + +int8_t dhcp_state = STATE_DHCP_INIT; // DHCP state +int8_t dhcp_retry_count = 0; + +uint32_t dhcp_lease_time = INFINITE_LEASETIME; +volatile uint32_t dhcp_tick_1s = 0; // unit 1 second +uint32_t dhcp_tick_next = DHCP_WAIT_TIME ; + +uint32_t DHCP_XID; // Any number + +RIP_MSG* pDHCPMSG; // Buffer pointer for DHCP processing + +uint8_t HOST_NAME[] = DCHP_HOST_NAME; + +uint8_t DHCP_CHADDR[6]; // DHCP Client MAC address. + +/* The default callback function */ +void default_ip_assign(void); +void default_ip_update(void); +void default_ip_conflict(void); + +/* Callback handler */ +void (*dhcp_ip_assign)(void) = default_ip_assign; /* handler to be called when the IP address from DHCP server is first assigned */ +void (*dhcp_ip_update)(void) = default_ip_update; /* handler to be called when the IP address from DHCP server is updated */ +void (*dhcp_ip_conflict)(void) = default_ip_conflict; /* handler to be called when the IP address from DHCP server is conflict */ + +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)); + +char NibbleToHex(uint8_t nibble); + +/* send DISCOVER message to DHCP server */ +void send_DHCP_DISCOVER(void); + +/* send REQEUST message to DHCP server */ +void send_DHCP_REQUEST(void); + +/* send DECLINE message to DHCP server */ +void send_DHCP_DECLINE(void); + +/* IP conflict check by sending ARP-request to leased IP and wait ARP-response. */ +int8_t check_DHCP_leasedIP(void); + +/* check the timeout in DHCP process */ +uint8_t check_DHCP_timeout(void); + +/* Initialize to timeout process. */ +void reset_DHCP_timeout(void); + +/* Parse message as OFFER and ACK and NACK from DHCP server.*/ +int8_t parseDHCPCMSG(void); + +/* The default handler of ip assign first */ +void default_ip_assign(void) +{ + setSIPR(DHCP_allocated_ip); + setSUBR(DHCP_allocated_sn); + setGAR (DHCP_allocated_gw); +} + +/* The default handler of ip changed */ +void default_ip_update(void) +{ + /* WIZchip Software Reset */ + setMR(MR_RST); + getMR(); // for delay + default_ip_assign(); + setSHAR(DHCP_CHADDR); +} + +/* The default handler of ip changed */ +void default_ip_conflict(void) +{ + // WIZchip Software Reset + setMR(MR_RST); + getMR(); // for delay + setSHAR(DHCP_CHADDR); +} + +/* register the call back func. */ +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)) +{ + dhcp_ip_assign = default_ip_assign; + dhcp_ip_update = default_ip_update; + dhcp_ip_conflict = default_ip_conflict; + if(ip_assign) dhcp_ip_assign = ip_assign; + if(ip_update) dhcp_ip_update = ip_update; + if(ip_conflict) dhcp_ip_conflict = ip_conflict; +} + +/* make the common DHCP message */ +void makeDHCPMSG(void) +{ + uint8_t bk_mac[6]; + uint8_t* ptmp; + uint8_t i; + getSHAR(bk_mac); + pDHCPMSG->op = DHCP_BOOTREQUEST; + pDHCPMSG->htype = DHCP_HTYPE10MB; + pDHCPMSG->hlen = DHCP_HLENETHERNET; + pDHCPMSG->hops = DHCP_HOPS; + ptmp = (uint8_t*)(&pDHCPMSG->xid); + *(ptmp+0) = (uint8_t)((DHCP_XID & 0xFF000000) >> 24); + *(ptmp+1) = (uint8_t)((DHCP_XID & 0x00FF0000) >> 16); + *(ptmp+2) = (uint8_t)((DHCP_XID & 0x0000FF00) >> 8); + *(ptmp+3) = (uint8_t)((DHCP_XID & 0x000000FF) >> 0); + pDHCPMSG->secs = DHCP_SECS; + ptmp = (uint8_t*)(&pDHCPMSG->flags); + *(ptmp+0) = (uint8_t)((DHCP_FLAGSBROADCAST & 0xFF00) >> 8); + *(ptmp+1) = (uint8_t)((DHCP_FLAGSBROADCAST & 0x00FF) >> 0); + + pDHCPMSG->ciaddr[0] = 0; + pDHCPMSG->ciaddr[1] = 0; + pDHCPMSG->ciaddr[2] = 0; + pDHCPMSG->ciaddr[3] = 0; + + pDHCPMSG->yiaddr[0] = 0; + pDHCPMSG->yiaddr[1] = 0; + pDHCPMSG->yiaddr[2] = 0; + pDHCPMSG->yiaddr[3] = 0; + + pDHCPMSG->siaddr[0] = 0; + pDHCPMSG->siaddr[1] = 0; + pDHCPMSG->siaddr[2] = 0; + pDHCPMSG->siaddr[3] = 0; + + pDHCPMSG->giaddr[0] = 0; + pDHCPMSG->giaddr[1] = 0; + pDHCPMSG->giaddr[2] = 0; + pDHCPMSG->giaddr[3] = 0; + + pDHCPMSG->chaddr[0] = DHCP_CHADDR[0]; + pDHCPMSG->chaddr[1] = DHCP_CHADDR[1]; + pDHCPMSG->chaddr[2] = DHCP_CHADDR[2]; + pDHCPMSG->chaddr[3] = DHCP_CHADDR[3]; + pDHCPMSG->chaddr[4] = DHCP_CHADDR[4]; + pDHCPMSG->chaddr[5] = DHCP_CHADDR[5]; + + for (i = 6; i < 16; i++) pDHCPMSG->chaddr[i] = 0; + for (i = 0; i < 64; i++) pDHCPMSG->sname[i] = 0; + for (i = 0; i < 128; i++) pDHCPMSG->file[i] = 0; + + // MAGIC_COOKIE + pDHCPMSG->OPT[0] = (uint8_t)((MAGIC_COOKIE & 0xFF000000) >> 24); + pDHCPMSG->OPT[1] = (uint8_t)((MAGIC_COOKIE & 0x00FF0000) >> 16); + pDHCPMSG->OPT[2] = (uint8_t)((MAGIC_COOKIE & 0x0000FF00) >> 8); + pDHCPMSG->OPT[3] = (uint8_t) (MAGIC_COOKIE & 0x000000FF) >> 0; +} + +/* SEND DHCP DISCOVER */ +void send_DHCP_DISCOVER(void) +{ + uint16_t i; + uint8_t ip[4]; + uint16_t k = 0; + + makeDHCPMSG(); + DHCP_SIP[0]=0; + DHCP_SIP[1]=0; + DHCP_SIP[2]=0; + DHCP_SIP[3]=0; + DHCP_REAL_SIP[0]=0; + DHCP_REAL_SIP[1]=0; + DHCP_REAL_SIP[2]=0; + DHCP_REAL_SIP[3]=0; + + k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() + + // Option Request Param + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_DISCOVER; + + // Client identifier + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + // host name + pDHCPMSG->OPT[k++] = hostName; + pDHCPMSG->OPT[k++] = 0; // fill zero length of hostname + for(i = 0 ; HOST_NAME[i] != 0; i++) + pDHCPMSG->OPT[k++] = HOST_NAME[i]; + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[3] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[3]); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[4] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[4]); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[5] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[5]); + pDHCPMSG->OPT[k - (i+6+1)] = i+6; // length of hostname + + pDHCPMSG->OPT[k++] = dhcpParamRequest; + pDHCPMSG->OPT[k++] = 0x06; // length of request + pDHCPMSG->OPT[k++] = subnetMask; + pDHCPMSG->OPT[k++] = routersOnSubnet; + pDHCPMSG->OPT[k++] = dns; + pDHCPMSG->OPT[k++] = domainName; + pDHCPMSG->OPT[k++] = dhcpT1value; + pDHCPMSG->OPT[k++] = dhcpT2value; + pDHCPMSG->OPT[k++] = endOption; + + for (i = k; i < OPT_SIZE; i++) pDHCPMSG->OPT[i] = 0; + + // send broadcasting packet + ip[0] = 255; + ip[1] = 255; + ip[2] = 255; + ip[3] = 255; + +#ifdef _DHCP_DEBUG_ + printf("> Send DHCP_DISCOVER\r\n"); +#endif + + wizchip_sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); +} + +/* SEND DHCP REQUEST */ +void send_DHCP_REQUEST(void) +{ + int i; + uint8_t ip[4]; + uint16_t k = 0; + + makeDHCPMSG(); + + if(dhcp_state == STATE_DHCP_LEASED || dhcp_state == STATE_DHCP_REREQUEST) + { + *((uint8_t*)(&pDHCPMSG->flags)) = ((DHCP_FLAGSUNICAST & 0xFF00)>> 8); + *((uint8_t*)(&pDHCPMSG->flags)+1) = (DHCP_FLAGSUNICAST & 0x00FF); + pDHCPMSG->ciaddr[0] = DHCP_allocated_ip[0]; + pDHCPMSG->ciaddr[1] = DHCP_allocated_ip[1]; + pDHCPMSG->ciaddr[2] = DHCP_allocated_ip[2]; + pDHCPMSG->ciaddr[3] = DHCP_allocated_ip[3]; + ip[0] = DHCP_SIP[0]; + ip[1] = DHCP_SIP[1]; + ip[2] = DHCP_SIP[2]; + ip[3] = DHCP_SIP[3]; + } + else + { + ip[0] = 255; + ip[1] = 255; + ip[2] = 255; + ip[3] = 255; + } + + k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() + + // Option Request Param. + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_REQUEST; + + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + if(ip[3] == 255) // if(dchp_state == STATE_DHCP_LEASED || dchp_state == DHCP_REREQUEST_STATE) + { + pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3]; + + pDHCPMSG->OPT[k++] = dhcpServerIdentifier; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_SIP[0]; + pDHCPMSG->OPT[k++] = DHCP_SIP[1]; + pDHCPMSG->OPT[k++] = DHCP_SIP[2]; + pDHCPMSG->OPT[k++] = DHCP_SIP[3]; + } + + // host name + pDHCPMSG->OPT[k++] = hostName; + pDHCPMSG->OPT[k++] = 0; // length of hostname + for(i = 0 ; HOST_NAME[i] != 0; i++) + pDHCPMSG->OPT[k++] = HOST_NAME[i]; + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[3] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[3]); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[4] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[4]); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[5] >> 4); + pDHCPMSG->OPT[k++] = NibbleToHex(DHCP_CHADDR[5]); + pDHCPMSG->OPT[k - (i+6+1)] = i+6; // length of hostname + + pDHCPMSG->OPT[k++] = dhcpParamRequest; + pDHCPMSG->OPT[k++] = 0x08; + pDHCPMSG->OPT[k++] = subnetMask; + pDHCPMSG->OPT[k++] = routersOnSubnet; + pDHCPMSG->OPT[k++] = dns; + pDHCPMSG->OPT[k++] = domainName; + pDHCPMSG->OPT[k++] = dhcpT1value; + pDHCPMSG->OPT[k++] = dhcpT2value; + pDHCPMSG->OPT[k++] = performRouterDiscovery; + pDHCPMSG->OPT[k++] = staticRoute; + pDHCPMSG->OPT[k++] = endOption; + + for (i = k; i < OPT_SIZE; i++) pDHCPMSG->OPT[i] = 0; + +#ifdef _DHCP_DEBUG_ + printf("> Send DHCP_REQUEST\r\n"); +#endif + + wizchip_sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); + +} + +/* SEND DHCP DHCPDECLINE */ +void send_DHCP_DECLINE(void) +{ + int i; + uint8_t ip[4]; + uint16_t k = 0; + + makeDHCPMSG(); + + k = 4; // because MAGIC_COOKIE already made by makeDHCPMSG() + + *((uint8_t*)(&pDHCPMSG->flags)) = ((DHCP_FLAGSUNICAST & 0xFF00)>> 8); + *((uint8_t*)(&pDHCPMSG->flags)+1) = (DHCP_FLAGSUNICAST & 0x00FF); + + // Option Request Param. + pDHCPMSG->OPT[k++] = dhcpMessageType; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_DECLINE; + + pDHCPMSG->OPT[k++] = dhcpClientIdentifier; + pDHCPMSG->OPT[k++] = 0x07; + pDHCPMSG->OPT[k++] = 0x01; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[0]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[1]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[2]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[3]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[4]; + pDHCPMSG->OPT[k++] = DHCP_CHADDR[5]; + + pDHCPMSG->OPT[k++] = dhcpRequestedIPaddr; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[0]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[1]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[2]; + pDHCPMSG->OPT[k++] = DHCP_allocated_ip[3]; + + pDHCPMSG->OPT[k++] = dhcpServerIdentifier; + pDHCPMSG->OPT[k++] = 0x04; + pDHCPMSG->OPT[k++] = DHCP_SIP[0]; + pDHCPMSG->OPT[k++] = DHCP_SIP[1]; + pDHCPMSG->OPT[k++] = DHCP_SIP[2]; + pDHCPMSG->OPT[k++] = DHCP_SIP[3]; + + pDHCPMSG->OPT[k++] = endOption; + + for (i = k; i < OPT_SIZE; i++) pDHCPMSG->OPT[i] = 0; + + //send broadcasting packet + ip[0] = 0xFF; + ip[1] = 0xFF; + ip[2] = 0xFF; + ip[3] = 0xFF; + +#ifdef _DHCP_DEBUG_ + printf("\r\n> Send DHCP_DECLINE\r\n"); +#endif + + wizchip_sendto(DHCP_SOCKET, (uint8_t *)pDHCPMSG, RIP_MSG_SIZE, ip, DHCP_SERVER_PORT); +} + +/* PARSE REPLY pDHCPMSG */ +int8_t parseDHCPMSG(void) +{ + uint8_t svr_addr[6]; + uint16_t svr_port; + uint16_t len; + + uint8_t * p; + uint8_t * e; + uint8_t type = 0; + uint8_t opt_len; + + if((len = getSn_RX_RSR(DHCP_SOCKET)) > 0) + { + len = wizchip_recvfrom(DHCP_SOCKET, (uint8_t *)pDHCPMSG, len, svr_addr, &svr_port); + #ifdef _DHCP_DEBUG_ + printf("DHCP message : %d.%d.%d.%d(%d) %d received. \r\n",svr_addr[0],svr_addr[1],svr_addr[2], svr_addr[3],svr_port, len); + #endif + } + else return 0; + if (svr_port == DHCP_SERVER_PORT) { + // compare mac address + if ( (pDHCPMSG->chaddr[0] != DHCP_CHADDR[0]) || (pDHCPMSG->chaddr[1] != DHCP_CHADDR[1]) || + (pDHCPMSG->chaddr[2] != DHCP_CHADDR[2]) || (pDHCPMSG->chaddr[3] != DHCP_CHADDR[3]) || + (pDHCPMSG->chaddr[4] != DHCP_CHADDR[4]) || (pDHCPMSG->chaddr[5] != DHCP_CHADDR[5]) ) + { +#ifdef _DHCP_DEBUG_ + printf("No My DHCP Message. This message is ignored.\r\n"); +#endif + return 0; + } + //compare DHCP server ip address + if((DHCP_SIP[0]!=0) || (DHCP_SIP[1]!=0) || (DHCP_SIP[2]!=0) || (DHCP_SIP[3]!=0)){ + if( ((svr_addr[0]!=DHCP_SIP[0])|| (svr_addr[1]!=DHCP_SIP[1])|| (svr_addr[2]!=DHCP_SIP[2])|| (svr_addr[3]!=DHCP_SIP[3])) && + ((svr_addr[0]!=DHCP_REAL_SIP[0])|| (svr_addr[1]!=DHCP_REAL_SIP[1])|| (svr_addr[2]!=DHCP_REAL_SIP[2])|| (svr_addr[3]!=DHCP_REAL_SIP[3])) ) + { +#ifdef _DHCP_DEBUG_ + printf("Another DHCP sever send a response message. This is ignored.\r\n"); +#endif + return 0; + } + } + p = (uint8_t *)(&pDHCPMSG->op); + p = p + 240; // 240 = sizeof(RIP_MSG) + MAGIC_COOKIE size in RIP_MSG.opt - sizeof(RIP_MSG.opt) + e = p + (len - 240); + + while ( p < e ) { + + switch ( *p ) { + + case endOption : + p = e; // for break while(p < e) + break; + case padOption : + p++; + break; + case dhcpMessageType : + p++; + p++; + type = *p++; + break; + case subnetMask : + p++; + p++; + DHCP_allocated_sn[0] = *p++; + DHCP_allocated_sn[1] = *p++; + DHCP_allocated_sn[2] = *p++; + DHCP_allocated_sn[3] = *p++; + break; + case routersOnSubnet : + p++; + opt_len = *p++; + DHCP_allocated_gw[0] = *p++; + DHCP_allocated_gw[1] = *p++; + DHCP_allocated_gw[2] = *p++; + DHCP_allocated_gw[3] = *p++; + p = p + (opt_len - 4); + break; + case dns : + p++; + opt_len = *p++; + DHCP_allocated_dns[0] = *p++; + DHCP_allocated_dns[1] = *p++; + DHCP_allocated_dns[2] = *p++; + DHCP_allocated_dns[3] = *p++; + p = p + (opt_len - 4); + break; + case dhcpIPaddrLeaseTime : + p++; + opt_len = *p++; + dhcp_lease_time = *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; + dhcp_lease_time = (dhcp_lease_time << 8) + *p++; + #ifdef _DHCP_DEBUG_ + dhcp_lease_time = 10; + #endif + break; + case dhcpServerIdentifier : + p++; + opt_len = *p++; + DHCP_SIP[0] = *p++; + DHCP_SIP[1] = *p++; + DHCP_SIP[2] = *p++; + DHCP_SIP[3] = *p++; + DHCP_REAL_SIP[0]=svr_addr[0]; + DHCP_REAL_SIP[1]=svr_addr[1]; + DHCP_REAL_SIP[2]=svr_addr[2]; + DHCP_REAL_SIP[3]=svr_addr[3]; + break; + default : + p++; + opt_len = *p++; + p += opt_len; + break; + } // switch + } // while + } // if + return type; +} + +uint8_t DHCP_run(void) +{ + uint8_t type; + uint8_t ret; + + if(dhcp_state == STATE_DHCP_STOP) return DHCP_STOPPED; + + if(getSn_SR(DHCP_SOCKET) != SOCK_UDP) + wizchip_socket(DHCP_SOCKET, Sn_MR_UDP, DHCP_CLIENT_PORT, 0x00); + + ret = DHCP_RUNNING; + type = parseDHCPMSG(); + + switch ( dhcp_state ) { + case STATE_DHCP_INIT : + DHCP_allocated_ip[0] = 0; + DHCP_allocated_ip[1] = 0; + DHCP_allocated_ip[2] = 0; + DHCP_allocated_ip[3] = 0; + send_DHCP_DISCOVER(); + dhcp_state = STATE_DHCP_DISCOVER; + break; + case STATE_DHCP_DISCOVER : + if (type == DHCP_OFFER){ +#ifdef _DHCP_DEBUG_ + printf("> Receive DHCP_OFFER\r\n"); +#endif + DHCP_allocated_ip[0] = pDHCPMSG->yiaddr[0]; + DHCP_allocated_ip[1] = pDHCPMSG->yiaddr[1]; + DHCP_allocated_ip[2] = pDHCPMSG->yiaddr[2]; + DHCP_allocated_ip[3] = pDHCPMSG->yiaddr[3]; + + send_DHCP_REQUEST(); + dhcp_state = STATE_DHCP_REQUEST; + } else ret = check_DHCP_timeout(); + break; + + case STATE_DHCP_REQUEST : + if (type == DHCP_ACK) { + +#ifdef _DHCP_DEBUG_ + printf("> Receive DHCP_ACK\r\n"); +#endif + if (check_DHCP_leasedIP()) { + // Network info assignment from DHCP + dhcp_ip_assign(); + reset_DHCP_timeout(); + + dhcp_state = STATE_DHCP_LEASED; + } else { + // IP address conflict occurred + reset_DHCP_timeout(); + dhcp_ip_conflict(); + dhcp_state = STATE_DHCP_INIT; + } + } else if (type == DHCP_NAK) { + +#ifdef _DHCP_DEBUG_ + printf("> Receive DHCP_NACK\r\n"); +#endif + + reset_DHCP_timeout(); + + dhcp_state = STATE_DHCP_DISCOVER; + } else ret = check_DHCP_timeout(); + break; + + case STATE_DHCP_LEASED : + if ((dhcp_lease_time != INFINITE_LEASETIME) && ((dhcp_lease_time/2) < dhcp_tick_1s)) { + +#ifdef _DHCP_DEBUG_ + printf("> Maintains the IP address \r\n"); +#endif + + type = 0; + OLD_allocated_ip[0] = DHCP_allocated_ip[0]; + OLD_allocated_ip[1] = DHCP_allocated_ip[1]; + OLD_allocated_ip[2] = DHCP_allocated_ip[2]; + OLD_allocated_ip[3] = DHCP_allocated_ip[3]; + + DHCP_XID++; + + send_DHCP_REQUEST(); + + reset_DHCP_timeout(); + + dhcp_state = STATE_DHCP_REREQUEST; + } + else + { + ret = DHCP_IP_LEASED; + } + break; + + case STATE_DHCP_REREQUEST : + if (type == DHCP_ACK) { + dhcp_retry_count = 0; + if (OLD_allocated_ip[0] != DHCP_allocated_ip[0] || + OLD_allocated_ip[1] != DHCP_allocated_ip[1] || + OLD_allocated_ip[2] != DHCP_allocated_ip[2] || + OLD_allocated_ip[3] != DHCP_allocated_ip[3]) + { + ret = DHCP_IP_CHANGED; + dhcp_ip_update(); + #ifdef _DHCP_DEBUG_ + printf(">IP changed.\r\n"); + #endif + + } + #ifdef _DHCP_DEBUG_ + else printf(">IP is continued.\r\n"); + #endif + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_LEASED; + } else if (type == DHCP_NAK) { + +#ifdef _DHCP_DEBUG_ + printf("> Receive DHCP_NACK, Failed to maintain ip\r\n"); +#endif + + reset_DHCP_timeout(); + + dhcp_state = STATE_DHCP_DISCOVER; + } else ret = check_DHCP_timeout(); + break; + default : + break; + } + + return ret; +} + +void DHCP_stop(void) +{ + wizchip_close(DHCP_SOCKET); + dhcp_state = STATE_DHCP_STOP; +} + +uint8_t check_DHCP_timeout(void) +{ + uint8_t ret = DHCP_RUNNING; + + if (dhcp_retry_count < MAX_DHCP_RETRY) { + if (dhcp_tick_next < dhcp_tick_1s) { + + switch ( dhcp_state ) { + case STATE_DHCP_DISCOVER : +// printf("<> state : STATE_DHCP_DISCOVER\r\n"); + send_DHCP_DISCOVER(); + break; + + case STATE_DHCP_REQUEST : +// printf("<> state : STATE_DHCP_REQUEST\r\n"); + + send_DHCP_REQUEST(); + break; + + case STATE_DHCP_REREQUEST : +// printf("<> state : STATE_DHCP_REREQUEST\r\n"); + + send_DHCP_REQUEST(); + break; + + default : + break; + } + + dhcp_tick_1s = 0; + dhcp_tick_next = dhcp_tick_1s + DHCP_WAIT_TIME; + dhcp_retry_count++; + } + } else { // timeout occurred + + switch(dhcp_state) { + case STATE_DHCP_DISCOVER: + dhcp_state = STATE_DHCP_INIT; + ret = DHCP_FAILED; + break; + case STATE_DHCP_REQUEST: + case STATE_DHCP_REREQUEST: + send_DHCP_DISCOVER(); + dhcp_state = STATE_DHCP_DISCOVER; + break; + default : + break; + } + reset_DHCP_timeout(); + } + return ret; +} + +int8_t check_DHCP_leasedIP(void) +{ + uint8_t tmp; + int32_t ret; + + //WIZchip RCR value changed for ARP Timeout count control + tmp = getRCR(); + setRCR(0x03); + + // IP conflict detection : ARP request - ARP reply + // Broadcasting ARP Request for check the IP conflict using UDP sendto() function + ret = wizchip_sendto(DHCP_SOCKET, (uint8_t *)"CHECK_IP_CONFLICT", 17, DHCP_allocated_ip, 5000); + + // RCR value restore + setRCR(tmp); + + if(ret == SOCKERR_TIMEOUT) { + // UDP send Timeout occurred : allocated IP address is unique, DHCP Success + +#ifdef _DHCP_DEBUG_ + printf("\r\n> Check leased IP - OK\r\n"); +#endif + + return 1; + } else { + // Received ARP reply or etc : IP address conflict occur, DHCP Failed + send_DHCP_DECLINE(); + + ret = dhcp_tick_1s; + while((dhcp_tick_1s - ret) < 2) ; // wait for 1s over; wait to complete to send DECLINE message; + + return 0; + } +} + +void DHCP_init(uint8_t s, uint8_t * buf) +{ + uint8_t zeroip[4] = {0,0,0,0}; + getSHAR(DHCP_CHADDR); + if((DHCP_CHADDR[0] | DHCP_CHADDR[1] | DHCP_CHADDR[2] | DHCP_CHADDR[3] | DHCP_CHADDR[4] | DHCP_CHADDR[5]) == 0x00) + { + // assigning temporary mac address, you should be set SHAR before call this function. + DHCP_CHADDR[0] = 0x00; + DHCP_CHADDR[1] = 0x08; + DHCP_CHADDR[2] = 0xdc; + DHCP_CHADDR[3] = 0x00; + DHCP_CHADDR[4] = 0x00; + DHCP_CHADDR[5] = 0x00; + setSHAR(DHCP_CHADDR); + } + + DHCP_SOCKET = s; // SOCK_DHCP + pDHCPMSG = (RIP_MSG*)buf; + DHCP_XID = 0x12345678; + { + DHCP_XID += DHCP_CHADDR[3]; + DHCP_XID += DHCP_CHADDR[4]; + DHCP_XID += DHCP_CHADDR[5]; + DHCP_XID += (DHCP_CHADDR[3] ^ DHCP_CHADDR[4] ^ DHCP_CHADDR[5]); + } + // WIZchip Netinfo Clear + setSIPR(zeroip); + setGAR(zeroip); + + reset_DHCP_timeout(); + dhcp_state = STATE_DHCP_INIT; +} + + +/* Reset the DHCP timeout count and retry count. */ +void reset_DHCP_timeout(void) +{ + dhcp_tick_1s = 0; + dhcp_tick_next = DHCP_WAIT_TIME; + dhcp_retry_count = 0; +} + +void DHCP_time_handler(void) +{ + dhcp_tick_1s++; +} + +void getIPfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_ip[0]; + ip[1] = DHCP_allocated_ip[1]; + ip[2] = DHCP_allocated_ip[2]; + ip[3] = DHCP_allocated_ip[3]; +} + +void getGWfromDHCP(uint8_t* ip) +{ + ip[0] =DHCP_allocated_gw[0]; + ip[1] =DHCP_allocated_gw[1]; + ip[2] =DHCP_allocated_gw[2]; + ip[3] =DHCP_allocated_gw[3]; +} + +void getSNfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_sn[0]; + ip[1] = DHCP_allocated_sn[1]; + ip[2] = DHCP_allocated_sn[2]; + ip[3] = DHCP_allocated_sn[3]; +} + +void getDNSfromDHCP(uint8_t* ip) +{ + ip[0] = DHCP_allocated_dns[0]; + ip[1] = DHCP_allocated_dns[1]; + ip[2] = DHCP_allocated_dns[2]; + ip[3] = DHCP_allocated_dns[3]; +} + +uint32_t getDHCPLeasetime(void) +{ + return dhcp_lease_time; +} + +uint32_t getDHCPTick1s(void) +{ + return dhcp_tick_1s; +} + +char NibbleToHex(uint8_t nibble) +{ + nibble &= 0x0F; + if (nibble <= 9) + return nibble + '0'; + else + return nibble + ('A'-0x0A); +} + + diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.h new file mode 100644 index 000000000..ae5dc6e0e --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DHCP/wizchip_dhcp.h @@ -0,0 +1,162 @@ +//***************************************************************************** +// +//! \file wizchip_dhcp.h +//! \brief DHCP APIs Header file. +//! \details Processig DHCP protocol as DISCOVER, OFFER, REQUEST, ACK, NACK and DECLINE. +//! \version 1.1.1 +//! \date 2019/10/08 +//! \par Revision history +//! <2019/10/08> compare DHCP server ip address +//! <2013/11/18> 1st Release +//! <2012/12/20> V1.1.0 +//! 1. Move unreferenced DEFINE to dhcp.c +//! <2012/12/26> V1.1.1 +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** +#ifndef _WIZCHIP_DHCP_H_ +#define _WIZCHIP_DHCP_H_ +#ifdef __cplusplus +extern "C" { +#endif + +/* + * @brief + * @details If you want to display debug & processing message, Define _DHCP_DEBUG_ + * @note If defined, it depends on + */ +//#define _DHCP_DEBUG_ + + +/* Retry to processing DHCP */ +#define MAX_DHCP_RETRY 2 ///< Maximum retry count +#define DHCP_WAIT_TIME 10 ///< Wait Time 10s + + +/* UDP port numbers for DHCP */ +#define DHCP_SERVER_PORT 67 ///< DHCP server port number +#define DHCP_CLIENT_PORT 68 ///< DHCP client port number + + +#define MAGIC_COOKIE 0x63825363 ///< You should not modify it number. + +#define DCHP_HOST_NAME "WIZnet\0" + +/* + * @brief return value of @ref DHCP_run() + */ +enum +{ + DHCP_FAILED = 0, ///< Processing Fail + DHCP_RUNNING, ///< Processing DHCP protocol + DHCP_IP_ASSIGN, ///< First Occupy IP from DHPC server (if cbfunc == null, act as default default_ip_assign) + DHCP_IP_CHANGED, ///< Change IP address by new ip from DHCP (if cbfunc == null, act as default default_ip_update) + DHCP_IP_LEASED, ///< Stand by + DHCP_STOPPED ///< Stop processing DHCP protocol +}; + +/* + * @brief DHCP client initialization (outside of the main loop) + * @param s - socket number + * @param buf - buffer for processing DHCP message + */ +void DHCP_init(uint8_t s, uint8_t * buf); + +/* + * @brief DHCP 1s Tick Timer handler + * @note SHOULD BE register to your system 1s Tick timer handler + */ +void DHCP_time_handler(void); + +/* + * @brief Register call back function + * @param ip_assign - callback func when IP is assigned from DHCP server first + * @param ip_update - callback func when IP is changed + * @param ip_conflict - callback func when the assigned IP is conflict with others. + */ +void reg_dhcp_cbfunc(void(*ip_assign)(void), void(*ip_update)(void), void(*ip_conflict)(void)); + +/* + * @brief DHCP client in the main loop + * @return The value is as the follow \n + * @ref DHCP_FAILED \n + * @ref DHCP_RUNNING \n + * @ref DHCP_IP_ASSIGN \n + * @ref DHCP_IP_CHANGED \n + * @ref DHCP_IP_LEASED \n + * @ref DHCP_STOPPED \n + * + * @note This function is always called by you main task. + */ +uint8_t DHCP_run(void); + +/* + * @brief Stop DHCP processing + * @note If you want to restart. call DHCP_init() and DHCP_run() + */ +void DHCP_stop(void); + +/* Get Network information assigned from DHCP server */ +/* + * @brief Get IP address + * @param ip - IP address to be returned + */ +void getIPfromDHCP(uint8_t* ip); +/* + * @brief Get Gateway address + * @param ip - Gateway address to be returned + */ +void getGWfromDHCP(uint8_t* ip); +/* + * @brief Get Subnet mask value + * @param ip - Subnet mask to be returned + */ +void getSNfromDHCP(uint8_t* ip); +/* + * @brief Get DNS address + * @param ip - DNS address to be returned + */ +void getDNSfromDHCP(uint8_t* ip); + +/* + * @brief Get the leased time by DHCP sever + * @return unit 1s + */ +uint32_t getDHCPLeasetime(void); + +uint32_t getDHCPTick1s(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIZCHIP_DHCP_H_ */ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.c new file mode 100644 index 000000000..bf40fedd8 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.c @@ -0,0 +1,564 @@ +//***************************************************************************** +// +//! \file wizchip_dns.c +//! \brief DNS APIs Implement file. +//! \details Send DNS query & Receive DNS reponse. \n +//! It depends on stdlib.h & string.h in ansi-c library +//! \version 1.1.0 +//! \date 2013/11/18 +//! \par Revision history +//! <2013/10/21> 1st Release +//! <2013/12/20> V1.1.0 +//! 1. Remove secondary DNS server in DNS_run +//! If 1st DNS_run failed, call DNS_run with 2nd DNS again +//! 2. DNS_timerHandler -> DNS_time_handler +//! 3. Remove the unused define +//! 4. Integrated dns.h dns.c & dns_parse.h dns_parse.c into dns.h & dns.c +//! <2013/12/20> V1.1.0 +//! +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +#include +#include +#include + +#include "DNS/wizchip_dns.h" + +#ifdef _DNS_DEBUG_ + #include +#endif + +#define INITRTT 2000L /* Initial smoothed response time */ +#define MAXCNAME (MAX_DOMAIN_NAME + (MAX_DOMAIN_NAME>>1)) /* Maximum amount of cname recursion */ + +#define TYPE_A 1 /* Host address */ +#define TYPE_NS 2 /* Name server */ +#define TYPE_MD 3 /* Mail destination (obsolete) */ +#define TYPE_MF 4 /* Mail forwarder (obsolete) */ +#define TYPE_CNAME 5 /* Canonical name */ +#define TYPE_SOA 6 /* Start of Authority */ +#define TYPE_MB 7 /* Mailbox name (experimental) */ +#define TYPE_MG 8 /* Mail group member (experimental) */ +#define TYPE_MR 9 /* Mail rename name (experimental) */ +#define TYPE_NULL 10 /* Null (experimental) */ +#define TYPE_WKS 11 /* Well-known sockets */ +#define TYPE_PTR 12 /* Pointer record */ +#define TYPE_HINFO 13 /* Host information */ +#define TYPE_MINFO 14 /* Mailbox information (experimental)*/ +#define TYPE_MX 15 /* Mail exchanger */ +#define TYPE_TXT 16 /* Text strings */ +#define TYPE_ANY 255 /* Matches any type */ + +#define CLASS_IN 1 /* The ARPA Internet */ + +/* Round trip timing parameters */ +#define AGAIN 8 /* Average RTT gain = 1/8 */ +#define LAGAIN 3 /* Log2(AGAIN) */ +#define DGAIN 4 /* Mean deviation gain = 1/4 */ +#define LDGAIN 2 /* log2(DGAIN) */ + +/* Header for all domain messages */ +struct dhdr +{ + uint16_t id; /* Identification */ + uint8_t qr; /* Query/Response */ +#define QUERY 0 +#define RESPONSE 1 + uint8_t opcode; +#define IQUERY 1 + uint8_t aa; /* Authoratative answer */ + uint8_t tc; /* Truncation */ + uint8_t rd; /* Recursion desired */ + uint8_t ra; /* Recursion available */ + uint8_t rcode; /* Response code */ +#define NO_ERROR 0 +#define FORMAT_ERROR 1 +#define SERVER_FAIL 2 +#define NAME_ERROR 3 +#define NOT_IMPL 4 +#define REFUSED 5 + uint16_t qdcount; /* Question count */ + uint16_t ancount; /* Answer count */ + uint16_t nscount; /* Authority (name server) count */ + uint16_t arcount; /* Additional record count */ +}; + + +uint8_t* pDNSMSG; // DNS message buffer +uint8_t DNS_SOCKET; // SOCKET number for DNS +uint16_t DNS_MSGID; // DNS message ID + +uint32_t dns_1s_tick; // for timout of DNS processing +static uint8_t retry_count; + +/* converts uint16_t from network buffer to a host byte order integer. */ +uint16_t get16(uint8_t * s) +{ + uint16_t i; + i = *s++ << 8; + i = i + *s; + return i; +} + +/* copies uint16_t to the network buffer with network byte order. */ +uint8_t * put16(uint8_t * s, uint16_t i) +{ + *s++ = i >> 8; + *s++ = i; + return s; +} + + +/* + * CONVERT A DOMAIN NAME TO THE HUMAN-READABLE FORM + * + * Description : This function converts a compressed domain name to the human-readable form + * Arguments : msg - is a pointer to the reply message + * compressed - is a pointer to the domain name in reply message. + * buf - is a pointer to the buffer for the human-readable form name. + * len - is the MAX. size of buffer. + * Returns : the length of compressed message + */ +int parse_name(uint8_t * msg, uint8_t * compressed, char * buf, int16_t len) +{ + uint16_t slen; /* Length of current segment */ + uint8_t * cp; + int clen = 0; /* Total length of compressed name */ + int indirect = 0; /* Set if indirection encountered */ + int nseg = 0; /* Total number of segments in name */ + + cp = compressed; + + for (;;) + { + slen = *cp++; /* Length of this segment */ + + if (!indirect) clen++; + + if ((slen & 0xc0) == 0xc0) + { + if (!indirect) + clen++; + indirect = 1; + /* Follow indirection */ + cp = &msg[((slen & 0x3f)<<8) + *cp]; + slen = *cp++; + } + + if (slen == 0) /* zero length == all done */ + break; + + len -= slen + 1; + + if (len < 0) return -1; + + if (!indirect) clen += slen; + + while (slen-- != 0) *buf++ = (char)*cp++; + *buf++ = '.'; + nseg++; + } + + if (nseg == 0) + { + /* Root name; represent as single dot */ + *buf++ = '.'; + len--; + } + + *buf++ = '\0'; + len--; + + return clen; /* Length of compressed message */ +} + +/* + * PARSE QUESTION SECTION + * + * Description : This function parses the qeustion record of the reply message. + * Arguments : msg - is a pointer to the reply message + * cp - is a pointer to the qeustion record. + * Returns : a pointer the to next record. + */ +uint8_t * dns_question(uint8_t * msg, uint8_t * cp) +{ + int len; + char name[MAXCNAME]; + + len = parse_name(msg, cp, name, MAXCNAME); + + + if (len == -1) return 0; + + cp += len; + cp += 2; /* type */ + cp += 2; /* class */ + + return cp; +} + + +/* + * PARSE ANSER SECTION + * + * Description : This function parses the answer record of the reply message. + * Arguments : msg - is a pointer to the reply message + * cp - is a pointer to the answer record. + * Returns : a pointer the to next record. + */ +uint8_t * dns_answer(uint8_t * msg, uint8_t * cp, uint8_t * ip_from_dns) +{ + int len, type; + char name[MAXCNAME]; + + len = parse_name(msg, cp, name, MAXCNAME); + + if (len == -1) return 0; + + cp += len; + type = get16(cp); + cp += 2; /* type */ + cp += 2; /* class */ + cp += 4; /* ttl */ + cp += 2; /* len */ + + + switch (type) + { + case TYPE_A: + /* Just read the address directly into the structure */ + ip_from_dns[0] = *cp++; + ip_from_dns[1] = *cp++; + ip_from_dns[2] = *cp++; + ip_from_dns[3] = *cp++; + break; + case TYPE_CNAME: + case TYPE_MB: + case TYPE_MG: + case TYPE_MR: + case TYPE_NS: + case TYPE_PTR: + /* These types all consist of a single domain name */ + /* convert it to ascii format */ + len = parse_name(msg, cp, name, MAXCNAME); + if (len == -1) return 0; + + cp += len; + break; + case TYPE_HINFO: + len = *cp++; + cp += len; + + len = *cp++; + cp += len; + break; + case TYPE_MX: + cp += 2; + /* Get domain name of exchanger */ + len = parse_name(msg, cp, name, MAXCNAME); + if (len == -1) return 0; + + cp += len; + break; + case TYPE_SOA: + /* Get domain name of name server */ + len = parse_name(msg, cp, name, MAXCNAME); + if (len == -1) return 0; + + cp += len; + + /* Get domain name of responsible person */ + len = parse_name(msg, cp, name, MAXCNAME); + if (len == -1) return 0; + + cp += len; + + cp += 4; + cp += 4; + cp += 4; + cp += 4; + cp += 4; + break; + case TYPE_TXT: + /* Just stash */ + break; + default: + /* Ignore */ + break; + } + + return cp; +} + +/* + * PARSE THE DNS REPLY + * + * Description : This function parses the reply message from DNS server. + * Arguments : dhdr - is a pointer to the header for DNS message + * buf - is a pointer to the reply message. + * len - is the size of reply message. + * Returns : -1 - Domain name lenght is too big + * 0 - Fail (Timout or parse error) + * 1 - Success, + */ +int8_t parseDNSMSG(struct dhdr * pdhdr, uint8_t * pbuf, uint8_t * ip_from_dns) +{ + uint16_t tmp; + uint16_t i; + uint8_t * msg; + uint8_t * cp; + + msg = pbuf; + memset(pdhdr, 0, sizeof(*pdhdr)); + + pdhdr->id = get16(&msg[0]); + tmp = get16(&msg[2]); + if (tmp & 0x8000) pdhdr->qr = 1; + + pdhdr->opcode = (tmp >> 11) & 0xf; + + if (tmp & 0x0400) pdhdr->aa = 1; + if (tmp & 0x0200) pdhdr->tc = 1; + if (tmp & 0x0100) pdhdr->rd = 1; + if (tmp & 0x0080) pdhdr->ra = 1; + + pdhdr->rcode = tmp & 0xf; + pdhdr->qdcount = get16(&msg[4]); + pdhdr->ancount = get16(&msg[6]); + pdhdr->nscount = get16(&msg[8]); + pdhdr->arcount = get16(&msg[10]); + + + /* Now parse the variable length sections */ + cp = &msg[12]; + + /* Question section */ + for (i = 0; i < pdhdr->qdcount; i++) + { + cp = dns_question(msg, cp); + #ifdef _DNS_DEUBG_ + printf("MAX_DOMAIN_NAME is too small, it should be redfine in dns.h"); + #endif + if(!cp) return -1; + } + + /* Answer section */ + for (i = 0; i < pdhdr->ancount; i++) + { + cp = dns_answer(msg, cp, ip_from_dns); + #ifdef _DNS_DEUBG_ + printf("MAX_DOMAIN_NAME is too small, it should be redfine in dns.h"); + #endif + if(!cp) return -1; + } + + /* Name server (authority) section */ + for (i = 0; i < pdhdr->nscount; i++) + { + ; + } + + /* Additional section */ + for (i = 0; i < pdhdr->arcount; i++) + { + ; + } + + if(pdhdr->rcode == 0) return 1; // No error + else return 0; +} + + +/* + * MAKE DNS QUERY MESSAGE + * + * Description : This function makes DNS query message. + * Arguments : op - Recursion desired + * name - is a pointer to the domain name. + * buf - is a pointer to the buffer for DNS message. + * len - is the MAX. size of buffer. + * Returns : the pointer to the DNS message. + */ +int16_t dns_makequery(uint16_t op, char * name, uint8_t * buf, uint16_t len) +{ + uint8_t *cp; + char *cp1; + char sname[MAXCNAME]; + char *dname; + uint16_t p; + uint16_t dlen; + + cp = buf; + + DNS_MSGID++; + cp = put16(cp, DNS_MSGID); + p = (op << 11) | 0x0100; /* Recursion desired */ + cp = put16(cp, p); + cp = put16(cp, 1); + cp = put16(cp, 0); + cp = put16(cp, 0); + cp = put16(cp, 0); + + strcpy(sname, name); + dname = sname; + dlen = strlen(dname); + for (;;) + { + /* Look for next dot */ + cp1 = strchr(dname, '.'); + + if (cp1 != NULL) len = cp1 - dname; /* More to come */ + else len = dlen; /* Last component */ + + *cp++ = len; /* Write length of component */ + if (len == 0) break; + + /* Copy component up to (but not including) dot */ + strncpy((char *)cp, dname, len); + cp += len; + if (cp1 == NULL) + { + *cp++ = 0; /* Last one; write null and finish */ + break; + } + dname += len+1; + dlen -= len+1; + } + + cp = put16(cp, 0x0001); /* type */ + cp = put16(cp, 0x0001); /* class */ + + return ((int16_t)((uint32_t)(cp) - (uint32_t)(buf))); +} + +/* + * CHECK DNS TIMEOUT + * + * Description : This function check the DNS timeout + * Arguments : None. + * Returns : -1 - timeout occurred, 0 - timer over, but no timeout, 1 - no timer over, no timeout occur + * Note : timeout : retry count and timer both over. + */ + +int8_t check_DNS_timeout(void) +{ + + if(dns_1s_tick >= DNS_WAIT_TIME) + { + dns_1s_tick = 0; + if(retry_count >= MAX_DNS_RETRY) { + retry_count = 0; + return -1; // timeout occurred + } + retry_count++; + return 0; // timer over, but no timeout + } + + return 1; // no timer over, no timeout occur +} + + + +/* DNS CLIENT INIT */ +void DNS_init(uint8_t s, uint8_t * buf) +{ + DNS_SOCKET = s; // SOCK_DNS + pDNSMSG = buf; // User's shared buffer + DNS_MSGID = DNS_MSG_ID; +} + +/* DNS CLIENT RUN */ +int8_t DNS_run(uint8_t * dns_ip, uint8_t * name, uint8_t * ip_from_dns) +{ + int8_t ret; + struct dhdr dhp; + uint8_t ip[4]; + uint16_t len, port; + int8_t ret_check_timeout; + + retry_count = 0; + dns_1s_tick = 0; + + // Socket open + wizchip_socket(DNS_SOCKET, Sn_MR_UDP, 0, 0); + +#ifdef _DNS_DEBUG_ + printf("> DNS Query to DNS Server : %d.%d.%d.%d\r\n", dns_ip[0], dns_ip[1], dns_ip[2], dns_ip[3]); +#endif + + len = dns_makequery(0, (char *)name, pDNSMSG, MAX_DNS_BUF_SIZE); + wizchip_sendto(DNS_SOCKET, pDNSMSG, len, dns_ip, IPPORT_DOMAIN); + + while (1) + { + if ((len = getSn_RX_RSR(DNS_SOCKET)) > 0) + { + if (len > MAX_DNS_BUF_SIZE) len = MAX_DNS_BUF_SIZE; + len = wizchip_recvfrom(DNS_SOCKET, pDNSMSG, len, ip, &port); + #ifdef _DNS_DEBUG_ + printf("> Receive DNS message from %d.%d.%d.%d(%d). len = %d\r\n", ip[0], ip[1], ip[2], ip[3],port,len); + #endif + ret = parseDNSMSG(&dhp, pDNSMSG, ip_from_dns); + break; + } + // Check Timeout + ret_check_timeout = check_DNS_timeout(); + if (ret_check_timeout < 0) { + +#ifdef _DNS_DEBUG_ + printf("> DNS Server is not responding : %d.%d.%d.%d\r\n", dns_ip[0], dns_ip[1], dns_ip[2], dns_ip[3]); +#endif + wizchip_close(DNS_SOCKET); + return 0; // timeout occurred + } + else if (ret_check_timeout == 0) { + +#ifdef _DNS_DEBUG_ + printf("> DNS Timeout\r\n"); +#endif + wizchip_sendto(DNS_SOCKET, pDNSMSG, len, dns_ip, IPPORT_DOMAIN); + } + } + wizchip_close(DNS_SOCKET); + // Return value + // 0 > : failed / 1 - success + return ret; +} + + +/* DNS TIMER HANDLER */ +void DNS_time_handler(void) +{ + dns_1s_tick++; +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.h new file mode 100644 index 000000000..31ef51734 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/ioLibrary/Internet/DNS/wizchip_dns.h @@ -0,0 +1,109 @@ +//***************************************************************************** +// +//! \file wizchip_dns.h +//! \brief DNS APIs Header file. +//! \details Send DNS query & Receive DNS reponse. +//! \version 1.1.0 +//! \date 2013/11/18 +//! \par Revision history +//! <2013/10/21> 1st Release +//! <2013/12/20> V1.1.0 +//! 1. Remove secondary DNS server in DNS_run +//! If 1st DNS_run failed, call DNS_run with 2nd DNS again +//! 2. DNS_timerHandler -> DNS_time_handler +//! 3. Move the no reference define to dns.c +//! 4. Integrated dns.h dns.c & dns_parse.h dns_parse.c into dns.h & dns.c +//! <2013/12/20> V1.1.0 +//! +//! \author Eric Jung & MidnightCow +//! \copyright +//! +//! Copyright (c) 2013, WIZnet Co., LTD. +//! All rights reserved. +//! +//! Redistribution and use in source and binary forms, with or without +//! modification, are permitted provided that the following conditions +//! are met: +//! +//! * Redistributions of source code must retain the above copyright +//! notice, this list of conditions and the following disclaimer. +//! * Redistributions in binary form must reproduce the above copyright +//! notice, this list of conditions and the following disclaimer in the +//! documentation and/or other materials provided with the distribution. +//! * Neither the name of the nor the names of its +//! contributors may be used to endorse or promote products derived +//! from this software without specific prior written permission. +//! +//! THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +//! AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +//! IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +//! ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +//! LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +//! CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +//! SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +//! INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +//! CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +//! ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +//! THE POSSIBILITY OF SUCH DAMAGE. +// +//***************************************************************************** + +#ifndef _WIZCHIP_DNS_H_ +#define _WIZCHIP_DNS_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +/* + * @brief Define it for Debug & Monitor DNS processing. + * @note If defined, it dependens on + */ +//#define _DNS_DEBUG_ + +#define MAX_DNS_BUF_SIZE 256 ///< maximum size of DNS buffer. */ +/* + * @brief Maxium length of your queried Domain name + * @todo SHOULD BE defined it equal as or greater than your Domain name lenght + null character(1) + * @note SHOULD BE careful to stack overflow because it is allocated 1.5 times as MAX_DOMAIN_NAME in stack. + */ +#define MAX_DOMAIN_NAME 16 // for example "www.google.com" + +#define MAX_DNS_RETRY 2 ///< Requery Count +#define DNS_WAIT_TIME 3 ///< Wait response time. unit 1s. + +#define IPPORT_DOMAIN 53 ///< DNS server port number + +#define DNS_MSG_ID 0x1122 ///< ID for DNS message. You can be modifyed it any number +/* + * @brief DNS process initialize + * @param s : Socket number for DNS + * @param buf : Buffer for DNS message + */ +void DNS_init(uint8_t s, uint8_t * buf); + +/* + * @brief DNS process + * @details Send DNS query and receive DNS response + * @param dns_ip : DNS server ip + * @param name : Domain name to be queryed + * @param ip_from_dns : IP address from DNS server + * @return -1 : failed. @ref MAX_DOMIN_NAME is too small \n + * 0 : failed (Timeout or Parse error)\n + * 1 : success + * @note This funtion blocks until success or fail. max time = @ref MAX_DNS_RETRY * @ref DNS_WAIT_TIME + */ +int8_t DNS_run(uint8_t * dns_ip, uint8_t * name, uint8_t * ip_from_dns); + +/* + * @brief DNS 1s Tick Timer handler + * @note SHOULD BE register to your system 1s Tick timer handler + */ +void DNS_time_handler(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _WIZCHIP_DNS_H_ */ diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz.c new file mode 100644 index 000000000..6e8105cad --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz.c @@ -0,0 +1,987 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#include +#include +#include + +#include +#include +//h此处为添加内容 +#ifdef __cplusplus +extern "C"{ +#endif +#include "drv_spi.h" +#ifdef __cplusplus +} +#endif +//添加内容结束 + +#include +#ifdef WIZ_USING_DHCP +#include +#endif + +#include +#include + +#if !defined(WIZ_SPI_DEVICE) || !defined(WIZ_RST_PIN) || !defined(WIZ_IRQ_PIN) +#error "please config SPI device name, reset pin and irq pin in menuconfig." +#endif + +#define DBG_ENABLE +#define DBG_SECTION_NAME "wiz" +#ifdef WIZ_DEBUG +#define DBG_LEVEL DBG_LOG +#else +#define DBG_LEVEL DBG_INFO +#endif /* WIZ_DEBUG */ +#define DBG_COLOR +#include + +#define IMR_SENDOK 0x10 +#define IMR_TIMEOUT 0x08 +#define IMR_RECV 0x04 +#define IMR_DISCON 0x02 +#define IMR_CON 0x01 +#define WIZ_DEFAULT_MAC "00-E0-81-DC-53-1A" + +#define WIZ_ID_LEN 6 +static char wiz_netdev_name[WIZ_ID_LEN]; + +#define WIZ_DHCP_SOCKET 7 + +extern struct rt_spi_device *wiz_device; +extern int wiz_device_init(const char *spi_dev_name); +extern int wiz_inet_init(void); +static int wiz_netdev_info_update(struct netdev *netdev, rt_bool_t reset); + +rt_bool_t wiz_init_ok = RT_FALSE; +static wiz_NetInfo wiz_net_info; +static rt_timer_t dns_tick_timer; + +#ifdef WIZ_USING_DHCP +static rt_timer_t dhcp_timer; +#endif +static struct rt_work *dhcp_work = RT_NULL; +extern int wiz_recv_notice_cb(int socket); +extern int wiz_closed_notice_cb(int socket); + +static rt_mailbox_t wiz_rx_mb = RT_NULL; + +static void wiz_isr(void) +{ + /* enter interrupt */ + rt_interrupt_enter(); + + rt_mb_send(wiz_rx_mb, (rt_ubase_t) wiz_device); + + /* leave interrupt */ + rt_interrupt_leave(); +} + +static void wiz_data_thread_entry(void *parameter) +{ +#define IR_SOCK(ch) (0x01 << ch) /**< check socket interrupt */ + + struct rt_spi_device* dev; + + while (1) + { + if (rt_mb_recv(wiz_rx_mb, (rt_ubase_t*) &dev, RT_WAITING_FOREVER) == RT_EOK) + { + uint8_t ir, sir, sn_ir; + int8_t socket = -1; + + /* get IR data than clean IR */ + ir = getIR(); + setIR(ir); + + if ((ir & IR_CONFLICT) == IR_CONFLICT) + { + setIR(IR_CONFLICT); + } + + if ((ir & IR_UNREACH) == IR_UNREACH) + { + setIR(IR_UNREACH); + } + + /* get and process socket interrupt register */ + sir = getSIR(); + + for (socket = 0; socket < 8; socket++) + { + sn_ir = 0; + + if (sir & IR_SOCK(socket)) + { + /* save interrupt value*/ + sn_ir = getSn_IR(socket); + + if (sn_ir & Sn_IR_CON) + { + setSn_IR(socket, Sn_IR_CON); + } + if (sn_ir & Sn_IR_DISCON) + { + wiz_closed_notice_cb(socket); + setSn_IR(socket, Sn_IR_DISCON); + } + if (sn_ir & Sn_IR_RECV) + { + wiz_recv_notice_cb(socket); + setSn_IR(socket, Sn_IR_RECV); + } + if (sn_ir & Sn_IR_TIMEOUT) + { + /* deal with timeout event in the wiznet ioLibrary */ + //setSn_IR(socket, Sn_IR_TIMEOUT); + } + } + } + } + } +} + +static void spi_write_byte(uint8_t data) +{ + struct rt_spi_message spi_msg; + + rt_memset(&spi_msg, 0x00, sizeof(spi_msg)); + + spi_msg.send_buf = &data; + spi_msg.length = 1; + + rt_spi_transfer_message(wiz_device, &spi_msg); +} + +static uint8_t spi_read_byte(void) +{ + struct rt_spi_message spi_msg; + uint8_t data; + + rt_memset(&spi_msg, 0x00, sizeof(spi_msg)); + + spi_msg.recv_buf = &data; + spi_msg.length = 1; + + rt_spi_transfer_message(wiz_device, &spi_msg); + + return data; +} + +static void spi_write_burst(uint8_t *pbuf, uint16_t len) +{ + struct rt_spi_message spi_msg; + + rt_memset(&spi_msg, 0x00, sizeof(spi_msg)); + + spi_msg.send_buf = pbuf; + spi_msg.length = len; + + rt_spi_transfer_message(wiz_device, &spi_msg); +} + +static void spi_read_burst(uint8_t *pbuf, uint16_t len) +{ + struct rt_spi_message spi_msg; + + rt_memset(&spi_msg, 0x00, sizeof(spi_msg)); + + spi_msg.recv_buf = pbuf; + spi_msg.length = len; + + rt_spi_transfer_message(wiz_device, &spi_msg); +} + +static void spi_cris_enter(void) +{ + rt_spi_take_bus(wiz_device); +} + +static void spi_cris_exit(void) +{ + rt_spi_release_bus(wiz_device); +} + +static void spi_cs_select(void) +{ + rt_spi_take(wiz_device); +} + +static void spi_cs_deselect(void) +{ + rt_spi_release(wiz_device); +} + +/* register TCP communication related callback function */ +static int wiz_callback_register(void) +{ + /* register critical section callback function */ + reg_wizchip_cris_cbfunc(spi_cris_enter, spi_cris_exit); + +#if (_WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_VDM_) || (_WIZCHIP_IO_MODE_ == _WIZCHIP_IO_MODE_SPI_FDM_) + /* register SPI device CS select callback function */ + reg_wizchip_cs_cbfunc(spi_cs_select, spi_cs_deselect); +#else +#if (_WIZCHIP_IO_MODE_ & _WIZCHIP_IO_MODE_SIP_) != _WIZCHIP_IO_MODE_SIP_ +#error "Unknown _WIZCHIP_IO_MODE_" +#else + reg_wizchip_cs_cbfunc(wizchip_select, wizchip_deselect); +#endif +#endif + /* register SPI device read/write data callback function */ + reg_wizchip_spi_cbfunc(spi_read_byte, spi_write_byte); + reg_wizchip_spiburst_cbfunc(spi_read_burst, spi_write_burst); + + return RT_EOK; +} + +/* initialize WIZnet chip configures */ +static int wiz_chip_cfg_init(void) +{ +#define CW_INIT_MODE 2 +#define CW_INIT_SOCKETS 8 +#define CW_INIT_TIMEOUT (2 * RT_TICK_PER_SECOND) + + uint8_t memsize[CW_INIT_MODE][CW_INIT_SOCKETS] = { 0 }; + + /* reset WIZnet chip internal PHY, configures PHY mode. */ + if (ctlwizchip(CW_INIT_WIZCHIP, (void*) memsize) == -1) + { + LOG_E("WIZCHIP initialize failed."); + return -RT_ERROR; + } + + struct wiz_NetTimeout_t net_timeout; + net_timeout.retry_cnt=5; + net_timeout.time_100us=20000; + ctlnetwork(CN_SET_TIMEOUT, (void*) &net_timeout); + + return RT_EOK; +} + +/* WIZnet chip hardware reset */ +static void wiz_reset(void) +{ + rt_pin_write(WIZ_RST_PIN, PIN_LOW); + rt_thread_mdelay(2); + + rt_pin_write(WIZ_RST_PIN, PIN_HIGH); + rt_thread_mdelay(2); +} + +#ifdef WIZ_USING_DHCP +static void wiz_ip_assign(void) +{ + /* get the assigned IP address and reconfigure the IP address of the chip */ + getIPfromDHCP(wiz_net_info.ip); + getGWfromDHCP(wiz_net_info.gw); + getSNfromDHCP(wiz_net_info.sn); + getDNSfromDHCP(wiz_net_info.dns); + wiz_net_info.dhcp = NETINFO_DHCP; + + ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info); +} + +static void wiz_ip_conflict(void) +{ + /* deal with conflict IP for WIZnet DHCP */ + LOG_D("conflict IP from DHCP."); + RT_ASSERT(0); +} + +static void wiz_dhcp_timer_entry(void *parameter) +{ + DHCP_time_handler(); +} +#endif /* WIZ_USING_DHCP */ + +static int wiz_netstr_to_array(const char *net_str, uint8_t *net_array) +{ + int ret; + unsigned int idx; + + RT_ASSERT(net_str); + RT_ASSERT(net_array); + + if (strstr(net_str, ".")) + { + int ip_addr[4]; + + /* resolve IP address, gateway address or subnet mask */ + ret = sscanf(net_str, "%d.%d.%d.%d", ip_addr + 0, ip_addr + 1, ip_addr + 2, ip_addr + 3); + if (ret != 4) + { + LOG_E("input address(%s) resolve error.", net_str); + return -RT_ERROR; + } + + for (idx = 0; idx < sizeof(ip_addr)/sizeof(ip_addr[0]); idx++) + { + net_array[idx] = ip_addr[idx]; + } + } + else + { + int mac_addr[6]; + + /* resolve MAC address */ + if (strstr(net_str, ":")) + { + ret = sscanf(net_str, "%02x:%02x:%02x:%02x:%02x:%02x", mac_addr + 0, mac_addr + 1, mac_addr + 2, + mac_addr + 3, mac_addr + 4, mac_addr + 5); + } + else if (strstr(net_str, "-")) + { + ret = sscanf(net_str, "%02x-%02x-%02x-%02x-%02x-%02x", mac_addr + 0, mac_addr + 1, mac_addr + 2, + mac_addr + 3, mac_addr + 4, mac_addr + 5); + } + else + { + LOG_E("input MAC address(%s) format error.", net_str); + return -RT_ERROR; + } + + if (ret != 6) + { + LOG_E("input MAC address(%s) resolve error.", net_str); + return -RT_ERROR; + } + + for (idx = 0; idx < sizeof(mac_addr)/sizeof(mac_addr[0]); idx++) + { + net_array[idx] = mac_addr[idx]; + } + } + + return RT_EOK; +} + +/* set WIZnet device MAC address */ +RT_WEAK void wiz_user_config_mac(char *mac_buf, rt_uint8_t buf_len) +{ + RT_ASSERT(mac_buf != RT_NULL); + RT_ASSERT(buf_len > 0); + + rt_memset(mac_buf, 0x0, buf_len); + rt_strncpy(mac_buf, WIZ_DEFAULT_MAC, buf_len); +} + +static void wiz_set_mac(void) +{ + char mac_str[32]; + + wiz_user_config_mac(mac_str, sizeof(mac_str)); + if (wiz_netstr_to_array(mac_str, wiz_net_info.mac) != RT_EOK) + { + wiz_netstr_to_array(WIZ_DEFAULT_MAC, wiz_net_info.mac); + } +} + +#ifdef WIZ_USING_DHCP +static int wiz_network_dhcp(struct netdev *netdev); +#endif + +/* initialize WIZnet network configures */ +static int wiz_network_init(rt_bool_t b_config) +{ + struct netdev * netdev; + netdev = netdev_get_by_name(wiz_netdev_name); + if (netdev == RT_NULL) + { + LOG_E("don`t find device(%s)", wiz_netdev_name); + return -RT_ERROR; + } + +#ifndef WIZ_USING_DHCP + if(wiz_netstr_to_array(WIZ_IPADDR, wiz_net_info.ip) != RT_EOK || + wiz_netstr_to_array(WIZ_MSKADDR, wiz_net_info.sn) != RT_EOK || + wiz_netstr_to_array(WIZ_GWADDR, wiz_net_info.dns) != RT_EOK || + wiz_netstr_to_array(WIZ_GWADDR, wiz_net_info.gw) != RT_EOK) + { + netdev_low_level_set_status(netdev, RT_FALSE); + netdev_low_level_set_link_status(netdev, RT_FALSE); + return -RT_ERROR; + } + wiz_net_info.dhcp = NETINFO_STATIC; +#endif + + int result = RT_EOK; + rt_bool_t b_status = b_config; + + /* set mac information */ + wiz_set_mac(); + /* set static WIZnet network information */ + ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info); + +#ifdef WIZ_USING_DHCP + /* alloc IP address through DHCP */ + { + result = wiz_network_dhcp(netdev); + if (result != RT_EOK) + { + b_status = RT_FALSE; + LOG_E("WIZnet network initialize failed, DHCP timeout."); + } + else + { + b_status = RT_TRUE; + LOG_D("WIZnet network initialize success."); + } + } +#endif + + netdev_low_level_set_status(netdev, b_status); + wiz_netdev_info_update(netdev, RT_FALSE); + netdev_low_level_set_link_status(netdev, b_status); + + return result; +} + +/* wizenet socket initialize */ +static int wiz_socket_init(void) +{ + int idx = 0; + + /* socket(0-7) initialize */ + setSIMR(0xff); + + /* set socket receive/send buffer size */ + for (idx = 0; idx < WIZ_SOCKETS_NUM; idx++) + { + setSn_RXBUF_SIZE(idx, 0x02); + setSn_TXBUF_SIZE(idx, 0x02); + } + + /* set socket ISR state support */ + for (idx = 0; idx < WIZ_SOCKETS_NUM; idx++) + { + setSn_IMR(idx, (IMR_TIMEOUT | IMR_RECV | IMR_DISCON)); + } + + return RT_EOK; +} + +static void wiz_dns_time_handler(void *arg) +{ + extern void DNS_time_handler(void); + DNS_time_handler(); +} + +static int wiz_netdev_info_update(struct netdev *netdev, rt_bool_t reset) +{ + wiz_NetInfo net_info; + rt_memset(&net_info, 0, sizeof(net_info)); + + if(reset == RT_FALSE) + { + ctlnetwork(CN_GET_NETINFO, (void *)&net_info); + } + else + { + /* clean dns server information */ + netdev->dns_servers->addr = 0; + ctlnetwork(CN_SET_NETINFO, (void *)&net_info); + } + netdev_low_level_set_ipaddr(netdev, (const ip_addr_t *)&net_info.ip); + netdev_low_level_set_gw(netdev, (const ip_addr_t *)&net_info.gw); + netdev_low_level_set_netmask(netdev, (const ip_addr_t *)&net_info.sn); + netdev_low_level_set_dns_server(netdev, 0, (const ip_addr_t *)&net_info.dns); + memcpy(netdev->hwaddr, (const void *)&net_info.mac, netdev->hwaddr_len); + /* 1 - Static, 2 - DHCP */ + netdev_low_level_set_dhcp_status(netdev, net_info.dhcp - 1); + + return RT_EOK; +} + +static int wiz_netdev_set_up(struct netdev *netdev) +{ + netdev_low_level_set_status(netdev, RT_TRUE); + return RT_EOK; +} + +static int wiz_netdev_set_down(struct netdev *netdev) +{ + netdev_low_level_set_status(netdev, RT_FALSE); + return RT_EOK; +} + +static int wiz_netdev_set_addr_info(struct netdev *netdev, ip_addr_t *ip_addr, ip_addr_t *netmask, ip_addr_t *gw) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(netdev); + RT_ASSERT(ip_addr || netmask || gw); + + ctlnetwork(CN_GET_NETINFO, (void *)&wiz_net_info); + + if (ip_addr) + rt_memcpy(wiz_net_info.ip, &ip_addr->addr, sizeof(wiz_net_info.ip)); + + if (netmask) + rt_memcpy(wiz_net_info.sn, &netmask->addr, sizeof(wiz_net_info.sn)); + + if (gw) + rt_memcpy(wiz_net_info.gw, &gw->addr, sizeof(wiz_net_info.gw)); + + if (ctlnetwork(CN_SET_NETINFO, (void *)&wiz_net_info) == RT_EOK) + { + if (ip_addr) + netdev_low_level_set_ipaddr(netdev, ip_addr); + + if (netmask) + netdev_low_level_set_netmask(netdev, netmask); + + if (gw) + netdev_low_level_set_gw(netdev, gw); + + result = RT_EOK; + } + else + { + LOG_E("%s set addr info failed!", wiz_netdev_name); + result = -RT_ERROR; + } + + return result; +} + +static int wiz_netdev_set_dns_server(struct netdev *netdev, uint8_t dns_num, ip_addr_t *dns_server) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(netdev); + RT_ASSERT(dns_server); + + ctlnetwork(CN_GET_NETINFO, (void *)&wiz_net_info); + + rt_memcpy(wiz_net_info.dns, &dns_server->addr, sizeof(wiz_net_info.dns)); + + if (ctlnetwork(CN_SET_NETINFO, (void *)&wiz_net_info) == RT_EOK) + { + netdev_low_level_set_dns_server(netdev, dns_num, (const ip_addr_t *)dns_server); + result = RT_EOK; + } + else + { + LOG_E("%s set dns server failed!", wiz_netdev_name); + result = -RT_ERROR; + } + + return result; +} + +static int wiz_netdev_set_dhcp(struct netdev *netdev, rt_bool_t is_enabled) +{ + rt_err_t result = RT_EOK; + + RT_ASSERT(netdev); + + ctlnetwork(CN_GET_NETINFO, (void *)&wiz_net_info); + + /* 1 - Static, 2 - DHCP */ + wiz_net_info.dhcp = (dhcp_mode)(is_enabled + 1); + + if (ctlnetwork(CN_SET_NETINFO, (void *)&wiz_net_info) == RT_EOK) + { + netdev_low_level_set_dhcp_status(netdev, is_enabled); + result = RT_EOK; + + if(is_enabled == RT_FALSE) + { + if(dhcp_work != RT_NULL) + { + rt_work_cancel(dhcp_work); + } + LOG_D("wiznet(w5500) dhcp status is disable."); + } + else + { +#ifndef WIZ_USING_DHCP + LOG_W("wiznet(w5500) dhcp function haven't compiled."); + result = -RT_ERROR; +#else + if(dhcp_work != RT_NULL) + { + rt_work_submit(dhcp_work, RT_WAITING_NO); + LOG_D("wiznet(w5500) dhcp status is enable."); + } +#endif + } + } + else + { + LOG_E("%s set dhcp info failed!", wiz_netdev_name); + result = -RT_ERROR; + } + + return result; +} + +#ifdef RT_USING_FINSH +static int wiz_netdev_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t timeout, struct netdev_ping_resp *ping_resp) +{ + RT_ASSERT(netdev); + RT_ASSERT(host); + RT_ASSERT(ping_resp); + + extern int wiz_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t times, struct netdev_ping_resp *ping_resp); + + return wiz_ping(netdev, host, data_len, timeout, ping_resp); +} +#endif + +void wiz_netdev_netstat(struct netdev *netdev) +{ + // TODO + return; +} + +const struct netdev_ops wiz_netdev_ops = +{ + wiz_netdev_set_up, + wiz_netdev_set_down, + + wiz_netdev_set_addr_info, + wiz_netdev_set_dns_server, + wiz_netdev_set_dhcp, +#ifdef RT_USING_FINSH + wiz_netdev_ping, + wiz_netdev_netstat, +#endif +}; + +static struct netdev *wiz_netdev_add(const char *netdev_name) +{ +#define ETHERNET_MTU 1472 +#define HWADDR_LEN 6 + struct netdev *netdev = RT_NULL; + + netdev = (struct netdev *)rt_calloc(1, sizeof(struct netdev)); + if (netdev == RT_NULL) + { + return RT_NULL; + } + + netdev->flags = 0; + netdev->mtu = ETHERNET_MTU; + netdev->ops = &wiz_netdev_ops; + netdev->hwaddr_len = HWADDR_LEN; + +#ifdef PKG_USING_WIZNET + extern int sal_wiz_netdev_set_pf_info(struct netdev *netdev); + /* set the network interface socket/netdb operations */ + sal_wiz_netdev_set_pf_info(netdev); +#endif + + netdev_register(netdev, netdev_name, RT_NULL); + + return netdev; +} + +#ifdef WIZ_USING_DHCP +static void wiz_dhcp_work(struct rt_work *dhcp_work, void *dhcp_work_data) +{ +#define WIZ_DHCP_WORK_RETRY 3 /* DHCP will have 3 times handshake */ +#define WIZ_DHCP_WORK_RETRY_TIME (2 * RT_TICK_PER_SECOND) + static int wiz_dhcp_retry_times = WIZ_DHCP_WORK_RETRY * 20; + + RT_ASSERT(dhcp_work_data != RT_NULL); + + struct netdev *netdev = (struct netdev *)dhcp_work_data; + + uint8_t dhcp_times = 0; + uint32_t dhcp_work_times; + static uint8_t data_buffer[1024]; + static uint32_t dhcp_status = DHCP_FAILED; + + if(dhcp_status == DHCP_FAILED) + { + DHCP_init(WIZ_DHCP_SOCKET, data_buffer); + rt_timer_start(dhcp_timer); + } + + + while (1) + { + /* DHCP start, return DHCP_IP_LEASED is success. */ + dhcp_status = DHCP_run(); + + switch (dhcp_status) + { + case DHCP_IP_ASSIGN: + case DHCP_IP_CHANGED: + { + /* to update netdev information */ + wiz_netdev_info_update(netdev, RT_FALSE); + break; + } + case DHCP_IP_LEASED: + { + int hour, min; + /* to update netdev information */ + wiz_netdev_info_update(netdev, RT_FALSE); + + /* reset the previous work configure */ + rt_work_cancel(dhcp_work); + dhcp_work_times = (getDHCPTick1s() > getDHCPLeasetime() / 2) ? + 0 : getDHCPLeasetime() / 2 - getDHCPTick1s(); + /* according to the DHCP leaset time, config next DHCP produce */ + rt_work_submit(dhcp_work, (dhcp_work_times+1) * RT_TICK_PER_SECOND); + hour = getDHCPLeasetime() / 3600; + min = (getDHCPLeasetime() % 3600) / 60; + LOG_D("DHCP countdown to lease renewal [%dH: %dMin], retry time[%04d]", hour, min, dhcp_times); + wiz_dhcp_retry_times = WIZ_DHCP_WORK_RETRY * 20; + return; + } + case DHCP_STOPPED: + dhcp_times = wiz_dhcp_retry_times; + break; + case DHCP_FAILED: + { + dhcp_times = wiz_dhcp_retry_times; + LOG_E("dhcp handshake failed!"); + break; + } + default: + { + dhcp_times++; + + /* DHCP_RUNNING status, include don't receive data */ + rt_thread_mdelay(10); + break; + } + } + + if (dhcp_times >= wiz_dhcp_retry_times) + { + LOG_D("DHCP work in %d seconds, [%03d|%03d]", WIZ_DHCP_WORK_RETRY_TIME / RT_TICK_PER_SECOND, dhcp_times, wiz_dhcp_retry_times); + + /* if dhcp service is too busy to manger IP, increase retry times */ + wiz_dhcp_retry_times = wiz_dhcp_retry_times + WIZ_DHCP_WORK_RETRY; + + DHCP_stop(); + dhcp_status = DHCP_FAILED; + rt_timer_stop(dhcp_timer); + + rt_work_cancel(dhcp_work); + + /* according to WIZ_DHCP_WORK_RETRY_TIME, reconfigure in 2 seconds */ + rt_work_submit(dhcp_work, WIZ_DHCP_WORK_RETRY_TIME); + break; + } + } +} + +static int wiz_network_dhcp(struct netdev *netdev) +{ + if (netdev == RT_NULL) + return -RT_EINVAL; + + /* set default MAC address for DHCP */ + setSHAR(wiz_net_info.mac); + /* DHCP configure initialize, clear information other than MAC address */ + setSn_RXBUF_SIZE(WIZ_DHCP_SOCKET, 0x02); + setSn_TXBUF_SIZE(WIZ_DHCP_SOCKET, 0x02); + /* register to assign IP address and conflict callback */ + reg_dhcp_cbfunc(wiz_ip_assign, wiz_ip_assign, wiz_ip_conflict); + + dhcp_timer = rt_timer_create("wiz_dhcp", wiz_dhcp_timer_entry, RT_NULL, 1 * RT_TICK_PER_SECOND, RT_TIMER_FLAG_PERIODIC); + if (dhcp_timer == RT_NULL) + return -RT_ERROR; + + dhcp_work = (struct rt_work *)rt_calloc(1, sizeof(struct rt_work)); + if (dhcp_work == RT_NULL) + return -RT_ENOMEM; + + rt_work_init(dhcp_work, wiz_dhcp_work, (void *)netdev); + rt_work_submit(dhcp_work, WIZ_DHCP_WORK_RETRY_TIME); + + return RT_EOK; +} +#endif /* WIZ_USING_DHCP */ + +static void wiz_link_status_thread_entry(void *parameter) +{ +#define WIZ_PHYCFGR_LINK_STATUS 0x01 + + uint8_t phycfgr = 0; + struct netdev *netdev = RT_NULL; + + netdev = netdev_get_by_name(wiz_netdev_name); + if (netdev == RT_NULL) + { + LOG_E("don`t find device(%s)", wiz_netdev_name); + return; + } + + while (1) + { + /* Get PHYCFGR data */ + phycfgr = getPHYCFGR(); + + /* If the register contents are different from the struct contents, the struct needs to be updated */ + if ((phycfgr & WIZ_PHYCFGR_LINK_STATUS) != ((netdev->flags & NETDEV_FLAG_LINK_UP) ? RT_TRUE : RT_FALSE)) + { + if (phycfgr & WIZ_PHYCFGR_LINK_STATUS) + { + wiz_socket_init(); +#ifdef WIZ_USING_DHCP + if(dhcp_work) + { + DHCP_stop(); + rt_work_submit(dhcp_work, RT_WAITING_NO); + } +#else + wiz_network_init(RT_TRUE); +#endif + netdev_low_level_set_link_status(netdev, phycfgr & WIZ_PHYCFGR_LINK_STATUS); + wiz_netdev_info_update(netdev, RT_FALSE); + LOG_I("%s netdev link status becomes link up", wiz_netdev_name); + } + else + { + netdev_low_level_set_link_status(netdev, phycfgr & WIZ_PHYCFGR_LINK_STATUS); + if(dhcp_work) + { + rt_work_cancel(dhcp_work); + } + wiz_netdev_info_update(netdev, RT_TRUE); + LOG_I("%s netdev link status becomes link down", wiz_netdev_name); + } + } + rt_thread_mdelay(1000); + } +} + +static int wiz_interrupt_init(rt_base_t isr_pin) +{ + rt_thread_t tid; + + /* initialize RX mailbox */ + wiz_rx_mb = rt_mb_create("wiz_mb", WIZ_RX_MBOX_NUM, RT_IPC_FLAG_FIFO); + if (wiz_rx_mb == RT_NULL) + { + LOG_E("WIZnet create receive data mailbox error."); + return -RT_ENOMEM; + } + + /* create WIZnet SPI RX thread */ + tid = rt_thread_create("wiz", wiz_data_thread_entry, RT_NULL, 1024, RT_THREAD_PRIORITY_MAX / 6, 20); + if (tid != RT_NULL) + { + rt_thread_startup(tid); + } + + /* initialize interrupt pin */ + rt_pin_mode(isr_pin, PIN_MODE_INPUT_PULLUP); + rt_pin_attach_irq(isr_pin, PIN_IRQ_MODE_FALLING, (void (*)(void*)) wiz_isr, RT_NULL); + rt_pin_irq_enable(isr_pin, PIN_IRQ_ENABLE); + + return 0; +} + +static int wiz_is_exist(void) +{ + wiz_NetInfo ni; + int ret; + + wiz_set_mac(); + ctlnetwork(CN_SET_NETINFO, (void *)&wiz_net_info); + ctlnetwork(CN_GET_NETINFO, (void *)&ni); + + ret = rt_memcmp(wiz_net_info.mac, ni.mac, sizeof(ni.mac)); + + return (ret == 0); +} + +/* #include "stm32f4xx_hal.h" */ +/* WIZnet initialize device and network */ +int wiz_init(void) +{ + int result = RT_EOK; + rt_thread_t tid; + + if (wiz_init_ok == RT_TRUE) + { + LOG_I("RT-Thread WIZnet package is already initialized."); + return RT_EOK; + } + + /* initialize reset pin */ + rt_pin_mode(WIZ_RST_PIN, PIN_MODE_OUTPUT); + + /* I think you can attach w5500 into spi bus at here. You can use this function to realize.*/ + /* extern rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, GPIO_TypeDef *cs_gpiox, uint16_t cs_gpio_pin); */ + //rt_hw_spi_device_attach("spi1", "spi10", GPIOD, GPIO_PIN_2);//此处为添加的功能函数 + //rt_spi_bus_attach_device(&wiz_device, "spi10", "spi1",(void *)NULL); + /* WIZnet SPI device and pin initialize */ + result = wiz_device_init(WIZ_SPI_DEVICE); + if (result != RT_EOK) + { + goto __exit; + } + + /* WIZnet SPI device reset */ + wiz_reset(); + /* set WIZnet device read/write data callback */ + wiz_callback_register(); + + if (!wiz_is_exist()) + { + result = -1; + LOG_E("Wiznet chip not detected"); + goto __exit; + } + + /* Add wiz to the netdev list */ + ctlwizchip(CW_GET_ID, (void *)wiz_netdev_name); + wiz_netdev_add(wiz_netdev_name); + + /* WIZnet chip configure initialize */ + wiz_chip_cfg_init(); + + /* WIZnet socket initialize */ + wiz_socket_init(); + /* WIZnet network initialize */ + result = wiz_network_init(RT_FALSE); + if (result != RT_EOK) + { + goto __exit; + } + + dns_tick_timer = rt_timer_create("dns_tick", wiz_dns_time_handler, RT_NULL, 1 * RT_TICK_PER_SECOND, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC); + rt_timer_start(dns_tick_timer); + + /* create WIZnet link status Polling thread */ + tid = rt_thread_create("wiz_stat", wiz_link_status_thread_entry, RT_NULL, 2048, RT_THREAD_PRIORITY_MAX - 4, 20); + if (tid != RT_NULL) + { + rt_thread_startup(tid); + } + + wiz_interrupt_init(WIZ_IRQ_PIN); + +__exit: + if (result == RT_EOK) + { + wiz_init_ok = RT_TRUE; + LOG_I("RT-Thread WIZnet package (V%s) initialize success.", WIZ_SW_VERSION); + } + else + { + LOG_E("RT-Thread WIZnet package (V%s) initialize failed(%d).", WIZ_SW_VERSION, result); + } + + return result; +} +INIT_ENV_EXPORT(wiz_init); diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_af_inet.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_af_inet.c new file mode 100644 index 000000000..323c8d00f --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_af_inet.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#include +#include + +#include +#include +#include + +#include +#include + +#ifdef SAL_USING_POSIX +#include +#endif + +#ifdef SAL_USING_POSIX +static int wiz_poll(struct dfs_fd *file, struct rt_pollreq *req) +{ + int mask = 0; + struct wiz_socket *sock; + struct sal_socket *sal_sock; + + sal_sock = sal_get_socket((int) file->data); + if(!sal_sock) + { + return -1; + } + + sock = wiz_get_socket((int)sal_sock->user_data); + if (sock != NULL) + { + rt_base_t level; + + rt_poll_add(&sock->wait_head, req); + + level = rt_hw_interrupt_disable(); + if (sock->rcvevent) + { + mask |= POLLIN; + } + if (sock->sendevent) + { + mask |= POLLOUT; + } + if (sock->errevent) + { + mask |= POLLERR; + } + rt_hw_interrupt_enable(level); + } + + return mask; +} +#endif + +static const struct sal_socket_ops wiz_socket_ops = +{ + wiz_socket, + wiz_closesocket, + wiz_bind, + wiz_listen, + wiz_connect, + wiz_accept, + wiz_sendto, + wiz_recvfrom, + wiz_getsockopt, + wiz_setsockopt, + wiz_shutdown, + NULL, + NULL, + NULL, +#ifdef SAL_USING_POSIX + wiz_poll, +#endif /* SAL_USING_POSIX */ +}; + +static const struct sal_netdb_ops wiz_netdb_ops = +{ + wiz_gethostbyname, + NULL, + wiz_getaddrinfo, + wiz_freeaddrinfo, +}; + + +static const struct sal_proto_family wiz_inet_family = +{ + AF_WIZ, + AF_INET, + &wiz_socket_ops, + &wiz_netdb_ops, +}; + +/* Set wiz network interface device protocol family information */ +int sal_wiz_netdev_set_pf_info(struct netdev *netdev) +{ + RT_ASSERT(netdev); + + netdev->sal_user_data = (void *) &wiz_inet_family; + return 0; +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_device.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_device.c new file mode 100644 index 000000000..d9b6b151d --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_device.c @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#include + +#include +#include +#include + +#define DBG_ENABLE +#define DBG_SECTION_NAME "wiz.dev" +#ifdef WIZ_DEBUG +#define DBG_LEVEL DBG_LOG +#else +#define DBG_LEVEL DBG_INFO +#endif /* WIZ_DEBUG */ +#define DBG_COLOR +#include + +struct rt_spi_device *wiz_device = RT_NULL; + +#ifndef WIZ_SPI_FREQ_MAX +#define WIZ_SPI_FREQ_MAX 10000000 +#endif + +static int wiz_spi_init(const char *spi_dev_name) +{ + RT_ASSERT(spi_dev_name); + + if (wiz_device != RT_NULL) + { + return 0; + } + + wiz_device = (struct rt_spi_device *) rt_device_find(spi_dev_name); + if (wiz_device == RT_NULL) + { + LOG_E("You should attach [%s] into SPI bus firstly.", spi_dev_name); + return -RT_ENOSYS; + } + + /* check SPI device type */ + RT_ASSERT(wiz_device->parent.type == RT_Device_Class_SPIDevice); + + /* configure SPI device*/ + { + struct rt_spi_configuration cfg; + cfg.data_width = 8; + cfg.mode = RT_SPI_MASTER | RT_SPI_MODE_0 | RT_SPI_MSB; /* SPI Compatible Modes 0 */ + cfg.max_hz = WIZ_SPI_FREQ_MAX; /* SPI Interface with Clock Speeds Up to 40 MHz */ + rt_spi_configure(wiz_device, &cfg); + } + + if (rt_device_open((rt_device_t) wiz_device, RT_DEVICE_OFLAG_RDWR) != RT_EOK) + { + LOG_E("open WIZnet SPI device %s error.", spi_dev_name); + return -RT_ERROR; + } + + return RT_EOK; +} + +int wiz_device_init(const char *spi_dev_name) +{ + int result = RT_EOK; + + /* WIZnet SPI device initialize */ + result = wiz_spi_init(spi_dev_name); + if (result != RT_EOK) + { + LOG_E("WIZnet SPI device initialize failed."); + return result; + } + + return RT_EOK; +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_ping.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_ping.c new file mode 100644 index 000000000..40f2cb23f --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_ping.c @@ -0,0 +1,259 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#include +#include + +#include + +#include +#include + +#include +#include + +#define Sn_PROTO(ch) (0x001408 + (ch<<5)) + +#define WIZ_PING_DATA_LEN 32 +#define WIZ_PING_HEAD_LEN 8 + +#define WIZ_PING_PORT 3000 +#define WIZ_PING_REQUEST 8 +#define WIZ_PING_REPLY 0 +#define WIZ_PING_CODE 0 +#define WIZ_PING_DELAY (1 * RT_TICK_PER_SECOND) +#define WIZ_PING_TIMEOUT (2 * RT_TICK_PER_SECOND) + +struct wiz_ping_msg +{ + uint8_t type; // 0 - Ping Reply, 8 - Ping Request + uint8_t code; // Always 0 + uint16_t check_sum; // Check sum + uint16_t id; // Identification + uint16_t seq_num; // Sequence Number + int8_t data[WIZ_PING_DATA_LEN]; // Ping Data : 1452 = IP RAW MTU - sizeof(type+code+check_sum+id+seq_num) +}; + +/* calculate string check value */ +static uint16_t wiz_checksum( uint8_t *src, uint32_t len ) +{ + uint16_t sum, tsum, i, j; + uint32_t lsum; + + j = len >> 1; + lsum = 0; + + for (i = 0; i < j; i++) + { + tsum = src[i * 2]; + tsum = tsum << 8; + tsum += src[i * 2 + 1]; + lsum += tsum; + } + + if (len % 2) + { + tsum = src[i * 2]; + lsum += (tsum << 8); + } + + sum = lsum; + sum = ~(sum + (lsum >> 16)); + return (uint16_t) sum; +} + +static int wiz_ping_request(int socket) +{ + int idx, send_len; + uint16_t tmp_checksum; + struct wiz_ping_msg ping_req; + + /* set request ping message object */ + ping_req.type = WIZ_PING_REQUEST; + ping_req.code = WIZ_PING_CODE; + ping_req.id = htons(rand() % 0xffff); + ping_req.seq_num = htons(rand() % 0xffff); + for (idx = 0; idx < WIZ_PING_DATA_LEN; idx++) + { + ping_req.data[idx] = (idx) % 8; + } + ping_req.check_sum = 0; + /* calculate request ping message check value */ + tmp_checksum = wiz_checksum((uint8_t *) &ping_req, sizeof(ping_req)); + ping_req.check_sum = htons(tmp_checksum); + + /* send request ping message */ + send_len = wiz_send(socket, &ping_req, sizeof(ping_req), 0); + if (send_len != sizeof(ping_req)) + { + return -1; + } + + return send_len - WIZ_PING_HEAD_LEN; +} + +static int wiz_ping_reply(int socket, struct sockaddr *from) +{ + uint16_t tmp_checksum; + uint8_t recv_buf[WIZ_PING_HEAD_LEN + WIZ_PING_DATA_LEN + 1]; + struct wiz_ping_msg ping_rep; + rt_tick_t start_tick; + int recv_len; + int idx; + + start_tick = rt_tick_get(); + while(1) + { + if (rt_tick_get() - start_tick > WIZ_PING_TIMEOUT) + { + return -1; + } + + if (getSn_RX_RSR(socket) <= 0) + { + rt_thread_mdelay(1); + continue; + } + else + { + struct sockaddr *sin = (struct sockaddr *)from; + socklen_t addr_len = sizeof(struct sockaddr_in); + recv_len = wiz_recvfrom(socket, recv_buf, WIZ_PING_HEAD_LEN + WIZ_PING_DATA_LEN, 0, sin, &addr_len); + if (recv_len < 0) + { + return -1; + } + break; + } + } + + if (recv_buf[0] == WIZ_PING_REPLY) + { + ping_rep.type = recv_buf[0]; + ping_rep.code = recv_buf[1]; + ping_rep.check_sum = (recv_buf[3] << 8) + recv_buf[2]; + ping_rep.id = (recv_buf[5] << 8) + recv_buf[4]; + ping_rep.seq_num = (recv_buf[7] << 8) + recv_buf[6]; + for (idx = 0; idx < recv_len - 8; idx++) + { + ping_rep.data[idx] = recv_buf[8 + idx]; + } + + tmp_checksum = ~wiz_checksum(recv_buf, recv_len); + if (tmp_checksum != 0xffff) + { + return -2; + } + } + else if (recv_buf[0] == WIZ_PING_REQUEST) + { + ping_rep.code = recv_buf[1]; + ping_rep.type = recv_buf[2]; + ping_rep.check_sum = (recv_buf[3] << 8) + recv_buf[2]; + ping_rep.id = (recv_buf[5] << 8) + recv_buf[4]; + ping_rep.seq_num = (recv_buf[7] << 8) + recv_buf[6]; + for (idx = 0; idx < recv_len - 8; idx++) + { + ping_rep.data[idx] = recv_buf[8 + idx]; + } + + tmp_checksum = ping_rep.check_sum; + ping_rep.check_sum = 0; + if (tmp_checksum != ping_rep.check_sum) + { + return -2; + } + } + else + { + rt_kprintf("wiz_ping: unknown ping receive message.\n"); + return -1; + } + + return recv_len - WIZ_PING_HEAD_LEN; +} + +int wiz_ping(struct netdev *netdev, const char *host, size_t data_len, uint32_t times, struct netdev_ping_resp *ping_resp) +{ + int result = RT_EOK, socket; + struct sockaddr_in server_addr; + struct timeval timeout; + struct in_addr ina; + struct hostent *hostent; + rt_tick_t recv_start_tick; + struct sal_proto_family *pf = (struct sal_proto_family *) netdev->sal_user_data; + + /* get network interface socket operations */ + if (pf == RT_NULL || pf->skt_ops == RT_NULL) + { + rt_kprintf("wiz_ping: pf or pf->skt_ops is RT_NULL.\n"); + return -RT_FALSE; + } + + hostent = (struct hostent *) pf->netdb_ops->gethostbyname(host); + if (hostent == RT_NULL) + { + rt_kprintf("wiz_ping: hostent is RT_NULL.\n"); + return -RT_FALSE; + } + + socket = -1; + + socket = wiz_socket(AF_WIZ, SOCK_RAW, 0); + if (socket < 0) + { + rt_kprintf("wiz_ping: create ping socket(%d) failed.\n",socket); + return -1; + } + /* set socket ICMP protocol */ + IINCHIP_WRITE(Sn_PROTO(socket), IPPROTO_ICMP); + + /* Check socket register */ + while(getSn_SR(socket) != SOCK_IPRAW); + + timeout.tv_sec = times / RT_TICK_PER_SECOND; + timeout.tv_usec = (times % RT_TICK_PER_SECOND) * 1000 / RT_TICK_PER_SECOND; + + /* set receive and send timeout option */ + wiz_setsockopt(socket, SOL_SOCKET, SO_RCVTIMEO, (void *) &timeout, + sizeof(timeout)); + wiz_setsockopt(socket, SOL_SOCKET, SO_SNDTIMEO, (void *) &timeout, + sizeof(timeout)); + + server_addr.sin_family = AF_WIZ; + server_addr.sin_port = htons(WIZ_PING_PORT); + server_addr.sin_addr = *((struct in_addr *)hostent->h_addr); + rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); + rt_memcpy(&ina, &server_addr.sin_addr, sizeof(ina)); + + if (wiz_connect(socket, (struct sockaddr *) &server_addr, sizeof(struct sockaddr)) < 0) + { + wiz_closesocket(socket); + return -1; + } + + if ((result = wiz_ping_request(socket)) > 0) + { + recv_start_tick = rt_tick_get(); + result = wiz_ping_reply(socket, (struct sockaddr *) &server_addr); + } + + if(result > 0) + { + ping_resp->ip_addr.addr = ((struct in_addr *)hostent->h_addr)->s_addr; + ping_resp->ticks = rt_tick_get() - recv_start_tick; + ping_resp->data_len = data_len; + ping_resp->ttl = getSn_TTL(socket); + } + + wiz_closesocket(socket); + + return result; +} diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_socket.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_socket.c new file mode 100644 index 000000000..cbf1ce930 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/app_match_rt-thread/wiznet/src/wiz_socket.c @@ -0,0 +1,1739 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-09-26 chenyong first version + */ + +#include +#include +#include +#include +#include + +#include +#ifdef SAL_USING_POSIX +#include +#endif + +#include + +#include +#include +#include + +#define DBG_ENABLE +#define DBG_SECTION_NAME "wiz.socket" +#ifdef WIZ_DEBUG +#define DBG_LEVEL DBG_LOG +#else +#define DBG_LEVEL DBG_INFO +#endif /* WIZ_DEBUG */ +#define DBG_COLOR +#include + +#ifndef WIZ_SOCKETS_NUM +#define WIZ_SOCKETS_NUM 8 +#endif + +#ifndef WIZ_DEF_LOCAL_PORT +#define WIZ_DEF_LOCAL_PORT 6000 +#endif + +#ifndef RT_USING_TIMER_SOFT +#error "please enable soft timer(RT_USING_TIMER_SOFT) support in kernel configure." +#endif + +extern rt_bool_t wiz_init_ok; +#define WIZ_INIT_STATUS_CHECK \ + if (wiz_init_ok == RT_FALSE || \ + (getPHYCFGR() & PHYCFGR_LNK_ON) != PHYCFGR_LNK_ON) \ + { \ + return -1; \ + } \ + +#define HTONS_PORT(x) ((((x) & 0x00ffUL) << 8) | (((x) & 0xff00UL) >> 8)) +#define NIPQUAD(addr, index) ((unsigned char *)&addr)[index] + +#define WIZ_MAX(x , y) (((x) > (y)) ? (x) : (y)) +#define WIZ_MIN(x , y) (((x) < (y)) ? (x) : (y)) + +typedef enum +{ + WIZ_EVENT_SEND, + WIZ_EVENT_RECV, + WIZ_EVENT_ERROR, +} wiz_event_t; + +/* the global array of available sockets */ +static struct wiz_socket sockets[WIZ_SOCKETS_NUM] = {0}; +static uint16_t wiz_port = WIZ_DEF_LOCAL_PORT; + +struct wiz_socket *wiz_get_socket(int socket) +{ + if (socket < 0 || socket >= WIZ_SOCKETS_NUM) + { + return RT_NULL; + } + + /* check socket structure valid or not */ + if (sockets[socket].magic != WIZ_SOCKET_MAGIC) + { + return RT_NULL; + } + + return &sockets[socket]; +} + +static void wiz_do_event_changes(struct wiz_socket *sock, wiz_event_t event, rt_bool_t is_plus) +{ + switch (event) + { + case WIZ_EVENT_SEND: + { + if (is_plus) + { + sock->sendevent = 1; + +#ifdef SAL_USING_POSIX + rt_wqueue_wakeup(&sock->wait_head, (void *)POLLOUT); +#endif + } + else if (sock->sendevent) + { + sock->sendevent = 0; + } + break; + } + case WIZ_EVENT_RECV: + { + if (is_plus) + { + sock->rcvevent++; + +#ifdef SAL_USING_POSIX + rt_wqueue_wakeup(&sock->wait_head, (void *)POLLIN); +#endif + } + else if (sock->rcvevent) + { + sock->rcvevent--; + } + break; + } + case WIZ_EVENT_ERROR: + { + if (is_plus) + { + sock->errevent++; + +#ifdef SAL_USING_POSIX + rt_wqueue_wakeup(&sock->wait_head, (void *)POLLERR); +#endif + } + else if (sock->errevent) + { + sock->errevent--; + } + break; + } + default: + LOG_E("Not supported event (%d)", event); + } +} + +static void wiz_do_event_clean(struct wiz_socket *sock, wiz_event_t event) +{ + switch (event) + { + case WIZ_EVENT_SEND: + { + sock->sendevent = 0; + break; + } + case WIZ_EVENT_RECV: + { + sock->rcvevent = 0; + break; + } + case WIZ_EVENT_ERROR: + { + sock->errevent = 0; + break; + } + default: + LOG_E("Not supported event (%d)", event); + } +} + +int wiz_recv_notice_cb(int socket) +{ + struct wiz_socket *sock = RT_NULL; + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + rt_sem_release(sock->recv_notice); + + wiz_do_event_changes(sock, WIZ_EVENT_RECV, RT_TRUE); + + return 0; +} + +int wiz_closed_notice_cb(int socket) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_state = 0; + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + socket_state = getSn_SR(socket); + if (socket_state != SOCK_CLOSE_WAIT) + { + return -1; + } + + if (wizchip_close(socket) != SOCK_OK) + { + LOG_E("WIZnet socket(%d) close failed.", socket); + return -1; + } + sock->state = SOCK_CLOSED; + + wiz_do_event_changes(sock, WIZ_EVENT_RECV, RT_TRUE); + wiz_do_event_changes(sock, WIZ_EVENT_ERROR, RT_TRUE); + + rt_sem_release(sock->recv_notice); + + return 0; +} + +static struct wiz_socket *alloc_socket(void) +{ + static rt_mutex_t wiz_slock = RT_NULL; + struct wiz_socket *sock = RT_NULL; + char name[RT_NAME_MAX] = {0}; + int idx = 0; + + if (wiz_slock == RT_NULL) + { + /* create WIZnet socket lock */ + wiz_slock = rt_mutex_create("w_lock", RT_IPC_FLAG_FIFO); + if (wiz_slock == RT_NULL) + { + LOG_E("No memory for WIZnet socket lock!"); + return RT_NULL; + } + } + + rt_mutex_take(wiz_slock, RT_WAITING_FOREVER); + + /* find an empty WIZnet socket entry */ + for (idx = 0; idx < WIZ_SOCKETS_NUM && sockets[idx].magic; idx++); + + /* can't find an empty protocol family entry */ + if (idx == WIZ_SOCKETS_NUM) + { + goto __err; + } + + sock = &(sockets[idx]); + sock->magic = WIZ_SOCKET_MAGIC; + sock->socket = idx; + sock->port = 0; + sock->state = SOCK_CLOSED; + sock->remote_addr = RT_NULL; + sock->recv_timeout = 0; + sock->send_timeout = 0; + sock->rcvevent = 0; + sock->sendevent = 0; + sock->errevent = 0; + /* for server socket */ + sock->svr_info = RT_NULL; + + rt_snprintf(name, RT_NAME_MAX, "%s%d", "wiz_sr", idx); + /* create WIZnet socket receive mailbox */ + if ((sock->recv_notice = rt_sem_create(name, 0, RT_IPC_FLAG_FIFO)) == RT_NULL) + { + goto __err; + } + + rt_snprintf(name, RT_NAME_MAX, "%s%d", "wiz_sr", idx); + /* create WIZnet socket receive ring buffer lock */ + if ((sock->recv_lock = rt_mutex_create(name, RT_IPC_FLAG_FIFO)) == RT_NULL) + { + goto __err; + } + + rt_mutex_release(wiz_slock); + return sock; + +__err: + rt_mutex_release(wiz_slock); + return RT_NULL; +} + +int wiz_socket(int domain, int type, int protocol) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_type = 0; + uint8_t socket_state = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + /* check socket family protocol */ + RT_ASSERT(domain == 46 || domain == 2); + + switch (type) + { + case SOCK_STREAM: + socket_type = Sn_MR_TCP; + break; + + case SOCK_DGRAM: + socket_type = Sn_MR_UDP; + break; + + case SOCK_RAW: + socket_type = Sn_MR_IPRAW; + break; + + default: + LOG_E("don't support socket type (%d)!", type); + return -1; + } + + /* allocate and initialize a new WIZnet socket */ + sock = alloc_socket(); + if (sock == RT_NULL) + { + LOG_E("allocate a new WIZnet socket failed!"); + return -1; + } + sock->type = socket_type; + +#ifdef SAL_USING_POSIX + rt_wqueue_init(&sock->wait_head); +#endif + + socket_state = getSn_SR(sock->socket); + if (socket_state == SOCK_CLOSED) + { + switch (sock->type) + { + case Sn_MR_TCP: + if (wizchip_socket(sock->socket, sock->type, wiz_port, Sn_MR_ND) != sock->socket) + { + LOG_E("WIZnet TCP socket(%d) create failed!", sock->socket); + rt_memset(sock, 0x00, sizeof(struct wiz_socket)); + return -1; + } + sock->port = wiz_port++; + break; + + case Sn_MR_UDP: + case Sn_MR_IPRAW: + if (wizchip_socket(sock->socket, sock->type, wiz_port, 0) != sock->socket) + { + LOG_E("WIZnet UDP socket(%d) create failed!", sock->socket); + rt_memset(sock, 0x00, sizeof(struct wiz_socket)); + return -1; + } + sock->port = wiz_port++; + break; + + default: + LOG_E("Socket (%d) type %d is not support.", sock->socket, sock->type); + return -1; + } + } + else + { + rt_memset(sock, 0x00, sizeof(struct wiz_socket)); + LOG_E("socket(%d) is not closed(0x%x).", sock->socket, socket_state); + return -1; + } + sock->state = SOCK_INIT; + + return sock->socket; +} + +/* free server information and close all client socket in server clnt_list */ +static int free_svr_info(struct wiz_socket *sock) +{ + struct wiz_svr_info *svr_info = sock->svr_info; + struct wiz_clnt_info *clnt_info = RT_NULL; + rt_slist_t *node = RT_NULL; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + /* close all client socket and free client information object */ + rt_slist_for_each(node, &svr_info->clnt_list) + { + clnt_info = rt_slist_entry(node, struct wiz_clnt_info, list); + if (clnt_info) + { + rt_slist_remove(&svr_info->clnt_list, node); + wiz_closesocket(clnt_info->socket); + rt_free(clnt_info); + } + } + rt_hw_interrupt_enable(level); + + if (svr_info->conn_tmr) + { + rt_timer_stop(svr_info->conn_tmr); + rt_timer_delete(svr_info->conn_tmr); + } + + if (svr_info->conn_mbox) + { + rt_mb_delete(svr_info->conn_mbox); + } + + if (svr_info) + { + rt_free(svr_info); + sock->svr_info = RT_NULL; + } + + return 0; +} + +/* remove client information from server socket clnt_list */ +static int remove_clnt_list(struct wiz_socket *sock) +{ + struct wiz_socket *svr_sock = RT_NULL; + struct wiz_svr_info *svr_info = RT_NULL; + int index = 0; + + for (index = 0; index < WIZ_SOCKETS_NUM; index++) + { + svr_sock = wiz_get_socket(index); + if (svr_sock == RT_NULL) + { + continue; + } + svr_info = svr_sock->svr_info; + + /* find server socket by bind port */ + if (svr_sock->svr_info && svr_sock->port == sock->port) + { + struct wiz_clnt_info *clnt_info = RT_NULL; + rt_slist_t *node = RT_NULL; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + /* find client socket information in server clnt_list */ + rt_slist_for_each(node, &svr_info->clnt_list) + { + clnt_info = rt_slist_entry(node, struct wiz_clnt_info, list); + if (clnt_info && clnt_info->socket == sock->socket) + { + rt_slist_remove(&svr_info->clnt_list, node); + rt_hw_interrupt_enable(level); + + /* add server socket listen number */ + svr_info->backlog++; + rt_free(clnt_info); + return 0; + } + } + rt_hw_interrupt_enable(level); + } + } + + return 0; +} + +static int free_socket(struct wiz_socket *sock) +{ + if (sock->recv_notice) + { + rt_sem_delete(sock->recv_notice); + } + + if (sock->recv_lock) + { + rt_mutex_delete(sock->recv_lock); + } + + if (sock->remote_addr) + { + rt_free(sock->remote_addr); + } + + if (sock->svr_info) + { + /* is server socket, free server information and close all client socket on clnt_list */ + free_svr_info(sock); + } + else + { + /* is client socket, remove client socket list from server socket */ + remove_clnt_list(sock); + } + + rt_memset(sock, 0x00, sizeof(struct wiz_socket)); + + return 0; +} + +int wiz_closesocket(int socket) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_state = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + socket_state = getSn_SR(socket); + if (socket_state == SOCK_CLOSED) + { + free_socket(sock); + return -1; + } + + if (wizchip_close(socket) != SOCK_OK) + { + LOG_E("WIZnet socket(%d) close failed.", socket); + free_socket(sock); + return -1; + } + + return free_socket(sock); +} + +int wiz_shutdown(int socket, int how) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_state = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + socket_state = getSn_SR(socket); + if (socket_state == SOCK_CLOSED) + { + free_socket(sock); + return -1; + } + + if (wizchip_close(socket) != SOCK_OK) + { + LOG_E("WIZnet socket(%d) shutdown failed.", socket); + free_socket(sock); + return -1; + } + + return free_socket(sock); +} + +static struct wiz_clnt_info *wiz_conn_clnt_info_get(struct wiz_socket *svr_sock) +{ + rt_base_t level; + rt_slist_t *node = RT_NULL; + struct wiz_clnt_info *clnt_info = RT_NULL; + struct wiz_svr_info *svr_info = svr_sock->svr_info; + + level = rt_hw_interrupt_disable(); + + rt_slist_for_each(node, &svr_info->clnt_list) + { + clnt_info = rt_slist_entry(node, struct wiz_clnt_info, list); + if (clnt_info->state == SOCK_LISTEN || clnt_info->state == SOCK_INIT) + { + rt_hw_interrupt_enable(level); + return clnt_info; + } + } + + rt_hw_interrupt_enable(level); + return RT_NULL; +} + +static void wiz_timer_entry(void *parameter) +{ + struct wiz_socket *svr_sock = RT_NULL; + struct wiz_socket *clnt_sock = RT_NULL; + struct wiz_clnt_info *clnt_info = RT_NULL; + int clnt_socket = -1; + int bind_port = 0; + uint8_t status = 0; + + svr_sock = (struct wiz_socket *)parameter; + bind_port = svr_sock->port; + + /* check server socket listen status */ + if (svr_sock->state != SOCK_LISTEN) + { + return; + } + + /* allocate and initialize a new client socket */ + clnt_info = wiz_conn_clnt_info_get(svr_sock); + if (clnt_info == RT_NULL) + { + /* check server listen backlog number */ + if (svr_sock->svr_info->backlog < 0) + { + goto __error; + } + + clnt_sock = alloc_socket(); + if (clnt_sock == RT_NULL) + { + goto __error; + } + clnt_sock->type = Sn_MR_TCP; + +#ifdef SAL_USING_POSIX + rt_wqueue_init(&clnt_sock->wait_head); +#endif + if (wizchip_socket(clnt_sock->socket, clnt_sock->type, bind_port, 0) != clnt_sock->socket) + { + goto __error; + } + clnt_sock->state = SOCK_INIT; + clnt_sock->port = svr_sock->port; + clnt_socket = clnt_sock->socket; + + /* create client information and add client to server clnt_list */ + clnt_info = rt_calloc(1, sizeof(struct wiz_clnt_info)); + if (clnt_info == RT_NULL) + { + LOG_E("no memory for clinet information."); + goto __error; + } + clnt_info->socket = clnt_sock->socket; + clnt_info->state = clnt_sock->state; + rt_slist_init(&clnt_info->list); + rt_slist_append(&svr_sock->svr_info->clnt_list, &clnt_info->list); + } + else + { + /* get client socket structure by socket descriptor */ + clnt_sock = wiz_get_socket(clnt_info->socket); + if (clnt_sock == RT_NULL) + { + goto __error; + } + } + clnt_socket = clnt_info->socket; + + status = getSn_SR(clnt_socket); + clnt_info->state = status; + switch (status) + { + case SOCK_INIT: + /* listen for open local ports and wait for client connections */ + if (wizchip_listen(clnt_socket) < 0) + { + goto __error; + } + break; + case SOCK_ESTABLISHED: + /* set server socket to recevice connected status */ + svr_sock->state = SOCK_SYNRECV; + svr_sock->svr_info->backlog--; + /* send client socket connect message and events */ + rt_mb_send(svr_sock->svr_info->conn_mbox, (rt_uint32_t)clnt_sock); + wiz_do_event_changes(svr_sock, WIZ_EVENT_RECV, RT_TRUE); + break; + case SOCK_LISTEN: + /* socket input listen mode, wait client connect */ + break; + case SOCK_CLOSE_WAIT: + case SOCK_CLOSED: + default: + goto __error; + } + return; + +__error: + rt_mb_send(svr_sock->svr_info->conn_mbox, (rt_uint32_t)clnt_sock); + wiz_do_event_changes(svr_sock, WIZ_EVENT_ERROR, RT_TRUE); +} + +int wiz_listen(int socket, int backlog) +{ + struct wiz_socket *sock = RT_NULL; + + /* limit the "backlog" parameter to fit in an uint8_t */ + backlog = WIZ_MIN(WIZ_MAX(backlog, 0), WIZ_SOCKETS_NUM); + if (backlog == 0) + { + LOG_E("not emought socket for server listen."); + return -1; + } + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + /* set server socket status and listen sockets number */ + sock->state = SOCK_LISTEN; + + /* create connect timer for client connect event notice */ + if (sock->svr_info == RT_NULL) + { +#define WIZ_MB_NUM 8 +#define WIZ_TIMER_TICK rt_tick_from_millisecond(100) + + char name[RT_NAME_MAX] = {0}; + static int socket_counts = 0; + struct wiz_svr_info *svr_info = RT_NULL; + + sock->svr_info = rt_calloc(1, sizeof(struct wiz_svr_info)); + if (sock->svr_info == RT_NULL) + { + return -1; + } + svr_info = sock->svr_info; + + /* full server infomation */ + svr_info->backlog = backlog - 1; + rt_slist_init(&svr_info->clnt_list); + + /* create client socket connection event mailbox */ + rt_snprintf(name, RT_NAME_MAX, "wiz_mb%d", socket_counts); + svr_info->conn_mbox = rt_mb_create(name, WIZ_MB_NUM, RT_IPC_FLAG_FIFO); + if (svr_info->conn_mbox == RT_NULL) + { + return -1; + } + + /* create client socket connect timer */ + rt_snprintf(name, RT_NAME_MAX, "wiz_tm%d", socket_counts++); + svr_info->conn_tmr = rt_timer_create(name, wiz_timer_entry, (void *)sock, + WIZ_TIMER_TICK, RT_TIMER_FLAG_SOFT_TIMER | RT_TIMER_FLAG_PERIODIC); + if (svr_info->conn_tmr == RT_NULL) + { + return -1; + } + rt_timer_start(svr_info->conn_tmr); + } + + return 0; +} + +/* get IP address and port by socketaddr structure information */ +static int socketaddr_to_ipaddr_port(const struct sockaddr *sockaddr, ip_addr_t *addr, uint16_t *port) +{ + const struct sockaddr_in *sin = (const struct sockaddr_in *)sockaddr; + +#if NETDEV_IPV4 && NETDEV_IPV6 + (*addr).u_addr.ip4.addr = sin->sin_addr.s_addr; +#elif NETDEV_IPV4 + (*addr).addr = sin->sin_addr.s_addr; +#elif NETDEV_IPV6 + LOG_E("not support IPV6."); +#endif /* NETDEV_IPV4 && NETDEV_IPV6 */ + + *port = (uint16_t)HTONS_PORT(sin->sin_port); + + return 0; +} + +/* ipaddr structure change to IP address */ +static int ipaddr_to_ipstr(const struct sockaddr *sockaddr, uint8_t *ipstr) +{ + struct sockaddr_in *sin = (struct sockaddr_in *)sockaddr; + + /* change network ip_addr to ip string */ + ipstr[0] = NIPQUAD(sin->sin_addr.s_addr, 0); + ipstr[1] = NIPQUAD(sin->sin_addr.s_addr, 1); + ipstr[2] = NIPQUAD(sin->sin_addr.s_addr, 2); + ipstr[3] = NIPQUAD(sin->sin_addr.s_addr, 3); + + return 0; +} + +int wiz_bind(int socket, const struct sockaddr *name, socklen_t namelen) +{ + struct wiz_socket *sock = RT_NULL; + uint16_t port = 0; + ip_addr_t ipaddr; + + RT_ASSERT(name); + + /* Only support signed w5500 network interface device in wiznet, + and the bind function is implemented in the SAL component, + need to define "wiz_bind" function and register it. */ + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + /* prase ip address and port */ + socketaddr_to_ipaddr_port(name, &ipaddr, &port); + + /* check socket bind port */ + if (sock->port != port && sock->type == Sn_MR_UDP) + { + struct wiz_socket *new_sock = RT_NULL; + + /* close old socket */ + if (wiz_closesocket(socket) < 0) + { + return -1; + } + + /* create new socket by bind port */ + new_sock = alloc_socket(); + if (new_sock == RT_NULL) + { + return -1; + } + new_sock->type = Sn_MR_UDP; + +#ifdef SAL_USING_POSIX + rt_wqueue_init(&new_sock->wait_head); +#endif + if (wizchip_socket(new_sock->socket, new_sock->type, port, 0) != new_sock->socket) + { + return -1; + } + new_sock->state = SOCK_INIT; + new_sock->port = port; + } + else + { + sock->port = port; + } + + return 0; +} + +int wiz_connect(int socket, const struct sockaddr *name, socklen_t namelen) +{ + struct wiz_socket *sock = RT_NULL; + ip_addr_t remote_addr; + uint16_t remote_port = 0; + uint8_t socket_state = 0; + uint8_t ipstr[4] = {0}; + int result = 0; + + RT_ASSERT(name); + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + socket_state = getSn_SR(socket); + if (socket_state == SOCK_UDP || socket_state == SOCK_IPRAW) + { + if (sock->remote_addr == RT_NULL) + { + sock->remote_addr = rt_calloc(1, sizeof(struct sockaddr)); + if (sock->remote_addr == RT_NULL) + { + LOG_E("no memory for structure sockaddr."); + return -1; + } + } + sock->remote_addr->sa_len = name->sa_len; + sock->remote_addr->sa_family = name->sa_family; + rt_memcpy(sock->remote_addr->sa_data, name->sa_data, 14); + + return 0; + } + else if (socket_state != SOCK_INIT) + { + LOG_E("WIZnet connect failed, get socket(%d) register state(%d) error.", socket, socket_state); + result = -1; + goto __exit; + } + + /* get IP address and port by socketaddr structure */ + socketaddr_to_ipaddr_port(name, &remote_addr, &remote_port); + ipaddr_to_ipstr(name, ipstr); + + if (wizchip_connect(socket, ipstr, remote_port) != SOCK_OK) + { + LOG_E("WIZnet socket(%d) connect failed.", socket); + result = -1; + goto __exit; + } + sock->state = SOCK_ESTABLISHED; + +__exit: + if (result < 0) + { + wiz_do_event_changes(sock, WIZ_EVENT_ERROR, RT_TRUE); + } + else + { + wiz_do_event_changes(sock, WIZ_EVENT_SEND, RT_TRUE); + } + + return result; +} + +int wiz_accept(int socket, struct sockaddr *addr, socklen_t *addrlen) +{ + struct wiz_socket *svr_sock = RT_NULL; + struct wiz_svr_info *svr_info = RT_NULL; + struct wiz_socket *clnt_sock = RT_NULL; + int clnt_socket = -1; + + RT_ASSERT(addr); + RT_ASSERT(addrlen); + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + svr_sock = wiz_get_socket(socket); + if (svr_sock == RT_NULL) + { + return -1; + } + svr_info = svr_sock->svr_info; + /* set server socket to SOCK_LISTEN status for socket connect timer */ + svr_sock->state = SOCK_LISTEN; + + while (1) + { + /* receive client connect message */ + if (rt_mb_recv(svr_info->conn_mbox, (void *)&clnt_sock, RT_WAITING_FOREVER) != RT_EOK) + { + return -1; + } + + /* check connect message type */ + clnt_socket = clnt_sock->socket; + if (getSn_SR(clnt_socket) != SOCK_ESTABLISHED) + { + /* clean server socket error event */ + wiz_do_event_clean(svr_sock, WIZ_EVENT_ERROR); + /* error massage, close client socket */ + wiz_closesocket(clnt_socket); + return -1; + } + + /* get new client socket information */ + { + struct sockaddr_in *sin = (struct sockaddr_in *)addr; + uint8_t ipstr[4] = {0}; + uint16_t remote_port = 0; + char remote_ipaddr[16] = {0}; + + /* set client socket status */ + clnt_sock->state = SOCK_ESTABLISHED; + + /* get the remote IP address and port */ + getSn_DIPR(clnt_socket, ipstr); + remote_port = getSn_DPORT(clnt_socket); + rt_snprintf(remote_ipaddr, sizeof(remote_ipaddr), "%d.%d.%d.%d", ipstr[0], ipstr[1], ipstr[2], ipstr[3]); + LOG_D("remote ip: %s, remote port: %d\n", remote_ipaddr, remote_port); + + /* full address and port */ + sin->sin_port = htons(remote_port); + sin->sin_addr.s_addr = inet_addr((const char *)remote_ipaddr); + rt_memset(&(sin->sin_zero), 0, sizeof(sin->sin_zero)); + *addrlen = sizeof(struct sockaddr); + + /* clean server socket receive event and status */ + wiz_do_event_clean(svr_sock, WIZ_EVENT_RECV); + + return clnt_socket; + } + } +} + +int wiz_sendto(int socket, const void *data, size_t size, int flags, const struct sockaddr *to, socklen_t tolen) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_state = 0; + int32_t send_len = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + if (data == RT_NULL || size == 0) + { + LOG_E("WIZnet sendto input data or size error!"); + return -1; + } + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + socket_state = getSn_SR(socket); + switch (sock->type) + { + case Sn_MR_TCP: + { + if (socket_state == SOCK_CLOSED) + { + return 0; + } + else if (socket_state != SOCK_ESTABLISHED) + { + LOG_E("WIZnet send failed, get socket(%d) register state(%d) error.", socket, socket_state); + return -1; + } + + if ((send_len = wizchip_send(socket, (uint8_t *)data, size)) < 0) + { + LOG_E("WIZnet socket(%d) send data failed(%d).", socket, send_len); + return -1; + } + break; + } + + case Sn_MR_UDP: + case Sn_MR_IPRAW: + { + ip_addr_t remote_addr; + uint16_t remote_port = 0; + uint8_t ipstr[4] = {0}; + + if (socket_state != SOCK_UDP && socket_state != SOCK_IPRAW) + { + LOG_E("WIZnet sendto failed, get socket(%d) register state(%d) error.", socket, socket_state); + return -1; + } + + if (to) + { + socketaddr_to_ipaddr_port(to, &remote_addr, &remote_port); + ipaddr_to_ipstr(to, ipstr); + } + else if (sock->remote_addr) + { + socketaddr_to_ipaddr_port(sock->remote_addr, &remote_addr, &remote_port); + ipaddr_to_ipstr(sock->remote_addr, ipstr); + } + + if ((send_len = wizchip_sendto(socket, (uint8_t *)data, size, ipstr, remote_port)) < 0) + { + LOG_E("WIZnet socket(%d) send data failed(%d).", socket, send_len); + return -1; + } + break; + } + + default: + LOG_E("WIZnet socket (%d) type %d is not support.", socket, sock->type); + return -1; + } + + return send_len; +} + +int wiz_send(int socket, const void *data, size_t size, int flags) +{ + return wiz_sendto(socket, data, size, flags, RT_NULL, 0); +} + +int wiz_recvfrom(int socket, void *mem, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen) +{ + struct wiz_socket *sock = RT_NULL; + uint8_t socket_state = 0; + int32_t recv_len = 0, timeout = 0; + int result = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + if (mem == RT_NULL || len == 0) + { + LOG_E("WIZnet recvfrom input data or length error!"); + return -1; + } + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + /* non-blocking sockets receive data */ + if (flags & MSG_DONTWAIT) + { + timeout = RT_WAITING_NO; + } + else if ((timeout = sock->recv_timeout) == 0) + { + /* set WIZNnet socket receive timeout */ + timeout = RT_WAITING_FOREVER; + } + + socket_state = getSn_SR(socket); + switch (sock->type) + { + case Sn_MR_TCP: + { + uint16_t recvsize = getSn_RX_RSR(socket); + /* receive last transmission of remaining data */ + if (recvsize > 0) + { + rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER); + recv_len = wizchip_recv(socket, mem, len); + if (recv_len > 0) + { + rt_mutex_release(sock->recv_lock); + goto __exit; + } + rt_mutex_release(sock->recv_lock); + } + + if (socket_state == SOCK_CLOSED) + { + return 0; + } + else if (socket_state != SOCK_ESTABLISHED) + { + LOG_E("WIZnet receive failed, get socket(%d) register state(%d) error.", socket, socket_state); + result = -1; + goto __exit; + } + + while (1) + { + /* wait the receive semaphore */ + if (rt_sem_take(sock->recv_notice, timeout) < 0) + { + result = -1; + /* blocking mode will prints an error and non-blocking mode exits directly */ + if ((flags & MSG_DONTWAIT) == 0) + { + LOG_E("WIZnet socket (%d) receive timeout (%d)!", socket, timeout); + errno = EAGAIN; + } + goto __exit; + } + else + { + if (sock->state == SOCK_ESTABLISHED) + { + /* get receive buffer to receiver ring buffer */ + rt_mutex_take(sock->recv_lock, RT_WAITING_FOREVER); + recv_len = wizchip_recv(socket, mem, len); + if (recv_len < 0) + { + LOG_E("WIZnet socket(%d) receive data failed(%d).", socket, recv_len); + rt_mutex_release(sock->recv_lock); + result = -1; + goto __exit; + } + rt_mutex_release(sock->recv_lock); + } + else if (sock->state == SOCK_CLOSED) + { + result = 0; + goto __exit; + } + break; + } + } + break; + } + + case Sn_MR_UDP: + case Sn_MR_IPRAW: + { + uint16_t remote_port = 0; + uint8_t ipstr[4] = {0}; + uint16_t rx_len = 0; + + if (socket_state != SOCK_UDP && socket_state != SOCK_IPRAW) + { + LOG_E("WIZnet recvfrom failed, get socket(%d) register state(%d) error.", socket, socket_state); + return -1; + } + +__continue: + if (rt_sem_take(sock->recv_notice, timeout) < 0) + { + result = -1; + /* blocking mode will prints an error and non-blocking mode exits directly */ + if ((flags & MSG_DONTWAIT) == 0) + { + LOG_E("WIZnet socket (%d) receive timeout (%d)!", socket, timeout); + } + goto __exit; + } + else + { + if ((rx_len = getSn_RX_RSR(socket)) > 0) + { + rx_len = rx_len > len ? len : rx_len; + + if (sock->state != SOCK_CLOSED) + { + recv_len = wizchip_recvfrom(socket, mem, rx_len, ipstr, &remote_port); + if (recv_len < 0) + { + LOG_E("WIZnet socket(%d) receive data failed(%d).", socket, recv_len); + result = -1; + goto __exit; + } + else + { + char remote_ipaddr[16] = {0}; + struct sockaddr_in *sin = (struct sockaddr_in *)from; + + rt_snprintf(remote_ipaddr, sizeof(remote_ipaddr), "%d.%d.%d.%d", + ipstr[0], ipstr[1], ipstr[2], ipstr[3]); + /* full address and port */ + sin->sin_port = htons(remote_port); + sin->sin_addr.s_addr = inet_addr((const char *)remote_ipaddr); + rt_memset(&(sin->sin_zero), 0, sizeof(sin->sin_zero)); + *fromlen = sizeof(struct sockaddr); + } + } + else if (sock->state == SOCK_CLOSED) + { + result = 0; + goto __exit; + } + } + else + { + /* clean receive data isr */ + goto __continue; + } + } + break; + } + + default: + LOG_E("WIZnet socket (%d) type %d is not support.", socket, sock->type); + return -1; + } + +__exit: + if (recv_len > 0) + { + errno = 0; + result = recv_len; + wiz_do_event_changes(sock, WIZ_EVENT_RECV, RT_FALSE); + + if (getSn_RX_RSR(socket) == 0) + { + wiz_do_event_clean(sock, WIZ_EVENT_RECV); + } + } + else + { + wiz_do_event_changes(sock, WIZ_EVENT_ERROR, RT_TRUE); + } + + return result; +} + +int wiz_recv(int socket, void *mem, size_t len, int flags) +{ + return wiz_recvfrom(socket, mem, len, flags, RT_NULL, RT_NULL); +} + +int wiz_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen) +{ + struct wiz_socket *sock = RT_NULL; + int32_t timeout = 0; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + if (optval == RT_NULL || optlen == RT_NULL) + { + LOG_E("WIZnet getsocketopt input option value or option length error."); + return -1; + } + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + switch (level) + { + case SOL_SOCKET: + { + switch (optname) + { + case SO_RCVTIMEO: + timeout = sock->recv_timeout; + ((struct timeval *)(optval))->tv_sec = (timeout) / 1000U; + ((struct timeval *)(optval))->tv_usec = (timeout % 1000U) * 1000U; + break; + + case SO_SNDTIMEO: + timeout = sock->send_timeout; + ((struct timeval *)optval)->tv_sec = timeout / 1000U; + ((struct timeval *)optval)->tv_usec = (timeout % 1000U) * 1000U; + break; + + default: + LOG_E("WIZnet socket (%d) not support option name : %d.", socket, optname); + return -1; + } + break; + } + + default: + { + int8_t ret = 0; + + ret = wizchip_getsockopt(socket, (sockopt_type)level, optval); + if (ret != SOCK_OK) + { + LOG_E("WIZnet getsocketopt input level(%d) error.", level); + return ret; + } + break; + } + } + + return 0; +} +int wiz_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) +{ + struct wiz_socket *sock = RT_NULL; + + /* check WIZnet initialize status */ + WIZ_INIT_STATUS_CHECK; + + if (optval == RT_NULL) + { + LOG_E("WIZnet setsockopt input option value or option length error."); + return -1; + } + + sock = wiz_get_socket(socket); + if (sock == RT_NULL) + { + return -1; + } + + switch (level) + { + case SOL_SOCKET: + { + switch (optname) + { + case SO_RCVTIMEO: + sock->recv_timeout = ((const struct timeval *)optval)->tv_sec * 1000 + ((const struct timeval *)optval)->tv_usec / 1000; + break; + + case SO_SNDTIMEO: + sock->send_timeout = ((const struct timeval *)optval)->tv_sec * 1000 + ((const struct timeval *)optval)->tv_usec / 1000; + break; + + default: + LOG_E("WIZnet socket (%d) not support option name : %d.", socket, optname); + return -1; + } + break; + } + + case IPPROTO_TCP: + { + switch (optname) + { + case TCP_NODELAY: + break; + } + break; + } + + default: + { + int8_t ret = 0; + + ret = wizchip_setsockopt(socket, (sockopt_type)optname, (void *)optval); + if (ret != SOCK_OK) + { + LOG_E("WIZnet getsocketopt input level(%d) error.", level); + return ret; + } + break; + } + } + + return 0; +} + +static uint32_t ipstr_atol(const char *nptr) +{ + uint32_t total = 0; + char sign = '+'; + /* jump space */ + while (isspace(*nptr)) + { + ++nptr; + } + + if (*nptr == '-' || *nptr == '+') + { + sign = *nptr++; + } + + while (isdigit(*nptr)) + { + total = 10 * total + ((*nptr++) - '0'); + } + return (sign == '-') ? -total : total; +} + +/* IP address to unsigned int type */ +static uint32_t ipstr_to_u32(char *ipstr) +{ + char ipBytes[4] = {0}; + uint32_t i; + + for (i = 0; i < 4; i++, ipstr++) + { + ipBytes[i] = (char)ipstr_atol(ipstr); + if ((ipstr = strchr(ipstr, '.')) == RT_NULL) + { + break; + } + } + return *(uint32_t *)ipBytes; +} + +struct hostent *wiz_gethostbyname(const char *name) +{ + ip_addr_t addr; + char ipstr[16] = {0}; + /* buffer variables for at_gethostbyname() */ + static struct hostent s_hostent; + static char *s_aliases; + static ip_addr_t s_hostent_addr; + static ip_addr_t *s_phostent_addr[2]; + static char s_hostname[DNS_MAX_NAME_LENGTH + 1]; + size_t idx = 0; + + /* check WIZnet initialize status */ + if (wiz_init_ok == RT_FALSE || + (getPHYCFGR() & PHYCFGR_LNK_ON) != PHYCFGR_LNK_ON) + { + return RT_NULL; + } + + if (name == RT_NULL) + { + LOG_E("WIZnet gethostbyname input name error!"); + return RT_NULL; + } + + /* check domain name or IP address */ + for (idx = 0; idx < rt_strlen(name) && !isalpha(name[idx]); idx++); + + if (idx < rt_strlen(name)) + { + int8_t ret = 0; + uint8_t remote_ip[4] = {0}; + uint8_t data_buffer[512]; + + /* allocate and initialize a new WIZnet socket */ + for (idx = 0; idx < WIZ_SOCKETS_NUM && sockets[idx].magic; idx++); + if (idx >= WIZ_SOCKETS_NUM) + { + LOG_E("WIZnet DNS failed, socket number is full."); + return RT_NULL; + } + + wiz_NetInfo net_info; + ctlnetwork(CN_GET_NETINFO, (void *)&net_info); + + /* DNS client initialize */ + DNS_init(idx, data_buffer); + /* DNS client processing */ + ret = DNS_run(net_info.dns, (uint8_t *)name, remote_ip); + if (ret == -1) + { + LOG_E("WIZnet MAX_DOMAIN_NAME is too small, should be redefined it."); + return RT_NULL; + } + else if (ret < 0) + { + return RT_NULL; + } + + /* domain resolve failed */ + if (remote_ip[0] == 0) + { + return RT_NULL; + } + + rt_snprintf(ipstr, 16, "%u.%u.%u.%u", remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]); + } + else + { + /* input name is IP address */ + rt_strncpy(ipstr, name, rt_strlen(name)); + } + +#if NETDEV_IPV4 && NETDEV_IPV6 + addr.u_addr.ip4.addr = ipstr_to_u32(ipstr); +#elif NETDEV_IPV4 + addr.addr = ipstr_to_u32(ipstr); +#elif NETDEV_IPV6 + LOG_E("not support IPV6."); +#endif /* NETDEV_IPV4 && NETDEV_IPV6 */ + + /* fill hostent structure */ + s_hostent_addr = addr; + s_phostent_addr[0] = &s_hostent_addr; + s_phostent_addr[1] = RT_NULL; + rt_strncpy(s_hostname, name, DNS_MAX_NAME_LENGTH); + s_hostname[DNS_MAX_NAME_LENGTH] = 0; + s_hostent.h_name = s_hostname; + s_aliases = RT_NULL; + s_hostent.h_aliases = &s_aliases; + s_hostent.h_addrtype = AF_WIZ; + s_hostent.h_length = sizeof(ip_addr_t); + s_hostent.h_addr_list = (char **)&s_phostent_addr; + + return &s_hostent; +} + +int wiz_getaddrinfo(const char *nodename, const char *servname, const struct addrinfo *hints, struct addrinfo **res) +{ + int port_nr = 0; + ip_addr_t addr; + struct addrinfo *ai = RT_NULL; + struct sockaddr_storage *sa = RT_NULL; + size_t total_size = 0; + size_t namelen = 0; + int ai_family = 0; + + /* check WIZnet initialize status */ + if (wiz_init_ok == RT_FALSE || + (getPHYCFGR() & PHYCFGR_LNK_ON) != PHYCFGR_LNK_ON) + { + return EAI_FAIL; + } + + if (res == RT_NULL) + { + return EAI_FAIL; + } + *res = RT_NULL; + + if ((nodename == RT_NULL) && (servname == RT_NULL)) + { + return EAI_NONAME; + } + + if (hints != RT_NULL) + { + ai_family = hints->ai_family; + if (hints->ai_family != AF_WIZ && hints->ai_family != AF_INET && hints->ai_family != AF_UNSPEC) + { + return EAI_FAMILY; + } + } + + if (servname != RT_NULL) + { + /* service name specified: convert to port number */ + port_nr = atoi(servname); + if ((port_nr <= 0) || (port_nr > 0xffff)) + { + return EAI_SERVICE; + } + } + + if (nodename != RT_NULL) + { + /* service location specified, try to resolve */ + if ((hints != RT_NULL) && (hints->ai_flags & AI_NUMERICHOST)) + { + /* no DNS lookup, just parse for an address string */ + if (!inet_aton(nodename, (ip4_addr_t *)&addr)) + { + return EAI_NONAME; + } + + if (ai_family == AF_WIZ || ai_family == AF_INET) + { + return EAI_NONAME; + } + } + else + { + char ipstr[16] = {0}; + size_t idx = 0; + + /* check domain name or IP address */ + for (idx = 0; idx < rt_strlen(nodename) && !isalpha(nodename[idx]); idx++); + + if (idx < rt_strlen(nodename)) + { + int8_t ret; + uint8_t remote_ip[4] = {0}; + uint8_t data_buffer[512]; + + for (idx = 0; idx < WIZ_SOCKETS_NUM && sockets[idx].magic; idx++); + if (idx >= WIZ_SOCKETS_NUM) + { + LOG_E("wizenet getaddrinfo failed, socket number is full."); + return EAI_FAIL; + } + + wiz_NetInfo net_info; + ctlnetwork(CN_GET_NETINFO, (void *)&net_info); + + /* DNS client initialize */ + DNS_init(idx, data_buffer); + /* DNS client processing */ + ret = DNS_run(net_info.dns, (uint8_t *)nodename, remote_ip); + if (ret == -1) + { + LOG_E("WIZnet MAX_DOMAIN_NAME is too small, should be redefined it."); + return EAI_FAIL; + } + else if (ret < 0) + { + LOG_E("WIZnet getaddrinfo failed(%d).", ret); + return EAI_FAIL; + } + + /* domain resolve failed */ + if (remote_ip[0] == 0) + { + return EAI_FAIL; + } + + rt_snprintf(ipstr, 16, "%u.%u.%u.%u", remote_ip[0], remote_ip[1], remote_ip[2], remote_ip[3]); + } + else + { + /* input name is IP address */ + rt_strncpy(ipstr, nodename, rt_strlen(nodename)); + } + +#if NETDEV_IPV4 && NETDEV_IPV6 + addr.type = IPADDR_TYPE_V4; + if ((addr.u_addr.ip4.addr = ipstr_to_u32(ip_str)) == 0) + { + return EAI_FAIL; + } +#elif NETDEV_IPV4 + addr.addr = ipstr_to_u32(ipstr); +#elif NETDEV_IPV6 + LOG_E("not support IPV6."); +#endif /* NETDEV_IPV4 && NETDEV_IPV6 */ + } + } + else + { + /* to do service location specified, use loopback address */ + } + + total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage); + if (nodename != RT_NULL) + { + namelen = rt_strlen(nodename); + if (namelen > DNS_MAX_NAME_LENGTH) + { + /* invalid name length */ + return EAI_FAIL; + } + RT_ASSERT(total_size + namelen + 1 > total_size); + total_size += namelen + 1; + } + /* If this fails, please report to lwip-devel! :-) */ + RT_ASSERT(total_size <= sizeof(struct addrinfo) + sizeof(struct sockaddr_storage) + DNS_MAX_NAME_LENGTH + 1); + ai = (struct addrinfo *)rt_malloc(total_size); + if (ai == RT_NULL) + { + return EAI_MEMORY; + } + rt_memset(ai, 0, total_size); + /* cast through void* to get rid of alignment warnings */ + sa = (struct sockaddr_storage *)(void *)((uint8_t *)ai + sizeof(struct addrinfo)); + struct sockaddr_in *sa4 = (struct sockaddr_in *)sa; + /* set up sockaddr */ +#if NETDEV_IPV4 && NETDEV_IPV6 + sa4->sin_addr.s_addr = addr.u_addr.ip4.addr; +#elif NETDEV_IPV4 + sa4->sin_addr.s_addr = addr.addr; +#elif NETDEV_IPV6 + LOG_E("not support IPV6."); +#endif /* NETDEV_IPV4 && NETDEV_IPV6 */ + sa4->sin_family = AF_INET; + sa4->sin_len = sizeof(struct sockaddr_in); + sa4->sin_port = htons((uint16_t)port_nr); + ai->ai_family = AF_INET; + + /* set up addrinfo */ + if (hints != RT_NULL) + { + /* copy socktype & protocol from hints if specified */ + ai->ai_socktype = hints->ai_socktype; + ai->ai_protocol = hints->ai_protocol; + } + if (nodename != RT_NULL) + { + /* copy nodename to canonname if specified */ + ai->ai_canonname = ((char *)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_storage)); + rt_memcpy(ai->ai_canonname, nodename, namelen); + ai->ai_canonname[namelen] = 0; + } + ai->ai_addrlen = sizeof(struct sockaddr_storage); + ai->ai_addr = (struct sockaddr *)sa; + + *res = ai; + + return 0; +} + +void wiz_freeaddrinfo(struct addrinfo *ai) +{ + struct addrinfo *next = RT_NULL; + + /* check WIZnet initialize status */ + if (wiz_init_ok == RT_FALSE || + (getPHYCFGR() & PHYCFGR_LNK_ON) != PHYCFGR_LNK_ON) + { + return; + } + + while (ai != NULL) + { + next = ai->ai_next; + rt_free(ai); + ai = next; + } +} From 4a39d325b21b4a4865d6cdacd976b49af58c02b4 Mon Sep 17 00:00:00 2001 From: Beichen Date: Wed, 3 Aug 2022 10:21:18 +0800 Subject: [PATCH 03/21] =?UTF-8?q?Ubiquitous/RT-Thread=5FFusion=5FXiUOS/aii?= =?UTF-8?q?t=5Fboard/xidatong-riscv64/applications/=EF=BC=9AAdd=20tcp=5Fcl?= =?UTF-8?q?ient.c=E3=80=81=20tcp=5Fclient.h=E3=80=81tcp=5Fserver.c?= =?UTF-8?q?=E3=80=81tcp=5Fserver.h=20and=20update=20the=20SConscript?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xidatong-riscv64/applications/SConscript | 5 + .../applications/tcp_client.c | 144 +++++++++++++++ .../applications/tcp_client.h | 10 + .../applications/tcp_server.c | 174 ++++++++++++++++++ .../applications/tcp_server.h | 10 + 5 files changed, 343 insertions(+) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript index 7bb0b8524..0c762e3fe 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/SConscript @@ -10,6 +10,11 @@ CPPPATH = [cwd] if GetDepend('BSP_USING_LCD'): src += ['lcd_test.c'] +## 设置 tcp_client.c 和 tcp_server.c 的依赖宏 +if GetDepend('PKG_USING_WIZNET'): + src += ['tcp_client.c'] + src += ['tcp_server.c'] + group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) Return('group') diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.c new file mode 100644 index 000000000..f5548502d --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.c @@ -0,0 +1,144 @@ +/* + * 程序清单:tcp 客户端 + * + * 这是一个 tcp 客户端的例程 + * 导出 tcpclient 命令到控制终端 + * 命令调用格式:tcpclient URL PORT + * URL:服务器地址 PORT::端口号 + * 程序功能:接收并显示从服务端发送过来的信息,接收到开头是 'q' 或 'Q' 的信息退出程序 + * Created by Ybeichen on 2022/7/28. +*/ + +#include "tcp_client.h" +#include +#include /* 使用BSD socket,需要包含socket.h头文件 */ +#include +#include + +#define BUFSZ 1024 + +static const char send_data[] = "This is TCP Client from RT-Thread."; /* 发送用到的数据 */ +static void tcp_client(int argc, char **argv) +{ + int ret; + char *recv_data; + struct hostent *host; + int sock, bytes_received; + struct sockaddr_in server_addr; + const char *url; + int port; + + if (argc < 3) + { + rt_kprintf("Usage: tcp_client URL PORT\n"); + rt_kprintf("Like: tcp_client 192.168.12.44 5000\n"); + return ; + } + + url = argv[1]; + port = strtoul(argv[2], 0, 10); + + /* 通过函数入口参数url获得host地址(如果是域名,会做域名解析) */ + host = gethostbyname(url); + + /* 分配用于存放接收数据的缓冲 */ + recv_data = rt_malloc(BUFSZ); + if (recv_data == RT_NULL) + { + rt_kprintf("No memory\n"); + return; + } + + /* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */ + if ((sock = socket(AF_WIZ, SOCK_STREAM, 0)) == -1) + { + /* 创建socket失败 */ + rt_kprintf("Socket error\n"); + + /* 释放已分配的接收缓冲 */ + rt_free(recv_data); + return; + } + + /* 初始化预连接的服务端地址 */ + server_addr.sin_family = AF_WIZ; + server_addr.sin_port = htons(port); + server_addr.sin_addr = *((struct in_addr *)host->h_addr); + rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); + + /* 连接到服务端 */ + if (connect(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) + { + /* 连接失败 */ + rt_kprintf("Connect fail!\n"); + closesocket(sock); + + /*释放接收缓冲 */ + rt_free(recv_data); + return; + } + + while (1) + { + /* 从sock连接中接收最大BUFSZ - 1字节数据 */ + bytes_received = recv(sock, recv_data, BUFSZ - 1, 0); + if (bytes_received < 0) + { + /* 接收失败,关闭这个连接 */ + closesocket(sock); + rt_kprintf("\nreceived error,close the socket.\r\n"); + + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + else if (bytes_received == 0) + { + /* 默认 recv 为阻塞模式,此时收到0认为连接出错,关闭这个连接 */ + closesocket(sock); + rt_kprintf("\nreceived error,close the socket.\r\n"); + + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + + /* 有接收到数据,把末端清零 */ + recv_data[bytes_received] = '\0'; + + if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0) + { + /* 如果是首字母是q或Q,关闭这个连接 */ + closesocket(sock); + rt_kprintf("\n got a 'q' or 'Q',close the socket.\r\n"); + + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + else + { + /* 在控制终端显示收到的数据 */ + rt_kprintf("\nReceived data = %s ", recv_data); + } + + /* 发送数据到sock连接 */ + ret = send(sock, send_data, strlen(send_data), 0); + if (ret < 0) + { + /* 接收失败,关闭这个连接 */ + closesocket(sock); + rt_kprintf("\nsend error,close the socket.\r\n"); + + rt_free(recv_data); + break; + } + else if (ret == 0) + { + /* 打印send函数返回值为0的警告信息 */ + rt_kprintf("\n Send warning,send function return 0.\r\n"); + } + } + return; +} +MSH_CMD_EXPORT(tcp_client, a tcp client sample); \ No newline at end of file diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h new file mode 100644 index 000000000..6190811cc --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h @@ -0,0 +1,10 @@ +// +// Created by Y北辰 on 2022/7/28. +// + +#ifndef RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H +#define RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H + +#endif //RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H + +static void tcpclient(int argc, char **argv); \ No newline at end of file diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.c new file mode 100644 index 000000000..287881827 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.c @@ -0,0 +1,174 @@ +/* +* 程序清单:tcp 服务端 + * + * 这是一个 tcp 服务端的例程 + * 导出 tcp_server 命令到控制终端 + * 命令调用格式:tcp_server + * 无参数 + * 程序功能:作为一个服务端,接收并显示客户端发来的数据 ,接收到 exit 退出程序 + * Created by Ybeichen on 2022/7/28. +*/ + +#include "tcp_server.h" +#include +#include /* 使用BSD socket,需要包含socket.h头文件 */ +#include "netdb.h" +#include +#include + +#define BUFSZ (2048) +static int port = 5000; +static int is_running = 0; /* 停止标志 */ +static const char send_data[] = "This is TCP Server from RT-Thread."; /* 发送用到的数据 */ + +static void tcp_server(void *argr) +{ + char *recv_data; /* 用于接收的指针,后面会做一次动态分配以请求可用内存 */ + socklen_t sin_size; + int sock, connected, bytes_received; + struct sockaddr_in server_addr, client_addr; + int ret; + + recv_data = rt_malloc(BUFSZ + 1); /* 分配接收用的数据缓冲 */ + if (recv_data == RT_NULL) + { + rt_kprintf("No memory\n"); + return; + } + + /* 创建一个socket,类型是SOCKET_STREAM,TCP类型 */ + if ((sock = socket(AF_WIZ, SOCK_STREAM, IPPROTO_TCP)) == -1) + { + /* 创建socket失败 */ + rt_kprintf("Socket error\n"); + + /* 释放已分配的接收缓冲 */ + rt_free(recv_data); + return; + } + + /* 初始化本地服务端地址 */ + server_addr.sin_family = AF_WIZ; + server_addr.sin_port = htons(port); /* 服务端工作的端口 */ + server_addr.sin_addr.s_addr = INADDR_ANY; + rt_memset(&(server_addr.sin_zero), 0, sizeof(server_addr.sin_zero)); + + /* 绑定socket到服务端地址 */ + if (bind(sock, (struct sockaddr *)&server_addr, sizeof(struct sockaddr)) == -1) + { + /* 绑定失败 */ + rt_kprintf("Unable to bind\n"); + + /* 释放已分配的接收缓冲 */ + rt_free(recv_data); + return; + } + + /* 在socket上进行监听 */ + if (listen(sock, 5) == -1) + { + rt_kprintf("Listen error\n"); + + /* release recv buffer */ + rt_free(recv_data); + return; + } + + rt_kprintf("\nTCPServer Waiting for client on port %d...\n", port); + + is_running=1; + while (is_running) + { + + /* 接受一个客户端连接socket的请求,这个函数调用是阻塞式的 */ + connected = accept(sock, (struct sockaddr *)&client_addr, (socklen_t *)sizeof(struct sockaddr_in)); + /* 返回的是连接成功的socket */ + if (connected < 0) + { + rt_kprintf("accept connection failed! errno = %d\n", errno); + /* release recv buffer */ + rt_free(recv_data); + continue; + } + + /* 接受返回的client_addr指向了客户端的地址信息 */ + rt_kprintf("I got a connection from (%s , %d)\n", + inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port)); + + /* 客户端连接的处理 */ + while (is_running) + { + /* 发送数据到connected socket */ + ret = send(connected, send_data, strlen(send_data), 0); + if (ret < 0) + { + /* 发送失败,关闭这个连接 */ + rt_kprintf("\nsend error,close the socket.\r\n"); + closesocket(connected); + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + else if (ret == 0) + { + /* 打印send函数返回值为0的警告信息 */ + rt_kprintf("\n Send warning,send function return 0.\r\n"); + + } + + /* 从connected socket中接收数据,接收buffer是1024大小,但并不一定能够收到1024大小的数据 */ + bytes_received = recv(connected, recv_data, BUFSZ, 0); + if (bytes_received < 0) + { + /* 接收失败,关闭这个connected socket */ + rt_kprintf("\nReceived error, close the connect.\r\n"); + closesocket(connected); + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + else if (bytes_received == 0) + { + /* 打印recv函数返回值为0的警告信息 */ + rt_kprintf("\nReceived warning,recv function return 0.\r\n"); + closesocket(connected); + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + + /* 有接收到数据,把末端清零 */ + recv_data[bytes_received] = '\0'; + if (strncmp(recv_data, "q", 1) == 0 || strncmp(recv_data, "Q", 1) == 0) + { + /* 如果是首字母是q或Q,关闭这个连接 */ + rt_kprintf("\nGot a 'q' or 'Q', close the connect.\r\n"); + closesocket(connected); + /* 释放接收缓冲 */ + rt_free(recv_data); + break; + } + else if (strcmp(recv_data, "exit") == 0) + { + /* 如果接收的是exit,则关闭整个服务端 */ + closesocket(connected); + is_running=0; + break; + } + else + { + /* 在控制终端显示收到的数据 */ + rt_kprintf("RECEIVED DATA = %s \n", recv_data); + } + } + } + + /* 退出服务 */ + closesocket(sock); + + /* 释放接收缓冲 */ + rt_free(recv_data); + + return; +} +MSH_CMD_EXPORT(tcp_server, a tcp server sample); \ No newline at end of file diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h new file mode 100644 index 000000000..2a79578b4 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h @@ -0,0 +1,10 @@ +// +// Created by Y北辰 on 2022/7/28. +// + +#ifndef RT_THREAD_FUSION_XIUOS_TCP_SERVER_H +#define RT_THREAD_FUSION_XIUOS_TCP_SERVER_H + +#endif //RT_THREAD_FUSION_XIUOS_TCP_SERVER_H + +static void tcp_server(void *argr); \ No newline at end of file From 75a9e76257cc88b519c4ba5cab1a7ffe41ed9770 Mon Sep 17 00:00:00 2001 From: Beichen Date: Wed, 3 Aug 2022 11:23:22 +0800 Subject: [PATCH 04/21] =?UTF-8?q?Ubiquitous/RT-Thread=5FFusion=5FXiUOS/aii?= =?UTF-8?q?t=5Fboard/xidatong-riscv64/=EF=BC=9AUpdate=20the=20.config?= =?UTF-8?q?=E3=80=81rtconfig.h=20and=20Update=20tcp=5Fclient.h=20and=20tcp?= =?UTF-8?q?=5Fserver.h=20in=20the=20applications=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiit_board/xidatong-riscv64/.config | 61 +++++++++++++------ .../applications/tcp_client.h | 3 +- .../applications/tcp_server.h | 3 +- .../aiit_board/xidatong-riscv64/rtconfig.h | 41 +++++++++---- 4 files changed, 77 insertions(+), 31 deletions(-) diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/.config b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/.config index 829ff9017..e12d40794 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/.config +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/.config @@ -28,7 +28,9 @@ CONFIG_RT_USING_IDLE_HOOK=y CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 CONFIG_IDLE_THREAD_STACK_SIZE=4096 CONFIG_SYSTEM_THREAD_STACK_SIZE=4096 -# CONFIG_RT_USING_TIMER_SOFT is not set +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=2048 # # kservice optimization @@ -231,12 +233,23 @@ CONFIG_RT_LIBC_DEFAULT_TIMEZONE=8 # # Socket abstraction layer # -# CONFIG_RT_USING_SAL is not set +CONFIG_RT_USING_SAL=y +CONFIG_SAL_INTERNET_CHECK=y +# CONFIG_SAL_USING_POSIX is not set +CONFIG_SAL_SOCKETS_NUM=16 # # Network interface device # -# CONFIG_RT_USING_NETDEV is not set +CONFIG_RT_USING_NETDEV=y +CONFIG_NETDEV_USING_IFCONFIG=y +CONFIG_NETDEV_USING_PING=y +CONFIG_NETDEV_USING_NETSTAT=y +CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_IPV6 is not set +CONFIG_NETDEV_IPV4=1 +CONFIG_NETDEV_IPV6=0 +# CONFIG_NETDEV_IPV6_SCOPES is not set # # light weight TCP/IP stack @@ -280,25 +293,20 @@ CONFIG_BSP_USING_UART_HS=y # CONFIG_BSP_USING_UART2 is not set # CONFIG_BSP_USING_UART3 is not set # CONFIG_BSP_USING_I2C1 is not set -# CONFIG_BSP_USING_SPI1 is not set +CONFIG_BSP_USING_SPI1=y +CONFIG_BSP_SPI1_CLK_PIN=9 +CONFIG_BSP_SPI1_D0_PIN=11 +CONFIG_BSP_SPI1_D1_PIN=10 +CONFIG_BSP_SPI1_USING_SS0=y +CONFIG_BSP_SPI1_SS0_PIN=12 +# CONFIG_BSP_SPI1_USING_SS1 is not set +# CONFIG_BSP_SPI1_USING_SS2 is not set +# CONFIG_BSP_SPI1_USING_SS3 is not set # # Onboard Peripheral Drivers # -CONFIG_BSP_USING_LCD=y -CONFIG_BSP_LCD_CS_PIN=41 -CONFIG_BSP_LCD_WR_PIN=38 -CONFIG_BSP_LCD_DC_PIN=39 -CONFIG_BSP_LCD_RST_PIN=37 -CONFIG_BSP_LCD_BACKLIGHT_PIN=-1 -CONFIG_BSP_LCD_BACKLIGHT_ACTIVE_LOW=y -# CONFIG_BSP_LCD_BACKLIGHT_ACTIVE_HIGH is not set -CONFIG_BSP_LCD_CLK_FREQ=15000000 -# CONFIG_BSP_BOARD_KD233 is not set -CONFIG_BSP_BOARD_K210_OPENMV_TEST=y -# CONFIG_BSP_BOARD_USER is not set -CONFIG_BSP_LCD_X_MAX=480 -CONFIG_BSP_LCD_Y_MAX=272 +# CONFIG_BSP_USING_LCD is not set # CONFIG_BSP_USING_CH438 is not set # @@ -318,6 +326,23 @@ CONFIG_PKG_KENDRYTE_SDK_VERNUM=0x0055 # CONFIG_DRV_USING_OV2640 is not set # CONFIG_DRV_USING_HS300X is not set # CONFIG_DRV_USING_SX1278 is not set +CONFIG_PKG_USING_WIZNET=y +CONFIG_PKG_WIZNET_PATH="/packages/iot/wiznet" +CONFIG_WIZ_USING_W5500=y +# CONFIG_WIZNET_DEVICE_EXTERN_CONFIG is not set + +# +# WIZnet device configure +# +CONFIG_WIZ_SPI_DEVICE="spi10" +CONFIG_WIZ_RST_PIN=13 +CONFIG_WIZ_IRQ_PIN=14 +CONFIG_WIZ_USING_DHCP=y +CONFIG_WIZ_USING_PING=y +# CONFIG_WIZ_DEBUG is not set +# CONFIG_PKG_USING_WIZNET_V200 is not set +CONFIG_PKG_USING_WIZNET_LATEST_VERSION=y +CONFIG_PKG_WIZNET_VER="latest" # # APP_Framework diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h index 6190811cc..2afa1818a 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_client.h @@ -5,6 +5,7 @@ #ifndef RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H #define RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H +static void tcpclient(int argc, char **argv); + #endif //RT_THREAD_FUSION_XIUOS_TCP_CLIENT_H -static void tcpclient(int argc, char **argv); \ No newline at end of file diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h index 2a79578b4..6b00ba1e3 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/applications/tcp_server.h @@ -5,6 +5,7 @@ #ifndef RT_THREAD_FUSION_XIUOS_TCP_SERVER_H #define RT_THREAD_FUSION_XIUOS_TCP_SERVER_H +static void tcp_server(void *argr); + #endif //RT_THREAD_FUSION_XIUOS_TCP_SERVER_H -static void tcp_server(void *argr); \ No newline at end of file diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/rtconfig.h b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/rtconfig.h index f66495531..eee446b17 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/rtconfig.h +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/rtconfig.h @@ -25,6 +25,9 @@ #define RT_IDLE_HOOK_LIST_SIZE 4 #define IDLE_THREAD_STACK_SIZE 4096 #define SYSTEM_THREAD_STACK_SIZE 4096 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 2048 /* kservice optimization */ @@ -143,9 +146,19 @@ /* Socket abstraction layer */ +#define RT_USING_SAL +#define SAL_INTERNET_CHECK +#define SAL_SOCKETS_NUM 16 /* Network interface device */ +#define RT_USING_NETDEV +#define NETDEV_USING_IFCONFIG +#define NETDEV_USING_PING +#define NETDEV_USING_NETSTAT +#define NETDEV_USING_AUTO_DEFAULT +#define NETDEV_IPV4 1 +#define NETDEV_IPV6 0 /* light weight TCP/IP stack */ @@ -168,20 +181,15 @@ #define __STACKSIZE__ 4096 #define BSP_USING_UART_HS +#define BSP_USING_SPI1 +#define BSP_SPI1_CLK_PIN 9 +#define BSP_SPI1_D0_PIN 11 +#define BSP_SPI1_D1_PIN 10 +#define BSP_SPI1_USING_SS0 +#define BSP_SPI1_SS0_PIN 12 /* Onboard Peripheral Drivers */ -#define BSP_USING_LCD -#define BSP_LCD_CS_PIN 41 -#define BSP_LCD_WR_PIN 38 -#define BSP_LCD_DC_PIN 39 -#define BSP_LCD_RST_PIN 37 -#define BSP_LCD_BACKLIGHT_PIN -1 -#define BSP_LCD_BACKLIGHT_ACTIVE_LOW -#define BSP_LCD_CLK_FREQ 15000000 -#define BSP_BOARD_K210_OPENMV_TEST -#define BSP_LCD_X_MAX 272 -#define BSP_LCD_Y_MAX 480 /* Kendryte SDK Config */ @@ -192,6 +200,17 @@ /* More Drivers */ +#define PKG_USING_WIZNET +#define WIZ_USING_W5500 + +/* WIZnet device configure */ + +#define WIZ_SPI_DEVICE "spi10" +#define WIZ_RST_PIN 13 +#define WIZ_IRQ_PIN 14 +#define WIZ_USING_DHCP +#define WIZ_USING_PING +#define PKG_USING_WIZNET_LATEST_VERSION /* APP_Framework */ From 8fb4c35a35ca521a2809a2fa78bbbe954cdca2d0 Mon Sep 17 00:00:00 2001 From: Beichen Date: Wed, 3 Aug 2022 11:25:45 +0800 Subject: [PATCH 05/21] =?UTF-8?q?Ubiquitous/RT-Thread=5FFusion=5FXiUOS/aii?= =?UTF-8?q?t=5Fboard/xidatong-riscv64/=EF=BC=9AAdd=20test=20folder?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xidatong-riscv64/test/tcp_client.png | Bin 0 -> 59466 bytes .../xidatong-riscv64/test/tcp_server.png | Bin 0 -> 100358 bytes .../aiit_board/xidatong-riscv64/test/w5500.png | Bin 0 -> 122523 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_client.png create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_server.png create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/w5500.png diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_client.png b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_client.png new file mode 100644 index 0000000000000000000000000000000000000000..4038ac35a86cf68b8c8b592d854cd7df2ef6aa38 GIT binary patch literal 59466 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 literal 0 HcmV?d00001 diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_server.png b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-riscv64/test/tcp_server.png new file mode 100644 index 0000000000000000000000000000000000000000..08eb3436f7742eba9366df0d892bf612bf45e7c6 GIT binary patch literal 100358 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

@p!SgdJjuug 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 A1^@s6 literal 0 HcmV?d00001 From bfa8c734c4849097e9edb7179103629e24ae1351 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Thu, 4 Aug 2022 14:42:25 +0800 Subject: [PATCH 06/21] xiuos\APP_Framework\Framework\connection\lora\e220: (1)Add Kconfig files and SConscript files related to e220 --- .../Framework/connection/lora/e220/Kconfig | 33 +++++++++++++++++-- .../Framework/connection/lora/e220/SConscript | 10 ++++++ 2 files changed, 41 insertions(+), 2 deletions(-) create mode 100644 APP_Framework/Framework/connection/lora/e220/SConscript diff --git a/APP_Framework/Framework/connection/lora/e220/Kconfig b/APP_Framework/Framework/connection/lora/e220/Kconfig index 322b307c8..34e106f47 100644 --- a/APP_Framework/Framework/connection/lora/e220/Kconfig +++ b/APP_Framework/Framework/connection/lora/e220/Kconfig @@ -39,11 +39,11 @@ if ADD_NUTTX_FETURES config ADAPTER_E220_M0_PATH string "E220 M0 pin device" - default "/dev/gpio0" + default "/dev/gpout0" config ADAPTER_E220_M1_PATH string "E220 M1 pin device" - default "/dev/gpio1" + default "/dev/gpout1" config ADAPTER_E220_DRIVER_EXTUART bool "Using extra uart to support lora" @@ -64,5 +64,34 @@ if ADD_NUTTX_FETURES endif if ADD_RTTHREAD_FETURES + config ADAPTER_E220_M0 + int "E220 M0 pin number" + default "11" + config ADAPTER_E220_M1 + int "E220 M1 pin number" + default "9" + + config ADAPTER_E220_PIN_DRIVER + string "E220 device pin driver path" + default "/dev/dev3" + + config ADAPTER_E220_DRIVER_EXTUART + bool "Using extra uart to support lora" + default y + + config ADAPTER_E220_DRIVER + string "E220 device uart driver path" + default "/dev/dev3" + depends on !ADAPTER_E220_DRIVER_EXTUART + + if ADAPTER_E220_DRIVER_EXTUART + config ADAPTER_E220_DRIVER + string "E220 device extra uart driver path" + default "/dev/dev3" + + config ADAPTER_E220_DRIVER_EXT_PORT + int "if E220 device using extuart, choose port" + default "3" + endif endif diff --git a/APP_Framework/Framework/connection/lora/e220/SConscript b/APP_Framework/Framework/connection/lora/e220/SConscript new file mode 100644 index 000000000..8ad6e9cc3 --- /dev/null +++ b/APP_Framework/Framework/connection/lora/e220/SConscript @@ -0,0 +1,10 @@ +from building import * +import os + +cwd = GetCurrentDir() +src = [] +if GetDepend(['ADAPTER_E220']): + src += ['e220.c'] +group = DefineGroup('connection lora e220', src, depend = [], CPPPATH = [cwd]) + +Return('group') \ No newline at end of file From f9984b5b7da52d36643e123a93a6b1f3dcf18022 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 10:53:00 +0800 Subject: [PATCH 07/21] xiuos\Ubiquitous\RT-Thread_Fusion_XiUOS\aiit_board\xidatong-arm32\test_photo: 1.Lora send and receive result --- .../test_photo/Lora/lora receive.PNG | Bin 0 -> 16836 bytes .../test_photo/Lora/lora send.PNG | Bin 0 -> 12399 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora receive.PNG create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora send.PNG diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora receive.PNG b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora receive.PNG new file mode 100644 index 0000000000000000000000000000000000000000..2650a4ede37ec93ae4ac8400f21ea121bb970239 GIT binary patch literal 16836 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 literal 0 HcmV?d00001 diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora send.PNG b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Lora/lora send.PNG new file mode 100644 index 0000000000000000000000000000000000000000..595a261573c6775d7194ed57602ba9556f855ffb GIT binary patch literal 12399 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 Date: Tue, 9 Aug 2022 10:54:12 +0800 Subject: [PATCH 08/21] =?UTF-8?q?xiuos\Ubiquitous\RT-Thread=5FFusion=5FXiU?= =?UTF-8?q?OS\aiit=5Fboard\xidatong-arm32\test=5Fphoto\CH438=EF=BC=9A=201.?= =?UTF-8?q?zigbee=20base=20ch438=20receive?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../CH438/zigbee base ch438 receive.png | Bin 0 -> 42809 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/CH438/zigbee base ch438 receive.png diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/CH438/zigbee base ch438 receive.png b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/CH438/zigbee base ch438 receive.png new file mode 100644 index 0000000000000000000000000000000000000000..72acb8cc451b691ea341caded175a56523241fcd GIT binary patch literal 42809 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%0A+UNY3i9 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 bo1Rv#C~=yK`F?E0ZFq*Xo$ONPe31Sd8MLjz literal 0 HcmV?d00001 From e812b5a49fddc5bf0e546506386969d411439a47 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 10:54:50 +0800 Subject: [PATCH 09/21] =?UTF-8?q?xiuos\Ubiquitous\RT-Thread=5FFusion=5FXiU?= =?UTF-8?q?OS\aiit=5Fboard\xidatong-arm32\board=EF=BC=9A=201.Add=20ch438.c?= =?UTF-8?q?=20and=20ch438.h=202.include=20test=20function=20about=20ch438,?= =?UTF-8?q?hc08,lora,e18?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../board/hardware/ch438/ch438.c | 1323 +++++++++++++++++ .../board/hardware/ch438/ch438.h | 413 +++++ 2 files changed, 1736 insertions(+) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/hardware/ch438/ch438.c create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/hardware/ch438/ch438.h diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/hardware/ch438/ch438.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/hardware/ch438/ch438.c new file mode 100644 index 000000000..4b5b21688 --- /dev/null +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/hardware/ch438/ch438.c @@ -0,0 +1,1323 @@ +/* +* Copyright (c) 2020 AIIT XUOS Lab +* XiOS is licensed under Mulan PSL v2. +* You can use this software according to the terms and conditions of the Mulan PSL v2. +* You may obtain a copy of Mulan PSL v2 at: +* http://license.coscl.org.cn/MulanPSL2 +* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, +* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, +* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE. +* See the Mulan PSL v2 for more details. +*/ + +/** + * @file .c + * @brief imxrt board sd card automount + * @version 1.0 + * @author AIIT XUOS Lab + * @date 2022.06.30 + */ + +#include "ch438.h" +#include + +#ifdef BSP_USING_CH438 + +#define RT (0U) // If config this macro ,we can use rt gpio for ch438 us + +/* This array shows whether the current serial port is selected */ +static bool const g_uart_selected[CH438PORTNUM] = +{ +#ifdef CONFIG_CH438_EXTUART0 + [0] = true, +#endif +#ifdef CONFIG_CH438_EXTUART1 + [1] = true, +#endif +#ifdef CONFIG_CH438_EXTUART2 + [2] = true, +#endif +#ifdef CONFIG_CH438_EXTUART3 + [3] = true, +#endif +#ifdef CONFIG_CH438_EXTUART4 + [4] = true, +#endif +#ifdef CONFIG_CH438_EXTUART5 + [5] = true, +#endif +#ifdef CONFIG_CH438_EXTUART6 + [6] = true, +#endif +#ifdef CONFIG_CH438_EXTUART7 + [7] = true, +#endif +}; + + +/* rt-thread sem and serial definition */ +struct rt_serial_device *extuart_serial_parm[CH438PORTNUM]; +static rt_sem_t rx_sem[CH438PORTNUM]={RT_NULL}; +char * sem[CH438PORTNUM]={"sem0","sem1","sem2","sem3","sem4","sem5","sem6","sem7"}; +/* rt-thread workqueue*/ +struct rt_workqueue* rq; +/* there is data available on the corresponding port */ +static volatile bool done[CH438PORTNUM] = {false,false,false,false,false,false,false,false}; + +/* Eight port data buffer */ +static uint8_t buff[CH438PORTNUM][CH438_BUFFSIZE]; + +/* the value of interrupt number of SSR register */ +static uint8_t Interruptnum[CH438PORTNUM] = {0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80,}; + +/* Offset address of serial port number */ +static uint8_t offsetadd[CH438PORTNUM] = {0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38,}; + + +const struct rt_uart_ops ch438_ops={ + rt_ch438_configure, + rt_ch438_control, + rt_ch438_putc, + rt_ch438_getc, + RT_NULL +}; + + +void Config_Interrupt(void){ + gpio_pin_config_t int_config = { + kGPIO_DigitalInput, + 0, + kGPIO_IntFallingEdge, + }; + GPIO_PinInit(CH438_CTL_GPIO, CH438_INT_PIN, &int_config); + /* Enable GPIO pin interrupt */ + GPIO_PortEnableInterrupts(CH438_CTL_GPIO, 1U << CH438_INT_PIN); +} + + +static void Ch438Irq(void * parameter) +{ + rt_uint8_t gInterruptStatus; + rt_uint8_t port = 0; + struct rt_serial_device *serial = (struct rt_serial_device *)parameter; + /* multi irq may happen*/ + gInterruptStatus = ReadCH438Data(REG_SSR_ADDR); + port=CH438_INT_PORT; + rt_hw_serial_isr(extuart_serial_parm[port], RT_SERIAL_EVENT_RX_IND); +} + + +static rt_err_t rt_ch438_configure(struct rt_serial_device *serial, struct serial_configure *cfg) +{ + rt_uint32_t baud_rate = cfg->baud_rate; + rt_uint16_t port = cfg->reserved; + CH438PortInit(port, baud_rate); + return RT_EOK; +} + +static rt_err_t rt_ch438_control(struct rt_serial_device *serial, int cmd, void *arg) +{ + rt_uint16_t ext_uart_no = serial->config.reserved; + static rt_uint16_t register_flag = 0; + + switch (cmd) + { + case RT_DEVICE_CTRL_CLR_INT: + if(1 == register_flag) + { + /* Close interrupt of CH438 */ + /* GPIO3_3 INT*/ + rt_pin_irq_enable(CH438_INT, PIN_IRQ_DISABLE ); + register_flag = 0; + }break; + case RT_DEVICE_CTRL_SET_INT: + if(0 == register_flag) + { + rt_pin_mode(CH438_INT, PIN_MODE_INPUT_PULLUP); + rt_pin_attach_irq(CH438_INT, PIN_IRQ_MODE_FALLING,Ch438Irq,(void *)serial); + rt_pin_irq_enable(CH438_INT, PIN_IRQ_ENABLE); + register_flag = 1; + }break; + } + return (RT_EOK); +} + +static int rt_ch438_putc(struct rt_serial_device *serial, char c) +{ + uint16_t ext_uart_no = serial->config.reserved; + rt_uint8_t REG_LSR_ADDR,REG_THR_ADDR; + + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_THR_ADDR = offsetadd[ext_uart_no] | REG_THR0_ADDR; + rt_thread_mdelay(5); + if((ReadCH438Data( REG_LSR_ADDR ) & BIT_LSR_TEMT) != 0) + { + + WriteCH438Block( REG_THR_ADDR, 1, &c ); + return 1; + } else { + return 0; + } + +} + + +static int rt_ch438_getc(struct rt_serial_device *serial) +{ + + rt_uint8_t dat = 0; + rt_uint8_t REG_LSR_ADDR,REG_RBR_ADDR; + uint16_t ext_uart_no = serial->config.reserved;///< get extern uart port + + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_RBR_ADDR = offsetadd[ext_uart_no] | REG_RBR0_ADDR; + rt_thread_mdelay(5); + if((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0x01) + { + dat = ReadCH438Data( REG_RBR_ADDR ); + if(dat >= 0) + return dat; + }else{ + return -1; + } + +} + + +int rt_hw_ch438_init(void) +{ + struct rt_serial_device *extserial; + struct device_uart *extuart; + struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT; + rt_err_t ret; + #ifdef CONFIG_CH438_EXTUART0 + static struct rt_serial_device extserial0; + + extserial = &extserial0; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 115200; + extserial->config.reserved = 0; ///< extern uart port + + extuart_serial_parm[0] = &extserial0; + + ret = rt_hw_serial_register(extserial, + "dev0", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev0 register failed.\n"); + } + #endif + #ifdef CONFIG_CH438_EXTUART1 + static struct rt_serial_device extserial1; + + extserial = &extserial1; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 115200; + extserial->config.reserved = 1; ///< extern uart port + + extuart_serial_parm[1] = &extserial1; + + ret = rt_hw_serial_register(extserial, + "dev1", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev1 register failed.\n"); + } + #endif + #ifdef CONFIG_CH438_EXTUART2 + static struct rt_serial_device extserial2; + + extserial = &extserial2; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 2; ///< extern uart port + + extuart_serial_parm[2] = &extserial2; + + ret = rt_hw_serial_register(extserial, + "dev2", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev2 register failed.\n"); + } + rt_kprintf("extuart_dev2 register succeed.\n"); + #endif + #ifdef CONFIG_CH438_EXTUART3 + static struct rt_serial_device extserial3; + + extserial = &extserial3; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 3; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "dev3", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev3 register failed.\n"); + } + + extuart_serial_parm[3] = &extserial3; + #endif + #ifdef CONFIG_CH438_EXTUART4 + static struct rt_serial_device extserial4; + + extserial = &extserial4; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 4; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "dev4", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev4 register failed.\n"); + } + + extuart_serial_parm[4] = &extserial4; + #endif + #ifdef CONFIG_CH438_EXTUART5 + static struct rt_serial_device extserial5; + + extserial = &extserial5; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 115200; + extserial->config.reserved = 5; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "dev5", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev5 register failed.\n"); + } + + extuart_serial_parm[5] = &extserial5; + #endif + #ifdef CONFIG_CH438_EXTUART6 + static struct rt_serial_device extserial6; + + extserial = &extserial6; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 57600; + extserial->config.reserved = 6; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "dev6", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev6 register failed.\n"); + } + + extuart_serial_parm[6] = &extserial6; + #endif + #ifdef CONFIG_CH438_EXTUART7 + static struct rt_serial_device extserial7; + + extserial = &extserial7; + extserial->ops = &ch438_ops; + extserial->config = config; + extserial->config.baud_rate = 9600; + extserial->config.reserved = 7; ///< extern uart port + + ret = rt_hw_serial_register(extserial, + "dev7", + RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX, + extuart); + if(ret < 0){ + rt_kprintf("extuart_dev7 register failed.\n"); + } + rt_kprintf("extuart_dev7 register succeed.\n"); + extuart_serial_parm[7] = &extserial7; + #endif + Ch438InitDefault(); + + return 0; + +} +INIT_DEVICE_EXPORT(rt_hw_ch438_init); + + +void up_udelay(void) +{ + volatile uint32_t i = 0; + for (i = 0; i < EXAMPLE_DELAY_COUNT; ++i) + { + __asm("NOP"); /* delay */ + } +} + +void up_mdelay(uint32_t time){ + + while(time--){ + udelay(1000); + } +} +/**************************************************************************** + * Name: CH438SetOutput + * + * Description: + * Configure pin mode to output + * + ****************************************************************************/ +static void CH438SetOutput(void) +{ + gpio_pin_config_t ch438_d0_config ={kGPIO_DigitalOutput, 0, kGPIO_NoIntmode}; + GPIO_PinInit(CH438_D_GPIO,CH438_D0_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D1_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D2_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D3_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D4_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D5_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D6_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D7_PIN,&ch438_d0_config); + +} + +/**************************************************************************** + * Name: CH438SetInput + * + * Description: + * Configure pin mode to input + * + ****************************************************************************/ +static void CH438SetInput(void) +{ + gpio_pin_config_t ch438_d0_config ={kGPIO_DigitalInput, 0, kGPIO_NoIntmode}; + GPIO_PinInit(CH438_D_GPIO,CH438_D0_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D1_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D2_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D3_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D4_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D5_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D6_PIN,&ch438_d0_config); + GPIO_PinInit(CH438_D_GPIO,CH438_D7_PIN,&ch438_d0_config); + +} +//#define ADAPTER_ZIGBEE_E18 +/**************************************************************************** + * Name: ReadCH438Data + * + * Description: + * Read data from ch438 address + * + ****************************************************************************/ +static uint8_t ReadCH438Data(uint8_t addr) +{ + uint8_t dat = 0; + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NWR_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NRD_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_HIGH); + + + CH438SetOutput(); + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + if(addr &0x80) GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_LOW); + if(addr &0x40) GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_LOW); + if(addr &0x20) GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_LOW); + if(addr &0x10) GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_LOW); + if(addr &0x08) GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_LOW); + if(addr &0x04) GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_LOW); + if(addr &0x02) GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_LOW); + if(addr &0x01) GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_LOW); + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_LOW); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + + CH438SetInput(); + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NRD_PIN,PIN_LOW); + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + + + if (GPIO_PinRead(CH438_D_GPIO,CH438_D7_PIN)) dat |= 0x80; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D6_PIN)) dat |= 0x40; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D5_PIN)) dat |= 0x20; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D4_PIN)) dat |= 0x10; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D3_PIN)) dat |= 0x08; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D2_PIN)) dat |= 0x04; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D1_PIN)) dat |= 0x02; + if (GPIO_PinRead(CH438_D_GPIO,CH438_D0_PIN)) dat |= 0x01; + + + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NRD_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_HIGH); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(10); + #else + udelay(1); + #endif + + + return dat; +} + +/**************************************************************************** + * Name: WriteCH438Data + * + * Description: + * write data to ch438 address + * + ****************************************************************************/ +static void WriteCH438Data(uint8_t addr, const uint8_t dat) +{ + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NRD_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NWR_PIN,PIN_HIGH); + + + CH438SetOutput(); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + + if(addr &0x80) GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_LOW); + if(addr &0x40) GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_LOW); + if(addr &0x20) GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_LOW); + if(addr &0x10) GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_LOW); + if(addr &0x08) GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_LOW); + if(addr &0x04) GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_LOW); + if(addr &0x02) GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_LOW); + if(addr &0x01) GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_LOW); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_LOW); + + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + + if(dat &0x80) GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D7_PIN,PIN_LOW); + if(dat &0x40) GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D6_PIN,PIN_LOW); + if(dat &0x20) GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D5_PIN,PIN_LOW); + if(dat &0x10) GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D4_PIN,PIN_LOW); + if(dat &0x08) GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D3_PIN,PIN_LOW); + if(dat &0x04) GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D2_PIN,PIN_LOW); + if(dat &0x02) GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D1_PIN,PIN_LOW); + if(dat &0x01) GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_HIGH); else GPIO_PinWrite(CH438_D_GPIO,CH438_D0_PIN,PIN_LOW); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NWR_PIN,PIN_LOW); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NWR_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_HIGH); + + + #ifdef ADAPTER_ZIGBEE_E18 + udelay(100); + #else + udelay(1); + #endif + + CH438SetInput(); + + return; +} + +/**************************************************************************** + * Name: WriteCH438Block + * + * Description: + * Write data block from ch438 address + * + ****************************************************************************/ +static void WriteCH438Block(uint8_t mAddr, uint8_t mLen, const uint8_t *mBuf) +{ + while(mLen--) + WriteCH438Data(mAddr, *mBuf++); +} + +/**************************************************************************** + * Name: CH438UARTSend + * + * Description: + * Enable FIFO mode, which is used for ch438 serial port to send multi byte data, + * with a maximum of 128 bytes of data sent at a time + * + ****************************************************************************/ +static void Ch438UartSend(uint8_t ext_uart_no, const uint8_t *Data, uint16_t Num) +{ + uint8_t REG_LSR_ADDR,REG_THR_ADDR; + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_THR_ADDR = offsetadd[ext_uart_no] | REG_THR0_ADDR; + + while(1) + { + while((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_TEMT) == 0); /* wait for sending data done, THR and TSR is NULL */ + if(Num <= 128) + { + WriteCH438Block(REG_THR_ADDR, Num, Data); + break; + } + else + { + WriteCH438Block(REG_THR_ADDR, 128, Data); + Num -= 128; + Data += 128; + } + } + +} + +/**************************************************************************** + * Name: CH438UARTRcv + * + * Description: + * Disable FIFO mode for ch438 serial port to receive multi byte data + * + ****************************************************************************/ +uint8_t CH438UARTRcv(uint8_t ext_uart_no, uint8_t *buf, size_t size) +{ + uint8_t rcv_num = 0; + uint8_t dat = 0; + uint8_t REG_LSR_ADDR,REG_RBR_ADDR; + uint8_t *read_buffer; + size_t buffer_index = 0; + + read_buffer = buf; + + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_RBR_ADDR = offsetadd[ext_uart_no] | REG_RBR0_ADDR; + + /* Wait for the data to be ready */ + int count=0; + while ((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0){ + } + while (((ReadCH438Data(REG_LSR_ADDR) & BIT_LSR_DATARDY) == 0x01) && (size != 0)) + { count++; + if(1==count){ + rt_thread_mdelay(5000); + } + rt_thread_mdelay(5); + dat = ReadCH438Data(REG_RBR_ADDR); + *read_buffer = dat; + read_buffer++; + buffer_index++; + if (255 == buffer_index) { + buffer_index = 0; + read_buffer = buf; + } + + ++rcv_num; + --size; + } + + return rcv_num; +} + +/**************************************************************************** + * Name: ImxrtCH438Init + * + * Description: + * ch438 initialization + * + ****************************************************************************/ +static void ImxrtCH438Init(void) +{ + CH438SetOutput(); + gpio_pin_config_t ch438_ctl_config ={kGPIO_DigitalOutput, 0, kGPIO_NoIntmode}; + GPIO_PinInit(CH438_CTL_GPIO,CH438_NWR_PIN,&ch438_ctl_config); + GPIO_PinInit(CH438_CTL_GPIO,CH438_NRD_PIN,&ch438_ctl_config); + GPIO_PinInit(CH438_CTL_GPIO,CH438_ALE_PIN,&ch438_ctl_config); + + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NWR_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_NRD_PIN,PIN_HIGH); + GPIO_PinWrite(CH438_CTL_GPIO,CH438_ALE_PIN,PIN_HIGH); +} + +/**************************************************************************** + * Name: CH438PortInit + * + * Description: + * ch438 port initialization + * + ****************************************************************************/ +static void CH438PortInit(uint8_t ext_uart_no, uint32_t baud_rate) +{ + uint32_t div; + uint8_t DLL,DLM,dlab; + uint8_t REG_LCR_ADDR; + uint8_t REG_DLL_ADDR; + uint8_t REG_DLM_ADDR; + uint8_t REG_IER_ADDR; + uint8_t REG_MCR_ADDR; + uint8_t REG_FCR_ADDR; + + REG_LCR_ADDR = offsetadd[ext_uart_no] | REG_LCR0_ADDR; + REG_DLL_ADDR = offsetadd[ext_uart_no] | REG_DLL0_ADDR; + REG_DLM_ADDR = offsetadd[ext_uart_no] | REG_DLM0_ADDR; + REG_IER_ADDR = offsetadd[ext_uart_no] | REG_IER0_ADDR; + REG_MCR_ADDR = offsetadd[ext_uart_no] | REG_MCR0_ADDR; + REG_FCR_ADDR = offsetadd[ext_uart_no] | REG_FCR0_ADDR; + + /* reset the uart */ + WriteCH438Data(REG_IER_ADDR, BIT_IER_RESET); + up_mdelay(50); + + dlab = ReadCH438Data(REG_IER_ADDR); + dlab &= 0xDF; + WriteCH438Data(REG_IER_ADDR, dlab); + + /* set LCR register DLAB bit 1 */ + dlab = ReadCH438Data(REG_LCR_ADDR); + dlab |= 0x80; + WriteCH438Data(REG_LCR_ADDR, dlab); + + div = (Fpclk >> 4) / baud_rate; + DLM = div >> 8; + DLL = div & 0xff; + + /* set bps */ + WriteCH438Data(REG_DLL_ADDR, DLL); + WriteCH438Data(REG_DLM_ADDR, DLM); + + /* set FIFO mode, 112 bytes */ + WriteCH438Data(REG_FCR_ADDR, BIT_FCR_RECVTG1 | BIT_FCR_RECVTG0 | BIT_FCR_FIFOEN); + + /* 8 bit word size, 1 bit stop bit, no crc */ + WriteCH438Data(REG_LCR_ADDR, BIT_LCR_WORDSZ1 | BIT_LCR_WORDSZ0); + + /* enable interrupt */ + WriteCH438Data(REG_IER_ADDR, BIT_IER_IERECV); + + /* allow interrupt output, DTR and RTS is 1 */ + WriteCH438Data(REG_MCR_ADDR, BIT_MCR_OUT2); + + /* release the data in FIFO */ + WriteCH438Data(REG_FCR_ADDR, ReadCH438Data(REG_FCR_ADDR)| BIT_FCR_TFIFORST); +} +/**************************************************************************** + * Name: ImxrtCh438ReadData + * + * Description: + * Read data from ch438 port + * + ****************************************************************************/ +static int ImxrtCh438WriteData(uint8_t ext_uart_no, const uint8_t *write_buffer, size_t size) +{ + int write_len, write_len_continue; + int i, write_index; + if(write_buffer == NULL){ + return ERROR; + } + + write_len = size; + write_len_continue = size; + + if(write_len > 256) + { + if(0 == write_len % 256) + { + write_index = write_len / 256; + for(i = 0; i < write_index; i ++) + { + Ch438UartSend(ext_uart_no, write_buffer + i * 256, 256); + } + } + else + { + write_index = 0; + while(write_len_continue > 256) + { + Ch438UartSend(ext_uart_no, write_buffer + write_index * 256, 256); + write_index++; + write_len_continue = write_len - write_index * 256; + } + Ch438UartSend(ext_uart_no, write_buffer + write_index * 256, write_len_continue); + } + } + else + { + Ch438UartSend(ext_uart_no, write_buffer, write_len); + } + + return OK; +} + +/**************************************************************************** + * Name: ImxrtCh438ReadData + * + * Description: + * Read data from ch438 port + * + ****************************************************************************/ +static size_t ImxrtCh438ReadData(uint8_t ext_uart_no, size_t size) +{ + size_t RevLen = 0; + uint8_t InterruptStatus; + uint8_t REG_IIR_ADDR; + uint8_t REG_LSR_ADDR; + uint8_t REG_MSR_ADDR; + + REG_IIR_ADDR = offsetadd[ext_uart_no] | REG_IIR0_ADDR; + REG_LSR_ADDR = offsetadd[ext_uart_no] | REG_LSR0_ADDR; + REG_MSR_ADDR = offsetadd[ext_uart_no] | REG_MSR0_ADDR; + /* Read the interrupt status of the serial port */ + InterruptStatus = ReadCH438Data(REG_IIR_ADDR) & 0x0f; + rt_kprintf("InterruptStatus is %d\n", InterruptStatus); + + switch(InterruptStatus) + { + case INT_NOINT: /* no interrupt */ + break; + case INT_THR_EMPTY: /* the transmit hold register is not interrupted */ + break; + case INT_RCV_OVERTIME: /* receive data timeout interrupt */ + case INT_RCV_SUCCESS: /* receive data available interrupt */ + RevLen = CH438UARTRcv(ext_uart_no, buff[ext_uart_no], size); + break; + case INT_RCV_LINES: /* receive line status interrupt */ + ReadCH438Data(REG_LSR_ADDR); + break; + case INT_MODEM_CHANGE: /* modem input change interrupt */ + ReadCH438Data(REG_MSR_ADDR); + break; + default: + break; + } + + return RevLen; +} + +/**************************************************************************** + * Name: Ch438InitDefault + * + * Description: + * Ch438 default initialization function + * + ****************************************************************************/ +static void Ch438InitDefault(void) +{ + /*Dynamically create semaphores */ + semaphoreChInit(); + + ImxrtCH438Init(); + +/* If a port is checked, the port will be initialized. Otherwise, the interrupt of the port will be disabled. */ + +#ifdef CONFIG_CH438_EXTUART0 + CH438PortInit(0, CONFIG_CH438_EXTUART0_BAUD); +#else + WriteCH438Data(REG_IER0_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART1 + CH438PortInit(1, CONFIG_CH438_EXTUART1_BAUD); +#else + WriteCH438Data(REG_IER1_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART2 + CH438PortInit(2, CONFIG_CH438_EXTUART2_BAUD); +#else + WriteCH438Data(REG_IER2_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART3 + CH438PortInit(3, CONFIG_CH438_EXTUART3_BAUD); +#else + WriteCH438Data(REG_IER3_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART4 + CH438PortInit(4, CONFIG_CH438_EXTUART4_BAUD); +#else + WriteCH438Data(REG_IER4_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART5 + CH438PortInit(5, CONFIG_CH438_EXTUART5_BAUD); +#else + WriteCH438Data(REG_IER5_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART6 + CH438PortInit(6, CONFIG_CH438_EXTUART6_BAUD); +#else + WriteCH438Data(REG_IER6_ADDR, 0x00); +#endif + +#ifdef CONFIG_CH438_EXTUART7 + CH438PortInit(7, CONFIG_CH438_EXTUART7_BAUD); +#else + WriteCH438Data(REG_IER7_ADDR, 0x00); +#endif + + up_mdelay(10); + +} +/**************************************************************************** + * Name: static void getChInterruptStatus( void *arg) + * + * Description: + * read Interrupt register of ch438 + * + ****************************************************************************/ + +static int getCh438InterruptStatus(void ){ + uint8_t gChInterruptStatus=0; /*Interrupt register status*/ + uint8_t i=7; + gChInterruptStatus=ReadCH438Data(REG_SSR_ADDR); + rt_kprintf("gChInterruptStatus: %x",gChInterruptStatus); + if(gChInterruptStatus){ + for(i=0;i +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include "fsl_gpio.h" +#include "drv_gpio.h" +#include "MIMXRT1052.h" + + +/*self define*/ +/* output pin */ +#define CH438_D_GPIO GPIO1 +#define ZIGBEE_GPIO GPIO2 +#define CH438_CTL_GPIO GPIO3 + +#define CH438_INT_IRQ GPIO3_Combined_0_15_IRQn +#define CH438_INT_IRQ_HANDLER GPIO3_Combined_0_15_IRQHandler + +/* ch438 ctl pin */ +#define CH438_NWR 68 +#define CH438_NRD 69 +#define CH438_ALE 66 +#define CH438_INT 67 + +/* ch438 r/w pin*/ +#define CH438_D0 25 +#define CH438_D1 24 +#define CH438_D2 20 +#define CH438_D3 21 +#define CH438_D4 31 +#define CH438_D5 28 +#define CH438_D6 30 +#define CH438_D7 29 + + +#ifdef CONFIG_CH438_EXTUART0 +#define EXTU_UART_0 "dev0" +#endif + +#ifdef CONFIG_CH438_EXTUART1 +#define EXTU_UART_1 "dev1" +#endif + +#ifdef CONFIG_CH438_EXTUART2 +#define EXTU_UART_2 "dev2" +#endif + +#ifdef CONFIG_CH438_EXTUART3 +#define EXTU_UART_3 "dev3" +#endif + +#ifdef CONFIG_CH438_EXTUART4 +#define EXTU_UART_4 "dev4" +#endif + +#ifdef CONFIG_CH438_EXTUART5 +#define EXTU_UART_5 "dev5" +#endif + +#ifdef CONFIG_CH438_EXTUART6 +#define EXTU_UART_6 "dev6" +#endif + +#ifdef CONFIG_CH438_EXTUART7 +#define EXTU_UART_7 "dev7" +#endif + + +#define ZIGBEE_MODE_PIN (11U) + +#define CH438_D0_PIN (25U) +#define CH438_D1_PIN (24U) +#define CH438_D2_PIN (20U) +#define CH438_D3_PIN (21U) +#define CH438_D4_PIN (31U) +#define CH438_D5_PIN (28U) +#define CH438_D6_PIN (30U) +#define CH438_D7_PIN (29U) + +#define CH438_NWR_PIN (4U) +#define CH438_NRD_PIN (5U) +#define CH438_ALE_PIN (2U) +#define CH438_INT_PIN (3U) + +#define EXAMPLE_DELAY_COUNT 5000 +#define CH438PORTNUM 8 +#define CH438_BUFFSIZE 256 +#define CH438_INCREMENT MSEC2TICK(33) + +#define CONFIG_CH438_EXTUART0_BAUD 115200 +#define CONFIG_CH438_EXTUART1_BAUD 115200 +#define CONFIG_CH438_EXTUART2_BAUD 9600 +#define CONFIG_CH438_EXTUART3_BAUD 9600 +#define CONFIG_CH438_EXTUART4_BAUD 115200 +#define CONFIG_CH438_EXTUART5_BAUD 115200 +#define CONFIG_CH438_EXTUART6_BAUD 115200 +#define CONFIG_CH438_EXTUART7_BAUD 9600 +#define OK 0 +#define ERROR 1 + +/* chip definition */ +/* CH438serial port0 register address */ + +#define REG_RBR0_ADDR 0x00 /* serial port0receive buffer register address */ +#define REG_THR0_ADDR 0x00 /* serial port0send hold register address */ +#define REG_IER0_ADDR 0x01 /* serial port0interrupt enable register address */ +#define REG_IIR0_ADDR 0x02 /* serial port0interrupt identifies register address */ +#define REG_FCR0_ADDR 0x02 /* serial port0FIFO controls register address */ +#define REG_LCR0_ADDR 0x03 /* serial port0circuit control register address */ +#define REG_MCR0_ADDR 0x04 /* serial port0MODEM controls register address */ +#define REG_LSR0_ADDR 0x05 /* serial port0line status register address */ +#define REG_MSR0_ADDR 0x06 /* serial port0address of MODEM status register */ +#define REG_SCR0_ADDR 0x07 /* serial port0the user can define the register address */ +#define REG_DLL0_ADDR 0x00 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM0_ADDR 0x01 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port1 register address */ + +#define REG_RBR1_ADDR 0x10 /* serial port1receive buffer register address */ +#define REG_THR1_ADDR 0x10 /* serial port1send hold register address */ +#define REG_IER1_ADDR 0x11 /* serial port1interrupt enable register address */ +#define REG_IIR1_ADDR 0x12 /* serial port1interrupt identifies register address */ +#define REG_FCR1_ADDR 0x12 /* serial port1FIFO controls register address */ +#define REG_LCR1_ADDR 0x13 /* serial port1circuit control register address */ +#define REG_MCR1_ADDR 0x14 /* serial port1MODEM controls register address */ +#define REG_LSR1_ADDR 0x15 /* serial port1line status register address */ +#define REG_MSR1_ADDR 0x16 /* serial port1address of MODEM status register */ +#define REG_SCR1_ADDR 0x17 /* serial port1the user can define the register address */ +#define REG_DLL1_ADDR 0x10 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM1_ADDR 0x11 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port2 register address */ + +#define REG_RBR2_ADDR 0x20 /* serial port2receive buffer register address */ +#define REG_THR2_ADDR 0x20 /* serial port2send hold register address */ +#define REG_IER2_ADDR 0x21 /* serial port2interrupt enable register address */ +#define REG_IIR2_ADDR 0x22 /* serial port2interrupt identifies register address */ +#define REG_FCR2_ADDR 0x22 /* serial port2FIFO controls register address */ +#define REG_LCR2_ADDR 0x23 /* serial port2circuit control register address */ +#define REG_MCR2_ADDR 0x24 /* serial port2MODEM controls register address */ +#define REG_LSR2_ADDR 0x25 /* serial port2line status register address */ +#define REG_MSR2_ADDR 0x26 /* serial port2address of MODEM status register */ +#define REG_SCR2_ADDR 0x27 /* serial port2the user can define the register address */ +#define REG_DLL2_ADDR 0x20 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM2_ADDR 0x21 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port3 register address */ + +#define REG_RBR3_ADDR 0x30 /* serial port3receive buffer register address */ +#define REG_THR3_ADDR 0x30 /* serial port3send hold register address */ +#define REG_IER3_ADDR 0x31 /* serial port3interrupt enable register address */ +#define REG_IIR3_ADDR 0x32 /* serial port3interrupt identifies register address */ +#define REG_FCR3_ADDR 0x32 /* serial port3FIFO controls register address */ +#define REG_LCR3_ADDR 0x33 /* serial port3circuit control register address */ +#define REG_MCR3_ADDR 0x34 /* serial port3MODEM controls register address */ +#define REG_LSR3_ADDR 0x35 /* serial port3line status register address */ +#define REG_MSR3_ADDR 0x36 /* serial port3address of MODEM status register */ +#define REG_SCR3_ADDR 0x37 /* serial port3the user can define the register address */ +#define REG_DLL3_ADDR 0x30 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM3_ADDR 0x31 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port4 register address */ + +#define REG_RBR4_ADDR 0x08 /* serial port4receive buffer register address */ +#define REG_THR4_ADDR 0x08 /* serial port4send hold register address */ +#define REG_IER4_ADDR 0x09 /* serial port4interrupt enable register address */ +#define REG_IIR4_ADDR 0x0A /* serial port4interrupt identifies register address */ +#define REG_FCR4_ADDR 0x0A /* serial port4FIFO controls register address */ +#define REG_LCR4_ADDR 0x0B /* serial port4circuit control register address */ +#define REG_MCR4_ADDR 0x0C /* serial port4MODEM controls register address */ +#define REG_LSR4_ADDR 0x0D /* serial port4line status register address */ +#define REG_MSR4_ADDR 0x0E /* serial port4address of MODEM status register */ +#define REG_SCR4_ADDR 0x0F /* serial port4the user can define the register address */ +#define REG_DLL4_ADDR 0x08 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM4_ADDR 0x09 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port5 register address */ + +#define REG_RBR5_ADDR 0x18 /* serial port5receive buffer register address */ +#define REG_THR5_ADDR 0x18 /* serial port5send hold register address */ +#define REG_IER5_ADDR 0x19 /* serial port5interrupt enable register address */ +#define REG_IIR5_ADDR 0x1A /* serial port5interrupt identifies register address */ +#define REG_FCR5_ADDR 0x1A /* serial port5FIFO controls register address */ +#define REG_LCR5_ADDR 0x1B /* serial port5circuit control register address */ +#define REG_MCR5_ADDR 0x1C /* serial port5MODEM controls register address */ +#define REG_LSR5_ADDR 0x1D /* serial port5line status register address */ +#define REG_MSR5_ADDR 0x1E /* serial port5address of MODEM status register */ +#define REG_SCR5_ADDR 0x1F /* serial port5the user can define the register address */ +#define REG_DLL5_ADDR 0x18 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM5_ADDR 0x19 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port6 register address */ + +#define REG_RBR6_ADDR 0x28 /* serial port6receive buffer register address */ +#define REG_THR6_ADDR 0x28 /* serial port6send hold register address */ +#define REG_IER6_ADDR 0x29 /* serial port6interrupt enable register address */ +#define REG_IIR6_ADDR 0x2A /* serial port6interrupt identifies register address */ +#define REG_FCR6_ADDR 0x2A /* serial port6FIFO controls register address */ +#define REG_LCR6_ADDR 0x2B /* serial port6circuit control register address */ +#define REG_MCR6_ADDR 0x2C /* serial port6MODEM controls register address */ +#define REG_LSR6_ADDR 0x2D /* serial port6line status register address */ +#define REG_MSR6_ADDR 0x2E /* serial port6address of MODEM status register */ +#define REG_SCR6_ADDR 0x2F /* serial port6the user can define the register address */ +#define REG_DLL6_ADDR 0x28 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM6_ADDR 0x29 /* Baud rate divisor latch high 8-bit byte address */ + + +/* CH438serial port7 register address */ + +#define REG_RBR7_ADDR 0x38 /* serial port7receive buffer register address */ +#define REG_THR7_ADDR 0x38 /* serial port7send hold register address */ +#define REG_IER7_ADDR 0x39 /* serial port7interrupt enable register address */ +#define REG_IIR7_ADDR 0x3A /* serial port7interrupt identifies register address */ +#define REG_FCR7_ADDR 0x3A /* serial port7FIFO controls register address */ +#define REG_LCR7_ADDR 0x3B /* serial port7circuit control register address */ +#define REG_MCR7_ADDR 0x3C /* serial port7MODEM controls register address */ +#define REG_LSR7_ADDR 0x3D /* serial port7line status register address */ +#define REG_MSR7_ADDR 0x3E /* serial port7address of MODEM status register */ +#define REG_SCR7_ADDR 0x3F /* serial port7the user can define the register address */ +#define REG_DLL7_ADDR 0x38 /* Baud rate divisor latch low 8-bit byte address */ +#define REG_DLM7_ADDR 0x39 /* Baud rate divisor latch high 8-bit byte address */ + + +#define REG_SSR_ADDR 0x4F /* pecial status register address */ + + +/* IER register bit */ + +#define BIT_IER_RESET 0x80 /* The bit is 1 soft reset serial port */ +#define BIT_IER_LOWPOWER 0x40 /* The bit is 1 close serial port internal reference clock */ +#define BIT_IER_SLP 0x20 /* serial port0 is SLP, 1 close clock vibrator */ +#define BIT_IER1_CK2X 0x20 /* serial port1 is CK2X, 1 force the external clock signal after 2 times as internal */ +#define BIT_IER_IEMODEM 0x08 /* The bit is 1 allows MODEM input status to interrupt */ +#define BIT_IER_IELINES 0x04 /* The bit is 1 allow receiving line status to be interrupted */ +#define BIT_IER_IETHRE 0x02 /* The bit is 1 allows the send hold register to break in mid-air */ +#define BIT_IER_IERECV 0x01 /* The bit is 1 allows receiving data interrupts */ + +/* IIR register bit */ + +#define BIT_IIR_FIFOENS1 0x80 +#define BIT_IIR_FIFOENS0 0x40 /* The two is 1 said use FIFO */ + +/* Interrupt type: 0001 has no interrupt, 0110 receiving line status is interrupted, 0100 receiving data can be interrupted, +1100 received data timeout interrupt, 0010THR register air interrupt, 0000MODEM input change interrupt */ +#define BIT_IIR_IID3 0x08 +#define BIT_IIR_IID2 0x04 +#define BIT_IIR_IID1 0x02 +#define BIT_IIR_NOINT 0x01 + +/* FCR register bit */ + +/* Trigger point: 00 corresponds to 1 byte, 01 corresponds to 16 bytes, 10 corresponds to 64 bytes, 11 corresponds to 112 bytes */ +#define BIT_FCR_RECVTG1 0x80 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ +#define BIT_FCR_RECVTG0 0x40 /* Set the trigger point for FIFO interruption and automatic hardware flow control */ + +#define BIT_FCR_TFIFORST 0x04 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_RFIFORST 0x02 /* The bit is 1 empty the data sent in FIFO */ +#define BIT_FCR_FIFOEN 0x01 /* The bit is 1 use FIFO, 0 disable FIFO */ + +/* LCR register bit */ + +#define BIT_LCR_DLAB 0x80 /* To access DLL, DLM, 0 to access RBR/THR/IER */ +#define BIT_LCR_BREAKEN 0x40 /* 1 forces a BREAK line interval*/ + +/* Set the check format: when PAREN is 1, 00 odd check, 01 even check, 10 MARK (set 1), 11 blank (SPACE, clear 0) */ +#define BIT_LCR_PARMODE1 0x20 /* Sets the parity bit format */ +#define BIT_LCR_PARMODE0 0x10 /* Sets the parity bit format */ + +#define BIT_LCR_PAREN 0x08 /* A value of 1 allows you to generate and receive parity bits when sending */ +#define BIT_LCR_STOPBIT 0x04 /* If is 1, then two stop bits, is 0, a stop bit */ + +/* Set word length: 00 for 5 data bits, 01 for 6 data bits, 10 for 7 data bits and 11 for 8 data bits */ +#define BIT_LCR_WORDSZ1 0x02 /* Set the word length length */ +#define BIT_LCR_WORDSZ0 0x01 + +/* MCR register bit */ + +#define BIT_MCR_AFE 0x20 /* For 1 allows automatic flow control of CTS and RTS hardware */ +#define BIT_MCR_LOOP 0x10 /* Is the test mode of 1 enabling internal loop */ +#define BIT_MCR_OUT2 0x08 /* 1 Allows an interrupt request for the serial port output */ +#define BIT_MCR_OUT1 0x04 /* The MODEM control bit defined for the user */ +#define BIT_MCR_RTS 0x02 /* The bit is 1 RTS pin output effective */ +#define BIT_MCR_DTR 0x01 /* The bit is 1 DTR pin output effective */ + +/* LSR register bit */ + +#define BIT_LSR_RFIFOERR 0x80 /* 1 said There is at least one error in receiving FIFO */ +#define BIT_LSR_TEMT 0x40 /* 1 said THR and TSR are empty */ +#define BIT_LSR_THRE 0x20 /* 1 said THR is empty*/ +#define BIT_LSR_BREAKINT 0x10 /* The bit is 1 said the BREAK line interval was detected*/ +#define BIT_LSR_FRAMEERR 0x08 /* The bit is 1 said error reading data frame */ +#define BIT_LSR_PARERR 0x04 /* The bit is 1 said parity error */ +#define BIT_LSR_OVERR 0x02 /* 1 said receive FIFO buffer overflow */ +#define BIT_LSR_DATARDY 0x01 /* The bit is 1 said receive data received in FIFO */ + +/* MSR register bit */ + +#define BIT_MSR_DCD 0x80 /* The bit is 1 said DCD pin effective */ +#define BIT_MSR_RI 0x40 /* The bit is 1 said RI pin effective */ +#define BIT_MSR_DSR 0x20 /* The bit is 1 said DSR pin effective */ +#define BIT_MSR_CTS 0x10 /* The bit is 1 said CTS pin effective */ +#define BIT_MSR_DDCD 0x08 /* The bit is 1 said DCD pin The input state has changed */ +#define BIT_MSR_TERI 0x04 /* The bit is 1 said RI pin The input state has changed */ +#define BIT_MSR_DDSR 0x02 /* The bit is 1 said DSR pin The input state has changed */ +#define BIT_MSR_DCTS 0x01 /* The bit is 1 said CTS pin The input state has changed */ + +/* Interrupt status code */ + +#define INT_NOINT 0x01 /* There is no interruption */ +#define INT_THR_EMPTY 0x02 /* THR empty interruption */ +#define INT_RCV_OVERTIME 0x0C /* Receive timeout interrupt */ +#define INT_RCV_SUCCESS 0x04 /* Interrupts are available to receive data */ +#define INT_RCV_LINES 0x06 /* Receiving line status interrupted */ +#define INT_MODEM_CHANGE 0x00 /* MODEM input changes interrupt */ + +#define CH438_IIR_FIFOS_ENABLED 0xC0 /* use FIFO */ + +#define Fpclk 1843200 /* Define the internal clock frequency*/ + +/* For Interrupt */ +unsigned char CH438_CheckIIR(uint32_t iiraddr); /* Config serial port1interrupt identifies register function */ +void CH438_INTConfig(uint32_t ieraddr,uint32_t iiraddr,uint32_t mcraddr); /* Serial interrupt enable function */ +void Disable_Interrupt(void); /* Disable EXTI interrupt*/ +void Config_Interrupt(void); /* Config EXTI interrupt*/ + +/* For RT-Thread Config */ +static void Ch438Irq(void *parameter); +static rt_err_t rt_ch438_configure(struct rt_serial_device *serial, struct serial_configure *cfg); +static rt_err_t rt_ch438_control(struct rt_serial_device *serial, int cmd, void *arg); +static int rt_ch438_putc(struct rt_serial_device *serial, char c); +static int rt_ch438_getc(struct rt_serial_device *serial); +int rt_hw_ch438_init(void); + +int semaphoreChInit(void); + +/* Delay */ +void up_udelay(void); +void up_mdelay(uint32_t time); + +void udelay(unsigned long usecs); +/* CH438 Config */ +static void CH438SetOutput(void); +static void CH438SetInput(void); +static uint8_t ReadCH438Data(uint8_t addr); +static void WriteCH438Data(uint8_t addr, const uint8_t dat); +static void WriteCH438Block(uint8_t mAddr, uint8_t mLen, const uint8_t *mBuf); +static void Ch438UartSend(uint8_t ext_uart_no, const uint8_t *Data, uint16_t Num); +uint8_t CH438UARTRcv(uint8_t ext_uart_no, uint8_t *buf, size_t size); +static void ImxrtCH438Init(void); +static void CH438PortInit(uint8_t ext_uart_no, uint32_t baud_rate); +static int ImxrtCh438WriteData(uint8_t ext_uart_no, const uint8_t *write_buffer, size_t size); +static size_t ImxrtCh438ReadData(uint8_t ext_uart_no, size_t size); +static void Ch438InitDefault(void); + +static int getCh438InterruptStatus(void ); +/* CH438 Test Function */ +void CH438Test(void); +void HC08Test(void); +void ZigBeeTest(void); +void CH438Init(void); +#endif \ No newline at end of file From 252edf1add8daadd3085853683170c6ec194f674 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 10:55:24 +0800 Subject: [PATCH 10/21] =?UTF-8?q?xiuos\Ubiquitous\RT-Thread=5FFusion=5FXiU?= =?UTF-8?q?OS\aiit=5Fboard\xidatong-arm32\applications=EF=BC=9A=201.modify?= =?UTF-8?q?=20this=20file=20to=20avoid=20conflict=200f=20mux=20related=20t?= =?UTF-8?q?o=20lora=20mode=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../xidatong-arm32/applications/main.c | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/applications/main.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/applications/main.c index 4c54c8355..56e528716 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/applications/main.c +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/applications/main.c @@ -13,22 +13,21 @@ #include #include "drv_gpio.h" #include - +#include "fsl_gpio.h" +#include "board/hardware/ch438/ch438.h" /* defined the LED pin: GPIO1_IO9 */ #define LED0_PIN GET_PIN(1,9) - +extern int FrameworkInit(void); int main(void) -{ +{ /* set LED0 pin mode to output */ rt_pin_mode(LED0_PIN, PIN_MODE_OUTPUT); rt_kprintf("XIUOS xidatong build %s %s\n",__DATE__,__TIME__); - while (1) - { - rt_pin_write(LED0_PIN, PIN_HIGH); - rt_thread_mdelay(500); - rt_pin_write(LED0_PIN, PIN_LOW); - rt_thread_mdelay(500); - } + FrameworkInit(); + while (1) + { + rt_thread_mdelay(5000); + } } #ifdef BSP_USING_SDRAM From bf9b87a314bf5546a52c4a4dc9a063dab42f5657 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 10:57:55 +0800 Subject: [PATCH 11/21] xiuos\APP_Framework\Framework\connection\4g: (1)Modify the msh instructions in 4G Test function --- APP_Framework/Framework/connection/4g/adapter_4g.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/APP_Framework/Framework/connection/4g/adapter_4g.c b/APP_Framework/Framework/connection/4g/adapter_4g.c index a0a96cd62..765840810 100644 --- a/APP_Framework/Framework/connection/4g/adapter_4g.c +++ b/APP_Framework/Framework/connection/4g/adapter_4g.c @@ -110,7 +110,9 @@ int Adapter4GTest(void) return 0; } -// SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0)|SHELL_CMD_DISABLE_RETURN, Adapter4GTest, Adapter4GTest, show adapter 4G information); -#ifdef ADD_RTTHREAD_FETURES -MSH_CMD_EXPORT(Adapter4GTestRTThread,a 4G adpter sample); + +#ifdef ADD_RTTHREAD_FETURES +MSH_CMD_EXPORT(Adapter4GTest,a EC200T adpter sample); +#else +SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0)|SHELL_CMD_DISABLE_RETURN, Adapter4GTest, Adapter4GTest, show adapter 4G information); #endif \ No newline at end of file From fe21a461a5699fcb4b9d963110ce7f3cb481758d Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 11:04:55 +0800 Subject: [PATCH 12/21] xiuos\APP_Framework\Framework\connection\lora\e220: 1.Add Kconfig files and SConscript files related to e220 --- .../Framework/connection/lora/e220/Kconfig | 4 +- .../Framework/connection/lora/e220/e220.c | 153 +++++++++++++++++- 2 files changed, 152 insertions(+), 5 deletions(-) diff --git a/APP_Framework/Framework/connection/lora/e220/Kconfig b/APP_Framework/Framework/connection/lora/e220/Kconfig index 34e106f47..aec5daab6 100644 --- a/APP_Framework/Framework/connection/lora/e220/Kconfig +++ b/APP_Framework/Framework/connection/lora/e220/Kconfig @@ -39,11 +39,11 @@ if ADD_NUTTX_FETURES config ADAPTER_E220_M0_PATH string "E220 M0 pin device" - default "/dev/gpout0" + default "/dev/gpio0" config ADAPTER_E220_M1_PATH string "E220 M1 pin device" - default "/dev/gpout1" + default "/dev/gpio1" config ADAPTER_E220_DRIVER_EXTUART bool "Using extra uart to support lora" diff --git a/APP_Framework/Framework/connection/lora/e220/e220.c b/APP_Framework/Framework/connection/lora/e220/e220.c index 880ba9e48..ce616269d 100644 --- a/APP_Framework/Framework/connection/lora/e220/e220.c +++ b/APP_Framework/Framework/connection/lora/e220/e220.c @@ -21,7 +21,12 @@ #include #define E220_GATEWAY_ADDRESS 0xFFFF + +#ifdef ADD_RTTHREAD_FETURES +#define E220_CHANNEL 0x02 +#else #define E220_CHANNEL 0x05 +#endif #ifdef AS_LORA_GATEWAY_ROLE #define E220_ADDRESS E220_GATEWAY_ADDRESS @@ -31,7 +36,11 @@ #define E220_ADDRESS ADAPTER_LORA_NET_ROLE_ID #endif +#ifdef ADD_RTTHREAD_FETURES +#define E220_UART_BAUD_RATE 9600 +#else #define E220_UART_BAUD_RATE 115200 +#endif enum E220LoraMode { @@ -265,13 +274,16 @@ static int E220SetRegisterParam(struct Adapter *adapter, uint16 address, uint8 c buffer[10] = 0; //low-cipher ret = PrivWrite(adapter->fd, (void *)buffer, 11); + if(ret < 0){ printf("E220SetRegisterParam send failed %d!\n", ret); } - PrivRead(adapter->fd, buffer, 11); - E220LoraModeConfig(DATA_TRANSFER_MODE); + PrivRead(adapter->fd, buffer, 11); + E220LoraModeConfig(DATA_TRANSFER_MODE); + + PrivTaskDelay(1000); return 0; @@ -337,6 +349,61 @@ static int E220Open(struct Adapter *adapter) return 0; } #else +#ifdef ADD_RTTHREAD_FETURES +static int E220Open(struct Adapter *adapter) +{ + /*step1: open e220 uart port*/ + adapter->fd = PrivOpen(ADAPTER_E220_DRIVER, O_RDWR); + if (adapter->fd < 0) { + printf("E220Open get uart %s fd error\n", ADAPTER_E220_DRIVER); + return -1; + } + + struct SerialDataCfg cfg; + memset(&cfg, 0 ,sizeof(struct SerialDataCfg)); + + cfg.serial_baud_rate = BAUD_RATE_9600; + cfg.serial_data_bits = DATA_BITS_8; + cfg.serial_stop_bits = STOP_BITS_1; + cfg.serial_parity_mode = PARITY_NONE; + cfg.serial_bit_order = BIT_ORDER_LSB; + cfg.serial_invert_mode = NRZ_NORMAL; + cfg.serial_buffer_size = SERIAL_RB_BUFSZ; + + /*aiit board use ch438, so it needs more serial configuration*/ +#ifdef ADAPTER_E220_DRIVER_EXTUART + cfg.ext_uart_no = ADAPTER_E220_DRIVER_EXT_PORT; + cfg.port_configure = PORT_CFG_INIT; +#endif + +#ifdef AS_LORA_GATEWAY_ROLE + //serial receive timeout 10s + cfg.serial_timeout = 10000; +#endif + +#ifdef AS_LORA_CLIENT_ROLE + //serial receive wait forever + cfg.serial_timeout = -1; +#endif + + struct PrivIoctlCfg ioctl_cfg; + ioctl_cfg.ioctl_driver_type = SERIAL_TYPE; + ioctl_cfg.args = &cfg; + + PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg); + + + + cfg.serial_baud_rate = E220_UART_BAUD_RATE; + ioctl_cfg.args = &cfg; + + PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg); + + ADAPTER_DEBUG("E220Open done\n"); + + return 0; +} +#else static int E220Open(struct Adapter *adapter) { /*step1: open e220 uart port*/ @@ -391,6 +458,7 @@ static int E220Open(struct Adapter *adapter) return 0; } #endif +#endif /** * @description: Close E220 uart function @@ -472,7 +540,7 @@ static int E220Send(struct Adapter *adapter, const void *buf, size_t len) */ static int E220Recv(struct Adapter *adapter, void *buf, size_t len) { - int recv_len, recv_len_continue; + int recv_len=0, recv_len_continue=0; uint8 *recv_buf = PrivMalloc(len); @@ -558,7 +626,46 @@ static void LoraOpen(void) E220Open(adapter); } +MSH_CMD_EXPORT(LoraOpen,Lora open test sample); +#ifdef ADD_RTTHREAD_FETURES +static void LoraRead(void *parameter) +{ + int RevLen; + int i, cnt = 0; + + uint8 buffer[256]; + + memset(buffer, 0, 256); + + struct Adapter *adapter = AdapterDeviceFindByName(ADAPTER_LORA_NAME); + if (NULL == adapter) { + printf("LoraRead find lora adapter error\n"); + return; + } + + while (1) + { + printf("ready to read lora data\n"); + + RevLen = E220Recv(adapter, buffer, 6); + if (RevLen) { + printf("lora get data %u\n", RevLen); + for (i = 0; i < RevLen; i ++) { + printf("i %u data 0x%x\n", i, buffer[i]); + } + + memset(buffer, 0, 256); + + PrivTaskDelay(1000); + + cnt ++; + E220Send(adapter, &cnt, 1); + } + } +} +MSH_CMD_EXPORT(LoraRead,Lora read test sample); +#else static void LoraRead(void *parameter) { int RevLen; @@ -594,6 +701,8 @@ static void LoraRead(void *parameter) } } } +#endif + #ifdef ADD_XIZI_FETURES static void LoraTest(void) @@ -667,3 +776,41 @@ void E220LoraSend(int argc, char *argv[]) } } #endif + +#ifdef ADD_RTTHREAD_FETURES + +static void LoraReadStart(void) +{ + int ret; + + LoraOpen(); + + rt_thread_t tid= rt_thread_create("LoraReadStart", LoraRead, RT_NULL,2048,10,5); + if(tid!=RT_NULL){ + rt_thread_startup(tid); + }else{ + rt_kprintf("LoraReadStart task_lora_read failed \r\n"); + return; + } + +} +MSH_CMD_EXPORT(LoraReadStart,Lora read task start sample); +#define E22400T_M1_PIN (11U) +#define E22400T_M0_PIN (9U) +static void LoraSend(int argc, char *argv[]) +{ + int8_t cmd[10]={0xFF,0xFF,0x02,0xAA,0XBB,0xCC}; //sned AA BB CC to address 01 channel05 + LoraOpen(); + struct Adapter *adapter = AdapterDeviceFindByName(ADAPTER_LORA_NAME); + if (NULL == adapter) { + printf("LoraRead find lora adapter error\n"); + return; + } + rt_pin_mode (E22400T_M1_PIN, PIN_MODE_OUTPUT); + rt_pin_mode (E22400T_M0_PIN, PIN_MODE_OUTPUT); + rt_pin_write(E22400T_M1_PIN, PIN_LOW); + rt_pin_write(E22400T_M0_PIN, PIN_HIGH); + E220Send(adapter, cmd, 6); +} +MSH_CMD_EXPORT(LoraSend,Lora send sample); +#endif \ No newline at end of file From 83112073a94ff774295db5b82324ecad9dbee9ae Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 11:07:53 +0800 Subject: [PATCH 13/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\lora=EF=BC=9A=201.test=20function=20has=20been=20modified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APP_Framework/Framework/connection/lora/adapter_lora.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/APP_Framework/Framework/connection/lora/adapter_lora.c b/APP_Framework/Framework/connection/lora/adapter_lora.c index 0051bb487..cf330d37c 100644 --- a/APP_Framework/Framework/connection/lora/adapter_lora.c +++ b/APP_Framework/Framework/connection/lora/adapter_lora.c @@ -907,8 +907,8 @@ static pthread_t lora_client_data_task; int AdapterLoraTest(void) { - struct Adapter *adapter = AdapterDeviceFindByName(ADAPTER_LORA_NAME); - + struct Adapter *adapter = AdapterDeviceFindByName(ADAPTER_LORA_NAME); + AdapterDeviceOpen(adapter); //create lora gateway task @@ -962,6 +962,9 @@ int AdapterLoraTest(void) return 0; } +#ifdef ADD_RTTHREAD_FETURES +MSH_CMD_EXPORT(AdapterLoraTest,a Lora adpter sample); +#endif #ifdef ADD_XIZI_FETURES SHELL_EXPORT_CMD(SHELL_CMD_PERMISSION(0)|SHELL_CMD_TYPE(SHELL_TYPE_CMD_FUNC)|SHELL_CMD_PARAM_NUM(0)|SHELL_CMD_DISABLE_RETURN, AdapterLoraTest, AdapterLoraTest, show adapter lora information); #endif From c69397a2b7fcdf7a1987a9d75f9ba149e9dbadd8 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 11:12:44 +0800 Subject: [PATCH 14/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\lora=EF=BC=9A=201.test=20function=20has=20been=20modified?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiit_board/xidatong-arm32/board/board.c | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/board.c b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/board.c index 48db851df..68f13bd1e 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/board.c +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/board.c @@ -1091,7 +1091,15 @@ void rt_hw_board_init() void rt_hw_us_delay(rt_uint32_t usec) { - ; + rt_uint32_t start, now, delta, reload, us_tick; + start = SysTick->VAL; + reload = SysTick->LOAD; + us_tick = SystemCoreClock / 1000000UL; + do { + now = SysTick->VAL; + delta = start > now ? start - now : reload + start - now; + } while(delta < us_tick * usec); + } static int reboot(void) From 0526e73a6a3daa8f502be0bbd8c2b4e27cc451af Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 11:13:13 +0800 Subject: [PATCH 15/21] =?UTF-8?q?xiuos\Ubiquitous\RT-Thread=5FFusion=5FXiU?= =?UTF-8?q?OS\aiit=5Fboard\xidatong-arm32\board=EF=BC=9A=201.add=20kconfig?= =?UTF-8?q?=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aiit_board/xidatong-arm32/board/Kconfig | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/Kconfig b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/Kconfig index c06392967..267083666 100644 --- a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/Kconfig +++ b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/board/Kconfig @@ -229,7 +229,44 @@ menu "Onboard Peripheral Drivers" config BSP_USB1_HOST bool "Enable USB1 Host" default n + + config BSP_USING_RT_THREAD_HC08 + bool "Enable Bluetooth" + default n + menuconfig BSP_USING_CH438 + bool "Enable CH438" + if BSP_USING_CH438 + config CONFIG_CH438_EXTUART0 + bool "Enable CH438_EXTUART0" + default n + config CONFIG_CH438_EXTUART1 + bool "Enable CH438_EXTUART1" + default n + config CONFIG_CH438_EXTUART2 + bool "Enable CH438_EXTUART2" + default n + config CONFIG_CH438_EXTUART3 + bool "Enable CH438_EXTUART3" + default n + config CONFIG_CH438_EXTUART4 + bool "Enable CH438_EXTUART4" + default n + config CONFIG_CH438_EXTUART5 + bool "Enable CH438_EXTUART5" + default n + config CONFIG_CH438_EXTUART6 + bool "Enable CH438_EXTUART6" + default n + config CONFIG_CH438_EXTUART7 + bool "Enable CH438_EXTUART7" + default n + config CH438_INT_PORT + int "ch438 Interrupt Port deafult 3" + range 0 7 + default 3 + endif + menuconfig BSP_USING_ETH bool "Enable Ethernet" select RT_USING_NETDEV From 50b40bcf1fd6307569c3afbc17dcef50e247902a Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Tue, 9 Aug 2022 11:20:33 +0800 Subject: [PATCH 16/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\lora\e220=EF=BC=9A=20Modify=20this=20file=20again,and=20if=20?= =?UTF-8?q?you=20want=20to=20test=20the=20lora=20module,you=20can=20find?= =?UTF-8?q?=20test=20function=20in=20this=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- APP_Framework/Framework/connection/lora/e220/e220.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/APP_Framework/Framework/connection/lora/e220/e220.c b/APP_Framework/Framework/connection/lora/e220/e220.c index ce616269d..6b5ec4ece 100644 --- a/APP_Framework/Framework/connection/lora/e220/e220.c +++ b/APP_Framework/Framework/connection/lora/e220/e220.c @@ -21,7 +21,6 @@ #include #define E220_GATEWAY_ADDRESS 0xFFFF - #ifdef ADD_RTTHREAD_FETURES #define E220_CHANNEL 0x02 #else @@ -281,7 +280,7 @@ static int E220SetRegisterParam(struct Adapter *adapter, uint16 address, uint8 c PrivRead(adapter->fd, buffer, 11); - E220LoraModeConfig(DATA_TRANSFER_MODE); + E220LoraModeConfig(DATA_TRANSFER_MODE); PrivTaskDelay(1000); @@ -392,9 +391,7 @@ static int E220Open(struct Adapter *adapter) PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg); - - - cfg.serial_baud_rate = E220_UART_BAUD_RATE; + cfg.serial_baud_rate = E220_UART_BAUD_RATE; ioctl_cfg.args = &cfg; PrivIoctl(adapter->fd, OPE_INT, &ioctl_cfg); From 1293577e93b81032976ec117709713e438359102 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Mon, 15 Aug 2022 14:02:18 +0800 Subject: [PATCH 17/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\bluetooth=EF=BC=9A=201.modify=20the=20Kconfig=20file?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../connection/bluetooth/hc08/Kconfig | 27 ++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/APP_Framework/Framework/connection/bluetooth/hc08/Kconfig b/APP_Framework/Framework/connection/bluetooth/hc08/Kconfig index 6e1f3f0fe..f7c0e5ec8 100644 --- a/APP_Framework/Framework/connection/bluetooth/hc08/Kconfig +++ b/APP_Framework/Framework/connection/bluetooth/hc08/Kconfig @@ -55,9 +55,30 @@ if ADD_NUTTX_FETURES endif if ADD_RTTHREAD_FETURES - config ADAPTER_HC08_DRIVER - string "HC08 device uart driver path" - default "/dev/uart4" + config ADAPTER_HC08_RECV_BUFFER_SIZE + int "HC08 recv data buffer size" + default "128" + config ADAPTER_HC08_WORK_ROLE string "HC08 work role M(MASTER) or S(SLAVER)" + default "M" + + config ADAPTER_HC08_DRIVER_EXTUART + bool "Using extra uart to support bluetooth" + default n + + config ADAPTER_HC08_DRIVER + string "HC08 device uart driver path" + default "/dev/dev2" + depends on !ADAPTER_HC08_DRIVER_EXTUART + + if ADAPTER_HC08_DRIVER_EXTUART + config ADAPTER_HC08_DRIVER + string "HC08 device extra uart driver path" + default "/dev/dev2" + + config ADAPTER_HC08_DRIVER_EXT_PORT + int "if HC08 device using extuart, choose port" + default "2" + endif endif From b5783ca4c5d9df8c0d722ada949a5ed3473fc1d9 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Mon, 15 Aug 2022 14:04:39 +0800 Subject: [PATCH 18/21] xiuos\APP_Framework\Framework\connection\bluetooth: 1.add code like setting Tuuid and Suuid --- .../connection/bluetooth/hc08/hc08.c | 63 ++++++++++++++++--- 1 file changed, 55 insertions(+), 8 deletions(-) diff --git a/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c b/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c index f261d3ba5..bc9e60eab 100644 --- a/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c +++ b/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c @@ -189,6 +189,30 @@ static int Hc08AtConfigure(ATAgentType agent, enum Hc08AtCmd hc08_at_cmd, void * AtSetReplyCharNum(agent, 13); ATOrderSend(agent, REPLY_TIME_OUT, reply, cmd_str); reply_ok_flag = 0; + break; + case HC08_AT_CMD_GET_SUUID: + AtSetReplyCharNum(agent, 13); + ATOrderSend(agent, REPLY_TIME_OUT, reply, HC08_GET_SUUID_CMD); + reply_ok_flag = 0; + break; + case HC08_AT_CMD_SET_SUUID: + luuid = *(unsigned int *)param; + sprintf(cmd_str, HC08_SET_SUUID_CMD, luuid); + AtSetReplyCharNum(agent, 13); + ATOrderSend(agent, REPLY_TIME_OUT, reply, cmd_str); + reply_ok_flag = 0; + break; + case HC08_AT_CMD_GET_TUUID: + AtSetReplyCharNum(agent, 13); + ATOrderSend(agent, REPLY_TIME_OUT, reply, HC08_GET_TUUID_CMD); + reply_ok_flag = 0; + break; + case HC08_AT_CMD_SET_TUUID: + luuid = *(unsigned int *)param; + sprintf(cmd_str, HC08_SET_TUUID_CMD, luuid); + AtSetReplyCharNum(agent, 13); + ATOrderSend(agent, REPLY_TIME_OUT, reply, cmd_str); + reply_ok_flag = 0; break; default: printf("hc08 do not support no.%d cmd\n", hc08_at_cmd); @@ -309,7 +333,8 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) return 0; } -#else + +#else static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) { if (OPE_INT != cmd) { @@ -360,15 +385,19 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) } PrivTaskDelay(500); - + #ifdef ADD_RTTHREAD_FETURES //Step3 : show hc08 device info, hc08_get send "AT+RX" response device info - // char device_info[HC08_RESP_DEFAULT_SIZE * 2] = {0}; - // if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_DEVICE_INFO, NULL, device_info) < 0) { - // return -1; - // } - + char device_info[HC08_RESP_DEFAULT_SIZE * 2] = {0}; + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_DEVICE_INFO, NULL, device_info) < 0) { + return -1; + } + #endif //Step4 : set LUUID、SUUID、TUUID, slave and master need to have same uuid param - luuid = 1234; + #ifdef ADD_RTTHREAD_FETURES + luuid = 1233; + #else + luuid = 1234; + #endif if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_LUUID, &luuid, NULL) < 0) { return -1; } @@ -376,11 +405,29 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_LUUID, NULL, NULL) < 0) { return -1; } + #ifdef ADD_RTTHREAD_FETURES + uint32_t suuid=1233; + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_SUUID, &luuid, NULL) < 0) { + return -1; + } + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_SUUID, NULL, NULL) < 0) { + return -1; + } + uint32_t tuuid=1233; + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_TUUID, &tuuid, NULL) < 0) { + return -1; + } + + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_TUUID, NULL, NULL) < 0) { + return -1; + } + #endif ADAPTER_DEBUG("Hc08 ioctl done\n"); return 0; } + #endif static int Hc08SetAddr(struct Adapter *adapter, const char *ip, const char *gateway, const char *netmask) From 48199d551cca3a6cab41285c0db4d3e148aa7573 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Mon, 15 Aug 2022 14:05:40 +0800 Subject: [PATCH 19/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\bluetooth=EF=BC=9A=201.add=20rt-thread=20msh?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/connection/bluetooth/adapter_bluetooth.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/APP_Framework/Framework/connection/bluetooth/adapter_bluetooth.c b/APP_Framework/Framework/connection/bluetooth/adapter_bluetooth.c index 03d116376..2f72ba2e2 100644 --- a/APP_Framework/Framework/connection/bluetooth/adapter_bluetooth.c +++ b/APP_Framework/Framework/connection/bluetooth/adapter_bluetooth.c @@ -82,7 +82,7 @@ int AdapterBlueToothInit(void) int AdapterBlueToothTest(void) { const char *bluetooth_msg = "BT Adapter Test"; - char bluetooth_recv_msg[128]; + char bluetooth_recv_msg[128]={0}; int len; int baud_rate = BAUD_RATE_9600; @@ -92,7 +92,7 @@ int AdapterBlueToothTest(void) AdapterDeviceOpen(adapter); //if bluetooth master and slave have already match, no need to AdapterDeviceControl and AdapterDeviceConnect - AdapterDeviceControl(adapter, OPE_INT, &baud_rate); + AdapterDeviceControl(adapter, OPE_INT, &baud_rate); //AdapterDeviceConnect(adapter, adapter->net_role, NULL, NULL, 0); len = strlen(bluetooth_msg); @@ -100,6 +100,7 @@ int AdapterBlueToothTest(void) while (1) { AdapterDeviceRecv(adapter, bluetooth_recv_msg, 8); printf("bluetooth_recv_msg %s\n", bluetooth_recv_msg); + AdapterDeviceSend(adapter, bluetooth_msg, len); printf("send %s after recv\n", bluetooth_msg); PrivTaskDelay(1000); From 8ccf8875ee489d721a88a3a4d89e23b8309dd128 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Mon, 15 Aug 2022 14:06:59 +0800 Subject: [PATCH 20/21] =?UTF-8?q?xiuos\Ubiquitous\RT-Thread=5FFusion=5FXiU?= =?UTF-8?q?OS\aiit=5Fboard\xidatong-arm32\test=5Fphoto\Bluetooth=EF=BC=9A?= =?UTF-8?q?=201.add=20test=20photo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../test_photo/Bluetooth/从机蓝牙初始化成功.PNG | Bin 0 -> 22196 bytes .../Bluetooth/从机蓝牙发送数据成功.PNG | Bin 0 -> 13743 bytes .../Bluetooth/从机蓝牙成功接收数据.PNG | Bin 0 -> 20162 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙初始化成功.PNG create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙发送数据成功.PNG create mode 100644 Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙成功接收数据.PNG diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙初始化成功.PNG b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙初始化成功.PNG new file mode 100644 index 0000000000000000000000000000000000000000..e7adbfa06c96bae11c1b88ab59282442f4820106 GIT binary patch literal 22196 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 F{|{P1uVDZH literal 0 HcmV?d00001 diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙发送数据成功.PNG b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙发送数据成功.PNG new file mode 100644 index 0000000000000000000000000000000000000000..976075d98533f8f05956e6ea9d2daac674c3f93d GIT binary patch literal 13743 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 CIXGni literal 0 HcmV?d00001 diff --git a/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙成功接收数据.PNG b/Ubiquitous/RT-Thread_Fusion_XiUOS/aiit_board/xidatong-arm32/test_photo/Bluetooth/从机蓝牙成功接收数据.PNG new file mode 100644 index 0000000000000000000000000000000000000000..0f2d0dd94e84012d756f6ab58ec0cb08990bdaff GIT binary patch literal 20162 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 literal 0 HcmV?d00001 From 125a1d1c613169f2453b865e330560956d15fbc2 Mon Sep 17 00:00:00 2001 From: zhujiarui666 Date: Mon, 15 Aug 2022 18:02:59 +0800 Subject: [PATCH 21/21] =?UTF-8?q?xiuos\APP=5FFramework\Framework\connectio?= =?UTF-8?q?n\bluetooth\hc08=EF=BC=9A=201.modify=20uuid?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Framework/connection/bluetooth/hc08/hc08.c | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c b/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c index bc9e60eab..d8cb9a4d5 100644 --- a/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c +++ b/APP_Framework/Framework/connection/bluetooth/hc08/hc08.c @@ -393,12 +393,8 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) } #endif //Step4 : set LUUID、SUUID、TUUID, slave and master need to have same uuid param - #ifdef ADD_RTTHREAD_FETURES - luuid = 1233; - #else - luuid = 1234; - #endif - if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_LUUID, &luuid, NULL) < 0) { + luuid = 1234; + if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_LUUID, &luuid, NULL) < 0) { return -1; } @@ -406,7 +402,7 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) return -1; } #ifdef ADD_RTTHREAD_FETURES - uint32_t suuid=1233; + uint32_t suuid=1234; if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_SUUID, &luuid, NULL) < 0) { return -1; } @@ -414,7 +410,7 @@ static int Hc08Ioctl(struct Adapter *adapter, int cmd, void *args) if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_GET_SUUID, NULL, NULL) < 0) { return -1; } - uint32_t tuuid=1233; + uint32_t tuuid=1234; if (Hc08AtConfigure(adapter->agent, HC08_AT_CMD_SET_TUUID, &tuuid, NULL) < 0) { return -1; }