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