initial commmit
This commit is contained in:
@@ -0,0 +1 @@
|
||||
eabf03fe3cb1bdb5325d2725f420c6324c3e150033d893ce3737222929455a55
|
||||
21
managed_components/espressif__usb_host_hid/CHANGELOG.md
Normal file
21
managed_components/espressif__usb_host_hid/CHANGELOG.md
Normal file
@@ -0,0 +1,21 @@
|
||||
## 1.0.3
|
||||
- Fixed a bug with interface mismatch on EP IN transfer complete while several HID devices are present.
|
||||
- Fixed a bug during device freeing, while detaching one of several attached HID devices.
|
||||
|
||||
## 1.0.2
|
||||
|
||||
- Added support for ESP32-P4
|
||||
- Fixed device open procedure for HID devices with multiple non-sequential interfaces.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Fixed a bug where configuring the driver with `create_background_task = false` did not properly initialize the driver. This lead to `the hid_host_uninstall()` hang-up.
|
||||
- Fixed a bug where `hid_host_uninstall()` would cause a crash during the call while USB device has not been removed.
|
||||
- Added `hid_host_get_device_info()` to get the basic information of a connected USB HID device.
|
||||
|
||||
## 1.0.0
|
||||
|
||||
- Initial version
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
idf_component_register( SRCS "hid_host.c"
|
||||
INCLUDE_DIRS "include"
|
||||
PRIV_REQUIRES usb )
|
||||
202
managed_components/espressif__usb_host_hid/LICENSE
Normal file
202
managed_components/espressif__usb_host_hid/LICENSE
Normal file
@@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
52
managed_components/espressif__usb_host_hid/README.md
Normal file
52
managed_components/espressif__usb_host_hid/README.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# USB Host HID (Human Interface Device) Driver
|
||||
|
||||
[](https://components.espressif.com/components/espressif/usb_host_hid)
|
||||
|
||||
This directory contains an implementation of a USB HID Driver implemented on top of the [USB Host Library](https://docs.espressif.com/projects/esp-idf/en/latest/esp32s2/api-reference/peripherals/usb_host.html).
|
||||
|
||||
HID driver allows access to HID devices.
|
||||
|
||||
## Usage
|
||||
|
||||
The following steps outline the typical API call pattern of the HID Class Driver:
|
||||
|
||||
1. Install the USB Host Library via 'usb_host_install()'
|
||||
2. Install the HID driver via 'hid_host_install()'
|
||||
3. The HID Host driver device callback provide the following events (via two callbacks):
|
||||
- HID_HOST_DRIVER_EVENT_CONNECTED
|
||||
- HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
- HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR
|
||||
- HID_HOST_INTERFACE_EVENT_DISCONNECTED
|
||||
|
||||
4. Specific HID device can be opened or closed with:
|
||||
- 'hid_host_device_open()'
|
||||
- 'hid_host_device_close()'
|
||||
5. To enable / disable data receiving in case of event (keyboard key was pressed or mouse device was moved e.t.c) use:
|
||||
- 'hid_host_device_start()'
|
||||
- 'hid_host_device_stop()'
|
||||
6. HID Class specific device requests:
|
||||
- 'hid_host_interface_get_report_descriptor()'
|
||||
- 'hid_class_request_get_report()'
|
||||
- 'hid_class_request_get_idle()'
|
||||
- 'hid_class_request_get_protocol()'
|
||||
- 'hid_class_request_set_report()'
|
||||
- 'hid_class_request_set_idle()'
|
||||
- 'hid_class_request_set_protocol()'
|
||||
7. When HID device event occurs the driver call an interface callback with events:
|
||||
- HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
- HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR
|
||||
- HID_HOST_INTERFACE_EVENT_DISCONNECTED
|
||||
8. The HID driver can be uninstalled via 'hid_host_uninstall()'
|
||||
|
||||
## Known issues
|
||||
|
||||
- Empty
|
||||
|
||||
## Examples
|
||||
|
||||
- For an example, refer to [hid_host_example](https://github.com/espressif/esp-idf/tree/master/examples/peripherals/usb/host/hid)
|
||||
|
||||
## Supported Devices
|
||||
|
||||
- HID Driver support any HID compatible device with a USB bIterfaceClass 0x03 (Human Interface Device).
|
||||
- There are two options to handle HID device input data: either in RAW format or via special event handlers (which are available only for HID Devices which support Boot Protocol).
|
||||
1527
managed_components/espressif__usb_host_hid/hid_host.c
Normal file
1527
managed_components/espressif__usb_host_hid/hid_host.c
Normal file
File diff suppressed because it is too large
Load Diff
13
managed_components/espressif__usb_host_hid/idf_component.yml
Normal file
13
managed_components/espressif__usb_host_hid/idf_component.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
dependencies:
|
||||
idf: '>=4.4'
|
||||
description: USB Host HID driver
|
||||
repository: git://github.com/espressif/esp-usb.git
|
||||
repository_info:
|
||||
commit_sha: 39b0de39a90c9e391db26f06d54e4b10f58be231
|
||||
path: host/class/hid/usb_host_hid
|
||||
targets:
|
||||
- esp32s2
|
||||
- esp32s3
|
||||
- esp32p4
|
||||
url: https://github.com/espressif/esp-usb/tree/master/host/class/hid/usb_host_hid
|
||||
version: 1.0.3
|
||||
147
managed_components/espressif__usb_host_hid/include/usb/hid.h
Normal file
147
managed_components/espressif__usb_host_hid/include/usb/hid.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HID Subclass
|
||||
*
|
||||
* @see 4.2 Subclass, p.8 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_SUBCLASS_NO_SUBCLASS = 0x00,
|
||||
HID_SUBCLASS_BOOT_INTERFACE = 0x01
|
||||
} __attribute__((packed)) hid_subclass_t;
|
||||
|
||||
/**
|
||||
* @brief HID Protocols
|
||||
*
|
||||
* @see 4.3 Protocols, p.9 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_PROTOCOL_NONE = 0x00,
|
||||
HID_PROTOCOL_KEYBOARD = 0x01,
|
||||
HID_PROTOCOL_MOUSE = 0x02,
|
||||
HID_PROTOCOL_MAX
|
||||
} __attribute__((packed)) hid_protocol_t;
|
||||
|
||||
/**
|
||||
* @brief HID Descriptor
|
||||
*
|
||||
* @see 6.2.1 HID Descriptor, p.22 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t bLength; // Numeric expression that is the total size of the HID descriptor
|
||||
uint8_t bDescriptorType; // Constant name specifying type of HID descriptor
|
||||
uint16_t bcdHID; // Numeric expression identifying the HIDClass Specification release
|
||||
uint8_t bCountryCode; // Numeric expression identifying country code of the localized hardware
|
||||
uint8_t bNumDescriptors; // Numeric expression specifying the number of class descriptors (always at least one i.e. Report descriptor.)
|
||||
uint8_t bReportDescriptorType; // Constant name identifying type of class descriptor. See Section 7.1.2: Set_Descriptor Request for a table of class descriptor constants
|
||||
uint16_t wReportDescriptorLength; // Numeric expression that is the total size of the Report descriptor
|
||||
// Optional descriptors may follow further
|
||||
} __attribute__((packed)) hid_descriptor_t;
|
||||
|
||||
/**
|
||||
* @brief HID Country Codes
|
||||
*
|
||||
* @see 6.2.1 HID Descriptor, p.23 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
HID_COUNTRY_CODE_NOT_SUPPORTED = 0x00,
|
||||
HID_COUNTRY_CODE_ARABIC = 0x01,
|
||||
HID_COUNTRY_CODE_BELGIAN = 0x02,
|
||||
HID_COUNTRY_CODE_CANADIAN_BILINGUAL = 0x03,
|
||||
HID_COUNTRY_CODE_CANADIAN_FRENCH = 0x04,
|
||||
HID_COUNTRY_CODE_CZECH = 0x05,
|
||||
HID_COUNTRY_CODE_DANISH = 0x06,
|
||||
HID_COUNTRY_CODE_FINNISH = 0x07,
|
||||
HID_COUNTRY_CODE_FRENCH = 0x08,
|
||||
HID_COUNTRY_CODE_GERMAN = 0x09,
|
||||
HID_COUNTRY_CODE_GREEK = 0x0A,
|
||||
HID_COUNTRY_CODE_HEBREW = 0x0B,
|
||||
HID_COUNTRY_CODE_HUNGARY = 0x0C,
|
||||
HID_COUNTRY_CODE_ISO = 0x0D,
|
||||
HID_COUNTRY_CODE_ITALIAN = 0x0E,
|
||||
HID_COUNTRY_CODE_JAPAN = 0x0F,
|
||||
HID_COUNTRY_CODE_KOREAN = 0x10,
|
||||
HID_COUNTRY_CODE_LATIN_AMERICAN = 0x11,
|
||||
HID_COUNTRY_CODE_NETHERLANDS = 0x12,
|
||||
HID_COUNTRY_CODE_NORWEGIAN = 0x13,
|
||||
HID_COUNTRY_CODE_PERSIAN = 0x14,
|
||||
HID_COUNTRY_CODE_POLAND = 0x15,
|
||||
HID_COUNTRY_CODE_PORTUGUESE = 0x16,
|
||||
HID_COUNTRY_CODE_RUSSIA = 0x17,
|
||||
HID_COUNTRY_CODE_SLOVAKIA = 0x18,
|
||||
HID_COUNTRY_CODE_SPANISH = 0x19,
|
||||
HID_COUNTRY_CODE_SWEDISH = 0x1A,
|
||||
HID_COUNTRY_CODE_SWISS_F = 0x1B,
|
||||
HID_COUNTRY_CODE_SWISS_G = 0x1C,
|
||||
HID_COUNTRY_CODE_SWITZERLAND = 0x1D,
|
||||
HID_COUNTRY_CODE_TAIWAN = 0x1E,
|
||||
HID_COUNTRY_CODE_TURKISH_Q = 0x1F,
|
||||
HID_COUNTRY_CODE_UK = 0x20,
|
||||
HID_COUNTRY_CODE_US = 0x21,
|
||||
HID_COUNTRY_CODE_YUGOSLAVIA = 0x22,
|
||||
HID_COUNTRY_CODE_TURKISH_F = 0x23
|
||||
} __attribute__((packed)) hid_country_code_t;
|
||||
|
||||
/**
|
||||
* @brief HID Class Descriptor Types
|
||||
*
|
||||
* @see 7.1, p.49 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_CLASS_DESCRIPTOR_TYPE_HID = 0x21,
|
||||
HID_CLASS_DESCRIPTOR_TYPE_REPORT = 0x22,
|
||||
HID_CLASS_DESCRIPTOR_TYPE_PHYSICAL = 0x23
|
||||
} __attribute__((packed)) hid_class_descritpor_type_t;
|
||||
|
||||
/**
|
||||
* @brief HID Class-Specific Requests
|
||||
*
|
||||
* @see 7.2, p.50 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_CLASS_SPECIFIC_REQ_GET_REPORT = 0x01,
|
||||
HID_CLASS_SPECIFIC_REQ_GET_IDLE = 0x02,
|
||||
HID_CLASS_SPECIFIC_REQ_GET_PROTOCOL = 0x03,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_REPORT = 0x09,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_IDLE = 0x0A,
|
||||
HID_CLASS_SPECIFIC_REQ_SET_PROTOCOL = 0x0B
|
||||
} __attribute__((packed)) hid_class_specific_req_t;
|
||||
|
||||
/**
|
||||
* @brief HID Report Types
|
||||
*
|
||||
* @see 7.2.1, p.51 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_REPORT_TYPE_INPUT = 0x01,
|
||||
HID_REPORT_TYPE_OUTPUT = 0x02,
|
||||
HID_REPORT_TYPE_FEATURE = 0x03,
|
||||
} __attribute__((packed)) hid_report_type_t;
|
||||
|
||||
/**
|
||||
* @brief HID Report protocol
|
||||
*
|
||||
* @see 7.2.5/7.2.6, p.54 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_REPORT_PROTOCOL_BOOT = 0x00,
|
||||
HID_REPORT_PROTOCOL_REPORT = 0x01,
|
||||
HID_REPORT_PROTOCOL_MAX
|
||||
} __attribute__((packed)) hid_report_protocol_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,313 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wchar.h>
|
||||
#include <stdint.h>
|
||||
#include "esp_err.h"
|
||||
#include <freertos/FreeRTOS.h>
|
||||
|
||||
#include "hid.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief USB HID HOST string descriptor maximal length
|
||||
*
|
||||
* The maximum possible number of characters in an embedded string is device specific.
|
||||
* For USB devices, the maximum string length is 126 wide characters (not including the terminating NULL character).
|
||||
* This is a length, which is available to upper level application during getting information
|
||||
* of HID Device with 'hid_host_get_device_info' call.
|
||||
*
|
||||
* To decrease memory usage 32 wide characters (64 bytes per every string) is used.
|
||||
*/
|
||||
#define HID_STR_DESC_MAX_LENGTH 32
|
||||
|
||||
typedef struct hid_interface *hid_host_device_handle_t; /**< Device Handle. Handle to a particular HID interface */
|
||||
|
||||
// ------------------------ USB HID Host events --------------------------------
|
||||
/**
|
||||
* @brief USB HID HOST Device event id
|
||||
*/
|
||||
typedef enum {
|
||||
HID_HOST_DRIVER_EVENT_CONNECTED = 0x00, /**< HID Device has been found in connected USB device (at least one) */
|
||||
} hid_host_driver_event_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID HOST Interface event id
|
||||
*/
|
||||
typedef enum {
|
||||
HID_HOST_INTERFACE_EVENT_INPUT_REPORT = 0x00, /**< HID Device input report */
|
||||
HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR, /**< HID Device transfer error */
|
||||
HID_HOST_INTERFACE_EVENT_DISCONNECTED, /**< HID Device has been disconnected */
|
||||
} hid_host_interface_event_t;
|
||||
|
||||
/**
|
||||
* @brief HID device descriptor common data.
|
||||
*/
|
||||
typedef struct {
|
||||
uint16_t VID;
|
||||
uint16_t PID;
|
||||
wchar_t iManufacturer[HID_STR_DESC_MAX_LENGTH];
|
||||
wchar_t iProduct[HID_STR_DESC_MAX_LENGTH];
|
||||
wchar_t iSerialNumber[HID_STR_DESC_MAX_LENGTH];
|
||||
} hid_host_dev_info_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID Host device parameters
|
||||
*/
|
||||
typedef struct {
|
||||
uint8_t addr; /**< USB Address of connected HID device */
|
||||
uint8_t iface_num; /**< HID Interface Number */
|
||||
uint8_t sub_class; /**< HID Interface SubClass */
|
||||
uint8_t proto; /**< HID Interface Protocol */
|
||||
} hid_host_dev_params_t;
|
||||
|
||||
// ------------------------ USB HID Host callbacks -----------------------------
|
||||
|
||||
/**
|
||||
* @brief USB HID driver event callback.
|
||||
*
|
||||
* @param[in] hid_handle HID device handle (HID Interface)
|
||||
* @param[in] event HID driver event
|
||||
* @param[in] arg User argument from HID driver configuration structure
|
||||
*/
|
||||
typedef void (*hid_host_driver_event_cb_t)(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg);
|
||||
|
||||
/**
|
||||
* @brief USB HID Interface event callback.
|
||||
*
|
||||
* @param[in] hid_device_handle HID device handle (HID Interface)
|
||||
* @param[in] event HID Interface event
|
||||
* @param[in] arg User argument
|
||||
*/
|
||||
typedef void (*hid_host_interface_event_cb_t)(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg);
|
||||
|
||||
// ----------------------------- Public ---------------------------------------
|
||||
/**
|
||||
* @brief HID configuration structure.
|
||||
*/
|
||||
typedef struct {
|
||||
bool create_background_task; /**< When set to true, background task handling USB events is created.
|
||||
Otherwise user has to periodically call hid_host_handle_events function */
|
||||
size_t task_priority; /**< Task priority of created background task */
|
||||
size_t stack_size; /**< Stack size of created background task */
|
||||
BaseType_t core_id; /**< Select core on which background task will run or tskNO_AFFINITY */
|
||||
hid_host_driver_event_cb_t callback; /**< Callback invoked when HID driver event occurs. Must not be NULL. */
|
||||
void *callback_arg; /**< User provided argument passed to callback */
|
||||
} hid_host_driver_config_t;
|
||||
|
||||
/**
|
||||
* @brief HID device configuration structure (HID Interface)
|
||||
*/
|
||||
typedef struct {
|
||||
hid_host_interface_event_cb_t callback; /**< Callback invoked when HID Interface event occurs */
|
||||
void *callback_arg; /**< User provided argument passed to callback */
|
||||
} hid_host_device_config_t;
|
||||
|
||||
/**
|
||||
* @brief USB HID Host install USB Host HID Class driver
|
||||
*
|
||||
* @param[in] config configuration structure HID to create
|
||||
* @return esp_err_r
|
||||
*/
|
||||
esp_err_t hid_host_install(const hid_host_driver_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host uninstall HID Class driver
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_uninstall(void);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host open a device with specific device parameters
|
||||
*
|
||||
* @param[in] iface_handle Handle of the HID device to open
|
||||
* @param[in] config Configuration structure HID device to open
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_open(hid_host_device_handle_t hid_dev_handle,
|
||||
const hid_host_device_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief USB HID Host close device
|
||||
*
|
||||
* @param[in] hid_dev_handle Handle of the HID device to close
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_close(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host USB event handler
|
||||
*
|
||||
* If HID Host install was made with create_background_task=false configuration,
|
||||
* application needs to handle USB Host events itself.
|
||||
* Do not used if HID host install was made with create_background_task=true configuration
|
||||
*
|
||||
* @param[in] timeout Timeout in ticks. For milliseconds, please use 'pdMS_TO_TICKS()' macros
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_handle_events(uint32_t timeout);
|
||||
|
||||
/**
|
||||
* @brief HID Device get parameters by handle.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] dev_params Pointer to a dev_params struct to fill
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_get_params(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_host_dev_params_t *dev_params);
|
||||
/**
|
||||
* @brief HID Host get device raw input report data pointer by handle
|
||||
*
|
||||
* This functions should be called after HID Interface device event HID_HOST_INTERFACE_EVENT_INPUT_REPORT
|
||||
* to get the actual raw data of input report.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] data Pointer to buffer where the input data will be copied
|
||||
* @param[in] data_length_max Max length of data can be copied to data buffer
|
||||
* @param[out] data_length Length of input report
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_get_raw_input_report_data(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t *data,
|
||||
size_t data_length_max,
|
||||
size_t *data_length);
|
||||
|
||||
// ------------------------ USB HID Host driver API ----------------------------
|
||||
|
||||
/**
|
||||
* @brief HID Host start awaiting event from a device by handle
|
||||
*
|
||||
* Calls a callback when the HID Interface event has occurred.
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_start(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host stop device
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_host_device_stop(hid_host_device_handle_t hid_dev_handle);
|
||||
|
||||
/**
|
||||
* @brief HID Host Get Report Descriptor
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] report_desc_len Length of report descriptor
|
||||
*
|
||||
* @return a uint8_t pointer to report descriptor data
|
||||
*/
|
||||
uint8_t *hid_host_get_report_descriptor(hid_host_device_handle_t hid_dev_handle,
|
||||
size_t *report_desc_len);
|
||||
|
||||
|
||||
/**
|
||||
* @brief HID Host Get device information
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
*/
|
||||
esp_err_t hid_host_get_device_info(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_host_dev_info_t *hid_dev_info);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET REPORT
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_id Report ID
|
||||
* @param[out] report Pointer to buffer for a report data
|
||||
* @param[in/out] report_length Report data length, before the get report contain the maximum value of a report buffer.
|
||||
* After get report there is a value of actual data in report buffer.
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_report(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_type,
|
||||
uint8_t report_id,
|
||||
uint8_t *report,
|
||||
size_t *report_length);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET IDLE
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_id ReportID
|
||||
* @param[out] idle_rate Idle rate [ms]
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_idle(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_id,
|
||||
uint8_t *idle_rate);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request GET PROTOCOL
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[out] protocol Pointer to HID report protocol (boot or report) of device
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_get_protocol(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_report_protocol_t *protocol);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET REPORT
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] report_type Report type
|
||||
* @param[in] report_id Report ID
|
||||
* @param[in] report Pointer to a buffer with report data
|
||||
* @param[in] report_length Report data length
|
||||
*
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_report(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t report_type,
|
||||
uint8_t report_id,
|
||||
uint8_t *report,
|
||||
size_t report_length);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET IDLE
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] duration 0 (zero) for the indefinite duration, non-zero, then a fixed duration used.
|
||||
* @param[in] report_id If 0 (zero) the idle rate applies to all input reports generated by the device, otherwise ReportID
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_idle(hid_host_device_handle_t hid_dev_handle,
|
||||
uint8_t duration,
|
||||
uint8_t report_id);
|
||||
|
||||
/**
|
||||
* @brief HID class specific request SET PROTOCOL
|
||||
*
|
||||
* @param[in] hid_dev_handle HID Device handle
|
||||
* @param[in] protocol HID report protocol (boot or report)
|
||||
* @return esp_err_t
|
||||
*/
|
||||
esp_err_t hid_class_request_set_protocol(hid_host_device_handle_t hid_dev_handle,
|
||||
hid_report_protocol_t protocol);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,292 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
//------------------------------------------ HID usage keys ---------------------------------------------------------------
|
||||
/**
|
||||
* @brief HID Keys
|
||||
*
|
||||
*/
|
||||
typedef enum {
|
||||
HID_KEY_NO_PRESS = 0x00,
|
||||
HID_KEY_ROLLOVER = 0x01,
|
||||
HID_KEY_POST_FAIL = 0x02,
|
||||
HID_KEY_ERROR_UNDEFINED = 0x03,
|
||||
HID_KEY_A = 0x04,
|
||||
HID_KEY_B = 0x05,
|
||||
HID_KEY_C = 0x06,
|
||||
HID_KEY_D = 0x07,
|
||||
HID_KEY_E = 0x08,
|
||||
HID_KEY_F = 0x09,
|
||||
HID_KEY_G = 0x0A,
|
||||
HID_KEY_H = 0x0B,
|
||||
HID_KEY_I = 0x0C,
|
||||
HID_KEY_J = 0x0D,
|
||||
HID_KEY_K = 0x0E,
|
||||
HID_KEY_L = 0x0F,
|
||||
HID_KEY_M = 0x10,
|
||||
HID_KEY_N = 0x11,
|
||||
HID_KEY_O = 0x12,
|
||||
HID_KEY_P = 0x13,
|
||||
HID_KEY_Q = 0x14,
|
||||
HID_KEY_R = 0x15,
|
||||
HID_KEY_S = 0x16,
|
||||
HID_KEY_T = 0x17,
|
||||
HID_KEY_U = 0x18,
|
||||
HID_KEY_V = 0x19,
|
||||
HID_KEY_W = 0x1A,
|
||||
HID_KEY_X = 0x1B,
|
||||
HID_KEY_Y = 0x1C,
|
||||
HID_KEY_Z = 0x1D,
|
||||
HID_KEY_1 = 0x1E,
|
||||
HID_KEY_2 = 0x1F,
|
||||
HID_KEY_3 = 0x20,
|
||||
HID_KEY_4 = 0x21,
|
||||
HID_KEY_5 = 0x22,
|
||||
HID_KEY_6 = 0x23,
|
||||
HID_KEY_7 = 0x24,
|
||||
HID_KEY_8 = 0x25,
|
||||
HID_KEY_9 = 0x26,
|
||||
HID_KEY_0 = 0x27,
|
||||
HID_KEY_ENTER = 0x28,
|
||||
HID_KEY_ESC = 0x29,
|
||||
HID_KEY_DEL = 0x2A,
|
||||
HID_KEY_TAB = 0x2B,
|
||||
HID_KEY_SPACE = 0x2C,
|
||||
HID_KEY_MINUS = 0x2D,
|
||||
HID_KEY_EQUAL = 0x2E,
|
||||
HID_KEY_OPEN_BRACKET = 0x2F,
|
||||
HID_KEY_CLOSE_BRACKET = 0x30,
|
||||
HID_KEY_BACK_SLASH = 0x31,
|
||||
HID_KEY_SHARP = 0x32,
|
||||
HID_KEY_COLON = 0x33,
|
||||
HID_KEY_QUOTE = 0x34,
|
||||
HID_KEY_TILDE = 0x35,
|
||||
HID_KEY_LESS = 0x36,
|
||||
HID_KEY_GREATER = 0x37,
|
||||
HID_KEY_SLASH = 0x38,
|
||||
HID_KEY_CAPS_LOCK = 0x39,
|
||||
HID_KEY_F1 = 0x3A,
|
||||
HID_KEY_F2 = 0x3B,
|
||||
HID_KEY_F3 = 0x3C,
|
||||
HID_KEY_F4 = 0x3D,
|
||||
HID_KEY_F5 = 0x3E,
|
||||
HID_KEY_F6 = 0x3F,
|
||||
HID_KEY_F7 = 0x40,
|
||||
HID_KEY_F8 = 0x41,
|
||||
HID_KEY_F9 = 0x42,
|
||||
HID_KEY_F10 = 0x43,
|
||||
HID_KEY_F11 = 0x44,
|
||||
HID_KEY_F12 = 0x45,
|
||||
HID_KEY_PRINT_SCREEN = 0x46,
|
||||
HID_KEY_SCROLL_LOCK = 0x47,
|
||||
HID_KEY_PAUSE = 0x48,
|
||||
HID_KEY_INSERT = 0x49,
|
||||
HID_KEY_HOME = 0x4A,
|
||||
HID_KEY_PAGEUP = 0x4B,
|
||||
HID_KEY_DELETE = 0x4C,
|
||||
HID_KEY_END = 0x4D,
|
||||
HID_KEY_PAGEDOWN = 0x4E,
|
||||
HID_KEY_RIGHT = 0x4F,
|
||||
HID_KEY_LEFT = 0x50,
|
||||
HID_KEY_DOWN = 0x51,
|
||||
HID_KEY_UP = 0x52,
|
||||
HID_KEY_NUM_LOCK = 0x53,
|
||||
HID_KEY_KEYPAD_DIV = 0x54,
|
||||
HID_KEY_KEYPAD_MUL = 0x55,
|
||||
HID_KEY_KEYPAD_SUB = 0x56,
|
||||
HID_KEY_KEYPAD_ADD = 0x57,
|
||||
HID_KEY_KEYPAD_ENTER = 0x58,
|
||||
HID_KEY_KEYPAD_1 = 0x59,
|
||||
HID_KEY_KEYPAD_2 = 0x5A,
|
||||
HID_KEY_KEYPAD_3 = 0x5B,
|
||||
HID_KEY_KEYPAD_4 = 0x5C,
|
||||
HID_KEY_KEYPAD_5 = 0x5D,
|
||||
HID_KEY_KEYPAD_6 = 0x5E,
|
||||
HID_KEY_KEYPAD_7 = 0x5F,
|
||||
HID_KEY_KEYPAD_8 = 0x60,
|
||||
HID_KEY_KEYPAD_9 = 0x61,
|
||||
HID_KEY_KEYPAD_0 = 0x62,
|
||||
HID_KEY_KEYPAD_DELETE = 0x63,
|
||||
HID_KEY_KEYPAD_SLASH = 0x64,
|
||||
HID_KEY_APPLICATION = 0x65,
|
||||
HID_KEY_POWER = 0x66,
|
||||
HID_KEY_KEYPAD_EQUAL = 0x67,
|
||||
HID_KEY_F13 = 0x68,
|
||||
HID_KEY_F14 = 0x69,
|
||||
HID_KEY_F15 = 0x6A,
|
||||
HID_KEY_F16 = 0x6B,
|
||||
HID_KEY_F17 = 0x6C,
|
||||
HID_KEY_F18 = 0x6D,
|
||||
HID_KEY_F19 = 0x6E,
|
||||
HID_KEY_F20 = 0x6F,
|
||||
HID_KEY_F21 = 0x70,
|
||||
HID_KEY_F22 = 0x71,
|
||||
HID_KEY_F23 = 0x72,
|
||||
HID_KEY_F24 = 0x73,
|
||||
HID_KEY_EXECUTE = 0x74,
|
||||
HID_KEY_HELP = 0x75,
|
||||
HID_KEY_MENU = 0x76,
|
||||
HID_KEY_SELECT = 0x77,
|
||||
HID_KEY_STOP = 0x78,
|
||||
HID_KEY_AGAIN = 0x79,
|
||||
HID_KEY_UNDO = 0x7A,
|
||||
HID_KEY_CUT = 0x7B,
|
||||
HID_KEY_COPY = 0x7C,
|
||||
HID_KEY_PASTE = 0x7D,
|
||||
HID_KEY_FIND = 0x7E,
|
||||
HID_KEY_MUTE = 0x7F,
|
||||
HID_KEY_VOLUME_UP = 0x80,
|
||||
HID_KEY_VOLUME_DOWN = 0x81,
|
||||
HID_KEY_LOCKING_CAPS_LOCK = 0x82,
|
||||
HID_KEY_LOCKING_NUM_LOCK = 0x83,
|
||||
HID_KEY_LOCKING_SCROLL_LOCK = 0x84,
|
||||
HID_KEY_KEYPAD_COMMA = 0x85,
|
||||
HID_KEY_KEYPAD_EQUAL_SIGN = 0x86,
|
||||
HID_KEY_INTERNATIONAL_1 = 0x87,
|
||||
HID_KEY_INTERNATIONAL_2 = 0x88,
|
||||
HID_KEY_INTERNATIONAL_3 = 0x89,
|
||||
HID_KEY_INTERNATIONAL_4 = 0x8A,
|
||||
HID_KEY_INTERNATIONAL_5 = 0x8B,
|
||||
HID_KEY_INTERNATIONAL_6 = 0x8C,
|
||||
HID_KEY_INTERNATIONAL_7 = 0x8D,
|
||||
HID_KEY_INTERNATIONAL_8 = 0x8E,
|
||||
HID_KEY_INTERNATIONAL_9 = 0x8F,
|
||||
HID_KEY_LANG_1 = 0x90,
|
||||
HID_KEY_LANG_2 = 0x91,
|
||||
HID_KEY_LANG_3 = 0x92,
|
||||
HID_KEY_LANG_4 = 0x93,
|
||||
HID_KEY_LANG_5 = 0x94,
|
||||
HID_KEY_LANG_6 = 0x95,
|
||||
HID_KEY_LANG_7 = 0x96,
|
||||
HID_KEY_LANG_8 = 0x97,
|
||||
HID_KEY_LANG_9 = 0x98,
|
||||
HID_KEY_ALTERNATE_ERASE = 0x99,
|
||||
HID_KEY_SYSREQ = 0x9A,
|
||||
HID_KEY_CANCEL = 0x9B,
|
||||
HID_KEY_CLEAR = 0x9C,
|
||||
HID_KEY_PRIOR = 0x9D,
|
||||
HID_KEY_RETURN = 0x9E,
|
||||
HID_KEY_SEPARATOR = 0x9F,
|
||||
HID_KEY_OUT = 0xA0,
|
||||
HID_KEY_OPER = 0xA1,
|
||||
HID_KEY_CLEAR_AGAIN = 0xA2,
|
||||
HID_KEY_CRSEL = 0xA3,
|
||||
HID_KEY_EXSEL = 0xA4,
|
||||
HID_KEY_KEYPAD_00 = 0xB0,
|
||||
HID_KEY_KEYPAD_000 = 0xB1,
|
||||
HID_KEY_THOUSANDS_SEPARATOR = 0xB2,
|
||||
HID_KEY_DECIMAL_SEPARATOR = 0xB3,
|
||||
HID_KEY_CURRENCY_UNIT = 0xB4,
|
||||
HID_KEY_CURRENCY_SUB_UNIT = 0xB5,
|
||||
HID_KEY_KEYPAD_OPEN_PARENTHESIS = 0xB6,
|
||||
HID_KEY_KEYPAD_CLOSE_PARENTHESIS = 0xB7,
|
||||
HID_KEY_KEYPAD_OPEN_BRACE = 0xB8,
|
||||
HID_KEY_KEYPAD_CLOSE_BRACE = 0xB9,
|
||||
HID_KEY_KEYPAD_TAB = 0xBA,
|
||||
HID_KEY_KEYPAD_BACKSPACE = 0xBB,
|
||||
HID_KEY_KEYPAD_A = 0xBC,
|
||||
HID_KEY_KEYPAD_B = 0xBD,
|
||||
HID_KEY_KEYPAD_C = 0xBE,
|
||||
HID_KEY_KEYPAD_D = 0xBF,
|
||||
HID_KEY_KEYPAD_E = 0xC0,
|
||||
HID_KEY_KEYPAD_F = 0xC1,
|
||||
HID_KEY_KEYPAD_XOR = 0xC2,
|
||||
HID_KEY_KEYPAD_CARET = 0xC3,
|
||||
HID_KEY_KEYPAD_PERCENT = 0xC4,
|
||||
HID_KEY_KEYPAD_LESSER = 0xC5,
|
||||
HID_KEY_KEYPAD_GREATER = 0xC6,
|
||||
HID_KEY_KEYPAD_AND = 0xC7,
|
||||
HID_KEY_KEYPAD_LOGICAL_AND = 0xC8,
|
||||
HID_KEY_KEYPAD_OR = 0xC9,
|
||||
HID_KEY_KEYPAD_LOGICAL_OR = 0xCA,
|
||||
HID_KEY_KEYPAD_COLON = 0xCB,
|
||||
HID_KEY_KEYPAD_SHARP = 0xCC,
|
||||
HID_KEY_KEYPAD_SPACE = 0xCD,
|
||||
HID_KEY_KEYPAD_AT = 0xCE,
|
||||
HID_KEY_KEYPAD_BANG = 0xCF,
|
||||
HID_KEY_KEYPAD_MEMORY_STORE = 0xD0,
|
||||
HID_KEY_KEYPAD_MEMORY_RECALL = 0xD1,
|
||||
HID_KEY_KEYPAD_MEMORY_CLEAD = 0xD2,
|
||||
HID_KEY_KEYPAD_MEMORY_ADD = 0xD3,
|
||||
HID_KEY_KEYPAD_MEMORY_SUBSTRACT = 0xD4,
|
||||
HID_KEY_KEYPAD_MEMORY_MULTIPLY = 0xD5,
|
||||
HID_KEY_KEYPAD_MEMORY_DIVIDE = 0xD6,
|
||||
HID_KEY_KEYPAD_SIGN = 0xD7,
|
||||
HID_KEY_KEYPAD_CLEAR = 0xD8,
|
||||
HID_KEY_KEYPAD_CLEAR_ENTRY = 0xD9,
|
||||
HID_KEY_KEYPAD_BINARY = 0xDA,
|
||||
HID_KEY_KEYPAD_OCTAL = 0xDB,
|
||||
HID_KEY_KEYPAD_DECIMAL = 0xDC,
|
||||
HID_KEY_KEYPAD_HEXADECIMAL = 0xDD,
|
||||
HID_KEY_LEFT_CONTROL = 0xE0,
|
||||
HID_KEY_LEFT_SHIFT = 0xE1,
|
||||
HID_KEY_LEFT_ALT = 0xE2,
|
||||
HID_KEY_LEFT_GUI = 0xE3,
|
||||
HID_KEY_RIGHT_CONTROL = 0xE0,
|
||||
HID_KEY_RIGHT_SHIFT = 0xE1,
|
||||
HID_KEY_RIGHT_ALT = 0xE2,
|
||||
HID_KEY_RIGHT_GUI = 0xE3
|
||||
} __attribute__((packed)) hid_key_t;
|
||||
|
||||
// Modifier bit mask
|
||||
#define HID_LEFT_CONTROL (1 << 0)
|
||||
#define HID_LEFT_SHIFT (1 << 1)
|
||||
#define HID_LEFT_ALT (1 << 2)
|
||||
#define HID_LEFT_GUI (1 << 3)
|
||||
#define HID_RIGHT_CONTROL (1 << 4)
|
||||
#define HID_RIGHT_SHIFT (1 << 5)
|
||||
#define HID_RIGHT_ALT (1 << 6)
|
||||
#define HID_RIGHT_GUI (1 << 7)
|
||||
|
||||
/**
|
||||
* @brief HID Keyboard Key number for Boot Interface
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef enum {
|
||||
HID_KEYBOARD_KEY_NUMBER0 = 0,
|
||||
HID_KEYBOARD_KEY_NUMBER1,
|
||||
HID_KEYBOARD_KEY_NUMBER2,
|
||||
HID_KEYBOARD_KEY_NUMBER3,
|
||||
HID_KEYBOARD_KEY_NUMBER4,
|
||||
HID_KEYBOARD_KEY_NUMBER5,
|
||||
HID_KEYBOARD_KEY_MAX,
|
||||
} hid_keyboard_key_number_t;
|
||||
|
||||
/**
|
||||
* @brief HID Keyboard Input Report for Boot Interfaces
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint8_t left_ctr: 1;
|
||||
uint8_t left_shift: 1;
|
||||
uint8_t left_alt: 1;
|
||||
uint8_t left_gui: 1;
|
||||
uint8_t rigth_ctr: 1;
|
||||
uint8_t right_shift: 1;
|
||||
uint8_t right_alt: 1;
|
||||
uint8_t right_gui: 1;
|
||||
};
|
||||
uint8_t val;
|
||||
} modifier;
|
||||
uint8_t reserved;
|
||||
uint8_t key[HID_KEYBOARD_KEY_MAX];
|
||||
} __attribute__((packed)) hid_keyboard_input_report_boot_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief HID Mouse Input Report for Boot Interfaces
|
||||
*
|
||||
* @see B.1, p.60 of Device Class Definition for Human Interface Devices (HID) Version 1.11
|
||||
*/
|
||||
typedef struct {
|
||||
union {
|
||||
struct {
|
||||
uint8_t button1: 1;
|
||||
uint8_t button2: 1;
|
||||
uint8_t button3: 1;
|
||||
uint8_t reserved: 5;
|
||||
};
|
||||
uint8_t val;
|
||||
} buttons;
|
||||
int8_t x_displacement;
|
||||
int8_t y_displacement;
|
||||
} __attribute__((packed)) hid_mouse_input_report_boot_t;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,15 @@
|
||||
# The following lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
|
||||
set(EXTRA_COMPONENT_DIRS
|
||||
../../usb_host_hid
|
||||
)
|
||||
|
||||
# Set the components to include the tests for.
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
list(APPEND EXTRA_COMPONENT_DIRS ../../../../../device/esp_tinyusb)
|
||||
endif()
|
||||
|
||||
project(test_app_usb_host_hid)
|
||||
@@ -0,0 +1,4 @@
|
||||
| Supported Targets | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | -------- | -------- |
|
||||
|
||||
# USB: HID Class test application
|
||||
@@ -0,0 +1,25 @@
|
||||
include($ENV{IDF_PATH}/tools/cmake/version.cmake)
|
||||
set (TINYUSB_LIB)
|
||||
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "5.0")
|
||||
set(TINYUSB_LIB "esp_tinyusb")
|
||||
else()
|
||||
set(TINYUSB_LIB "tinyusb")
|
||||
endif()
|
||||
|
||||
# TODO: once IDF_v4.4 is at the EOL support, use WHOLE_ARCHIVE
|
||||
idf_component_register(SRC_DIRS .
|
||||
INCLUDE_DIRS .
|
||||
REQUIRES unity usb usb_host_hid ${TINYUSB_LIB})
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
# the component can be registered as WHOLE_ARCHIVE
|
||||
|
||||
# Due to the backward compatibility to IDFv4.4 (in which WHOLE_ARCHIVE is not implemented) we use following approach:
|
||||
# Any non-static function test_app/main/*.c (apart from test_app_main.c) file is added as an undefined symbol
|
||||
# because otherwise the linker will ignore test_app/main/*.c as it has no other files depending on any
|
||||
# symbols in it.
|
||||
|
||||
# force-link test_hid_basic.c
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u test_hid_setup")
|
||||
# force-link test_hid_err_handling.c
|
||||
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u test_interface_callback_handler")
|
||||
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "tinyusb.h"
|
||||
#include "class/hid/hid_device.h"
|
||||
#include "esp_idf_version.h"
|
||||
#include "hid_mock_device.h"
|
||||
|
||||
static tusb_iface_count_t tusb_iface_count = 0;
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
/************* TinyUSB descriptors ****************/
|
||||
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_HID * TUD_HID_DESC_LEN)
|
||||
|
||||
/**
|
||||
* @brief HID report descriptor
|
||||
*
|
||||
* In this example we implement Keyboard + Mouse HID device,
|
||||
* so we must define both report descriptors
|
||||
*/
|
||||
const uint8_t hid_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_ITF_PROTOCOL_KEYBOARD) ),
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE) )
|
||||
};
|
||||
|
||||
const uint8_t hid_keyboard_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_KEYBOARD(HID_REPORT_ID(HID_ITF_PROTOCOL_KEYBOARD) )
|
||||
};
|
||||
|
||||
const uint8_t hid_mouse_report_descriptor[] = {
|
||||
TUD_HID_REPORT_DESC_MOUSE(HID_REPORT_ID(HID_ITF_PROTOCOL_MOUSE) )
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief String descriptor
|
||||
*/
|
||||
const char *hid_string_descriptor[5] = {
|
||||
// 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
|
||||
"Example HID interface", // 4: HID
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Configuration descriptor
|
||||
*
|
||||
* This is a simple configuration descriptor that defines 1 configuration and 1 HID interface
|
||||
*/
|
||||
static const uint8_t hid_configuration_descriptor_one_iface[] = {
|
||||
// Configuration number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, CFG_TUD_HID, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
// Interface number, string index, boot protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(0, 4, false, sizeof(hid_report_descriptor), 0x81, 16, 10),
|
||||
TUD_HID_DESCRIPTOR(1, 4, false, sizeof(hid_report_descriptor), 0x82, 16, 10),
|
||||
};
|
||||
|
||||
static const uint8_t hid_configuration_descriptor_two_ifaces[] = {
|
||||
// Configuration number, interface count, string index, total length, attribute, power in mA
|
||||
TUD_CONFIG_DESCRIPTOR(1, CFG_TUD_HID, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
|
||||
|
||||
// Interface number, string index, boot protocol, report descriptor len, EP In address, size & polling interval
|
||||
TUD_HID_DESCRIPTOR(0, 4, HID_ITF_PROTOCOL_KEYBOARD, sizeof(hid_keyboard_report_descriptor), 0x81, 16, 10),
|
||||
TUD_HID_DESCRIPTOR(2, 4, HID_ITF_PROTOCOL_MOUSE, sizeof(hid_mouse_report_descriptor), 0x82, 16, 10),
|
||||
};
|
||||
|
||||
static const uint8_t *hid_configuration_descriptor_list[TUSB_IFACE_COUNT_MAX] = {
|
||||
hid_configuration_descriptor_one_iface,
|
||||
hid_configuration_descriptor_two_ifaces
|
||||
};
|
||||
#endif // // esp idf >= v5.0.0
|
||||
/********* TinyUSB HID callbacks ***************/
|
||||
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
// Invoked when received GET HID REPORT DESCRIPTOR request
|
||||
// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
|
||||
uint8_t const *tud_hid_descriptor_report_cb(uint8_t instance)
|
||||
{
|
||||
switch (tusb_iface_count) {
|
||||
case TUSB_IFACE_COUNT_ONE:
|
||||
return hid_report_descriptor;
|
||||
|
||||
case TUSB_IFACE_COUNT_TWO:
|
||||
return (!!instance) ? hid_mouse_report_descriptor : hid_keyboard_report_descriptor;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
#endif // esp idf >= v5.0.0
|
||||
|
||||
#if ((ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)) && (ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)))
|
||||
#define HID_ITF_PROTOCOL_KEYBOARD HID_PROTOCOL_KEYBOARD
|
||||
#define HID_ITF_PROTOCOL_MOUSE HID_PROTOCOL_MOUSE
|
||||
#endif // 4.4.0 <= esp idf < v5.0.0
|
||||
|
||||
/**
|
||||
* @brief Get Keyboard report
|
||||
*
|
||||
* Fill buffer with test Keyboard report data
|
||||
*
|
||||
* @param[in] buffer Pointer to a buffer for filling
|
||||
* @return uint16_t Length of copied data to buffer
|
||||
*/
|
||||
static inline uint16_t get_keyboard_report(uint8_t *buffer)
|
||||
{
|
||||
hid_keyboard_report_t kb_report = {
|
||||
0, // Keyboard modifier
|
||||
0, // Reserved
|
||||
{ HID_KEY_M, HID_KEY_N, HID_KEY_O, HID_KEY_P, HID_KEY_Q, HID_KEY_R }
|
||||
};
|
||||
memcpy(buffer, &kb_report, sizeof(kb_report));
|
||||
return sizeof(kb_report);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Get Mouse report
|
||||
*
|
||||
* Fill buffer with test Mouse report data
|
||||
*
|
||||
* @param[in] buffer Pointer to a buffer for filling
|
||||
* @return uint16_t Length of copied data to buffer
|
||||
*/
|
||||
static inline uint16_t get_mouse_report(uint8_t *buffer)
|
||||
{
|
||||
hid_mouse_report_t mouse_report = {
|
||||
MOUSE_BUTTON_LEFT | MOUSE_BUTTON_RIGHT, // buttons
|
||||
-1, // x
|
||||
127, // y
|
||||
0, // wheel
|
||||
0 // pan
|
||||
};
|
||||
memcpy(buffer, &mouse_report, sizeof(mouse_report));
|
||||
return sizeof(mouse_report);
|
||||
}
|
||||
|
||||
// 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 instance, uint8_t report_id, hid_report_type_t report_type, uint8_t *buffer, uint16_t reqlen)
|
||||
{
|
||||
switch (report_id) {
|
||||
case HID_ITF_PROTOCOL_KEYBOARD:
|
||||
return get_keyboard_report(buffer);
|
||||
|
||||
case HID_ITF_PROTOCOL_MOUSE:
|
||||
return get_mouse_report(buffer);
|
||||
|
||||
default:
|
||||
printf("HID mock device, Unhandled ReportID %d\n", report_id);
|
||||
break;
|
||||
}
|
||||
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 instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const *buffer, uint16_t bufsize)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief HID Mock device start
|
||||
*
|
||||
* @param[in] iface_count Interface count, when TUSB_IFACE_COUNT_ONE then there is two Interfaces, but equal (Protocol=None).
|
||||
* when TUSB_IFACE_COUNT_TWO then HID device mocked with two independent Interfaces (Protocol=BootKeyboard, Protocol=BootMouse).
|
||||
*/
|
||||
void hid_mock_device(tusb_iface_count_t iface_count)
|
||||
{
|
||||
if (iface_count > TUSB_IFACE_COUNT_MAX) {
|
||||
printf("UHID mock device, wrong iface_count paramteter (%d)\n",
|
||||
iface_count);
|
||||
return;
|
||||
}
|
||||
|
||||
// Global Interfaces count value
|
||||
tusb_iface_count = iface_count;
|
||||
|
||||
const tinyusb_config_t tusb_cfg = {
|
||||
.external_phy = false,
|
||||
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
|
||||
.device_descriptor = NULL,
|
||||
.string_descriptor = hid_string_descriptor,
|
||||
.string_descriptor_count = sizeof(hid_string_descriptor) / sizeof(hid_string_descriptor[0]),
|
||||
.configuration_descriptor = hid_configuration_descriptor_list[tusb_iface_count],
|
||||
#endif // esp idf >= v5.0.0
|
||||
};
|
||||
|
||||
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
|
||||
|
||||
printf("HID mock device with %s has been started\n",
|
||||
(TUSB_IFACE_COUNT_ONE == tusb_iface_count)
|
||||
? "1xInterface (Protocol=None)"
|
||||
: "2xInterfaces (Protocol=BootKeyboard, Protocol=BootMouse)");
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
typedef enum {
|
||||
TUSB_IFACE_COUNT_ONE = 0x00,
|
||||
TUSB_IFACE_COUNT_TWO = 0x01,
|
||||
TUSB_IFACE_COUNT_MAX
|
||||
} tusb_iface_count_t;
|
||||
|
||||
void hid_mock_device(tusb_iface_count_t iface_count);
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include "unity.h"
|
||||
#include "esp_heap_caps.h"
|
||||
|
||||
static size_t before_free_8bit;
|
||||
static size_t before_free_32bit;
|
||||
|
||||
#define TEST_MEMORY_LEAK_THRESHOLD (-530)
|
||||
static void check_leak(size_t before_free, size_t after_free, const char *type)
|
||||
{
|
||||
ssize_t delta = after_free - before_free;
|
||||
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta);
|
||||
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
|
||||
}
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
// ____ ___ ___________________ __ __
|
||||
// | | \/ _____/\______ \ _/ |_ ____ _______/ |_
|
||||
// | | /\_____ \ | | _/ \ __\/ __ \ / ___/\ __\.
|
||||
// | | / / \ | | \ | | \ ___/ \___ \ | |
|
||||
// |______/ /_______ / |______ / |__| \___ >____ > |__|
|
||||
// \/ \/ \/ \/
|
||||
printf(" ____ ___ ___________________ __ __ \r\n");
|
||||
printf("| | \\/ _____/\\______ \\ _/ |_ ____ _______/ |_ \r\n");
|
||||
printf("| | /\\_____ \\ | | _/ \\ __\\/ __ \\ / ___/\\ __\\\r\n");
|
||||
printf("| | / / \\ | | \\ | | \\ ___/ \\___ \\ | | \r\n");
|
||||
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
|
||||
printf(" \\/ \\/ \\/ \\/ \r\n");
|
||||
|
||||
UNITY_BEGIN();
|
||||
unity_run_menu();
|
||||
UNITY_END();
|
||||
}
|
||||
|
||||
/* setUp runs before every test */
|
||||
void setUp(void)
|
||||
{
|
||||
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
}
|
||||
|
||||
/* tearDown runs after every test */
|
||||
void tearDown(void)
|
||||
{
|
||||
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
|
||||
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
|
||||
check_leak(before_free_8bit, after_free_8bit, "8BIT");
|
||||
check_leak(before_free_32bit, after_free_32bit, "32BIT");
|
||||
}
|
||||
@@ -0,0 +1,684 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/event_groups.h"
|
||||
#include "freertos/queue.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_private/usb_phy.h"
|
||||
#include "usb/usb_host.h"
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
#include "usb/hid_usage_keyboard.h"
|
||||
#include "usb/hid_usage_mouse.h"
|
||||
|
||||
#include "test_hid_basic.h"
|
||||
#include "hid_mock_device.h"
|
||||
|
||||
// USB PHY for device discinnection emulation
|
||||
static usb_phy_handle_t phy_hdl = NULL;
|
||||
|
||||
// Global variable to verify user arg passing through callbacks
|
||||
static uint32_t user_arg_value = 0x8A53E0A4; // Just a constant renadom number
|
||||
|
||||
// Queue and task for possibility to interact with USB device
|
||||
// IMPORTANT: Interaction is not possible within device/interface callback
|
||||
static bool time_to_shutdown = false;
|
||||
static bool time_to_stop_polling = false;
|
||||
QueueHandle_t hid_host_test_event_queue;
|
||||
TaskHandle_t hid_test_task_handle;
|
||||
|
||||
// Multiple tasks testing
|
||||
static hid_host_device_handle_t global_hdl;
|
||||
static int test_num_passed;
|
||||
|
||||
static const char *test_hid_sub_class_names[] = {
|
||||
"NO_SUBCLASS",
|
||||
"BOOT_INTERFACE",
|
||||
};
|
||||
|
||||
static const char *test_hid_proto_names[] = {
|
||||
"NONE",
|
||||
"KEYBOARD",
|
||||
"MOUSE"
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
hid_host_device_handle_t hid_device_handle;
|
||||
hid_host_driver_event_t event;
|
||||
void *arg;
|
||||
} hid_host_test_event_queue_t;
|
||||
|
||||
typedef enum {
|
||||
HID_HOST_TEST_TOUCH_WAY_ASSERT = 0x00,
|
||||
HID_HOST_TEST_TOUCH_WAY_SUDDEN_DISCONNECT = 0x01,
|
||||
} hid_host_test_touch_way_t;
|
||||
|
||||
static void force_conn_state(bool connected, TickType_t delay_ticks)
|
||||
{
|
||||
TEST_ASSERT_NOT_NULL(phy_hdl);
|
||||
if (delay_ticks > 0) {
|
||||
//Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
|
||||
vTaskDelay(delay_ticks);
|
||||
}
|
||||
ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN));
|
||||
}
|
||||
|
||||
void hid_host_test_interface_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
uint8_t data[64] = { 0 };
|
||||
size_t data_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
printf("USB port %d, Interface num %d: ",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num);
|
||||
|
||||
hid_host_device_get_raw_input_report_data(hid_device_handle,
|
||||
data,
|
||||
64,
|
||||
&data_length);
|
||||
|
||||
for (int i = 0; i < data_length; i++) {
|
||||
printf("%02x ", data[i]);
|
||||
}
|
||||
printf("\n");
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
printf("USB port %d, iface num %d removed\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_close(hid_device_handle) );
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
printf("USB Host transfer error\n");
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Interface unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = (void *) &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_concurrent(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
|
||||
global_hdl = hid_device_handle;
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_device_callback_to_queue(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
const hid_host_test_event_queue_t evt_queue = {
|
||||
.hid_device_handle = hid_device_handle,
|
||||
.event = event,
|
||||
.arg = arg
|
||||
};
|
||||
xQueueSend(hid_host_test_event_queue, &evt_queue, 0);
|
||||
}
|
||||
|
||||
void hid_host_test_requests_callback(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(hid_device_handle, &dev_params));
|
||||
TEST_ASSERT_EQUAL_PTR_MESSAGE(&user_arg_value, arg, "User argument has lost");
|
||||
|
||||
uint8_t *test_buffer = NULL; // for report descriptor
|
||||
unsigned int test_length = 0;
|
||||
uint8_t tmp[10] = { 0 }; // for input report
|
||||
size_t rep_len = 0;
|
||||
|
||||
switch (event) {
|
||||
case HID_HOST_DRIVER_EVENT_CONNECTED:
|
||||
printf("USB port %d, interface %d, '%s', '%s'\n",
|
||||
dev_params.addr,
|
||||
dev_params.iface_num,
|
||||
test_hid_sub_class_names[dev_params.sub_class],
|
||||
test_hid_proto_names[dev_params.proto]);
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = hid_host_test_interface_callback,
|
||||
.callback_arg = &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
|
||||
// Class device requests
|
||||
// hid_host_get_report_descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(hid_device_handle, &test_length);
|
||||
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
printf("HID Report descriptor length: %d\n", test_length);
|
||||
|
||||
// // HID Device info
|
||||
hid_host_dev_info_t hid_dev_info;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_get_device_info(hid_device_handle,
|
||||
&hid_dev_info) );
|
||||
|
||||
printf("\t VID: 0x%04X\n", hid_dev_info.VID);
|
||||
printf("\t PID: 0x%04X\n", hid_dev_info.PID);
|
||||
wprintf(L"\t iProduct: %S \n", hid_dev_info.iProduct);
|
||||
wprintf(L"\t iManufacturer: %S \n", hid_dev_info.iManufacturer);
|
||||
wprintf(L"\t iSerialNumber: %S \n", hid_dev_info.iSerialNumber);
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_NONE) {
|
||||
// If Protocol NONE, based on hid1_11.pdf, p.78, all other devices should support
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x01 (Keyboard ReportID)
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len)) {
|
||||
printf("HID Get Report, type %d, id %d, length: %d:\n",
|
||||
HID_REPORT_TYPE_INPUT, 0, rep_len);
|
||||
for (int i = 0; i < rep_len; i++) {
|
||||
printf("%02X ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x02 (Mouse ReportID)
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len)) {
|
||||
printf("HID Get Report, type %d, id %d, length: %d:\n",
|
||||
HID_REPORT_TYPE_INPUT, 0, rep_len);
|
||||
for (int i = 0; i < rep_len; i++) {
|
||||
printf("%02X ", tmp[i]);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
} else {
|
||||
// hid_class_request_get_protocol
|
||||
hid_report_protocol_t proto;
|
||||
if (ESP_OK == hid_class_request_get_protocol(hid_device_handle, &proto)) {
|
||||
printf("HID protocol: %d\n", proto);
|
||||
}
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_KEYBOARD) {
|
||||
uint8_t idle_rate;
|
||||
// hid_class_request_get_idle
|
||||
if (ESP_OK == hid_class_request_get_idle(hid_device_handle,
|
||||
0, &idle_rate)) {
|
||||
printf("HID idle rate: %d\n", idle_rate);
|
||||
}
|
||||
// hid_class_request_set_idle
|
||||
if (ESP_OK == hid_class_request_set_idle(hid_device_handle,
|
||||
0, 0)) {
|
||||
printf("HID idle rate set to 0\n");
|
||||
}
|
||||
|
||||
// hid_class_request_get_report
|
||||
rep_len = sizeof(tmp);
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len)) {
|
||||
printf("HID get report type %d, id %d, length: %d\n",
|
||||
HID_REPORT_TYPE_INPUT, 0x00, rep_len);
|
||||
}
|
||||
|
||||
// hid_class_request_set_report
|
||||
uint8_t rep[1] = { 0x00 };
|
||||
if (ESP_OK == hid_class_request_set_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_OUTPUT, 0x01, rep, 1)) {
|
||||
printf("HID set report type %d, id %d\n", HID_REPORT_TYPE_OUTPUT, 0x00);
|
||||
}
|
||||
}
|
||||
|
||||
if (dev_params.proto == HID_PROTOCOL_MOUSE) {
|
||||
// hid_class_request_get_report
|
||||
rep_len = sizeof(tmp);
|
||||
if (ESP_OK == hid_class_request_get_report(hid_device_handle,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len)) {
|
||||
printf("HID get report type %d, id %d, length: %d\n",
|
||||
HID_REPORT_TYPE_INPUT, 0x00, rep_len);
|
||||
}
|
||||
}
|
||||
|
||||
// hid_class_request_set_protocol
|
||||
if (ESP_OK == hid_class_request_set_protocol(hid_device_handle,
|
||||
HID_REPORT_PROTOCOL_BOOT)) {
|
||||
printf("HID protocol change to BOOT: %d\n", proto);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_start(hid_device_handle) );
|
||||
break;
|
||||
default:
|
||||
TEST_FAIL_MESSAGE("HID Driver unhandled event");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void hid_host_test_task(void *pvParameters)
|
||||
{
|
||||
hid_host_test_event_queue_t evt_queue;
|
||||
// Create queue
|
||||
hid_host_test_event_queue = xQueueCreate(10, sizeof(hid_host_test_event_queue_t));
|
||||
|
||||
// Wait queue
|
||||
while (!time_to_shutdown) {
|
||||
if (xQueueReceive(hid_host_test_event_queue, &evt_queue, pdMS_TO_TICKS(50))) {
|
||||
hid_host_test_requests_callback(evt_queue.hid_device_handle,
|
||||
evt_queue.event,
|
||||
evt_queue.arg);
|
||||
}
|
||||
}
|
||||
|
||||
xQueueReset(hid_host_test_event_queue);
|
||||
vQueueDelete(hid_host_test_event_queue);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void hid_host_test_polling_task(void *pvParameters)
|
||||
{
|
||||
// Wait queue
|
||||
while (!time_to_stop_polling) {
|
||||
hid_host_handle_events(portMAX_DELAY);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static void test_hid_host_device_touch(hid_host_dev_params_t *dev_params,
|
||||
hid_host_test_touch_way_t touch_way)
|
||||
{
|
||||
uint8_t tmp[10] = { 0 }; // for input report
|
||||
size_t rep_len = 0;
|
||||
hid_report_protocol_t proto;
|
||||
|
||||
if (dev_params->proto == HID_PROTOCOL_NONE) {
|
||||
rep_len = sizeof(tmp);
|
||||
// For testing with ESP32 we used ReportID = 0x01 (Keyboard ReportID)
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len);
|
||||
}
|
||||
|
||||
} else {
|
||||
// Get Protocol
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_protocol(global_hdl, &proto));
|
||||
// Get Report for Keyboard protocol, ReportID = 0x00 (Boot Keyboard ReportID)
|
||||
if (dev_params->proto == HID_PROTOCOL_KEYBOARD) {
|
||||
rep_len = sizeof(tmp);
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x01, tmp, &rep_len);
|
||||
}
|
||||
}
|
||||
if (dev_params->proto == HID_PROTOCOL_MOUSE) {
|
||||
rep_len = sizeof(tmp);
|
||||
if (HID_HOST_TEST_TOUCH_WAY_ASSERT == touch_way) {
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len));
|
||||
} else {
|
||||
hid_class_request_get_report(global_hdl,
|
||||
HID_REPORT_TYPE_INPUT, 0x02, tmp, &rep_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define MULTIPLE_TASKS_TASKS_NUM 10
|
||||
|
||||
void concurrent_task(void *arg)
|
||||
{
|
||||
uint8_t *test_buffer = NULL;
|
||||
unsigned int test_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(global_hdl, &dev_params));
|
||||
|
||||
// Get Report descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(global_hdl, &test_length);
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
|
||||
test_hid_host_device_touch(&dev_params, HID_HOST_TEST_TOUCH_WAY_ASSERT);
|
||||
test_num_passed++;
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void access_task(void *arg)
|
||||
{
|
||||
uint8_t *test_buffer = NULL;
|
||||
unsigned int test_length = 0;
|
||||
hid_host_dev_params_t dev_params;
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_get_params(global_hdl, &dev_params));
|
||||
|
||||
// Get Report descriptor
|
||||
test_buffer = hid_host_get_report_descriptor(global_hdl, &test_length);
|
||||
TEST_ASSERT_NOT_NULL(test_buffer);
|
||||
|
||||
while (!time_to_shutdown) {
|
||||
test_hid_host_device_touch(&dev_params, HID_HOST_TEST_TOUCH_WAY_ASSERT/* HID_HOST_TEST_TOUCH_WAY_SUDDEN_DISCONNECT */);
|
||||
}
|
||||
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Creates MULTIPLE_TASKS_TASKS_NUM to get report descriptor and get protocol from HID device.
|
||||
* After test_num_passed - is a global static variable that increases during every successful task test.
|
||||
*/
|
||||
void test_multiple_tasks_access(void)
|
||||
{
|
||||
// Create tasks that will try to access HID dev with global hdl
|
||||
for (int i = 0; i < MULTIPLE_TASKS_TASKS_NUM; i++) {
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(concurrent_task, "HID multi touch", 4096, NULL, i + 3, NULL));
|
||||
}
|
||||
// Wait until all tasks finish
|
||||
vTaskDelay(pdMS_TO_TICKS(500));
|
||||
}
|
||||
|
||||
void test_task_access(void)
|
||||
{
|
||||
// Create task which will be touching the device with control requests, while device is present
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(access_task, "HID touch", 4096, NULL, 3, NULL));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Start USB Host and handle common USB host library events while devices/clients are present
|
||||
*
|
||||
* @param[in] arg Main task handle
|
||||
*/
|
||||
static void usb_lib_task(void *arg)
|
||||
{
|
||||
// Initialize the internal USB PHY to connect to the USB OTG peripheral.
|
||||
// We manually install the USB PHY for testing
|
||||
usb_phy_config_t phy_config = {
|
||||
.controller = USB_PHY_CTRL_OTG,
|
||||
.target = USB_PHY_TARGET_INT,
|
||||
.otg_mode = USB_OTG_MODE_HOST,
|
||||
.otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl));
|
||||
|
||||
const usb_host_config_t host_config = {
|
||||
.skip_phy_setup = true,
|
||||
.intr_flags = ESP_INTR_FLAG_LEVEL1,
|
||||
};
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_install(&host_config) );
|
||||
printf("USB Host installed\n");
|
||||
xTaskNotifyGive(arg);
|
||||
|
||||
bool all_clients_gone = false;
|
||||
bool all_dev_free = false;
|
||||
while (!all_clients_gone || !all_dev_free) {
|
||||
uint32_t event_flags;
|
||||
usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
|
||||
|
||||
// Release devices once all clients has deregistered
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
|
||||
usb_host_device_free_all();
|
||||
printf("USB Event flags: NO_CLIENTS\n");
|
||||
all_clients_gone = true;
|
||||
}
|
||||
// All devices were removed
|
||||
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
|
||||
printf("USB Event flags: ALL_FREE\n");
|
||||
all_dev_free = true;
|
||||
time_to_stop_polling = true;
|
||||
// Notify that device was being disconnected
|
||||
xTaskNotifyGive(arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Change global flag for all tasks still running
|
||||
time_to_shutdown = true;
|
||||
|
||||
// Clean up USB Host
|
||||
vTaskDelay(10); // Short delay to allow clients clean-up
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_host_uninstall());
|
||||
TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); //Tear down USB PHY
|
||||
phy_hdl = NULL;
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
// ----------------------- Public -------------------------
|
||||
|
||||
/**
|
||||
* @brief Setups HID testing
|
||||
*
|
||||
* - Create USB lib task
|
||||
* - Install HID Host driver
|
||||
*/
|
||||
void test_hid_setup(hid_host_driver_event_cb_t device_callback,
|
||||
hid_test_event_handle_t hid_test_event_handle)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreatePinnedToCore(usb_lib_task,
|
||||
"usb_events",
|
||||
4096,
|
||||
xTaskGetCurrentTaskHandle(),
|
||||
2, NULL, 0));
|
||||
// Wait for notification from usb_lib_task
|
||||
ulTaskNotifyTake(false, 1000);
|
||||
|
||||
// HID host driver config
|
||||
const hid_host_driver_config_t hid_host_driver_config = {
|
||||
.create_background_task = (hid_test_event_handle == HID_TEST_EVENT_HANDLE_IN_DRIVER)
|
||||
? true
|
||||
: false,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = device_callback,
|
||||
.callback_arg = (void *) &user_arg_value
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_install(&hid_host_driver_config) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Teardowns HID testing
|
||||
* - Disconnect connected USB device manually by PHY triggering
|
||||
* - Wait for USB lib task was closed
|
||||
* - Uninstall HID Host driver
|
||||
* - Clear the notification value to 0
|
||||
* - Short delay to allow task to be cleaned up
|
||||
*/
|
||||
void test_hid_teardown(void)
|
||||
{
|
||||
force_conn_state(false, pdMS_TO_TICKS(1000));
|
||||
vTaskDelay(50);
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_uninstall() );
|
||||
ulTaskNotifyValueClear(NULL, 1);
|
||||
vTaskDelay(20);
|
||||
}
|
||||
|
||||
// ------------------------- HID Test ------------------------------------------
|
||||
static void test_setup_hid_task(void)
|
||||
{
|
||||
// Task is working until the devices are gone
|
||||
time_to_shutdown = false;
|
||||
// Create process
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(&hid_host_test_task,
|
||||
"hid_task",
|
||||
4 * 1024,
|
||||
NULL,
|
||||
3,
|
||||
&hid_test_task_handle));
|
||||
}
|
||||
|
||||
static void test_setup_hid_polling_task(void)
|
||||
{
|
||||
time_to_stop_polling = false;
|
||||
|
||||
TEST_ASSERT_EQUAL(pdTRUE, xTaskCreate(&hid_host_test_polling_task,
|
||||
"hid_task_polling",
|
||||
4 * 1024,
|
||||
NULL, 2, NULL));
|
||||
}
|
||||
|
||||
TEST_CASE("memory_leakage", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with the regular 'hid_host_test_callback'
|
||||
test_hid_setup(hid_host_test_callback, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("multiple_task_access", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with 'hid_host_test_concurrent'
|
||||
test_hid_setup(hid_host_test_concurrent, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Refresh the num passed test value
|
||||
test_num_passed = 0;
|
||||
// Start multiple task access to USB device with control requests
|
||||
test_multiple_tasks_access();
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify how much tests was done
|
||||
TEST_ASSERT_EQUAL(MULTIPLE_TASKS_TASKS_NUM, test_num_passed);
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// All specific control requests will be verified during device connection callback 'hid_host_test_requests_callback'
|
||||
// Wait for test completed for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests_with_external_polling", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_EXTERNAL);
|
||||
// Create HID Driver events polling task
|
||||
test_setup_hid_polling_task();
|
||||
// All specific control requests will be verified during device connection callback 'hid_host_test_requests_callback'
|
||||
// Wait for test completed for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("class_specific_requests_with_external_polling_without_polling", "[hid_host]")
|
||||
{
|
||||
// Create external HID events task
|
||||
test_setup_hid_task();
|
||||
// Install USB and HID driver with 'hid_host_test_device_callback_to_queue'
|
||||
test_hid_setup(hid_host_test_device_callback_to_queue, HID_TEST_EVENT_HANDLE_EXTERNAL);
|
||||
// Do not create HID Driver events polling task to eliminate events polling
|
||||
// ...
|
||||
// Wait for 250 ms
|
||||
vTaskDelay(250);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
// Verify the memory leackage during test environment tearDown()
|
||||
}
|
||||
|
||||
TEST_CASE("sudden_disconnect", "[hid_host]")
|
||||
{
|
||||
// Install USB and HID driver with 'hid_host_test_concurrent'
|
||||
test_hid_setup(hid_host_test_concurrent, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Start task to access USB device with control requests
|
||||
test_task_access();
|
||||
// Tear down test during thr task_access stress the HID device
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
TEST_CASE("mock_hid_device", "[hid_device][ignore]")
|
||||
{
|
||||
hid_mock_device(TUSB_IFACE_COUNT_ONE);
|
||||
while (1) {
|
||||
vTaskDelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("mock_hid_device_with_two_ifaces", "[hid_device2][ignore]")
|
||||
{
|
||||
hid_mock_device(TUSB_IFACE_COUNT_TWO);
|
||||
while (1) {
|
||||
vTaskDelay(10);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "usb/hid_host.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef enum {
|
||||
HID_TEST_EVENT_HANDLE_IN_DRIVER = 0,
|
||||
HID_TEST_EVENT_HANDLE_EXTERNAL
|
||||
} hid_test_event_handle_t;
|
||||
|
||||
// ------------------------ HID Test -------------------------------------------
|
||||
|
||||
void test_hid_setup(hid_host_driver_event_cb_t device_callback,
|
||||
hid_test_event_handle_t hid_test_event_handle);
|
||||
|
||||
void test_hid_teardown(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
@@ -0,0 +1,213 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include "unity.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "usb/hid_host.h"
|
||||
|
||||
#include "test_hid_basic.h"
|
||||
|
||||
// ----------------------- Private -------------------------
|
||||
/**
|
||||
* @brief USB HID Host interface callback.
|
||||
*
|
||||
* Handle close event only.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_interface_event_close(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
switch (event) {
|
||||
case HID_HOST_INTERFACE_EVENT_INPUT_REPORT:
|
||||
case HID_HOST_INTERFACE_EVENT_TRANSFER_ERROR:
|
||||
break;
|
||||
case HID_HOST_INTERFACE_EVENT_DISCONNECTED:
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_close(hid_device_handle) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB HID Host event callback stub.
|
||||
*
|
||||
* Does not handle anything.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_event_callback_stub(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
if (event == HID_HOST_DRIVER_EVENT_CONNECTED) {
|
||||
// Device connected
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief USB HID Host event callback.
|
||||
*
|
||||
* Handle connected event and open a device.
|
||||
*
|
||||
* @param[in] event HID Host device event
|
||||
* @param[in] arg Pointer to arguments, does not used
|
||||
*
|
||||
*/
|
||||
static void test_hid_host_event_callback_open(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_driver_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
if (event == HID_HOST_DRIVER_EVENT_CONNECTED) {
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = test_hid_host_interface_event_close,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_OK, hid_host_device_open(hid_device_handle, &dev_config) );
|
||||
}
|
||||
}
|
||||
|
||||
// Install HID driver without USB Host and without configuration
|
||||
static void test_install_hid_driver_without_config(void)
|
||||
{
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(NULL));
|
||||
}
|
||||
|
||||
// Install HID driver without USB Host and with configuration
|
||||
static void test_install_hid_driver_with_wrong_config(void)
|
||||
{
|
||||
const hid_host_driver_config_t hid_host_config_callback_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = NULL, /* error expected */
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_callback_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_stack_size_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 0, /* error expected */
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_stack_size_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_task_priority_null = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 0,/* error expected */
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_ARG, hid_host_install(&hid_host_config_task_priority_null));
|
||||
|
||||
const hid_host_driver_config_t hid_host_config_correct = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
// Invalid state without USB Host installed
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_install(&hid_host_config_correct));
|
||||
}
|
||||
|
||||
void test_interface_callback_handler(hid_host_device_handle_t hid_device_handle,
|
||||
const hid_host_interface_event_t event,
|
||||
void *arg)
|
||||
{
|
||||
// ...
|
||||
}
|
||||
|
||||
// Open device without installed driver
|
||||
static void test_claim_interface_without_driver(void)
|
||||
{
|
||||
hid_host_device_handle_t hid_dev_handle = NULL;
|
||||
|
||||
const hid_host_device_config_t dev_config = {
|
||||
.callback = test_interface_callback_handler,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE,
|
||||
hid_host_device_open(hid_dev_handle, &dev_config) );
|
||||
}
|
||||
|
||||
static void test_install_hid_driver_when_already_installed(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_stub, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Try to install HID driver again
|
||||
const hid_host_driver_config_t hid_host_config = {
|
||||
.create_background_task = true,
|
||||
.task_priority = 5,
|
||||
.stack_size = 4096,
|
||||
.core_id = 0,
|
||||
.callback = test_hid_host_event_callback_stub,
|
||||
.callback_arg = NULL
|
||||
};
|
||||
// Verify error code
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_install(&hid_host_config));
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
static void test_uninstall_hid_driver_while_device_was_not_opened(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_stub, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
static void test_uninstall_hid_driver_while_device_is_present(void)
|
||||
{
|
||||
// Install USB and HID driver with the stub test_hid_host_event_callback_stub
|
||||
test_hid_setup(test_hid_host_event_callback_open, HID_TEST_EVENT_HANDLE_IN_DRIVER);
|
||||
// Wait for USB device appearing for 250 msec
|
||||
vTaskDelay(250);
|
||||
// Uninstall HID Driver while device is still connected and verify a result
|
||||
printf("HID Driver uninstall attempt while HID Device is still present ...\n");
|
||||
TEST_ASSERT_EQUAL(ESP_ERR_INVALID_STATE, hid_host_uninstall());
|
||||
// Tear down test
|
||||
test_hid_teardown();
|
||||
}
|
||||
|
||||
// ----------------------- Public --------------------------
|
||||
|
||||
/**
|
||||
* @brief HID Error handling test
|
||||
*
|
||||
* There are multiple erroneous scenarios checked in this test.
|
||||
*
|
||||
*/
|
||||
TEST_CASE("error_handling", "[hid_host]")
|
||||
{
|
||||
test_install_hid_driver_without_config();
|
||||
test_install_hid_driver_with_wrong_config();
|
||||
test_claim_interface_without_driver();
|
||||
test_install_hid_driver_when_already_installed();
|
||||
test_uninstall_hid_driver_while_device_was_not_opened();
|
||||
test_uninstall_hid_driver_while_device_is_present();
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
from typing import Tuple
|
||||
|
||||
import pytest
|
||||
from pytest_embedded_idf.dut import IdfDut
|
||||
|
||||
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.usb_host
|
||||
@pytest.mark.parametrize('count', [
|
||||
2,
|
||||
], indirect=True)
|
||||
def test_usb_host_hid(dut: Tuple[IdfDut, IdfDut]) -> None:
|
||||
device = dut[0]
|
||||
host = dut[1]
|
||||
|
||||
# 3.1 Prepare USB device with one Interface for HID tests
|
||||
device.expect_exact('Press ENTER to see the list of tests.')
|
||||
device.write('[hid_device]')
|
||||
device.expect_exact('HID mock device with 1xInterface (Protocol=None) has been started')
|
||||
|
||||
# 3.2 Run HID tests
|
||||
host.run_all_single_board_cases(group='hid_host')
|
||||
|
||||
# 3.3 Prepare USB device with two Interfaces for HID tests
|
||||
device.serial.hard_reset()
|
||||
device.expect_exact('Press ENTER to see the list of tests.')
|
||||
device.write('[hid_device2]')
|
||||
device.expect_exact('HID mock device with 2xInterfaces (Protocol=BootKeyboard, Protocol=BootMouse) has been started')
|
||||
|
||||
# 3.4 Run HID tests
|
||||
host.run_all_single_board_cases(group='hid_host')
|
||||
@@ -0,0 +1,19 @@
|
||||
# Configure TinyUSB, it will be used to mock USB devices
|
||||
CONFIG_TINYUSB=y
|
||||
CONFIG_TINYUSB_MSC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_ENABLED=n
|
||||
CONFIG_TINYUSB_CDC_COUNT=0
|
||||
CONFIG_TINYUSB_HID_COUNT=2
|
||||
|
||||
# Disable watchdogs, they'd get triggered during unity interactive menu
|
||||
CONFIG_ESP_INT_WDT=n
|
||||
CONFIG_ESP_TASK_WDT=n
|
||||
|
||||
# Run-time checks of Heap and Stack
|
||||
CONFIG_HEAP_POISONING_COMPREHENSIVE=y
|
||||
CONFIG_COMPILER_STACK_CHECK_MODE_STRONG=y
|
||||
CONFIG_COMPILER_STACK_CHECK=y
|
||||
|
||||
CONFIG_UNITY_ENABLE_BACKTRACE_ON_FAIL=y
|
||||
|
||||
CONFIG_COMPILER_CXX_EXCEPTIONS=y
|
||||
Reference in New Issue
Block a user