Compare commits

...

117 commits

Author SHA1 Message Date
Li Bo
7da906e535 Merge branch 'fix/4g_modem_web_config' into 'usb/add_usb_solutions'
fix(4g_modem): fix router web config function

See merge request ae_group/esp-iot-solution!598
2022-08-31 17:24:54 +08:00
Li Bo
6b757d65ec fix(4g_modem): fix router web config function 2022-08-31 17:24:54 +08:00
Li Bo
9582fc06e2 Merge branch 'feature/4g_module_support_web_config' into 'usb/add_usb_solutions'
feat(4g_module): support config using webpage

See merge request ae_group/esp-iot-solution!585
2022-05-10 17:31:25 +08:00
Li Junru
5b7618008e feat(4g_module): support config using webpage 2022-05-10 16:58:31 +08:00
Xu Chun Guang
8ea35502a9 Merge branch 'usb/add_webusb' into 'usb/add_usb_solutions'
Usb/add webusb

See merge request ae_group/esp-iot-solution!581
2022-04-07 14:15:46 +08:00
zhaokeke
c87add67af feat(usb): add dfu upload firmware support 2022-03-30 10:45:26 +08:00
zhaokeke
2e92f0e687 feat: add webusb fearure 2022-03-28 11:46:17 +08:00
Li Bo
14d9f9bbea Merge branch 'usb/add_tinyusb_dual_cdc_example' into 'usb/add_usb_solutions'
usb(cdc): add tinyusb dual cdc port example

See merge request ae_group/esp-iot-solution!579
2022-03-16 13:58:26 +08:00
Li Bo
f6d0d2d807 usb(cdc): add tinyusb dual cdc port example 2022-03-16 13:40:46 +08:00
Li Bo
3505a91fe2 Merge branch 'usb/rebase_on_idf_release_v4.4' into 'usb/add_usb_solutions'
usb: rebase on idf release/v4.4

See merge request rd/esp-iot-solution!570
2022-03-07 15:39:12 +08:00
Li Bo
0473e9b941 usb(uvc_stream/usbh_cdc): get fullspeed descriptor by default
resolve #146, resolve #152, resolve #156, resolve #157
2022-03-07 14:47:39 +08:00
Li Bo
2ba36c5152 usb_phy: using usb phy api 2022-02-22 14:42:03 +08:00
Li Bo
e2523e5e84 hcd: fix ctrl transfer size 2022-02-21 10:32:34 +08:00
Xu Chun Guang
7b333fa902 Merge branch 'usb/dfu_class' into 'usb/add_usb_solutions'
usb: add dfu class

See merge request rd/esp-iot-solution!564
2022-01-24 06:51:05 +00:00
Xu Chun Guang
2c2140a336 Merge branch 'feature/improve_usb_msc_upload_file_speed' into 'usb/add_usb_solutions'
feature: change wear levelling default sector size to 4096 in msc example

See merge request rd/esp-iot-solution!558
2022-01-21 07:59:33 +00:00
zhaokeke
8e93154db2 docs(USB): add USB DFU and ECM usage 2022-01-20 10:58:10 +08:00
zhaokeke
b7f2eea1ca usb: add dfu class 2022-01-18 11:13:22 +08:00
Xu Chun Guang
427667030d Merge branch 'usb/fix_ecm_error' into 'usb/add_usb_solutions'
Add USB ECM support

See merge request rd/esp-iot-solution!563
2022-01-17 12:21:42 +00:00
zhaokeke
16dc60d15d USB(ECM): Automatic switching network 2022-01-17 20:10:18 +08:00
Xu Chun Guang
c093d77d2a Merge branch 'bugfix/usb_dongle_for_esp32s3' into 'usb/add_usb_solutions'
bugfix(usb_dongle): usb communication exception for esp32s3 dual core

See merge request rd/esp-iot-solution!561
2022-01-17 09:38:30 +00:00
Tian Sen Wen
c211307bc9 bugfix(usb_dongle): usb communication exception for esp32s3 dual core 2022-01-17 17:18:42 +08:00
zhaokeke
6111f64d7a USB: fix ecm mode report probe error 2022-01-17 16:19:28 +08:00
Xu Chun Guang
5a488103b3 Merge branch 'usb/auto_recall_dhcp' into 'usb/add_usb_solutions'
feat: After the networt is switched, the network adapter automatically restarts

See merge request rd/esp-iot-solution!545
2022-01-17 04:14:47 +00:00
zhaokeke
0d65119c6b USB(RNDIS): Automatic switching network
After the RNDIS networt is switched, the network adapter automatically restarts.
This feature only works on Windows; Linux does not support it.
For details, please refer to https://iter01.com/600150.html
2022-01-17 11:51:39 +08:00
Wang Yu Xin
262acde05c feat: change wear levelling default sector size to 4096 2022-01-10 14:36:50 +08:00
Tian Sen Wen
3505e6bfe3 Merge branch 'docs/update_usb_dongle_readme' into 'usb/add_usb_solutions'
docs: update usb_dongle readme

See merge request rd/esp-iot-solution!555
2021-12-23 08:32:16 +00:00
Tian Sen Wen
74ca6c6caf docs: update usb_dongle readme 2021-12-23 16:08:38 +08:00
Li Bo
0e43d61398 Merge branch 'docs/uvc_add_english_readme' into 'usb/add_usb_solutions'
docs: uvc add english readme

See merge request rd/esp-iot-solution!553
2021-12-03 03:54:52 +00:00
Li Bo
3daed6f6cb docs: uvc add english readme 2021-12-03 03:54:51 +00:00
Li Bo
49c940ca7d Merge branch 'bugfix/fix_4g_module_network' into 'usb/add_usb_solutions'
bugfix(cdc_4g): fix network issues

See merge request rd/esp-iot-solution!552
2021-12-01 11:12:27 +00:00
Li Bo
7b82c1f4a3 bugfix(cdc_4g): fix network issues
1. fix set dns address issue
2. fix ip packet loss
3. fix wifi no password
2021-12-01 17:44:41 +08:00
Li Bo
450e8166de Merge branch 'usb/add_hid_device_example' into 'usb/add_usb_solutions'
examples(hid): add usb hid device example

See merge request rd/esp-iot-solution!551
2021-11-22 07:05:22 +00:00
Li Bo
e4148e84ca examples(hid): add usb hid device example 2021-11-22 14:26:47 +08:00
Li Bo
d036d0f79b Merge branch 'bugfix/fix_4g_module_sudden_disconnect' into 'usb/add_usb_solutions'
bugfix(usb_cdc_4g_module): handle usb sudden disconnect

See merge request rd/esp-iot-solution!549
2021-11-18 09:33:56 +00:00
Li Bo
294e7c796b bugfix(usb_cdc_4g_module): handle usb sudden disconnect 2021-11-18 16:35:15 +08:00
Li Bo
b26ede89a2 Merge branch 'feature/usb_cdc_support_hot_plug' into 'usb/add_usb_solutions'
feature(usbh_cdc): usb host cdc support hot plug

See merge request rd/esp-iot-solution!546
2021-11-18 03:45:46 +00:00
Li Bo
f75b5eb95a feature(usbh_cdc): usb host cdc support hot plug 2021-11-18 11:33:44 +08:00
Xu Chun Guang
86a45f1d65 Merge branch 'doc/usb_dongle_idf_commit_dependent' into 'usb/add_usb_solutions'
Doc/usb dongle idf commit dependent

See merge request rd/esp-iot-solution!548
2021-11-12 01:38:11 +00:00
Tian Sen Wen
1dfe02d3a3 doc(usb_dongle): add idf commit dependent 2021-11-10 16:39:37 +08:00
Li Bo
81f2ac2d7a Merge branch 'feature/usb_uvc_support_hot_plug' into 'usb/add_usb_solutions'
feature(uvc): usb host uvc support hot plug

See merge request rd/esp-iot-solution!547
2021-11-04 08:35:10 +00:00
Li Bo
7a3fb2c187 docs(uvc): add english version uvc component readme 2021-11-04 16:30:29 +08:00
Li Bo
bc12b4102e examples(uvc): using idf api to change usb phy 2021-11-04 15:20:11 +08:00
Li Bo
53dc96f9de feature(uvc): usb host uvc support hot plug 2021-10-28 20:50:27 +08:00
Li Bo
a6dad407bd Merge branch 'bugfix/uvc_streaming_stop' into 'usb/add_usb_solutions'
fix(host/uvc_stream): fix esp32s3 can't stop streaming

See merge request rd/esp-iot-solution!544
2021-09-29 13:29:43 +00:00
Li Bo
037380aaf6 fix(host/uvc_stream): fix esp32s3 can't stop streaming 2021-09-27 12:59:32 +08:00
Xu Chun Guang
ef92581eb6 Merge branch 'doc/update_usb_dongle_readme' into 'usb/add_usb_solutions'
doc: update usb_dongle readme

See merge request rd/esp-iot-solution!540
2021-09-09 06:20:48 +00:00
Li Bo
933f12f618 Merge branch 'usb/idf_usb_api_update' into 'usb/add_usb_solutions'
update: upstream idf usb api update

See merge request rd/esp-iot-solution!541
2021-09-06 12:42:36 +00:00
Li Bo
346dfe8f0f update: adapt upstream idf api to latest d5f58ab135
Breakchange: some usb related api renamed
2021-09-06 19:21:13 +08:00
Tian Sen Wen
208e240c8a doc: update usb_dongle readme 2021-09-01 16:21:40 +08:00
Xu Chun Guang
488f751d4b Merge branch 'feature/add_usb_ble_adapter' into 'usb/add_usb_solutions'
feat: add BLE adapter into USB_Dongle

See merge request rd/esp-iot-solution!533
2021-08-31 07:18:39 +00:00
Xu Long Zhuang
787e6c10d1 fix: get le acl buffer size frist 2021-08-31 14:06:28 +08:00
Li Bo
0d91ac2e89 Merge branch 'fix/usb_examples_add_default_iot_solution_path' into 'usb/add_usb_solutions'
fix(examples/usb): cmakelist add default iot_solution path

See merge request rd/esp-iot-solution!539
2021-08-30 11:26:19 +00:00
Li Bo
f8dc3f333c fix(examples/usb): cmakelist add default iot_solution path
fix #124, fix #126
2021-08-30 18:05:45 +08:00
Tian Sen Wen
a686eacd35 doc: supplement BLE adapter description 2021-08-30 11:39:34 +08:00
Tian Sen Wen
84582cc3db feat: add BLE adapter into USB_Dongle 2021-08-30 11:39:24 +08:00
Li Bo
0c94b50a29 Merge branch '4g_module/support_a7600c1' into 'usb/add_usb_solutions'
4g_module: support a7600c1

See merge request rd/esp-iot-solution!536
2021-08-19 07:53:39 +00:00
Li Bo
02be652859 4g_module: support a7600c1 2021-08-19 14:46:29 +08:00
Xu Long Zhuang
3174999337 feat: add BLE S3 adapter for linux system 2021-08-19 11:05:30 +08:00
Xu Chun Guang
a704157929 Merge branch 'feature/USB_Dongle_WiFi' into 'usb/add_usb_solutions'
feature(usb_dongle): add USB Dongle WiFi Solution

See merge request rd/esp-iot-solution!521
2021-08-19 02:57:13 +00:00
Tian Sen Wen
f35216cc9d feat(usb_rndis): add USB Dongle WiFi Solution 2021-08-19 10:01:37 +08:00
Li Bo
914d1d7344 Merge branch 'usb/cdc_4g_module_optimize' into 'usb/add_usb_solutions'
usb: cdc 4g module optimize

See merge request rd/esp-iot-solution!520
2021-08-17 12:56:18 +00:00
Li Bo
dd9eb1ef44 4g_module: support air724ug ec600n
fix #111, fix #116
2021-08-17 19:53:02 +08:00
Li Bo
9fc08ebf22 usbh_cdc: control line state can be configured
fix #121
2021-08-17 19:35:42 +08:00
Li Bo
5ea34d5c55 4g_module: printf current ringbuffer load 2021-08-17 15:59:40 +08:00
Li Bo
1e13bff142 4g_module: add sdkconfig.defaults for different target 2021-08-17 15:59:34 +08:00
Li Bo
542c1a1dd3 4g_module: using notify to handle usb event 2021-08-17 15:50:34 +08:00
Li Bo
c590714a19 usbh_cdc: usb cdc task can be configured 2021-08-17 15:50:34 +08:00
Li Bo
792cbacb39 Merge branch 'examples/modify_usb_example_dir_structure' into 'usb/add_usb_solutions'
examples: modify usb example dir structure

See merge request rd/esp-iot-solution!534
2021-08-16 04:45:00 +00:00
Li Bo
d653f66ab5 host(cdc): remove examples to usb host directory 2021-08-16 11:50:05 +08:00
Li Bo
811c8b8cc9 host(uvc): remove examples to usb host directory 2021-08-16 11:47:29 +08:00
Li Bo
2df6f18863 device(msc/cdc): remove examples to usb device directory 2021-08-16 11:09:50 +08:00
Li Bo
b5fabb2778 Merge branch 'usb/add_tinyusb_examples' into 'usb/add_usb_solutions'
usb: add tinyusb examples

See merge request rd/esp-iot-solution!526
2021-08-13 05:56:13 +00:00
Li Bo
38ac718a5a example(device/msc): add usb msc wireless disk 2021-08-13 13:18:13 +08:00
Li Bo
5d36355d0b examples(device/cdc): add usb_uart_bridge example 2021-08-13 13:18:05 +08:00
Li Bo
22336d984e Merge branch 'usb/add_tinyusb_additions' into 'usb/add_usb_solutions'
usb: add tinyusb additions

See merge request rd/esp-iot-solution!524
2021-08-12 08:50:23 +00:00
Li Bo
ee9d396085 Merge branch 'boards/add_esp32s3_usb_otg_board' into 'usb/add_usb_solutions'
boards: add esp32s3-usb-otg-ev board

See merge request rd/esp-iot-solution!523
2021-08-12 02:06:15 +00:00
Tian Sen Wen
fdd3d3ef37 Merge branch 'bugfix/EXTRA_COMPONENT_DIRS_for_FreeRTOS-Plus-CLI' into 'usb/add_usb_solutions'
fix: modify EXTRA_COMPONENT_DIRS for FreeRTOS-Plus-CLI

See merge request rd/esp-iot-solution!525
2021-08-11 12:06:56 +00:00
Li Bo
290ca8f1b5 boards: add board_common_wifi 2021-08-11 19:58:26 +08:00
Tian Sen Wen
645b69c57d fix: modify EXTRA_COMPONENT_DIRS for FreeRTOS-Plus-CLI 2021-08-11 19:12:58 +08:00
Li Bo
b99a30d93b boards: add free pin definitions 2021-08-11 17:28:28 +08:00
Li Bo
d6e371b33a config_descriptor: clean remote-wakeup bit as esp32sx currently not support 2021-08-11 15:04:46 +08:00
Li Bo
023172596c device(cdc): support dual interface 2021-08-11 15:04:46 +08:00
Li Bo
6ec820ac67 device(msc): eject after all logical disk disable 2021-08-11 15:04:46 +08:00
Li Bo
d2a03219b7 idf:update to 81448dc 2021-08-11 15:03:24 +08:00
Li Bo
f29697dc91 tinyusb: add msc and hid support 2021-08-11 15:01:33 +08:00
Li Bo
7aee86cbb5 tinyusb: idf update to 2c1cc50 2021-08-11 14:59:13 +08:00
Li Bo
5efcb5545a boards: add esp32s3-usb-otg-ev 2021-08-11 14:38:18 +08:00
Li Bo
31f5d3746a boards: unified common functions 2021-08-11 13:50:38 +08:00
Li Bo
e7b276ed06 Merge branch 'feature/FreeRTOS_CLI' into 'usb/add_usb_solutions'
Feature/FreeRTOS-CLI

See merge request rd/esp-iot-solution!519
2021-08-10 06:30:02 +00:00
Tian Sen Wen
fec57dd42c fix(FreeRTOS_CLI): Adapt to ESP-IDF platform 2021-08-10 14:19:35 +08:00
Tian Sen Wen
41789a1ecc feat: add FreeRTOS CLI component 2021-08-10 14:19:35 +08:00
Li Bo
72327d4242 feature(4g_module): support manually set dhcp dns 2021-08-04 14:47:34 +08:00
Li Bo
93bb98de86 fix(4g_module): fix ap dhcp dns problem 2021-08-04 10:51:36 +08:00
Li Bo
45506dc8f1 Merge branch 'usb/reconnect_after_4g_lost' into 'usb/add_usb_solutions'
usb/reconnect after 4g lost

See merge request rd/esp-iot-solution!518
2021-08-02 13:59:40 +00:00
Li Bo
1b33e0226e fix(4g_module):try re-dial if 4g lost 2021-08-02 21:19:54 +08:00
Li Bo
5e97e2d4b5 Merge branch 'usb/fix_4g_module_gpio' into 'usb/add_usb_solutions'
fix(4g_module): led/control gpio can be 0 if not use

See merge request rd/esp-iot-solution!517
2021-08-02 13:00:20 +00:00
Li Bo
f10e7d3a34 fix(4g_module): led/control gpio can be 0 if not use 2021-08-02 20:06:16 +08:00
Li Bo
e7f7cdac8e Merge branch 'usb/add_cdc_4g_module' into 'usb/add_usb_solutions'
examples(cdc_4g_module): support usb cdc mode 4g module

See merge request rd/esp-iot-solution!515
2021-07-26 10:52:31 +00:00
Li Bo
62e99f7fee examples(cdc_4g_module): add usb host driving 4g module 2021-07-26 18:34:50 +08:00
Li Bo
07a7c1da7e Merge branch 'usb/add_usb_cdc_driver' into 'usb/add_usb_solutions'
usb(cdc): add usb host cdc driver

See merge request rd/esp-iot-solution!514
2021-07-26 10:26:20 +00:00
Li Bo
afe63e2e5f Merge branch 'usb/add_usb_cdc_driver' into 'usb/add_usb_solutions'
usb(cdc): add usb host cdc driver

See merge request rd/esp-iot-solution!514
2021-07-26 10:26:17 +00:00
Li Bo
066757fb98 usb(cdc): add usb host cdc driver 2021-07-26 17:51:45 +08:00
Li Bo
15400ccc22 Merge branch 'usb/uvc_esp32s3_support' into 'usb/add_usb_solutions'
usb(uvc): add esp32s3 support

See merge request rd/esp-iot-solution!513
2021-07-20 04:01:42 +00:00
Li Bo
313c23ae87 usb(uvc): add esp32s3 support 2021-07-20 12:01:38 +08:00
Li Bo
cefcbf27e5 Merge branch 'usb/uvc_hcd_change_irp_to_urb' into 'usb/add_usb_solutions'
usb/uvc hcd change irp to urb

See merge request rd/esp-iot-solution!512
2021-06-29 10:05:48 +00:00
Li Bo
aeaf16c5ac IDF: update idf patch 2021-06-29 16:26:26 +08:00
Li Bo
6d4be32e9f hcd: change irp to urb 2021-06-18 16:24:38 +08:00
Li Bo
ef20483dcc examples(usb_camera_lcd_display): using camera real stream by default 2021-06-07 11:24:48 +08:00
Li Bo
f8d96c2443 usb: add latest esp-idf patch 2021-06-04 20:39:53 +08:00
Li Bo
6af80a8a43 readme: fix broken reference 2021-06-04 20:38:20 +08:00
Li Bo
43b99ab617 ci: sync usb_solutions to github 2021-06-04 19:56:00 +08:00
Li Bo
310c5a9c62 examples(uvc): update readme 2021-06-04 17:52:43 +08:00
Li Bo
e3a9a43d99 examples(uvc): add usb camera sdcard saving 2021-06-04 17:36:38 +08:00
Li Bo
be73c2f267 examples(uvc): add usb camera wifi http transfer 2021-06-04 17:36:04 +08:00
Li Bo
630da9f721 examples(uvc): add usb camera lcd display 2021-06-04 17:35:40 +08:00
Li Bo
6bd97c7fcb usb: add esp-idf usb temp patch 2021-06-04 17:34:40 +08:00
Li Bo
aa4d43c899 uvc_stream: add usb camera component 2021-06-04 17:24:35 +08:00
519 changed files with 140343 additions and 582 deletions

6
.gitignore vendored
View file

@ -44,4 +44,8 @@ tools/unit-test-app/builds
tools/unit-test-app/output
# ESP-IDF library
build
build
# Local ENV
.envrc
dependencies.lock

View file

@ -91,6 +91,7 @@ push_to_github:
only:
- master
- /^release\/v/
- usb/add_usb_solutions
# when: on_success
image: $CI_DOCKER_REGISTRY/esp32-ci-env
variables:

View file

@ -21,5 +21,6 @@ list(APPEND EXTRA_COMPONENT_DIRS
"$ENV{IOT_SOLUTION_PATH}/components/sensors/pressure"
"$ENV{IOT_SOLUTION_PATH}/components/storage"
"$ENV{IOT_SOLUTION_PATH}/components/storage/eeprom"
"$ENV{IOT_SOLUTION_PATH}/components/FreeRTOS-Plus-CLI"
)

View file

@ -17,3 +17,4 @@ EXTRA_COMPONENT_DIRS += $(IOT_SOLUTION_PATH)/components/sensors/light_sensor
EXTRA_COMPONENT_DIRS += $(IOT_SOLUTION_PATH)/components/sensors/pressure
EXTRA_COMPONENT_DIRS += $(IOT_SOLUTION_PATH)/components/storage
EXTRA_COMPONENT_DIRS += $(IOT_SOLUTION_PATH)/components/storage/eeprom
EXTRA_COMPONENT_DIRS += $(IOT_SOLUTION_PATH)/components/FreeRTOS-Plus-CLI

View file

@ -0,0 +1,3 @@
idf_component_register(SRCS "FreeRTOS_CLI.c"
INCLUDE_DIRS include
PRIV_REQUIRES )

View file

@ -0,0 +1,359 @@
/*
* FreeRTOS+CLI V1.0.4
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
/* Standard includes. */
#include <string.h>
#include <stdint.h>
/* FreeRTOS includes. */
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
/* Utils includes. */
#include "FreeRTOS_CLI.h"
/* If the application writer needs to place the buffer used by the CLI at a
fixed address then set configAPPLICATION_PROVIDES_cOutputBuffer to 1 in
FreeRTOSConfig.h, then declare an array with the following name and size in
one of the application files:
char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
*/
#ifndef configAPPLICATION_PROVIDES_cOutputBuffer
#define configAPPLICATION_PROVIDES_cOutputBuffer 0
#endif
PRIVILEGED_DATA portMUX_TYPE xCLIMux = portMUX_INITIALIZER_UNLOCKED;
typedef struct xCOMMAND_INPUT_LIST
{
const CLI_Command_Definition_t *pxCommandLineDefinition;
struct xCOMMAND_INPUT_LIST *pxNext;
} CLI_Definition_List_Item_t;
/*
* The callback function that is executed when "help" is entered. This is the
* only default command that is always present.
*/
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
/*
* Return the number of parameters that follow the command name.
*/
static int8_t prvGetNumberOfParameters( const char *pcCommandString );
/* The definition of the "help" command. This command is always at the front
of the list of registered commands. */
static const CLI_Command_Definition_t xHelpCommand =
{
"help",
"help:\r\n Lists all the registered commands\r\n\r\n",
prvHelpCommand,
0
};
/* The definition of the list of commands. Commands that are registered are
added to this list. */
static CLI_Definition_List_Item_t xRegisteredCommands =
{
&xHelpCommand, /* The first command in the list is always the help command, defined in this file. */
NULL /* The next pointer is initialised to NULL, as there are no other registered commands yet. */
};
/* A buffer into which command outputs can be written is declared here, rather
than in the command console implementation, to allow multiple command consoles
to share the same buffer. For example, an application may allow access to the
command interpreter by UART and by Ethernet. Sharing a buffer is done purely
to save RAM. Note, however, that the command console itself is not re-entrant,
so only one command interpreter interface can be used at any one time. For that
reason, no attempt at providing mutual exclusion to the cOutputBuffer array is
attempted.
configAPPLICATION_PROVIDES_cOutputBuffer is provided to allow the application
writer to provide their own cOutputBuffer declaration in cases where the
buffer needs to be placed at a fixed address (rather than by the linker). */
#if( configAPPLICATION_PROVIDES_cOutputBuffer == 0 )
static char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
#else
extern char cOutputBuffer[ configCOMMAND_INT_MAX_OUTPUT_SIZE ];
#endif
/*-----------------------------------------------------------*/
void FreeRTOS_CLICreatMux(void)
{
vPortCPUInitializeMutex( &xCLIMux );
}
/*-----------------------------------------------------------*/
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister )
{
static CLI_Definition_List_Item_t *pxLastCommandInList = &xRegisteredCommands;
CLI_Definition_List_Item_t *pxNewListItem;
BaseType_t xReturn = pdFAIL;
/* Check the parameter is not NULL. */
configASSERT( pxCommandToRegister );
/* Create a new list item that will reference the command being registered. */
pxNewListItem = ( CLI_Definition_List_Item_t * ) pvPortMalloc( sizeof( CLI_Definition_List_Item_t ) );
configASSERT( pxNewListItem );
if( pxNewListItem != NULL )
{
taskENTER_CRITICAL( &xCLIMux );
{
/* Reference the command being registered from the newly created
list item. */
pxNewListItem->pxCommandLineDefinition = pxCommandToRegister;
/* The new list item will get added to the end of the list, so
pxNext has nowhere to point. */
pxNewListItem->pxNext = NULL;
/* Add the newly created list item to the end of the already existing
list. */
pxLastCommandInList->pxNext = pxNewListItem;
/* Set the end of list marker to the new list item. */
pxLastCommandInList = pxNewListItem;
}
taskEXIT_CRITICAL( &xCLIMux );
xReturn = pdPASS;
}
return xReturn;
}
/*-----------------------------------------------------------*/
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen )
{
static const CLI_Definition_List_Item_t *pxCommand = NULL;
BaseType_t xReturn = pdTRUE;
const char *pcRegisteredCommandString;
size_t xCommandStringLength;
/* Note: This function is not re-entrant. It must not be called from more
thank one task. */
if( pxCommand == NULL )
{
/* Search for the command string in the list of registered commands. */
for( pxCommand = &xRegisteredCommands; pxCommand != NULL; pxCommand = pxCommand->pxNext )
{
pcRegisteredCommandString = pxCommand->pxCommandLineDefinition->pcCommand;
xCommandStringLength = strlen( pcRegisteredCommandString );
/* To ensure the string lengths match exactly, so as not to pick up
a sub-string of a longer command, check the byte after the expected
end of the string is either the end of the string or a space before
a parameter. */
if( strncmp( pcCommandInput, pcRegisteredCommandString, xCommandStringLength ) == 0 )
{
if( ( pcCommandInput[ xCommandStringLength ] == ' ' ) || ( pcCommandInput[ xCommandStringLength ] == 0x00 ) )
{
/* The command has been found. Check it has the expected
number of parameters. If cExpectedNumberOfParameters is -1,
then there could be a variable number of parameters and no
check is made. */
if( pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters >= 0 )
{
if( prvGetNumberOfParameters( pcCommandInput ) != pxCommand->pxCommandLineDefinition->cExpectedNumberOfParameters )
{
xReturn = pdFALSE;
}
}
break;
}
}
}
}
if( ( pxCommand != NULL ) && ( xReturn == pdFALSE ) )
{
/* The command was found, but the number of parameters with the command
was incorrect. */
strncpy( pcWriteBuffer, "Incorrect command parameter(s). Enter \"help\" to view a list of available commands.\r\n", xWriteBufferLen );
pxCommand = NULL;
}
else if( pxCommand != NULL )
{
/* Call the callback function that is registered to this command. */
xReturn = pxCommand->pxCommandLineDefinition->pxCommandInterpreter( pcWriteBuffer, xWriteBufferLen, pcCommandInput );
/* If xReturn is pdFALSE, then no further strings will be returned
after this one, and pxCommand can be reset to NULL ready to search
for the next entered command. */
if( xReturn == pdFALSE )
{
pxCommand = NULL;
}
}
else
{
/* pxCommand was NULL, the command was not found. */
strncpy( pcWriteBuffer, "Command not recognised. Enter 'help' to view a list of available commands.\r\n", xWriteBufferLen );
xReturn = pdFALSE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
char *FreeRTOS_CLIGetOutputBuffer( void )
{
return cOutputBuffer;
}
/*-----------------------------------------------------------*/
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength )
{
UBaseType_t uxParametersFound = 0;
const char *pcReturn = NULL;
*pxParameterStringLength = 0;
while( uxParametersFound < uxWantedParameter )
{
/* Index the character pointer past the current word. If this is the start
of the command string then the first word is the command itself. */
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
{
pcCommandString++;
}
/* Find the start of the next string. */
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) == ' ' ) )
{
pcCommandString++;
}
/* Was a string found? */
if( *pcCommandString != 0x00 )
{
/* Is this the start of the required parameter? */
uxParametersFound++;
if( uxParametersFound == uxWantedParameter )
{
/* How long is the parameter? */
pcReturn = pcCommandString;
while( ( ( *pcCommandString ) != 0x00 ) && ( ( *pcCommandString ) != ' ' ) )
{
( *pxParameterStringLength )++;
pcCommandString++;
}
if( *pxParameterStringLength == 0 )
{
pcReturn = NULL;
}
break;
}
}
else
{
break;
}
}
return pcReturn;
}
/*-----------------------------------------------------------*/
static BaseType_t prvHelpCommand( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString )
{
static const CLI_Definition_List_Item_t * pxCommand = NULL;
BaseType_t xReturn;
( void ) pcCommandString;
if( pxCommand == NULL )
{
/* Reset the pxCommand pointer back to the start of the list. */
pxCommand = &xRegisteredCommands;
}
/* Return the next command help string, before moving the pointer on to
the next command in the list. */
strncpy( pcWriteBuffer, pxCommand->pxCommandLineDefinition->pcHelpString, xWriteBufferLen );
pxCommand = pxCommand->pxNext;
if( pxCommand == NULL )
{
/* There are no more commands in the list, so there will be no more
strings to return after this one and pdFALSE should be returned. */
xReturn = pdFALSE;
}
else
{
xReturn = pdTRUE;
}
return xReturn;
}
/*-----------------------------------------------------------*/
static int8_t prvGetNumberOfParameters( const char *pcCommandString )
{
int8_t cParameters = 0;
BaseType_t xLastCharacterWasSpace = pdFALSE;
/* Count the number of space delimited words in pcCommandString. */
while( *pcCommandString != 0x00 )
{
if( ( *pcCommandString ) == ' ' )
{
if( xLastCharacterWasSpace != pdTRUE )
{
cParameters++;
xLastCharacterWasSpace = pdTRUE;
}
}
else
{
xLastCharacterWasSpace = pdFALSE;
}
pcCommandString++;
}
/* If the command string ended with spaces, then there will have been too
many parameters counted. */
if( xLastCharacterWasSpace == pdTRUE )
{
cParameters--;
}
/* The value returned is one less than the number of space delimited words,
as the first word should be the command itself. */
return cParameters;
}

View file

@ -0,0 +1,32 @@
Changes between V1.0.3 and V1.0.4 released
+ Update to use stdint and the FreeRTOS specific typedefs that were
introduced in FreeRTOS V8.0.0.
Changes between V1.0.2 and V1.0.3 released
+ Previously, and in line with good software engineering practice, the
FreeRTOS coding standard did not permit the use of char types that were
not explicitly qualified as either signed or unsigned. As a result char
pointers used to reference strings required casts, as did the use of any
standard string handling functions. The casts ensured compiler warnings
were not generated by compilers that defaulted unqualified char types to
be signed or compilers that defaulted unqualified char types to be
unsigned. As it has in later MISRA standards, this rule has now been
relaxed, and unqualified char types are now permitted, but only when:
1) The char is used to point to a human readable text string.
2) The char is used to hold a single ASCII character.
Changes between V1.0.1 and V1.0.2 released 14/10/2013
+ Changed double quotes (") to single quotes (') in the help string to
allow the strings to be used with JSON in FreeRTOS+Nabto.
Changes between V1.0.0 and V1.0.1 released 05/07/2012
+ Change the name of the structure used to map a function that implements
a CLI command to the string used to call the command from
xCommandLineInput to CLI_Command_Definition_t, as it was always intended
to be. A #define was added to map the old name to the new name for
reasons of backward compatibility.

View file

@ -0,0 +1,19 @@
FreeRTOS+CLI is released under the following MIT license.
Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View file

@ -0,0 +1,5 @@
[InternetShortcut]
URL=http://www.freertos.org/cli
IDList=
[{000214A0-0000-0000-C000-000000000046}]
Prop3=19,2

View file

@ -0,0 +1,7 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)
COMPONENT_ADD_INCLUDEDIRS := ./include
COMPONENT_SRCDIRS := .

View file

@ -0,0 +1,115 @@
/*
* FreeRTOS+CLI V1.0.4
* Copyright (C) 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of
* this software and associated documentation files (the "Software"), to deal in
* the Software without restriction, including without limitation the rights to
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
* the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*
* http://www.FreeRTOS.org
* http://aws.amazon.com/freertos
*
* 1 tab == 4 spaces!
*/
#ifndef COMMAND_INTERPRETER_H
#define COMMAND_INTERPRETER_H
#include "freertos/FreeRTOS.h"
/* The prototype to which callback functions used to process command line
commands must comply. pcWriteBuffer is a buffer into which the output from
executing the command can be written, xWriteBufferLen is the length, in bytes of
the pcWriteBuffer buffer, and pcCommandString is the entire string as input by
the user (from which parameters can be extracted).*/
typedef BaseType_t (*pdCOMMAND_LINE_CALLBACK)( char *pcWriteBuffer, size_t xWriteBufferLen, const char *pcCommandString );
/* The structure that defines command line commands. A command line command
should be defined by declaring a const structure of this type. */
typedef struct xCOMMAND_LINE_INPUT
{
const char * const pcCommand; /* The command that causes pxCommandInterpreter to be executed. For example "help". Must be all lower case. */
const char * const pcHelpString; /* String that describes how to use the command. Should start with the command itself, and end with "\r\n". For example "help: Returns a list of all the commands\r\n". */
const pdCOMMAND_LINE_CALLBACK pxCommandInterpreter; /* A pointer to the callback function that will return the output generated by the command. */
int8_t cExpectedNumberOfParameters; /* Commands expect a fixed number of parameters, which may be zero. */
} CLI_Command_Definition_t;
/* For backward compatibility. */
#define xCommandLineInput CLI_Command_Definition_t
#define configCOMMAND_INT_MAX_OUTPUT_SIZE 500
/*
* Create a Mux.
*
* Note: Must be called before FreeRTOS_CLIProcessCommand.
*/
void FreeRTOS_CLICreatMux(void);
/*
* Register the command passed in using the pxCommandToRegister parameter.
* Registering a command adds the command to the list of commands that are
* handled by the command interpreter. Once a command has been registered it
* can be executed from the command line.
*/
BaseType_t FreeRTOS_CLIRegisterCommand( const CLI_Command_Definition_t * const pxCommandToRegister );
/*
* Runs the command interpreter for the command string "pcCommandInput". Any
* output generated by running the command will be placed into pcWriteBuffer.
* xWriteBufferLen must indicate the size, in bytes, of the buffer pointed to
* by pcWriteBuffer.
*
* FreeRTOS_CLIProcessCommand should be called repeatedly until it returns pdFALSE.
*
* pcCmdIntProcessCommand is not reentrant. It must not be called from more
* than one task - or at least - by more than one task at a time.
*/
BaseType_t FreeRTOS_CLIProcessCommand( const char * const pcCommandInput, char * pcWriteBuffer, size_t xWriteBufferLen );
/*-----------------------------------------------------------*/
/*
* A buffer into which command outputs can be written is declared in the
* main command interpreter, rather than in the command console implementation,
* to allow application that provide access to the command console via multiple
* interfaces to share a buffer, and therefore save RAM. Note, however, that
* the command interpreter itself is not re-entrant, so only one command
* console interface can be used at any one time. For that reason, no attempt
* is made to provide any mutual exclusion mechanism on the output buffer.
*
* FreeRTOS_CLIGetOutputBuffer() returns the address of the output buffer.
*/
char *FreeRTOS_CLIGetOutputBuffer( void );
/*
* Return a pointer to the xParameterNumber'th word in pcCommandString.
*/
const char *FreeRTOS_CLIGetParameter( const char *pcCommandString, UBaseType_t uxWantedParameter, BaseType_t *pxParameterStringLength );
#endif /* COMMAND_INTERPRETER_H */

View file

@ -0,0 +1,4 @@
Contains source and header files that implement FreeRTOS+CLI. See
http://www.FreeRTOS.org/cli for documentation and license information.

View file

@ -69,7 +69,11 @@ spi_bus_handle_t spi_bus_create(spi_host_device_t host_id, const spi_config_t *b
.quadhd_io_num = -1,
.max_transfer_sz = bus_conf->max_transfer_sz,
};
#if CONFIG_IDF_TARGET_ESP32S3
int dma_chan = SPI_DMA_CH_AUTO;
#else
int dma_chan = host_id; //set dma channel equals to host_id by default
#endif
esp_err_t ret = spi_bus_initialize(host_id, &buscfg, dma_chan);
SPI_BUS_CHECK(ESP_OK == ret, "spi bus create failed", NULL);
s_spi_bus[index].host_id = host_id;

View file

@ -36,20 +36,32 @@ typedef struct {
static esp_err_t _i2s_lcd_write_data(void *handle, uint16_t data)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv);
return i2s_lcd_write_data(interface_i2s->i2s_lcd_handle, data);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
static esp_err_t _i2s_lcd_write_cmd(void *handle, uint16_t cmd)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv);
return i2s_lcd_write_cmd(interface_i2s->i2s_lcd_handle, cmd);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
static esp_err_t _i2s_lcd_write(void *handle, const uint8_t *data, uint32_t length)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv);
return i2s_lcd_write(interface_i2s->i2s_lcd_handle, data, length);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
static esp_err_t _i2s_lcd_read(void *handle, uint8_t *data, uint32_t length)
@ -59,14 +71,22 @@ static esp_err_t _i2s_lcd_read(void *handle, uint8_t *data, uint32_t length)
static esp_err_t _i2s_lcd_acquire(void *handle)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv);
return i2s_lcd_acquire(interface_i2s->i2s_lcd_handle);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
static esp_err_t _i2s_lcd_release(void *handle)
{
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = __containerof(handle, interface_i2s_handle_t, interface_drv);
return i2s_lcd_release(interface_i2s->i2s_lcd_handle);
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
}
/**--------------------- I2C interface driver ----------------------*/
@ -279,6 +299,7 @@ esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_inte
switch (type) {
case SCREEN_IFACE_8080: {
#ifndef CONFIG_IDF_TARGET_ESP32S3
interface_i2s_handle_t *interface_i2s = heap_caps_malloc(sizeof(interface_i2s_handle_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
LCD_IFACE_CHECK(NULL != interface_i2s, "memory of iface i2s is not enough", ESP_ERR_NO_MEM);
interface_i2s->i2s_lcd_handle = i2s_lcd_driver_init((i2s_lcd_config_t *)config);
@ -297,6 +318,9 @@ esp_err_t scr_interface_create(scr_interface_type_t type, void *config, scr_inte
interface_i2s->interface_drv.bus_release = _i2s_lcd_release;
*out_driver = &interface_i2s->interface_drv;
#else
return ESP_ERR_NOT_SUPPORTED;
#endif
} break;
case SCREEN_IFACE_SPI: {
interface_spi_handle_t *interface_spi = heap_caps_malloc(sizeof(interface_spi_handle_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);

View file

@ -0,0 +1,3 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "include" "${IDF_PATH}/components/usb/private_include"
REQUIRES usb)

View file

@ -0,0 +1,53 @@
menu "USB Host CDC"
config CDC_GET_DEVICE_DESC
bool "Get device descriptor during emum"
default y
config CDC_GET_CONFIG_DESC
bool "Get config descriptor during emum"
default y
config CDC_SEND_DTE_ACTIVE
bool "Set control line state during init"
default y
config USB_TASK_CORE_ID
int "usb task core_id"
default 0
range 0 0 if IDF_TARGET_ESP32S2
range 0 1 if IDF_TARGET_ESP32S3
help
pin usb task to specified core
config USB_TASK_BASE_PRIORITY
int "usb task base priority"
default 5
help
usb task base priority, usb task = base + 2, cdc task = base + 1
config CTRL_TRANSFER_DATA_MAX_BYTES
int "Max control transfer data size (Bytes)"
range 64 2048
default 256
config CDC_BULK_IN_URB_NUM
int "cdc bulk_in urb num"
default 2
help
bulk in urb numbers, increase this to handle heavy traffic
config CDC_BULK_OUT_URB_NUM
int "cdc bulk_out urb num"
default 2
help
bulk out urb numbers, increase this to handle heavy traffic
config CDC_BULK_IN_URB_BUFFER_SIZE
int "cdc bulk_in urb buffer size"
default 512
help
buffer size of each bulk in urb, should be multiple of endpoint mps
config CDC_BULK_OUT_URB_BUFFER_SIZE
int "cdc bulk_out urb buffer size"
default 512
help
buffer size of each bulk out urb
config CDC_USE_TRACE_FACILITY
bool "Trace internal memory status"
default n
help
set to trace internal buffer usage for debug
endmenu

View file

@ -0,0 +1,41 @@
* [中文版本](./README_cn.md)
## USB Host CDC
This component implements a simple version of the USB CDC host function. It only retains the default control endpoint and two bulk transfer endpoints, which streamlines the enumeration logic of the USB host. Users only need to bind the USB CDC device endpoint address to achieve fast Initialization. The component is suitable for the CDC-like vendor classes that require high startup speed.
The design logic of the component API is similar to the [ESP-IDF UART driver](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/uart.html) interface, which can directly replace the UART interface to realize the update of the original code from UART to USB.
## API Guide
1. Using `usbh_cdc_driver_install` to configure and start internal USB tasks, most importantly the CDC bulk endpoint addresses `bulk_in_ep_addr` and `bulk_out_ep_addr` is required to be specified for communication, and the tramsfer buffer memory size needs to be configured too. user is allowed to config descriptor detials too through `bulk_in_ep` and `bulk_out_ep`.
```
/* @brief install usbh cdc driver with bulk endpoint configs and size of internal ringbuffer*/
static usbh_cdc_config_t config = {
/* use default endpoint descriptor with user address */
.bulk_in_ep_addr = EXAMPLE_BULK_IN_EP_ADDR,
.bulk_out_ep_addr = EXAMPLE_BULK_OUT_EP_ADDR,
.rx_buffer_size = IN_RINGBUF_SIZE,
.tx_buffer_size = OUT_RINGBUF_SIZE,
.conn_callback = usb_connect_callback,
.disconn_callback = usb_disconnect_callback,
};
/* install USB host CDC driver */
usbh_cdc_driver_install(&config);
/* Waitting for USB device connected */
usbh_cdc_wait_connect(portMAX_DELAY);
```
2. After the driver initialization, the internal state machine will automatically handle the hot plug of the USB, and user can also configure the hot plug related callback function `conn_callback` `disconn_callback`.
3. `usbh_cdc_wait_connect` can be used to block task until USB CDC Device is connected or timeout.
4. After successfully connected, the host will automatically receive USB data from CDC device to the internal `ringbuffer`, user can poll `usbh_cdc_get_buffered_data_len` to read buffered data size or register a receive callback to get notified when data is ready. Then `usbh_cdc_read_bytes` can be used to read buffered data out.
5. `usbh_cdc_write_bytes` can be used to send data to USB Device. The data is first written to the internal transmit `ringbuffer`then will be sent out during USB bus free.
6. `usbh_cdc_driver_delete` can uninstall the USB driver completely to release all resources.
## Examples
* [usb host cdc basic example](../../../examples/usb/host/usb_cdc_basic)
* [usb host cdc 4G modem](../../../examples/usb/host/usb_cdc_4g_module)

View file

@ -0,0 +1,41 @@
* [English version](./README.md)
## USB Host CDC
该组件实现了简易版本的 USB CDC 主机功能,仅保留了默认控制传输端点和 2 个批量传输端点,精简了 USB 主机的枚举逻辑,用户只需绑定 USB CDC 设备端点地址即可实现快速的初始化,适用于对启动速度要求较高的自定义类设备。
该组件 API 的设计逻辑与 [ESP-IDF UART 驱动](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/uart.html)接口类似,可直接替换 UART 接口,实现原有代码 UART-> USB 的更新。
## API 使用说明
1. 使用 `usbh_cdc_driver_install` 来配置和启动内部 USB 任务,最重要的是需要指定 CDC 批量端点地址 `bulk_in_ep_addr``bulk_out_ep_addr` 用于通信,并且还需要配置 tramsfer 缓冲区内存大小。 用户也可以通过 `bulk_in_ep``bulk_out_ep` 配置整个描述符详细信息。
```
/* @brief install usbh cdc driver with bulk endpoint configs and size of internal ringbuffer*/
static usbh_cdc_config_t config = {
/* use default endpoint descriptor with user address */
.bulk_in_ep_addr = EXAMPLE_BULK_IN_EP_ADDR,
.bulk_out_ep_addr = EXAMPLE_BULK_OUT_EP_ADDR,
.rx_buffer_size = IN_RINGBUF_SIZE,
.tx_buffer_size = OUT_RINGBUF_SIZE,
.conn_callback = usb_connect_callback,
.disconn_callback = usb_disconnect_callback,
};
/* install USB host CDC driver */
usbh_cdc_driver_install(&config);
/* Waitting for USB device connected */
usbh_cdc_wait_connect(portMAX_DELAY);
```
2. 初始化成功以后,内部状态机会自动处理 USB 的热插拔,用户也可以在初始化时配置热插拔相关回调函数 `conn_callback``disconn_callback`
3. `usbh_cdc_wait_connect` 可用于阻塞任务,直到 USB CDC 设备连接或超时。
4. 连接成功后,主机会自动从 CDC 设备接收 USB 数据到内部 `ringbuffer`,用户可以轮询`usbh_cdc_get_buffered_data_len` 来读取缓存数据的大小或注册接收回调以在数据准备好时得到通知。然后可以使用 `usbh_cdc_read_bytes` 来读取缓冲的数据。
5. `usbh_cdc_write_bytes` 可用于向 USB 设备发送数据。 数据首先写入内部 `ringbuffer`,然后在 USB 总线空闲时发送出去。
5、`usbh_cdc_driver_delete` 可以完全卸载 USB 驱动,释放所有资源。
## Examples
* [CDC 基础功能示例](../../../examples/usb/host/usb_cdc_basic)
* [CDC 4G 上网](../../../examples/usb/host/usb_cdc_4g_module)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,113 @@
// Copyright 2019-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "stdio.h"
#include "freertos/FreeRTOS.h"
#include "esp_err.h"
#include "hal/usbh_ll.h"
#include "hcd.h"
/**
* @brief USB receive callback type
*/
typedef void(*usbh_cdc_cb_t)(void *arg);
/**
* @brief USB host CDC configuration type, callbacks should not in block state
*/
typedef struct usbh_cdc_config{
uint8_t bulk_in_ep_addr; /*!< USB CDC bulk in endpoint address, will be overwritten if bulk_in_ep is specified */
uint8_t bulk_out_ep_addr; /*!< USB CDC bulk out endpoint address, will be overwritten if bulk_out_ep is specified */
int rx_buffer_size; /*!< USB receive/in ringbuffer size */
int tx_buffer_size; /*!< USB transport/out ringbuffer size */
usb_ep_desc_t *bulk_in_ep; /*!< USB CDC bulk in endpoint descriptor, set NULL if using default param */
usb_ep_desc_t *bulk_out_ep; /*!< USB CDC bulk out endpoint descriptor, set NULL if using default param */
usbh_cdc_cb_t conn_callback; /*!< USB connect calllback, set NULL if not use */
usbh_cdc_cb_t disconn_callback; /*!< USB disconnect calllback, usb cdc driver reset to connect waitting after disconnect, set NULL if not use */
usbh_cdc_cb_t rx_callback; /*!< packet receive callback, set NULL if not use */
void *conn_callback_arg; /*!< USB connect calllback args, set NULL if not use */
void *disconn_callback_arg; /*!< USB disconnect calllback args, set NULL if not use */
void *rx_callback_arg; /*!< packet receive callback args, set NULL if not use */
}usbh_cdc_config_t;
/**
* @brief Install USB CDC driver.
*
* @param config USB Host CDC configs
* @return
* ESP_ERR_INVALID_STATE driver has been installed
* ESP_ERR_INVALID_ARG args not supported
* ESP_FAIL driver install failed
* ESP_OK driver install succeed
*/
esp_err_t usbh_cdc_driver_install(const usbh_cdc_config_t *config);
/**
* @brief Uninstall USB driver.
*
* @return
* ESP_ERR_INVALID_STATE driver not installed
* ESP_OK start succeed
*/
esp_err_t usbh_cdc_driver_delete(void);
/**
* @brief Waitting until CDC device connect
*
* @param ticks_to_wait Wait timeout value, count in RTOS ticks
* @return
* ESP_ERR_INVALID_STATE driver not installed
* ESP_ERR_TIMEOUT wait timeout
* ESP_OK device connect succeed
*/
esp_err_t usbh_cdc_wait_connect(TickType_t ticks_to_wait);
/**
* @brief Send data to connected USB device from a given buffer and length,
* this function will return after copying all the data to tx ring buffer.
*
* @param buf data buffer address
* @param length data length to send
* @return int The number of bytes pushed to the tx buffer
*/
int usbh_cdc_write_bytes(const uint8_t *buf, size_t length);
/**
* @brief Get USB receive ring buffer cached data length.
*
* @param size
* @return
* ESP_ERR_INVALID_STATE cdc not configured, or not running
* ESP_ERR_INVALID_ARG args not supported
* ESP_FAIL start failed
* ESP_OK start succeed
*/
esp_err_t usbh_cdc_get_buffered_data_len(size_t *size);
/**
* @brief Read data bytes from USB receive/in buffer.
*
* @param buf data buffer address
* @param length data length to read
* @param ticks_to_wait sTimeout, count in RTOS ticks
* @return int The number of bytes read from USB FIFO
*/
int usbh_cdc_read_bytes(uint8_t *buf, size_t length, TickType_t ticks_to_wait);
/**
* @brief print internal memory usage for debug
* @return void
*/
void usbh_cdc_print_buffer_msg(void);

View file

@ -0,0 +1,2 @@
idf_component_register(SRC_DIRS "."
PRIV_REQUIRES cmock test_utils esp_usbh_cdc)

View file

@ -0,0 +1,96 @@
// Copyright 2019-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "string.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "unity.h"
#include "test_utils.h"
#include "esp_usbh_cdc.h"
#include "esp_log.h"
#define IN_RINGBUF_SIZE (1024 * 1)
#define OUT_RINGBUF_SIZE (1024 * 1)
#define READ_TASK_KILL_BIT BIT1
#define TAG "host_cdc"
EventGroupHandle_t s_event_group_hdl = NULL;
static usb_ep_desc_t bulk_out_ep_desc = {
.bLength = sizeof(usb_ep_desc_t),
.bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
.bEndpointAddress = 0x01, //EP 1 OUT
.bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
.wMaxPacketSize = 64, //MPS of 64 bytes
.bInterval = 0,
};
static usb_ep_desc_t bulk_in_ep_desc = {
.bLength = sizeof(usb_ep_desc_t),
.bDescriptorType = USB_B_DESCRIPTOR_TYPE_ENDPOINT,
.bEndpointAddress = 0x81, //EP 2 IN
.bmAttributes = USB_BM_ATTRIBUTES_XFER_BULK,
.wMaxPacketSize = 64, //MPS of 64 bytes
.bInterval = 0,
};
static void usb_read_task(void *param)
{
size_t data_len = 0;
uint8_t buf[256];
while (!(xEventGroupGetBits(s_event_group_hdl) & READ_TASK_KILL_BIT)) {
usbh_cdc_get_buffered_data_len(&data_len);
if (data_len == 0 || data_len > 256) {
vTaskDelay(1);
continue;
}
usbh_cdc_read_bytes(buf, data_len, 10);
ESP_LOGI(TAG, "RCV len=%d: %.*s", data_len, data_len, buf);
}
xEventGroupClearBits(s_event_group_hdl, READ_TASK_KILL_BIT);
ESP_LOGW(TAG, "CDC read task deleted");
vTaskDelete(NULL);
}
TEST_CASE("usb cdc R/W", "[esp_usbh_cdc]")
{
s_event_group_hdl = xEventGroupCreate();
TEST_ASSERT(s_event_group_hdl);
static usbh_cdc_config_t config = {
.bulk_in_ep = &bulk_in_ep_desc,
.bulk_out_ep = &bulk_out_ep_desc,
.rx_buffer_size = IN_RINGBUF_SIZE,
.tx_buffer_size = OUT_RINGBUF_SIZE,
};
TEST_ASSERT_EQUAL(ESP_OK, usbh_cdc_driver_install(&config));
/* Waitting for USB device connected */
TEST_ASSERT_EQUAL(ESP_OK, usbh_cdc_wait_connect(portMAX_DELAY));
xTaskCreate(usb_read_task, "usb_read", 4096, NULL, 2, NULL);
uint8_t buff[] = "AT\r\n";
uint8_t loop_num = 40;
while (--loop_num) {
int len = usbh_cdc_write_bytes(buff, 4);
ESP_LOGI(TAG, "Send len=%d: %s", len, buff);
vTaskDelay(pdMS_TO_TICKS(500));
}
xEventGroupSetBits(s_event_group_hdl, READ_TASK_KILL_BIT);
ESP_LOGW(TAG, "Waiting CDC read task delete");
while (xEventGroupGetBits(s_event_group_hdl) & READ_TASK_KILL_BIT) {
vTaskDelay(10 / portTICK_PERIOD_MS);
}
vEventGroupDelete(s_event_group_hdl);
TEST_ASSERT_EQUAL(ESP_OK, usbh_cdc_driver_delete());
}

View file

@ -0,0 +1,126 @@
idf_build_get_property(target IDF_TARGET)
set(srcs)
set(includes_public)
set(includes_private)
set(compile_options)
if(CONFIG_TINYUSB)
if(target STREQUAL "esp32s3")
set(tusb_mcu "OPT_MCU_ESP32S3")
set(tusb_family "esp32sx")
elseif(target STREQUAL "esp32s2")
set(tusb_mcu "OPT_MCU_ESP32S2")
set(tusb_family "esp32sx")
else()
# CONFIG_TINYUSB dependency has been garanteed by Kconfig logic,
# So it's not possible that cmake goes here
message(FATAL_ERROR "TinyUSB is not support on ${target}.")
return()
endif()
list(APPEND compile_options
"-DCFG_TUSB_MCU=${tusb_mcu}"
"-DCFG_TUSB_DEBUG=${CONFIG_TINYUSB_DEBUG_LEVEL}"
)
idf_component_get_property(freertos_component_dir freertos COMPONENT_DIR)
list(APPEND includes_private
"${IDF_PATH}/components/tinyusb/tinyusb/hw/bsp/"
"${IDF_PATH}/components/tinyusb/tinyusb/src/"
"${IDF_PATH}/components/tinyusb/tinyusb/src/device"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/bth"
#"additions/include_private"
)
list(APPEND includes_public
"${IDF_PATH}/components/tinyusb/tinyusb/src/"
"additions/include"
"additions/include_private"
"additions/tusb/src/lib/networking"
"additions/tusb/src/class/dfu"
"additions/tusb/src/class/vendor"
# The FreeRTOS API include convention in tinyusb is different from esp-idf
"${freertos_component_dir}/include/freertos"
)
list(APPEND srcs
"${IDF_PATH}/components/tinyusb/tinyusb/src/portable/espressif/${tusb_family}/dcd_${tusb_family}.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/cdc/cdc_device.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/hid/hid_device.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/midi/midi_device.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/msc/msc_device.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/vendor/vendor_device.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/common/tusb_fifo.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/device/usbd_control.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/tusb.c"
#"${IDF_PATH}/components/tinyusb/tinyusb/src/device/usbd.c"
#"${IDF_PATH}/components/tinyusb/additions/src/descriptors_control.c"
#"${IDF_PATH}/components/tinyusb/additions/src/tinyusb.c"
#"${IDF_PATH}/components/tinyusb/additions/src/tusb_tasks.c"
#"${IDF_PATH}/components/tinyusb/additions/src/usb_descriptors.c"
"additions/src/usbd.c"
"additions/src/descriptors_control.c"
"additions/src/tinyusb.c"
"additions/src/tusb_tasks.c"
"additions/src/usb_descriptors.c"
)
# when no builtin class driver is enabled, an uint8_t data compared with `BUILTIN_DRIVER_COUNT` will always be false
set_source_files_properties("tinyusb/src/device/usbd.c" PROPERTIES COMPILE_FLAGS "-Wno-type-limits")
if(CONFIG_TINYUSB_CDCACM_ENABLED)
list(APPEND srcs
"additions/src/cdc.c"
"additions/src/tusb_cdc_acm.c"
"additions/src/tusb_console.c"
"additions/src/vfs_tinyusb.c"
)
endif() # CONFIG_TINYUSB_CDCACM_ENABLED
if(CONFIG_TINYUSB_HID_ENABLED)
list(APPEND srcs
"additions/src/tusb_hid.c")
endif()
if(CONFIG_TINYUSB_MSC_ENABLED)
list(APPEND srcs
"additions/src/tusb_msc.c")
endif()
if(CONFIG_TINYUSB_NET_ENABLED)
list(APPEND srcs
"additions/src/tusb_net.c"
"additions/tusb/src/class/net/net_device.c"
"additions/tusb/src/lib/networking/rndis_reports.c")
endif()
if(CONFIG_TINYUSB_BTH_ENABLED)
list(APPEND srcs
"additions/src/tusb_bth.c"
"${IDF_PATH}/components/tinyusb/tinyusb/src/class/bth/bth_device.c")
endif()
if(CONFIG_TINYUSB_DFU_ENABLED)
list(APPEND srcs
"additions/src/tusb_dfu.c"
"additions/tusb/src/class/dfu/dfu_device.c")
endif()
if(CONFIG_TINYUSB_WEBUSB_ENABLED)
list(APPEND srcs
"additions/src/tusb_webusb.c"
"additions/tusb/src/class/vendor/vendor_device.c")
endif()
endif() # CONFIG_TINYUSB
idf_component_register(SRCS ${srcs}
INCLUDE_DIRS ${includes_public}
PRIV_INCLUDE_DIRS ${includes_private}
PRIV_REQUIRES "vfs" "fatfs" "bt" "app_update"
)
if(CONFIG_TINYUSB)
target_compile_options(${COMPONENT_LIB} PRIVATE ${compile_options})
endif()

View file

@ -0,0 +1,273 @@
menu "TinyUSB Stack"
visible if USB_OTG_SUPPORTED
config TINYUSB
bool "Use TinyUSB Stack"
depends on USB_OTG_SUPPORTED
default n
help
Enable TinyUSB stack support.
Note that, esp-idf only uses the device stack provided by TinyUSB.
if TINYUSB
config TINYUSB_DEBUG_LEVEL
int "TinyUSB log level (0-3)"
default 0
range 0 3
help
Specify verbosity of TinyUSB log output.
menu "TinyUSB task configuration"
config TINYUSB_NO_DEFAULT_TASK
bool "Do not create a TinyUSB task"
default n
help
This option allows to not create the FreeRTOS task during the driver initialization.
User will have to handle TinyUSB events manually.
config TINYUSB_TASK_PRIORITY
int "TinyUSB task priority"
default 5
depends on !TINYUSB_NO_DEFAULT_TASK
help
Set the priority of the default TinyUSB main task.
config TINYUSB_TASK_STACK_SIZE
int "TinyUSB task stack size (bytes)"
default 4096
depends on !TINYUSB_NO_DEFAULT_TASK
help
Set the stack size of the default TinyUSB main task.
endmenu
menu "Descriptor configuration"
config TINYUSB_DESC_USE_ESPRESSIF_VID
bool "VID: Use Espressif's vendor ID"
default y
help
Enable this option, USB device will use Espressif's vendor ID as its VID.
This is helpful at product develop stage.
config TINYUSB_DESC_CUSTOM_VID
hex "VID: Custom vendor ID"
default 0x1234
depends on !TINYUSB_DESC_USE_ESPRESSIF_VID
help
Custom Vendor ID.
config TINYUSB_DESC_USE_DEFAULT_PID
bool "PID: Use a default PID assigned to TinyUSB"
default y
help
Default TinyUSB PID assigning uses values 0x4000...0x4007.
config TINYUSB_DESC_CUSTOM_PID
hex "PID: Custom product ID"
default 0x5678
depends on !TINYUSB_DESC_USE_DEFAULT_PID
help
Custom Product ID.
config TINYUSB_DESC_BCD_DEVICE
hex "bcdDevice"
default 0x0100
help
Version of the firmware of the USB device.
config TINYUSB_DESC_MANUFACTURER_STRING
string "Manufacturer name"
default "Espressif Systems"
help
Name of the manufacturer of the USB device.
config TINYUSB_DESC_PRODUCT_STRING
string "Product name"
default "Espressif Device"
help
Name of the USB device.
config TINYUSB_DESC_SERIAL_STRING
string "Serial string"
default "123456"
help
Serial number of the USB device.
config TINYUSB_DESC_CDC_STRING
depends on TINYUSB_CDC_ENABLED
string "CDC Device String"
default "Espressif CDC Device"
help
Name of the CDC device.
config TINYUSB_DESC_MSC_STRING
depends on TINYUSB_MSC_ENABLED
string "MSC Device String"
default "Espressif MSC Device"
help
Name of the MSC device.
config TINYUSB_DESC_HID_STRING
depends on TINYUSB_HID_ENABLED
string "HID Device String"
default "Espressif HID Device"
help
Name of the HID device
config TINYUSB_DESC_NET_STRING
depends on TINYUSB_NET_ENABLED
string "NET Device String"
default "Espressif NET Device"
help
Name of the NET device.
config TINYUSB_DESC_BTH_STRING
depends on TINYUSB_BTH_ENABLED
string "BTH String"
default "Espressif BTH Device"
help
Name of the BTH device.
endmenu # "Descriptor configuration"
menu "Massive Storage Class (MSC)"
config TINYUSB_MSC_ENABLED
bool "Enable TinyUSB MSC feature"
default n
help
Enable TinyUSB MSC feature.
config TINYUSB_MSC_BUFSIZE
depends on TINYUSB_MSC_ENABLED
int "MSC FIFO size"
default 512
help
MSC FIFO size, in bytes.
endmenu # "Massive Storage Class"
menu "Communication Device Class (CDC)"
config TINYUSB_CDC_ENABLED
bool "Enable TinyUSB CDC feature"
default n
help
Enable TinyUSB CDC feature.
config TINYUSB_CDC_RX_BUFSIZE
depends on TINYUSB_CDC_ENABLED
int "CDC FIFO size of RX channel"
default 64
help
CDC FIFO size of RX channel.
config TINYUSB_CDC_TX_BUFSIZE
depends on TINYUSB_CDC_ENABLED
int "CDC FIFO size of TX channel"
default 64
help
CDC FIFO size of TX channel.
menu "Abstract Control Model (ACM)"
depends on TINYUSB_CDC_ENABLED
config TINYUSB_CDCACM_ENABLED
bool "Enable TinyUSB CDC-ACM feature"
default n
help
Enable TinyUSB CDC-ACM feature.
config TINYUSB_CDC_PORT_NUM
depends on TINYUSB_CDCACM_ENABLED
int "Number of Serial (CDC) Port"
default 1
range 1 2
help
Number of Serial (CDC) Port.
endmenu # "Abstract Control Model"
endmenu # "Communication Device Class"
menu "WebUSB"
config TINYUSB_WEBUSB_ENABLED
bool "Enable TinyUSB WebUSB feature"
default n
help
Enable TinyUSB WebUSB feature.
endmenu # "WebUSB"
menu "Human Interface Device Class (HID)"
config TINYUSB_HID_ENABLED
bool "Enable TinyUSB HID feature"
default n
help
Enable TinyUSB HID feature.
config TINYUSB_HID_BUFSIZE
int "HID FIFO size"
default 64
depends on TINYUSB_HID_ENABLED
help
HID FIFO size
endmenu # "Human Interface Device Class"
menu "USB Network Class (RNDIS, ECM)"
config TINYUSB_NET_ENABLED
bool "Enable TinyUSB NET driver"
default n
help
Enable USB NET TinyUSB driver.
config TINYUSB_RNDIS_VENDOR
string "USB Vendor"
default "Espressif Incorporated"
depends on TINYUSB_NET_ENABLED
help
Vendor description.
choice TINYUSB_NET
prompt "Choose a network communication standard"
default TINYUSB_NET_RNDIS
depends on TINYUSB_NET_ENABLED
help
RNDIS and CDC-ECM
- Windows only works with RNDIS
- MacOS only works with CDC-ECM
- Linux will work on both
config TINYUSB_NET_RNDIS
bool "RNDIS"
help
RNDIS.
config TINYUSB_NET_ECM
bool "CDC-ECM"
help
CDC-ECM.
endchoice
endmenu # "usb network"
menu "Bluetooth Host Class (BTH)"
config TINYUSB_BTH_ENABLED
bool "Enable TinyUSB BTH feature"
default n
help
Enable TinyUSB BTH feature.
config TINYUSB_BTH_ISO_ALT_COUNT
depends on TINYUSB_BTH_ENABLED
int "BTH ISO ALT COUNT"
default 0
help
BTH ISO ALT COUNT.
endmenu # "Bluetooth Host Device Class"
menu "Firmware Upgrade Class (DFU)"
config TINYUSB_DFU_ENABLED
bool "Enable TinyUSB DFU feature"
default n
help
Enable TinyUSB DFU feature.
config TINYUSB_DFU_BUFSIZE
depends on TINYUSB_DFU_ENABLED
int "DFU XFER BUFFSIZE"
default 512
help
DFU XFER BUFFSIZE.
endmenu # "Firmware Upgrade Class"
endif # TINYUSB
endmenu # "TinyUSB Stack"

View file

@ -0,0 +1,103 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdbool.h>
#include "tusb.h"
#include "tusb_option.h"
#include "tusb_config.h"
#include "tinyusb_types.h"
#ifdef __cplusplus
extern "C" {
#endif
/* tinyusb uses buffers with type of uint8_t[] but in our driver we are reading them as a 32-bit word */
#if (CFG_TUD_ENDPOINT0_SIZE < 4)
# define CFG_TUD_ENDPOINT0_SIZE 4
# warning "CFG_TUD_ENDPOINT0_SIZE was too low and was set to 4"
#endif
#if TUSB_OPT_DEVICE_ENABLED
# if CFG_TUD_HID
# if (CFG_TUD_HID_BUFSIZE < 4)
# define CFG_TUD_HID_BUFSIZE 4
# warning "CFG_TUD_HID_BUFSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_CDC
# if (CFG_TUD_CDC_EP_BUFSIZE < 4)
# define CFG_TUD_CDC_EP_BUFSIZE 4
# warning "CFG_TUD_CDC_EP_BUFSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_MSC
# if (CFG_TUD_MSC_BUFSIZE < 4)
# define CFG_TUD_MSC_BUFSIZE 4
# warning "CFG_TUD_MSC_BUFSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_MIDI
# if (CFG_TUD_MIDI_EPSIZE < 4)
# define CFG_TUD_MIDI_EPSIZE 4
# warning "CFG_TUD_MIDI_EPSIZE was too low and was set to 4"
# endif
# endif
# if CFG_TUD_CUSTOM_CLASS
# warning "Please check that the buffer is more then 4 bytes"
# endif
#endif
/**
* @brief Configuration structure of the tinyUSB core
*/
typedef struct {
tusb_desc_device_t *descriptor; /*!< Pointer to a device descriptor */
const char **string_descriptor; /*!< Pointer to an array of string descriptors */
const uint8_t *config_descriptor; /*!< Pointer to config descriptors */
bool external_phy; /*!< Should USB use an external PHY */
} tinyusb_config_t;
/**
* @brief This is an all-in-one helper function, including:
* 1. USB device driver initialization
* 2. Descriptors preparation
* 3. TinyUSB stack initialization
* 4. Creates and start a task to handle usb events
*
* @note Don't change Custom descriptor, but if it has to be done,
* Suggest to define as follows in order to match the Interface Association Descriptor (IAD):
* bDeviceClass = TUSB_CLASS_MISC,
* bDeviceSubClass = MISC_SUBCLASS_COMMON,
*
* @param config tinyusb stack specific configuration
* @retval ESP_ERR_INVALID_ARG Install driver and tinyusb stack failed because of invalid argument
* @retval ESP_FAIL Install driver and tinyusb stack failed because of internal error
* @retval ESP_OK Install driver and tinyusb stack successfully
*/
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config);
// TODO esp_err_t tinyusb_driver_uninstall(void); (IDF-1474)
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,32 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#define USB_ESPRESSIF_VID 0x303A
#define USB_STRING_DESCRIPTOR_ARRAY_SIZE 7
typedef enum{
TINYUSB_USBDEV_0,
} tinyusb_usbdev_t;
typedef const char *tusb_desc_strarray_device_t[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,39 @@
/**
* @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
#include "tinyusb.h"
/* HCI message type definitions (for H4 messages) */
#define HCIT_TYPE_COMMAND 1
#define HCIT_TYPE_ACL_DATA 2
#define HCIT_TYPE_SCO_DATA 3
#define HCIT_TYPE_EVENT 4
/**
* @brief Initialize BTH Device.
*/
void tusb_bth_init(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,204 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "tusb.h"
#include "tinyusb.h"
/**
* @brief CDC ports available to setup
*/
typedef enum{
TINYUSB_CDC_ACM_0 = 0x0,
TINYUSB_CDC_ACM_1,
TINYUSB_CDC_TOTAL
}tinyusb_cdcacm_itf_t;
/* Callbacks and events
********************************************************************* */
/**
* @brief Data provided to the input of the `callback_rx_wanted_char` callback
*/
typedef struct {
char wanted_char; /*!< Wanted character */
} cdcacm_event_rx_wanted_char_data_t;
/**
* @brief Data provided to the input of the `callback_line_state_changed` callback
*/
typedef struct {
bool dtr; /*!< Data Terminal Ready (DTR) line state */
bool rts; /*!< Request To Send (RTS) line state */
} cdcacm_event_line_state_changed_data_t;
/**
* @brief Data provided to the input of the `line_coding_changed` callback
*/
typedef struct {
cdc_line_coding_t const *p_line_coding; /*!< New line coding value */
} cdcacm_event_line_coding_changed_data_t;
/**
* @brief Types of CDC ACM events
*/
typedef enum {
CDC_EVENT_RX,
CDC_EVENT_RX_WANTED_CHAR,
CDC_EVENT_LINE_STATE_CHANGED,
CDC_EVENT_LINE_CODING_CHANGED
} cdcacm_event_type_t;
/**
* @brief Describes an event passing to the input of a callbacks
*/
typedef struct {
cdcacm_event_type_t type; /*!< Event type */
union {
cdcacm_event_rx_wanted_char_data_t rx_wanted_char_data; /*!< Data input of the `callback_rx_wanted_char` callback */
cdcacm_event_line_state_changed_data_t line_state_changed_data; /*!< Data input of the `callback_line_state_changed` callback */
cdcacm_event_line_coding_changed_data_t line_coding_changed_data; /*!< Data input of the `line_coding_changed` callback */
};
} cdcacm_event_t;
/**
* @brief CDC-ACM callback type
*/
typedef void(*tusb_cdcacm_callback_t)(int itf, cdcacm_event_t *event);
/*********************************************************************** Callbacks and events*/
/* Other structs
********************************************************************* */
/**
* @brief Configuration structure for CDC-ACM
*/
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< Usb device to set up */
tinyusb_cdcacm_itf_t cdc_port; /*!< CDC port */
size_t rx_unread_buf_sz; /*!< Amount of data that can be passed to the AMC at once */
tusb_cdcacm_callback_t callback_rx; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_rx_wanted_char; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_line_state_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
tusb_cdcacm_callback_t callback_line_coding_changed; /*!< Pointer to the function with the `tusb_cdcacm_callback_t` type that will be handled as a callback */
} tinyusb_config_cdcacm_t;
/*********************************************************************** Other structs*/
/* Public functions
********************************************************************* */
/**
* @brief Initialize CDC ACM. Initialization will be finished with
* the `tud_cdc_line_state_cb` callback
*
* @param cfg - init configuration structure
* @return esp_err_t
*/
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg);
/**
* @brief Register a callback invoking on CDC event. If the callback had been
* already registered, it will be overwritten
*
* @param itf - number of a CDC object
* @param event_type - type of registered event for a callback
* @param callback - callback function
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type,
tusb_cdcacm_callback_t callback);
/**
* @brief Unregister a callback invoking on CDC event.
*
* @param itf - number of a CDC object
* @param event_type - type of registered event for a callback
* @return esp_err_t - ESP_OK or ESP_ERR_INVALID_ARG
*/
esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf, cdcacm_event_type_t event_type);
/**
* @brief Sent one character to a write buffer
*
* @param itf - number of a CDC object
* @param ch - character to send
* @return size_t - amount of queued bytes
*/
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch);
/**
* @brief Write data to write buffer from a byte array
*
* @param itf - number of a CDC object
* @param in_buf - a source array
* @param in_size - size to write from arr_src
* @return size_t - amount of queued bytes
*/
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, uint8_t *in_buf, size_t in_size);
/**
* @brief Send all data from a write buffer. Use `tinyusb_cdcacm_write_queue` to add data to the buffer.
*
* WARNING! TinyUSB can block output Endpoint for several RX callbacks, after will do additional flush
* after the each trasfer. That can leads to the situation when you requested a flush, but it will fail until
* ont of the next callbacks ends.
* SO USING OF THE FLUSH WITH TIMEOUTS IN CALLBACKS IS NOT RECOMENDED - YOU CAN GET A LOCK FOR THE TIMEOUT
*
* @param itf - number of a CDC object
* @param timeout_ticks - waiting until flush will be considered as failed
* @return esp_err_t - ESP_OK if (timeout_ticks > 0) and and flush was successful,
* ESP_ERR_TIMEOUT if timeout occurred3 or flush was successful with (timeout_ticks == 0)
* ESP_FAIL if flush was unsuccessful
*/
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks);
/**
* @brief Read a content to the array, and defines it's size to the sz_store
*
* @param itf - number of a CDC object
* @param out_buf - to this array will be stored the object from a CDC buffer
* @param out_buf_sz - size of buffer for results
* @param rx_data_size - to this address will be stored the object's size
* @return esp_err_t ESP_OK, ESP_FAIL or ESP_ERR_INVALID_STATE
*/
esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size);
/**
* @brief Check if the ACM initialized
*
* @param itf - number of a CDC object
* @return true or false
*/
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf);
/*********************************************************************** Public functions*/
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,138 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org),
* Additions Copyright (c) 2020, Espressif Systems (Shanghai) PTE LTD
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#pragma once
#include <stdbool.h>
#include "tusb_option.h"
#include "sdkconfig.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CONFIG_TINYUSB_CDC_ENABLED
# define CONFIG_TINYUSB_CDC_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_CDCACM_ENABLED
# define CONFIG_TINYUSB_CDCACM_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_MSC_ENABLED
# define CONFIG_TINYUSB_MSC_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_HID_ENABLED
# define CONFIG_TINYUSB_HID_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_MIDI_ENABLED
# define CONFIG_TINYUSB_MIDI_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED
# define CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_NET_ENABLED
# define CONFIG_TINYUSB_NET_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_WEBUSB_ENABLED
# define CONFIG_TINYUSB_WEBUSB_ENABLED 0
#endif
#ifndef CONFIG_TINYUSB_BTH_ENABLED
# define CONFIG_TINYUSB_BTH_ENABLED 0
# define CONFIG_TINYUSB_BTH_ISO_ALT_COUNT 0
#endif
#ifndef CONFIG_TINYUSB_DFU_ENABLED
# define CONFIG_TINYUSB_DFU_ENABLED 0
# define CONFIG_TINYUSB_DFU_BUFSIZE 512
#endif
#define CFG_TUSB_RHPORT0_MODE OPT_MODE_DEVICE | OPT_MODE_FULL_SPEED
#define CFG_TUSB_OS OPT_OS_FREERTOS
/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment.
* Tinyusb use follows macros to declare transferring memory so that they can be put
* into those specific section.
* e.g
* - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") ))
* - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4)))
*/
#ifndef CFG_TUSB_MEM_SECTION
# define CFG_TUSB_MEM_SECTION
#endif
#ifndef CFG_TUSB_MEM_ALIGN
# define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
#endif
#ifndef CFG_TUD_ENDPOINT0_SIZE
#define CFG_TUD_ENDPOINT0_SIZE 64
#endif
// CDC FIFO size of TX and RX
#define CFG_TUD_CDC_RX_BUFSIZE CONFIG_TINYUSB_CDC_RX_BUFSIZE
#define CFG_TUD_CDC_TX_BUFSIZE CONFIG_TINYUSB_CDC_TX_BUFSIZE
// Vendor FIFO size of TX and RX
// If not configured vendor endpoints will not be buffered
#define CFG_TUD_VENDOR_RX_BUFSIZE 64
#define CFG_TUD_VENDOR_TX_BUFSIZE 64
// MSC Buffer size of Device Mass storage
#define CFG_TUD_MSC_BUFSIZE CONFIG_TINYUSB_MSC_BUFSIZE
// HID buffer size Should be sufficient to hold ID (if any) + Data
#define CFG_TUD_HID_BUFSIZE CONFIG_TINYUSB_HID_BUFSIZE
// Number of BTH ISO alternatives
#define CFG_TUD_BTH_ISO_ALT_COUNT CONFIG_TINYUSB_BTH_ISO_ALT_COUNT
#define CFG_TUD_DFU_XFER_BUFSIZE CONFIG_TINYUSB_DFU_BUFSIZE
// Enabled device class driver
#define CFG_TUD_CDC CONFIG_TINYUSB_CDC_ENABLED
#define CFG_TUD_CDCACM CONFIG_TINYUSB_CDCACM_ENABLED
#define CFG_TUD_MSC CONFIG_TINYUSB_MSC_ENABLED
#define CFG_TUD_HID CONFIG_TINYUSB_HID_ENABLED
#define CFG_TUD_MIDI CONFIG_TINYUSB_MIDI_ENABLED
#define CFG_TUD_CUSTOM_CLASS CONFIG_TINYUSB_CUSTOM_CLASS_ENABLED
#define CFG_TUD_NET CONFIG_TINYUSB_NET_ENABLED
#define CFG_TUD_BTH CONFIG_TINYUSB_BTH_ENABLED
#define CFG_TUD_DFU CONFIG_TINYUSB_DFU_ENABLED
#define CFG_TUD_VENDOR CONFIG_TINYUSB_WEBUSB_ENABLED
/* TODO: will be removed if upstream feat: Add net xmit status cb for application can block to get it #1001*/
__attribute__((weak)) void tud_network_idle_status_change_cb(bool idle);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,41 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include "esp_err.h"
/**
* @brief Redirect output to the USB serial
* @param cdc_intf - interface number of TinyUSB's CDC
*
* @return esp_err_t - ESP_OK, ESP_FAIL or an error code
*/
esp_err_t esp_tusb_init_console(int cdc_intf);
/**
* @brief Switch log to the default output
* @param cdc_intf - interface number of TinyUSB's CDC
*
* @return esp_err_t
*/
esp_err_t esp_tusb_deinit_console(int cdc_intf);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,239 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "tusb.h"
#include "tinyusb.h"
/**
* @brief Report delta movement of mouse.
*
* @param x Current delta x movement of the mouse
* @param y Current delta y movement on the mouse
* @param vertical Current delta wheel movement on the mouse
* @param horizontal using AC Pan
*/
void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t horizontal);
/**
* @brief Report button click in the mouse, using bitmap here.
* eg. MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT, if click left and right button at same time.
*
* @param buttons hid mouse button bit mask
*/
void tinyusb_hid_mouse_button_report(uint8_t buttons_map);
/**
* @brief Report key press in the keyboard, using array here, contains six keys at most.
*
* @param keycode hid keyboard code array
*/
void tinyusb_hid_keyboard_report(uint8_t keycode[]);
//--------------------------------------------------------------------+
// HID MOUSE BUTTON BIT MASK
//--------------------------------------------------------------------+
// MOUSE_BUTTON_LEFT = TU_BIT(0), ///< Left button
// MOUSE_BUTTON_RIGHT = TU_BIT(1), ///< Right button
// MOUSE_BUTTON_MIDDLE = TU_BIT(2), ///< Middle button
// MOUSE_BUTTON_BACKWARD = TU_BIT(3), ///< Backward button,
// MOUSE_BUTTON_FORWARD = TU_BIT(4), ///< Forward button,
//--------------------------------------------------------------------+
// HID KEYCODE
//--------------------------------------------------------------------+
// #define HID_KEY_NONE 0x00
// #define HID_KEY_A 0x04
// #define HID_KEY_B 0x05
// #define HID_KEY_C 0x06
// #define HID_KEY_D 0x07
// #define HID_KEY_E 0x08
// #define HID_KEY_F 0x09
// #define HID_KEY_G 0x0A
// #define HID_KEY_H 0x0B
// #define HID_KEY_I 0x0C
// #define HID_KEY_J 0x0D
// #define HID_KEY_K 0x0E
// #define HID_KEY_L 0x0F
// #define HID_KEY_M 0x10
// #define HID_KEY_N 0x11
// #define HID_KEY_O 0x12
// #define HID_KEY_P 0x13
// #define HID_KEY_Q 0x14
// #define HID_KEY_R 0x15
// #define HID_KEY_S 0x16
// #define HID_KEY_T 0x17
// #define HID_KEY_U 0x18
// #define HID_KEY_V 0x19
// #define HID_KEY_W 0x1A
// #define HID_KEY_X 0x1B
// #define HID_KEY_Y 0x1C
// #define HID_KEY_Z 0x1D
// #define HID_KEY_1 0x1E
// #define HID_KEY_2 0x1F
// #define HID_KEY_3 0x20
// #define HID_KEY_4 0x21
// #define HID_KEY_5 0x22
// #define HID_KEY_6 0x23
// #define HID_KEY_7 0x24
// #define HID_KEY_8 0x25
// #define HID_KEY_9 0x26
// #define HID_KEY_0 0x27
// #define HID_KEY_ENTER 0x28
// #define HID_KEY_ESCAPE 0x29
// #define HID_KEY_BACKSPACE 0x2A
// #define HID_KEY_TAB 0x2B
// #define HID_KEY_SPACE 0x2C
// #define HID_KEY_MINUS 0x2D
// #define HID_KEY_EQUAL 0x2E
// #define HID_KEY_BRACKET_LEFT 0x2F
// #define HID_KEY_BRACKET_RIGHT 0x30
// #define HID_KEY_BACKSLASH 0x31
// #define HID_KEY_EUROPE_1 0x32
// #define HID_KEY_SEMICOLON 0x33
// #define HID_KEY_APOSTROPHE 0x34
// #define HID_KEY_GRAVE 0x35
// #define HID_KEY_COMMA 0x36
// #define HID_KEY_PERIOD 0x37
// #define HID_KEY_SLASH 0x38
// #define HID_KEY_CAPS_LOCK 0x39
// #define HID_KEY_F1 0x3A
// #define HID_KEY_F2 0x3B
// #define HID_KEY_F3 0x3C
// #define HID_KEY_F4 0x3D
// #define HID_KEY_F5 0x3E
// #define HID_KEY_F6 0x3F
// #define HID_KEY_F7 0x40
// #define HID_KEY_F8 0x41
// #define HID_KEY_F9 0x42
// #define HID_KEY_F10 0x43
// #define HID_KEY_F11 0x44
// #define HID_KEY_F12 0x45
// #define HID_KEY_PRINT_SCREEN 0x46
// #define HID_KEY_SCROLL_LOCK 0x47
// #define HID_KEY_PAUSE 0x48
// #define HID_KEY_INSERT 0x49
// #define HID_KEY_HOME 0x4A
// #define HID_KEY_PAGE_UP 0x4B
// #define HID_KEY_DELETE 0x4C
// #define HID_KEY_END 0x4D
// #define HID_KEY_PAGE_DOWN 0x4E
// #define HID_KEY_ARROW_RIGHT 0x4F
// #define HID_KEY_ARROW_LEFT 0x50
// #define HID_KEY_ARROW_DOWN 0x51
// #define HID_KEY_ARROW_UP 0x52
// #define HID_KEY_NUM_LOCK 0x53
// #define HID_KEY_KEYPAD_DIVIDE 0x54
// #define HID_KEY_KEYPAD_MULTIPLY 0x55
// #define HID_KEY_KEYPAD_SUBTRACT 0x56
// #define HID_KEY_KEYPAD_ADD 0x57
// #define HID_KEY_KEYPAD_ENTER 0x58
// #define HID_KEY_KEYPAD_1 0x59
// #define HID_KEY_KEYPAD_2 0x5A
// #define HID_KEY_KEYPAD_3 0x5B
// #define HID_KEY_KEYPAD_4 0x5C
// #define HID_KEY_KEYPAD_5 0x5D
// #define HID_KEY_KEYPAD_6 0x5E
// #define HID_KEY_KEYPAD_7 0x5F
// #define HID_KEY_KEYPAD_8 0x60
// #define HID_KEY_KEYPAD_9 0x61
// #define HID_KEY_KEYPAD_0 0x62
// #define HID_KEY_KEYPAD_DECIMAL 0x63
// #define HID_KEY_EUROPE_2 0x64
// #define HID_KEY_APPLICATION 0x65
// #define HID_KEY_POWER 0x66
// #define HID_KEY_KEYPAD_EQUAL 0x67
// #define HID_KEY_F13 0x68
// #define HID_KEY_F14 0x69
// #define HID_KEY_F15 0x6A
// #define HID_KEY_F16 0x6B
// #define HID_KEY_F17 0x6C
// #define HID_KEY_F18 0x6D
// #define HID_KEY_F19 0x6E
// #define HID_KEY_F20 0x6F
// #define HID_KEY_F21 0x70
// #define HID_KEY_F22 0x71
// #define HID_KEY_F23 0x72
// #define HID_KEY_F24 0x73
// #define HID_KEY_EXECUTE 0x74
// #define HID_KEY_HELP 0x75
// #define HID_KEY_MENU 0x76
// #define HID_KEY_SELECT 0x77
// #define HID_KEY_STOP 0x78
// #define HID_KEY_AGAIN 0x79
// #define HID_KEY_UNDO 0x7A
// #define HID_KEY_CUT 0x7B
// #define HID_KEY_COPY 0x7C
// #define HID_KEY_PASTE 0x7D
// #define HID_KEY_FIND 0x7E
// #define HID_KEY_MUTE 0x7F
// #define HID_KEY_VOLUME_UP 0x80
// #define HID_KEY_VOLUME_DOWN 0x81
// #define HID_KEY_LOCKING_CAPS_LOCK 0x82
// #define HID_KEY_LOCKING_NUM_LOCK 0x83
// #define HID_KEY_LOCKING_SCROLL_LOCK 0x84
// #define HID_KEY_KEYPAD_COMMA 0x85
// #define HID_KEY_KEYPAD_EQUAL_SIGN 0x86
// #define HID_KEY_KANJI1 0x87
// #define HID_KEY_KANJI2 0x88
// #define HID_KEY_KANJI3 0x89
// #define HID_KEY_KANJI4 0x8A
// #define HID_KEY_KANJI5 0x8B
// #define HID_KEY_KANJI6 0x8C
// #define HID_KEY_KANJI7 0x8D
// #define HID_KEY_KANJI8 0x8E
// #define HID_KEY_KANJI9 0x8F
// #define HID_KEY_LANG1 0x90
// #define HID_KEY_LANG2 0x91
// #define HID_KEY_LANG3 0x92
// #define HID_KEY_LANG4 0x93
// #define HID_KEY_LANG5 0x94
// #define HID_KEY_LANG6 0x95
// #define HID_KEY_LANG7 0x96
// #define HID_KEY_LANG8 0x97
// #define HID_KEY_LANG9 0x98
// #define HID_KEY_ALTERNATE_ERASE 0x99
// #define HID_KEY_SYSREQ_ATTENTION 0x9A
// #define HID_KEY_CANCEL 0x9B
// #define HID_KEY_CLEAR 0x9C
// #define HID_KEY_PRIOR 0x9D
// #define HID_KEY_RETURN 0x9E
// #define HID_KEY_SEPARATOR 0x9F
// #define HID_KEY_OUT 0xA0
// #define HID_KEY_OPER 0xA1
// #define HID_KEY_CLEAR_AGAIN 0xA2
// #define HID_KEY_CRSEL_PROPS 0xA3
// #define HID_KEY_EXSEL 0xA4
// //RESERVED 0xA5-DF
// #define HID_KEY_CONTROL_LEFT 0xE0
// #define HID_KEY_SHIFT_LEFT 0xE1
// #define HID_KEY_ALT_LEFT 0xE2
// #define HID_KEY_GUI_LEFT 0xE3
// #define HID_KEY_CONTROL_RIGHT 0xE4
// #define HID_KEY_SHIFT_RIGHT 0xE5
// #define HID_KEY_ALT_RIGHT 0xE6
// #define HID_KEY_GUI_RIGHT 0xE7
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,42 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "tusb.h"
#include "tinyusb.h"
/**
* @brief Configuration structure for MSC
*/
typedef struct {
uint8_t pdrv; /* Physical drive nmuber (0..) */
} tinyusb_config_msc_t;
/**
* @brief Initialize MSC Device.
*
* @param cfg - init configuration structure
* @return esp_err_t
*/
esp_err_t tusb_msc_init(const tinyusb_config_msc_t *cfg);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,49 @@
/**
* @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "esp_err.h"
#include "tinyusb.h"
/**
* @brief Forward packets from Wi-Fi to USB.
*
* @param buffer - Data pointer
*
* @param len - Data length
*
* @return esp_err_t
*/
esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb);
/**
* @brief Initialize NET Device.
*/
void tusb_net_init(void);
void ecm_close(void);
void ecm_open(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,46 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief This helper function creates and starts a task which wraps `tud_task()`.
*
* The wrapper function basically wraps tud_task and some log.
* Default parameters: stack size and priority as configured, argument = NULL, not pinned to any core.
* If you have more requirements for this task, you can create your own task which calls tud_task as the last step.
*
* @retval ESP_OK run tinyusb main task successfully
* @retval ESP_FAIL run tinyusb main task failed of internal error
* @retval ESP_ERR_INVALID_STATE tinyusb main task has been created before
*/
esp_err_t tusb_run_task(void);
/**
* @brief This helper function stops and destroys the task created by `tusb_run_task()`
*
* @retval ESP_OK stop and destory tinyusb main task successfully
* @retval ESP_ERR_INVALID_STATE tinyusb main task hasn't been created yet
*/
esp_err_t tusb_stop_task(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,42 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "esp_err.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief Register TinyUSB CDC at VFS with path
* @param cdc_intf - interface number of TinyUSB's CDC
* @param path - path where the CDC will be registered, `/dev/tusb_cdc` will be used if left NULL.
*
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path);
/**
* @brief Unregister TinyUSB CDC from VFS
* @param path - path where the CDC will be unregistered if NULL will be used `/dev/tusb_cdc`
*
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,99 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include "freertos/FreeRTOS.h"
#include "freertos/ringbuf.h"
#include "freertos/semphr.h"
#include "freertos/timers.h"
#include "tusb.h"
#include "tinyusb_types.h"
/* CDC classification
********************************************************************* */
typedef enum {
TINYUSB_CDC_DATA = 0x00,
} cdc_data_sublcass_type_t; // CDC120 specification
/* Note:other classification is represented in the file components\tinyusb\tinyusb\src\class\cdc\cdc.h */
/*********************************************************************** CDC classification*/
/* Structs
********************************************************************* */
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< USB device to set up */
tusb_class_code_t cdc_class; /*!< CDC device class : Communications or Data device */
union {
cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */
cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/
} cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */
} tinyusb_config_cdc_t; /*!< Main configuration structure of a CDC device */
typedef struct {
tinyusb_usbdev_t usb_dev; /*!< USB device used for the instance */
tusb_class_code_t type;
union {
cdc_comm_sublcass_type_t comm_subclass; /*!< Communications device subclasses: AMC, ECM, etc. */
cdc_data_sublcass_type_t data_subclass; /*!< Data device has only one subclass.*/
} cdc_subclass; /*!< CDC device subclass according to Class Definitions for Communications Devices the CDC v.1.20 */
void *subclass_obj; /*!< Dynamically allocated subclass specific object */
} esp_tusb_cdc_t;
/*********************************************************************** Structs*/
/* Functions
********************************************************************* */
/**
* @brief Initializing CDC basic object
* @param itf - number of a CDC object
* @param cfg - CDC configuration structure
*
* @return esp_err_t ESP_OK or ESP_FAIL
*/
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg);
/**
* @brief De-initializing CDC. Clean its objects
* @param itf - number of a CDC object
* @return esp_err_t ESP_OK, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_STATE
*
*/
esp_err_t tinyusb_cdc_deinit(int itf);
/**
* @brief Checks if the CDC initialized and ready to interaction
*
* @return true or false
*/
bool tinyusb_cdc_initialized(int itf);
/**
* @brief Return interface of a CDC device
*
* @param itf_num
* @return esp_tusb_cdc_t* pointer to the interface or (NULL) on error
*/
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num);
/*********************************************************************** Functions*/
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,172 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <string.h>
#include "usb_descriptors.h"
/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug.
* Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC.
*
* Auto ProductID layout's Bitmap:
* [MSB] NET | VENDOR | MIDI | HID | MSC | CDC [LSB]
*/
#ifdef __cplusplus
extern "C" {
#endif
//------------- EndPoint Descriptor -------------//
enum {
EPNUM_DEFAULT = 0,
# if CFG_TUD_BTH
EPNUM_BT_EVT,
EPNUM_BT_BULK_OUT,
# endif
# if CFG_TUD_CDC
EPNUM_CDC_NOTIF,
EPNUM_CDC_DATA,
# endif
# if CFG_TUD_VENDOR
EPNUM_VENDOR,
# endif
# if CFG_TUD_NET
EPNUM_NET_NOTIF,
EPNUM_NET_DATA,
# endif
# if CFG_TUD_MSC
EPNUM_MSC_DATA,
# endif
# if CFG_TUD_HID
EPNUM_HID_DATA,
# endif
};
#if ((CFG_TUD_BTH * 2) + (CFG_TUD_NET * 2) + (CFG_TUD_CDC * 2) + CFG_TUD_MSC + CFG_TUD_HID) > 4
#error "USB endpoint number not be more than 5"
#endif
//------------- HID Report Descriptor -------------//
#if CFG_TUD_HID
enum {
REPORT_ID_KEYBOARD = 1,
REPORT_ID_MOUSE
};
#endif
//------------- Configuration Descriptor -------------//
enum {
# if CFG_TUD_BTH
ITF_NUM_BTH = 0,
ITF_NUM_BTH_DATA,
# endif
# if CFG_TUD_NET
ITF_NUM_NET,
ITF_NUM_NET_DATA,
# endif
# if CFG_TUD_CDC
ITF_NUM_CDC,
ITF_NUM_CDC_DATA,
# endif
# if CFG_TUD_VENDOR
ITF_NUM_VENDOR,
# endif
# if CFG_TUD_MSC
ITF_NUM_MSC,
# endif
# if CFG_TUD_HID
ITF_NUM_HID,
# endif
# if CFG_TUD_DFU
ITF_NUM_DFU,
# endif
ITF_NUM_TOTAL
};
//------------- STRID -------------//
enum {
STRID_LANGID = 0,
STRID_MANUFACTURER,
STRID_PRODUCT,
STRID_SERIAL,
#if CFG_TUD_CDC
STRID_CDC_INTERFACE,
#endif
#if CFG_TUD_NET
STRID_NET_INTERFACE,
STRID_MAC,
#endif
#if CFG_TUD_VENDOR
STRID_WEBUSB_INTERFACE,
#endif
#if CFG_TUD_MSC
STRID_MSC_INTERFACE,
#endif
#if CFG_TUD_HID
STRID_HID_INTERFACE,
#endif
#if CFG_TUD_BTH
STRID_BTH_INTERFACE,
#endif
#if CFG_TUD_DFU
STRID_DFU_INTERFACE,
#endif
};
#define DFU_ALT_COUNT 2
#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9)
enum {
TUSB_DESC_TOTAL_LEN = TUD_CONFIG_DESC_LEN +
TUD_CDC_DESC_LEN * CFG_TUD_CDC +
TUD_RNDIS_DESC_LEN * CFG_TUD_NET +
TUD_VENDOR_DESC_LEN * CFG_TUD_VENDOR +
TUD_MSC_DESC_LEN * CFG_TUD_MSC +
TUD_HID_DESC_LEN * CFG_TUD_HID +
TUD_BTH_DESC_LEN * CFG_TUD_BTH +
TUD_DFU_DESC_LEN(DFU_ALT_COUNT) * CFG_TUD_DFU,
ALT_CONFIG_TOTAL_LEN = TUD_CONFIG_DESC_LEN +
TUD_CDC_ECM_DESC_LEN * CFG_TUD_NET +
TUD_CDC_DESC_LEN * CFG_TUD_CDC +
TUD_VENDOR_DESC_LEN * CFG_TUD_VENDOR +
TUD_MSC_DESC_LEN * CFG_TUD_MSC +
TUD_HID_DESC_LEN * CFG_TUD_HID +
TUD_BTH_DESC_LEN * CFG_TUD_BTH +
TUD_DFU_DESC_LEN(DFU_ALT_COUNT) * CFG_TUD_DFU
};
bool tusb_desc_set;
void tusb_set_descriptor(tusb_desc_device_t *desc, const char **str_desc);
void tusb_set_config_descriptor(const uint8_t *config_desc);
tusb_desc_device_t *tusb_get_active_desc(void);
char **tusb_get_active_str_desc(void);
void tusb_clear_descriptor(void);
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,34 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "tusb.h"
#include "tinyusb_types.h"
#ifdef __cplusplus
extern "C" {
#endif
#define _PID_MAP(itf, n) ((CFG_TUD_##itf) << (n))
extern tusb_desc_device_t descriptor_tinyusb;
extern tusb_desc_strarray_device_t descriptor_str_tinyusb;
extern tusb_desc_device_t descriptor_kconfig;
extern tusb_desc_strarray_device_t descriptor_str_kconfig;
#ifdef __cplusplus
}
#endif

View file

@ -0,0 +1,162 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "tusb.h"
#include "cdc.h"
#include "tusb_cdc_acm.h"
#include "sdkconfig.h"
static const char *TAG = "tusb_cdc";
#define CDC_INTF_NUM CFG_TUD_CDCACM // number of cdc blocks
static esp_tusb_cdc_t *cdc_obj[CFG_TUD_CDCACM] = {};
/* Common CDC functions
********************************************************************* */
bool tinyusb_cdc_initialized(int itf)
{
return (cdc_obj[itf] != NULL);
}
static esp_err_t cdc_interface_check(int itf)
{
if (tinyusb_cdc_initialized(itf)) {
return ESP_OK;
} else {
ESP_LOGE(TAG, "Interface %d is not initialized. Use `tinyusb_cdc_init` for initialization", itf);
return ESP_ERR_INVALID_STATE;
}
}
/**
* @brief
*
* @param itf
* @param expected_inited
* @param expected_type use -1 if you don't care
* @return esp_err_t
*/
static esp_err_t cdc_obj_check(int itf, bool expected_inited, tusb_class_code_t expected_type)
{
bool inited = (cdc_obj[itf] != NULL);
if (expected_inited != inited) {
ESP_LOGE(TAG, "Wrong state of the interface. Expected state: %s",
expected_inited ? "initialized" : "not initialized");
return ESP_ERR_INVALID_STATE;
}
if (inited && (expected_type != -1) && !(cdc_obj[itf]->type == expected_type)) {
ESP_LOGE(TAG, "Wrong type of the interface. Should be : 0x%x (tusb_class_code_t)", expected_type);
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_tusb_cdc_t *tinyusb_cdc_get_intf(int itf_num)
{
if (cdc_interface_check(itf_num) != ESP_OK) {
return NULL;
}
return cdc_obj[itf_num];
}
/*********************************************************************** Common CDC functions*/
/* CDC class funcs
********************************************************************* */
static esp_err_t tusb_cdc_comm_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, -1), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC;
ESP_LOGD(TAG, "CDC Comm class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Comm initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_comm(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
static esp_err_t tusb_cdc_data_init(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, false, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
cdc_obj[itf] = calloc(1, sizeof(esp_tusb_cdc_t));
if (cdc_obj[itf] != NULL) {
cdc_obj[itf]->type = TUSB_CLASS_CDC_DATA;
ESP_LOGD(TAG, "CDC Data class initialized");
return ESP_OK;
} else {
ESP_LOGE(TAG, "CDC Data initialization error");
return ESP_FAIL;
}
}
static esp_err_t tusb_cdc_deinit_data(int itf)
{
ESP_RETURN_ON_ERROR(cdc_obj_check(itf, true, TUSB_CLASS_CDC_DATA), TAG, "cdc_obj_check failed");
free(cdc_obj[itf]);
cdc_obj[itf] = NULL;
return ESP_OK;
}
/*********************************************************************** CDC class funcs*/
/* CDC initialization
********************************************************************* */
esp_err_t tinyusb_cdc_init(int itf, const tinyusb_config_cdc_t *cfg)
{
ESP_LOGD(TAG, "CDC initialization...");
if (itf >= TINYUSB_CDC_TOTAL) {
ESP_LOGE(TAG, "There is not CDC no.%d", itf);
return ESP_ERR_INVALID_ARG;
}
if (cfg->cdc_class == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_comm_init(itf), TAG, "tusb_cdc_comm_init failed");
cdc_obj[itf]->cdc_subclass.comm_subclass = cfg->cdc_subclass.comm_subclass;
} else {
ESP_RETURN_ON_ERROR(tusb_cdc_data_init(itf), TAG, "tusb_cdc_data_init failed");
cdc_obj[itf]->cdc_subclass.data_subclass = cfg->cdc_subclass.data_subclass;
}
cdc_obj[itf]->usb_dev = cfg->usb_dev;
return ESP_OK;
}
esp_err_t tinyusb_cdc_deinit(int itf)
{
if (itf >= TINYUSB_CDC_TOTAL) {
ESP_LOGE(TAG, "There is not CDC no.%d", itf);
return ESP_ERR_INVALID_ARG;
}
if (cdc_obj[itf]->type == TUSB_CLASS_CDC) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_comm(itf), TAG, "tusb_cdc_deinit_comm failed");
} else if (cdc_obj[itf]->type == TUSB_CLASS_CDC_DATA) {
ESP_RETURN_ON_ERROR(tusb_cdc_deinit_data(itf), TAG, "tusb_cdc_deinit_data failed");
} else {
return ESP_ERR_INVALID_ARG;
}
ESP_LOGD(TAG, "De-initialized");
return ESP_OK;
}
/*********************************************************************** CDC initialization*/

View file

@ -0,0 +1,260 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_log.h"
#include "descriptors_control.h"
#include "dfu_device.h"
static const char *TAG = "tusb_desc";
static tusb_desc_device_t s_descriptor;
static char *s_str_descriptor[USB_STRING_DESCRIPTOR_ARRAY_SIZE];
static uint8_t *s_config_descriptor = NULL;
#define MAX_DESC_BUF_SIZE 32
#if CFG_TUD_HID //HID Report Descriptor
uint8_t const desc_hid_report[] = {
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(REPORT_ID_KEYBOARD)),
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(REPORT_ID_MOUSE))
};
#endif
#define FUNC_ATTRS (DFU_ATTR_CAN_UPLOAD | DFU_ATTR_CAN_DOWNLOAD | DFU_ATTR_MANIFESTATION_TOLERANT)
uint8_t const desc_configuration[] = {
#if CONFIG_TINYUSB_NET_ECM
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, ALT_CONFIG_TOTAL_LEN, 0, 100),
#else
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, 0, 100),
#endif
#if CFG_TUD_BTH
// BT Primary controller descriptor
// Interface number, string index, attributes, event endpoint, event endpoint size, interval, data in, data out, data endpoint size, iso endpoint sizes
TUD_BTH_DESCRIPTOR(ITF_NUM_BTH, 0 /* STRID_BTH_INTERFACE */, (0x80 | EPNUM_BT_EVT), 16, 1, (0x80 | EPNUM_BT_BULK_OUT), EPNUM_BT_BULK_OUT, 64, 0, 9, 17, 25, 33, 49),
#endif
#if CFG_TUD_CDC
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_CDC_DESCRIPTOR(ITF_NUM_CDC, STRID_CDC_INTERFACE, (0x80 | EPNUM_CDC_NOTIF), 8, EPNUM_CDC_DATA, (0x80 | EPNUM_CDC_DATA), 64),
#endif
#if CFG_TUD_NET
#if CONFIG_TINYUSB_NET_ECM
// Interface number, description string index, MAC address string index, EP notification address and size, EP data address (out, in), and size, max segment size.
TUD_CDC_ECM_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, STRID_MAC, (0x80 | EPNUM_NET_NOTIF), 64, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE, CFG_TUD_NET_MTU),
#elif CONFIG_TINYUSB_NET_RNDIS
// Interface number, string index, EP notification address and size, EP data address (out, in) and size.
TUD_RNDIS_DESCRIPTOR(ITF_NUM_NET, STRID_NET_INTERFACE, (0x80 | EPNUM_NET_NOTIF), 8, EPNUM_NET_DATA, (0x80 | EPNUM_NET_DATA), CFG_TUD_NET_ENDPOINT_SIZE),
#endif
#endif
#if CFG_TUD_VENDOR
// Interface number, string index, EP Out & IN address, EP size
TUD_VENDOR_DESCRIPTOR(ITF_NUM_VENDOR, STRID_WEBUSB_INTERFACE, EPNUM_VENDOR, 0x80 | EPNUM_VENDOR, 64),
#endif
#if CFG_TUD_MSC
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, STRID_MSC_INTERFACE, EPNUM_MSC_DATA, (0x80 | EPNUM_MSC_DATA), 64), // highspeed 512
#endif
#if CFG_TUD_HID
// Interface number, string index, protocol, report descriptor len, EP In address, size & polling interval
TUD_HID_DESCRIPTOR(ITF_NUM_HID, STRID_HID_INTERFACE, HID_PROTOCOL_NONE, sizeof(desc_hid_report), (0x80 | EPNUM_HID_DATA), 16, 10)
#endif
#if CFG_TUD_DFU
// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size
TUD_DFU_DESCRIPTOR(ITF_NUM_DFU, DFU_ALT_COUNT, STRID_DFU_INTERFACE, FUNC_ATTRS, 1000, CFG_TUD_DFU_XFER_BUFSIZE),
#endif
};
// =============================================================================
// CALLBACKS
// =============================================================================
/**
* @brief Invoked when received GET DEVICE DESCRIPTOR.
* Application returns pointer to descriptor
*
* @return uint8_t const*
*/
uint8_t const *tud_descriptor_device_cb(void)
{
return (uint8_t const *)&s_descriptor;
}
/**
* @brief Invoked when received GET CONFIGURATION DESCRIPTOR.
* Descriptor contents must exist long enough for transfer to complete
*
* @param index
* @return uint8_t const* Application return pointer to descriptor
*/
uint8_t const *tud_descriptor_configuration_cb(uint8_t index)
{
(void)index; // for multiple configurations
return s_config_descriptor;
}
static uint16_t _desc_str[MAX_DESC_BUF_SIZE];
// Invoked when received GET STRING DESCRIPTOR request
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid)
{
(void) langid;
uint8_t chr_count;
if ( index == 0) {
memcpy(&_desc_str[1], s_str_descriptor[0], 2);
chr_count = 1;
}
#if CONFIG_TINYUSB_NET_ECM
else if (STRID_MAC == index)
{
// Convert MAC address into UTF-16
for (unsigned i=0; i<sizeof(tud_network_mac_address); i++)
{
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 4) & 0xf];
_desc_str[1+chr_count++] = "0123456789ABCDEF"[(tud_network_mac_address[i] >> 0) & 0xf];
}
}
#endif
else
{
// Convert ASCII string into UTF-16
if ( index >= sizeof(s_str_descriptor) / sizeof(s_str_descriptor[0]) ) {
return NULL;
}
const char *str = s_str_descriptor[index];
// Cap at max char
chr_count = strlen(str);
if ( chr_count > MAX_DESC_BUF_SIZE - 1 ) {
chr_count = MAX_DESC_BUF_SIZE - 1;
}
for (uint8_t i = 0; i < chr_count; i++) {
_desc_str[1 + i] = str[i];
}
}
// first byte is length (including header), second byte is string type
_desc_str[0] = (TUSB_DESC_STRING << 8 ) | (2 * chr_count + 2);
return _desc_str;
}
/**
* @brief Invoked when received GET HID REPORT DESCRIPTOR
* Application returns pointer to descriptor. Descriptor contents must exist
* long enough for transfer to complete
*
* @return uint8_t const*
*/
#if CFG_TUD_HID
uint8_t const *tud_hid_descriptor_report_cb(uint8_t itf)
{
(void)itf;
return desc_hid_report;
}
#endif
// =============================================================================
// Driver functions
// =============================================================================
void tusb_set_descriptor(tusb_desc_device_t *dev_desc, const char **str_desc)
{
ESP_LOGI(TAG, "\n"
"┌─────────────────────────────────┐\n"
"│ USB Device Descriptor Summary │\n"
"├───────────────────┬─────────────┤\n"
"│bDeviceClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceSubClass │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bDeviceProtocol │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│bMaxPacketSize0 │ %-4u │\n"
"├───────────────────┼─────────────┤\n"
"│idVendor │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│idProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bcdDevice │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iManufacturer │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iProduct │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│iSerialNumber │ %-#10x │\n"
"├───────────────────┼─────────────┤\n"
"│bNumConfigurations │ %-#10x │\n"
"└───────────────────┴─────────────┘",
dev_desc->bDeviceClass, dev_desc->bDeviceSubClass,
dev_desc->bDeviceProtocol, dev_desc->bMaxPacketSize0,
dev_desc->idVendor, dev_desc->idProduct, dev_desc->bcdDevice,
dev_desc->iManufacturer, dev_desc->iProduct, dev_desc->iSerialNumber,
dev_desc->bNumConfigurations);
s_descriptor = *dev_desc;
if (str_desc != NULL) {
memcpy(s_str_descriptor, str_desc,
sizeof(s_str_descriptor[0])*USB_STRING_DESCRIPTOR_ARRAY_SIZE);
}
tusb_desc_set = true;
}
void tusb_set_config_descriptor(const uint8_t *config_desc)
{
size_t length = 0;
const uint8_t *config_descriptor = NULL;
if (config_desc == NULL) {
config_descriptor = desc_configuration;
ESP_LOGI(TAG, "using default config desc");
} else {
config_descriptor = config_desc;
ESP_LOGI(TAG, "using custom config desc");
}
length = (config_descriptor[3]<<8) + config_descriptor[2];
ESP_LOGI(TAG, "config desc size=%d", length);
s_config_descriptor = realloc(s_config_descriptor, length);
memcpy(s_config_descriptor, config_descriptor, length);
}
tusb_desc_device_t *tusb_get_active_desc(void)
{
return &s_descriptor;
}
char **tusb_get_active_str_desc(void)
{
return s_str_descriptor;
}
void tusb_clear_descriptor(void)
{
memset(&s_descriptor, 0, sizeof(s_descriptor));
memset(&s_str_descriptor, 0, sizeof(s_str_descriptor));
free(s_config_descriptor);
s_config_descriptor = NULL;
tusb_desc_set = false;
}

View file

@ -0,0 +1,85 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_log.h"
#include "esp_check.h"
#include "esp_rom_gpio.h"
#include "hal/gpio_ll.h"
#include "hal/usb_hal.h"
#include "soc/gpio_periph.h"
#include "soc/usb_periph.h"
#include "tinyusb.h"
#include "descriptors_control.h"
#include "tusb.h"
#include "tusb_tasks.h"
const static char *TAG = "TinyUSB";
static void configure_pins(usb_hal_context_t *usb)
{
/* usb_periph_iopins currently configures USB_OTG as USB Device.
* Introduce additional parameters in usb_hal_context_t when adding support
* for USB Host.
*/
for (const usb_iopin_dsc_t *iopin = usb_periph_iopins; iopin->pin != -1; ++iopin) {
if ((usb->use_external_phy) || (iopin->ext_phy_only == 0)) {
esp_rom_gpio_pad_select_gpio(iopin->pin);
if (iopin->is_output) {
esp_rom_gpio_connect_out_signal(iopin->pin, iopin->func, false, false);
} else {
esp_rom_gpio_connect_in_signal(iopin->pin, iopin->func, false);
if ((iopin->pin != GPIO_FUNC_IN_LOW) && (iopin->pin != GPIO_FUNC_IN_HIGH)) {
gpio_ll_input_enable(&GPIO, iopin->pin);
}
}
esp_rom_gpio_pad_unhold(iopin->pin);
}
}
if (!usb->use_external_phy) {
gpio_set_drive_capability(USBPHY_DM_NUM, GPIO_DRIVE_CAP_3);
gpio_set_drive_capability(USBPHY_DP_NUM, GPIO_DRIVE_CAP_3);
}
}
esp_err_t tinyusb_driver_install(const tinyusb_config_t *config)
{
tusb_desc_device_t *dev_descriptor;
const char **string_descriptor;
ESP_RETURN_ON_FALSE(config, ESP_ERR_INVALID_ARG, TAG, "invalid argument");
// Enable APB CLK to USB peripheral
periph_module_enable(PERIPH_USB_MODULE);
periph_module_reset(PERIPH_USB_MODULE);
// Initialize HAL layer
usb_hal_context_t hal = {
.use_external_phy = config->external_phy
};
usb_hal_init(&hal);
configure_pins(&hal);
dev_descriptor = config->descriptor ? config->descriptor : &descriptor_kconfig;
string_descriptor = config->string_descriptor ? config->string_descriptor : descriptor_str_kconfig;
tusb_set_config_descriptor(config->config_descriptor); //if NULL, using default descriptor config by menuconfig
tusb_set_descriptor(dev_descriptor, string_descriptor);
ESP_RETURN_ON_FALSE(tusb_init(), ESP_FAIL, TAG, "Init TinyUSB stack failed");
#if !CONFIG_TINYUSB_NO_DEFAULT_TASK
ESP_RETURN_ON_ERROR(tusb_run_task(), TAG, "Run TinyUSB task failed");
#endif
ESP_LOGI(TAG, "TinyUSB Driver installed");
return ESP_OK;
}

View file

@ -0,0 +1,212 @@
/**
* @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "esp_bt.h"
#include "tinyusb.h"
#include "tusb_bth.h"
#define BUFFER_SIZE_MAX 256
#define LE_READ_BUFF_SIZE 0x2002
#define HCI_H4_CMD_PREAMBLE_SIZE (4)
#define UINT16_TO_STREAM(p, u16) {*(p)++ = (uint8_t)(u16); *(p)++ = (uint8_t)((u16) >> 8);}
#define UINT8_TO_STREAM(p, u8) {*(p)++ = (uint8_t)(u8);}
static const char *TAG = "tusb_bth";
static uint16_t acl_buf_size_max = 0;
uint8_t * p_acl_buf = NULL;
void ble_controller_init(void) {
esp_err_t ret;
esp_bt_controller_config_t bt_cfg = BT_CONTROLLER_INIT_CONFIG_DEFAULT();
if ((ret = esp_bt_controller_mem_release(ESP_BT_MODE_CLASSIC_BT)) != ESP_OK) {
ESP_LOGI(TAG, "Bluetooth controller release classic bt memory failed: %s", esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_init(&bt_cfg)) != ESP_OK) {
ESP_LOGI(TAG, "Bluetooth controller initialize failed: %s", esp_err_to_name(ret));
return;
}
if ((ret = esp_bt_controller_enable(ESP_BT_MODE_BLE)) != ESP_OK) {
ESP_LOGI(TAG, "Bluetooth controller enable failed: %s", esp_err_to_name(ret));
return;
}
}
/*
* @brief: BT controller callback function, used to notify the upper layer that
* controller is ready to receive command
*/
static void controller_rcv_pkt_ready(void)
{
//TODO
}
/*
* @brief: BT controller callback function, to transfer data packet to upper
* controller is ready to receive command
*/
static int host_rcv_pkt(uint8_t *data, uint16_t len)
{
uint16_t act_len = len - 1;
if(data[0] == HCIT_TYPE_EVENT) { // event data from controller
uint8_t *hci_buf = (uint8_t *)malloc(len);
memcpy(hci_buf, data +1 , act_len);
ESP_LOGI(TAG, "evt_data from controller, evt_data_length: %d :", act_len);
tud_bt_event_send(hci_buf, act_len);
free(hci_buf);
} else if(data[0] == HCIT_TYPE_ACL_DATA) { // acl data from controller
uint8_t *hci_acl_buf_contr = (uint8_t *)malloc(len);
memcpy(hci_acl_buf_contr, data +1 , act_len);
ESP_LOGI(TAG, "acl_data from controller, acl_data_length: %d :", act_len);
tud_bt_acl_data_send(hci_acl_buf_contr, act_len);
free(hci_acl_buf_contr);
}
return 0;
}
static esp_vhci_host_callback_t vhci_host_cb = {
controller_rcv_pkt_ready,
host_rcv_pkt
};
static int host_rcv_pkt_test (uint8_t *data, uint16_t len) {
ESP_LOGI(TAG, "host_rcv_pkt_test evt_data_length: %d :", len);
if(data[1] == 0x0e && data[2] == 0x07
&& data[4] == 0x02 && data[5] == 0x20) {
// LE Read Buffer size command complete event
uint16_t* size_p = NULL;
uint16_t cmd_value;
size_p = (uint8_t*)(&cmd_value);
*size_p = data[7];
*(size_p+1) = data[8];
acl_buf_size_max = *size_p;
ESP_LOGI(TAG, "acl_buf_size_max: %d", acl_buf_size_max);
}
if (!acl_buf_size_max) {
acl_buf_size_max = BUFFER_SIZE_MAX;
}
p_acl_buf = (uint8_t *)malloc(acl_buf_size_max + 1);
esp_vhci_host_register_callback(&vhci_host_cb);
return 0;
}
uint16_t make_cmd_le_read_buff_size(uint8_t *buf)
{
UINT8_TO_STREAM (buf, HCIT_TYPE_COMMAND);
UINT16_TO_STREAM (buf, LE_READ_BUFF_SIZE);
UINT8_TO_STREAM (buf, 0);
return HCI_H4_CMD_PREAMBLE_SIZE;
}
static esp_vhci_host_callback_t vhci_host_cb_test = {
controller_rcv_pkt_ready,
host_rcv_pkt_test
};
void tusb_bth_init(void)
{
ble_controller_init();
// register vhci_host_cb_test, test le read buffer size
esp_vhci_host_register_callback(&vhci_host_cb_test);
uint8_t buf[6];
uint16_t sz = make_cmd_le_read_buff_size(buf);
esp_vhci_host_send_packet(buf, sz);
}
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+
// Invoked when HCI command was received over USB from Bluetooth host.
// Detailed format is described in Bluetooth core specification Vol 2,
// Part E, 5.4.1.
// Length of the command is from 3 bytes (2 bytes for OpCode,
// 1 byte for parameter total length) to 258.
void tud_bt_hci_cmd_cb(void *hci_cmd, size_t cmd_len)
{
uint8_t *hci_cmd_buf = (uint8_t *)malloc(cmd_len + 1);
hci_cmd_buf[0] = HCIT_TYPE_COMMAND;
memcpy(hci_cmd_buf+1, hci_cmd, cmd_len);
esp_vhci_host_send_packet(hci_cmd_buf, cmd_len +1);
free(hci_cmd_buf);
}
// Invoked when ACL data was received over USB from Bluetooth host.
// Detailed format is described in Bluetooth core specification Vol 2,
// Part E, 5.4.2.
// Length is from 4 bytes, (12 bits for Handle, 4 bits for flags
// and 16 bits for data total length) to endpoint size.
static bool prepare_write = false;
static uint16_t write_offset = 0;
static uint16_t acl_data_length = 0;
void tud_bt_acl_data_received_cb(void *acl_data, uint16_t data_len)
{
// if acl_data is long data
if(!prepare_write) {
// first get acl_data_length
acl_data_length = *(((uint16_t * )acl_data) + 1);
if(acl_data_length > data_len) {
prepare_write = true;
p_acl_buf[0] = HCIT_TYPE_ACL_DATA;
memcpy(p_acl_buf + 1, acl_data, data_len);
write_offset = data_len + 1;
} else {
p_acl_buf[0] = HCIT_TYPE_ACL_DATA;
memcpy(p_acl_buf + 1, acl_data, data_len);
ESP_LOGI(TAG, "short acl_data from host, will send to controller, length: %d", (data_len + 1));
esp_vhci_host_send_packet(p_acl_buf, data_len + 1);
}
} else {
memcpy(p_acl_buf + write_offset, acl_data, data_len);
write_offset += data_len;
if(acl_data_length > write_offset) {
ESP_LOGI(TAG, "Remaining bytes: %d", (acl_data_length - write_offset));
return;
}
ESP_LOGI(TAG, "long acl_data from host, will send to controller, length: %d", (data_len + 1));
prepare_write = false;
esp_vhci_host_send_packet(p_acl_buf, data_len + write_offset);
}
//free(hci_data_buf);
}
// Called when event sent with tud_bt_event_send() was delivered to BT stack.
// Controller can release/reuse buffer with Event packet at this point.
void tud_bt_event_sent_cb(uint16_t sent_bytes)
{
//TODO
}
// Called when ACL data that was sent with tud_bt_acl_data_send()
// was delivered to BT stack.
// Controller can release/reuse buffer with ACL packet at this point.
void tud_bt_acl_data_sent_cb(uint16_t sent_bytes)
{
//TODO
}

View file

@ -0,0 +1,426 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "esp_check.h"
#include "esp_err.h"
#include "esp_log.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tusb.h"
#include "tusb_cdc_acm.h"
#include "cdc.h"
#include "sdkconfig.h"
#define RX_UNREADBUF_SZ_DEFAULT 64 // buffer storing all unread RX data
typedef struct {
bool initialized;
size_t rx_unread_buf_sz;
RingbufHandle_t rx_unread_buf;
xSemaphoreHandle ringbuf_read_mux;
uint8_t *rx_tfbuf;
tusb_cdcacm_callback_t callback_rx;
tusb_cdcacm_callback_t callback_rx_wanted_char;
tusb_cdcacm_callback_t callback_line_state_changed;
tusb_cdcacm_callback_t callback_line_coding_changed;
} esp_tusb_cdcacm_t; /*!< CDC_AMC object */
static const char *TAG = "tusb_cdc_acm";
static inline esp_tusb_cdcacm_t *get_acm(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
if (cdc_inst == NULL) {
return (esp_tusb_cdcacm_t *)NULL;
}
return (esp_tusb_cdcacm_t *)(cdc_inst->subclass_obj);
}
/* TinyUSB callbacks
********************************************************************* */
/* Invoked by cdc interface when line state changed e.g connected/disconnected */
void tud_cdc_line_state_cb(uint8_t itf, bool dtr, bool rts)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (dtr && rts) { // connected
if (acm != NULL) {
ESP_LOGV(TAG, "Host connected to CDC no.%d.", itf);
} else {
ESP_LOGW(TAG, "Host is connected to CDC no.%d, but it is not initialized. Initialize it using `tinyusb_cdc_init`.", itf);
return;
}
} else { // disconnected
if (acm != NULL) {
ESP_LOGV(TAG, "Serial device is ready to connect to CDC no.%d", itf);
} else {
return;
}
}
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_line_state_changed;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_STATE_CHANGED,
.line_state_changed_data = {
.dtr = dtr,
.rts = rts
}
};
cb(itf, &event);
}
}
}
/* Invoked when CDC interface received data from host */
void tud_cdc_rx_cb(uint8_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
if (!acm->rx_unread_buf) {
ESP_LOGE(TAG, "There is no RX buffer created");
abort();
}
} else {
tud_cdc_n_read_flush(itf); // we have no place to store data, so just drop it
return;
}
while (tud_cdc_n_available(itf)) {
int read_res = tud_cdc_n_read( itf,
acm->rx_tfbuf,
CONFIG_TINYUSB_CDC_RX_BUFSIZE );
int res = xRingbufferSend(acm->rx_unread_buf,
acm->rx_tfbuf,
read_res, 0);
if (res != pdTRUE) {
ESP_LOGW(TAG, "The unread buffer is too small, the data has been lost");
} else {
ESP_LOGV(TAG, "Sent %d bytes to the buffer", read_res);
}
}
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_rx;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX
};
cb(itf, &event);
}
}
}
// Invoked when line coding is change via SET_LINE_CODING
void tud_cdc_line_coding_cb(uint8_t itf, cdc_line_coding_t const *p_line_coding)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_line_coding_changed;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_LINE_CODING_CHANGED,
.line_coding_changed_data = {
.p_line_coding = p_line_coding,
}
};
cb(itf, &event);
}
} else {
return;
}
}
// Invoked when received `wanted_char`
void tud_cdc_rx_wanted_cb(uint8_t itf, char wanted_char)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
tusb_cdcacm_callback_t cb = acm->callback_rx_wanted_char;
if (cb) {
cdcacm_event_t event = {
.type = CDC_EVENT_RX_WANTED_CHAR,
.rx_wanted_char_data = {
.wanted_char = wanted_char,
}
};
cb(itf, &event);
}
} else {
return;
}
}
esp_err_t tinyusb_cdcacm_register_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type,
tusb_cdcacm_callback_t callback)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
switch (event_type) {
case CDC_EVENT_RX:
acm->callback_rx = callback;
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
acm->callback_rx_wanted_char = callback;
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
acm->callback_line_state_changed = callback;
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
acm->callback_line_coding_changed = callback;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
} else {
ESP_LOGE(TAG, "CDC-ACM is not initialized");
return ESP_ERR_INVALID_STATE;
}
}
esp_err_t tinyusb_cdcacm_unregister_callback(tinyusb_cdcacm_itf_t itf,
cdcacm_event_type_t event_type)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (!acm) {
ESP_LOGE(TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
return ESP_ERR_INVALID_STATE;
}
switch (event_type) {
case CDC_EVENT_RX:
acm->callback_rx = NULL;
return ESP_OK;
case CDC_EVENT_RX_WANTED_CHAR:
acm->callback_rx_wanted_char = NULL;
return ESP_OK;
case CDC_EVENT_LINE_STATE_CHANGED:
acm->callback_line_state_changed = NULL;
return ESP_OK;
case CDC_EVENT_LINE_CODING_CHANGED:
acm->callback_line_coding_changed = NULL;
return ESP_OK;
default:
ESP_LOGE(TAG, "Wrong event type");
return ESP_ERR_INVALID_ARG;
}
}
/*********************************************************************** TinyUSB callbacks*/
/* CDC-ACM
********************************************************************* */
static esp_err_t read_from_rx_unread_to_buffer(esp_tusb_cdcacm_t *acm, uint8_t *out_buf, size_t req_bytes, size_t *read_bytes)
{
uint8_t *buf = xRingbufferReceiveUpTo(acm->rx_unread_buf, read_bytes, 0, req_bytes);
if (buf) {
memcpy(out_buf, buf, *read_bytes);
vRingbufferReturnItem(acm->rx_unread_buf, (void *)(buf));
return ESP_OK;
} else {
return ESP_ERR_NO_MEM;
}
}
static esp_err_t ringbuf_mux_take(esp_tusb_cdcacm_t *acm)
{
if (xSemaphoreTake(acm->ringbuf_read_mux, 0) != pdTRUE) {
ESP_LOGW(TAG, "Read error: ACM is busy");
return ESP_ERR_INVALID_STATE;
}
return ESP_OK;
}
static esp_err_t ringbuf_mux_give(esp_tusb_cdcacm_t *acm)
{
BaseType_t ret = xSemaphoreGive(acm->ringbuf_read_mux);
assert(ret == pdTRUE);
return ESP_OK;
}
esp_err_t tinyusb_cdcacm_read(tinyusb_cdcacm_itf_t itf, uint8_t *out_buf, size_t out_buf_sz, size_t *rx_data_size)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
ESP_RETURN_ON_FALSE(acm, ESP_ERR_INVALID_STATE, TAG, "Interface is not initialized. Use `tinyusb_cdc_init` for initialization");
size_t read_sz;
/* Take a mutex to proceed two uninterrupted read operations */
ESP_RETURN_ON_ERROR(ringbuf_mux_take(acm), TAG, "ringbuf_mux_take failed");
esp_err_t res = read_from_rx_unread_to_buffer(acm, out_buf, out_buf_sz, &read_sz);
if (res != ESP_OK) {
ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed");
return res;
}
*rx_data_size = read_sz;
/* Buffer's data can be wrapped, at that situations we should make another retrievement */
if (read_from_rx_unread_to_buffer(acm, out_buf + read_sz, out_buf_sz - read_sz, &read_sz) == ESP_OK) {
*rx_data_size += read_sz;
}
ESP_RETURN_ON_ERROR(ringbuf_mux_give(acm), TAG, "ringbuf_mux_give failed");
return ESP_OK;
}
size_t tinyusb_cdcacm_write_queue_char(tinyusb_cdcacm_itf_t itf, char ch)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
return tud_cdc_n_write_char(itf, ch);
}
size_t tinyusb_cdcacm_write_queue(tinyusb_cdcacm_itf_t itf, uint8_t *in_buf, size_t in_size)
{
if (!get_acm(itf)) { // non-initialized
return 0;
}
return tud_cdc_n_write(itf, in_buf, in_size);
}
static uint32_t tud_cdc_n_write_occupied(tinyusb_cdcacm_itf_t itf)
{
return CFG_TUD_CDC_TX_BUFSIZE - tud_cdc_n_write_available(itf);
}
esp_err_t tinyusb_cdcacm_write_flush(tinyusb_cdcacm_itf_t itf, uint32_t timeout_ticks)
{
if (!get_acm(itf)) { // non-initialized
return ESP_FAIL;
}
if (!timeout_ticks) { // if no timeout - nonblocking mode
int res = tud_cdc_n_write_flush(itf);
if (!res) {
ESP_LOGW(TAG, "flush failed (res: %d)", res);
return ESP_FAIL;
} else {
if (tud_cdc_n_write_occupied(itf)) {
ESP_LOGW(TAG, "remained data to flush!");
return ESP_FAIL;
}
}
return ESP_ERR_TIMEOUT;
} else { // trying during the timeout
uint32_t ticks_start = xTaskGetTickCount();
uint32_t ticks_now = ticks_start;
while (1) { // loop until success or until the time runs out
ticks_now = xTaskGetTickCount();
if (!tud_cdc_n_write_occupied(itf)) { // if nothing to write - nothing to flush
break;
}
if (tud_cdc_n_write_flush(itf)) { // Success
break;
}
if ( (ticks_now - ticks_start) > timeout_ticks ) { // Time is up
ESP_LOGW(TAG, "Flush failed");
return ESP_ERR_TIMEOUT;
}
vTaskDelay(1);
}
return ESP_OK;
}
}
static esp_err_t alloc_obj(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
cdc_inst->subclass_obj = calloc(1, sizeof(esp_tusb_cdcacm_t));
if (!cdc_inst->subclass_obj) {
return ESP_FAIL;
} else {
return ESP_OK;
}
}
static void free_obj(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdc_t *cdc_inst = tinyusb_cdc_get_intf(itf);
free(cdc_inst->subclass_obj);
cdc_inst->subclass_obj = NULL;
}
esp_err_t tusb_cdc_acm_init(const tinyusb_config_cdcacm_t *cfg)
{
int itf = (int)cfg->cdc_port;
/* Creating a CDC object */
const tinyusb_config_cdc_t cdc_cfg = {
.usb_dev = cfg->usb_dev,
.cdc_class = TUSB_CLASS_CDC,
.cdc_subclass.comm_subclass = CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL
};
ESP_RETURN_ON_ERROR(tinyusb_cdc_init(itf, &cdc_cfg), TAG, "tinyusb_cdc_init failed");
ESP_RETURN_ON_ERROR(alloc_obj(itf), TAG, "alloc_obj failed");
esp_tusb_cdcacm_t *acm = get_acm(itf);
/* Callbacks setting up*/
if (cfg->callback_rx) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX, cfg->callback_rx);
}
if (cfg->callback_rx_wanted_char) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_RX_WANTED_CHAR, cfg->callback_rx_wanted_char);
}
if (cfg->callback_line_state_changed) {
tinyusb_cdcacm_register_callback(itf, CDC_EVENT_LINE_STATE_CHANGED, cfg->callback_line_state_changed);
}
if (cfg->callback_line_coding_changed) {
tinyusb_cdcacm_register_callback( itf, CDC_EVENT_LINE_CODING_CHANGED, cfg->callback_line_coding_changed);
}
/* Buffers */
acm->ringbuf_read_mux = xSemaphoreCreateMutex();
if (acm->ringbuf_read_mux == NULL) {
ESP_LOGE(TAG, "Creation of a ringbuf mutex failed");
free_obj(itf);
return ESP_ERR_NO_MEM;
}
acm->rx_tfbuf = malloc(CONFIG_TINYUSB_CDC_RX_BUFSIZE);
if (!acm->rx_tfbuf) {
ESP_LOGE(TAG, "Creation buffer error");
free_obj(itf);
return ESP_ERR_NO_MEM;
}
acm->rx_unread_buf_sz = cfg->rx_unread_buf_sz == 0 ? RX_UNREADBUF_SZ_DEFAULT : cfg->rx_unread_buf_sz;
acm->rx_unread_buf = xRingbufferCreate(acm->rx_unread_buf_sz, RINGBUF_TYPE_BYTEBUF);
if (acm->rx_unread_buf == NULL) {
ESP_LOGE(TAG, "Creation buffer error");
free_obj(itf);
return ESP_ERR_NO_MEM;
} else {
ESP_LOGD(TAG, "Comm Initialized buff:%d bytes", cfg->rx_unread_buf_sz);
return ESP_OK;
}
}
bool tusb_cdc_acm_initialized(tinyusb_cdcacm_itf_t itf)
{
esp_tusb_cdcacm_t *acm = get_acm(itf);
if (acm) {
return true;
} else {
return false;
}
}
/*********************************************************************** CDC-ACM*/

View file

@ -0,0 +1,142 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include <stdio_ext.h>
#include "esp_log.h"
#include "cdc.h"
#include "tusb_console.h"
#include "tinyusb.h"
#include "vfs_tinyusb.h"
#define STRINGIFY(s) STRINGIFY2(s)
#define STRINGIFY2(s) #s
static const char *TAG = "tusb_console";
typedef struct {
FILE *in;
FILE *out;
FILE *err;
} console_handle_t;
static console_handle_t con;
/**
* @brief Reopen standard streams using a new path
*
* @param f_in - pointer to a pointer holding a file for in or NULL to don't change stdin
* @param f_out - pointer to a pointer holding a file for out or NULL to don't change stdout
* @param f_err - pointer to a pointer holding a file for err or NULL to don't change stderr
* @param path - mount point
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t redirect_std_streams_to(FILE **f_in, FILE **f_out, FILE **f_err, const char *path)
{
if (f_in) {
*f_in = freopen(path, "r", stdin);
if (*f_in == NULL) {
ESP_LOGE(TAG, "Failed to reopen in!");
return ESP_FAIL;
}
}
if (f_out) {
*f_out = freopen(path, "w", stdout);
if (*f_out == NULL) {
ESP_LOGE(TAG, "Failed to reopen out!");
return ESP_FAIL;
}
}
if (f_err) {
*f_err = freopen(path, "w", stderr);
if (*f_err == NULL) {
ESP_LOGE(TAG, "Failed to reopen err!");
return ESP_FAIL;
}
}
return ESP_OK;
}
/**
* @brief Restore output to default
*
* @param f_in - pointer to a pointer of an in file updated with `redirect_std_streams_to` or NULL to don't change stdin
* @param f_out - pointer to a pointer of an out file updated with `redirect_std_streams_to` or NULL to don't change stdout
* @param f_err - pointer to a pointer of an err file updated with `redirect_std_streams_to` or NULL to don't change stderr
* @return esp_err_t ESP_FAIL or ESP_OK
*/
static esp_err_t restore_std_streams(FILE **f_in, FILE **f_out, FILE **f_err)
{
const char *default_uart_dev = "/dev/uart/" STRINGIFY(CONFIG_ESP_CONSOLE_UART_NUM);
if (f_in) {
stdin = freopen(default_uart_dev, "r", *f_in);
if (stdin == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdin!");
return ESP_FAIL;
}
}
if (f_out) {
stdout = freopen(default_uart_dev, "w", *f_out);
if (stdout == NULL) {
ESP_LOGE(TAG, "Failed to reopen stdout!");
return ESP_FAIL;
}
}
if (f_err) {
stderr = freopen(default_uart_dev, "w", *f_err);
if (stderr == NULL) {
ESP_LOGE(TAG, "Failed to reopen stderr!");
return ESP_FAIL;
}
}
return ESP_OK;
}
esp_err_t esp_tusb_init_console(int cdc_intf)
{
if (!tinyusb_cdc_initialized(cdc_intf)) {
ESP_LOGE(TAG, "Can't init the console because TinyUSB's CDC is not initialized!");
return ESP_ERR_INVALID_STATE;
}
/* Registering TUSB at VFS */
int res = esp_vfs_tusb_cdc_register(cdc_intf, NULL);
if (res != ESP_OK) {
return res;
}
res = redirect_std_streams_to(&con.in, &con.out, &con.err, "/dev/tusb_cdc");
if (res != ESP_OK) {
return res;
}
return ESP_OK;
}
esp_err_t esp_tusb_deinit_console(int cdc_intf)
{
if (!tinyusb_cdc_initialized(cdc_intf)) {
ESP_LOGE(TAG, "Can't deinit the console because TinyUSB's CDC is not initialized!");
return ESP_ERR_INVALID_STATE;
}
int res = restore_std_streams(&con.in, &con.out, &con.err);
if (res != ESP_OK) {
return res;
}
esp_vfs_tusb_cdc_unregister(NULL);
return ESP_OK;
}

View file

@ -0,0 +1,321 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/*
* After device is enumerated in dfu mode run the following commands
*
* To transfer firmware from host to device (best to test with text file)
*
* $ dfu-util -d cafe -a 0 -D [filename]
* $ dfu-util -d cafe -a 1 -D [filename]
*
* To transfer firmware from device to host:
*
* $ dfu-util -d cafe -a 0 -U [filename]
* $ dfu-util -d cafe -a 1 -U [filename]
*
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tusb.h"
#include "tinyusb.h"
#include "nvs_flash.h"
#include "esp_log.h"
#include "esp_err.h"
#include "dfu_device.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_ota_ops.h"
#include "esp_flash_partitions.h"
#include "esp_partition.h"
static char* TAG = "esp_dfu";
#define BUFFSIZE CONFIG_TINYUSB_DFU_BUFSIZE
/*an ota data write buffer ready to write to the flash*/
static char ota_write_data[BUFFSIZE + 1] = { 0 };
static esp_ota_handle_t update_handle = 0;
static const esp_partition_t *update_partition = NULL;
static int binary_file_length = 0;
/*deal with all receive packet*/
static bool image_header_was_checked = false;
static esp_partition_t *ota_partition;
static uint32_t current_index = 0;
static esp_err_t ota_start(uint8_t* ota_write_data, uint32_t data_read)
{
esp_err_t err;
/* update handle : set by esp_ota_begin(), must be freed via esp_ota_end() */
if (image_header_was_checked == false) {
esp_app_desc_t new_app_info;
ESP_LOGI(TAG, "Starting OTA example");
const esp_partition_t *running = esp_ota_get_running_partition();
ESP_LOGI(TAG, "Running partition type %d subtype %d (offset 0x%08x)",
running->type, running->subtype, running->address);
update_partition = esp_ota_get_next_update_partition(NULL);
assert(update_partition != NULL);
ESP_LOGI(TAG, "Writing to partition subtype %d at offset 0x%x",
update_partition->subtype, update_partition->address);
if (data_read > sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t) + sizeof(esp_app_desc_t)) {
// check current version with downloading
memcpy(&new_app_info, &ota_write_data[sizeof(esp_image_header_t) + sizeof(esp_image_segment_header_t)], sizeof(esp_app_desc_t));
ESP_LOGI(TAG, "New firmware version: %s", new_app_info.version);
esp_app_desc_t running_app_info;
if (esp_ota_get_partition_description(running, &running_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Running firmware version: %s", running_app_info.version);
}
const esp_partition_t* last_invalid_app = esp_ota_get_last_invalid_partition();
esp_app_desc_t invalid_app_info;
if (esp_ota_get_partition_description(last_invalid_app, &invalid_app_info) == ESP_OK) {
ESP_LOGI(TAG, "Last invalid firmware version: %s", invalid_app_info.version);
}
// check current version with last invalid partition
if (last_invalid_app != NULL) {
if (memcmp(invalid_app_info.version, new_app_info.version, sizeof(new_app_info.version)) == 0) {
ESP_LOGW(TAG, "New version is the same as invalid version.");
ESP_LOGW(TAG, "Previously, there was an attempt to launch the firmware with %s version, but it failed.", invalid_app_info.version);
ESP_LOGW(TAG, "The firmware has been rolled back to the previous version.");
}
}
image_header_was_checked = true;
err = esp_ota_begin(update_partition, OTA_WITH_SEQUENTIAL_WRITES, &update_handle);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_begin failed (%s)", esp_err_to_name(err));
esp_ota_abort(update_handle);
return err;
}
ESP_LOGI(TAG, "esp_ota_begin succeeded");
} else {
ESP_LOGE(TAG, "received package is not fit len");
esp_ota_abort(update_handle);
return err;
}
}
err = esp_ota_write( update_handle, (const void *)ota_write_data, data_read);
if (err != ESP_OK) {
esp_ota_abort(update_handle);
return err;
}
binary_file_length += data_read;
ESP_LOGD(TAG, "Written image length %d", binary_file_length);
return err;
}
static esp_err_t ota_complete(void)
{
esp_err_t err = ESP_OK;
ESP_LOGI(TAG, "Total Write binary data length: %d", binary_file_length);
err = esp_ota_end(update_handle);
if (err != ESP_OK) {
if (err == ESP_ERR_OTA_VALIDATE_FAILED) {
ESP_LOGE(TAG, "Image validation failed, image is corrupted");
} else {
ESP_LOGE(TAG, "esp_ota_end failed (%s)!", esp_err_to_name(err));
}
return err;
}
err = esp_ota_set_boot_partition(update_partition);
if (err != ESP_OK) {
ESP_LOGE(TAG, "esp_ota_set_boot_partition failed (%s)!", esp_err_to_name(err));
return err;
}
ESP_LOGI(TAG, "OTA done, please restart system!");
return ESP_OK;
}
static uint16_t upload_bin(uint8_t* data, uint16_t length)
{
uint16_t read_size = length;
if (ota_partition == NULL) {
ota_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_OTA_MIN, NULL);
if (ota_partition == NULL) {
ESP_LOGE(TAG, "not found ota_0");
return 0;
}
ESP_LOGI(TAG, "Bin size: %d, addr: %p", ota_partition->size, ota_partition->address);
}
if (current_index > ota_partition->size) {
ESP_LOGI(TAG, "read error %d,%d\n", current_index, ota_partition->size);
return 0;
}
if (current_index == ota_partition->size) { // upload done
ESP_LOGI(TAG, "Upload done");
current_index = 0;
free(ota_partition);
ota_partition = NULL;
return 0;
} else if (current_index + read_size > ota_partition->size) { // the last data
read_size = ota_partition->size - current_index;
}
ESP_ERROR_CHECK(esp_partition_read(ota_partition, current_index, data, read_size));
current_index += read_size;
return read_size;
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
ESP_LOGI(TAG, "mounted\n");
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
ESP_LOGI(TAG, "umounted\n");
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
}
//--------------------------------------------------------------------+
// DFU callbacks
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
//--------------------------------------------------------------------+
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
// During this period, USB host won't try to communicate with us.
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state)
{
if ( state == DFU_DNBUSY )
{
// For this example
// - Atl0 Flash is fast : 1 ms
// - Alt1 EEPROM is slow: 100 ms
return (alt == 0) ? 1 : 100;
}
else if (state == DFU_MANIFEST)
{
// since we don't buffer entire image and do any flashing in manifest stage
return 0;
}
return 0;
}
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
// This callback could be returned before flashing op is complete (async).
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_download_cb(uint8_t alt, uint16_t block_num, uint8_t const* data, uint16_t length)
{
(void) alt;
(void) block_num;
ESP_LOGI(TAG, "Received Alt %u BlockNum %u of length %u", alt, block_num, length);
esp_err_t ret = ota_start(data, length);
if (ret == ESP_OK) {
tud_dfu_finish_flashing(DFU_STATUS_OK);
} else {
tud_dfu_finish_flashing(DFU_STATUS_ERR_WRITE);
}
}
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
// Application can do checksum, or actual flashing if buffered entire image previously.
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_manifest_cb(uint8_t alt)
{
(void) alt;
ESP_LOGI(TAG, "Download completed, enter manifestation\r\n");
esp_err_t ret = ota_complete();
// flashing op for manifest is complete without error
// Application can perform checksum, should it fail, use appropriate status such as errVERIFY.
if (ret == ESP_OK) {
tud_dfu_finish_flashing(DFU_STATUS_OK);
} else {
tud_dfu_finish_flashing(DFU_STATUS_ERR_VERIFY);
}
}
// Invoked when received DFU_UPLOAD request
// Application must populate data with up to length bytes and
// Return the number of written bytes
uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length)
{
(void) block_num;
(void) length;
ESP_LOGI(TAG, "upload data,block_num: %d,length: %d\n", block_num, length);
uint16_t xfer_len = upload_bin(data, length);
return xfer_len;
}
// Invoked when the Host has terminated a download or upload transfer
void tud_dfu_abort_cb(uint8_t alt)
{
(void) alt;
ESP_LOGI(TAG, "Host aborted transfer\r\n");
}
// Invoked when a DFU_DETACH request is received
void tud_dfu_detach_cb(void)
{
ESP_LOGI(TAG, "Host detach, upload/download done\r\n");
//esp_restart();
}

View file

@ -0,0 +1,141 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdint.h>
#include "esp_err.h"
#include "sdkconfig.h"
#include "esp_log.h"
#include "tusb_hid.h"
#include "descriptors_control.h"
static const char *TAG = "tusb_hid";
static bool s_keyboard_pressed = false;
void tinyusb_hid_mouse_move_report(int8_t x, int8_t y, int8_t vertical, int8_t horizontal)
{
ESP_LOGD(TAG, "x=%d, y=%d, vertical=%d, horizontal=%d", x, y, vertical, horizontal);
// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}
tud_hid_mouse_report(REPORT_ID_MOUSE, 0x00, x, y, vertical, horizontal);
}
}
void tinyusb_hid_mouse_button_report(uint8_t buttons_map)
{
ESP_LOGD(TAG, "buttons_map = %u", buttons_map);
// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}
tud_hid_mouse_report(REPORT_ID_MOUSE, buttons_map, 0, 0, 0, 0);
}
}
void tinyusb_hid_keyboard_report(uint8_t keycode[])
{
ESP_LOGD(TAG, "keycode = %u %u %u %u %u %u", keycode[0], keycode[1], keycode[2], keycode[3], keycode[4], keycode[5]);
// Remote wakeup
if (tud_suspended()) {
// Wake up host if we are in suspend mode
// and REMOTE_WAKEUP feature is enabled by host
tud_remote_wakeup();
} else {
// Send the 1st of report chain, the rest will be sent by tud_hid_report_complete_cb()
// skip if hid is not ready yet
if (!tud_hid_ready()) {
ESP_LOGW(TAG, "tinyusb not ready %s %d", __func__, __LINE__);
return;
}
uint8_t _keycode[6] = { 0 };
_keycode[0] = keycode[0];
_keycode[1] = keycode[1];
_keycode[2] = keycode[2];
_keycode[3] = keycode[3];
_keycode[4] = keycode[4];
_keycode[5] = keycode[5];
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, _keycode);
s_keyboard_pressed = true;
}
}
/************************************************** TinyUSB callbacks ***********************************************/
// Invoked when sent REPORT successfully to host
// Application can use this to send the next report
// Note: For composite reports, report[0] is report ID
void tud_hid_report_complete_cb(uint8_t itf, uint8_t const *report, uint8_t len)
{
(void) itf;
(void) len;
uint8_t report_id = report[0];
if (report_id == REPORT_ID_KEYBOARD && s_keyboard_pressed) {
tud_hid_keyboard_report(REPORT_ID_KEYBOARD, 0, NULL);
s_keyboard_pressed = false;
}
}
// Invoked when received GET_REPORT control request
// Application must fill buffer report's content and return its length.
// Return zero will cause the stack to STALL request
uint16_t tud_hid_get_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
{
// TODO not Implemented
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) reqlen;
return 0;
}
// Invoked when received SET_REPORT control request or
// received data on OUT endpoint ( Report ID = 0, Type = 0 )
void tud_hid_set_report_cb(uint8_t itf, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
{
// TODO set LED based on CAPLOCK, NUMLOCK etc...
(void) itf;
(void) report_id;
(void) report_type;
(void) buffer;
(void) bufsize;
}

View file

@ -0,0 +1,291 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "esp_err.h"
#include "esp_log.h"
#include "ffconf.h"
#include "ff.h"
#include "diskio.h"
#include "tusb_msc.h"
static uint8_t s_pdrv = 0;
static int s_disk_block_size = 0;
#define LOGICAL_DISK_NUM 1
static bool ejected[LOGICAL_DISK_NUM] = {true};
esp_err_t tusb_msc_init(const tinyusb_config_msc_t *cfg)
{
if (cfg == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (cfg->pdrv > 0) {
return ESP_ERR_NOT_SUPPORTED;
}
s_pdrv = cfg->pdrv;
return ESP_OK;
}
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
// Reset the ejection tracking every time we're plugged into USB. This allows for us to battery
// power the device, eject, unplug and plug it back in to get the drive.
for (uint8_t i = 0; i < LOGICAL_DISK_NUM; i++) {
ejected[i] = false;
}
ESP_LOGI(__func__, "");
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
ESP_LOGW(__func__, "");
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allows us to perform remote wakeup
// USB Specs: Within 7ms, device must draw an average current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
ESP_LOGW(__func__, "");
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
ESP_LOGW(__func__, "");
}
// Callback invoked when WRITE10 command is completed (status received and accepted by host).
// used to flush any pending cache.
void tud_msc_write10_complete_cb(uint8_t lun)
{
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return;
}
// This write is complete, start the autoreload clock.
ESP_LOGD(__func__, "");
}
static bool _logical_disk_ejected(void)
{
bool all_ejected = true;
for (uint8_t i = 0; i < LOGICAL_DISK_NUM; i++) {
all_ejected &= ejected[i];
}
return all_ejected;
}
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return;
}
const char vid[] = "Espressif";
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
memcpy(vendor_id, vid, strlen(vid));
memcpy(product_id, pid, strlen(pid));
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return false;
}
if (_logical_disk_ejected()) {
// Set 0x3a for media not present.
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3A, 0x00);
return false;
}
return true;
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
{
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return;
}
disk_ioctl(s_pdrv, GET_SECTOR_COUNT, block_count);
disk_ioctl(s_pdrv, GET_SECTOR_SIZE, block_size);
s_disk_block_size = *block_size;
ESP_LOGD(__func__, "GET_SECTOR_COUNT = %dGET_SECTOR_SIZE = %d", *block_count, *block_size);
}
bool tud_msc_is_writable_cb(uint8_t lun)
{
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return false;
}
return true;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
ESP_LOGI(__func__, "");
(void) power_condition;
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return false;
}
if (load_eject) {
if (!start) {
// Eject but first flush.
if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) {
return false;
} else {
ejected[lun] = true;
}
} else {
// We can only load if it hasn't been ejected.
return !ejected[lun];
}
} else {
if (!start) {
// Stop the unit but don't eject.
if (disk_ioctl(s_pdrv, CTRL_SYNC, NULL) != RES_OK) {
return false;
}
}
// Always start the unit, even if ejected. Whether media is present is a separate check.
}
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
{
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return 0;
}
const uint32_t block_count = bufsize / s_disk_block_size;
disk_read(s_pdrv, buffer, lba, block_count);
return block_count * s_disk_block_size;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
{
ESP_LOGD(__func__, "");
(void) offset;
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return 0;
}
const uint32_t block_count = bufsize / s_disk_block_size;
disk_write(s_pdrv, buffer, lba, block_count);
return block_count * s_disk_block_size;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb(uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
ESP_LOGD(__func__, "");
if (lun >= LOGICAL_DISK_NUM) {
ESP_LOGE(__func__, "invalid lun number %u", lun);
return 0;
}
void const *response = NULL;
uint16_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
switch (scsi_cmd[0]) {
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;
break;
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// negative means error -> tinyusb could stall and/or response with failed status
resplen = -1;
break;
}
// return resplen must not larger than bufsize
if (resplen > bufsize) {
resplen = bufsize;
}
if (response && (resplen > 0)) {
if (in_xfer) {
memcpy(buffer, response, resplen);
} else {
// SCSI output
}
}
return resplen;
}

View file

@ -0,0 +1,93 @@
/**
* @copyright Copyright 2021 Espressif Systems (Shanghai) Co. Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "lwip/netif.h"
#include "esp_private/wifi.h"
#include "tusb_net.h"
extern bool s_wifi_is_connected;
static SemaphoreHandle_t Net_Semphore;
bool tud_network_wait_xmit(uint32_t ms)
{
if (xSemaphoreTake(Net_Semphore, ms/portTICK_PERIOD_MS) == pdTRUE) {
xSemaphoreGive(Net_Semphore);
return true;
}
return false;
}
esp_err_t pkt_wifi2usb(void *buffer, uint16_t len, void *eb)
{
if (!tud_ready()) {
esp_wifi_internal_free_rx_buffer(eb);
return ERR_USE;
}
if (tud_network_wait_xmit(100)) {
/* if the network driver can accept another packet, we make it happen */
if (tud_network_can_xmit()) {
tud_network_xmit(buffer, len);
}
}
esp_wifi_internal_free_rx_buffer(eb);
return ESP_OK;
}
void tusb_net_init(void)
{
vSemaphoreCreateBinary(Net_Semphore);
}
//--------------------------------------------------------------------+
// tinyusb callbacks
//--------------------------------------------------------------------+
bool tud_network_recv_cb(const uint8_t *src, uint16_t size)
{
if (s_wifi_is_connected) {
esp_wifi_internal_tx(ESP_IF_WIFI_STA, src, size);
}
tud_network_recv_renew();
return true;
}
uint16_t tud_network_xmit_cb(uint8_t *dst, void *ref, uint16_t arg)
{
uint16_t len = arg;
/* traverse the "pbuf chain"; see ./lwip/src/core/pbuf.c for more info */
memcpy(dst, ref, len);
return len;
}
void tud_network_init_cb(void)
{
/* TODO */
}
void tud_network_idle_status_change_cb(bool enable)
{
if (enable == true) {
xSemaphoreGive(Net_Semphore);
} else {
xSemaphoreTake(Net_Semphore, 0);
}
}

View file

@ -0,0 +1,54 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "sdkconfig.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "esp_check.h"
#include "tinyusb.h"
#include "tusb_tasks.h"
const static char *TAG = "tusb_tsk";
static TaskHandle_t s_tusb_tskh;
/**
* @brief This top level thread processes all usb events and invokes callbacks
*/
static void tusb_device_task(void *arg)
{
ESP_LOGD(TAG, "tinyusb task started");
while (1) { // RTOS forever loop
tud_task();
}
}
esp_err_t tusb_run_task(void)
{
// This function is not garanteed to be thread safe, if invoked multiple times without calling `tusb_stop_task`, will cause memory leak
// doing a sanity check anyway
ESP_RETURN_ON_FALSE(!s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task already started");
// Create a task for tinyusb device stack:
xTaskCreate(tusb_device_task, "TinyUSB", CONFIG_TINYUSB_TASK_STACK_SIZE, NULL, CONFIG_TINYUSB_TASK_PRIORITY, &s_tusb_tskh);
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_FAIL, TAG, "create TinyUSB main task failed");
return ESP_OK;
}
esp_err_t tusb_stop_task(void)
{
ESP_RETURN_ON_FALSE(s_tusb_tskh, ESP_ERR_INVALID_STATE, TAG, "TinyUSB main task not started yet");
vTaskDelete(s_tusb_tskh);
s_tusb_tskh = NULL;
return ESP_OK;
}

View file

@ -0,0 +1,346 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
/* This example demonstrates WebUSB as web serial with browser with WebUSB support (e.g Chrome).
* After enumerated successfully, browser will pop-up notification
* with URL to landing page, click on it to test
* - Click "Connect" and select device, When connected the on-board LED will litted up.
* - Any charters received from either webusb/Serial will be echo back to webusb and Serial
*
* Note:
* - The WebUSB landing page notification is currently disabled in Chrome
* on Windows due to Chromium issue 656702 (https://crbug.com/656702). You have to
* go to landing page (below) to test
*
* - On Windows 7 and prior: You need to use Zadig tool to manually bind the
* WebUSB interface with the WinUSB driver for Chrome to access. From windows 8 and 10, this
* is done automatically by firmware.
*
* - On Linux/macOS, udev permission may need to be updated by
* - copying '/examples/device/99-tinyusb.rules' file to /etc/udev/rules.d/ then
* - run 'sudo udevadm control --reload-rules && sudo udevadm trigger'
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "tusb.h"
#include "tinyusb.h"
#include "usbd.h"
#include "vendor_device.h"
#include "descriptors_control.h"
#include "esp_log.h"
#include "esp_err.h"
static char* TAG = "web_usb";
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF PROTYPES
//--------------------------------------------------------------------+
enum
{
VENDOR_REQUEST_WEBUSB = 1,
VENDOR_REQUEST_MICROSOFT = 2
};
extern uint8_t const desc_ms_os_20[];
static uint8_t webusb_connected_flag = 0;
/* Blink pattern
* - 250 ms : device not mounted
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
enum {
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
BLINK_ALWAYS_ON = UINT32_MAX,
BLINK_ALWAYS_OFF = 0
};
static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
#define URL "example.tinyusb.org/webusb-serial/"
const tusb_desc_webusb_url_t desc_url =
{
.bLength = 3 + sizeof(URL) - 1,
.bDescriptorType = 3, // WEBUSB URL type
.bScheme = 1, // 0: http, 1: https, 0xFF: ""
.url = URL
};
static bool web_serial_connected = false;
//--------------------------------------------------------------------+
// BOS Descriptor
//--------------------------------------------------------------------+
/* Microsoft OS 2.0 registry property descriptor
Per MS requirements https://msdn.microsoft.com/en-us/library/windows/hardware/hh450799(v=vs.85).aspx
device should create DeviceInterfaceGUIDs. It can be done by driver and
in case of real PnP solution device should expose MS "Microsoft OS 2.0
registry property descriptor". Such descriptor can insert any record
into Windows registry per device/configuration/interface. In our case it
will insert "DeviceInterfaceGUIDs" multistring property.
GUID is freshly generated and should be OK to use.
https://developers.google.com/web/fundamentals/native-hardware/build-for-webusb/
(Section Microsoft OS compatibility descriptors)
*/
#define BOS_TOTAL_LEN (TUD_BOS_DESC_LEN + TUD_BOS_WEBUSB_DESC_LEN + TUD_BOS_MICROSOFT_OS_DESC_LEN)
#define MS_OS_20_DESC_LEN 0xB2
// BOS Descriptor is required for webUSB
uint8_t const desc_bos[] =
{
// total length, number of device caps
TUD_BOS_DESCRIPTOR(BOS_TOTAL_LEN, 2),
// Vendor Code, iLandingPage
TUD_BOS_WEBUSB_DESCRIPTOR(VENDOR_REQUEST_WEBUSB, 1),
// Microsoft OS 2.0 descriptor
TUD_BOS_MS_OS_20_DESCRIPTOR(MS_OS_20_DESC_LEN, VENDOR_REQUEST_MICROSOFT)
};
uint8_t const * _tud_descriptor_bos_cb(void)
{
return desc_bos;
}
uint8_t const desc_ms_os_20[] =
{
// Set header: length, type, windows version, total length
U16_TO_U8S_LE(0x000A), U16_TO_U8S_LE(MS_OS_20_SET_HEADER_DESCRIPTOR), U32_TO_U8S_LE(0x06030000), U16_TO_U8S_LE(MS_OS_20_DESC_LEN),
// Configuration subset header: length, type, configuration index, reserved, configuration total length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_CONFIGURATION), 0, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A),
// Function Subset header: length, type, first interface, reserved, subset length
U16_TO_U8S_LE(0x0008), U16_TO_U8S_LE(MS_OS_20_SUBSET_HEADER_FUNCTION), ITF_NUM_VENDOR, 0, U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08),
// MS OS 2.0 Compatible ID descriptor: length, type, compatible ID, sub compatible ID
U16_TO_U8S_LE(0x0014), U16_TO_U8S_LE(MS_OS_20_FEATURE_COMPATBLE_ID), 'W', 'I', 'N', 'U', 'S', 'B', 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // sub-compatible
// MS OS 2.0 Registry property descriptor: length, type
U16_TO_U8S_LE(MS_OS_20_DESC_LEN-0x0A-0x08-0x08-0x14), U16_TO_U8S_LE(MS_OS_20_FEATURE_REG_PROPERTY),
U16_TO_U8S_LE(0x0007), U16_TO_U8S_LE(0x002A), // wPropertyDataType, wPropertyNameLength and PropertyName "DeviceInterfaceGUIDs\0" in UTF-16
'D', 0x00, 'e', 0x00, 'v', 0x00, 'i', 0x00, 'c', 0x00, 'e', 0x00, 'I', 0x00, 'n', 0x00, 't', 0x00, 'e', 0x00,
'r', 0x00, 'f', 0x00, 'a', 0x00, 'c', 0x00, 'e', 0x00, 'G', 0x00, 'U', 0x00, 'I', 0x00, 'D', 0x00, 's', 0x00, 0x00, 0x00,
U16_TO_U8S_LE(0x0050), // wPropertyDataLength
//bPropertyData: “{975F44D9-0D08-43FD-8B3E-127CA8AFFF9D}”.
'{', 0x00, '9', 0x00, '7', 0x00, '5', 0x00, 'F', 0x00, '4', 0x00, '4', 0x00, 'D', 0x00, '9', 0x00, '-', 0x00,
'0', 0x00, 'D', 0x00, '0', 0x00, '8', 0x00, '-', 0x00, '4', 0x00, '3', 0x00, 'F', 0x00, 'D', 0x00, '-', 0x00,
'8', 0x00, 'B', 0x00, '3', 0x00, 'E', 0x00, '-', 0x00, '1', 0x00, '2', 0x00, '7', 0x00, 'C', 0x00, 'A', 0x00,
'8', 0x00, 'A', 0x00, 'F', 0x00, 'F', 0x00, 'F', 0x00, '9', 0x00, 'D', 0x00, '}', 0x00, 0x00, 0x00, 0x00, 0x00
};
TU_VERIFY_STATIC(sizeof(desc_ms_os_20) == MS_OS_20_DESC_LEN, "Incorrect size");
extern esp_err_t wifi_cmd_sta_join(const char* ssid, const char* pass);
static void webusb_task(void *pvParameters)
{
esp_err_t ret;
uint32_t recv_len = 0;
uint32_t str_index = 0;
char ssid[64], pwd[64];
bool is_ssid = true;
while(1) {
if ( web_serial_connected )
{
if ( tud_vendor_available() )
{
uint8_t buf;
uint32_t count = tud_vendor_read(&buf, 1);
if (count != 1) {
ESP_LOGI(TAG, "error len: %d", count);
}
if (is_ssid) { // ssid
if (buf == '\r') {
ESP_LOGI(TAG, "Done\n");
ssid[str_index] = '\0';
tud_vendor_write("Received ssid\r\n", strlen("Received ssid\r\n"));
is_ssid = false;
str_index = 0;
continue;
}
ssid[str_index++] = buf;
} else { //password
if (buf == '\r') {
pwd[str_index] = '\0';
is_ssid = true;
str_index = 0;
tud_vendor_write("Received pwd\r\n", strlen("Received pwd\r\n"));
ESP_LOGI(TAG, "\r\nReceived ssid:%s\r\n", ssid);
ESP_LOGI(TAG, "\r\nReceived pwd: %s\r\n", pwd);
ret = wifi_cmd_sta_join(ssid, pwd);
if (ret == ESP_OK) {
ESP_LOGI(TAG, "connect success\n");
tud_vendor_write("Connect success\r\n", strlen("Connect success\r\n"));
} else {
ESP_LOGI(TAG, "connect fail\n");
tud_vendor_write("Connect fail\r\n", strlen("Connect fail\r\n"));
}
continue;
}
pwd[str_index++] = buf;
}
}
} else {
if (webusb_connected_flag) { // Created webusb task
webusb_connected_flag = 0;
vTaskDelete(NULL);
}
}
vTaskDelay(100 / portTICK_RATE_MS);
}
}
//--------------------------------------------------------------------+
// Device callbacks
//--------------------------------------------------------------------+
// Invoked when device is mounted
void tud_mount_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
void tud_umount_cb(void)
{
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// Invoked when usb bus is suspended
// remote_wakeup_en : if host allow us to perform remote wakeup
// Within 7ms, device must draw an average of current less than 2.5 mA from bus
void tud_suspend_cb(bool remote_wakeup_en)
{
(void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
void tud_resume_cb(void)
{
blink_interval_ms = BLINK_MOUNTED;
}
//--------------------------------------------------------------------+
// WebUSB use vendor class
//--------------------------------------------------------------------+
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool tud_vendor_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// nothing to with DATA & ACK stage
if (stage != CONTROL_STAGE_SETUP) return true;
switch (request->bmRequestType_bit.type)
{
ESP_LOGI(TAG, "bmRequestType_bit:%d\n", request->bmRequestType_bit.type);
case TUSB_REQ_TYPE_VENDOR:
switch (request->bRequest)
{
case VENDOR_REQUEST_WEBUSB:
// match vendor request in BOS descriptor
// Get landing page url
return tud_control_xfer(rhport, request, (void*)(uintptr_t) &desc_url, desc_url.bLength);
case VENDOR_REQUEST_MICROSOFT:
if ( request->wIndex == 7 )
{
// Get Microsoft OS 2.0 compatible descriptor
uint16_t total_len;
memcpy(&total_len, desc_ms_os_20+8, 2);
return tud_control_xfer(rhport, request, (void*)(uintptr_t) desc_ms_os_20, total_len);
}else
{
return false;
}
default: break;
}
break;
case TUSB_REQ_TYPE_CLASS:
if (request->bRequest == 0x22)
{
ESP_LOGI(TAG, "wValue:%d\n", request->wValue);
// Webserial simulate the CDC_REQUEST_SET_CONTROL_LINE_STATE (0x22) to connect and disconnect.
web_serial_connected = (request->wValue != 0);
// Always lit LED if connected
if ( web_serial_connected )
{
if (!webusb_connected_flag) {
xTaskCreate(webusb_task, "webusb", 4096, NULL, 5, NULL);
webusb_connected_flag = 1;
}
//tud_vendor_write_str("\r\nTinyUSB WebUSB device example\r\n");
}
// response with status OK
return tud_control_status(rhport, request);
}
break;
default: break;
}
// stall unknown request
return false;
}

View file

@ -0,0 +1,121 @@
// Copyright 2020 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "usb_descriptors.h"
#include "descriptors_control.h"
#include "sdkconfig.h"
#define USB_TUSB_PID 0x4012
tusb_desc_strarray_device_t descriptor_str_tinyusb = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
"TinyUSB", // 1: Manufacturer
"TinyUSB Device", // 2: Product
"123456", // 3: Serials, should use chip ID
"TinyUSB CDC", // 4: CDC Interface
"TinyUSB WebUSB", // 5. Webusb
"TinyUSB MSC", // 6: MSC Interface
"TinyUSB HID" // 7: HID
};
/* End of TinyUSB default */
/**** Kconfig driven Descriptor ****/
tusb_desc_device_t descriptor_kconfig = {
.bLength = sizeof(descriptor_kconfig),
.bDescriptorType = TUSB_DESC_DEVICE,
#if CFG_TUD_VENDOR
.bcdUSB = 0x0210,
#else
.bcdUSB = 0x0200,
#endif
#if CFG_TUD_CDC
// Use Interface Association Descriptor (IAD) for CDC
// As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1)
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
#elif CFG_TUD_BTH
.bDeviceClass = TUSB_CLASS_WIRELESS_CONTROLLER,
.bDeviceSubClass = TUD_BT_APP_SUBCLASS,
.bDeviceProtocol = TUD_BT_PROTOCOL_PRIMARY_CONTROLLER,
#else
.bDeviceClass = 0x00,
.bDeviceSubClass = 0x00,
.bDeviceProtocol = 0x00,
#endif
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
#if CONFIG_TINYUSB_DESC_USE_ESPRESSIF_VID
.idVendor = USB_ESPRESSIF_VID,
#else
.idVendor = CONFIG_TINYUSB_DESC_CUSTOM_VID,
#endif
#if CONFIG_TINYUSB_DESC_USE_DEFAULT_PID
.idProduct = USB_TUSB_PID,
#else
.idProduct = CONFIG_TINYUSB_DESC_CUSTOM_PID,
#endif
.bcdDevice = CONFIG_TINYUSB_DESC_BCD_DEVICE,
.iManufacturer = 0x01,
.iProduct = 0x02,
.iSerialNumber = 0x03,
.bNumConfigurations = 0x01
};
tusb_desc_strarray_device_t descriptor_str_kconfig = {
// array of pointer to string descriptors
(char[]){0x09, 0x04}, // 0: is supported language is English (0x0409)
CONFIG_TINYUSB_DESC_MANUFACTURER_STRING, // 1: Manufacturer
CONFIG_TINYUSB_DESC_PRODUCT_STRING, // 2: Product
CONFIG_TINYUSB_DESC_SERIAL_STRING, // 3: Serials, should use chip ID
#if CFG_TUD_CDC
CONFIG_TINYUSB_DESC_CDC_STRING, // CDC Interface
#endif
#if CFG_TUD_NET
CONFIG_TINYUSB_DESC_NET_STRING, // NET Interface
"", // MAC
#endif
#if CFG_TUD_VENDOR
"TinyUSB vendor", // Vendor Interface
#endif
#if CFG_TUD_MSC
CONFIG_TINYUSB_DESC_MSC_STRING, // MSC Interface
#endif
#if CFG_TUD_HID
CONFIG_TINYUSB_DESC_HID_STRING // HIDs
#endif
#if CFG_TUD_BTH
CONFIG_TINYUSB_DESC_BTH_STRING, // BTH
#endif
#if CFG_TUD_DFU
"FLASH", // 4: DFU Partition 1
"EEPROM", // 5: DFU Partition 2
#endif
};
/* End of Kconfig driven Descriptor */

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,297 @@
// Copyright 2020 Espressif Systems (Shanghai) Co. Ltd.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdio_ext.h>
#include <string.h>
#include <sys/errno.h>
#include <sys/fcntl.h>
#include <sys/lock.h>
#include <sys/param.h>
#include "esp_attr.h"
#include "esp_log.h"
#include "esp_vfs.h"
#include "esp_vfs_dev.h"
#include "tinyusb.h"
#include "tusb_cdc_acm.h"
#include "vfs_tinyusb.h"
#include "sdkconfig.h"
const static char *TAG = "tusb_vfs";
#define VFS_TUSB_MAX_PATH 16
#define VFS_TUSB_PATH_DEFAULT "/dev/tusb_cdc"
// Token signifying that no character is available
#define NONE -1
#define FD_CHECK(fd, ret_val) do { \
if ((fd) != 0) { \
errno = EBADF; \
return (ret_val); \
} \
} while (0)
#if CONFIG_NEWLIB_STDOUT_LINE_ENDING_CRLF
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDOUT_LINE_ENDING_CR
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_TX_MODE ESP_LINE_ENDINGS_LF
#endif
#if CONFIG_NEWLIB_STDIN_LINE_ENDING_CRLF
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CRLF
#elif CONFIG_NEWLIB_STDIN_LINE_ENDING_CR
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_CR
#else
# define DEFAULT_RX_MODE ESP_LINE_ENDINGS_LF
#endif
typedef struct {
_lock_t write_lock;
_lock_t read_lock;
esp_line_endings_t tx_mode; // Newline conversion mode when transmitting
esp_line_endings_t rx_mode; // Newline conversion mode when receiving
uint32_t flags;
char vfs_path[VFS_TUSB_MAX_PATH];
int cdc_intf;
} vfs_tinyusb_t;
static vfs_tinyusb_t s_vfstusb;
static esp_err_t apply_path(char const *path)
{
if (path != NULL) {
size_t path_len = strlen(path) + 1;
if (path_len > VFS_TUSB_MAX_PATH) {
ESP_LOGE(TAG, "The path is too long; maximum is %d characters", VFS_TUSB_MAX_PATH);
return ESP_ERR_INVALID_ARG;
}
strncpy(s_vfstusb.vfs_path, path, (VFS_TUSB_MAX_PATH - 1));
} else {
strncpy(s_vfstusb.vfs_path,
VFS_TUSB_PATH_DEFAULT,
(VFS_TUSB_MAX_PATH - 1));
}
ESP_LOGV(TAG, "Path is set to `%s`", s_vfstusb.vfs_path);
return ESP_OK;
}
/**
* @brief Fill s_vfstusb
*
* @param cdc_intf - interface of tusb for registration
* @param path - a path where the CDC will be registered
* @return esp_err_t ESP_OK or ESP_ERR_INVALID_ARG
*/
static esp_err_t vfstusb_init(int cdc_intf, char const *path)
{
s_vfstusb.cdc_intf = cdc_intf;
s_vfstusb.tx_mode = DEFAULT_TX_MODE;
s_vfstusb.rx_mode = DEFAULT_RX_MODE;
return apply_path(path);
}
/**
* @brief Clear s_vfstusb to default values
*/
static void vfstusb_deinit(void)
{
memset(&s_vfstusb, 0, sizeof(s_vfstusb));
}
static int tusb_open(const char *path, int flags, int mode)
{
(void) mode;
(void) path;
s_vfstusb.flags = flags | O_NONBLOCK; // for now only non-blocking mode is implemented
return 0;
}
static ssize_t tusb_write(int fd, const void *data, size_t size)
{
FD_CHECK(fd, -1);
size_t written_sz = 0;
const char *data_c = (const char *)data;
_lock_acquire(&(s_vfstusb.write_lock));
for (size_t i = 0; i < size; i++) {
int c = data_c[i];
/* handling the EOL */
if (c == '\n' && s_vfstusb.tx_mode != ESP_LINE_ENDINGS_LF) {
if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, '\r')) {
written_sz++;
} else {
break; // can't write anymore
}
if (s_vfstusb.tx_mode == ESP_LINE_ENDINGS_CR) {
continue;
}
}
/* write a char */
if (tinyusb_cdcacm_write_queue_char(s_vfstusb.cdc_intf, c)) {
written_sz++;
} else {
break; // can't write anymore
}
}
tud_cdc_n_write_flush(s_vfstusb.cdc_intf);
_lock_release(&(s_vfstusb.write_lock));
return written_sz;
}
static int tusb_close(int fd)
{
FD_CHECK(fd, -1);
return 0;
}
static ssize_t tusb_read(int fd, void *data, size_t size)
{
FD_CHECK(fd, -1);
char *data_c = (char *) data;
size_t received = 0;
_lock_acquire(&(s_vfstusb.read_lock));
int cm1 = NONE;
int c = NONE;
while (received < size) {
cm1 = c; // store the old char
int c = tud_cdc_n_read_char(0); // get a new one
if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
if (c == '\r') {
c = '\n';
}
} else if (s_vfstusb.rx_mode == ESP_LINE_ENDINGS_CR) {
if ((c == '\n') & (cm1 == '\r')) {
--received; // step back
c = '\n';
}
}
if ( c == NONE) { // if data ends
break;
}
data_c[received] = (char) c;
++received;
if (c == '\n') {
break;
}
}
_lock_release(&(s_vfstusb.read_lock));
if (received > 0) {
return received;
}
errno = EWOULDBLOCK;
return -1;
}
static int tusb_fstat(int fd, struct stat *st)
{
FD_CHECK(fd, -1);
memset(st, 0, sizeof(*st));
st->st_mode = S_IFCHR;
return 0;
}
static int tusb_fcntl(int fd, int cmd, int arg)
{
FD_CHECK(fd, -1);
int result = 0;
switch (cmd) {
case F_GETFL:
result = s_vfstusb.flags;
break;
case F_SETFL:
s_vfstusb.flags = arg;
break;
default:
result = -1;
errno = ENOSYS;
break;
}
return result;
}
esp_err_t esp_vfs_tusb_cdc_unregister(char const *path)
{
ESP_LOGD(TAG, "Unregistering TinyUSB driver");
int res;
if (path == NULL) { // NULL means using the default path for unregistering: VFS_TUSB_PATH_DEFAULT
res = strcmp(s_vfstusb.vfs_path, VFS_TUSB_PATH_DEFAULT);
} else {
res = strcmp(s_vfstusb.vfs_path, path);
}
if (res) {
res = ESP_ERR_INVALID_ARG;
ESP_LOGE(TAG, "There is no TinyUSB driver registerred to the path '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
return res;
}
res = esp_vfs_unregister(s_vfstusb.vfs_path);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't unregister TinyUSB driver from '%s' (err: 0x%x)", s_vfstusb.vfs_path, res);
} else {
ESP_LOGD(TAG, "Unregistered TinyUSB driver");
vfstusb_deinit();
}
return res;
}
esp_err_t esp_vfs_tusb_cdc_register(int cdc_intf, char const *path)
{
ESP_LOGD(TAG, "Registering TinyUSB CDC driver");
int res;
if (!tusb_cdc_acm_initialized(cdc_intf)) {
ESP_LOGE(TAG, "TinyUSB CDC#%d is not initialized", cdc_intf);
return ESP_ERR_INVALID_STATE;
}
res = vfstusb_init(cdc_intf, path);
if (res != ESP_OK) {
return res;
}
esp_vfs_t vfs = {
.flags = ESP_VFS_FLAG_DEFAULT,
.close = &tusb_close,
.fcntl = &tusb_fcntl,
.fstat = &tusb_fstat,
.open = &tusb_open,
.read = &tusb_read,
.write = &tusb_write,
};
res = esp_vfs_register(s_vfstusb.vfs_path, &vfs, NULL);
if (res != ESP_OK) {
ESP_LOGE(TAG, "Can't register TinyUSB driver (err: %x)", res);
} else {
ESP_LOGD(TAG, "TinyUSB CDC registered (%s)", s_vfstusb.vfs_path);
}
return res;
}

View file

@ -0,0 +1,119 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 XMOS LIMITED
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_DFU_H_
#define _TUSB_DFU_H_
#include "common/tusb_common.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Common Definitions
//--------------------------------------------------------------------+
// DFU Protocol
typedef enum
{
DFU_PROTOCOL_RT = 0x01,
DFU_PROTOCOL_DFU = 0x02,
} dfu_protocol_type_t;
// DFU Descriptor Type
typedef enum
{
DFU_DESC_FUNCTIONAL = 0x21,
} dfu_descriptor_type_t;
// DFU Requests
typedef enum {
DFU_REQUEST_DETACH = 0,
DFU_REQUEST_DNLOAD = 1,
DFU_REQUEST_UPLOAD = 2,
DFU_REQUEST_GETSTATUS = 3,
DFU_REQUEST_CLRSTATUS = 4,
DFU_REQUEST_GETSTATE = 5,
DFU_REQUEST_ABORT = 6,
} dfu_requests_t;
// DFU States
typedef enum {
APP_IDLE = 0,
APP_DETACH = 1,
DFU_IDLE = 2,
DFU_DNLOAD_SYNC = 3,
DFU_DNBUSY = 4,
DFU_DNLOAD_IDLE = 5,
DFU_MANIFEST_SYNC = 6,
DFU_MANIFEST = 7,
DFU_MANIFEST_WAIT_RESET = 8,
DFU_UPLOAD_IDLE = 9,
DFU_ERROR = 10,
} dfu_state_t;
// DFU Status
typedef enum {
DFU_STATUS_OK = 0x00,
DFU_STATUS_ERR_TARGET = 0x01,
DFU_STATUS_ERR_FILE = 0x02,
DFU_STATUS_ERR_WRITE = 0x03,
DFU_STATUS_ERR_ERASE = 0x04,
DFU_STATUS_ERR_CHECK_ERASED = 0x05,
DFU_STATUS_ERR_PROG = 0x06,
DFU_STATUS_ERR_VERIFY = 0x07,
DFU_STATUS_ERR_ADDRESS = 0x08,
DFU_STATUS_ERR_NOTDONE = 0x09,
DFU_STATUS_ERR_FIRMWARE = 0x0A,
DFU_STATUS_ERR_VENDOR = 0x0B,
DFU_STATUS_ERR_USBR = 0x0C,
DFU_STATUS_ERR_POR = 0x0D,
DFU_STATUS_ERR_UNKNOWN = 0x0E,
DFU_STATUS_ERR_STALLEDPKT = 0x0F,
} dfu_status_t;
#define DFU_ATTR_CAN_DOWNLOAD (1u << 0)
#define DFU_ATTR_CAN_UPLOAD (1u << 1)
#define DFU_ATTR_MANIFESTATION_TOLERANT (1u << 2)
#define DFU_ATTR_WILL_DETACH (1u << 3)
// DFU Status Request Payload
typedef struct TU_ATTR_PACKED
{
uint8_t bStatus;
uint8_t bwPollTimeout[3];
uint8_t bState;
uint8_t iString;
} dfu_status_response_t;
TU_VERIFY_STATIC( sizeof(dfu_status_response_t) == 6, "size is not correct");
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DFU_H_ */

View file

@ -0,0 +1,480 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 XMOS LIMITED
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU)
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#include "dfu_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
typedef struct
{
uint8_t attrs;
uint8_t alt;
dfu_state_t state;
dfu_status_t status;
bool flashing_in_progress;
uint16_t block;
uint16_t length;
CFG_TUSB_MEM_ALIGN uint8_t transfer_buf[CFG_TUD_DFU_XFER_BUFSIZE];
} dfu_state_ctx_t;
typedef struct TU_ATTR_PACKED
{
uint8_t bLength;
uint8_t bDescriptorType;
union {
struct TU_ATTR_PACKED {
uint8_t bitCanDnload : 1;
uint8_t bitCanUpload : 1;
uint8_t bitManifestationTolerant : 1;
uint8_t bitWillDetach : 1;
uint8_t reserved : 4;
} bmAttributes;
uint8_t bAttributes;
};
uint16_t wDetachTimeOut;
uint16_t wTransferSize;
uint16_t bcdDFUVersion;
} tusb_desc_dfu_functional_t;
// Only a single dfu state is allowed
CFG_TUSB_MEM_SECTION static dfu_state_ctx_t _dfu_ctx;
static void reset_state(void)
{
_dfu_ctx.state = DFU_IDLE;
_dfu_ctx.status = DFU_STATUS_OK;
_dfu_ctx.flashing_in_progress = false;
}
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout);
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
//--------------------------------------------------------------------+
// Debug
//--------------------------------------------------------------------+
#if CFG_TUSB_DEBUG >= 2
static tu_lookup_entry_t const _dfu_request_lookup[] =
{
{ .key = DFU_REQUEST_DETACH , .data = "DETACH" },
{ .key = DFU_REQUEST_DNLOAD , .data = "DNLOAD" },
{ .key = DFU_REQUEST_UPLOAD , .data = "UPLOAD" },
{ .key = DFU_REQUEST_GETSTATUS , .data = "GETSTATUS" },
{ .key = DFU_REQUEST_CLRSTATUS , .data = "CLRSTATUS" },
{ .key = DFU_REQUEST_GETSTATE , .data = "GETSTATE" },
{ .key = DFU_REQUEST_ABORT , .data = "ABORT" },
};
static tu_lookup_table_t const _dfu_request_table =
{
.count = TU_ARRAY_SIZE(_dfu_request_lookup),
.items = _dfu_request_lookup
};
static tu_lookup_entry_t const _dfu_state_lookup[] =
{
{ .key = APP_IDLE , .data = "APP_IDLE" },
{ .key = APP_DETACH , .data = "APP_DETACH" },
{ .key = DFU_IDLE , .data = "IDLE" },
{ .key = DFU_DNLOAD_SYNC , .data = "DNLOAD_SYNC" },
{ .key = DFU_DNBUSY , .data = "DNBUSY" },
{ .key = DFU_DNLOAD_IDLE , .data = "DNLOAD_IDLE" },
{ .key = DFU_MANIFEST_SYNC , .data = "MANIFEST_SYNC" },
{ .key = DFU_MANIFEST , .data = "MANIFEST" },
{ .key = DFU_MANIFEST_WAIT_RESET , .data = "MANIFEST_WAIT_RESET" },
{ .key = DFU_UPLOAD_IDLE , .data = "UPLOAD_IDLE" },
{ .key = DFU_ERROR , .data = "ERROR" },
};
static tu_lookup_table_t const _dfu_state_table =
{
.count = TU_ARRAY_SIZE(_dfu_state_lookup),
.items = _dfu_state_lookup
};
static tu_lookup_entry_t const _dfu_status_lookup[] =
{
{ .key = DFU_STATUS_OK , .data = "OK" },
{ .key = DFU_STATUS_ERR_TARGET , .data = "errTARGET" },
{ .key = DFU_STATUS_ERR_FILE , .data = "errFILE" },
{ .key = DFU_STATUS_ERR_WRITE , .data = "errWRITE" },
{ .key = DFU_STATUS_ERR_ERASE , .data = "errERASE" },
{ .key = DFU_STATUS_ERR_CHECK_ERASED , .data = "errCHECK_ERASED" },
{ .key = DFU_STATUS_ERR_PROG , .data = "errPROG" },
{ .key = DFU_STATUS_ERR_VERIFY , .data = "errVERIFY" },
{ .key = DFU_STATUS_ERR_ADDRESS , .data = "errADDRESS" },
{ .key = DFU_STATUS_ERR_NOTDONE , .data = "errNOTDONE" },
{ .key = DFU_STATUS_ERR_FIRMWARE , .data = "errFIRMWARE" },
{ .key = DFU_STATUS_ERR_VENDOR , .data = "errVENDOR" },
{ .key = DFU_STATUS_ERR_USBR , .data = "errUSBR" },
{ .key = DFU_STATUS_ERR_POR , .data = "errPOR" },
{ .key = DFU_STATUS_ERR_UNKNOWN , .data = "errUNKNOWN" },
{ .key = DFU_STATUS_ERR_STALLEDPKT , .data = "errSTALLEDPKT" },
};
static tu_lookup_table_t const _dfu_status_table =
{
.count = TU_ARRAY_SIZE(_dfu_status_lookup),
.items = _dfu_status_lookup
};
#endif
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void dfu_moded_reset(uint8_t rhport)
{
(void) rhport;
_dfu_ctx.attrs = 0;
_dfu_ctx.alt = 0;
reset_state();
}
void dfu_moded_init(void)
{
dfu_moded_reset(0);
}
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void) rhport;
//------------- Interface (with Alt) descriptor -------------//
uint8_t const itf_num = itf_desc->bInterfaceNumber;
uint8_t alt_count = 0;
uint16_t drv_len = 0;
while(itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS && itf_desc->bInterfaceProtocol == DFU_PROTOCOL_DFU)
{
TU_ASSERT(max_len > drv_len, 0);
// Alternate must have the same interface number
TU_ASSERT(itf_desc->bInterfaceNumber == itf_num, 0);
// Alt should increase by one every time
TU_ASSERT(itf_desc->bAlternateSetting == alt_count, 0);
alt_count++;
drv_len += tu_desc_len(itf_desc);
itf_desc = (tusb_desc_interface_t const *) tu_desc_next(itf_desc);
}
//------------- DFU Functional descriptor -------------//
tusb_desc_dfu_functional_t const *func_desc = (tusb_desc_dfu_functional_t const *) itf_desc;
TU_ASSERT(tu_desc_type(func_desc) == TUSB_DESC_FUNCTIONAL, 0);
drv_len += sizeof(tusb_desc_dfu_functional_t);
_dfu_ctx.attrs = func_desc->bAttributes;
// CFG_TUD_DFU_XFER_BUFSIZE has to be set to the buffer size used in TUD_DFU_DESCRIPTOR
uint16_t const transfer_size = tu_le16toh( tu_unaligned_read16((uint8_t const*) func_desc + offsetof(tusb_desc_dfu_functional_t, wTransferSize)) );
TU_ASSERT(transfer_size <= CFG_TUD_DFU_XFER_BUFSIZE, drv_len);
return drv_len;
}
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
TU_LOG2(" DFU State : %s, Status: %s\r\n", tu_lookup_find(&_dfu_state_table, _dfu_ctx.state), tu_lookup_find(&_dfu_status_table, _dfu_ctx.status));
if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD )
{
// Standard request include GET/SET_INTERFACE
switch ( request->bRequest )
{
case TUSB_REQ_SET_INTERFACE:
if ( stage == CONTROL_STAGE_SETUP )
{
// Switch Alt interface and reset state machine
_dfu_ctx.alt = (uint8_t) request->wValue;
reset_state();
return tud_control_status(rhport, request);
}
break;
case TUSB_REQ_GET_INTERFACE:
if(stage == CONTROL_STAGE_SETUP)
{
return tud_control_xfer(rhport, request, &_dfu_ctx.alt, 1);
}
break;
// unsupported request
default: return false;
}
}
else if ( request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS )
{
TU_LOG2(" DFU Request: %s\r\n", tu_lookup_find(&_dfu_request_table, request->bRequest));
// Class request
switch ( request->bRequest )
{
case DFU_REQUEST_DETACH:
if ( stage == CONTROL_STAGE_SETUP )
{
tud_control_status(rhport, request);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( tud_dfu_detach_cb ) tud_dfu_detach_cb();
}
break;
case DFU_REQUEST_CLRSTATUS:
if ( stage == CONTROL_STAGE_SETUP )
{
reset_state();
tud_control_status(rhport, request);
}
break;
case DFU_REQUEST_GETSTATE:
if ( stage == CONTROL_STAGE_SETUP )
{
tud_control_xfer(rhport, request, &_dfu_ctx.state, 1);
}
break;
case DFU_REQUEST_ABORT:
if ( stage == CONTROL_STAGE_SETUP )
{
reset_state();
tud_control_status(rhport, request);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( tud_dfu_abort_cb ) tud_dfu_abort_cb(_dfu_ctx.alt);
}
break;
case DFU_REQUEST_UPLOAD:
if ( stage == CONTROL_STAGE_SETUP )
{
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_UPLOAD);
TU_VERIFY(tud_dfu_upload_cb);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
uint16_t const xfer_len = tud_dfu_upload_cb(_dfu_ctx.alt, request->wValue, _dfu_ctx.transfer_buf, request->wLength);
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, xfer_len);
}
break;
case DFU_REQUEST_DNLOAD:
if ( stage == CONTROL_STAGE_SETUP )
{
TU_VERIFY(_dfu_ctx.attrs & DFU_ATTR_CAN_DOWNLOAD);
TU_VERIFY(_dfu_ctx.state == DFU_IDLE || _dfu_ctx.state == DFU_DNLOAD_IDLE);
TU_VERIFY(request->wLength <= CFG_TUD_DFU_XFER_BUFSIZE);
// set to true for both download and manifest
_dfu_ctx.flashing_in_progress = true;
// save block and length for flashing
_dfu_ctx.block = request->wValue;
_dfu_ctx.length = request->wLength;
if ( request->wLength )
{
// Download with payload -> transition to DOWNLOAD SYNC
_dfu_ctx.state = DFU_DNLOAD_SYNC;
return tud_control_xfer(rhport, request, _dfu_ctx.transfer_buf, request->wLength);
}
else
{
// Download is complete -> transition to MANIFEST SYNC
_dfu_ctx.state = DFU_MANIFEST_SYNC;
return tud_control_status(rhport, request);
}
}
break;
case DFU_REQUEST_GETSTATUS:
switch ( _dfu_ctx.state )
{
case DFU_DNLOAD_SYNC:
return process_download_get_status(rhport, stage, request);
break;
case DFU_MANIFEST_SYNC:
return process_manifest_get_status(rhport, stage, request);
break;
default:
if ( stage == CONTROL_STAGE_SETUP ) return reply_getstatus(rhport, request, _dfu_ctx.state, _dfu_ctx.status, 0);
break;
}
break;
default: return false; // stall unsupported request
}
}else
{
return false; // unsupported request
}
return true;
}
void tud_dfu_finish_flashing(uint8_t status)
{
_dfu_ctx.flashing_in_progress = false;
if ( status == DFU_STATUS_OK )
{
if (_dfu_ctx.state == DFU_DNBUSY)
{
_dfu_ctx.state = DFU_DNLOAD_SYNC;
}
else if (_dfu_ctx.state == DFU_MANIFEST)
{
_dfu_ctx.state = (_dfu_ctx.attrs & DFU_ATTR_MANIFESTATION_TOLERANT)
? DFU_MANIFEST_SYNC : DFU_MANIFEST_WAIT_RESET;
}
}
else
{
// failed while flashing, move to dfuError
_dfu_ctx.state = DFU_ERROR;
_dfu_ctx.status = (dfu_status_t)status;
}
}
static bool process_download_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if ( stage == CONTROL_STAGE_SETUP )
{
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
if ( _dfu_ctx.flashing_in_progress )
{
next_state = DFU_DNBUSY;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, (uint8_t) next_state);
}
else
{
next_state = DFU_DNLOAD_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( _dfu_ctx.flashing_in_progress )
{
_dfu_ctx.state = DFU_DNBUSY;
tud_dfu_download_cb(_dfu_ctx.alt, _dfu_ctx.block, _dfu_ctx.transfer_buf, _dfu_ctx.length);
}else
{
_dfu_ctx.state = DFU_DNLOAD_IDLE;
}
}
return true;
}
static bool process_manifest_get_status(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if ( stage == CONTROL_STAGE_SETUP )
{
// only transition to next state on CONTROL_STAGE_ACK
dfu_state_t next_state;
uint32_t timeout;
if ( _dfu_ctx.flashing_in_progress )
{
next_state = DFU_MANIFEST;
timeout = tud_dfu_get_timeout_cb(_dfu_ctx.alt, next_state);
}
else
{
next_state = DFU_IDLE;
timeout = 0;
}
return reply_getstatus(rhport, request, next_state, _dfu_ctx.status, timeout);
}
else if ( stage == CONTROL_STAGE_ACK )
{
if ( _dfu_ctx.flashing_in_progress )
{
_dfu_ctx.state = DFU_MANIFEST;
tud_dfu_manifest_cb(_dfu_ctx.alt);
}
else
{
_dfu_ctx.state = DFU_IDLE;
}
}
return true;
}
static bool reply_getstatus(uint8_t rhport, tusb_control_request_t const * request, dfu_state_t state, dfu_status_t status, uint32_t timeout)
{
dfu_status_response_t resp;
resp.bStatus = (uint8_t) status;
resp.bwPollTimeout[0] = TU_U32_BYTE0(timeout);
resp.bwPollTimeout[1] = TU_U32_BYTE1(timeout);
resp.bwPollTimeout[2] = TU_U32_BYTE2(timeout);
resp.bState = (uint8_t) state;
resp.iString = 0;
return tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
#endif

View file

@ -0,0 +1,124 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2021 XMOS LIMITED
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_DFU_DEVICE_H_
#define _TUSB_DFU_DEVICE_H_
#include "dfu.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Class Driver Default Configure & Validation
//--------------------------------------------------------------------+
#if !defined(CFG_TUD_DFU_XFER_BUFSIZE)
#define CFG_TUD_DFU_XFER_BUFSIZE 512
#endif
#define TU_U32_BYTE3(_u32) ((uint8_t) ((((uint32_t) _u32) >> 24) & 0x000000ff)) // MSB
#define TU_U32_BYTE2(_u32) ((uint8_t) ((((uint32_t) _u32) >> 16) & 0x000000ff))
#define TU_U32_BYTE1(_u32) ((uint8_t) ((((uint32_t) _u32) >> 8) & 0x000000ff))
#define TU_U32_BYTE0(_u32) ((uint8_t) (((uint32_t) _u32) & 0x000000ff)) // LSB
// Length of template descriptor: 9 bytes + number of alternatives * 9
#define TUD_DFU_DESC_LEN(_alt_count) (9 + (_alt_count) * 9)
// Interface number, Alternate count, starting string index, attributes, detach timeout, transfer size
// Note: Alternate count must be numberic or macro, string index is increased by one for each Alt interface
#define TUD_DFU_DESCRIPTOR(_itfnum, _alt_count, _stridx, _attr, _timeout, _xfer_size) \
TU_XSTRCAT(_TUD_DFU_ALT_,_alt_count)(_itfnum, 0, _stridx), \
/* Function */ \
9, DFU_DESC_FUNCTIONAL, _attr, U16_TO_U8S_LE(_timeout), U16_TO_U8S_LE(_xfer_size), U16_TO_U8S_LE(0x0101)
#define _TUD_DFU_ALT(_itfnum, _alt, _stridx) \
/* Interface */ \
9, TUSB_DESC_INTERFACE, _itfnum, _alt, 0, TUD_DFU_APP_CLASS, TUD_DFU_APP_SUBCLASS, DFU_PROTOCOL_DFU, _stridx
#define _TUD_DFU_ALT_1(_itfnum, _alt_count, _stridx) \
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx)
#define _TUD_DFU_ALT_2(_itfnum, _alt_count, _stridx) \
_TUD_DFU_ALT(_itfnum, _alt_count, _stridx), \
_TUD_DFU_ALT_1(_itfnum, _alt_count+1, _stridx+1)
//--------------------------------------------------------------------+
// Application API
//--------------------------------------------------------------------+
// Must be called when the application is done with flashing started by
// tud_dfu_download_cb() and tud_dfu_manifest_cb().
// status is DFU_STATUS_OK if successful, any other error status will cause state to enter dfuError
void tud_dfu_finish_flashing(uint8_t status);
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
// Note: alt is used as the partition number, in order to support multiple partitions like FLASH, EEPROM, etc.
// Invoked right before tud_dfu_download_cb() (state=DFU_DNBUSY) or tud_dfu_manifest_cb() (state=DFU_MANIFEST)
// Application return timeout in milliseconds (bwPollTimeout) for the next download/manifest operation.
// During this period, USB host won't try to communicate with us.
uint32_t tud_dfu_get_timeout_cb(uint8_t alt, uint8_t state);
// Invoked when received DFU_DNLOAD (wLength>0) following by DFU_GETSTATUS (state=DFU_DNBUSY) requests
// This callback could be returned before flashing op is complete (async).
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_download_cb (uint8_t alt, uint16_t block_num, uint8_t const *data, uint16_t length);
// Invoked when download process is complete, received DFU_DNLOAD (wLength=0) following by DFU_GETSTATUS (state=Manifest)
// Application can do checksum, or actual flashing if buffered entire image previously.
// Once finished flashing, application must call tud_dfu_finish_flashing()
void tud_dfu_manifest_cb(uint8_t alt);
// Invoked when received DFU_UPLOAD request
// Application must populate data with up to length bytes and
// Return the number of written bytes
TU_ATTR_WEAK uint16_t tud_dfu_upload_cb(uint8_t alt, uint16_t block_num, uint8_t* data, uint16_t length);
// Invoked when a DFU_DETACH request is received
TU_ATTR_WEAK void tud_dfu_detach_cb(void);
// Invoked when the Host has terminated a download or upload transfer
TU_ATTR_WEAK void tud_dfu_abort_cb(uint8_t alt);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void dfu_moded_init(void);
void dfu_moded_reset(uint8_t rhport);
uint16_t dfu_moded_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool dfu_moded_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DFU_MODE_DEVICE_H_ */

View file

@ -0,0 +1,128 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_DFU_RUNTIME)
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#include "dfu_rt_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void dfu_rtd_init(void)
{
}
void dfu_rtd_reset(uint8_t rhport)
{
(void) rhport;
}
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
(void) rhport;
(void) max_len;
// Ensure this is DFU Runtime
TU_VERIFY((itf_desc->bInterfaceSubClass == TUD_DFU_APP_SUBCLASS) &&
(itf_desc->bInterfaceProtocol == DFU_PROTOCOL_RT), 0);
uint8_t const * p_desc = tu_desc_next( itf_desc );
uint16_t drv_len = sizeof(tusb_desc_interface_t);
if ( TUSB_DESC_FUNCTIONAL == tu_desc_type(p_desc) )
{
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
return drv_len;
}
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
// nothing to do with DATA or ACK stage
if ( stage != CONTROL_STAGE_SETUP ) return true;
TU_VERIFY(request->bmRequestType_bit.recipient == TUSB_REQ_RCPT_INTERFACE);
// dfu-util will try to claim the interface with SET_INTERFACE request before sending DFU request
if ( TUSB_REQ_TYPE_STANDARD == request->bmRequestType_bit.type &&
TUSB_REQ_SET_INTERFACE == request->bRequest )
{
tud_control_status(rhport, request);
return true;
}
// Handle class request only from here
TU_VERIFY(request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS);
switch (request->bRequest)
{
case DFU_REQUEST_DETACH:
{
TU_LOG2(" DFU RT Request: DETACH\r\n");
tud_control_status(rhport, request);
tud_dfu_runtime_reboot_to_dfu_cb();
}
break;
case DFU_REQUEST_GETSTATUS:
{
TU_LOG2(" DFU RT Request: GETSTATUS\r\n");
dfu_status_response_t resp;
// Status = OK, Poll timeout is ignored during RT, State = APP_IDLE, IString = 0
memset(&resp, 0x00, sizeof(dfu_status_response_t));
tud_control_xfer(rhport, request, &resp, sizeof(dfu_status_response_t));
}
break;
default:
{
TU_LOG2(" DFU RT Unexpected Request: %d\r\n", request->bRequest);
return false; // stall unsupported request
}
}
return true;
}
#endif

View file

@ -0,0 +1,54 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Sylvain Munaut <tnt@246tNt.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_DFU_RT_DEVICE_H_
#define _TUSB_DFU_RT_DEVICE_H_
#include "dfu.h"
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
// Invoked when a DFU_DETACH request is received and bitWillDetach is set
void tud_dfu_runtime_reboot_to_dfu_cb(void);
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void dfu_rtd_init(void);
void dfu_rtd_reset(uint8_t rhport);
uint16_t dfu_rtd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool dfu_rtd_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const * request);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_DFU_RT_DEVICE_H_ */

View file

@ -0,0 +1,481 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2020 Peter Lawrence
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if ( TUSB_OPT_DEVICE_ENABLED && CFG_TUD_NET )
#include "class/net/net_device.h"
#include "device/usbd_pvt.h"
#include "rndis_protocol.h"
void rndis_class_set_handler(uint8_t *data, int size); /* found in ./misc/networking/rndis_reports.c */
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num; // Index number of Management Interface, +1 for Data Interface
uint8_t itf_data_alt; // Alternate setting of Data Interface. 0 : inactive, 1 : active
uint8_t ep_notif;
uint8_t ep_in;
uint8_t ep_out;
bool ecm_mode;
// Endpoint descriptor use to open/close when receving SetInterface
// TODO since configuration descriptor may not be long-lived memory, we should
// keep a copy of endpoint attribute instead
uint8_t const * ecm_desc_epdata;
} netd_interface_t;
#define CFG_TUD_NET_PACKET_PREFIX_LEN sizeof(rndis_data_packet_t)
#define CFG_TUD_NET_PACKET_SUFFIX_LEN 0
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t received[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t transmitted[CFG_TUD_NET_PACKET_PREFIX_LEN + CFG_TUD_NET_MTU + CFG_TUD_NET_PACKET_PREFIX_LEN];
struct ecm_notify_struct
{
tusb_control_request_t header;
uint32_t downlink, uplink;
};
static const struct ecm_notify_struct ecm_notify_nc =
{
.header = {
.bmRequestType = 0xA1,
.bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
.wValue = 1 /* Connected */,
.wLength = 0,
},
};
static const struct ecm_notify_struct ecm_notify_csc =
{
.header = {
.bmRequestType = 0xA1,
.bRequest = 0x2A /* CONNECTION_SPEED_CHANGE aka ConnectionSpeedChange */,
.wLength = 8,
},
.downlink = 9728000,
.uplink = 9728000,
};
// TODO remove CFG_TUSB_MEM_SECTION, control internal buffer is already in this special section
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static union
{
uint8_t rndis_buf[120];
struct ecm_notify_struct ecm_buf;
} notify;
//--------------------------------------------------------------------+
// INTERNAL OBJECT & FUNCTION DECLARATION
//--------------------------------------------------------------------+
// TODO remove CFG_TUSB_MEM_SECTION
CFG_TUSB_MEM_SECTION static netd_interface_t _netd_itf;
static bool can_xmit;
void tud_network_recv_renew(void)
{
usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_out, received, sizeof(received));
}
static void do_in_xfer(uint8_t *buf, uint16_t len)
{
can_xmit = false;
if (tud_network_idle_status_change_cb) {
tud_network_idle_status_change_cb(can_xmit);
}
usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_in, buf, len);
}
void netd_report(uint8_t *buf, uint16_t len)
{
usbd_edpt_xfer(TUD_OPT_RHPORT, _netd_itf.ep_notif, buf, len);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void netd_init(void)
{
tu_memclr(&_netd_itf, sizeof(_netd_itf));
}
void netd_reset(uint8_t rhport)
{
(void) rhport;
netd_init();
}
uint16_t netd_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len)
{
bool const is_rndis = (TUD_RNDIS_ITF_CLASS == itf_desc->bInterfaceClass &&
TUD_RNDIS_ITF_SUBCLASS == itf_desc->bInterfaceSubClass &&
TUD_RNDIS_ITF_PROTOCOL == itf_desc->bInterfaceProtocol);
bool const is_ecm = (TUSB_CLASS_CDC == itf_desc->bInterfaceClass &&
CDC_COMM_SUBCLASS_ETHERNET_CONTROL_MODEL == itf_desc->bInterfaceSubClass &&
0x00 == itf_desc->bInterfaceProtocol);
TU_VERIFY(is_rndis || is_ecm, 0);
// confirm interface hasn't already been allocated
TU_ASSERT(0 == _netd_itf.ep_notif, 0);
// sanity check the descriptor
_netd_itf.ecm_mode = is_ecm;
//------------- Management Interface -------------//
_netd_itf.itf_num = itf_desc->bInterfaceNumber;
uint16_t drv_len = sizeof(tusb_desc_interface_t);
uint8_t const * p_desc = tu_desc_next( itf_desc );
// Communication Functional Descriptors
while ( TUSB_DESC_CS_INTERFACE == tu_desc_type(p_desc) && drv_len <= max_len )
{
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
// notification endpoint (if any)
if ( TUSB_DESC_ENDPOINT == tu_desc_type(p_desc) )
{
TU_ASSERT( usbd_edpt_open(rhport, (tusb_desc_endpoint_t const *) p_desc), 0 );
_netd_itf.ep_notif = ((tusb_desc_endpoint_t const *) p_desc)->bEndpointAddress;
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}
//------------- Data Interface -------------//
// - RNDIS Data followed immediately by a pair of endpoints
// - CDC-ECM data interface has 2 alternate settings
// - 0 : zero endpoints for inactive (default)
// - 1 : IN & OUT endpoints for active networking
TU_ASSERT(TUSB_DESC_INTERFACE == tu_desc_type(p_desc), 0);
do
{
tusb_desc_interface_t const * data_itf_desc = (tusb_desc_interface_t const *) p_desc;
TU_ASSERT(TUSB_CLASS_CDC_DATA == data_itf_desc->bInterfaceClass, 0);
drv_len += tu_desc_len(p_desc);
p_desc = tu_desc_next(p_desc);
}while( _netd_itf.ecm_mode && (TUSB_DESC_INTERFACE == tu_desc_type(p_desc)) && (drv_len <= max_len) );
// Pair of endpoints
TU_ASSERT(TUSB_DESC_ENDPOINT == tu_desc_type(p_desc), 0);
if ( _netd_itf.ecm_mode )
{
// ECM by default is in-active, save the endpoint attribute
// to open later when received setInterface
_netd_itf.ecm_desc_epdata = p_desc;
}else
{
// Open endpoint pair for RNDIS
TU_ASSERT( usbd_open_edpt_pair(rhport, p_desc, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in), 0 );
tud_network_init_cb();
// we are ready to transmit a packet
can_xmit = true;
if (tud_network_idle_status_change_cb) {
tud_network_idle_status_change_cb(can_xmit);
}
// prepare for incoming packets
tud_network_recv_renew();
}
drv_len += 2*sizeof(tusb_desc_endpoint_t);
return drv_len;
}
void ecm_close(void)
{
printf("Ecm close\n");
tusb_control_request_t notify_data =
{
.bmRequestType = 0xA1,
.bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
.wValue = 0 /* Disconnected */,
.wLength = 0,
};
notify_data.wIndex = _netd_itf.itf_num;
netd_report((uint8_t *)&notify_data, sizeof(notify_data));
}
void ecm_open(void)
{
printf("Ecm OPEN\n");
tusb_control_request_t notify_data =
{
.bmRequestType = 0xA1,
.bRequest = 0 /* NETWORK_CONNECTION aka NetworkConnection */,
.wValue = 1 /* Connected */,
.wLength = 0,
};
notify_data.wIndex = _netd_itf.itf_num;
netd_report((uint8_t *)&notify_data, sizeof(notify_data));
}
static void ecm_report(bool nc)
{
notify.ecm_buf = (nc) ? ecm_notify_nc : ecm_notify_csc;
notify.ecm_buf.header.wIndex = _netd_itf.itf_num;
netd_report((uint8_t *)&notify.ecm_buf, (nc) ? sizeof(notify.ecm_buf.header) : sizeof(notify.ecm_buf));
}
// Invoked when a control transfer occurred on an interface of this class
// Driver response accordingly to the request and the transfer stage (setup/data/ack)
// return false to stall control endpoint (e.g unsupported request)
bool netd_control_xfer_cb (uint8_t rhport, uint8_t stage, tusb_control_request_t const * request)
{
if ( stage == CONTROL_STAGE_SETUP )
{
switch ( request->bmRequestType_bit.type )
{
case TUSB_REQ_TYPE_STANDARD:
switch ( request->bRequest )
{
case TUSB_REQ_GET_INTERFACE:
{
uint8_t const req_itfnum = (uint8_t) request->wIndex;
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum);
tud_control_xfer(rhport, request, &_netd_itf.itf_data_alt, 1);
}
break;
case TUSB_REQ_SET_INTERFACE:
{
uint8_t const req_itfnum = (uint8_t) request->wIndex;
uint8_t const req_alt = (uint8_t) request->wValue;
// Only valid for Data Interface with Alternate is either 0 or 1
TU_VERIFY(_netd_itf.itf_num+1 == req_itfnum && req_alt < 2);
// ACM-ECM only: qequest to enable/disable network activities
TU_VERIFY(_netd_itf.ecm_mode);
_netd_itf.itf_data_alt = req_alt;
if ( _netd_itf.itf_data_alt )
{
// TODO since we don't actually close endpoint
// hack here to not re-open it
if ( _netd_itf.ep_in == 0 && _netd_itf.ep_out == 0 )
{
TU_ASSERT(_netd_itf.ecm_desc_epdata);
TU_ASSERT( usbd_open_edpt_pair(rhport, _netd_itf.ecm_desc_epdata, 2, TUSB_XFER_BULK, &_netd_itf.ep_out, &_netd_itf.ep_in) );
// TODO should be merge with RNDIS's after endpoint opened
// Also should have opposite callback for application to disable network !!
tud_network_init_cb();
can_xmit = true; // we are ready to transmit a packet
if (tud_network_idle_status_change_cb) {
tud_network_idle_status_change_cb(can_xmit);
}
tud_network_recv_renew(); // prepare for incoming packets
}
}else
{
// TODO close the endpoint pair
// For now pretend that we did, this should have no harm since host won't try to
// communicate with the endpoints again
// _netd_itf.ep_in = _netd_itf.ep_out = 0
}
tud_control_status(rhport, request);
}
break;
// unsupported request
default: return false;
}
break;
case TUSB_REQ_TYPE_CLASS:
TU_VERIFY (_netd_itf.itf_num == request->wIndex);
if (_netd_itf.ecm_mode)
{
/* the only required CDC-ECM Management Element Request is SetEthernetPacketFilter */
if (0x43 /* SET_ETHERNET_PACKET_FILTER */ == request->bRequest)
{
tud_control_xfer(rhport, request, NULL, 0);
ecm_report(true);
}
}
else
{
if (request->bmRequestType_bit.direction == TUSB_DIR_IN)
{
rndis_generic_msg_t *rndis_msg = (rndis_generic_msg_t *) ((void*) notify.rndis_buf);
uint32_t msglen = tu_le32toh(rndis_msg->MessageLength);
TU_ASSERT(msglen <= sizeof(notify.rndis_buf));
tud_control_xfer(rhport, request, notify.rndis_buf, msglen);
}
else
{
tud_control_xfer(rhport, request, notify.rndis_buf, sizeof(notify.rndis_buf));
}
}
break;
// unsupported request
default: return false;
}
}
else if ( stage == CONTROL_STAGE_DATA )
{
// Handle RNDIS class control OUT only
if (request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
request->bmRequestType_bit.direction == TUSB_DIR_OUT &&
_netd_itf.itf_num == request->wIndex)
{
if ( !_netd_itf.ecm_mode )
{
rndis_class_set_handler(notify.rndis_buf, request->wLength);
}
}
}
return true;
}
static void handle_incoming_packet(uint32_t len)
{
uint8_t *pnt = received;
uint32_t size = 0;
if (_netd_itf.ecm_mode)
{
size = len;
}
else
{
rndis_data_packet_t *r = (rndis_data_packet_t *) ((void*) pnt);
if (len >= sizeof(rndis_data_packet_t))
if ( (r->MessageType == REMOTE_NDIS_PACKET_MSG) && (r->MessageLength <= len))
if ( (r->DataOffset + offsetof(rndis_data_packet_t, DataOffset) + r->DataLength) <= len)
{
pnt = &received[r->DataOffset + offsetof(rndis_data_packet_t, DataOffset)];
size = r->DataLength;
}
}
if (!tud_network_recv_cb(pnt, size))
{
/* if a buffer was never handled by user code, we must renew on the user's behalf */
tud_network_recv_renew();
}
}
bool netd_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) rhport;
(void) result;
/* new packet received */
if ( ep_addr == _netd_itf.ep_out )
{
handle_incoming_packet(xferred_bytes);
}
/* data transmission finished */
if ( ep_addr == _netd_itf.ep_in )
{
/* TinyUSB requires the class driver to implement ZLP (since ZLP usage is class-specific) */
if ( xferred_bytes && (0 == (xferred_bytes % CFG_TUD_NET_ENDPOINT_SIZE)) )
{
do_in_xfer(NULL, 0); /* a ZLP is needed */
}
else
{
/* we're finally finished */
can_xmit = true;
if (tud_network_idle_status_change_cb) {
tud_network_idle_status_change_cb(can_xmit);
}
}
}
if ( _netd_itf.ecm_mode && (ep_addr == _netd_itf.ep_notif) )
{
if (sizeof(notify.ecm_buf.header) == xferred_bytes) ecm_report(false);
}
return true;
}
bool tud_network_can_xmit(void)
{
return can_xmit;
}
void tud_network_xmit(void *ref, uint16_t arg)
{
uint8_t *data;
uint16_t len;
if (!can_xmit)
return;
len = (_netd_itf.ecm_mode) ? 0 : CFG_TUD_NET_PACKET_PREFIX_LEN;
data = transmitted + len;
len += tud_network_xmit_cb(data, ref, arg);
if (!_netd_itf.ecm_mode)
{
rndis_data_packet_t *hdr = (rndis_data_packet_t *) ((void*) transmitted);
memset(hdr, 0, sizeof(rndis_data_packet_t));
hdr->MessageType = REMOTE_NDIS_PACKET_MSG;
hdr->MessageLength = len;
hdr->DataOffset = sizeof(rndis_data_packet_t) - offsetof(rndis_data_packet_t, DataOffset);
hdr->DataLength = len - sizeof(rndis_data_packet_t);
}
do_in_xfer(transmitted, len);
}
#endif

View file

@ -0,0 +1,257 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#include "tusb_option.h"
#if (TUSB_OPT_DEVICE_ENABLED && CFG_TUD_VENDOR)
#include "device/usbd.h"
#include "device/usbd_pvt.h"
#include "vendor_device.h"
//--------------------------------------------------------------------+
// MACRO CONSTANT TYPEDEF
//--------------------------------------------------------------------+
typedef struct
{
uint8_t itf_num;
uint8_t ep_in;
uint8_t ep_out;
/*------------- From this point, data is not cleared by bus reset -------------*/
tu_fifo_t rx_ff;
tu_fifo_t tx_ff;
uint8_t rx_ff_buf[CFG_TUD_VENDOR_RX_BUFSIZE];
uint8_t tx_ff_buf[CFG_TUD_VENDOR_TX_BUFSIZE];
#if CFG_FIFO_MUTEX
osal_mutex_def_t rx_ff_mutex;
osal_mutex_def_t tx_ff_mutex;
#endif
// Endpoint Transfer buffer
CFG_TUSB_MEM_ALIGN uint8_t epout_buf[CFG_TUD_VENDOR_EPSIZE];
CFG_TUSB_MEM_ALIGN uint8_t epin_buf[CFG_TUD_VENDOR_EPSIZE];
} vendord_interface_t;
CFG_TUSB_MEM_SECTION static vendord_interface_t _vendord_itf[CFG_TUD_VENDOR];
#define ITF_MEM_RESET_SIZE offsetof(vendord_interface_t, rx_ff)
bool tud_vendor_n_mounted (uint8_t itf)
{
return _vendord_itf[itf].ep_in && _vendord_itf[itf].ep_out;
}
uint32_t tud_vendor_n_available (uint8_t itf)
{
return tu_fifo_count(&_vendord_itf[itf].rx_ff);
}
bool tud_vendor_n_peek(uint8_t itf, uint8_t* u8)
{
return tu_fifo_peek(&_vendord_itf[itf].rx_ff, u8);
}
//--------------------------------------------------------------------+
// Read API
//--------------------------------------------------------------------+
static void _prep_out_transaction (vendord_interface_t* p_itf)
{
// skip if previous transfer not complete
if ( usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_out) ) return;
// Prepare for incoming data but only allow what we can store in the ring buffer.
uint16_t max_read = tu_fifo_remaining(&p_itf->rx_ff);
if ( max_read >= CFG_TUD_VENDOR_EPSIZE )
{
usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_out, p_itf->epout_buf, CFG_TUD_VENDOR_EPSIZE);
}
}
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize)
{
vendord_interface_t* p_itf = &_vendord_itf[itf];
uint32_t num_read = tu_fifo_read_n(&p_itf->rx_ff, buffer, bufsize);
_prep_out_transaction(p_itf);
return num_read;
}
void tud_vendor_n_read_flush (uint8_t itf)
{
vendord_interface_t* p_itf = &_vendord_itf[itf];
tu_fifo_clear(&p_itf->rx_ff);
_prep_out_transaction(p_itf);
}
//--------------------------------------------------------------------+
// Write API
//--------------------------------------------------------------------+
static bool maybe_transmit(vendord_interface_t* p_itf)
{
// skip if previous transfer not complete
TU_VERIFY( !usbd_edpt_busy(TUD_OPT_RHPORT, p_itf->ep_in) );
uint16_t count = tu_fifo_read_n(&p_itf->tx_ff, p_itf->epin_buf, CFG_TUD_VENDOR_EPSIZE);
if (count > 0)
{
TU_ASSERT( usbd_edpt_xfer(TUD_OPT_RHPORT, p_itf->ep_in, p_itf->epin_buf, count) );
}
return true;
}
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize)
{
vendord_interface_t* p_itf = &_vendord_itf[itf];
uint16_t ret = tu_fifo_write_n(&p_itf->tx_ff, buffer, bufsize);
maybe_transmit(p_itf);
return ret;
}
uint32_t tud_vendor_n_write_available (uint8_t itf)
{
return tu_fifo_remaining(&_vendord_itf[itf].tx_ff);
}
//--------------------------------------------------------------------+
// USBD Driver API
//--------------------------------------------------------------------+
void vendord_init(void)
{
tu_memclr(_vendord_itf, sizeof(_vendord_itf));
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
{
vendord_interface_t* p_itf = &_vendord_itf[i];
// config fifo
tu_fifo_config(&p_itf->rx_ff, p_itf->rx_ff_buf, CFG_TUD_VENDOR_RX_BUFSIZE, 1, false);
tu_fifo_config(&p_itf->tx_ff, p_itf->tx_ff_buf, CFG_TUD_VENDOR_TX_BUFSIZE, 1, false);
#if CFG_FIFO_MUTEX
tu_fifo_config_mutex(&p_itf->rx_ff, NULL, osal_mutex_create(&p_itf->rx_ff_mutex));
tu_fifo_config_mutex(&p_itf->tx_ff, osal_mutex_create(&p_itf->tx_ff_mutex), NULL);
#endif
}
}
void vendord_reset(uint8_t rhport)
{
(void) rhport;
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
{
vendord_interface_t* p_itf = &_vendord_itf[i];
tu_memclr(p_itf, ITF_MEM_RESET_SIZE);
tu_fifo_clear(&p_itf->rx_ff);
tu_fifo_clear(&p_itf->tx_ff);
}
}
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * desc_itf, uint16_t max_len)
{
TU_VERIFY(TUSB_CLASS_VENDOR_SPECIFIC == desc_itf->bInterfaceClass, 0);
uint8_t const * p_desc = tu_desc_next(desc_itf);
uint8_t const * desc_end = p_desc + max_len;
// Find available interface
vendord_interface_t* p_vendor = NULL;
for(uint8_t i=0; i<CFG_TUD_VENDOR; i++)
{
if ( _vendord_itf[i].ep_in == 0 && _vendord_itf[i].ep_out == 0 )
{
p_vendor = &_vendord_itf[i];
break;
}
}
TU_VERIFY(p_vendor, 0);
p_vendor->itf_num = desc_itf->bInterfaceNumber;
if (desc_itf->bNumEndpoints)
{
// skip non-endpoint descriptors
while ( (TUSB_DESC_ENDPOINT != tu_desc_type(p_desc)) && (p_desc < desc_end) )
{
p_desc = tu_desc_next(p_desc);
}
// Open endpoint pair with usbd helper
TU_ASSERT(usbd_open_edpt_pair(rhport, p_desc, desc_itf->bNumEndpoints, TUSB_XFER_BULK, &p_vendor->ep_out, &p_vendor->ep_in), 0);
p_desc += desc_itf->bNumEndpoints*sizeof(tusb_desc_endpoint_t);
// Prepare for incoming data
if ( p_vendor->ep_out )
{
TU_ASSERT(usbd_edpt_xfer(rhport, p_vendor->ep_out, p_vendor->epout_buf, sizeof(p_vendor->epout_buf)), 0);
}
if ( p_vendor->ep_in ) maybe_transmit(p_vendor);
}
return (uintptr_t) p_desc - (uintptr_t) desc_itf;
}
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
{
(void) rhport;
(void) result;
uint8_t itf = 0;
vendord_interface_t* p_itf = _vendord_itf;
for ( ; ; itf++, p_itf++)
{
if (itf >= TU_ARRAY_SIZE(_vendord_itf)) return false;
if ( ( ep_addr == p_itf->ep_out ) || ( ep_addr == p_itf->ep_in ) ) break;
}
if ( ep_addr == p_itf->ep_out )
{
// Receive new data
tu_fifo_write_n(&p_itf->rx_ff, p_itf->epout_buf, xferred_bytes);
// Invoked callback if any
if (tud_vendor_rx_cb) tud_vendor_rx_cb(itf);
_prep_out_transaction(p_itf);
}
else if ( ep_addr == p_itf->ep_in )
{
// Send complete, try to send more if possible
maybe_transmit(p_itf);
}
return true;
}
#endif

View file

@ -0,0 +1,151 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2019 Ha Thach (tinyusb.org)
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
* This file is part of the TinyUSB stack.
*/
#ifndef _TUSB_VENDOR_DEVICE_H_
#define _TUSB_VENDOR_DEVICE_H_
#include "common/tusb_common.h"
#ifndef CFG_TUD_VENDOR_EPSIZE
#define CFG_TUD_VENDOR_EPSIZE 64
#endif
#ifdef __cplusplus
extern "C" {
#endif
//--------------------------------------------------------------------+
// Vendor Descriptor Templates
//--------------------------------------------------------------------+
#define TUD_VENDOR_DESC_LEN (9+7+7)
// Interface number, string index, EP Out & IN address, EP size
#define TUD_VENDOR_DESCRIPTOR(_itfnum, _stridx, _epout, _epin, _epsize) \
/* Interface */\
9, TUSB_DESC_INTERFACE, _itfnum, 0, 2, TUSB_CLASS_VENDOR_SPECIFIC, 0x00, 0x00, _stridx,\
/* Endpoint Out */\
7, TUSB_DESC_ENDPOINT, _epout, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0,\
/* Endpoint In */\
7, TUSB_DESC_ENDPOINT, _epin, TUSB_XFER_BULK, U16_TO_U8S_LE(_epsize), 0
//--------------------------------------------------------------------+
// Application API (Multiple Interfaces)
//--------------------------------------------------------------------+
bool tud_vendor_n_mounted (uint8_t itf);
uint32_t tud_vendor_n_available (uint8_t itf);
uint32_t tud_vendor_n_read (uint8_t itf, void* buffer, uint32_t bufsize);
bool tud_vendor_n_peek (uint8_t itf, uint8_t* ui8);
void tud_vendor_n_read_flush (uint8_t itf);
uint32_t tud_vendor_n_write (uint8_t itf, void const* buffer, uint32_t bufsize);
uint32_t tud_vendor_n_write_available (uint8_t itf);
static inline
uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str);
//--------------------------------------------------------------------+
// Application API (Single Port)
//--------------------------------------------------------------------+
static inline bool tud_vendor_mounted (void);
static inline uint32_t tud_vendor_available (void);
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize);
static inline bool tud_vendor_peek (uint8_t* ui8);
static inline void tud_vendor_read_flush (void);
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize);
static inline uint32_t tud_vendor_write_str (char const* str);
static inline uint32_t tud_vendor_write_available (void);
//--------------------------------------------------------------------+
// Application Callback API (weak is optional)
//--------------------------------------------------------------------+
// Invoked when received new data
TU_ATTR_WEAK void tud_vendor_rx_cb(uint8_t itf);
//--------------------------------------------------------------------+
// Inline Functions
//--------------------------------------------------------------------+
static inline uint32_t tud_vendor_n_write_str (uint8_t itf, char const* str)
{
return tud_vendor_n_write(itf, str, strlen(str));
}
static inline bool tud_vendor_mounted (void)
{
return tud_vendor_n_mounted(0);
}
static inline uint32_t tud_vendor_available (void)
{
return tud_vendor_n_available(0);
}
static inline uint32_t tud_vendor_read (void* buffer, uint32_t bufsize)
{
return tud_vendor_n_read(0, buffer, bufsize);
}
static inline bool tud_vendor_peek (uint8_t* ui8)
{
return tud_vendor_n_peek(0, ui8);
}
static inline void tud_vendor_read_flush(void)
{
tud_vendor_n_read_flush(0);
}
static inline uint32_t tud_vendor_write (void const* buffer, uint32_t bufsize)
{
return tud_vendor_n_write(0, buffer, bufsize);
}
static inline uint32_t tud_vendor_write_str (char const* str)
{
return tud_vendor_n_write_str(0, str);
}
static inline uint32_t tud_vendor_write_available (void)
{
return tud_vendor_n_write_available(0);
}
//--------------------------------------------------------------------+
// Internal Class Driver API
//--------------------------------------------------------------------+
void vendord_init(void);
void vendord_reset(uint8_t rhport);
uint16_t vendord_open(uint8_t rhport, tusb_desc_interface_t const * itf_desc, uint16_t max_len);
bool vendord_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t event, uint32_t xferred_bytes);
#ifdef __cplusplus
}
#endif
#endif /* _TUSB_VENDOR_DEVICE_H_ */

View file

@ -0,0 +1,266 @@
/* This file has been prepared for Doxygen automatic documentation generation.*/
/*! \file ndis.h ***************************************************************
*
* \brief
* This file contains the possible external configuration of the USB.
*
* \addtogroup usbstick
*
*
******************************************************************************/
/**
\ingroup usbstick
\defgroup RNDIS RNDIS Support
@{
*/
/*
* ndis.h
*
* Modified by Colin O'Flynn <coflynn@newae.com>
* ntddndis.h modified by Benedikt Spranger <b.spranger@pengutronix.de>
*
* Thanks to the cygwin development team,
* espacially to Casper S. Hornstrup <chorns@users.sourceforge.net>
*
* THIS SOFTWARE IS NOT COPYRIGHTED
*
* This source code is offered for use in the public domain. You may
* use, modify or distribute it freely.
*
* This code is distributed in the hope that it will be useful but
* WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
* DISCLAIMED. This includes but is not limited to warranties of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
*/
#ifndef _LINUX_NDIS_H
#define _LINUX_NDIS_H
#define NDIS_STATUS_MULTICAST_FULL 0xC0010009
#define NDIS_STATUS_MULTICAST_EXISTS 0xC001000A
#define NDIS_STATUS_MULTICAST_NOT_FOUND 0xC001000B
/* from drivers/net/sk98lin/h/skgepnmi.h */
#define OID_PNP_CAPABILITIES 0xFD010100
#define OID_PNP_SET_POWER 0xFD010101
#define OID_PNP_QUERY_POWER 0xFD010102
#define OID_PNP_ADD_WAKE_UP_PATTERN 0xFD010103
#define OID_PNP_REMOVE_WAKE_UP_PATTERN 0xFD010104
#define OID_PNP_ENABLE_WAKE_UP 0xFD010106
enum NDIS_DEVICE_POWER_STATE {
NdisDeviceStateUnspecified = 0,
NdisDeviceStateD0,
NdisDeviceStateD1,
NdisDeviceStateD2,
NdisDeviceStateD3,
NdisDeviceStateMaximum
};
struct NDIS_PM_WAKE_UP_CAPABILITIES {
enum NDIS_DEVICE_POWER_STATE MinMagicPacketWakeUp;
enum NDIS_DEVICE_POWER_STATE MinPatternWakeUp;
enum NDIS_DEVICE_POWER_STATE MinLinkChangeWakeUp;
};
/* NDIS_PNP_CAPABILITIES.Flags constants */
#define NDIS_DEVICE_WAKE_UP_ENABLE 0x00000001
#define NDIS_DEVICE_WAKE_ON_PATTERN_MATCH_ENABLE 0x00000002
#define NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE 0x00000004
/*
struct NDIS_PNP_CAPABILITIES {
__le32 Flags;
struct NDIS_PM_WAKE_UP_CAPABILITIES WakeUpCapabilities;
};
struct NDIS_PM_PACKET_PATTERN {
__le32 Priority;
__le32 Reserved;
__le32 MaskSize;
__le32 PatternOffset;
__le32 PatternSize;
__le32 PatternFlags;
};
*/
/* Required Object IDs (OIDs) */
#define OID_GEN_SUPPORTED_LIST 0x00010101
#define OID_GEN_HARDWARE_STATUS 0x00010102
#define OID_GEN_MEDIA_SUPPORTED 0x00010103
#define OID_GEN_MEDIA_IN_USE 0x00010104
#define OID_GEN_MAXIMUM_LOOKAHEAD 0x00010105
#define OID_GEN_MAXIMUM_FRAME_SIZE 0x00010106
#define OID_GEN_LINK_SPEED 0x00010107
#define OID_GEN_TRANSMIT_BUFFER_SPACE 0x00010108
#define OID_GEN_RECEIVE_BUFFER_SPACE 0x00010109
#define OID_GEN_TRANSMIT_BLOCK_SIZE 0x0001010A
#define OID_GEN_RECEIVE_BLOCK_SIZE 0x0001010B
#define OID_GEN_VENDOR_ID 0x0001010C
#define OID_GEN_VENDOR_DESCRIPTION 0x0001010D
#define OID_GEN_CURRENT_PACKET_FILTER 0x0001010E
#define OID_GEN_CURRENT_LOOKAHEAD 0x0001010F
#define OID_GEN_DRIVER_VERSION 0x00010110
#define OID_GEN_MAXIMUM_TOTAL_SIZE 0x00010111
#define OID_GEN_PROTOCOL_OPTIONS 0x00010112
#define OID_GEN_MAC_OPTIONS 0x00010113
#define OID_GEN_MEDIA_CONNECT_STATUS 0x00010114
#define OID_GEN_MAXIMUM_SEND_PACKETS 0x00010115
#define OID_GEN_VENDOR_DRIVER_VERSION 0x00010116
#define OID_GEN_SUPPORTED_GUIDS 0x00010117
#define OID_GEN_NETWORK_LAYER_ADDRESSES 0x00010118
#define OID_GEN_TRANSPORT_HEADER_OFFSET 0x00010119
#define OID_GEN_MACHINE_NAME 0x0001021A
#define OID_GEN_RNDIS_CONFIG_PARAMETER 0x0001021B
#define OID_GEN_VLAN_ID 0x0001021C
/* Optional OIDs */
#define OID_GEN_MEDIA_CAPABILITIES 0x00010201
#define OID_GEN_PHYSICAL_MEDIUM 0x00010202
/* Required statistics OIDs */
#define OID_GEN_XMIT_OK 0x00020101
#define OID_GEN_RCV_OK 0x00020102
#define OID_GEN_XMIT_ERROR 0x00020103
#define OID_GEN_RCV_ERROR 0x00020104
#define OID_GEN_RCV_NO_BUFFER 0x00020105
/* Optional statistics OIDs */
#define OID_GEN_DIRECTED_BYTES_XMIT 0x00020201
#define OID_GEN_DIRECTED_FRAMES_XMIT 0x00020202
#define OID_GEN_MULTICAST_BYTES_XMIT 0x00020203
#define OID_GEN_MULTICAST_FRAMES_XMIT 0x00020204
#define OID_GEN_BROADCAST_BYTES_XMIT 0x00020205
#define OID_GEN_BROADCAST_FRAMES_XMIT 0x00020206
#define OID_GEN_DIRECTED_BYTES_RCV 0x00020207
#define OID_GEN_DIRECTED_FRAMES_RCV 0x00020208
#define OID_GEN_MULTICAST_BYTES_RCV 0x00020209
#define OID_GEN_MULTICAST_FRAMES_RCV 0x0002020A
#define OID_GEN_BROADCAST_BYTES_RCV 0x0002020B
#define OID_GEN_BROADCAST_FRAMES_RCV 0x0002020C
#define OID_GEN_RCV_CRC_ERROR 0x0002020D
#define OID_GEN_TRANSMIT_QUEUE_LENGTH 0x0002020E
#define OID_GEN_GET_TIME_CAPS 0x0002020F
#define OID_GEN_GET_NETCARD_TIME 0x00020210
#define OID_GEN_NETCARD_LOAD 0x00020211
#define OID_GEN_DEVICE_PROFILE 0x00020212
#define OID_GEN_INIT_TIME_MS 0x00020213
#define OID_GEN_RESET_COUNTS 0x00020214
#define OID_GEN_MEDIA_SENSE_COUNTS 0x00020215
#define OID_GEN_FRIENDLY_NAME 0x00020216
#define OID_GEN_MINIPORT_INFO 0x00020217
#define OID_GEN_RESET_VERIFY_PARAMETERS 0x00020218
/* IEEE 802.3 (Ethernet) OIDs */
#define NDIS_802_3_MAC_OPTION_PRIORITY 0x00000001
#define OID_802_3_PERMANENT_ADDRESS 0x01010101
#define OID_802_3_CURRENT_ADDRESS 0x01010102
#define OID_802_3_MULTICAST_LIST 0x01010103
#define OID_802_3_MAXIMUM_LIST_SIZE 0x01010104
#define OID_802_3_MAC_OPTIONS 0x01010105
#define OID_802_3_RCV_ERROR_ALIGNMENT 0x01020101
#define OID_802_3_XMIT_ONE_COLLISION 0x01020102
#define OID_802_3_XMIT_MORE_COLLISIONS 0x01020103
#define OID_802_3_XMIT_DEFERRED 0x01020201
#define OID_802_3_XMIT_MAX_COLLISIONS 0x01020202
#define OID_802_3_RCV_OVERRUN 0x01020203
#define OID_802_3_XMIT_UNDERRUN 0x01020204
#define OID_802_3_XMIT_HEARTBEAT_FAILURE 0x01020205
#define OID_802_3_XMIT_TIMES_CRS_LOST 0x01020206
#define OID_802_3_XMIT_LATE_COLLISIONS 0x01020207
/* Wireless LAN OIDs */
/* Mandatory */
#define OID_802_11_BSSID 0x0D010101 /* Q S */
#define OID_802_11_SSID 0x0D010102 /* Q S */
#define OID_802_11_NETWORK_TYPE_IN_USE 0x0D010204 /* Q S */
#define OID_802_11_RSSI 0x0D010206 /* Q I */
#define OID_802_11_BSSID_LIST 0x0D010217 /* Q */
#define OID_802_11_BSSID_LIST_SCAN 0x0D01011A /* S */
#define OID_802_11_INFRASTRUCTURE_MODE 0x0D010108 /* Q S */
#define OID_802_11_SUPPORTED_RATES 0x0D01020E /* Q */
#define OID_802_11_CONFIGURATION 0x0D010211 /* Q S */
#define OID_802_11_ADD_WEP 0x0D010113 /* S */
#define OID_802_11_WEP_STATUS 0x0D01011B /* Q S */
#define OID_802_11_REMOVE_WEP 0x0D010114 /* S */
#define OID_802_11_DISASSOCIATE 0x0D010115 /* S */
#define OID_802_11_AUTHENTICATION_MODE 0x0D010118 /* Q S */
#define OID_802_11_RELOAD_DEFAULTS 0x0D01011C /* S */
/* OID_GEN_MINIPORT_INFO constants */
#define NDIS_MINIPORT_BUS_MASTER 0x00000001
#define NDIS_MINIPORT_WDM_DRIVER 0x00000002
#define NDIS_MINIPORT_SG_LIST 0x00000004
#define NDIS_MINIPORT_SUPPORTS_MEDIA_QUERY 0x00000008
#define NDIS_MINIPORT_INDICATES_PACKETS 0x00000010
#define NDIS_MINIPORT_IGNORE_PACKET_QUEUE 0x00000020
#define NDIS_MINIPORT_IGNORE_REQUEST_QUEUE 0x00000040
#define NDIS_MINIPORT_IGNORE_TOKEN_RING_ERRORS 0x00000080
#define NDIS_MINIPORT_INTERMEDIATE_DRIVER 0x00000100
#define NDIS_MINIPORT_IS_NDIS_5 0x00000200
#define NDIS_MINIPORT_IS_CO 0x00000400
#define NDIS_MINIPORT_DESERIALIZE 0x00000800
#define NDIS_MINIPORT_REQUIRES_MEDIA_POLLING 0x00001000
#define NDIS_MINIPORT_SUPPORTS_MEDIA_SENSE 0x00002000
#define NDIS_MINIPORT_NETBOOT_CARD 0x00004000
#define NDIS_MINIPORT_PM_SUPPORTED 0x00008000
#define NDIS_MINIPORT_SUPPORTS_MAC_ADDRESS_OVERWRITE 0x00010000
#define NDIS_MINIPORT_USES_SAFE_BUFFER_APIS 0x00020000
#define NDIS_MINIPORT_HIDDEN 0x00040000
#define NDIS_MINIPORT_SWENUM 0x00080000
#define NDIS_MINIPORT_SURPRISE_REMOVE_OK 0x00100000
#define NDIS_MINIPORT_NO_HALT_ON_SUSPEND 0x00200000
#define NDIS_MINIPORT_HARDWARE_DEVICE 0x00400000
#define NDIS_MINIPORT_SUPPORTS_CANCEL_SEND_PACKETS 0x00800000
#define NDIS_MINIPORT_64BITS_DMA 0x01000000
#define NDIS_MEDIUM_802_3 0x00000000
#define NDIS_MEDIUM_802_5 0x00000001
#define NDIS_MEDIUM_FDDI 0x00000002
#define NDIS_MEDIUM_WAN 0x00000003
#define NDIS_MEDIUM_LOCAL_TALK 0x00000004
#define NDIS_MEDIUM_DIX 0x00000005
#define NDIS_MEDIUM_ARCENT_RAW 0x00000006
#define NDIS_MEDIUM_ARCENT_878_2 0x00000007
#define NDIS_MEDIUM_ATM 0x00000008
#define NDIS_MEDIUM_WIRELESS_LAN 0x00000009
#define NDIS_MEDIUM_IRDA 0x0000000A
#define NDIS_MEDIUM_BPC 0x0000000B
#define NDIS_MEDIUM_CO_WAN 0x0000000C
#define NDIS_MEDIUM_1394 0x0000000D
#define NDIS_PACKET_TYPE_DIRECTED 0x00000001
#define NDIS_PACKET_TYPE_MULTICAST 0x00000002
#define NDIS_PACKET_TYPE_ALL_MULTICAST 0x00000004
#define NDIS_PACKET_TYPE_BROADCAST 0x00000008
#define NDIS_PACKET_TYPE_SOURCE_ROUTING 0x00000010
#define NDIS_PACKET_TYPE_PROMISCUOUS 0x00000020
#define NDIS_PACKET_TYPE_SMT 0x00000040
#define NDIS_PACKET_TYPE_ALL_LOCAL 0x00000080
#define NDIS_PACKET_TYPE_GROUP 0x00000100
#define NDIS_PACKET_TYPE_ALL_FUNCTIONAL 0x00000200
#define NDIS_PACKET_TYPE_FUNCTIONAL 0x00000400
#define NDIS_PACKET_TYPE_MAC_FRAME 0x00000800
#define NDIS_MEDIA_STATE_CONNECTED 0x00000000
#define NDIS_MEDIA_STATE_DISCONNECTED 0x00000001
#define NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA 0x00000001
#define NDIS_MAC_OPTION_RECEIVE_SERIALIZED 0x00000002
#define NDIS_MAC_OPTION_TRANSFERS_NOT_PEND 0x00000004
#define NDIS_MAC_OPTION_NO_LOOPBACK 0x00000008
#define NDIS_MAC_OPTION_FULL_DUPLEX 0x00000010
#define NDIS_MAC_OPTION_EOTX_INDICATION 0x00000020
#define NDIS_MAC_OPTION_8021P_PRIORITY 0x00000040
#define NDIS_MAC_OPTION_RESERVED 0x80000000
#endif /* _LINUX_NDIS_H */
/** @} */

View file

@ -0,0 +1,313 @@
/**
* \file rndis_protocol.h
* RNDIS Defines
*
* \author
* Colin O'Flynn <coflynn@newae.com>
*
* \addtogroup usbstick
*/
/* Copyright (c) 2008 Colin O'Flynn
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in
the documentation and/or other materials provided with the
distribution.
* Neither the name of the copyright holders nor the names of
contributors may be used to endorse or promote products derived
from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef _RNDIS_H
#define _RNDIS_H
/**
\addtogroup RNDIS
@{
*/
#include <stdint.h>
#define RNDIS_MAJOR_VERSION 1
#define RNDIS_MINOR_VERSION 0
#define RNDIS_STATUS_SUCCESS 0X00000000
#define RNDIS_STATUS_FAILURE 0XC0000001
#define RNDIS_STATUS_INVALID_DATA 0XC0010015
#define RNDIS_STATUS_NOT_SUPPORTED 0XC00000BB
#define RNDIS_STATUS_MEDIA_CONNECT 0X4001000B
#define RNDIS_STATUS_MEDIA_DISCONNECT 0X4001000C
/* Message set for Connectionless (802.3) Devices */
#define REMOTE_NDIS_PACKET_MSG 0x00000001
#define REMOTE_NDIS_INITIALIZE_MSG 0X00000002
#define REMOTE_NDIS_HALT_MSG 0X00000003
#define REMOTE_NDIS_QUERY_MSG 0X00000004
#define REMOTE_NDIS_SET_MSG 0X00000005
#define REMOTE_NDIS_RESET_MSG 0X00000006
#define REMOTE_NDIS_INDICATE_STATUS_MSG 0X00000007
#define REMOTE_NDIS_KEEPALIVE_MSG 0X00000008
#define REMOTE_NDIS_INITIALIZE_CMPLT 0X80000002
#define REMOTE_NDIS_QUERY_CMPLT 0X80000004
#define REMOTE_NDIS_SET_CMPLT 0X80000005
#define REMOTE_NDIS_RESET_CMPLT 0X80000006
#define REMOTE_NDIS_KEEPALIVE_CMPLT 0X80000008
typedef uint32_t rndis_MessageType_t;
typedef uint32_t rndis_MessageLength_t;
typedef uint32_t rndis_RequestId_t;
typedef uint32_t rndis_MajorVersion_t;
typedef uint32_t rndis_MinorVersion_t;
typedef uint32_t rndis_MaxTransferSize_t;
typedef uint32_t rndis_Status_t;
/* Device Flags */
#define RNDIS_DF_CONNECTIONLESS 0x00000001
#define RNDIS_DF_CONNECTION_ORIENTED 0x00000002
typedef uint32_t rndis_DeviceFlags_t;
/* Mediums */
#define RNDIS_MEDIUM_802_3 0x00000000
typedef uint32_t rndis_Medium_t;
typedef uint32_t rndis_MaxPacketsPerTransfer_t;
typedef uint32_t rndis_PacketAlignmentFactor_t;
typedef uint32_t rndis_AfListOffset_t;
typedef uint32_t rndis_AfListSize_t;
/*** Remote NDIS Generic Message type ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
} rndis_generic_msg_t;
/*** Remote NDIS Initialize Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_MajorVersion_t MajorVersion;
rndis_MinorVersion_t MinorVersion;
rndis_MaxTransferSize_t MaxTransferSize;
} rndis_initialize_msg_t;
/* Response: */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
rndis_MajorVersion_t MajorVersion;
rndis_MinorVersion_t MinorVersion;
rndis_DeviceFlags_t DeviceFlags;
rndis_Medium_t Medium;
rndis_MaxPacketsPerTransfer_t MaxPacketsPerTransfer;
rndis_MaxTransferSize_t MaxTransferSize;
rndis_PacketAlignmentFactor_t PacketAlignmentFactor;
rndis_AfListOffset_t AfListOffset;
rndis_AfListSize_t AfListSize;
} rndis_initialize_cmplt_t;
/*** Remote NDIS Halt Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
} rndis_halt_msg_t;
typedef uint32_t rndis_Oid_t;
typedef uint32_t rndis_InformationBufferLength_t;
typedef uint32_t rndis_InformationBufferOffset_t;
typedef uint32_t rndis_DeviceVcHandle_t;
/*** Remote NDIS Query Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Oid_t Oid;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
rndis_DeviceVcHandle_t DeviceVcHandle;
} rndis_query_msg_t;
/* Response: */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
} rndis_query_cmplt_t;
/*** Remote NDIS Set Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Oid_t Oid;
rndis_InformationBufferLength_t InformationBufferLength;
rndis_InformationBufferOffset_t InformationBufferOffset;
rndis_DeviceVcHandle_t DeviceVcHandle;
} rndis_set_msg_t;
/* Response */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
}rndis_set_cmplt_t;
/* Information buffer layout for OID_GEN_RNDIS_CONFIG_PARAMETER */
typedef uint32_t rndis_ParameterNameOffset_t;
typedef uint32_t rndis_ParameterNameLength_t;
typedef uint32_t rndis_ParameterType_t;
typedef uint32_t rndis_ParameterValueOffset_t;
typedef uint32_t rndis_ParameterValueLength_t;
#define PARAMETER_TYPE_STRING 2
#define PARAMETER_TYPE_NUMERICAL 0
typedef struct{
rndis_ParameterNameOffset_t ParameterNameOffset;
rndis_ParameterNameLength_t ParameterNameLength;
rndis_ParameterType_t ParameterType;
rndis_ParameterValueOffset_t ParameterValueOffset;
rndis_ParameterValueLength_t ParameterValueLength;
}rndis_config_parameter_t;
typedef uint32_t rndis_Reserved_t;
/*** Remote NDIS Soft Reset Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Reserved_t Reserved;
} rndis_reset_msg_t;
typedef uint32_t rndis_AddressingReset_t;
/* Response: */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Status_t Status;
rndis_AddressingReset_t AddressingReset;
} rndis_reset_cmplt_t;
/*** Remote NDIS Indicate Status Message ***/
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_Status_t Status;
rndis_Status_t StatusBufferLength;
rndis_Status_t StatusBufferOffset;
} rndis_indicate_status_t;
typedef uint32_t rndis_DiagStatus_t;
typedef uint32_t rndis_ErrorOffset_t;
typedef struct {
rndis_DiagStatus_t DiagStatus;
rndis_ErrorOffset_t ErrorOffset;
}rndis_diagnostic_info_t;
/*** Remote NDIS Keepalive Message */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
}rndis_keepalive_msg_t;
/* Response: */
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_RequestId_t RequestId;
rndis_Status_t Status;
}rndis_keepalive_cmplt_t;
/*** Remote NDIS Data Packet ***/
typedef uint32_t rndis_DataOffset_t;
typedef uint32_t rndis_DataLength_t;
typedef uint32_t rndis_OOBDataOffset_t;
typedef uint32_t rndis_OOBDataLength_t;
typedef uint32_t rndis_NumOOBDataElements_t;
typedef uint32_t rndis_PerPacketInfoOffset_t;
typedef uint32_t rndis_PerPacketInfoLength_t;
typedef struct{
rndis_MessageType_t MessageType;
rndis_MessageLength_t MessageLength;
rndis_DataOffset_t DataOffset;
rndis_DataLength_t DataLength;
rndis_OOBDataOffset_t OOBDataOffset;
rndis_OOBDataLength_t OOBDataLength;
rndis_NumOOBDataElements_t NumOOBDataElements;
rndis_PerPacketInfoOffset_t PerPacketInfoOffset;
rndis_PerPacketInfoLength_t PerPacketInfoLength;
rndis_DeviceVcHandle_t DeviceVcHandle;
rndis_Reserved_t Reserved;
}rndis_data_packet_t;
typedef uint32_t rndis_ClassInformationOffset_t;
typedef uint32_t rndis_Size_t;
typedef uint32_t rndis_Type_t;
typedef struct{
rndis_Size_t Size;
rndis_Type_t Type;
rndis_ClassInformationOffset_t ClassInformationType;
}rndis_OOB_packet_t;
#include "ndis.h"
typedef enum rnids_state_e {
rndis_uninitialized,
rndis_initialized,
rndis_data_initialized
} rndis_state_t;
typedef struct {
uint32_t txok;
uint32_t rxok;
uint32_t txbad;
uint32_t rxbad;
} usb_eth_stat_t;
//void rndis_indicate_status(int status);
void rndis_disconnect(void);
void rndis_connect(void);
#endif /* _RNDIS_H */
/** @} */

View file

@ -0,0 +1,329 @@
/*
The original version of this code was lrndis/usbd_rndis_core.c from https://github.com/fetisov/lrndis
It has since been overhauled to suit this application
*/
/*
* The MIT License (MIT)
*
* Copyright (c) 2015 by Sergey Fetisov <fsenok@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdalign.h>
#include <string.h>
#include "class/net/net_device.h"
#include "rndis_protocol.h"
#include "netif/ethernet.h"
#include "esp_wifi.h"
#define RNDIS_LINK_SPEED 12000000 /* Link baudrate (12Mbit/s for USB-FS) */
#define RNDIS_VENDOR CONFIG_TINYUSB_RNDIS_VENDOR /* NIC vendor name */
static usb_eth_stat_t usb_eth_stat = { 0, 0, 0, 0 };
static uint32_t oid_packet_filter = 0x0000000;
static rndis_state_t rndis_state;
CFG_TUSB_MEM_SECTION CFG_TUSB_MEM_ALIGN static uint8_t ndis_report[8] = { 0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
static const uint32_t OIDSupportedList[] =
{
OID_GEN_SUPPORTED_LIST,
OID_GEN_HARDWARE_STATUS,
OID_GEN_MEDIA_SUPPORTED,
OID_GEN_MEDIA_IN_USE,
OID_GEN_MAXIMUM_FRAME_SIZE,
OID_GEN_LINK_SPEED,
OID_GEN_TRANSMIT_BLOCK_SIZE,
OID_GEN_RECEIVE_BLOCK_SIZE,
OID_GEN_VENDOR_ID,
OID_GEN_VENDOR_DESCRIPTION,
OID_GEN_VENDOR_DRIVER_VERSION,
OID_GEN_CURRENT_PACKET_FILTER,
OID_GEN_MAXIMUM_TOTAL_SIZE,
OID_GEN_PROTOCOL_OPTIONS,
OID_GEN_MAC_OPTIONS,
OID_GEN_MEDIA_CONNECT_STATUS,
OID_GEN_MAXIMUM_SEND_PACKETS,
OID_802_3_PERMANENT_ADDRESS,
OID_802_3_CURRENT_ADDRESS,
OID_802_3_MULTICAST_LIST,
OID_802_3_MAXIMUM_LIST_SIZE,
OID_802_3_MAC_OPTIONS
};
#define OID_LIST_LENGTH TU_ARRAY_SIZE(OIDSupportedList)
#define ENC_BUF_SIZE (OID_LIST_LENGTH * 4 + 32)
static void *encapsulated_buffer;
static void rndis_report(void)
{
netd_report(ndis_report, sizeof(ndis_report));
}
static void rndis_query_cmplt32(int status, uint32_t data)
{
rndis_query_cmplt_t *c;
c = (rndis_query_cmplt_t *)encapsulated_buffer;
c->MessageType = REMOTE_NDIS_QUERY_CMPLT;
c->MessageLength = sizeof(rndis_query_cmplt_t) + 4;
c->InformationBufferLength = 4;
c->InformationBufferOffset = 16;
c->Status = status;
memcpy(c + 1, &data, sizeof(data));
rndis_report();
}
static void rndis_query_cmplt(int status, const void *data, int size)
{
rndis_query_cmplt_t *c;
c = (rndis_query_cmplt_t *)encapsulated_buffer;
c->MessageType = REMOTE_NDIS_QUERY_CMPLT;
c->MessageLength = sizeof(rndis_query_cmplt_t) + size;
c->InformationBufferLength = size;
c->InformationBufferOffset = 16;
c->Status = status;
memcpy(c + 1, data, size);
rndis_report();
}
#define MAC_OPT NDIS_MAC_OPTION_COPY_LOOKAHEAD_DATA | \
NDIS_MAC_OPTION_RECEIVE_SERIALIZED | \
NDIS_MAC_OPTION_TRANSFERS_NOT_PEND | \
NDIS_MAC_OPTION_NO_LOOPBACK
static const char *rndis_vendor = RNDIS_VENDOR;
static void rndis_query(void)
{
uint8_t sta_mac[6] = {0};
esp_wifi_get_mac(WIFI_IF_STA, sta_mac);
switch (((rndis_query_msg_t *)encapsulated_buffer)->Oid)
{
case OID_GEN_SUPPORTED_LIST: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, OIDSupportedList, 4 * OID_LIST_LENGTH); return;
case OID_GEN_VENDOR_DRIVER_VERSION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00001000); return;
case OID_802_3_CURRENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, sta_mac, 6); return;
case OID_802_3_PERMANENT_ADDRESS: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, sta_mac, 6); return;
case OID_GEN_MEDIA_SUPPORTED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return;
case OID_GEN_MEDIA_IN_USE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return;
case OID_GEN_PHYSICAL_MEDIUM: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIUM_802_3); return;
case OID_GEN_HARDWARE_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
case OID_GEN_LINK_SPEED: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, RNDIS_LINK_SPEED / 100); return;
case OID_GEN_VENDOR_ID: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0x00FFFFFF); return;
case OID_GEN_VENDOR_DESCRIPTION: rndis_query_cmplt(RNDIS_STATUS_SUCCESS, rndis_vendor, strlen(rndis_vendor) + 1); return;
case OID_GEN_CURRENT_PACKET_FILTER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, oid_packet_filter); return;
case OID_GEN_MAXIMUM_FRAME_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU - SIZEOF_ETH_HDR); return;
case OID_GEN_MAXIMUM_TOTAL_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return;
case OID_GEN_TRANSMIT_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return;
case OID_GEN_RECEIVE_BLOCK_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, CFG_TUD_NET_MTU); return;
case OID_GEN_MEDIA_CONNECT_STATUS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, NDIS_MEDIA_STATE_CONNECTED); return;
case OID_GEN_RNDIS_CONFIG_PARAMETER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
case OID_802_3_MAXIMUM_LIST_SIZE: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 1); return;
case OID_802_3_MULTICAST_LIST: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return;
case OID_802_3_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_NOT_SUPPORTED, 0); return;
case OID_GEN_MAC_OPTIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, /*MAC_OPT*/ 0); return;
case OID_802_3_RCV_ERROR_ALIGNMENT: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
case OID_802_3_XMIT_ONE_COLLISION: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
case OID_802_3_XMIT_MORE_COLLISIONS: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
case OID_GEN_XMIT_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txok); return;
case OID_GEN_RCV_OK: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxok); return;
case OID_GEN_RCV_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.rxbad); return;
case OID_GEN_XMIT_ERROR: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, usb_eth_stat.txbad); return;
case OID_GEN_RCV_NO_BUFFER: rndis_query_cmplt32(RNDIS_STATUS_SUCCESS, 0); return;
default: rndis_query_cmplt(RNDIS_STATUS_FAILURE, NULL, 0); return;
}
}
#define INFBUF ((uint8_t *)&(m->RequestId) + m->InformationBufferOffset)
static void rndis_handle_config_parm(const char *data, int keyoffset, int valoffset, int keylen, int vallen)
{
(void)data;
(void)keyoffset;
(void)valoffset;
(void)keylen;
(void)vallen;
}
static void rndis_packetFilter(uint32_t newfilter)
{
(void)newfilter;
}
static void rndis_handle_set_msg(void)
{
rndis_set_cmplt_t *c;
rndis_set_msg_t *m;
rndis_Oid_t oid;
c = (rndis_set_cmplt_t *)encapsulated_buffer;
m = (rndis_set_msg_t *)encapsulated_buffer;
oid = m->Oid;
c->MessageType = REMOTE_NDIS_SET_CMPLT;
c->MessageLength = sizeof(rndis_set_cmplt_t);
c->Status = RNDIS_STATUS_SUCCESS;
switch (oid)
{
/* Parameters set up in 'Advanced' tab */
case OID_GEN_RNDIS_CONFIG_PARAMETER:
{
rndis_config_parameter_t *p;
char *ptr = (char *)m;
ptr += sizeof(rndis_generic_msg_t);
ptr += m->InformationBufferOffset;
p = (rndis_config_parameter_t *) ((void*) ptr);
rndis_handle_config_parm(ptr, p->ParameterNameOffset, p->ParameterValueOffset, p->ParameterNameLength, p->ParameterValueLength);
}
break;
/* Mandatory general OIDs */
case OID_GEN_CURRENT_PACKET_FILTER:
memcpy(&oid_packet_filter, INFBUF, 4);
if (oid_packet_filter)
{
rndis_packetFilter(oid_packet_filter);
rndis_state = rndis_data_initialized;
}
else
{
rndis_state = rndis_initialized;
}
break;
case OID_GEN_CURRENT_LOOKAHEAD:
break;
case OID_GEN_PROTOCOL_OPTIONS:
break;
/* Mandatory 802_3 OIDs */
case OID_802_3_MULTICAST_LIST:
break;
/* Power Managment: fails for now */
case OID_PNP_ADD_WAKE_UP_PATTERN:
case OID_PNP_REMOVE_WAKE_UP_PATTERN:
case OID_PNP_ENABLE_WAKE_UP:
default:
c->Status = RNDIS_STATUS_FAILURE;
break;
}
/* c->MessageID is same as before */
rndis_report();
return;
}
void rndis_indicate_status(int status)
{
rndis_indicate_status_t *c;
c = (rndis_indicate_status_t *)encapsulated_buffer;
c->MessageType = REMOTE_NDIS_INDICATE_STATUS_MSG;
c->MessageLength = sizeof(rndis_indicate_status_t);
c->Status = status;
c->StatusBufferLength = 0;
c->StatusBufferOffset = 0;
rndis_report();
}
void rndis_disconnect(void)
{
int status = RNDIS_STATUS_MEDIA_DISCONNECT;
rndis_indicate_status(status);
}
void rndis_connect(void)
{
int status = RNDIS_STATUS_MEDIA_CONNECT;
rndis_indicate_status(status);
}
void rndis_class_set_handler(uint8_t *data, int size)
{
encapsulated_buffer = data;
(void)size;
switch (((rndis_generic_msg_t *)encapsulated_buffer)->MessageType)
{
case REMOTE_NDIS_INITIALIZE_MSG:
{
rndis_initialize_cmplt_t *m;
m = ((rndis_initialize_cmplt_t *)encapsulated_buffer);
/* m->MessageID is same as before */
m->MessageType = REMOTE_NDIS_INITIALIZE_CMPLT;
m->MessageLength = sizeof(rndis_initialize_cmplt_t);
m->MajorVersion = RNDIS_MAJOR_VERSION;
m->MinorVersion = RNDIS_MINOR_VERSION;
m->Status = RNDIS_STATUS_SUCCESS;
m->DeviceFlags = RNDIS_DF_CONNECTIONLESS;
m->Medium = RNDIS_MEDIUM_802_3;
m->MaxPacketsPerTransfer = 1;
m->MaxTransferSize = CFG_TUD_NET_MTU + sizeof(rndis_data_packet_t);
m->PacketAlignmentFactor = 0;
m->AfListOffset = 0;
m->AfListSize = 0;
rndis_state = rndis_initialized;
rndis_report();
}
break;
case REMOTE_NDIS_QUERY_MSG:
rndis_query();
break;
case REMOTE_NDIS_SET_MSG:
rndis_handle_set_msg();
break;
case REMOTE_NDIS_RESET_MSG:
{
rndis_reset_cmplt_t * m;
m = ((rndis_reset_cmplt_t *)encapsulated_buffer);
rndis_state = rndis_uninitialized;
m->MessageType = REMOTE_NDIS_RESET_CMPLT;
m->MessageLength = sizeof(rndis_reset_cmplt_t);
m->Status = RNDIS_STATUS_SUCCESS;
m->AddressingReset = 1; /* Make it look like we did something */
/* m->AddressingReset = 0; - Windows halts if set to 1 for some reason */
rndis_report();
}
break;
case REMOTE_NDIS_KEEPALIVE_MSG:
{
rndis_keepalive_cmplt_t * m;
m = (rndis_keepalive_cmplt_t *)encapsulated_buffer;
m->MessageType = REMOTE_NDIS_KEEPALIVE_CMPLT;
m->MessageLength = sizeof(rndis_keepalive_cmplt_t);
m->Status = RNDIS_STATUS_SUCCESS;
}
/* We have data to send back */
rndis_report();
break;
default:
break;
}
}

View file

@ -0,0 +1,10 @@
version: "1.1.1" # Component version, required only for components pushed to the service
targets: # List of supported targets (optional, if missing all targets are considered to be supported)
- esp32s2
- esp32s3
description: ESP32-S2 TinyUSB Addtions # Description (optional)
url: https://github.com/iot-components/tinyusb # Original repository (optional)
dependencies:
# Required IDF version
idf:
version: "=4.4"

View file

@ -0,0 +1,27 @@
if(CONFIG_SOURCE_SIMULATE)
set(PIC_DIR "pictures/320240")
set( embed_files
${PIC_DIR}/0001.jpg
${PIC_DIR}/0002.jpg
${PIC_DIR}/0003.jpg
${PIC_DIR}/0004.jpg
${PIC_DIR}/0005.jpg
${PIC_DIR}/0006.jpg
${PIC_DIR}/0007.jpg
${PIC_DIR}/0008.jpg
${PIC_DIR}/0009.jpg
${PIC_DIR}/0010.jpg
${PIC_DIR}/0011.jpg
${PIC_DIR}/0012.jpg
${PIC_DIR}/0013.jpg
${PIC_DIR}/0014.jpg
${PIC_DIR}/0015.jpg)
endif()
#ESP-IDF USB component HCD level API default to private now,
#to use uvc_stream, related API must manually set to public.
idf_component_register(SRCS uvc_stream.c descriptor.c
INCLUDE_DIRS "include" "${IDF_PATH}/components/usb/private_include"
EMBED_FILES ${embed_files}
REQUIRES usb)

View file

@ -0,0 +1,27 @@
menu "UVC Stream"
config CTRL_TRANSFER_DATA_MAX_BYTES
int "Max control transfer data size (Bytes)"
range 64 2048
default 600
config UVC_GET_DEVICE_DESC
bool "Get camera device descriptor during emum"
default y
config UVC_GET_CONFIG_DESC
bool "Get camera config descriptor during emum"
default y
config UVC_PRINT_DESC_VERBOSE
bool "Print camera more descriptor info"
default n
config UVC_PRINT_PROBE_RESULT
bool "Print video cur probe result"
default y
config SOURCE_SIMULATE
bool "Enable Simulate Pictures From Flash"
default n
config TRIGGER_PIN
bool "Enable Trigger Pin For Debug"
default n
endmenu

View file

@ -0,0 +1,63 @@
## UVC Stream Component
`UVC Stream` is a USB camera driver based on `UVC` Protocol. Developers can use ESP32-S2 / ESP32-S3 as a USB host send request then continuously receive USB camera `MJPEG` frames. With the help of `ESP-IOT-SOLUTION` decoding or network transmission components, real-time camera display or IPC can be implemented.
Users can control USB video stream to start, pause, restart, or stop via a simple API interface. By registering a frame callback, the user's method can be called when a new frame is received.
### Setup Environment
1. Setup ESP-IDF `master` Environment, please refer [installation-step-by-step](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html#installation-step-by-step)
2. Setup `ESP-IOT-Solution` Environment, please refer [Setup ESP-IOT-Solution Environment](../../../README.md)
### Hardware Requirement
* Development board
1. Any `ESP32-S2`,` ESP32-S3` board with USB Host port can be used.
2. Please refer example readme for special hardware requirements
* USB camera
1. Camera must be compatible with `USB1.1` full-speed mode
2. Camera must support `MJPEG` compression
3. Camera interface `max packet size` be less than `512` bytes
4. Frame stream bandwidth should be less than `4 Mbps` (500 KB/s)
5. Please refer example readme for special camera requirements
### UVC Stream API Guide
1. Users need to know USB camera detailed descriptors in advance, Linux can print the information using the `lsusb -v`, the` uvc_config_t` should be filled based on the information, the parameters correspondence is as follows:
```
uvc_config_t uvc_config = {
.dev_speed = usb_speed_full, / / usb speed mode, must fixed to usb_speed_full
.configuration = 1, // configure descriptor index, generally 1
.format_index = 1, // mjpeg format index, for example 1
.frame_width = 320, // mjpeg width pixel, for example 320
.frame_height = 240, // mjpeg height pixel, for example 240
.frame_index = 1, // frame resolution index, for example 1
.frame_interval = 666666, / frame interval (100µs units), such as 15fps
.interface = 1, // video stream interface number, generally 1
.interface_alt = 1, // alt interface number, `max packet size` should be less than `512` bytes
.isoc_ep_addr = 0x81, // alt interface endpoint address, for example 0x81
.isoc_ep_mps = 512, // alt interface endpoint MPS, for example 512
.xfer_buffer_size = 32 * 1024, // single frame image size, need to be determined according to actual testing, 320 * 240 generally less than 35KB
.xfer_buffer_a = pointer_buffer_a, // the internal transfer buffer
.xfer_buffer_b = pointer_buffer_b, // the internal transfer buffer
.frame_buffer_size = 32 * 1024, // single frame image size, need to determine according to actual test
.frame_buffer = pointer_frame_buffer, // the image frame buffer
}
```
1. Use the `uvc_streaming_config` and above uvc_config_t parameters to config the driver;
2. Use the `uvc_streaming_start` to turn on the video stream, after USB connection and UVC parameter negotiation, the camera will continue to output the data stream. This driver will call the user's callback function to decode, display, transmission, etc.
3. If you perform an error, reduce the resolution or frame rate, return to step 1 to modify the parameters;
4. Use the `uvc_streaming_suspend` to temporarily suspend the camera stream;
5. Use the `uvc_streaming_resume` to resume the camera stream from suspend;
6. Use the `uvc_streaming_stop` to stop the video stream, USB resource will be completely released.
### USB Camera Examples
1. [USB Camera + Wi-Fi](../../../examples/usb/host/usb_camera_wifi_transfer)
2. [USB Camera + LCD](../../../examples/usb/host/usb_camera_lcd_display)
3. [USB Camera + SD Card](../../../examples/usb/host/usb_camera_sd_card)

View file

@ -0,0 +1,63 @@
## UVC Stream 组件说明
`UVC Stream` 是基于 `UVC` 协议开发的 USB 摄像头驱动,用户可使用 ESP32-S2/ESP32-S3 作为 USB 主机,请求和并连续接收 USB 摄像头 `MJPEG` 图像帧。配合 `ESP-IOT-Solution` 图像解码或网络传输组件,可以实现屏幕显示或 IPC 等应用。
用户可通过简单的 API 接口控制视频流启动、暂停、重启、和停止操作。通过注册回调函数,可在得到完整图像帧时,对图像数据进行应用层处理。
### 开发环境准备
1. 搭建 ESP-IDF `master` 分支开发环境:[installation-step-by-step](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/get-started/index.html#installation-step-by-step)
2. 搭建 `ESP-IOT-Solution` 环境:[Setup ESP-IOT-Solution Environment](../../../README.md)
### 硬件准备
* 开发板
1. 本仓库已经适配的芯片为 `ESP32-S2` 、`ESP32-S3`
2. 资源使用情况详见示例程序说明文件
* USB 摄像头
1. 摄像头必须兼容 `USB1.1` 全速模式
2. 摄像头需要自带 `MJPEG` 压缩
3. 摄像头支持设置接口 `Max Packet Size``512`
4. 图像数据流 USB 传输总带宽应小于 `4 Mbps` 500 KB/s
5. 分辨率等要求详见示例程序说明文件
### UVC Stream API 使用说明
1. 用户需提前了解待适配摄像头`配置描述符`的详细参数Linux 用户可使用 `lsusb -v` 查看, 据此填写 `uvc_config_t` 的配置项,参数对应关系如下:
```
uvc_config_t uvc_config = {
.dev_speed = USB_SPEED_FULL, //固定为 USB_SPEED_FULL
.configuration = 1, //配置描述符编号,一般为 1
.format_index = 1, // MJPEG 对应的 bFormatIndex, 例如为 1
.frame_width = 320, // MJPEG 横向像素,例如 320
.frame_height = 240, // MJPEG 纵向像素,例如 240
.frame_index = 1, //MJPEG 320*240 对应的 bFrameIndex, 例如为 1
.frame_interval = 666666, //可选的帧率 dwFrameInterval例如 15fps
.interface = 1, // 可选的视频流接口 bInterfaceNumber一般为 1
.interface_alt = 1, // 接口选项 bAlternateSetting, 例如 1 对应端点 MPS 最大支持 512
.isoc_ep_addr = 0x81, // 接口选项对应的 bEndpointAddress, 例如为 0x81
.isoc_ep_mps = 512, // 接口选项的确定的 MPS 例如为 512
.xfer_buffer_size = 32*1024, //单帧图像大小需要根据实际测试确定320*240 一般小于 35KB
.xfer_buffer_a = pointer_buffer_a, // 已经申请的 buffer 指针
.xfer_buffer_b = pointer_buffer_b, // 已经申请的 buffer 指针
.frame_buffer_size = 32*1024, //单帧图像大小,需要根据实际测试确定
.frame_buffer = pointer_frame_buffer, // 已经申请的 buffer 指针
};
```
2. 使用 `uvc_streaming_config` 将第 1 步确定的 `uvc_config_t` 参数传入驱动;
3. 使用 `uvc_streaming_start` 以第 2 步传入的参数开启视频流,如果参数通过协商,摄像头将持续输出数据流。该驱动将在检测到完整的图像帧时,调用用户的回调函数对图像进行解码、刷屏、传输等操作;
4. 如果第 3 步执行出错,请降低分辨率或帧率目标,回到第 1 步修改参数;
5. 使用 `uvc_streaming_suspend` 可暂停摄像头视频流;
6. 使用 `uvc_streaming_resume` 可重启摄像头视频流;
7. 使用 `uvc_streaming_stop` 停止视频流USB 资源将被完全释放。
### USB 摄像头示例程序
1. [USB Camera + Wi-Fi 图传](../../../examples/usb/host/usb_camera_wifi_transfer)
2. [USB Camera + LCD 本地显示](../../../examples/usb/host/usb_camera_lcd_display)
3. [USB Camera + SD 卡存储](../../../examples/usb/host/usb_camera_sd_card)

View file

@ -0,0 +1,287 @@
// Copyright 2016-2022 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License
#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include "esp_log.h"
#include "usb/usb_host.h"
#include "usb/usb_types_ch9.h"
typedef enum {
CS_INTERFACE_DESC = 0x24,
CS_ENDPOINT_DESC = 0x25,
} descriptor_types_t;
typedef enum {
SC_VIDEOCONTROL = 1,
SC_VIDEOSTREAMING = 2,
} interface_sub_class_t;
static interface_sub_class_t interface_sub_class = SC_VIDEOCONTROL;
typedef enum {
VC_HEADER = 0x01,
VC_INPUT_TERMINAL = 0x02,
VC_OUTPUT_TERMINAL = 0x03,
VC_SELECTOR_UNIT = 0x04,
VC_PROCESSING_UNIT = 0x05,
VS_FORMAT_MJPEG = 0x06,
VS_FRAME_MJPEG = 0x07,
VS_STILL_FRAME = 0x03,
VS_COLORFORMAT = 0x0D,
} descriptor_subtypes_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubtype;
} desc_header_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bFirstInterface;
uint8_t bInterfaceCount;
uint8_t bFunctionClass;
uint8_t bFunctionSubClass;
uint8_t bFunctionProtocol;
uint8_t iFunction;
} USB_DESC_ATTR ifc_assoc_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t bcdUVC;
uint16_t wTotalLength;
uint32_t dwClockFrequency;
uint8_t bFunctionProtocol;
uint8_t bInCollection;
uint8_t baInterfaceNr;
} USB_DESC_ATTR vc_interface_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bNumFormats;
uint16_t wTotalLength;
uint8_t bEndpointAddress;
uint8_t bFunctionProtocol;
uint8_t bmInfo;
uint8_t bTerminalLink;
uint8_t bStillCaptureMethod;
uint8_t bTriggerSupport;
uint8_t bTriggerUsage;
uint8_t bControlSize;
uint8_t bmaControls;
} USB_DESC_ATTR vs_interface_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint16_t wMaxTransferSize;
} USB_DESC_ATTR class_specific_endpoint_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFormatIndex;
uint8_t bNumFrameDescriptors;
uint8_t bmFlags;
uint8_t bDefaultFrameIndex;
uint8_t bAspectRatioX;
uint8_t bAspectRatioY;
uint8_t bmInterlaceFlags;
uint8_t bCopyProtect;
} USB_DESC_ATTR vs_format_desc_t;
typedef struct {
uint8_t bLength;
uint8_t bDescriptorType;
uint8_t bDescriptorSubType;
uint8_t bFrameIndex;
uint8_t bmCapabilities;
uint16_t wWidth;
uint16_t wHeigh;
uint32_t dwMinBitRate;
uint32_t dwMaxBitRate;
uint32_t dwMaxVideoFrameBufSize;
uint32_t dwDefaultFrameInterval;
uint8_t bFrameIntervalType;
union {
uint32_t dwFrameInterval;
struct {
uint32_t dwMinFrameInterval;
uint32_t dwMaxFrameInterval;
uint32_t dwFrameIntervalStep;
};
};
} USB_DESC_ATTR vs_frame_desc_t;
static void print_class_header_desc(const uint8_t *buff)
{
if (interface_sub_class == SC_VIDEOCONTROL) {
const vc_interface_desc_t *desc = (const vc_interface_desc_t *) buff;
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\t*** Class-specific VC Interface Descriptor ***\n");
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
#endif
printf("\tbcdUVC %x\n", desc->bcdUVC);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\twTotalLength %u\n", desc->wTotalLength);
printf("\tdwClockFrequency %u\n", desc->dwClockFrequency);
printf("\tbFunctionProtocol %u\n", desc->bFunctionProtocol);
printf("\tbInCollection %u\n", desc->bInCollection);
printf("\tbaInterfaceNr %u\n", desc->baInterfaceNr);
#endif
} else if (interface_sub_class == SC_VIDEOSTREAMING) {
const vs_interface_desc_t *desc = (const vs_interface_desc_t *) buff;
printf("\t*** Class-specific VS Interface Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType %u\n", desc->bDescriptorSubType);
#endif
printf("\tbNumFormats %x\n", desc->bNumFormats);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\twTotalLength %u\n", desc->wTotalLength);
printf("\tbEndpointAddress %u\n", desc->bEndpointAddress);
printf("\tbFunctionProtocol %u\n", desc->bFunctionProtocol);
printf("\tbmInfo 0x%x\n", desc->bmInfo);
printf("\tbTerminalLink %u\n", desc->bTerminalLink);
printf("\tbStillCaptureMethod %u\n", desc->bStillCaptureMethod);
printf("\tbTriggerSupport %u\n", desc->bTriggerSupport);
printf("\tbTriggerUsage %u\n", desc->bTriggerUsage);
printf("\tbControlSize %u\n", desc->bControlSize);
printf("\tbmaControls 0x%x\n", desc->bmaControls);
#endif
}
}
static void print_vs_format_mjpeg_desc(const uint8_t *buff)
{
const vs_format_desc_t *desc = (const vs_format_desc_t *) buff;
printf("\t*** VS Format MJPEG Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFormatIndex 0x%x\n", desc->bFormatIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbNumFrameDescriptors %u\n", desc->bNumFrameDescriptors);
printf("\tbmFlags 0x%x\n", desc->bmFlags);
#endif
printf("\tbDefaultFrameIndex %u\n", desc->bDefaultFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbAspectRatioX %u\n", desc->bAspectRatioX);
printf("\tbAspectRatioY %u\n", desc->bAspectRatioY);
printf("\tbmInterlaceFlags 0x%x\n", desc->bmInterlaceFlags);
printf("\tbCopyProtect %u\n", desc->bCopyProtect);
#endif
}
static void print_vs_frame_mjpeg_desc(const uint8_t *buff)
{
// Copy to local buffer due to potential misalignment issues.
uint32_t raw_desc[25];
uint32_t desc_size = ((const vs_frame_desc_t *)buff)->bLength;
memcpy(raw_desc, buff, desc_size);
const vs_frame_desc_t *desc = (const vs_frame_desc_t *) raw_desc;
printf("\t*** VS MJPEG Frame Descriptor ***\n");
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbLength 0x%x\n", desc->bLength);
printf("\tbDescriptorType 0x%x\n", desc->bDescriptorType);
printf("\tbDescriptorSubType 0x%x\n", desc->bDescriptorSubType);
#endif
printf("\tbFrameIndex 0x%x\n", desc->bFrameIndex);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tbmCapabilities 0x%x\n", desc->bmCapabilities);
#endif
printf("\twWidth %u\n", desc->wWidth);
printf("\twHeigh %u\n", desc->wHeigh);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tdwMinBitRate %u\n", desc->dwMinBitRate);
printf("\tdwMaxBitRate %u\n", desc->dwMaxBitRate);
#endif
printf("\tdwMaxVideoFrameBufSize %u\n", desc->dwMaxVideoFrameBufSize);
#ifdef CONFIG_UVC_PRINT_DESC_VERBOSE
printf("\tdwDefaultFrameInterval %u\n", desc->dwDefaultFrameInterval);
printf("\tbFrameIntervalType %u\n", desc->bFrameIntervalType);
#endif
if (desc->bFrameIntervalType == 0) {
// Continuous Frame Intervals
printf("\tdwMinFrameInterval %u\n", desc->dwMinFrameInterval);
printf("\tdwMaxFrameInterval %u\n", desc->dwMaxFrameInterval);
printf("\tdwFrameIntervalStep %u\n", desc->dwFrameIntervalStep);
} else {
// Discrete Frame Intervals
size_t num_of_intervals = (desc->bLength - 26) / 4;
uint32_t *interval = (uint32_t *)&desc->dwFrameInterval;
for (int i = 0; i < num_of_intervals; ++i) {
printf("\tFrameInterval[%d] %u\n", i, interval[i]);
}
}
}
static void print_class_specific_desc(const uint8_t *buff)
{
desc_header_t *header = (desc_header_t *)buff;
switch (header->bDescriptorSubtype) {
case VC_HEADER:
print_class_header_desc(buff);
break;
case VS_FORMAT_MJPEG:
if (interface_sub_class == SC_VIDEOCONTROL) {
printf("\t*** Extension Unit Descriptor unsupported, skipping... ***\n");;
interface_sub_class = SC_VIDEOSTREAMING;
return;
}
print_vs_format_mjpeg_desc(buff);
interface_sub_class = SC_VIDEOCONTROL;
break;
case VS_FRAME_MJPEG:
print_vs_frame_mjpeg_desc(buff);
break;
default:
break;
}
}
void _print_uvc_class_descriptors_cb(const usb_standard_desc_t *desc)
{
const uint8_t *buff = (uint8_t *)desc;
desc_header_t *header = (desc_header_t *)desc;
switch (header->bDescriptorType) {
case CS_INTERFACE_DESC:
print_class_specific_desc(buff);
break;
default:
break;
}
}

View file

@ -0,0 +1,195 @@
#pragma once
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
/** Handle on an open UVC device.
* only one device supported
*/
typedef void* uvc_device_handle_t;
/** UVC error types, based on libusb errors
* @ingroup diag
*/
typedef enum uvc_error {
/** Success (no error) */
UVC_SUCCESS = 0,
/** Input/output error */
UVC_ERROR_IO = -1,
/** Invalid parameter */
UVC_ERROR_INVALID_PARAM = -2,
/** Access denied */
UVC_ERROR_ACCESS = -3,
/** No such device */
UVC_ERROR_NO_DEVICE = -4,
/** Entity not found */
UVC_ERROR_NOT_FOUND = -5,
/** Resource busy */
UVC_ERROR_BUSY = -6,
/** Operation timed out */
UVC_ERROR_TIMEOUT = -7,
/** Overflow */
UVC_ERROR_OVERFLOW = -8,
/** Pipe error */
UVC_ERROR_PIPE = -9,
/** System call interrupted */
UVC_ERROR_INTERRUPTED = -10,
/** Insufficient memory */
UVC_ERROR_NO_MEM = -11,
/** Operation not supported */
UVC_ERROR_NOT_SUPPORTED = -12,
/** Device is not UVC-compliant */
UVC_ERROR_INVALID_DEVICE = -50,
/** Mode not supported */
UVC_ERROR_INVALID_MODE = -51,
/** Resource has a callback (can't use polling and async) */
UVC_ERROR_CALLBACK_EXISTS = -52,
/** Undefined error */
UVC_ERROR_OTHER = -99
} uvc_error_t;
/** Color coding of stream, transport-independent
* @ingroup streaming
*/
enum uvc_frame_format {
UVC_FRAME_FORMAT_UNKNOWN = 0,
/** Any supported format */
UVC_FRAME_FORMAT_ANY = 0,
UVC_FRAME_FORMAT_UNCOMPRESSED,
UVC_FRAME_FORMAT_COMPRESSED,
/** YUYV/YUV2/YUV422: YUV encoding with one luminance value per pixel and
* one UV (chrominance) pair for every two pixels.
*/
UVC_FRAME_FORMAT_YUYV,
UVC_FRAME_FORMAT_UYVY,
/** 24-bit RGB */
UVC_FRAME_FORMAT_RGB,
UVC_FRAME_FORMAT_BGR,
/** Motion-JPEG (or JPEG) encoded images */
UVC_FRAME_FORMAT_MJPEG,
UVC_FRAME_FORMAT_H264,
/** Greyscale images */
UVC_FRAME_FORMAT_GRAY8,
UVC_FRAME_FORMAT_GRAY16,
/* Raw colour mosaic images */
UVC_FRAME_FORMAT_BY8,
UVC_FRAME_FORMAT_BA81,
UVC_FRAME_FORMAT_SGRBG8,
UVC_FRAME_FORMAT_SGBRG8,
UVC_FRAME_FORMAT_SRGGB8,
UVC_FRAME_FORMAT_SBGGR8,
/** YUV420: NV12 */
UVC_FRAME_FORMAT_NV12,
/** Number of formats understood */
UVC_FRAME_FORMAT_COUNT,
};
/** Converts an unaligned four-byte little-endian integer into an int32 */
#define DW_TO_INT(p) ((p)[0] | ((p)[1] << 8) | ((p)[2] << 16) | ((p)[3] << 24))
/** Converts an unaligned two-byte little-endian integer into an int16 */
#define SW_TO_SHORT(p) ((p)[0] | ((p)[1] << 8))
/** Converts an int16 into an unaligned two-byte little-endian integer */
#define SHORT_TO_SW(s, p) \
(p)[0] = (s); \
(p)[1] = (s) >> 8;
/** Converts an int32 into an unaligned four-byte little-endian integer */
#define INT_TO_DW(i, p) \
(p)[0] = (i); \
(p)[1] = (i) >> 8; \
(p)[2] = (i) >> 16; \
(p)[3] = (i) >> 24;
/** Streaming mode, includes all information needed to select stream
* @ingroup streaming
*/
typedef struct uvc_stream_ctrl {
uint16_t bmHint;
uint8_t bFormatIndex;
uint8_t bFrameIndex;
uint32_t dwFrameInterval;
uint16_t wKeyFrameRate;
uint16_t wPFrameRate;
uint16_t wCompQuality;
uint16_t wCompWindowSize;
uint16_t wDelay;
uint32_t dwMaxVideoFrameSize;
uint32_t dwMaxPayloadTransferSize;
uint32_t dwClockFrequency;
uint8_t bmFramingInfo;
uint8_t bPreferredVersion;
uint8_t bMinVersion;
uint8_t bMaxVersion;
uint8_t bInterfaceNumber;
} uvc_stream_ctrl_t;
/** UVC request code (A.8) */
enum uvc_req_code {
UVC_RC_UNDEFINED = 0x00,
UVC_SET_CUR = 0x01,
UVC_GET_CUR = 0x81,
UVC_GET_MIN = 0x82,
UVC_GET_MAX = 0x83,
UVC_GET_RES = 0x84,
UVC_GET_LEN = 0x85,
UVC_GET_INFO = 0x86,
UVC_GET_DEF = 0x87
};
/** VideoStreaming interface control selector (A.9.7) */
enum uvc_vs_ctrl_selector {
UVC_VS_CONTROL_UNDEFINED = 0x00,
UVC_VS_PROBE_CONTROL = 0x01,
UVC_VS_COMMIT_CONTROL = 0x02,
UVC_VS_STILL_PROBE_CONTROL = 0x03,
UVC_VS_STILL_COMMIT_CONTROL = 0x04,
UVC_VS_STILL_IMAGE_TRIGGER_CONTROL = 0x05,
UVC_VS_STREAM_ERROR_CODE_CONTROL = 0x06,
UVC_VS_GENERATE_KEY_FRAME_CONTROL = 0x07,
UVC_VS_UPDATE_FRAME_SEGMENT_CONTROL = 0x08,
UVC_VS_SYNC_DELAY_CONTROL = 0x09
};
/** An image frame received from the UVC device
* @ingroup streaming
*/
typedef struct uvc_frame {
/** Image data for this frame */
void *data;
/** Size of image data buffer */
size_t data_bytes;
/** Width of image in pixels */
uint32_t width;
/** Height of image in pixels */
uint32_t height;
/** Pixel data format */
enum uvc_frame_format frame_format;
/** Number of bytes per horizontal line (undefined for compressed format) */
size_t step;
/** Frame number (may skip, but is strictly monotonically increasing) */
uint32_t sequence;
/** Estimate of system time when the device started capturing the image */
struct timeval capture_time;
/** Estimate of system time when the device finished receiving the image */
struct timespec capture_time_finished;
/** Handle on the device that produced the image.
* @warning You must not call any uvc_* functions during a callback. */
uvc_device_handle_t *source;
/** Is the data buffer owned by the library?
* If 1, the data buffer can be arbitrarily reallocated by frame conversion
* functions.
* If 0, the data buffer will not be reallocated or freed by the library.
* Set this field to zero if you are supplying the buffer.
*/
uint8_t library_owns_data;
/** Metadata for this frame if available */
void *metadata;
/** Size of metadata buffer */
size_t metadata_bytes;
} uvc_frame_t;
/** A callback function to handle incoming assembled UVC frames
* @ingroup streaming
*/
typedef void(uvc_frame_callback_t)(struct uvc_frame *frame, void *user_ptr);

View file

@ -0,0 +1,68 @@
#pragma once
#if (defined CONFIG_TRIGGER_PIN) && (!defined CONFIG_LCD_INTERFACE_I2S)
#include <string.h>
#include <time.h>
#include "driver/gpio.h"
#define TRIGGER_XFER_PIN 10 //hcd level trigger
#define TRIGGER_URB_PIN 11 //URB level trigger
#define TRIGGER_NEW_FARAME 12 //frame level trigger
#define TRIGGER_DECODER_PIN 13 //user level trigger
#define GPIO_OUTPUT_PIN_SEL ((1ULL<<TRIGGER_DECODER_PIN) | (1ULL<<TRIGGER_NEW_FARAME) | (1ULL<<TRIGGER_URB_PIN) | (1ULL<<TRIGGER_XFER_PIN))
#define TRIGGER_INIT() trigger_init() //call in app_main
#define TRIGGER_XFER_RUN() gpio_set_level(TRIGGER_XFER_PIN, 1)
#define TRIGGER_XFER_LEAVE() gpio_set_level(TRIGGER_XFER_PIN, 0)
#define TRIGGER_URB_ENQUEUE() gpio_set_level(TRIGGER_URB_PIN, 1)
#define TRIGGER_URB_DEQUEUE() gpio_set_level(TRIGGER_URB_PIN, 0)
#define TRIGGER_NEW_FRAME() toggle_pin2(TRIGGER_NEW_FARAME)
#define TRIGGER_PIPE_EVENT() toggle_pin(TRIGGER_XFER_PIN)
#define TRIGGER_DECODER_RUN() gpio_set_level(TRIGGER_DECODER_PIN, 1)
#define TRIGGER_DECODER_LEAVE() gpio_set_level(TRIGGER_DECODER_PIN, 0)
static inline void trigger_init(void) {
gpio_config_t io_conf;
//disable interrupt
io_conf.intr_type = GPIO_INTR_DISABLE;
//set as output mode
io_conf.mode = GPIO_MODE_OUTPUT;
//bit mask of the pins that you want to set,e.g.GPIO18/19
io_conf.pin_bit_mask = GPIO_OUTPUT_PIN_SEL;
//disable pull-down mode
io_conf.pull_down_en = 0;
//disable pull-up mode
io_conf.pull_up_en = 0;
//configure GPIO with the given settings
gpio_config(&io_conf);
gpio_set_level(TRIGGER_XFER_PIN, 0);
gpio_set_level(TRIGGER_URB_PIN, 0);
gpio_set_level(TRIGGER_NEW_FARAME, 0);
gpio_set_level(TRIGGER_DECODER_PIN, 0);
}
static inline void toggle_pin(int pin)
{
static bool state = 0;
state = !state;
gpio_set_level(pin, state);
}
static inline void toggle_pin2(int pin)
{
static bool state = 0;
state = !state;
gpio_set_level(pin, state);
}
#else
#define TRIGGER_INIT()
#define TRIGGER_XFER_RUN()
#define TRIGGER_XFER_LEAVE()
#define TRIGGER_URB_ENQUEUE()
#define TRIGGER_URB_DEQUEUE()
#define TRIGGER_NEW_FRAME()
#define TRIGGER_DECODER_RUN()
#define TRIGGER_DECODER_LEAVE()
#define TRIGGER_PIPE_EVENT()
#endif

View file

@ -0,0 +1,125 @@
// Copyright 2016-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "usb/usb_types_stack.h"
#include "libuvc_def.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief users need to get params from camera descriptors,
* eg. run `lsusb -v` in linux
*/
typedef struct uvc_config{
usb_speed_t dev_speed; /*!< USB Device speed, Fix to USB_SPEED_FULL now */
uint16_t configuration; /*!< bConfigurationValue */
uint8_t format_index; /*!< bFormatIndex */
uint16_t frame_width; /*!< wWidth */
uint16_t frame_height; /*!< wHeight */
uint8_t frame_index; /*!< bFrameIndex */
uint32_t frame_interval; /*!< dwFrameInterval */
uint16_t interface; /*!< bInterfaceNumber */
uint16_t interface_alt; /*!< bAlternateSetting, ep MPS must =< 512 */
uint8_t isoc_ep_addr; /*!< bEndpointAddress */
uint32_t isoc_ep_mps; /*!< MPS size of bAlternateSetting */
uint32_t xfer_buffer_size; /*!< transfer buffer size */
uint8_t *xfer_buffer_a; /*!< buffer for usb payload */
uint8_t *xfer_buffer_b; /*!< buffer for usb payload */
uint32_t frame_buffer_size; /*!< frame buffer size */
uint8_t *frame_buffer; /*!< buffer for one frame */
} uvc_config_t;
/**
* @brief Pre-config UVC driver with params from known USB Camera Descriptor
*
* @param config config struct described in uvc_config_t
* @return esp_err_t
* ESP_ERR_INVALID_ARG Args not supported
* ESP_OK Config driver suceed
*/
esp_err_t uvc_streaming_config(const uvc_config_t *config);
/**
* @brief Start camera IN streaming with pre-configs, uvc driver will create multi-tasks internal
* to handle usb data from different pipes, and run user's callback after new frame ready.
* only one streaming supported now.
*
* @param cb callback function to handle incoming assembled UVC frame
* @param user_ptr user pointer used in callback
* @return
* ESP_ERR_INVALID_STATE streaming not configured, or streaming running
* ESP_ERR_INVALID_ARG args not supported
* ESP_FAIL start failed
* ESP_OK start suceed
*/
esp_err_t uvc_streaming_start(uvc_frame_callback_t *cb, void *user_ptr);
/**
* @brief Suspend current IN streaming
*
* @return
* ESP_ERR_INVALID_STATE not inited
* ESP_FAIL suspend failed
* ESP_OK suspend suceed
* ESP_ERR_TIMEOUT suspend wait timeout
*/
esp_err_t uvc_streaming_suspend(void);
/**
* @brief Resume current IN streaming
*
* @return
* ESP_ERR_INVALID_STATE not inited
* ESP_FAIL resume failed
* ESP_OK resume suceed
* ESP_ERR_TIMEOUT resume wait timeout
*/
esp_err_t uvc_streaming_resume(void);
/**
* @brief Stop current IN streaming, internal tasks will be delete, related resourses will be free
*
* @return
* ESP_ERR_INVALID_STATE not inited
* ESP_OK stop suceed
* ESP_ERR_TIMEOUT stop wait timeout
*/
esp_err_t uvc_streaming_stop(void);
/**
* @brief start a simulate streaming from internal flash,
* this api can be used to test display system without camera input,
* this api is disabled by default to save flash size.
*
* @param cb function to handle incoming assembled simulate frame
* @param user_ptr user pointer used in callback
* @return
* ESP_ERR_NOT_SUPPORTED simulate not enable
* ESP_ERR_INVALID_ARG invalid input args
* ESP_ERR_NO_MEM no enough memory
* ESP_OK suceed
*/
esp_err_t uvc_streaming_simulate_start(uvc_frame_callback_t *cb, void *user_ptr);
#ifdef __cplusplus
}
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -0,0 +1,3 @@
idf_component_register(SRC_DIRS "."
INCLUDE_DIRS "."
REQUIRES unity uvc_stream)

View file

@ -0,0 +1,144 @@
// Copyright 2016-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "unity.h"
#include "esp_system.h"
#include "esp_heap_caps.h"
#include "hal/usb_hal.h"
#include "uvc_stream.h"
#include "esp_log.h"
static const char *TAG = "uvc_test";
/* USB PIN fixed in esp32-s2, can not use io matrix */
#define BOARD_USB_DP_PIN 20
#define BOARD_USB_DN_PIN 19
/* USB Camera Descriptors Related MACROS,
the quick demo skip the standred get descriptors process,
users need to get params from camera descriptors from PC side,
eg. run `lsusb -v` in linux,
then hardcode the related MACROS below
*/
#define DESCRIPTOR_CONFIGURATION_INDEX 1
#define DESCRIPTOR_FORMAT_MJPEG_INDEX 2
#define DESCRIPTOR_FRAME_320_240_INDEX 3
#define DESCRIPTOR_FRAME_15FPS_INTERVAL 666666
#define DESCRIPTOR_STREAM_INTERFACE_INDEX 1
#define DESCRIPTOR_STREAM_INTERFACE_ALT_MPS_512 3
#define DESCRIPTOR_STREAM_ISOC_ENDPOINT_ADDR 0x81
#define DEMO_FRAME_WIDTH 320
#define DEMO_FRAME_HEIGHT 240
#define DEMO_XFER_BUFFER_SIZE (35 * 1024)
#define DEMO_FRAME_INDEX DESCRIPTOR_FRAME_320_240_INDEX
#define DEMO_FRAME_INTERVAL DESCRIPTOR_FRAME_15FPS_INTERVAL
/* max packet size of esp32-s2 is 1*512, bigger is not supported*/
#define DEMO_ISOC_EP_MPS 512
#define DEMO_ISOC_INTERFACE_ALT DESCRIPTOR_STREAM_INTERFACE_ALT_MPS_512
static void *_malloc(size_t size)
{
return heap_caps_malloc(size, MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
}
static void _free(void *ptr)
{
heap_caps_free(ptr);
}
/* *******************************************************************************************
* This callback function runs once per frame. Use it to perform any
* quick processing you need, or have it put the frame into your application's
* input queue. If this function takes too long, you'll start losing frames. */
static void frame_cb(uvc_frame_t *frame, void *ptr)
{
ESP_LOGI(TAG, "callback! frame_format = %d, seq = %u, width = %d, height = %d, length = %u, ptr = %d",
frame->frame_format, frame->sequence, frame->width, frame->height, frame->data_bytes, (int) ptr);
switch (frame->frame_format) {
case UVC_FRAME_FORMAT_MJPEG:
break;
default:
break;
}
}
TEST_CASE("test uvc streaming", "[usb][uvc_stream]")
{
/* using internal PHY */
usb_hal_context_t hal = {
.use_external_phy = false
};
usb_hal_init(&hal);
/* malloc double buffer for usb payload, xfer_buffer_size >= frame_buffer_size*/
uint8_t *xfer_buffer_a = (uint8_t *)_malloc(DEMO_XFER_BUFFER_SIZE);
TEST_ASSERT(xfer_buffer_a != NULL);
uint8_t *xfer_buffer_b = (uint8_t *)_malloc(DEMO_XFER_BUFFER_SIZE);
TEST_ASSERT(xfer_buffer_b != NULL);
/* malloc frame buffer for a jpeg frame*/
uint8_t *frame_buffer = (uint8_t *)_malloc(DEMO_XFER_BUFFER_SIZE);
TEST_ASSERT(frame_buffer != NULL);
uvc_config_t uvc_config = {
.dev_speed = USB_SPEED_FULL,
.configuration = DESCRIPTOR_CONFIGURATION_INDEX,
.format_index = DESCRIPTOR_FORMAT_MJPEG_INDEX,
.frame_width = DEMO_FRAME_WIDTH,
.frame_height = DEMO_FRAME_HEIGHT,
.frame_index = DEMO_FRAME_INDEX,
.frame_interval = DEMO_FRAME_INTERVAL,
.interface = DESCRIPTOR_STREAM_INTERFACE_INDEX,
.interface_alt = DEMO_ISOC_INTERFACE_ALT,
.isoc_ep_addr = DESCRIPTOR_STREAM_ISOC_ENDPOINT_ADDR,
.isoc_ep_mps = DEMO_ISOC_EP_MPS,
.xfer_buffer_size = DEMO_XFER_BUFFER_SIZE,
.xfer_buffer_a = xfer_buffer_a,
.xfer_buffer_b = xfer_buffer_b,
.frame_buffer_size = DEMO_XFER_BUFFER_SIZE,
.frame_buffer = frame_buffer,
};
size_t test_count = 20;
for (size_t i = 0; i < test_count; i++) {
/* pre-config UVC driver with params from known USB Camera Descriptors*/
TEST_ASSERT_EQUAL(ESP_OK, uvc_streaming_config(&uvc_config));
/* Start camera IN stream with pre-configs, uvc driver will create multi-tasks internal
to handle usb data from different pipes, and user's callback will be called after new frame ready. */
TEST_ASSERT_EQUAL(ESP_OK, uvc_streaming_start(frame_cb, NULL));
vTaskDelay(2000 / portTICK_PERIOD_MS);
/* test streaming suspend */
TEST_ASSERT_EQUAL(ESP_OK, uvc_streaming_suspend());
vTaskDelay(1000 / portTICK_PERIOD_MS);
/* test streaming resume */
TEST_ASSERT_EQUAL(ESP_OK, uvc_streaming_resume());
vTaskDelay(1000 / portTICK_PERIOD_MS);
/* test streaming stop */
TEST_ASSERT_EQUAL(ESP_OK, uvc_streaming_stop());
}
_free(xfer_buffer_a);
_free(xfer_buffer_b);
_free(frame_buffer);
}

File diff suppressed because it is too large Load diff

View file

@ -164,7 +164,7 @@ Mandatory APIs
* @return board_res_handle_t Resource's handle
* if no related handle,NULL will be returned
*/
board_res_handle_t iot_board_get_handle(board_res_id_t id);
board_res_handle_t iot_board_get_handle(int id);
/**
* @brief Get board information

View file

@ -164,7 +164,7 @@ Boards 组件的结构如下:
* @return board_res_handle_t Resource's handle
* if no related handle,NULL will be returned
*/
board_res_handle_t iot_board_get_handle(board_res_id_t id);
board_res_handle_t iot_board_get_handle(int id);
/**
* @brief Get board information

View file

@ -9,6 +9,8 @@ elseif(CONFIG_BOARD_ESP32S2_SAOLA_1)
set(boards_dir "esp32s2-saola-1")
elseif(CONFIG_BOARD_ESP32_LCDKIT)
set(boards_dir "esp32-lcdkit")
elseif(CONFIG_BOARD_ESP32S3_USB_OTG_EV)
set(boards_dir "esp32s3_usb_otg_ev")
endif()
message(STATUS "-----------Board Info---------")
@ -16,7 +18,7 @@ message(STATUS "IDF_TARGET = ${IDF_TARGET}")
message(STATUS "Board DIR = ${boards_dir}")
message(STATUS "---------Board Info End---------")
idf_component_register( SRC_DIRS "${boards_dir}"
INCLUDE_DIRS "${boards_dir}"
REQUIRES bus)
#The SRC_DIRS order cannot be changed, otherwise weak functions may not be overridden
idf_component_register( SRC_DIRS "${boards_dir}" "."
INCLUDE_DIRS "${boards_dir}" "."
REQUIRES bus button esp_adc_cal nvs_flash)

View file

@ -4,13 +4,15 @@ menu "Board Options"
prompt "Choose Target Board"
config BOARD_ESP32_DEVKITC_V4
bool "Devkitc-V4 With Esp32 Onboard From Espressif"
bool "Devkitc-V4 With ESP32 Onboard From Espressif"
config BOARD_ESP32_MESHKIT_SENSE
bool "Meshkit Sense With Esp32 Onboard From Espressif"
bool "Meshkit Sense With ESP32 Onboard From Espressif"
config BOARD_ESP32S2_SAOLA_1
bool "Saola With Esp32S2 Onboard From Espressif"
bool "Saola With ESP32S2 Onboard From Espressif"
config BOARD_ESP32_LCDKIT
bool "Board ESP32-LCDKit_V1.1"
bool "ESP32-LCDKit_V1.1"
config BOARD_ESP32S3_USB_OTG_EV
bool "ESP32S3-USB-OTG-EV (ESP32S2 compatible)"
endchoice
menu "Devkitc-V4 Board Options"
@ -20,13 +22,6 @@ menu "Board Options"
#source "{absolute path}/esp32-devkitc-v4/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "Saola Board Options"
visible if BOARD_ESP32S2_SAOLA_1
orsource "./esp32s2-saola-1/Kconfig.in"
#source "{absolute path}/esp32s2-saola-1/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "Meshkit Sense Board Options"
visible if BOARD_ESP32_MESHKIT_SENSE
@ -34,4 +29,76 @@ menu "Board Options"
#source "{absolute path}/esp32-meshkit-sense/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "Saola Board Options"
visible if BOARD_ESP32S2_SAOLA_1
orsource "./esp32s2-saola-1/Kconfig.in"
#source "{absolute path}/esp32s2-saola-1/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "ESP32 LCDkit Board Options"
visible if BOARD_ESP32_LCDKIT
orsource "./esp32-lcdkit/Kconfig.in"
#source "{absolute path}/esp32-lcdkit/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "ESP32S3-USB-OTG-EV Board Options"
visible if BOARD_ESP32S3_USB_OTG_EV
orsource "./esp32s3_usb_otg_ev/Kconfig.in"
#source "{absolute path}/esp32s3_usb_otg_ev/Kconfig.in" # v4.0 Backwards compatibility
endmenu
menu "Board Wi-Fi Settings"
config BOARD_WIFI_SSID
string "WiFi STA SSID"
default ""
help
WiFi SSID (network name) to connect to or empty for Off.
config BOARD_WIFI_PASSWORD
string "WiFi STA Password"
default ""
help
WiFi Password if WEP/WPA/WPA2 or empty if Open.
config BOARD_WIFI_AP_SSID
string "WiFi AP SSID"
default "ESP32S3-OTG"
help
AP SSID (network name) to create or empty for Off.
config BOARD_WIFI_AP_PASSWORD
string "WiFi AP Password"
default ""
help
AP password for WPA2 or empty for Open.
config BOARD_MAX_STA_CONN
int "WiFi AP Maximal STA connections"
default 3
help
Max number of the STA connects to AP.
config BOARD_WIFI_AP_CHANNEL
int "WiFi AP Channel"
default 6
help
AP channel for better connection performance.
config BOARD_SERVER_IP
string "WiFi AP IP Address"
default "192.168.4.1"
help
IP address that the ESP will assign to it's AP interface. You can use this IP to connect to the camera after flashing.
config BOARD_MAXIMUM_RETRY
int "Maximum retry"
default 5
help
Set the Maximum retry to avoid station reconnecting to the AP unlimited when the AP is really inexistent.
endmenu
endmenu

View file

@ -13,20 +13,15 @@
// limitations under the License.
#include <stdio.h>
#include "board.h"
#include "esp_log.h"
#include "board_common.h"
#include "board.h"
static const char *TAG = "Board";
static const char *TAG = "BOARD_COMMON";
static bool s_board_is_init = false;
#define BOARD_CHECK(a, str, ret) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret); \
}
/****Private board level API ****/
static i2c_bus_handle_t s_i2c0_bus_handle = NULL;
static i2c_bus_handle_t s_spi2_bus_handle = NULL;
static spi_bus_handle_t s_spi2_bus_handle = NULL;
static esp_err_t board_i2c_bus_init(void)
{
@ -40,12 +35,10 @@ static esp_err_t board_i2c_bus_init(void)
.master.clk_speed = BOARD_I2C0_SPEED,
};
i2c_bus_handle_t handle0 = i2c_bus_create(I2C_NUM_0, &board_i2c0_conf);
BOARD_CHECK(handle0 != NULL, "i2c_bus0 creat failed", ESP_FAIL);
BOARD_CHECK(handle0 != NULL, "i2c_bus0 create failed", ESP_FAIL);
s_i2c0_bus_handle = handle0;
#else
s_i2c0_bus_handle = NULL;
ESP_LOGI(TAG, "i2c_bus 0 create succeed");
#endif
return ESP_OK;
}
@ -57,7 +50,6 @@ static esp_err_t board_i2c_bus_deinit(void)
return ESP_FAIL;
}
}
return ESP_OK;
}
@ -71,6 +63,7 @@ static esp_err_t board_spi_bus_init(void)
};
s_spi2_bus_handle = spi_bus_create(SPI2_HOST, &bus_conf);
BOARD_CHECK(s_spi2_bus_handle != NULL, "spi_bus2 creat failed", ESP_FAIL);
ESP_LOGI(TAG, "spi_bus 2 create succeed");
#endif
return ESP_OK;
}
@ -84,7 +77,7 @@ static esp_err_t board_spi_bus_deinit(void)
return ESP_OK;
}
esp_err_t iot_board_init(void)
ATTR_WEAK esp_err_t iot_board_init(void)
{
if(s_board_is_init) {
return ESP_OK;
@ -103,7 +96,7 @@ esp_err_t iot_board_init(void)
return ESP_OK;
}
esp_err_t iot_board_deinit(void)
ATTR_WEAK esp_err_t iot_board_deinit(void)
{
if(!s_board_is_init) {
return ESP_OK;
@ -121,12 +114,12 @@ esp_err_t iot_board_deinit(void)
return ESP_OK;
}
bool iot_board_is_init(void)
ATTR_WEAK bool iot_board_is_init(void)
{
return s_board_is_init;
}
board_res_handle_t iot_board_get_handle(board_res_id_t id)
ATTR_WEAK board_res_handle_t iot_board_get_handle(int id)
{
board_res_handle_t handle;
switch (id)
@ -144,7 +137,7 @@ board_res_handle_t iot_board_get_handle(board_res_id_t id)
return handle;
}
char* iot_board_get_info()
ATTR_WEAK char* iot_board_get_info()
{
static char* info = BOARD_NAME;
return info;

View file

@ -0,0 +1,105 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef _IOT_BOARDS_COMMON_H_
#define _IOT_BOARDS_COMMON_H_
#include "i2c_bus.h"
#include "spi_bus.h"
#define ATTR_WEAK __attribute__((weak))
#define BOARD_CHECK(a, str, ret) if(!(a)) { \
ESP_LOGE(TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
return (ret); \
}
#ifndef _ENABLE
#define _ENABLE 1
#endif
#ifndef _DISABLE
#define _DISABLE 0
#endif
#ifndef _UNDEFINE
#define _UNDEFINE
#endif
#ifndef _POSITIVE
#define _POSITIVE 1
#endif
#ifndef _NEGATIVE
#define _NEGATIVE 0
#endif
typedef void* board_res_handle_t;
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Board level init.
* Peripherals can be chosen through menuconfig, which will be initialized with default configurations during iot_board_init.
* After board init, initialized peripherals can be referenced by handles directly.
*
* @return esp_err_t
*/
esp_err_t iot_board_init(void);
/**
* @brief Board level deinit.
* After board deinit, initialized peripherals will be deinit and related handles will be set to NULL.
*
* @return esp_err_t
*/
esp_err_t iot_board_deinit(void);
/**
* @brief Check if board is initialized
*
* @return true if board is initialized
* @return false if board is not initialized
*/
bool iot_board_is_init(void);
/**
* @brief init esp32xx Wi-Fi with configs from menuconfig, deinit currentlly not supported
*
* @return esp_err_t
*/
esp_err_t iot_board_wifi_init(void);
/**
* @brief Using resource's ID declared in board_res_id_t to get board level resource's handle
*
* @param id Resource's ID declared in board_res_id_t
* @return board_res_handle_t Resource's handle
* if no related handle,NULL will be returned
*/
board_res_handle_t iot_board_get_handle(int id);
/**
* @brief Get board information
*
* @return String include BOARD_NAME etc.
*/
char* iot_board_get_info();
#ifdef __cplusplus
}
#endif
#endif

View file

@ -0,0 +1,194 @@
// Copyright 2020-2021 Espressif Systems (Shanghai) PTE LTD
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_wifi.h"
#include "esp_event.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "sdkconfig.h"
#include "lwip/err.h"
#include "lwip/sys.h"
#include "board_common.h"
/* The component use WiFi configuration that you can set via 'menuconfig' */
#define BOARD_WIFI_SSID CONFIG_BOARD_WIFI_SSID
#define BOARD_WIFI_PASS CONFIG_BOARD_WIFI_PASSWORD
#define BOARD_MAXIMUM_RETRY CONFIG_BOARD_MAXIMUM_RETRY
#define BOARD_WIFI_AP_SSID CONFIG_BOARD_WIFI_AP_SSID
#define BOARD_WIFI_AP_PASS CONFIG_BOARD_WIFI_AP_PASSWORD
#define BOARD_MAX_STA_CONN CONFIG_BOARD_MAX_STA_CONN
#define BOARD_IP_ADDR CONFIG_BOARD_SERVER_IP
#define BOARD_WIFI_AP_CHANNEL CONFIG_BOARD_WIFI_AP_CHANNEL
static const char *TAG = "BOARD_WIFI";
static int s_retry_num = 0;
static void wifi_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
if (event_base != WIFI_EVENT) {
return;
}
switch (event_id) {
case WIFI_EVENT_AP_STACONNECTED: {
wifi_event_ap_staconnected_t *event = (wifi_event_ap_staconnected_t *) event_data;
ESP_LOGI(TAG, "station "MACSTR" join, AID=%d",
MAC2STR(event->mac), event->aid);
break;
}
case WIFI_EVENT_AP_STADISCONNECTED: {
wifi_event_ap_stadisconnected_t *event = (wifi_event_ap_stadisconnected_t *) event_data;
ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d",
MAC2STR(event->mac), event->aid);
break;
}
case WIFI_EVENT_STA_START:
esp_wifi_connect();
break;
case WIFI_EVENT_STA_DISCONNECTED:
if (s_retry_num < BOARD_MAXIMUM_RETRY) {
esp_wifi_connect();
s_retry_num++;
ESP_LOGI(TAG, "retry to connect to the AP");
} else {
ESP_LOGI(TAG, "giveup retry");
}
ESP_LOGI(TAG, "connect to the AP fail");
break;
case IP_EVENT_STA_GOT_IP: {
ip_event_got_ip_t *event = (ip_event_got_ip_t *) event_data;
ESP_LOGI(TAG, "got ip:" IPSTR, IP2STR(&event->ip_info.ip));
s_retry_num = 0;
}
break;
default:
break;
}
}
static void wifi_init_softap(esp_netif_t *wifi_netif)
{
if (strcmp(BOARD_IP_ADDR, "192.168.4.1")) {
int a, b, c, d;
sscanf(BOARD_IP_ADDR, "%d.%d.%d.%d", &a, &b, &c, &d);
esp_netif_ip_info_t ip_info;
IP4_ADDR(&ip_info.ip, a, b, c, d);
IP4_ADDR(&ip_info.gw, a, b, c, d);
IP4_ADDR(&ip_info.netmask, 255, 255, 255, 0);
ESP_ERROR_CHECK(esp_netif_dhcps_stop(wifi_netif));
ESP_ERROR_CHECK(esp_netif_set_ip_info(wifi_netif, &ip_info));
ESP_ERROR_CHECK(esp_netif_dhcps_start(wifi_netif));
}
wifi_config_t wifi_config;
memset(&wifi_config, 0, sizeof(wifi_config_t));
snprintf((char *)wifi_config.ap.ssid, 32, "%s", BOARD_WIFI_AP_SSID);
wifi_config.ap.ssid_len = strlen((char *)wifi_config.ap.ssid);
snprintf((char *)wifi_config.ap.password, 64, "%s", BOARD_WIFI_AP_PASS);
wifi_config.ap.max_connection = BOARD_MAX_STA_CONN;
wifi_config.ap.authmode = WIFI_AUTH_WPA_WPA2_PSK;
wifi_config.ap.channel = BOARD_WIFI_AP_CHANNEL;
if (strlen(BOARD_WIFI_AP_PASS) == 0) {
wifi_config.ap.authmode = WIFI_AUTH_OPEN;
}
ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &wifi_config));
ESP_LOGI(TAG, "wifi_init_softap finished. SSID:%s password:%s channel:%d",
BOARD_WIFI_AP_SSID, BOARD_WIFI_AP_PASS, BOARD_WIFI_AP_CHANNEL);
}
static void wifi_init_sta(esp_netif_t *wifi_netif)
{
(void) wifi_netif;
wifi_config_t wifi_config;
memset(&wifi_config, 0, sizeof(wifi_config_t));
snprintf((char *)wifi_config.sta.ssid, 32, "%s", BOARD_WIFI_SSID);
snprintf((char *)wifi_config.sta.password, 64, "%s", BOARD_WIFI_PASS);
ESP_ERROR_CHECK(esp_wifi_set_config(ESP_IF_WIFI_STA, &wifi_config));
ESP_LOGI(TAG, "wifi_init_sta finished.");
ESP_LOGI(TAG, "connect to ap SSID:%s password:%s",
BOARD_WIFI_SSID, BOARD_WIFI_PASS);
}
ATTR_WEAK esp_err_t iot_board_wifi_init()
{
wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT();
wifi_mode_t mode = WIFI_MODE_NULL;
esp_netif_t *wifi_netif = NULL;
if (strlen(BOARD_WIFI_AP_SSID) && strlen(BOARD_WIFI_SSID)) {
mode = WIFI_MODE_APSTA;
} else if (strlen(BOARD_WIFI_AP_SSID)) {
mode = WIFI_MODE_AP;
} else if (strlen(BOARD_WIFI_SSID)) {
mode = WIFI_MODE_STA;
}
esp_err_t ret = nvs_flash_init();
if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) {
ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init();
}
ESP_ERROR_CHECK(ret);
if (mode == WIFI_MODE_NULL) {
ESP_LOGW(TAG, "Neither AP or STA have been configured. WiFi will be off.");
return ESP_ERR_NVS_INVALID_STATE;
}
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());
ESP_ERROR_CHECK(esp_wifi_init(&cfg));
ESP_ERROR_CHECK(esp_event_handler_instance_register(WIFI_EVENT,
ESP_EVENT_ANY_ID,
&wifi_event_handler,
NULL,
NULL));
ESP_ERROR_CHECK(esp_wifi_set_mode(mode));
if (mode & WIFI_MODE_AP) {
wifi_netif = esp_netif_create_default_wifi_ap();
wifi_init_softap(wifi_netif);
}
if (mode & WIFI_MODE_STA) {
wifi_netif = esp_netif_create_default_wifi_sta();
wifi_init_sta(wifi_netif);
}
ESP_ERROR_CHECK(esp_wifi_start());
ESP_ERROR_CHECK(esp_wifi_set_ps(WIFI_PS_NONE));
return ESP_OK;
}

View file

@ -15,13 +15,7 @@
#define _IOT_BOARD_H_
#include "esp_err.h"
#include "i2c_bus.h"
#include "spi_bus.h"
/*ENABLE Initialization Process in iot_board_init(void)*/
#define _ENABLE 1
#define _DISABLE 0
#define _UNDEFINE
#include "board_common.h"
/**
* Resource ID on Board,
@ -33,8 +27,6 @@ typedef enum {
BOARD_SPI2_ID,
}board_res_id_t;
typedef void* board_res_handle_t;
/*Definations of Board*/
#define BOARD_NAME "ESP32-Devkitc"
#define BOARD_VENDOR "Espressif"
@ -58,60 +50,28 @@ typedef void* board_res_handle_t;
#define BOARD_IO_SPI2_MOSI 23
#define BOARD_IO_SPI2_MISO 19
/* Free pins */
#define BOARD_IO_FREE_2 2
#define BOARD_IO_FREE_4 4
#define BOARD_IO_FREE_5 5
#define BOARD_IO_FREE_12 12
#define BOARD_IO_FREE_13 13
#define BOARD_IO_FREE_14 14
#define BOARD_IO_FREE_15 15
#define BOARD_IO_FREE_16 16
#define BOARD_IO_FREE_17 17
#define BOARD_IO_FREE_25 25
#define BOARD_IO_FREE_26 26
#define BOARD_IO_FREE_27 27
#define BOARD_IO_FREE_32 32
#define BOARD_IO_FREE_33 33
#define BOARD_IO_FREE_34 34
#define BOARD_IO_FREE_35 35
/*Definations of Peripheral*/
#define BOARD_I2C0_MODE I2C_MODE_MASTER
#define BOARD_I2C0_SPEED (100000)
#define BOARD_I2C0_SCL_PULLUP_EN _ENABLE
#define BOARD_I2C0_SDA_PULLUP_EN _ENABLE
#ifdef __cplusplus
extern "C"
{
#endif
/**
* @brief Board level init.
* Peripherals can be chosen through menuconfig, which will be initialized with default configurations during iot_board_init.
* After board init, initialized peripherals can be referenced by handles directly.
*
* @return esp_err_t
*/
esp_err_t iot_board_init(void);
/**
* @brief Board level deinit.
* After board deinit, initialized peripherals will be deinit and related handles will be set to NULL.
*
* @return esp_err_t
*/
esp_err_t iot_board_deinit(void);
/**
* @brief Check if board is initialized
*
* @return true if board is initialized
* @return false if board is not initialized
*/
bool iot_board_is_init(void);
/**
* @brief Using resource's ID declared in board_res_id_t to get board level resource's handle
*
* @param id Resource's ID declared in board_res_id_t
* @return board_res_handle_t Resource's handle
* if no related handle,NULL will be returned
*/
board_res_handle_t iot_board_get_handle(board_res_id_t id);
/**
* @brief Get board information
*
* @return String include BOARD_NAME etc.
*/
char* iot_board_get_info();
#ifdef __cplusplus
}
#endif
#endif /* _IOT_BOARD_H_ */

View file

@ -1,7 +1,18 @@
menu "esp32-lcdkit board configuration"
menu "Board Init Options"
config BOARD_I2C0_INIT
bool "init i2c0 during board init"
default n
help
if enable, i2c0 will be initialized with default configuration during board init,
then, you can use iot_board_get_handle to get the bus handle.
config BOARD_SPI2_INIT
bool "init spi2 during board init"
default y
help
if yes, spi2 will be initialized with default configuration during board init,
then, you can use iot_board_get_handle to get the bus handle.
endmenu
endmenu

Some files were not shown because too many files have changed in this diff Show more