Compare commits
15 Commits
537792e298
...
e547a83938
Author | SHA1 | Date |
---|---|---|
|
e547a83938 | |
|
70b026228a | |
|
c5f46accc3 | |
|
2ac6d894b8 | |
|
070abcfeee | |
|
45e7b6d90c | |
|
5bab7fed51 | |
|
6fdfa76a14 | |
|
a4d65c367d | |
|
90782fda82 | |
|
fa3e6ec92f | |
|
16f8b53a76 | |
|
8abe565836 | |
|
acfd967106 | |
|
3a7ed5922f |
|
@ -17,8 +17,6 @@ on:
|
|||
pull_request:
|
||||
# The branches below must be a subset of the branches above
|
||||
branches: [ master, develop ]
|
||||
schedule:
|
||||
- cron: '16 17 * * 2'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
|
@ -34,13 +32,13 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v1
|
||||
uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
config-file: ./.github/workflows/codeql/codeql-config.yml
|
||||
|
@ -64,16 +62,13 @@ jobs:
|
|||
- if: matrix.language == 'cpp' || matrix.language == 'c'
|
||||
name: Build
|
||||
run: |
|
||||
ls
|
||||
sudo apt update
|
||||
sudo apt install -y gcc git wget make libncurses-dev flex bison python python-setuptools python-serial ninja-build
|
||||
wget https://dl.espressif.com/dl/xtensa-lx106-elf-linux64-1.22.0-100-ge567ec7-5.2.0.tar.gz
|
||||
tar -xzf ./xtensa-lx106-elf-linux64-1.22.0-100-ge567ec7-5.2.0.tar.gz
|
||||
python -m pip install --user -r ./ESP8266_RTOS_SDK/requirements.txt
|
||||
export IDF_PATH=$PWD/ESP8266_RTOS_SDK
|
||||
export PATH="$PATH:$PWD/xtensa-lx106-elf/bin"
|
||||
python ./idf.py fullclean
|
||||
python ./idf.py build
|
||||
sudo apt-get install git wget flex bison gperf python3 python3-pip python3-venv cmake ninja-build ccache libffi-dev libssl-dev dfu-util libusb-1.0-0
|
||||
wget https://github.com/espressif/esp-idf/releases/download/v5.2.1/esp-idf-v5.2.1.zip
|
||||
unzip esp-idf-v5.2.1.zip
|
||||
./esp-idf-v5.2.1/install.sh esp32c3
|
||||
. ./esp-idf-v5.2.1/export.sh
|
||||
idf.py set-target esp32c3
|
||||
idf.py build
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v1
|
||||
uses: github/codeql-action/analyze@v3
|
||||
|
|
|
@ -7,7 +7,7 @@ on:
|
|||
- 'circuit'
|
||||
|
||||
|
||||
# env:
|
||||
#env:
|
||||
# UPLOAD_USER_FIRMWARE: false
|
||||
|
||||
|
||||
|
@ -17,7 +17,7 @@ jobs:
|
|||
continue-on-error: false
|
||||
strategy:
|
||||
matrix:
|
||||
target-hardware: [esp8266, esp32, esp32c3]
|
||||
target-hardware: [esp32c3]
|
||||
|
||||
|
||||
steps:
|
||||
|
@ -25,32 +25,6 @@ jobs:
|
|||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: before_install
|
||||
if: matrix.target-hardware == 'esp8266'
|
||||
run: |
|
||||
sudo apt update
|
||||
sudo apt install -y gcc git wget make libncurses-dev flex bison python python-setuptools python3-serial ninja-build
|
||||
wget https://dl.espressif.com/dl/xtensa-lx106-elf-linux64-1.22.0-100-ge567ec7-5.2.0.tar.gz
|
||||
curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
|
||||
python get-pip.py
|
||||
|
||||
- name: install
|
||||
if: matrix.target-hardware == 'esp8266'
|
||||
run: |
|
||||
tar -xzf ./xtensa-lx106-elf-linux64-1.22.0-100-ge567ec7-5.2.0.tar.gz
|
||||
python -m pip install --user -r ./ESP8266_RTOS_SDK/requirements.txt
|
||||
|
||||
- name: script
|
||||
id: script
|
||||
if: matrix.target-hardware == 'esp8266'
|
||||
run: |
|
||||
export IDF_PATH=$PWD/ESP8266_RTOS_SDK
|
||||
export PATH="$PATH:$PWD/xtensa-lx106-elf/bin"
|
||||
echo "FIRMWARE=$PWD/build" >> $GITHUB_ENV
|
||||
python ./idf.py fullclean
|
||||
python ./idf.py build
|
||||
echo "::set-output name=status::success"
|
||||
|
||||
# - name: Generate release tag
|
||||
# id: tag
|
||||
# if: env.UPLOAD_USER_FIRMWARE == 'true' && steps.script.outputs.status == 'success' && !cancelled()
|
||||
|
@ -63,34 +37,34 @@ jobs:
|
|||
if: matrix.target-hardware != 'esp8266'
|
||||
uses: espressif/esp-idf-ci-action@v1
|
||||
with:
|
||||
esp_idf_version: v4.4.2
|
||||
esp_idf_version: v5.2.1
|
||||
target: ${{ matrix.target-hardware }}
|
||||
path: './'
|
||||
|
||||
# - name: Merge bin files (esp32)
|
||||
# if: matrix.target-hardware == 'esp32'
|
||||
# run: |
|
||||
# git clone https://github.com/espressif/esptool.git
|
||||
# git -C ./esptool/ checkout tags/v4.7.0 -b merge_wirless_bin
|
||||
# sudo python3 ./esptool/esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_esp_dap_full.bin 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/wireless_tools_esp32.bin
|
||||
# sudo mv build/wireless_tools_esp32.bin build/wireless_esp_dap_app.bin
|
||||
|
||||
- name: Merge bin files (esp8266)
|
||||
if: matrix.target-hardware == 'esp8266'
|
||||
run: |
|
||||
git clone https://github.com/espressif/esptool.git
|
||||
git -C ./esptool/ checkout tags/v4.6.2 -b merge_wirless_bin
|
||||
sudo python3 ./esptool/esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_esp_dap_full.bin 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/wireless_esp_dap.bin
|
||||
sudo mv build/wireless_esp_dap.bin build/wireless_esp_dap_app.bin
|
||||
# - name: Merge bin files (esp32c3)
|
||||
# if: matrix.target-hardware == 'esp32c3'
|
||||
# run: |
|
||||
# git clone https://github.com/espressif/esptool.git
|
||||
# git -C ./esptool/ checkout tags/v4.7.0 -b merge_wirless_bin
|
||||
# sudo python3 ./esptool/esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_esp_dap_full.bin 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/wireless_tools_esp32.bin
|
||||
# sudo mv build/wireless_tools_esp32.bin build/wireless_esp_dap_app.bin
|
||||
|
||||
- name: Merge bin files (esp32)
|
||||
if: matrix.target-hardware == 'esp32'
|
||||
run: |
|
||||
git clone https://github.com/espressif/esptool.git
|
||||
git -C ./esptool/ checkout tags/v4.6.2 -b merge_wirless_bin
|
||||
sudo python3 ./esptool/esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_esp_dap_full.bin 0x1000 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/wireless_esp_dap.bin
|
||||
sudo mv build/wireless_esp_dap.bin build/wireless_esp_dap_app.bin
|
||||
|
||||
- name: Merge bin files (esp32c3)
|
||||
- name: merge bin files (esp32c3)
|
||||
if: matrix.target-hardware == 'esp32c3'
|
||||
run: |
|
||||
git clone https://github.com/espressif/esptool.git
|
||||
git -C ./esptool/ checkout tags/v4.6.2 -b merge_wirless_bin
|
||||
sudo python3 ./esptool/esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_esp_dap_full.bin 0x0 build/bootloader/bootloader.bin 0x8000 build/partition_table/partition-table.bin 0x10000 build/wireless_esp_dap.bin
|
||||
sudo mv build/wireless_esp_dap.bin build/wireless_esp_dap_app.bin
|
||||
uses: espressif/esp-idf-ci-action@v1
|
||||
with:
|
||||
esp_idf_version: v5.2.1
|
||||
target: ${{ matrix.target-hardware }}
|
||||
path: './'
|
||||
command: esptool.py --chip ${{ matrix.target-hardware }} merge_bin -o build/wireless_tools_esp32_full.bin 0x0 build/bootloader/bootloader.bin 0xF000 build/partition_table/partition-table.bin 0x20000 build/wireless_tools_esp32.bin
|
||||
|
||||
|
||||
- name: Upload firmware
|
||||
|
@ -98,7 +72,7 @@ jobs:
|
|||
with:
|
||||
name: firmware_${{ matrix.target-hardware }}.zip
|
||||
path: |
|
||||
build/wireless_esp_dap_full.bin
|
||||
build/wireless_esp_dap_app.bin
|
||||
build/wireless_tools_esp32_full.bin
|
||||
build/wireless_tools_esp32.bin
|
||||
build/bootloader/bootloader.bin
|
||||
build/partition_table/partition-table.bin
|
||||
|
|
|
@ -3,8 +3,10 @@ build/
|
|||
tmp/
|
||||
.history/
|
||||
sdkconfig.old
|
||||
sdkconfig
|
||||
.idea/
|
||||
dependencies.lock
|
||||
package-lock.json
|
||||
**/priv_note.md
|
||||
_priv_tools/
|
||||
project_components/wifi_manager/wifi_configuration.h
|
||||
|
|
177
README.md
177
README.md
|
@ -1,7 +1,3 @@
|
|||
<p align="center"><img src="https://user-images.githubusercontent.com/17078589/120061980-49274280-c092-11eb-9916-4965f6c48388.png"/></p>
|
||||
|
||||

