summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorjzlv <[email protected]>2021-07-10 18:31:58 +0800
committerjzlv <[email protected]>2021-07-10 18:31:58 +0800
commit8a143df5094b0b1db72c8b9c3120cfc390b54c50 (patch)
treeeb1e18cb31abc4fafb45b5b7e6cf25f2d15a5932
first commit
-rw-r--r--.clang-format171
-rw-r--r--.gitignore1
-rw-r--r--LICENSE201
-rw-r--r--README.rst99
-rw-r--r--class/audio/usbd_audio.c96
-rw-r--r--class/audio/usbd_audio.h277
-rw-r--r--class/cdc/usbd_cdc.c168
-rw-r--r--class/cdc/usbd_cdc.h374
-rw-r--r--class/hid/usbd_hid.c227
-rw-r--r--class/hid/usbd_hid.h344
-rw-r--r--class/msc/usbd_msc.c838
-rw-r--r--class/msc/usbd_msc.h101
-rw-r--r--class/msc/usbd_scsi.h302
-rw-r--r--class/video/usbd_video.c134
-rw-r--r--class/video/usbd_video.h821
-rw-r--r--class/webusb/usbd_webusb.h37
-rw-r--r--class/winusb/usbd_winusb.h35
-rw-r--r--common/usb_dc.h179
-rw-r--r--common/usb_def.h522
-rw-r--r--common/usb_slist.h224
-rw-r--r--common/usb_util.h131
-rw-r--r--core/usbd_core.c1232
-rw-r--r--core/usbd_core.h136
23 files changed, 6650 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format
new file mode 100644
index 00000000..336989de
--- /dev/null
+++ b/.clang-format
@@ -0,0 +1,171 @@
+# clang-format configuration file. Intended for clang-format >= 11.0
+#
+# For more information, see:
+#
+# https://clang.llvm.org/docs/ClangFormat.html
+# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
+#
+---
+# 语言: None, Cpp, Java, JavaScript, ObjC, Proto, TableGen, TextProto
+Language: Cpp
+# BasedOnStyle: LLVM
+# 访问说明符(public、private等)的偏移
+AccessModifierOffset: -4
+# 开括号(开圆括号、开尖括号、开方括号)后的对齐: Align, DontAlign, AlwaysBreak(总是在开括号后换行)
+AlignAfterOpenBracket: Align
+# 连续赋值时,对齐所有等号
+AlignConsecutiveAssignments: false
+# 对齐位域
+AlignConsecutiveBitFields: true
+# 连续声明时,对齐所有声明的变量名
+AlignConsecutiveDeclarations: false
+# 连续宏时,进行对齐
+AlignConsecutiveMacros: true
+# 左对齐逃脱换行(使用反斜杠换行)的反斜杠
+AlignEscapedNewlines: Left
+# 水平对齐二元和三元表达式的操作数
+AlignOperands: true
+# 对齐连续的尾随的注释
+AlignTrailingComments: true
+# 允许函数声明的所有参数在放在下一行
+AllowAllParametersOfDeclarationOnNextLine: false
+# 允许短的块放在同一行
+AllowShortBlocksOnASingleLine: false
+# 允许短的case标签放在同一行
+AllowShortCaseLabelsOnASingleLine: false
+# 允许短的函数放在同一行: None, InlineOnly(定义在类中), Empty(空函数), Inline(定义在类中,空函数), All
+AllowShortFunctionsOnASingleLine: None
+# 允许短的if语句保持在同一行
+AllowShortIfStatementsOnASingleLine: false
+# 允许短的循环保持在同一行
+AllowShortLoopsOnASingleLine: false
+# 总是在定义返回类型后换行(deprecated)
+AlwaysBreakAfterDefinitionReturnType: None
+# 总是在返回类型后换行: None, All, TopLevel(顶级函数,不包括在类中的函数),
+# AllDefinitions(所有的定义,不包括声明), TopLevelDefinitions(所有的顶级函数的定义)
+AlwaysBreakAfterReturnType: None
+# 总是在多行string字面量前换行
+AlwaysBreakBeforeMultilineStrings: false
+# 总是在template声明后换行
+AlwaysBreakTemplateDeclarations: false
+# false表示函数实参要么都在同一行,要么都各自一行
+BinPackArguments: true
+# false表示所有形参要么都在同一行,要么都各自一行
+BinPackParameters: true
+# 大括号换行,只有当BreakBeforeBraces设置为Custom时才有效
+BraceWrapping:
+ AfterClass: false
+ AfterControlStatement: false
+ AfterEnum: false
+ AfterFunction: true
+ AfterNamespace: false
+ AfterObjCDeclaration: false
+ AfterStruct: false
+ AfterUnion: false
+ AfterExternBlock: false # Unknown to clang-format-5.0
+ BeforeCatch: false
+ BeforeElse: false
+ IndentBraces: false
+ SplitEmptyFunction: true # Unknown to clang-format-4.0
+ SplitEmptyRecord: true # Unknown to clang-format-4.0
+ SplitEmptyNamespace: true # Unknown to clang-format-4.0
+# 在二元运算符前换行: None(在操作符后换行), NonAssignment(在非赋值的操作符前换行), All(在操作符前换行)
+BreakBeforeBinaryOperators: None
+BreakBeforeBraces: Custom
+#BreakBeforeInheritanceComma: false # Unknown to clang-format-4.0
+# 在三元运算符前换行
+BreakBeforeTernaryOperators: false
+# 在构造函数的初始化列表的逗号前换行
+BreakConstructorInitializersBeforeComma: false
+BreakAfterJavaFieldAnnotations: false
+BreakStringLiterals: false
+# 每行字符的限制,0表示没有限制
+ColumnLimit: 0
+# 描述具有特殊意义的注释的正则表达式,它不应该被分割为多行或以其它方式改变
+CommentPragmas: '^ IWYU pragma:'
+CompactNamespaces: false # Unknown to clang-format-4.0
+# 构造函数的初始化列表要么都在同一行,要么都各自一行
+ConstructorInitializerAllOnOneLineOrOnePerLine: false
+# 构造函数的初始化列表的缩进宽度
+ConstructorInitializerIndentWidth: 4
+# 延续的行的缩进宽度
+ContinuationIndentWidth: 4
+# 去除C++11的列表初始化的大括号{后和}前的空格
+Cpp11BracedListStyle: false
+# 继承最常用的指针和引用的对齐方式
+DerivePointerAlignment: false
+# 关闭格式化
+DisableFormat: false
+ForEachMacros:
+ - 'SHELL_EXPORT_CMD'
+
+# 自动检测函数的调用和定义是否被格式为每行一个参数(Experimental)
+ExperimentalAutoDetectBinPacking: false
+# 缩进case标签
+IndentCaseLabels: true
+# 缩进宽度
+IndentWidth: 4
+# 函数返回类型换行时,缩进函数声明或函数定义的函数名
+IndentWrappedFunctionNames: false
+# 保留在块开始处的空行
+KeepEmptyLinesAtTheStartOfBlocks: false
+# 开始一个块的宏的正则表达式
+MacroBlockBegin: ''
+# 结束一个块的宏的正则表达式
+MacroBlockEnd: ''
+# 连续空行的最大数量
+MaxEmptyLinesToKeep: 1
+# 命名空间的缩进: None, Inner(缩进嵌套的命名空间中的内容), All
+NamespaceIndentation: None
+# 使用ObjC块时缩进宽度
+ObjCBlockIndentWidth: 4
+# 在ObjC的@property后添加一个空格
+ObjCSpaceAfterProperty: false
+# 在ObjC的protocol列表前添加一个空格
+ObjCSpaceBeforeProtocolList: true
+# 在call(后对函数调用换行的penalty
+PenaltyBreakBeforeFirstCallParameter: 30
+# 在一个注释中引入换行的penalty
+PenaltyBreakComment: 10
+# 第一次在<<前换行的penalty
+PenaltyBreakFirstLessLess: 0
+# 在一个字符串字面量中引入换行的penalty
+PenaltyBreakString: 10
+# 对于每个在行字符数限制之外的字符的penalty
+PenaltyExcessCharacter: 100
+# 将函数的返回类型放到它自己的行的penalty
+PenaltyReturnTypeOnItsOwnLine: 60
+# 指针和引用的对齐: Left, Right, Middle
+PointerAlignment: Right
+# 允许重新排版注释
+ReflowComments: false
+# 允许排序#include
+SortIncludes: false
+# 在C风格类型转换后添加空格
+SpaceAfterCStyleCast: false
+# 在赋值运算符之前添加空格
+SpaceBeforeAssignmentOperators: true
+# 开圆括号之前添加一个空格: Never, ControlStatements, Always
+SpaceBeforeParens: ControlStatements
+# 在空的圆括号中添加空格
+SpaceInEmptyParentheses: false
+# 在尾随的评论前添加的空格数(只适用于//)
+SpacesBeforeTrailingComments: 1
+# 在尖括号的<后和>前添加空格
+SpacesInAngles: false
+# 在容器(ObjC和JavaScript的数组和字典等)字面量中添加空格
+SpacesInContainerLiterals: false
+# 在C风格类型转换的括号中添加空格
+SpacesInCStyleCastParentheses: false
+# 在圆括号的(后和)前添加空格
+SpacesInParentheses: false
+# 在方括号的[后和]前添加空格,lamda表达式和未指明大小的数组的声明不受影响
+SpacesInSquareBrackets: false
+# 标准: Cpp03, Cpp11, Auto
+Standard: Cpp03
+# tab宽度
+TabWidth: 4
+# 使用tab字符: Never, ForIndentation, ForContinuationAndIndentation, Always
+UseTab: Never
+...
+
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..600d2d33
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+.vscode \ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 00000000..261eeb9e
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,201 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/README.rst b/README.rst
new file mode 100644
index 00000000..ebb44e23
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,99 @@
+USB Stack
+=======================
+
+USB Stack 是一个跨平台的、用于嵌入式 MCU 的 USB 协议栈。其中 DEVICE 协议栈对标准设备请求、CLASS 请求、VENDOR 请求规范了一套统一的函数框架,从而对复合设备或者使用自定义设备类时,能够在极短的时间内进行添加和移植。同时提供了一套标准的 dcd porting 接口,供给不同的 MCU 使用,因此,通用性也非常高。此外在代码优美方面,以及内存占用方面也是相当出色。USB DEVICE 协议栈当前具有以下功能:
+
+- 支持 USB2.0 全速和高速设备
+- 支持端点中断注册功能,porting 给用户自己处理中断里的数据
+- 支持复合设备
+- 支持 Communication Class (CDC)
+- 支持 Human Interface Device (HID)
+- 支持 Custom human Interface Device (HID)
+- 支持 Mass Storage Class (MSC)
+- 支持 USB VIDEO CLASS (UVC)
+- 支持 USB AUDIO CLASS (UAC)
+- 支持 vendor 类 class
+- 支持 WINUSB1.0、WINUSB2.0
+
+当前支持的芯片(当然,是个 有 USB 的 mcu 都支持)以及 USB IP如下:
+
+- STM32
+- MicroChip
+- Kinetis
+- Synopsys USB IP
+- faraday USB IP
+- 开源 USB IP `<https://github.com/www-asics-ws/usb2_dev>`_
+
+.. note:: USB DEVICE 协议栈的代码实现过程参考 `<https://www.bilibili.com/video/BV1Ef4y1t73d>`_
+
+USB DEVICE 协议栈 porting 接口
+-------------------------------
+
+USB DEVICE 协议栈 porting 接口在 ``usb_stack/common/usb_dc.h`` 文件中声明,用户根据自己的 MCU 实现以下接口
+
+ - ``usbd_set_address`` 调用 :ref:`usb_dc_set_address`
+ - ``usbd_ep_open`` 调用 :ref:`usb_dc_ep_open`
+ - ``usbd_ep_close`` 调用 :ref:`usb_dc_ep_close`
+ - ``usbd_ep_set_stall`` 调用 :ref:`usb_dc_ep_set_stall`
+ - ``usbd_ep_clear_stall`` 调用 :ref:`usb_dc_ep_clear_stall`
+ - ``usbd_ep_is_stalled`` 调用 :ref:`usb_dc_ep_is_stalled`
+ - ``usbd_ep_write`` 调用 :ref:`usb_dc_ep_write`
+ - ``usbd_ep_read`` 调用 :ref:`usb_dc_ep_read`
+
+USB DEVICE 控制器接口
+-------------------------------
+
+**usb_dc_init**
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+``usb_dc_init`` 用来注册 USB 设备和初始化 USB 硬件相关寄存器,注册 usb 中断回调函数。在注册之前需要打开对应 USB 设备的宏定义,例如定义宏 ``BSP_USING_USB`` 方可使用 USB 设备。
+
+.. code-block:: C
+
+ struct device *usb_dc_init(void)
+ {
+ usb_dc_register(USB_INDEX, "usb", DEVICE_OFLAG_RDWR);
+ usb = device_find("usb");
+ device_set_callback(usb, usb_dc_event_callback);
+ device_open(usb, 0);
+ return usb;
+ }
+
+- device 返回 USB 设备句柄
+
+.. note::中断处理函数则是调用 ``usbd_event_notify_handler``
+
+USB DEVICE 应用层接口
+------------------------
+
+USB DEVICE 通用接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+**usbd_desc_register**
+""""""""""""""""""""""""""""""""""""
+
+**usbd_msosv1_desc_register**
+""""""""""""""""""""""""""""""""""""
+
+**usbd_class_add_interface**
+""""""""""""""""""""""""""""""""""""
+
+**usbd_interface_add_endpoint**
+""""""""""""""""""""""""""""""""""""
+
+**usb_device_is_configured**
+""""""""""""""""""""""""""""""""""""
+
+USB Device CDC 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+USB Device MSC 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+USB Device HID 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+USB Device AUDIO 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+USB Device VIDEO 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/class/audio/usbd_audio.c b/class/audio/usbd_audio.c
new file mode 100644
index 00000000..e48fd354
--- /dev/null
+++ b/class/audio/usbd_audio.c
@@ -0,0 +1,96 @@
+#include "usbd_core.h"
+#include "usbd_audio.h"
+
+struct usbd_audio_control_info audio_control_info = { 0xdb00, 0x0000, 0x0100, 0xf600, 0 };
+
+int audio_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("Class request:"
+ "bRequest 0x%02x, bmRequestType 0x%02x len %d",
+ setup->bRequest, setup->bmRequestType, *len);
+
+ switch (setup->bRequest) {
+ case AUDIO_REQUEST_SET_CUR:
+
+ if (setup->wValueL == 0x01) {
+ if (setup->wValueH == AUDIO_FU_CONTROL_MUTE) {
+ memcpy(&audio_control_info.mute, *data, *len);
+ } else if (setup->wValueH == AUDIO_FU_CONTROL_VOLUME) {
+ memcpy(&audio_control_info.vol_current, *data, *len);
+ USBD_LOG_DBG("vol:0x%x\r\n", audio_control_info.vol_current);
+ }
+ }
+
+ break;
+
+ case AUDIO_REQUEST_GET_CUR:
+ if (setup->wValueH == AUDIO_FU_CONTROL_MUTE) {
+ *data = (uint8_t *)&audio_control_info.mute;
+ *len = 1;
+ } else if (setup->wValueH == AUDIO_FU_CONTROL_VOLUME) {
+ *data = (uint8_t *)&audio_control_info.vol_current;
+ *len = 2;
+ }
+
+ break;
+
+ case AUDIO_REQUEST_SET_RES:
+ break;
+
+ case AUDIO_REQUEST_GET_MIN:
+ *data = (uint8_t *)&audio_control_info.vol_min;
+ *len = 2;
+ break;
+
+ case AUDIO_REQUEST_GET_MAX:
+ *data = (uint8_t *)&audio_control_info.vol_max;
+ *len = 2;
+ break;
+
+ case AUDIO_REQUEST_GET_RES:
+ *data = (uint8_t *)&audio_control_info.vol_res;
+ *len = 2;
+ break;
+
+ default:
+ USBD_LOG_ERR("Unhandled request 0x%02x", setup->bRequest);
+ break;
+ }
+
+ return 0;
+}
+
+void audio_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+
+ break;
+
+ case USB_EVENT_SOF:
+ break;
+
+ case USB_EVENT_SET_INTERFACE:
+ usbd_audio_set_interface_callback(((uint8_t *)arg)[3]);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void usbd_audio_add_interface(usbd_class_t *class, usbd_interface_t *intf)
+{
+ static usbd_class_t *last_class = NULL;
+
+ if (last_class != class) {
+ last_class = class;
+ usbd_class_register(class);
+ }
+
+ intf->class_handler = audio_class_request_handler;
+ intf->custom_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = audio_notify_handler;
+ usbd_class_add_interface(class, intf);
+} \ No newline at end of file
diff --git a/class/audio/usbd_audio.h b/class/audio/usbd_audio.h
new file mode 100644
index 00000000..6ce0dc87
--- /dev/null
+++ b/class/audio/usbd_audio.h
@@ -0,0 +1,277 @@
+/**
+ * @file
+ * @brief USB Audio Device Class public header
+ *
+ * Header follows below documentation:
+ * - USB Device Class Definition for Audio Devices (audio10.pdf)
+ *
+ * Additional documentation considered a part of USB Audio v1.0:
+ * - USB Device Class Definition for Audio Data Formats (frmts10.pdf)
+ * - USB Device Class Definition for Terminal Types (termt10.pdf)
+ */
+
+#ifndef _USBD_AUDIO_H_
+#define _USBD_AUDIO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Audio Interface Subclass Codes
+ * Refer to Table A-2 from audio10.pdf
+ */
+#define AUDIO_SUBCLASS_UNDEFINED 0x00
+#define AUDIO_SUBCLASS_AUDIOCONTROL 0x01
+#define AUDIO_SUBCLASS_AUDIOSTREAMING 0x02
+#define AUDIO_SUBCLASS_MIDISTREAMING 0x03
+
+#define AUDIO_PROTOCOL_UNDEFINED 0x00U
+
+#define AUDIO_ENDPOINT_GENERAL 0x01U
+
+/** Audio Class-Specific Control Interface Descriptor Subtypes
+ * Refer to Table A-5 from audio10.pdf
+ */
+#define AUDIO_CONTROL_UNDEFINED 0x01U
+#define AUDIO_CONTROL_HEADER 0x01U
+#define AUDIO_CONTROL_INPUT_TERMINAL 0x02U
+#define AUDIO_CONTROL_OUTPUT_TERMINAL 0x03U
+#define AUDIO_CONTROL_MIXER_UNIT 0x04U
+#define AUDIO_CONTROL_SELECTOR_UNIT 0x05U
+#define AUDIO_CONTROL_FEATURE_UNIT 0x06U
+#define AUDIO_CONTROL_PROCESSING_UNIT 0x07U
+#define AUDIO_CONTROL_EXTENSION_UNIT 0x08U
+
+/** Audio Class-Specific AS Interface Descriptor Subtypes
+ * Refer to Table A-6 from audio10.pdf
+ */
+#define AUDIO_STREAMING_UNDEFINED 0x00U
+#define AUDIO_STREAMING_GENERAL 0x01U
+#define AUDIO_STREAMING_FORMAT_TYPE 0x02U
+#define AUDIO_STREAMING_FORMAT_SPECIFIC 0x03U
+
+/** Audio Class-Specific Request Codes
+ * Refer to Table A-9 from audio10.pdf
+ */
+#define AUDIO_REQUEST_UNDEFINED 0x00
+#define AUDIO_REQUEST_SET_CUR 0x01
+#define AUDIO_REQUEST_GET_CUR 0x81
+#define AUDIO_REQUEST_SET_MIN 0x02
+#define AUDIO_REQUEST_GET_MIN 0x82
+#define AUDIO_REQUEST_SET_MAX 0x03
+#define AUDIO_REQUEST_GET_MAX 0x83
+#define AUDIO_REQUEST_SET_RES 0x04
+#define AUDIO_REQUEST_GET_RES 0x84
+#define AUDIO_REQUEST_SET_MEM 0x05
+#define AUDIO_REQUEST_GET_MEM 0x85
+#define AUDIO_REQUEST_GET_STAT 0xFF
+
+/* Feature Unit Control Bits */
+#define AUDIO_CONTROL_MUTE 0x0001
+#define AUDIO_CONTROL_VOLUME 0x0002
+#define AUDIO_CONTROL_BASS 0x0004
+#define AUDIO_CONTROL_MID 0x0008
+#define AUDIO_CONTROL_TREBLE 0x0010
+#define AUDIO_CONTROL_GRAPHIC_EQUALIZER 0x0020
+#define AUDIO_CONTROL_AUTOMATIC_GAIN 0x0040
+#define AUDIO_CONTROL_DEALY 0x0080
+#define AUDIO_CONTROL_BASS_BOOST 0x0100
+#define AUDIO_CONTROL_LOUDNESS 0x0200
+
+/** Feature Unit Control Selectors
+ * Refer to Table A-11 from audio10.pdf
+ */
+#define AUDIO_FU_CONTROL_MUTE 0x01
+#define AUDIO_FU_CONTROL_VOLUME 0x02
+#define AUDIO_FU_CONTROL_BASS 0x03
+#define AUDIO_FU_CONTROL_MID 0x04
+#define AUDIO_FU_CONTROL_TREBLE 0x05
+#define AUDIO_FU_CONTROL_GRAPHIC_EQUALIZER 0x06
+#define AUDIO_FU_CONTROL_AUTOMATIC_GAIN 0x07
+#define AUDIO_FU_CONTROL_DELAY 0x08
+#define AUDIO_FU_CONTROL_BASS_BOOST 0x09
+#define AUDIO_FU_CONTROL_LOUDNESS 0x0A
+
+/* Audio Descriptor Types */
+#define AUDIO_UNDEFINED_DESCRIPTOR_TYPE 0x20
+#define AUDIO_DEVICE_DESCRIPTOR_TYPE 0x21
+#define AUDIO_CONFIGURATION_DESCRIPTOR_TYPE 0x22
+#define AUDIO_STRING_DESCRIPTOR_TYPE 0x23
+#define AUDIO_INTERFACE_DESCRIPTOR_TYPE 0x24
+#define AUDIO_ENDPOINT_DESCRIPTOR_TYPE 0x25
+
+/* Audio Data Format Type I Codes */
+#define AUDIO_FORMAT_TYPE_I_UNDEFINED 0x0000
+#define AUDIO_FORMAT_PCM 0x0001
+#define AUDIO_FORMAT_PCM8 0x0002
+#define AUDIO_FORMAT_IEEE_FLOAT 0x0003
+#define AUDIO_FORMAT_ALAW 0x0004
+#define AUDIO_FORMAT_MULAW 0x0005
+
+/* Predefined Audio Channel Configuration Bits */
+#define AUDIO_CHANNEL_M 0x0000 /* Mono */
+#define AUDIO_CHANNEL_L 0x0001 /* Left Front */
+#define AUDIO_CHANNEL_R 0x0002 /* Right Front */
+#define AUDIO_CHANNEL_C 0x0004 /* Center Front */
+#define AUDIO_CHANNEL_LFE 0x0008 /* Low Freq. Enhance. */
+#define AUDIO_CHANNEL_LS 0x0010 /* Left Surround */
+#define AUDIO_CHANNEL_RS 0x0020 /* Right Surround */
+#define AUDIO_CHANNEL_LC 0x0040 /* Left of Center */
+#define AUDIO_CHANNEL_RC 0x0080 /* Right of Center */
+#define AUDIO_CHANNEL_S 0x0100 /* Surround */
+#define AUDIO_CHANNEL_SL 0x0200 /* Side Left */
+#define AUDIO_CHANNEL_SR 0x0400 /* Side Right */
+#define AUDIO_CHANNEL_T 0x0800 /* Top */
+
+#define AUDIO_FORMAT_TYPE_I 0x01
+#define AUDIO_FORMAT_TYPE_II 0x02
+#define AUDIO_FORMAT_TYPE_III 0x03
+
+/** USB Terminal Types
+ * Refer to Table 2-1 - Table 2-4 from termt10.pdf
+ */
+enum usb_audio_terminal_types {
+ /* USB Terminal Types */
+ USB_AUDIO_USB_UNDEFINED = 0x0100,
+ USB_AUDIO_USB_STREAMING = 0x0101,
+ USB_AUDIO_USB_VENDOR_SPEC = 0x01FF,
+
+ /* Input Terminal Types */
+ USB_AUDIO_IN_UNDEFINED = 0x0200,
+ USB_AUDIO_IN_MICROPHONE = 0x0201,
+ USB_AUDIO_IN_DESKTOP_MIC = 0x0202,
+ USB_AUDIO_IN_PERSONAL_MIC = 0x0203,
+ USB_AUDIO_IN_OM_DIR_MIC = 0x0204,
+ USB_AUDIO_IN_MIC_ARRAY = 0x0205,
+ USB_AUDIO_IN_PROC_MIC_ARRAY = 0x0205,
+
+ /* Output Terminal Types */
+ USB_AUDIO_OUT_UNDEFINED = 0x0300,
+ USB_AUDIO_OUT_SPEAKER = 0x0301,
+ USB_AUDIO_OUT_HEADPHONES = 0x0302,
+ USB_AUDIO_OUT_HEAD_AUDIO = 0x0303,
+ USB_AUDIO_OUT_DESKTOP_SPEAKER = 0x0304,
+ USB_AUDIO_OUT_ROOM_SPEAKER = 0x0305,
+ USB_AUDIO_OUT_COMM_SPEAKER = 0x0306,
+ USB_AUDIO_OUT_LOW_FREQ_SPEAKER = 0x0307,
+
+ /* Bi-directional Terminal Types */
+ USB_AUDIO_IO_UNDEFINED = 0x0400,
+ USB_AUDIO_IO_HANDSET = 0x0401,
+ USB_AUDIO_IO_HEADSET = 0x0402,
+ USB_AUDIO_IO_SPEAKERPHONE_ECHO_NONE = 0x0403,
+ USB_AUDIO_IO_SPEAKERPHONE_ECHO_SUP = 0x0404,
+ USB_AUDIO_IO_SPEAKERPHONE_ECHO_CAN = 0x0405,
+};
+
+/**
+ * @warning Size of baInterface is 2 just to make it useable
+ * for all kind of devices: headphones, microphone and headset.
+ * Actual size of the struct should be checked by reading
+ * .bLength.
+ */
+struct cs_ac_if_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint16_t bcdADC;
+ uint16_t wTotalLength;
+ uint8_t bInCollection;
+ uint8_t baInterfaceNr[2];
+} __packed;
+
+struct input_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bNrChannels;
+ uint16_t wChannelConfig;
+ uint8_t iChannelNames;
+ uint8_t iTerminal;
+} __packed;
+
+/**
+ * @note Size of Feature unit descriptor is not fixed.
+ * This structure is just a helper not a common type.
+ */
+struct feature_unit_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bUnitID;
+ uint8_t bSourceID;
+ uint8_t bControlSize;
+ uint16_t bmaControls[1];
+} __packed;
+
+struct output_terminal_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalID;
+ uint16_t wTerminalType;
+ uint8_t bAssocTerminal;
+ uint8_t bSourceID;
+ uint8_t iTerminal;
+} __packed;
+
+struct as_cs_interface_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bTerminalLink;
+ uint8_t bDelay;
+ uint16_t wFormatTag;
+} __packed;
+
+struct format_type_i_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bFormatType;
+ uint8_t bNrChannels;
+ uint8_t bSubframeSize;
+ uint8_t bBitResolution;
+ uint8_t bSamFreqType;
+ uint8_t tSamFreq[3];
+} __packed;
+
+struct std_as_ad_endpoint_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bEndpointAddress;
+ uint8_t bmAttributes;
+ uint16_t wMaxPacketSize;
+ uint8_t bInterval;
+ uint8_t bRefresh;
+ uint8_t bSynchAddress;
+} __packed;
+
+struct cs_as_ad_ep_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bmAttributes;
+ uint8_t bLockDelayUnits;
+ uint16_t wLockDelay;
+} __packed;
+
+struct usbd_audio_control_info {
+ uint16_t vol_min;
+ uint16_t vol_max;
+ uint16_t vol_res;
+ uint16_t vol_current;
+ uint8_t mute;
+};
+
+void usbd_audio_add_interface(usbd_class_t *class, usbd_interface_t *intf);
+void usbd_audio_set_interface_callback(uint8_t value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _USB_AUDIO_H_ */
diff --git a/class/cdc/usbd_cdc.c b/class/cdc/usbd_cdc.c
new file mode 100644
index 00000000..69d8d307
--- /dev/null
+++ b/class/cdc/usbd_cdc.c
@@ -0,0 +1,168 @@
+/**
+ * @file usbd_cdc.c
+ * @brief
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "usbd_core.h"
+#include "usbd_cdc.h"
+
+const char *stop_name[] = { "1", "1.5", "2" };
+const char *parity_name[] = { "N", "O", "E", "M", "S" };
+
+/* Device data structure */
+struct cdc_acm_cfg_private {
+ /* CDC ACM line coding properties. LE order */
+ struct cdc_line_coding line_coding;
+ /* CDC ACM line state bitmap, DTE side */
+ uint8_t line_state;
+ /* CDC ACM serial state bitmap, DCE side */
+ uint8_t serial_state;
+ /* CDC ACM notification sent status */
+ uint8_t notification_sent;
+ /* CDC ACM configured flag */
+ bool configured;
+ /* CDC ACM suspended flag */
+ bool suspended;
+ uint32_t uart_first_init_flag;
+
+} usbd_cdc_acm_cfg;
+
+static void usbd_cdc_acm_reset(void)
+{
+ usbd_cdc_acm_cfg.line_coding.dwDTERate = 2000000;
+ usbd_cdc_acm_cfg.line_coding.bDataBits = 8;
+ usbd_cdc_acm_cfg.line_coding.bParityType = 0;
+ usbd_cdc_acm_cfg.line_coding.bCharFormat = 0;
+ usbd_cdc_acm_cfg.configured = false;
+ usbd_cdc_acm_cfg.uart_first_init_flag = 0;
+}
+
+/**
+ * @brief Handler called for Class requests not handled by the USB stack.
+ *
+ * @param pSetup Information about the request to execute.
+ * @param len Size of the buffer.
+ * @param data Buffer containing the request result.
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+static int cdc_acm_class_request_handler(struct usb_setup_packet *pSetup, uint8_t **data, uint32_t *len)
+{
+ switch (pSetup->bRequest) {
+ case CDC_REQUEST_SET_LINE_CODING:
+
+ /*******************************************************************************/
+ /* Line Coding Structure */
+ /*-----------------------------------------------------------------------------*/
+ /* Offset | Field | Size | Value | Description */
+ /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/
+ /* 4 | bCharFormat | 1 | Number | Stop bits */
+ /* 0 - 1 Stop bit */
+ /* 1 - 1.5 Stop bits */
+ /* 2 - 2 Stop bits */
+ /* 5 | bParityType | 1 | Number | Parity */
+ /* 0 - None */
+ /* 1 - Odd */
+ /* 2 - Even */
+ /* 3 - Mark */
+ /* 4 - Space */
+ /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */
+ /*******************************************************************************/
+ if (usbd_cdc_acm_cfg.uart_first_init_flag == 0) {
+ usbd_cdc_acm_cfg.uart_first_init_flag = 1;
+ return 0;
+ }
+
+ memcpy(&usbd_cdc_acm_cfg.line_coding, *data, sizeof(usbd_cdc_acm_cfg.line_coding));
+ USBD_LOG_DBG("CDC_SET_LINE_CODING <%d %d %s %s>\r\n",
+ usbd_cdc_acm_cfg.line_coding.dwDTERate,
+ usbd_cdc_acm_cfg.line_coding.bDataBits,
+ parity_name[usbd_cdc_acm_cfg.line_coding.bParityType],
+ stop_name[usbd_cdc_acm_cfg.line_coding.bCharFormat]);
+ usbd_cdc_acm_set_line_coding(usbd_cdc_acm_cfg.line_coding.dwDTERate, usbd_cdc_acm_cfg.line_coding.bDataBits,
+ usbd_cdc_acm_cfg.line_coding.bParityType, usbd_cdc_acm_cfg.line_coding.bCharFormat);
+ break;
+
+ case CDC_REQUEST_SET_CONTROL_LINE_STATE:
+ usbd_cdc_acm_cfg.line_state = (uint8_t)pSetup->wValue;
+ bool dtr = (pSetup->wValue & 0x01);
+ bool rts = (pSetup->wValue & 0x02);
+ USBD_LOG_DBG("DTR 0x%x,RTS 0x%x\r\n",
+ dtr, rts);
+ usbd_cdc_acm_set_dtr(dtr);
+ usbd_cdc_acm_set_rts(rts);
+ break;
+
+ case CDC_REQUEST_GET_LINE_CODING:
+ *data = (uint8_t *)(&usbd_cdc_acm_cfg.line_coding);
+ *len = sizeof(usbd_cdc_acm_cfg.line_coding);
+ USBD_LOG_DBG("CDC_GET_LINE_CODING %d %d %d %d\r\n",
+ usbd_cdc_acm_cfg.line_coding.dwDTERate,
+ usbd_cdc_acm_cfg.line_coding.bCharFormat,
+ usbd_cdc_acm_cfg.line_coding.bParityType,
+ usbd_cdc_acm_cfg.line_coding.bDataBits);
+ break;
+
+ default:
+ USBD_LOG_DBG("CDC ACM request 0x%x, value 0x%x\r\n",
+ pSetup->bRequest, pSetup->wValue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void cdc_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+ usbd_cdc_acm_reset();
+ break;
+
+ default:
+ break;
+ }
+}
+
+__weak void usbd_cdc_acm_set_line_coding(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits)
+{
+}
+__weak void usbd_cdc_acm_set_dtr(bool dtr)
+{
+}
+__weak void usbd_cdc_acm_set_rts(bool rts)
+{
+}
+
+void usbd_cdc_add_acm_interface(usbd_class_t *class, usbd_interface_t *intf)
+{
+ static usbd_class_t *last_class = NULL;
+
+ if (last_class != class) {
+ last_class = class;
+ usbd_class_register(class);
+ }
+
+ intf->class_handler = cdc_acm_class_request_handler;
+ intf->custom_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = cdc_notify_handler;
+ usbd_class_add_interface(class, intf);
+}
diff --git a/class/cdc/usbd_cdc.h b/class/cdc/usbd_cdc.h
new file mode 100644
index 00000000..a8913162
--- /dev/null
+++ b/class/cdc/usbd_cdc.h
@@ -0,0 +1,374 @@
+/**
+ * @file
+ * @brief USB Communications Device Class (CDC) public header
+ *
+ * Header follows the Class Definitions for
+ * Communications Devices Specification (CDC120-20101103-track.pdf),
+ * PSTN Devices Specification (PSTN120.pdf) and
+ * Ethernet Control Model Devices Specification (ECM120.pdf).
+ * Header is limited to ACM and ECM Subclasses.
+ */
+
+#ifndef _USBD_CDC_H
+#define _USBD_CDC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*------------------------------------------------------------------------------
+ * Definitions based on usbcdc11.pdf (www.usb.org)
+ *----------------------------------------------------------------------------*/
+/* Communication device class specification version 1.10 */
+#define CDC_V1_10 0x0110U
+// Communication device class specification version 1.2
+#define CDC_V1_2_0 0x0120U
+
+/* Communication interface class code */
+/* (usbcdc11.pdf, 4.2, Table 15) */
+#define CDC_COMMUNICATION_INTERFACE_CLASS 0x02U
+
+/* Communication interface class subclass codes */
+/* (usbcdc11.pdf, 4.3, Table 16) */
+#define CDC_DIRECT_LINE_CONTROL_MODEL 0x01U
+#define CDC_ABSTRACT_CONTROL_MODEL 0x02U
+#define CDC_TELEPHONE_CONTROL_MODEL 0x03U
+#define CDC_MULTI_CHANNEL_CONTROL_MODEL 0x04U
+#define CDC_CAPI_CONTROL_MODEL 0x05U
+#define CDC_ETHERNET_NETWORKING_CONTROL_MODEL 0x06U
+#define CDC_ATM_NETWORKING_CONTROL_MODEL 0x07U
+#define CDC_WIRELESS_HANDSET_CONTROL_MODEL 0x08U
+#define CDC_DEVICE_MANAGEMENT 0x09U
+#define CDC_MOBILE_DIRECT_LINE_MODEL 0x0AU
+#define CDC_OBEX 0x0BU
+#define CDC_ETHERNET_EMULATION_MODEL 0x0CU
+#define CDC_NETWORK_CONTROL_MODEL 0x0DU
+
+/* Communication interface class control protocol codes */
+/* (usbcdc11.pdf, 4.4, Table 17) */
+#define CDC_COMMON_PROTOCOL_NONE 0x00U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS 0x01U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101 0x02U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_PCCA_101_AND_ANNEXO 0x03U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_GSM_707 0x04U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_3GPP_27007 0x05U
+#define CDC_COMMON_PROTOCOL_AT_COMMANDS_CDMA 0x06U
+#define CDC_COMMON_PROTOCOL_ETHERNET_EMULATION_MODEL 0x07U
+// NCM Communication Interface Protocol Codes
+// (usbncm10.pdf, 4.2, Table 4-2)
+#define CDC_NCM_PROTOCOL_NONE 0x00U
+#define CDC_NCM_PROTOCOL_OEM 0xFEU
+
+/* Data interface class code */
+/* (usbcdc11.pdf, 4.5, Table 18) */
+#define CDC_DATA_INTERFACE_CLASS 0x0A
+
+/* Data interface class protocol codes */
+/* (usbcdc11.pdf, 4.7, Table 19) */
+#define CDC_DATA_PROTOCOL_ISDN_BRI 0x30
+#define CDC_DATA_PROTOCOL_HDLC 0x31
+#define CDC_DATA_PROTOCOL_TRANSPARENT 0x32
+#define CDC_DATA_PROTOCOL_Q921_MANAGEMENT 0x50
+#define CDC_DATA_PROTOCOL_Q921_DATA_LINK 0x51
+#define CDC_DATA_PROTOCOL_Q921_MULTIPLEXOR 0x52
+#define CDC_DATA_PROTOCOL_V42 0x90
+#define CDC_DATA_PROTOCOL_EURO_ISDN 0x91
+#define CDC_DATA_PROTOCOL_V24_RATE_ADAPTATION 0x92
+#define CDC_DATA_PROTOCOL_CAPI 0x93
+#define CDC_DATA_PROTOCOL_HOST_BASED_DRIVER 0xFD
+#define CDC_DATA_PROTOCOL_DESCRIBED_IN_PUFD 0xFE
+
+/* Type values for bDescriptorType field of functional descriptors */
+/* (usbcdc11.pdf, 5.2.3, Table 24) */
+#define CDC_CS_INTERFACE 0x24
+#define CDC_CS_ENDPOINT 0x25
+
+/* Type values for bDescriptorSubtype field of functional descriptors */
+/* (usbcdc11.pdf, 5.2.3, Table 25) */
+#define CDC_FUNC_DESC_HEADER 0x00
+#define CDC_FUNC_DESC_CALL_MANAGEMENT 0x01
+#define CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT 0x02
+#define CDC_FUNC_DESC_DIRECT_LINE_MANAGEMENT 0x03
+#define CDC_FUNC_DESC_TELEPHONE_RINGER 0x04
+#define CDC_FUNC_DESC_REPORTING_CAPABILITIES 0x05
+#define CDC_FUNC_DESC_UNION 0x06
+#define CDC_FUNC_DESC_COUNTRY_SELECTION 0x07
+#define CDC_FUNC_DESC_TELEPHONE_OPERATIONAL_MODES 0x08
+#define CDC_FUNC_DESC_USB_TERMINAL 0x09
+#define CDC_FUNC_DESC_NETWORK_CHANNEL 0x0A
+#define CDC_FUNC_DESC_PROTOCOL_UNIT 0x0B
+#define CDC_FUNC_DESC_EXTENSION_UNIT 0x0C
+#define CDC_FUNC_DESC_MULTI_CHANNEL_MANAGEMENT 0x0D
+#define CDC_FUNC_DESC_CAPI_CONTROL_MANAGEMENT 0x0E
+#define CDC_FUNC_DESC_ETHERNET_NETWORKING 0x0F
+#define CDC_FUNC_DESC_ATM_NETWORKING 0x10
+#define CDC_FUNC_DESC_WIRELESS_HANDSET_CONTROL_MODEL 0x11
+#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL 0x12
+#define CDC_FUNC_DESC_MOBILE_DIRECT_LINE_MODEL_DETAIL 0x13
+#define CDC_FUNC_DESC_DEVICE_MANAGEMENT_MODEL 0x14
+#define CDC_FUNC_DESC_OBEX 0x15
+#define CDC_FUNC_DESC_COMMAND_SET 0x16
+#define CDC_FUNC_DESC_COMMAND_SET_DETAIL 0x17
+#define CDC_FUNC_DESC_TELEPHONE_CONTROL_MODEL 0x18
+#define CDC_FUNC_DESC_OBEX_SERVICE_IDENTIFIER 0x19
+
+/* CDC class-specific request codes */
+/* (usbcdc11.pdf, 6.2, Table 46) */
+/* see Table 45 for info about the specific requests. */
+#define CDC_REQUEST_SEND_ENCAPSULATED_COMMAND 0x00
+#define CDC_REQUEST_GET_ENCAPSULATED_RESPONSE 0x01
+#define CDC_REQUEST_SET_COMM_FEATURE 0x02
+#define CDC_REQUEST_GET_COMM_FEATURE 0x03
+#define CDC_REQUEST_CLEAR_COMM_FEATURE 0x04
+#define CDC_REQUEST_SET_AUX_LINE_STATE 0x10
+#define CDC_REQUEST_SET_HOOK_STATE 0x11
+#define CDC_REQUEST_PULSE_SETUP 0x12
+#define CDC_REQUEST_SEND_PULSE 0x13
+#define CDC_REQUEST_SET_PULSE_TIME 0x14
+#define CDC_REQUEST_RING_AUX_JACK 0x15
+#define CDC_REQUEST_SET_LINE_CODING 0x20
+#define CDC_REQUEST_GET_LINE_CODING 0x21
+#define CDC_REQUEST_SET_CONTROL_LINE_STATE 0x22
+#define CDC_REQUEST_SEND_BREAK 0x23
+#define CDC_REQUEST_SET_RINGER_PARMS 0x30
+#define CDC_REQUEST_GET_RINGER_PARMS 0x31
+#define CDC_REQUEST_SET_OPERATION_PARMS 0x32
+#define CDC_REQUEST_GET_OPERATION_PARMS 0x33
+#define CDC_REQUEST_SET_LINE_PARMS 0x34
+#define CDC_REQUEST_GET_LINE_PARMS 0x35
+#define CDC_REQUEST_DIAL_DIGITS 0x36
+#define CDC_REQUEST_SET_UNIT_PARAMETER 0x37
+#define CDC_REQUEST_GET_UNIT_PARAMETER 0x38
+#define CDC_REQUEST_CLEAR_UNIT_PARAMETER 0x39
+#define CDC_REQUEST_GET_PROFILE 0x3A
+#define CDC_REQUEST_SET_ETHERNET_MULTICAST_FILTERS 0x40
+#define CDC_REQUEST_SET_ETHERNET_PMP_FILTER 0x41
+#define CDC_REQUEST_GET_ETHERNET_PMP_FILTER 0x42
+#define CDC_REQUEST_SET_ETHERNET_PACKET_FILTER 0x43
+#define CDC_REQUEST_GET_ETHERNET_STATISTIC 0x44
+#define CDC_REQUEST_SET_ATM_DATA_FORMAT 0x50
+#define CDC_REQUEST_GET_ATM_DEVICE_STATISTICS 0x51
+#define CDC_REQUEST_SET_ATM_DEFAULT_VC 0x52
+#define CDC_REQUEST_GET_ATM_VC_STATISTICS 0x53
+
+/* Communication feature selector codes */
+/* (usbcdc11.pdf, 6.2.2..6.2.4, Table 47) */
+#define CDC_ABSTRACT_STATE 0x01
+#define CDC_COUNTRY_SETTING 0x02
+
+/** Control Signal Bitmap Values for SetControlLineState */
+#define SET_CONTROL_LINE_STATE_RTS 0x02
+#define SET_CONTROL_LINE_STATE_DTR 0x01
+
+/* Feature Status returned for ABSTRACT_STATE Selector */
+/* (usbcdc11.pdf, 6.2.3, Table 48) */
+#define CDC_IDLE_SETTING (1 << 0)
+#define CDC_DATA_MULTPLEXED_STATE (1 << 1)
+
+/* Control signal bitmap values for the SetControlLineState request */
+/* (usbcdc11.pdf, 6.2.14, Table 51) */
+#define CDC_DTE_PRESENT (1 << 0)
+#define CDC_ACTIVATE_CARRIER (1 << 1)
+
+/* CDC class-specific notification codes */
+/* (usbcdc11.pdf, 6.3, Table 68) */
+/* see Table 67 for Info about class-specific notifications */
+#define CDC_NOTIFICATION_NETWORK_CONNECTION 0x00
+#define CDC_RESPONSE_AVAILABLE 0x01
+#define CDC_AUX_JACK_HOOK_STATE 0x08
+#define CDC_RING_DETECT 0x09
+#define CDC_NOTIFICATION_SERIAL_STATE 0x20
+#define CDC_CALL_STATE_CHANGE 0x28
+#define CDC_LINE_STATE_CHANGE 0x29
+#define CDC_CONNECTION_SPEED_CHANGE 0x2A
+
+/* UART state bitmap values (Serial state notification). */
+/* (usbcdc11.pdf, 6.3.5, Table 69) */
+#define CDC_SERIAL_STATE_OVERRUN (1 << 6) /* receive data overrun error has occurred */
+#define CDC_SERIAL_STATE_OVERRUN_Pos (6)
+#define CDC_SERIAL_STATE_OVERRUN_Msk (1 << CDC_SERIAL_STATE_OVERRUN_Pos)
+#define CDC_SERIAL_STATE_PARITY (1 << 5) /* parity error has occurred */
+#define CDC_SERIAL_STATE_PARITY_Pos (5)
+#define CDC_SERIAL_STATE_PARITY_Msk (1 << CDC_SERIAL_STATE_PARITY_Pos)
+#define CDC_SERIAL_STATE_FRAMING (1 << 4) /* framing error has occurred */
+#define CDC_SERIAL_STATE_FRAMING_Pos (4)
+#define CDC_SERIAL_STATE_FRAMING_Msk (1 << CDC_SERIAL_STATE_FRAMING_Pos)
+#define CDC_SERIAL_STATE_RING (1 << 3) /* state of ring signal detection */
+#define CDC_SERIAL_STATE_RING_Pos (3)
+#define CDC_SERIAL_STATE_RING_Msk (1 << CDC_SERIAL_STATE_RING_Pos)
+#define CDC_SERIAL_STATE_BREAK (1 << 2) /* state of break detection */
+#define CDC_SERIAL_STATE_BREAK_Pos (2)
+#define CDC_SERIAL_STATE_BREAK_Msk (1 << CDC_SERIAL_STATE_BREAK_Pos)
+#define CDC_SERIAL_STATE_TX_CARRIER (1 << 1) /* state of transmission carrier */
+#define CDC_SERIAL_STATE_TX_CARRIER_Pos (1)
+#define CDC_SERIAL_STATE_TX_CARRIER_Msk (1 << CDC_SERIAL_STATE_TX_CARRIER_Pos)
+#define CDC_SERIAL_STATE_RX_CARRIER (1 << 0) /* state of receiver carrier */
+#define CDC_SERIAL_STATE_RX_CARRIER_Pos (0)
+#define CDC_SERIAL_STATE_RX_CARRIER_Msk (1 << CDC_SERIAL_STATE_RX_CARRIER_Pos)
+
+/*------------------------------------------------------------------------------
+ * Structures based on usbcdc11.pdf (www.usb.org)
+ *----------------------------------------------------------------------------*/
+
+/* Header functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.1) */
+/* This header must precede any list of class-specific descriptors. */
+struct cdc_header_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* Header functional descriptor subtype */
+ uint16_t bcdCDC; /* USB CDC specification release version */
+} __packed;
+
+/* Call management functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.2) */
+/* Describes the processing of calls for the communication class interface. */
+struct cdc_call_management_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* call management functional descriptor subtype */
+ uint8_t bmCapabilities; /* capabilities that this configuration supports */
+ uint8_t bDataInterface; /* interface number of the data class interface used for call management (optional) */
+} __packed;
+
+/* Abstract control management functional descriptor */
+/* (usbcdc11.pdf, 5.2.3.3) */
+/* Describes the command supported by the communication interface class with the Abstract Control Model subclass code. */
+struct cdc_abstract_control_management_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* abstract control management functional descriptor subtype */
+ uint8_t bmCapabilities; /* capabilities supported by this configuration */
+} __packed;
+
+/* Union functional descriptors */
+/* (usbcdc11.pdf, 5.2.3.8) */
+/* Describes the relationship between a group of interfaces that can be considered to form a functional unit. */
+struct cdc_union_descriptor {
+ uint8_t bFunctionLength; /* size of this descriptor in bytes */
+ uint8_t bDescriptorType; /* CS_INTERFACE descriptor type */
+ uint8_t bDescriptorSubtype; /* union functional descriptor subtype */
+ uint8_t bMasterInterface; /* interface number designated as master */
+} __packed;
+
+/* Union functional descriptors with one slave interface */
+/* (usbcdc11.pdf, 5.2.3.8) */
+struct cdc_union_1slave_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t bControlInterface;
+ uint8_t bSubordinateInterface0;
+} __packed;
+
+/* Line coding structure for GET_LINE_CODING / SET_LINE_CODING class requests*/
+/* Format of the data returned when a GetLineCoding request is received */
+/* (usbcdc11.pdf, 6.2.13) */
+struct cdc_line_coding {
+ uint32_t dwDTERate; /* Data terminal rate in bits per second */
+ uint8_t bCharFormat; /* Number of stop bits */
+ uint8_t bParityType; /* Parity bit type */
+ uint8_t bDataBits; /* Number of data bits */
+} __packed;
+
+/** Data structure for the notification about SerialState */
+struct cdc_acm_notification {
+ uint8_t bmRequestType;
+ uint8_t bNotificationType;
+ uint16_t wValue;
+ uint16_t wIndex;
+ uint16_t wLength;
+ uint16_t data;
+} __packed;
+
+/** Ethernet Networking Functional Descriptor */
+struct cdc_ecm_descriptor {
+ uint8_t bFunctionLength;
+ uint8_t bDescriptorType;
+ uint8_t bDescriptorSubtype;
+ uint8_t iMACAddress;
+ uint32_t bmEthernetStatistics;
+ uint16_t wMaxSegmentSize;
+ uint16_t wNumberMCFilters;
+ uint8_t bNumberPowerFilters;
+} __packed;
+
+/*Length of template descriptor: 66 bytes*/
+#define CDC_ACM_DESCRIPTOR_LEN (8 + 9 + 5 + 5 + 4 + 5 + 7 + 9 + 7 + 7)
+
+#define CDC_ACM_DESCRIPTOR_INIT(bFirstInterface, int_ep, out_ep, in_ep, str_idx) \
+ /* Interface Associate */ \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ 0x02, /* bInterfaceCount */ \
+ USB_DEVICE_CLASS_CDC, /* bFunctionClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bFunctionSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bFunctionProtocol */ \
+ 0x00, /* iFunction */ /* CDC Control Interface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x01, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_CDC, /* bInterfaceClass */ \
+ CDC_ABSTRACT_CONTROL_MODEL, /* bInterfaceSubClass */ \
+ CDC_COMMON_PROTOCOL_AT_COMMANDS, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ /* CDC Header */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_HEADER, /* bDescriptorSubtype */ \
+ WBVAL(CDC_V1_10), /* bcdCDC */ /* CDC Call */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_CALL_MANAGEMENT, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bmCapabilities */ \
+ (uint8_t)(bFirstInterface + 1), /* bDataInterface */ /* CDC ACM: support line request */ \
+ 0x04, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_ABSTRACT_CONTROL_MANAGEMENT, /* bDescriptorSubtype */ \
+ 0x02, /* bmCapabilities */ /* CDC Union */ \
+ 0x05, /* bLength */ \
+ CDC_CS_INTERFACE, /* bDescriptorType */ \
+ CDC_FUNC_DESC_UNION, /* bDescriptorSubtype */ \
+ bFirstInterface, /* bMasterInterface */ \
+ (uint8_t)(bFirstInterface + 1), /* bSlaveInterface0 */ /* Endpoint Notification */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ int_ep, /* bEndpointAddress */ \
+ 0x03, /* bmAttributes */ \
+ 0x40, 0x00, /* wMaxPacketSize */ \
+ 0x01, /* bInterval */ /* CDC Data Interface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ (uint8_t)(bFirstInterface + 1), /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ CDC_DATA_INTERFACE_CLASS, /* bInterfaceClass */ \
+ 0x00, /* bInterfaceSubClass */ \
+ 0x00, /* bInterfaceProtocol */ \
+ 0x00, /* iInterface */ /* Endpoint Out */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ 0x40, 0x00, /* wMaxPacketSize */ \
+ 0x01, /* bInterval */ /* Endpoint In */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ 0x40, 0x00, /* wMaxPacketSize */ \
+ 0x01 /* bInterval */
+
+void usbd_cdc_add_acm_interface(usbd_class_t *class, usbd_interface_t *intf);
+
+void usbd_cdc_acm_set_line_coding(uint32_t baudrate, uint8_t databits, uint8_t parity, uint8_t stopbits);
+void usbd_cdc_acm_set_dtr(bool dtr);
+void usbd_cdc_acm_set_rts(bool rts);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USB_CDC_H_ */
diff --git a/class/hid/usbd_hid.c b/class/hid/usbd_hid.c
new file mode 100644
index 00000000..503753e2
--- /dev/null
+++ b/class/hid/usbd_hid.c
@@ -0,0 +1,227 @@
+/**
+ * @file usbd_hid.c
+ * @brief
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "usbd_core.h"
+#include "usbd_hid.h"
+
+#define HID_STATE_IDLE 0
+#define HID_STATE_BUSY 1
+
+struct usbd_hid_cfg_private {
+ const uint8_t *hid_descriptor;
+ const uint8_t *hid_report_descriptor;
+ uint32_t hid_report_descriptor_len;
+ uint32_t protocol;
+ uint32_t idle_state;
+ uint8_t hid_state;
+ uint8_t report;
+ uint8_t current_intf_num;
+ usb_slist_t list;
+} usbd_hid_cfg[4];
+
+static usb_slist_t usbd_hid_class_head = USB_SLIST_OBJECT_INIT(usbd_hid_class_head);
+
+static void usbd_hid_reset(void)
+{
+ usb_slist_t *i;
+ usb_slist_for_each(i, &usbd_hid_class_head)
+ {
+ struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list);
+ hid_intf->hid_state = HID_STATE_IDLE;
+ hid_intf->idle_state = 0;
+ hid_intf->protocol = 0;
+ }
+}
+
+int hid_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("Standard request:"
+ "bmRequestType 0x%02x, bRequest 0x%02x, len %d\r\n",
+ setup->bmRequestType, setup->bRequest, *len);
+
+ if (REQTYPE_GET_DIR(setup->bmRequestType) == USB_REQUEST_DEVICE_TO_HOST &&
+ setup->bRequest == USB_REQUEST_GET_DESCRIPTOR) {
+ uint8_t value = (uint8_t)(setup->wValue >> 8);
+ uint8_t intf_num = (uint8_t)setup->wIndex;
+
+ struct usbd_hid_cfg_private *current_hid_intf = NULL;
+ usb_slist_t *i;
+ usb_slist_for_each(i, &usbd_hid_class_head)
+ {
+ struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list);
+
+ if (hid_intf->current_intf_num == intf_num) {
+ current_hid_intf = hid_intf;
+ break;
+ }
+ }
+
+ if (current_hid_intf == NULL) {
+ return -2;
+ }
+
+ switch (value) {
+ case HID_DESCRIPTOR_TYPE_HID:
+ USBD_LOG("get HID Descriptor\r\n");
+ *data = (uint8_t *)current_hid_intf->hid_descriptor;
+ *len = current_hid_intf->hid_descriptor[0];
+ break;
+
+ case HID_DESCRIPTOR_TYPE_HID_REPORT:
+ USBD_LOG("get Report Descriptor\r\n");
+ *data = (uint8_t *)current_hid_intf->hid_report_descriptor;
+ *len = current_hid_intf->hid_report_descriptor_len;
+ break;
+
+ case HID_DESCRIPTOR_TYPE_HID_PHYSICAL:
+ USBD_LOG_DBG("get PHYSICAL Descriptor\r\n");
+
+ break;
+
+ default:
+ return -2;
+ }
+
+ return 0;
+ }
+
+ return -1;
+}
+
+int hid_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG("Class request:"
+ "bmRequestType 0x%02x bRequest 0x%02x, len %d\r\n",
+ setup->bmRequestType, setup->bRequest, *len);
+
+ struct usbd_hid_cfg_private *current_hid_intf = NULL;
+ usb_slist_t *i;
+ usb_slist_for_each(i, &usbd_hid_class_head)
+ {
+ struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list);
+ uint8_t intf_num = (uint8_t)setup->wIndex;
+ if (hid_intf->current_intf_num == intf_num) {
+ current_hid_intf = hid_intf;
+ break;
+ }
+ }
+
+ if (current_hid_intf == NULL) {
+ return -2;
+ }
+
+ switch (setup->bRequest) {
+ case HID_REQUEST_GET_REPORT:
+ *data = (uint8_t *)&current_hid_intf->report;
+ *len = 1;
+ break;
+ case HID_REQUEST_GET_IDLE:
+ *data = (uint8_t *)&current_hid_intf->idle_state;
+ *len = 1;
+ break;
+ case HID_REQUEST_GET_PROTOCOL:
+ *data = (uint8_t *)&current_hid_intf->protocol;
+ *len = 1;
+ break;
+ case HID_REQUEST_SET_REPORT:
+ current_hid_intf->report = **data;
+ break;
+ case HID_REQUEST_SET_IDLE:
+ current_hid_intf->idle_state = setup->wValueH;
+ break;
+ case HID_REQUEST_SET_PROTOCOL:
+ current_hid_intf->protocol = setup->wValueL;
+ break;
+
+ default:
+ USBD_LOG_ERR("Unhandled request 0x%02x\r\n", setup->bRequest);
+ break;
+ }
+
+ return 0;
+}
+
+static void hid_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+ usbd_hid_reset();
+ break;
+
+ default:
+ break;
+ }
+}
+
+void usbd_hid_reset_state(void)
+{
+ // usbd_hid_cfg.hid_state = HID_STATE_IDLE;
+}
+
+void usbd_hid_send_report(uint8_t ep, uint8_t *data, uint8_t len)
+{
+ // if(usbd_hid_cfg.hid_state == HID_STATE_IDLE)
+ // {
+ // usbd_hid_cfg.hid_state = HID_STATE_BUSY;
+ // usbd_ep_write(ep, data, len, NULL);
+ // }
+}
+
+void usbd_hid_descriptor_register(uint8_t intf_num, const uint8_t *desc)
+{
+ // usbd_hid_cfg.hid_descriptor = desc;
+}
+
+void usbd_hid_report_descriptor_register(uint8_t intf_num, const uint8_t *desc, uint32_t desc_len)
+{
+ usb_slist_t *i;
+ usb_slist_for_each(i, &usbd_hid_class_head)
+ {
+ struct usbd_hid_cfg_private *hid_intf = usb_slist_entry(i, struct usbd_hid_cfg_private, list);
+
+ if (hid_intf->current_intf_num == intf_num) {
+ hid_intf->hid_report_descriptor = desc;
+ hid_intf->hid_report_descriptor_len = desc_len;
+ return;
+ }
+ }
+}
+
+void usbd_hid_add_interface(usbd_class_t *class, usbd_interface_t *intf)
+{
+ static usbd_class_t *last_class = NULL;
+ static uint8_t hid_num = 0;
+ if (last_class != class) {
+ last_class = class;
+ usbd_class_register(class);
+ }
+
+ intf->class_handler = hid_class_request_handler;
+ intf->custom_handler = hid_custom_request_handler;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = hid_notify_handler;
+ usbd_class_add_interface(class, intf);
+
+ usbd_hid_cfg[hid_num].current_intf_num = intf->intf_num;
+ usb_slist_add_tail(&usbd_hid_class_head, &usbd_hid_cfg[hid_num].list);
+ hid_num++;
+}
diff --git a/class/hid/usbd_hid.h b/class/hid/usbd_hid.h
new file mode 100644
index 00000000..19ccb875
--- /dev/null
+++ b/class/hid/usbd_hid.h
@@ -0,0 +1,344 @@
+/**
+ * @file
+ * @brief USB Human Interface Device (HID) Class public header
+ *
+ * Header follows Device Class Definition for Human Interface Devices (HID)
+ * Version 1.11 document (HID1_11-1.pdf).
+ */
+#ifndef _USBD_HID_H_
+#define _USBD_HID_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* HID Class Descriptor Types */
+#define HID_DESCRIPTOR_TYPE_HID 0x21
+#define HID_DESCRIPTOR_TYPE_HID_REPORT 0x22
+#define HID_DESCRIPTOR_TYPE_HID_PHYSICAL 0x23
+
+/* HID Class Specific Requests */
+#define HID_REQUEST_GET_REPORT 0x01
+#define HID_REQUEST_GET_IDLE 0x02
+#define HID_REQUEST_GET_PROTOCOL 0x03
+#define HID_REQUEST_SET_REPORT 0x09
+#define HID_REQUEST_SET_IDLE 0x0A
+#define HID_REQUEST_SET_PROTOCOL 0x0B
+
+/* HID Report Definitions */
+struct usb_hid_class_subdescriptor {
+ uint8_t bDescriptorType;
+ uint16_t wDescriptorLength;
+} __packed;
+
+struct usb_hid_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bcdHID;
+ uint8_t bCountryCode;
+ uint8_t bNumDescriptors;
+
+ /*
+ * Specification says at least one Class Descriptor needs to
+ * be present (Report Descriptor).
+ */
+ struct usb_hid_class_subdescriptor subdesc[1];
+} __packed;
+
+/* HID Items types */
+#define ITEM_MAIN 0x0
+#define ITEM_GLOBAL 0x1
+#define ITEM_LOCAL 0x2
+
+/* HID Main Items tags */
+#define ITEM_TAG_INPUT 0x8
+#define ITEM_TAG_OUTPUT 0x9
+#define ITEM_TAG_COLLECTION 0xA
+#define ITEM_TAG_COLLECTION_END 0xC
+
+/* HID Global Items tags */
+#define ITEM_TAG_USAGE_PAGE 0x0
+#define ITEM_TAG_LOGICAL_MIN 0x1
+#define ITEM_TAG_LOGICAL_MAX 0x2
+#define ITEM_TAG_REPORT_SIZE 0x7
+#define ITEM_TAG_REPORT_ID 0x8
+#define ITEM_TAG_REPORT_COUNT 0x9
+
+/* HID Local Items tags */
+#define ITEM_TAG_USAGE 0x0
+#define ITEM_TAG_USAGE_MIN 0x1
+#define ITEM_TAG_USAGE_MAX 0x2
+
+#define HID_ITEM(bTag, bType, bSize) (((bTag & 0xF) << 4) | \
+ ((bType & 0x3) << 2) | (bSize & 0x3))
+
+#define HID_MAIN_ITEM(bTag, bSize) HID_ITEM(bTag, ITEM_MAIN, bSize)
+#define HID_GLOBAL_ITEM(bTag, bSize) HID_ITEM(bTag, ITEM_GLOBAL, bSize)
+#define HID_LOCAL_ITEM(bTag, bSize) HID_ITEM(bTag, ITEM_LOCAL, bSize)
+
+#define HID_MI_COLLECTION HID_MAIN_ITEM(ITEM_TAG_COLLECTION, 1)
+#define HID_MI_COLLECTION_END HID_MAIN_ITEM(ITEM_TAG_COLLECTION_END, \
+ 0)
+#define HID_MI_INPUT HID_MAIN_ITEM(ITEM_TAG_INPUT, 1)
+#define HID_MI_OUTPUT HID_MAIN_ITEM(ITEM_TAG_OUTPUT, 1)
+
+#define HID_GI_USAGE_PAGE HID_GLOBAL_ITEM(ITEM_TAG_USAGE_PAGE, 1)
+#define HID_GI_LOGICAL_MIN(size) HID_GLOBAL_ITEM(ITEM_TAG_LOGICAL_MIN, \
+ size)
+#define HID_GI_LOGICAL_MAX(size) HID_GLOBAL_ITEM(ITEM_TAG_LOGICAL_MAX, \
+ size)
+#define HID_GI_REPORT_SIZE HID_GLOBAL_ITEM(ITEM_TAG_REPORT_SIZE, \
+ 1)
+#define HID_GI_REPORT_ID HID_GLOBAL_ITEM(ITEM_TAG_REPORT_ID, \
+ 1)
+#define HID_GI_REPORT_COUNT HID_GLOBAL_ITEM(ITEM_TAG_REPORT_COUNT, \
+ 1)
+
+#define HID_LI_USAGE HID_LOCAL_ITEM(ITEM_TAG_USAGE, 1)
+#define HID_LI_USAGE_MIN(size) HID_LOCAL_ITEM(ITEM_TAG_USAGE_MIN, \
+ size)
+#define HID_LI_USAGE_MAX(size) HID_LOCAL_ITEM(ITEM_TAG_USAGE_MAX, \
+ size)
+
+/* Defined in Universal Serial Bus HID Usage Tables version 1.11 */
+#define USAGE_GEN_DESKTOP 0x01
+#define USAGE_GEN_KEYBOARD 0x07
+#define USAGE_GEN_LEDS 0x08
+#define USAGE_GEN_BUTTON 0x09
+
+/* Generic Desktop Page usages */
+#define USAGE_GEN_DESKTOP_UNDEFINED 0x00
+#define USAGE_GEN_DESKTOP_POINTER 0x01
+#define USAGE_GEN_DESKTOP_MOUSE 0x02
+#define USAGE_GEN_DESKTOP_JOYSTICK 0x04
+#define USAGE_GEN_DESKTOP_GAMEPAD 0x05
+#define USAGE_GEN_DESKTOP_KEYBOARD 0x06
+#define USAGE_GEN_DESKTOP_KEYPAD 0x07
+#define USAGE_GEN_DESKTOP_X 0x30
+#define USAGE_GEN_DESKTOP_Y 0x31
+#define USAGE_GEN_DESKTOP_WHEEL 0x38
+
+/* Collection types */
+#define COLLECTION_PHYSICAL 0x00
+#define COLLECTION_APPLICATION 0x01
+
+/* Protocols */
+#define HID_PROTOCOL_BOOT 0x00
+#define HID_PROTOCOL_REPORT 0x01
+
+/* Example HID report descriptors */
+/**
+ * @brief Simple HID mouse report descriptor for n button mouse.
+ *
+ * @param bcnt Button count. Allowed values from 1 to 8.
+ */
+#define HID_MOUSE_REPORT_DESC(bcnt) \
+ { \
+ /* USAGE_PAGE (Generic Desktop) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_DESKTOP, /* USAGE (Mouse) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_MOUSE, /* COLLECTION (Application) */ \
+ HID_MI_COLLECTION, COLLECTION_APPLICATION, /* USAGE (Pointer) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_POINTER, /* COLLECTION (Physical) */ \
+ HID_MI_COLLECTION, COLLECTION_PHYSICAL, /* Bits used for button signalling */ /* USAGE_PAGE (Button) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_BUTTON, /* USAGE_MINIMUM (Button 1) */ \
+ HID_LI_USAGE_MIN(1), 0x01, /* USAGE_MAXIMUM (Button bcnt) */ \
+ HID_LI_USAGE_MAX(1), bcnt, /* LOGICAL_MINIMUM (0) */ \
+ HID_GI_LOGICAL_MIN(1), 0x00, /* LOGICAL_MAXIMUM (1) */ \
+ HID_GI_LOGICAL_MAX(1), 0x01, /* REPORT_SIZE (1) */ \
+ HID_GI_REPORT_SIZE, 0x01, /* REPORT_COUNT (bcnt) */ \
+ HID_GI_REPORT_COUNT, bcnt, /* INPUT (Data,Var,Abs) */ \
+ HID_MI_INPUT, 0x02, /* Unused bits */ /* REPORT_SIZE (8 - bcnt) */ \
+ HID_GI_REPORT_SIZE, (8 - bcnt), /* REPORT_COUNT (1) */ \
+ HID_GI_REPORT_COUNT, 0x01, /* INPUT (Cnst,Ary,Abs) */ \
+ HID_MI_INPUT, 0x01, /* X and Y axis, scroll */ /* USAGE_PAGE (Generic Desktop) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_DESKTOP, /* USAGE (X) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_X, /* USAGE (Y) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_Y, /* USAGE (WHEEL) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_WHEEL, /* LOGICAL_MINIMUM (-127) */ \
+ HID_GI_LOGICAL_MIN(1), -127, /* LOGICAL_MAXIMUM (127) */ \
+ HID_GI_LOGICAL_MAX(1), 127, /* REPORT_SIZE (8) */ \
+ HID_GI_REPORT_SIZE, 0x08, /* REPORT_COUNT (3) */ \
+ HID_GI_REPORT_COUNT, 0x03, /* INPUT (Data,Var,Rel) */ \
+ HID_MI_INPUT, 0x06, /* END_COLLECTION */ \
+ HID_MI_COLLECTION_END, /* END_COLLECTION */ \
+ HID_MI_COLLECTION_END, \
+ }
+
+/**
+ * @brief Simple HID keyboard report descriptor.
+ */
+#define HID_KEYBOARD_REPORT_DESC() \
+ { \
+ /* USAGE_PAGE (Generic Desktop) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_DESKTOP, /* USAGE (Keyboard) */ \
+ HID_LI_USAGE, USAGE_GEN_DESKTOP_KEYBOARD, /* COLLECTION (Application) */ \
+ HID_MI_COLLECTION, COLLECTION_APPLICATION, /* USAGE_PAGE (Keypad) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_DESKTOP_KEYPAD, /* USAGE_MINIMUM (Keyboard LeftControl) */ \
+ HID_LI_USAGE_MIN(1), 0xE0, /* USAGE_MAXIMUM (Keyboard Right GUI) */ \
+ HID_LI_USAGE_MAX(1), 0xE7, /* LOGICAL_MINIMUM (0) */ \
+ HID_GI_LOGICAL_MIN(1), 0x00, /* LOGICAL_MAXIMUM (1) */ \
+ HID_GI_LOGICAL_MAX(1), 0x01, /* REPORT_SIZE (1) */ \
+ HID_GI_REPORT_SIZE, 0x01, /* REPORT_COUNT (8) */ \
+ HID_GI_REPORT_COUNT, 0x08, /* INPUT (Data,Var,Abs) */ \
+ HID_MI_INPUT, 0x02, /* REPORT_SIZE (8) */ \
+ HID_GI_REPORT_SIZE, 0x08, /* REPORT_COUNT (1) */ \
+ HID_GI_REPORT_COUNT, 0x01, /* INPUT (Cnst,Var,Abs) */ \
+ HID_MI_INPUT, 0x03, /* REPORT_SIZE (1) */ \
+ HID_GI_REPORT_SIZE, 0x01, /* REPORT_COUNT (5) */ \
+ HID_GI_REPORT_COUNT, 0x05, /* USAGE_PAGE (LEDs) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_LEDS, /* USAGE_MINIMUM (Num Lock) */ \
+ HID_LI_USAGE_MIN(1), 0x01, /* USAGE_MAXIMUM (Kana) */ \
+ HID_LI_USAGE_MAX(1), 0x05, /* OUTPUT (Data,Var,Abs) */ \
+ HID_MI_OUTPUT, 0x02, /* REPORT_SIZE (3) */ \
+ HID_GI_REPORT_SIZE, 0x03, /* REPORT_COUNT (1) */ \
+ HID_GI_REPORT_COUNT, 0x01, /* OUTPUT (Cnst,Var,Abs) */ \
+ HID_MI_OUTPUT, 0x03, /* REPORT_SIZE (8) */ \
+ HID_GI_REPORT_SIZE, 0x08, /* REPORT_COUNT (6) */ \
+ HID_GI_REPORT_COUNT, 0x06, /* LOGICAL_MINIMUM (0) */ \
+ HID_GI_LOGICAL_MIN(1), 0x00, /* LOGICAL_MAXIMUM (101) */ \
+ HID_GI_LOGICAL_MAX(1), 0x65, /* USAGE_PAGE (Keypad) */ \
+ HID_GI_USAGE_PAGE, USAGE_GEN_DESKTOP_KEYPAD, /* USAGE_MINIMUM (Reserved) */ \
+ HID_LI_USAGE_MIN(1), 0x00, /* USAGE_MAXIMUM (Keyboard Application) */ \
+ HID_LI_USAGE_MAX(1), 0x65, /* INPUT (Data,Ary,Abs) */ \
+ HID_MI_INPUT, 0x00, /* END_COLLECTION */ \
+ HID_MI_COLLECTION_END, \
+ }
+
+/**
+ * @brief HID keyboard button codes.
+ */
+enum hid_kbd_code {
+ HID_KEY_A = 4,
+ HID_KEY_B = 5,
+ HID_KEY_C = 6,
+ HID_KEY_D = 7,
+ HID_KEY_E = 8,
+ HID_KEY_F = 9,
+ HID_KEY_G = 10,
+ HID_KEY_H = 11,
+ HID_KEY_I = 12,
+ HID_KEY_J = 13,
+ HID_KEY_K = 14,
+ HID_KEY_L = 15,
+ HID_KEY_M = 16,
+ HID_KEY_N = 17,
+ HID_KEY_O = 18,
+ HID_KEY_P = 19,
+ HID_KEY_Q = 20,
+ HID_KEY_R = 21,
+ HID_KEY_S = 22,
+ HID_KEY_T = 23,
+ HID_KEY_U = 24,
+ HID_KEY_V = 25,
+ HID_KEY_W = 26,
+ HID_KEY_X = 27,
+ HID_KEY_Y = 28,
+ HID_KEY_Z = 29,
+ HID_KEY_1 = 30,
+ HID_KEY_2 = 31,
+ HID_KEY_3 = 32,
+ HID_KEY_4 = 33,
+ HID_KEY_5 = 34,
+ HID_KEY_6 = 35,
+ HID_KEY_7 = 36,
+ HID_KEY_8 = 37,
+ HID_KEY_9 = 38,
+ HID_KEY_0 = 39,
+ HID_KEY_ENTER = 40,
+ HID_KEY_ESC = 41,
+ HID_KEY_BACKSPACE = 42,
+ HID_KEY_TAB = 43,
+ HID_KEY_SPACE = 44,
+ HID_KEY_MINUS = 45,
+ HID_KEY_EQUAL = 46,
+ HID_KEY_LEFTBRACE = 47,
+ HID_KEY_RIGHTBRACE = 48,
+ HID_KEY_BACKSLASH = 49,
+ HID_KEY_HASH = 50, /* Non-US # and ~ */
+ HID_KEY_SEMICOLON = 51,
+ HID_KEY_APOSTROPHE = 52,
+ HID_KEY_GRAVE = 53,
+ HID_KEY_COMMA = 54,
+ HID_KEY_DOT = 55,
+ HID_KEY_SLASH = 56,
+ HID_KEY_CAPSLOCK = 57,
+ HID_KEY_F1 = 58,
+ HID_KEY_F2 = 59,
+ HID_KEY_F3 = 60,
+ HID_KEY_F4 = 61,
+ HID_KEY_F5 = 62,
+ HID_KEY_F6 = 63,
+ HID_KEY_F7 = 64,
+ HID_KEY_F8 = 65,
+ HID_KEY_F9 = 66,
+ HID_KEY_F10 = 67,
+ HID_KEY_F11 = 68,
+ HID_KEY_F12 = 69,
+ HID_KEY_SYSRQ = 70, /* PRINTSCREEN */
+ HID_KEY_SCROLLLOCK = 71,
+ HID_KEY_PAUSE = 72,
+ HID_KEY_INSERT = 73,
+ HID_KEY_HOME = 74,
+ HID_KEY_PAGEUP = 75,
+ HID_KEY_DELETE = 76,
+ HID_KEY_END = 77,
+ HID_KEY_PAGEDOWN = 78,
+ HID_KEY_RIGHT = 79,
+ HID_KEY_LEFT = 80,
+ HID_KEY_DOWN = 81,
+ HID_KEY_UP = 82,
+ HID_KEY_NUMLOCK = 83,
+ HID_KEY_KPSLASH = 84, /* NUMPAD DIVIDE */
+ HID_KEY_KPASTERISK = 85, /* NUMPAD MULTIPLY */
+ HID_KEY_KPMINUS = 86,
+ HID_KEY_KPPLUS = 87,
+ HID_KEY_KPENTER = 88,
+ HID_KEY_KP_1 = 89,
+ HID_KEY_KP_2 = 90,
+ HID_KEY_KP_3 = 91,
+ HID_KEY_KP_4 = 92,
+ HID_KEY_KP_5 = 93,
+ HID_KEY_KP_6 = 94,
+ HID_KEY_KP_7 = 95,
+ HID_KEY_KP_8 = 96,
+ HID_KEY_KP_9 = 97,
+ HID_KEY_KP_0 = 98,
+};
+
+/**
+ * @brief HID keyboard modifiers.
+ */
+enum hid_kbd_modifier {
+ HID_KBD_MODIFIER_NONE = 0x00,
+ HID_KBD_MODIFIER_LEFT_CTRL = 0x01,
+ HID_KBD_MODIFIER_LEFT_SHIFT = 0x02,
+ HID_KBD_MODIFIER_LEFT_ALT = 0x04,
+ HID_KBD_MODIFIER_LEFT_UI = 0x08,
+ HID_KBD_MODIFIER_RIGHT_CTRL = 0x10,
+ HID_KBD_MODIFIER_RIGHT_SHIFT = 0x20,
+ HID_KBD_MODIFIER_RIGHT_ALT = 0x40,
+ HID_KBD_MODIFIER_RIGHT_UI = 0x80,
+};
+
+/**
+ * @brief HID keyboard LEDs.
+ */
+enum hid_kbd_led {
+ HID_KBD_LED_NUM_LOCK = 0x01,
+ HID_KBD_LED_CAPS_LOCK = 0x02,
+ HID_KBD_LED_SCROLL_LOCK = 0x04,
+ HID_KBD_LED_COMPOSE = 0x08,
+ HID_KBD_LED_KANA = 0x10,
+};
+
+void usbd_hid_descriptor_register(uint8_t intf_num, const uint8_t *desc);
+void usbd_hid_report_descriptor_register(uint8_t intf_num, const uint8_t *desc, uint32_t desc_len);
+void usbd_hid_add_interface(usbd_class_t *class, usbd_interface_t *intf);
+void usbd_hid_reset_state(void);
+void usbd_hid_send_report(uint8_t ep, uint8_t *data, uint8_t len);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _USB_HID_H_ */
diff --git a/class/msc/usbd_msc.c b/class/msc/usbd_msc.c
new file mode 100644
index 00000000..55eefd3f
--- /dev/null
+++ b/class/msc/usbd_msc.c
@@ -0,0 +1,838 @@
+/**
+ * @file usbd_msc.c
+ * @brief
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "usbd_core.h"
+#include "usbd_scsi.h"
+#include "usbd_msc.h"
+
+/* max USB packet size */
+#define MASS_STORAGE_BULK_EP_MPS 64
+#define MASS_STORAGE_BLOCK_SIZE 512
+
+#define MSD_OUT_EP_IDX 0
+#define MSD_IN_EP_IDX 1
+
+/* Describe EndPoints configuration */
+static usbd_endpoint_t mass_ep_data[2];
+
+/* MSC Bulk-only Stage */
+enum Stage {
+ /* MSC Bulk-only Stage */
+ MSC_BS_CBW = 0, /* Command Block Wrapper */
+ MSC_BS_DATA_OUT = 1, /* Data Out Phase */
+ MSC_BS_DATA_IN = 2, /* Data In Phase */
+ MSC_BS_DATA_IN_LAST = 3, /* Data In Last Phase */
+ MSC_BS_DATA_IN_LAST_STALL = 4, /* Data In Last Phase with Stall */
+ MSC_BS_CSW = 5, /* Command Status Wrapper */
+ MSC_BS_ERROR = 6, /* Error */
+ MSC_BS_RESET = 7, /* Bulk-Only Mass Storage Reset */
+};
+
+/* Device data structure */
+struct usbd_msc_cfg_private {
+ /* state of the bulk-only state machine */
+ enum Stage stage;
+ struct CBW cbw;
+ struct CSW csw;
+
+ uint8_t max_lun_count;
+ uint16_t scsi_blk_size;
+ uint32_t scsi_blk_nbr;
+
+ uint32_t scsi_blk_addr;
+ uint32_t scsi_blk_len;
+ uint8_t block_buffer[MASS_STORAGE_BLOCK_SIZE];
+
+} usbd_msc_cfg;
+
+/*memory OK (after a usbd_msc_memory_verify)*/
+static bool memOK;
+
+static void usbd_msc_reset(void)
+{
+ usbd_msc_cfg.stage = MSC_BS_CBW;
+ (void)memset((void *)&usbd_msc_cfg.cbw, 0, sizeof(struct CBW));
+ (void)memset((void *)&usbd_msc_cfg.csw, 0, sizeof(struct CSW));
+ usbd_msc_cfg.scsi_blk_addr = 0U;
+ usbd_msc_cfg.scsi_blk_len = 0U;
+ usbd_msc_get_cap(0, &usbd_msc_cfg.scsi_blk_nbr, &usbd_msc_cfg.scsi_blk_size);
+ usbd_msc_cfg.max_lun_count = 0;
+}
+
+/**
+ * @brief Handler called for Class requests not handled by the USB stack.
+ *
+ * @param pSetup Information about the request to execute.
+ * @param len Size of the buffer.
+ * @param data Buffer containing the request result.
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+static int msc_storage_class_request_handler(struct usb_setup_packet *pSetup, uint8_t **data, uint32_t *len)
+{
+ switch (pSetup->bRequest) {
+ case MSC_REQUEST_RESET:
+ USBD_LOG_DBG("MSC_REQUEST_RESET");
+
+ if (pSetup->wLength) {
+ USBD_LOG_WRN("Invalid length");
+ return -1;
+ }
+
+ usbd_msc_reset();
+ break;
+
+ case MSC_REQUEST_GET_MAX_LUN:
+ USBD_LOG_DBG("MSC_REQUEST_GET_MAX_LUN");
+
+ if (pSetup->wLength != 1) {
+ USBD_LOG_WRN("Invalid length");
+ return -1;
+ }
+
+ *data = (uint8_t *)(&usbd_msc_cfg.max_lun_count);
+ *len = 1;
+ break;
+
+ default:
+ USBD_LOG_WRN("Unknown request 0x%02x, value 0x%02x",
+ pSetup->bRequest, pSetup->wValue);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void usbd_msc_send_csw(void)
+{
+ usbd_msc_cfg.csw.Signature = MSC_CSW_Signature;
+
+ if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, (uint8_t *)&usbd_msc_cfg.csw,
+ sizeof(struct CSW), NULL) != 0) {
+ USBD_LOG_ERR("usb write failure");
+ }
+
+ usbd_msc_cfg.stage = MSC_BS_CSW;
+}
+
+static bool usbd_msc_datain_check(void)
+{
+ if (!usbd_msc_cfg.cbw.DataLength) {
+ USBD_LOG_WRN("Zero length in CBW");
+ //SCSI_SenseCode(pdev, hmsc->cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ return false;
+ }
+
+ if ((usbd_msc_cfg.cbw.Flags & 0x80) == 0) {
+ usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ return false;
+ }
+
+ return true;
+}
+
+static bool usbd_msc_send_to_host(uint8_t *buffer, uint16_t size)
+{
+ if (size >= usbd_msc_cfg.cbw.DataLength) {
+ size = usbd_msc_cfg.cbw.DataLength;
+ usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST;
+ } else {
+ usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST_STALL;
+ }
+
+ if (usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr, buffer, size, NULL)) {
+ USBD_LOG_ERR("USB write failed");
+ return false;
+ }
+
+ usbd_msc_cfg.csw.DataResidue -= size;
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+
+ return true;
+}
+
+static void usbd_msc_memory_verify(uint8_t *buf, uint16_t size)
+{
+ uint32_t n;
+
+ if ((usbd_msc_cfg.scsi_blk_addr + size) > (usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size)) {
+ size = usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size - usbd_msc_cfg.scsi_blk_addr;
+ usbd_msc_cfg.stage = MSC_BS_ERROR;
+ usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ USBD_LOG_WRN("addr overflow,verify error\r\n");
+ }
+
+ /* beginning of a new block -> load a whole block in RAM */
+ if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) {
+ USBD_LOG_DBG("Disk READ sector %d", usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size);
+ // if (disk_access_read(disk_pdrv, page, addr / BLOCK_SIZE, 1))
+ // {
+ // USBD_LOG_ERR("---- Disk Read Error %d", addr / BLOCK_SIZE);
+ // }
+ }
+
+ /* info are in RAM -> no need to re-read memory */
+ for (n = 0U; n < size; n++) {
+ if (usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + n] != buf[n]) {
+ USBD_LOG_DBG("Mismatch sector %d offset %d",
+ usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size, n);
+ memOK = false;
+ break;
+ }
+ }
+
+ usbd_msc_cfg.scsi_blk_addr += size;
+ usbd_msc_cfg.scsi_blk_len -= size;
+ usbd_msc_cfg.csw.DataResidue -= size;
+
+ if (!usbd_msc_cfg.scsi_blk_len || (usbd_msc_cfg.stage == MSC_BS_CSW)) {
+ usbd_msc_cfg.csw.Status = (memOK) ? CSW_STATUS_CMD_PASSED : CSW_STATUS_CMD_FAILED;
+ usbd_msc_send_csw();
+ }
+}
+
+static void usbd_msc_memory_write(uint8_t *buf, uint16_t size)
+{
+ USBD_LOG_DBG("w:%d\r\n", usbd_msc_cfg.scsi_blk_addr);
+
+ if ((usbd_msc_cfg.scsi_blk_addr + size) > (usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size)) {
+ size = usbd_msc_cfg.scsi_blk_nbr * usbd_msc_cfg.scsi_blk_size - usbd_msc_cfg.scsi_blk_addr;
+ usbd_msc_cfg.stage = MSC_BS_ERROR;
+ usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ USBD_LOG_WRN("addr overflow,write error\r\n");
+ }
+
+ /* we fill an array in RAM of 1 block before writing it in memory */
+ for (int i = 0; i < size; i++) {
+ usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size + i] = buf[i];
+ }
+
+ /* if the array is filled, write it in memory */
+ if ((usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size) + size >= usbd_msc_cfg.scsi_blk_size) {
+ usbd_msc_sector_write((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size);
+ }
+
+ usbd_msc_cfg.scsi_blk_addr += size;
+ usbd_msc_cfg.scsi_blk_len -= size;
+ usbd_msc_cfg.csw.DataResidue -= size;
+
+ if ((!usbd_msc_cfg.scsi_blk_len) || (usbd_msc_cfg.stage == MSC_BS_CSW)) {
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ usbd_msc_send_csw();
+ }
+}
+
+static void usbd_msc_memory_read(void)
+{
+ uint32_t transfer_len;
+
+ transfer_len = MIN(usbd_msc_cfg.scsi_blk_len, MASS_STORAGE_BULK_EP_MPS);
+
+ /* we read an entire block */
+ if (!(usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size)) {
+ usbd_msc_sector_read((usbd_msc_cfg.scsi_blk_addr / usbd_msc_cfg.scsi_blk_size), usbd_msc_cfg.block_buffer, usbd_msc_cfg.scsi_blk_size);
+ }
+
+ USBD_LOG_DBG("addr:%d\r\n", usbd_msc_cfg.scsi_blk_addr);
+
+ usbd_ep_write(mass_ep_data[MSD_IN_EP_IDX].ep_addr,
+ &usbd_msc_cfg.block_buffer[usbd_msc_cfg.scsi_blk_addr % usbd_msc_cfg.scsi_blk_size], transfer_len, NULL);
+
+ usbd_msc_cfg.scsi_blk_addr += transfer_len;
+ usbd_msc_cfg.scsi_blk_len -= transfer_len;
+ usbd_msc_cfg.csw.DataResidue -= transfer_len;
+
+ if (!usbd_msc_cfg.scsi_blk_len) {
+ usbd_msc_cfg.stage = MSC_BS_DATA_IN_LAST;
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ }
+}
+
+/*********************************SCSI CMD*******************************************************************/
+static bool scsi_test_unit_ready(void)
+{
+ if (usbd_msc_cfg.cbw.DataLength != 0U) {
+ if ((usbd_msc_cfg.cbw.Flags & 0x80) != 0U) {
+ USBD_LOG_WRN("Stall IN endpoint\r\n");
+ usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ } else {
+ USBD_LOG_WRN("Stall OUT endpoint\r\n");
+ usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ }
+
+ return false;
+ }
+
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ usbd_msc_send_csw();
+ return true;
+}
+
+static bool scsi_request_sense(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_sense_fixed_resp_t sense_rsp = {
+ .response_code = 0x70,
+ .valid = 1
+ };
+
+ sense_rsp.add_sense_len = sizeof(scsi_sense_fixed_resp_t) - 8;
+ sense_rsp.sense_key = 0x00;
+ sense_rsp.add_sense_code = 0x00;
+ sense_rsp.add_sense_qualifier = 0x00;
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(sense_rsp)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(sense_rsp);
+ usbd_msc_cfg.csw.DataResidue = sizeof(sense_rsp);
+ }
+
+#if 0
+ request_sense[ 2] = 0x06; /* UNIT ATTENTION */
+ request_sense[12] = 0x28; /* Additional Sense Code: Not ready to ready transition */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x02; /* NOT READY */
+ request_sense[12] = 0x3A; /* Additional Sense Code: Medium not present */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x05; /* ILLEGAL REQUEST */
+ request_sense[12] = 0x20; /* Additional Sense Code: Invalid command */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+#if 0
+ request_sense[ 2] = 0x00; /* NO SENSE */
+ request_sense[12] = 0x00; /* Additional Sense Code: No additional code */
+ request_sense[13] = 0x00; /* Additional Sense Code Qualifier */
+#endif
+
+ return usbd_msc_send_to_host((uint8_t *)&sense_rsp, sizeof(sense_rsp));
+}
+
+static bool scsi_inquiry_request(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_inquiry_resp_t inquiry_rsp = {
+ .is_removable = 1, /* RMB = 1: Removable Medium */
+ .version = 2,
+ .response_data_format = 2,
+ .additional_length = 31
+ };
+ // vendor_id, product_id, product_rev is space padded string
+ memcpy(inquiry_rsp.vendor_id, "BL702USB", sizeof(inquiry_rsp.vendor_id));
+ memcpy(inquiry_rsp.product_id, "FAT16 RAM DEMO ", sizeof(inquiry_rsp.product_id));
+ memcpy(inquiry_rsp.product_rev, "1.0 ", sizeof(inquiry_rsp.product_rev));
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(inquiry_rsp)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(inquiry_rsp);
+ usbd_msc_cfg.csw.DataResidue = sizeof(inquiry_rsp);
+ }
+
+ return usbd_msc_send_to_host((uint8_t *)&inquiry_rsp, sizeof(inquiry_rsp));
+}
+
+static bool scsi_mode_sense_6(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_mode_sense6_resp_t mode_resp = {
+ .data_len = 3,
+ .medium_type = 0,
+ .write_protected = false,
+ .reserved = 0,
+ .block_descriptor_len = 0 // no block descriptor are included
+ };
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(mode_resp)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(mode_resp);
+ usbd_msc_cfg.csw.DataResidue = sizeof(mode_resp);
+ }
+
+ return usbd_msc_send_to_host((uint8_t *)&mode_resp, sizeof(mode_resp));
+}
+
+static bool scsi_start_stop_unit(void)
+{
+ // if (!cbw.CB[3]) { /* If power condition modifier is 0 */
+ // USBD_MSC_MediaReady = cbw.CB[4] & 0x01; /* Media ready = START bit value */
+ // usbd_msc_start_stop(USBD_MSC_MediaReady);
+ // cbw.bStatus = CSW_CMD_PASSED; /* Start Stop Unit -> pass */
+ // USBD_MSC_SetCSW();
+ // return;
+ // }
+
+ // cbw.bStatus = CSW_CMD_FAILED; /* Start Stop Unit -> fail */
+ // usbd_msc_send_csw();
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ usbd_msc_send_csw();
+ return true;
+}
+/*
+ * USB Device MSC SCSI Media Removal Callback
+ * Parameters: None
+ * Return Value: None
+ */
+
+static bool scsi_media_removal(void)
+{
+ // if (USBD_MSC_CBW.CB[4] & 1) { /* If prevent */
+ // USBD_MSC_CSW.bStatus = CSW_CMD_FAILED; /* Prevent media removal -> fail */
+ // } else { /* If allow */
+ // USBD_MSC_CSW.bStatus = CSW_CMD_PASSED; /* Allow media removal -> pass */
+ // }
+
+ // USBD_MSC_SetCSW();
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ usbd_msc_send_csw();
+ return true;
+}
+
+static bool scsi_read_format_capacity(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_read_format_capacity_resp_t read_fmt_capa = {
+ .list_length = 8, /* Capacity List Length */
+ .block_num = 0,
+ .descriptor_type = 2, /* Descriptor Code: Formatted Media */
+ .block_size_u16 = 0
+ };
+ /* Block Count */
+ read_fmt_capa.block_num = BSWAP32(usbd_msc_cfg.scsi_blk_nbr);
+ /* Block Length */
+ read_fmt_capa.block_size_u16 = BSWAP16(usbd_msc_cfg.scsi_blk_size);
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(read_fmt_capa)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(read_fmt_capa);
+ usbd_msc_cfg.csw.DataResidue = sizeof(read_fmt_capa);
+ }
+
+ return usbd_msc_send_to_host((uint8_t *)&read_fmt_capa, sizeof(read_fmt_capa));
+}
+
+static bool scsi_read_capacity(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_read_capacity10_resp_t read_capa10;
+ /* Last Logical Block */
+ read_capa10.last_lba = BSWAP32((usbd_msc_cfg.scsi_blk_nbr - 1));
+ /* Block Length */
+ read_capa10.block_size = BSWAP32(usbd_msc_cfg.scsi_blk_size);
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(read_capa10)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(read_capa10);
+ usbd_msc_cfg.csw.DataResidue = sizeof(read_capa10);
+ }
+
+ return usbd_msc_send_to_host((uint8_t *)&read_capa10, sizeof(read_capa10));
+}
+
+static bool usbd_msc_read_write_process(void)
+{
+ /* Logical Block Address of First Block */
+ uint32_t lba;
+ uint32_t len = 0;
+
+ if (!usbd_msc_cfg.cbw.DataLength) {
+ USBD_LOG_WRN("Zero length in CBW\r\n");
+ //SCSI_SenseCode(pdev, hmsc->cbw.bLUN, ILLEGAL_REQUEST, INVALID_CDB);
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ return false;
+ }
+
+ lba = GET_BE32(&usbd_msc_cfg.cbw.CB[2]);
+
+ USBD_LOG_DBG("LBA (block) : 0x%x\r\n", lba);
+ usbd_msc_cfg.scsi_blk_addr = lba * usbd_msc_cfg.scsi_blk_size;
+
+ /* Number of Blocks to transfer */
+ switch (usbd_msc_cfg.cbw.CB[0]) {
+ case SCSI_READ10:
+ case SCSI_WRITE10:
+ case SCSI_VERIFY10:
+ len = GET_BE16(&usbd_msc_cfg.cbw.CB[7]);
+ break;
+
+ case SCSI_READ12:
+ case SCSI_WRITE12:
+ len = GET_BE32(&usbd_msc_cfg.cbw.CB[6]);
+ break;
+
+ default:
+ break;
+ }
+
+ USBD_LOG_DBG("len (block) : 0x%x\r\n", len);
+ usbd_msc_cfg.scsi_blk_len = len * usbd_msc_cfg.scsi_blk_size;
+
+ if ((lba + len) > usbd_msc_cfg.scsi_blk_nbr) {
+ USBD_LOG_ERR("LBA out of range\r\n");
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
+ usbd_msc_send_csw();
+ return false;
+ }
+
+ if (usbd_msc_cfg.cbw.DataLength != usbd_msc_cfg.scsi_blk_len) {
+ if ((usbd_msc_cfg.cbw.Flags & 0x80) != 0U) {
+ USBD_LOG_WRN("read write process error\r\n");
+ usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ } else {
+ USBD_LOG_WRN("read write process error\r\n");
+ usbd_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ }
+
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
+ usbd_msc_send_csw();
+ return false;
+ }
+
+ return true;
+}
+
+static bool scsi_mode_sense_10(void)
+{
+ if (!usbd_msc_datain_check()) {
+ return false;
+ }
+
+ scsi_mode_10_resp_t mode10_resp = {
+ .mode_data_length_low = 0x06,
+ .write_protect = 1,
+ };
+
+ /* Win host requests maximum number of bytes but as all we have is 4 bytes we have
+ to tell host back that it is all we have, that's why we correct residue */
+ if (usbd_msc_cfg.csw.DataResidue > sizeof(mode10_resp)) {
+ usbd_msc_cfg.cbw.DataLength = sizeof(mode10_resp);
+ usbd_msc_cfg.csw.DataResidue = sizeof(mode10_resp);
+ }
+
+ return usbd_msc_send_to_host((uint8_t *)&mode10_resp, sizeof(mode10_resp));
+}
+
+static void usbd_msc_cbw_decode(uint8_t *buf, uint16_t size)
+{
+ if (size != sizeof(usbd_msc_cfg.cbw)) {
+ USBD_LOG_ERR("size != sizeof(cbw)");
+ return;
+ }
+
+ memcpy((uint8_t *)&usbd_msc_cfg.cbw, buf, size);
+
+ if (usbd_msc_cfg.cbw.Signature != MSC_CBW_Signature) {
+ USBD_LOG_ERR("CBW Signature Mismatch");
+ return;
+ }
+
+ usbd_msc_cfg.csw.Tag = usbd_msc_cfg.cbw.Tag;
+ usbd_msc_cfg.csw.DataResidue = usbd_msc_cfg.cbw.DataLength;
+
+ if ((usbd_msc_cfg.cbw.CBLength < 1) || (usbd_msc_cfg.cbw.CBLength > 16) || (usbd_msc_cfg.cbw.LUN != 0U)) {
+ USBD_LOG_WRN("cbw.CBLength %d", usbd_msc_cfg.cbw.CBLength);
+ /* Stall data stage */
+ usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_FAILED;
+ usbd_msc_send_csw();
+ } else {
+ switch (usbd_msc_cfg.cbw.CB[0]) {
+ case SCSI_TEST_UNIT_READY:
+ USBD_LOG_DBG(">> TUR");
+ scsi_test_unit_ready();
+ break;
+
+ case SCSI_REQUEST_SENSE:
+ USBD_LOG_DBG(">> REQ_SENSE");
+ scsi_request_sense();
+ break;
+
+ case SCSI_INQUIRY:
+ USBD_LOG_DBG(">> INQ");
+ scsi_inquiry_request();
+ break;
+
+ case SCSI_START_STOP_UNIT:
+ scsi_start_stop_unit();
+ break;
+
+ case SCSI_MEDIA_REMOVAL:
+ scsi_media_removal();
+ break;
+
+ case SCSI_MODE_SENSE6:
+ USBD_LOG_DBG(">> MODE_SENSE6");
+ scsi_mode_sense_6();
+ break;
+
+ case SCSI_MODE_SENSE10:
+ USBD_LOG_DBG(">> MODE_SENSE10");
+ scsi_mode_sense_10();
+ break;
+
+ case SCSI_READ_FORMAT_CAPACITIES:
+ USBD_LOG_DBG(">> READ_FORMAT_CAPACITY");
+ scsi_read_format_capacity();
+ break;
+
+ case SCSI_READ_CAPACITY:
+ USBD_LOG_DBG(">> READ_CAPACITY");
+ scsi_read_capacity();
+ break;
+
+ case SCSI_READ10:
+ case SCSI_READ12:
+ USBD_LOG_DBG(">> READ");
+
+ if (usbd_msc_read_write_process()) {
+ if ((usbd_msc_cfg.cbw.Flags & 0x80)) {
+ usbd_msc_cfg.stage = MSC_BS_DATA_IN;
+ usbd_msc_memory_read();
+ } else {
+ usbd_ep_set_stall(
+ mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
+ USBD_LOG_WRN("Stall OUT endpoint");
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ }
+ }
+
+ break;
+
+ case SCSI_WRITE10:
+ case SCSI_WRITE12:
+ USBD_LOG_DBG(">> WRITE");
+
+ if (usbd_msc_read_write_process()) {
+ if (!(usbd_msc_cfg.cbw.Flags & 0x80)) {
+ usbd_msc_cfg.stage = MSC_BS_DATA_OUT;
+ } else {
+ usbd_ep_set_stall(
+ mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ USBD_LOG_WRN("Stall IN endpoint");
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ }
+ }
+
+ break;
+
+ case SCSI_VERIFY10:
+ USBD_LOG_DBG(">> VERIFY10");
+
+ if (!(usbd_msc_cfg.cbw.CB[1] & 0x02)) {
+ usbd_msc_cfg.csw.Status = CSW_STATUS_CMD_PASSED;
+ usbd_msc_send_csw();
+ break;
+ }
+
+ if (usbd_msc_read_write_process()) {
+ if (!(usbd_msc_cfg.cbw.Flags & 0x80)) {
+ usbd_msc_cfg.stage = MSC_BS_DATA_OUT;
+ memOK = true;
+ } else {
+ usbd_ep_set_stall(
+ mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ USBD_LOG_WRN("Stall IN endpoint");
+ usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ usbd_msc_send_csw();
+ }
+ }
+
+ break;
+
+ default:
+ USBD_LOG_WRN(">> default CB[0] %x", usbd_msc_cfg.cbw.CB[0]);
+ /* Stall data stage */
+ usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ usbd_msc_cfg.stage = MSC_BS_ERROR;
+ break;
+ }
+ }
+}
+
+static void mass_storage_bulk_out(uint8_t ep)
+{
+ uint32_t bytes_read = 0U;
+ uint8_t bo_buf[MASS_STORAGE_BULK_EP_MPS];
+
+ usbd_ep_read(ep, bo_buf, MASS_STORAGE_BULK_EP_MPS,
+ &bytes_read);
+
+ switch (usbd_msc_cfg.stage) {
+ /*the device has to decode the CBW received*/
+ case MSC_BS_CBW:
+ USBD_LOG_DBG("> BO - MSC_BS_CBW\r\n");
+ usbd_msc_cbw_decode(bo_buf, bytes_read);
+ break;
+
+ /*the device has to receive data from the host*/
+ case MSC_BS_DATA_OUT:
+ switch (usbd_msc_cfg.cbw.CB[0]) {
+ case SCSI_WRITE10:
+ case SCSI_WRITE12:
+ /* USBD_LOG_DBG("> BO - PROC_CBW WR");*/
+ usbd_msc_memory_write(bo_buf, bytes_read);
+ break;
+
+ case SCSI_VERIFY10:
+ USBD_LOG_DBG("> BO - PROC_CBW VER\r\n");
+ usbd_msc_memory_verify(bo_buf, bytes_read);
+ break;
+
+ default:
+ USBD_LOG_ERR("> BO - PROC_CBW default <<ERROR!!!>>\r\n");
+ break;
+ }
+
+ break;
+
+ case MSC_BS_CSW:
+ break;
+
+ /*an error has occurred: stall endpoint and send CSW*/
+ default:
+ USBD_LOG_WRN("Stall OUT endpoint, stage: %d\r\n", usbd_msc_cfg.stage);
+ // usbd_ep_set_stall(ep);
+ // usbd_msc_cfg.csw.Status = CSW_STATUS_PHASE_ERROR;
+ // usbd_msc_send_csw();
+ break;
+ }
+
+ /*set ep ack to recv next data*/
+ usbd_ep_read(ep, NULL, 0, NULL);
+}
+
+/**
+ * @brief EP Bulk IN handler, used to send data to the Host
+ *
+ * @param ep Endpoint address.
+ * @param ep_status Endpoint status code.
+ *
+ * @return N/A.
+ */
+static void mass_storage_bulk_in(uint8_t ep)
+{
+ USBD_LOG_DBG("I:%d\r\n", usbd_msc_cfg.stage);
+
+ switch (usbd_msc_cfg.stage) {
+ /*the device has to send data to the host*/
+ case MSC_BS_DATA_IN:
+ switch (usbd_msc_cfg.cbw.CB[0]) {
+ case SCSI_READ10:
+ case SCSI_READ12:
+ /* USBD_LOG_DBG("< BI - PROC_CBW READ"); */
+ usbd_msc_memory_read();
+ break;
+
+ default:
+ USBD_LOG_ERR("< BI-PROC_CBW default <<ERROR!!>>\r\n");
+ break;
+ }
+
+ break;
+
+ /*the device has to send a CSW*/
+ case MSC_BS_DATA_IN_LAST:
+ USBD_LOG_DBG("< BI - MSC_BS_DATA_IN_LAST\r\n");
+ usbd_msc_send_csw();
+ break;
+
+ case MSC_BS_DATA_IN_LAST_STALL:
+ USBD_LOG_WRN("Stall IN endpoint, stage: %d\r\n", usbd_msc_cfg.stage);
+ //usbd_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
+ usbd_msc_send_csw();
+ break;
+
+ /*the host has received the CSW -> we wait a CBW*/
+ case MSC_BS_CSW:
+ USBD_LOG_DBG("< BI - MSC_BS_CSW\r\n");
+ usbd_msc_cfg.stage = MSC_BS_CBW;
+ break;
+
+ default:
+ break;
+ }
+}
+
+void msc_storage_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+ usbd_msc_reset();
+ break;
+
+ default:
+ break;
+ }
+}
+
+static usbd_class_t msc_class;
+
+static usbd_interface_t msc_intf = {
+ .class_handler = msc_storage_class_request_handler,
+ .vendor_handler = NULL,
+ .notify_handler = msc_storage_notify_handler,
+};
+
+void usbd_msc_class_init(uint8_t out_ep, uint8_t in_ep)
+{
+ msc_class.name = "usbd_msc";
+
+ usbd_class_register(&msc_class);
+ usbd_class_add_interface(&msc_class, &msc_intf);
+
+ mass_ep_data[0].ep_addr = out_ep;
+ mass_ep_data[0].ep_cb = mass_storage_bulk_out;
+ mass_ep_data[1].ep_addr = in_ep;
+ mass_ep_data[1].ep_cb = mass_storage_bulk_in;
+
+ usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[0]);
+ usbd_interface_add_endpoint(&msc_intf, &mass_ep_data[1]);
+} \ No newline at end of file
diff --git a/class/msc/usbd_msc.h b/class/msc/usbd_msc.h
new file mode 100644
index 00000000..1673ace4
--- /dev/null
+++ b/class/msc/usbd_msc.h
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * @brief USB Mass Storage Class public header
+ *
+ * Header follows the Mass Storage Class Specification
+ * (Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf) and
+ * Mass Storage Class Bulk-Only Transport Specification
+ * (usbmassbulk_10.pdf).
+ * Header is limited to Bulk-Only Transfer protocol.
+ */
+
+#ifndef _USBD_MSC_H__
+#define _USBD_MSC_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MSC Subclass Codes */
+#define MSC_SUBCLASS_RBC 0x01
+#define MSC_SUBCLASS_SFF8020I_MMC2 0x02
+#define MSC_SUBCLASS_QIC157 0x03
+#define MSC_SUBCLASS_UFI 0x04
+#define MSC_SUBCLASS_SFF8070I 0x05
+#define MSC_SUBCLASS_SCSI 0x06
+
+/* MSC Protocol Codes */
+#define MSC_PROTOCOL_CBI_INT 0x00
+#define MSC_PROTOCOL_CBI_NOINT 0x01
+#define MSC_PROTOCOL_BULK_ONLY 0x50
+
+/* MSC Request Codes */
+#define MSC_REQUEST_RESET 0xFF
+#define MSC_REQUEST_GET_MAX_LUN 0xFE
+
+/** MSC Command Block Wrapper (CBW) Signature */
+#define MSC_CBW_Signature 0x43425355
+/** Bulk-only Command Status Wrapper (CSW) Signature */
+#define MSC_CSW_Signature 0x53425355
+
+/** MSC Command Block Status Values */
+#define CSW_STATUS_CMD_PASSED 0x00
+#define CSW_STATUS_CMD_FAILED 0x01
+#define CSW_STATUS_PHASE_ERROR 0x02
+
+/** MSC Bulk-Only Command Block Wrapper (CBW) */
+struct CBW {
+ uint32_t Signature;
+ uint32_t Tag;
+ uint32_t DataLength;
+ uint8_t Flags;
+ uint8_t LUN;
+ uint8_t CBLength;
+ uint8_t CB[16];
+} __packed;
+
+/** MSC Bulk-Only Command Status Wrapper (CSW) */
+struct CSW {
+ uint32_t Signature;
+ uint32_t Tag;
+ uint32_t DataResidue;
+ uint8_t Status;
+} __packed;
+
+/*Length of template descriptor: 23 bytes*/
+#define MSC_DESCRIPTOR_LEN (9 + 7 + 7)
+
+#define MSC_DESCRIPTOR_INIT(bFirstInterface, out_ep, in_ep, str_idx) \
+ /* Interface */ \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bFirstInterface, /* bInterfaceNumber */ \
+ 0x00, /* bAlternateSetting */ \
+ 0x02, /* bNumEndpoints */ \
+ USB_DEVICE_CLASS_MASS_STORAGE, /* bInterfaceClass */ \
+ MSC_SUBCLASS_SCSI, /* bInterfaceSubClass */ \
+ MSC_PROTOCOL_BULK_ONLY, /* bInterfaceProtocol */ \
+ str_idx, /* iInterface */ /* Endpoint Out */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ out_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ 0x40, 0x00, /* wMaxPacketSize */ \
+ 0x01, /* bInterval */ /* Endpoint In */ \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ in_ep, /* bEndpointAddress */ \
+ 0x02, /* bmAttributes */ \
+ 0x40, 0x00, /* wMaxPacketSize */ \
+ 0x01 /* bInterval */
+
+void usbd_msc_class_init(uint8_t out_ep, uint8_t in_ep);
+void usbd_msc_get_cap(uint8_t lun, uint32_t *block_num, uint16_t *block_size);
+int usbd_msc_sector_read(uint32_t sector, uint8_t *buffer, uint32_t length);
+int usbd_msc_sector_write(uint32_t sector, uint8_t *buffer, uint32_t length);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USB_MSC_H_ */
diff --git a/class/msc/usbd_scsi.h b/class/msc/usbd_scsi.h
new file mode 100644
index 00000000..6dc5ffe3
--- /dev/null
+++ b/class/msc/usbd_scsi.h
@@ -0,0 +1,302 @@
+/**
+ * @file
+ * @brief USB Mass Storage Class SCSI public header
+ *
+ * Header follows the Mass Storage Class Specification
+ * (Mass_Storage_Specification_Overview_v1.4_2-19-2010.pdf) and
+ * Mass Storage Class Bulk-Only Transport Specification
+ * (usbmassbulk_10.pdf).
+ * Header is limited to Bulk-Only Transfer protocol.
+ */
+
+#ifndef _USBD_SCSI_H_
+#define _USBD_SCSI_H_
+
+/* SCSI Commands */
+#define SCSI_TEST_UNIT_READY 0x00
+#define SCSI_REQUEST_SENSE 0x03
+#define SCSI_FORMAT_UNIT 0x04
+#define SCSI_INQUIRY 0x12
+#define SCSI_MODE_SELECT6 0x15
+#define SCSI_MODE_SENSE6 0x1A
+#define SCSI_START_STOP_UNIT 0x1B
+#define SCSI_SEND_DIAGNOSTIC 0x1D
+#define SCSI_MEDIA_REMOVAL 0x1E
+#define SCSI_READ_FORMAT_CAPACITIES 0x23
+#define SCSI_READ_CAPACITY 0x25
+#define SCSI_READ10 0x28
+#define SCSI_WRITE10 0x2A
+#define SCSI_VERIFY10 0x2F
+#define SCSI_SYNC_CACHE10 0x35
+#define SCSI_READ12 0xA8
+#define SCSI_WRITE12 0xAA
+#define SCSI_MODE_SELECT10 0x55
+#define SCSI_MODE_SENSE10 0x5A
+#define SCSI_ATA_COMMAND_PASS_THROUGH16 0x85
+#define SCSI_READ16 0x88
+#define SCSI_WRITE16 0x8A
+#define SCSI_VERIFY16 0x8F
+#define SCSI_SYNC_CACHE16 0x91
+#define SCSI_SERVICE_ACTION_IN16 0x9E
+#define SCSI_READ_CAPACITY16 0x9E
+#define SCSI_SERVICE_ACTION_OUT16 0x9F
+#define SCSI_ATA_COMMAND_PASS_THROUGH12 0xA1
+#define SCSI_REPORT_ID_INFO 0xA3
+#define SCSI_READ12 0xA8
+#define SCSI_SERVICE_ACTION_OUT12 0xA9
+#define SCSI_SERVICE_ACTION_IN12 0xAB
+#define SCSI_VERIFY12 0xAF
+
+/* SCSI Sense Key */
+#define SCSI_SENSE_NONE 0x00
+#define SCSI_SENSE_RECOVERED_ERROR 0x01
+#define SCSI_SENSE_NOT_READY 0x02
+#define SCSI_SENSE_MEDIUM_ERROR 0x03
+#define SCSI_SENSE_HARDWARE_ERROR 0x04
+#define SCSI_SENSE_ILLEGAL_REQUEST 0x05
+#define SCSI_SENSE_UNIT_ATTENTION 0x06
+#define SCSI_SENSE_DATA_PROTECT 0x07
+#define SCSI_SENSE_FIRMWARE_ERROR 0x08
+#define SCSI_SENSE_ABORTED_COMMAND 0x0b
+#define SCSI_SENSE_EQUAL 0x0c
+#define SCSI_SENSE_VOLUME_OVERFLOW 0x0d
+#define SCSI_SENSE_MISCOMPARE 0x0e
+
+//--------------------------------------------------------------------+
+// SCSI Primary Command (SPC-4)
+//--------------------------------------------------------------------+
+
+/// SCSI Test Unit Ready Command
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_TEST_UNIT_READY
+ uint8_t lun; ///< Logical Unit
+ uint8_t reserved[3];
+ uint8_t control;
+} scsi_test_unit_ready_cmd_t;
+
+/// SCSI Inquiry Command
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_INQUIRY
+ uint8_t reserved1;
+ uint8_t page_code;
+ uint8_t reserved2;
+ uint8_t alloc_length; ///< specifies the maximum number of bytes that USB host has allocated in the Data-In Buffer. An allocation length of zero specifies that no data shall be transferred.
+ uint8_t control;
+} scsi_inquiry_cmd_t, scsi_request_sense_cmd_t;
+
+/// SCSI Inquiry Response Data
+typedef struct __packed {
+ uint8_t peripheral_device_type : 5;
+ uint8_t peripheral_qualifier : 3;
+
+ uint8_t : 7;
+ uint8_t is_removable : 1;
+
+ uint8_t version;
+
+ uint8_t response_data_format : 4;
+ uint8_t hierarchical_support : 1;
+ uint8_t normal_aca : 1;
+ uint8_t : 2;
+
+ uint8_t additional_length;
+
+ uint8_t protect : 1;
+ uint8_t : 2;
+ uint8_t third_party_copy : 1;
+ uint8_t target_port_group_support : 2;
+ uint8_t access_control_coordinator : 1;
+ uint8_t scc_support : 1;
+
+ uint8_t addr16 : 1;
+ uint8_t : 3;
+ uint8_t multi_port : 1;
+ uint8_t : 1; // vendor specific
+ uint8_t enclosure_service : 1;
+ uint8_t : 1;
+
+ uint8_t : 1; // vendor specific
+ uint8_t cmd_que : 1;
+ uint8_t : 2;
+ uint8_t sync : 1;
+ uint8_t wbus16 : 1;
+ uint8_t : 2;
+
+ uint8_t vendor_id[8]; ///< 8 bytes of ASCII data identifying the vendor of the product.
+ uint8_t product_id[16]; ///< 16 bytes of ASCII data defined by the vendor.
+ uint8_t product_rev[4]; ///< 4 bytes of ASCII data defined by the vendor.
+} scsi_inquiry_resp_t;
+
+typedef struct __packed {
+ uint8_t response_code : 7; ///< 70h - current errors, Fixed Format 71h - deferred errors, Fixed Format
+ uint8_t valid : 1;
+
+ uint8_t reserved;
+
+ uint8_t sense_key : 4;
+ uint8_t : 1;
+ uint8_t ili : 1; ///< Incorrect length indicator
+ uint8_t end_of_medium : 1;
+ uint8_t filemark : 1;
+
+ uint32_t information;
+ uint8_t add_sense_len;
+ uint32_t command_specific_info;
+ uint8_t add_sense_code;
+ uint8_t add_sense_qualifier;
+ uint8_t field_replaceable_unit_code;
+
+ uint8_t sense_key_specific[3]; ///< sense key specific valid bit is bit 7 of key[0], aka MSB in Big Endian layout
+
+} scsi_sense_fixed_resp_t;
+
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_MODE_SENSE_6
+
+ uint8_t : 3;
+ uint8_t disable_block_descriptor : 1;
+ uint8_t : 4;
+
+ uint8_t page_code : 6;
+ uint8_t page_control : 2;
+
+ uint8_t subpage_code;
+ uint8_t alloc_length;
+ uint8_t control;
+} scsi_mode_sense6_cmd_t;
+
+// This is only a Mode parameter header(6).
+typedef struct __packed {
+ uint8_t data_len;
+ uint8_t medium_type;
+
+ uint8_t reserved : 7;
+ bool write_protected : 1;
+
+ uint8_t block_descriptor_len;
+} scsi_mode_sense6_resp_t;
+
+typedef struct
+{
+ uint8_t cmd_code;
+
+ uint8_t reserved1 : 3;
+ uint8_t disable_block_descriptor : 1;
+ uint8_t long_LBA : 1;
+ uint8_t reserved2 : 3;
+
+ uint8_t page_code : 6;
+ uint8_t page_control : 2;
+
+ uint8_t subpage_code;
+
+ uint8_t reserved3;
+ uint8_t reserved4;
+ uint8_t reserved5;
+
+ uint8_t length[2];
+
+ uint8_t control;
+} scsi_mode_sense_10_cmd_t;
+
+typedef struct
+{
+ uint8_t mode_data_length_high;
+ uint8_t mode_data_length_low;
+ uint8_t medium_type;
+
+ uint8_t reserved1 : 4;
+ uint8_t DPO_FUA : 1; /**< [Disable Page Out] and [Force Unit Access] in the SCSI_READ10 command is valid or not */
+ uint8_t reserved2 : 2;
+ uint8_t write_protect : 1;
+
+ uint8_t long_LBA : 1;
+ uint8_t reserved3 : 7;
+
+ uint8_t reserved4;
+ uint8_t block_desc_length[2];
+} scsi_mode_10_resp_t;
+
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL
+ uint8_t reserved[3];
+ uint8_t prohibit_removal;
+ uint8_t control;
+} scsi_prevent_allow_medium_removal_t;
+
+typedef struct __packed {
+ uint8_t cmd_code;
+
+ uint8_t immded : 1;
+ uint8_t : 7;
+
+ uint8_t TU_RESERVED;
+
+ uint8_t power_condition_mod : 4;
+ uint8_t : 4;
+
+ uint8_t start : 1;
+ uint8_t load_eject : 1;
+ uint8_t no_flush : 1;
+ uint8_t : 1;
+ uint8_t power_condition : 4;
+
+ uint8_t control;
+} scsi_start_stop_unit_cmd_t;
+
+//--------------------------------------------------------------------+
+// SCSI MMC
+//--------------------------------------------------------------------+
+/// SCSI Read Format Capacity: Write Capacity
+typedef struct __packed {
+ uint8_t cmd_code;
+ uint8_t reserved[6];
+ uint16_t alloc_length;
+ uint8_t control;
+} scsi_read_format_capacity_cmd_t;
+
+typedef struct __packed {
+ uint8_t reserved[3];
+ uint8_t list_length; /// must be 8*n, length in bytes of formattable capacity descriptor followed it.
+
+ uint32_t block_num; /// Number of Logical Blocks
+ uint8_t descriptor_type; // 00: reserved, 01 unformatted media , 10 Formatted media, 11 No media present
+
+ uint8_t reserved2;
+ uint16_t block_size_u16;
+
+} scsi_read_format_capacity_resp_t;
+
+//--------------------------------------------------------------------+
+// SCSI Block Command (SBC-3)
+// NOTE: All data in SCSI command are in Big Endian
+//--------------------------------------------------------------------+
+
+/// SCSI Read Capacity 10 Command: Read Capacity
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode for \ref SCSI_CMD_READ_CAPACITY_10
+ uint8_t reserved1;
+ uint32_t lba; ///< The first Logical Block Address (LBA) accessed by this command
+ uint16_t reserved2;
+ uint8_t partial_medium_indicator;
+ uint8_t control;
+} scsi_read_capacity10_cmd_t;
+
+/// SCSI Read Capacity 10 Response Data
+typedef struct
+{
+ uint32_t last_lba; ///< The last Logical Block Address of the device
+ uint32_t block_size; ///< Block size in bytes
+} scsi_read_capacity10_resp_t;
+
+/// SCSI Read 10 Command
+typedef struct __packed {
+ uint8_t cmd_code; ///< SCSI OpCode
+ uint8_t reserved; // has LUN according to wiki
+ uint32_t lba; ///< The first Logical Block Address (LBA) accessed by this command
+ uint8_t reserved2;
+ uint16_t block_count; ///< Number of Blocks used by this command
+ uint8_t control;
+} scsi_read10_t, scsi_write10_t, scsi_read_write_10_t;
+
+#endif /* ZEPHYR_INCLUDE_USB_CLASS_USB_CDC_H_ */
diff --git a/class/video/usbd_video.c b/class/video/usbd_video.c
new file mode 100644
index 00000000..11c31649
--- /dev/null
+++ b/class/video/usbd_video.c
@@ -0,0 +1,134 @@
+/**
+ * @file usbd_video.c
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "usbd_core.h"
+#include "usbd_video.h"
+
+extern struct video_probe_and_commit_controls probe;
+extern struct video_probe_and_commit_controls commit;
+
+int video_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("Class request:"
+ "bRequest 0x%02x, bmRequestType 0x%02x len %d",
+ setup->bRequest, setup->bmRequestType, *len);
+
+ switch (setup->bRequest) {
+ case VIDEO_REQUEST_SET_CUR:
+ if (setup->wValue == 256) {
+ memcpy((uint8_t *)&probe, *data, setup->wLength);
+ } else if (setup->wValue == 512) {
+ memcpy((uint8_t *)&commit, *data, setup->wLength);
+ }
+
+ break;
+
+ case VIDEO_REQUEST_GET_CUR:
+ if (setup->wValue == 256) {
+ *data = (uint8_t *)&probe;
+ } else if (setup->wValue == 512) {
+ *data = (uint8_t *)&commit;
+ }
+
+ break;
+
+ case VIDEO_REQUEST_GET_MIN:
+ if (setup->wValue == 256) {
+ *data = (uint8_t *)&probe;
+ } else if (setup->wValue == 512) {
+ *data = (uint8_t *)&commit;
+ }
+
+ break;
+
+ case VIDEO_REQUEST_GET_MAX:
+ if (setup->wValue == 256) {
+ *data = (uint8_t *)&probe;
+ } else if (setup->wValue == 512) {
+ *data = (uint8_t *)&commit;
+ }
+
+ break;
+
+ case VIDEO_REQUEST_GET_RES:
+
+ break;
+
+ case VIDEO_REQUEST_GET_LEN:
+
+ break;
+
+ case VIDEO_REQUEST_GET_INFO:
+
+ break;
+
+ case VIDEO_REQUEST_GET_DEF:
+ if (setup->wLength == 256) {
+ *data = (uint8_t *)&probe;
+ } else if (setup->wLength == 512) {
+ *data = (uint8_t *)&commit;
+ }
+
+ break;
+
+ default:
+ USBD_LOG_ERR("Unhandled request 0x%02x", setup->bRequest);
+ break;
+ }
+
+ return 0;
+}
+
+void video_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+
+ break;
+
+ case USB_EVENT_SOF:
+ usbd_video_sof_callback();
+ break;
+
+ case USB_EVENT_SET_INTERFACE:
+ usbd_video_set_interface_callback(((uint8_t *)arg)[3]);
+ break;
+
+ default:
+ break;
+ }
+}
+
+void usbd_video_add_interface(usbd_class_t *class, usbd_interface_t *intf)
+{
+ static usbd_class_t *last_class = NULL;
+
+ if (last_class != class) {
+ last_class = class;
+ usbd_class_register(class);
+ }
+
+ intf->class_handler = video_class_request_handler;
+ intf->custom_handler = NULL;
+ intf->vendor_handler = NULL;
+ intf->notify_handler = video_notify_handler;
+ usbd_class_add_interface(class, intf);
+} \ No newline at end of file
diff --git a/class/video/usbd_video.h b/class/video/usbd_video.h
new file mode 100644
index 00000000..09c452f5
--- /dev/null
+++ b/class/video/usbd_video.h
@@ -0,0 +1,821 @@
+/**
+ * @file
+ * @brief USB Video Device Class public header
+ *
+ * Header follows below documentation:
+ * - USB Device Class Definition for Video Devices UVC 1.5 Class specification.pdf
+ */
+
+#ifndef _USBD_VIDEO_H_
+#define _USBD_VIDEO_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define USB_DEVICE_VIDEO_CLASS_VERSION_1_5 0
+
+/*! @brief Video device subclass code */
+#define VIDEO_SC_UNDEFINED 0x00U
+#define VIDEO_SC_VIDEOCONTROL 0x01U
+#define VIDEO_SC_VIDEOSTREAMING 0x02U
+#define VIDEO_SC_VIDEO_INTERFACE_COLLECTION 0x03U
+
+/*! @brief Video device protocol code */
+#define VIDEO_PC_PROTOCOL_UNDEFINED 0x00U
+#define VIDEO_PC_PROTOCOL_15 0x01U
+
+/*! @brief Video device class-specific descriptor type */
+#define VIDEO_CS_UNDEFINED_DESCRIPTOR_TYPE 0x20U
+#define VIDEO_CS_DEVICE_DESCRIPTOR_TYPE 0x21U
+#define VIDEO_CS_CONFIGURATION_DESCRIPTOR_TYPE 0x22U
+#define VIDEO_CS_STRING_DESCRIPTOR_TYPE 0x23U
+#define VIDEO_CS_INTERFACE_DESCRIPTOR_TYPE 0x24U
+#define VIDEO_CS_ENDPOINT_DESCRIPTOR_TYPE 0x25U
+
+/*! @brief Video device class-specific VC interface descriptor subtype */
+#define VIDEO_VC_DESCRIPTOR_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_VC_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_VC_INPUT_TERMINAL_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_VC_OUTPUT_TERMINAL_DESCRIPTOR_SUBTYPE 0x03U
+#define VIDEO_VC_SELECTOR_UNIT_DESCRIPTOR_SUBTYPE 0x04U
+#define VIDEO_VC_PROCESSING_UNIT_DESCRIPTOR_SUBTYPE 0x05U
+#define VIDEO_VC_EXTENSION_UNIT_DESCRIPTOR_SUBTYPE 0x06U
+#define VIDEO_VC_ENCODING_UNIT_DESCRIPTOR_SUBTYPE 0x07U
+
+/*! @brief Video device class-specific VS interface descriptor subtype */
+#define VIDEO_VS_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_VS_INPUT_HEADER_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_VS_OUTPUT_HEADER_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_VS_STILL_IMAGE_FRAME_DESCRIPTOR_SUBTYPE 0x03U
+#define VIDEO_VS_FORMAT_UNCOMPRESSED_DESCRIPTOR_SUBTYPE 0x04U
+#define VIDEO_VS_FRAME_UNCOMPRESSED_DESCRIPTOR_SUBTYPE 0x05U
+#define VIDEO_VS_FORMAT_MJPEG_DESCRIPTOR_SUBTYPE 0x06U
+#define VIDEO_VS_FRAME_MJPEG_DESCRIPTOR_SUBTYPE 0x07U
+#define VIDEO_VS_FORMAT_MPEG2TS_DESCRIPTOR_SUBTYPE 0x0AU
+#define VIDEO_VS_FORMAT_DV_DESCRIPTOR_SUBTYPE 0x0CU
+#define VIDEO_VS_COLORFORMAT_DESCRIPTOR_SUBTYPE 0x0DU
+#define VIDEO_VS_FORMAT_FRAME_BASED_DESCRIPTOR_SUBTYPE 0x10U
+#define VIDEO_VS_FRAME_FRAME_BASED_DESCRIPTOR_SUBTYPE 0x11U
+#define VIDEO_VS_FORMAT_STREAM_BASED_DESCRIPTOR_SUBTYPE 0x12U
+#define VIDEO_VS_FORMAT_H264_DESCRIPTOR_SUBTYPE 0x13U
+#define VIDEO_VS_FRAME_H264_DESCRIPTOR_SUBTYPE 0x14U
+#define VIDEO_VS_FORMAT_H264_SIMULCAST_DESCRIPTOR_SUBTYPE 0x15U
+#define VIDEO_VS_FORMAT_VP8_DESCRIPTOR_SUBTYPE 0x16U
+#define VIDEO_VS_FRAME_VP8_DESCRIPTOR_SUBTYPE 0x17U
+#define VIDEO_VS_FORMAT_VP8_SIMULCAST_DESCRIPTOR_SUBTYPE 0x18U
+
+/*! @brief Video device class-specific VC endpoint descriptor subtype */
+#define VIDEO_EP_UNDEFINED_DESCRIPTOR_SUBTYPE 0x00U
+#define VIDEO_EP_GENERAL_DESCRIPTOR_SUBTYPE 0x01U
+#define VIDEO_EP_ENDPOINT_DESCRIPTOR_SUBTYPE 0x02U
+#define VIDEO_EP_INTERRUPT_DESCRIPTOR_SUBTYPE 0x03U
+
+/*! @brief Video device class-specific request code */
+#define VIDEO_REQUEST_UNDEFINED 0x00U
+#define VIDEO_REQUEST_SET_CUR 0x01U
+#define VIDEO_REQUEST_SET_CUR_ALL 0x11U
+#define VIDEO_REQUEST_GET_CUR 0x81U
+#define VIDEO_REQUEST_GET_MIN 0x82U
+#define VIDEO_REQUEST_GET_MAX 0x83U
+#define VIDEO_REQUEST_GET_RES 0x84U
+#define VIDEO_REQUEST_GET_LEN 0x85U
+#define VIDEO_REQUEST_GET_INFO 0x86U
+#define VIDEO_REQUEST_GET_DEF 0x87U
+#define VIDEO_REQUEST_GET_CUR_ALL 0x91U
+#define VIDEO_REQUEST_GET_MIN_ALL 0x92U
+#define VIDEO_REQUEST_GET_MAX_ALL 0x93U
+#define VIDEO_REQUEST_GET_RES_ALL 0x94U
+#define VIDEO_REQUEST_GET_DEF_ALL 0x97U
+
+/*! @brief Video device class-specific VideoControl interface control selector */
+#define VIDEO_VC_CONTROL_UNDEFINED 0x00U
+#define VIDEO_VC_VIDEO_POWER_MODE_CONTROL 0x01U
+#define VIDEO_VC_REQUEST_ERROR_CODE_CONTROL 0x02U
+
+/*! @brief Video device class-specific Terminal control selector */
+#define VIDEO_TE_CONTROL_UNDEFINED 0x00U
+
+/*! @brief Video device class-specific Selector Unit control selector */
+#define VIDEO_SU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_SU_INPUT_SELECT_CONTROL 0x01U
+
+/*! @brief Video device class-specific Camera Terminal control selector */
+#define VIDEO_CT_CONTROL_UNDEFINED 0x00U
+#define VIDEO_CT_SCANNING_MODE_CONTROL 0x01U
+#define VIDEO_CT_AE_MODE_CONTROL 0x02U
+#define VIDEO_CT_AE_PRIORITY_CONTROL 0x03U
+#define VIDEO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x04U
+#define VIDEO_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x05U
+#define VIDEO_CT_FOCUS_ABSOLUTE_CONTROL 0x06U
+#define VIDEO_CT_FOCUS_RELATIVE_CONTROL 0x07U
+#define VIDEO_CT_FOCUS_AUTO_CONTROL 0x08U
+#define VIDEO_CT_IRIS_ABSOLUTE_CONTROL 0x09U
+#define VIDEO_CT_IRIS_RELATIVE_CONTROL 0x0AU
+#define VIDEO_CT_ZOOM_ABSOLUTE_CONTROL 0x0BU
+#define VIDEO_CT_ZOOM_RELATIVE_CONTROL 0x0CU
+#define VIDEO_CT_PANTILT_ABSOLUTE_CONTROL 0x0DU
+#define VIDEO_CT_PANTILT_RELATIVE_CONTROL 0x0EU
+#define VIDEO_CT_ROLL_ABSOLUTE_CONTROL 0x0FU
+#define VIDEO_CT_ROLL_RELATIVE_CONTROL 0x10U
+#define VIDEO_CT_PRIVACY_CONTROL 0x11U
+#define VIDEO_CT_FOCUS_SIMPLE_CONTROL 0x12U
+#define VIDEO_CT_WINDOW_CONTROL 0x13U
+#define VIDEO_CT_REGION_OF_INTEREST_CONTROL 0x14U
+
+/*! @brief Video device class-specific Processing Unit control selector */
+#define VIDEO_PU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_PU_BACKLIGHT_COMPENSATION_CONTROL 0x01U
+#define VIDEO_PU_BRIGHTNESS_CONTROL 0x02U
+#define VIDEO_PU_CONTRAST_CONTROL 0x03U
+#define VIDEO_PU_GAIN_CONTROL 0x04U
+#define VIDEO_PU_POWER_LINE_FREQUENCY_CONTROL 0x05U
+#define VIDEO_PU_HUE_CONTROL 0x06U
+#define VIDEO_PU_SATURATION_CONTROL 0x07U
+#define VIDEO_PU_SHARPNESS_CONTROL 0x08U
+#define VIDEO_PU_GAMMA_CONTROL 0x09U
+#define VIDEO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x0AU
+#define VIDEO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x0BU
+#define VIDEO_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x0CU
+#define VIDEO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x0DU
+#define VIDEO_PU_DIGITAL_MULTIPLIER_CONTROL 0x0EU
+#define VIDEO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x0FU
+#define VIDEO_PU_HUE_AUTO_CONTROL 0x10U
+#define VIDEO_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x11U
+#define VIDEO_PU_ANALOG_LOCK_STATUS_CONTROL 0x12U
+#define VIDEO_PU_CONTRAST_AUTO_CONTROL 0x13U
+
+/*! @brief Video device class-specific Encoding Unit control selector */
+#define VIDEO_EU_CONTROL_UNDEFINED 0x00U
+#define VIDEO_EU_SELECT_LAYER_CONTROL 0x01U
+#define VIDEO_EU_PROFILE_TOOLSET_CONTROL 0x02U
+#define VIDEO_EU_VIDEO_RESOLUTION_CONTROL 0x03U
+#define VIDEO_EU_MIN_FRAME_INTERVAL_CONTROL 0x04U
+#define VIDEO_EU_SLICE_MODE_CONTROL 0x05U
+#define VIDEO_EU_RATE_CONTROL_MODE_CONTROL 0x06U
+#define VIDEO_EU_AVERAGE_BITRATE_CONTROL 0x07U
+#define VIDEO_EU_CPB_SIZE_CONTROL 0x08U
+#define VIDEO_EU_PEAK_BIT_RATE_CONTROL 0x09U
+#define VIDEO_EU_QUANTIZATION_PARAMS_CONTROL 0x0AU
+#define VIDEO_EU_SYNC_REF_FRAME_CONTROL 0x0BU
+#define VIDEO_EU_LTR_BUFFER_ CONTROL0x0CU
+#define VIDEO_EU_LTR_PICTURE_CONTROL 0x0DU
+#define VIDEO_EU_LTR_VALIDATION_CONTROL 0x0EU
+#define VIDEO_EU_LEVEL_IDC_LIMIT_CONTROL 0x0FU
+#define VIDEO_EU_SEI_PAYLOADTYPE_CONTROL 0x10U
+#define VIDEO_EU_QP_RANGE_CONTROL 0x11U
+#define VIDEO_EU_PRIORITY_CONTROL 0x12U
+#define VIDEO_EU_START_OR_STOP_LAYER_CONTROL 0x13U
+#define VIDEO_EU_ERROR_RESILIENCY_CONTROL 0x14U
+
+/*! @brief Video device class-specific Extension Unit control selector */
+#define VIDEO_XU_CONTROL_UNDEFINED 0x00U
+
+/*! @brief Video device class-specific VideoStreaming Interface control selector */
+#define VIDEO_VS_CONTROL_UNDEFINED 0x00U
+#define VIDEO_VS_PROBE_CONTROL 0x01U
+#define VIDEO_VS_COMMIT_CONTROL 0x02U
+#define VIDEO_VS_STILL_PROBE_CONTROL 0x03U
+#define VIDEO_VS_STILL_COMMIT_CONTROL 0x04U
+#define VIDEO_VS_STILL_IMAGE_TRIGGER_CONTROL 0x05U
+#define VIDEO_VS_STREAM_ERROR_CODE_CONTROL 0x06U
+#define VIDEO_VS_GENERATE_KEY_FRAME_CONTROL 0x07U
+#define VIDEO_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x08U
+#define VIDEO_VS_SYNCH_DELAY_CONTROL 0x09U
+
+/*! @}*/
+
+/*!
+ * @name USB Video class terminal types
+ * @{
+ */
+
+/*! @brief Video device USB terminal type */
+#define VIDEO_TT_VENDOR_SPECIFIC 0x0100U
+#define VIDEO_TT_STREAMING 0x0101U
+
+/*! @brief Video device input terminal type */
+#define VIDEO_ITT_VENDOR_SPECIFIC 0x0200U
+#define VIDEO_ITT_CAMERA 0x0201U
+#define VIDEO_ITT_MEDIA_TRANSPORT_INPUT 0x0202U
+
+/*! @brief Video device output terminal type */
+#define VIDEO_OTT_VENDOR_SPECIFIC 0x0300U
+#define VIDEO_OTT_DISPLAY 0x0301U
+#define VIDEO_OTT_MEDIA_TRANSPORT_OUTPUT 0x0302U
+
+/*! @brief Video device external terminal type */
+#define VIDEO_ET_VENDOR_SPECIFIC 0x0400U
+#define VIDEO_ET_COMPOSITE_CONNECTOR 0x0401U
+#define VIDEO_ET_SVIDEO_CONNECTOR 0x0402U
+#define VIDEO_ET_COMPONENT_CONNECTOR 0x0403U
+
+/*! @}*/
+
+/*!
+ * @name USB Video class setup request types
+ * @{
+ */
+
+/*! @brief Video device class setup request set type */
+#define VIDEO_SET_REQUEST_INTERFACE 0x21U
+#define VIDEO_SET_REQUEST_ENDPOINT 0x22U
+
+/*! @brief Video device class setup request get type */
+#define VIDEO_GET_REQUEST_INTERFACE 0xA1U
+#define VIDEO_GET_REQUEST_ENDPOINT 0xA2U
+
+/*! @}*/
+
+/*! @brief Video device still image trigger control */
+#define VIDEO_STILL_IMAGE_TRIGGER_NORMAL_OPERATION 0x00U
+#define VIDEO_STILL_IMAGE_TRIGGER_TRANSMIT_STILL_IMAGE 0x01U
+#define VIDEO_STILL_IMAGE_TRIGGER_TRANSMIT_STILL_IMAGE_VS_DEDICATED_BULK_PIPE 0x02U
+#define VIDEO_STILL_IMAGE_TRIGGER_ABORT_STILL_IMAGE_TRANSMISSION 0x03U
+
+/*!
+ * @name USB Video device class-specific request commands
+ * @{
+ */
+
+/*! @brief Video device class-specific request GET CUR COMMAND */
+#define VIDEO_GET_CUR_VC_POWER_MODE_CONTROL 0x8101U
+#define VIDEO_GET_CUR_VC_ERROR_CODE_CONTROL 0x8102U
+
+#define VIDEO_GET_CUR_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8121U
+#define VIDEO_GET_CUR_PU_BRIGHTNESS_CONTROL 0x8122U
+#define VIDEO_GET_CUR_PU_CONTRACT_CONTROL 0x8123U
+#define VIDEO_GET_CUR_PU_GAIN_CONTROL 0x8124U
+#define VIDEO_GET_CUR_PU_POWER_LINE_FREQUENCY_CONTROL 0x8125U
+#define VIDEO_GET_CUR_PU_HUE_CONTROL 0x8126U
+#define VIDEO_GET_CUR_PU_SATURATION_CONTROL 0x8127U
+#define VIDEO_GET_CUR_PU_SHARRNESS_CONTROL 0x8128U
+#define VIDEO_GET_CUR_PU_GAMMA_CONTROL 0x8129U
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x812AU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x812BU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x812CU
+#define VIDEO_GET_CUR_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x812DU
+#define VIDEO_GET_CUR_PU_DIGITAL_MULTIPLIER_CONTROL 0x812EU
+#define VIDEO_GET_CUR_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x812FU
+#define VIDEO_GET_CUR_PU_HUE_AUTO_CONTROL 0x8130U
+#define VIDEO_GET_CUR_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x8131U
+#define VIDEO_GET_CUR_PU_ANALOG_LOCK_STATUS_CONTROL 0x8132U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_PU_CONTRAST_AUTO_CONTROL 0x8133U
+#endif
+
+#define VIDEO_GET_CUR_CT_SCANNING_MODE_CONTROL 0x8141U
+#define VIDEO_GET_CUR_CT_AE_MODE_CONTROL 0x8142U
+#define VIDEO_GET_CUR_CT_AE_PRIORITY_CONTROL 0x8143U
+#define VIDEO_GET_CUR_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8144U
+#define VIDEO_GET_CUR_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x8145U
+#define VIDEO_GET_CUR_CT_FOCUS_ABSOLUTE_CONTROL 0x8146U
+#define VIDEO_GET_CUR_CT_FOCUS_RELATIVE_CONTROL 0x8147U
+#define VIDEO_GET_CUR_CT_FOCUS_AUTO_CONTROL 0x8148U
+#define VIDEO_GET_CUR_CT_IRIS_ABSOLUTE_CONTROL 0x8149U
+#define VIDEO_GET_CUR_CT_IRIS_RELATIVE_CONTROL 0x814AU
+#define VIDEO_GET_CUR_CT_ZOOM_ABSOLUTE_CONTROL 0x814BU
+#define VIDEO_GET_CUR_CT_ZOOM_RELATIVE_CONTROL 0x814CU
+#define VIDEO_GET_CUR_CT_PANTILT_ABSOLUTE_CONTROL 0x814DU
+#define VIDEO_GET_CUR_CT_PANTILT_RELATIVE_CONTROL 0x814EU
+#define VIDEO_GET_CUR_CT_ROLL_ABSOLUTE_CONTROL 0x814FU
+#define VIDEO_GET_CUR_CT_ROLL_RELATIVE_CONTROL 0x8150U
+#define VIDEO_GET_CUR_CT_PRIVACY_CONTROL 0x8151U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_CT_FOCUS_SIMPLE_CONTROL 0x8152U
+#define VIDEO_GET_CUR_CT_DIGITAL_WINDOW_CONTROL 0x8153U
+#define VIDEO_GET_CUR_CT_REGION_OF_INTEREST_CONTROL 0x8154U
+#endif
+
+#define VIDEO_GET_CUR_VS_PROBE_CONTROL 0x8161U
+#define VIDEO_GET_CUR_VS_COMMIT_CONTROL 0x8162U
+#define VIDEO_GET_CUR_VS_STILL_PROBE_CONTROL 0x8163U
+#define VIDEO_GET_CUR_VS_STILL_COMMIT_CONTROL 0x8164U
+#define VIDEO_GET_CUR_VS_STILL_IMAGE_TRIGGER_CONTROL 0x8165U
+#define VIDEO_GET_CUR_VS_STREAM_ERROR_CODE_CONTROL 0x8166U
+#define VIDEO_GET_CUR_VS_GENERATE_KEY_FRAME_CONTROL 0x8167U
+#define VIDEO_GET_CUR_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8168U
+#define VIDEO_GET_CUR_VS_SYNCH_DELAY_CONTROL 0x8169U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_CUR_EU_SELECT_LAYER_CONTROL 0x8181U
+#define VIDEO_GET_CUR_EU_PROFILE_TOOLSET_CONTROL 0x8182U
+#define VIDEO_GET_CUR_EU_VIDEO_RESOLUTION_CONTROL 0x8183U
+#define VIDEO_GET_CUR_EU_MIN_FRAME_INTERVAL_CONTROL 0x8184U
+#define VIDEO_GET_CUR_EU_SLICE_MODE_CONTROL 0x8185U
+#define VIDEO_GET_CUR_EU_RATE_CONTROL_MODE_CONTROL 0x8186U
+#define VIDEO_GET_CUR_EU_AVERAGE_BITRATE_CONTROL 0x8187U
+#define VIDEO_GET_CUR_EU_CPB_SIZE_CONTROL 0x8188U
+#define VIDEO_GET_CUR_EU_PEAK_BIT_RATE_CONTROL 0x8189U
+#define VIDEO_GET_CUR_EU_QUANTIZATION_PARAMS_CONTROL 0x818AU
+#define VIDEO_GET_CUR_EU_SYNC_REF_FRAME_CONTROL 0x818BU
+#define VIDEO_GET_CUR_EU_LTR_BUFFER_CONTROL 0x818CU
+#define VIDEO_GET_CUR_EU_LTR_PICTURE_CONTROL 0x818DU
+#define VIDEO_GET_CUR_EU_LTR_VALIDATION_CONTROL 0x818EU
+#define VIDEO_GET_CUR_EU_LEVEL_IDC_LIMIT_CONTROL 0x818FU
+#define VIDEO_GET_CUR_EU_SEI_PAYLOADTYPE_CONTROL 0x8190U
+#define VIDEO_GET_CUR_EU_QP_RANGE_CONTROL 0x8191U
+#define VIDEO_GET_CUR_EU_PRIORITY_CONTROL 0x8192U
+#define VIDEO_GET_CUR_EU_START_OR_STOP_LAYER_CONTROL 0x8193U
+#define VIDEO_GET_CUR_EU_ERROR_RESILIENCY_CONTROL 0x8194U
+#endif
+
+/*! @brief Video device class-specific request GET MIN COMMAND */
+#define VIDEO_GET_MIN_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8221U
+#define VIDEO_GET_MIN_PU_BRIGHTNESS_CONTROL 0x8222U
+#define VIDEO_GET_MIN_PU_CONTRACT_CONTROL 0x8223U
+#define VIDEO_GET_MIN_PU_GAIN_CONTROL 0x8224U
+#define VIDEO_GET_MIN_PU_HUE_CONTROL 0x8226U
+#define VIDEO_GET_MIN_PU_SATURATION_CONTROL 0x8227U
+#define VIDEO_GET_MIN_PU_SHARRNESS_CONTROL 0x8228U
+#define VIDEO_GET_MIN_PU_GAMMA_CONTROL 0x8229U
+#define VIDEO_GET_MIN_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x822AU
+#define VIDEO_GET_MIN_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x822CU
+#define VIDEO_GET_MIN_PU_DIGITAL_MULTIPLIER_CONTROL 0x822EU
+#define VIDEO_GET_MIN_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x822FU
+
+#define VIDEO_GET_MIN_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8244U
+#define VIDEO_GET_MIN_CT_FOCUS_ABSOLUTE_CONTROL 0x8246U
+#define VIDEO_GET_MIN_CT_FOCUS_RELATIVE_CONTROL 0x8247U
+#define VIDEO_GET_MIN_CT_IRIS_ABSOLUTE_CONTROL 0x8249U
+#define VIDEO_GET_MIN_CT_ZOOM_ABSOLUTE_CONTROL 0x824BU
+#define VIDEO_GET_MIN_CT_ZOOM_RELATIVE_CONTROL 0x824CU
+#define VIDEO_GET_MIN_CT_PANTILT_ABSOLUTE_CONTROL 0x824DU
+#define VIDEO_GET_MIN_CT_PANTILT_RELATIVE_CONTROL 0x824EU
+#define VIDEO_GET_MIN_CT_ROLL_ABSOLUTE_CONTROL 0x824FU
+#define VIDEO_GET_MIN_CT_ROLL_RELATIVE_CONTROL 0x8250U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MIN_CT_DIGITAL_WINDOW_CONTROL 0x8251U
+#define VIDEO_GET_MIN_CT_REGION_OF_INTEREST_CONTROL 0x8252U
+#endif
+
+#define VIDEO_GET_MIN_VS_PROBE_CONTROL 0x8261U
+#define VIDEO_GET_MIN_VS_STILL_PROBE_CONTROL 0x8263U
+#define VIDEO_GET_MIN_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8268U
+#define VIDEO_GET_MIN_VS_SYNCH_DELAY_CONTROL 0x8269U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MIN_EU_VIDEO_RESOLUTION_CONTROL 0x8283U
+#define VIDEO_GET_MIN_EU_MIN_FRAME_INTERVAL_CONTROL 0x8284U
+#define VIDEO_GET_MIN_EU_SLICE_MODE_CONTROL 0x8285U
+#define VIDEO_GET_MIN_EU_AVERAGE_BITRATE_CONTROL 0x8287U
+#define VIDEO_GET_MIN_EU_CPB_SIZE_CONTROL 0x8288U
+#define VIDEO_GET_MIN_EU_PEAK_BIT_RATE_CONTROL 0x8289U
+#define VIDEO_GET_MIN_EU_QUANTIZATION_PARAMS_CONTROL 0x828AU
+#define VIDEO_GET_MIN_EU_SYNC_REF_FRAME_CONTROL 0x828BU
+#define VIDEO_GET_MIN_EU_LEVEL_IDC_LIMIT_CONTROL 0x828FU
+#define VIDEO_GET_MIN_EU_SEI_PAYLOADTYPE_CONTROL 0x8290U
+#define VIDEO_GET_MIN_EU_QP_RANGE_CONTROL 0x8291U
+#endif
+
+/*! @brief Video device class-specific request GET MAX COMMAND */
+#define VIDEO_GET_MAX_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8321U
+#define VIDEO_GET_MAX_PU_BRIGHTNESS_CONTROL 0x8322U
+#define VIDEO_GET_MAX_PU_CONTRACT_CONTROL 0x8323U
+#define VIDEO_GET_MAX_PU_GAIN_CONTROL 0x8324U
+#define VIDEO_GET_MAX_PU_HUE_CONTROL 0x8326U
+#define VIDEO_GET_MAX_PU_SATURATION_CONTROL 0x8327U
+#define VIDEO_GET_MAX_PU_SHARRNESS_CONTROL 0x8328U
+#define VIDEO_GET_MAX_PU_GAMMA_CONTROL 0x8329U
+#define VIDEO_GET_MAX_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x832AU
+#define VIDEO_GET_MAX_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x832CU
+#define VIDEO_GET_MAX_PU_DIGITAL_MULTIPLIER_CONTROL 0x832EU
+#define VIDEO_GET_MAX_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x832FU
+
+#define VIDEO_GET_MAX_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8344U
+#define VIDEO_GET_MAX_CT_FOCUS_ABSOLUTE_CONTROL 0x8346U
+#define VIDEO_GET_MAX_CT_FOCUS_RELATIVE_CONTROL 0x8347U
+#define VIDEO_GET_MAX_CT_IRIS_ABSOLUTE_CONTROL 0x8349U
+#define VIDEO_GET_MAX_CT_ZOOM_ABSOLUTE_CONTROL 0x834BU
+#define VIDEO_GET_MAX_CT_ZOOM_RELATIVE_CONTROL 0x834CU
+#define VIDEO_GET_MAX_CT_PANTILT_ABSOLUTE_CONTROL 0x834DU
+#define VIDEO_GET_MAX_CT_PANTILT_RELATIVE_CONTROL 0x834EU
+#define VIDEO_GET_MAX_CT_ROLL_ABSOLUTE_CONTROL 0x834FU
+#define VIDEO_GET_MAX_CT_ROLL_RELATIVE_CONTROL 0x8350U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MAX_CT_DIGITAL_WINDOW_CONTROL 0x8351U
+#define VIDEO_GET_MAX_CT_REGION_OF_INTEREST_CONTROL 0x8352U
+#endif
+
+#define VIDEO_GET_MAX_VS_PROBE_CONTROL 0x8361U
+#define VIDEO_GET_MAX_VS_STILL_PROBE_CONTROL 0x8363U
+#define VIDEO_GET_MAX_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8368U
+#define VIDEO_GET_MAX_VS_SYNCH_DELAY_CONTROL 0x8369U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_MAX_EU_VIDEO_RESOLUTION_CONTROL 0x8383U
+#define VIDEO_GET_MAX_EU_MIN_FRAME_INTERVAL_CONTROL 0x8384U
+#define VIDEO_GET_MAX_EU_SLICE_MODE_CONTROL 0x8385U
+#define VIDEO_GET_MAX_EU_AVERAGE_BITRATE_CONTROL 0x8387U
+#define VIDEO_GET_MAX_EU_CPB_SIZE_CONTROL 0x8388U
+#define VIDEO_GET_MAX_EU_PEAK_BIT_RATE_CONTROL 0x8389U
+#define VIDEO_GET_MAX_EU_QUANTIZATION_PARAMS_CONTROL 0x838AU
+#define VIDEO_GET_MAX_EU_SYNC_REF_FRAME_CONTROL 0x838BU
+#define VIDEO_GET_MAX_EU_LTR_BUFFER_CONTROL 0x838CU
+#define VIDEO_GET_MAX_EU_LEVEL_IDC_LIMIT_CONTROL 0x838FU
+#define VIDEO_GET_MAX_EU_SEI_PAYLOADTYPE_CONTROL 0x8390U
+#define VIDEO_GET_MAX_EU_QP_RANGE_CONTROL 0x8391U
+#endif
+
+/*! @brief Video device class-specific request GET RES COMMAND */
+#define VIDEO_GET_RES_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8421U
+#define VIDEO_GET_RES_PU_BRIGHTNESS_CONTROL 0x8422U
+#define VIDEO_GET_RES_PU_CONTRACT_CONTROL 0x8423U
+#define VIDEO_GET_RES_PU_GAIN_CONTROL 0x8424U
+#define VIDEO_GET_RES_PU_HUE_CONTROL 0x8426U
+#define VIDEO_GET_RES_PU_SATURATION_CONTROL 0x8427U
+#define VIDEO_GET_RES_PU_SHARRNESS_CONTROL 0x8428U
+#define VIDEO_GET_RES_PU_GAMMA_CONTROL 0x8429U
+#define VIDEO_GET_RES_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x842AU
+#define VIDEO_GET_RES_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x842CU
+#define VIDEO_GET_RES_PU_DIGITAL_MULTIPLIER_CONTROL 0x842EU
+#define VIDEO_GET_RES_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x842FU
+
+#define VIDEO_GET_RES_CT_AE_MODE_CONTROL 0x8442U
+#define VIDEO_GET_RES_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8444U
+#define VIDEO_GET_RES_CT_FOCUS_ABSOLUTE_CONTROL 0x8446U
+#define VIDEO_GET_RES_CT_FOCUS_RELATIVE_CONTROL 0x8447U
+#define VIDEO_GET_RES_CT_IRIS_ABSOLUTE_CONTROL 0x8449U
+#define VIDEO_GET_RES_CT_ZOOM_ABSOLUTE_CONTROL 0x844BU
+#define VIDEO_GET_RES_CT_ZOOM_RELATIVE_CONTROL 0x844CU
+#define VIDEO_GET_RES_CT_PANTILT_ABSOLUTE_CONTROL 0x844DU
+#define VIDEO_GET_RES_CT_PANTILT_RELATIVE_CONTROL 0x844EU
+#define VIDEO_GET_RES_CT_ROLL_ABSOLUTE_CONTROL 0x844FU
+#define VIDEO_GET_RES_CT_ROLL_RELATIVE_CONTROL 0x8450U
+
+#define VIDEO_GET_RES_VS_PROBE_CONTROL 0x8461U
+#define VIDEO_GET_RES_VS_STILL_PROBE_CONTROL 0x8463U
+#define VIDEO_GET_RES_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8468U
+#define VIDEO_GET_RES_VS_SYNCH_DELAY_CONTROL 0x8469U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_RES_EU_AVERAGE_BITRATE_CONTROL 0x8487U
+#define VIDEO_GET_RES_EU_CPB_SIZE_CONTROL 0x8488U
+#define VIDEO_GET_RES_EU_PEAK_BIT_RATE_CONTROL 0x8489U
+#define VIDEO_GET_RES_EU_QUANTIZATION_PARAMS_CONTROL 0x848AU
+#define VIDEO_GET_RES_EU_ERROR_RESILIENCY_CONTROL 0x8494U
+#endif
+
+/*! @brief Video device class-specific request GET LEN COMMAND */
+
+#define VIDEO_GET_LEN_VS_PROBE_CONTROL 0x8561U
+#define VIDEO_GET_LEN_VS_COMMIT_CONTROL 0x8562U
+#define VIDEO_GET_LEN_VS_STILL_PROBE_CONTROL 0x8563U
+#define VIDEO_GET_LEN_VS_STILL_COMMIT_CONTROL 0x8564U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_LEN_EU_SELECT_LAYER_CONTROL 0x8581U
+#define VIDEO_GET_LEN_EU_PROFILE_TOOLSET_CONTROL 0x8582U
+#define VIDEO_GET_LEN_EU_VIDEO_RESOLUTION_CONTROL 0x8583U
+#define VIDEO_GET_LEN_EU_MIN_FRAME_INTERVAL_CONTROL 0x8584U
+#define VIDEO_GET_LEN_EU_SLICE_MODE_CONTROL 0x8585U
+#define VIDEO_GET_LEN_EU_RATE_CONTROL_MODE_CONTROL 0x8586U
+#define VIDEO_GET_LEN_EU_AVERAGE_BITRATE_CONTROL 0x8587U
+#define VIDEO_GET_LEN_EU_CPB_SIZE_CONTROL 0x8588U
+#define VIDEO_GET_LEN_EU_PEAK_BIT_RATE_CONTROL 0x8589U
+#define VIDEO_GET_LEN_EU_QUANTIZATION_PARAMS_CONTROL 0x858AU
+#define VIDEO_GET_LEN_EU_SYNC_REF_FRAME_CONTROL 0x858BU
+#define VIDEO_GET_LEN_EU_LTR_BUFFER_CONTROL 0x858CU
+#define VIDEO_GET_LEN_EU_LTR_PICTURE_CONTROL 0x858DU
+#define VIDEO_GET_LEN_EU_LTR_VALIDATION_CONTROL 0x858EU
+#define VIDEO_GET_LEN_EU_QP_RANGE_CONTROL 0x8591U
+#define VIDEO_GET_LEN_EU_PRIORITY_CONTROL 0x8592U
+#define VIDEO_GET_LEN_EU_START_OR_STOP_LAYER_CONTROL 0x8593U
+#endif
+
+/*! @brief Video device class-specific request GET INFO COMMAND */
+#define VIDEO_GET_INFO_VC_POWER_MODE_CONTROL 0x8601U
+#define VIDEO_GET_INFO_VC_ERROR_CODE_CONTROL 0x8602U
+
+#define VIDEO_GET_INFO_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8621U
+#define VIDEO_GET_INFO_PU_BRIGHTNESS_CONTROL 0x8622U
+#define VIDEO_GET_INFO_PU_CONTRACT_CONTROL 0x8623U
+#define VIDEO_GET_INFO_PU_GAIN_CONTROL 0x8624U
+#define VIDEO_GET_INFO_PU_POWER_LINE_FREQUENCY_CONTROL 0x8625U
+#define VIDEO_GET_INFO_PU_HUE_CONTROL 0x8626U
+#define VIDEO_GET_INFO_PU_SATURATION_CONTROL 0x8627U
+#define VIDEO_GET_INFO_PU_SHARRNESS_CONTROL 0x8628U
+#define VIDEO_GET_INFO_PU_GAMMA_CONTROL 0x8629U
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x862AU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x862BU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x862CU
+#define VIDEO_GET_INFO_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x862DU
+#define VIDEO_GET_INFO_PU_DIGITAL_MULTIPLIER_CONTROL 0x862EU
+#define VIDEO_GET_INFO_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x862FU
+#define VIDEO_GET_INFO_PU_HUE_AUTO_CONTROL 0x8630U
+#define VIDEO_GET_INFO_PU_ANALOG_VIDEO_STANDARD_CONTROL 0x8631U
+#define VIDEO_GET_INFO_PU_ANALOG_LOCK_STATUS_CONTROL 0x8632U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_PU_CONTRAST_AUTO_CONTROL 0x8633U
+#endif
+
+#define VIDEO_GET_INFO_CT_SCANNING_MODE_CONTROL 0x8641U
+#define VIDEO_GET_INFO_CT_AE_MODE_CONTROL 0x8642U
+#define VIDEO_GET_INFO_CT_AE_PRIORITY_CONTROL 0x8643U
+#define VIDEO_GET_INFO_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8644U
+#define VIDEO_GET_INFO_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x8645U
+#define VIDEO_GET_INFO_CT_FOCUS_ABSOLUTE_CONTROL 0x8646U
+#define VIDEO_GET_INFO_CT_FOCUS_RELATIVE_CONTROL 0x8647U
+#define VIDEO_GET_INFO_CT_FOCUS_AUTO_CONTROL 0x8648U
+#define VIDEO_GET_INFO_CT_IRIS_ABSOLUTE_CONTROL 0x8649U
+#define VIDEO_GET_INFO_CT_IRIS_RELATIVE_CONTROL 0x864AU
+#define VIDEO_GET_INFO_CT_ZOOM_ABSOLUTE_CONTROL 0x864BU
+#define VIDEO_GET_INFO_CT_ZOOM_RELATIVE_CONTROL 0x864CU
+#define VIDEO_GET_INFO_CT_PANTILT_ABSOLUTE_CONTROL 0x864DU
+#define VIDEO_GET_INFO_CT_PANTILT_RELATIVE_CONTROL 0x864EU
+#define VIDEO_GET_INFO_CT_ROLL_ABSOLUTE_CONTROL 0x864FU
+#define VIDEO_GET_INFO_CT_ROLL_RELATIVE_CONTROL 0x8650U
+#define VIDEO_GET_INFO_CT_PRIVACY_CONTROL 0x8651U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_CT_FOCUS_SIMPLE_CONTROL 0x8652U
+#endif
+
+#define VIDEO_GET_INFO_VS_PROBE_CONTROL 0x8661U
+#define VIDEO_GET_INFO_VS_COMMIT_CONTROL 0x8662U
+#define VIDEO_GET_INFO_VS_STILL_PROBE_CONTROL 0x8663U
+#define VIDEO_GET_INFO_VS_STILL_COMMIT_CONTROL 0x8664U
+#define VIDEO_GET_INFO_VS_STILL_IMAGE_TRIGGER_CONTROL 0x8665U
+#define VIDEO_GET_INFO_VS_STREAM_ERROR_CODE_CONTROL 0x8666U
+#define VIDEO_GET_INFO_VS_GENERATE_KEY_FRAME_CONTROL 0x8667U
+#define VIDEO_GET_INFO_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8668U
+#define VIDEO_GET_INFO_VS_SYNCH_DELAY_CONTROL 0x8669U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_INFO_EU_SELECT_LAYER_CONTROL 0x8681U
+#define VIDEO_GET_INFO_EU_PROFILE_TOOLSET_CONTROL 0x8682U
+#define VIDEO_GET_INFO_EU_VIDEO_RESOLUTION_CONTROL 0x8683U
+#define VIDEO_GET_INFO_EU_MIN_FRAME_INTERVAL_CONTROL 0x8684U
+#define VIDEO_GET_INFO_EU_SLICE_MODE_CONTROL 0x8685U
+#define VIDEO_GET_INFO_EU_RATE_CONTROL_MODE_CONTROL 0x8686U
+#define VIDEO_GET_INFO_EU_AVERAGE_BITRATE_CONTROL 0x8687U
+#define VIDEO_GET_INFO_EU_CPB_SIZE_CONTROL 0x8688U
+#define VIDEO_GET_INFO_EU_PEAK_BIT_RATE_CONTROL 0x8689U
+#define VIDEO_GET_INFO_EU_QUANTIZATION_PARAMS_CONTROL 0x868AU
+#define VIDEO_GET_INFO_EU_SYNC_REF_FRAME_CONTROL 0x868BU
+#define VIDEO_GET_INFO_EU_LTR_BUFFER_CONTROL 0x868CU
+#define VIDEO_GET_INFO_EU_LTR_PICTURE_CONTROL 0x868DU
+#define VIDEO_GET_INFO_EU_LTR_VALIDATION_CONTROL 0x868EU
+#define VIDEO_GET_INFO_EU_SEI_PAYLOADTYPE_CONTROL 0x8690U
+#define VIDEO_GET_INFO_EU_QP_RANGE_CONTROL 0x8691U
+#define VIDEO_GET_INFO_EU_PRIORITY_CONTROL 0x8692U
+#define VIDEO_GET_INFO_EU_START_OR_STOP_LAYER_CONTROL 0x8693U
+#endif
+
+/*! @brief Video device class-specific request GET DEF COMMAND */
+#define VIDEO_GET_DEF_PU_BACKLIGHT_COMPENSATION_CONTROL 0x8721U
+#define VIDEO_GET_DEF_PU_BRIGHTNESS_CONTROL 0x8722U
+#define VIDEO_GET_DEF_PU_CONTRACT_CONTROL 0x8723U
+#define VIDEO_GET_DEF_PU_GAIN_CONTROL 0x8724U
+#define VIDEO_GET_DEF_PU_POWER_LINE_FREQUENCY_CONTROL 0x8725U
+#define VIDEO_GET_DEF_PU_HUE_CONTROL 0x8726U
+#define VIDEO_GET_DEF_PU_SATURATION_CONTROL 0x8727U
+#define VIDEO_GET_DEF_PU_SHARRNESS_CONTROL 0x8728U
+#define VIDEO_GET_DEF_PU_GAMMA_CONTROL 0x8729U
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x872AU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x872BU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x872CU
+#define VIDEO_GET_DEF_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x872DU
+#define VIDEO_GET_DEF_PU_DIGITAL_MULTIPLIER_CONTROL 0x872EU
+#define VIDEO_GET_DEF_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x872FU
+#define VIDEO_GET_DEF_PU_HUE_AUTO_CONTROL 0x8730U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_PU_CONTRAST_AUTO_CONTROL 0x8731U
+#endif
+
+#define VIDEO_GET_DEF_CT_AE_MODE_CONTROL 0x8742U
+#define VIDEO_GET_DEF_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x8744U
+#define VIDEO_GET_DEF_CT_FOCUS_ABSOLUTE_CONTROL 0x8746U
+#define VIDEO_GET_DEF_CT_FOCUS_RELATIVE_CONTROL 0x8747U
+#define VIDEO_GET_DEF_CT_FOCUS_AUTO_CONTROL 0x8748U
+#define VIDEO_GET_DEF_CT_IRIS_ABSOLUTE_CONTROL 0x8749U
+#define VIDEO_GET_DEF_CT_ZOOM_ABSOLUTE_CONTROL 0x874BU
+#define VIDEO_GET_DEF_CT_ZOOM_RELATIVE_CONTROL 0x874CU
+#define VIDEO_GET_DEF_CT_PANTILT_ABSOLUTE_CONTROL 0x874DU
+#define VIDEO_GET_DEF_CT_PANTILT_RELATIVE_CONTROL 0x874EU
+#define VIDEO_GET_DEF_CT_ROLL_ABSOLUTE_CONTROL 0x874FU
+#define VIDEO_GET_DEF_CT_ROLL_RELATIVE_CONTROL 0x8750U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_CT_FOCUS_SIMPLE_CONTROL 0x8751U
+#define VIDEO_GET_DEF_CT_DIGITAL_WINDOW_CONTROL 0x8752U
+#define VIDEO_GET_DEF_CT_REGION_OF_INTEREST_CONTROL 0x8753U
+#endif
+
+#define VIDEO_GET_DEF_VS_PROBE_CONTROL 0x8761U
+#define VIDEO_GET_DEF_VS_STILL_PROBE_CONTROL 0x8763U
+#define VIDEO_GET_DEF_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x8768U
+#define VIDEO_GET_DEF_VS_SYNCH_DELAY_CONTROL 0x8769U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_GET_DEF_EU_PROFILE_TOOLSET_CONTROL 0x8782U
+#define VIDEO_GET_DEF_EU_VIDEO_RESOLUTION_CONTROL 0x8783U
+#define VIDEO_GET_DEF_EU_MIN_FRAME_INTERVAL_CONTROL 0x8784U
+#define VIDEO_GET_DEF_EU_SLICE_MODE_CONTROL 0x8785U
+#define VIDEO_GET_DEF_EU_RATE_CONTROL_MODE_CONTROL 0x8786U
+#define VIDEO_GET_DEF_EU_AVERAGE_BITRATE_CONTROL 0x8787U
+#define VIDEO_GET_DEF_EU_CPB_SIZE_CONTROL 0x8788U
+#define VIDEO_GET_DEF_EU_PEAK_BIT_RATE_CONTROL 0x8789U
+#define VIDEO_GET_DEF_EU_QUANTIZATION_PARAMS_CONTROL 0x878AU
+#define VIDEO_GET_DEF_EU_LTR_BUFFER_CONTROL 0x878CU
+#define VIDEO_GET_DEF_EU_LTR_PICTURE_CONTROL 0x878DU
+#define VIDEO_GET_DEF_EU_LTR_VALIDATION_CONTROL 0x878EU
+#define VIDEO_GET_DEF_EU_LEVEL_IDC_LIMIT_CONTROL 0x878FU
+#define VIDEO_GET_DEF_EU_SEI_PAYLOADTYPE_CONTROL 0x8790U
+#define VIDEO_GET_DEF_EU_QP_RANGE_CONTROL 0x8791U
+#define VIDEO_GET_DEF_EU_ERROR_RESILIENCY_CONTROL 0x8794U
+#endif
+
+/*! @brief Video device class-specific request SET CUR COMMAND */
+#define VIDEO_SET_CUR_VC_POWER_MODE_CONTROL 0x0101U
+
+#define VIDEO_SET_CUR_PU_BACKLIGHT_COMPENSATION_CONTROL 0x0121U
+#define VIDEO_SET_CUR_PU_BRIGHTNESS_CONTROL 0x0122U
+#define VIDEO_SET_CUR_PU_CONTRACT_CONTROL 0x0123U
+#define VIDEO_SET_CUR_PU_GAIN_CONTROL 0x0124U
+#define VIDEO_SET_CUR_PU_POWER_LINE_FREQUENCY_CONTROL 0x0125U
+#define VIDEO_SET_CUR_PU_HUE_CONTROL 0x0126U
+#define VIDEO_SET_CUR_PU_SATURATION_CONTROL 0x0127U
+#define VIDEO_SET_CUR_PU_SHARRNESS_CONTROL 0x0128U
+#define VIDEO_SET_CUR_PU_GAMMA_CONTROL 0x0129U
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_TEMPERATURE_CONTROL 0x012AU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_TEMPERATURE_AUTO_CONTROL 0x012BU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_COMPONENT_CONTROL 0x012CU
+#define VIDEO_SET_CUR_PU_WHITE_BALANCE_COMPONENT_AUTO_CONTROL 0x012DU
+#define VIDEO_SET_CUR_PU_DIGITAL_MULTIPLIER_CONTROL 0x012EU
+#define VIDEO_SET_CUR_PU_DIGITAL_MULTIPLIER_LIMIT_CONTROL 0x012FU
+#define VIDEO_SET_CUR_PU_HUE_AUTO_CONTROL 0x0130U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_PU_CONTRAST_AUTO_CONTROL 0x0131U
+#endif
+
+#define VIDEO_SET_CUR_CT_SCANNING_MODE_CONTROL 0x0141U
+#define VIDEO_SET_CUR_CT_AE_MODE_CONTROL 0x0142U
+#define VIDEO_SET_CUR_CT_AE_PRIORITY_CONTROL 0x0143U
+#define VIDEO_SET_CUR_CT_EXPOSURE_TIME_ABSOLUTE_CONTROL 0x0144U
+#define VIDEO_SET_CUR_CT_EXPOSURE_TIME_RELATIVE_CONTROL 0x0145U
+#define VIDEO_SET_CUR_CT_FOCUS_ABSOLUTE_CONTROL 0x0146U
+#define VIDEO_SET_CUR_CT_FOCUS_RELATIVE_CONTROL 0x0147U
+#define VIDEO_SET_CUR_CT_FOCUS_AUTO_CONTROL 0x0148U
+#define VIDEO_SET_CUR_CT_IRIS_ABSOLUTE_CONTROL 0x0149U
+#define VIDEO_SET_CUR_CT_IRIS_RELATIVE_CONTROL 0x014AU
+#define VIDEO_SET_CUR_CT_ZOOM_ABSOLUTE_CONTROL 0x014BU
+#define VIDEO_SET_CUR_CT_ZOOM_RELATIVE_CONTROL 0x014CU
+#define VIDEO_SET_CUR_CT_PANTILT_ABSOLUTE_CONTROL 0x014DU
+#define VIDEO_SET_CUR_CT_PANTILT_RELATIVE_CONTROL 0x014EU
+#define VIDEO_SET_CUR_CT_ROLL_ABSOLUTE_CONTROL 0x014FU
+#define VIDEO_SET_CUR_CT_ROLL_RELATIVE_CONTROL 0x0150U
+#define VIDEO_SET_CUR_CT_PRIVACY_CONTROL 0x0151U
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_CT_FOCUS_SIMPLE_CONTROL 0x0152U
+#define VIDEO_SET_CUR_CT_DIGITAL_WINDOW_CONTROL 0x0153U
+#define VIDEO_SET_CUR_CT_REGION_OF_INTEREST_CONTROL 0x0154U
+#endif
+
+#define VIDEO_SET_CUR_VS_PROBE_CONTROL 0x0161U
+#define VIDEO_SET_CUR_VS_COMMIT_CONTROL 0x0162U
+#define VIDEO_SET_CUR_VS_STILL_PROBE_CONTROL 0x0163U
+#define VIDEO_SET_CUR_VS_STILL_COMMIT_CONTROL 0x0164U
+#define VIDEO_SET_CUR_VS_STILL_IMAGE_TRIGGER_CONTROL 0x0165U
+#define VIDEO_SET_CUR_VS_STREAM_ERROR_CODE_CONTROL 0x0166U
+#define VIDEO_SET_CUR_VS_GENERATE_KEY_FRAME_CONTROL 0x0167U
+#define VIDEO_SET_CUR_VS_UPDATE_FRAME_SEGMENT_CONTROL 0x0168U
+#define VIDEO_SET_CUR_VS_SYNCH_DELAY_CONTROL 0x0169U
+
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+#define VIDEO_SET_CUR_EU_SELECT_LAYER_CONTROL 0x0181U
+#define VIDEO_SET_CUR_EU_PROFILE_TOOLSET_CONTROL 0x0182U
+#define VIDEO_SET_CUR_EU_VIDEO_RESOLUTION_CONTROL 0x0183U
+#define VIDEO_SET_CUR_EU_MIN_FRAME_INTERVAL_CONTROL 0x0184U
+#define VIDEO_SET_CUR_EU_SLICE_MODE_CONTROL 0x0185U
+#define VIDEO_SET_CUR_EU_RATE_CONTROL_MODE_CONTROL 0x0186U
+#define VIDEO_SET_CUR_EU_AVERAGE_BITRATE_CONTROL 0x0187U
+#define VIDEO_SET_CUR_EU_CPB_SIZE_CONTROL 0x0188U
+#define VIDEO_SET_CUR_EU_PEAK_BIT_RATE_CONTROL 0x0189U
+#define VIDEO_SET_CUR_EU_QUANTIZATION_PARAMS_CONTROL 0x018AU
+#define VIDEO_SET_CUR_EU_SYNC_REF_FRAME_CONTROL 0x018BU
+#define VIDEO_SET_CUR_EU_LTR_BUFFER_CONTROL 0x018CU
+#define VIDEO_SET_CUR_EU_LTR_PICTURE_CONTROL 0x018DU
+#define VIDEO_SET_CUR_EU_LTR_VALIDATION_CONTROL 0x018EU
+#define VIDEO_SET_CUR_EU_LEVEL_IDC_LIMIT_CONTROL 0x018FU
+#define VIDEO_SET_CUR_EU_SEI_PAYLOADTYPE_CONTROL 0x0190U
+#define VIDEO_SET_CUR_EU_QP_RANGE_CONTROL 0x0191U
+#define VIDEO_SET_CUR_EU_PRIORITY_CONTROL 0x0192U
+#define VIDEO_SET_CUR_EU_START_OR_STOP_LAYER_CONTROL 0x0193U
+#define VIDEO_SET_CUR_EU_ERROR_RESILIENCY_CONTROL 0x0194U
+#endif
+
+/*! @brief The payload header structure for MJPEG payload format. */
+struct video_mjpeg_payload_header {
+ uint8_t bHeaderLength; /*!< The payload header length. */
+ union {
+ uint8_t bmheaderInfo; /*!< The payload header bitmap field. */
+ struct
+ {
+ uint8_t frameIdentifier : 1U; /*!< Frame Identifier. This bit toggles at each frame start boundary and stays
+ constant for the rest of the frame.*/
+ uint8_t endOfFrame : 1U; /*!< End of Frame. This bit indicates the end of a video frame and is set in the
+ last video sample that belongs to a frame.*/
+ uint8_t
+ presentationTimeStamp : 1U; /*!< Presentation Time Stamp. This bit, when set, indicates the presence of
+ a PTS field.*/
+ uint8_t sourceClockReference : 1U; /*!< Source Clock Reference. This bit, when set, indicates the presence
+ of a SCR field.*/
+ uint8_t reserved : 1U; /*!< Reserved. Set to 0. */
+ uint8_t stillImage : 1U; /*!< Still Image. This bit, when set, identifies a video sample that belongs to a
+ still image.*/
+ uint8_t errorBit : 1U; /*!< Error Bit. This bit, when set, indicates an error in the device streaming.*/
+ uint8_t endOfHeader : 1U; /*!< End of Header. This bit, when set, indicates the end of the BFH fields.*/
+ } headerInfoBits;
+ struct
+ {
+ uint8_t FID : 1U; /*!< Frame Identifier. This bit toggles at each frame start boundary and stays constant
+ for the rest of the frame.*/
+ uint8_t EOI : 1U; /*!< End of Frame. This bit indicates the end of a video frame and is set in the last
+ video sample that belongs to a frame.*/
+ uint8_t PTS : 1U; /*!< Presentation Time Stamp. This bit, when set, indicates the presence of a PTS field.*/
+ uint8_t SCR : 1U; /*!< Source Clock Reference. This bit, when set, indicates the presence of a SCR field.*/
+ uint8_t RES : 1U; /*!< Reserved. Set to 0. */
+ uint8_t STI : 1U; /*!< Still Image. This bit, when set, identifies a video sample that belongs to a still
+ image.*/
+ uint8_t ERR : 1U; /*!< Error Bit. This bit, when set, indicates an error in the device streaming.*/
+ uint8_t EOH : 1U; /*!< End of Header. This bit, when set, indicates the end of the BFH fields.*/
+ } headerInfoBitmap;
+ } headerInfoUnion;
+ uint32_t dwPresentationTime; /*!< Presentation time stamp (PTS) field.*/
+ uint8_t bSourceClockReference[6]; /*!< Source clock reference (SCR) field.*/
+} __packed;
+
+/*! @brief The Video probe and commit controls structure.*/
+struct video_probe_and_commit_controls {
+ union {
+ uint8_t bmHint; /*!< Bit-field control indicating to the function what fields shall be kept fixed. */
+ struct
+ {
+ uint8_t dwFrameInterval : 1U; /*!< dwFrameInterval field.*/
+ uint8_t wKeyFrameRate : 1U; /*!< wKeyFrameRate field.*/
+ uint8_t wPFrameRate : 1U; /*!< wPFrameRate field.*/
+ uint8_t wCompQuality : 1U; /*!< wCompQuality field.*/
+ uint8_t wCompWindowSize : 1U; /*!< wCompWindowSize field.*/
+ uint8_t reserved : 3U; /*!< Reserved field.*/
+ } hintBitmap;
+ } hintUnion;
+ union {
+ uint8_t bmHint; /*!< Bit-field control indicating to the function what fields shall be kept fixed. */
+ struct
+ {
+ uint8_t reserved : 8U; /*!< Reserved field.*/
+ } hintBitmap;
+ } hintUnion1;
+ uint8_t bFormatIndex; /*!< Video format index from a format descriptor.*/
+ uint8_t bFrameIndex; /*!< Video frame index from a frame descriptor.*/
+ uint32_t dwFrameInterval; /*!< Frame interval in 100ns units.*/
+ uint16_t wKeyFrameRate; /*!< Key frame rate in key-frame per video-frame units.*/
+ uint16_t wPFrameRate; /*!< PFrame rate in PFrame/key frame units.*/
+ uint16_t wCompQuality; /*!< Compression quality control in abstract units 0U (lowest) to 10000U (highest).*/
+ uint16_t wCompWindowSize; /*!< Window size for average bit rate control.*/
+ uint16_t wDelay; /*!< Internal video streaming interface latency in ms from video data capture to presentation on
+ the USB.*/
+ uint32_t dwMaxVideoFrameSize; /*!< Maximum video frame or codec-specific segment size in bytes.*/
+ uint32_t dwMaxPayloadTransferSize; /*!< Specifies the maximum number of bytes that the device can transmit or
+ receive in a single payload transfer.*/
+ uint32_t dwClockFrequency; /*!< The device clock frequency in Hz for the specified format. This specifies the
+ units used for the time information fields in the Video Payload Headers in the data
+ stream.*/
+ uint8_t bmFramingInfo; /*!< Bit-field control supporting the following values: D0 Frame ID, D1 EOF.*/
+ uint8_t bPreferedVersion; /*!< The preferred payload format version supported by the host or device for the
+ specified bFormatIndex value.*/
+ uint8_t bMinVersion; /*!< The minimum payload format version supported by the device for the specified bFormatIndex
+ value.*/
+ uint8_t bMaxVersion; /*!< The maximum payload format version supported by the device for the specified bFormatIndex
+ value.*/
+#if defined(USB_DEVICE_VIDEO_CLASS_VERSION_1_5) && USB_DEVICE_VIDEO_CLASS_VERSION_1_5
+ uint8_t bUsage; /*!< This bitmap enables features reported by the bmUsages field of the Video Frame Descriptor.*/
+ uint8_t
+ bBitDepthLuma; /*!< Represents bit_depth_luma_minus8 + 8U, which must be the same as bit_depth_chroma_minus8 +
+ 8.*/
+ uint8_t bmSettings; /*!< A bitmap of flags that is used to discover and control specific features of a temporally
+ encoded video stream.*/
+ uint8_t bMaxNumberOfRefFramesPlus1; /*!< Host indicates the maximum number of frames stored for use as references.*/
+ uint16_t bmRateControlModes; /*!< This field contains 4U sub-fields, each of which is a 4U bit number.*/
+ uint64_t bmLayoutPerStream; /*!< This field contains 4U sub-fields, each of which is a 2U byte number.*/
+#endif
+} __packed;
+
+/*! @brief The Video still probe and still commit controls structure.*/
+struct video_still_probe_and_commit_controls {
+ uint8_t bFormatIndex; /*!< Video format index from a format descriptor.*/
+ uint8_t bFrameIndex; /*!< Video frame index from a frame descriptor.*/
+ uint8_t bCompressionIndex; /*!< Compression index from a frame descriptor.*/
+ uint32_t dwMaxVideoFrameSize; /*!< Maximum still image size in bytes.*/
+ uint32_t dwMaxPayloadTransferSize; /*!< Specifies the maximum number of bytes that the device can transmit or
+ receive in a single payload transfer.*/
+} __packed;
+
+void usbd_video_sof_callback(void);
+void usbd_video_set_interface_callback(uint8_t value);
+void usbd_video_add_interface(usbd_class_t *class, usbd_interface_t *intf);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* USB_VIDEO_H_ */
diff --git a/class/webusb/usbd_webusb.h b/class/webusb/usbd_webusb.h
new file mode 100644
index 00000000..6274f496
--- /dev/null
+++ b/class/webusb/usbd_webusb.h
@@ -0,0 +1,37 @@
+#ifndef _USBD_WEBUSB_H
+#define _USBD_WEBUSB_H
+
+/* WebUSB Descriptor Types */
+#define WEBUSB_DESCRIPTOR_SET_HEADER_TYPE 0x00
+#define WEBUSB_CONFIGURATION_SUBSET_HEADER_TYPE 0x01
+#define WEBUSB_FUNCTION_SUBSET_HEADER_TYPE 0x02
+#define WEBUSB_URL_TYPE 0x03
+
+/* WebUSB Request Codes */
+#define WEBUSB_REQUEST_GET_URL 0x02
+
+/* bScheme in URL descriptor */
+#define WEBUSB_URL_SCHEME_HTTP 0x00
+#define WEBUSB_URL_SCHEME_HTTPS 0x01
+
+/* WebUSB Descriptor sizes */
+#define WEBUSB_DESCRIPTOR_SET_HEADER_SIZE 5
+#define WEBUSB_CONFIGURATION_SUBSET_HEADER_SIZE 4
+#define WEBUSB_FUNCTION_SUBSET_HEADER_SIZE 3
+
+/* BOS Capability webusb */
+struct usb_bos_webusb_platform_capability_descriptor {
+ struct usb_bos_capability_descriptor webusb_platform;
+ uint16_t bcdVersion;
+ uint8_t bVendorCode;
+ uint8_t iLandingPage;
+} __packed;
+
+struct webusb_url_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bScheme;
+ char URL[];
+} __packed;
+
+#endif \ No newline at end of file
diff --git a/class/winusb/usbd_winusb.h b/class/winusb/usbd_winusb.h
new file mode 100644
index 00000000..5e76bac8
--- /dev/null
+++ b/class/winusb/usbd_winusb.h
@@ -0,0 +1,35 @@
+#ifndef _USBD_WINUSB_H
+#define _USBD_WINUSB_H
+
+/* WinUSB Microsoft OS 2.0 descriptor request codes */
+#define WINUSB_REQUEST_GET_DESCRIPTOR_SET 0x07
+#define WINUSB_REQUEST_SET_ALT_ENUM 0x08
+
+/* WinUSB Microsoft OS 2.0 descriptor sizes */
+#define WINUSB_DESCRIPTOR_SET_HEADER_SIZE 10
+#define WINUSB_FUNCTION_SUBSET_HEADER_SIZE 8
+#define WINUSB_FEATURE_COMPATIBLE_ID_SIZE 20
+
+/* WinUSB Microsoft OS 2.0 Descriptor Types */
+#define WINUSB_SET_HEADER_DESCRIPTOR_TYPE 0x00
+#define WINUSB_SUBSET_HEADER_CONFIGURATION_TYPE 0x01
+#define WINUSB_SUBSET_HEADER_FUNCTION_TYPE 0x02
+#define WINUSB_FEATURE_COMPATIBLE_ID_TYPE 0x03
+#define WINUSB_FEATURE_REG_PROPERTY_TYPE 0x04
+#define WINUSB_FEATURE_MIN_RESUME_TIME_TYPE 0x05
+#define WINUSB_FEATURE_MODEL_ID_TYPE 0x06
+#define WINUSB_FEATURE_CCGP_DEVICE_TYPE 0x07
+
+#define WINUSB_PROP_DATA_TYPE_REG_SZ 0x01
+#define WINUSB_PROP_DATA_TYPE_REG_MULTI_SZ 0x07
+
+/* WinUSB Microsoft OS 2.0 descriptor Platform Capability Descriptor */
+struct usb_bos_winusb_platform_capability_descriptor {
+ struct usb_bos_capability_descriptor winusb_platform;
+ uint32_t dwWindowsVersion;
+ uint16_t wMSOSDescriptorSetTotalLength;
+ uint8_t bMS_VendorCode;
+ uint8_t bAltEnumCode;
+} __packed;
+
+#endif \ No newline at end of file
diff --git a/common/usb_dc.h b/common/usb_dc.h
new file mode 100644
index 00000000..ea57fcde
--- /dev/null
+++ b/common/usb_dc.h
@@ -0,0 +1,179 @@
+#ifndef _USB_DC_H
+#define _USB_DC_H
+
+#include "stdint.h"
+#include "bflb_platform.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @brief USB Device Controller API
+ * @defgroup _usb_device_controller_api USB Device Controller API
+ * @{
+ */
+/**< maximum packet size (MPS) for EP 0 */
+#define USB_CTRL_EP_MPS 64
+/**
+ * USB endpoint Transfer Type mask.
+ */
+#define USBD_EP_TYPE_CTRL 0
+#define USBD_EP_TYPE_ISOC 1
+#define USBD_EP_TYPE_BULK 2
+#define USBD_EP_TYPE_INTR 3
+#define USBD_EP_TYPE_MASK 3
+
+/* Default USB control EP, always 0 and 0x80 */
+#define USB_CONTROL_OUT_EP0 0
+#define USB_CONTROL_IN_EP0 0x80
+
+/**
+ * @brief USB Endpoint Transfer Type
+ */
+enum usb_dc_ep_transfer_type {
+ /** Control type endpoint */
+ USB_DC_EP_CONTROL = 0,
+ /** Isochronous type endpoint */
+ USB_DC_EP_ISOCHRONOUS,
+ /** Bulk type endpoint */
+ USB_DC_EP_BULK,
+ /** Interrupt type endpoint */
+ USB_DC_EP_INTERRUPT
+};
+
+/**
+ * @brief USB Endpoint Configuration.
+ *
+ * Structure containing the USB endpoint configuration.
+ */
+struct usbd_endpoint_cfg {
+ /** The number associated with the EP in the device
+ * configuration structure
+ * IN EP = 0x80 | \<endpoint number\>
+ * OUT EP = 0x00 | \<endpoint number\>
+ */
+ uint8_t ep_addr;
+ /** Endpoint max packet size */
+ uint16_t ep_mps;
+ /** Endpoint Transfer Type.
+ * May be Bulk, Interrupt, Control or Isochronous
+ */
+ enum usb_dc_ep_transfer_type ep_type;
+};
+
+/**
+ * @brief USB Device Core Layer API
+ * @defgroup _usb_device_core_api USB Device Core API
+ * @{
+ */
+
+/**
+ * @brief Set USB device address
+ *
+ * @param[in] addr Device address
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_set_address(const uint8_t addr);
+
+/*
+ * @brief configure and enable endpoint
+ *
+ * This function sets endpoint configuration according to one specified in USB
+ * endpoint descriptor and then enables it for data transfers.
+ *
+ * @param [in] ep_desc Endpoint descriptor byte array
+ *
+ * @return true if successfully configured and enabled
+ */
+int usbd_ep_open(const struct usbd_endpoint_cfg *ep_cfg);
+/**
+ * @brief Disable the selected endpoint
+ *
+ * Function to disable the selected endpoint. Upon success interrupts are
+ * disabled for the corresponding endpoint and the endpoint is no longer able
+ * for transmitting/receiving data.
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_close(const uint8_t ep);
+/**
+ * @brief Set stall condition for the selected endpoint
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_set_stall(const uint8_t ep);
+/**
+ * @brief Clear stall condition for the selected endpoint
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_clear_stall(const uint8_t ep);
+/**
+ * @brief Check if the selected endpoint is stalled
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ * @param[out] stalled Endpoint stall status
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_is_stalled(const uint8_t ep, uint8_t *stalled);
+/**
+ * @brief Write data to the specified endpoint
+ *
+ * This function is called to write data to the specified endpoint. The
+ * supplied usbd_endpoint_callback function will be called when data is transmitted
+ * out.
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ * @param[in] data Pointer to data to write
+ * @param[in] data_len Length of the data requested to write. This may
+ * be zero for a zero length status packet.
+ * @param[out] ret_bytes Bytes scheduled for transmission. This value
+ * may be NULL if the application expects all
+ * bytes to be written
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_write(const uint8_t ep, const uint8_t *data, uint32_t data_len, uint32_t *ret_bytes);
+/**
+ * @brief Read data from the specified endpoint
+ *
+ * This is similar to usb_dc_ep_read, the difference being that, it doesn't
+ * clear the endpoint NAKs so that the consumer is not bogged down by further
+ * upcalls till he is done with the processing of the data. The caller should
+ * reactivate ep by invoking usb_dc_ep_read_continue() do so.
+ *
+ * @param[in] ep Endpoint address corresponding to the one
+ * listed in the device configuration table
+ * @param[in] data Pointer to data buffer to write to
+ * @param[in] max_data_len Max length of data to read
+ * @param[out] read_bytes Number of bytes read. If data is NULL and
+ * max_data_len is 0 the number of bytes
+ * available for read should be returned.
+ *
+ * @return 0 on success, negative errno code on fail.
+ */
+int usbd_ep_read(const uint8_t ep, uint8_t *data, uint32_t max_data_len, uint32_t *read_bytes);
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/common/usb_def.h b/common/usb_def.h
new file mode 100644
index 00000000..8a592c3a
--- /dev/null
+++ b/common/usb_def.h
@@ -0,0 +1,522 @@
+#ifndef USB_REQUEST_H
+#define USB_REQUEST_H
+
+/* Useful define */
+#define USB_1_1 0x0110
+#define USB_2_0 0x0200
+/* Set USB version to 2.1 so that the host will request the BOS descriptor */
+#define USB_2_1 0x0210
+
+// USB Speed
+#define USB_SPEED_LOW 0U
+#define USB_SPEED_FULL 1U
+#define USB_SPEED_HIGH 2U
+
+// USB PID Types
+#define USB_PID_RESERVED 0U
+#define USB_PID_OUT 1U
+#define USB_PID_ACK 2U
+#define USB_PID_DATA0 3U
+#define USB_PID_PING 4U
+#define USB_PID_SOF 5U
+#define USB_PID_DATA2 7U
+#define USB_PID_NYET 6U
+#define USB_PID_SPLIT 8U
+#define USB_PID_IN 9U
+#define USB_PID_NAK 10U
+#define USB_PID_DATA1 11U
+#define USB_PID_PRE 12U
+#define USB_PID_ERR 12U
+#define USB_PID_SETUP 13U
+#define USB_PID_STALL 14U
+#define USB_PID_MDATA 15U
+
+// bmRequestType.Dir
+#define USB_REQUEST_HOST_TO_DEVICE 0U
+#define USB_REQUEST_DEVICE_TO_HOST 1U
+
+// bmRequestType.Type
+#define USB_REQUEST_STANDARD 0U
+#define USB_REQUEST_CLASS 1U
+#define USB_REQUEST_VENDOR 2U
+#define USB_REQUEST_RESERVED 3U
+
+// bmRequestType.Recipient
+#define USB_REQUEST_TO_DEVICE 0U
+#define USB_REQUEST_TO_INTERFACE 1U
+#define USB_REQUEST_TO_ENDPOINT 2U
+#define USB_REQUEST_TO_OTHER 3U
+
+/* USB Standard Request Codes */
+#define USB_REQUEST_GET_STATUS 0x00
+#define USB_REQUEST_CLEAR_FEATURE 0x01
+#define USB_REQUEST_SET_FEATURE 0x03
+#define USB_REQUEST_SET_ADDRESS 0x05
+#define USB_REQUEST_GET_DESCRIPTOR 0x06
+#define USB_REQUEST_SET_DESCRIPTOR 0x07
+#define USB_REQUEST_GET_CONFIGURATION 0x08
+#define USB_REQUEST_SET_CONFIGURATION 0x09
+#define USB_REQUEST_GET_INTERFACE 0x0A
+#define USB_REQUEST_SET_INTERFACE 0x0B
+#define USB_REQUEST_SYNCH_FRAME 0x0C
+#define USB_REQUEST_SET_ENCRYPTION 0x0D
+#define USB_REQUEST_GET_ENCRYPTION 0x0E
+#define USB_REQUEST_RPIPE_ABORT 0x0E
+#define USB_REQUEST_SET_HANDSHAKE 0x0F
+#define USB_REQUEST_RPIPE_RESET 0x0F
+#define USB_REQUEST_GET_HANDSHAKE 0x10
+#define USB_REQUEST_SET_CONNECTION 0x11
+#define USB_REQUEST_SET_SECURITY_DATA 0x12
+#define USB_REQUEST_GET_SECURITY_DATA 0x13
+#define USB_REQUEST_SET_WUSB_DATA 0x14
+#define USB_REQUEST_LOOPBACK_DATA_WRITE 0x15
+#define USB_REQUEST_LOOPBACK_DATA_READ 0x16
+#define USB_REQUEST_SET_INTERFACE_DS 0x17
+
+/* USB GET_STATUS Bit Values */
+#define USB_GETSTATUS_SELF_POWERED 0x01
+#define USB_GETSTATUS_REMOTE_WAKEUP 0x02
+#define USB_GETSTATUS_ENDPOINT_STALL 0x01
+
+/* USB Standard Feature selectors */
+#define USB_FEATURE_ENDPOINT_STALL 0
+#define USB_FEATURE_REMOTE_WAKEUP 1
+#define USB_FEATURE_TEST_MODE 2
+
+/* Descriptor size in bytes */
+#define USB_DEVICE_DESC_SIZE 0x12
+#define USB_CONFIGURATION_DESC_SIZE 0x09
+#define USB_INTERFACE_DESC_SIZE 0x09
+#define USB_ENDPOINT_DESC_SIZE 0x07
+#define USB_LANGID_STRING_DESC_SIZE 0x04
+#define USB_OTHER_SPEED_DESC_SIZE 0x09
+#define USB_DEVICE_QUAL_DESC_SIZE 0x0A
+#define USB_INTERFACE_ASSOC_DESC_SIZE 0x08
+#define USB_FUNCTION_DESC_SIZE 0x03
+#define USB_OTG_DESC_SIZE 0x03
+
+/* USB Descriptor Types */
+#define USB_DESCRIPTOR_TYPE_DEVICE 0x01U
+#define USB_DESCRIPTOR_TYPE_CONFIGURATION 0x02U
+#define USB_DESCRIPTOR_TYPE_STRING 0x03U
+#define USB_DESCRIPTOR_TYPE_INTERFACE 0x04U
+#define USB_DESCRIPTOR_TYPE_ENDPOINT 0x05U
+#define USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER 0x06U
+#define USB_DESCRIPTOR_TYPE_OTHER_SPEED 0x07U
+#define USB_DESCRIPTOR_TYPE_INTERFACE_POWER 0x08U
+#define USB_DESCRIPTOR_TYPE_OTG 0x09U
+#define USB_DESCRIPTOR_TYPE_DEBUG 0x0AU
+#define USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION 0x0BU
+#define USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE 0x0FU
+#define USB_DESCRIPTOR_TYPE_DEVICE_CAPABILITY 0x10U
+
+#define USB_DESCRIPTOR_TYPE_FUNCTIONAL 0x21U
+
+// Class Specific Descriptor
+#define USB_CS_DESCRIPTOR_TYPE_DEVICE 0x21U
+#define USB_CS_DESCRIPTOR_TYPE_CONFIGURATION 0x22U
+#define USB_CS_DESCRIPTOR_TYPE_STRING 0x23U
+#define USB_CS_DESCRIPTOR_TYPE_INTERFACE 0x24U
+#define USB_CS_DESCRIPTOR_TYPE_ENDPOINT 0x25U
+
+#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ENDPOINT_COMPANION 0x30U
+#define USB_DESCRIPTOR_TYPE_SUPERSPEED_ISO_ENDPOINT_COMPANION 0x31U
+
+/* USB Device Classes */
+#define USB_DEVICE_CLASS_RESERVED 0x00
+#define USB_DEVICE_CLASS_AUDIO 0x01
+#define USB_DEVICE_CLASS_CDC 0x02
+#define USB_DEVICE_CLASS_HID 0x03
+#define USB_DEVICE_CLASS_MONITOR 0x04
+#define USB_DEVICE_CLASS_PHYSICAL 0x05
+#define USB_DEVICE_CLASS_IMAGE 0x06
+#define USB_DEVICE_CLASS_PRINTER 0x07
+#define USB_DEVICE_CLASS_MASS_STORAGE 0x08
+#define USB_DEVICE_CLASS_HUB 0x09
+#define USB_DEVICE_CLASS_CDC_DATA 0x0a
+#define USB_DEVICE_CLASS_SMART_CARD 0x0b
+#define USB_DEVICE_CLASS_SECURITY 0x0d
+#define USB_DEVICE_CLASS_VIDEO 0x0e
+#define USB_DEVICE_CLASS_HEALTHCARE 0x0f
+#define USB_DEVICE_CLASS_DIAG_DEVICE 0xdc
+#define USB_DEVICE_CLASS_WIRELESS 0xe0
+#define USB_DEVICE_CLASS_MISC 0xef
+#define USB_DEVICE_CLASS_APP_SPECIFIC 0xfe
+#define USB_DEVICE_CLASS_VEND_SPECIFIC 0xff
+
+/* usb string index define */
+#define USB_STRING_LANGID_INDEX 0x00
+#define USB_STRING_MFC_INDEX 0x01
+#define USB_STRING_PRODUCT_INDEX 0x02
+#define USB_STRING_SERIAL_INDEX 0x03
+#define USB_STRING_CONFIG_INDEX 0x04
+#define USB_STRING_INTERFACE_INDEX 0x05
+#define USB_STRING_OS_INDEX 0x06
+#define USB_STRING_MAX USB_STRING_OS_INDEX
+/*
+ * Devices supporting Microsoft OS Descriptors store special string
+ * descriptor at fixed index (0xEE). It is read when a new device is
+ * attached to a computer for the first time.
+ */
+#define USB_OSDESC_STRING_DESC_INDEX 0xEE
+
+/* bmAttributes in Configuration Descriptor */
+#define USB_CONFIG_POWERED_MASK 0x40
+#define USB_CONFIG_BUS_POWERED 0x80
+#define USB_CONFIG_SELF_POWERED 0xC0
+#define USB_CONFIG_REMOTE_WAKEUP 0x20
+
+/* bMaxPower in Configuration Descriptor */
+#define USB_CONFIG_POWER_MA(mA) ((mA) / 2)
+
+/* bEndpointAddress in Endpoint Descriptor */
+#define USB_ENDPOINT_DIRECTION_MASK 0x80
+#define USB_ENDPOINT_OUT(addr) ((addr) | 0x00)
+#define USB_ENDPOINT_IN(addr) ((addr) | 0x80)
+
+/* bmAttributes in Endpoint Descriptor */
+#define USB_ENDPOINT_TYPE_MASK 0x03
+#define USB_ENDPOINT_TYPE_CONTROL 0x00
+#define USB_ENDPOINT_TYPE_ISOCHRONOUS 0x01
+#define USB_ENDPOINT_TYPE_BULK 0x02
+#define USB_ENDPOINT_TYPE_INTERRUPT 0x03
+#define USB_ENDPOINT_SYNC_MASK 0x0C
+#define USB_ENDPOINT_SYNC_NO_SYNCHRONIZATION 0x00
+#define USB_ENDPOINT_SYNC_ASYNCHRONOUS 0x04
+#define USB_ENDPOINT_SYNC_ADAPTIVE 0x08
+#define USB_ENDPOINT_SYNC_SYNCHRONOUS 0x0C
+#define USB_ENDPOINT_USAGE_MASK 0x30
+#define USB_ENDPOINT_USAGE_DATA 0x00
+#define USB_ENDPOINT_USAGE_FEEDBACK 0x10
+#define USB_ENDPOINT_USAGE_IMPLICIT_FEEDBACK 0x20
+#define USB_ENDPOINT_USAGE_RESERVED 0x30
+
+/* bDevCapabilityType in Device Capability Descriptor */
+#define USB_DEVICE_CAPABILITY_WIRELESS_USB 1
+#define USB_DEVICE_CAPABILITY_USB_2_0_EXTENSION 2
+#define USB_DEVICE_CAPABILITY_SUPERSPEED_USB 3
+#define USB_DEVICE_CAPABILITY_CONTAINER_ID 4
+#define USB_DEVICE_CAPABILITY_PLATFORM 5
+#define USB_DEVICE_CAPABILITY_POWER_DELIVERY_CAPABILITY 6
+#define USB_DEVICE_CAPABILITY_BATTERY_INFO_CAPABILITY 7
+#define USB_DEVICE_CAPABILITY_PD_CONSUMER_PORT_CAPABILITY 8
+#define USB_DEVICE_CAPABILITY_PD_PROVIDER_PORT_CAPABILITY 9
+#define USB_DEVICE_CAPABILITY_SUPERSPEED_PLUS 10
+#define USB_DEVICE_CAPABILITY_PRECISION_TIME_MEASUREMENT 11
+#define USB_DEVICE_CAPABILITY_WIRELESS_USB_EXT 12
+
+#define USB_BOS_CAPABILITY_EXTENSION 0x02
+#define USB_BOS_CAPABILITY_PLATFORM 0x05
+
+/* Setup packet definition used to read raw data from USB line */
+struct usb_setup_packet {
+ __packed union {
+ uint8_t bmRequestType; /* bmRequestType */
+ struct
+ {
+ uint8_t Recipient : 5; /* D4..0: Recipient */
+ uint8_t Type : 2; /* D6..5: Type */
+ uint8_t Dir : 1; /* D7: Data Phase Txsfer Direction */
+ } bmRequestType_b;
+ };
+ uint8_t bRequest;
+ __packed union {
+ uint16_t wValue; /* wValue */
+ struct
+ {
+ uint8_t wValueL;
+ uint8_t wValueH;
+ };
+ };
+ __packed union {
+ uint16_t wIndex; /* wIndex */
+ struct
+ {
+ uint8_t wIndexL;
+ uint8_t wIndexH;
+ };
+ };
+ uint16_t wLength;
+} __packed;
+
+/** Standard Device Descriptor */
+struct usb_device_descriptor {
+ uint8_t bLength; /* Descriptor size in bytes = 18 */
+ uint8_t bDescriptorType; /* DEVICE descriptor type = 1 */
+ uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */
+ uint8_t bDeviceClass; /* Class code, if 0 see interface */
+ uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */
+ uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */
+ uint8_t bMaxPacketSize0; /* Endpoint 0 max. size */
+ uint16_t idVendor; /* Vendor ID per USB-IF */
+ uint16_t idProduct; /* Product ID per manufacturer */
+ uint16_t bcdDevice; /* Device release # in BCD */
+ uint8_t iManufacturer; /* Index to manufacturer string */
+ uint8_t iProduct; /* Index to product string */
+ uint8_t iSerialNumber; /* Index to serial number string */
+ uint8_t bNumConfigurations; /* Number of possible configurations */
+} __packed;
+
+/** USB device_qualifier descriptor */
+struct usb_device_qualifier_descriptor {
+ uint8_t bLength; /* Descriptor size in bytes = 10 */
+ uint8_t bDescriptorType; /* DEVICE QUALIFIER type = 6 */
+ uint16_t bcdUSB; /* USB spec in BCD, e.g. 0x0200 */
+ uint8_t bDeviceClass; /* Class code, if 0 see interface */
+ uint8_t bDeviceSubClass; /* Sub-Class code, 0 if class = 0 */
+ uint8_t bDeviceProtocol; /* Protocol, if 0 see interface */
+ uint8_t bMaxPacketSize; /* Endpoint 0 max. size */
+ uint8_t bNumConfigurations; /* Number of possible configurations */
+ uint8_t bReserved; /* Reserved = 0 */
+} __packed;
+
+/** Standard Configuration Descriptor */
+struct usb_configuration_descriptor {
+ uint8_t bLength; /* Descriptor size in bytes = 9 */
+ uint8_t bDescriptorType; /* CONFIGURATION type = 2 or 7 */
+ uint16_t wTotalLength; /* Length of concatenated descriptors */
+ uint8_t bNumInterfaces; /* Number of interfaces, this config. */
+ uint8_t bConfigurationValue; /* Value to set this config. */
+ uint8_t iConfiguration; /* Index to configuration string */
+ uint8_t bmAttributes; /* Config. characteristics */
+ uint8_t bMaxPower; /* Max.power from bus, 2mA units */
+} __packed;
+
+/** Standard Interface Descriptor */
+struct usb_interface_descriptor {
+ uint8_t bLength; /* Descriptor size in bytes = 9 */
+ uint8_t bDescriptorType; /* INTERFACE descriptor type = 4 */
+ uint8_t bInterfaceNumber; /* Interface no.*/
+ uint8_t bAlternateSetting; /* Value to select this IF */
+ uint8_t bNumEndpoints; /* Number of endpoints excluding 0 */
+ uint8_t bInterfaceClass; /* Class code, 0xFF = vendor */
+ uint8_t bInterfaceSubClass; /* Sub-Class code, 0 if class = 0 */
+ uint8_t bInterfaceProtocol; /* Protocol, 0xFF = vendor */
+ uint8_t iInterface; /* Index to interface string */
+} __packed;
+
+/** Standard Endpoint Descriptor */
+struct usb_endpoint_descriptor {
+ uint8_t bLength; /* Descriptor size in bytes = 7 */
+ uint8_t bDescriptorType; /* ENDPOINT descriptor type = 5 */
+ uint8_t bEndpointAddress; /* Endpoint # 0 - 15 | IN/OUT */
+ uint8_t bmAttributes; /* Transfer type */
+ uint16_t wMaxPacketSize; /* Bits 10:0 = max. packet size */
+ uint8_t bInterval; /* Polling interval in (micro) frames */
+} __packed;
+
+/** Unicode (UTF16LE) String Descriptor */
+struct usb_string_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t bString;
+} __packed;
+
+/* USB Interface Association Descriptor */
+struct usb_interface_association_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bFirstInterface;
+ uint8_t bInterfaceCount;
+ uint8_t bFunctionClass;
+ uint8_t bFunctionSubClass;
+ uint8_t bFunctionProtocol;
+ uint8_t iFunction;
+} __packed;
+
+/* MS OS 1.0 string descriptor */
+struct usb_msosv1_string_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bString[14];
+ uint8_t bMS_VendorCode; /* Vendor Code, used for a control request */
+ uint8_t bPad; /* Padding byte for VendorCode look as UTF16 */
+} __packed;
+
+/* MS OS 1.0 Header descriptor */
+struct usb_msosv1_compat_id_header_descriptor {
+ uint32_t dwLength;
+ uint16_t bcdVersion;
+ uint16_t wIndex;
+ uint8_t bCount;
+ uint8_t reserved[7];
+} __packed;
+
+/* MS OS 1.0 Function descriptor */
+struct usb_msosv1_comp_id_function_descriptor {
+ uint8_t bFirstInterfaceNumber;
+ uint8_t reserved1;
+ uint8_t compatibleID[8];
+ uint8_t subCompatibleID[8];
+ uint8_t reserved2[6];
+} __packed;
+
+#define usb_msosv1_comp_id_property_create(x) \
+ struct usb_msosv1_comp_id_property { \
+ struct usb_msosv1_compat_id_header_descriptor compat_id_header; \
+ struct usb_msosv1_comp_id_function_descriptor compat_id_function[x]; \
+ };
+
+struct usb_msosv1_descriptor {
+ uint8_t *string;
+ uint8_t string_len;
+ uint8_t vendor_code;
+ uint8_t *compat_id;
+ uint16_t compat_id_len;
+ uint8_t *comp_id_property;
+ uint16_t comp_id_property_len;
+};
+
+/* MS OS 2.0 Header descriptor */
+struct usb_msosv2_property_header_descriptor {
+ uint32_t dwLength;
+ uint16_t bcdVersion;
+ uint16_t wIndex;
+ uint8_t bCount;
+} __packed;
+
+/* WinUSB Microsoft OS 2.0 descriptor set header */
+struct winusb_header_descriptor {
+ uint16_t wLength;
+ uint16_t wDescriptorType;
+ uint32_t dwWindowsVersion;
+ uint16_t wDescriptorSetTotalLength;
+} __packed;
+
+/* WinUSB Microsoft OS 2.0 subset function descriptor */
+struct winusb_subset_function_descriptor {
+ uint16_t wLength;
+ uint16_t wDescriptorType;
+ uint8_t bFirstInterface;
+ uint8_t bReserved;
+ uint16_t wSubsetLength;
+} __packed;
+
+/* MS OS 2.0 Function Section */
+struct usb_msosv2_comp_id_function_descriptor {
+ uint16_t wLength;
+ uint16_t wDescriptorType;
+ uint8_t compatibleID[8];
+ uint8_t subCompatibleID[8];
+} __packed;
+
+/* MS OS 2.0 property descriptor */
+struct usb_msosv2_proerty_descriptor {
+ uint16_t wLength;
+ uint16_t wDescriptorType;
+ uint32_t dwPropertyDataType;
+ uint16_t wPropertyNameLength;
+ const char *bPropertyName;
+ uint32_t dwPropertyDataLength;
+ const char *bPropertyData;
+};
+
+struct usb_msosv2_descriptor {
+ uint8_t *compat_id;
+ uint16_t compat_id_len;
+};
+
+/* BOS Descriptor */
+struct usb_bos_header_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint16_t wTotalLength;
+ uint8_t bNumDeviceCaps;
+} __packed;
+
+/* BOS Capability Descriptor */
+struct usb_bos_capability_descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDevCapabilityType;
+ uint8_t bReserved;
+ uint8_t PlatformCapabilityUUID[16];
+} __packed;
+
+struct usb_bos_capability_lpm {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDevCapabilityType;
+ uint32_t bmAttributes;
+} __packed;
+
+struct usb_bos_descriptor {
+ uint8_t *bos_id;
+ uint8_t bos_id_len;
+};
+
+/* USB Device Capability Descriptor */
+struct usb_device_capability__descriptor {
+ uint8_t bLength;
+ uint8_t bDescriptorType;
+ uint8_t bDevCapabilityType;
+} __packed;
+
+/** USB descriptor header */
+struct usb_desc_header {
+ uint8_t bLength; /**< descriptor length */
+ uint8_t bDescriptorType; /**< descriptor type */
+};
+
+#define USB_DEVICE_DESCRIPTOR_INIT(bcdUSB, bDeviceClass, bDeviceSubClass, bDeviceProtocol, idVendor, idProduct, bcdDevice, bNumConfigurations) \
+ 0x12, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_DEVICE, /* bDescriptorType */ \
+ WBVAL(bcdUSB), /* bcdUSB */ \
+ bDeviceClass, /* bDeviceClass */ \
+ bDeviceSubClass, /* bDeviceSubClass */ \
+ bDeviceProtocol, /* bDeviceProtocol */ \
+ 0x40, /* bMaxPacketSize */ \
+ WBVAL(idVendor), /* idVendor */ \
+ WBVAL(idProduct), /* idProduct */ \
+ WBVAL(bcdDevice), /* bcdDevice */ \
+ USB_STRING_MFC_INDEX, /* iManufacturer */ \
+ USB_STRING_PRODUCT_INDEX, /* iProduct */ \
+ USB_STRING_SERIAL_INDEX, /* iSerial */ \
+ bNumConfigurations /* bNumConfigurations */
+
+#define USB_CONFIG_DESCRIPTOR_INIT(wTotalLength, bNumInterfaces, bConfigurationValue, bmAttributes, bMaxPower) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_CONFIGURATION, /* bDescriptorType */ \
+ WBVAL(wTotalLength), /* wTotalLength */ \
+ bNumInterfaces, /* bNumInterfaces */ \
+ bConfigurationValue, /* bConfigurationValue */ \
+ 0x00, /* iConfiguration */ \
+ bmAttributes, /* bmAttributes */ \
+ USB_CONFIG_POWER_MA(bMaxPower) /* bMaxPower */
+
+#define USB_INTERFACE_DESCRIPTOR_INIT(bInterfaceNumber, bAlternateSetting, bNumEndpoints, \
+ bInterfaceClass, bInterfaceSubClass, bInterfaceProtocol, iInterface) \
+ 0x09, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE, /* bDescriptorType */ \
+ bInterfaceNumber, /* bInterfaceNumber */ \
+ bAlternateSetting, /* bAlternateSetting */ \
+ bNumEndpoints, /* bNumEndpoints */ \
+ bInterfaceClass, /* bInterfaceClass */ \
+ bInterfaceSubClass, /* bInterfaceSubClass */ \
+ bInterfaceProtocol, /* bInterfaceProtocol */ \
+ iInterface /* iInterface */
+
+#define USB_ENDPOINT_DESCRIPTOR_INIT(bEndpointAddress, bmAttributes, wMaxPacketSize, bInterval) \
+ 0x07, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_ENDPOINT, /* bDescriptorType */ \
+ bEndpointAddress, /* bEndpointAddress */ \
+ bmAttributes, /* bmAttributes */ \
+ WBVAL(wMaxPacketSize), /* wMaxPacketSize */ \
+ bInterval /* bInterval */
+
+#define USB_IAD_INIT(bFirstInterface, bInterfaceCount, bFunctionClass, bFunctionSubClass, bFunctionProtocol) \
+ 0x08, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_INTERFACE_ASSOCIATION, /* bDescriptorType */ \
+ bFirstInterface, /* bFirstInterface */ \
+ bInterfaceCount, /* bInterfaceCount */ \
+ bFunctionClass, /* bFunctionClass */ \
+ bFunctionSubClass, /* bFunctionSubClass */ \
+ bFunctionProtocol, /* bFunctionProtocol */ \
+ 0x00 /* iFunction */
+
+#define USB_LANGID_INIT(id) \
+ 0x04, /* bLength */ \
+ USB_DESCRIPTOR_TYPE_STRING, /* bDescriptorType */ \
+ WBVAL(id) /* wLangID0 */
+
+#endif \ No newline at end of file
diff --git a/common/usb_slist.h b/common/usb_slist.h
new file mode 100644
index 00000000..357df1be
--- /dev/null
+++ b/common/usb_slist.h
@@ -0,0 +1,224 @@
+#ifndef __USB_SLIST_H__
+#define __USB_SLIST_H__
+
+#include "string.h"
+#include "stdint.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * usb_container_of - return the member address of ptr, if the type of ptr is the
+ * struct type.
+ */
+#define usb_container_of(ptr, type, member) \
+ ((type *)((char *)(ptr) - (unsigned long)(&((type *)0)->member)))
+
+/**
+ * Single List structure
+ */
+struct usb_slist_node {
+ struct usb_slist_node *next; /**< point to next node. */
+};
+typedef struct usb_slist_node usb_slist_t; /**< Type for single list. */
+
+/**
+ * @brief initialize a single list
+ *
+ * @param l the single list to be initialized
+ */
+static inline void usb_slist_init(usb_slist_t *l)
+{
+ l->next = NULL;
+}
+
+static inline void usb_slist_add_head(usb_slist_t *l, usb_slist_t *n)
+{
+ n->next = l->next;
+ l->next = n;
+}
+
+static inline void usb_slist_add_tail(usb_slist_t *l, usb_slist_t *n)
+{
+ while (l->next) {
+ l = l->next;
+ }
+
+ /* append the node to the tail */
+ l->next = n;
+ n->next = NULL;
+}
+
+static inline void usb_slist_insert(usb_slist_t *l, usb_slist_t *next, usb_slist_t *n)
+{
+ if (!next) {
+ usb_slist_add_tail(next, l);
+ return;
+ }
+
+ while (l->next) {
+ if (l->next == next) {
+ l->next = n;
+ n->next = next;
+ }
+
+ l = l->next;
+ }
+}
+
+static inline usb_slist_t *usb_slist_remove(usb_slist_t *l, usb_slist_t *n)
+{
+ /* remove slist head */
+ while (l->next && l->next != n) {
+ l = l->next;
+ }
+
+ /* remove node */
+ if (l->next != (usb_slist_t *)0) {
+ l->next = l->next->next;
+ }
+
+ return l;
+}
+
+static inline unsigned int usb_slist_len(const usb_slist_t *l)
+{
+ unsigned int len = 0;
+ const usb_slist_t *list = l->next;
+
+ while (list != NULL) {
+ list = list->next;
+ len++;
+ }
+
+ return len;
+}
+
+static inline unsigned int usb_slist_contains(usb_slist_t *l, usb_slist_t *n)
+{
+ while (l->next) {
+ if (l->next == n) {
+ return 0;
+ }
+
+ l = l->next;
+ }
+
+ return 1;
+}
+
+static inline usb_slist_t *usb_slist_head(usb_slist_t *l)
+{
+ return l->next;
+}
+
+static inline usb_slist_t *usb_slist_tail(usb_slist_t *l)
+{
+ while (l->next) {
+ l = l->next;
+ }
+
+ return l;
+}
+
+static inline usb_slist_t *usb_slist_next(usb_slist_t *n)
+{
+ return n->next;
+}
+
+static inline int usb_slist_isempty(usb_slist_t *l)
+{
+ return l->next == NULL;
+}
+
+/**
+ * @brief initialize a slist object
+ */
+#define USB_SLIST_OBJECT_INIT(object) \
+ { \
+ NULL \
+ }
+
+/**
+ * @brief initialize a slist object
+ */
+#define USB_SLIST_DEFINE(slist) \
+ usb_slist_t slist = { NULL }
+
+/**
+ * @brief get the struct for this single list node
+ * @param node the entry point
+ * @param type the type of structure
+ * @param member the name of list in structure
+ */
+#define usb_slist_entry(node, type, member) \
+ usb_container_of(node, type, member)
+
+/**
+ * usb_slist_first_entry - get the first element from a slist
+ * @ptr: the slist head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define usb_slist_first_entry(ptr, type, member) \
+ usb_slist_entry((ptr)->next, type, member)
+
+/**
+ * usb_slist_tail_entry - get the tail element from a slist
+ * @ptr: the slist head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define usb_slist_tail_entry(ptr, type, member) \
+ usb_slist_entry(usb_slist_tail(ptr), type, member)
+
+/**
+ * usb_slist_first_entry_or_null - get the first element from a slist
+ * @ptr: the slist head to take the element from.
+ * @type: the type of the struct this is embedded in.
+ * @member: the name of the slist_struct within the struct.
+ *
+ * Note, that slist is expected to be not empty.
+ */
+#define usb_slist_first_entry_or_null(ptr, type, member) \
+ (usb_slist_isempty(ptr) ? NULL : usb_slist_first_entry(ptr, type, member))
+
+/**
+ * usb_slist_for_each - iterate over a single list
+ * @pos: the usb_slist_t * to use as a loop cursor.
+ * @head: the head for your single list.
+ */
+#define usb_slist_for_each(pos, head) \
+ for (pos = (head)->next; pos != NULL; pos = pos->next)
+
+#define usb_slist_for_each_safe(pos, next, head) \
+ for (pos = (head)->next, next = pos->next; pos; \
+ pos = next, next = pos->next)
+
+/**
+ * usb_slist_for_each_entry - iterate over single list of given type
+ * @pos: the type * to use as a loop cursor.
+ * @head: the head for your single list.
+ * @member: the name of the list_struct within the struct.
+ */
+#define usb_slist_for_each_entry(pos, head, member) \
+ for (pos = usb_slist_entry((head)->next, typeof(*pos), member); \
+ &pos->member != (NULL); \
+ pos = usb_slist_entry(pos->member.next, typeof(*pos), member))
+
+#define usb_slist_for_each_entry_safe(pos, n, head, member) \
+ for (pos = usb_slist_entry((head)->next, typeof(*pos), member), \
+ n = usb_slist_entry(pos->member.next, typeof(*pos), member); \
+ &pos->member != (NULL); \
+ pos = n, n = usb_slist_entry(pos->member.next, typeof(*pos), member))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif \ No newline at end of file
diff --git a/common/usb_util.h b/common/usb_util.h
new file mode 100644
index 00000000..83c59744
--- /dev/null
+++ b/common/usb_util.h
@@ -0,0 +1,131 @@
+#ifndef _USB_UTIL_H
+#define _USB_UTIL_H
+
+#include "stdbool.h"
+#include "string.h"
+#include "stdint.h"
+#include "stdio.h"
+#include "usb_slist.h"
+
+#ifndef __packed
+#define __packed __attribute__((__packed__))
+#endif
+#ifndef __aligned
+#define __aligned(x) __attribute__((__aligned__(x)))
+#endif
+#define __may_alias __attribute__((__may_alias__))
+#ifndef __printf_like
+#define __printf_like(f, a) __attribute__((format(printf, f, a)))
+#endif
+#define __used __attribute__((__used__))
+#ifndef __deprecated
+#define __deprecated __attribute__((deprecated))
+#endif
+#define ARG_UNUSED(x) (void)(x)
+
+// #define likely(x) __builtin_expect((bool)!!(x), true)
+// #define unlikely(x) __builtin_expect((bool)!!(x), false)
+
+#define popcount(x) __builtin_popcount(x)
+
+#ifndef __no_optimization
+#define __no_optimization __attribute__((optimize("-O0")))
+#endif
+
+#ifndef __weak
+#define __weak __attribute__((__weak__))
+#endif
+#define __unused __attribute__((__unused__))
+
+#define __ALIGN_END __attribute__((aligned(4)))
+#define __ALIGN_BEGIN
+
+#ifndef LO_BYTE
+#define LO_BYTE(x) ((uint8_t)(x & 0x00FF))
+#endif
+
+#ifndef HI_BYTE
+#define HI_BYTE(x) ((uint8_t)((x & 0xFF00) >> 8))
+#endif
+
+/**
+ * @def MAX
+ * @brief The larger value between @p a and @p b.
+ * @note Arguments are evaluated twice.
+ */
+#ifndef MAX
+/* Use Z_MAX for a GCC-only, single evaluation version */
+#define MAX(a, b) (((a) > (b)) ? (a) : (b))
+#endif
+
+/**
+ * @def MIN
+ * @brief The smaller value between @p a and @p b.
+ * @note Arguments are evaluated twice.
+ */
+#ifndef MIN
+/* Use Z_MIN for a GCC-only, single evaluation version */
+#define MIN(a, b) (((a) < (b)) ? (a) : (b))
+#endif
+
+#define BCD(x) ((((x) / 10) << 4) | ((x) % 10))
+
+#define BIT(x) (1 << (x))
+
+#define ARRAY_SIZE(array) \
+ ((int)((sizeof(array) / sizeof((array)[0]))))
+
+#define USB_DESC_SECTION __attribute__((section("usb_desc"))) __used __aligned(1)
+
+#define BSWAP16(u16) (__builtin_bswap16(u16))
+#define BSWAP32(u32) (__builtin_bswap32(u32))
+
+#define GET_BE16(field) \
+ (((uint16_t)(field)[0] << 8) | ((uint16_t)(field)[1]))
+
+#define GET_BE32(field) \
+ (((uint32_t)(field)[0] << 24) | ((uint32_t)(field)[1] << 16) | ((uint32_t)(field)[2] << 8) | ((uint32_t)(field)[3] << 0))
+
+#define SET_BE16(field, value) \
+ do { \
+ (field)[0] = (uint8_t)((value) >> 8); \
+ (field)[1] = (uint8_t)((value) >> 0); \
+ } while (0)
+
+#define SET_BE24(field, value) \
+ do { \
+ (field)[0] = (uint8_t)((value) >> 16); \
+ (field)[1] = (uint8_t)((value) >> 8); \
+ (field)[2] = (uint8_t)((value) >> 0); \
+ } while (0)
+
+#define SET_BE32(field, value) \
+ do { \
+ (field)[0] = (uint8_t)((value) >> 24); \
+ (field)[1] = (uint8_t)((value) >> 16); \
+ (field)[2] = (uint8_t)((value) >> 8); \
+ (field)[3] = (uint8_t)((value) >> 0); \
+ } while (0)
+
+#define REQTYPE_GET_DIR(x) (((x) >> 7) & 0x01)
+#define REQTYPE_GET_TYPE(x) (((x) >> 5) & 0x03U)
+#define REQTYPE_GET_RECIP(x) ((x)&0x1F)
+
+#define GET_DESC_TYPE(x) (((x) >> 8) & 0xFFU)
+#define GET_DESC_INDEX(x) ((x)&0xFFU)
+
+#define WBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF)
+#define DBVAL(x) (x & 0xFF), ((x >> 8) & 0xFF), ((x >> 16) & 0xFF), ((x >> 24) & 0xFF)
+
+#if 0
+#define USBD_LOG_WRN(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#define USBD_LOG_DBG(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#define USBD_LOG_ERR(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#else
+#define USBD_LOG_WRN(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#define USBD_LOG_DBG(a, ...)
+#define USBD_LOG_ERR(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#define USBD_LOG(a, ...) bflb_platform_printf(a, ##__VA_ARGS__)
+#endif
+
+#endif \ No newline at end of file
diff --git a/core/usbd_core.c b/core/usbd_core.c
new file mode 100644
index 00000000..ffcb5202
--- /dev/null
+++ b/core/usbd_core.c
@@ -0,0 +1,1232 @@
+/**
+ * @file usbd_core.c
+ * @brief
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#include "usbd_core.h"
+
+#define USBD_EP_CALLBACK_LIST_SEARCH 0
+#define USBD_EP_CALLBACK_ARR_SEARCH 1
+#define USBD_EP_CALLBACK_SEARCH_METHOD USBD_EP_CALLBACK_ARR_SEARCH
+
+/* general descriptor field offsets */
+#define DESC_bLength 0 /** Length offset */
+#define DESC_bDescriptorType 1 /** Descriptor type offset */
+
+/* config descriptor field offsets */
+#define CONF_DESC_wTotalLength 2 /** Total length offset */
+#define CONF_DESC_bConfigurationValue 5 /** Configuration value offset */
+#define CONF_DESC_bmAttributes 7 /** configuration characteristics */
+
+/* interface descriptor field offsets */
+#define INTF_DESC_bInterfaceNumber 2 /** Interface number offset */
+#define INTF_DESC_bAlternateSetting 3 /** Alternate setting offset */
+
+#define USB_REQUEST_BUFFER_SIZE 256
+#define USB_EP_OUT_NUM 8
+#define USB_EP_IN_NUM 8
+
+static struct usbd_core_cfg_priv {
+ /** Setup packet */
+ struct usb_setup_packet setup;
+ /** Pointer to data buffer */
+ uint8_t *ep0_data_buf;
+ /** Remaining bytes in buffer */
+ uint32_t ep0_data_buf_residue;
+ /** Total length of control transfer */
+ uint32_t ep0_data_buf_len;
+ /** Zero length packet flag of control transfer */
+ bool zlp_flag;
+ /** Pointer to registered descriptors */
+ const uint8_t *descriptors;
+ /* Buffer used for storing standard, class and vendor request data */
+ uint8_t req_data[USB_REQUEST_BUFFER_SIZE];
+
+#if USBD_EP_CALLBACK_SEARCH_METHOD == 1
+ usbd_endpoint_callback in_ep_cb[USB_EP_IN_NUM];
+ usbd_endpoint_callback out_ep_cb[USB_EP_OUT_NUM];
+#endif
+ /** Variable to check whether the usb has been enabled */
+ bool enabled;
+ /** Variable to check whether the usb has been configured */
+ bool configured;
+ /** Currently selected configuration */
+ uint8_t configuration;
+ /** Remote wakeup feature status */
+ uint16_t remote_wakeup;
+ uint8_t reserved;
+} usbd_core_cfg;
+
+static usb_slist_t usbd_class_head = USB_SLIST_OBJECT_INIT(usbd_class_head);
+static struct usb_msosv1_descriptor *msosv1_desc;
+static struct usb_bos_descriptor *bos_desc;
+
+/**
+ * @brief print the contents of a setup packet
+ *
+ * @param [in] setup The setup packet
+ *
+ */
+static void usbd_print_setup(struct usb_setup_packet *setup)
+{
+ USBD_LOG_ERR("Setup: "
+ "bmRequestType 0x%02x, bRequest 0x%02x, wValue 0x%04x, wIndex 0x%04x, wLength 0x%04x\r\n",
+ setup->bmRequestType,
+ setup->bRequest,
+ setup->wValue,
+ setup->wIndex,
+ setup->wLength);
+}
+
+/**
+ * @brief Check if the device is in Configured state
+ *
+ * @return true if Configured, false otherwise.
+ */
+static bool is_device_configured(void)
+{
+ return (usbd_core_cfg.configuration != 0);
+}
+/**
+ * @brief Check if the interface of given number is valid
+ *
+ * @param [in] interface Number of the addressed interface
+ *
+ * This function searches through descriptor and checks
+ * is the Host has addressed valid interface.
+ *
+ * @return true if interface exists - valid
+ */
+static bool is_interface_valid(uint8_t interface)
+{
+ const uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors;
+ const struct usb_configuration_descriptor *cfg_descr;
+
+ /* Search through descriptor for matching interface */
+ while (p[DESC_bLength] != 0U) {
+ if (p[DESC_bDescriptorType] == USB_DESCRIPTOR_TYPE_CONFIGURATION) {
+ cfg_descr = (const struct usb_configuration_descriptor *)p;
+
+ if (interface < cfg_descr->bNumInterfaces) {
+ return true;
+ }
+ }
+
+ p += p[DESC_bLength];
+ }
+
+ return false;
+}
+/**
+ * @brief Check if the endpoint of given address is valid
+ *
+ * @param [in] ep Address of the Endpoint
+ *
+ * This function checks if the Endpoint of given address
+ * is valid for the configured device. Valid Endpoint is
+ * either Control Endpoint or one used by the device.
+ *
+ * @return true if endpoint exists - valid
+ */
+static bool is_ep_valid(uint8_t ep)
+{
+ /* Check if its Endpoint 0 */
+ if ((ep & 0x7f) == 0) {
+ return true;
+ }
+
+ return true;
+}
+#if USBD_EP_CALLBACK_SEARCH_METHOD == 1
+static void usbd_ep_callback_register(void)
+{
+ usb_slist_t *i, *j, *k;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ usb_slist_for_each(k, &intf->ep_list)
+ {
+ usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list);
+
+ if (ept->ep_cb) {
+ if (ept->ep_addr & 0x80) {
+ usbd_core_cfg.in_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb;
+ } else {
+ usbd_core_cfg.out_ep_cb[ept->ep_addr & 0x7f] = ept->ep_cb;
+ }
+ }
+ }
+ }
+ }
+}
+#endif
+/**
+ * @brief configure and enable endpoint
+ *
+ * This function sets endpoint configuration according to one specified in USB
+ * endpoint descriptor and then enables it for data transfers.
+ *
+ * @param [in] ep_desc Endpoint descriptor byte array
+ *
+ * @return true if successfully configured and enabled
+ */
+static bool usbd_set_endpoint(const struct usb_endpoint_descriptor *ep_desc)
+{
+ struct usbd_endpoint_cfg ep_cfg;
+
+ ep_cfg.ep_addr = ep_desc->bEndpointAddress;
+ ep_cfg.ep_mps = ep_desc->wMaxPacketSize;
+ ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK;
+
+ USBD_LOG("Open endpoint:0x%x type:%u mps:%u\r\n",
+ ep_cfg.ep_addr, ep_cfg.ep_type, ep_cfg.ep_mps);
+
+ usbd_ep_open(&ep_cfg);
+ usbd_core_cfg.configured = true;
+
+ return true;
+}
+/**
+ * @brief Disable endpoint for transferring data
+ *
+ * This function cancels transfers that are associated with endpoint and
+ * disabled endpoint itself.
+ *
+ * @param [in] ep_desc Endpoint descriptor byte array
+ *
+ * @return true if successfully deconfigured and disabled
+ */
+static bool usbd_reset_endpoint(const struct usb_endpoint_descriptor *ep_desc)
+{
+ struct usbd_endpoint_cfg ep_cfg;
+
+ ep_cfg.ep_addr = ep_desc->bEndpointAddress;
+ ep_cfg.ep_mps = ep_desc->wMaxPacketSize;
+ ep_cfg.ep_type = ep_desc->bmAttributes & USBD_EP_TYPE_MASK;
+
+ USBD_LOG("Close endpoint:0x%x type:%u\r\n",
+ ep_cfg.ep_addr, ep_cfg.ep_type);
+
+ usbd_ep_close(ep_cfg.ep_addr);
+
+ return true;
+}
+
+/**
+ * @brief get specified USB descriptor
+ *
+ * This function parses the list of installed USB descriptors and attempts
+ * to find the specified USB descriptor.
+ *
+ * @param [in] type_index Type and index of the descriptor
+ * @param [in] lang_id Language ID of the descriptor (currently unused)
+ * @param [out] len Descriptor length
+ * @param [out] data Descriptor data
+ *
+ * @return true if the descriptor was found, false otherwise
+ */
+static bool usbd_get_descriptor(uint16_t type_index, uint8_t **data, uint32_t *len)
+{
+ uint8_t type = 0U;
+ uint8_t index = 0U;
+ uint8_t *p = NULL;
+ uint32_t cur_index = 0U;
+ bool found = false;
+
+ type = GET_DESC_TYPE(type_index);
+ index = GET_DESC_INDEX(type_index);
+
+ if ((type == USB_DESCRIPTOR_TYPE_STRING) && (index == USB_OSDESC_STRING_DESC_INDEX)) {
+ USBD_LOG("MS OS Descriptor string read\r\n");
+
+ if (!msosv1_desc) {
+ return false;
+ }
+
+ *data = (uint8_t *)msosv1_desc->string;
+ *len = sizeof(struct usb_msosv1_string_descriptor);
+
+ return true;
+ } else if (type == USB_DESCRIPTOR_TYPE_BINARY_OBJECT_STORE) {
+ USBD_LOG("BOS descriptor string read\r\n");
+
+ if (!bos_desc) {
+ return false;
+ }
+
+ *data = bos_desc->bos_id;
+ *len = bos_desc->bos_id_len;
+ return true;
+ }
+ /*
+ * Invalid types of descriptors,
+ * see USB Spec. Revision 2.0, 9.4.3 Get Descriptor
+ */
+ else if ((type == USB_DESCRIPTOR_TYPE_INTERFACE) || (type == USB_DESCRIPTOR_TYPE_ENDPOINT) ||
+ (type > USB_DESCRIPTOR_TYPE_OTHER_SPEED)) {
+ return false;
+ }
+
+ p = (uint8_t *)usbd_core_cfg.descriptors;
+
+ cur_index = 0U;
+
+ while (p[DESC_bLength] != 0U) {
+ if (p[DESC_bDescriptorType] == type) {
+ if (cur_index == index) {
+ found = true;
+ break;
+ }
+
+ cur_index++;
+ }
+
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ if (found) {
+ /* set data pointer */
+ *data = p;
+
+ /* get length from structure */
+ if (type == USB_DESCRIPTOR_TYPE_CONFIGURATION) {
+ /* configuration descriptor is an
+ * exception, length is at offset
+ * 2 and 3
+ */
+ *len = (p[CONF_DESC_wTotalLength]) |
+ (p[CONF_DESC_wTotalLength + 1] << 8);
+ } else {
+ /* normally length is at offset 0 */
+ *len = p[DESC_bLength];
+ }
+ } else {
+ /* nothing found */
+ USBD_LOG_ERR("descriptor <type:%x,index:%x> not found!\r\n", type, index);
+ }
+
+ return found;
+}
+
+/**
+ * @brief set USB configuration
+ *
+ * This function configures the device according to the specified configuration
+ * index and alternate setting by parsing the installed USB descriptor list.
+ * A configuration index of 0 unconfigures the device.
+ *
+ * @param [in] config_index Configuration index
+ * @param [in] alt_setting Alternate setting number
+ *
+ * @return true if successfully configured false if error or unconfigured
+ */
+static bool usbd_set_configuration(uint8_t config_index, uint8_t alt_setting)
+{
+ uint8_t *p = (uint8_t *)usbd_core_cfg.descriptors;
+ uint8_t cur_alt_setting = 0xFF;
+ uint8_t cur_config = 0xFF;
+ bool found = false;
+
+ if (config_index == 0U) {
+ /* TODO: unconfigure device */
+ USBD_LOG_ERR("Device not configured - invalid configuration\r\n");
+ return true;
+ }
+
+ /* configure endpoints for this configuration/altsetting */
+ while (p[DESC_bLength] != 0U) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_CONFIGURATION:
+ /* remember current configuration index */
+ cur_config = p[CONF_DESC_bConfigurationValue];
+
+ if (cur_config == config_index) {
+ found = true;
+ }
+
+ break;
+
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ /* remember current alternate setting */
+ cur_alt_setting =
+ p[INTF_DESC_bAlternateSetting];
+ break;
+
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ if ((cur_config != config_index) ||
+ (cur_alt_setting != alt_setting)) {
+ break;
+ }
+
+ found = usbd_set_endpoint((struct usb_endpoint_descriptor *)p);
+ break;
+
+ default:
+ break;
+ }
+
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ return found;
+}
+
+/**
+ * @brief set USB interface
+ *
+ * @param [in] iface Interface index
+ * @param [in] alt_setting Alternate setting number
+ *
+ * @return true if successfully configured false if error or unconfigured
+ */
+static bool usbd_set_interface(uint8_t iface, uint8_t alt_setting)
+{
+ const uint8_t *p = usbd_core_cfg.descriptors;
+ const uint8_t *if_desc = NULL;
+ struct usb_endpoint_descriptor *ep_desc;
+ uint8_t cur_alt_setting = 0xFF;
+ uint8_t cur_iface = 0xFF;
+ bool ret = false;
+
+ USBD_LOG_DBG("iface %u alt_setting %u\r\n", iface, alt_setting);
+
+ while (p[DESC_bLength] != 0U) {
+ switch (p[DESC_bDescriptorType]) {
+ case USB_DESCRIPTOR_TYPE_INTERFACE:
+ /* remember current alternate setting */
+ cur_alt_setting = p[INTF_DESC_bAlternateSetting];
+ cur_iface = p[INTF_DESC_bInterfaceNumber];
+
+ if (cur_iface == iface &&
+ cur_alt_setting == alt_setting) {
+ if_desc = (void *)p;
+ }
+
+ USBD_LOG_DBG("Current iface %u alt setting %u",
+ cur_iface, cur_alt_setting);
+ break;
+
+ case USB_DESCRIPTOR_TYPE_ENDPOINT:
+ if (cur_iface == iface) {
+ ep_desc = (struct usb_endpoint_descriptor *)p;
+
+ if (cur_alt_setting != alt_setting) {
+ ret = usbd_reset_endpoint(ep_desc);
+ } else {
+ ret = usbd_set_endpoint(ep_desc);
+ }
+ }
+
+ break;
+
+ default:
+ break;
+ }
+
+ /* skip to next descriptor */
+ p += p[DESC_bLength];
+ }
+
+ usbd_event_notify_handler(USB_EVENT_SET_INTERFACE, (void *)if_desc);
+
+ return ret;
+}
+
+/**
+ * @brief handle a standard device request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in,out] ep0_data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usbd_std_device_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint16_t value = setup->wValue;
+ // uint16_t index = setup->wIndex;
+ bool ret = true;
+
+ switch (setup->bRequest) {
+ case USB_REQUEST_GET_STATUS:
+ USBD_LOG_DBG("REQ_GET_STATUS\r\n");
+ /* bit 0: self-powered */
+ /* bit 1: remote wakeup */
+ *data = (uint8_t *)&usbd_core_cfg.remote_wakeup;
+
+ *len = 2;
+ break;
+
+ case USB_REQUEST_CLEAR_FEATURE:
+ USBD_LOG_DBG("REQ_CLEAR_FEATURE\r\n");
+ ret = false;
+
+ if (value == USB_FEATURE_REMOTE_WAKEUP) {
+ usbd_core_cfg.remote_wakeup = 0;
+ usbd_event_notify_handler(USB_EVENT_CLEAR_REMOTE_WAKEUP, NULL);
+ ret = true;
+ }
+
+ break;
+
+ case USB_REQUEST_SET_FEATURE:
+ USBD_LOG_DBG("REQ_SET_FEATURE\r\n");
+ ret = false;
+
+ if (value == USB_FEATURE_REMOTE_WAKEUP) {
+ usbd_core_cfg.remote_wakeup = 1;
+ usbd_event_notify_handler(USB_EVENT_SET_REMOTE_WAKEUP, NULL);
+ ret = true;
+ }
+
+ if (value == USB_FEATURE_TEST_MODE) {
+ /* put TEST_MODE code here */
+ }
+
+ break;
+
+ case USB_REQUEST_SET_ADDRESS:
+ USBD_LOG_DBG("REQ_SET_ADDRESS, addr 0x%x\r\n", value);
+ usbd_set_address(value);
+ break;
+
+ case USB_REQUEST_GET_DESCRIPTOR:
+ USBD_LOG_DBG("REQ_GET_DESCRIPTOR\r\n");
+ ret = usbd_get_descriptor(value, data, len);
+ break;
+
+ case USB_REQUEST_SET_DESCRIPTOR:
+ USBD_LOG_DBG("Device req 0x%02x not implemented\r\n", setup->bRequest);
+ ret = false;
+ break;
+
+ case USB_REQUEST_GET_CONFIGURATION:
+ USBD_LOG_DBG("REQ_GET_CONFIGURATION\r\n");
+ /* indicate if we are configured */
+ *data = (uint8_t *)&usbd_core_cfg.configuration;
+ *len = 1;
+ break;
+
+ case USB_REQUEST_SET_CONFIGURATION:
+ value &= 0xFF;
+ USBD_LOG_DBG("REQ_SET_CONFIGURATION, conf 0x%x\r\n", value);
+
+ if (!usbd_set_configuration(value, 0)) {
+ USBD_LOG_DBG("USB Set Configuration failed\r\n");
+ ret = false;
+ } else {
+ /* configuration successful,
+ * update current configuration
+ */
+ usbd_core_cfg.configuration = value;
+ usbd_event_notify_handler(USB_EVENT_CONFIGURED, NULL);
+ }
+
+ break;
+
+ case USB_REQUEST_GET_INTERFACE:
+ break;
+
+ case USB_REQUEST_SET_INTERFACE:
+ break;
+
+ default:
+ USBD_LOG_ERR("Illegal device req 0x%02x\r\n", setup->bRequest);
+ ret = false;
+ break;
+ }
+
+ return ret;
+}
+
+/**
+ * @brief handle a standard interface request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in] ep0_data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usbd_std_interface_req_handler(struct usb_setup_packet *setup,
+ uint8_t **data, uint32_t *len)
+{
+ /** The device must be configured to accept standard interface
+ * requests and the addressed Interface must be valid.
+ */
+ if (!is_device_configured() ||
+ (!is_interface_valid((uint8_t)setup->wIndex))) {
+ return false;
+ }
+
+ switch (setup->bRequest) {
+ case USB_REQUEST_GET_STATUS:
+ /* no bits specified */
+ *data = (uint8_t *)&usbd_core_cfg.remote_wakeup;
+
+ *len = 2;
+ break;
+
+ case USB_REQUEST_CLEAR_FEATURE:
+ case USB_REQUEST_SET_FEATURE:
+ /* not defined for interface */
+ return false;
+
+ case USB_REQUEST_GET_INTERFACE:
+ /** This handler is called for classes that does not support
+ * alternate Interfaces so always return 0. Classes that
+ * support alternative interfaces handles GET_INTERFACE
+ * in custom_handler.
+ */
+ *data = (uint8_t *)&usbd_core_cfg.reserved;
+
+ *len = 1;
+ break;
+
+ case USB_REQUEST_SET_INTERFACE:
+ USBD_LOG_DBG("REQ_SET_INTERFACE\r\n");
+ usbd_set_interface(setup->wIndex, setup->wValue);
+ break;
+
+ default:
+ USBD_LOG_ERR("Illegal interface req 0x%02x\r\n", setup->bRequest);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief handle a standard endpoint request
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] len Pointer to data length
+ * @param [in] ep0_data_buf Data buffer
+ *
+ * @return true if the request was handled successfully
+ */
+static bool usbd_std_endpoint_req_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t ep = (uint8_t)setup->wIndex;
+
+ /* Check if request addresses valid Endpoint */
+ if (!is_ep_valid(ep)) {
+ return false;
+ }
+
+ switch (setup->bRequest) {
+ case USB_REQUEST_GET_STATUS:
+
+ /** This request is valid for Control Endpoints when
+ * the device is not yet configured. For other
+ * Endpoints the device must be configured.
+ * Firstly check if addressed ep is Control Endpoint.
+ * If no then the device must be in Configured state
+ * to accept the request.
+ */
+ if (((ep & 0x7f) == 0) || is_device_configured()) {
+ /* bit 0 - Endpoint halted or not */
+ usbd_ep_is_stalled(ep, (uint8_t *)&usbd_core_cfg.remote_wakeup);
+ *data = (uint8_t *)&usbd_core_cfg.remote_wakeup;
+
+ *len = 2;
+ break;
+ }
+
+ return false;
+
+ case USB_REQUEST_CLEAR_FEATURE:
+ if (setup->wValue == USB_FEATURE_ENDPOINT_STALL) {
+ /** This request is valid for Control Endpoints when
+ * the device is not yet configured. For other
+ * Endpoints the device must be configured.
+ * Firstly check if addressed ep is Control Endpoint.
+ * If no then the device must be in Configured state
+ * to accept the request.
+ */
+ if (((ep & 0x7f) == 0) || is_device_configured()) {
+ USBD_LOG_ERR("ep:%x clear halt\r\n", ep);
+ usbd_ep_clear_stall(ep);
+ usbd_event_notify_handler(USB_EVENT_CLEAR_HALT, NULL);
+ break;
+ }
+ }
+
+ /* only ENDPOINT_HALT defined for endpoints */
+ return false;
+
+ case USB_REQUEST_SET_FEATURE:
+ if (setup->wValue == USB_FEATURE_ENDPOINT_STALL) {
+ /** This request is valid for Control Endpoints when
+ * the device is not yet configured. For other
+ * Endpoints the device must be configured.
+ * Firstly check if addressed ep is Control Endpoint.
+ * If no then the device must be in Configured state
+ * to accept the request.
+ */
+ if (((ep & 0x7f) == 0) || is_device_configured()) {
+ /* set HALT by stalling */
+ USBD_LOG_ERR("ep:%x set halt\r\n", ep);
+ usbd_ep_set_stall(ep);
+ usbd_event_notify_handler(USB_EVENT_SET_HALT, NULL);
+ break;
+ }
+ }
+
+ /* only ENDPOINT_HALT defined for endpoints */
+ return false;
+
+ case USB_REQUEST_SYNCH_FRAME:
+
+ /* For Synch Frame request the device must be configured */
+ if (is_device_configured()) {
+ /* Not supported, return false anyway */
+ USBD_LOG_DBG("ep req 0x%02x not implemented\r\n", setup->bRequest);
+ }
+
+ return false;
+
+ default:
+ USBD_LOG_ERR("Illegal ep req 0x%02x\r\n", setup->bRequest);
+ return false;
+ }
+
+ return true;
+}
+
+/**
+ * @brief default handler for standard ('chapter 9') requests
+ *
+ * If a custom request handler was installed, this handler is called first.
+ *
+ * @param [in] setup The setup packet
+ * @param [in] ep0_data_buf Data buffer
+ * @param [in,out] len Pointer to data length
+ *
+ * @return true if the request was handled successfully
+ */
+static int usbd_standard_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ int rc = 0;
+
+ switch (setup->bmRequestType_b.Recipient) {
+ case USB_REQUEST_TO_DEVICE:
+ if (usbd_std_device_req_handler(setup, data, len) == false) {
+ rc = -1;
+ }
+
+ break;
+
+ case USB_REQUEST_TO_INTERFACE:
+ if (usbd_std_interface_req_handler(setup, data, len) == false) {
+ rc = -1;
+ }
+
+ break;
+
+ case USB_REQUEST_TO_ENDPOINT:
+ if (usbd_std_endpoint_req_handler(setup, data, len) == false) {
+ rc = -1;
+ }
+
+ break;
+
+ default:
+ rc = -1;
+ }
+
+ return rc;
+}
+
+/*
+ * The functions usbd_class_request_handler(), usbd_custom_request_handler() and usbd_vendor_request_handler()
+ * go through the interfaces one after the other and compare the
+ * bInterfaceNumber with the wIndex and and then call the appropriate
+ * callback of the USB function.
+ * Note, a USB function can have more than one interface and the
+ * request does not have to be directed to the first interface (unlikely).
+ * These functions can be simplified and moved to usb_handle_request()
+ * when legacy initialization throgh the usb_set_config() and
+ * usb_enable() is no longer needed.
+ */
+static int usbd_class_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x", setup->bRequest, setup->wIndex);
+
+ if (setup->bmRequestType_b.Recipient != USB_REQUEST_TO_INTERFACE) {
+ return -1;
+ }
+
+ usb_slist_t *i, *j;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ if (intf->class_handler && (intf->intf_num == (setup->wIndex & 0xFF))) {
+ return intf->class_handler(setup, data, len);
+ }
+ }
+ }
+ return -1;
+}
+
+static int usbd_vendor_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("bRequest 0x%02x, wValue0x%04x, wIndex 0x%04x", setup->bRequest, setup->wValue, setup->wIndex);
+
+ // if(setup->bmRequestType_b.Recipient != USB_REQUEST_TO_DEVICE)
+ // {
+ // return -1;
+ // }
+
+ if (msosv1_desc) {
+ if (setup->bRequest == msosv1_desc->vendor_code) {
+ switch (setup->wIndex) {
+ case 0x04:
+ USBD_LOG("Handle Compat ID\r\n");
+ *data = (uint8_t *)msosv1_desc->compat_id;
+ *len = msosv1_desc->compat_id_len;
+
+ return 0;
+ case 0x05:
+ USBD_LOG("Handle Compat properties\r\n");
+ *data = (uint8_t *)msosv1_desc->comp_id_property;
+ *len = msosv1_desc->comp_id_property_len;
+
+ return 0;
+ default:
+ break;
+ }
+ }
+ }
+
+ usb_slist_t *i, *j;
+
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ if (intf->vendor_handler && !intf->vendor_handler(setup, data, len)) {
+ return 0;
+ }
+ }
+ }
+
+ return -1;
+}
+
+static int usbd_custom_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ USBD_LOG_DBG("bRequest 0x%02x, wIndex 0x%04x", setup->bRequest, setup->wIndex);
+
+ if (setup->bmRequestType_b.Recipient != USB_REQUEST_TO_INTERFACE) {
+ return -1;
+ }
+
+ usb_slist_t *i, *j;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ if (intf->custom_handler && (intf->intf_num == (setup->wIndex & 0xFF))) {
+ return intf->custom_handler(setup, data, len);
+ }
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * @brief handle a request by calling one of the installed request handlers
+ *
+ * Local function to handle a request by calling one of the installed request
+ * handlers. In case of data going from host to device, the data is at *ppbData.
+ * In case of data going from device to host, the handler can either choose to
+ * write its data at *ppbData or update the data pointer.
+ *
+ * @param [in] setup The setup packet
+ * @param [in,out] data Data buffer
+ * @param [in,out] len Pointer to data length
+ *
+ * @return true if the request was handles successfully
+ */
+static bool usbd_setup_request_handler(struct usb_setup_packet *setup, uint8_t **data, uint32_t *len)
+{
+ uint8_t type = setup->bmRequestType_b.Type;
+
+ if (type == USB_REQUEST_STANDARD) {
+ if (!usbd_custom_request_handler(setup, data, len)) {
+ return true;
+ }
+
+ if (usbd_standard_request_handler(setup, data, len) < 0) {
+ USBD_LOG_ERR("Handler Error %d\r\n", type);
+ usbd_print_setup(setup);
+ return false;
+ }
+ } else if (type == USB_REQUEST_CLASS) {
+ if (usbd_class_request_handler(setup, data, len) < 0) {
+ USBD_LOG_ERR("Handler Error %d\r\n", type);
+ usbd_print_setup(setup);
+ return false;
+ }
+ } else if (type == USB_REQUEST_VENDOR) {
+ if (usbd_vendor_request_handler(setup, data, len) < 0) {
+ USBD_LOG_ERR("Handler Error %d\r\n", type);
+ usbd_print_setup(setup);
+ return false;
+ }
+ } else {
+ return false;
+ }
+
+ return true;
+}
+/**
+ * @brief send data or status to host
+ *
+ * @return N/A
+ */
+static void usbd_send_to_host(uint16_t len)
+{
+ uint32_t chunk = 0U;
+
+ if (usbd_core_cfg.zlp_flag == false) {
+ chunk = usbd_core_cfg.ep0_data_buf_residue;
+ usbd_ep_write(USB_CONTROL_IN_EP0, usbd_core_cfg.ep0_data_buf,
+ usbd_core_cfg.ep0_data_buf_residue, &chunk);
+ usbd_core_cfg.ep0_data_buf += chunk;
+ usbd_core_cfg.ep0_data_buf_residue -= chunk;
+
+ /*
+ * Set ZLP flag when host asks for a bigger length and the
+ * last chunk is wMaxPacketSize long, to indicate the last
+ * packet.
+ */
+ /* Send less data as requested during the Setup stage */
+ if ((!usbd_core_cfg.ep0_data_buf_residue) && !(usbd_core_cfg.ep0_data_buf_len % USB_CTRL_EP_MPS)) {
+ /* Transfers a zero-length packet */
+ // USBD_LOG("ZLP, requested %u , length %u ",
+ // len, usb_dev.ep0_data_buf_len);
+ usbd_core_cfg.zlp_flag = true;
+ }
+ } else {
+ usbd_core_cfg.zlp_flag = false;
+ usbd_ep_write(USB_CONTROL_IN_EP0, NULL, 0, NULL);
+ }
+}
+
+static void usbd_ep0_setup_handler(void)
+{
+ struct usb_setup_packet *setup = &usbd_core_cfg.setup;
+
+ /*
+ * OUT transfer, Setup packet,
+ * reset request message state machine
+ */
+ if (usbd_ep_read(USB_CONTROL_OUT_EP0, (uint8_t *)setup,
+ sizeof(struct usb_setup_packet), NULL) < 0) {
+ USBD_LOG_ERR("Read Setup Packet failed\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ //usbd_print_setup(setup);
+
+ if (setup->wLength > USB_REQUEST_BUFFER_SIZE) {
+ if (setup->bmRequestType_b.Dir != USB_REQUEST_DEVICE_TO_HOST) {
+ USBD_LOG_ERR("Request buffer too small\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+ }
+
+ // usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data;
+ usbd_core_cfg.ep0_data_buf_residue = setup->wLength;
+ usbd_core_cfg.ep0_data_buf_len = setup->wLength;
+ usbd_core_cfg.zlp_flag = false;
+
+ /* this maybe set code in class request code */
+ if (setup->wLength &&
+ setup->bmRequestType_b.Dir == USB_REQUEST_HOST_TO_DEVICE) {
+ USBD_LOG_DBG("prepare to out data\r\n");
+ /*set ep ack to recv next data*/
+ usbd_ep_read(USB_CONTROL_OUT_EP0, NULL, 0, NULL);
+ return;
+ }
+
+ /* Ask installed handler to process request */
+ if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) {
+ USBD_LOG_ERR("usbd_setup_request_handler failed\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ /* Send smallest of requested and offered length */
+ usbd_core_cfg.ep0_data_buf_residue = MIN(usbd_core_cfg.ep0_data_buf_len,
+ setup->wLength);
+ /*Send data or status to host*/
+ usbd_send_to_host(setup->wLength);
+}
+
+static void usbd_ep0_out_handler(void)
+{
+ uint32_t chunk = 0U;
+ struct usb_setup_packet *setup = &usbd_core_cfg.setup;
+
+ /* OUT transfer, status packets */
+ if (usbd_core_cfg.ep0_data_buf_residue == 0) {
+ /* absorb zero-length status message */
+ USBD_LOG_DBG("recv status\r\n");
+
+ if (usbd_ep_read(USB_CONTROL_OUT_EP0,
+ NULL,
+ 0, NULL) < 0) {
+ USBD_LOG_ERR("Read DATA Packet failed\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ }
+
+ return;
+ }
+
+ usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data;
+
+ /* OUT transfer, data packets */
+ if (usbd_ep_read(USB_CONTROL_OUT_EP0,
+ usbd_core_cfg.ep0_data_buf,
+ usbd_core_cfg.ep0_data_buf_residue, &chunk) < 0) {
+ USBD_LOG_ERR("Read DATA Packet failed\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ usbd_core_cfg.ep0_data_buf += chunk;
+ usbd_core_cfg.ep0_data_buf_residue -= chunk;
+
+ if (usbd_core_cfg.ep0_data_buf_residue == 0) {
+ /* Received all, send data to handler */
+ usbd_core_cfg.ep0_data_buf = usbd_core_cfg.req_data;
+
+ if (!usbd_setup_request_handler(setup, &usbd_core_cfg.ep0_data_buf, &usbd_core_cfg.ep0_data_buf_len)) {
+ USBD_LOG_ERR("usbd_setup_request_handler1 failed\r\n");
+ usbd_ep_set_stall(USB_CONTROL_IN_EP0);
+ return;
+ }
+
+ /*Send status to host*/
+ usbd_send_to_host(setup->wLength);
+ } else {
+ USBD_LOG_ERR("ep0_data_buf_residue is not zero\r\n");
+ }
+}
+static void usbd_ep0_in_handler(void)
+{
+ struct usb_setup_packet *setup = &usbd_core_cfg.setup;
+
+ /* Send more data if available */
+ if (usbd_core_cfg.ep0_data_buf_residue != 0 || usbd_core_cfg.zlp_flag == true) {
+ usbd_send_to_host(setup->wLength);
+ }
+}
+
+static void usbd_ep_out_handler(uint8_t ep)
+{
+#if USBD_EP_CALLBACK_SEARCH_METHOD == 0
+ usb_slist_t *i, *j, *k;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ usb_slist_for_each(k, &intf->ep_list)
+ {
+ usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list);
+
+ if ((ept->ep_addr == ep) && ept->ep_cb) {
+ ept->ep_cb(ep);
+ }
+ }
+ }
+ }
+#else
+
+ if (usbd_core_cfg.out_ep_cb[ep & 0x7f]) {
+ usbd_core_cfg.out_ep_cb[ep & 0x7f](ep);
+ }
+
+#endif
+}
+
+static void usbd_ep_in_handler(uint8_t ep)
+{
+#if USBD_EP_CALLBACK_SEARCH_METHOD == 0
+ usb_slist_t *i, *j, *k;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ usb_slist_for_each(k, &intf->ep_list)
+ {
+ usbd_endpoint_t *ept = usb_slist_entry(k, struct usbd_endpoint, list);
+
+ if ((ept->ep_addr == ep) && ept->ep_cb) {
+ ept->ep_cb(ep);
+ }
+ }
+ }
+ }
+#else
+
+ if (usbd_core_cfg.in_ep_cb[ep & 0x7f]) {
+ usbd_core_cfg.in_ep_cb[ep & 0x7f](ep);
+ }
+
+#endif
+}
+
+static void usbd_class_event_notify_handler(uint8_t event, void *arg)
+{
+ usb_slist_t *i, *j;
+ usb_slist_for_each(i, &usbd_class_head)
+ {
+ usbd_class_t *class = usb_slist_entry(i, struct usbd_class, list);
+
+ usb_slist_for_each(j, &class->intf_list)
+ {
+ usbd_interface_t *intf = usb_slist_entry(j, struct usbd_interface, list);
+
+ if (intf->notify_handler) {
+ intf->notify_handler(event, arg);
+ }
+ }
+ }
+}
+
+void usbd_event_notify_handler(uint8_t event, void *arg)
+{
+ switch (event) {
+ case USB_EVENT_RESET:
+ usbd_set_address(0);
+#if USBD_EP_CALLBACK_SEARCH_METHOD == 1
+ usbd_ep_callback_register();
+#endif
+
+ case USB_EVENT_ERROR:
+ case USB_EVENT_SOF:
+ case USB_EVENT_CONNECTED:
+ case USB_EVENT_CONFIGURED:
+ case USB_EVENT_SUSPEND:
+ case USB_EVENT_DISCONNECTED:
+ case USB_EVENT_RESUME:
+ case USB_EVENT_SET_INTERFACE:
+ case USB_EVENT_SET_REMOTE_WAKEUP:
+ case USB_EVENT_CLEAR_REMOTE_WAKEUP:
+ case USB_EVENT_SET_HALT:
+ case USB_EVENT_CLEAR_HALT:
+ usbd_class_event_notify_handler(event, arg);
+ break;
+
+ case USB_EVENT_SETUP_NOTIFY:
+ usbd_ep0_setup_handler();
+ break;
+
+ case USB_EVENT_EP0_IN_NOTIFY:
+ usbd_ep0_in_handler();
+ break;
+
+ case USB_EVENT_EP0_OUT_NOTIFY:
+ usbd_ep0_out_handler();
+ break;
+
+ case USB_EVENT_EP_IN_NOTIFY:
+ usbd_ep_in_handler((uint32_t)arg);
+ break;
+
+ case USB_EVENT_EP_OUT_NOTIFY:
+ usbd_ep_out_handler((uint32_t)arg);
+ break;
+
+ default:
+ USBD_LOG_ERR("USB unknown event: %d", event);
+ break;
+ }
+}
+
+void usbd_desc_register(const uint8_t *desc)
+{
+ usbd_core_cfg.descriptors = desc;
+}
+/* Register MS OS Descriptors version 1 */
+void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc)
+{
+ msosv1_desc = desc;
+}
+
+void usbd_class_register(usbd_class_t *class)
+{
+ usb_slist_add_tail(&usbd_class_head, &class->list);
+ usb_slist_init(&class->intf_list);
+}
+
+void usbd_class_add_interface(usbd_class_t *class, usbd_interface_t *intf)
+{
+ static uint8_t intf_offset = 0;
+ intf->intf_num = intf_offset;
+ usb_slist_add_tail(&class->intf_list, &intf->list);
+ usb_slist_init(&intf->ep_list);
+ intf_offset++;
+}
+
+void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep)
+{
+ usb_slist_add_tail(&intf->ep_list, &ep->list);
+}
+
+bool usb_device_is_configured(void)
+{
+ return usbd_core_cfg.configured;
+}
diff --git a/core/usbd_core.h b/core/usbd_core.h
new file mode 100644
index 00000000..5881106e
--- /dev/null
+++ b/core/usbd_core.h
@@ -0,0 +1,136 @@
+/**
+ * @file usbd_core.h
+ *
+ * Copyright (c) 2021 Bouffalolab team
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership. The
+ * ASF licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the
+ * License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+#ifndef _USBD_CORE_H
+#define _USBD_CORE_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "usb_util.h"
+#include "usb_def.h"
+#include "usb_dc.h"
+
+enum usb_event_type {
+ /** USB error reported by the controller */
+ USB_EVENT_ERROR,
+ /** USB reset */
+ USB_EVENT_RESET,
+ /** Start of Frame received */
+ USB_EVENT_SOF,
+ /** USB connection established, hardware enumeration is completed */
+ USB_EVENT_CONNECTED,
+ /** USB configuration done */
+ USB_EVENT_CONFIGURED,
+ /** USB connection suspended by the HOST */
+ USB_EVENT_SUSPEND,
+ /** USB connection lost */
+ USB_EVENT_DISCONNECTED,
+ /** USB connection resumed by the HOST */
+ USB_EVENT_RESUME,
+
+ /** USB interface selected */
+ USB_EVENT_SET_INTERFACE,
+ /** USB interface selected */
+ USB_EVENT_SET_REMOTE_WAKEUP,
+ /** USB interface selected */
+ USB_EVENT_CLEAR_REMOTE_WAKEUP,
+ /** Set Feature ENDPOINT_HALT received */
+ USB_EVENT_SET_HALT,
+ /** Clear Feature ENDPOINT_HALT received */
+ USB_EVENT_CLEAR_HALT,
+ /** setup packet received */
+ USB_EVENT_SETUP_NOTIFY,
+ /** ep0 in packet received */
+ USB_EVENT_EP0_IN_NOTIFY,
+ /** ep0 out packet received */
+ USB_EVENT_EP0_OUT_NOTIFY,
+ /** ep in packet except ep0 received */
+ USB_EVENT_EP_IN_NOTIFY,
+ /** ep out packet except ep0 received */
+ USB_EVENT_EP_OUT_NOTIFY,
+ /** Initial USB connection status */
+ USB_EVENT_UNKNOWN
+};
+
+/**
+ * @brief Callback function signature for the USB Endpoint status
+ */
+typedef void (*usbd_endpoint_callback)(uint8_t ep);
+
+/**
+ * @brief Callback function signature for class specific requests
+ *
+ * Function which handles Class specific requests corresponding to an
+ * interface number specified in the device descriptor table. For host
+ * to device direction the 'len' and 'payload_data' contain the length
+ * of the received data and the pointer to the received data respectively.
+ * For device to host class requests, 'len' and 'payload_data' should be
+ * set by the callback function with the length and the address of the
+ * data to be transmitted buffer respectively.
+ */
+typedef int (*usbd_request_handler)(struct usb_setup_packet *setup,
+ uint8_t **data, uint32_t *transfer_len);
+
+/* callback function pointer structure for Application to handle events */
+typedef void (*usbd_notify_handler)(uint8_t event, void *arg);
+
+typedef struct usbd_endpoint {
+ usb_slist_t list;
+ uint8_t ep_addr;
+ usbd_endpoint_callback ep_cb;
+} usbd_endpoint_t;
+
+typedef struct usbd_interface {
+ usb_slist_t list;
+ /** Handler for USB Class specific Control (EP 0) communications */
+ usbd_request_handler class_handler;
+ /** Handler for USB Vendor specific commands */
+ usbd_request_handler vendor_handler;
+ /** Handler for USB custom specific commands */
+ usbd_request_handler custom_handler;
+ /** Handler for USB event notify commands */
+ usbd_notify_handler notify_handler;
+ uint8_t intf_num;
+ usb_slist_t ep_list;
+} usbd_interface_t;
+
+typedef struct usbd_class {
+ usb_slist_t list;
+ const char *name;
+ usb_slist_t intf_list;
+} usbd_class_t;
+
+void usbd_event_notify_handler(uint8_t event, void *arg);
+
+void usbd_desc_register(const uint8_t *desc);
+void usbd_class_register(usbd_class_t *class);
+void usbd_msosv1_desc_register(struct usb_msosv1_descriptor *desc);
+void usbd_class_add_interface(usbd_class_t *class, usbd_interface_t *intf);
+void usbd_interface_add_endpoint(usbd_interface_t *intf, usbd_endpoint_t *ep);
+bool usb_device_is_configured(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif