CMSIS DAP/DAP Link Debugger (#1897)
* Apps: DAP Link * API: furi_hal_console_init Co-authored-by: あく <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									42df7aa04a
								
							
						
					
					
						commit
						f56c94922d
					
				
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
								
							| @ -31,3 +31,6 @@ | |||||||
| [submodule "lib/cxxheaderparser"] | [submodule "lib/cxxheaderparser"] | ||||||
| 	path = lib/cxxheaderparser | 	path = lib/cxxheaderparser | ||||||
| 	url = https://github.com/robotpy/cxxheaderparser.git | 	url = https://github.com/robotpy/cxxheaderparser.git | ||||||
|  | [submodule "applications/plugins/dap_link/lib/free-dap"] | ||||||
|  | 	path = applications/plugins/dap_link/lib/free-dap | ||||||
|  | 	url = https://github.com/ataradov/free-dap.git | ||||||
|  | |||||||
							
								
								
									
										105
									
								
								applications/plugins/dap_link/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								applications/plugins/dap_link/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,105 @@ | |||||||
|  | # Flipper Zero as CMSIS DAP/DAP Link | ||||||
|  | Flipper Zero as a [Free-DAP](https://github.com/ataradov/free-dap) based SWD\JTAG debugger. Free-DAP is a free and open source firmware implementation of the [CMSIS-DAP](https://www.keil.com/pack/doc/CMSIS_Dev/DAP/html/index.html) debugger. | ||||||
|  | 
 | ||||||
|  | ## Protocols | ||||||
|  | SWD, JTAG , CMSIS-DAP v1 (18 KiB/s), CMSIS-DAP v2 (46 KiB/s), VCP (USB-UART). | ||||||
|  | 
 | ||||||
|  | WinUSB for driverless installation for Windows 8 and above. | ||||||
|  | 
 | ||||||
|  | ## Usage | ||||||
|  | 
 | ||||||
|  | ### VSCode + Cortex-Debug | ||||||
|  |   Set `"device": "cmsis-dap"` | ||||||
|  |    | ||||||
|  | <details> | ||||||
|  |   <summary>BluePill configuration example</summary> | ||||||
|  |    | ||||||
|  |   ```json | ||||||
|  | { | ||||||
|  |     "name": "Attach (DAP)", | ||||||
|  |     "cwd": "${workspaceFolder}", | ||||||
|  |     "executable": "./build/firmware.elf", | ||||||
|  |     "request": "attach", | ||||||
|  |     "type": "cortex-debug", | ||||||
|  |     "servertype": "openocd", | ||||||
|  |     "device": "cmsis-dap", | ||||||
|  |     "configFiles": [ | ||||||
|  |         "interface/cmsis-dap.cfg", | ||||||
|  |         "target/stm32f1x.cfg", | ||||||
|  |     ], | ||||||
|  | }, | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
|  | 
 | ||||||
|  | <details> | ||||||
|  |   <summary>Flipper Zero configuration example</summary> | ||||||
|  |    | ||||||
|  |   ```json | ||||||
|  | { | ||||||
|  |     "name": "Attach (DAP)", | ||||||
|  |     "cwd": "${workspaceFolder}", | ||||||
|  |     "executable": "./build/latest/firmware.elf", | ||||||
|  |     "request": "attach", | ||||||
|  |     "type": "cortex-debug", | ||||||
|  |     "servertype": "openocd", | ||||||
|  |     "device": "cmsis-dap", | ||||||
|  |     "svdFile": "./debug/STM32WB55_CM4.svd", | ||||||
|  |     "rtos": "FreeRTOS", | ||||||
|  |     "configFiles": [ | ||||||
|  |         "interface/cmsis-dap.cfg", | ||||||
|  |         "./debug/stm32wbx.cfg", | ||||||
|  |     ], | ||||||
|  |     "postAttachCommands": [ | ||||||
|  |         "source debug/flipperapps.py", | ||||||
|  |     ], | ||||||
|  | }, | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
|  | 
 | ||||||
|  | ### OpenOCD | ||||||
|  | Use `interface/cmsis-dap.cfg`. You will need OpenOCD v0.11.0. | ||||||
|  | 
 | ||||||
|  | Additional commands:  | ||||||
|  | * `cmsis_dap_backend hid` for CMSIS-DAP v1 protocol. | ||||||
|  | * `cmsis_dap_backend usb_bulk` for CMSIS-DAP v2 protocol. | ||||||
|  | * `cmsis_dap_serial DAP_Oyevoxo` use DAP-Link running on Flipper named `Oyevoxo`. | ||||||
|  | * `cmsis-dap cmd 81` - reboot connected DAP-Link. | ||||||
|  | 
 | ||||||
|  | <details> | ||||||
|  |   <summary>Flash BluePill</summary> | ||||||
|  |    | ||||||
|  |   ``` | ||||||
|  | openocd -f interface/cmsis-dap.cfg -f target/stm32f1x.cfg -c init -c "program build/firmware.bin reset exit 0x8000000" | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
|  | 
 | ||||||
|  | <details> | ||||||
|  |   <summary>Flash Flipper Zero using DAP v2 protocol</summary> | ||||||
|  |    | ||||||
|  |   ``` | ||||||
|  | openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_backend usb_bulk" -f debug/stm32wbx.cfg -c init -c "program build/latest/firmware.bin reset exit 0x8000000" | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
|  | 
 | ||||||
|  | <details> | ||||||
|  |   <summary>Reboot connected DAP-Link on Flipper named Oyevoxo</summary> | ||||||
|  |    | ||||||
|  |   ``` | ||||||
|  | openocd -f interface/cmsis-dap.cfg -c "cmsis_dap_serial DAP_Oyevoxo" -c "transport select swd" -c "adapter speed 4000000" -c init -c "cmsis-dap cmd 81" -c "exit" | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
|  | 
 | ||||||
|  | ### PlatformIO | ||||||
|  | Use `debug_tool = cmsis-dap` and `upload_protocol = cmsis-dap`. [Documentation](https://docs.platformio.org/en/latest/plus/debug-tools/cmsis-dap.html#debugging-tool-cmsis-dap). Remember that Windows 8 and above do not require drivers. | ||||||
|  | 
 | ||||||
|  | <details> | ||||||
|  |   <summary>BluePill platformio.ini example</summary> | ||||||
|  |    | ||||||
|  |   ``` | ||||||
|  | [env:bluepill_f103c8] | ||||||
|  | platform = ststm32 | ||||||
|  | board = bluepill_f103c8 | ||||||
|  | debug_tool = cmsis-dap | ||||||
|  | upload_protocol = cmsis-dap | ||||||
|  |   ``` | ||||||
|  | </details> | ||||||
							
								
								
									
										24
									
								
								applications/plugins/dap_link/application.fam
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								applications/plugins/dap_link/application.fam
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,24 @@ | |||||||
|  | App( | ||||||
|  |     appid="dap_link", | ||||||
|  |     name="DAP Link", | ||||||
|  |     apptype=FlipperAppType.PLUGIN, | ||||||
|  |     entry_point="dap_link_app", | ||||||
|  |     requires=[ | ||||||
|  |         "gui", | ||||||
|  |         "dialogs", | ||||||
|  |     ], | ||||||
|  |     stack_size=4 * 1024, | ||||||
|  |     order=20, | ||||||
|  |     fap_icon="dap_link.png", | ||||||
|  |     fap_category="Tools", | ||||||
|  |     fap_private_libs=[ | ||||||
|  |         Lib( | ||||||
|  |             name="free-dap", | ||||||
|  |             cincludes=["."], | ||||||
|  |             sources=[ | ||||||
|  |                 "dap.c", | ||||||
|  |             ], | ||||||
|  |         ), | ||||||
|  |     ], | ||||||
|  |     fap_icon_assets="icons", | ||||||
|  | ) | ||||||
							
								
								
									
										234
									
								
								applications/plugins/dap_link/dap_config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										234
									
								
								applications/plugins/dap_link/dap_config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,234 @@ | |||||||
|  | // SPDX-License-Identifier: BSD-3-Clause
 | ||||||
|  | // Copyright (c) 2022, Alex Taradov <alex@taradov.com>. All rights reserved.
 | ||||||
|  | 
 | ||||||
|  | #ifndef _DAP_CONFIG_H_ | ||||||
|  | #define _DAP_CONFIG_H_ | ||||||
|  | 
 | ||||||
|  | /*- Includes ----------------------------------------------------------------*/ | ||||||
|  | #include <furi_hal_gpio.h> | ||||||
|  | 
 | ||||||
|  | /*- Definitions -------------------------------------------------------------*/ | ||||||
|  | #define DAP_CONFIG_ENABLE_JTAG | ||||||
|  | 
 | ||||||
|  | #define DAP_CONFIG_DEFAULT_PORT DAP_PORT_SWD | ||||||
|  | #define DAP_CONFIG_DEFAULT_CLOCK 4200000 // Hz
 | ||||||
|  | 
 | ||||||
|  | #define DAP_CONFIG_PACKET_SIZE 64 | ||||||
|  | #define DAP_CONFIG_PACKET_COUNT 1 | ||||||
|  | 
 | ||||||
|  | #define DAP_CONFIG_JTAG_DEV_COUNT 8 | ||||||
|  | 
 | ||||||
|  | // DAP_CONFIG_PRODUCT_STR must contain "CMSIS-DAP" to be compatible with the standard
 | ||||||
|  | #define DAP_CONFIG_VENDOR_STR "Flipper Zero" | ||||||
|  | #define DAP_CONFIG_PRODUCT_STR "Generic CMSIS-DAP Adapter" | ||||||
|  | #define DAP_CONFIG_SER_NUM_STR usb_serial_number | ||||||
|  | #define DAP_CONFIG_CMSIS_DAP_VER_STR "2.0.0" | ||||||
|  | 
 | ||||||
|  | #define DAP_CONFIG_RESET_TARGET_FN dap_app_target_reset | ||||||
|  | #define DAP_CONFIG_VENDOR_FN dap_app_vendor_cmd | ||||||
|  | 
 | ||||||
|  | // Attribute to use for performance-critical functions
 | ||||||
|  | #define DAP_CONFIG_PERFORMANCE_ATTR | ||||||
|  | 
 | ||||||
|  | // A value at which dap_clock_test() produces 1 kHz output on the SWCLK pin
 | ||||||
|  | // #define DAP_CONFIG_DELAY_CONSTANT 19000
 | ||||||
|  | #define DAP_CONFIG_DELAY_CONSTANT 6290 | ||||||
|  | 
 | ||||||
|  | // A threshold for switching to fast clock (no added delays)
 | ||||||
|  | // This is the frequency produced by dap_clock_test(1) on the SWCLK pin
 | ||||||
|  | #define DAP_CONFIG_FAST_CLOCK 2400000 // Hz
 | ||||||
|  | 
 | ||||||
|  | /*- Prototypes --------------------------------------------------------------*/ | ||||||
|  | extern char usb_serial_number[16]; | ||||||
|  | 
 | ||||||
|  | /*- Implementations ---------------------------------------------------------*/ | ||||||
|  | extern GpioPin flipper_dap_swclk_pin; | ||||||
|  | extern GpioPin flipper_dap_swdio_pin; | ||||||
|  | extern GpioPin flipper_dap_reset_pin; | ||||||
|  | extern GpioPin flipper_dap_tdo_pin; | ||||||
|  | extern GpioPin flipper_dap_tdi_pin; | ||||||
|  | 
 | ||||||
|  | extern void dap_app_vendor_cmd(uint8_t cmd); | ||||||
|  | extern void dap_app_target_reset(); | ||||||
|  | extern void dap_app_disconnect(); | ||||||
|  | extern void dap_app_connect_swd(); | ||||||
|  | extern void dap_app_connect_jtag(); | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWCLK_TCK_write(int value) { | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swclk_pin, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWDIO_TMS_write(int value) { | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swdio_pin, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_TDI_write(int value) { | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_tdi_pin, value); | ||||||
|  | #else | ||||||
|  |     (void)value; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_TDO_write(int value) { | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_tdo_pin, value); | ||||||
|  | #else | ||||||
|  |     (void)value; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_nTRST_write(int value) { | ||||||
|  |     (void)value; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_nRESET_write(int value) { | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_reset_pin, value); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_SWCLK_TCK_read(void) { | ||||||
|  |     return furi_hal_gpio_read(&flipper_dap_swclk_pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_SWDIO_TMS_read(void) { | ||||||
|  |     return furi_hal_gpio_read(&flipper_dap_swdio_pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_TDO_read(void) { | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     return furi_hal_gpio_read(&flipper_dap_tdo_pin); | ||||||
|  | #else | ||||||
|  |     return 0; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_TDI_read(void) { | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     return furi_hal_gpio_read(&flipper_dap_tdi_pin); | ||||||
|  | #else | ||||||
|  |     return 0; | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_nTRST_read(void) { | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline int DAP_CONFIG_nRESET_read(void) { | ||||||
|  |     return furi_hal_gpio_read(&flipper_dap_reset_pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWCLK_TCK_set(void) { | ||||||
|  |     LL_GPIO_SetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWCLK_TCK_clr(void) { | ||||||
|  |     LL_GPIO_ResetOutputPin(flipper_dap_swclk_pin.port, flipper_dap_swclk_pin.pin); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWDIO_TMS_in(void) { | ||||||
|  |     LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_INPUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SWDIO_TMS_out(void) { | ||||||
|  |     LL_GPIO_SetPinMode(flipper_dap_swdio_pin.port, flipper_dap_swdio_pin.pin, LL_GPIO_MODE_OUTPUT); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_SETUP(void) { | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | #endif | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_DISCONNECT(void) { | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | #endif | ||||||
|  |     dap_app_disconnect(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_CONNECT_SWD(void) { | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swdio_pin, true); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swclk_pin, true); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_reset_pin, true); | ||||||
|  | 
 | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | #endif | ||||||
|  |     dap_app_connect_swd(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_CONNECT_JTAG(void) { | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_swdio_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swdio_pin, true); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_swclk_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_swclk_pin, true); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_reset_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_reset_pin, true); | ||||||
|  | 
 | ||||||
|  | #ifdef DAP_CONFIG_ENABLE_JTAG | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeInput, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  | 
 | ||||||
|  |     furi_hal_gpio_init( | ||||||
|  |         &flipper_dap_tdi_pin, GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh); | ||||||
|  |     furi_hal_gpio_write(&flipper_dap_tdi_pin, true); | ||||||
|  | #endif | ||||||
|  |     dap_app_connect_jtag(); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | static inline void DAP_CONFIG_LED(int index, int state) { | ||||||
|  |     (void)index; | ||||||
|  |     (void)state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //-----------------------------------------------------------------------------
 | ||||||
|  | __attribute__((always_inline)) static inline void DAP_CONFIG_DELAY(uint32_t cycles) { | ||||||
|  |     asm volatile("1: subs %[cycles], %[cycles], #1 \n" | ||||||
|  |                  "   bne 1b \n" | ||||||
|  |                  : [cycles] "+l"(cycles)); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #endif // _DAP_CONFIG_H_
 | ||||||
							
								
								
									
										521
									
								
								applications/plugins/dap_link/dap_link.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										521
									
								
								applications/plugins/dap_link/dap_link.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,521 @@ | |||||||
|  | #include <dap.h> | ||||||
|  | #include <furi.h> | ||||||
|  | #include <furi_hal_version.h> | ||||||
|  | #include <furi_hal_gpio.h> | ||||||
|  | #include <furi_hal_uart.h> | ||||||
|  | #include <furi_hal_console.h> | ||||||
|  | #include <furi_hal_resources.h> | ||||||
|  | #include <furi_hal_power.h> | ||||||
|  | #include <stm32wbxx_ll_usart.h> | ||||||
|  | #include <stm32wbxx_ll_lpuart.h> | ||||||
|  | 
 | ||||||
|  | #include "dap_link.h" | ||||||
|  | #include "dap_config.h" | ||||||
|  | #include "gui/dap_gui.h" | ||||||
|  | #include "usb/dap_v2_usb.h" | ||||||
|  | 
 | ||||||
|  | /***************************************************************************/ | ||||||
|  | /****************************** DAP COMMON *********************************/ | ||||||
|  | /***************************************************************************/ | ||||||
|  | 
 | ||||||
|  | struct DapApp { | ||||||
|  |     FuriThread* dap_thread; | ||||||
|  |     FuriThread* cdc_thread; | ||||||
|  |     FuriThread* gui_thread; | ||||||
|  | 
 | ||||||
|  |     DapState state; | ||||||
|  |     DapConfig config; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | void dap_app_get_state(DapApp* app, DapState* state) { | ||||||
|  |     *state = app->state; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #define DAP_PROCESS_THREAD_TICK 500 | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapThreadEventStop = (1 << 0), | ||||||
|  | } DapThreadEvent; | ||||||
|  | 
 | ||||||
|  | void dap_thread_send_stop(FuriThread* thread) { | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(thread), DapThreadEventStop); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | GpioPin flipper_dap_swclk_pin; | ||||||
|  | GpioPin flipper_dap_swdio_pin; | ||||||
|  | GpioPin flipper_dap_reset_pin; | ||||||
|  | GpioPin flipper_dap_tdo_pin; | ||||||
|  | GpioPin flipper_dap_tdi_pin; | ||||||
|  | 
 | ||||||
|  | /***************************************************************************/ | ||||||
|  | /****************************** DAP PROCESS ********************************/ | ||||||
|  | /***************************************************************************/ | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     uint8_t data[DAP_CONFIG_PACKET_SIZE]; | ||||||
|  |     uint8_t size; | ||||||
|  | } DapPacket; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DAPThreadEventStop = DapThreadEventStop, | ||||||
|  |     DAPThreadEventRxV1 = (1 << 1), | ||||||
|  |     DAPThreadEventRxV2 = (1 << 2), | ||||||
|  |     DAPThreadEventUSBConnect = (1 << 3), | ||||||
|  |     DAPThreadEventUSBDisconnect = (1 << 4), | ||||||
|  |     DAPThreadEventApplyConfig = (1 << 5), | ||||||
|  |     DAPThreadEventAll = DAPThreadEventStop | DAPThreadEventRxV1 | DAPThreadEventRxV2 | | ||||||
|  |                         DAPThreadEventUSBConnect | DAPThreadEventUSBDisconnect | | ||||||
|  |                         DAPThreadEventApplyConfig, | ||||||
|  | } DAPThreadEvent; | ||||||
|  | 
 | ||||||
|  | #define USB_SERIAL_NUMBER_LEN 16 | ||||||
|  | char usb_serial_number[USB_SERIAL_NUMBER_LEN] = {0}; | ||||||
|  | 
 | ||||||
|  | const char* dap_app_get_serial(DapApp* app) { | ||||||
|  |     UNUSED(app); | ||||||
|  |     return usb_serial_number; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_rx1_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     FuriThreadId thread_id = (FuriThreadId)context; | ||||||
|  |     furi_thread_flags_set(thread_id, DAPThreadEventRxV1); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_rx2_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     FuriThreadId thread_id = (FuriThreadId)context; | ||||||
|  |     furi_thread_flags_set(thread_id, DAPThreadEventRxV2); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_usb_state_callback(bool state, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     FuriThreadId thread_id = (FuriThreadId)context; | ||||||
|  |     if(state) { | ||||||
|  |         furi_thread_flags_set(thread_id, DAPThreadEventUSBConnect); | ||||||
|  |     } else { | ||||||
|  |         furi_thread_flags_set(thread_id, DAPThreadEventUSBDisconnect); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_process_v1() { | ||||||
|  |     DapPacket tx_packet; | ||||||
|  |     DapPacket rx_packet; | ||||||
|  |     memset(&tx_packet, 0, sizeof(DapPacket)); | ||||||
|  |     rx_packet.size = dap_v1_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); | ||||||
|  |     dap_process_request(rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); | ||||||
|  |     dap_v1_usb_tx(tx_packet.data, DAP_CONFIG_PACKET_SIZE); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_process_v2() { | ||||||
|  |     DapPacket tx_packet; | ||||||
|  |     DapPacket rx_packet; | ||||||
|  |     memset(&tx_packet, 0, sizeof(DapPacket)); | ||||||
|  |     rx_packet.size = dap_v2_usb_rx(rx_packet.data, DAP_CONFIG_PACKET_SIZE); | ||||||
|  |     size_t len = dap_process_request( | ||||||
|  |         rx_packet.data, rx_packet.size, tx_packet.data, DAP_CONFIG_PACKET_SIZE); | ||||||
|  |     dap_v2_usb_tx(tx_packet.data, len); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_app_vendor_cmd(uint8_t cmd) { | ||||||
|  |     // openocd -c "cmsis-dap cmd 81"
 | ||||||
|  |     if(cmd == 0x01) { | ||||||
|  |         furi_hal_power_reset(); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_app_target_reset() { | ||||||
|  |     FURI_LOG_I("DAP", "Target reset"); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_init_gpio(DapSwdPins swd_pins) { | ||||||
|  |     switch(swd_pins) { | ||||||
|  |     case DapSwdPinsPA7PA6: | ||||||
|  |         flipper_dap_swclk_pin = gpio_ext_pa7; | ||||||
|  |         flipper_dap_swdio_pin = gpio_ext_pa6; | ||||||
|  |         break; | ||||||
|  |     case DapSwdPinsPA14PA13: | ||||||
|  |         flipper_dap_swclk_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_14}; | ||||||
|  |         flipper_dap_swdio_pin = (GpioPin){.port = GPIOA, .pin = LL_GPIO_PIN_13}; | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     flipper_dap_reset_pin = gpio_ext_pa4; | ||||||
|  |     flipper_dap_tdo_pin = gpio_ext_pb3; | ||||||
|  |     flipper_dap_tdi_pin = gpio_ext_pb2; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_deinit_gpio(DapSwdPins swd_pins) { | ||||||
|  |     // setup gpio pins to default state
 | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_reset_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdo_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     furi_hal_gpio_init(&flipper_dap_tdi_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  | 
 | ||||||
|  |     if(DapSwdPinsPA14PA13 == swd_pins) { | ||||||
|  |         // PA14 and PA13 are used by SWD
 | ||||||
|  |         furi_hal_gpio_init_ex( | ||||||
|  |             &flipper_dap_swclk_pin, | ||||||
|  |             GpioModeAltFunctionPushPull, | ||||||
|  |             GpioPullDown, | ||||||
|  |             GpioSpeedLow, | ||||||
|  |             GpioAltFn0JTCK_SWCLK); | ||||||
|  |         furi_hal_gpio_init_ex( | ||||||
|  |             &flipper_dap_swdio_pin, | ||||||
|  |             GpioModeAltFunctionPushPull, | ||||||
|  |             GpioPullUp, | ||||||
|  |             GpioSpeedVeryHigh, | ||||||
|  |             GpioAltFn0JTMS_SWDIO); | ||||||
|  |     } else { | ||||||
|  |         furi_hal_gpio_init(&flipper_dap_swclk_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |         furi_hal_gpio_init(&flipper_dap_swdio_pin, GpioModeAnalog, GpioPullNo, GpioSpeedLow); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t dap_process(void* p) { | ||||||
|  |     DapApp* app = p; | ||||||
|  |     DapState* dap_state = &(app->state); | ||||||
|  | 
 | ||||||
|  |     // allocate resources
 | ||||||
|  |     FuriHalUsbInterface* usb_config_prev; | ||||||
|  |     app->config.swd_pins = DapSwdPinsPA7PA6; | ||||||
|  |     DapSwdPins swd_pins_prev = app->config.swd_pins; | ||||||
|  | 
 | ||||||
|  |     // init pins
 | ||||||
|  |     dap_init_gpio(swd_pins_prev); | ||||||
|  | 
 | ||||||
|  |     // init dap
 | ||||||
|  |     dap_init(); | ||||||
|  | 
 | ||||||
|  |     // get name
 | ||||||
|  |     const char* name = furi_hal_version_get_name_ptr(); | ||||||
|  |     if(!name) { | ||||||
|  |         name = "Flipper"; | ||||||
|  |     } | ||||||
|  |     snprintf(usb_serial_number, USB_SERIAL_NUMBER_LEN, "DAP_%s", name); | ||||||
|  | 
 | ||||||
|  |     // init usb
 | ||||||
|  |     usb_config_prev = furi_hal_usb_get_config(); | ||||||
|  |     dap_common_usb_alloc_name(usb_serial_number); | ||||||
|  |     dap_common_usb_set_context(furi_thread_get_id(furi_thread_get_current())); | ||||||
|  |     dap_v1_usb_set_rx_callback(dap_app_rx1_callback); | ||||||
|  |     dap_v2_usb_set_rx_callback(dap_app_rx2_callback); | ||||||
|  |     dap_common_usb_set_state_callback(dap_app_usb_state_callback); | ||||||
|  |     furi_hal_usb_set_config(&dap_v2_usb_hid, NULL); | ||||||
|  | 
 | ||||||
|  |     // work
 | ||||||
|  |     uint32_t events; | ||||||
|  |     while(1) { | ||||||
|  |         events = furi_thread_flags_wait(DAPThreadEventAll, FuriFlagWaitAny, FuriWaitForever); | ||||||
|  | 
 | ||||||
|  |         if(!(events & FuriFlagError)) { | ||||||
|  |             if(events & DAPThreadEventRxV1) { | ||||||
|  |                 dap_app_process_v1(); | ||||||
|  |                 dap_state->dap_counter++; | ||||||
|  |                 dap_state->dap_version = DapVersionV1; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & DAPThreadEventRxV2) { | ||||||
|  |                 dap_app_process_v2(); | ||||||
|  |                 dap_state->dap_counter++; | ||||||
|  |                 dap_state->dap_version = DapVersionV2; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & DAPThreadEventUSBConnect) { | ||||||
|  |                 dap_state->usb_connected = true; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & DAPThreadEventUSBDisconnect) { | ||||||
|  |                 dap_state->usb_connected = false; | ||||||
|  |                 dap_state->dap_version = DapVersionUnknown; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & DAPThreadEventApplyConfig) { | ||||||
|  |                 if(swd_pins_prev != app->config.swd_pins) { | ||||||
|  |                     dap_deinit_gpio(swd_pins_prev); | ||||||
|  |                     swd_pins_prev = app->config.swd_pins; | ||||||
|  |                     dap_init_gpio(swd_pins_prev); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & DAPThreadEventStop) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     // deinit usb
 | ||||||
|  |     furi_hal_usb_set_config(usb_config_prev, NULL); | ||||||
|  |     dap_common_wait_for_deinit(); | ||||||
|  |     dap_common_usb_free_name(); | ||||||
|  |     dap_deinit_gpio(swd_pins_prev); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /***************************************************************************/ | ||||||
|  | /****************************** CDC PROCESS ********************************/ | ||||||
|  | /***************************************************************************/ | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     CDCThreadEventStop = DapThreadEventStop, | ||||||
|  |     CDCThreadEventUARTRx = (1 << 1), | ||||||
|  |     CDCThreadEventCDCRx = (1 << 2), | ||||||
|  |     CDCThreadEventCDCConfig = (1 << 3), | ||||||
|  |     CDCThreadEventApplyConfig = (1 << 4), | ||||||
|  |     CDCThreadEventAll = CDCThreadEventStop | CDCThreadEventUARTRx | CDCThreadEventCDCRx | | ||||||
|  |                         CDCThreadEventCDCConfig | CDCThreadEventApplyConfig, | ||||||
|  | } CDCThreadEvent; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     FuriStreamBuffer* rx_stream; | ||||||
|  |     FuriThreadId thread_id; | ||||||
|  |     FuriHalUartId uart_id; | ||||||
|  |     struct usb_cdc_line_coding line_coding; | ||||||
|  | } CDCProcess; | ||||||
|  | 
 | ||||||
|  | static void cdc_uart_irq_cb(UartIrqEvent ev, uint8_t data, void* ctx) { | ||||||
|  |     CDCProcess* app = ctx; | ||||||
|  | 
 | ||||||
|  |     if(ev == UartIrqEventRXNE) { | ||||||
|  |         furi_stream_buffer_send(app->rx_stream, &data, 1, 0); | ||||||
|  |         furi_thread_flags_set(app->thread_id, CDCThreadEventUARTRx); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cdc_usb_rx_callback(void* context) { | ||||||
|  |     CDCProcess* app = context; | ||||||
|  |     furi_thread_flags_set(app->thread_id, CDCThreadEventCDCRx); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cdc_usb_control_line_callback(uint8_t state, void* context) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cdc_usb_config_callback(struct usb_cdc_line_coding* config, void* context) { | ||||||
|  |     CDCProcess* app = context; | ||||||
|  |     app->line_coding = *config; | ||||||
|  |     furi_thread_flags_set(app->thread_id, CDCThreadEventCDCConfig); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static FuriHalUartId cdc_init_uart( | ||||||
|  |     DapUartType type, | ||||||
|  |     DapUartTXRX swap, | ||||||
|  |     uint32_t baudrate, | ||||||
|  |     void (*cb)(UartIrqEvent ev, uint8_t data, void* ctx), | ||||||
|  |     void* ctx) { | ||||||
|  |     FuriHalUartId uart_id = FuriHalUartIdUSART1; | ||||||
|  |     if(baudrate == 0) baudrate = 115200; | ||||||
|  | 
 | ||||||
|  |     switch(type) { | ||||||
|  |     case DapUartTypeUSART1: | ||||||
|  |         uart_id = FuriHalUartIdUSART1; | ||||||
|  |         furi_hal_console_disable(); | ||||||
|  |         furi_hal_uart_deinit(uart_id); | ||||||
|  |         if(swap == DapUartTXRXSwap) { | ||||||
|  |             LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_SWAPPED); | ||||||
|  |         } else { | ||||||
|  |             LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); | ||||||
|  |         } | ||||||
|  |         furi_hal_uart_init(uart_id, baudrate); | ||||||
|  |         furi_hal_uart_set_irq_cb(uart_id, cb, ctx); | ||||||
|  |         break; | ||||||
|  |     case DapUartTypeLPUART1: | ||||||
|  |         uart_id = FuriHalUartIdLPUART1; | ||||||
|  |         furi_hal_uart_deinit(uart_id); | ||||||
|  |         if(swap == DapUartTXRXSwap) { | ||||||
|  |             LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_SWAPPED); | ||||||
|  |         } else { | ||||||
|  |             LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); | ||||||
|  |         } | ||||||
|  |         furi_hal_uart_init(uart_id, baudrate); | ||||||
|  |         furi_hal_uart_set_irq_cb(uart_id, cb, ctx); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return uart_id; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cdc_deinit_uart(DapUartType type) { | ||||||
|  |     switch(type) { | ||||||
|  |     case DapUartTypeUSART1: | ||||||
|  |         furi_hal_uart_deinit(FuriHalUartIdUSART1); | ||||||
|  |         LL_USART_SetTXRXSwap(USART1, LL_USART_TXRX_STANDARD); | ||||||
|  |         furi_hal_console_init(); | ||||||
|  |         break; | ||||||
|  |     case DapUartTypeLPUART1: | ||||||
|  |         furi_hal_uart_deinit(FuriHalUartIdLPUART1); | ||||||
|  |         LL_LPUART_SetTXRXSwap(LPUART1, LL_LPUART_TXRX_STANDARD); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static int32_t cdc_process(void* p) { | ||||||
|  |     DapApp* dap_app = p; | ||||||
|  |     DapState* dap_state = &(dap_app->state); | ||||||
|  | 
 | ||||||
|  |     dap_app->config.uart_pins = DapUartTypeLPUART1; | ||||||
|  |     dap_app->config.uart_swap = DapUartTXRXNormal; | ||||||
|  | 
 | ||||||
|  |     DapUartType uart_pins_prev = dap_app->config.uart_pins; | ||||||
|  |     DapUartTXRX uart_swap_prev = dap_app->config.uart_swap; | ||||||
|  | 
 | ||||||
|  |     CDCProcess* app = malloc(sizeof(CDCProcess)); | ||||||
|  |     app->thread_id = furi_thread_get_id(furi_thread_get_current()); | ||||||
|  |     app->rx_stream = furi_stream_buffer_alloc(512, 1); | ||||||
|  | 
 | ||||||
|  |     const uint8_t rx_buffer_size = 64; | ||||||
|  |     uint8_t* rx_buffer = malloc(rx_buffer_size); | ||||||
|  | 
 | ||||||
|  |     app->uart_id = cdc_init_uart( | ||||||
|  |         uart_pins_prev, uart_swap_prev, dap_state->cdc_baudrate, cdc_uart_irq_cb, app); | ||||||
|  | 
 | ||||||
|  |     dap_cdc_usb_set_context(app); | ||||||
|  |     dap_cdc_usb_set_rx_callback(cdc_usb_rx_callback); | ||||||
|  |     dap_cdc_usb_set_control_line_callback(cdc_usb_control_line_callback); | ||||||
|  |     dap_cdc_usb_set_config_callback(cdc_usb_config_callback); | ||||||
|  | 
 | ||||||
|  |     uint32_t events; | ||||||
|  |     while(1) { | ||||||
|  |         events = furi_thread_flags_wait(CDCThreadEventAll, FuriFlagWaitAny, FuriWaitForever); | ||||||
|  | 
 | ||||||
|  |         if(!(events & FuriFlagError)) { | ||||||
|  |             if(events & CDCThreadEventCDCConfig) { | ||||||
|  |                 if(dap_state->cdc_baudrate != app->line_coding.dwDTERate) { | ||||||
|  |                     dap_state->cdc_baudrate = app->line_coding.dwDTERate; | ||||||
|  |                     if(dap_state->cdc_baudrate > 0) { | ||||||
|  |                         furi_hal_uart_set_br(app->uart_id, dap_state->cdc_baudrate); | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & CDCThreadEventUARTRx) { | ||||||
|  |                 size_t len = | ||||||
|  |                     furi_stream_buffer_receive(app->rx_stream, rx_buffer, rx_buffer_size, 0); | ||||||
|  | 
 | ||||||
|  |                 if(len > 0) { | ||||||
|  |                     dap_cdc_usb_tx(rx_buffer, len); | ||||||
|  |                 } | ||||||
|  |                 dap_state->cdc_rx_counter += len; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & CDCThreadEventCDCRx) { | ||||||
|  |                 size_t len = dap_cdc_usb_rx(rx_buffer, rx_buffer_size); | ||||||
|  |                 if(len > 0) { | ||||||
|  |                     furi_hal_uart_tx(app->uart_id, rx_buffer, len); | ||||||
|  |                 } | ||||||
|  |                 dap_state->cdc_tx_counter += len; | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & CDCThreadEventApplyConfig) { | ||||||
|  |                 if(uart_pins_prev != dap_app->config.uart_pins || | ||||||
|  |                    uart_swap_prev != dap_app->config.uart_swap) { | ||||||
|  |                     cdc_deinit_uart(uart_pins_prev); | ||||||
|  |                     uart_pins_prev = dap_app->config.uart_pins; | ||||||
|  |                     uart_swap_prev = dap_app->config.uart_swap; | ||||||
|  |                     app->uart_id = cdc_init_uart( | ||||||
|  |                         uart_pins_prev, | ||||||
|  |                         uart_swap_prev, | ||||||
|  |                         dap_state->cdc_baudrate, | ||||||
|  |                         cdc_uart_irq_cb, | ||||||
|  |                         app); | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             if(events & CDCThreadEventStop) { | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     cdc_deinit_uart(uart_pins_prev); | ||||||
|  |     free(rx_buffer); | ||||||
|  |     furi_stream_buffer_free(app->rx_stream); | ||||||
|  |     free(app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /***************************************************************************/ | ||||||
|  | /******************************* MAIN APP **********************************/ | ||||||
|  | /***************************************************************************/ | ||||||
|  | 
 | ||||||
|  | static FuriThread* furi_thread_alloc_ex( | ||||||
|  |     const char* name, | ||||||
|  |     uint32_t stack_size, | ||||||
|  |     FuriThreadCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     FuriThread* thread = furi_thread_alloc(); | ||||||
|  |     furi_thread_set_name(thread, name); | ||||||
|  |     furi_thread_set_stack_size(thread, stack_size); | ||||||
|  |     furi_thread_set_callback(thread, callback); | ||||||
|  |     furi_thread_set_context(thread, context); | ||||||
|  |     return thread; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static DapApp* dap_app_alloc() { | ||||||
|  |     DapApp* dap_app = malloc(sizeof(DapApp)); | ||||||
|  |     dap_app->dap_thread = furi_thread_alloc_ex("DAP Process", 1024, dap_process, dap_app); | ||||||
|  |     dap_app->cdc_thread = furi_thread_alloc_ex("DAP CDC", 1024, cdc_process, dap_app); | ||||||
|  |     dap_app->gui_thread = furi_thread_alloc_ex("DAP GUI", 1024, dap_gui_thread, dap_app); | ||||||
|  |     return dap_app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_app_free(DapApp* dap_app) { | ||||||
|  |     furi_assert(dap_app); | ||||||
|  |     furi_thread_free(dap_app->dap_thread); | ||||||
|  |     furi_thread_free(dap_app->cdc_thread); | ||||||
|  |     furi_thread_free(dap_app->gui_thread); | ||||||
|  |     free(dap_app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static DapApp* app_handle = NULL; | ||||||
|  | 
 | ||||||
|  | void dap_app_disconnect() { | ||||||
|  |     app_handle->state.dap_mode = DapModeDisconnected; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_app_connect_swd() { | ||||||
|  |     app_handle->state.dap_mode = DapModeSWD; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_app_connect_jtag() { | ||||||
|  |     app_handle->state.dap_mode = DapModeJTAG; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_app_set_config(DapApp* app, DapConfig* config) { | ||||||
|  |     app->config = *config; | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(app->dap_thread), DAPThreadEventApplyConfig); | ||||||
|  |     furi_thread_flags_set(furi_thread_get_id(app->cdc_thread), CDCThreadEventApplyConfig); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DapConfig* dap_app_get_config(DapApp* app) { | ||||||
|  |     return &app->config; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t dap_link_app(void* p) { | ||||||
|  |     UNUSED(p); | ||||||
|  | 
 | ||||||
|  |     // alloc app
 | ||||||
|  |     DapApp* app = dap_app_alloc(); | ||||||
|  |     app_handle = app; | ||||||
|  | 
 | ||||||
|  |     furi_thread_start(app->dap_thread); | ||||||
|  |     furi_thread_start(app->cdc_thread); | ||||||
|  |     furi_thread_start(app->gui_thread); | ||||||
|  | 
 | ||||||
|  |     // wait until gui thread is finished
 | ||||||
|  |     furi_thread_join(app->gui_thread); | ||||||
|  | 
 | ||||||
|  |     // send stop event to threads
 | ||||||
|  |     dap_thread_send_stop(app->dap_thread); | ||||||
|  |     dap_thread_send_stop(app->cdc_thread); | ||||||
|  | 
 | ||||||
|  |     // wait for threads to stop
 | ||||||
|  |     furi_thread_join(app->dap_thread); | ||||||
|  |     furi_thread_join(app->cdc_thread); | ||||||
|  | 
 | ||||||
|  |     // free app
 | ||||||
|  |     dap_app_free(app); | ||||||
|  | 
 | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								applications/plugins/dap_link/dap_link.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/plugins/dap_link/dap_link.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapModeDisconnected, | ||||||
|  |     DapModeSWD, | ||||||
|  |     DapModeJTAG, | ||||||
|  | } DapMode; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapVersionUnknown, | ||||||
|  |     DapVersionV1, | ||||||
|  |     DapVersionV2, | ||||||
|  | } DapVersion; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     bool usb_connected; | ||||||
|  |     DapMode dap_mode; | ||||||
|  |     DapVersion dap_version; | ||||||
|  |     uint32_t dap_counter; | ||||||
|  |     uint32_t cdc_baudrate; | ||||||
|  |     uint32_t cdc_tx_counter; | ||||||
|  |     uint32_t cdc_rx_counter; | ||||||
|  | } DapState; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapSwdPinsPA7PA6, // Pins 2, 3
 | ||||||
|  |     DapSwdPinsPA14PA13, // Pins 10, 12
 | ||||||
|  | } DapSwdPins; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapUartTypeUSART1, // Pins 13, 14
 | ||||||
|  |     DapUartTypeLPUART1, // Pins 15, 16
 | ||||||
|  | } DapUartType; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapUartTXRXNormal, | ||||||
|  |     DapUartTXRXSwap, | ||||||
|  | } DapUartTXRX; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     DapSwdPins swd_pins; | ||||||
|  |     DapUartType uart_pins; | ||||||
|  |     DapUartTXRX uart_swap; | ||||||
|  | } DapConfig; | ||||||
|  | 
 | ||||||
|  | typedef struct DapApp DapApp; | ||||||
|  | 
 | ||||||
|  | void dap_app_get_state(DapApp* app, DapState* state); | ||||||
|  | 
 | ||||||
|  | const char* dap_app_get_serial(DapApp* app); | ||||||
|  | 
 | ||||||
|  | void dap_app_set_config(DapApp* app, DapConfig* config); | ||||||
|  | 
 | ||||||
|  | DapConfig* dap_app_get_config(DapApp* app); | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/dap_link.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/dap_link.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 143 B | 
							
								
								
									
										92
									
								
								applications/plugins/dap_link/gui/dap_gui.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								applications/plugins/dap_link/gui/dap_gui.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,92 @@ | |||||||
|  | #include "dap_gui.h" | ||||||
|  | #include "dap_gui_i.h" | ||||||
|  | 
 | ||||||
|  | #define DAP_GUI_TICK 250 | ||||||
|  | 
 | ||||||
|  | static bool dap_gui_custom_event_callback(void* context, uint32_t event) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     return scene_manager_handle_custom_event(app->scene_manager, event); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool dap_gui_back_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     return scene_manager_handle_back_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_gui_tick_event_callback(void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     scene_manager_handle_tick_event(app->scene_manager); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DapGuiApp* dap_gui_alloc() { | ||||||
|  |     DapGuiApp* app = malloc(sizeof(DapGuiApp)); | ||||||
|  |     app->gui = furi_record_open(RECORD_GUI); | ||||||
|  |     app->view_dispatcher = view_dispatcher_alloc(); | ||||||
|  |     app->scene_manager = scene_manager_alloc(&dap_scene_handlers, app); | ||||||
|  |     view_dispatcher_enable_queue(app->view_dispatcher); | ||||||
|  |     view_dispatcher_set_event_callback_context(app->view_dispatcher, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_set_custom_event_callback(app->view_dispatcher, dap_gui_custom_event_callback); | ||||||
|  |     view_dispatcher_set_navigation_event_callback( | ||||||
|  |         app->view_dispatcher, dap_gui_back_event_callback); | ||||||
|  |     view_dispatcher_set_tick_event_callback( | ||||||
|  |         app->view_dispatcher, dap_gui_tick_event_callback, DAP_GUI_TICK); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_attach_to_gui(app->view_dispatcher, app->gui, ViewDispatcherTypeFullscreen); | ||||||
|  | 
 | ||||||
|  |     app->notifications = furi_record_open(RECORD_NOTIFICATION); | ||||||
|  | 
 | ||||||
|  |     app->var_item_list = variable_item_list_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, | ||||||
|  |         DapGuiAppViewVarItemList, | ||||||
|  |         variable_item_list_get_view(app->var_item_list)); | ||||||
|  | 
 | ||||||
|  |     app->main_view = dap_main_view_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, DapGuiAppViewMainView, dap_main_view_get_view(app->main_view)); | ||||||
|  | 
 | ||||||
|  |     app->widget = widget_alloc(); | ||||||
|  |     view_dispatcher_add_view( | ||||||
|  |         app->view_dispatcher, DapGuiAppViewWidget, widget_get_view(app->widget)); | ||||||
|  | 
 | ||||||
|  |     scene_manager_next_scene(app->scene_manager, DapSceneMain); | ||||||
|  | 
 | ||||||
|  |     return app; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_gui_free(DapGuiApp* app) { | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewVarItemList); | ||||||
|  |     variable_item_list_free(app->var_item_list); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewMainView); | ||||||
|  |     dap_main_view_free(app->main_view); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_remove_view(app->view_dispatcher, DapGuiAppViewWidget); | ||||||
|  |     widget_free(app->widget); | ||||||
|  | 
 | ||||||
|  |     // View dispatcher
 | ||||||
|  |     view_dispatcher_free(app->view_dispatcher); | ||||||
|  |     scene_manager_free(app->scene_manager); | ||||||
|  | 
 | ||||||
|  |     // Close records
 | ||||||
|  |     furi_record_close(RECORD_GUI); | ||||||
|  |     furi_record_close(RECORD_NOTIFICATION); | ||||||
|  | 
 | ||||||
|  |     free(app); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t dap_gui_thread(void* arg) { | ||||||
|  |     DapGuiApp* app = dap_gui_alloc(); | ||||||
|  |     app->dap_app = arg; | ||||||
|  | 
 | ||||||
|  |     notification_message_block(app->notifications, &sequence_display_backlight_enforce_on); | ||||||
|  |     view_dispatcher_run(app->view_dispatcher); | ||||||
|  |     notification_message_block(app->notifications, &sequence_display_backlight_enforce_auto); | ||||||
|  | 
 | ||||||
|  |     dap_gui_free(app); | ||||||
|  |     return 0; | ||||||
|  | } | ||||||
							
								
								
									
										4
									
								
								applications/plugins/dap_link/gui/dap_gui.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								applications/plugins/dap_link/gui/dap_gui.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,4 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | int32_t dap_gui_thread(void* arg); | ||||||
							
								
								
									
										7
									
								
								applications/plugins/dap_link/gui/dap_gui_custom_event.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								applications/plugins/dap_link/gui/dap_gui_custom_event.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapAppCustomEventConfig, | ||||||
|  |     DapAppCustomEventHelp, | ||||||
|  |     DapAppCustomEventAbout, | ||||||
|  | } DapAppCustomEvent; | ||||||
							
								
								
									
										34
									
								
								applications/plugins/dap_link/gui/dap_gui_i.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								applications/plugins/dap_link/gui/dap_gui_i.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/gui.h> | ||||||
|  | #include <gui/view_dispatcher.h> | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | #include <gui/modules/submenu.h> | ||||||
|  | #include <notification/notification_messages.h> | ||||||
|  | #include <gui/modules/variable_item_list.h> | ||||||
|  | #include <gui/modules/widget.h> | ||||||
|  | 
 | ||||||
|  | #include "dap_gui.h" | ||||||
|  | #include "../dap_link.h" | ||||||
|  | #include "scenes/config/dap_scene.h" | ||||||
|  | #include "dap_gui_custom_event.h" | ||||||
|  | #include "views/dap_main_view.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     DapApp* dap_app; | ||||||
|  | 
 | ||||||
|  |     Gui* gui; | ||||||
|  |     NotificationApp* notifications; | ||||||
|  |     ViewDispatcher* view_dispatcher; | ||||||
|  |     SceneManager* scene_manager; | ||||||
|  | 
 | ||||||
|  |     VariableItemList* var_item_list; | ||||||
|  |     DapMainView* main_view; | ||||||
|  |     Widget* widget; | ||||||
|  | } DapGuiApp; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapGuiAppViewVarItemList, | ||||||
|  |     DapGuiAppViewMainView, | ||||||
|  |     DapGuiAppViewWidget, | ||||||
|  | } DapGuiAppView; | ||||||
							
								
								
									
										30
									
								
								applications/plugins/dap_link/gui/scenes/config/dap_scene.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								applications/plugins/dap_link/gui/scenes/config/dap_scene.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,30 @@ | |||||||
|  | #include "dap_scene.h" | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_enter, | ||||||
|  | void (*const dap_scene_on_enter_handlers[])(void*) = { | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_event, | ||||||
|  | bool (*const dap_scene_on_event_handlers[])(void* context, SceneManagerEvent event) = { | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers array
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) prefix##_scene_##name##_on_exit, | ||||||
|  | void (*const dap_scene_on_exit_handlers[])(void* context) = { | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | }; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Initialize scene handlers configuration structure
 | ||||||
|  | const SceneManagerHandlers dap_scene_handlers = { | ||||||
|  |     .on_enter_handlers = dap_scene_on_enter_handlers, | ||||||
|  |     .on_event_handlers = dap_scene_on_event_handlers, | ||||||
|  |     .on_exit_handlers = dap_scene_on_exit_handlers, | ||||||
|  |     .scene_num = DapSceneNum, | ||||||
|  | }; | ||||||
							
								
								
									
										29
									
								
								applications/plugins/dap_link/gui/scenes/config/dap_scene.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								applications/plugins/dap_link/gui/scenes/config/dap_scene.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,29 @@ | |||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <gui/scene_manager.h> | ||||||
|  | 
 | ||||||
|  | // Generate scene id and total number
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) DapScene##id, | ||||||
|  | typedef enum { | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  |     DapSceneNum, | ||||||
|  | } DapScene; | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | extern const SceneManagerHandlers dap_scene_handlers; | ||||||
|  | 
 | ||||||
|  | // Generate scene on_enter handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_enter(void*); | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_event handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) \ | ||||||
|  |     bool prefix##_scene_##name##_on_event(void* context, SceneManagerEvent event); | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
|  | 
 | ||||||
|  | // Generate scene on_exit handlers declaration
 | ||||||
|  | #define ADD_SCENE(prefix, name, id) void prefix##_scene_##name##_on_exit(void* context); | ||||||
|  | #include "dap_scene_config.h" | ||||||
|  | #undef ADD_SCENE | ||||||
| @ -0,0 +1,4 @@ | |||||||
|  | ADD_SCENE(dap, main, Main) | ||||||
|  | ADD_SCENE(dap, config, Config) | ||||||
|  | ADD_SCENE(dap, help, Help) | ||||||
|  | ADD_SCENE(dap, about, About) | ||||||
							
								
								
									
										68
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_about.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_about.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,68 @@ | |||||||
|  | #include "../dap_gui_i.h" | ||||||
|  | 
 | ||||||
|  | #define DAP_VERSION_APP "0.1.0" | ||||||
|  | #define DAP_DEVELOPED "Dr_Zlo" | ||||||
|  | #define DAP_GITHUB "https://github.com/flipperdevices/flipperzero-firmware"
 | ||||||
|  | 
 | ||||||
|  | void dap_scene_about_on_enter(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  | 
 | ||||||
|  |     FuriString* temp_str; | ||||||
|  |     temp_str = furi_string_alloc(); | ||||||
|  |     furi_string_printf(temp_str, "\e#%s\n", "Information"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "Version: %s\n", DAP_VERSION_APP); | ||||||
|  |     furi_string_cat_printf(temp_str, "Developed by: %s\n", DAP_DEVELOPED); | ||||||
|  |     furi_string_cat_printf(temp_str, "Github: %s\n\n", DAP_GITHUB); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf(temp_str, "\e#%s\n", "Description"); | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, "CMSIS-DAP debugger\nbased on Free-DAP\nThanks to Alex Taradov\n\n"); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat_printf( | ||||||
|  |         temp_str, | ||||||
|  |         "Supported protocols:\n" | ||||||
|  |         "SWD, JTAG, UART\n" | ||||||
|  |         "DAP v1 (cmsis_backend hid), DAP v2 (cmsis_backend usb_bulk), VCP\n"); | ||||||
|  | 
 | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         app->widget, | ||||||
|  |         0, | ||||||
|  |         0, | ||||||
|  |         128, | ||||||
|  |         14, | ||||||
|  |         AlignCenter, | ||||||
|  |         AlignBottom, | ||||||
|  |         "\e#\e!                                                      \e!\n", | ||||||
|  |         false); | ||||||
|  |     widget_add_text_box_element( | ||||||
|  |         app->widget, | ||||||
|  |         0, | ||||||
|  |         2, | ||||||
|  |         128, | ||||||
|  |         14, | ||||||
|  |         AlignCenter, | ||||||
|  |         AlignBottom, | ||||||
|  |         "\e#\e!              DAP Link              \e!\n", | ||||||
|  |         false); | ||||||
|  |     widget_add_text_scroll_element(app->widget, 0, 16, 128, 50, furi_string_get_cstr(temp_str)); | ||||||
|  |     furi_string_free(temp_str); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool dap_scene_about_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     bool consumed = false; | ||||||
|  |     UNUSED(app); | ||||||
|  |     UNUSED(event); | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_about_on_exit(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  | 
 | ||||||
|  |     // Clear views
 | ||||||
|  |     widget_reset(app->widget); | ||||||
|  | } | ||||||
							
								
								
									
										107
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										107
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_config.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,107 @@ | |||||||
|  | #include "../dap_gui_i.h" | ||||||
|  | 
 | ||||||
|  | static const char* swd_pins[] = {[DapSwdPinsPA7PA6] = "2,3", [DapSwdPinsPA14PA13] = "10,12"}; | ||||||
|  | static const char* uart_pins[] = {[DapUartTypeUSART1] = "13,14", [DapUartTypeLPUART1] = "15,16"}; | ||||||
|  | static const char* uart_swap[] = {[DapUartTXRXNormal] = "No", [DapUartTXRXSwap] = "Yes"}; | ||||||
|  | 
 | ||||||
|  | static void swd_pins_cb(VariableItem* item) { | ||||||
|  |     DapGuiApp* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, swd_pins[index]); | ||||||
|  | 
 | ||||||
|  |     DapConfig* config = dap_app_get_config(app->dap_app); | ||||||
|  |     config->swd_pins = index; | ||||||
|  |     dap_app_set_config(app->dap_app, config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void uart_pins_cb(VariableItem* item) { | ||||||
|  |     DapGuiApp* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, uart_pins[index]); | ||||||
|  | 
 | ||||||
|  |     DapConfig* config = dap_app_get_config(app->dap_app); | ||||||
|  |     config->uart_pins = index; | ||||||
|  |     dap_app_set_config(app->dap_app, config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void uart_swap_cb(VariableItem* item) { | ||||||
|  |     DapGuiApp* app = variable_item_get_context(item); | ||||||
|  |     uint8_t index = variable_item_get_current_value_index(item); | ||||||
|  | 
 | ||||||
|  |     variable_item_set_current_value_text(item, uart_swap[index]); | ||||||
|  | 
 | ||||||
|  |     DapConfig* config = dap_app_get_config(app->dap_app); | ||||||
|  |     config->uart_swap = index; | ||||||
|  |     dap_app_set_config(app->dap_app, config); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void ok_cb(void* context, uint32_t index) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     switch(index) { | ||||||
|  |     case 3: | ||||||
|  |         view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventHelp); | ||||||
|  |         break; | ||||||
|  |     case 4: | ||||||
|  |         view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventAbout); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_config_on_enter(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     VariableItemList* var_item_list = app->var_item_list; | ||||||
|  |     VariableItem* item; | ||||||
|  |     DapConfig* config = dap_app_get_config(app->dap_app); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         var_item_list, "SWC SWD Pins", COUNT_OF(swd_pins), swd_pins_cb, app); | ||||||
|  |     variable_item_set_current_value_index(item, config->swd_pins); | ||||||
|  |     variable_item_set_current_value_text(item, swd_pins[config->swd_pins]); | ||||||
|  | 
 | ||||||
|  |     item = | ||||||
|  |         variable_item_list_add(var_item_list, "UART Pins", COUNT_OF(uart_pins), uart_pins_cb, app); | ||||||
|  |     variable_item_set_current_value_index(item, config->uart_pins); | ||||||
|  |     variable_item_set_current_value_text(item, uart_pins[config->uart_pins]); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add( | ||||||
|  |         var_item_list, "Swap TX RX", COUNT_OF(uart_swap), uart_swap_cb, app); | ||||||
|  |     variable_item_set_current_value_index(item, config->uart_swap); | ||||||
|  |     variable_item_set_current_value_text(item, uart_swap[config->uart_swap]); | ||||||
|  | 
 | ||||||
|  |     item = variable_item_list_add(var_item_list, "Help and Pinout", 0, NULL, NULL); | ||||||
|  |     item = variable_item_list_add(var_item_list, "About", 0, NULL, NULL); | ||||||
|  | 
 | ||||||
|  |     variable_item_list_set_selected_item( | ||||||
|  |         var_item_list, scene_manager_get_scene_state(app->scene_manager, DapSceneConfig)); | ||||||
|  | 
 | ||||||
|  |     variable_item_list_set_enter_callback(var_item_list, ok_cb, app); | ||||||
|  | 
 | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewVarItemList); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool dap_scene_config_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == DapAppCustomEventHelp) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DapSceneHelp); | ||||||
|  |             return true; | ||||||
|  |         } else if(event.event == DapAppCustomEventAbout) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DapSceneAbout); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_config_on_exit(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     scene_manager_set_scene_state( | ||||||
|  |         app->scene_manager, | ||||||
|  |         DapSceneConfig, | ||||||
|  |         variable_item_list_get_selected_item_index(app->var_item_list)); | ||||||
|  |     variable_item_list_reset(app->var_item_list); | ||||||
|  | } | ||||||
							
								
								
									
										102
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_help.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_help.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,102 @@ | |||||||
|  | #include "../dap_gui_i.h" | ||||||
|  | 
 | ||||||
|  | void dap_scene_help_on_enter(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     DapConfig* config = dap_app_get_config(app->dap_app); | ||||||
|  |     FuriString* string = furi_string_alloc(); | ||||||
|  | 
 | ||||||
|  |     furi_string_cat(string, "CMSIS DAP/DAP Link v2\r\n"); | ||||||
|  |     furi_string_cat_printf(string, "Serial: %s\r\n", dap_app_get_serial(app->dap_app)); | ||||||
|  |     furi_string_cat( | ||||||
|  |         string, | ||||||
|  |         "Pinout:\r\n" | ||||||
|  |         "\e#SWD:\r\n"); | ||||||
|  | 
 | ||||||
|  |     switch(config->swd_pins) { | ||||||
|  |     case DapSwdPinsPA7PA6: | ||||||
|  |         furi_string_cat( | ||||||
|  |             string, | ||||||
|  |             "    SWC: 2 [A7]\r\n" | ||||||
|  |             "    SWD: 3 [A6]\r\n"); | ||||||
|  |         break; | ||||||
|  |     case DapSwdPinsPA14PA13: | ||||||
|  |         furi_string_cat( | ||||||
|  |             string, | ||||||
|  |             "    SWC: 10 [SWC]\r\n" | ||||||
|  |             "    SWD: 12 [SIO]\r\n"); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_cat(string, "\e#JTAG:\r\n"); | ||||||
|  |     switch(config->swd_pins) { | ||||||
|  |     case DapSwdPinsPA7PA6: | ||||||
|  |         furi_string_cat( | ||||||
|  |             string, | ||||||
|  |             "    TCK: 2 [A7]\r\n" | ||||||
|  |             "    TMS: 3 [A6]\r\n" | ||||||
|  |             "    RST: 4 [A4]\r\n" | ||||||
|  |             "    TDO: 5 [B3]\r\n" | ||||||
|  |             "    TDI: 6 [B2]\r\n"); | ||||||
|  |         break; | ||||||
|  |     case DapSwdPinsPA14PA13: | ||||||
|  |         furi_string_cat( | ||||||
|  |             string, | ||||||
|  |             "    RST: 4 [A4]\r\n" | ||||||
|  |             "    TDO: 5 [B3]\r\n" | ||||||
|  |             "    TDI: 6 [B2]\r\n" | ||||||
|  |             "    TCK: 10 [SWC]\r\n" | ||||||
|  |             "    TMS: 12 [SIO]\r\n"); | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_string_cat(string, "\e#UART:\r\n"); | ||||||
|  |     switch(config->uart_pins) { | ||||||
|  |     case DapUartTypeUSART1: | ||||||
|  |         if(config->uart_swap == DapUartTXRXNormal) { | ||||||
|  |             furi_string_cat( | ||||||
|  |                 string, | ||||||
|  |                 "    TX: 13 [TX]\r\n" | ||||||
|  |                 "    RX: 14 [RX]\r\n"); | ||||||
|  |         } else { | ||||||
|  |             furi_string_cat( | ||||||
|  |                 string, | ||||||
|  |                 "    RX: 13 [TX]\r\n" | ||||||
|  |                 "    TX: 14 [RX]\r\n"); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     case DapUartTypeLPUART1: | ||||||
|  |         if(config->uart_swap == DapUartTXRXNormal) { | ||||||
|  |             furi_string_cat( | ||||||
|  |                 string, | ||||||
|  |                 "    TX: 15 [С1]\r\n" | ||||||
|  |                 "    RX: 16 [С0]\r\n"); | ||||||
|  |         } else { | ||||||
|  |             furi_string_cat( | ||||||
|  |                 string, | ||||||
|  |                 "    RX: 15 [С1]\r\n" | ||||||
|  |                 "    TX: 16 [С0]\r\n"); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     widget_add_text_scroll_element(app->widget, 0, 0, 128, 64, furi_string_get_cstr(string)); | ||||||
|  |     furi_string_free(string); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewWidget); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool dap_scene_help_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     UNUSED(context); | ||||||
|  |     UNUSED(event); | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_help_on_exit(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     widget_reset(app->widget); | ||||||
|  | } | ||||||
							
								
								
									
										154
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_main.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								applications/plugins/dap_link/gui/scenes/dap_scene_main.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,154 @@ | |||||||
|  | #include "../dap_gui_i.h" | ||||||
|  | #include "../../dap_link.h" | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     DapState dap_state; | ||||||
|  |     bool dap_active; | ||||||
|  |     bool tx_active; | ||||||
|  |     bool rx_active; | ||||||
|  | } DapSceneMainState; | ||||||
|  | 
 | ||||||
|  | static bool process_dap_state(DapGuiApp* app) { | ||||||
|  |     DapSceneMainState* state = | ||||||
|  |         (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); | ||||||
|  |     if(state == NULL) return true; | ||||||
|  | 
 | ||||||
|  |     DapState* prev_state = &state->dap_state; | ||||||
|  |     DapState next_state; | ||||||
|  |     dap_app_get_state(app->dap_app, &next_state); | ||||||
|  |     bool need_to_update = false; | ||||||
|  | 
 | ||||||
|  |     if(prev_state->dap_mode != next_state.dap_mode) { | ||||||
|  |         switch(next_state.dap_mode) { | ||||||
|  |         case DapModeDisconnected: | ||||||
|  |             dap_main_view_set_mode(app->main_view, DapMainViewModeDisconnected); | ||||||
|  |             notification_message(app->notifications, &sequence_blink_stop); | ||||||
|  |             break; | ||||||
|  |         case DapModeSWD: | ||||||
|  |             dap_main_view_set_mode(app->main_view, DapMainViewModeSWD); | ||||||
|  |             notification_message(app->notifications, &sequence_blink_start_blue); | ||||||
|  |             break; | ||||||
|  |         case DapModeJTAG: | ||||||
|  |             dap_main_view_set_mode(app->main_view, DapMainViewModeJTAG); | ||||||
|  |             notification_message(app->notifications, &sequence_blink_start_magenta); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         need_to_update = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->dap_version != next_state.dap_version) { | ||||||
|  |         switch(next_state.dap_version) { | ||||||
|  |         case DapVersionUnknown: | ||||||
|  |             dap_main_view_set_version(app->main_view, DapMainViewVersionUnknown); | ||||||
|  |             break; | ||||||
|  |         case DapVersionV1: | ||||||
|  |             dap_main_view_set_version(app->main_view, DapMainViewVersionV1); | ||||||
|  |             break; | ||||||
|  |         case DapVersionV2: | ||||||
|  |             dap_main_view_set_version(app->main_view, DapMainViewVersionV2); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |         need_to_update = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->usb_connected != next_state.usb_connected) { | ||||||
|  |         dap_main_view_set_usb_connected(app->main_view, next_state.usb_connected); | ||||||
|  |         need_to_update = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->dap_counter != next_state.dap_counter) { | ||||||
|  |         if(!state->dap_active) { | ||||||
|  |             state->dap_active = true; | ||||||
|  |             dap_main_view_set_dap(app->main_view, state->dap_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(state->dap_active) { | ||||||
|  |             state->dap_active = false; | ||||||
|  |             dap_main_view_set_dap(app->main_view, state->dap_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->cdc_baudrate != next_state.cdc_baudrate) { | ||||||
|  |         dap_main_view_set_baudrate(app->main_view, next_state.cdc_baudrate); | ||||||
|  |         need_to_update = true; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->cdc_tx_counter != next_state.cdc_tx_counter) { | ||||||
|  |         if(!state->tx_active) { | ||||||
|  |             state->tx_active = true; | ||||||
|  |             dap_main_view_set_tx(app->main_view, state->tx_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |             notification_message(app->notifications, &sequence_blink_start_red); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(state->tx_active) { | ||||||
|  |             state->tx_active = false; | ||||||
|  |             dap_main_view_set_tx(app->main_view, state->tx_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |             notification_message(app->notifications, &sequence_blink_stop); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(prev_state->cdc_rx_counter != next_state.cdc_rx_counter) { | ||||||
|  |         if(!state->rx_active) { | ||||||
|  |             state->rx_active = true; | ||||||
|  |             dap_main_view_set_rx(app->main_view, state->rx_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |             notification_message(app->notifications, &sequence_blink_start_green); | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         if(state->rx_active) { | ||||||
|  |             state->rx_active = false; | ||||||
|  |             dap_main_view_set_rx(app->main_view, state->rx_active); | ||||||
|  |             need_to_update = true; | ||||||
|  |             notification_message(app->notifications, &sequence_blink_stop); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(need_to_update) { | ||||||
|  |         dap_main_view_update(app->main_view); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     *prev_state = next_state; | ||||||
|  |     return true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void dap_scene_main_on_left(void* context) { | ||||||
|  |     DapGuiApp* app = (DapGuiApp*)context; | ||||||
|  |     view_dispatcher_send_custom_event(app->view_dispatcher, DapAppCustomEventConfig); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_main_on_enter(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     DapSceneMainState* state = malloc(sizeof(DapSceneMainState)); | ||||||
|  |     dap_main_view_set_left_callback(app->main_view, dap_scene_main_on_left, app); | ||||||
|  |     view_dispatcher_switch_to_view(app->view_dispatcher, DapGuiAppViewMainView); | ||||||
|  |     scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)state); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | bool dap_scene_main_on_event(void* context, SceneManagerEvent event) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  | 
 | ||||||
|  |     if(event.type == SceneManagerEventTypeCustom) { | ||||||
|  |         if(event.event == DapAppCustomEventConfig) { | ||||||
|  |             scene_manager_next_scene(app->scene_manager, DapSceneConfig); | ||||||
|  |             return true; | ||||||
|  |         } | ||||||
|  |     } else if(event.type == SceneManagerEventTypeTick) { | ||||||
|  |         return process_dap_state(app); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return false; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_scene_main_on_exit(void* context) { | ||||||
|  |     DapGuiApp* app = context; | ||||||
|  |     DapSceneMainState* state = | ||||||
|  |         (DapSceneMainState*)scene_manager_get_scene_state(app->scene_manager, DapSceneMain); | ||||||
|  |     scene_manager_set_scene_state(app->scene_manager, DapSceneMain, (uint32_t)NULL); | ||||||
|  |     FURI_SW_MEMBARRIER(); | ||||||
|  |     free(state); | ||||||
|  |     notification_message(app->notifications, &sequence_blink_stop); | ||||||
|  | } | ||||||
							
								
								
									
										189
									
								
								applications/plugins/dap_link/gui/views/dap_main_view.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										189
									
								
								applications/plugins/dap_link/gui/views/dap_main_view.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,189 @@ | |||||||
|  | #include "dap_main_view.h" | ||||||
|  | #include "dap_link_icons.h" | ||||||
|  | #include <gui/elements.h> | ||||||
|  | 
 | ||||||
|  | // extern const Icon I_ArrowDownEmpty_12x18;
 | ||||||
|  | // extern const Icon I_ArrowDownFilled_12x18;
 | ||||||
|  | // extern const Icon I_ArrowUpEmpty_12x18;
 | ||||||
|  | // extern const Icon I_ArrowUpFilled_12x18;
 | ||||||
|  | 
 | ||||||
|  | struct DapMainView { | ||||||
|  |     View* view; | ||||||
|  |     DapMainViewButtonCallback cb_left; | ||||||
|  |     void* cb_context; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     DapMainViewMode mode; | ||||||
|  |     DapMainViewVersion version; | ||||||
|  |     bool usb_connected; | ||||||
|  |     uint32_t baudrate; | ||||||
|  |     bool dap_active; | ||||||
|  |     bool tx_active; | ||||||
|  |     bool rx_active; | ||||||
|  | } DapMainViewModel; | ||||||
|  | 
 | ||||||
|  | static void dap_main_view_draw_callback(Canvas* canvas, void* _model) { | ||||||
|  |     DapMainViewModel* model = _model; | ||||||
|  |     UNUSED(model); | ||||||
|  |     canvas_clear(canvas); | ||||||
|  |     elements_button_left(canvas, "Config"); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     canvas_draw_box(canvas, 0, 0, 127, 11); | ||||||
|  |     canvas_set_color(canvas, ColorWhite); | ||||||
|  | 
 | ||||||
|  |     const char* header_string; | ||||||
|  |     if(model->usb_connected) { | ||||||
|  |         if(model->version == DapMainViewVersionV1) { | ||||||
|  |             header_string = "DAP Link V1 Connected"; | ||||||
|  |         } else if(model->version == DapMainViewVersionV2) { | ||||||
|  |             header_string = "DAP Link V2 Connected"; | ||||||
|  |         } else { | ||||||
|  |             header_string = "DAP Link Connected"; | ||||||
|  |         } | ||||||
|  |     } else { | ||||||
|  |         header_string = "DAP Link"; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str_aligned(canvas, 64, 9, AlignCenter, AlignBottom, header_string); | ||||||
|  | 
 | ||||||
|  |     canvas_set_color(canvas, ColorBlack); | ||||||
|  |     if(model->dap_active) { | ||||||
|  |         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpFilled_12x18); | ||||||
|  |         canvas_draw_icon(canvas, 28, 16, &I_ArrowDownFilled_12x18); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 14, 16, &I_ArrowUpEmpty_12x18); | ||||||
|  |         canvas_draw_icon(canvas, 28, 16, &I_ArrowDownEmpty_12x18); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(model->mode) { | ||||||
|  |     case DapMainViewModeDisconnected: | ||||||
|  |         canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "----"); | ||||||
|  |         break; | ||||||
|  |     case DapMainViewModeSWD: | ||||||
|  |         canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "SWD"); | ||||||
|  |         break; | ||||||
|  |     case DapMainViewModeJTAG: | ||||||
|  |         canvas_draw_str_aligned(canvas, 26, 38, AlignCenter, AlignTop, "JTAG"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->tx_active) { | ||||||
|  |         canvas_draw_icon(canvas, 87, 16, &I_ArrowUpFilled_12x18); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 87, 16, &I_ArrowUpEmpty_12x18); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(model->rx_active) { | ||||||
|  |         canvas_draw_icon(canvas, 101, 16, &I_ArrowDownFilled_12x18); | ||||||
|  |     } else { | ||||||
|  |         canvas_draw_icon(canvas, 101, 16, &I_ArrowDownEmpty_12x18); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     canvas_draw_str_aligned(canvas, 100, 38, AlignCenter, AlignTop, "UART"); | ||||||
|  | 
 | ||||||
|  |     canvas_draw_line(canvas, 44, 52, 123, 52); | ||||||
|  |     if(model->baudrate == 0) { | ||||||
|  |         canvas_draw_str(canvas, 45, 62, "Baud: ????"); | ||||||
|  |     } else { | ||||||
|  |         char baudrate_str[18]; | ||||||
|  |         snprintf(baudrate_str, 18, "Baud: %lu", model->baudrate); | ||||||
|  |         canvas_draw_str(canvas, 45, 62, baudrate_str); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool dap_main_view_input_callback(InputEvent* event, void* context) { | ||||||
|  |     furi_assert(context); | ||||||
|  |     DapMainView* dap_main_view = context; | ||||||
|  |     bool consumed = false; | ||||||
|  | 
 | ||||||
|  |     if(event->type == InputTypeShort) { | ||||||
|  |         if(event->key == InputKeyLeft) { | ||||||
|  |             if(dap_main_view->cb_left) { | ||||||
|  |                 dap_main_view->cb_left(dap_main_view->cb_context); | ||||||
|  |             } | ||||||
|  |             consumed = true; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return consumed; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | DapMainView* dap_main_view_alloc() { | ||||||
|  |     DapMainView* dap_main_view = malloc(sizeof(DapMainView)); | ||||||
|  | 
 | ||||||
|  |     dap_main_view->view = view_alloc(); | ||||||
|  |     view_allocate_model(dap_main_view->view, ViewModelTypeLocking, sizeof(DapMainViewModel)); | ||||||
|  |     view_set_context(dap_main_view->view, dap_main_view); | ||||||
|  |     view_set_draw_callback(dap_main_view->view, dap_main_view_draw_callback); | ||||||
|  |     view_set_input_callback(dap_main_view->view, dap_main_view_input_callback); | ||||||
|  |     return dap_main_view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_free(DapMainView* dap_main_view) { | ||||||
|  |     view_free(dap_main_view->view); | ||||||
|  |     free(dap_main_view); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | View* dap_main_view_get_view(DapMainView* dap_main_view) { | ||||||
|  |     return dap_main_view->view; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_left_callback( | ||||||
|  |     DapMainView* dap_main_view, | ||||||
|  |     DapMainViewButtonCallback callback, | ||||||
|  |     void* context) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, | ||||||
|  |         DapMainViewModel * model, | ||||||
|  |         { | ||||||
|  |             UNUSED(model); | ||||||
|  |             dap_main_view->cb_left = callback; | ||||||
|  |             dap_main_view->cb_context = context; | ||||||
|  |         }, | ||||||
|  |         true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->mode = mode; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_dap(DapMainView* dap_main_view, bool active) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->dap_active = active; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_tx(DapMainView* dap_main_view, bool active) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->tx_active = active; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_rx(DapMainView* dap_main_view, bool active) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->rx_active = active; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->baudrate = baudrate; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_update(DapMainView* dap_main_view) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { UNUSED(model); }, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, DapMainViewModel * model, { model->version = version; }, false); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected) { | ||||||
|  |     with_view_model( | ||||||
|  |         dap_main_view->view, | ||||||
|  |         DapMainViewModel * model, | ||||||
|  |         { model->usb_connected = connected; }, | ||||||
|  |         false); | ||||||
|  | } | ||||||
							
								
								
									
										45
									
								
								applications/plugins/dap_link/gui/views/dap_main_view.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										45
									
								
								applications/plugins/dap_link/gui/views/dap_main_view.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,45 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <gui/view.h> | ||||||
|  | 
 | ||||||
|  | typedef struct DapMainView DapMainView; | ||||||
|  | 
 | ||||||
|  | typedef void (*DapMainViewButtonCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapMainViewVersionUnknown, | ||||||
|  |     DapMainViewVersionV1, | ||||||
|  |     DapMainViewVersionV2, | ||||||
|  | } DapMainViewVersion; | ||||||
|  | 
 | ||||||
|  | typedef enum { | ||||||
|  |     DapMainViewModeDisconnected, | ||||||
|  |     DapMainViewModeSWD, | ||||||
|  |     DapMainViewModeJTAG, | ||||||
|  | } DapMainViewMode; | ||||||
|  | 
 | ||||||
|  | DapMainView* dap_main_view_alloc(); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_free(DapMainView* dap_main_view); | ||||||
|  | 
 | ||||||
|  | View* dap_main_view_get_view(DapMainView* dap_main_view); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_left_callback( | ||||||
|  |     DapMainView* dap_main_view, | ||||||
|  |     DapMainViewButtonCallback callback, | ||||||
|  |     void* context); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_mode(DapMainView* dap_main_view, DapMainViewMode mode); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_version(DapMainView* dap_main_view, DapMainViewVersion version); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_dap(DapMainView* dap_main_view, bool active); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_tx(DapMainView* dap_main_view, bool active); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_rx(DapMainView* dap_main_view, bool active); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_usb_connected(DapMainView* dap_main_view, bool connected); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_set_baudrate(DapMainView* dap_main_view, uint32_t baudrate); | ||||||
|  | 
 | ||||||
|  | void dap_main_view_update(DapMainView* dap_main_view); | ||||||
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowDownEmpty_12x18.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 160 B | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowDownFilled_12x18.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 168 B | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowUpEmpty_12x18.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 159 B | 
							
								
								
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								applications/plugins/dap_link/icons/ArrowUpFilled_12x18.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 173 B | 
							
								
								
									
										1
									
								
								applications/plugins/dap_link/lib/free-dap
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
								
									
									
								
							
						
						
									
										1
									
								
								applications/plugins/dap_link/lib/free-dap
									
									
									
									
									
										Submodule
									
								
							| @ -0,0 +1 @@ | |||||||
|  | Subproject commit e7752beb5e8a69119af67b70b9179cb3c90f3ac5 | ||||||
							
								
								
									
										994
									
								
								applications/plugins/dap_link/usb/dap_v2_usb.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										994
									
								
								applications/plugins/dap_link/usb/dap_v2_usb.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,994 @@ | |||||||
|  | #include <furi.h> | ||||||
|  | #include <usb.h> | ||||||
|  | #include <usb_std.h> | ||||||
|  | #include <usb_hid.h> | ||||||
|  | #include <usb_cdc.h> | ||||||
|  | #include <furi_hal_console.h> | ||||||
|  | 
 | ||||||
|  | #include "dap_v2_usb.h" | ||||||
|  | 
 | ||||||
|  | // #define DAP_USB_LOG
 | ||||||
|  | 
 | ||||||
|  | #define HID_EP_IN 0x80 | ||||||
|  | #define HID_EP_OUT 0x00 | ||||||
|  | 
 | ||||||
|  | #define DAP_HID_EP_SEND 1 | ||||||
|  | #define DAP_HID_EP_RECV 2 | ||||||
|  | #define DAP_HID_EP_BULK_RECV 3 | ||||||
|  | #define DAP_HID_EP_BULK_SEND 4 | ||||||
|  | #define DAP_CDC_EP_COMM 5 | ||||||
|  | #define DAP_CDC_EP_SEND 6 | ||||||
|  | #define DAP_CDC_EP_RECV 7 | ||||||
|  | 
 | ||||||
|  | #define DAP_HID_EP_IN (HID_EP_IN | DAP_HID_EP_SEND) | ||||||
|  | #define DAP_HID_EP_OUT (HID_EP_OUT | DAP_HID_EP_RECV) | ||||||
|  | #define DAP_HID_EP_BULK_IN (HID_EP_IN | DAP_HID_EP_BULK_SEND) | ||||||
|  | #define DAP_HID_EP_BULK_OUT (HID_EP_OUT | DAP_HID_EP_BULK_RECV) | ||||||
|  | 
 | ||||||
|  | #define DAP_HID_EP_SIZE 64 | ||||||
|  | #define DAP_CDC_COMM_EP_SIZE 8 | ||||||
|  | #define DAP_CDC_EP_SIZE 64 | ||||||
|  | 
 | ||||||
|  | #define DAP_BULK_INTERVAL 0 | ||||||
|  | #define DAP_HID_INTERVAL 1 | ||||||
|  | #define DAP_CDC_INTERVAL 0 | ||||||
|  | #define DAP_CDC_COMM_INTERVAL 1 | ||||||
|  | 
 | ||||||
|  | #define DAP_HID_VID 0x0483 | ||||||
|  | #define DAP_HID_PID 0x5740 | ||||||
|  | 
 | ||||||
|  | #define DAP_USB_EP0_SIZE 8 | ||||||
|  | 
 | ||||||
|  | #define EP_CFG_DECONFIGURE 0 | ||||||
|  | #define EP_CFG_CONFIGURE 1 | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     USB_INTF_HID, | ||||||
|  |     USB_INTF_BULK, | ||||||
|  |     USB_INTF_CDC_COMM, | ||||||
|  |     USB_INTF_CDC_DATA, | ||||||
|  |     USB_INTF_COUNT, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum { | ||||||
|  |     USB_STR_ZERO, | ||||||
|  |     USB_STR_MANUFACTURER, | ||||||
|  |     USB_STR_PRODUCT, | ||||||
|  |     USB_STR_SERIAL_NUMBER, | ||||||
|  |     USB_STR_CMSIS_DAP_V1, | ||||||
|  |     USB_STR_CMSIS_DAP_V2, | ||||||
|  |     USB_STR_COM_PORT, | ||||||
|  |     USB_STR_COUNT, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // static const char* usb_str[] = {
 | ||||||
|  | //     [USB_STR_MANUFACTURER] = "Flipper Devices Inc.",
 | ||||||
|  | //     [USB_STR_PRODUCT] = "Combined VCP and CMSIS-DAP Adapter",
 | ||||||
|  | //     [USB_STR_COM_PORT] = "Virtual COM-Port",
 | ||||||
|  | //     [USB_STR_CMSIS_DAP_V1] = "CMSIS-DAP v1 Adapter",
 | ||||||
|  | //     [USB_STR_CMSIS_DAP_V2] = "CMSIS-DAP v2 Adapter",
 | ||||||
|  | //     [USB_STR_SERIAL_NUMBER] = "01234567890ABCDEF",
 | ||||||
|  | // };
 | ||||||
|  | 
 | ||||||
|  | static const struct usb_string_descriptor dev_manuf_descr = | ||||||
|  |     USB_STRING_DESC("Flipper Devices Inc."); | ||||||
|  | 
 | ||||||
|  | static const struct usb_string_descriptor dev_prod_descr = | ||||||
|  |     USB_STRING_DESC("Combined VCP and CMSIS-DAP Adapter"); | ||||||
|  | 
 | ||||||
|  | static struct usb_string_descriptor* dev_serial_descr = NULL; | ||||||
|  | 
 | ||||||
|  | static const struct usb_string_descriptor dev_dap_v1_descr = | ||||||
|  |     USB_STRING_DESC("CMSIS-DAP v1 Adapter"); | ||||||
|  | 
 | ||||||
|  | static const struct usb_string_descriptor dev_dap_v2_descr = | ||||||
|  |     USB_STRING_DESC("CMSIS-DAP v2 Adapter"); | ||||||
|  | 
 | ||||||
|  | static const struct usb_string_descriptor dev_com_descr = USB_STRING_DESC("Virtual COM-Port"); | ||||||
|  | 
 | ||||||
|  | struct HidConfigDescriptor { | ||||||
|  |     struct usb_config_descriptor configuration; | ||||||
|  | 
 | ||||||
|  |     // CMSIS-DAP v1
 | ||||||
|  |     struct usb_interface_descriptor hid_interface; | ||||||
|  |     struct usb_hid_descriptor hid; | ||||||
|  |     struct usb_endpoint_descriptor hid_ep_in; | ||||||
|  |     struct usb_endpoint_descriptor hid_ep_out; | ||||||
|  | 
 | ||||||
|  |     // CMSIS-DAP v2
 | ||||||
|  |     struct usb_interface_descriptor bulk_interface; | ||||||
|  |     struct usb_endpoint_descriptor bulk_ep_out; | ||||||
|  |     struct usb_endpoint_descriptor bulk_ep_in; | ||||||
|  | 
 | ||||||
|  |     // CDC
 | ||||||
|  |     struct usb_iad_descriptor iad; | ||||||
|  |     struct usb_interface_descriptor interface_comm; | ||||||
|  |     struct usb_cdc_header_desc cdc_header; | ||||||
|  |     struct usb_cdc_call_mgmt_desc cdc_acm; | ||||||
|  |     struct usb_cdc_acm_desc cdc_call_mgmt; | ||||||
|  |     struct usb_cdc_union_desc cdc_union; | ||||||
|  |     struct usb_endpoint_descriptor ep_comm; | ||||||
|  |     struct usb_interface_descriptor interface_data; | ||||||
|  |     struct usb_endpoint_descriptor ep_in; | ||||||
|  |     struct usb_endpoint_descriptor ep_out; | ||||||
|  | 
 | ||||||
|  | } __attribute__((packed)); | ||||||
|  | 
 | ||||||
|  | static const struct usb_device_descriptor hid_device_desc = { | ||||||
|  |     .bLength = sizeof(struct usb_device_descriptor), | ||||||
|  |     .bDescriptorType = USB_DTYPE_DEVICE, | ||||||
|  |     .bcdUSB = VERSION_BCD(2, 1, 0), | ||||||
|  |     .bDeviceClass = USB_CLASS_MISC, | ||||||
|  |     .bDeviceSubClass = USB_SUBCLASS_IAD, | ||||||
|  |     .bDeviceProtocol = USB_PROTO_IAD, | ||||||
|  |     .bMaxPacketSize0 = DAP_USB_EP0_SIZE, | ||||||
|  |     .idVendor = DAP_HID_VID, | ||||||
|  |     .idProduct = DAP_HID_PID, | ||||||
|  |     .bcdDevice = VERSION_BCD(1, 0, 0), | ||||||
|  |     .iManufacturer = USB_STR_MANUFACTURER, | ||||||
|  |     .iProduct = USB_STR_PRODUCT, | ||||||
|  |     .iSerialNumber = USB_STR_SERIAL_NUMBER, | ||||||
|  |     .bNumConfigurations = 1, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const uint8_t hid_report_desc[] = { | ||||||
|  |     0x05, 0x01, // Usage Page (Generic Desktop Ctrls)
 | ||||||
|  |     0x09, 0x00, // Usage (Undefined)
 | ||||||
|  |     0xa1, 0x01, // Collection (Application)
 | ||||||
|  |     0x15, 0x00, //   Logical Minimum (0)
 | ||||||
|  |     0x26, 0xff, 0x00, //   Logical Maximum (255)
 | ||||||
|  |     0x75, 0x08, //   Report Size (8)
 | ||||||
|  |     0x95, 0x40, //   Report Count (64)
 | ||||||
|  |     0x09, 0x00, //   Usage (Undefined)
 | ||||||
|  |     0x81, 0x82, //   Input (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position)
 | ||||||
|  |     0x75, 0x08, //   Report Size (8)
 | ||||||
|  |     0x95, 0x40, //   Report Count (64)
 | ||||||
|  |     0x09, 0x00, //   Usage (Undefined)
 | ||||||
|  |     0x91, 0x82, //   Output (Data,Var,Abs,No Wrap,Linear,Preferred State,No Null Position,Volatile)
 | ||||||
|  |     0xc0, // End Collection
 | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static const struct HidConfigDescriptor hid_cfg_desc = { | ||||||
|  |     .configuration = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_config_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_CONFIGURATION, | ||||||
|  |             .wTotalLength = sizeof(struct HidConfigDescriptor), | ||||||
|  |             .bNumInterfaces = USB_INTF_COUNT, | ||||||
|  |             .bConfigurationValue = 1, | ||||||
|  |             .iConfiguration = NO_DESCRIPTOR, | ||||||
|  |             .bmAttributes = USB_CFG_ATTR_RESERVED, | ||||||
|  |             .bMaxPower = USB_CFG_POWER_MA(500), | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     // CMSIS-DAP v1
 | ||||||
|  |     .hid_interface = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_interface_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_INTERFACE, | ||||||
|  |             .bInterfaceNumber = USB_INTF_HID, | ||||||
|  |             .bAlternateSetting = 0, | ||||||
|  |             .bNumEndpoints = 2, | ||||||
|  |             .bInterfaceClass = USB_CLASS_HID, | ||||||
|  |             .bInterfaceSubClass = USB_HID_SUBCLASS_NONBOOT, | ||||||
|  |             .bInterfaceProtocol = USB_HID_PROTO_NONBOOT, | ||||||
|  |             .iInterface = USB_STR_CMSIS_DAP_V1, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .hid = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_hid_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_HID, | ||||||
|  |             .bcdHID = VERSION_BCD(1, 1, 1), | ||||||
|  |             .bCountryCode = USB_HID_COUNTRY_NONE, | ||||||
|  |             .bNumDescriptors = 1, | ||||||
|  |             .bDescriptorType0 = USB_DTYPE_HID_REPORT, | ||||||
|  |             .wDescriptorLength0 = sizeof(hid_report_desc), | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .hid_ep_in = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = DAP_HID_EP_IN, | ||||||
|  |             .bmAttributes = USB_EPTYPE_INTERRUPT, | ||||||
|  |             .wMaxPacketSize = DAP_HID_EP_SIZE, | ||||||
|  |             .bInterval = DAP_HID_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .hid_ep_out = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = DAP_HID_EP_OUT, | ||||||
|  |             .bmAttributes = USB_EPTYPE_INTERRUPT, | ||||||
|  |             .wMaxPacketSize = DAP_HID_EP_SIZE, | ||||||
|  |             .bInterval = DAP_HID_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     // CMSIS-DAP v2
 | ||||||
|  |     .bulk_interface = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_interface_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_INTERFACE, | ||||||
|  |             .bInterfaceNumber = USB_INTF_BULK, | ||||||
|  |             .bAlternateSetting = 0, | ||||||
|  |             .bNumEndpoints = 2, | ||||||
|  |             .bInterfaceClass = USB_CLASS_VENDOR, | ||||||
|  |             .bInterfaceSubClass = 0, | ||||||
|  |             .bInterfaceProtocol = 0, | ||||||
|  |             .iInterface = USB_STR_CMSIS_DAP_V2, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .bulk_ep_out = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = DAP_HID_EP_BULK_OUT, | ||||||
|  |             .bmAttributes = USB_EPTYPE_BULK, | ||||||
|  |             .wMaxPacketSize = DAP_HID_EP_SIZE, | ||||||
|  |             .bInterval = DAP_BULK_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .bulk_ep_in = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = DAP_HID_EP_BULK_IN, | ||||||
|  |             .bmAttributes = USB_EPTYPE_BULK, | ||||||
|  |             .wMaxPacketSize = DAP_HID_EP_SIZE, | ||||||
|  |             .bInterval = DAP_BULK_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     // CDC
 | ||||||
|  |     .iad = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_iad_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_INTERFASEASSOC, | ||||||
|  |             .bFirstInterface = USB_INTF_CDC_COMM, | ||||||
|  |             .bInterfaceCount = 2, | ||||||
|  |             .bFunctionClass = USB_CLASS_CDC, | ||||||
|  |             .bFunctionSubClass = USB_CDC_SUBCLASS_ACM, | ||||||
|  |             .bFunctionProtocol = USB_PROTO_NONE, | ||||||
|  |             .iFunction = USB_STR_COM_PORT, | ||||||
|  |         }, | ||||||
|  |     .interface_comm = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_interface_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_INTERFACE, | ||||||
|  |             .bInterfaceNumber = USB_INTF_CDC_COMM, | ||||||
|  |             .bAlternateSetting = 0, | ||||||
|  |             .bNumEndpoints = 1, | ||||||
|  |             .bInterfaceClass = USB_CLASS_CDC, | ||||||
|  |             .bInterfaceSubClass = USB_CDC_SUBCLASS_ACM, | ||||||
|  |             .bInterfaceProtocol = USB_PROTO_NONE, | ||||||
|  |             .iInterface = 0, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .cdc_header = | ||||||
|  |         { | ||||||
|  |             .bFunctionLength = sizeof(struct usb_cdc_header_desc), | ||||||
|  |             .bDescriptorType = USB_DTYPE_CS_INTERFACE, | ||||||
|  |             .bDescriptorSubType = USB_DTYPE_CDC_HEADER, | ||||||
|  |             .bcdCDC = VERSION_BCD(1, 1, 0), | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .cdc_acm = | ||||||
|  |         { | ||||||
|  |             .bFunctionLength = sizeof(struct usb_cdc_call_mgmt_desc), | ||||||
|  |             .bDescriptorType = USB_DTYPE_CS_INTERFACE, | ||||||
|  |             .bDescriptorSubType = USB_DTYPE_CDC_CALL_MANAGEMENT, | ||||||
|  |             // .bmCapabilities = USB_CDC_CAP_LINE | USB_CDC_CAP_BRK,
 | ||||||
|  |             .bmCapabilities = 0, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .cdc_call_mgmt = | ||||||
|  |         { | ||||||
|  |             .bFunctionLength = sizeof(struct usb_cdc_acm_desc), | ||||||
|  |             .bDescriptorType = USB_DTYPE_CS_INTERFACE, | ||||||
|  |             .bDescriptorSubType = USB_DTYPE_CDC_ACM, | ||||||
|  |             .bmCapabilities = USB_CDC_CALL_MGMT_CAP_DATA_INTF, | ||||||
|  |             // .bDataInterface = USB_INTF_CDC_DATA,
 | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .cdc_union = | ||||||
|  |         { | ||||||
|  |             .bFunctionLength = sizeof(struct usb_cdc_union_desc), | ||||||
|  |             .bDescriptorType = USB_DTYPE_CS_INTERFACE, | ||||||
|  |             .bDescriptorSubType = USB_DTYPE_CDC_UNION, | ||||||
|  |             .bMasterInterface0 = USB_INTF_CDC_COMM, | ||||||
|  |             .bSlaveInterface0 = USB_INTF_CDC_DATA, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .ep_comm = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_COMM, | ||||||
|  |             .bmAttributes = USB_EPTYPE_INTERRUPT, | ||||||
|  |             .wMaxPacketSize = DAP_CDC_COMM_EP_SIZE, | ||||||
|  |             .bInterval = DAP_CDC_COMM_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .interface_data = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_interface_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_INTERFACE, | ||||||
|  |             .bInterfaceNumber = USB_INTF_CDC_DATA, | ||||||
|  |             .bAlternateSetting = 0, | ||||||
|  |             .bNumEndpoints = 2, | ||||||
|  |             .bInterfaceClass = USB_CLASS_CDC_DATA, | ||||||
|  |             .bInterfaceSubClass = USB_SUBCLASS_NONE, | ||||||
|  |             .bInterfaceProtocol = USB_PROTO_NONE, | ||||||
|  |             .iInterface = NO_DESCRIPTOR, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .ep_in = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = HID_EP_IN | DAP_CDC_EP_SEND, | ||||||
|  |             .bmAttributes = USB_EPTYPE_BULK, | ||||||
|  |             .wMaxPacketSize = DAP_CDC_EP_SIZE, | ||||||
|  |             .bInterval = DAP_CDC_INTERVAL, | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .ep_out = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(struct usb_endpoint_descriptor), | ||||||
|  |             .bDescriptorType = USB_DTYPE_ENDPOINT, | ||||||
|  |             .bEndpointAddress = HID_EP_OUT | DAP_CDC_EP_RECV, | ||||||
|  |             .bmAttributes = USB_EPTYPE_BULK, | ||||||
|  |             .wMaxPacketSize = DAP_CDC_EP_SIZE, | ||||||
|  |             .bInterval = DAP_CDC_INTERVAL, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // WinUSB
 | ||||||
|  | #include "usb_winusb.h" | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     usb_binary_object_store_descriptor_t bos; | ||||||
|  |     usb_winusb_capability_descriptor_t winusb; | ||||||
|  | } usb_bos_hierarchy_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     usb_winusb_subset_header_function_t header; | ||||||
|  |     usb_winusb_feature_compatble_id_t comp_id; | ||||||
|  |     usb_winusb_feature_reg_property_guids_t property; | ||||||
|  | } usb_msos_descriptor_subset_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     usb_winusb_set_header_descriptor_t header; | ||||||
|  |     usb_msos_descriptor_subset_t subset; | ||||||
|  | } usb_msos_descriptor_set_t; | ||||||
|  | 
 | ||||||
|  | #define USB_DTYPE_BINARY_OBJECT_STORE 15 | ||||||
|  | #define USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR 16 | ||||||
|  | #define USB_DC_TYPE_PLATFORM 5 | ||||||
|  | 
 | ||||||
|  | const usb_bos_hierarchy_t usb_bos_hierarchy = { | ||||||
|  |     .bos = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(usb_binary_object_store_descriptor_t), | ||||||
|  |             .bDescriptorType = USB_DTYPE_BINARY_OBJECT_STORE, | ||||||
|  |             .wTotalLength = sizeof(usb_bos_hierarchy_t), | ||||||
|  |             .bNumDeviceCaps = 1, | ||||||
|  |         }, | ||||||
|  |     .winusb = | ||||||
|  |         { | ||||||
|  |             .bLength = sizeof(usb_winusb_capability_descriptor_t), | ||||||
|  |             .bDescriptorType = USB_DTYPE_DEVICE_CAPABILITY_DESCRIPTOR, | ||||||
|  |             .bDevCapabilityType = USB_DC_TYPE_PLATFORM, | ||||||
|  |             .bReserved = 0, | ||||||
|  |             .PlatformCapabilityUUID = USB_WINUSB_PLATFORM_CAPABILITY_ID, | ||||||
|  |             .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, | ||||||
|  |             .wMSOSDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), | ||||||
|  |             .bMS_VendorCode = USB_WINUSB_VENDOR_CODE, | ||||||
|  |             .bAltEnumCode = 0, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | const usb_msos_descriptor_set_t usb_msos_descriptor_set = { | ||||||
|  |     .header = | ||||||
|  |         { | ||||||
|  |             .wLength = sizeof(usb_winusb_set_header_descriptor_t), | ||||||
|  |             .wDescriptorType = USB_WINUSB_SET_HEADER_DESCRIPTOR, | ||||||
|  |             .dwWindowsVersion = USB_WINUSB_WINDOWS_VERSION, | ||||||
|  |             .wDescriptorSetTotalLength = sizeof(usb_msos_descriptor_set_t), | ||||||
|  |         }, | ||||||
|  | 
 | ||||||
|  |     .subset = | ||||||
|  |         { | ||||||
|  |             .header = | ||||||
|  |                 { | ||||||
|  |                     .wLength = sizeof(usb_winusb_subset_header_function_t), | ||||||
|  |                     .wDescriptorType = USB_WINUSB_SUBSET_HEADER_FUNCTION, | ||||||
|  |                     .bFirstInterface = USB_INTF_BULK, | ||||||
|  |                     .bReserved = 0, | ||||||
|  |                     .wSubsetLength = sizeof(usb_msos_descriptor_subset_t), | ||||||
|  |                 }, | ||||||
|  | 
 | ||||||
|  |             .comp_id = | ||||||
|  |                 { | ||||||
|  |                     .wLength = sizeof(usb_winusb_feature_compatble_id_t), | ||||||
|  |                     .wDescriptorType = USB_WINUSB_FEATURE_COMPATBLE_ID, | ||||||
|  |                     .CompatibleID = "WINUSB\0\0", | ||||||
|  |                     .SubCompatibleID = {0}, | ||||||
|  |                 }, | ||||||
|  | 
 | ||||||
|  |             .property = | ||||||
|  |                 { | ||||||
|  |                     .wLength = sizeof(usb_winusb_feature_reg_property_guids_t), | ||||||
|  |                     .wDescriptorType = USB_WINUSB_FEATURE_REG_PROPERTY, | ||||||
|  |                     .wPropertyDataType = USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ, | ||||||
|  |                     .wPropertyNameLength = | ||||||
|  |                         sizeof(usb_msos_descriptor_set.subset.property.PropertyName), | ||||||
|  |                     .PropertyName = {'D', 0, 'e', 0, 'v', 0, 'i', 0, 'c', 0, 'e', 0, 'I', 0, | ||||||
|  |                                      'n', 0, 't', 0, 'e', 0, 'r', 0, 'f', 0, 'a', 0, 'c', 0, | ||||||
|  |                                      'e', 0, 'G', 0, 'U', 0, 'I', 0, 'D', 0, 's', 0, 0,   0}, | ||||||
|  |                     .wPropertyDataLength = | ||||||
|  |                         sizeof(usb_msos_descriptor_set.subset.property.PropertyData), | ||||||
|  |                     .PropertyData = {'{', 0, 'C', 0, 'D', 0, 'B', 0, '3', 0, 'B', 0, '5', 0, | ||||||
|  |                                      'A', 0, 'D', 0, '-', 0, '2', 0, '9', 0, '3', 0, 'B', 0, | ||||||
|  |                                      '-', 0, '4', 0, '6', 0, '6', 0, '3', 0, '-', 0, 'A', 0, | ||||||
|  |                                      'A', 0, '3', 0, '6', 0, '-', 0, '1', 0, 'A', 0, 'A', 0, | ||||||
|  |                                      'E', 0, '4', 0, '6', 0, '4', 0, '6', 0, '3', 0, '7', 0, | ||||||
|  |                                      '7', 0, '6', 0, '}', 0, 0,   0, 0,   0}, | ||||||
|  |                 }, | ||||||
|  |         }, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | typedef struct { | ||||||
|  |     FuriSemaphore* semaphore_v1; | ||||||
|  |     FuriSemaphore* semaphore_v2; | ||||||
|  |     FuriSemaphore* semaphore_cdc; | ||||||
|  |     bool connected; | ||||||
|  |     usbd_device* usb_dev; | ||||||
|  |     DapStateCallback state_callback; | ||||||
|  |     DapRxCallback rx_callback_v1; | ||||||
|  |     DapRxCallback rx_callback_v2; | ||||||
|  |     DapRxCallback rx_callback_cdc; | ||||||
|  |     DapCDCControlLineCallback control_line_callback_cdc; | ||||||
|  |     DapCDCConfigCallback config_callback_cdc; | ||||||
|  |     void* context; | ||||||
|  |     void* context_cdc; | ||||||
|  | } DAPState; | ||||||
|  | 
 | ||||||
|  | static DAPState dap_state = { | ||||||
|  |     .semaphore_v1 = NULL, | ||||||
|  |     .semaphore_v2 = NULL, | ||||||
|  |     .semaphore_cdc = NULL, | ||||||
|  |     .connected = false, | ||||||
|  |     .usb_dev = NULL, | ||||||
|  |     .state_callback = NULL, | ||||||
|  |     .rx_callback_v1 = NULL, | ||||||
|  |     .rx_callback_v2 = NULL, | ||||||
|  |     .rx_callback_cdc = NULL, | ||||||
|  |     .control_line_callback_cdc = NULL, | ||||||
|  |     .config_callback_cdc = NULL, | ||||||
|  |     .context = NULL, | ||||||
|  |     .context_cdc = NULL, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static struct usb_cdc_line_coding cdc_config = {0}; | ||||||
|  | static uint8_t cdc_ctrl_line_state = 0; | ||||||
|  | 
 | ||||||
|  | #ifdef DAP_USB_LOG | ||||||
|  | void furi_console_log_printf(const char* format, ...) _ATTRIBUTE((__format__(__printf__, 1, 2))); | ||||||
|  | 
 | ||||||
|  | void furi_console_log_printf(const char* format, ...) { | ||||||
|  |     char buffer[256]; | ||||||
|  |     va_list args; | ||||||
|  |     va_start(args, format); | ||||||
|  |     vsnprintf(buffer, sizeof(buffer), format, args); | ||||||
|  |     va_end(args); | ||||||
|  |     furi_hal_console_puts(buffer); | ||||||
|  |     furi_hal_console_puts("\r\n"); | ||||||
|  |     UNUSED(format); | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | #define furi_console_log_printf(...) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size) { | ||||||
|  |     if((dap_state.semaphore_v1 == NULL) || (dap_state.connected == false)) return 0; | ||||||
|  | 
 | ||||||
|  |     furi_check(furi_semaphore_acquire(dap_state.semaphore_v1, FuriWaitForever) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_IN, buffer, size); | ||||||
|  |         furi_console_log_printf("v1 tx %ld", len); | ||||||
|  |         return len; | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size) { | ||||||
|  |     if((dap_state.semaphore_v2 == NULL) || (dap_state.connected == false)) return 0; | ||||||
|  | 
 | ||||||
|  |     furi_check(furi_semaphore_acquire(dap_state.semaphore_v2, FuriWaitForever) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         int32_t len = usbd_ep_write(dap_state.usb_dev, DAP_HID_EP_BULK_IN, buffer, size); | ||||||
|  |         furi_console_log_printf("v2 tx %ld", len); | ||||||
|  |         return len; | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size) { | ||||||
|  |     if((dap_state.semaphore_cdc == NULL) || (dap_state.connected == false)) return 0; | ||||||
|  | 
 | ||||||
|  |     furi_check(furi_semaphore_acquire(dap_state.semaphore_cdc, FuriWaitForever) == FuriStatusOk); | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         int32_t len = usbd_ep_write(dap_state.usb_dev, HID_EP_IN | DAP_CDC_EP_SEND, buffer, size); | ||||||
|  |         furi_console_log_printf("cdc tx %ld", len); | ||||||
|  |         return len; | ||||||
|  |     } else { | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_v1_usb_set_rx_callback(DapRxCallback callback) { | ||||||
|  |     dap_state.rx_callback_v1 = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_v2_usb_set_rx_callback(DapRxCallback callback) { | ||||||
|  |     dap_state.rx_callback_v2 = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_rx_callback(DapRxCallback callback) { | ||||||
|  |     dap_state.rx_callback_cdc = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback) { | ||||||
|  |     dap_state.control_line_callback_cdc = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback) { | ||||||
|  |     dap_state.config_callback_cdc = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_context(void* context) { | ||||||
|  |     dap_state.context_cdc = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_set_context(void* context) { | ||||||
|  |     dap_state.context = context; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_set_state_callback(DapStateCallback callback) { | ||||||
|  |     dap_state.state_callback = callback; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void* dap_usb_alloc_string_descr(const char* str) { | ||||||
|  |     furi_assert(str); | ||||||
|  | 
 | ||||||
|  |     uint8_t len = strlen(str); | ||||||
|  |     uint8_t wlen = (len + 1) * sizeof(uint16_t); | ||||||
|  |     struct usb_string_descriptor* dev_str_desc = malloc(wlen); | ||||||
|  |     dev_str_desc->bLength = wlen; | ||||||
|  |     dev_str_desc->bDescriptorType = USB_DTYPE_STRING; | ||||||
|  |     for(uint8_t i = 0; i < len; i++) { | ||||||
|  |         dev_str_desc->wString[i] = str[i]; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return dev_str_desc; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_alloc_name(const char* name) { | ||||||
|  |     dev_serial_descr = dap_usb_alloc_string_descr(name); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_free_name() { | ||||||
|  |     free(dev_serial_descr); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx); | ||||||
|  | static void hid_deinit(usbd_device* dev); | ||||||
|  | static void hid_on_wakeup(usbd_device* dev); | ||||||
|  | static void hid_on_suspend(usbd_device* dev); | ||||||
|  | 
 | ||||||
|  | static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg); | ||||||
|  | static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback); | ||||||
|  | 
 | ||||||
|  | FuriHalUsbInterface dap_v2_usb_hid = { | ||||||
|  |     .init = hid_init, | ||||||
|  |     .deinit = hid_deinit, | ||||||
|  |     .wakeup = hid_on_wakeup, | ||||||
|  |     .suspend = hid_on_suspend, | ||||||
|  |     .dev_descr = (struct usb_device_descriptor*)&hid_device_desc, | ||||||
|  |     .cfg_descr = (void*)&hid_cfg_desc, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | static void hid_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) { | ||||||
|  |     UNUSED(intf); | ||||||
|  |     UNUSED(ctx); | ||||||
|  | 
 | ||||||
|  |     dap_v2_usb_hid.str_manuf_descr = (void*)&dev_manuf_descr; | ||||||
|  |     dap_v2_usb_hid.str_prod_descr = (void*)&dev_prod_descr; | ||||||
|  |     dap_v2_usb_hid.str_serial_descr = (void*)dev_serial_descr; | ||||||
|  | 
 | ||||||
|  |     dap_state.usb_dev = dev; | ||||||
|  |     if(dap_state.semaphore_v1 == NULL) dap_state.semaphore_v1 = furi_semaphore_alloc(1, 1); | ||||||
|  |     if(dap_state.semaphore_v2 == NULL) dap_state.semaphore_v2 = furi_semaphore_alloc(1, 1); | ||||||
|  |     if(dap_state.semaphore_cdc == NULL) dap_state.semaphore_cdc = furi_semaphore_alloc(1, 1); | ||||||
|  | 
 | ||||||
|  |     usb_hid.dev_descr->idVendor = DAP_HID_VID; | ||||||
|  |     usb_hid.dev_descr->idProduct = DAP_HID_PID; | ||||||
|  | 
 | ||||||
|  |     usbd_reg_config(dev, hid_ep_config); | ||||||
|  |     usbd_reg_control(dev, hid_control); | ||||||
|  | 
 | ||||||
|  |     usbd_connect(dev, true); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static bool deinit_flag = false; | ||||||
|  | 
 | ||||||
|  | void dap_common_wait_for_deinit() { | ||||||
|  |     while(!deinit_flag) { | ||||||
|  |         furi_delay_ms(50); | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_deinit(usbd_device* dev) { | ||||||
|  |     dap_state.usb_dev = NULL; | ||||||
|  | 
 | ||||||
|  |     furi_semaphore_free(dap_state.semaphore_v1); | ||||||
|  |     furi_semaphore_free(dap_state.semaphore_v2); | ||||||
|  |     furi_semaphore_free(dap_state.semaphore_cdc); | ||||||
|  |     dap_state.semaphore_v1 = NULL; | ||||||
|  |     dap_state.semaphore_v2 = NULL; | ||||||
|  |     dap_state.semaphore_cdc = NULL; | ||||||
|  | 
 | ||||||
|  |     usbd_reg_config(dev, NULL); | ||||||
|  |     usbd_reg_control(dev, NULL); | ||||||
|  | 
 | ||||||
|  |     free(usb_hid.str_manuf_descr); | ||||||
|  |     free(usb_hid.str_prod_descr); | ||||||
|  | 
 | ||||||
|  |     FURI_SW_MEMBARRIER(); | ||||||
|  |     deinit_flag = true; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_on_wakeup(usbd_device* dev) { | ||||||
|  |     UNUSED(dev); | ||||||
|  |     if(!dap_state.connected) { | ||||||
|  |         dap_state.connected = true; | ||||||
|  |         if(dap_state.state_callback != NULL) { | ||||||
|  |             dap_state.state_callback(dap_state.connected, dap_state.context); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_on_suspend(usbd_device* dev) { | ||||||
|  |     UNUSED(dev); | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         dap_state.connected = false; | ||||||
|  |         if(dap_state.state_callback != NULL) { | ||||||
|  |             dap_state.state_callback(dap_state.connected, dap_state.context); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t dap_v1_usb_rx(uint8_t* buffer, size_t size) { | ||||||
|  |     size_t len = 0; | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_OUT, buffer, size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t dap_v2_usb_rx(uint8_t* buffer, size_t size) { | ||||||
|  |     size_t len = 0; | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         len = usbd_ep_read(dap_state.usb_dev, DAP_HID_EP_BULK_OUT, buffer, size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size) { | ||||||
|  |     size_t len = 0; | ||||||
|  | 
 | ||||||
|  |     if(dap_state.connected) { | ||||||
|  |         len = usbd_ep_read(dap_state.usb_dev, HID_EP_OUT | DAP_CDC_EP_RECV, buffer, size); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return len; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|  |     UNUSED(dev); | ||||||
|  |     UNUSED(ep); | ||||||
|  | 
 | ||||||
|  |     switch(event) { | ||||||
|  |     case usbd_evt_eptx: | ||||||
|  |         furi_semaphore_release(dap_state.semaphore_v1); | ||||||
|  |         furi_console_log_printf("hid tx complete"); | ||||||
|  |         break; | ||||||
|  |     case usbd_evt_eprx: | ||||||
|  |         if(dap_state.rx_callback_v1 != NULL) { | ||||||
|  |             dap_state.rx_callback_v1(dap_state.context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_console_log_printf("hid %d, %d", event, ep); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void hid_txrx_ep_bulk_callback(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|  |     UNUSED(dev); | ||||||
|  |     UNUSED(ep); | ||||||
|  | 
 | ||||||
|  |     switch(event) { | ||||||
|  |     case usbd_evt_eptx: | ||||||
|  |         furi_semaphore_release(dap_state.semaphore_v2); | ||||||
|  |         furi_console_log_printf("bulk tx complete"); | ||||||
|  |         break; | ||||||
|  |     case usbd_evt_eprx: | ||||||
|  |         if(dap_state.rx_callback_v2 != NULL) { | ||||||
|  |             dap_state.rx_callback_v2(dap_state.context); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_console_log_printf("bulk %d, %d", event, ep); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static void cdc_txrx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) { | ||||||
|  |     UNUSED(dev); | ||||||
|  |     UNUSED(ep); | ||||||
|  | 
 | ||||||
|  |     switch(event) { | ||||||
|  |     case usbd_evt_eptx: | ||||||
|  |         furi_semaphore_release(dap_state.semaphore_cdc); | ||||||
|  |         furi_console_log_printf("cdc tx complete"); | ||||||
|  |         break; | ||||||
|  |     case usbd_evt_eprx: | ||||||
|  |         if(dap_state.rx_callback_cdc != NULL) { | ||||||
|  |             dap_state.rx_callback_cdc(dap_state.context_cdc); | ||||||
|  |         } | ||||||
|  |         break; | ||||||
|  |     default: | ||||||
|  |         furi_console_log_printf("cdc %d, %d", event, ep); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | static usbd_respond hid_ep_config(usbd_device* dev, uint8_t cfg) { | ||||||
|  |     switch(cfg) { | ||||||
|  |     case EP_CFG_DECONFIGURE: | ||||||
|  |         usbd_ep_deconfig(dev, DAP_HID_EP_OUT); | ||||||
|  |         usbd_ep_deconfig(dev, DAP_HID_EP_IN); | ||||||
|  |         usbd_ep_deconfig(dev, DAP_HID_EP_BULK_IN); | ||||||
|  |         usbd_ep_deconfig(dev, DAP_HID_EP_BULK_OUT); | ||||||
|  |         usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_COMM); | ||||||
|  |         usbd_ep_deconfig(dev, HID_EP_IN | DAP_CDC_EP_SEND); | ||||||
|  |         usbd_ep_deconfig(dev, HID_EP_OUT | DAP_CDC_EP_RECV); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_OUT, NULL); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_IN, NULL); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, NULL); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, NULL); | ||||||
|  |         usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, 0); | ||||||
|  |         usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, 0); | ||||||
|  |         return usbd_ack; | ||||||
|  |     case EP_CFG_CONFIGURE: | ||||||
|  |         usbd_ep_config(dev, DAP_HID_EP_IN, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, DAP_HID_EP_OUT, USB_EPTYPE_INTERRUPT, DAP_HID_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, DAP_HID_EP_BULK_OUT, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, DAP_HID_EP_BULK_IN, USB_EPTYPE_BULK, DAP_HID_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, HID_EP_OUT | DAP_CDC_EP_RECV, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_SEND, USB_EPTYPE_BULK, DAP_CDC_EP_SIZE); | ||||||
|  |         usbd_ep_config(dev, HID_EP_IN | DAP_CDC_EP_COMM, USB_EPTYPE_INTERRUPT, DAP_CDC_EP_SIZE); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_IN, hid_txrx_ep_callback); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_OUT, hid_txrx_ep_callback); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_BULK_OUT, hid_txrx_ep_bulk_callback); | ||||||
|  |         usbd_reg_endpoint(dev, DAP_HID_EP_BULK_IN, hid_txrx_ep_bulk_callback); | ||||||
|  |         usbd_reg_endpoint(dev, HID_EP_OUT | DAP_CDC_EP_RECV, cdc_txrx_ep_callback); | ||||||
|  |         usbd_reg_endpoint(dev, HID_EP_IN | DAP_CDC_EP_SEND, cdc_txrx_ep_callback); | ||||||
|  |         // usbd_ep_write(dev, DAP_HID_EP_IN, NULL, 0);
 | ||||||
|  |         // usbd_ep_write(dev, DAP_HID_EP_BULK_IN, NULL, 0);
 | ||||||
|  |         // usbd_ep_write(dev, HID_EP_IN | DAP_CDC_EP_SEND, NULL, 0);
 | ||||||
|  |         return usbd_ack; | ||||||
|  |     default: | ||||||
|  |         return usbd_fail; | ||||||
|  |     } | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | #ifdef DAP_USB_LOG | ||||||
|  | static void dump_request_type(uint8_t type) { | ||||||
|  |     switch(type & USB_REQ_DIRECTION) { | ||||||
|  |     case USB_REQ_HOSTTODEV: | ||||||
|  |         furi_hal_console_puts("host to dev, "); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_DEVTOHOST: | ||||||
|  |         furi_hal_console_puts("dev to host, "); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(type & USB_REQ_TYPE) { | ||||||
|  |     case USB_REQ_STANDARD: | ||||||
|  |         furi_hal_console_puts("standard, "); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_CLASS: | ||||||
|  |         furi_hal_console_puts("class, "); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_VENDOR: | ||||||
|  |         furi_hal_console_puts("vendor, "); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     switch(type & USB_REQ_RECIPIENT) { | ||||||
|  |     case USB_REQ_DEVICE: | ||||||
|  |         furi_hal_console_puts("device"); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_INTERFACE: | ||||||
|  |         furi_hal_console_puts("interface"); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_ENDPOINT: | ||||||
|  |         furi_hal_console_puts("endpoint"); | ||||||
|  |         break; | ||||||
|  |     case USB_REQ_OTHER: | ||||||
|  |         furi_hal_console_puts("other"); | ||||||
|  |         break; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     furi_hal_console_puts("\r\n"); | ||||||
|  | } | ||||||
|  | #else | ||||||
|  | #define dump_request_type(...) | ||||||
|  | #endif | ||||||
|  | 
 | ||||||
|  | static usbd_respond hid_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) { | ||||||
|  |     UNUSED(callback); | ||||||
|  | 
 | ||||||
|  |     dump_request_type(req->bmRequestType); | ||||||
|  |     furi_console_log_printf( | ||||||
|  |         "control: RT %02x, R %02x, V %04x, I %04x, L %04x", | ||||||
|  |         req->bmRequestType, | ||||||
|  |         req->bRequest, | ||||||
|  |         req->wValue, | ||||||
|  |         req->wIndex, | ||||||
|  |         req->wLength); | ||||||
|  | 
 | ||||||
|  |     if(((USB_REQ_RECIPIENT | USB_REQ_TYPE | USB_REQ_DIRECTION) & req->bmRequestType) == | ||||||
|  |        (USB_REQ_STANDARD | USB_REQ_VENDOR | USB_REQ_DEVTOHOST)) { | ||||||
|  |         // vendor request, device to host
 | ||||||
|  |         furi_console_log_printf("vendor request"); | ||||||
|  |         if(USB_WINUSB_VENDOR_CODE == req->bRequest) { | ||||||
|  |             // WINUSB request
 | ||||||
|  |             if(USB_WINUSB_DESCRIPTOR_INDEX == req->wIndex) { | ||||||
|  |                 furi_console_log_printf("WINUSB descriptor"); | ||||||
|  |                 uint16_t length = req->wLength; | ||||||
|  |                 if(length > sizeof(usb_msos_descriptor_set_t)) { | ||||||
|  |                     length = sizeof(usb_msos_descriptor_set_t); | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 dev->status.data_ptr = (uint8_t*)&usb_msos_descriptor_set; | ||||||
|  |                 dev->status.data_count = length; | ||||||
|  |                 return usbd_ack; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == | ||||||
|  |        (USB_REQ_STANDARD | USB_REQ_DEVICE)) { | ||||||
|  |         // device request
 | ||||||
|  |         if(req->bRequest == USB_STD_GET_DESCRIPTOR) { | ||||||
|  |             const uint8_t dtype = req->wValue >> 8; | ||||||
|  |             const uint8_t dnumber = req->wValue & 0xFF; | ||||||
|  |             // get string descriptor
 | ||||||
|  |             if(USB_DTYPE_STRING == dtype) { | ||||||
|  |                 if(dnumber == USB_STR_CMSIS_DAP_V1) { | ||||||
|  |                     furi_console_log_printf("str CMSIS-DAP v1"); | ||||||
|  |                     dev->status.data_ptr = (uint8_t*)&dev_dap_v1_descr; | ||||||
|  |                     dev->status.data_count = dev_dap_v1_descr.bLength; | ||||||
|  |                     return usbd_ack; | ||||||
|  |                 } else if(dnumber == USB_STR_CMSIS_DAP_V2) { | ||||||
|  |                     furi_console_log_printf("str CMSIS-DAP v2"); | ||||||
|  |                     dev->status.data_ptr = (uint8_t*)&dev_dap_v2_descr; | ||||||
|  |                     dev->status.data_count = dev_dap_v2_descr.bLength; | ||||||
|  |                     return usbd_ack; | ||||||
|  |                 } else if(dnumber == USB_STR_COM_PORT) { | ||||||
|  |                     furi_console_log_printf("str COM port"); | ||||||
|  |                     dev->status.data_ptr = (uint8_t*)&dev_com_descr; | ||||||
|  |                     dev->status.data_count = dev_com_descr.bLength; | ||||||
|  |                     return usbd_ack; | ||||||
|  |                 } | ||||||
|  |             } else if(USB_DTYPE_BINARY_OBJECT_STORE == dtype) { | ||||||
|  |                 furi_console_log_printf("BOS descriptor"); | ||||||
|  |                 uint16_t length = req->wLength; | ||||||
|  |                 if(length > sizeof(usb_bos_hierarchy_t)) { | ||||||
|  |                     length = sizeof(usb_bos_hierarchy_t); | ||||||
|  |                 } | ||||||
|  |                 dev->status.data_ptr = (uint8_t*)&usb_bos_hierarchy; | ||||||
|  |                 dev->status.data_count = length; | ||||||
|  |                 return usbd_ack; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == | ||||||
|  |            (USB_REQ_INTERFACE | USB_REQ_CLASS) && | ||||||
|  |        req->wIndex == 0) { | ||||||
|  |         // class request
 | ||||||
|  |         switch(req->bRequest) { | ||||||
|  |         // get hid descriptor
 | ||||||
|  |         case USB_HID_GETREPORT: | ||||||
|  |             furi_console_log_printf("get report"); | ||||||
|  |             return usbd_fail; | ||||||
|  |         // set hid idle
 | ||||||
|  |         case USB_HID_SETIDLE: | ||||||
|  |             furi_console_log_printf("set idle"); | ||||||
|  |             return usbd_ack; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == | ||||||
|  |            (USB_REQ_INTERFACE | USB_REQ_CLASS) && | ||||||
|  |        req->wIndex == 2) { | ||||||
|  |         // class request
 | ||||||
|  |         switch(req->bRequest) { | ||||||
|  |         // control line state
 | ||||||
|  |         case USB_CDC_SET_CONTROL_LINE_STATE: | ||||||
|  |             furi_console_log_printf("set control line state"); | ||||||
|  |             cdc_ctrl_line_state = req->wValue; | ||||||
|  |             if(dap_state.control_line_callback_cdc != NULL) { | ||||||
|  |                 dap_state.control_line_callback_cdc(cdc_ctrl_line_state, dap_state.context_cdc); | ||||||
|  |             } | ||||||
|  |             return usbd_ack; | ||||||
|  |         // set cdc line coding
 | ||||||
|  |         case USB_CDC_SET_LINE_CODING: | ||||||
|  |             furi_console_log_printf("set line coding"); | ||||||
|  |             memcpy(&cdc_config, req->data, sizeof(cdc_config)); | ||||||
|  |             if(dap_state.config_callback_cdc != NULL) { | ||||||
|  |                 dap_state.config_callback_cdc(&cdc_config, dap_state.context_cdc); | ||||||
|  |             } | ||||||
|  |             return usbd_ack; | ||||||
|  |         // get cdc line coding
 | ||||||
|  |         case USB_CDC_GET_LINE_CODING: | ||||||
|  |             furi_console_log_printf("get line coding"); | ||||||
|  |             dev->status.data_ptr = &cdc_config; | ||||||
|  |             dev->status.data_count = sizeof(cdc_config); | ||||||
|  |             return usbd_ack; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) == | ||||||
|  |            (USB_REQ_INTERFACE | USB_REQ_STANDARD) && | ||||||
|  |        req->wIndex == 0 && req->bRequest == USB_STD_GET_DESCRIPTOR) { | ||||||
|  |         // standard request
 | ||||||
|  |         switch(req->wValue >> 8) { | ||||||
|  |         // get hid descriptor
 | ||||||
|  |         case USB_DTYPE_HID: | ||||||
|  |             furi_console_log_printf("get hid descriptor"); | ||||||
|  |             dev->status.data_ptr = (uint8_t*)&(hid_cfg_desc.hid); | ||||||
|  |             dev->status.data_count = sizeof(hid_cfg_desc.hid); | ||||||
|  |             return usbd_ack; | ||||||
|  |         // get hid report descriptor
 | ||||||
|  |         case USB_DTYPE_HID_REPORT: | ||||||
|  |             furi_console_log_printf("get hid report descriptor"); | ||||||
|  |             dev->status.data_ptr = (uint8_t*)hid_report_desc; | ||||||
|  |             dev->status.data_count = sizeof(hid_report_desc); | ||||||
|  |             return usbd_ack; | ||||||
|  |         default: | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     return usbd_fail; | ||||||
|  | } | ||||||
							
								
								
									
										55
									
								
								applications/plugins/dap_link/usb/dap_v2_usb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								applications/plugins/dap_link/usb/dap_v2_usb.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,55 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <furi_hal_usb.h> | ||||||
|  | #include <usb_cdc.h> | ||||||
|  | 
 | ||||||
|  | extern FuriHalUsbInterface dap_v2_usb_hid; | ||||||
|  | 
 | ||||||
|  | // receive callback type
 | ||||||
|  | typedef void (*DapRxCallback)(void* context); | ||||||
|  | 
 | ||||||
|  | typedef void (*DapStateCallback)(bool state, void* context); | ||||||
|  | 
 | ||||||
|  | /************************************ V1 ***************************************/ | ||||||
|  | 
 | ||||||
|  | int32_t dap_v1_usb_tx(uint8_t* buffer, uint8_t size); | ||||||
|  | 
 | ||||||
|  | size_t dap_v1_usb_rx(uint8_t* buffer, size_t size); | ||||||
|  | 
 | ||||||
|  | void dap_v1_usb_set_rx_callback(DapRxCallback callback); | ||||||
|  | 
 | ||||||
|  | /************************************ V2 ***************************************/ | ||||||
|  | 
 | ||||||
|  | int32_t dap_v2_usb_tx(uint8_t* buffer, uint8_t size); | ||||||
|  | 
 | ||||||
|  | size_t dap_v2_usb_rx(uint8_t* buffer, size_t size); | ||||||
|  | 
 | ||||||
|  | void dap_v2_usb_set_rx_callback(DapRxCallback callback); | ||||||
|  | 
 | ||||||
|  | /************************************ CDC **************************************/ | ||||||
|  | 
 | ||||||
|  | typedef void (*DapCDCControlLineCallback)(uint8_t state, void* context); | ||||||
|  | typedef void (*DapCDCConfigCallback)(struct usb_cdc_line_coding* config, void* context); | ||||||
|  | 
 | ||||||
|  | int32_t dap_cdc_usb_tx(uint8_t* buffer, uint8_t size); | ||||||
|  | 
 | ||||||
|  | size_t dap_cdc_usb_rx(uint8_t* buffer, size_t size); | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_rx_callback(DapRxCallback callback); | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_control_line_callback(DapCDCControlLineCallback callback); | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_config_callback(DapCDCConfigCallback callback); | ||||||
|  | 
 | ||||||
|  | void dap_cdc_usb_set_context(void* context); | ||||||
|  | 
 | ||||||
|  | /*********************************** Common ************************************/ | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_set_context(void* context); | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_set_state_callback(DapStateCallback callback); | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_alloc_name(const char* name); | ||||||
|  | 
 | ||||||
|  | void dap_common_usb_free_name(); | ||||||
|  | 
 | ||||||
|  | void dap_common_wait_for_deinit(); | ||||||
							
								
								
									
										143
									
								
								applications/plugins/dap_link/usb/usb_winusb.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								applications/plugins/dap_link/usb/usb_winusb.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,143 @@ | |||||||
|  | #pragma once | ||||||
|  | #include <stdint.h> | ||||||
|  | 
 | ||||||
|  | /*- Definitions -------------------------------------------------------------*/ | ||||||
|  | 
 | ||||||
|  | #define USB_PACK __attribute__((packed)) | ||||||
|  | 
 | ||||||
|  | #define USB_WINUSB_VENDOR_CODE 0x20 | ||||||
|  | 
 | ||||||
|  | #define USB_WINUSB_WINDOWS_VERSION 0x06030000 // Windows 8.1
 | ||||||
|  | 
 | ||||||
|  | #define USB_WINUSB_PLATFORM_CAPABILITY_ID                                                         \ | ||||||
|  |     {                                                                                             \ | ||||||
|  |         0xdf, 0x60, 0xdd, 0xd8, 0x89, 0x45, 0xc7, 0x4c, 0x9c, 0xd2, 0x65, 0x9d, 0x9e, 0x64, 0x8a, \ | ||||||
|  |             0x9f                                                                                  \ | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | enum // WinUSB Microsoft OS 2.0 descriptor request codes
 | ||||||
|  | { | ||||||
|  |     USB_WINUSB_DESCRIPTOR_INDEX = 0x07, | ||||||
|  |     USB_WINUSB_SET_ALT_ENUMERATION = 0x08, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum // wDescriptorType
 | ||||||
|  | { | ||||||
|  |     USB_WINUSB_SET_HEADER_DESCRIPTOR = 0x00, | ||||||
|  |     USB_WINUSB_SUBSET_HEADER_CONFIGURATION = 0x01, | ||||||
|  |     USB_WINUSB_SUBSET_HEADER_FUNCTION = 0x02, | ||||||
|  |     USB_WINUSB_FEATURE_COMPATBLE_ID = 0x03, | ||||||
|  |     USB_WINUSB_FEATURE_REG_PROPERTY = 0x04, | ||||||
|  |     USB_WINUSB_FEATURE_MIN_RESUME_TIME = 0x05, | ||||||
|  |     USB_WINUSB_FEATURE_MODEL_ID = 0x06, | ||||||
|  |     USB_WINUSB_FEATURE_CCGP_DEVICE = 0x07, | ||||||
|  |     USB_WINUSB_FEATURE_VENDOR_REVISION = 0x08, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | enum // wPropertyDataType
 | ||||||
|  | { | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_SZ = 1, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_EXPAND_SZ = 2, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_BINARY = 3, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_LITTLE_ENDIAN = 4, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_DWORD_BIG_ENDIAN = 5, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_LINK = 6, | ||||||
|  |     USB_WINUSB_PROPERTY_DATA_TYPE_MULTI_SZ = 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /*- Types BOS -------------------------------------------------------------------*/ | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint8_t bLength; | ||||||
|  |     uint8_t bDescriptorType; | ||||||
|  |     uint16_t wTotalLength; | ||||||
|  |     uint8_t bNumDeviceCaps; | ||||||
|  | } usb_binary_object_store_descriptor_t; | ||||||
|  | 
 | ||||||
|  | /*- Types WinUSB -------------------------------------------------------------------*/ | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint8_t bLength; | ||||||
|  |     uint8_t bDescriptorType; | ||||||
|  |     uint8_t bDevCapabilityType; | ||||||
|  |     uint8_t bReserved; | ||||||
|  |     uint8_t PlatformCapabilityUUID[16]; | ||||||
|  |     uint32_t dwWindowsVersion; | ||||||
|  |     uint16_t wMSOSDescriptorSetTotalLength; | ||||||
|  |     uint8_t bMS_VendorCode; | ||||||
|  |     uint8_t bAltEnumCode; | ||||||
|  | } usb_winusb_capability_descriptor_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint32_t dwWindowsVersion; | ||||||
|  |     uint16_t wDescriptorSetTotalLength; | ||||||
|  | } usb_winusb_set_header_descriptor_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint8_t bConfigurationValue; | ||||||
|  |     uint8_t bReserved; | ||||||
|  |     uint16_t wTotalLength; | ||||||
|  | } usb_winusb_subset_header_configuration_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint8_t bFirstInterface; | ||||||
|  |     uint8_t bReserved; | ||||||
|  |     uint16_t wSubsetLength; | ||||||
|  | } usb_winusb_subset_header_function_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint8_t CompatibleID[8]; | ||||||
|  |     uint8_t SubCompatibleID[8]; | ||||||
|  | } usb_winusb_feature_compatble_id_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint16_t wPropertyDataType; | ||||||
|  |     //uint16_t  wPropertyNameLength;
 | ||||||
|  |     //uint8_t   PropertyName[...];
 | ||||||
|  |     //uint16_t  wPropertyDataLength
 | ||||||
|  |     //uint8_t   PropertyData[...];
 | ||||||
|  | } usb_winusb_feature_reg_property_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint16_t wPropertyDataType; | ||||||
|  |     uint16_t wPropertyNameLength; | ||||||
|  |     uint8_t PropertyName[42]; | ||||||
|  |     uint16_t wPropertyDataLength; | ||||||
|  |     uint8_t PropertyData[80]; | ||||||
|  | } usb_winusb_feature_reg_property_guids_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint8_t bResumeRecoveryTime; | ||||||
|  |     uint8_t bResumeSignalingTime; | ||||||
|  | } usb_winusb_feature_min_resume_time_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint8_t ModelID[16]; | ||||||
|  | } usb_winusb_feature_model_id_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  | } usb_winusb_feature_ccgp_device_t; | ||||||
|  | 
 | ||||||
|  | typedef struct USB_PACK { | ||||||
|  |     uint16_t wLength; | ||||||
|  |     uint16_t wDescriptorType; | ||||||
|  |     uint16_t VendorRevision; | ||||||
|  | } usb_winusb_feature_vendor_revision_t; | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Sergey Gavrilov
						Sergey Gavrilov