|
||||
|
||||
[中文](README_CN.md)
|
||||
|
||||
## Introduce
|
||||
|
@ -21,7 +17,6 @@ For Keil users, we now also support [elaphureLink](https://github.com/windowsair
|
|||
## Feature
|
||||
|
||||
1. SoC Compatibility
|
||||
- [x] ESP32
|
||||
- [x] ESP32C3
|
||||
|
||||
2. Debug Communication Mode
|
||||
|
@ -32,8 +27,6 @@ For Keil users, we now also support [elaphureLink](https://github.com/windowsair
|
|||
- [x] USB-HID
|
||||
- [x] WCID & WinUSB (Default)
|
||||
|
||||
4. Debug Trace (Uart)
|
||||
- [x] Uart TCP Bridge
|
||||
|
||||
5. More..
|
||||
- [x] SWD protocol based on SPI acceleration (Up to 40MHz)
|
||||
|
@ -46,13 +39,6 @@ For Keil users, we now also support [elaphureLink](https://github.com/windowsair
|
|||
|
||||
### WIFI
|
||||
|
||||
The default connected WIFI SSID is `DAP` or `OTA` , password `12345678`
|
||||
|
||||
Support for specifying multiple possible WAP. It can be added here: [wifi_configuration.h](project_components/wifi_manager/wifi_configuration.h)
|
||||
|
||||
You can also specify your IP in the above file (We recommend using the static address binding feature of the router).
|
||||
|
||||

|
||||
|
||||
There is built-in ipv4 only mDNS server. You can access the device using `dap.local`.
|
||||
|
||||
|
@ -61,46 +47,6 @@ There is built-in ipv4 only mDNS server. You can access the device using `dap.lo
|
|||
|
||||
### Debugger
|
||||
|
||||
<details>
|
||||
<summary>ESP32</summary>
|
||||
|
||||
| SWD | |
|
||||
|----------------|--------|
|
||||
| SWCLK | GPIO14 |
|
||||
| SWDIO | GPIO13 |
|
||||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
|
||||
--------------
|
||||
|
||||
|
||||
| JTAG | |
|
||||
|--------------------|---------|
|
||||
| TCK | GPIO14 |
|
||||
| TMS | GPIO13 |
|
||||
| TDI | GPIO18 |
|
||||
| TDO | GPIO19 |
|
||||
| nTRST \(optional\) | GPIO25 |
|
||||
| nRESET | GPIO26 |
|
||||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
--------------
|
||||
|
||||
| Other | |
|
||||
|--------------------|---------------|
|
||||
| LED\_WIFI\_STATUS | GPIO27 |
|
||||
| Tx | GPIO23 |
|
||||
| Rx | GPIO22 |
|
||||
|
||||
|
||||
> Rx and Tx is used for uart bridge, not enabled by default.
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ESP32C3</summary>
|
||||
|
||||
|
@ -126,14 +72,6 @@ There is built-in ipv4 only mDNS server. You can access the device using `dap.lo
|
|||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
--------------
|
||||
|
||||
| Other | |
|
||||
|--------------------|---------------|
|
||||
| LED\_WIFI\_STATUS | GPIO10 |
|
||||
| Tx | GPIO19 |
|
||||
| Rx | GPIO18 |
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
@ -145,19 +83,15 @@ There is built-in ipv4 only mDNS server. You can access the device using `dap.lo
|
|||
|
||||
You can build locally or use Github Action to build online and then download firmware to flash.
|
||||
|
||||
### Build with Github Action Online
|
||||
|
||||
See: [Build with Github Action](https://github.com/windowsair/wireless-esp8266-dap/wiki/Build-with-Github-Action)
|
||||
|
||||
|
||||
### General build and Flash
|
||||
|
||||
<details>
|
||||
<summary>ESP32/ESP32C3</summary>
|
||||
<summary>ESP32C3</summary>
|
||||
|
||||
1. Get esp-idf
|
||||
|
||||
For now, please use esp-idf v4.4.2 : https://github.com/espressif/esp-idf/releases/tag/v4.4.2
|
||||
For now, please use esp-idf v5.2.1 : https://github.com/espressif/esp-idf/releases/tag/v5.2.1
|
||||
|
||||
2. Build & Flash
|
||||
|
||||
|
@ -168,7 +102,7 @@ The following example shows a possible way to build:
|
|||
|
||||
```bash
|
||||
# Set build target
|
||||
idf.py set-target esp32
|
||||
idf.py set-target esp32c3
|
||||
# Build
|
||||
idf.py build
|
||||
# Flash
|
||||
|
@ -266,109 +200,6 @@ Note that if you want to use a 40MHz SPI acceleration, you need to specify the s
|
|||
|
||||
> Keil's timing handling is somewhat different from OpenOCD's. For example, OpenOCD lacks the SWD line reset sequence before reading the `IDCODE` registers.
|
||||
|
||||
|
||||
### System OTA
|
||||
|
||||
When this project is updated, you can update the firmware over the air.
|
||||
|
||||
Visit the following website for OTA operations: [online OTA](http://corsacota.surge.sh/?address=dap.local:3241)
|
||||
|
||||
|
||||
For most devices, you don't need to care about flash size. However, improper setting of the flash size may cause the OTA to fail. In this case, please change the flash size with `idf.py menuconfig`, or modify `sdkconfig`:
|
||||
|
||||
```
|
||||
# Choose a flash size.
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_1MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
|
||||
# Then set a flash size
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
||||
```
|
||||
|
||||
If flash size is 2MB, the sdkconfig file might look like this:
|
||||
|
||||
```
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
||||
```
|
||||
|
||||
|
||||
For devices with 1MB flash size such as ESP8285, the following changes must be made:
|
||||
|
||||
```
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_two_ota.1MB.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_1MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="1MB"
|
||||
CONFIG_ESP8266_BOOT_COPY_APP=y
|
||||
```
|
||||
|
||||
The flash size of the board can be checked with the esptool.py tool:
|
||||
|
||||
```bash
|
||||
esptool.py -p (PORT) flash_id
|
||||
```
|
||||
|
||||
### Uart TCP Bridge
|
||||
|
||||
This feature provides a bridge between TCP and Uart:
|
||||
```
|
||||
Send data -> TCP -> Uart TX -> external devices
|
||||
|
||||
Recv data <- TCP <- Uart Rx <- external devices
|
||||
```
|
||||
|
||||

|
||||
|
||||
When the TCP connection is established, bridge will try to resolve the text sent for the first packet. When the text is a valid baud rate, bridge will switch to it.
|
||||
For example, sending the ASCII text `115200` will switch the baud rate to 115200.
|
||||
|
||||
|
||||
For performance reasons, this feature is not enabled by default. You can modify [wifi_configuration.h](main/wifi_configuration.h) to turn it on.
|
||||
|
||||
|
||||
----
|
||||
|
||||
## Develop
|
||||
|
||||
Check other branches to know the latest development progress.
|
||||
|
||||
Any kind of contribute is welcome, including but not limited to new features, ideas about circuits, documentation.
|
||||
|
||||
You can also ask questions to make this project better.
|
||||
|
||||
- [New issues](https://github.com/windowsair/wireless-esp8266-dap/issues)
|
||||
- [New pull](https://github.com/windowsair/wireless-esp8266-dap/pulls)
|
||||
|
||||
|
||||
### Issue
|
||||
|
||||
2020.12.1
|
||||
|
||||
TCP transmission speed needs to be further improved.
|
||||
|
||||
2020.11.11
|
||||
|
||||
Winusb is now available, but it is very slow.
|
||||
|
||||
|
||||
2020.2.4
|
||||
|
||||
Due to the limitation of USB-HID (I'm not sure if this is a problem with USBIP or Windows), now each URB packet can only reach 255 bytes (About 1MBps bandwidth), which has not reached the upper limit of ESP8266 transmission bandwidth.
|
||||
|
||||
I now have an idea to construct a Man-in-the-middle between the two to forward traffic, thereby increasing the bandwidth of each transmission.
|
||||
|
||||
2020.1.31
|
||||
|
||||
At present, the adaptation to WCID, WinUSB, etc. has all been completed. However, when transmitting data on the endpoint, we received an error message from USBIP. This is most likely a problem with the USBIP project itself.
|
||||
|
||||
Due to the completeness of the USBIP protocol document, we have not yet understood its role in the Bulk transmission process, which may also lead to errors in subsequent processes.
|
||||
|
||||
We will continue to try to make it work on USB HID. Once the USBIP problem is solved, we will immediately transfer it to work on WinUSB
|
||||
|
||||
|
||||
------
|
||||
|
||||
## Credit
|
||||
|
@ -376,6 +207,7 @@ We will continue to try to make it work on USB HID. Once the USBIP problem is so
|
|||
|
||||
Credits to the following project, people and organizations:
|
||||
|
||||
> - https://www.github.com/windowsair/wireless-esp8266-dap origin of this project
|
||||
> - https://github.com/thevoidnn/esp8266-wifi-cmsis-dap for adapter firmware based on CMSIS-DAP v1.0
|
||||
> - https://github.com/ARM-software/CMSIS_5 for CMSIS
|
||||
> - https://github.com/cezanne/usbip-win for usbip windows
|
||||
|
@ -390,4 +222,3 @@ Credits to the following project, people and organizations:
|
|||
## License
|
||||
|
||||
[Apache2.0 LICENSE](LICENSE)
|
||||
|
||||
|
|
174
README_CN.md
174
README_CN.md
|
@ -1,10 +1,3 @@
|
|||
<p align="center"><b>请注意:不同语言版本的翻译可能落后于项目的原始文档。请以原始文档为准。</b></p>
|
||||
|
||||
<p align="center"><img src="https://user-images.githubusercontent.com/17078589/120061980-49274280-c092-11eb-9916-4965f6c48388.png"/></p>
|
||||
|
||||

|
||||
|
||||
|
||||
## 简介
|
||||
|
||||
只需要**一枚ESP芯片**即可开始无线调试!通过USBIP协议栈和CMSIS-DAP协议栈实现。
|
||||
|
@ -20,7 +13,6 @@
|
|||
## 特性
|
||||
|
||||
1. 支持的ESP芯片
|
||||
- [x] ESP32
|
||||
- [x] ESP32C3
|
||||
|
||||
2. 支持的调试接口:
|
||||
|
@ -42,63 +34,15 @@
|
|||
|
||||
### WIFI连接
|
||||
|
||||
固件默认的WIFI SSID是`DAP`或者`OTA`,密码是`12345678`。
|
||||
|
||||
你可以在[wifi_configuration.h](main/wifi_configuration.h)文件中添加多个无线接入点。
|
||||
|
||||
你还可以在上面的配置文件中修改IP地址(但是我们更推荐你通过在路由器上绑定静态IP地址)。
|
||||
|
||||

|
||||
|
||||
固件中已经内置了一个mDNS服务。你可以通过`dap.local`的地址访问到设备。
|
||||
|
||||
|
||||

|
||||
|
||||
|
||||
### 调试接口连接
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ESP32</summary>
|
||||
|
||||
| SWD | |
|
||||
|----------------|--------|
|
||||
| SWCLK | GPIO14 |
|
||||
| SWDIO | GPIO13 |
|
||||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
|
||||
--------------
|
||||
|
||||
|
||||
| JTAG | |
|
||||
|--------------------|---------|
|
||||
| TCK | GPIO14 |
|
||||
| TMS | GPIO13 |
|
||||
| TDI | GPIO18 |
|
||||
| TDO | GPIO19 |
|
||||
| nTRST \(optional\) | GPIO25 |
|
||||
| nRESET | GPIO26 |
|
||||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
--------------
|
||||
|
||||
| Other | |
|
||||
|--------------------|---------------|
|
||||
| LED\_WIFI\_STATUS | GPIO27 |
|
||||
| Tx | GPIO23 |
|
||||
| Rx | GPIO22 |
|
||||
|
||||
|
||||
> Rx和Tx用于TCP转发的串口,默认不开启该功能。
|
||||
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ESP32C3</summary>
|
||||
|
||||
|
@ -124,16 +68,6 @@
|
|||
| TVCC | 3V3 |
|
||||
| GND | GND |
|
||||
|
||||
--------------
|
||||
|
||||
| Other | |
|
||||
|--------------------|---------------|
|
||||
| LED\_WIFI\_STATUS | GPIO10 |
|
||||
| Tx | GPIO19 |
|
||||
| Rx | GPIO18 |
|
||||
|
||||
|
||||
> Rx和Tx用于TCP转发的串口,默认不开启该功能。
|
||||
|
||||
|
||||
</details>
|
||||
|
@ -145,44 +79,15 @@
|
|||
|
||||
你可以在本地构建或使用Github Action在线构建固件,然后下载固件进行烧写。
|
||||
|
||||
### 使用Github Action在线构建固件
|
||||
|
||||
详见:[Build with Github Action](https://github.com/windowsair/wireless-esp8266-dap/wiki/Build-with-Github-Action)
|
||||
|
||||
### 在本地构建并烧写
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ESP8266</summary>
|
||||
|
||||
1. 获取ESP8266 SDK
|
||||
|
||||
项目中已经随附了一个SDK。请不要使用其他版本的SDK。
|
||||
|
||||
2. 编译和烧写
|
||||
|
||||
使用ESP-IDF编译系统进行构建。
|
||||
更多的信息,请见:[Build System](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html "Build System")
|
||||
|
||||
|
||||
下面例子展示了在Windows上完成这些任务的一种可行方法:
|
||||
|
||||
```bash
|
||||
# 编译
|
||||
python ./idf.py build
|
||||
# 烧写
|
||||
python ./idf.py -p /dev/ttyS5 flash
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
<details>
|
||||
<summary>ESP32/ESP32C3</summary>
|
||||
<summary>ESP32C3</summary>
|
||||
|
||||
1. 获取esp-idf
|
||||
|
||||
目前,请考虑使用esp-idf v4.4.2: https://github.com/espressif/esp-idf/releases/tag/v4.4.2
|
||||
目前,请考虑使用esp-idf v5.2.1: https://github.com/espressif/esp-idf/releases/tag/v5.2.1
|
||||
|
||||
2. 编译和烧写
|
||||
|
||||
|
@ -193,6 +98,7 @@ python ./idf.py -p /dev/ttyS5 flash
|
|||
下面例子展示了在Windows上完成这些任务的一种可行方法:
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32c3
|
||||
# 编译
|
||||
idf.py build
|
||||
# 烧写
|
||||
|
@ -209,7 +115,6 @@ idf.py -p /dev/ttyS5 flash
|
|||
|
||||
|
||||
|
||||
|
||||
## 使用
|
||||
|
||||
1. 获取USBIP项目
|
||||
|
@ -256,13 +161,10 @@ idf.py -p /dev/ttyS5 flash
|
|||
- Logitech Arx Control
|
||||
- ...
|
||||
|
||||
对于ESP8266, 这无异于UDP洪水攻击...😰
|
||||
|
||||
|
||||
周围的射频环境同样会造成影响,此外距离、网卡性能等也可能是需要考虑的。
|
||||
|
||||
|
||||
|
||||
## 文档
|
||||
|
||||
### 速度策略
|
||||
|
@ -290,82 +192,16 @@ idf.py -p /dev/ttyS5 flash
|
|||
|
||||
> Keil的操作时序与OpenOCD的有些不同。例如,OpenOCD在读取 "IDCODE "寄存器之前缺少SWD线复位序列。
|
||||
|
||||
### 系统 OTA
|
||||
|
||||
当这个项目被更新时,你可以通过无线方式更新固件。
|
||||
|
||||
请访问以下网站了解OTA操作。[在线OTA](http://corsacota.surge.sh/?address=dap.local:3241)
|
||||
|
||||
对于大多数ESP8266设备,你不需要关心闪存的大小。然而,闪存大小设置不当可能会导致OTA失败。在这种情况下,请用`idf.py menuconfig`改变闪存大小,或者修改`sdkconfig`:
|
||||
|
||||
```
|
||||
# 选择一个flash大小
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_1MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y
|
||||
|
||||
# 然后设置flash大小
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
||||
```
|
||||
|
||||
如果闪存大小为2MB,sdkconfig文件会看起来像这样:
|
||||
|
||||
```
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_2MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="2MB"
|
||||
```
|
||||
|
||||
对于闪存大小为1MB的设备,如ESP8285,必须做以下修改。
|
||||
|
||||
```
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_two_ota.1MB.csv"
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE_1MB=y
|
||||
CONFIG_ESPTOOLPY_FLASHSIZE="1MB"
|
||||
CONFIG_ESP8266_BOOT_COPY_APP=y
|
||||
```
|
||||
|
||||
可以用esptool.py工具检查你使用的ESP设备闪存大小:
|
||||
|
||||
```bash
|
||||
esptool.py -p (PORT) flash_id
|
||||
```
|
||||
|
||||
### TCP转发的串口
|
||||
|
||||
该功能在TCP和Uart之间提供了一个桥梁:
|
||||
```
|
||||
发送数据 -> TCP -> Uart TX -> 外部设备
|
||||
|
||||
接收数据 <- TCP <- Uart Rx <- 外部设备
|
||||
```
|
||||
|
||||

|
||||
|
||||
当TCP连接建立后,ESP芯片将尝试解决首次发送的文本。当文本是一个有效的波特率时,转发器就会切换到该波特率。例如,发送ASCII文本`115200`会将波特率切换为115200。
|
||||
由于性能原因,该功能默认不启用。你可以修改 [wifi_configuration.h](main/wifi_configuration.h) 来打开它。
|
||||
|
||||
----
|
||||
|
||||
## 开发
|
||||
|
||||
请查看其他分支以了解最新的开发进展。我们欢迎任何形式的贡献,包括但不限于新功能、关于电路的想法和文档。
|
||||
|
||||
如果你有什么想法,欢迎在下面提出:
|
||||
- [新的Issues](https://github.com/windowsair/wireless-esp8266-dap/issues)
|
||||
- [新的pull request](https://github.com/windowsair/wireless-esp8266-dap/pulls)
|
||||
|
||||
|
||||
# 致谢
|
||||
|
||||
归功于以下项目、人员和组织。
|
||||
|
||||
> - https://www.github.com/windowsair/wireless-esp8266-dap origin of this project
|
||||
> - https://github.com/thevoidnn/esp8266-wifi-cmsis-dap for adapter firmware based on CMSIS-DAP v1.0
|
||||
> - https://github.com/ARM-software/CMSIS_5 for CMSIS
|
||||
> - https://github.com/cezanne/usbip-win for usbip windows
|
||||
|
||||
- [@windowsair](https://github.com/windowsair/wireless-esp8266-dap)
|
||||
- [@windowsair](https://www.github.com/windowsair/wireless-esp8266-dap)
|
||||
- [@HeavenSpree](https://www.github.com/HeavenSpree)
|
||||
- [@Zy19930907](https://www.github.com/Zy19930907)
|
||||
- [@caiguang1997](https://www.github.com/caiguang1997)
|
||||
|
|
|
@ -1,12 +1,5 @@
|
|||
file(GLOB SOURCES
|
||||
./cmsis-dap/source/DAP.c
|
||||
./cmsis-dap/source/DAP_vendor.c
|
||||
./cmsis-dap/source/JTAG_DP.c
|
||||
./cmsis-dap/source/SW_DP.c
|
||||
./cmsis-dap/source/SWO.c
|
||||
./cmsis-dap/source/spi_op.c
|
||||
./cmsis-dap/source/spi_switch.c
|
||||
./cmsis-dap/source/dap_utility.c
|
||||
./cmsis-dap/source/*.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
|
|
|
@ -17,14 +17,29 @@
|
|||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* $Date: 1. December 2017
|
||||
* $Revision: V2.0.0
|
||||
* $Date: 16. June 2021
|
||||
* $Revision: V2.1.0
|
||||
*
|
||||
* Project: CMSIS-DAP Configuration
|
||||
* Title: DAP_config.h CMSIS-DAP Configuration File (Template)
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
/**
|
||||
* @file DAP_config.h
|
||||
* @author windowsair
|
||||
* @brief Adaptation of GPIO and SPI pin
|
||||
* @change: 2021-2-10 Support GPIO and SPI
|
||||
* 2021-2-18 Try to support SWO
|
||||
* 2024-1-28 Update to CMSIS-DAP v2.1.0
|
||||
* @version 0.3
|
||||
* @date 2024-1-28
|
||||
*
|
||||
* @copyright Copyright (c) 2021-2024
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef __DAP_CONFIG_H__
|
||||
#define __DAP_CONFIG_H__
|
||||
|
||||
|
@ -134,6 +149,9 @@ This information includes:
|
|||
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
|
||||
#define SWO_UART SWO_FUNCTION_ENABLE ///< SWO UART: 1 = available, 0 = not available.
|
||||
|
||||
/// USART Driver instance number for the UART SWO.
|
||||
#define SWO_UART_DRIVER 0 ///< USART Driver instance number (Driver_USART#).
|
||||
|
||||
/// Maximum SWO UART Baudrate.
|
||||
#define SWO_UART_MAX_BAUDRATE (115200U * 40U) ///< SWO UART Maximum Baudrate in Hz.
|
||||
// <<<<<<<<<<<<<<<<<<<<<<<<<<<<< 5MHz
|
||||
|
@ -155,22 +173,47 @@ This information includes:
|
|||
#define TIMESTAMP_CLOCK 5000000U ///< Timestamp clock in Hz (0 = timestamps not supported).
|
||||
// <<<<<<<<<<<<<<<<<<<<<5MHz
|
||||
|
||||
/// Indicate that UART Communication Port is available.
|
||||
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
|
||||
#define DAP_UART 0 ///< DAP UART: 1 = available, 0 = not available.
|
||||
|
||||
/// USART Driver instance number for the UART Communication Port.
|
||||
#define DAP_UART_DRIVER 1 ///< USART Driver instance number (Driver_USART#).
|
||||
|
||||
/// UART Receive Buffer Size.
|
||||
#define DAP_UART_RX_BUFFER_SIZE 1024U ///< Uart Receive Buffer Size in bytes (must be 2^n).
|
||||
|
||||
/// UART Transmit Buffer Size.
|
||||
#define DAP_UART_TX_BUFFER_SIZE 1024U ///< Uart Transmit Buffer Size in bytes (must be 2^n).
|
||||
|
||||
/// Indicate that UART Communication via USB COM Port is available.
|
||||
/// This information is returned by the command \ref DAP_Info as part of <b>Capabilities</b>.
|
||||
#define DAP_UART_USB_COM_PORT 0 ///< USB COM Port: 1 = available, 0 = not available.
|
||||
|
||||
/// Debug Unit is connected to fixed Target Device.
|
||||
/// The Debug Unit may be part of an evaluation board and always connected to a fixed
|
||||
/// known device. In this case a Device Vendor and Device Name string is stored which
|
||||
/// may be used by the debugger or IDE to configure device parameters.
|
||||
#define TARGET_DEVICE_FIXED 0 ///< Target Device: 1 = known, 0 = unknown;
|
||||
/// known device. In this case a Device Vendor, Device Name, Board Vendor and Board Name strings
|
||||
/// are stored and may be used by the debugger or IDE to configure device parameters.
|
||||
#define TARGET_FIXED 1 ///< Target: 1 = known, 0 = unknown;
|
||||
|
||||
#if TARGET_DEVICE_FIXED
|
||||
#define TARGET_DEVICE_VENDOR "ARM" ///< String indicating the Silicon Vendor
|
||||
#define TARGET_DEVICE_NAME "Cortex-M4" ///< String indicating the Target Device
|
||||
#define TARGET_DEVICE_VENDOR "" ///< String indicating the Silicon Vendor
|
||||
#define TARGET_DEVICE_NAME "" ///< String indicating the Target Device
|
||||
#define TARGET_BOARD_VENDOR "kerms" ///< String indicating the Board Vendor
|
||||
#define TARGET_BOARD_NAME "ESP wireless tool" ///< String indicating the Board Name
|
||||
|
||||
#if TARGET_FIXED != 0
|
||||
#include <string.h>
|
||||
static const char TargetDeviceVendor [] = TARGET_DEVICE_VENDOR;
|
||||
static const char TargetDeviceName [] = TARGET_DEVICE_NAME;
|
||||
static const char TargetBoardVendor [] = TARGET_BOARD_VENDOR;
|
||||
static const char TargetBoardName [] = TARGET_BOARD_NAME;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Get Vendor ID string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string.
|
||||
* @return String length.
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length. (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetVendorString(char *str)
|
||||
{
|
||||
|
@ -184,8 +227,8 @@ __STATIC_INLINE uint8_t DAP_GetVendorString(char *str)
|
|||
/**
|
||||
* @brief Get Product ID string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string.
|
||||
* @return String length.
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length. (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetProductString(char *str)
|
||||
{
|
||||
|
@ -197,8 +240,8 @@ __STATIC_INLINE uint8_t DAP_GetProductString(char *str)
|
|||
/**
|
||||
* @brief Get Serial Number string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string.
|
||||
* @return String length.
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length. (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetSerNumString(char *str)
|
||||
{
|
||||
|
@ -206,6 +249,93 @@ __STATIC_INLINE uint8_t DAP_GetSerNumString(char *str)
|
|||
return (sizeof("1234"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Target Device Vendor string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetTargetDeviceVendorString (char *str) {
|
||||
#if TARGET_FIXED != 0
|
||||
uint8_t len;
|
||||
|
||||
strcpy(str, TargetDeviceVendor);
|
||||
len = (uint8_t)(strlen(TargetDeviceVendor) + 1U);
|
||||
return (len);
|
||||
#else
|
||||
(void)str;
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Target Device Name string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetTargetDeviceNameString (char *str) {
|
||||
#if TARGET_FIXED != 0
|
||||
uint8_t len;
|
||||
|
||||
strcpy(str, TargetDeviceName);
|
||||
len = (uint8_t)(strlen(TargetDeviceName) + 1U);
|
||||
return (len);
|
||||
#else
|
||||
(void)str;
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Target Board Vendor string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetTargetBoardVendorString (char *str) {
|
||||
#if TARGET_FIXED != 0
|
||||
uint8_t len;
|
||||
|
||||
strcpy(str, TargetBoardVendor);
|
||||
len = (uint8_t)(strlen(TargetBoardVendor) + 1U);
|
||||
return (len);
|
||||
#else
|
||||
(void)str;
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Target Board Name string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetTargetBoardNameString (char *str) {
|
||||
#if TARGET_FIXED != 0
|
||||
uint8_t len;
|
||||
|
||||
strcpy(str, TargetBoardName);
|
||||
len = (uint8_t)(strlen(TargetBoardName) + 1U);
|
||||
return (len);
|
||||
#else
|
||||
(void)str;
|
||||
return (0U);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Product Firmware Version string.
|
||||
*
|
||||
* @param str Pointer to buffer to store the string (max 60 characters).
|
||||
* @return String length (including terminating NULL character) or 0 (no string).
|
||||
*/
|
||||
__STATIC_INLINE uint8_t DAP_GetProductFirmwareVersionString (char *str) {
|
||||
(void)str;
|
||||
return (0U);
|
||||
}
|
||||
|
||||
///@}
|
||||
|
||||
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* $Date: 26. November 2019
|
||||
* $Revision: V2.0.0
|
||||
* $Date: 26. May 2021
|
||||
* $Revision: V2.1.0
|
||||
*
|
||||
* Project: CMSIS-DAP Include
|
||||
* Title: DAP.h Definitions
|
||||
|
@ -31,9 +31,9 @@
|
|||
|
||||
// DAP Firmware Version
|
||||
#ifdef DAP_FW_V1
|
||||
#define DAP_FW_VER "1.2.0"
|
||||
#define DAP_FW_VER "1.3.0"
|
||||
#else
|
||||
#define DAP_FW_VER "2.0.0"
|
||||
#define DAP_FW_VER "2.1.0"
|
||||
#endif
|
||||
|
||||
// DAP Command IDs
|
||||
|
@ -63,6 +63,11 @@
|
|||
#define ID_DAP_SWO_Status 0x1BU
|
||||
#define ID_DAP_SWO_ExtendedStatus 0x1EU
|
||||
#define ID_DAP_SWO_Data 0x1CU
|
||||
#define ID_DAP_UART_Transport 0x1FU
|
||||
#define ID_DAP_UART_Configure 0x20U
|
||||
#define ID_DAP_UART_Control 0x22U
|
||||
#define ID_DAP_UART_Status 0x23U
|
||||
#define ID_DAP_UART_Transfer 0x21U
|
||||
|
||||
#define ID_DAP_QueueCommands 0x7EU
|
||||
#define ID_DAP_ExecuteCommands 0x7FU
|
||||
|
@ -111,11 +116,16 @@
|
|||
#define DAP_ID_VENDOR 1U
|
||||
#define DAP_ID_PRODUCT 2U
|
||||
#define DAP_ID_SER_NUM 3U
|
||||
#define DAP_ID_FW_VER 4U
|
||||
#define DAP_ID_DAP_FW_VER 4U
|
||||
#define DAP_ID_DEVICE_VENDOR 5U
|
||||
#define DAP_ID_DEVICE_NAME 6U
|
||||
#define DAP_ID_BOARD_VENDOR 7U
|
||||
#define DAP_ID_BOARD_NAME 8U
|
||||
#define DAP_ID_PRODUCT_FW_VER 9U
|
||||
#define DAP_ID_CAPABILITIES 0xF0U
|
||||
#define DAP_ID_TIMESTAMP_CLOCK 0xF1U
|
||||
#define DAP_ID_UART_RX_BUFFER_SIZE 0xFBU
|
||||
#define DAP_ID_UART_TX_BUFFER_SIZE 0xFCU
|
||||
#define DAP_ID_SWO_BUFFER_SIZE 0xFDU
|
||||
#define DAP_ID_PACKET_COUNT 0xFEU
|
||||
#define DAP_ID_PACKET_SIZE 0xFFU
|
||||
|
@ -165,6 +175,30 @@
|
|||
#define DAP_SWO_STREAM_ERROR (1U<<6)
|
||||
#define DAP_SWO_BUFFER_OVERRUN (1U<<7)
|
||||
|
||||
// DAP UART Transport
|
||||
#define DAP_UART_TRANSPORT_NONE 0U
|
||||
#define DAP_UART_TRANSPORT_USB_COM_PORT 1U
|
||||
#define DAP_UART_TRANSPORT_DAP_COMMAND 2U
|
||||
|
||||
// DAP UART Control
|
||||
#define DAP_UART_CONTROL_RX_ENABLE (1U<<0)
|
||||
#define DAP_UART_CONTROL_RX_DISABLE (1U<<1)
|
||||
#define DAP_UART_CONTROL_RX_BUF_FLUSH (1U<<2)
|
||||
#define DAP_UART_CONTROL_TX_ENABLE (1U<<4)
|
||||
#define DAP_UART_CONTROL_TX_DISABLE (1U<<5)
|
||||
#define DAP_UART_CONTROL_TX_BUF_FLUSH (1U<<6)
|
||||
|
||||
// DAP UART Status
|
||||
#define DAP_UART_STATUS_RX_ENABLED (1U<<0)
|
||||
#define DAP_UART_STATUS_RX_DATA_LOST (1U<<1)
|
||||
#define DAP_UART_STATUS_FRAMING_ERROR (1U<<2)
|
||||
#define DAP_UART_STATUS_PARITY_ERROR (1U<<3)
|
||||
#define DAP_UART_STATUS_TX_ENABLED (1U<<4)
|
||||
|
||||
// DAP UART Configure Error
|
||||
#define DAP_UART_CFG_ERROR_DATA_BITS (1U<<0)
|
||||
#define DAP_UART_CFG_ERROR_PARITY (1U<<1)
|
||||
#define DAP_UART_CFG_ERROR_STOP_BITS (1U<<2)
|
||||
|
||||
// Debug Port Register Addresses
|
||||
#define DP_IDCODE 0x00U // IDCODE Register (SW Read only)
|
||||
|
@ -270,17 +304,25 @@ extern void SWO_QueueTransfer (uint8_t *buf, uint32_t num);
|
|||
extern void SWO_AbortTransfer (void);
|
||||
extern void SWO_TransferComplete (void);
|
||||
|
||||
extern uint32_t UART_SWO_Mode (uint32_t enable);
|
||||
extern uint32_t UART_SWO_Baudrate (uint32_t baudrate);
|
||||
extern uint32_t UART_SWO_Control (uint32_t active);
|
||||
// extern void UART_SWO_Capture (uint8_t *buf, uint32_t num);
|
||||
// extern uint32_t UART_SWO_GetCount (void);
|
||||
extern uint32_t SWO_Mode_UART (uint32_t enable);
|
||||
extern uint32_t SWO_Baudrate_UART (uint32_t baudrate);
|
||||
extern uint32_t SWO_Control_UART (uint32_t active);
|
||||
extern void SWO_Capture_UART (uint8_t *buf, uint32_t num);
|
||||
extern uint32_t SWO_GetCount_UART (void);
|
||||
|
||||
extern uint32_t Manchester_SWO_Mode (uint32_t enable);
|
||||
extern uint32_t Manchester_SWO_Baudrate (uint32_t baudrate);
|
||||
extern uint32_t Manchester_SWO_Control (uint32_t active);
|
||||
extern void Manchester_SWO_Capture (uint8_t *buf, uint32_t num);
|
||||
extern uint32_t Manchester_SWO_GetCount (void);
|
||||
extern uint32_t SWO_Mode_Manchester (uint32_t enable);
|
||||
extern uint32_t SWO_Baudrate_Manchester (uint32_t baudrate);
|
||||
extern uint32_t SWO_Control_Manchester (uint32_t active);
|
||||
extern void SWO_Capture_Manchester (uint8_t *buf, uint32_t num);
|
||||
extern uint32_t SWO_GetCount_Manchester (void);
|
||||
|
||||
extern uint32_t UART_Transport (const uint8_t *request, uint8_t *response);
|
||||
extern uint32_t UART_Configure (const uint8_t *request, uint8_t *response);
|
||||
extern uint32_t UART_Control (const uint8_t *request, uint8_t *response);
|
||||
extern uint32_t UART_Status (uint8_t *response);
|
||||
extern uint32_t UART_Transfer (const uint8_t *request, uint8_t *response);
|
||||
|
||||
extern uint8_t USB_COM_PORT_Activate (uint32_t cmd);
|
||||
|
||||
extern uint32_t DAP_ProcessVendorCommand (const uint8_t *request, uint8_t *response);
|
||||
extern uint32_t DAP_ProcessCommand (const uint8_t *request, uint8_t *response);
|
||||
|
|
|
@ -17,8 +17,8 @@
|
|||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* $Date: 1. December 2017
|
||||
* $Revision: V2.0.0
|
||||
* $Date: 16. June 2021
|
||||
* $Revision: V2.1.0
|
||||
*
|
||||
* Project: CMSIS-DAP Source
|
||||
* Title: DAP.c CMSIS-DAP Commands
|
||||
|
@ -63,10 +63,6 @@ volatile uint8_t DAP_TransferAbort; // Transfer Abort Flag
|
|||
|
||||
static const char DAP_FW_Ver [] = DAP_FW_VER;
|
||||
|
||||
#if TARGET_DEVICE_FIXED
|
||||
static const char TargetDeviceVendor [] = TARGET_DEVICE_VENDOR;
|
||||
static const char TargetDeviceName [] = TARGET_DEVICE_NAME;
|
||||
#endif
|
||||
|
||||
|
||||
// Get DAP Information
|
||||
|
@ -86,21 +82,24 @@ static uint8_t DAP_Info(uint8_t id, uint8_t *info) {
|
|||
case DAP_ID_SER_NUM:
|
||||
length = DAP_GetSerNumString((char *)info);
|
||||
break;
|
||||
case DAP_ID_FW_VER:
|
||||
case DAP_ID_DAP_FW_VER:
|
||||
length = (uint8_t)sizeof(DAP_FW_Ver);
|
||||
memcpy(info, DAP_FW_Ver, length);
|
||||
break;
|
||||
case DAP_ID_DEVICE_VENDOR:
|
||||
#if TARGET_DEVICE_FIXED
|
||||
length = (uint8_t)sizeof(TargetDeviceVendor);
|
||||
memcpy(info, TargetDeviceVendor, length);
|
||||
#endif
|
||||
length = DAP_GetTargetDeviceVendorString((char *)info);
|
||||
break;
|
||||
case DAP_ID_DEVICE_NAME:
|
||||
#if TARGET_DEVICE_FIXED
|
||||
length = (uint8_t)sizeof(TargetDeviceName);
|
||||
memcpy(info, TargetDeviceName, length);
|
||||
#endif
|
||||
length = DAP_GetTargetDeviceNameString((char *)info);
|
||||
break;
|
||||
case DAP_ID_BOARD_VENDOR:
|
||||
length = DAP_GetTargetBoardVendorString((char *)info);
|
||||
break;
|
||||
case DAP_ID_BOARD_NAME:
|
||||
length = DAP_GetTargetBoardNameString((char *)info);
|
||||
break;
|
||||
case DAP_ID_PRODUCT_FW_VER:
|
||||
length = DAP_GetProductFirmwareVersionString((char *)info);
|
||||
break;
|
||||
case DAP_ID_CAPABILITIES:
|
||||
info[0] = ((DAP_SWD != 0) ? (1U << 0) : 0U) |
|
||||
|
@ -109,8 +108,11 @@ static uint8_t DAP_Info(uint8_t id, uint8_t *info) {
|
|||
((SWO_MANCHESTER != 0) ? (1U << 3) : 0U) |
|
||||
/* Atomic Commands */ (1U << 4) |
|
||||
((TIMESTAMP_CLOCK != 0U) ? (1U << 5) : 0U) |
|
||||
((SWO_STREAM != 0U) ? (1U << 6) : 0U);
|
||||
length = 1U;
|
||||
((SWO_STREAM != 0U) ? (1U << 6) : 0U) |
|
||||
((DAP_UART != 0U) ? (1U << 7) : 0U);
|
||||
|
||||
info[1] = ((DAP_UART_USB_COM_PORT != 0) ? (1U << 0) : 0U);
|
||||
length = 2U;
|
||||
break;
|
||||
case DAP_ID_TIMESTAMP_CLOCK:
|
||||
#if (TIMESTAMP_CLOCK != 0U)
|
||||
|
@ -119,6 +121,24 @@ static uint8_t DAP_Info(uint8_t id, uint8_t *info) {
|
|||
info[2] = (uint8_t)(TIMESTAMP_CLOCK >> 16);
|
||||
info[3] = (uint8_t)(TIMESTAMP_CLOCK >> 24);
|
||||
length = 4U;
|
||||
#endif
|
||||
break;
|
||||
case DAP_ID_UART_RX_BUFFER_SIZE:
|
||||
#if (DAP_UART != 0)
|
||||
info[0] = (uint8_t)(DAP_UART_RX_BUFFER_SIZE >> 0);
|
||||
info[1] = (uint8_t)(DAP_UART_RX_BUFFER_SIZE >> 8);
|
||||
info[2] = (uint8_t)(DAP_UART_RX_BUFFER_SIZE >> 16);
|
||||
info[3] = (uint8_t)(DAP_UART_RX_BUFFER_SIZE >> 24);
|
||||
length = 4U;
|
||||
#endif
|
||||
break;
|
||||
case DAP_ID_UART_TX_BUFFER_SIZE:
|
||||
#if (DAP_UART != 0)
|
||||
info[0] = (uint8_t)(DAP_UART_TX_BUFFER_SIZE >> 0);
|
||||
info[1] = (uint8_t)(DAP_UART_TX_BUFFER_SIZE >> 8);
|
||||
info[2] = (uint8_t)(DAP_UART_TX_BUFFER_SIZE >> 16);
|
||||
info[3] = (uint8_t)(DAP_UART_TX_BUFFER_SIZE >> 24);
|
||||
length = 4U;
|
||||
#endif
|
||||
break;
|
||||
case DAP_ID_SWO_BUFFER_SIZE:
|
||||
|
@ -1751,6 +1771,24 @@ uint32_t DAP_ProcessCommand(const uint8_t *request, uint8_t *response) {
|
|||
break;
|
||||
#endif
|
||||
|
||||
#if (DAP_UART != 0)
|
||||
case ID_DAP_UART_Transport:
|
||||
num = UART_Transport(request, response);
|
||||
break;
|
||||
case ID_DAP_UART_Configure:
|
||||
num = UART_Configure(request, response);
|
||||
break;
|
||||
case ID_DAP_UART_Control:
|
||||
num = UART_Control(request, response);
|
||||
break;
|
||||
case ID_DAP_UART_Status:
|
||||
num = UART_Status(response);
|
||||
break;
|
||||
case ID_DAP_UART_Transfer:
|
||||
num = UART_Transfer(request, response);
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
*(response-1) = ID_DAP_Invalid;
|
||||
return ((1U << 16) | 1U);
|
||||
|
|
|
@ -0,0 +1,652 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ARM Limited. All rights reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* 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
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* ----------------------------------------------------------------------
|
||||
*
|
||||
* $Date: 1. March 2021
|
||||
* $Revision: V1.0.0
|
||||
*
|
||||
* Project: CMSIS-DAP Source
|
||||
* Title: UART.c CMSIS-DAP UART
|
||||
*
|
||||
*---------------------------------------------------------------------------*/
|
||||
|
||||
#include "DAP_config.h"
|
||||
#include "cmsis-dap/include/DAP.h"
|
||||
|
||||
#if (DAP_UART != 0)
|
||||
|
||||
#ifdef DAP_FW_V1
|
||||
#error "UART Communication Port not supported in DAP V1!"
|
||||
#endif
|
||||
|
||||
#include "Driver_USART.h"
|
||||
|
||||
#include "cmsis_os2.h"
|
||||
#include <string.h>
|
||||
|
||||
#define UART_RX_BLOCK_SIZE 32U /* Uart Rx Block Size (must be 2^n) */
|
||||
|
||||
// USART Driver
|
||||
#define _USART_Driver_(n) Driver_USART##n
|
||||
#define USART_Driver_(n) _USART_Driver_(n)
|
||||
extern ARM_DRIVER_USART USART_Driver_(DAP_UART_DRIVER);
|
||||
#define pUSART (&USART_Driver_(DAP_UART_DRIVER))
|
||||
|
||||
// UART Configuration
|
||||
#if (DAP_UART_USB_COM_PORT != 0)
|
||||
static uint8_t UartTransport = DAP_UART_TRANSPORT_USB_COM_PORT;
|
||||
#else
|
||||
static uint8_t UartTransport = DAP_UART_TRANSPORT_NONE;
|
||||
#endif
|
||||
|
||||
// UART Flags
|
||||
static uint8_t UartConfigured = 0U;
|
||||
static uint8_t UartReceiveEnabled = 0U;
|
||||
static uint8_t UartTransmitEnabled = 0U;
|
||||
static uint8_t UartTransmitActive = 0U;
|
||||
|
||||
// UART TX Buffer
|
||||
static uint8_t UartTxBuf[DAP_UART_TX_BUFFER_SIZE];
|
||||
static volatile uint32_t UartTxIndexI = 0U;
|
||||
static volatile uint32_t UartTxIndexO = 0U;
|
||||
|
||||
// UART RX Buffer
|
||||
static uint8_t UartRxBuf[DAP_UART_RX_BUFFER_SIZE];
|
||||
static volatile uint32_t UartRxIndexI = 0U;
|
||||
static volatile uint32_t UartRxIndexO = 0U;
|
||||
|
||||
// Uart Errors
|
||||
static volatile uint8_t UartErrorRxDataLost = 0U;
|
||||
static volatile uint8_t UartErrorFraming = 0U;
|
||||
static volatile uint8_t UartErrorParity = 0U;
|
||||
|
||||
// UART Transmit
|
||||
static uint32_t UartTxNum = 0U;
|
||||
|
||||
// Function prototypes
|
||||
static uint8_t UART_Init (void);
|
||||
static void UART_Uninit (void);
|
||||
static uint8_t UART_Get_Status (void);
|
||||
static uint8_t UART_Receive_Enable (void);
|
||||
static uint8_t UART_Transmit_Enable (void);
|
||||
static void UART_Receive_Disable (void);
|
||||
static void UART_Transmit_Disable (void);
|
||||
static void UART_Receive_Flush (void);
|
||||
static void UART_Transmit_Flush (void);
|
||||
static void UART_Receive (void);
|
||||
static void UART_Transmit (void);
|
||||
|
||||
|
||||
// USART Driver Callback function
|
||||
// event: event mask
|
||||
static void USART_Callback (uint32_t event) {
|
||||
if (event & ARM_USART_EVENT_SEND_COMPLETE) {
|
||||
UartTxIndexO += UartTxNum;
|
||||
UartTransmitActive = 0U;
|
||||
UART_Transmit();
|
||||
}
|
||||
if (event & ARM_USART_EVENT_RECEIVE_COMPLETE) {
|
||||
UartRxIndexI += UART_RX_BLOCK_SIZE;
|
||||
UART_Receive();
|
||||
}
|
||||
if (event & ARM_USART_EVENT_RX_OVERFLOW) {
|
||||
UartErrorRxDataLost = 1U;
|
||||
}
|
||||
if (event & ARM_USART_EVENT_RX_FRAMING_ERROR) {
|
||||
UartErrorFraming = 1U;
|
||||
}
|
||||
if (event & ARM_USART_EVENT_RX_PARITY_ERROR) {
|
||||
UartErrorParity = 1U;
|
||||
}
|
||||
}
|
||||
|
||||
// Init UART
|
||||
// return: DAP_OK or DAP_ERROR
|
||||
static uint8_t UART_Init (void) {
|
||||
int32_t status;
|
||||
uint8_t ret = DAP_ERROR;
|
||||
|
||||
UartConfigured = 0U;
|
||||
UartReceiveEnabled = 0U;
|
||||
UartTransmitEnabled = 0U;
|
||||
UartTransmitActive = 0U;
|
||||
UartErrorRxDataLost = 0U;
|
||||
UartErrorFraming = 0U;
|
||||
UartErrorParity = 0U;
|
||||
UartTxIndexI = 0U;
|
||||
UartTxIndexO = 0U;
|
||||
UartRxIndexI = 0U;
|
||||
UartRxIndexO = 0U;
|
||||
UartTxNum = 0U;
|
||||
|
||||
status = pUSART->Initialize(USART_Callback);
|
||||
if (status == ARM_DRIVER_OK) {
|
||||
status = pUSART->PowerControl(ARM_POWER_FULL);
|
||||
}
|
||||
if (status == ARM_DRIVER_OK) {
|
||||
ret = DAP_OK;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
// Un-Init UART
|
||||
static void UART_Uninit (void) {
|
||||
UartConfigured = 0U;
|
||||
|
||||
pUSART->PowerControl(ARM_POWER_OFF);
|
||||
pUSART->Uninitialize();
|
||||
}
|
||||
|
||||
// Get UART Status
|
||||
// return: status
|
||||
static uint8_t UART_Get_Status (void) {
|
||||
uint8_t status = 0U;
|
||||
|
||||
if (UartReceiveEnabled != 0U) {
|
||||
status |= DAP_UART_STATUS_RX_ENABLED;
|
||||
}
|
||||
if (UartErrorRxDataLost != 0U) {
|
||||
UartErrorRxDataLost = 0U;
|
||||
status |= DAP_UART_STATUS_RX_DATA_LOST;
|
||||
}
|
||||
if (UartErrorFraming != 0U) {
|
||||
UartErrorFraming = 0U;
|
||||
status |= DAP_UART_STATUS_FRAMING_ERROR;
|
||||
}
|
||||
if (UartErrorParity != 0U) {
|
||||
UartErrorParity = 0U;
|
||||
status |= DAP_UART_STATUS_PARITY_ERROR;
|
||||
}
|
||||
if (UartTransmitEnabled != 0U) {
|
||||
status |= DAP_UART_STATUS_TX_ENABLED;
|
||||
}
|
||||
|
||||
return (status);
|
||||
}
|
||||
|
||||
// Enable UART Receive
|
||||
// return: DAP_OK or DAP_ERROR
|
||||
static uint8_t UART_Receive_Enable (void) {
|
||||
int32_t status;
|
||||
uint8_t ret = DAP_ERROR;
|
||||
|
||||
if (UartReceiveEnabled == 0U) {
|
||||
// Flush Buffers
|
||||
UartRxIndexI = 0U;
|
||||
UartRxIndexO = 0U;
|
||||
|
||||
UART_Receive();
|
||||
status = pUSART->Control(ARM_USART_CONTROL_RX, 1U);
|
||||
if (status == ARM_DRIVER_OK) {
|
||||
UartReceiveEnabled = 1U;
|
||||
ret = DAP_OK;
|
||||
}
|
||||
} else {
|
||||
ret = DAP_OK;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
// Enable UART Transmit
|
||||
// return: DAP_OK or DAP_ERROR
|
||||
static uint8_t UART_Transmit_Enable (void) {
|
||||
int32_t status;
|
||||
uint8_t ret = DAP_ERROR;
|
||||
|
||||
if (UartTransmitEnabled == 0U) {
|
||||
// Flush Buffers
|
||||
UartTransmitActive = 0U;
|
||||
UartTxIndexI = 0U;
|
||||
UartTxIndexO = 0U;
|
||||
UartTxNum = 0U;
|
||||
|
||||
status = pUSART->Control(ARM_USART_CONTROL_TX, 1U);
|
||||
if (status == ARM_DRIVER_OK) {
|
||||
UartTransmitEnabled = 1U;
|
||||
ret = DAP_OK;
|
||||
}
|
||||
} else {
|
||||
ret = DAP_OK;
|
||||
}
|
||||
|
||||
return (ret);
|
||||
}
|
||||
|
||||
// Disable UART Receive
|
||||
static void UART_Receive_Disable (void) {
|
||||
if (UartReceiveEnabled != 0U) {
|
||||
pUSART->Control(ARM_USART_CONTROL_RX, 0U);
|
||||
pUSART->Control(ARM_USART_ABORT_RECEIVE, 0U);
|
||||
UartReceiveEnabled = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Disable UART Transmit
|
||||
static void UART_Transmit_Disable (void) {
|
||||
if (UartTransmitEnabled != 0U) {
|
||||
pUSART->Control(ARM_USART_ABORT_SEND, 0U);
|
||||
pUSART->Control(ARM_USART_CONTROL_TX, 0U);
|
||||
UartTransmitActive = 0U;
|
||||
UartTransmitEnabled = 0U;
|
||||
}
|
||||
}
|
||||
|
||||
// Flush UART Receive buffer
|
||||
static void UART_Receive_Flush (void) {
|
||||
pUSART->Control(ARM_USART_ABORT_RECEIVE, 0U);
|
||||
UartRxIndexI = 0U;
|
||||
UartRxIndexO = 0U;
|
||||
if (UartReceiveEnabled != 0U) {
|
||||
UART_Receive();
|
||||
}
|
||||
}
|
||||
|
||||
// Flush UART Transmit buffer
|
||||
static void UART_Transmit_Flush (void) {
|
||||
pUSART->Control(ARM_USART_ABORT_SEND, 0U);
|
||||
UartTransmitActive = 0U;
|
||||
UartTxIndexI = 0U;
|
||||
UartTxIndexO = 0U;
|
||||
UartTxNum = 0U;
|
||||
}
|
||||
|
||||
// Receive data from target via UART
|
||||
static void UART_Receive (void) {
|
||||
uint32_t index;
|
||||
|
||||
index = UartRxIndexI & (DAP_UART_RX_BUFFER_SIZE - 1U);
|
||||
pUSART->Receive(&UartRxBuf[index], UART_RX_BLOCK_SIZE);
|
||||
}
|
||||
|
||||
// Transmit available data to target via UART
|
||||
static void UART_Transmit (void) {
|
||||
uint32_t count;
|
||||
uint32_t index;
|
||||
|
||||
count = UartTxIndexI - UartTxIndexO;
|
||||
index = UartTxIndexO & (DAP_UART_TX_BUFFER_SIZE - 1U);
|
||||
|
||||
if (count != 0U) {
|
||||
if ((index + count) <= DAP_UART_TX_BUFFER_SIZE) {
|
||||
UartTxNum = count;
|
||||
} else {
|
||||
UartTxNum = DAP_UART_TX_BUFFER_SIZE - index;
|
||||
}
|
||||
UartTransmitActive = 1U;
|
||||
pUSART->Send(&UartTxBuf[index], UartTxNum);
|
||||
}
|
||||
}
|
||||
|
||||
// Process UART Transport command and prepare response
|
||||
// request: pointer to request data
|
||||
// response: pointer to response data
|
||||
// return: number of bytes in response (lower 16 bits)
|
||||
// number of bytes in request (upper 16 bits)
|
||||
uint32_t UART_Transport (const uint8_t *request, uint8_t *response) {
|
||||
uint8_t transport;
|
||||
uint8_t ret = DAP_ERROR;
|
||||
|
||||
transport = *request;
|
||||
switch (transport) {
|
||||
case DAP_UART_TRANSPORT_NONE:
|
||||
switch (UartTransport) {
|
||||
case DAP_UART_TRANSPORT_NONE:
|
||||
ret = DAP_OK;
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_USB_COM_PORT:
|
||||
#if (DAP_UART_USB_COM_PORT != 0)
|
||||
USB_COM_PORT_Activate(0U);
|
||||
UartTransport = DAP_UART_TRANSPORT_NONE;
|
||||
ret = DAP_OK;
|
||||
#endif
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_DAP_COMMAND:
|
||||
UART_Receive_Disable();
|
||||
UART_Transmit_Disable();
|
||||
UART_Uninit();
|
||||
UartTransport = DAP_UART_TRANSPORT_NONE;
|
||||
ret= DAP_OK;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_USB_COM_PORT:
|
||||
switch (UartTransport) {
|
||||
case DAP_UART_TRANSPORT_NONE:
|
||||
#if (DAP_UART_USB_COM_PORT != 0)
|
||||
if (USB_COM_PORT_Activate(1U) == 0U) {
|
||||
UartTransport = DAP_UART_TRANSPORT_USB_COM_PORT;
|
||||
ret = DAP_OK;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_USB_COM_PORT:
|
||||
ret = DAP_OK;
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_DAP_COMMAND:
|
||||
UART_Receive_Disable();
|
||||
UART_Transmit_Disable();
|
||||
UART_Uninit();
|
||||
UartTransport = DAP_UART_TRANSPORT_NONE;
|
||||
#if (DAP_UART_USB_COM_PORT != 0)
|
||||
if (USB_COM_PORT_Activate(1U) == 0U) {
|
||||
UartTransport = DAP_UART_TRANSPORT_USB_COM_PORT;
|
||||
ret = DAP_OK;
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_DAP_COMMAND:
|
||||
switch (UartTransport) {
|
||||
case DAP_UART_TRANSPORT_NONE:
|
||||
ret = UART_Init();
|
||||
if (ret == DAP_OK) {
|
||||
UartTransport = DAP_UART_TRANSPORT_DAP_COMMAND;
|
||||
}
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_USB_COM_PORT:
|
||||
#if (DAP_UART_USB_COM_PORT != 0)
|
||||
USB_COM_PORT_Activate(0U);
|
||||
UartTransport = DAP_UART_TRANSPORT_NONE;
|
||||
#endif
|
||||
ret = UART_Init();
|
||||
if (ret == DAP_OK) {
|
||||
UartTransport = DAP_UART_TRANSPORT_DAP_COMMAND;
|
||||
}
|
||||
break;
|
||||
case DAP_UART_TRANSPORT_DAP_COMMAND:
|
||||
ret = DAP_OK;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
*response = ret;
|
||||
|
||||
return ((1U << 16) | 1U);
|
||||
}
|
||||
|
||||
// Process UART Configure command and prepare response
|
||||
// request: pointer to request data
|
||||
// response: pointer to response data
|
||||
// return: number of bytes in response (lower 16 bits)
|
||||
// number of bytes in request (upper 16 bits)
|
||||
uint32_t UART_Configure (const uint8_t *request, uint8_t *response) {
|
||||
uint8_t control, status;
|
||||
uint32_t baudrate;
|
||||
int32_t result;
|
||||
|
||||
if (UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) {
|
||||
status = DAP_UART_CFG_ERROR_DATA_BITS |
|
||||
DAP_UART_CFG_ERROR_PARITY |
|
||||
DAP_UART_CFG_ERROR_STOP_BITS;
|
||||
baudrate = 0U; // baudrate error
|
||||
} else {
|
||||
|
||||
status = 0U;
|
||||
control = *request;
|
||||
baudrate = (uint32_t)(*(request+1) << 0) |
|
||||
(uint32_t)(*(request+2) << 8) |
|
||||
(uint32_t)(*(request+3) << 16) |
|
||||
(uint32_t)(*(request+4) << 24);
|
||||
|
||||
result = pUSART->Control(control |
|
||||
ARM_USART_MODE_ASYNCHRONOUS |
|
||||
ARM_USART_FLOW_CONTROL_NONE,
|
||||
baudrate);
|
||||
if (result == ARM_DRIVER_OK) {
|
||||
UartConfigured = 1U;
|
||||
} else {
|
||||
UartConfigured = 0U;
|
||||
switch (result) {
|
||||
case ARM_USART_ERROR_BAUDRATE:
|
||||
status = 0U;
|
||||
baudrate = 0U;
|
||||
break;
|
||||
case ARM_USART_ERROR_DATA_BITS:
|
||||
status = DAP_UART_CFG_ERROR_DATA_BITS;
|
||||
break;
|
||||
case ARM_USART_ERROR_PARITY:
|
||||
status = DAP_UART_CFG_ERROR_PARITY;
|
||||
break;
|
||||
case ARM_USART_ERROR_STOP_BITS:
|
||||
status = DAP_UART_CFG_ERROR_STOP_BITS;
|
||||
break;
|
||||
default:
|
||||
status = DAP_UART_CFG_ERROR_DATA_BITS |
|
||||
DAP_UART_CFG_ERROR_PARITY |
|
||||
DAP_UART_CFG_ERROR_STOP_BITS;
|
||||
baudrate = 0U;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*response++ = status;
|
||||
*response++ = (uint8_t)(baudrate >> 0);
|
||||
*response++ = (uint8_t)(baudrate >> 8);
|
||||
*response++ = (uint8_t)(baudrate >> 16);
|
||||
*response = (uint8_t)(baudrate >> 24);
|
||||
|
||||
return ((5U << 16) | 5U);
|
||||
}
|
||||
|
||||
// Process UART Control command and prepare response
|
||||
// request: pointer to request data
|
||||
// response: pointer to response data
|
||||
// return: number of bytes in response (lower 16 bits)
|
||||
// number of bytes in request (upper 16 bits)
|
||||
uint32_t UART_Control (const uint8_t *request, uint8_t *response) {
|
||||
uint8_t control;
|
||||
uint8_t result;
|
||||
uint8_t ret = DAP_OK;
|
||||
|
||||
if (UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) {
|
||||
ret = DAP_ERROR;
|
||||
} else {
|
||||
|
||||
control = *request;
|
||||
|
||||
if ((control & DAP_UART_CONTROL_RX_DISABLE) != 0U) {
|
||||
// Receive disable
|
||||
UART_Receive_Disable();
|
||||
} else if ((control & DAP_UART_CONTROL_RX_ENABLE) != 0U) {
|
||||
// Receive enable
|
||||
if (UartConfigured != 0U) {
|
||||
result = UART_Receive_Enable();
|
||||
if (result != DAP_OK) {
|
||||
ret = DAP_ERROR;
|
||||
}
|
||||
} else {
|
||||
ret = DAP_ERROR;
|
||||
}
|
||||
}
|
||||
if ((control & DAP_UART_CONTROL_RX_BUF_FLUSH) != 0U) {
|
||||
UART_Receive_Flush();
|
||||
}
|
||||
|
||||
if ((control & DAP_UART_CONTROL_TX_DISABLE) != 0U) {
|
||||
// Transmit disable
|
||||
UART_Transmit_Disable();
|
||||
} else if ((control & DAP_UART_CONTROL_TX_ENABLE) != 0U) {
|
||||
// Transmit enable
|
||||
if (UartConfigured != 0U) {
|
||||
result = UART_Transmit_Enable();
|
||||
if (result != DAP_OK) {
|
||||
ret = DAP_ERROR;
|
||||
}
|
||||
} else {
|
||||
ret = DAP_ERROR;
|
||||
}
|
||||
}
|
||||
if ((control & DAP_UART_CONTROL_TX_BUF_FLUSH) != 0U) {
|
||||
UART_Transmit_Flush();
|
||||
}
|
||||
}
|
||||
|
||||
*response = ret;
|
||||
|
||||
return ((1U << 16) | 1U);
|
||||
}
|
||||
|
||||
// Process UART Status command and prepare response
|
||||
// response: pointer to response data
|
||||
// return: number of bytes in response (lower 16 bits)
|
||||
// number of bytes in request (upper 16 bits)
|
||||
uint32_t UART_Status (uint8_t *response) {
|
||||
uint32_t rx_cnt, tx_cnt;
|
||||
uint32_t cnt;
|
||||
uint8_t status;
|
||||
|
||||
if ((UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) ||
|
||||
(UartConfigured == 0U)) {
|
||||
rx_cnt = 0U;
|
||||
tx_cnt = 0U;
|
||||
status = 0U;
|
||||
} else {
|
||||
|
||||
rx_cnt = UartRxIndexI - UartRxIndexO;
|
||||
rx_cnt += pUSART->GetRxCount();
|
||||
if (rx_cnt > (DAP_UART_RX_BUFFER_SIZE - (UART_RX_BLOCK_SIZE*2))) {
|
||||
// Overflow
|
||||
UartErrorRxDataLost = 1U;
|
||||
rx_cnt = (DAP_UART_RX_BUFFER_SIZE - (UART_RX_BLOCK_SIZE*2));
|
||||
UartRxIndexO = UartRxIndexI - rx_cnt;
|
||||
}
|
||||
|
||||
tx_cnt = UartTxIndexI - UartTxIndexO;
|
||||
cnt = pUSART->GetTxCount();
|
||||
if (UartTransmitActive != 0U) {
|
||||
tx_cnt -= cnt;
|
||||
}
|
||||
|
||||
status = UART_Get_Status();
|
||||
}
|
||||
|
||||
*response++ = status;
|
||||
*response++ = (uint8_t)(rx_cnt >> 0);
|
||||
*response++ = (uint8_t)(rx_cnt >> 8);
|
||||
*response++ = (uint8_t)(rx_cnt >> 16);
|
||||
*response++ = (uint8_t)(rx_cnt >> 24);
|
||||
*response++ = (uint8_t)(tx_cnt >> 0);
|
||||
*response++ = (uint8_t)(tx_cnt >> 8);
|
||||
*response++ = (uint8_t)(tx_cnt >> 16);
|
||||
*response = (uint8_t)(tx_cnt >> 24);
|
||||
|
||||
return ((0U << 16) | 9U);
|
||||
}
|
||||
|
||||
// Process UART Transfer command and prepare response
|
||||
// request: pointer to request data
|
||||
// response: pointer to response data
|
||||
// return: number of bytes in response (lower 16 bits)
|
||||
// number of bytes in request (upper 16 bits)
|
||||
uint32_t UART_Transfer (const uint8_t *request, uint8_t *response) {
|
||||
uint32_t rx_cnt, tx_cnt;
|
||||
uint32_t rx_num, tx_num;
|
||||
uint8_t *rx_data;
|
||||
const
|
||||
uint8_t *tx_data;
|
||||
uint32_t num;
|
||||
uint32_t index;
|
||||
uint8_t status;
|
||||
|
||||
if (UartTransport != DAP_UART_TRANSPORT_DAP_COMMAND) {
|
||||
status = 0U;
|
||||
rx_cnt = 0U;
|
||||
tx_cnt = 0U;
|
||||
} else {
|
||||
|
||||
// RX Data
|
||||
rx_cnt = ((uint32_t)(*(request+0) << 0) |
|
||||
(uint32_t)(*(request+1) << 8));
|
||||
|
||||
if (rx_cnt > (DAP_PACKET_SIZE - 6U)) {
|
||||
rx_cnt = (DAP_PACKET_SIZE - 6U);
|
||||
}
|
||||
rx_num = UartRxIndexI - UartRxIndexO;
|
||||
rx_num += pUSART->GetRxCount();
|
||||
if (rx_num > (DAP_UART_RX_BUFFER_SIZE - (UART_RX_BLOCK_SIZE*2))) {
|
||||
// Overflow
|
||||
UartErrorRxDataLost = 1U;
|
||||
rx_num = (DAP_UART_RX_BUFFER_SIZE - (UART_RX_BLOCK_SIZE*2));
|
||||
UartRxIndexO = UartRxIndexI - rx_num;
|
||||
}
|
||||
if (rx_cnt > rx_num) {
|
||||
rx_cnt = rx_num;
|
||||
}
|
||||
|
||||
rx_data = (response+5);
|
||||
index = UartRxIndexO & (DAP_UART_RX_BUFFER_SIZE - 1U);
|
||||
if ((index + rx_cnt) <= DAP_UART_RX_BUFFER_SIZE) {
|
||||
memcpy( rx_data, &UartRxBuf[index], rx_cnt);
|
||||
} else {
|
||||
num = DAP_UART_RX_BUFFER_SIZE - index;
|
||||
memcpy( rx_data, &UartRxBuf[index], num);
|
||||
memcpy(&rx_data[num], &UartRxBuf[0], rx_cnt - num);
|
||||
}
|
||||
UartRxIndexO += rx_cnt;
|
||||
|
||||
// TX Data
|
||||
tx_cnt = ((uint32_t)(*(request+2) << 0) |
|
||||
(uint32_t)(*(request+3) << 8));
|
||||
tx_data = (request+4);
|
||||
|
||||
if (tx_cnt > (DAP_PACKET_SIZE - 5U)) {
|
||||
tx_cnt = (DAP_PACKET_SIZE - 5U);
|
||||
}
|
||||
tx_num = UartTxIndexI - UartTxIndexO;
|
||||
num = pUSART->GetTxCount();
|
||||
if (UartTransmitActive != 0U) {
|
||||
tx_num -= num;
|
||||
}
|
||||
if (tx_cnt > (DAP_UART_TX_BUFFER_SIZE - tx_num)) {
|
||||
tx_cnt = (DAP_UART_TX_BUFFER_SIZE - tx_num);
|
||||
}
|
||||
|
||||
index = UartTxIndexI & (DAP_UART_TX_BUFFER_SIZE - 1U);
|
||||
if ((index + tx_cnt) <= DAP_UART_TX_BUFFER_SIZE) {
|
||||
memcpy(&UartTxBuf[index], tx_data, tx_cnt);
|
||||
} else {
|
||||
num = DAP_UART_TX_BUFFER_SIZE - index;
|
||||
memcpy(&UartTxBuf[index], tx_data, num);
|
||||
memcpy(&UartTxBuf[0], &tx_data[num], tx_cnt - num);
|
||||
}
|
||||
UartTxIndexI += tx_cnt;
|
||||
|
||||
if (UartTransmitActive == 0U) {
|
||||
UART_Transmit();
|
||||
}
|
||||
|
||||
status = UART_Get_Status();
|
||||
}
|
||||
|
||||
*response++ = status;
|
||||
*response++ = (uint8_t)(tx_cnt >> 0);
|
||||
*response++ = (uint8_t)(tx_cnt >> 8);
|
||||
*response++ = (uint8_t)(rx_cnt >> 0);
|
||||
*response = (uint8_t)(rx_cnt >> 8);
|
||||
|
||||
return (((4U + tx_cnt) << 16) | (5U + rx_cnt));
|
||||
}
|
||||
|
||||
#endif /* DAP_UART */
|
|
@ -83,10 +83,12 @@ const uint8_t kUSBd0InterfaceDescriptor[]=
|
|||
USBD_CUSTOM_CLASS0_IF0_CLASS, // bInterfaceClass
|
||||
USBD_CUSTOM_CLASS0_IF0_SUBCLASS, // bInterfaceSubClass
|
||||
USBD_CUSTOM_CLASS0_IF0_PROTOCOL, // bInterfaceProtocol
|
||||
0x00, // iInterface
|
||||
/**
|
||||
* pyOCD identifies the debugger by USB interface name, which include substring "CMSIS-DAP".
|
||||
* See `strings_list` in `usb_handle.c`
|
||||
*/
|
||||
0x02, // iInterface
|
||||
// Index of string descriptor describing this interface
|
||||
////TODO: fix this 0x04 ?
|
||||
|
||||
|
||||
// Standard Endpoint Descriptor
|
||||
|
||||
|
|
|
@ -98,14 +98,14 @@ void handle_dap_data_request(usbip_stage2_header *header, uint32_t length)
|
|||
// Point to the beginning of the URB packet
|
||||
|
||||
#if (USE_WINUSB == 1)
|
||||
send_stage2_submit(header, 0, 0);
|
||||
send_stage2_submit_data_fast(header, NULL, 0);
|
||||
|
||||
// always send constant size buf -> cuz we don't care about the IN packet size
|
||||
// and to unify the style, we set aside the length of the section
|
||||
xRingbufferSend(dap_dataIN_handle, data_in - sizeof(uint32_t), DAP_HANDLE_SIZE, portMAX_DELAY);
|
||||
|
||||
#else
|
||||
send_stage2_submit(header, 0, 0);
|
||||
send_stage2_submit_data_fast(header, NULL, 0);
|
||||
|
||||
xRingbufferSend(dap_dataIN_handle, data_in, DAP_HANDLE_SIZE, portMAX_DELAY);
|
||||
|
||||
|
@ -116,22 +116,6 @@ void handle_dap_data_request(usbip_stage2_header *header, uint32_t length)
|
|||
// send_stage2_submit(header, 0, 0);
|
||||
}
|
||||
|
||||
void handle_dap_data_response(usbip_stage2_header *header)
|
||||
{
|
||||
return;
|
||||
// int resLength = dap_respond & 0xFFFF;
|
||||
// if (resLength)
|
||||
// {
|
||||
|
||||
// send_stage2_submit_data(header, 0, (void *)DAPDataProcessed.buf, resLength);
|
||||
// dap_respond = 0;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// send_stage2_submit(header, 0, 0);
|
||||
// }
|
||||
}
|
||||
|
||||
void handle_swo_trace_response(usbip_stage2_header *header)
|
||||
{
|
||||
#if (SWO_FUNCTION_ENABLE == 1)
|
||||
|
@ -247,54 +231,43 @@ void DAP_Thread(void *argument)
|
|||
}
|
||||
}
|
||||
|
||||
int fast_reply(uint8_t *buf, uint32_t length)
|
||||
int fast_reply(uint8_t *buf, uint32_t length, int dap_req_num)
|
||||
{
|
||||
usbip_stage2_header *buf_header = (usbip_stage2_header *)buf;
|
||||
if (length == 48 &&
|
||||
buf_header->base.command == PP_HTONL(USBIP_STAGE2_REQ_SUBMIT) &&
|
||||
buf_header->base.direction == PP_HTONL(USBIP_DIR_IN) &&
|
||||
buf_header->base.ep == PP_HTONL(1))
|
||||
{
|
||||
if (dap_respond > 0)
|
||||
{
|
||||
DapPacket_t *item;
|
||||
size_t packetSize = 0;
|
||||
item = (DapPacket_t *)xRingbufferReceiveUpTo(dap_dataOUT_handle, &packetSize,
|
||||
pdMS_TO_TICKS(10), DAP_HANDLE_SIZE);
|
||||
if (packetSize == DAP_HANDLE_SIZE)
|
||||
{
|
||||
|
||||
if (dap_req_num > 0) {
|
||||
DapPacket_t *item;
|
||||
size_t packetSize = 0;
|
||||
item = (DapPacket_t *)xRingbufferReceiveUpTo(dap_dataOUT_handle, &packetSize,
|
||||
portMAX_DELAY, DAP_HANDLE_SIZE);
|
||||
if (packetSize == DAP_HANDLE_SIZE) {
|
||||
#if (USE_WINUSB == 1)
|
||||
send_stage2_submit_data_fast((usbip_stage2_header *)buf, item->buf, item->length);
|
||||
send_stage2_submit_data_fast((usbip_stage2_header *)buf, item->buf, item->length);
|
||||
#else
|
||||
send_stage2_submit_data_fast((usbip_stage2_header *)buf, item->buf, DAP_HANDLE_SIZE);
|
||||
send_stage2_submit_data_fast((usbip_stage2_header *)buf, item->buf, DAP_HANDLE_SIZE);
|
||||
#endif
|
||||
|
||||
if (xSemaphoreTake(data_response_mux, portMAX_DELAY) == pdTRUE)
|
||||
{
|
||||
--dap_respond;
|
||||
xSemaphoreGive(data_response_mux);
|
||||
}
|
||||
if (xSemaphoreTake(data_response_mux, portMAX_DELAY) == pdTRUE) {
|
||||
--dap_respond;
|
||||
xSemaphoreGive(data_response_mux);
|
||||
}
|
||||
|
||||
vRingbufferReturnItem(dap_dataOUT_handle, (void *)item);
|
||||
return 1;
|
||||
}
|
||||
else if (packetSize > 0)
|
||||
{
|
||||
printf("Wrong data out packet size:%d!\r\n", packetSize);
|
||||
}
|
||||
////TODO: fast reply
|
||||
}
|
||||
else
|
||||
{
|
||||
buf_header->base.command = PP_HTONL(USBIP_STAGE2_RSP_SUBMIT);
|
||||
buf_header->base.direction = PP_HTONL(USBIP_DIR_OUT);
|
||||
buf_header->u.ret_submit.status = 0;
|
||||
buf_header->u.ret_submit.data_length = 0;
|
||||
buf_header->u.ret_submit.error_count = 0;
|
||||
usbip_network_send(kSock, buf, 48, 0);
|
||||
vRingbufferReturnItem(dap_dataOUT_handle, (void *)item);
|
||||
return 1;
|
||||
} else if (packetSize > 0) {
|
||||
printf("Wrong data out packet size:%d!\r\n", packetSize);
|
||||
}
|
||||
////TODO: fast reply
|
||||
} else {
|
||||
buf_header->base.command = PP_HTONL(USBIP_STAGE2_RSP_SUBMIT);
|
||||
buf_header->base.direction = PP_HTONL(USBIP_DIR_OUT);
|
||||
buf_header->u.ret_submit.status = 0;
|
||||
buf_header->u.ret_submit.data_length = 0;
|
||||
buf_header->u.ret_submit.error_count = 0;
|
||||
usbip_network_send(kSock, buf, 48, 0);
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,11 +11,10 @@ enum reset_handle_t
|
|||
};
|
||||
|
||||
void handle_dap_data_request(usbip_stage2_header *header, uint32_t length);
|
||||
void handle_dap_data_response(usbip_stage2_header *header);
|
||||
void handle_swo_trace_response(usbip_stage2_header *header);
|
||||
void handle_dap_unlink();
|
||||
|
||||
int fast_reply(uint8_t *buf, uint32_t length);
|
||||
int fast_reply(uint8_t *buf, uint32_t length, int dap_req_num);
|
||||
void DAP_Thread(void *argument);
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
#ifndef PROXY_SERVER_CONF_H_GUARD
|
||||
#define PROXY_SERVER_CONF_H_GUARD
|
||||
|
||||
#define DAP_PROXY_PORT 1234
|
||||
#define DAP_PROXY_PORT 3240
|
||||
|
||||
#endif //PROXY_SERVER_CONF_H_GUARD
|
|
@ -23,36 +23,39 @@
|
|||
|
||||
extern int kRestartDAPHandle;
|
||||
|
||||
uint8_t kState = ACCEPTING;
|
||||
int kSock = -1;
|
||||
|
||||
void tcp_server_task(void *pvParameters)
|
||||
{
|
||||
uint8_t tcp_rx_buffer[1500];
|
||||
char addr_str[128];
|
||||
enum usbip_server_state_t usbip_state = WAIT_DEVLIST;
|
||||
uint8_t *data;
|
||||
int addr_family;
|
||||
int ip_protocol;
|
||||
int header;
|
||||
int ret, sz;
|
||||
|
||||
int on = 1;
|
||||
while (1)
|
||||
{
|
||||
|
||||
#ifdef CONFIG_EXAMPLE_IPV4
|
||||
struct sockaddr_in destAddr;
|
||||
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
destAddr.sin_family = AF_INET;
|
||||
destAddr.sin_port = htons(PORT);
|
||||
addr_family = AF_INET;
|
||||
ip_protocol = IPPROTO_IP;
|
||||
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
||||
#else // IPV6
|
||||
struct sockaddr_in6 destAddr;
|
||||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
struct sockaddr_in6 destAddr;
|
||||
bzero(&destAddr.sin6_addr.un, sizeof(destAddr.sin6_addr.un));
|
||||
destAddr.sin6_family = AF_INET6;
|
||||
destAddr.sin6_port = htons(DAP_PROXY_PORT);
|
||||
addr_family = AF_INET6;
|
||||
ip_protocol = IPPROTO_IPV6;
|
||||
inet6_ntoa_r(destAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
#else // IPV6
|
||||
struct sockaddr_in destAddr;
|
||||
destAddr.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
destAddr.sin_family = AF_INET;
|
||||
destAddr.sin_port = htons(DAP_PROXY_PORT);
|
||||
addr_family = AF_INET;
|
||||
ip_protocol = IPPROTO_IP;
|
||||
inet_ntoa_r(destAddr.sin_addr, addr_str, sizeof(addr_str) - 1);
|
||||
#endif
|
||||
|
||||
int listen_sock = socket(addr_family, SOCK_STREAM, ip_protocol);
|
||||
|
@ -100,75 +103,39 @@ void tcp_server_task(void *pvParameters)
|
|||
setsockopt(kSock, IPPROTO_TCP, TCP_NODELAY, (void *)&on, sizeof(on));
|
||||
printf("Socket accepted\r\n");
|
||||
|
||||
while (1)
|
||||
{
|
||||
int len = recv(kSock, tcp_rx_buffer, sizeof(tcp_rx_buffer), 0);
|
||||
// Error occured during receiving
|
||||
if (len < 0)
|
||||
{
|
||||
printf("recv failed: errno %d\r\n", errno);
|
||||
break;
|
||||
}
|
||||
// Connection closed
|
||||
else if (len == 0)
|
||||
{
|
||||
printf("Connection closed\r\n");
|
||||
break;
|
||||
}
|
||||
// Data received
|
||||
// Read header
|
||||
sz = 4;
|
||||
data = &tcp_rx_buffer[0];
|
||||
do {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
goto cleanup;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
} while (sz > 0);
|
||||
|
||||
header = *((int *)(tcp_rx_buffer));
|
||||
header = ntohl(header);
|
||||
|
||||
if (header == EL_LINK_IDENTIFIER) {
|
||||
el_dap_work(tcp_rx_buffer, sizeof(tcp_rx_buffer));
|
||||
} else if ((header & 0xFFFF) == 0x8003 ||
|
||||
(header & 0xFFFF) == 0x8005) { // usbip OP_REQ_DEVLIST/OP_REQ_IMPORT
|
||||
if ((header & 0xFFFF) == 0x8005)
|
||||
usbip_state = WAIT_DEVLIST;
|
||||
else
|
||||
{
|
||||
// #ifdef CONFIG_EXAMPLE_IPV6
|
||||
// // Get the sender's ip address as string
|
||||
// if (sourceAddr.sin6_family == PF_INET)
|
||||
// {
|
||||
// inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
|
||||
// }
|
||||
// else if (sourceAddr.sin6_family == PF_INET6)
|
||||
// {
|
||||
// inet6_ntoa_r(sourceAddr.sin6_addr, addr_str, sizeof(addr_str) - 1);
|
||||
// }
|
||||
// #else
|
||||
// inet_ntoa_r(((struct sockaddr_in *)&sourceAddr)->sin_addr.s_addr, addr_str, sizeof(addr_str) - 1);
|
||||
// #endif
|
||||
|
||||
switch (kState)
|
||||
{
|
||||
case ACCEPTING:
|
||||
kState = ATTACHING;
|
||||
__attribute__((fallthrough));
|
||||
case ATTACHING:
|
||||
// elaphureLink handshake
|
||||
if (el_handshake_process(kSock, tcp_rx_buffer, len) == 0) {
|
||||
// handshake successed
|
||||
kState = EL_DATA_PHASE;
|
||||
kRestartDAPHandle = DELETE_HANDLE;
|
||||
el_process_buffer_malloc();
|
||||
break;
|
||||
}
|
||||
|
||||
attach(tcp_rx_buffer, len);
|
||||
break;
|
||||
|
||||
case EMULATING:
|
||||
emulate(tcp_rx_buffer, len);
|
||||
break;
|
||||
case EL_DATA_PHASE:
|
||||
el_dap_data_process(tcp_rx_buffer, len);
|
||||
break;
|
||||
default:
|
||||
printf("unkonw kstate!\r\n");
|
||||
}
|
||||
}
|
||||
usbip_state = WAIT_IMPORT;
|
||||
usbip_worker(tcp_rx_buffer, sizeof(tcp_rx_buffer), &usbip_state);
|
||||
} else {
|
||||
printf("Unknown protocol\n");
|
||||
}
|
||||
// kState = ACCEPTING;
|
||||
|
||||
cleanup:
|
||||
if (kSock != -1)
|
||||
{
|
||||
printf("Shutting down socket and restarting...\r\n");
|
||||
//shutdown(kSock, 0);
|
||||
close(kSock);
|
||||
if (kState == EMULATING || kState == EL_DATA_PHASE)
|
||||
kState = ACCEPTING;
|
||||
|
||||
// Restart DAP Handle
|
||||
el_process_buffer_free();
|
||||
|
|
|
@ -10,6 +10,13 @@
|
|||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
|
||||
#ifndef likely
|
||||
#define likely(x) __builtin_expect(!!(x), 1)
|
||||
#endif
|
||||
|
||||
#ifndef unlikely
|
||||
#define unlikely(x) __builtin_expect(!!(x), 0)
|
||||
#endif
|
||||
|
||||
// attach helper function
|
||||
static int read_stage1_command(uint8_t *buffer, uint32_t length);
|
||||
|
@ -23,8 +30,6 @@ static void send_interface_info();
|
|||
// emulate helper function
|
||||
static void pack(void *data, int size);
|
||||
static void unpack(void *data, int size);
|
||||
static int handle_submit(usbip_stage2_header *header, uint32_t length);
|
||||
static int read_stage2_command(usbip_stage2_header *header, uint32_t length);
|
||||
|
||||
static void handle_unlink(usbip_stage2_header *header);
|
||||
// unlink helper function
|
||||
|
@ -34,7 +39,7 @@ int usbip_network_send(int s, const void *dataptr, size_t size, int flags) {
|
|||
return send(s, dataptr, size, flags);
|
||||
}
|
||||
|
||||
int attach(uint8_t *buffer, uint32_t length)
|
||||
static int attach(uint8_t *buffer, uint32_t length)
|
||||
{
|
||||
int command = read_stage1_command(buffer, length);
|
||||
if (command < 0)
|
||||
|
@ -91,8 +96,6 @@ static void handle_device_attach(uint8_t *buffer, uint32_t length)
|
|||
send_stage1_header(USBIP_STAGE1_CMD_DEVICE_ATTACH, 0);
|
||||
|
||||
send_device_info();
|
||||
|
||||
kState = EMULATING;
|
||||
}
|
||||
|
||||
static void send_stage1_header(uint16_t command, uint32_t status)
|
||||
|
@ -170,57 +173,126 @@ static void send_interface_info()
|
|||
usbip_network_send(kSock, (uint8_t *)&interface, sizeof(usbip_stage1_usb_interface), 0);
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int emulate(uint8_t *buffer, uint32_t length)
|
||||
static int usbip_urb_process(uint8_t *base, uint32_t length)
|
||||
{
|
||||
usbip_stage2_header *header = (usbip_stage2_header *)base;
|
||||
uint8_t *data;
|
||||
uint32_t command, dir, ep;
|
||||
uint32_t unlink_count = 0;
|
||||
bool may_has_data;
|
||||
int sz, ret;
|
||||
int dap_req_num = 0;
|
||||
|
||||
if(fast_reply(buffer, length))
|
||||
{
|
||||
return 0;
|
||||
while (1) {
|
||||
// header
|
||||
data = base;
|
||||
sz = 48; // for USBIP_CMD_SUBMIT/USBIP_CMD_UNLINK
|
||||
do {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
} while (sz > 0);
|
||||
|
||||
command = ntohl(header->base.command);
|
||||
dir = ntohl(header->base.direction);
|
||||
ep = ntohl(header->base.ep);
|
||||
may_has_data = (command == USBIP_STAGE2_REQ_SUBMIT && dir == USBIP_DIR_OUT);
|
||||
sz = may_has_data ? ntohl(header->u.cmd_submit.data_length) : 0;
|
||||
|
||||
while (sz) {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
goto out;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
}
|
||||
|
||||
if (likely(command == USBIP_STAGE2_REQ_SUBMIT)) {
|
||||
if (likely(ep == 1 && dir == USBIP_DIR_IN)) {
|
||||
fast_reply(base, sizeof(usbip_stage2_header), dap_req_num);
|
||||
if (dap_req_num > 0)
|
||||
dap_req_num--;
|
||||
} else if (likely(ep == 1 && dir == USBIP_DIR_OUT)) {
|
||||
dap_req_num++;
|
||||
handle_dap_data_request(header, length);
|
||||
} else if (ep == 0) {
|
||||
unpack(base, sizeof(usbip_stage2_header));
|
||||
handleUSBControlRequest(header);
|
||||
} else {
|
||||
// ep3 reserved for SWO
|
||||
printf("ep reserved:%lu\r\n", ep);
|
||||
send_stage2_submit(header, 0, 0);
|
||||
}
|
||||
} else if (command == USBIP_STAGE2_REQ_UNLINK) {
|
||||
if (unlink_count == 0 || unlink_count % 100 == 0)
|
||||
printf("unlink\r\n");
|
||||
unlink_count++;
|
||||
unpack(base, sizeof(usbip_stage2_header));
|
||||
handle_unlink(header);
|
||||
} else {
|
||||
printf("emulate unknown command:%lu\r\n", command);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
int command = read_stage2_command((usbip_stage2_header *)buffer, length);
|
||||
if (command < 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case USBIP_STAGE2_REQ_SUBMIT:
|
||||
handle_submit((usbip_stage2_header *)buffer , length);
|
||||
break;
|
||||
|
||||
case USBIP_STAGE2_REQ_UNLINK:
|
||||
handle_unlink((usbip_stage2_header *)buffer);
|
||||
break;
|
||||
|
||||
default:
|
||||
printf("emulate unknown command:%d\r\n", command);
|
||||
//handle_submit((usbip_stage2_header *)buffer, length);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
out:
|
||||
if (ret < 0)
|
||||
printf("recv failed: errno %d\r\n", errno);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int read_stage2_command(usbip_stage2_header *header, uint32_t length)
|
||||
int usbip_worker(uint8_t *base, uint32_t length, enum usbip_server_state_t *state)
|
||||
{
|
||||
if (length < sizeof(usbip_stage2_header))
|
||||
{
|
||||
return -1;
|
||||
uint8_t *data;
|
||||
int pre_read_sz = 4;
|
||||
int sz, ret;
|
||||
|
||||
// OP_REQ_DEVLIST status field
|
||||
if (*state == WAIT_DEVLIST) {
|
||||
data = base + 4;
|
||||
sz = 8 - pre_read_sz;
|
||||
do {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
} while (sz > 0);
|
||||
|
||||
ret = attach(base, 8);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
pre_read_sz = 0;
|
||||
}
|
||||
|
||||
//client.readBytes((uint8_t *)&header, sizeof(usbip_stage2_header));
|
||||
unpack((uint32_t *)header, sizeof(usbip_stage2_header));
|
||||
return header->base.command;
|
||||
*state = WAIT_IMPORT;
|
||||
// OP_REQ_IMPORT
|
||||
data = base + pre_read_sz;
|
||||
sz = 40 - pre_read_sz;
|
||||
do {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
} while (sz > 0);
|
||||
|
||||
ret = attach(base, 40);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
// URB process
|
||||
*state = WAIT_URB;
|
||||
ret = usbip_urb_process(base, length);
|
||||
if (ret) {
|
||||
*state = WAIT_DEVLIST;
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -270,65 +342,6 @@ static void unpack(void *data, int size)
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB transaction processing
|
||||
*
|
||||
*/
|
||||
static int handle_submit(usbip_stage2_header *header, uint32_t length)
|
||||
{
|
||||
switch (header->base.ep)
|
||||
{
|
||||
// control endpoint(endpoint 0)
|
||||
case 0x00:
|
||||
//// TODO: judge usb setup 8 byte?
|
||||
handleUSBControlRequest(header);
|
||||
break;
|
||||
|
||||
// endpoint 1 data receicve and response
|
||||
case 0x01:
|
||||
if (header->base.direction == 0)
|
||||
{
|
||||
//os_printf("EP 01 DATA FROM HOST");
|
||||
handle_dap_data_request(header ,length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// os_printf("EP 01 DATA TO HOST\r\n");
|
||||
handle_dap_data_response(header);
|
||||
}
|
||||
break;
|
||||
// endpoint 2 for SWO trace
|
||||
case 0x02:
|
||||
if (header->base.direction == 0)
|
||||
{
|
||||
// os_printf("EP 02 DATA FROM HOST");
|
||||
send_stage2_submit(header, 0, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
// os_printf("EP 02 DATA TO HOST");
|
||||
handle_swo_trace_response(header);
|
||||
}
|
||||
break;
|
||||
// request to save data to device
|
||||
case 0x81:
|
||||
if (header->base.direction == 0)
|
||||
{
|
||||
printf("*** WARN! EP 81 DATA TX");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("*** WARN! EP 81 DATA RX");
|
||||
}
|
||||
return -1;
|
||||
|
||||
default:
|
||||
printf("*** WARN ! UNKNOWN ENDPOINT: %d\r\n", (int) header->base.ep);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void send_stage2_submit(usbip_stage2_header *req_header, int32_t status, int32_t data_length)
|
||||
{
|
||||
|
||||
|
@ -365,9 +378,9 @@ void send_stage2_submit_data_fast(usbip_stage2_header *req_header, const void *c
|
|||
memset(&(req_header->u.ret_submit), 0, sizeof(usbip_stage2_header_ret_submit));
|
||||
req_header->u.ret_submit.data_length = htonl(data_length);
|
||||
|
||||
|
||||
// payload
|
||||
memcpy(&send_buf[sizeof(usbip_stage2_header)], data, data_length);
|
||||
if (data)
|
||||
memcpy(&send_buf[sizeof(usbip_stage2_header)], data, data_length);
|
||||
usbip_network_send(kSock, send_buf, sizeof(usbip_stage2_header) + data_length, 0);
|
||||
}
|
||||
|
||||
|
@ -396,4 +409,4 @@ static void send_stage2_unlink(usbip_stage2_header *req_header)
|
|||
pack(req_header, sizeof(usbip_stage2_header));
|
||||
|
||||
usbip_network_send(kSock, req_header, sizeof(usbip_stage2_header), 0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,18 +5,16 @@
|
|||
|
||||
#include "components/USBIP/usbip_defs.h"
|
||||
|
||||
enum state_t
|
||||
enum usbip_server_state_t
|
||||
{
|
||||
ACCEPTING,
|
||||
ATTACHING,
|
||||
EMULATING,
|
||||
EL_DATA_PHASE
|
||||
WAIT_DEVLIST = 0,
|
||||
WAIT_IMPORT,
|
||||
WAIT_URB,
|
||||
};
|
||||
extern uint8_t kState;
|
||||
|
||||
extern int kSock;
|
||||
|
||||
int attach(uint8_t *buffer, uint32_t length);
|
||||
int emulate(uint8_t *buffer, uint32_t length);
|
||||
int usbip_worker(uint8_t *base, uint32_t length, enum usbip_server_state_t *state);
|
||||
void send_stage2_submit_data(usbip_stage2_header *req_header, int32_t status, const void * const data, int32_t data_length);
|
||||
void send_stage2_submit(usbip_stage2_header *req_header, int32_t status, int32_t data_length);
|
||||
void send_stage2_submit_data_fast(usbip_stage2_header *req_header, const void *const data, int32_t data_length);
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
set(COMPONENT_ADD_INCLUDEDIRS ".")
|
||||
set(COMPONENT_SRCS "./elaphureLink_protocol.c")
|
||||
file(GLOB SOURCES *.c)
|
||||
|
||||
register_component()
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES dap_proxy
|
||||
)
|
|
@ -1,7 +1,13 @@
|
|||
#include "components/elaphureLink/elaphureLink_protocol.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "DAP_handle.h"
|
||||
|
||||
#include "lwip/err.h"
|
||||
#include "lwip/sockets.h"
|
||||
#include "lwip/sys.h"
|
||||
#include <lwip/netdb.h>
|
||||
|
||||
extern int kRestartDAPHandle;
|
||||
extern int kSock;
|
||||
extern int usbip_network_send(int s, const void *dataptr, size_t size, int flags);
|
||||
|
||||
|
@ -21,7 +27,6 @@ void el_process_buffer_malloc() {
|
|||
el_process_buffer = malloc(1500);
|
||||
}
|
||||
|
||||
|
||||
void el_process_buffer_free() {
|
||||
if (el_process_buffer != NULL) {
|
||||
free(el_process_buffer);
|
||||
|
@ -29,7 +34,6 @@ void el_process_buffer_free() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
int el_handshake_process(int fd, void *buffer, size_t len) {
|
||||
if (len != sizeof(el_request_handshake)) {
|
||||
return -1;
|
||||
|
@ -55,10 +59,42 @@ int el_handshake_process(int fd, void *buffer, size_t len) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void el_dap_data_process(void* buffer, size_t len) {
|
||||
int res = DAP_ExecuteCommand(buffer, (uint8_t *)el_process_buffer);
|
||||
res &= 0xFFFF;
|
||||
|
||||
usbip_network_send(kSock, el_process_buffer, res, 0);
|
||||
}
|
||||
|
||||
int el_dap_work(uint8_t* base, size_t len)
|
||||
{
|
||||
uint8_t *data;
|
||||
int sz, ret;
|
||||
|
||||
// read command code and protocol version
|
||||
data = base + 4;
|
||||
sz = 8;
|
||||
do {
|
||||
ret = recv(kSock, data, sz, 0);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
sz -= ret;
|
||||
data += ret;
|
||||
} while (sz > 0);
|
||||
|
||||
ret = el_handshake_process(kSock, base, 12);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
kRestartDAPHandle = DELETE_HANDLE;
|
||||
el_process_buffer_malloc();
|
||||
// data process
|
||||
while(1) {
|
||||
ret = recv(kSock, base, len, 0);
|
||||
if (ret <= 0)
|
||||
return ret;
|
||||
el_dap_data_process(base, ret);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,8 @@ int el_handshake_process(int fd, void* buffer, size_t len);
|
|||
void el_dap_data_process(void* buffer, size_t len);
|
||||
|
||||
|
||||
int el_dap_work(uint8_t* base, size_t len);
|
||||
|
||||
void el_process_buffer_malloc();
|
||||
void el_process_buffer_free();
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
file(GLOB SOURCES *.c)
|
||||
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
)
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: Copyright (c) 2009, Jouni Malinen <j@w1.fi>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef LIST_H_GUARD
|
||||
#define LIST_H_GUARD
|
||||
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* struct dl_list - Doubly-linked list
|
||||
*/
|
||||
struct dl_list {
|
||||
struct dl_list *next;
|
||||
struct dl_list *prev;
|
||||
};
|
||||
|
||||
static inline void dl_list_init(struct dl_list *list)
|
||||
{
|
||||
list->next = list;
|
||||
list->prev = list;
|
||||
}
|
||||
|
||||
static inline void dl_list_add(struct dl_list *list, struct dl_list *item)
|
||||
{
|
||||
item->next = list->next;
|
||||
item->prev = list;
|
||||
list->next->prev = item;
|
||||
list->next = item;
|
||||
}
|
||||
|
||||
static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item)
|
||||
{
|
||||
dl_list_add(list->prev, item);
|
||||
}
|
||||
|
||||
static inline void dl_list_del(struct dl_list *item)
|
||||
{
|
||||
item->next->prev = item->prev;
|
||||
item->prev->next = item->next;
|
||||
item->next = NULL;
|
||||
item->prev = NULL;
|
||||
}
|
||||
|
||||
static inline int dl_list_empty(struct dl_list *list)
|
||||
{
|
||||
return list->next == list;
|
||||
}
|
||||
|
||||
static inline unsigned int dl_list_len(struct dl_list *list)
|
||||
{
|
||||
struct dl_list *item;
|
||||
int count = 0;
|
||||
for (item = list->next; item != list; item = item->next)
|
||||
count++;
|
||||
return count;
|
||||
}
|
||||
|
||||
#ifndef offsetof
|
||||
#define offsetof(type, member) ((long) &((type *) 0)->member)
|
||||
#endif
|
||||
|
||||
#define dl_list_entry(item, type, member) \
|
||||
((type *) ((char *) item - offsetof(type, member)))
|
||||
|
||||
#define dl_list_first(list, type, member) \
|
||||
(dl_list_empty((list)) ? NULL : \
|
||||
dl_list_entry((list)->next, type, member))
|
||||
|
||||
#define dl_list_last(list, type, member) \
|
||||
(dl_list_empty((list)) ? NULL : \
|
||||
dl_list_entry((list)->prev, type, member))
|
||||
|
||||
#define dl_list_for_each(item, list, type, member) \
|
||||
for (item = dl_list_entry((list)->next, type, member); \
|
||||
&item->member != (list); \
|
||||
item = dl_list_entry(item->member.next, type, member))
|
||||
|
||||
#define dl_list_for_each_safe(item, n, list, type, member) \
|
||||
for (item = dl_list_entry((list)->next, type, member), \
|
||||
n = dl_list_entry(item->member.next, type, member); \
|
||||
&item->member != (list); \
|
||||
item = n, n = dl_list_entry(n->member.next, type, member))
|
||||
|
||||
#define dl_list_for_each_reverse(item, list, type, member) \
|
||||
for (item = dl_list_entry((list)->prev, type, member); \
|
||||
&item->member != (list); \
|
||||
item = dl_list_entry(item->member.prev, type, member))
|
||||
|
||||
#define DEFINE_DL_LIST(name) \
|
||||
struct dl_list name = { &(name), &(name) }
|
||||
|
||||
#endif //LIST_H_GUARD
|
|
@ -10,12 +10,18 @@
|
|||
#include "wt_storage.h"
|
||||
#include "wifi_manager.h"
|
||||
#include "web_server.h"
|
||||
#include "static_buffer.h"
|
||||
#include "request_runner.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
void app_main()
|
||||
{
|
||||
assert(static_buffer_init() == 0);
|
||||
assert(request_runner_init() == 0);
|
||||
wt_storage_init();
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
ESP_ERROR_CHECK(esp_netif_init());
|
||||
ESP_ERROR_CHECK(esp_event_loop_create_default());
|
||||
|
||||
wifi_manager_init();
|
||||
DAP_Setup();
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# Name , Type , SubType , Offset , Size , Flags
|
||||
# 1st stage ROM ,data, ,
|
||||
# 2nd stage boot ,data,0x1000,
|
||||
# partition table,data,0xF000,4K(0x1000),
|
||||
begin ,0x40,0x00 , ,0k ,
|
||||
nvs ,data,nvs ,0x10000,16K ,
|
||||
phy_init ,data,phy , ,4K ,
|
||||
none0 ,0x40,0x00 , ,0k ,
|
||||
ota_0 ,app ,ota_0 ,0x20000,0x1B0000,
|
||||
wt_nvs ,data,nvs , ,64K ,
|
|
|
@ -5,5 +5,5 @@ file(GLOB SOURCES *
|
|||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES json
|
||||
REQUIRES json request_runner
|
||||
)
|
||||
|
|
|
@ -33,6 +33,7 @@ int api_json_module_add(api_json_init_func func)
|
|||
err = func(&api_module);
|
||||
if (err) {
|
||||
printf("module %p init failed\n", func);
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (api_module.module_id >= API_MODULE_MAX) {
|
||||
|
@ -51,10 +52,10 @@ int api_json_module_add(api_json_init_func func)
|
|||
return 0;
|
||||
}
|
||||
|
||||
int api_json_module_call(uint8_t id, uint16_t cmd, api_json_req_t *in, api_json_resp_t *out)
|
||||
int api_json_module_call(uint8_t id, uint16_t cmd, api_json_req_t *in, api_json_module_async_t *out)
|
||||
{
|
||||
if (unlikely(id >= API_MODULE_MAX || module_arr[id].on_req == NULL)) {
|
||||
return 1;
|
||||
return API_JSON_BAD_REQUEST;
|
||||
}
|
||||
|
||||
return module_arr[id].on_req(cmd, in, out);
|
||||
|
|
|
@ -1,18 +1,33 @@
|
|||
#ifndef API_JSON_MODULE_H_GUARD
|
||||
#define API_JSON_MODULE_H_GUARD
|
||||
|
||||
#include "request_runner.h"
|
||||
#include <cJSON.h>
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct api_json_req_t {
|
||||
cJSON *json;
|
||||
cJSON *in;
|
||||
cJSON *out;
|
||||
} api_json_req_t;
|
||||
|
||||
typedef struct api_json_resp_t {
|
||||
cJSON *json;
|
||||
} api_json_resp_t;
|
||||
typedef struct api_json_module_req_t {
|
||||
int (*func)(api_json_req_t *req);
|
||||
void *arg; /* request context (=api_json_req) */
|
||||
} api_json_module_req_t;
|
||||
|
||||
typedef int (*api_json_on_req)(uint16_t cmd, api_json_req_t *req, api_json_resp_t *rsp);
|
||||
typedef struct api_json_module_async_t {
|
||||
api_json_module_req_t module;
|
||||
req_task_cb_t req_task;
|
||||
} api_json_module_async_t;
|
||||
|
||||
|
||||
typedef enum api_json_req_status_e {
|
||||
API_JSON_OK = 0,
|
||||
API_JSON_ASYNC = 1,
|
||||
API_JSON_BAD_REQUEST = 2,
|
||||
} api_json_req_status_e;
|
||||
|
||||
typedef int (*api_json_on_req)(uint16_t cmd, api_json_req_t *req, api_json_module_async_t *rsp);
|
||||
|
||||
typedef struct api_json_module_cfg_t {
|
||||
api_json_on_req on_req;
|
||||
|
@ -32,6 +47,6 @@ int api_json_module_add(api_json_init_func);
|
|||
__attribute__((used, constructor(PRI))) void cons_ ## INIT(); \
|
||||
void cons_ ## INIT() { api_json_module_add(INIT); }
|
||||
|
||||
int api_json_module_call(uint8_t id, uint16_t cmd, api_json_req_t *in, api_json_resp_t *out);
|
||||
int api_json_module_call(uint8_t id, uint16_t cmd, api_json_req_t *in, api_json_module_async_t *out);
|
||||
|
||||
#endif //API_JSON_MODULE_H_GUARD
|
||||
|
|
|
@ -3,35 +3,37 @@
|
|||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <esp_compiler.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#define TAG __FILENAME__
|
||||
|
||||
int api_json_router_init()
|
||||
{
|
||||
api_json_module_dump();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int api_json_route(api_json_req_t *req, api_json_resp_t *rsp)
|
||||
int api_json_route(api_json_req_t *req, api_json_module_async_t *rsp)
|
||||
{
|
||||
uint16_t cmd;
|
||||
uint8_t module_id;
|
||||
cJSON *cmd_json;
|
||||
cJSON *module_json;
|
||||
|
||||
if (unlikely(req->json == NULL)) {
|
||||
return 1;
|
||||
if (unlikely(req == NULL)) {
|
||||
return API_JSON_BAD_REQUEST;
|
||||
}
|
||||
|
||||
cmd_json = cJSON_GetObjectItem(req->json, "cmd");
|
||||
module_json = cJSON_GetObjectItem(req->json, "module");
|
||||
cmd_json = cJSON_GetObjectItem(req->in, "cmd");
|
||||
module_json = cJSON_GetObjectItem(req->in, "module");
|
||||
|
||||
if (!cJSON_IsNumber(cmd_json) || !cJSON_IsNumber(module_json)) {
|
||||
return 1;
|
||||
return API_JSON_BAD_REQUEST;
|
||||
}
|
||||
|
||||
cmd = cmd_json->valueint;
|
||||
module_id = module_json->valueint;
|
||||
|
||||
printf("cmd %d received\n", cmd);
|
||||
ESP_LOGI(TAG, "cmd %d received\n", cmd);
|
||||
|
||||
return api_json_module_call(module_id, cmd, req, rsp);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
|
||||
int api_json_router_init();
|
||||
|
||||
int api_json_route(api_json_req_t *req, api_json_resp_t *out);
|
||||
int api_json_route(api_json_req_t *req, api_json_module_async_t *out);
|
||||
|
||||
#endif //API_JSON_ROUTER_H_GUARD
|
|
@ -0,0 +1,7 @@
|
|||
file(GLOB HTML_FILES
|
||||
*.gz
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
EMBED_FILES ${HTML_FILES}
|
||||
)
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,9 @@
|
|||
file(GLOB SOURCES
|
||||
*.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES
|
||||
)
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 kerms
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "request_runner.h"
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/task.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
|
||||
static QueueHandle_t long_run_queue = NULL;
|
||||
static QueueHandle_t send_out_queue = NULL;
|
||||
|
||||
_Noreturn static void req_long_task(void *arg);
|
||||
|
||||
_Noreturn static void req_send_out_task(void *arg);
|
||||
|
||||
_Noreturn void req_long_task(void *arg)
|
||||
{
|
||||
req_task_cb_t *req;
|
||||
while (1) {
|
||||
if (unlikely(xQueueReceive(long_run_queue, &req, portMAX_DELAY) != pdTRUE)) {
|
||||
continue;
|
||||
}
|
||||
req->status = req->module.helper_cb(req->module.arg);
|
||||
|
||||
/* if send out queue is busy, set status and let the cb to cancel send out
|
||||
* */
|
||||
if (req_queue_push_send_out(req, pdMS_TO_TICKS(20)) != 0) {
|
||||
req->status = -1;
|
||||
req->send_out.cb(req->send_out.arg, req->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Noreturn void req_send_out_task(void *arg)
|
||||
{
|
||||
req_task_cb_t *req;
|
||||
while (1) {
|
||||
if (likely(xQueueReceive(send_out_queue, &req, portMAX_DELAY))) {
|
||||
req->send_out.cb(req->send_out.arg, req->status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int request_runner_init()
|
||||
{
|
||||
BaseType_t res;
|
||||
if (long_run_queue != NULL || send_out_queue != NULL) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
long_run_queue = xQueueCreate(2, sizeof(req_task_cb_t *));
|
||||
send_out_queue = xQueueCreate(4, sizeof(req_task_cb_t *));
|
||||
assert(long_run_queue != NULL && send_out_queue != NULL);
|
||||
|
||||
res = xTaskCreate(req_long_task, "Ltask", 4 * 1024, NULL, 8, NULL);
|
||||
res &= xTaskCreate(req_send_out_task, "send out task", 4 * 1024, NULL, 9, NULL);
|
||||
assert(res == pdPASS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int req_queue_push_long_run(req_task_cb_t *req, uint32_t delay)
|
||||
{
|
||||
if (unlikely(xQueueSend(long_run_queue, &req, delay) != pdTRUE)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int req_queue_push_send_out(req_task_cb_t *req, uint32_t delay)
|
||||
{
|
||||
if (unlikely(xQueueSend(send_out_queue, &req, delay) != pdTRUE)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 kerms
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef REQUEST_RUNNER_H_GUARD
|
||||
#define REQUEST_RUNNER_H_GUARD
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct req_send_out_cb_t {
|
||||
void (*cb)(void *arg, int status);
|
||||
void *arg; /* socket info */
|
||||
} req_send_out_cb_t;
|
||||
|
||||
typedef struct req_module_cb_t {
|
||||
int (*helper_cb)(void *arg);
|
||||
void *arg;
|
||||
} req_module_cb_t;
|
||||
|
||||
typedef struct req_task_cb_t {
|
||||
req_module_cb_t module;
|
||||
req_send_out_cb_t send_out;
|
||||
int status;
|
||||
} req_task_cb_t;
|
||||
|
||||
int request_runner_init();
|
||||
|
||||
int req_queue_push_long_run(req_task_cb_t *req, uint32_t delay);
|
||||
int req_queue_push_send_out(req_task_cb_t *req, uint32_t delay);
|
||||
|
||||
|
||||
#endif //REQUEST_RUNNER_H_GUARD
|
|
@ -0,0 +1,9 @@
|
|||
file(GLOB SOURCES
|
||||
*.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES
|
||||
)
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 kerms
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "static_buffer.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#define BUFFER_NR 4
|
||||
#define BUFFER_SZ 2048
|
||||
|
||||
static uint8_t buf[BUFFER_NR][BUFFER_SZ];
|
||||
static QueueHandle_t buf_queue = NULL;
|
||||
|
||||
int static_buffer_init()
|
||||
{
|
||||
if (buf_queue != NULL)
|
||||
return 0;
|
||||
|
||||
buf_queue = xQueueCreate(BUFFER_NR, sizeof(void *));
|
||||
if (buf_queue == NULL) {
|
||||
return 1;
|
||||
}
|
||||
for (int i = 0; i < BUFFER_NR; ++i) {
|
||||
uint8_t *buf_ptr = buf[i];
|
||||
if (xQueueSend(buf_queue, &buf_ptr, 0) != pdTRUE) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void *static_buffer_get(uint32_t tick_wait)
|
||||
{
|
||||
void *ptr = NULL;
|
||||
xQueueReceive(buf_queue, &ptr, tick_wait);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void static_buffer_put(void *ptr)
|
||||
{
|
||||
//printf("put buf %d\n", uxQueueMessagesWaiting(buf_queue));
|
||||
if (unlikely(xQueueSend(buf_queue, &ptr, 0) != pdTRUE)) {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t static_buffer_get_buf_size()
|
||||
{
|
||||
return BUFFER_SZ;
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 kerms
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef STATIC_BUFFER_H_GUARD
|
||||
#define STATIC_BUFFER_H_GUARD
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
int static_buffer_init();
|
||||
|
||||
void *static_buffer_get(uint32_t tick_wait);
|
||||
|
||||
void static_buffer_put(void *ptr);
|
||||
|
||||
uint32_t static_buffer_get_buf_size();
|
||||
|
||||
|
||||
#endif //STATIC_BUFFER_H_GUARD
|
|
@ -1,14 +1,16 @@
|
|||
file(GLOB SOURCES
|
||||
web_server.c
|
||||
web_uri_module.c
|
||||
uri_modules/uri_html_base.c
|
||||
uri_modules/uri_ws.c
|
||||
uri_modules/uri_api.c
|
||||
uri_modules/uri_html_base.c
|
||||
)
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
REQUIRES esp_http_server api_router json
|
||||
REQUIRES esp_http_server
|
||||
PRIV_REQUIRES request_runner api_router json static_buffer utils html
|
||||
)
|
||||
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include "web_uri_module.h"
|
||||
#include "api_json_router.h"
|
||||
#include "request_runner.h"
|
||||
#include "static_buffer.h"
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_log.h>
|
||||
|
@ -8,65 +10,135 @@
|
|||
|
||||
#define TAG __FILE_NAME__
|
||||
|
||||
static char buf[2048];
|
||||
static api_json_req_t json_in;
|
||||
static api_json_resp_t json_out;
|
||||
#define HTTPD_STATUS_503 "503 Busy"
|
||||
|
||||
typedef struct post_request_t {
|
||||
api_json_req_t json;
|
||||
api_json_module_async_t async;
|
||||
httpd_req_t *req_out;
|
||||
char buf[0];
|
||||
} post_request_t;
|
||||
|
||||
static void async_send_out_cb(void *arg, int module_status);
|
||||
static int uri_api_send_out(httpd_req_t *req, post_request_t *post_req, int err);
|
||||
|
||||
|
||||
static esp_err_t api_post_handler(httpd_req_t *req)
|
||||
{
|
||||
uint32_t buf_len;
|
||||
int data_len;
|
||||
int err;
|
||||
post_request_t *post_req;
|
||||
char *buf;
|
||||
uint32_t remaining = req->content_len;
|
||||
|
||||
data_len = httpd_req_recv(req, buf, MIN(remaining, sizeof(buf)));
|
||||
if (unlikely(data_len <= 0)) {
|
||||
ESP_LOGE(TAG, "httpd recv error");
|
||||
buf_len = static_buffer_get_buf_size() - sizeof(post_request_t);
|
||||
if (unlikely(buf_len < remaining)) {
|
||||
ESP_LOGE(TAG, "req size %lu > buf_len %lu", remaining, buf_len);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
|
||||
post_req = static_buffer_get(pdMS_TO_TICKS(20));
|
||||
if (unlikely(post_req == NULL)) {
|
||||
ESP_LOGE(TAG, "static buf busy");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
buf = post_req->buf;
|
||||
|
||||
data_len = httpd_req_recv(req, buf, buf_len);
|
||||
if (unlikely(data_len <= 0)) {
|
||||
ESP_LOGE(TAG, "httpd recv error");
|
||||
err = ESP_FAIL;
|
||||
goto put_buf;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "=========== RECEIVED DATA ========== %d", httpd_req_to_sockfd(req));
|
||||
ESP_LOGI(TAG, "%.*s", data_len, buf);
|
||||
ESP_LOGI(TAG, "====================================");
|
||||
ESP_LOGI(TAG, "heap min: %lu, cur: %lu", esp_get_minimum_free_heap_size(), esp_get_free_heap_size());
|
||||
|
||||
/* Decode */
|
||||
json_in.json = cJSON_ParseWithLength(buf, data_len);
|
||||
if (unlikely(json_in.json == NULL)) {
|
||||
post_req->json.in = cJSON_ParseWithLength(buf, data_len);
|
||||
if (unlikely(post_req->json.in == NULL)) {
|
||||
httpd_resp_set_status(req, HTTPD_400);
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
return ESP_OK;
|
||||
goto end;
|
||||
}
|
||||
|
||||
err = api_json_route(&json_in, &json_out);
|
||||
if (err) {
|
||||
httpd_resp_set_status(req, HTTPD_400);
|
||||
httpd_resp_send(req, NULL, 0);
|
||||
post_req->json.out = NULL;
|
||||
err = api_json_route(&post_req->json, &post_req->async);
|
||||
if (err == API_JSON_ASYNC) {
|
||||
httpd_req_async_handler_begin(req, &post_req->req_out);
|
||||
post_req->async.req_task.send_out.cb = async_send_out_cb;
|
||||
post_req->async.req_task.send_out.arg = post_req;
|
||||
if (req_queue_push_long_run(&post_req->async.req_task, pdMS_TO_TICKS(20))) {
|
||||
/* queued failed */
|
||||
httpd_req_async_handler_complete(post_req->req_out);
|
||||
httpd_resp_set_status(req, HTTPD_STATUS_503);
|
||||
goto end;
|
||||
}
|
||||
return ESP_OK;
|
||||
} else if (unlikely(err != API_JSON_OK)) {
|
||||
httpd_resp_set_status(req, HTTPD_400);
|
||||
goto end;
|
||||
}
|
||||
cJSON_Delete(json_in.json);
|
||||
|
||||
if (json_out.json == NULL) {
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
if (post_req->json.out == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* api function returns something, send back to http client */
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_JSON);
|
||||
err = !cJSON_PrintPreallocated(json_out.json, buf, sizeof(buf) - 5, 0);
|
||||
cJSON_Delete(json_out.json);
|
||||
json_out.json = NULL;
|
||||
err = uri_api_send_out(req, post_req, 0);
|
||||
goto put_buf;
|
||||
|
||||
if (err) {
|
||||
end:
|
||||
cJSON_Delete(post_req->json.in);
|
||||
err = httpd_resp_send(req, NULL, 0);
|
||||
if (unlikely(err)) {
|
||||
ESP_LOGE(TAG, "resp_send err: %s", esp_err_to_name(err));
|
||||
}
|
||||
put_buf:
|
||||
static_buffer_put(post_req);
|
||||
return err;
|
||||
}
|
||||
|
||||
int uri_api_send_out(httpd_req_t *req, post_request_t *post_req, int err)
|
||||
{
|
||||
char *buf;
|
||||
uint32_t buf_len;
|
||||
|
||||
buf = post_req->buf;
|
||||
buf_len = static_buffer_get_buf_size() - sizeof(post_request_t);
|
||||
cJSON_Delete(post_req->json.in);
|
||||
if (post_req->json.out) {
|
||||
ESP_LOGI(TAG, "json out ok");
|
||||
httpd_resp_set_type(req, HTTPD_TYPE_JSON);
|
||||
err = !cJSON_PrintPreallocated(post_req->json.out, buf, buf_len - 5, 0);
|
||||
cJSON_Delete(post_req->json.out);
|
||||
}
|
||||
|
||||
if (unlikely(err)) {
|
||||
httpd_resp_set_status(req, HTTPD_500);
|
||||
return httpd_resp_send(req, NULL, 0);
|
||||
}
|
||||
|
||||
err = httpd_resp_send(req, buf, strlen(buf));
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "resp_send err: %s", esp_err_to_name(err));
|
||||
}
|
||||
return err;
|
||||
return httpd_resp_send(req, buf, strlen(buf));
|
||||
}
|
||||
|
||||
void async_send_out_cb(void *arg, int module_status)
|
||||
{
|
||||
post_request_t *req = arg;
|
||||
if (module_status != API_JSON_OK) {
|
||||
httpd_sess_trigger_close(req->req_out->handle,
|
||||
httpd_req_to_sockfd(req->req_out));
|
||||
}
|
||||
|
||||
uri_api_send_out(req->req_out, req, module_status);
|
||||
|
||||
/* clean resources */
|
||||
httpd_req_async_handler_complete(req->req_out);
|
||||
static_buffer_put(req);
|
||||
};
|
||||
|
||||
/**
|
||||
* REGISTER MODULE
|
||||
* */
|
||||
|
@ -78,15 +150,16 @@ static const httpd_uri_t uri_api = {
|
|||
.user_ctx = NULL
|
||||
};
|
||||
|
||||
int URI_API_INIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &uri_api;
|
||||
api_json_router_init();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int URI_API_EXIT(const httpd_uri_t **uri_conf) {
|
||||
static int URI_API_INIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &uri_api;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WEB_URI_MODULE_REGISTER(0x81, URI_API_INIT, URI_API_EXIT);
|
||||
static int URI_API_EXIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &uri_api;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WEB_URI_MODULE_REGISTER(0x81, URI_API_INIT, URI_API_EXIT)
|
||||
|
||||
|
||||
|
|
|
@ -5,68 +5,39 @@
|
|||
|
||||
#define TAG __FILE_NAME__
|
||||
|
||||
static esp_err_t hello_get_handler(httpd_req_t *req)
|
||||
#define URI_ROOT 0x0000002F /* / */
|
||||
#define URI_WS_DOT 0x2E73772F /* /ws. */
|
||||
|
||||
#define HTML_INDEX "index_html_gz"
|
||||
#define JS_WS_SHARED "ws_sharedworker_js_gz"
|
||||
|
||||
#define SEND_FILE(req, filename) do { \
|
||||
extern const unsigned char filename##_start[] asm("_binary_" filename "_start"); \
|
||||
extern const unsigned char filename##_end[] asm("_binary_" filename "_end"); \
|
||||
const ssize_t file_size = filename##_end - filename##_start; \
|
||||
httpd_resp_send(req, (const char *)filename##_start, file_size); \
|
||||
} while(0)
|
||||
|
||||
static esp_err_t html_base_get_handler(httpd_req_t *req)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_len;
|
||||
|
||||
/* Get header value string length and allocate memory for length + 1,
|
||||
* extra byte for null termination */
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Host") + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = malloc(buf_len);
|
||||
/* Copy null terminated value string into buffer */
|
||||
if (httpd_req_get_hdr_value_str(req, "Host", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found header => Host: %s", buf);
|
||||
}
|
||||
free(buf);
|
||||
/* this "hash" actually use the first 4 chars as an int32_t "hash" */
|
||||
const int *URI_HASH = (const int *)req->uri;
|
||||
|
||||
httpd_resp_set_hdr(req, "Connection", "close");
|
||||
|
||||
if (*URI_HASH == URI_WS_DOT) {
|
||||
httpd_resp_set_type(req, "text/javascript");
|
||||
httpd_resp_set_hdr(req, "Content-encoding", "gzip");
|
||||
SEND_FILE(req, JS_WS_SHARED);
|
||||
} else {
|
||||
httpd_resp_set_type(req, "text/html");
|
||||
httpd_resp_set_hdr(req, "Content-encoding", "gzip");
|
||||
SEND_FILE(req, HTML_INDEX);
|
||||
}
|
||||
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-2") + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = malloc(buf_len);
|
||||
if (httpd_req_get_hdr_value_str(req, "Test-Header-2", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found header => Test-Header-2: %s", buf);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
buf_len = httpd_req_get_hdr_value_len(req, "Test-Header-1") + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = malloc(buf_len);
|
||||
if (httpd_req_get_hdr_value_str(req, "Test-Header-1", buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found header => Test-Header-1: %s", buf);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* Read URL query string length and allocate memory for length + 1,
|
||||
* extra byte for null termination */
|
||||
buf_len = httpd_req_get_url_query_len(req) + 1;
|
||||
if (buf_len > 1) {
|
||||
buf = malloc(buf_len);
|
||||
if (httpd_req_get_url_query_str(req, buf, buf_len) == ESP_OK) {
|
||||
ESP_LOGI(TAG, "Found URL query => %s", buf);
|
||||
/* Get value of expected key from query string */
|
||||
ESP_LOGI(TAG, "query: %s", buf);
|
||||
}
|
||||
free(buf);
|
||||
}
|
||||
|
||||
/* Set some custom headers */
|
||||
httpd_resp_set_hdr(req, "Custom-Header-1", "Custom-Value-1");
|
||||
httpd_resp_set_hdr(req, "Custom-Header-2", "Custom-Value-2");
|
||||
|
||||
/* Send response with custom headers and body set as the
|
||||
* string passed in user context*/
|
||||
const char *resp_str = (const char *) req->user_ctx;
|
||||
httpd_resp_send(req, resp_str, HTTPD_RESP_USE_STRLEN);
|
||||
|
||||
/* After sending the HTTP response the old HTTP request
|
||||
* headers are lost. Check if HTTP request headers can be read now. */
|
||||
if (httpd_req_get_hdr_value_len(req, "Host") == 0) {
|
||||
ESP_LOGI(TAG, "Request headers lost");
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
|
@ -75,20 +46,17 @@ static esp_err_t hello_get_handler(httpd_req_t *req)
|
|||
* */
|
||||
|
||||
static const httpd_uri_t hello = {
|
||||
.uri = "/hello",
|
||||
.uri = "/",
|
||||
.method = HTTP_GET,
|
||||
.handler = hello_get_handler,
|
||||
/* Let's pass response string in user
|
||||
* context to demonstrate it's usage */
|
||||
.user_ctx = "Hello World!"
|
||||
.handler = html_base_get_handler,
|
||||
};
|
||||
|
||||
int URI_HTML_BASE_INIT(const httpd_uri_t **uri_conf) {
|
||||
static int URI_HTML_BASE_INIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &hello;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int URI_HTML_BASE_EXIT(const httpd_uri_t **uri_conf) {
|
||||
static int URI_HTML_BASE_EXIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &hello;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2024 kerms
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "web_uri_module.h"
|
||||
#include "api_json_router.h"
|
||||
#include "static_buffer.h"
|
||||
|
||||
#include <esp_http_server.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
#include <cJSON.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <freertos/FreeRTOS.h>
|
||||
#include <freertos/queue.h>
|
||||
|
||||
#define TAG __FILE_NAME__
|
||||
#define MSG_BUSY_ERROR "{\"error\":\"Resource busy\"}"
|
||||
#define MSG_JSON_ERROR "{\"error\":\"JSON parse error\"}"
|
||||
#define MSG_SEND_JSON_ERROR "{\"error\":\"JSON generation error\"}"
|
||||
#define MSG_INTERNAL_ERROR "{\"error\":\"\"}"
|
||||
|
||||
typedef struct ws_msg_t {
|
||||
api_json_req_t json;
|
||||
api_json_module_async_t async;
|
||||
httpd_handle_t hd;
|
||||
int fd;
|
||||
httpd_ws_frame_t ws_pkt;
|
||||
uint8_t delim[0];
|
||||
uint8_t payload[0]; /* size = static_buf_size - offsetof(this, delim) */
|
||||
} ws_msg_t;
|
||||
|
||||
#define PAYLOAD_LEN static_buffer_get_buf_size() - sizeof(ws_msg_t)
|
||||
|
||||
struct ws_ctx_t {
|
||||
struct ws_client_info_t {
|
||||
httpd_handle_t hd;
|
||||
int fd; /* range 58 ~ 58+max_socket */
|
||||
} clients[CONFIG_LWIP_MAX_SOCKETS+1];
|
||||
TaskHandle_t task_heartbeat;
|
||||
int8_t client_count;
|
||||
} ws_ctx;
|
||||
|
||||
static int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg);
|
||||
static int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg);
|
||||
static int ws_on_socket_open(httpd_req_t *req);
|
||||
static int ws_on_close(httpd_req_t *req, ws_msg_t *msg);
|
||||
|
||||
static void ws_async_resp(void *arg);
|
||||
static void async_send_out_cb(void *arg, int module_status);
|
||||
static void json_to_text(ws_msg_t *msg);
|
||||
|
||||
/* Heartbeat related */
|
||||
static inline void ws_add_fd(httpd_handle_t hd, int fd);
|
||||
static inline void ws_rm_fd(int fd);
|
||||
|
||||
_Noreturn static void heartbeat_task(void *arg);
|
||||
|
||||
|
||||
|
||||
static esp_err_t ws_req_handler(httpd_req_t *req)
|
||||
{
|
||||
if (unlikely(req->method == HTTP_GET)) {
|
||||
return ws_on_socket_open(req);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "ws_handler: httpd_handle_t=%p, sockfd=%d, client_info:%d, client_count: %d", req->handle,
|
||||
httpd_req_to_sockfd(req), httpd_ws_get_fd_info(req->handle, httpd_req_to_sockfd(req)),
|
||||
ws_ctx.client_count);
|
||||
|
||||
int err = ESP_OK;
|
||||
httpd_ws_frame_t *ws_pkt;
|
||||
ws_msg_t *ws_msg;
|
||||
|
||||
ws_msg = static_buffer_get(pdMS_TO_TICKS(10));
|
||||
if (unlikely(ws_msg == NULL)) {
|
||||
httpd_ws_frame_t resp_pkt;
|
||||
resp_pkt.type = HTTPD_WS_TYPE_TEXT;
|
||||
resp_pkt.len = strlen(MSG_BUSY_ERROR);
|
||||
resp_pkt.payload = (uint8_t *)MSG_BUSY_ERROR;
|
||||
resp_pkt.final = 1;
|
||||
httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), &resp_pkt);
|
||||
goto end;
|
||||
}
|
||||
ws_pkt = &ws_msg->ws_pkt;
|
||||
ws_pkt->len = 0;
|
||||
|
||||
/* get and check frame size */
|
||||
err = httpd_ws_recv_frame(req, ws_pkt, 0);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "ws recv len error");
|
||||
return ws_on_close(req, ws_msg);
|
||||
}
|
||||
ESP_LOGI(TAG, "frame len: %d, type: %d", ws_pkt->len, ws_pkt->type);
|
||||
if (unlikely(ws_pkt->len > PAYLOAD_LEN)) {
|
||||
ESP_LOGE(TAG, "frame len is too big");
|
||||
return ws_on_close(req, ws_msg);
|
||||
}
|
||||
|
||||
switch (ws_pkt->type) {
|
||||
case HTTPD_WS_TYPE_CONTINUE:
|
||||
goto end;
|
||||
case HTTPD_WS_TYPE_TEXT:
|
||||
ws_pkt->payload = ws_msg->payload;
|
||||
/* read incoming data */
|
||||
err = httpd_ws_recv_frame(req, ws_pkt, ws_pkt->len);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "ws recv data error");
|
||||
return ws_on_close(req, ws_msg);
|
||||
}
|
||||
return ws_on_text_data(req, ws_msg);
|
||||
case HTTPD_WS_TYPE_BINARY:
|
||||
ws_pkt->payload = ws_msg->payload;
|
||||
/* read incoming data */
|
||||
err = httpd_ws_recv_frame(req, ws_pkt, ws_pkt->len);
|
||||
if (unlikely(err != ESP_OK)) {
|
||||
ESP_LOGE(TAG, "ws recv data error");
|
||||
return ws_on_close(req, ws_msg);
|
||||
}
|
||||
return ws_on_binary_data(req, ws_msg);
|
||||
case HTTPD_WS_TYPE_CLOSE:
|
||||
return ws_on_close(req, ws_msg);
|
||||
case HTTPD_WS_TYPE_PING:
|
||||
/* Now turn the frame to PONG */
|
||||
ws_pkt->type = HTTPD_WS_TYPE_PONG;
|
||||
err = httpd_ws_send_frame(req, ws_pkt);
|
||||
goto end;
|
||||
case HTTPD_WS_TYPE_PONG:
|
||||
err = ESP_OK;
|
||||
goto end;
|
||||
default:
|
||||
err = ESP_FAIL;
|
||||
goto end;
|
||||
}
|
||||
end:
|
||||
static_buffer_put(ws_msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* REGISTER MODULE
|
||||
* */
|
||||
|
||||
static const httpd_uri_t uri_api = {
|
||||
.uri = "/ws",
|
||||
.method = HTTP_GET,
|
||||
.handler = ws_req_handler,
|
||||
.user_ctx = NULL,
|
||||
.is_websocket = true,
|
||||
.supported_subprotocol = NULL,
|
||||
.handle_ws_control_frames = true,
|
||||
};
|
||||
|
||||
static int WS_REQ_INIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &uri_api;
|
||||
xTaskCreate(heartbeat_task, "hb task", 2048, NULL, 3, &ws_ctx.task_heartbeat);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int WS_REQ_EXIT(const httpd_uri_t **uri_conf) {
|
||||
*uri_conf = &uri_api;
|
||||
vTaskDelete(ws_ctx.task_heartbeat);
|
||||
ws_ctx.task_heartbeat = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
WEB_URI_MODULE_REGISTER(101, WS_REQ_INIT, WS_REQ_EXIT)
|
||||
|
||||
int ws_on_text_data(httpd_req_t *req, ws_msg_t *ws_msg)
|
||||
{
|
||||
int err = ESP_OK;
|
||||
int ret;
|
||||
httpd_ws_frame_t *ws_pkt;
|
||||
|
||||
ws_pkt = &ws_msg->ws_pkt;
|
||||
ESP_LOGI(TAG, "=========== RECEIVED DATA ==========");
|
||||
ESP_LOGI(TAG, "%.*s", ws_pkt->len, ws_pkt->payload);
|
||||
ESP_LOGI(TAG, "====================================");
|
||||
ESP_LOGI(TAG, "heap min: %lu, cur: %lu", esp_get_minimum_free_heap_size(), esp_get_free_heap_size());
|
||||
|
||||
/* Decode */
|
||||
ws_msg->json.in = cJSON_ParseWithLength((char *) ws_pkt->payload, ws_pkt->len);
|
||||
if (unlikely(ws_msg->json.in == NULL)) {
|
||||
ws_pkt->payload = (uint8_t *) MSG_JSON_ERROR;
|
||||
ws_pkt->len = strlen(MSG_JSON_ERROR);
|
||||
goto put_buf;
|
||||
}
|
||||
|
||||
ws_msg->json.out = NULL;
|
||||
ret = api_json_route(&ws_msg->json, &ws_msg->async);
|
||||
if (ret == API_JSON_ASYNC) {
|
||||
ws_msg->hd = req->handle;
|
||||
ws_msg->fd = httpd_req_to_sockfd(req);
|
||||
ws_msg->async.req_task.send_out.cb = async_send_out_cb;
|
||||
ws_msg->async.req_task.send_out.arg = ws_msg;
|
||||
if (req_queue_push_long_run(&ws_msg->async.req_task, pdMS_TO_TICKS(20))) {
|
||||
/* queued failed */
|
||||
goto end;
|
||||
}
|
||||
/* ret, buf will be release latter in async send out */
|
||||
return ESP_OK;
|
||||
} else if (ret != API_JSON_OK) {
|
||||
ws_pkt->len = strlen(MSG_BUSY_ERROR);
|
||||
ws_pkt->payload = (uint8_t *)MSG_BUSY_ERROR;
|
||||
ws_pkt->final = 1;
|
||||
err = httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), ws_pkt);
|
||||
goto end;
|
||||
} else if (ws_msg->json.out == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* api function returns something, send it to http client */
|
||||
json_to_text(ws_msg);
|
||||
|
||||
end:
|
||||
cJSON_Delete(ws_msg->json.in);
|
||||
put_buf:
|
||||
httpd_ws_send_frame_async(req->handle, httpd_req_to_sockfd(req), ws_pkt);
|
||||
static_buffer_put(ws_msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
int ws_on_binary_data(httpd_req_t *req, ws_msg_t *ws_msg)
|
||||
{
|
||||
(void) req;
|
||||
static_buffer_put(ws_msg);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ws_on_socket_open(httpd_req_t *req)
|
||||
{
|
||||
int sock_fd = httpd_req_to_sockfd(req);
|
||||
ws_add_fd(req->handle, sock_fd);
|
||||
ESP_LOGI(TAG, "ws open: %d", sock_fd);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static int ws_on_close(httpd_req_t *req, ws_msg_t *msg)
|
||||
{
|
||||
/* Read the rest of the CLOSE frame and response */
|
||||
/* Please refer to RFC6455 Section 5.5.1 for more details */
|
||||
msg->ws_pkt.len = 0;
|
||||
msg->ws_pkt.type = HTTPD_WS_TYPE_CLOSE;
|
||||
ESP_LOGI(TAG, "ws %d closed", httpd_req_to_sockfd(req));
|
||||
ws_rm_fd(httpd_req_to_sockfd(req));
|
||||
int err = httpd_ws_send_frame(req, &msg->ws_pkt);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "on close %s", esp_err_to_name(err));
|
||||
}
|
||||
httpd_sess_trigger_close(req->handle, httpd_req_to_sockfd(req));
|
||||
static_buffer_put(msg);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ws_async_resp(void *arg)
|
||||
{
|
||||
ws_msg_t *req = arg;
|
||||
httpd_handle_t hd = req->hd;
|
||||
int fd = req->fd;
|
||||
int err;
|
||||
|
||||
ESP_LOGI(TAG, "ws async fd : %d", fd);
|
||||
err = httpd_ws_send_frame_async(hd, fd, &req->ws_pkt);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "%s", esp_err_to_name(err));
|
||||
}
|
||||
static_buffer_put(req);
|
||||
}
|
||||
|
||||
void async_send_out_cb(void *arg, int module_status)
|
||||
{
|
||||
ws_msg_t *req = arg;
|
||||
if (module_status != API_JSON_OK) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
int err;
|
||||
json_to_text(req);
|
||||
err = httpd_queue_work(req->hd, ws_async_resp, req);
|
||||
if (likely(err == ESP_OK)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGE(TAG, "errno: %d, fd %p: %s", errno, req->hd, esp_err_to_name(err));
|
||||
|
||||
end:
|
||||
/* clean resources */
|
||||
static_buffer_put(req);
|
||||
}
|
||||
|
||||
void json_to_text(ws_msg_t *ws_msg)
|
||||
{
|
||||
int err;
|
||||
httpd_ws_frame_t *ws_pkt = &ws_msg->ws_pkt;
|
||||
/* api function returns something, send it to http client */
|
||||
err = !cJSON_PrintPreallocated(ws_msg->json.out, (char *) ws_msg->payload, PAYLOAD_LEN - 5, 0);
|
||||
cJSON_Delete(ws_msg->json.out);
|
||||
if (unlikely(err)) {
|
||||
ws_pkt->len = strlen(MSG_SEND_JSON_ERROR);
|
||||
ws_pkt->payload = (uint8_t *) MSG_SEND_JSON_ERROR;
|
||||
ws_pkt->final = 1;
|
||||
}
|
||||
ws_pkt->len = strlen((char *) ws_pkt->payload);
|
||||
}
|
||||
|
||||
|
||||
/* Clients array manipulation function
|
||||
* */
|
||||
static inline void ws_add_fd(httpd_handle_t hd, int fd)
|
||||
{
|
||||
if (ws_ctx.client_count > CONFIG_LWIP_MAX_SOCKETS) {
|
||||
return;
|
||||
}
|
||||
ws_ctx.clients[ws_ctx.client_count].fd = fd;
|
||||
ws_ctx.clients[ws_ctx.client_count].hd = hd;
|
||||
ws_ctx.client_count++;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief replace the fd and hd to be removed by the last item
|
||||
* all front items are valid
|
||||
*/
|
||||
static inline void ws_rm_fd(int fd)
|
||||
{
|
||||
for (int i = 0; i < ws_ctx.client_count; ++i) {
|
||||
if (ws_ctx.clients[i].fd == fd) {
|
||||
ws_ctx.clients[i].fd = ws_ctx.clients[ws_ctx.client_count-1].fd;
|
||||
ws_ctx.clients[i].hd = ws_ctx.clients[ws_ctx.client_count-1].hd;
|
||||
ws_ctx.client_count--;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void send_heartbeat(void *arg)
|
||||
{
|
||||
static httpd_ws_frame_t ws_pkt = {
|
||||
.len = 0,
|
||||
.payload = NULL,
|
||||
.type = HTTPD_WS_TYPE_TEXT,
|
||||
};
|
||||
|
||||
struct ws_client_info_t *client_info = arg;
|
||||
int err;
|
||||
err = httpd_ws_send_frame_async(client_info->hd, client_info->fd, &ws_pkt);
|
||||
if (err) {
|
||||
ws_rm_fd(client_info->fd);
|
||||
httpd_sess_trigger_close(client_info->hd, client_info->fd);
|
||||
ESP_LOGE(TAG, "hb send err: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
static inline void ws_broadcast_heartbeat()
|
||||
{
|
||||
int err;
|
||||
for (int i = 0; i < ws_ctx.client_count; ++i) {
|
||||
err = httpd_queue_work(ws_ctx.clients[i].hd, send_heartbeat, &ws_ctx.clients[i]);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "hb queue work err: %s", esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_Noreturn
|
||||
void heartbeat_task(void *arg)
|
||||
{
|
||||
(void) arg;
|
||||
while (1) {
|
||||
vTaskDelay(pdMS_TO_TICKS(2000));
|
||||
ws_broadcast_heartbeat();
|
||||
}
|
||||
};
|
||||
|
|
@ -44,7 +44,7 @@ static bool uri_match(const char *reference_uri, const char *uri_to_match, size_
|
|||
/* ref should be shorter than target */
|
||||
ref_length = strlen(reference_uri);
|
||||
if (ref_length > match_upto) {
|
||||
ESP_LOGI(TAG, "no match length");
|
||||
ESP_LOGI(TAG, "no match length ref %s t: %s", reference_uri, uri_to_match);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -74,7 +74,7 @@ static bool uri_match(const char *reference_uri, const char *uri_to_match, size_
|
|||
return true;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "fall back false");
|
||||
ESP_LOGI(TAG, "fall back false %s t: %s", reference_uri, uri_to_match);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -83,20 +83,19 @@ static int8_t opened_socket = 0;
|
|||
static esp_err_t web_server_on_open(httpd_handle_t hd, int sockfd)
|
||||
{
|
||||
opened_socket++;
|
||||
printf("%d open, now: %d\n", sockfd, opened_socket);
|
||||
ESP_LOGI(TAG, "%d open, now: %d", sockfd, opened_socket);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void web_server_on_close(httpd_handle_t hd, int sockfd)
|
||||
{
|
||||
opened_socket--;
|
||||
printf("%d closed, now: %d\n", sockfd, opened_socket);
|
||||
ESP_LOGI(TAG, "%d closed, now: %d", sockfd, opened_socket);
|
||||
close(sockfd);
|
||||
}
|
||||
|
||||
void start_webserver(void)
|
||||
{
|
||||
httpd_handle_t server = NULL;
|
||||
httpd_config_t config = HTTPD_DEFAULT_CONFIG();
|
||||
int err;
|
||||
|
||||
|
@ -106,16 +105,17 @@ void start_webserver(void)
|
|||
config.uri_match_fn = uri_match;
|
||||
config.open_fn = web_server_on_open;
|
||||
config.close_fn = web_server_on_close;
|
||||
config.keep_alive_enable = 1;
|
||||
config.keep_alive_count = 1;
|
||||
|
||||
ESP_LOGI(TAG, "Starting server on port: '%d'", config.server_port);
|
||||
if ((err = httpd_start(&server, &config) != ESP_OK)) {
|
||||
if ((err = httpd_start(&http_server, &config)) != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Error starting server!");
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "Registering URI handlers");
|
||||
uri_module_init(server);
|
||||
http_server = server;
|
||||
uri_module_init(http_server);
|
||||
}
|
||||
|
||||
static esp_err_t stop_webserver(httpd_handle_t server)
|
||||
|
|
|
@ -1,59 +1,71 @@
|
|||
#include "web_uri_module.h"
|
||||
#include "list.h"
|
||||
|
||||
#include <esp_log.h>
|
||||
|
||||
|
||||
#define URI_MODULE_MAX 8
|
||||
|
||||
#define TAG __FILE_NAME__
|
||||
|
||||
static uri_module_t module_arr[URI_MODULE_MAX];
|
||||
typedef struct uri_module_list_t {
|
||||
struct dl_list list;
|
||||
uri_module_t module;
|
||||
} uri_module_list_t;
|
||||
|
||||
static uri_module_list_t module_arr[URI_MODULE_MAX];
|
||||
static uint8_t module_count = 0;
|
||||
static DEFINE_DL_LIST(list_head);
|
||||
|
||||
int uri_module_init(httpd_handle_t server)
|
||||
{
|
||||
int err;
|
||||
const httpd_uri_t *uri;
|
||||
uri_module_list_t *item;
|
||||
uint8_t index = 0;
|
||||
|
||||
for (int i = 0; i < module_count; ++i) {
|
||||
err = module_arr[i].init(&uri);
|
||||
dl_list_for_each(item, &list_head, uri_module_list_t, list) {
|
||||
err = item->module.init(&uri);
|
||||
ESP_LOGI(TAG, "uri %s init", uri->uri);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "%d init error", i);
|
||||
ESP_LOGE(TAG, "%d init error", index);
|
||||
}
|
||||
|
||||
err = httpd_register_uri_handler(server, uri);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "%d %s", i, esp_err_to_name(err));
|
||||
ESP_LOGE(TAG, "%d %s", index, esp_err_to_name(err));
|
||||
}
|
||||
index++;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uri_module_exit(httpd_handle_t server)
|
||||
{
|
||||
int err;
|
||||
const httpd_uri_t *uri;
|
||||
for (int i = 0; i < module_count; ++i) {
|
||||
module_arr[i].exit(&uri);
|
||||
err = httpd_unregister_uri_handler(server, uri->uri, uri->method);
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "%d %s", i, esp_err_to_name(err));
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int uri_module_add(uri_module_func init, uri_module_func exit)
|
||||
int uri_module_add(uint8_t priority, uri_module_func init, uri_module_func exit)
|
||||
{
|
||||
ESP_LOGE(TAG, "adding module %p", init);
|
||||
ESP_LOGI(TAG, "adding module %p", init);
|
||||
|
||||
if (module_count >= URI_MODULE_MAX) {
|
||||
ESP_LOGE(TAG, "too much module > URI_MODULE_MAX");
|
||||
return 1;
|
||||
}
|
||||
|
||||
module_arr[module_count].exit = exit;
|
||||
module_arr[module_count].init = init;
|
||||
module_arr[module_count].module.exit = exit;
|
||||
module_arr[module_count].module.init = init;
|
||||
module_arr[module_count].module.priority = priority;
|
||||
|
||||
uri_module_list_t *item;
|
||||
dl_list_for_each(item, &list_head, uri_module_list_t, list) {
|
||||
if (item->module.priority <= priority) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
dl_list_add(&item->list, &module_arr[module_count].list);
|
||||
module_count++;
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -8,16 +8,17 @@ typedef int (*uri_module_func)(const httpd_uri_t **uri);
|
|||
typedef struct uri_module_t {
|
||||
uri_module_func init;
|
||||
uri_module_func exit;
|
||||
uint8_t priority;
|
||||
} uri_module_t;
|
||||
|
||||
int uri_module_add(uri_module_func init, uri_module_func exit);
|
||||
int uri_module_add(uint8_t priority, uri_module_func init, uri_module_func exit);
|
||||
|
||||
/**
|
||||
* @brief Register a uri module that will be init with PRI(priority) order.
|
||||
*/
|
||||
#define WEB_URI_MODULE_REGISTER(PRI, INIT, EXIT) \
|
||||
__attribute__((used, constructor(PRI))) void cons_ ## INIT(); \
|
||||
void cons_ ## INIT() { uri_module_add(INIT, EXIT); }
|
||||
void cons_ ## INIT() { uri_module_add(PRI, INIT, EXIT); }
|
||||
|
||||
int uri_module_init(httpd_handle_t server);
|
||||
int uri_module_exit(httpd_handle_t server);
|
||||
|
|
|
@ -4,13 +4,14 @@ file(GLOB SOURCES
|
|||
wifi_api_json.c
|
||||
wifi_api.c
|
||||
wifi_json_utils.c
|
||||
wifi_storage.c
|
||||
)
|
||||
|
||||
|
||||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES mdns esp_wifi esp_event api_router
|
||||
PRIV_REQUIRES mdns esp_wifi esp_event api_router wt_storage
|
||||
)
|
||||
|
||||
idf_component_set_property(${COMPONENT_NAME} WHOLE_ARCHIVE ON)
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "wifi_api.h"
|
||||
#include "wifi_manager.h"
|
||||
#include "wifi_configuration.h"
|
||||
#include <esp_wifi.h>
|
||||
|
||||
void wifi_api_get_ap_info(wifi_api_ap_info_t *ap_info)
|
||||
void wifi_api_sta_get_ap_info(wifi_api_ap_info_t *ap_info)
|
||||
{
|
||||
wifi_ap_record_t ap_record;
|
||||
esp_wifi_sta_get_ap_info(&ap_record);
|
||||
|
@ -18,18 +19,25 @@ void wifi_api_get_ap_info(wifi_api_ap_info_t *ap_info)
|
|||
ip4_addr_set(&ap_info->netmask, &ip_info.netmask);
|
||||
}
|
||||
|
||||
void wifi_api_ap_get_info(wifi_api_ap_info_t *ap_info)
|
||||
{
|
||||
strncpy(ap_info->ssid, WIFI_DEFAULT_AP_SSID, strlen(WIFI_DEFAULT_AP_SSID)+1);
|
||||
esp_wifi_get_mac(WIFI_IF_AP, (uint8_t *) &ap_info->mac);
|
||||
IP4_ADDR_EXPAND(&ap_info->ip, WIFI_DEFAULT_AP_IP);
|
||||
IP4_ADDR_EXPAND(&ap_info->gateway, WIFI_DEFAULT_AP_GATEWAY);
|
||||
IP4_ADDR_EXPAND(&ap_info->netmask, WIFI_DEFAULT_AP_NETMASK);
|
||||
ap_info->rssi = 0;
|
||||
}
|
||||
|
||||
static int rssi_comp(const void *a, const void *b)
|
||||
{
|
||||
const wifi_ap_record_t *r1 = a;
|
||||
const wifi_ap_record_t *r2 = b;
|
||||
const wifi_api_ap_info_t *r1 = a;
|
||||
const wifi_api_ap_info_t *r2 = b;
|
||||
return r2->rssi - r1->rssi;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief blocking function
|
||||
* @param number
|
||||
* @param ap_info
|
||||
* @return
|
||||
*/
|
||||
int wifi_api_get_scan_list(uint16_t *number, wifi_api_ap_info_t *ap_info)
|
||||
{
|
||||
|
@ -76,10 +84,12 @@ static void wifi_manager_scan_done(uint16_t ap_found, wifi_ap_record_t *records,
|
|||
scan_done_cb(ap_found, ap_info, arg);
|
||||
}
|
||||
|
||||
|
||||
int wifi_api_trigger_scan(uint16_t *max_ap_count, wifi_api_scan_done_cb cb, void *cb_arg)
|
||||
int wifi_api_connect(const char *ssid, const char *password)
|
||||
{
|
||||
wifi_manager_trigger_scan(wifi_manager_scan_done, cb_arg);
|
||||
scan_done_cb = cb;
|
||||
return 0;
|
||||
return wifi_manager_connect(ssid, password);
|
||||
}
|
||||
|
||||
int wifi_api_disconnect(void)
|
||||
{
|
||||
return wifi_manager_disconnect();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
#include <lwip/ip4_addr.h>
|
||||
|
||||
#define WIFI_API_MODULE_ID 1
|
||||
|
||||
typedef enum wifi_api_json_cmd_t {
|
||||
UNKNOWN = 0,
|
||||
WIFI_API_JSON_STA_GET_AP_INFO,
|
||||
WIFI_API_JSON_CONNECT,
|
||||
WIFI_API_JSON_GET_SCAN,
|
||||
WIFI_API_JSON_DISCONNECT,
|
||||
WIFI_API_JSON_AP_GET_INFO,
|
||||
} wifi_api_json_cmd_t;
|
||||
|
||||
typedef struct wifi_api_ap_info_t {
|
||||
ip4_addr_t ip;
|
||||
ip4_addr_t gateway;
|
||||
|
@ -12,7 +23,9 @@ typedef struct wifi_api_ap_info_t {
|
|||
signed char rssi;
|
||||
} wifi_api_ap_info_t;
|
||||
|
||||
void wifi_api_get_ap_info(wifi_api_ap_info_t *ap_info);
|
||||
void wifi_api_sta_get_ap_info(wifi_api_ap_info_t *ap_info);
|
||||
|
||||
void wifi_api_ap_get_info(wifi_api_ap_info_t *ap_info);
|
||||
|
||||
typedef void (*wifi_api_scan_done_cb)(uint16_t found, wifi_api_ap_info_t *aps, void *arg);
|
||||
|
||||
|
@ -20,5 +33,9 @@ int wifi_api_trigger_scan(uint16_t *max_ap_count, wifi_api_scan_done_cb cb, void
|
|||
|
||||
int wifi_api_get_scan_list(uint16_t *number, wifi_api_ap_info_t *ap_info);
|
||||
|
||||
int wifi_api_connect(const char *ssid, const char *password);
|
||||
|
||||
int wifi_api_disconnect(void);
|
||||
|
||||
|
||||
#endif //WIFI_API_H_GUARD
|
|
@ -1,61 +1,125 @@
|
|||
#include "wifi_api_json.h"
|
||||
|
||||
#include "api_json_router.h"
|
||||
#include "api_json_module.h"
|
||||
#include "wifi_api.h"
|
||||
#include "wifi_json_utils.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <esp_log.h>
|
||||
|
||||
static int wifi_api_json_get_ap_info(api_json_req_t *req, api_json_resp_t *resp);
|
||||
#define TAG __FILENAME__
|
||||
|
||||
static int wifi_api_json_get_scan(api_json_req_t *req, api_json_resp_t *resp);
|
||||
static int wifi_api_json_sta_get_ap_info(api_json_req_t *req);
|
||||
|
||||
static int on_json_req(uint16_t cmd, api_json_req_t *req, api_json_resp_t *rsp)
|
||||
static int wifi_api_json_get_scan(api_json_req_t *req);
|
||||
|
||||
static int wifi_api_json_connect(api_json_req_t *req);
|
||||
|
||||
static int wifi_api_json_disconnect(api_json_req_t *req);
|
||||
|
||||
static int wifi_api_json_ap_get_info(api_json_req_t *req);
|
||||
|
||||
|
||||
static int async_helper_cb(void *arg)
|
||||
{
|
||||
api_json_module_req_t *req = arg;
|
||||
return req->func(req->arg);
|
||||
}
|
||||
|
||||
static inline int set_async(api_json_req_t *req, api_json_module_async_t *async, int (*func)(api_json_req_t *))
|
||||
{
|
||||
async->module.func = func;
|
||||
async->module.arg = req;
|
||||
async->req_task.module.helper_cb = async_helper_cb;
|
||||
async->req_task.module.arg = &async->module;
|
||||
return API_JSON_ASYNC;
|
||||
}
|
||||
|
||||
static int on_json_req(uint16_t cmd, api_json_req_t *req, api_json_module_async_t *async)
|
||||
{
|
||||
wifi_api_json_cmd_t wifi_cmd = cmd;
|
||||
switch (wifi_cmd) {
|
||||
case WIFI_API_JSON_GET_AP_INFO:
|
||||
return wifi_api_json_get_ap_info(req, rsp);
|
||||
case WIFI_API_JSON_CONNECT:
|
||||
|
||||
break;
|
||||
case WIFI_API_JSON_GET_SCAN:
|
||||
return wifi_api_json_get_scan(req, rsp);
|
||||
case UNKNOWN:
|
||||
default:
|
||||
break;
|
||||
case WIFI_API_JSON_STA_GET_AP_INFO:
|
||||
return wifi_api_json_sta_get_ap_info(req);
|
||||
case WIFI_API_JSON_CONNECT:
|
||||
return set_async(req, async, wifi_api_json_connect);
|
||||
case WIFI_API_JSON_GET_SCAN:
|
||||
return set_async(req, async, wifi_api_json_get_scan);
|
||||
case WIFI_API_JSON_DISCONNECT:
|
||||
return wifi_api_json_disconnect(req);
|
||||
case WIFI_API_JSON_AP_GET_INFO:
|
||||
return wifi_api_json_ap_get_info(req);
|
||||
break;
|
||||
}
|
||||
|
||||
printf("%d\n", cmd);
|
||||
|
||||
return 0;
|
||||
ESP_LOGI(TAG, "cmd %d not executed\n", cmd);
|
||||
return API_JSON_BAD_REQUEST;
|
||||
}
|
||||
|
||||
static int wifi_api_json_get_ap_info(api_json_req_t *req, api_json_resp_t *resp)
|
||||
static int wifi_api_json_sta_get_ap_info(api_json_req_t *req)
|
||||
{
|
||||
wifi_api_ap_info_t ap_info;
|
||||
wifi_api_get_ap_info(&ap_info);
|
||||
resp->json = wifi_api_json_serialize_ap_info(&ap_info);
|
||||
wifi_api_sta_get_ap_info(&ap_info);
|
||||
req->out = wifi_api_json_serialize_ap_info(&ap_info, WIFI_API_JSON_STA_GET_AP_INFO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_api_json_get_scan(api_json_req_t *req, api_json_resp_t *resp)
|
||||
static int wifi_api_json_ap_get_info(api_json_req_t *req)
|
||||
{
|
||||
wifi_api_ap_info_t ap_info;
|
||||
wifi_api_ap_get_info(&ap_info);
|
||||
req->out = wifi_api_json_serialize_ap_info(&ap_info, WIFI_API_JSON_AP_GET_INFO);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int wifi_api_json_get_scan(api_json_req_t *req)
|
||||
{
|
||||
wifi_api_ap_info_t ap_info[20];
|
||||
uint16_t max_count = 20;
|
||||
int err;
|
||||
|
||||
ESP_LOGI(TAG, "get scan\n");
|
||||
|
||||
err = wifi_api_get_scan_list(&max_count, ap_info);
|
||||
if (err == ESP_ERR_NOT_FINISHED) {
|
||||
resp->json = wifi_api_json_create_err_rsp(req->json, "Wi-Fi scan busy");
|
||||
req->out = wifi_api_json_create_err_rsp(req->in, "Wi-Fi scan busy");
|
||||
return 1;
|
||||
}
|
||||
|
||||
resp->json = wifi_api_json_serialize_scan_list(ap_info, max_count);
|
||||
ESP_LOGI(TAG, "scan ok\n");
|
||||
|
||||
req->out = wifi_api_json_serialize_scan_list(ap_info, max_count);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int wifi_api_json_connect(api_json_req_t *req)
|
||||
{
|
||||
char *ssid;
|
||||
char *password;
|
||||
|
||||
ssid = cJSON_GetStringValue(cJSON_GetObjectItem(req->in, "ssid"));
|
||||
password = cJSON_GetStringValue(cJSON_GetObjectItem(req->in, "password"));
|
||||
if (ssid == NULL || password == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
int err = wifi_api_connect(ssid, password);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
return wifi_api_json_sta_get_ap_info(req);
|
||||
};
|
||||
|
||||
int wifi_api_json_disconnect(api_json_req_t *req)
|
||||
{
|
||||
return wifi_api_disconnect();
|
||||
}
|
||||
|
||||
/* ****
|
||||
* register module
|
||||
* */
|
||||
|
@ -63,10 +127,10 @@ static int wifi_api_json_get_scan(api_json_req_t *req, api_json_resp_t *resp)
|
|||
static int wifi_api_json_init(api_json_module_cfg_t *cfg)
|
||||
{
|
||||
cfg->on_req = on_json_req;
|
||||
cfg->module_id = 1;
|
||||
cfg->module_id = WIFI_API_MODULE_ID;
|
||||
return 0;
|
||||
}
|
||||
|
||||
API_JSON_MODULE_REGISTER(0x90, wifi_api_json_init);
|
||||
API_JSON_MODULE_REGISTER(0x90, wifi_api_json_init)
|
||||
|
||||
|
||||
|
|
|
@ -1,12 +1,4 @@
|
|||
#ifndef WIFI_API_JSON_H_GUARD
|
||||
#define WIFI_API_JSON_H_GUARD
|
||||
|
||||
typedef enum wifi_api_json_cmd_t {
|
||||
UNKNOWN = 0,
|
||||
WIFI_API_JSON_GET_AP_INFO,
|
||||
WIFI_API_JSON_CONNECT,
|
||||
WIFI_API_JSON_GET_SCAN,
|
||||
|
||||
} wifi_api_json_cmd_t;
|
||||
|
||||
#endif //WIFI_API_JSON_H_GUARD
|
|
@ -1,11 +1,17 @@
|
|||
#ifndef WIFI_CONFIGURATION_H_GUARD
|
||||
#define WIFI_CONFIGURATION_H_GUARD
|
||||
|
||||
#define WIFI_DEFAULT_HOSTNAME "无线DAP"
|
||||
|
||||
#define WIFI_DEFAULT_AP_SSID "无线DAP"
|
||||
#define WIFI_DEFAULT_AP_PASS "12345678"
|
||||
#define WIFI_DEFAULT_HOSTNAME "无线DAP"
|
||||
#define WIFI_DEFAULT_AP_IP 192, 168, 1, 1
|
||||
#define WIFI_DEFAULT_AP_GATEWAY 192, 168, 1, 1
|
||||
#define WIFI_DEFAULT_AP_NETMASK 255, 255, 255, 0
|
||||
|
||||
#define WIFI_DEFAULT_STA_SSID "example_ssid"
|
||||
#define WIFI_DEFAULT_STA_PASS "12345678"
|
||||
|
||||
#define IP4_ADDR_EXPAND(...) IP4_ADDR(__VA_ARGS__)
|
||||
|
||||
#endif //WIFI_CONFIGURATION_H_GUARD
|
|
@ -9,6 +9,8 @@
|
|||
|
||||
#define TAG __FILE_NAME__
|
||||
|
||||
void event_on_connected(ip_event_got_ip_t *event);
|
||||
|
||||
void ip_event_handler(void *handler_arg __attribute__((unused)),
|
||||
esp_event_base_t event_base __attribute__((unused)),
|
||||
int32_t event_id,
|
||||
|
@ -19,6 +21,7 @@ void ip_event_handler(void *handler_arg __attribute__((unused)),
|
|||
ip_event_got_ip_t *event = event_data;
|
||||
printf("STA GOT IP : %s\n",
|
||||
ip4addr_ntoa((const ip4_addr_t *) &event->ip_info.ip));
|
||||
event_on_connected(event);
|
||||
break;
|
||||
}
|
||||
case IP_EVENT_STA_LOST_IP:
|
||||
|
@ -44,42 +47,52 @@ void ip_event_handler(void *handler_arg __attribute__((unused)),
|
|||
}
|
||||
}
|
||||
|
||||
typedef struct wifi_scan_ctx_t {
|
||||
wifi_ap_record_t *ap;
|
||||
wifi_event_scan_done_cb cb;
|
||||
uint16_t number;
|
||||
} wifi_scan_ctx_t;
|
||||
typedef struct wifi_event_ctx_t {
|
||||
/* WIFI scan */
|
||||
struct {
|
||||
wifi_ap_record_t *ap;
|
||||
wifi_event_scan_done_cb cb;
|
||||
uint16_t number;
|
||||
};
|
||||
/* WIFI connect */
|
||||
struct {
|
||||
wifi_event_connect_done_cb cb;
|
||||
void *arg;
|
||||
void (*disconn_handler)(void);
|
||||
uint8_t attempt;
|
||||
} conn;
|
||||
uint8_t is_connected;
|
||||
} wifi_event_ctx_t;
|
||||
|
||||
static wifi_scan_ctx_t scan_ctx = {
|
||||
static wifi_event_ctx_t event_ctx = {
|
||||
.ap = NULL,
|
||||
.is_connected = 0,
|
||||
.conn.disconn_handler = NULL,
|
||||
};
|
||||
|
||||
static void wifi_on_scan_done(wifi_event_sta_scan_done_t *event)
|
||||
{
|
||||
int err;
|
||||
if (!scan_ctx.cb || !scan_ctx.ap) {
|
||||
scan_ctx.number = 0;
|
||||
if (!event_ctx.cb || !event_ctx.ap) {
|
||||
event_ctx.number = 0;
|
||||
} else if (event->status == 1) {
|
||||
/* error */
|
||||
scan_ctx.number = 0;
|
||||
event_ctx.number = 0;
|
||||
} else if (event->number == 0) {
|
||||
/* no ap found on current channel */
|
||||
scan_ctx.number = 0;
|
||||
event_ctx.number = 0;
|
||||
}
|
||||
|
||||
err = esp_wifi_scan_get_ap_records(&scan_ctx.number, scan_ctx.ap);
|
||||
if (err) {
|
||||
esp_wifi_clear_ap_list();
|
||||
}
|
||||
esp_wifi_scan_get_ap_records(&event_ctx.number, event_ctx.ap);
|
||||
esp_wifi_clear_ap_list();
|
||||
|
||||
int i;
|
||||
for (i = 0; i < scan_ctx.number; ++i) {
|
||||
if (scan_ctx.ap[i].rssi < -80)
|
||||
for (i = 0; i < event_ctx.number; ++i) {
|
||||
if (event_ctx.ap[i].rssi < -80)
|
||||
break;
|
||||
}
|
||||
|
||||
scan_ctx.number = i;
|
||||
return scan_ctx.cb(scan_ctx.number, scan_ctx.ap);
|
||||
event_ctx.number = i;
|
||||
return event_ctx.cb(event_ctx.number, event_ctx.ap);
|
||||
}
|
||||
|
||||
|
||||
|
@ -108,12 +121,18 @@ int wifi_event_trigger_scan(uint8_t channel, wifi_event_scan_done_cb cb, uint16_
|
|||
return err;
|
||||
}
|
||||
|
||||
scan_ctx.cb = cb;
|
||||
scan_ctx.number = number;
|
||||
scan_ctx.ap = aps;
|
||||
event_ctx.cb = cb;
|
||||
event_ctx.number = number;
|
||||
event_ctx.ap = aps;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* WIFI EVENT
|
||||
* */
|
||||
|
||||
static void reconnect_after_disco();
|
||||
|
||||
void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
||||
esp_event_base_t event_base __attribute__((unused)),
|
||||
int32_t event_id,
|
||||
|
@ -128,13 +147,12 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
|||
// printf("event: WIFI_EVENT_SCAN_DONE: ok: %lu, nr: %u, seq: %u\n",
|
||||
// event->status, event->number, event->scan_id);
|
||||
wifi_on_scan_done(event);
|
||||
scan_ctx.ap = NULL;
|
||||
scan_ctx.cb = NULL;
|
||||
event_ctx.ap = NULL;
|
||||
event_ctx.cb = NULL;
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_STA_START:
|
||||
printf("event: WIFI_EVENT_STA_START\n");
|
||||
esp_wifi_connect();
|
||||
break;
|
||||
case WIFI_EVENT_STA_CONNECTED: {
|
||||
wifi_event_sta_connected_t *event = event_data;
|
||||
|
@ -145,6 +163,7 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
|||
#ifdef CONFIG_EXAMPLE_IPV6
|
||||
tcpip_adapter_create_ip6_linklocal(TCPIP_ADAPTER_IF_STA);
|
||||
#endif
|
||||
event_ctx.is_connected = 1;
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_STA_DISCONNECTED: {
|
||||
|
@ -153,8 +172,8 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
|||
printf("event: WIFI_EVENT_STA_DISCONNECTED ");
|
||||
printf("sta %02X:%02X:%02X:%02X:%02X:%02X disconnect reason %d\n",
|
||||
m[0], m[1], m[2], m[3], m[4], m[5], event->reason);
|
||||
/* auto reconnect after disconnection */
|
||||
esp_wifi_connect();
|
||||
event_ctx.is_connected = 0;
|
||||
reconnect_after_disco();
|
||||
break;
|
||||
}
|
||||
case WIFI_EVENT_AP_START:
|
||||
|
@ -172,7 +191,7 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
|||
wifi_event_ap_staconnected_t *event = event_data;
|
||||
uint8_t *m = event->mac;
|
||||
printf("event: WIFI_EVENT_AP_STADISCONNECTED\n");
|
||||
printf("%02X:%02X:%02X:%02X:%02X:%02X is connected\n",
|
||||
printf("%02X:%02X:%02X:%02X:%02X:%02X is disconnected\n",
|
||||
m[0], m[1], m[2], m[3], m[4], m[5]);
|
||||
break;
|
||||
}
|
||||
|
@ -181,3 +200,65 @@ void wifi_event_handler(void *handler_arg __attribute__((unused)),
|
|||
}
|
||||
}
|
||||
|
||||
void wifi_event_set_disco_handler(void (*disconn_handler)(void))
|
||||
{
|
||||
event_ctx.conn.disconn_handler = disconn_handler;
|
||||
}
|
||||
|
||||
int wifi_event_trigger_connect(uint8_t attempt, wifi_event_connect_done_cb cb, void *arg)
|
||||
{
|
||||
int err;
|
||||
event_ctx.conn.attempt = attempt;
|
||||
event_ctx.conn.cb = cb;
|
||||
event_ctx.conn.arg = arg;
|
||||
if (event_ctx.is_connected) {
|
||||
err = esp_wifi_disconnect();
|
||||
} else {
|
||||
err = esp_wifi_connect();
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void event_on_connected(ip_event_got_ip_t *event)
|
||||
{
|
||||
if (event_ctx.conn.cb) {
|
||||
event_ctx.conn.cb(event_ctx.conn.arg, event);
|
||||
}
|
||||
event_ctx.conn.attempt = 0;
|
||||
event_ctx.conn.cb = NULL;
|
||||
event_ctx.conn.arg = NULL;
|
||||
}
|
||||
|
||||
void reconnect_after_disco()
|
||||
{
|
||||
int err;
|
||||
|
||||
ESP_LOGI(TAG, "reco... atempt %d", event_ctx.conn.attempt);
|
||||
|
||||
if (event_ctx.conn.attempt == 0) {
|
||||
goto call;
|
||||
}
|
||||
|
||||
err = esp_wifi_connect();
|
||||
if (err) {
|
||||
event_ctx.conn.attempt = 0;
|
||||
/* connect err: stop connect and call cb */
|
||||
ESP_LOGE(TAG, "wifi connect error %s", esp_err_to_name(err));
|
||||
goto call;
|
||||
}
|
||||
|
||||
event_ctx.conn.attempt--;
|
||||
return;
|
||||
|
||||
call:
|
||||
if (event_ctx.conn.cb) {
|
||||
event_ctx.conn.cb(event_ctx.conn.arg, NULL);
|
||||
} else {
|
||||
/* disconnected from extern environment, notify */
|
||||
if (event_ctx.conn.disconn_handler) {
|
||||
event_ctx.conn.disconn_handler();
|
||||
}
|
||||
}
|
||||
event_ctx.conn.cb = NULL;
|
||||
event_ctx.conn.arg = NULL;
|
||||
}
|
||||
|
|
|
@ -4,15 +4,23 @@
|
|||
#include <stdint.h>
|
||||
#include <esp_event_base.h>
|
||||
#include <esp_wifi_types.h>
|
||||
#include <esp_netif_types.h>
|
||||
|
||||
void ip_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
|
||||
void wifi_event_handler(void *handler_arg, esp_event_base_t event_base, int32_t event_id, void *event_data);
|
||||
|
||||
|
||||
typedef void (*wifi_event_scan_done_cb)(uint16_t ap_count, wifi_ap_record_t *aps_info);
|
||||
|
||||
int wifi_event_trigger_scan(uint8_t channel, wifi_event_scan_done_cb cb, uint16_t number, wifi_ap_record_t *aps);
|
||||
void wifi_event_set_disco_handler(void (*disconn_handler)(void));
|
||||
|
||||
/**
|
||||
* @brief
|
||||
* @param connect_status 0: SUCCESS, OTHER: ERROR
|
||||
*/
|
||||
typedef void (*wifi_event_connect_done_cb)(void *arg, ip_event_got_ip_t *event);
|
||||
int wifi_event_trigger_connect(uint8_t attempt, wifi_event_connect_done_cb cb, void *arg);
|
||||
|
||||
|
||||
#endif //WIFI_EVENT_HANDLER_H_GUARD
|
|
@ -3,12 +3,19 @@
|
|||
#include "wifi_json_utils.h"
|
||||
#include "wifi_api.h"
|
||||
|
||||
cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info)
|
||||
static void wifi_api_json_set_header(cJSON *root, uint16_t cmd)
|
||||
{
|
||||
cJSON_AddNumberToObject(root, "cmd", cmd);
|
||||
cJSON_AddNumberToObject(root, "module", WIFI_API_MODULE_ID);
|
||||
}
|
||||
|
||||
cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info, wifi_api_json_cmd_t cmd)
|
||||
{
|
||||
cJSON *root;
|
||||
|
||||
root = cJSON_CreateObject();
|
||||
|
||||
wifi_api_json_set_header(root, cmd);
|
||||
cJSON_AddStringToObject(root, "ip", ip4addr_ntoa(&ap_info->ip));
|
||||
cJSON_AddStringToObject(root, "gateway", ip4addr_ntoa(&ap_info->gateway));
|
||||
cJSON_AddStringToObject(root, "netmask", ip4addr_ntoa(&ap_info->netmask));
|
||||
|
@ -30,6 +37,7 @@ cJSON *wifi_api_json_serialize_scan_list(wifi_api_ap_info_t *aps_info, uint16_t
|
|||
char mac_str[18];
|
||||
|
||||
root = cJSON_CreateObject();
|
||||
wifi_api_json_set_header(root, WIFI_API_JSON_GET_SCAN);
|
||||
cJSON *scan_list = cJSON_AddArrayToObject(root, "scan_list");
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include "wifi_api.h"
|
||||
|
||||
cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info);
|
||||
cJSON *wifi_api_json_serialize_ap_info(wifi_api_ap_info_t *ap_info, wifi_api_json_cmd_t cmd);
|
||||
cJSON *wifi_api_json_serialize_scan_list(wifi_api_ap_info_t *aps_info, uint16_t count);
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include "wifi_manager.h"
|
||||
#include "wifi_configuration.h"
|
||||
#include "wifi_event_handler.h"
|
||||
#include "wifi_storage.h"
|
||||
|
||||
#include <esp_err.h>
|
||||
#include <esp_netif.h>
|
||||
|
@ -12,29 +13,46 @@
|
|||
|
||||
#include <lwip/ip4_addr.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
|
||||
#define TAG __FILENAME__
|
||||
|
||||
typedef struct wifi_scan_ctx_t {
|
||||
wifi_ap_record_t *ap;
|
||||
wifi_manager_scan_done_cb cb;
|
||||
void *arg;
|
||||
typedef struct wifi_ctx_t {
|
||||
SemaphoreHandle_t lock;
|
||||
TaskHandle_t task;
|
||||
uint16_t total_aps;
|
||||
uint16_t nr_aps_in_channel;
|
||||
uint8_t status;
|
||||
uint8_t max_ap;
|
||||
} wifi_scan_ctx_t;
|
||||
union {
|
||||
struct {
|
||||
wifi_ap_record_t *ap;
|
||||
wifi_manager_scan_done_cb cb;
|
||||
void *arg;
|
||||
uint16_t total_aps;
|
||||
uint16_t nr_aps_in_channel;
|
||||
uint8_t max_ap;
|
||||
} scan;
|
||||
struct {
|
||||
ip_event_got_ip_t *event;
|
||||
uint8_t need_unlock; /* used when trigger connection from wifi_manager instead of wifi_api */
|
||||
} conn;
|
||||
};
|
||||
uint8_t is_endless_connect:1;
|
||||
uint8_t auto_reconnect:1;
|
||||
uint8_t do_fast_connect:1; /* 0 delay connect on boot or just disconnected, else 5 seconds delay from each connection try */
|
||||
uint8_t reserved:5;
|
||||
} wifi_ctx_t;
|
||||
|
||||
static esp_netif_t *ap_netif;
|
||||
static esp_netif_t *sta_netif;
|
||||
|
||||
static wifi_scan_ctx_t scan_ctx;
|
||||
static wifi_ctx_t ctx;
|
||||
|
||||
static void set_sta_cred(const char *ssid, const char *password);
|
||||
static void disconn_handler(void);
|
||||
static int set_default_sta_cred(void);
|
||||
|
||||
void wifi_manager_init(void)
|
||||
{
|
||||
esp_err_t err;
|
||||
uint8_t do_connect = 0;
|
||||
|
||||
ap_netif = esp_netif_create_default_wifi_ap();
|
||||
assert(ap_netif);
|
||||
|
@ -53,32 +71,29 @@ void wifi_manager_init(void)
|
|||
ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_APSTA));
|
||||
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
|
||||
|
||||
wifi_config_t ap_config = {0};
|
||||
{
|
||||
wifi_config_t ap_config = {0};
|
||||
|
||||
strncpy((char *) ap_config.ap.ssid, WIFI_DEFAULT_AP_SSID, 32);
|
||||
strncpy((char *) ap_config.ap.password, WIFI_DEFAULT_AP_PASS, 64);
|
||||
ap_config.ap.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
|
||||
ap_config.ap.max_connection = 4;
|
||||
ap_config.ap.channel = 6;
|
||||
ap_config.ap.ssid_hidden = 0;
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
|
||||
strncpy((char *) ap_config.ap.ssid, WIFI_DEFAULT_AP_SSID, 32);
|
||||
strncpy((char *) ap_config.ap.password, WIFI_DEFAULT_AP_PASS, 64);
|
||||
ap_config.ap.authmode = WIFI_AUTH_WPA2_WPA3_PSK;
|
||||
ap_config.ap.max_connection = 4;
|
||||
ap_config.ap.channel = 6;
|
||||
ap_config.ap.ssid_hidden = 0;
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_config));
|
||||
}
|
||||
|
||||
// TODO: scan once and connect to known wifi
|
||||
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.ssid = WIFI_DEFAULT_STA_SSID,
|
||||
.password = WIFI_DEFAULT_STA_PASS,
|
||||
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
|
||||
},
|
||||
};
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
if (set_default_sta_cred() == 0) {
|
||||
ESP_LOGI(TAG, "STA connect to saved cred");
|
||||
do_connect = 1;
|
||||
ctx.do_fast_connect = 1;
|
||||
}
|
||||
|
||||
/* TODO: Read from nvs */
|
||||
esp_netif_ip_info_t ip_info;
|
||||
IP4_ADDR(&ip_info.ip, 192, 168, 1, 1);
|
||||
IP4_ADDR(&ip_info.gw, 192, 168, 1, 1);
|
||||
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
|
||||
IP4_ADDR_EXPAND(&ip_info.ip, WIFI_DEFAULT_AP_IP);
|
||||
IP4_ADDR_EXPAND(&ip_info.gw, WIFI_DEFAULT_AP_GATEWAY);
|
||||
IP4_ADDR_EXPAND(&ip_info.netmask, WIFI_DEFAULT_AP_NETMASK);
|
||||
|
||||
err = esp_netif_dhcps_stop(ap_netif);
|
||||
if (err) {
|
||||
|
@ -95,97 +110,98 @@ void wifi_manager_init(void)
|
|||
|
||||
ESP_ERROR_CHECK(esp_wifi_start());
|
||||
ESP_LOGI(TAG, "wifi started");
|
||||
esp_log_level_set("wifi", ESP_LOG_WARN);
|
||||
|
||||
scan_ctx.lock = xSemaphoreCreateBinary();
|
||||
scan_ctx.status = 0;
|
||||
xSemaphoreGive(scan_ctx.lock);
|
||||
}
|
||||
ctx.lock = xSemaphoreCreateBinary();
|
||||
ctx.is_endless_connect = 0;
|
||||
ctx.auto_reconnect = 1;
|
||||
xSemaphoreGive(ctx.lock);
|
||||
|
||||
static void wifi_event_scan_channel_done(uint16_t number, wifi_ap_record_t *aps)
|
||||
{
|
||||
scan_ctx.nr_aps_in_channel = number;
|
||||
scan_ctx.total_aps += number;
|
||||
if (scan_ctx.task) {
|
||||
xTaskNotifyGive(scan_ctx.task);
|
||||
wifi_event_set_disco_handler(disconn_handler);
|
||||
if (do_connect) {
|
||||
disconn_handler();
|
||||
}
|
||||
}
|
||||
|
||||
static void scan_loop()
|
||||
/**
|
||||
* @brief called by wifi_event_handler on scan done
|
||||
* */
|
||||
static void wifi_event_scan_channel_done(uint16_t number, wifi_ap_record_t *aps)
|
||||
{
|
||||
ctx.scan.nr_aps_in_channel = number;
|
||||
ctx.scan.total_aps += number;
|
||||
if (ctx.task) {
|
||||
xTaskNotifyGive(ctx.task);
|
||||
}
|
||||
}
|
||||
|
||||
static int scan_loop()
|
||||
{
|
||||
uint32_t ret;
|
||||
uint16_t number;
|
||||
|
||||
scan_ctx.total_aps = 0;
|
||||
ctx.scan.total_aps = 0;
|
||||
|
||||
for (int scan_channel = 1; scan_channel <= 13; ++scan_channel) {
|
||||
number = scan_ctx.max_ap - scan_ctx.total_aps;
|
||||
number = ctx.scan.max_ap - ctx.scan.total_aps;
|
||||
if (wifi_event_trigger_scan(scan_channel,
|
||||
wifi_event_scan_channel_done, number,
|
||||
&scan_ctx.ap[scan_ctx.total_aps])) {
|
||||
&ctx.scan.ap[ctx.scan.total_aps])) {
|
||||
ESP_LOGE(TAG, "trigger scan %d error", scan_channel);
|
||||
goto end;
|
||||
return 1;
|
||||
}
|
||||
/* shadow wifi_event_scan_channel_done() called */
|
||||
vTaskDelay(100);
|
||||
|
||||
vTaskDelay(pdMS_TO_TICKS(100));
|
||||
ret = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(1000));
|
||||
if (ret == 0) {
|
||||
/* timeout */
|
||||
ESP_LOGE(TAG, "scan channel %d timeout", scan_channel);
|
||||
goto end;
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
end:
|
||||
if (scan_ctx.cb) {
|
||||
// scan_ctx.cb(scan_ctx.total_aps, scan_ctx.ap);
|
||||
}
|
||||
}
|
||||
|
||||
static void wifi_manager_scan_task(void *arg)
|
||||
{
|
||||
scan_loop();
|
||||
free(scan_ctx.ap);
|
||||
xSemaphoreGive(scan_ctx.lock);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
int wifi_manager_trigger_scan(wifi_manager_scan_done_cb cb, void *arg)
|
||||
{
|
||||
if (xSemaphoreTake(scan_ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
return 1;
|
||||
}
|
||||
/* TODO: let API allocate ? */
|
||||
scan_ctx.ap = malloc(sizeof(wifi_scan_ctx_t) * 32);
|
||||
if (scan_ctx.ap == NULL) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
scan_ctx.cb = cb;
|
||||
scan_ctx.arg = arg;
|
||||
scan_ctx.max_ap = 32;
|
||||
|
||||
ulTaskNotifyTake(pdTRUE, 0);
|
||||
xTaskCreatePinnedToCore(wifi_manager_scan_task, "scan task", 4 * 1024,
|
||||
NULL, 7, &scan_ctx.task, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_manager_get_scan_list(uint16_t *number, wifi_ap_record_t *aps)
|
||||
{
|
||||
if (xSemaphoreTake(scan_ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
return 1;
|
||||
int err;
|
||||
int broke_endless_connect = 0;
|
||||
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
if (ctx.is_endless_connect == 0) {
|
||||
return 1;
|
||||
}
|
||||
/* Is in connecting mode */
|
||||
ESP_LOGI(TAG, "deleting connecting %p", ctx.task);
|
||||
vTaskDelete(ctx.task);
|
||||
/* in case lock is released when deleting the task */
|
||||
xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0));
|
||||
esp_wifi_disconnect();
|
||||
ctx.is_endless_connect = 0;
|
||||
broke_endless_connect = 1;
|
||||
|
||||
} else if (ctx.is_endless_connect == 1) {
|
||||
ESP_LOGI(TAG, "deleting delay %p", ctx.task);
|
||||
vTaskDelete(ctx.task);
|
||||
ctx.is_endless_connect = 0;
|
||||
broke_endless_connect = 1;
|
||||
}
|
||||
|
||||
scan_ctx.ap = aps;
|
||||
scan_ctx.max_ap = *number;
|
||||
scan_ctx.cb = NULL;
|
||||
scan_ctx.task = xTaskGetCurrentTaskHandle();
|
||||
ctx.scan.ap = aps;
|
||||
ctx.scan.max_ap = *number;
|
||||
ctx.scan.cb = NULL;
|
||||
ctx.task = xTaskGetCurrentTaskHandle();
|
||||
|
||||
scan_loop();
|
||||
xSemaphoreGive(scan_ctx.lock);
|
||||
*number = scan_ctx.total_aps;
|
||||
return 0;
|
||||
err = scan_loop();
|
||||
xSemaphoreGive(ctx.lock);
|
||||
*number = ctx.scan.total_aps;
|
||||
|
||||
if (broke_endless_connect) {
|
||||
if (set_default_sta_cred() == 0) {
|
||||
disconn_handler();
|
||||
}
|
||||
}
|
||||
return err;
|
||||
}
|
||||
|
||||
void *wifi_manager_get_ap_netif()
|
||||
|
@ -197,3 +213,152 @@ void *wifi_manager_get_sta_netif()
|
|||
{
|
||||
return sta_netif;
|
||||
}
|
||||
|
||||
|
||||
static void try_connect_done(void *arg, ip_event_got_ip_t *event)
|
||||
{
|
||||
ctx.conn.event = event;
|
||||
if (ctx.task) {
|
||||
xTaskNotifyGive(ctx.task);
|
||||
}
|
||||
if (ctx.conn.need_unlock) {
|
||||
ctx.conn.need_unlock = 0;
|
||||
xSemaphoreGive(ctx.lock);
|
||||
}
|
||||
}
|
||||
|
||||
int set_default_sta_cred()
|
||||
{
|
||||
int err;
|
||||
wifi_credential_t credential;
|
||||
err = wifi_data_get_last_conn_cred(&credential);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
set_sta_cred(credential.ssid, credential.password);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int wifi_manager_connect(const char *ssid, const char *password)
|
||||
{
|
||||
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
if (ctx.is_endless_connect == 0) {
|
||||
return 1;
|
||||
}
|
||||
vTaskDelete(ctx.task);
|
||||
ctx.is_endless_connect = 0;
|
||||
}
|
||||
int ret;
|
||||
set_sta_cred(ssid, password);
|
||||
|
||||
ctx.task = xTaskGetCurrentTaskHandle();
|
||||
ctx.auto_reconnect = 1;
|
||||
|
||||
wifi_event_trigger_connect(2, try_connect_done, NULL);
|
||||
ret = ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(10000));
|
||||
|
||||
xSemaphoreGive(ctx.lock);
|
||||
if (ret == 0 || ctx.conn.event == NULL) {
|
||||
ESP_LOGI(TAG, "conn error");
|
||||
if (set_default_sta_cred() == 0) {
|
||||
disconn_handler();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
wifi_credential_t credential;
|
||||
memcpy(credential.ssid, ssid, 32);
|
||||
memcpy(credential.password, password, 64);
|
||||
ret = wifi_save_ap_credential(&credential);
|
||||
if (ret) {
|
||||
ESP_LOGE(TAG, "nvs save error: %s", esp_err_to_name(ret));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_sta_cred(const char *ssid, const char *password)
|
||||
{
|
||||
wifi_config_t wifi_config = {
|
||||
.sta = {
|
||||
.threshold.rssi = -80,
|
||||
.sort_method = WIFI_CONNECT_AP_BY_SIGNAL,
|
||||
},
|
||||
};
|
||||
memcpy((char *) wifi_config.sta.ssid, ssid, 32);
|
||||
memcpy((char *) wifi_config.sta.password, password, 64);
|
||||
|
||||
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config));
|
||||
}
|
||||
|
||||
static void reconnection_task(void *arg)
|
||||
{
|
||||
int err;
|
||||
|
||||
ctx.is_endless_connect = 1;
|
||||
ctx.task = xTaskGetCurrentTaskHandle();
|
||||
|
||||
do {
|
||||
ESP_LOGI(TAG, "reco task: try connect, task %p", xTaskGetCurrentTaskHandle());
|
||||
if (ctx.do_fast_connect) {
|
||||
err = wifi_event_trigger_connect(5, try_connect_done, NULL);
|
||||
} else {
|
||||
err = wifi_event_trigger_connect(0, try_connect_done, NULL);
|
||||
}
|
||||
|
||||
if (err) {
|
||||
ESP_LOGE(TAG, "trigger connect err: %s", esp_err_to_name(err));
|
||||
break;
|
||||
}
|
||||
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(20000));
|
||||
if (ctx.conn.event || ctx.auto_reconnect == 0) {
|
||||
/* reconnection successful or stop reconnect */
|
||||
break;
|
||||
}
|
||||
|
||||
/* long wait to not spam try to reconnect */
|
||||
xSemaphoreGive(ctx.lock);
|
||||
ESP_LOGI(TAG, "retry connection in 5 seconds");
|
||||
vTaskDelay(pdMS_TO_TICKS(5 * 1000));
|
||||
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "reconnection failed");
|
||||
break;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
ctx.is_endless_connect = 0;
|
||||
xSemaphoreGive(ctx.lock);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void disconn_handler(void)
|
||||
{
|
||||
/* disconnected
|
||||
* 1. WI-FI AP is far away
|
||||
* 2. WI-FI AP or AP device is closed
|
||||
* 3. this device is ejected by AP
|
||||
* */
|
||||
|
||||
if (ctx.auto_reconnect == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (xSemaphoreTake(ctx.lock, pdMS_TO_TICKS(0)) != pdTRUE) {
|
||||
ESP_LOGE(TAG, "disconn_handler failed");
|
||||
return;
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "start reconn task");
|
||||
ctx.do_fast_connect = 1;
|
||||
xTaskCreate(reconnection_task, "reconn task", 4 * 1024, NULL, 7, NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief kill reconnection task
|
||||
* @return
|
||||
*/
|
||||
int wifi_manager_disconnect(void)
|
||||
{
|
||||
ctx.auto_reconnect = 0;
|
||||
return esp_wifi_disconnect();
|
||||
}
|
||||
|
|
|
@ -5,17 +5,14 @@
|
|||
|
||||
void wifi_manager_init();
|
||||
|
||||
int wifi_set_ap_cred(const char *ssid, const char *password);
|
||||
|
||||
int wifi_set_sta_cred(const char *ssid, const char *password);
|
||||
|
||||
void *wifi_manager_get_ap_netif();
|
||||
void *wifi_manager_get_sta_netif();
|
||||
|
||||
typedef void (*wifi_manager_scan_done_cb)(uint16_t ap_found, wifi_ap_record_t *record, void *arg);
|
||||
int wifi_manager_scan(uint16_t *number, wifi_ap_record_t *records, uint8_t is_async);
|
||||
int wifi_manager_trigger_scan(wifi_manager_scan_done_cb cb, void *arg);
|
||||
int wifi_manager_get_scan_list(uint16_t *number, wifi_ap_record_t *aps);
|
||||
int wifi_manager_connect(const char *ssid, const char *password);
|
||||
int wifi_manager_disconnect(void);
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,77 @@
|
|||
#include "wifi_storage.h"
|
||||
#include "wifi_storage_priv.h"
|
||||
#include "wt_nvs.h"
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define NVS_NAMESPACE "wt_wifi"
|
||||
|
||||
int wifi_data_get_last_conn_cred(wifi_credential_t *ap_credential)
|
||||
{
|
||||
uint32_t ap_bitmap = 0;
|
||||
nvs_handle_t handle;
|
||||
int err;
|
||||
|
||||
err = wt_nvs_open(NVS_NAMESPACE, &handle);
|
||||
if (err) {
|
||||
return WT_NVS_ERR;
|
||||
}
|
||||
|
||||
err = wt_nvs_get(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
|
||||
if (err || ap_bitmap == 0) {
|
||||
return WT_NVS_ERR_NOT_FOUND;
|
||||
}
|
||||
|
||||
err = wt_nvs_get(handle, KEY_WIFI_STA_LAST_AP_CRED,
|
||||
ap_credential, sizeof(wifi_credential_t));
|
||||
if (err) {
|
||||
return WT_NVS_ERR;
|
||||
}
|
||||
|
||||
wt_nvs_close(handle);
|
||||
return WT_NVS_OK;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when connect to an AP,
|
||||
*/
|
||||
int wifi_save_ap_credential(wifi_credential_t *ap_credential)
|
||||
{
|
||||
uint32_t ap_bitmap;
|
||||
int err;
|
||||
nvs_handle_t handle;
|
||||
|
||||
err = wt_nvs_open(NVS_NAMESPACE, &handle);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = wt_nvs_get(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
|
||||
if (err) {
|
||||
if (err != ESP_ERR_NVS_NOT_FOUND) {
|
||||
return err;
|
||||
}
|
||||
ap_bitmap = 0;
|
||||
}
|
||||
if (ap_bitmap == 0) {
|
||||
ap_bitmap = 1;
|
||||
err = wt_nvs_set(handle, KEY_WIFI_STA_AP_BITMAP, &ap_bitmap, sizeof(ap_bitmap));
|
||||
}
|
||||
|
||||
|
||||
wifi_credential_t credential;
|
||||
err = wt_nvs_get(handle, KEY_WIFI_STA_LAST_AP_CRED, &credential, sizeof(ap_bitmap));
|
||||
if (err) {
|
||||
if (err != ESP_ERR_NVS_NOT_FOUND) {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
err = wt_nvs_set(handle, KEY_WIFI_STA_LAST_AP_CRED, ap_credential, sizeof(wifi_credential_t));
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
wt_nvs_close(handle);
|
||||
return WT_NVS_OK;
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
#ifndef WIFI_STORAGE_H_GUARD
|
||||
#define WIFI_STORAGE_H_GUARD
|
||||
|
||||
#include <stdint-gcc.h>
|
||||
|
||||
typedef struct nvs_wifi_credential_t {
|
||||
char ssid[32];
|
||||
char password[64];
|
||||
} wifi_credential_t;
|
||||
|
||||
int wifi_data_get_last_conn_cred(wifi_credential_t *ap_credential);
|
||||
|
||||
int wifi_save_ap_credential(wifi_credential_t *ap_credential);
|
||||
|
||||
#endif //WIFI_STORAGE_H_GUARD
|
|
@ -0,0 +1,29 @@
|
|||
#ifndef WIFI_STORAGE_PRIV_H_GUARD
|
||||
#define WIFI_STORAGE_PRIV_H_GUARD
|
||||
|
||||
#define WIFI_MAX_AP_CRED_RECORD 1
|
||||
|
||||
typedef struct w_cache_t {
|
||||
uint32_t ap_bitmap;
|
||||
wifi_credential_t ap_creds[WIFI_MAX_AP_CRED_RECORD];
|
||||
} w_cache_t;
|
||||
|
||||
typedef enum wt_wifi_key_enum {
|
||||
KEY_WIFI_RESERVED = 0x000,
|
||||
/* WIFI */
|
||||
KEY_WIFI_AP_SSID,
|
||||
KEY_WIFI_AP_PASSWORD,
|
||||
|
||||
/* TODO: should have 1 for each AP */
|
||||
KEY_WIFI_STA_USE_STATIC, /* bit[0:31]=[IP, MASK, GATEWAY, DNS] */
|
||||
KEY_WIFI_STA_STATIC_IP, /* 4B */
|
||||
KEY_WIFI_STA_STATIC_MASK, /* 4B */
|
||||
KEY_WIFI_STA_STATIC_GATEWAY, /* 4B */
|
||||
KEY_WIFI_STA_STATIC_DNS, /* 4B */
|
||||
|
||||
/* AP's information */
|
||||
KEY_WIFI_STA_LAST_AP_CRED, /*!< ssid[32] + password[64] */
|
||||
KEY_WIFI_STA_AP_BITMAP,
|
||||
} wt_wifi_key;
|
||||
|
||||
#endif //WIFI_STORAGE_PRIV_H_GUARD
|
|
@ -3,5 +3,6 @@ file(GLOB SOURCES wt_nvs.c wt_storage.c)
|
|||
idf_component_register(
|
||||
SRCS ${SOURCES}
|
||||
INCLUDE_DIRS "."
|
||||
PRIV_REQUIRES nvs_flash
|
||||
PRIV_REQUIRES
|
||||
REQUIRES nvs_flash
|
||||
)
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include <nvs_flash.h>
|
||||
|
||||
#define WT_PARTITION_NAME "wt_nvs"
|
||||
|
||||
void wt_nvs_init()
|
||||
{
|
||||
// Initialize default NVS
|
||||
|
@ -13,4 +15,83 @@ void wt_nvs_init()
|
|||
err = nvs_flash_init();
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
|
||||
err = nvs_flash_init_partition(WT_PARTITION_NAME);
|
||||
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND) {
|
||||
// NVS partition was truncated and needs to be erased
|
||||
// Retry nvs_flash_init
|
||||
ESP_ERROR_CHECK(nvs_flash_erase());
|
||||
err = nvs_flash_init_partition(WT_PARTITION_NAME);
|
||||
}
|
||||
ESP_ERROR_CHECK(err);
|
||||
}
|
||||
|
||||
int wt_nvs_open(const char* namespace, nvs_handle_t *out_handle)
|
||||
{
|
||||
return nvs_open_from_partition(WT_PARTITION_NAME, namespace, NVS_READWRITE, out_handle);
|
||||
}
|
||||
|
||||
void wt_nvs_close(nvs_handle_t handle)
|
||||
{
|
||||
ESP_ERROR_CHECK(nvs_commit(handle));
|
||||
nvs_close(handle);
|
||||
}
|
||||
|
||||
int wt_nvs_get(nvs_handle_t handle, const uint32_t key, void *data, uint32_t data_size)
|
||||
{
|
||||
switch (data_size) {
|
||||
case 0: {
|
||||
uint8_t tmp;
|
||||
return nvs_get_u8(handle, (const char *) &key, &tmp);
|
||||
}
|
||||
case 1:
|
||||
return nvs_get_u8(handle, (const char *) &key, data);
|
||||
case 2:
|
||||
return nvs_get_u16(handle, (const char *) &key, data);
|
||||
case 4:
|
||||
return nvs_get_u32(handle, (const char *) &key, data);
|
||||
case 8:
|
||||
return nvs_get_u64(handle, (const char *) &key, data);
|
||||
default:
|
||||
return nvs_get_blob(handle, (const char *) &key, data,
|
||||
(size_t *) &data_size);
|
||||
}
|
||||
}
|
||||
|
||||
int wt_nvs_set(nvs_handle_t handle, const uint32_t key, void *data, uint32_t data_size)
|
||||
{
|
||||
switch (data_size) {
|
||||
case 0: {
|
||||
uint8_t tmp = 0xFF;
|
||||
return nvs_set_u8(handle, (const char *) &key, tmp);
|
||||
}
|
||||
case 1:
|
||||
return nvs_set_u8(handle, (const char *) &key, *(uint8_t *)data);
|
||||
case 2:
|
||||
return nvs_set_u16(handle, (const char *) &key, *(uint16_t *)data);
|
||||
case 4:
|
||||
return nvs_set_u32(handle, (const char *) &key, *(uint32_t *)data);
|
||||
case 8:
|
||||
return nvs_set_u64(handle, (const char *) &key, *(uint64_t *)data);
|
||||
default:
|
||||
return nvs_set_blob(handle, (const char *) &key, data, data_size);
|
||||
}
|
||||
}
|
||||
|
||||
int wt_nvs_get_once(const char* namespace, const uint32_t key, void *data, uint32_t data_size)
|
||||
{
|
||||
nvs_handle_t handle;
|
||||
int err;
|
||||
err = wt_nvs_open(namespace, &handle);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
err = wt_nvs_get(handle, key, data, data_size);
|
||||
if (err) {
|
||||
return err;
|
||||
}
|
||||
|
||||
wt_nvs_close(handle);
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,25 @@
|
|||
#define WT_NVS_H_GUARD
|
||||
|
||||
#include <stdint.h>
|
||||
#include <nvs.h>
|
||||
|
||||
typedef enum wt_nvs_error_enum {
|
||||
WT_NVS_OK = 0,
|
||||
WT_NVS_ERR,
|
||||
WT_NVS_NO_MEM,
|
||||
WT_NVS_ERR_NOT_FOUND,
|
||||
|
||||
} wt_nvs_key_error;
|
||||
|
||||
int wt_nvs_open(const char* namespace, nvs_handle_t *out_handle);
|
||||
|
||||
void wt_nvs_close(nvs_handle_t handle);
|
||||
|
||||
int wt_nvs_get(nvs_handle_t handle, uint32_t key, void *data, uint32_t data_size);
|
||||
|
||||
int wt_nvs_set(nvs_handle_t handle, uint32_t key, void *data, uint32_t data_size);
|
||||
|
||||
int wt_nvs_flush(nvs_handle_t handle);
|
||||
|
||||
void wt_nvs_init();
|
||||
|
||||
|
|
|
@ -108,12 +108,13 @@ CONFIG_ESPTOOLPY_MONITOR_BAUD=921600
|
|||
#
|
||||
# Partition Table
|
||||
#
|
||||
CONFIG_PARTITION_TABLE_SINGLE_APP=y
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP is not set
|
||||
# CONFIG_PARTITION_TABLE_SINGLE_APP_LARGE is not set
|
||||
# CONFIG_PARTITION_TABLE_TWO_OTA is not set
|
||||
# CONFIG_PARTITION_TABLE_CUSTOM is not set
|
||||
CONFIG_PARTITION_TABLE_CUSTOM=y
|
||||
CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions_singleapp.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0x8000
|
||||
CONFIG_PARTITION_TABLE_FILENAME="partitions.csv"
|
||||
CONFIG_PARTITION_TABLE_OFFSET=0xF000
|
||||
CONFIG_PARTITION_TABLE_MD5=y
|
||||
# end of Partition Table
|
||||
|
||||
|
@ -338,11 +339,12 @@ CONFIG_ESP_EVENT_POST_FROM_IRAM_ISR=y
|
|||
# HTTP Server
|
||||
#
|
||||
CONFIG_HTTPD_MAX_REQ_HDR_LEN=512
|
||||
CONFIG_HTTPD_MAX_URI_LEN=512
|
||||
CONFIG_HTTPD_MAX_URI_LEN=256
|
||||
CONFIG_HTTPD_ERR_RESP_NO_DELAY=y
|
||||
CONFIG_HTTPD_PURGE_BUF_LEN=32
|
||||
# CONFIG_HTTPD_LOG_PURGE_DATA is not set
|
||||
# CONFIG_HTTPD_WS_SUPPORT is not set
|
||||
CONFIG_HTTPD_WS_SUPPORT=y
|
||||
# CONFIG_HTTPD_QUEUE_WORK_BLOCKING is not set
|
||||
# end of HTTP Server
|
||||
|
||||
#
|
||||
|
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue