jzlv пре 4 година
комит
8a143df509

+ 171 - 0
.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
+...
+

+ 1 - 0
.gitignore

@@ -0,0 +1 @@
+.vscode

+ 201 - 0
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.

+ 99 - 0
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 类接口
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^

+ 96 - 0
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);
+}

+ 277 - 0
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_ */

+ 168 - 0
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);
+}

+ 374 - 0
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_ */

+ 227 - 0
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++;
+}

+ 344 - 0
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_ */

+ 838 - 0
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]);
+}

+ 101 - 0
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_ */

+ 302 - 0
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_ */

+ 134 - 0
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);
+}

+ 821 - 0
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_ */

+ 37 - 0
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

+ 35 - 0
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

+ 179 - 0
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

+ 522 - 0
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

+ 224 - 0
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

+ 131 - 0
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

+ 1232 - 0
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;
+}

+ 136 - 0
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