From 8718c10f03b6a3a1b2ebcc36dfc7d7a7b4d37359 Mon Sep 17 00:00:00 2001 From: Bottersnike Date: Sun, 4 Aug 2024 22:27:46 +0100 Subject: [PATCH] Damn thats a lot --- .gitignore | 3 + .vscode/extensions.json | 3 +- .vscode/settings.json | 63 +- Development.md | 27 - GCC.mk | 2 +- Makefile | 3 - NUC123/NUC123.ld | 124 +- NUC123/startup_NUC123.s | 42 +- README.md | 42 +- docs/Colours.md | 11 + docs/Development.md | 79 ++ ... USB 2 - Passed - 2024-07-23 19-44-26.html | 1089 +++++++++++++++++ ... Tests - Passed - 2024-07-23 19-45-53.html | 349 ++++++ ...ummary - Passed - 2024-07-23 19-46-25.html | 205 ++++ ... Tests - Passed - 2024-07-23 19-46-13.html | 254 ++++ flash.cmd | 32 +- src/_compiler.h | 10 + src/delay.s | 6 - src/descriptors.c | 152 +-- src/fmc.c | 2 +- src/fmc_user.h | 2 +- src/hid.c | 301 ++--- src/hid_def.h | 61 + src/io4.c | 156 +++ src/io4.h | 31 + src/led.c | 582 ++++----- src/led.h | 102 +- src/led_impl.c | 273 +++++ src/led_shared.h | 213 ++++ src/main.c | 138 ++- src/pins.h | 6 +- src/psoc.c | 332 +++-- src/psoc.h | 80 +- src/slider.c | 165 ++- src/slider.h | 43 +- src/sys.c | 164 ++- src/tasoller.h | 252 +--- src/ui.c | 338 +++-- src/usb_def.h | 129 ++ src/usb_inc/keymap.h | 440 ++++++- src/usb_inc/usb.h | 2 + src/usbd_driver.c | 20 +- src/usbd_user.c | 62 +- src/vcom.c | 9 +- src/vcom.h | 25 + 45 files changed, 4903 insertions(+), 1521 deletions(-) delete mode 100644 Development.md create mode 100644 docs/Colours.md create mode 100644 docs/Development.md create mode 100644 docs/USB/Chapter 9 Tests - USB 2 - Passed - 2024-07-23 19-44-26.html create mode 100644 docs/USB/Connector Type Tests - Passed - 2024-07-23 19-45-53.html create mode 100644 docs/USB/Device Summary - Passed - 2024-07-23 19-46-25.html create mode 100644 docs/USB/HID Tests - Passed - 2024-07-23 19-46-13.html create mode 100644 src/_compiler.h delete mode 100644 src/delay.s create mode 100644 src/hid_def.h create mode 100644 src/io4.c create mode 100644 src/io4.h create mode 100644 src/led_impl.c create mode 100644 src/led_shared.h create mode 100644 src/usb_def.h create mode 100644 src/vcom.h diff --git a/.gitignore b/.gitignore index 63ffbd5..66aead2 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,6 @@ picolibc/* # Build artifacts obj/* host_aprom.bin + +# Flashing tool +flashtool/* diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 8236676..a71f8c4 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -2,6 +2,7 @@ "recommendations": [ "aaron-bond.better-comments", "marus25.cortex-debug", - "llvm-vs-code-extensions.vscode-clangd" + "llvm-vs-code-extensions.vscode-clangd", + "aaron-bond.better-comments" ] } \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index 845cb15..8a870d4 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,13 +6,70 @@ "-I${workspaceFolder}/NUC123/inc", "-I${workspaceFolder}/NUC123/StdDriver/inc", "-D__GNUC__", - "-D__XSTRING(x)=", // Workaround for string.h + "-D__XSTRING(x)=", // Workaround for string.h "-Wno-pointer-to-int-cast", - "-Wno-int-to-pointer-cast", + "-Wno-int-to-pointer-cast" ], - "cortex-debug.variableUseNaturalFormat": true, + "cortex-debug.variableUseNaturalFormat": false, "cortex-debug.liveWatchRefreshRate": 1000, "cortex-debug.enableTelemetry": false, "files.trimFinalNewlines": true, "editor.defaultFormatter": "llvm-vs-code-extensions.vscode-clangd", + + "better-comments.tags": [ + { + "tag": "===", + "color": "#00F5FF", + "strikethrough": false, + "underline": false, + "backgroundColor": "#00F5FF11", + "bold": false, + "italic": false + }, + { + "tag": "!", + "color": "#FF2D00", + "strikethrough": false, + "underline": false, + "backgroundColor": "transparent", + "bold": false, + "italic": false + }, + { + "tag": "?", + "color": "#3498DB", + "strikethrough": false, + "underline": false, + "backgroundColor": "transparent", + "bold": false, + "italic": false + }, + { + "tag": "//", + "color": "#474747", + "strikethrough": true, + "underline": false, + "backgroundColor": "transparent", + "bold": false, + "italic": false + }, + { + "tag": "todo", + "color": "#FF8C00", + "strikethrough": false, + "underline": false, + "backgroundColor": "transparent", + "bold": false, + "italic": false + }, + { + "tag": "*", + "color": "#98C379", + "strikethrough": false, + "underline": false, + "backgroundColor": "transparent", + "bold": false, + "italic": false + } + ] } diff --git a/Development.md b/Development.md deleted file mode 100644 index 48273c6..0000000 --- a/Development.md +++ /dev/null @@ -1,27 +0,0 @@ -# Development -## Requirements -- Windows (Strongly recommend. Compiling on anything else is not officially supported.) -- [Make for Windows](https://gnuwin32.sourceforge.net/packages/make.htm) - - On the path, please -- [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) (Currently using 13.2 Rel1) - - To make your life easier, ensure this is located at `C:\Program Files (x86)\GNU Arm Embedded Toolchain\13.2 Rel1` -- [picolibc 1.8.6](https://github.com/picolibc/picolibc/releases/download/1.8.6/picolibc-1.8.6-13.2.Rel1.zip) - - Place the contents of this zip file in `picolibc` - -VSCode is recommended for development, and all recommended extensions should be installed. - -## Compiling and Uploading -Run `make -j` to compile the firmware. - -For rapid development, the HID bootloader is quite annoying. `flash.cmd` is provided which reprograms the chip using an ST-Link V2. This requires an OpenOCD installation in the root project folder, along with an existing compiled host bootloader binary. - -For debugging in VSCode, install the recommended extensions then hit F5. This requires an OpenOCD installation in the root project folder, and an ST-Link V2. - -## NUC123 BSP -The `NUC123` folder is a heavily reduced form of the complete BSP provided by Nuvoton. Modifications should not be made to any files within the `inc` or `StdDriver` folder. - -`startup_NUC123.s` is the program entrypoint, and may require modification. - -`NUC123.ld` is a modified version of the GCC linker script provided by Nuvoton, and may require modification. - -To pull in additional BSP drivers, if required, the `Makefile` should be modified. diff --git a/GCC.mk b/GCC.mk index 0dcb3b8..d9b3b95 100644 --- a/GCC.mk +++ b/GCC.mk @@ -16,6 +16,6 @@ OPTIM ?= -flto -Os GCCFLAGS := $(SPECS) -nolibc -nostdlib -nostartfiles -nodefaultlibs -mcpu=cortex-m0 -mthumb -Wl,--gc-sections $(OPTIM) -g CFLAGS := $(GCCFLAGS) -c -ffunction-sections -fdata-sections -fsigned-char \ - -fmessage-length=0 -ffreestanding + -fmessage-length=0 -ffreestanding -fstack-usage -Wall ASFLAGS := $(GCCFLAGS) -c -x assembler-with-cpp LDFLAGS := $(GCCFLAGS) -Lpicolibc/arm-none-eabi/picolibc/arm-none-eabi/lib/thumb/v6-m/nofp diff --git a/Makefile b/Makefile index 77a4bdd..0f469de 100644 --- a/Makefile +++ b/Makefile @@ -3,12 +3,9 @@ OBJ_DIR := obj BINARY_NAME := host_aprom OPTIM := -O0 -# OPTIM := -Ofast -flto # OPTIM := -Os -flto include GCC.mk LIBRARY_MODULES := clk uart timer i2c include NUC123/NUC123.mk -CFLAGS += -Wno-gnu-variable-sized-type-not-at-end - include generic.mk diff --git a/NUC123/NUC123.ld b/NUC123/NUC123.ld index 5c3ca27..5fa8485 100644 --- a/NUC123/NUC123.ld +++ b/NUC123/NUC123.ld @@ -1,6 +1,5 @@ /* Linker script to configure memory regions. */ -MEMORY -{ +MEMORY { FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x10000 /* 64k */ RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x5000 /* 20k */ } @@ -8,127 +7,45 @@ MEMORY /* Library configurations */ GROUP(libgcc.a libc.a libm.a libnosys.a) -/* Linker script to place sections and symbol values. Should be used together - * with other linker script that defines memory regions FLASH and RAM. - * It references following symbols, which must be defined in code: - * Reset_Handler : Entry of reset handler - * - * It defines following symbols, which code can use without definition: - * __exidx_start - * __exidx_end - * __copy_table_start__ - * __copy_table_end__ - * __zero_table_start__ - * __zero_table_end__ +/** + * Used for loading .data into memory: * __etext * __data_start__ - * __preinit_array_start - * __preinit_array_end - * __init_array_start - * __init_array_end - * __fini_array_start - * __fini_array_end * __data_end__ + * Used for zeroing .bss: * __bss_start__ * __bss_end__ - * __end__ - * end - * __HeapLimit - * __StackLimit - * __StackTop - * __stack - * __Vectors_End - * __Vectors_Size + * Used for stack setup: + * __StackLimit + * __StackTop + * Used for heap setup: + * __HeapBase + * __HeapLimit */ ENTRY(Reset_Handler) -SECTIONS -{ - .text : - { +SECTIONS { + .text : { KEEP(*(.vectors)) - __Vectors_End = .; - __Vectors_Size = __Vectors_End - __Vectors; - __end__ = .; - . = . + 16; - + /* KEEP(*(.init)) */ + /* KEEP(*(.text._entry)) */ *(.text*) - - KEEP(*(.init)) - KEEP(*(.fini)) - - /* .ctors */ - *crtbegin.o(.ctors) - *crtbegin?.o(.ctors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors) - *(SORT(.ctors.*)) - *(.ctors) - - /* .dtors */ - *crtbegin.o(.dtors) - *crtbegin?.o(.dtors) - *(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors) - *(SORT(.dtors.*)) - *(.dtors) - *(.rodata*) - - KEEP(*(.eh_frame*)) } > FLASH - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) - } > FLASH - - __exidx_start = .; - .ARM.exidx : - { - *(.ARM.exidx* .gnu.linkonce.armexidx.*) - } > FLASH - __exidx_end = .; - . = ALIGN(4); __etext = .; - .data : AT (__etext) - { + .data : AT (__etext) { . = ALIGN(4); __data_start__ = .; - *(vtable) *(.data*) - . = ALIGN(4); - /* preinit data */ - PROVIDE_HIDDEN (__preinit_array_start = .); - KEEP(*(.preinit_array)) - PROVIDE_HIDDEN (__preinit_array_end = .); - - . = ALIGN(4); - /* init data */ - PROVIDE_HIDDEN (__init_array_start = .); - KEEP(*(SORT(.init_array.*))) - KEEP(*(.init_array)) - PROVIDE_HIDDEN (__init_array_end = .); - - - . = ALIGN(4); - /* finit data */ - PROVIDE_HIDDEN (__fini_array_start = .); - KEEP(*(SORT(.fini_array.*))) - KEEP(*(.fini_array)) - PROVIDE_HIDDEN (__fini_array_end = .); - - KEEP(*(.jcr*)) - . = ALIGN(4); - /* All data end */ __data_end__ = .; - } > RAM - .bss : - { + .bss : { . = ALIGN(4); __bss_start__ = .; *(.bss*) @@ -137,11 +54,8 @@ SECTIONS __bss_end__ = .; } > RAM - .heap (COPY): - { + .heap (COPY): { __HeapBase = .; - __end__ = .; - end = __end__; KEEP(*(.heap*)) __HeapLimit = .; } > RAM @@ -149,8 +63,7 @@ SECTIONS /* .stack_dummy section doesn't contains any symbols. It is only * used for linker to calculate size of stack sections, and assign * values to stack symbols later */ - .stack_dummy (COPY): - { + .stack_dummy (COPY): { KEEP(*(.stack*)) } > RAM @@ -158,7 +71,6 @@ SECTIONS * size of stack_dummy section */ __StackTop = ORIGIN(RAM) + LENGTH(RAM); __StackLimit = __StackTop - SIZEOF(.stack_dummy); - PROVIDE(__stack = __StackTop); /* Check if data + heap + stack exceeds RAM limit */ ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack") diff --git a/NUC123/startup_NUC123.s b/NUC123/startup_NUC123.s index aaad46a..cc27d67 100644 --- a/NUC123/startup_NUC123.s +++ b/NUC123/startup_NUC123.s @@ -89,33 +89,33 @@ __Vectors: .size __Vectors, . - __Vectors - - .text + // I really want to move this into its own section but GDB messes up mapping when I do + @ .section .init + .section .text .thumb .thumb_func .align 2 .global Reset_Handler - .type Reset_Handler, % function - + .type Reset_Handler, %function Reset_Handler: // Unlock Register - ldr r0, =0x50000100 // REGCTL - movs r1, #0x59 - str r1, [r0] - movs r1, #0x16 - str r1, [r0] - movs r1, #0x88 - str r1, [r0] + ldr r0, =0x50000100 // REGCTL + movs r1, #0x59 + str r1, [r0] + movs r1, #0x16 + str r1, [r0] + movs r1, #0x88 + str r1, [r0] // Init POR - ldr r2, =0x50000024 // PORCTL - movs r1, #0x5A + ldr r2, =0x50000024 // PORCTL + movs r1, #0x5A lsls r1,r1,8 adds r1,r1,#0xA5 - str r1, [r2] + str r1, [r2] // Lock registers - movs r1, #0 - str r1, [r0] + movs r1, #0 + str r1, [r0] /* Single section scheme. * @@ -160,10 +160,12 @@ Reset_Handler: bgt .L_loop3 .L_loop3_done: -#ifndef __ENTRY -#define __ENTRY _entry -#endif - bl __ENTRY + // GCC doesn't realise that we're in a very strict thumb-16 mode here. + // GCC's linker isn't very good at locality. + // These two together mean that we can't perform a simple branch, + // because it'll end up generating a 32-bit instruction. + ldr r0, =_entry + bx r0 .pool .size Reset_Handler, . - Reset_Handler diff --git a/README.md b/README.md index 816d659..1dc8b3c 100644 --- a/README.md +++ b/README.md @@ -39,31 +39,45 @@ If `COM1` is already in use, check what device it is assigned to in Device Manag Pre-chusan Chunithm uses IO3. This firmware does not (and unfortunately cannot) support IO3. It is recommended to enable the HID keyboard mode, and continue to use keyboard input for IRs. -## Configuration -Hold FN2 for configuration. It is not the same as stock DAO. +## Controls +### General +Tap FN1 to insert a coin. FN2 is currently unbound. -- Pad 1/2 (cell 0): Left wing colour +### Configuration +Hold FN1 for configuration: + +- Pad 1/2 (cell 0): Left tower colour - Pad 3/4 (cell 1): Ground colour - Pad 5/6 (cell 2): Ground colour when pressed, and separator colour -- Pad 7/8 (cell 3): Right wing colour +- Pad 7/8 (cell 3): Right tower colour - Pad 9/10 (cell 4): No function - Pad 11/12 (cell 5): Toggle rainbow effect on/off - Pad 13/14 (cell 6): Increase/decrease ground brightness -- Pad 15/16 (cell 7): Increase/decrease wing brightness +- Pad 15/16 (cell 7): Increase/decrease tower brightness - Pad 17/18 (cell 8): No function -- Pad 19/20 (cell 9): No function -- Pad 21/22 (cell 10): No function +- Pad 19/20 (cell 9): System volume up/down +- Pad 21/22 (cell 10): Holds Enter for 5 seconds (insert Aime card) - Pad 23/24 (cell 11): No function - Pad 25/26 (cell 12): Increase/decrease sensitivity - Pad 27/28 (cell 13): No function -- Pad 29/30 (cell 14): Toggle HID keyboard mode -- Pad 31/32 (cell 15): Toggle IO4 emulation mode +- Pad 29/30 (cell 14): No function +- Pad 31/32 (cell 15): Toggle HID keyboard mode -## Calibration -Make sure no hands or objects are near the slider before starting calibration. +### Test menu +To enter the system test menu, double-tap FN2. This will mirror the on-screen controls, and additionally adds controls for the TEST and SERVICE buttons on a real cabinet. Double-tap FN2 again to leave this mode on the controller (note that this is not synced with the game exiting the menu!). -Hold FN1 for two seconds; the slider will flash red for a few seconds, then fill up with a blue bar. +Cell 6 and 7 are the TEST button, and cell 8 and 9 are the SERVICE button. -The slider will then briefly flash green. After this, begin to rub your hands across the slider as much as possible! The cells will turn increasingly green; the greener you can get them the better. Once the dividers have turned green you can save your calibration by pressing FN2. +To access the TEST and SERVICE buttons without entering the test menu, hold FN2 instead. -**Note:** Calibration is a separate process to adjusting the sensor sensitivity. It is recommended to re-calibrate after adjustment of sensitivity. For the best performance, run a re-calibration before every play session to account for changes in your room's temperature and humidity. +## Keyboard Mapping +When the HID keyboard is enabled, the following mapping (UMIGURI defaults) is used: + +``` +I8U7Y6T5R4E3W2Q1 +9KMJNHBGVFCDXSZA +``` + +with the airs mapped as `0OLP,.`. + +Please note that this is slightly different to stock firmware! diff --git a/docs/Colours.md b/docs/Colours.md new file mode 100644 index 0000000..4a06a8c --- /dev/null +++ b/docs/Colours.md @@ -0,0 +1,11 @@ +| | RGB | Approx HSV | +| ----------- | ------- | ----------- | +| Light gates | #910c45 | 334,92 ,57 | +| Cells: | #FEFE00 | 60 ,100,100 | +| Dividers | #FE00FE | 300,100,100 | + + +Something: FF 00 8B (FF) [Magenta] +Something: 30 0A 60 (FF) [Purple?] +Something: 2D 0A 5A (FF) +Something: 2B 0A 55 (FF) diff --git a/docs/Development.md b/docs/Development.md new file mode 100644 index 0000000..7d1c0f2 --- /dev/null +++ b/docs/Development.md @@ -0,0 +1,79 @@ +# Development +## Requirements +- Windows (Strongly recommend. Compiling on anything else is not officially supported.) +- [Make for Windows](https://gnuwin32.sourceforge.net/packages/make.htm) + - On the path, please +- [Arm GNU Toolchain](https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads) (Currently using 13.2 Rel1) + - To make your life easier, ensure this is located at `C:\Program Files (x86)\GNU Arm Embedded Toolchain\13.2 Rel1` +- [picolibc 1.8.6](https://github.com/picolibc/picolibc/releases/download/1.8.6/picolibc-1.8.6-13.2.Rel1.zip) + - Place the contents of this zip file in `picolibc` + +VSCode is recommended for development, and all recommended extensions should be installed. + +## Compiling and Uploading +Run `make -j` to compile the firmware. + +For rapid development, the HID bootloader is quite annoying. `flash.cmd` is provided which reprograms the chip using an ST-Link V2. This requires an OpenOCD installation in the root project folder, along with an existing compiled host bootloader binary. + +For debugging in VSCode, install the recommended extensions then hit F5. This requires an OpenOCD installation in the root project folder, and an ST-Link V2. + +## NUC123 BSP +The `NUC123` folder is a heavily reduced form of the complete BSP provided by Nuvoton. Modifications should not be made to any files within the `inc` or `StdDriver` folder. + +`startup_NUC123.s` is the program entrypoint, and may require modification. + +`NUC123.ld` is a modified version of the GCC linker script provided by Nuvoton, and may require modification. + +To pull in additional BSP drivers, if required, the `Makefile` should be modified. + +## CONFIG Flags + +Stock TASOLLERs ship with Config0=FFFFFF7D, Config1=FFFFFFFF. The breakdown of Config0 is as follows: + +| Bit | Function | Stock Value | Description | +| ----- | ---------- | ----------- | ----------------------------------- | +| 31 | `CWDTEN` | 1 | Watchdog inactive | +| 30 | `CWDTPDEN` | 1 | N/A | +| 29:28 | Reserved | 11 | | +| 27 | `CGPFMFP` | 1 | PF.0/PF.1 used for external crystal | +| 26:24 | `CFOSC` | 111 | 22.11284 MHz internal HIRC | +| 23 | `CBODEN` | 1 | Brown-out detection disabled | +| 22:21 | `CBOV` | 11 | Brown-out voltage is 4.5V | +| 20 | `CBORST` | 1 | Brown-out reset disabled | +| 19:11 | Reserved | 111111111 | | +| 10 | `CIOINI` | 1 | GPIO defaults to quasi-directional | +| 9:8 | Reserved | 11 | | +| 7:6 | `CBS` | 01 | Boot from LDROM without IAP | +| 5:3 | Reserved | 111 | | +| 2 | `DFVSEN` | 1 | Data flash fixed at 4kB | +| 1 | `LOCK` | 0 | Flash memory is locked | +| 0 | `DFEN` | 1 | N/A | + +For debugging, `LOCK` being enabled can be problematic. Unlocking the processor involves performing a complete chip erase, at which point we must provision out bootloader. We can then set config0 to `FFFFFF7F`, allowing access to flash memory for debugging. `flash.cmd` does this. + +Config1 is just used for `DFBADR` and because `DFVSEN=1` this is ignored anyway. + +Of note, neither the watchdog nor brown-out detection is enabled. This custom firmware makes no attempt to alter this, as it would present complications when reverting to stock firmware. I would much prefer to have the watchdog enabled, trust me. + +## USB +This firmware is compliant with the USB specification. The following USB3CV tests are used to verify compliance: + +- `Chapter 9 Tests [USB 2 devices]` +- `Connector Type Tests` +- `HID Tests` + +If you make changes to the USB code, it is recommended to re-run these tests to ensure continued compliance. + +Additionally, the following tests may be run for diagnostics: + +- `Device Summary` +- `Current Measurement Test [USB 2 devices]` + +xHSETT can be used to directly control the USB device, such as sending SUSPEND signals. + +## Airs +See https://www.shinkoh-elecs.jp/wp-content/uploads/2024/05/C_KB1281_1581_24A.pdf + +The loop of wire between the two wings is for connecting emitter pin 3 to detector pin 1. + +The right bottom is an emitter, and then it alternates up from there. diff --git a/docs/USB/Chapter 9 Tests - USB 2 - Passed - 2024-07-23 19-44-26.html b/docs/USB/Chapter 9 Tests - USB 2 - Passed - 2024-07-23 19-44-26.html new file mode 100644 index 0000000..fb23667 --- /dev/null +++ b/docs/USB/Chapter 9 Tests - USB 2 - Passed - 2024-07-23 19-44-26.html @@ -0,0 +1,1089 @@ + + + + + Chapter 9 Tests - USB 2 - 2024-07-23 19-44-26 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
APPLICATION: USB 3 Gen X Command Verifier
+
TEST SUITE: Chapter 9 Tests [USB 2 devices].cvtests
+
OPERATING SYSTEM:
+
WORKSTATION:
+
DATE: Tuesday, July 23, 2024
+
TIME: 07:45:50 PM
+
OPERATOR: Emma
+
NUMBER OF TESTS: 28
+
LOG NAME: Chapter 9 Tests - USB 2 - 2024-07-23 19-44-26
+
RESULT: passed
+
+
+

+
+
Initialize Test Suite
+
+
INFO
Test log initialized.
+
INFO
Log Level: Normal
+
INFO
User Input module initialized
+
INFO
Windows Version 22H2 (OS Build 19045)
+
INFO
GuiCV.exe ver 4.1.0.0
+
INFO
BaseUtilities.dll ver 4.1.0.0
+
INFO
GuiHelper.dll ver 4.1.0.0
+
INFO
ComplianceUtilities.dll ver 4.1.0.0
+
INFO
TestSuiteParser.dll ver 4.1.0.0
+
INFO
xhci_DevIOCTL.dll ver 2.2.7.0
+
INFO
xhci_TestServices.dll ver 2.2.7.0
+
INFO
USBUtilities.dll ver 1.4.5.1
+
INFO
StackSwitcher.dll ver 1.4.5.1
+
INFO
xhci_CVServices.dll ver 2.2.7.0
+
INFO
xhci_USB2_CVServices.dll ver 2.2.7.0
+
INFO
xhci_USB2_Chapter9.dll ver 3.1.3.0
+
INFO
XHCISpecVersion: 1.10.
+
INFO
Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)
+
INFO
User selection from list: "FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000"
+
INFO
Please select USB 2 Device to test
+
INFO
USB 2 Device Under Test is operating at Full Speed.
+
INFO
Topology: XHCI HC -- DUT
+
+
Get Number Of Configurations
+
+
INFO
USB Version number of device: 2.00
+
INFO
Number of configurations: 1.
+
+
Get Number Of Configurations - Other Speed
+
+
INFO
Number of Other Speed configurations: 0.
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:29
+
INFO
Device descriptor length : 0x12
+
INFO
Device descriptor type : 0x1
+
INFO
Major version : 0x2
+
INFO
Minor version : 0x0
+
INFO
Each interface specifies its own device class type
+
INFO
Device sub class : 0x0
+
INFO
Device protocol : 0x0
+
INFO
Device MaxPacketSize0 : 0x40
+
INFO
Looking for Vendor Name File: usbif.json
+
INFO
Vendor information for VendorID : 0xCA3, SEGA CORPORATION
+
INFO
Device ProductID : 0x21
+
INFO
Device BCD : 0x100
+
INFO
Checking iManufacturer String Descriptor: index = 0x01.
+
INFO
String Descriptor : "Bottersnike". (ENGLISH_US)
+
INFO
String Descriptor : "Bottersnike". (NEUTRAL)
+
INFO
Checking iProduct String Descriptor: index = 0x02.
+
INFO
String Descriptor : "TASOLLER". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER". (NEUTRAL)
+
INFO
Checking iSerialNumber String Descriptor: index = 0x03.
+
INFO
String Descriptor : "10000000FFFF000077030000". (ENGLISH_US)
+
INFO
String Descriptor : "10000000FFFF000077030000". (NEUTRAL)
+
INFO
Number of configurations device supports : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:30
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.1 - Device Descriptor Test (Configuration Index 0) - default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:30
+
INFO
Configuration descriptor contains descriptor of type : 0xB (Interface Association Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Number of interface descriptors found 0x4
+
INFO
Number of alternate interface descriptors found : 0x0
+
INFO
Number of endpoint descriptors found : 0x6
+
INFO
Configuration descriptor length : 0x9
+
INFO
Configuration descriptor type : 0x2
+
INFO
Configuration descriptor TotalLength : 0x84
+
INFO
Configuration descriptor NumInterfaces : 0x4
+
INFO
Configuration descriptor ConfigurationValue: 0x1
+
INFO
Checking iConfiguration String Descriptor: index = 0x00.
+
INFO
The device omits this string descriptor.
+
INFO
Configuration descriptor bmAttributes : 0x80
+
INFO
Device does not support remote wake up
+
INFO
MaxPower = 250 PowerMultiplier = 2
+
INFO
Maximum power device requires : 500mA
+
INFO
Device is BUS POWERED
+
INFO
+Stop time: Jul 23, 2024 - 19:44:31
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.2 - Configuration Descriptor Test (Configuration Index 0) - default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:31
+
INFO
+Device has 3 functions, of which 1 has an Interface Association descriptor.
+
INFO
+Interface Association Descriptor :
+
INFO
bLength = 0x08
+
INFO
bDescriptorType = 0x0b
+
INFO
bFirstInterface = 0
+
INFO
bInterfaceCount = 2
+
INFO
bFunctionClass = 0x02
+
INFO
bFunctionSubClass = 0x02
+
INFO
bFunctionProtocol = 0x00
+
INFO
iFunction = 0x04
+
INFO
Running TD 9.8 : String Descriptor Test for iFunction : 0x04.
+
INFO
Checking iFunction String Descriptor: index = 0x04.
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (NEUTRAL)
+
INFO
Found Interface Descriptor with number : 0
+
INFO
Found Interface Descriptor with number : 1
+
INFO
Function #2 does not have an Interface Association Descriptor.
+
INFO
Function #3 does not have an Interface Association Descriptor.
+
INFO
+
+
INFO
+Stop time: Jul 23, 2024 - 19:44:33
+
INFO
Duration: 2 seconds.
+
INFO
Stopping Test [ TD 9.3 Interface Association Descriptor Test (Config 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:33
+
INFO
Bandwidth check passed
+
INFO
Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x02.
+
INFO
Interface class code indicates [CDC-Control] Interface.
+
INFO
Interface sub class : 2 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x02.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x0A.
+
INFO
Interface class code indicates [CDC-Data] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x05.
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (ENGLISH_US)
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (NEUTRAL)
+
INFO
Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x06.
+
INFO
String Descriptor : "TASOLLER HID". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER HID". (NEUTRAL)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:34
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.4 - Interface Descriptor Test (Configuration Index 0) - default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:34
+
INFO
+Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x3, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x10
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x1, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x2, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
+Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x4, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x8
+
INFO
+Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x5, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x6, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:35
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.5 - Endpoint Descriptor Test (Configuration Index 0) - default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:35
+
INFO
Configuration Index 0x00 has a Configuration Value of 1
+
INFO
USB Version supported: 2.00
+
INFO
+
+
INFO
Devices with bcdUSB <= 0x200 must respond to a GET_DESCRIPTOR(BOS) request with a Request Error.
+
INFO
Device Under Test responded with a Request Error to a GET_DESCRIPTOR(BOS) request. (as expected)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:36
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.7 - BOS Descriptor Test - Device State Default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:36
+
INFO
Initialized the device to the Default state. +
+
INFO
Retrieved Device Descriptor.
+
INFO
Device Under Test supports USB Version 0x2.0x00.
+
INFO
Configuration Summary Descriptors are not supported in this version. (Requires BOS descriptor)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:37
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.30 Configuration Summary Descriptor Test - Device State Default: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:37
+
INFO
Device descriptor length : 0x12
+
INFO
Device descriptor type : 0x1
+
INFO
Major version : 0x2
+
INFO
Minor version : 0x0
+
INFO
Each interface specifies its own device class type
+
INFO
Device sub class : 0x0
+
INFO
Device protocol : 0x0
+
INFO
Device MaxPacketSize0 : 0x40
+
INFO
Looking for Vendor Name File: usbif.json
+
INFO
Vendor information for VendorID : 0xCA3, SEGA CORPORATION
+
INFO
Device ProductID : 0x21
+
INFO
Device BCD : 0x100
+
INFO
Checking iManufacturer String Descriptor: index = 0x01.
+
INFO
String Descriptor : "Bottersnike". (ENGLISH_US)
+
INFO
String Descriptor : "Bottersnike". (NEUTRAL)
+
INFO
Checking iProduct String Descriptor: index = 0x02.
+
INFO
String Descriptor : "TASOLLER". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER". (NEUTRAL)
+
INFO
Checking iSerialNumber String Descriptor: index = 0x03.
+
INFO
String Descriptor : "10000000FFFF000077030000". (ENGLISH_US)
+
INFO
String Descriptor : "10000000FFFF000077030000". (NEUTRAL)
+
INFO
Number of configurations device supports : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:38
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.1 - Device Descriptor Test (Configuration Index 0) - addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:38
+
INFO
Configuration descriptor contains descriptor of type : 0xB (Interface Association Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Number of interface descriptors found 0x4
+
INFO
Number of alternate interface descriptors found : 0x0
+
INFO
Number of endpoint descriptors found : 0x6
+
INFO
Configuration descriptor length : 0x9
+
INFO
Configuration descriptor type : 0x2
+
INFO
Configuration descriptor TotalLength : 0x84
+
INFO
Configuration descriptor NumInterfaces : 0x4
+
INFO
Configuration descriptor ConfigurationValue: 0x1
+
INFO
Checking iConfiguration String Descriptor: index = 0x00.
+
INFO
The device omits this string descriptor.
+
INFO
Configuration descriptor bmAttributes : 0x80
+
INFO
Device does not support remote wake up
+
INFO
MaxPower = 250 PowerMultiplier = 2
+
INFO
Maximum power device requires : 500mA
+
INFO
Device is BUS POWERED
+
INFO
+Stop time: Jul 23, 2024 - 19:44:39
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.2 - Configuration Descriptor Test (Configuration Index 0) - addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:39
+
INFO
+Device has 3 functions, of which 1 has an Interface Association descriptor.
+
INFO
+Interface Association Descriptor :
+
INFO
bLength = 0x08
+
INFO
bDescriptorType = 0x0b
+
INFO
bFirstInterface = 0
+
INFO
bInterfaceCount = 2
+
INFO
bFunctionClass = 0x02
+
INFO
bFunctionSubClass = 0x02
+
INFO
bFunctionProtocol = 0x00
+
INFO
iFunction = 0x04
+
INFO
Running TD 9.8 : String Descriptor Test for iFunction : 0x04.
+
INFO
Checking iFunction String Descriptor: index = 0x04.
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (NEUTRAL)
+
INFO
Found Interface Descriptor with number : 0
+
INFO
Found Interface Descriptor with number : 1
+
INFO
Function #2 does not have an Interface Association Descriptor.
+
INFO
Function #3 does not have an Interface Association Descriptor.
+
INFO
+
+
INFO
+Stop time: Jul 23, 2024 - 19:44:40
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.3 Interface Association Descriptor Test (Config 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:40
+
INFO
Bandwidth check passed
+
INFO
Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x02.
+
INFO
Interface class code indicates [CDC-Control] Interface.
+
INFO
Interface sub class : 2 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x02.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x0A.
+
INFO
Interface class code indicates [CDC-Data] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x05.
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (ENGLISH_US)
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (NEUTRAL)
+
INFO
Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x06.
+
INFO
String Descriptor : "TASOLLER HID". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER HID". (NEUTRAL)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:41
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.4 - Interface Descriptor Test (Configuration Index 0) - addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:41
+
INFO
+Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x3, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x10
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x1, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x2, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
+Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x4, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x8
+
INFO
+Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x5, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x6, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:42
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.5 - Endpoint Descriptor Test (Configuration Index 0) - addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:42
+
INFO
Configuration Index 0x00 has a Configuration Value of 1
+
INFO
USB Version supported: 2.00
+
INFO
+
+
INFO
Devices with bcdUSB <= 0x200 must respond to a GET_DESCRIPTOR(BOS) request with a Request Error.
+
INFO
Device Under Test responded with a Request Error to a GET_DESCRIPTOR(BOS) request. (as expected)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:43
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.7 - BOS Descriptor Test - Device State Addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:43
+
INFO
Initialized the device to the Addressed state. +
+
INFO
Retrieved Device Descriptor.
+
INFO
Device Under Test supports USB Version 0x2.0x00.
+
INFO
Configuration Summary Descriptors are not supported in this version. (Requires BOS descriptor)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:44
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.30 Configuration Summary Descriptor Test - Device State Addressed: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:44
+
INFO
Device descriptor length : 0x12
+
INFO
Device descriptor type : 0x1
+
INFO
Major version : 0x2
+
INFO
Minor version : 0x0
+
INFO
Each interface specifies its own device class type
+
INFO
Device sub class : 0x0
+
INFO
Device protocol : 0x0
+
INFO
Device MaxPacketSize0 : 0x40
+
INFO
Looking for Vendor Name File: usbif.json
+
INFO
Vendor information for VendorID : 0xCA3, SEGA CORPORATION
+
INFO
Device ProductID : 0x21
+
INFO
Device BCD : 0x100
+
INFO
Checking iManufacturer String Descriptor: index = 0x01.
+
INFO
String Descriptor : "Bottersnike". (ENGLISH_US)
+
INFO
String Descriptor : "Bottersnike". (NEUTRAL)
+
INFO
Checking iProduct String Descriptor: index = 0x02.
+
INFO
String Descriptor : "TASOLLER". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER". (NEUTRAL)
+
INFO
Checking iSerialNumber String Descriptor: index = 0x03.
+
INFO
String Descriptor : "10000000FFFF000077030000". (ENGLISH_US)
+
INFO
String Descriptor : "10000000FFFF000077030000". (NEUTRAL)
+
INFO
Number of configurations device supports : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:45
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.1 - Device Descriptor Test (Configuration Index 0) - configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:45
+
INFO
Configuration descriptor contains descriptor of type : 0xB (Interface Association Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x24
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x4 (Interface Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x21 (HID Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Configuration descriptor contains descriptor of type : 0x5 (Endpoint Descriptor)
+
INFO
Number of interface descriptors found 0x4
+
INFO
Number of alternate interface descriptors found : 0x0
+
INFO
Number of endpoint descriptors found : 0x6
+
INFO
Configuration descriptor length : 0x9
+
INFO
Configuration descriptor type : 0x2
+
INFO
Configuration descriptor TotalLength : 0x84
+
INFO
Configuration descriptor NumInterfaces : 0x4
+
INFO
Configuration descriptor ConfigurationValue: 0x1
+
INFO
Checking iConfiguration String Descriptor: index = 0x00.
+
INFO
The device omits this string descriptor.
+
INFO
Configuration descriptor bmAttributes : 0x80
+
INFO
Device does not support remote wake up
+
INFO
MaxPower = 250 PowerMultiplier = 2
+
INFO
Maximum power device requires : 500mA
+
INFO
Device is BUS POWERED
+
INFO
Device is currently BUS POWERED
+
INFO
Currently remote wakeup is DISABLED
+
INFO
+Stop time: Jul 23, 2024 - 19:44:46
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.2 - Configuration Descriptor Test (Configuration Index 0) - configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:46
+
INFO
+Device has 3 functions, of which 1 has an Interface Association descriptor.
+
INFO
+Interface Association Descriptor :
+
INFO
bLength = 0x08
+
INFO
bDescriptorType = 0x0b
+
INFO
bFirstInterface = 0
+
INFO
bInterfaceCount = 2
+
INFO
bFunctionClass = 0x02
+
INFO
bFunctionSubClass = 0x02
+
INFO
bFunctionProtocol = 0x00
+
INFO
iFunction = 0x04
+
INFO
Running TD 9.8 : String Descriptor Test for iFunction : 0x04.
+
INFO
Checking iFunction String Descriptor: index = 0x04.
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER Slider Serial (COM1)". (NEUTRAL)
+
INFO
Found Interface Descriptor with number : 0
+
INFO
Found Interface Descriptor with number : 1
+
INFO
Function #2 does not have an Interface Association Descriptor.
+
INFO
Function #3 does not have an Interface Association Descriptor.
+
INFO
+
+
INFO
+Stop time: Jul 23, 2024 - 19:44:47
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.3 Interface Association Descriptor Test (Config 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:47
+
INFO
Bandwidth check passed
+
INFO
Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x02.
+
INFO
Interface class code indicates [CDC-Control] Interface.
+
INFO
Interface sub class : 2 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x02.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x0A.
+
INFO
Interface class code indicates [CDC-Data] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 0 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Device does not use a class-specific protocol on this interface.
+
INFO
Interface descriptor iInterface : 0x0
+
INFO
Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x05.
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (ENGLISH_US)
+
INFO
String Descriptor : "I/O CONTROL BD;15257 ;01;90;1831;6679A;00;GOUT=14_ADIN=8,E_ROTIN=4_COININ=2_SWIN=2,E_UQ1=41,6;". (NEUTRAL)
+
INFO
Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Interface descriptor length : 0x9
+
INFO
Interface descriptor bDescriptorType : 0x4
+
INFO
Interface descriptor bAlternateSetting : 0x0
+
INFO
Interface descriptor bNumEndPoints: 0x2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 0x03.
+
INFO
Interface class code indicates [HID] Interface.
+
INFO
Interface sub class : 0 <not defined>
+
INFO
Interface protocol : 1 <not defined>
+
INFO
Interface descriptor bInterfaceSubClass : 0x00.
+
INFO
Interface descriptor bInterfaceProtocol assigned by the USB-IF : 0x01.
+
INFO
Checking iInterface String Descriptor: index = 0x06.
+
INFO
String Descriptor : "TASOLLER HID". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER HID". (NEUTRAL)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:49
+
INFO
Duration: 2 seconds.
+
INFO
Stopping Test [ TD 9.4 - Interface Descriptor Test (Configuration Index 0) - configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:49
+
INFO
+Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x3, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x10
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x1, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Bulk, Number : 0x2, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x2
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x0
+
INFO
+Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x4, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x8
+
INFO
+Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x5, Direction : IN
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
Endpoint descriptor length : 0x7
+
INFO
Endpoint descriptor type : 0x5
+
INFO
Endpoint Type : Interrupt, Number : 0x6, Direction : OUT
+
INFO
Endpoint descriptor bmAttributes : 0x3
+
INFO
Endpoint descriptor raw MaxPacketSize : 0x40
+
INFO
Endpoint descriptor interval : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:50
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.5 - Endpoint Descriptor Test (Configuration Index 0) - configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:50
+
INFO
Configuration Index 0x00 has a Configuration Value of 1
+
INFO
Initializing the device to the Configured state with Configuration Value 1 +
+
INFO
USB Version supported: 2.00
+
INFO
+
+
INFO
Devices with bcdUSB <= 0x200 must respond to a GET_DESCRIPTOR(BOS) request with a Request Error.
+
INFO
Device Under Test responded with a Request Error to a GET_DESCRIPTOR(BOS) request. (as expected)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:51
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.7 - BOS Descriptor Test (Configuration Index 0) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:51
+
INFO
Testing Interface number : 0x0 Alternate setting : 0x0
+
INFO
Testing EndPoint type : Interrupt, Address : 0x83
+
INFO
Endpoint is currently not halted for endpoint 0x83
+
INFO
Setting feature endpoint halt for endpoint 0x83
+
INFO
Endpoint is halted for endpoint 0x83
+
INFO
Clearing feature endpoint halt for endpoint 0x83
+
INFO
Endpoint is no longer halted for endpoint 0x83
+
INFO
Testing Interface number : 0x1 Alternate setting : 0x0
+
INFO
Testing EndPoint type : Bulk, Address : 0x81
+
INFO
Endpoint is currently not halted for endpoint 0x81
+
INFO
Setting feature endpoint halt for endpoint 0x81
+
INFO
Endpoint is halted for endpoint 0x81
+
INFO
Clearing feature endpoint halt for endpoint 0x81
+
INFO
Endpoint is no longer halted for endpoint 0x81
+
INFO
Testing EndPoint type : Bulk, Address : 0x2
+
INFO
Endpoint is currently not halted for endpoint 0x02
+
INFO
Setting feature endpoint halt for endpoint 0x02
+
INFO
Endpoint is halted for endpoint 0x02
+
INFO
Clearing feature endpoint halt for endpoint 0x02
+
INFO
Endpoint is no longer halted for endpoint 0x02
+
INFO
Testing Interface number : 0x2 Alternate setting : 0x0
+
INFO
Testing EndPoint type : Interrupt, Address : 0x84
+
INFO
Endpoint is currently not halted for endpoint 0x84
+
INFO
Setting feature endpoint halt for endpoint 0x84
+
INFO
Endpoint is halted for endpoint 0x84
+
INFO
Clearing feature endpoint halt for endpoint 0x84
+
INFO
Endpoint is no longer halted for endpoint 0x84
+
INFO
Testing Interface number : 0x3 Alternate setting : 0x0
+
INFO
Testing EndPoint type : Interrupt, Address : 0x85
+
INFO
Endpoint is currently not halted for endpoint 0x85
+
INFO
Setting feature endpoint halt for endpoint 0x85
+
INFO
Endpoint is halted for endpoint 0x85
+
INFO
Clearing feature endpoint halt for endpoint 0x85
+
INFO
Endpoint is no longer halted for endpoint 0x85
+
INFO
Testing EndPoint type : Interrupt, Address : 0x6
+
INFO
Endpoint is currently not halted for endpoint 0x06
+
INFO
Setting feature endpoint halt for endpoint 0x06
+
INFO
Endpoint is halted for endpoint 0x06
+
INFO
Clearing feature endpoint halt for endpoint 0x06
+
INFO
Endpoint is no longer halted for endpoint 0x06
+
INFO
+Stop time: Jul 23, 2024 - 19:44:52
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.9 - Halt Endpoint Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:52
+
INFO
SetConfiguration with configuration value : 0x1
+
INFO
Unconfigured the device
+
INFO
SetConfiguration with configuration value : 0x1
+
INFO
+Stop time: Jul 23, 2024 - 19:44:53
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.13 - SetConfiguration Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:53
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
Retrieved Device Descriptor.
+
INFO
Device Under Test supports USB Version 0x2.0x00.
+
INFO
Configuration Summary Descriptors are not supported in this version. (Requires BOS descriptor)
+
INFO
+Stop time: Jul 23, 2024 - 19:44:54
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.30 Configuration Summary Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:54
+
INFO
Suspended the parent port of the Device Under Test.
+
INFO
Resumed the parent port of the Device Under Test.
+
INFO
Got device descriptor of Device Under Test
+
INFO
+Stop time: Jul 23, 2024 - 19:44:56
+
INFO
Duration: 2 seconds.
+
INFO
Stopping Test [ TD 9.14 - Suspend/Resume Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:56
+
INFO
The device does not support remote wakeup
+
INFO
+Stop time: Jul 23, 2024 - 19:44:57
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.12 - Remote Wakeup Test (Configuration Index 0) - Enabled:: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:57
+
INFO
The device does not support remote wakeup
+
INFO
+Stop time: Jul 23, 2024 - 19:44:58
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.12 - Remote Wakeup Test (Configuration Index 0) - Disabled:: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:58
+
INFO
Checking Device Under Test for LPM L1 Compatibility...
+
INFO
USB version of device is 2.00.
+
INFO
DUT is NOT compatible with LPM.
+
INFO
LPM is NOT required for DUT
+
INFO
LPM is only supported in USB version 2.01 and above.
+
INFO
+Stop time: Jul 23, 2024 - 19:44:59
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ L1Suspend/Resume Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:44:59
+
INFO
Device speed is Full.
+
INFO
Beginning enumeration test with 150 enumerations. This may take several minutes to complete.
+
INFO
Enumerated 150 iterations.
+
INFO
Device tree re-enumerated.
+
INFO
Recovered Device Under Test after device tree re-enumeration.
+
INFO
Device speed of Device Under Test: Full Speed.
+
INFO
+Stop time: Jul 23, 2024 - 19:45:48
+
INFO
Duration: 49 seconds.
+
INFO
Stopping Test [ TD 9.16 - Enumeration Test(repeat 150 times): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+
Display Other Tests To Run
+
+
INFO
Informed user: run Connector Type Tests on this device.
+
INFO
Informed user: run HID Tests on this device.
+
+
Summary
+
+
INFO
TEST SUITE SUMMARY: + [ Fails (0); Aborts (0); Warnings (0) ]
+
INFO
TEST RESULTS: + [ Passed (28); Failed (0) ]
+
+
+ diff --git a/docs/USB/Connector Type Tests - Passed - 2024-07-23 19-45-53.html b/docs/USB/Connector Type Tests - Passed - 2024-07-23 19-45-53.html new file mode 100644 index 0000000..dc88312 --- /dev/null +++ b/docs/USB/Connector Type Tests - Passed - 2024-07-23 19-45-53.html @@ -0,0 +1,349 @@ + + + + + Connector Type Tests - 2024-07-23 19-45-53 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
APPLICATION: USB 3 Gen X Command Verifier
+
TEST SUITE: Connector Type Tests.cvtests
+
OPERATING SYSTEM:
+
WORKSTATION:
+
DATE: Tuesday, July 23, 2024
+
TIME: 07:46:09 PM
+
OPERATOR: Emma
+
NUMBER OF TESTS: 10
+
LOG NAME: Connector Type Tests - 2024-07-23 19-45-53
+
RESULT: passed
+
+
+

+
+
InitializeTestSuite
+
+
INFO
Test log initialized.
+
INFO
Log Level: Normal
+
INFO
User Input module initialized
+
INFO
Windows Version 22H2 (OS Build 19045)
+
INFO
GuiCV.exe ver 4.1.0.0
+
INFO
BaseUtilities.dll ver 4.1.0.0
+
INFO
GuiHelper.dll ver 4.1.0.0
+
INFO
ComplianceUtilities.dll ver 4.1.0.0
+
INFO
TestSuiteParser.dll ver 4.1.0.0
+
INFO
xhci_DevIOCTL.dll ver 2.2.7.0
+
INFO
xhci_TestServices.dll ver 2.2.7.0
+
INFO
USBUtilities.dll ver 1.4.5.1
+
INFO
StackSwitcher.dll ver 1.4.5.1
+
INFO
xhci_CVServices.dll ver 2.2.7.0
+
INFO
xhci_ConnectorType.dll ver 3.1.3.0
+
INFO
XHCISpecVersion: 1.10.
+
INFO
Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)
+
INFO
User selection from list: "FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000"
+
INFO
Please select USB Device to test
+
INFO
USB Device Under Test is operating at Full Speed.
+
INFO
Topology: XHCI HC -- DUT
+
+
Get Number Of Configurations
+
+
INFO
USB Version number of device: 2.00
+
INFO
Number of configurations: 1.
+
+
Get Number Of Other Speed Configurations
+
+
INFO
Number of Other Speed configurations: 0.
+
+
Get Number Of Ports
+
+
INFO
Device Under Test is not a Hub.
+
+
Find Out Which Downstream Facing Ports Are Physically Inaccessible
+
+
INFO
Device Under Test is not a Hub.
+
+
Get Active Power State
+
+
INFO
Acquiring power state helper.
+
INFO
User selected power state: "Standard USB Current".
+
+
Read VIF from file
+
+
INFO
Device Under Test is embedded, and cannot have a Vendor Info File (VIF) for its upstream facing port.
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:45:58
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
Device Under Test does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:45:59
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.1 PD Configuration Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:45:59
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:00
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.2 PD Capability Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:01
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:02
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.3 PD Battery Info Capability Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:02
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:03
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.4 PD Consumer Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:03
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:04
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.5 PD Provider Port Capability Descriptor Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:04
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:05
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.6 PD Battery Status Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:05
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:06
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.7 PD Remote Wake Test - Enabled (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:06
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:07
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.7 Remote Wake Test - Disabled (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:07
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
VIF contains 0 components.
+
INFO
VIF has 0 Upstream Facing Ports.
+
INFO
Device Under Test does not have a BOS descriptor and so does not support PD.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:08
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.8 PD CHARGING_POLICY Test (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:08
+
INFO
Configuration Index 0x00 has a Configuration Value of 1.
+
INFO
Initialized the device to the Configured state with Configuration Value 1. +
+
INFO
Device Under Test is not a hub.
+
INFO
+Stop time: Jul 23, 2024 - 19:46:09
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ TD 9.9 PD Hub Bridge Test on single host (Configuration Index 0x00) - Device State Configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+
PDCommon_CleanupVIFs
+
+
INFO
Cleaning up VIF for Upstream Facing Port
+
INFO
Cleaning up VIF for Downstream Facing Ports.
+
+
Cleanup
+
+
INFO
TEST SUITE SUMMARY: + [ Fails (0); Aborts (0); Warnings (0) ]
+
INFO
TEST RESULTS: + [ Passed (10); Failed (0) ]
+
+
+ diff --git a/docs/USB/Device Summary - Passed - 2024-07-23 19-46-25.html b/docs/USB/Device Summary - Passed - 2024-07-23 19-46-25.html new file mode 100644 index 0000000..6d5d143 --- /dev/null +++ b/docs/USB/Device Summary - Passed - 2024-07-23 19-46-25.html @@ -0,0 +1,205 @@ + + + + + Device Summary - 2024-07-23 19-46-25 + + + + + + + + + + + + + + + + + + + + +
+
APPLICATION: USB 3 Gen X Command Verifier
+
TEST SUITE: Device Summary.cvtests
+
OPERATING SYSTEM:
+
WORKSTATION:
+
DATE: Tuesday, July 23, 2024
+
TIME: 07:46:29 PM
+
OPERATOR: Emma
+
NUMBER OF TESTS: 1
+
LOG NAME: Device Summary - 2024-07-23 19-46-25
+
RESULT: passed
+
+
+

+
+
Initialize Test Suite
+
+
INFO
Test log initialized.
+
INFO
Log Level: Normal
+
INFO
User Input module initialized
+
INFO
Windows Version 22H2 (OS Build 19045)
+
INFO
GuiCV.exe ver 4.1.0.0
+
INFO
BaseUtilities.dll ver 4.1.0.0
+
INFO
GuiHelper.dll ver 4.1.0.0
+
INFO
ComplianceUtilities.dll ver 4.1.0.0
+
INFO
TestSuiteParser.dll ver 4.1.0.0
+
INFO
xhci_DevIOCTL.dll ver 2.2.7.0
+
INFO
xhci_TestServices.dll ver 2.2.7.0
+
INFO
USBUtilities.dll ver 1.4.5.1
+
INFO
StackSwitcher.dll ver 1.4.5.1
+
INFO
xhci_CVServices.dll ver 2.2.7.0
+
INFO
XHCISpecVersion: 1.10.
+
INFO
Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)
+
INFO
User selection from list: "FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000"
+
INFO
Please select USB Device to test
+
INFO
USB Device Under Test is operating at Full Speed.
+
INFO
Topology: XHCI HC -- DUT
+
+
Get Number Of Configurations
+
+
INFO
USB Version number of device: 2.00
+
INFO
Number of configurations: 1.
+
+
Get Number Of Other Speed Configurations
+
+
INFO
Number of Other Speed configurations: 0.
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:28
+
INFO
Host VEN = 1022h +Host DEV = 149ch +Host Revision number = 0h +Host HCI Version = 110 +
+
INFO
Device is attached to port 4
+
INFO
USB Version: 2.00
+
INFO
Looking for Vendor Name File: usbif.json
+
INFO
Device Vendor : 3235 (0xca3) -- SEGA CORPORATION
+
INFO
Device Product ID : 33 (0x21)
+
INFO
Device Revision (bcdDevice) : 0x0100
+
INFO
Checking iProduct String Descriptor: index = 0x02.
+
INFO
String Descriptor : "TASOLLER". (ENGLISH_US)
+
INFO
String Descriptor : "TASOLLER". (NEUTRAL)
+
INFO
bDeviceClass = 0x0
+
INFO
bDeviceProtocol = 0x0
+
INFO
bDeviceSubClass = 0x0
+
INFO
Device Type : HID
+
INFO
Device Speed : Full Speed
+
INFO
Number of configurations device supports : 1
+
INFO
Got configuration descriptor for config index : 0
+
INFO
Full length of configuration descriptor is 132 bytes
+
INFO
Max Power : 500mA
+
INFO
Self-powered : No
+
INFO
Remote Wakeup : No
+
INFO
Number of interfaces device supports : 4
+
INFO
Descriptor for interface 0
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 2
+
INFO
Interface class code indicates [CDC-Control] Interface
+
INFO
Descriptor for interface 1
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : a
+
INFO
Interface class code indicates [CDC-Data] Interface
+
INFO
Descriptor for interface 2
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 3
+
INFO
Interface class code indicates [HID] Interface
+
INFO
Descriptor for interface 3
+
INFO
Interface descriptor bInterfaceClass reserved for assignment by the USB-IF : 3
+
INFO
Interface class code indicates [HID] Interface
+
INFO
+Stop time: Jul 23, 2024 - 19:46:29
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ Device Summary: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+
Summary
+
+
INFO
TEST SUITE SUMMARY: + [ Fails (0); Aborts (0); Warnings (0) ]
+
INFO
TEST RESULTS: + [ Passed (1); Failed (0) ]
+
+
+ diff --git a/docs/USB/HID Tests - Passed - 2024-07-23 19-46-13.html b/docs/USB/HID Tests - Passed - 2024-07-23 19-46-13.html new file mode 100644 index 0000000..e4ffa3e --- /dev/null +++ b/docs/USB/HID Tests - Passed - 2024-07-23 19-46-13.html @@ -0,0 +1,254 @@ + + + + + HID Tests - 2024-07-23 19-46-13 + + + + + + + + + + + + + + + + + + + + + + + + + + +
+
APPLICATION: USB 3 Gen X Command Verifier
+
TEST SUITE: HID Tests.cvtests
+
OPERATING SYSTEM:
+
WORKSTATION:
+
DATE: Tuesday, July 23, 2024
+
TIME: 07:46:21 PM
+
OPERATOR: Emma
+
NUMBER OF TESTS: 5
+
LOG NAME: HID Tests - 2024-07-23 19-46-13
+
RESULT: passed
+
+
+

+
+
Initialize Test Suite
+
+
INFO
Test log initialized.
+
INFO
Log Level: Normal
+
INFO
User Input module initialized
+
INFO
Windows Version 22H2 (OS Build 19045)
+
INFO
GuiCV.exe ver 4.1.0.0
+
INFO
BaseUtilities.dll ver 4.1.0.0
+
INFO
GuiHelper.dll ver 4.1.0.0
+
INFO
ComplianceUtilities.dll ver 4.1.0.0
+
INFO
TestSuiteParser.dll ver 4.1.0.0
+
INFO
xhci_DevIOCTL.dll ver 2.2.7.0
+
INFO
xhci_TestServices.dll ver 2.2.7.0
+
INFO
USBUtilities.dll ver 1.4.5.1
+
INFO
StackSwitcher.dll ver 1.4.5.1
+
INFO
xhci_CVServices.dll ver 2.2.7.0
+
INFO
HidParser.dll ver 1.4.5.0
+
INFO
xhci_HID.dll ver 3.1.3.0
+
INFO
XHCISpecVersion: 1.10.
+
INFO
Host 1 selected: xHCI Host: VID=0x1022, PID=0x149C (PCI bus 45, device 0, function 3)
+
INFO
User selection from list: "FS Device (HID) addr=1: VID=0CA3, PID=0021 RouteString 0x00000"
+
INFO
Please select HID Device to test
+
INFO
HID Device Under Test is operating at Full Speed.
+
INFO
Topology: XHCI HC -- DUT
+
+
Get Number Of Configurations
+
+
INFO
USB Version number of device: 2.00
+
INFO
Number of configurations: 1.
+
+
Get Number Of Configurations - Other Speed
+
+
INFO
Number of Other Speed configurations: 0.
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:16
+
INFO
USB Version number of device: 2.00
+
INFO
Interface 0x2 Alternate Setting 0x0 is draft 4 Compliant
+
INFO
Interface 0x3 Alternate Setting 0x0 is draft 4 Compliant
+
INFO
+Stop time: Jul 23, 2024 - 19:46:17
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ HID Class WhichSpecCompliant Test: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:17
+
INFO
Testing HID descriptor for Interface 0x2
+
INFO
HID descriptor total length : 0x9
+
INFO
HID descriptor type : 0x21
+
INFO
HID specification version : 0x110
+
INFO
CountryCode : 0x0
+
INFO
Number of other descriptors present : 0x1
+
INFO
HID report descriptor length : 0x71
+
INFO
Testing HID descriptor for Interface 0x3
+
INFO
HID descriptor total length : 0x9
+
INFO
HID descriptor type : 0x21
+
INFO
HID specification version : 0x110
+
INFO
CountryCode : 0x0
+
INFO
Number of other descriptors present : 0x1
+
INFO
HID report descriptor length : 0x97
+
INFO
+Stop time: Jul 23, 2024 - 19:46:18
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ HID Class Descriptor Test (Configuration Index 0) - configured: + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:18
+
INFO
Starting GET/SET protocol tests for interface : 2
+
INFO
GET/SET protocol request is optional. Skipping tests
+
INFO
Starting GET/SET protocol tests for interface : 3
+
INFO
GET/SET protocol request is optional. Skipping tests
+
INFO
+Stop time: Jul 23, 2024 - 19:46:19
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ HID Class Protocol Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:19
+
INFO
GET/SETIdle test for report ID 0x1. Setting idle rate to : 0x7F
+
INFO
GET/SETIdle test for report ID 0x1. Setting idle rate to : 0xFF
+
INFO
GET/SETIdle test for report ID 0x1. Setting idle rate to : 0x0
+
INFO
GET/SETIdle test for report ID 0x2. Setting idle rate to : 0x7F
+
INFO
GET/SETIdle test for report ID 0x2. Setting idle rate to : 0xFF
+
INFO
GET/SETIdle test for report ID 0x2. Setting idle rate to : 0x0
+
INFO
GET/SETIdle test for report ID 0x3. Setting idle rate to : 0x7F
+
INFO
GET/SETIdle test for report ID 0x3. Setting idle rate to : 0xFF
+
INFO
GET/SETIdle test for report ID 0x3. Setting idle rate to : 0x0
+
INFO
GET/SETIdle test for report ID 0x4. Setting idle rate to : 0x7F
+
INFO
GET/SETIdle test for report ID 0x4. Setting idle rate to : 0xFF
+
INFO
GET/SETIdle test for report ID 0x4. Setting idle rate to : 0x0
+
INFO
+Stop time: Jul 23, 2024 - 19:46:20
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ HID Class GET/SET Idle Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+ +
+
INFO
Start time: Jul 23, 2024 - 19:46:20
+
INFO
Report descriptor length: 113
+
INFO
Item count in report descriptor: 54
+
INFO
Report descriptor test passed. Proceeding to next descriptor
+
INFO
Report descriptor length: 151
+
INFO
Item count in report descriptor: 71
+
INFO
Report descriptor test passed. Proceeding to next descriptor
+
INFO
+Stop time: Jul 23, 2024 - 19:46:21
+
INFO
Duration: 1 second.
+
INFO
Stopping Test [ HID Class Report Descriptor Test (Configuration Index 0): + Number of: Fails (0); Aborts (0); Warnings (0) ] +
+
+
Summary
+
+
INFO
TEST SUITE SUMMARY: + [ Fails (0); Aborts (0); Warnings (0) ]
+
INFO
TEST RESULTS: + [ Passed (5); Failed (0) ]
+
+
+ diff --git a/flash.cmd b/flash.cmd index eaf1458..f15476d 100644 --- a/flash.cmd +++ b/flash.cmd @@ -1,17 +1,31 @@ -@..\..\openocd-0.12.0-3\bin\openocd.exe -f interface/stlink-v2.cfg -f ..\..\nucxxx.cfg ^ +@ECHO off + +FOR /F "tokens=* USEBACKQ" %%g IN (` + ..\..\openocd-0.12.0-3\bin\openocd.exe -f ..\..\nucxxx.cfg ^ + -c "init; PrintPart; exit" 2^>NUL +`) DO (SET "PART_NUM=%%g") + +IF NOT "%PART_NUM%"=="NUC123SD4AN0" ( + ECHO Invalid part number %PART_NUM%. Make sure you're connected to HOST not LED! + EXIT /B 1 +) + +REM SET LDROM_PATH=../bootloader/host_bl.bin +SET LDROM_PATH=../../../TASOLLER_HOST_LDROM.bin +SET APROM_PATH=host_aprom.bin + +..\..\openocd-0.12.0-3\bin\openocd.exe -f ..\..\nucxxx.cfg ^ -c "SysReset halt" ^ -c "flash read_bank 1 dataflash.bin" ^ -c "ChipErase" ^ - -c "exit" - -@..\..\openocd-0.12.0-3\bin\openocd.exe -f interface/stlink-v2.cfg -f ..\..\nucxxx.cfg ^ - -c "SysReset halt" ^ -c "WriteConfigRegs 0xFFFFFF7F 0xFFFFFFFF" ^ -c "ReadConfigRegs" ^ - -c "program ../bootloader/host_bl.bin 0x100000" ^ - -c "program host_aprom.bin 0" ^ + -c "program %LDROM_PATH% 0x100000" ^ + -c "program %APROM_PATH% 0" ^ -c "program dataflash.bin 0x1F000" ^ + -c "ReadConfigRegs" ^ -c "SysReset aprom run" ^ - -c "exit" + -c "exit" -d0 -@del dataflash.bin +REM Clean up after ourself +del dataflash.bin diff --git a/src/_compiler.h b/src/_compiler.h new file mode 100644 index 0000000..beb2cda --- /dev/null +++ b/src/_compiler.h @@ -0,0 +1,10 @@ +#pragma once + +#ifndef __packed +#if defined(__CC_ARM) +#elif defined(__GNUC__) +#define __packed __attribute__((packed)) +#else +#error Unknown compiler +#endif +#endif diff --git a/src/delay.s b/src/delay.s deleted file mode 100644 index 08349a3..0000000 --- a/src/delay.s +++ /dev/null @@ -1,6 +0,0 @@ -.syntax unified - .global DelayCycles -DelayCycles: - subs r0, r0, #1 - bcs DelayCycles - bx lr diff --git a/src/descriptors.c b/src/descriptors.c index 598604d..4d5cced 100644 --- a/src/descriptors.c +++ b/src/descriptors.c @@ -74,7 +74,7 @@ static const uint8_t IO4_ReportDescriptor[] = { HID_COLLECTION(APPLICATION), HID_USAGE(UNDEFINED), HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(1, 255), + HID_LOGICAL_MAXIMUM(2, 255), HID_REPORT_SIZE(8), HID_REPORT_COUNT(63), HID_OUTPUT(DATA, VARIABLE, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION, @@ -85,7 +85,7 @@ static const uint8_t IO4_ReportDescriptor[] = { }; static const uint8_t Keyboard_ReportDescriptor[] = { - // Keyboard input descriptor + // Keyboard input report HID_USAGE_PAGE(GENERIC_DESKTOP), HID_USAGE(KEYBOARD), HID_COLLECTION(APPLICATION), @@ -93,7 +93,7 @@ static const uint8_t Keyboard_ReportDescriptor[] = { HID_USAGE_PAGE(KEYBOARD), HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(1, 231), + HID_LOGICAL_MAXIMUM(2, 231), HID_USAGE_MINIMUM(1, 0), HID_USAGE_MAXIMUM(1, 231), HID_REPORT_SIZE(8), @@ -102,130 +102,37 @@ static const uint8_t Keyboard_ReportDescriptor[] = { HID_END_COLLECTION(APPLICATION), - // Debugging reports descriptors (they dump the raw PSoC data) - HID_USAGE_PAGE(GENERIC_DESKTOP), - HID_USAGE(JOYSTICK), + // Consumer control report + HID_USAGE_PAGE(CONSUMER), + HID_USAGE(CONSUMER_CONTROL), HID_COLLECTION(APPLICATION), + HID_REPORT_ID(HID_REPORT_ID_CONSUMER_CONTROL), - HID_REPORT_ID(HID_REPORT_ID_DEBUG_A), - HID_USAGE(POINTER), - HID_COLLECTION(LOGICAL), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), + HID_USAGE_PAGE(CONSUMER), + HID_USAGE_MINIMUM(1, 0), + HID_USAGE_MAXIMUM(2, 0x0FFF), HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(4, 0xffff), - HID_PHYSICAL_MINIMUM(1, 0), - HID_PHYSICAL_MAXIMUM(4, 0xffff), - HID_REPORT_COUNT(16), + HID_LOGICAL_MAXIMUM(2, 0x0FFF), HID_REPORT_SIZE(16), - HID_INPUT(DATA, VARIABLE, ABSOLUTE), - HID_END_COLLECTION(LOGICAL), - - HID_REPORT_ID(HID_REPORT_ID_DEBUG_B), - HID_USAGE(POINTER), - HID_COLLECTION(LOGICAL), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(4, 0xffff), - HID_PHYSICAL_MINIMUM(1, 0), - HID_PHYSICAL_MAXIMUM(4, 0xffff), - HID_REPORT_SIZE(16), - HID_REPORT_COUNT(16), - HID_INPUT(DATA, VARIABLE, ABSOLUTE), - HID_END_COLLECTION(LOGICAL), + HID_REPORT_COUNT(2), + HID_INPUT(DATA, ARRAY, ABSOLUTE, NO_WRAP, LINEAR, PREFERRED_STATE, NO_NULL_POSITION), HID_END_COLLECTION(APPLICATION), -}; -static const uint8_t Debug_ReportDescriptor[] = { + // Report for sending the enter key HID_USAGE_PAGE(GENERIC_DESKTOP), - HID_USAGE(JOYSTICK), + HID_USAGE(KEYBOARD), HID_COLLECTION(APPLICATION), + HID_REPORT_ID(HID_REPORT_ID_ENTER), - HID_REPORT_ID(HID_REPORT_ID_DEBUG_A), - HID_USAGE(POINTER), - HID_COLLECTION(LOGICAL), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), + HID_USAGE_PAGE(KEYBOARD), HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(4, 0xffff), - HID_PHYSICAL_MINIMUM(1, 0), - HID_PHYSICAL_MAXIMUM(4, 0xffff), - HID_REPORT_COUNT(16), - HID_REPORT_SIZE(16), - HID_INPUT(DATA, VARIABLE, ABSOLUTE), - HID_END_COLLECTION(LOGICAL), - - HID_REPORT_ID(HID_REPORT_ID_DEBUG_B), - HID_USAGE(POINTER), - HID_COLLECTION(LOGICAL), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_USAGE(X), - HID_USAGE(Y), - HID_LOGICAL_MINIMUM(1, 0), - HID_LOGICAL_MAXIMUM(4, 0xffff), - HID_PHYSICAL_MINIMUM(1, 0), - HID_PHYSICAL_MAXIMUM(4, 0xffff), - HID_REPORT_SIZE(16), - HID_REPORT_COUNT(16), - HID_INPUT(DATA, VARIABLE, ABSOLUTE), - HID_END_COLLECTION(LOGICAL), + HID_LOGICAL_MAXIMUM(2, 231), + HID_USAGE_MINIMUM(1, 0), + HID_USAGE_MAXIMUM(1, 231), + HID_REPORT_SIZE(8), + HID_REPORT_COUNT(1), + HID_INPUT(DATA, ARRAY, ABSOLUTE), HID_END_COLLECTION(APPLICATION), }; @@ -291,7 +198,7 @@ static const config_desc_t gConfigDescriptor = { _USBD_ITF_MAX, 0x01, 0x00, - 0x80 | (USBD_SELF_POWERED << 6) | (USBD_REMOTE_WAKEUP << 5), + 0x80 | USBD_REMOTE_WAKEUP_Msk, USBD_MAX_POWER, }, @@ -313,7 +220,7 @@ static const config_desc_t gConfigDescriptor = { DESC_INTERFACE, USBD_ITF_CDC_CMD, 0x00, - 0x01, + 1, USB_CLASS_CDC, CDC_COMM_SUBCLASS_ABSTRACT_CONTROL_MODEL, CDC_COMM_PROTOCOL_NONE, @@ -389,7 +296,7 @@ static const config_desc_t gConfigDescriptor = { DESC_INTERFACE, USBD_ITF_HID_IO4, 0x00, - 0x01, + 1, USB_CLASS_HID, 0, HID_KEYBOARD, @@ -419,7 +326,7 @@ static const config_desc_t gConfigDescriptor = { DESC_INTERFACE, USBD_ITF_HID_MISC, 0x00, - 0x01, + 2, USB_CLASS_HID, 0, HID_KEYBOARD, @@ -452,12 +359,11 @@ static const config_desc_t gConfigDescriptor = { }, }; -const char* gszVendorInitial = "Bottersnike"; const char* gszVendor = IO4_VENDOR; const char* gszProduct = "TASOLLER"; -const usb_device_descr_t *gpDeviceDescriptor = &gIO4DeviceDescriptor; -const usb_desc_config_t *gpConfigDescriptor = &gConfigDescriptor.Config; +const usb_device_descr_t* gpDeviceDescriptor = &gIO4DeviceDescriptor; +const usb_desc_config_t* gpConfigDescriptor = &gConfigDescriptor.Config; const uint32_t gu32HidDescIO4Offset = (offsetof(config_desc_t, HID_IO4)); const uint32_t gu32HidDescMiscOffset = (offsetof(config_desc_t, HID_Misc)); const uint32_t gu32UsbHidIO4ReportLen = sizeof IO4_ReportDescriptor; diff --git a/src/fmc.c b/src/fmc.c index 222fbe4..1b5b317 100644 --- a/src/fmc.c +++ b/src/fmc.c @@ -51,7 +51,6 @@ void FMC_EEPROM_Load(void) { if (gConfig.u32Magic != DATAFLASH_MAGIC) { // Zeroing flags first means GCC knows we don't care about the other bits gConfig.u8Flags = 0; - gConfig.bEnableIO4 = 1; gConfig.bEnableKeyboard = 0; gConfig.bEnableRainbow = 1; @@ -59,6 +58,7 @@ void FMC_EEPROM_Load(void) { gConfig.u16HueWingLeft = 330; gConfig.u16HueWingRight = 180; + // TODO: These are the DJ DAO defaults, but the game looks like the hue should be 60 and 300 gConfig.u16HueGround = 45; gConfig.u16HueGroundActive = 330; diff --git a/src/fmc_user.h b/src/fmc_user.h index 345351b..04cabc7 100644 --- a/src/fmc_user.h +++ b/src/fmc_user.h @@ -34,7 +34,7 @@ typedef struct __attribute__((aligned(4), packed)) { // Flags union { struct __packed { - uint8_t bEnableIO4 : 1; + uint8_t bRsv00 : 1; uint8_t bEnableKeyboard : 1; uint8_t bEnableRainbow : 1; }; diff --git a/src/hid.c b/src/hid.c index 9c2a0c4..37a9ebf 100644 --- a/src/hid.c +++ b/src/hid.c @@ -1,144 +1,189 @@ #include "tasoller.h" -#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN))) -#define HID_MISC_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_MISC_IN))) +uint16_t u16RequestedConsumerControl = 0; +uint32_t u32EnterPressStarted = 0xFFFFFFFF; + +#define ENTER_HOLD_TIME 5000 // 5 seconds + +#define HID_MISC_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_MISC_IN))) +#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN))) + +#define HID_MISC_SEND(buf) \ + do { \ + USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *(buf)); \ + gu8HIDMiscReady = 0; \ + } while (0) -static const uint8_t u8GroundThreshold = 20; static const uint8_t u8GroundKeymap[32] = { - KEY_I, KEY_COMMA, // - KEY_8, KEY_K, // - KEY_U, KEY_M, // - KEY_7, KEY_J, // - KEY_Y, KEY_N, // - KEY_6, KEY_H, // - KEY_T, KEY_B, // - KEY_5, KEY_G, // - KEY_R, KEY_V, // - KEY_4, KEY_F, // - KEY_E, KEY_C, // - KEY_3, KEY_D, // - KEY_W, KEY_X, // - KEY_2, KEY_S, // - KEY_Q, KEY_Z, // - KEY_1, KEY_A, // + KEY_I, KEY_9, // Dao uses: KEY_I, KEY_COMMA + KEY_8, KEY_K, // + KEY_U, KEY_M, // + KEY_7, KEY_J, // + KEY_Y, KEY_N, // + KEY_6, KEY_H, // + KEY_T, KEY_B, // + KEY_5, KEY_G, // + KEY_R, KEY_V, // + KEY_4, KEY_F, // + KEY_E, KEY_C, // + KEY_3, KEY_D, // + KEY_W, KEY_X, // + KEY_2, KEY_S, // + KEY_Q, KEY_Z, // + KEY_1, KEY_A, // }; static const uint8_t u8AirKeymap[6] = { - HID_KEYBOARD_SLASH_AND_QUESTION_MARK, // VK_OEM_2 - HID_KEYBOARD_PERIOD_AND_GREATER_THAN, // VK_OEM_PERIOD - HID_KEYBOARD_QUOTE_AND_DOUBLEQUOTE, // VK_OEM_7 - HID_KEYBOARD_SEMICOLON_AND_COLON, // VK_OEM_1 - HID_KEYBOARD_RIGHT_BRACKET_AND_RIGHT_CURLY_BRACE, // VK_OEM_6 - HID_KEYBOARD_LEFT_BRACKET_AND_LEFT_CURLY_BRACE, // VK_OEM_4 + // Dao mapping: + // HID_KEYBOARD_SLASH_AND_QUESTION_MARK, // VK_OEM_2 + // HID_KEYBOARD_PERIOD_AND_GREATER_THAN, // VK_OEM_PERIOD + // HID_KEYBOARD_QUOTE_AND_DOUBLEQUOTE, // VK_OEM_7 + // HID_KEYBOARD_SEMICOLON_AND_COLON, // VK_OEM_1 + // HID_KEYBOARD_RIGHT_BRACKET_AND_RIGHT_CURLY_BRACE, // VK_OEM_6 + // HID_KEYBOARD_LEFT_BRACKET_AND_LEFT_CURLY_BRACE, // VK_OEM_4 + + // UMIGURI mapping: + KEY_0, KEY_O, KEY_L, KEY_P, KEY_COMMA, KEY_PERIOD, }; -static inline void _HID_Keyboard_Tick(uint8_t bReal) { - hid_report_t *buf = (hid_report_t *)HID_MISC_BUF; + +static uint8_t _HID_Keyboard_Tick(uint8_t bReleaseAll) { + hid_kbd_report_t *buf = (hid_kbd_report_t *)HID_MISC_BUF; + + // Send a report of zeroes to release all keys + if (bReleaseAll) { + memset(buf, 0, sizeof *buf); + buf->bReportId = HID_REPORT_ID_KEYBOARD; + + HID_MISC_SEND(buf); + return 1; + } + + static uint8_t u8LastButtons = 0; + static uint32_t u32LastSlider = 0; + + // If nothing changed, do nothing + if (gu8DigitalButtons == u8LastButtons && gu32PSoCDigital == u32LastSlider) { + return 0; + } memset(buf, 0, sizeof *buf); buf->bReportId = HID_REPORT_ID_KEYBOARD; uint8_t kI = 0; - if (bReal) { - if (gu8DigitalButtons & 0x01) buf->bKeyboard[kI++] = HID_KEYBOARD_F1; - if (gu8DigitalButtons & 0x02) buf->bKeyboard[kI++] = HID_KEYBOARD_F2; - if (gu8DigitalButtons & 0x04) buf->bKeyboard[kI++] = u8AirKeymap[0]; - if (gu8DigitalButtons & 0x08) buf->bKeyboard[kI++] = u8AirKeymap[1]; - if (gu8DigitalButtons & 0x10) buf->bKeyboard[kI++] = u8AirKeymap[2]; - if (gu8DigitalButtons & 0x20) buf->bKeyboard[kI++] = u8AirKeymap[3]; - if (gu8DigitalButtons & 0x40) buf->bKeyboard[kI++] = u8AirKeymap[4]; - if (gu8DigitalButtons & 0x80) buf->bKeyboard[kI++] = u8AirKeymap[5]; + if (gu8DigitalButtons & DIGITAL_FN2_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F1; + if (gu8DigitalButtons & DIGITAL_FN1_Msk) buf->bKeyboard[kI++] = HID_KEYBOARD_F2; + if (gu8DigitalButtons & 0x04) buf->bKeyboard[kI++] = u8AirKeymap[0]; + if (gu8DigitalButtons & 0x08) buf->bKeyboard[kI++] = u8AirKeymap[1]; + if (gu8DigitalButtons & 0x10) buf->bKeyboard[kI++] = u8AirKeymap[2]; + if (gu8DigitalButtons & 0x20) buf->bKeyboard[kI++] = u8AirKeymap[3]; + if (gu8DigitalButtons & 0x40) buf->bKeyboard[kI++] = u8AirKeymap[4]; + if (gu8DigitalButtons & 0x80) buf->bKeyboard[kI++] = u8AirKeymap[5]; - for (int i = 0; i < 32; i++) { - if (gu8GroundData[i] > u8GroundThreshold) buf->bKeyboard[kI++] = u8GroundKeymap[i]; - } + for (int i = 0; i < 32; i++) { + if (gu32PSoCDigital & (1 << i)) buf->bKeyboard[kI++] = u8GroundKeymap[i]; } - USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *buf); + HID_MISC_SEND(buf); + u8LastButtons = gu8DigitalButtons; + u32LastSlider = gu32PSoCDigital; + return 1; } +static uint8_t _HID_Consumer_Tick(void) { + static uint16_t u16Last = 0; + if (u16Last == u16RequestedConsumerControl) return 0; + u16Last = u16RequestedConsumerControl; -#define IO4_BUTTON_TEST (1 << 9) -#define IO4_BUTTON_SERVICE (1 << 6) - -#define IO4_CMD_SET_COMM_TIMEOUT 0x01 -#define IO4_CMD_SET_SAMPLING_COUNT 0x02 -#define IO4_CMD_CLEAR_BOARD_STATUS 0x03 -#define IO4_CMD_SET_GENERAL_OUTPUT 0x04 -#define IO4_CMD_SET_PWM_OUTPUT 0x05 -#define IO4_CMD_SET_UNIQUE_OUTPUT 0x41 -#define IO4_CMD_UPDATE_FIRMWARE 0x85 - -volatile uint8_t u8IO4SystemStatus = 0; -volatile uint8_t u8IO4USBStatus = 0; -volatile uint16_t u16IO4CommTimeout = 0; -volatile uint8_t u8IO4SamplingCount = 0; - -static void _HID_IO4_Prepare(volatile uint8_t *pu8EpBuf) { - io4_hid_in_t *buf = (io4_hid_in_t *)pu8EpBuf; + hid_consumer_report_t *buf = (hid_consumer_report_t *)HID_MISC_BUF; memset(buf, 0, sizeof *buf); - buf->bReportId = HID_REPORT_ID_IO4; - - // System buttons - if (gu8DigitalButtons & 0x01) buf->wButtons[0] |= IO4_BUTTON_TEST; - if (gu8DigitalButtons & 0x02) buf->wButtons[0] |= IO4_BUTTON_SERVICE; - // Airs - if (!(gu8DigitalButtons & 0x04)) buf->wButtons[0] |= 1 << 13; - if (!(gu8DigitalButtons & 0x08)) buf->wButtons[1] |= 1 << 13; - if (!(gu8DigitalButtons & 0x10)) buf->wButtons[0] |= 1 << 12; - if (!(gu8DigitalButtons & 0x20)) buf->wButtons[1] |= 1 << 12; - if (!(gu8DigitalButtons & 0x40)) buf->wButtons[0] |= 1 << 11; - if (!(gu8DigitalButtons & 0x80)) buf->wButtons[1] |= 1 << 11; - - buf->bUsbStatus = u8IO4USBStatus; - buf->bSystemStatus = u8IO4SystemStatus; + buf->bReportId = HID_REPORT_ID_CONSUMER_CONTROL; + buf->u16Control[0] = u16RequestedConsumerControl; + HID_MISC_SEND(buf); + return 1; } -static void _HID_IO4_Tick() { - _HID_IO4_Prepare(HID_IO4_BUF); - // We must send data every 8ms! None of that "only sending changed keys" stuff - // Trigger a write - gu8HIDIO4Ready = 0; - USBD_SET_PAYLOAD_LEN(EP_HID_IO4_IN, sizeof(io4_hid_in_t)); -} -static void _HID_Debug_Tick() { - debug_hid_report_t *buf = (debug_hid_report_t *)HID_MISC_BUF; - memset(buf, 0, sizeof *buf); +static uint8_t _HID_Enter_Tick(void) { + // TODO: This isn't working, so we're using su8LastState for now instead + if (u32EnterPressStarted == 0xFFFFFFFF) return 0; - static uint8_t bWhich = 0; - if ((bWhich++) & 1) { - buf->bReportId = HID_REPORT_ID_DEBUG_A; - for (uint8_t i = 0; i < 32; i += 2) buf->wData[i / 2] = gu8GroundData[i]; + static uint8_t su8LastState = 0; + uint8_t u8State = 0; + + // There's an _incredibly_ small chance the user tapped the cell at exactly the ms (49 days in!) + // when the timer wrapped round to 0. This is too stupid to account for. + if (u32EnterPressStarted && (MS_SINCE(u32EnterPressStarted) < ENTER_HOLD_TIME)) { + u8State = 1; } else { - buf->bReportId = HID_REPORT_ID_DEBUG_B; - for (uint8_t i = 1; i < 32; i += 2) buf->wData[i / 2] = gu8GroundData[i]; + u8State = 0; + u32EnterPressStarted = 0xFFFFFFFF; } - USBD_SET_PAYLOAD_LEN(EP_HID_MISC_IN, sizeof *buf); + + if (u8State == su8LastState) return 0; + su8LastState = u8State; + + hid_enter_report_t *buf = (hid_enter_report_t *)HID_MISC_BUF; + memset(buf, 0, sizeof *buf); + buf->bReportId = HID_REPORT_ID_ENTER; + if (u8State) buf->u8Keyboard[0] = KEY_ENTER; + + HID_MISC_SEND(buf); + return 1; } -static void _HID_Misc_Tick() { +typedef enum { + TIMESLOT_KEYBOARD = 0, + TIMESLOT_CONSUMER, + TIMESLOT_ENTER, + _TIMESLOT_COUNT, +} eTimeslot_t; +static void _HID_Misc_Tick(void) { static uint8_t sbLastEnableKeyboard = 0; - if (gConfig.bEnableKeyboard) { - sbLastEnableKeyboard = 1; - _HID_Keyboard_Tick(1); - } else if (sbLastEnableKeyboard) { - // If we've just disabled the keyboard, make sure to send a packet with all keys released! - sbLastEnableKeyboard = 0; - _HID_Keyboard_Tick(0); - } - // TODO: gbEnableDebug (we'll need to use a toggle to alternate) - gu8HIDMiscReady = 0; + // We have multiple things we're going to be sending over this HID endpoint, so we timeshare + // which reports are sent. If a particular report has nothing to report in its slot, the next + // report gets a chance instead. + static eTimeslot_t eTimeslot = 0; + + uint8_t u8Tries = _TIMESLOT_COUNT; + while (u8Tries--) { + switch (eTimeslot++) { + case TIMESLOT_KEYBOARD: + if (gConfig.bEnableKeyboard) { + sbLastEnableKeyboard = 1; + if (_HID_Keyboard_Tick(0)) goto timeslot_used; + } else if (sbLastEnableKeyboard) { + // If we've just disabled the keyboard, make sure to send a packet with all keys + // released! + sbLastEnableKeyboard = 0; + if (_HID_Keyboard_Tick(1)) goto timeslot_used; + } + break; + + case TIMESLOT_CONSUMER: + if (_HID_Consumer_Tick()) goto timeslot_used; + break; + + case TIMESLOT_ENTER: + if (_HID_Enter_Tick()) goto timeslot_used; + break; + + default: + break; + } + if (eTimeslot > _TIMESLOT_COUNT) eTimeslot = 0; + } +timeslot_used:; + ; } -void USBD_HID_PrepareReport() { - if (gu8HIDIO4Ready) _HID_IO4_Tick(); +void USBD_HID_PrepareReport(void) { + if (gu8HIDIO4Ready) IO4_HID_Tick(); if (gu8HIDMiscReady) _HID_Misc_Tick(); } static uint8_t sIO4InBuffer[sizeof(io4_hid_in_t)] = { 0 }; uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size) { switch (u8ReportId) { case HID_REPORT_ID_IO4: - if (!gConfig.bEnableIO4) return NULL; - _HID_IO4_Prepare(sIO4InBuffer); + IO4_HID_Prepare(sIO4InBuffer); *pu32Size = sizeof sIO4InBuffer; return sIO4InBuffer; default: @@ -148,51 +193,7 @@ uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size) { void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size) { // TODO: is pu8EpBuf[0] the report ID? // We need to switch on that report ID so we know what we're doing! - if (!gConfig.bEnableIO4) return; if (u32Size < 2) return; - switch (pu8EpBuf[1]) { - case IO4_CMD_SET_COMM_TIMEOUT: - if (u32Size >= 2 + 1) { - u16IO4CommTimeout = (uint16_t)pu8EpBuf[2] * 200; - u8IO4SystemStatus |= 0x10; - - gu8HIDIO4Ready = 1; - _HID_IO4_Tick(); - } - break; - case IO4_CMD_SET_SAMPLING_COUNT: - if (u32Size >= 2 + 1) { - u8IO4SamplingCount = pu8EpBuf[2]; - u8IO4SystemStatus |= 0x20; - - gu8HIDIO4Ready = 1; - _HID_IO4_Tick(); - } - break; - case IO4_CMD_CLEAR_BOARD_STATUS: - u8IO4SystemStatus &= 0x0F; - u8IO4USBStatus &= 0x04; - - gu8HIDIO4Ready = 1; - _HID_IO4_Tick(); - break; - case IO4_CMD_SET_GENERAL_OUTPUT: - if (u32Size >= 2 + 3) { - // 20 bits of data for GPO (+4 of padding) - } - break; - case IO4_CMD_SET_PWM_OUTPUT: - if (u32Size >= 2 + 0) { - // 0 bytes of data for PWM duty cycles (IO4 has no PWM!) - } - break; - case IO4_CMD_SET_UNIQUE_OUTPUT: - if (u32Size >= 2 + 62) { - // 62 bytes of unique output data - } - break; - case IO4_CMD_UPDATE_FIRMWARE: - break; - } + IO4_Control(pu8EpBuf[1], u32Size - 2, &pu8EpBuf[2]); } diff --git a/src/hid_def.h b/src/hid_def.h new file mode 100644 index 0000000..0aefc4c --- /dev/null +++ b/src/hid_def.h @@ -0,0 +1,61 @@ +#pragma once + +#include + +#include "_compiler.h" + +// Triggers to start sending data +extern uint8_t volatile gu8HIDIO4Ready; +extern uint8_t volatile gu8HIDMiscReady; + +extern uint16_t u16RequestedConsumerControl; +extern uint32_t u32EnterPressStarted; + +enum { + HID_REPORT_ID_IO4 = 1, + HID_REPORT_ID_KEYBOARD, + HID_REPORT_ID_CONSUMER_CONTROL, + HID_REPORT_ID_ENTER, + HID_REPORT_ID_IO4_CMD = 16, +}; + +#define NUM_FN 2 +#define NUM_AIR 6 +#define NUM_GROUND 32 + +typedef struct __packed { + uint8_t bReportId; + uint8_t bKeyboard[NUM_FN + NUM_AIR + NUM_GROUND]; +} hid_kbd_report_t; + +typedef struct __packed { + uint8_t bReportId; + uint16_t u16Control[2]; +} hid_consumer_report_t; + +typedef struct __packed { + uint8_t bReportId; + uint8_t u8Keyboard[1]; +} hid_enter_report_t; + +typedef struct __packed { + uint8_t bReportId; + uint16_t wADC[8]; + uint16_t wRotary[4]; + uint16_t wCoin[2]; + uint16_t wButtons[2]; + uint8_t bSystemStatus; + uint8_t bUsbStatus; + uint8_t bUnique[29]; +} io4_hid_in_t; + +typedef struct __packed { + uint8_t bReportId; + uint8_t bCmd; + uint8_t bData[62]; +} io4_hid_out_t; + +// void HID_Tick(); +void USBD_HID_PrepareReport(); +uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size); +void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size); diff --git a/src/io4.c b/src/io4.c new file mode 100644 index 0000000..53a68b0 --- /dev/null +++ b/src/io4.c @@ -0,0 +1,156 @@ +#include "tasoller.h" + +#define HID_IO4_BUF ((uint8_t *)(USBD_BUF_BASE + USBD_GET_EP_BUF_ADDR(EP_HID_IO4_IN))) + +volatile uint8_t u8IO4SystemStatus = 0; +volatile uint8_t u8IO4USBStatus = 0; +volatile uint16_t u16IO4CommTimeout = 0; +volatile uint8_t u8IO4SamplingCount = 0; + +volatile uint16_t u16IO4Coins[2]; + +volatile uint8_t gu8IO4PWMScale; +volatile uint8_t gu8IO4PWMOutput[6]; +volatile uint8_t gu8IO4DigitalOutput[3]; // 20 bits + +uint16_t gu16IO4ForceButtons = 0; +void IO4_HID_Prepare(volatile uint8_t *pu8EpBuf) { + io4_hid_in_t *buf = (io4_hid_in_t *)pu8EpBuf; + + memset(buf, 0, sizeof *buf); + buf->bReportId = HID_REPORT_ID_IO4; + + // System buttons + buf->wButtons[0] = gu16IO4ForceButtons; + // if (gu8DigitalButtons & DIGITAL_FN2_Msk) buf->wButtons[0] |= IO4_BUTTON_TEST; + // if (gu8DigitalButtons & DIGITAL_FN1_Msk) buf->wButtons[0] |= IO4_BUTTON_SERVICE; + // Airs + if (!(gu8DigitalButtons & 0x04)) buf->wButtons[0] |= 1 << 13; + if (!(gu8DigitalButtons & 0x08)) buf->wButtons[1] |= 1 << 13; + if (!(gu8DigitalButtons & 0x10)) buf->wButtons[0] |= 1 << 12; + if (!(gu8DigitalButtons & 0x20)) buf->wButtons[1] |= 1 << 12; + if (!(gu8DigitalButtons & 0x40)) buf->wButtons[0] |= 1 << 11; + if (!(gu8DigitalButtons & 0x80)) buf->wButtons[1] |= 1 << 11; + + buf->bUsbStatus = u8IO4USBStatus; + buf->bSystemStatus = u8IO4SystemStatus; + + // Pos-edge trigger + static uint8_t su8LastDigitalButtons = 0; + if ((gu8DigitalButtons & ~su8LastDigitalButtons) & DIGITAL_FN1_Msk) { + if (!IO4_GetCoinBlocker()) { + u16IO4Coins[0]++; + } + } + su8LastDigitalButtons = gu8DigitalButtons; + + buf->wCoin[0] = BYTESWAP_U16(u16IO4Coins[0]); + buf->wCoin[1] = BYTESWAP_U16(u16IO4Coins[1]); +} + +void IO4_HID_Tick(void) { + IO4_HID_Prepare(HID_IO4_BUF); + + // We must send data every 8ms! None of that "only sending changed keys" stuff + // Trigger a write + gu8HIDIO4Ready = 0; + USBD_SET_PAYLOAD_LEN(EP_HID_IO4_IN, sizeof(io4_hid_in_t)); +} + +void IO4_Control(uint8_t u8Cmd, uint32_t u32Size, volatile uint8_t *pu8Buffer) { + /** + * Setup sequence: + * + * - IO4_CMD_CLEAR_BOARD_STATUS + * - IO4_CMD_SET_COMM_TIMEOUT (00) + * - IO4_CMD_SET_SAMPLING_COUNT (06) + * - IO4_CMD_SET_GENERAL_OUTPUT (00 00 00) + * - IO4_CMD_SET_UNIQUE_OUTPUT (38 00 00 00 ..) + */ + + switch (u8Cmd) { + case IO4_CMD_SET_COMM_TIMEOUT: + if (u32Size >= 1) { + u16IO4CommTimeout = (uint16_t)pu8Buffer[0] * 200; + u8IO4SystemStatus |= 0x10; + + gu8HIDIO4Ready = 1; + IO4_HID_Tick(); + } + break; + case IO4_CMD_SET_SAMPLING_COUNT: + if (u32Size >= 1) { + u8IO4SamplingCount = pu8Buffer[0]; + u8IO4SystemStatus |= 0x20; + + gu8HIDIO4Ready = 1; + IO4_HID_Tick(); + } + break; + case IO4_CMD_CLEAR_BOARD_STATUS: + u8IO4SystemStatus &= 0x0F; + u8IO4USBStatus &= 0x04; + + gu8HIDIO4Ready = 1; + IO4_HID_Tick(); + break; + case IO4_CMD_SET_GENERAL_OUTPUT: + // 20 bits of data for GPO (+4 bits of padding) + if (u32Size >= 3) { + gu8IO4DigitalOutput[0] = pu8Buffer[0]; + gu8IO4DigitalOutput[1] = pu8Buffer[1]; + gu8IO4DigitalOutput[2] = pu8Buffer[2]; + } + break; + case IO4_CMD_SET_PWM_OUTPUT: + if (u32Size >= 0) { + // 0 bytes of data for PWM duty cycles (IO4 has no PWM!) + } + break; + case IO4_CMD_SET_UNIQUE_OUTPUT: + // 62 bytes of unique output data + if (u32Size >= 62) { + // [0]: Enable mask (bit 7=PWM1, 2=PWM6, 0~1 unused) + // [1]: Brightness scaler (1~256, with 0=256) + // [2]: PWM1 brightness (pin CN3.55) + // [3]: PWM2 brightness (pin CN3.56) + // [4]: PWM3 brightness (pin CN9.5 ) + // [5]: PWM4 brightness (pin CN9.6 ) + // [6]: PWM5 brightness (pin CN9.9 ) + // [7]: PWM6 brightness (pin CN9.10) + + // For maimai DX: + // [0]: 11111100 (Enable PWM1~6) + // [1]: 00 + // [2]: 1P Billboard Red + // [3]: 2P Billboard Red + // [4]: 1P Billboard Green + // [5]: 2P Billboard Green + // [6]: 1P Billboard Blue + // [7]: 2P Billboard Blue + // + // For chunithm: + // [0]: 00111000 (Enable PWM3~5) + // [1]: 00 + // [2]: Unused + // [3]: Unused + // [4]: Light gate Blue + // [5]: Light gate Red + // [6]: Light gate Green + // [7]: Unused + + uint8_t u8Mask = pu8Buffer[0]; + if (pu8Buffer[1] == 0) + gu8IO4PWMScale = 255; + else + gu8IO4PWMScale = pu8Buffer[1] - 1; + + for (uint8_t i = 0; i < 6; i++) { + gu8IO4PWMOutput[i] = (u8Mask & (1 << (7 - i))) ? pu8Buffer[2 + i] : 0; + } + } + break; + case IO4_CMD_UPDATE_FIRMWARE: + break; + } +} diff --git a/src/io4.h b/src/io4.h new file mode 100644 index 0000000..26a3e25 --- /dev/null +++ b/src/io4.h @@ -0,0 +1,31 @@ +#pragma once + +#include + +#define IO4_BUTTON_TEST (1 << 9) +#define IO4_BUTTON_SERVICE (1 << 6) + +#define IO4_CMD_SET_COMM_TIMEOUT 0x01 +#define IO4_CMD_SET_SAMPLING_COUNT 0x02 +#define IO4_CMD_CLEAR_BOARD_STATUS 0x03 +#define IO4_CMD_SET_GENERAL_OUTPUT 0x04 +#define IO4_CMD_SET_PWM_OUTPUT 0x05 +#define IO4_CMD_SET_UNIQUE_OUTPUT 0x41 +#define IO4_CMD_84 0x84 +#define IO4_CMD_UPDATE_FIRMWARE 0x85 +#define IO4_CMD_88 0x88 // data[0] = D9 + +extern volatile uint8_t gu8IO4PWMScale; +extern volatile uint8_t gu8IO4PWMOutput[6]; +extern volatile uint8_t gu8IO4DigitalOutput[3]; + +extern uint16_t gu16IO4ForceButtons; + +static inline uint8_t IO4_GetCoinBlocker(void) { + // Chunithm's coin blocker is wired to OUT1 + return (gu8IO4DigitalOutput[0] & 0x80) ? 0 : 1; +} + +void IO4_HID_Prepare(volatile uint8_t *pu8EpBuf); +void IO4_HID_Tick(void); +void IO4_Control(uint8_t u8Cmd, uint32_t u32Size, volatile uint8_t *pu8Buffer); diff --git a/src/led.c b/src/led.c index 7e119a0..cab5f8a 100644 --- a/src/led.c +++ b/src/led.c @@ -1,61 +1,137 @@ #include "tasoller.h" -hsv_t gaControlledIntLedData[LED_NUM_GROUND] = { 0 }; +hsv_t gaControlledIntLedData[LED_NUM_GROUND_LOGICAL] = { 0 }; uint8_t gbLedDataIsControlledInt = 0; -uint8_t gu8aControlledExtLedData[32 * 3]; +rgb_t gaControlledExtLedData[32]; uint8_t gbLedDataIsControlledExt = 0; +uint8_t gbLedIsCustom = 0; -volatile uint8_t gu8LEDTx[LED_Tx_BUFFER]; -static void (*s_I2C1HandlerFn)(uint32_t u32Status) = NULL; +volatile uint8_t gu8LEDTx[LED_PACKET_MAX_SIZE]; -volatile static uint8_t su8LedTxDataLock = 0; +typedef enum : uint8_t { + I2C_SLAVE_TX_REPEAT_START_STOP = 0xA0, + I2C_SLAVE_TX_ADDR_ACK = 0xA8, + I2C_SLAVE_TX_ARBITRATION_LOST = 0xB0, + I2C_SLAVE_TX_DATA_ACK = 0xB8, + I2C_SLAVE_TX_DATA_NACK = 0xC0, + I2C_SLAVE_TX_LAST_DATA_ACK = 0xC8, + I2C_SLAVE_RX_ADDR_ACK = 0x60, + I2C_SLAVE_RX_ARBITRATION_LOST = 0x68, + I2C_SLAVE_RX_DATA_ACK = 0x80, + I2C_SLAVE_RX_DATA_NACK = 0x88, +} I2C_Status_Slave; -// Helper definitions -#define SLAVE_RX_ADDR_ACK 0x60 -#define SLAVE_RX_ACK 0x80 -#define I2C_SLAVE_RX_NACK 0x88 -#define I2C_SLAVE_TX_REPEAT_START_STOP 0xA0 -#define SLAVE_TX_ACK 0xA8 -#define I2C_SLAVE_TX_NACK 0xC0 +volatile uint8_t* gpu8I2CRx = NULL; +volatile uint16_t u16I2CRxIndex = 0; +void I2C1_SlaveTx(I2C_Status_Slave eStatus) { + if (!eStatus) { + // Something went very wrong; restart the I2C controller + I2C_Close(I2C1); + LED_I2C1_Init(); + return; + } + static uint8_t u8Cmd = 0; + static uint16_t su16I2CReadAddr = 0; + + /** + * Bulk data receive request: + * (B0)Rx: START+SLA+W + * (B1)Tx: ACK + * (B2)Rx: [u8Cmd] + * (B3)Tx: ACK + * (B4)Rx: (Repeat START)+SLA+R + * (B5)Tx: ACK + * (B6)Rx: ACK + * (B7)Tx: [u8Data[i]] | + * (B8)Rx: ACK | Looped until Rx:NACK + * Rx: STOP + * + * Single data receive request: + * (S0)Rx: START+SLA+W + * (S1)Tx: ACK + * (S2)Rx: [u8Cmd] + * (S3)Tx: ACK + * Rx: STOP + * --- + * (S4)Rx: START+SLA+R + * (S5)Tx: ACK + */ + switch (eStatus) { + // === Receive address and command === + case I2C_SLAVE_RX_ADDR_ACK: // (B0,S0) + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B1,S1) + break; + case I2C_SLAVE_RX_DATA_ACK: // (B2,S2) + uint8_t u8Data = I2C_GET_DATA(I2C1); + if (u16I2CRxIndex == 0) { + u8Cmd = u8Data; + switch (u8Cmd) { + case LED_I2C_REG_PACKET: + // The master is requesting a read-out of all the data we have in our + // buffer. + su16I2CReadAddr = 0; + break; + default: + // If we don't recognise this command, treat it as a register read. + // (Back-compat with stock LED firmware) + su16I2CReadAddr = u8Cmd; + break; + } + u16I2CRxIndex++; + } else { + // TODO: Currently we don't expose u8Cmd anywhere + if (gpu8I2CRx != NULL) { + gpu8I2CRx[u16I2CRxIndex - 1] = u8Data; + // TODO: Have some bounds checking, and NACK an out of bounds write + } + u16I2CRxIndex++; + } + + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B3,S3) + break; + case I2C_SLAVE_RX_DATA_NACK: + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); + u16I2CRxIndex = 0; + break; + case I2C_SLAVE_TX_REPEAT_START_STOP: // (B4) + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); // (B5) + u16I2CRxIndex = 0; + break; + + // === Transmit our data === + case I2C_SLAVE_TX_ADDR_ACK: // (B6) + case I2C_SLAVE_TX_DATA_ACK: // We got an ACK, and need to continue + I2C_SET_DATA(I2C1, gu8LEDTx[su16I2CReadAddr++]); // (B7) + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); + break; + case I2C_SLAVE_TX_LAST_DATA_ACK: // We got an ACK, but it's time to stop + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI); + u16I2CRxIndex = 0; + break; + case I2C_SLAVE_TX_DATA_NACK: // We got a NACK; master has read enough data + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); + u16I2CRxIndex = 0; + break; + + // === Error cases === + case I2C_SLAVE_RX_ARBITRATION_LOST: // SLA+W + case I2C_SLAVE_TX_ARBITRATION_LOST: // SLA+R + I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); + u16I2CRxIndex = 0; + break; + } +} void I2C1_IRQHandler(void) { if (I2C_GET_TIMEOUT_FLAG(I2C1)) { I2C_ClearTimeoutFlag(I2C1); } else { - if (s_I2C1HandlerFn != NULL) (s_I2C1HandlerFn)(I2C1->I2CSTATUS); - } -} - -void I2C1_SlaveTx(uint32_t u32Status) { - static uint8_t su8I2CReadAddr = 0; - - switch (u32Status) { - case SLAVE_RX_ACK: - su8I2CReadAddr = I2C1->I2CDAT; - su8LedTxDataLock = 1; - I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); - break; - case SLAVE_TX_ACK: - I2C_SET_DATA(I2C1, gu8LEDTx[su8I2CReadAddr]); - I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); - break; - - case SLAVE_RX_ADDR_ACK: - case I2C_SLAVE_TX_NACK: - case I2C_SLAVE_RX_NACK: - case I2C_SLAVE_TX_REPEAT_START_STOP: - I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); - break; - - default: - // Hmm? - I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); - break; + I2C1_SlaveTx(I2C1->I2CSTATUS); } } void LED_I2C1_Init(void) { - I2C_Open(I2C1, 100000); + I2C_Open(I2C1, 400 kHz); I2C_SetSlaveAddr(I2C1, 0, 0x18, 0); I2C_SetSlaveAddr(I2C1, 1, 0x30, 0); I2C_SetSlaveAddr(I2C1, 2, 0x55, 0); @@ -69,344 +145,118 @@ void LED_I2C1_Init(void) { // I2C1 enter no address SLV mode I2C_SET_CONTROL_REG(I2C1, I2C_I2CON_SI_AA); - - s_I2C1HandlerFn = I2C1_SlaveTx; } -// static inline void LEDTxLock(void) { -// gu8LEDTx[0] = 0; -// } -// static inline void LEDTxCommit(uint8_t u8Command, uint8_t u8NExpected) { -// gu8LEDTx[0] = u8Command; -// } +static const uint8_t _LED_GroundBrightness(void) { + if (gbLedDataIsControlledInt) return gConfig.u8LedGroundBrightness; + if (g_u8UsbState == USB_STATE_SUSPEND && gu32NowMs > 5000) return 0; + if (gbLedDataIsControlledExt) { + // The game is going to tell us how bright it wants the LEDs + // For chunithm, that's 40/63 = 63.5% brightness. + // TODO: Do we actually want to do this? Chunithm has no way for operators to change it + // TODO: This won't be reflected in gu8LEDTx[1] with our custom firmware -/** - * @brief Convert from RGB to HSV - * - * @param pu8aRGB Destination 3-tuple to receive RGB values - * @param u16H Hue, ranging 0~LED_HUE_MAX - * @param u8S Saturation, ranging 0~255 - * @param u8V Value, ranging 0~255 - */ -void HsvToRgb(uint8_t* pu8aRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V) { - if (u8S == 0) { - pu8aRGB[0] = u8V; - pu8aRGB[1] = u8V; - pu8aRGB[2] = u8V; - return; + // The real range for this value is 0~63 + // Chunithm will always be sending a constant value of 40 though, as far as I'm aware. + // Because of that, if we performed the scaling we'd be getting 40/63 = 63.5% brightness. + // Instead, we're only going to scale on the off-chance that the brightness is actually + // changed and it goes below 40. + // During startup the brightness is set to 0, but all LEDs are zeroed too so... :D + if (gu8GameBrightness < 40) + return ((uint16_t)gConfig.u8LedGroundBrightness * (uint16_t)gu8GameBrightness) / 63; + return gConfig.u8LedGroundBrightness; } - - uint8_t region = u16H / (LED_HUE_MAX / 6); - uint8_t remainder = (u16H - (region * (LED_HUE_MAX / 6))) * (255 / (LED_HUE_MAX / 6)); - - uint8_t p = (u8V * (255 - u8S)) >> 8; - uint8_t q = (u8V * (255 - ((u8S * remainder) >> 8))) >> 8; - uint8_t t = (u8V * (255 - ((u8S * (255 - remainder)) >> 8))) >> 8; - - switch (region) { - case 0: - pu8aRGB[0] = u8V; - pu8aRGB[1] = t; - pu8aRGB[2] = p; - break; - case 1: - pu8aRGB[0] = q; - pu8aRGB[1] = u8V; - pu8aRGB[2] = p; - break; - case 2: - pu8aRGB[0] = p; - pu8aRGB[1] = u8V; - pu8aRGB[2] = t; - break; - case 3: - pu8aRGB[0] = p; - pu8aRGB[1] = q; - pu8aRGB[2] = u8V; - break; - case 4: - pu8aRGB[0] = t; - pu8aRGB[1] = p; - pu8aRGB[2] = u8V; - break; - default: - pu8aRGB[0] = u8V; - pu8aRGB[1] = p; - pu8aRGB[2] = q; - break; - } - - return; + return gConfig.u8LedGroundBrightness; +} +static const uint8_t _LED_WingBrightness(void) { + if (gbLedDataIsControlledInt) return gConfig.u8LedWingBrightness; + if (g_u8UsbState == USB_STATE_SUSPEND && gu32NowMs > 5000) return 0; + if (gbLedDataIsControlledExt) + return ((uint16_t)gConfig.u8LedGroundBrightness * (uint16_t)gu8IO4PWMScale) / 255; + return gConfig.u8LedWingBrightness; } -// 0x00: Normal operation (all other values ignore ground data) -// 0x01: [Stock] Uses colours. Bar 1 full (from right) -// 0x02: [Stock] Uses colours. Bar 2 full (from right) -// 0x03: [Stock] Uses colours. Bar 3 full (from right) -// 0x04: [Stock] Uses colours. Bar 4 full (from right) -// 0x1X: [Stock] All white with black gaps. X bars black (from right) -// 0x1X: [CFW] Rainbow with black gaps. X bars black (from right) -// 0x20: [CFW] Flashes three times -// 0x80: [Stock] Flashes three times -static uint8_t su8LedSpecial = 0x05; - -// 0: Separator bar every 4 (4 sections) -// 1: Separator bar every 2 (8 sections) -// 2: Separator bar every 1 (16 sections) -// 3: No separator bars -// 4~7: LEDs off -// For some reason this value can be |8, even though firmware suggests otherwise -static uint8_t su8LedSeparators = 1; - -// 0: Invalid, but functions as 1 -// 1: 32-key mode -// 2: 16-key mode (same as 32key mode) -// 3: 8-key mode (bars light in pairs) -// 4: 4-key mode (bars light in quads) -static uint8_t su8LedNKey = 1; - -// 0x40: Turns off ground LEDs -// 0x80: Turns off wing LEDs -static uint8_t su8LedOff = 0; - -// 0x8X: Separator 1~(X+1) lit (ie X ranges from 0~E; F is the same as E) -static uint8_t su8LedCfwRainbow = 0x8F; - -void LED_WriteBasicGrounds(void) { - gu8LEDTx[0] = LED_CMD_BASIC; - - // 32 bits of grounds - // (01,02)=key1, (04,08)=key2 (10,20)=key3, (40,80)=key4 - gu8LEDTx[1] = 0; - gu8LEDTx[2] = 0; - gu8LEDTx[3] = 0; - gu8LEDTx[4] = 0; - for (uint8_t i = 0; i < 4; i++) { - for (uint8_t j = 0; j < 8; j++) { - if (gu8GroundData[i * 8 + j] > PSoC_INTERNAL_DIGITAL_TH) gu8LEDTx[i + 1] |= (1 << j); - } - } - -#ifdef LED_FIRMWARE_CFW - gu8LEDTx[5] = 0; // Wing fill - for (uint8_t i = 0; i < 6; i++) - if (gu8DigitalButtons & (1 << (i + 2))) gu8LEDTx[5] |= 1 << i; - gu8LEDTx[6] = su8LedCfwRainbow; - gu8LEDTx[7] = 0; // Unused -#else - // Wings - gu8LEDTx[5] = 0; // Wing fill - for (uint8_t i = 0; i < 6; i++) - if (gu8DigitalButtons & (1 << (i + 2))) gu8LEDTx[5] |= 1 << i; - gu8LEDTx[6] = gConfig.u16HueWingLeft / LED_HUE_SCALE; // Hue left (default 330) - gu8LEDTx[7] = gConfig.u16HueWingRight / LED_HUE_SCALE; // Hue right (default 180) -#endif - - // Unused in CFW - gu8LEDTx[8] = gConfig.u16HueGround / LED_HUE_SCALE; // Hue ground inactive (default 45) - gu8LEDTx[9] = gConfig.u16HueGroundActive / LED_HUE_SCALE; // Hue ground active (default 330) - - // In CFW only su8LedOff is respected - gu8LEDTx[10] = (su8LedSeparators << 4) | su8LedOff | su8LedNKey; - gu8LEDTx[11] = su8LedSpecial; +static inline void _LED_SetPower(void) { + PIN_LED_GROUND_PWR = _LED_GroundBrightness() ? 1 : 0; + PIN_LED_WING_PWR = _LED_WingBrightness() ? 1 : 0; } -static const uint8_t su8aWingSensors[LED_NUM_LEFT] = { - DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, // - DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, // - DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, // - DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, // - DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, // - DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, // -}; +void LED_Write(void) { + Pled_rx_custom_rgb pTxRGB = (Pled_rx_custom_rgb)gu8LEDTx; + Pled_rx_custom_hsv pTxHSV = (Pled_rx_custom_hsv)gu8LEDTx; + Pled_rx_custom_mixed pTxMix = (Pled_rx_custom_mixed)gu8LEDTx; + // We might not use all of these (we aren't using RGB at the moment!) but they're just + // convenience aliases. + (void)pTxRGB; + (void)pTxHSV; + (void)pTxMix; -#define SATURATION_ACTIVE 255 -#define SATURATION_INACTIVE 240 -#define VALUE_ACTIVE (gConfig.u8LedWingBrightness) -#define VALUE_INACTIVE (gConfig.u8LedWingBrightness / 2) - -static void LED_OffGround(void) { - memset((uint8_t*)&gu8LEDTx[LED_DATA_OFFSET], 0, LED_NUM_GROUND * 3); -} -static void LED_OffWings(void) { - memset((uint8_t*)&gu8LEDTx[LED_DATA_OFFSET + LED_NUM_GROUND * 3], 0, - (LED_NUM_LEFT + LED_NUM_RIGHT) * 3); -} -static void LED_AirWings(void) { - uint8_t u8aRgbActive[3]; - uint8_t u8aRgbInactive[3]; - - uint8_t j, i = LED_NUM_GROUND; - - // Left wing - HsvToRgb(u8aRgbActive, gConfig.u16HueWingLeft, SATURATION_ACTIVE, VALUE_ACTIVE); - HsvToRgb(u8aRgbInactive, gConfig.u16HueWingLeft, SATURATION_INACTIVE, VALUE_INACTIVE); - for (j = 0; i < LED_NUM_GROUND + LED_NUM_LEFT; i++, j++) { - // GRB - if (gu8DigitalButtons & su8aWingSensors[LED_NUM_LEFT - j - 1]) { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbActive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbActive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbActive[2]; - } else { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbInactive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbInactive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbInactive[2]; - } - } - - // Right wing - HsvToRgb(u8aRgbActive, gConfig.u16HueWingRight, SATURATION_ACTIVE, VALUE_ACTIVE); - HsvToRgb(u8aRgbInactive, gConfig.u16HueWingRight, SATURATION_INACTIVE, VALUE_INACTIVE); - for (j = 0; i < LED_NUM_GROUND + LED_NUM_LEFT + LED_NUM_RIGHT; i++, j++) { - // GRB - if (gu8DigitalButtons & su8aWingSensors[j]) { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbActive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbActive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbActive[2]; - } else { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRgbInactive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRgbInactive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRgbInactive[2]; - } - } -} - -static uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) { - return ((uint16_t)u8V * (uint16_t)u8Scale) / 255; -} - -static void LED_GroundRainbow(void) { - uint8_t u8aRGB[3]; - - // 5 ticks * 360 hue = 1800 calls for one cycle (1.8s) - static uint16_t u16Hue = 0; - static uint8_t u8Ticker = 0; - if (++u8Ticker == 5) { - u8Ticker = 0; - u16Hue++; - if (u16Hue == LED_HUE_MAX) u16Hue = 0; - } - - /** - * There are 48 LEDs for ground, but we only send 31 values - * They're mapped to the LEDs as follows: - * 00a11b22c33d44f55g66h77i... - * - * That is, we can't split-colour a key :P - */ - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - uint8_t v = 190; - uint8_t h = 0; - uint8_t nCell = i >> 1; - if (i % 2 == 0) { - if (gu16PSoCDigital & (1 << nCell)) { - v = 255; - h = LED_HUE_MAX / 2; - } - } else if (nCell % 4 == 3) { - h = LED_HUE_MAX / 2; - } - - // GRB - HsvToRgb(u8aRGB, (u16Hue + h + (i * (LED_HUE_MAX / LED_NUM_GROUND))) % LED_HUE_MAX, v, - LED_ScaleU8(v - 63, gConfig.u8LedGroundBrightness)); - - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRGB[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRGB[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRGB[2]; - } -} -static void LED_GroundStatic(void) { - uint8_t u8aGround[3]; - uint8_t u8aGroundActive[3]; - - HsvToRgb(u8aGround, gConfig.u16HueGround, 255, gConfig.u8LedGroundBrightness); - HsvToRgb(u8aGroundActive, gConfig.u16HueGroundActive, 255, gConfig.u8LedGroundBrightness); - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - const uint8_t nCell = i >> 1; - if (i % 2 == 0) { - // This is a cell. Light it according to the touch input - if (gu16PSoCDigital & (1 << nCell)) { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2]; - } else { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGround[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGround[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGround[2]; - } - } else if (nCell % 4 == 3) { - // This is a separating divider. Light it with the active colour - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2]; - } else { - // This is a non-separating divider. Light it based on the two cells either side - if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGroundActive[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGroundActive[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGroundActive[2]; - } else { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aGround[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aGround[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aGround[2]; - } - } - } -} - -void LED_WriteRGB(void) { - gu8LEDTx[0] = LED_CMD_RGB_FULL; - -#ifdef LED_FIRMWARE_CFW - // "CFW" added this byte - gu8LEDTx[1] = su8LedSpecial | su8LedOff; -#endif - - // Even when grounds are disabled, internal control overrides that + // If we're internally controlled, data will be HSV if (gbLedDataIsControlledInt) { - PIN_LED_GROUND_PWR = 1; + pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV; + pTxHSV->u8GroundBrightness = _LED_GroundBrightness(); + pTxHSV->u8WingBrightness = _LED_WingBrightness(); + _LED_SetPower(); - uint8_t u8aRGB[3]; - // Convert from HSV to GRB - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - HsvToRgb(u8aRGB, gaControlledIntLedData[LED_NUM_GROUND - i - 1].u16H, - gaControlledIntLedData[LED_NUM_GROUND - i - 1].u8S, - gaControlledIntLedData[LED_NUM_GROUND - i - 1].u8V); + LED_Ground_Internal_HSV(pTxHSV->aGround); + LED_Wings_Reactive_HSV(&pTxHSV->Wings); + } else if (gbLedDataIsControlledExt) { + // RGB control from the game + pTxRGB->u8Cmd = LED_CMD_CUSTOM_RGB; + pTxRGB->u8GroundBrightness = _LED_GroundBrightness(); + pTxRGB->u8WingBrightness = _LED_WingBrightness(); + _LED_SetPower(); - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = u8aRGB[1]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = u8aRGB[0]; - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = u8aRGB[2]; - } - } else if (gConfig.u8LedGroundBrightness) { - PIN_LED_GROUND_PWR = 1; + LED_Ground_Controlled_RGB(pTxRGB->aGround); + LED_Wings_Controlled_RGB(&pTxRGB->Wings); + } else { + // User-set coloring scheme + pTxHSV->u8Cmd = LED_CMD_CUSTOM_HSV; + pTxHSV->u8GroundBrightness = _LED_GroundBrightness(); + pTxHSV->u8WingBrightness = _LED_WingBrightness(); + _LED_SetPower(); - if (gbLedDataIsControlledExt) { - // Swap from BRG to GRB - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 0] = - LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 2], gConfig.u8LedGroundBrightness); - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 1] = - LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 1], gConfig.u8LedGroundBrightness); - gu8LEDTx[LED_DATA_OFFSET + i * 3 + 2] = - LED_ScaleU8(gu8aControlledExtLedData[i * 3 + 0], gConfig.u8LedGroundBrightness); - } - } else if (gConfig.bEnableRainbow) { - LED_GroundRainbow(); + if (gConfig.bEnableRainbow) { + LED_Ground_Rainbow_HSV(pTxHSV->aGround); } else { - LED_GroundStatic(); + LED_Ground_Static_HSV(pTxHSV->aGround); } - } else { - PIN_LED_GROUND_PWR = 0; - LED_OffGround(); - } - - if (gConfig.u8LedWingBrightness) { - PIN_LED_WING_PWR = 1; - // TODO: Get data from game when gbLedDataIsControlledExt (HID, probably) - LED_AirWings(); - } else { - PIN_LED_WING_PWR = 0; - LED_OffWings(); + LED_Wings_Reactive_HSV(&pTxHSV->Wings); } } + +#define I2C_WaitTimeout (SystemCoreClock / 5); +#define WAIT_INDEX(cond) \ + do { \ + u32TimeOutCnt = I2C_WaitTimeout; \ + while (u16I2CRxIndex cond) { \ + if (--u32TimeOutCnt == 0) return 0; \ + } \ + } while (0) + +uint8_t LED_FMC_Read(uint32_t u32Offset, uint32_t* pu32Data) { + static volatile uint32_t u32Data; + u32Data = 0xFFFFFFFF; + uint32_t u32TimeOutCnt; + + // Wait for anything in the buffer to be read + WAIT_INDEX(!= 0); + WAIT_INDEX(== 0); + // Request a 4-byte read from the LED board + gpu8I2CRx = (volatile uint8_t*)&u32Data; + gu8LEDTx[0] = 0; + gu8LEDTx[1] = u32Offset & 0xff; + gu8LEDTx[2] = (u32Offset >> 8) & 0xff; + gu8LEDTx[3] = (u32Offset >> 16) & 0xff; + gu8LEDTx[4] = (u32Offset >> 24) & 0xff; + gu8LEDTx[0] = LED_CMD_FMC_READ; + + // Wait for our packet to be sent + WAIT_INDEX(!= 0); + WAIT_INDEX(== 0); + // Wait for the LED board to send its response + WAIT_INDEX(!= 5); + + if (pu32Data) *pu32Data = u32Data; + return 1; +} diff --git a/src/led.h b/src/led.h index 63cdd6e..7c460d4 100644 --- a/src/led.h +++ b/src/led.h @@ -1,73 +1,49 @@ #pragma once -#include +#include "led_shared.h" -#define LED_FIRMWARE_CFW -#ifndef LED_FIRMWARE_CFW -#define LED_DATA_OFFSET 1 -#else -#define LED_DATA_OFFSET 2 -#endif +// Defined as volatile due to the I2C interrupt reading at random times! +extern volatile uint8_t gu8LEDTx[LED_PACKET_MAX_SIZE]; -// LED count definitions -#define LED_NUM_GROUND 31 -#define LED_NUM_LEFT 24 -#define LED_NUM_RIGHT 24 - -// HSV definitions -#define LED_HUE_SCALE 5 // For transmission to LED board in basic mode -#define LED_HUE_MAX 360 - -// Host->LED MCU commands -#define LED_CMD_BASIC 0xA5 -#define LED_CMD_RGB_FULL 0x5A - -// LED index names -#define LED_CELL_0 0 -#define LED_CELL_1 2 -#define LED_CELL_2 4 -#define LED_CELL_3 6 -#define LED_CELL_4 8 -#define LED_CELL_5 10 -#define LED_CELL_6 12 -#define LED_CELL_7 14 -#define LED_CELL_8 16 -#define LED_CELL_9 18 -#define LED_CELL_10 20 -#define LED_CELL_11 22 -#define LED_CELL_12 24 -#define LED_CELL_13 26 -#define LED_CELL_14 28 -#define LED_CELL_15 30 - -#define LED_DIVIDER_0_1 1 -#define LED_DIVIDER_1_2 3 -#define LED_DIVIDER_2_3 5 -#define LED_DIVIDER_3_4 7 -#define LED_DIVIDER_4_5 9 -#define LED_DIVIDER_5_6 11 -#define LED_DIVIDER_6_7 13 -#define LED_DIVIDER_7_8 15 -#define LED_DIVIDER_8_9 17 -#define LED_DIVIDER_9_10 19 -#define LED_DIVIDER_10_11 21 -#define LED_DIVIDER_11_12 23 -#define LED_DIVIDER_12_13 25 -#define LED_DIVIDER_13_14 27 -#define LED_DIVIDER_14_15 29 - -typedef struct { - uint16_t u16H; - uint8_t u8S; - uint8_t u8V; -} hsv_t; // Internal LED control, from the settings UI (gets priority) -extern hsv_t gaControlledIntLedData[LED_NUM_GROUND]; +extern hsv_t gaControlledIntLedData[LED_NUM_GROUND_LOGICAL]; extern uint8_t gbLedDataIsControlledInt; // External LED control, from the game -extern uint8_t gu8aControlledExtLedData[32 * 3]; +extern rgb_t gaControlledExtLedData[32]; extern uint8_t gbLedDataIsControlledExt; +// For reception of data from I2C +extern volatile uint8_t* gpu8I2CRx; +extern volatile uint16_t u16I2CRxIndex; + +extern uint8_t gbLedIsCustom; + +// === Assigners (led_impl.c) === +void LED_Wings_Reactive_HSV(_led_wings_hsv* pWings); +void LED_Ground_Rainbow_HSV(hsv_t* aGround); +void LED_Ground_Static_HSV(hsv_t* aGround); +void LED_Ground_Internal_HSV(hsv_t* aGround); +/** All "controlled" data is in RGB format, so these aren't a thing + * void LED_Wings_Controlled_HSV(_led_wings_hsv* pWings); + * void LED_Ground_Controlled_HSV(hsv_t* aGround); + */ + +// Unimplemented nonsense stuff +void LED_Wings_Reactive_RGB(_led_wings_rgb* pWings); +void LED_Ground_Rainbow_RGB(rgb_t* pWings); +void LED_Ground_Internal_RGB(rgb_t* aGround); +void LED_Ground_Static_RGB(rgb_t* aGround); +// Actual RGBs that're used +void LED_Wings_Controlled_RGB(_led_wings_rgb* pWings); +void LED_Ground_Controlled_RGB(rgb_t* aGround); + +// === Exported functions === +void HsvToHost(rgb_t* pRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V); void LED_I2C1_Init(void); -void LED_WriteBasicGrounds(void); -void LED_WriteRGB(void); + +/** + * Write data out to the LEDs + */ +void LED_Write(void); + +uint8_t LED_FMC_Read(uint32_t u32Offset, uint32_t* pu32Data); diff --git a/src/led_impl.c b/src/led_impl.c new file mode 100644 index 0000000..edb0324 --- /dev/null +++ b/src/led_impl.c @@ -0,0 +1,273 @@ +#include "tasoller.h" + +static inline uint8_t LED_ScaleU8(uint8_t u8V, uint8_t u8Scale) { + // When using our custom firmware, don't perform scaling here because it'll be performed on the + // LED processor for us instead +#if LED_FIRMWARE_TYPE == LED_FW_OURS + return u8V; +#else + return ((uint16_t)u8V * (uint16_t)u8Scale) / 255; +#endif +} + +static const uint8_t su8aWingSensors[LED_NUM_WING] = { + DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, DIGITAL_AIR1_Msk, // + DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, DIGITAL_AIR2_Msk, // + DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, DIGITAL_AIR3_Msk, // + DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, DIGITAL_AIR4_Msk, // + DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, DIGITAL_AIR5_Msk, // + DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, DIGITAL_AIR6_Msk, // +}; + +#define SATURATION_ACTIVE 255 +#define SATURATION_INACTIVE 240 +#define VALUE_ACTIVE (gConfig.u8LedWingBrightness) +#define VALUE_INACTIVE (gConfig.u8LedWingBrightness / 2) + +void LED_Wings_Reactive_HSV(_led_wings_hsv* pWings) { + for (uint8_t i = 0; i < LED_NUM_WING; i++) { + // Left wing + if (gu8DigitalButtons & su8aWingSensors[LED_NUM_WING - i - 1]) { + pWings->aWingL[i].h = gConfig.u16HueWingLeft; + pWings->aWingL[i].s = SATURATION_ACTIVE; + pWings->aWingL[i].v = VALUE_ACTIVE; + } else { + pWings->aWingL[i].h = gConfig.u16HueWingLeft; + pWings->aWingL[i].s = SATURATION_INACTIVE; + pWings->aWingL[i].v = VALUE_INACTIVE; + } + + // Right wing + if (gu8DigitalButtons & su8aWingSensors[i]) { + pWings->aWingR[i].h = gConfig.u16HueWingRight; + pWings->aWingR[i].s = SATURATION_ACTIVE; + pWings->aWingR[i].v = VALUE_ACTIVE; + } else { + pWings->aWingR[i].h = gConfig.u16HueWingRight; + pWings->aWingR[i].s = SATURATION_INACTIVE; + pWings->aWingR[i].v = VALUE_INACTIVE; + } + } +} +void LED_Ground_Rainbow_HSV(hsv_t* aGround) { + // 5 ticks * 360 hue = 1800 calls for one cycle (1.8s) + static uint16_t u16Hue = 0; + static uint8_t u8Ticker = 0; + if (++u8Ticker == 5) { + u8Ticker = 0; + u16Hue++; + if (u16Hue == LED_HUE_MAX) u16Hue = 0; + } + + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + uint16_t h = 0; + uint8_t v = 190; + uint8_t nCell = i >> 1; + if (i % 2 == 0) { + if (gu16PSoCDigital & (1 << nCell)) { + v = 255; + h = LED_HUE_MAX / 2; + } + } else if (nCell % 4 == 3) { + h = LED_HUE_MAX / 2; + } + + aGround[i].h = (u16Hue + h + (i * (LED_HUE_MAX / LED_NUM_GROUND_LOGICAL))) % LED_HUE_MAX; + aGround[i].s = v; + aGround[i].v = v - 63; + } +} +void LED_Ground_Static_HSV(hsv_t* aGround) { + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + const uint8_t nCell = i >> 1; + if (i % 2 == 0) { + // This is a cell. Light it according to the touch input + if (gu16PSoCDigital & (1 << nCell)) { + aGround[i].h = gConfig.u16HueGroundActive; + aGround[i].s = 255; + aGround[i].v = 255; + } else { + aGround[i].h = gConfig.u16HueGround; + aGround[i].s = 255; + aGround[i].v = 255; + } + } else if (nCell % 4 == 3) { + // This is a separating divider. Light it with the active colour + aGround[i].h = gConfig.u16HueGroundActive; + aGround[i].s = 255; + aGround[i].v = 255; + } else { + // This is a non-separating divider. Light it based on the two cells either side + if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) { + aGround[i].h = gConfig.u16HueGroundActive; + aGround[i].s = 255; + aGround[i].v = 255; + } else { + aGround[i].h = gConfig.u16HueGround; + aGround[i].s = 255; + aGround[i].v = 255; + } + } + } +} +void LED_Ground_Internal_HSV(hsv_t* aGround) { + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + aGround[i].h = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].h; + aGround[i].s = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].s; + aGround[i].v = gaControlledIntLedData[LED_NUM_GROUND_LOGICAL - i - 1].v; + } +} + +void HsvToHost(rgb_t* pRGB, uint16_t u16H, uint8_t u8S, uint8_t u8V) { + if (u8S == 0) { + pRGB->host.r = u8V; + pRGB->host.g = u8V; + pRGB->host.b = u8V; + return; + } + + uint8_t region = u16H / (LED_HUE_MAX / 6); + uint8_t remainder = (u16H - (region * (LED_HUE_MAX / 6))) * (255 / (LED_HUE_MAX / 6)); + + uint8_t p = (u8V * (255 - u8S)) >> 8; + uint8_t q = (u8V * (255 - ((u8S * remainder) >> 8))) >> 8; + uint8_t t = (u8V * (255 - ((u8S * (255 - remainder)) >> 8))) >> 8; + + switch (region) { + case 0: + pRGB->host.r = u8V; + pRGB->host.g = t; + pRGB->host.b = p; + break; + case 1: + pRGB->host.r = q; + pRGB->host.g = u8V; + pRGB->host.b = p; + break; + case 2: + pRGB->host.r = p; + pRGB->host.g = u8V; + pRGB->host.b = t; + break; + case 3: + pRGB->host.r = p; + pRGB->host.g = q; + pRGB->host.b = u8V; + break; + case 4: + pRGB->host.r = t; + pRGB->host.g = p; + pRGB->host.b = u8V; + break; + default: + pRGB->host.r = u8V; + pRGB->host.g = p; + pRGB->host.b = q; + break; + } + + return; +} + +void LED_Wings_Reactive_RGB(_led_wings_rgb* pWings) { + rgb_t u8aRgbActive; + rgb_t u8aRgbInactive; + + uint8_t i; + + // Left wing + HsvToHost(&u8aRgbActive, gConfig.u16HueWingLeft, SATURATION_ACTIVE, VALUE_ACTIVE); + HsvToHost(&u8aRgbInactive, gConfig.u16HueWingLeft, SATURATION_INACTIVE, VALUE_INACTIVE); + for (i = 0; i < LED_NUM_WING; i++) { + // GRB + if (gu8DigitalButtons & su8aWingSensors[LED_NUM_WING - i - 1]) { + pWings->aWingL[i].host.r = u8aRgbActive.host.r; + pWings->aWingL[i].host.g = u8aRgbActive.host.g; + pWings->aWingL[i].host.b = u8aRgbActive.host.b; + } else { + pWings->aWingL[i].host.r = u8aRgbInactive.host.r; + pWings->aWingL[i].host.g = u8aRgbInactive.host.g; + pWings->aWingL[i].host.b = u8aRgbInactive.host.b; + } + } + + // Right wing + HsvToHost(&u8aRgbActive, gConfig.u16HueWingRight, SATURATION_ACTIVE, VALUE_ACTIVE); + HsvToHost(&u8aRgbInactive, gConfig.u16HueWingRight, SATURATION_INACTIVE, VALUE_INACTIVE); + for (i = 0; i < LED_NUM_WING; i++) { + // GRB + if (gu8DigitalButtons & su8aWingSensors[i]) { + pWings->aWingR[i].host.r = u8aRgbActive.host.r; + pWings->aWingR[i].host.g = u8aRgbActive.host.g; + pWings->aWingR[i].host.b = u8aRgbActive.host.b; + } else { + pWings->aWingR[i].host.r = u8aRgbInactive.host.r; + pWings->aWingR[i].host.g = u8aRgbInactive.host.g; + pWings->aWingR[i].host.b = u8aRgbInactive.host.b; + } + } +} +void LED_Ground_Rainbow_RGB(rgb_t* aGround) { + // TODO: Even bother? + return; +} +void LED_Ground_Static_RGB(rgb_t* aGround) { + // TODO: Even bother? + rgb_t rgbGround; + rgb_t rgbGroundActive; + HsvToHost(&rgbGround, gConfig.u16HueGround, 255, gConfig.u8LedGroundBrightness); + HsvToHost(&rgbGroundActive, gConfig.u16HueGroundActive, 255, gConfig.u8LedGroundBrightness); + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + const uint8_t nCell = i >> 1; + if (i % 2 == 0) { + // This is a cell. Light it according to the touch input + if (gu16PSoCDigital & (1 << nCell)) { + aGround[i].host.r = rgbGroundActive.host.r; + aGround[i].host.g = rgbGroundActive.host.g; + aGround[i].host.b = rgbGroundActive.host.b; + } else { + aGround[i].host.r = rgbGround.host.r; + aGround[i].host.g = rgbGround.host.g; + aGround[i].host.b = rgbGround.host.b; + } + } else if (nCell % 4 == 3) { + // This is a separating divider. Light it with the active colour + aGround[i].host.r = rgbGroundActive.host.r; + aGround[i].host.g = rgbGroundActive.host.g; + aGround[i].host.b = rgbGroundActive.host.b; + } else { + // This is a non-separating divider. Light it based on the two cells either side + if (gu16PSoCDigital & (1 << nCell) && gu16PSoCDigital & (1 << (nCell + 1))) { + aGround[i].host.r = rgbGroundActive.host.r; + aGround[i].host.g = rgbGroundActive.host.g; + aGround[i].host.b = rgbGroundActive.host.b; + } else { + aGround[i].host.r = rgbGround.host.r; + aGround[i].host.g = rgbGround.host.g; + aGround[i].host.b = rgbGround.host.b; + } + } + } +} + +void LED_Wings_Controlled_RGB(_led_wings_rgb* pWings) { + // TODO: Get data from game when gbLedDataIsControlledExt (HID, probably) + + for (uint8_t i = 0; i < LED_NUM_WING; i++) { + // The PWM output is wired as BGR on PWM3~5 + pWings->aWingL[i].host.b = gu8IO4PWMOutput[2]; + pWings->aWingR[i].host.b = gu8IO4PWMOutput[2]; + pWings->aWingL[i].host.r = gu8IO4PWMOutput[3]; + pWings->aWingR[i].host.r = gu8IO4PWMOutput[3]; + pWings->aWingL[i].host.g = gu8IO4PWMOutput[4]; + pWings->aWingR[i].host.g = gu8IO4PWMOutput[4]; + } +} +void LED_Ground_Controlled_RGB(rgb_t* aGround) { + // Swap from BRG(game) to GRB(host) + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + aGround[i].host.r = LED_ScaleU8(gaControlledExtLedData[i].game.r, 255); + aGround[i].host.g = LED_ScaleU8(gaControlledExtLedData[i].game.g, 255); + aGround[i].host.b = LED_ScaleU8(gaControlledExtLedData[i].game.b, 255); + } +} diff --git a/src/led_shared.h b/src/led_shared.h new file mode 100644 index 0000000..c821439 --- /dev/null +++ b/src/led_shared.h @@ -0,0 +1,213 @@ +/** + * !!WARNING!! + * This file is included into the LED APROM firmware too. + * Don't place anything in here that would be inappropriate there. + * !!WARNING!! + */ + +#pragma once + +#include + +#include "_compiler.h" + +// === LED Firmware Type Configuration === +// (Only applicable on the host) +#define LED_FW_STOCK 0 +#define LED_FW_MAINLAND 1 +#define LED_FW_OURS 2 + +// #define LED_FIRMWARE_TYPE LED_FW_STOCK +// #define LED_FIRMWARE_TYPE LED_FW_MAINLAND +#define LED_FIRMWARE_TYPE LED_FW_OURS + +// === LED Count Definitions === +/** + * There are 48 LEDs for ground, but we only send 31 values + * They're mapped to the LEDs as follows: + * 00a11b22c33d44f55g66h77i... + * + * That is, we can't split-colour a key :P + */ +#define LED_NUM_GROUND_LOGICAL 31 +// Ground: [0] = Obscured, [1] = Right, [47] = Left +#define LED_NUM_GROUND_PHYSICAL 48 +// Right wing: [0] = Bottom, [23] = Top +// Left wing: [23] = Bottom, [0] = Top +#define LED_NUM_WING 24 + +// === Colour Byte Order Definitions === +// What we know and love +#define RGB_R 0 +#define RGB_G 1 +#define RGB_B 2 +// What WS2813 uses, and what we receive from the host in stock mode +#define GRB_R 1 +#define GRB_G 0 +#define GRB_B 2 +// Our internal reversed WS2813 format +#define BRG_R 1 +#define BRG_G 2 +#define BRG_B 0 + +#define HOST_FORMAT GRB +#define _CHANNEL(fmt, c) fmt##_##c +#define CHANNEL(fmt, c) _CHANNEL(fmt, c) + +#define HOST_R CHANNEL(HOST_FORMAT, R) +#define HOST_G CHANNEL(HOST_FORMAT, G) +#define HOST_B CHANNEL(HOST_FORMAT, B) + +typedef union { + struct __packed { + uint8_t r; + uint8_t g; + uint8_t b; + } rgb; + // Data received from the game is in BRG format + struct __packed { + uint8_t b; + uint8_t r; + uint8_t g; + } game; + // WS2813 uses GRB, and that's what we received from the host + struct __packed { + uint8_t g; + uint8_t r; + uint8_t b; + } host; + // Internally we use a reversed WS2813 format (BRG) + struct __packed { + uint8_t b; + uint8_t r; + uint8_t g; + } internal; + uint8_t aRaw[3]; +} rgb_t; +typedef struct __packed { + uint16_t h; + uint8_t s; + uint8_t v; +} hsv_t; + +// === Host->LED MCU Commands === +#define LED_I2C_REG_PACKET 0xFF + +#define LED_CMD_BASIC 0xA5 +#define LED_CMD_RGB_FULL 0x5A +#define LED_CMD_CUSTOM_RGB 0x5B +#define LED_CMD_CUSTOM_HSV 0x5C +#define LED_CMD_CUSTOM_MIXED 0x5D +#define LED_CMD_FMC_READ 0x5E +#define LED_CMD_FMC_ENTER_LDROM 0x5F + +// === HOST->LED MCU Packets === +typedef struct __packed { + rgb_t aWingL[LED_NUM_WING]; + rgb_t aWingR[LED_NUM_WING]; +} _led_wings_rgb; +typedef struct __packed { + hsv_t aWingL[LED_NUM_WING]; + hsv_t aWingR[LED_NUM_WING]; +} _led_wings_hsv; + +typedef struct __packed { + uint8_t u8Cmd; + uint32_t u32Ground; + + uint8_t u8WingFill; +#if LED_FIRMWARE_TYPE == LED_FW_MAINLAND + uint8_t u8Rainbow; + uint8_t u8Rsv07; + uint8_t u8Rsv08; + uint8_t u8Rsv09; +#else + uint8_t u8HueLeft; + uint8_t u8HueRight; + uint8_t u8HueGround; + uint8_t u8HueGroundActive; +#endif + uint8_t u8Flags0; + uint8_t u8Flags1; +} led_rx_basic, *Pled_rx_basic; + +typedef struct __packed { + uint8_t u8Cmd; +#if LED_FIRMWARE_TYPE == LED_FW_MAINLAND + uint8_t u8Config; +#endif + rgb_t aGround[LED_NUM_GROUND_LOGICAL]; + _led_wings_rgb Wings; +} led_rx_full, *Pled_rx_full; + +typedef struct __packed { + uint8_t u8Cmd; + uint8_t u8GroundBrightness; + uint8_t u8WingBrightness; + rgb_t aGround[LED_NUM_GROUND_LOGICAL]; + _led_wings_rgb Wings; +} led_rx_custom_rgb, *Pled_rx_custom_rgb; + +typedef struct __packed { + uint8_t u8Cmd; + uint8_t u8GroundBrightness; + uint8_t u8WingBrightness; + rgb_t aGround[LED_NUM_GROUND_LOGICAL]; + _led_wings_hsv Wings; +} led_rx_custom_mixed, *Pled_rx_custom_mixed; + +typedef struct __packed { + uint8_t u8Cmd; + uint8_t u8GroundBrightness; + uint8_t u8WingBrightness; + hsv_t aGround[LED_NUM_GROUND_LOGICAL]; + _led_wings_hsv Wings; +} led_rx_custom_hsv, *Pled_rx_custom_hsv; + +typedef struct __packed { + uint8_t u8Cmd; + uint32_t u32Offset; +} led_rx_fmc_read, *Pled_rx_fmc_read; + +#define LED_PACKET_MAX_SIZE \ + Maximum(Maximum(Maximum(sizeof(led_rx_basic), sizeof(led_rx_full)), \ + Maximum(sizeof(led_rx_custom_rgb), sizeof(led_rx_custom_hsv))), \ + sizeof(led_rx_custom_mixed)) + +// === LED index names === +#define LED_CELL_0 0 +#define LED_CELL_1 2 +#define LED_CELL_2 4 +#define LED_CELL_3 6 +#define LED_CELL_4 8 +#define LED_CELL_5 10 +#define LED_CELL_6 12 +#define LED_CELL_7 14 +#define LED_CELL_8 16 +#define LED_CELL_9 18 +#define LED_CELL_10 20 +#define LED_CELL_11 22 +#define LED_CELL_12 24 +#define LED_CELL_13 26 +#define LED_CELL_14 28 +#define LED_CELL_15 30 + +#define LED_DIVIDER_0_1 1 +#define LED_DIVIDER_1_2 3 +#define LED_DIVIDER_2_3 5 +#define LED_DIVIDER_3_4 7 +#define LED_DIVIDER_4_5 9 +#define LED_DIVIDER_5_6 11 +#define LED_DIVIDER_6_7 13 +#define LED_DIVIDER_7_8 15 +#define LED_DIVIDER_8_9 17 +#define LED_DIVIDER_9_10 19 +#define LED_DIVIDER_10_11 21 +#define LED_DIVIDER_11_12 23 +#define LED_DIVIDER_12_13 25 +#define LED_DIVIDER_13_14 27 +#define LED_DIVIDER_14_15 29 + +// === HSV Definitions === +#define LED_HUE_SCALE 5 // For transmission to LED board in basic mode +#define LED_HUE_MAX 360 diff --git a/src/main.c b/src/main.c index 083381c..752099c 100644 --- a/src/main.c +++ b/src/main.c @@ -2,69 +2,70 @@ uint8_t volatile gu8HIDIO4Ready = 0; uint8_t volatile gu8HIDMiscReady = 0; +uint32_t gu32NowMs = 0; -#define DEBOUNCE_COUNT 8 +#define NUM_BUTTONS 8 +#define DEBOUNCE_ON (2 * 4) // We need a digital high for 2ms to latch on +#define DEBOUNCE_OFF (8 * 4) // Then a digital low for 8ms to latch off uint8_t TickDebouncer(uint8_t u8Buttons) { - static uint8_t u8Counters[8] = { 0 }; - uint8_t debounced = 0; - for (uint8_t i = 0; i < 8; i++) { - if (u8Buttons & (1 << i)) - u8Counters[i] = 0; - else if (u8Counters[i] < DEBOUNCE_COUNT) - u8Counters[i]++; + static uint8_t u8CountUp[NUM_BUTTONS] = { 0 }; + static uint8_t u8CountDown[NUM_BUTTONS] = { 0 }; - if (u8Counters[i] < DEBOUNCE_COUNT) debounced |= (1 << i); + uint8_t debounced = 0; + for (uint8_t i = 0; i < NUM_BUTTONS; i++) { + if (u8Buttons & (1 << i)) { + if (u8CountUp[i] < DEBOUNCE_ON) + u8CountUp[i]++; + else + u8CountDown[i] = DEBOUNCE_OFF; + } else { + if (u8CountDown[i]) + u8CountDown[i]--; + else + u8CountUp[i] = 0; + } + + if (u8CountDown[i]) debounced |= (1 << i); } return debounced; } +/** + * @brief Perform a sensor read using Quasi-bidirectional Mode + * + * See TRM section 6.5.5.4 for details + */ +static inline uint8_t DigitalRead(volatile uint32_t* pData, GPIO_T* pGpio, uint32_t u32Pin) { + GPIO_SetMode(pGpio, u32Pin, GPIO_PMD_INPUT); + *pData = 1; + DELAY_CYCLES_2; + DELAY_CYCLES_2; + DELAY_CYCLES_2; + GPIO_SetMode(pGpio, u32Pin, GPIO_PMD_QUASI); + DELAY_CYCLES_2; + DELAY_CYCLES_2; + DELAY_CYCLES_2; + return *pData; +} + uint8_t gu8DigitalButtons; -#define DELAY_FN 2 -#define DELAY_AIR 2 void Digital_TickInputs() { uint8_t u8Buttons = 0; - GPIO_SetMode(_PIN_FN1, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_FN); - GPIO_SetMode(_PIN_FN1, GPIO_PMD_QUASI); - u8Buttons |= PIN_FN1 ? 0 : DIGITAL_FN1_Msk; + // The two FN buttons need a quasi read cycle + u8Buttons |= DigitalRead(&PIN_FN1, _PIN_FN1) ? 0 : DIGITAL_FN2_Msk; + u8Buttons |= DigitalRead(&PIN_FN2, _PIN_FN1) ? 0 : DIGITAL_FN1_Msk; - GPIO_SetMode(_PIN_FN2, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_FN); - GPIO_SetMode(_PIN_FN2, GPIO_PMD_QUASI); - u8Buttons |= PIN_FN2 ? 0 : DIGITAL_FN2_Msk; + // The 6 AIR sensors drive a ~4.5V signal on the pin, so are just in INPUT mode + u8Buttons |= DigitalRead(&PIN_AIR1, _PIN_AIR1) ? DIGITAL_AIR1_Msk : 0; + u8Buttons |= DigitalRead(&PIN_AIR2, _PIN_AIR2) ? DIGITAL_AIR2_Msk : 0; + u8Buttons |= DigitalRead(&PIN_AIR3, _PIN_AIR3) ? DIGITAL_AIR3_Msk : 0; + u8Buttons |= DigitalRead(&PIN_AIR4, _PIN_AIR4) ? DIGITAL_AIR4_Msk : 0; + u8Buttons |= DigitalRead(&PIN_AIR5, _PIN_AIR5) ? DIGITAL_AIR5_Msk : 0; + u8Buttons |= DigitalRead(&PIN_AIR6, _PIN_AIR6) ? DIGITAL_AIR6_Msk : 0; - GPIO_SetMode(_PIN_AIR1, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR1, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR1 ? DIGITAL_AIR1_Msk : 0; - - GPIO_SetMode(_PIN_AIR2, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR2, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR2 ? DIGITAL_AIR2_Msk : 0; - - GPIO_SetMode(_PIN_AIR3, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR3, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR3 ? DIGITAL_AIR3_Msk : 0; - - GPIO_SetMode(_PIN_AIR4, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR4, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR4 ? DIGITAL_AIR4_Msk : 0; - - GPIO_SetMode(_PIN_AIR5, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR5, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR5 ? DIGITAL_AIR5_Msk : 0; - - GPIO_SetMode(_PIN_AIR6, GPIO_PMD_INPUT); - DelayCycles_Small(DELAY_AIR); - GPIO_SetMode(_PIN_AIR6, GPIO_PMD_QUASI); - u8Buttons |= PIN_AIR6 ? DIGITAL_AIR6_Msk : 0; - - gu8DigitalButtons = TickDebouncer(u8Buttons); + u8Buttons = TickDebouncer(u8Buttons); + gu8DigitalButtons = u8Buttons; } int _entry(void) { @@ -78,7 +79,7 @@ int _entry(void) { FMC_EEPROM_Load(); - gu8VcomReady = 1; + gu8VComReady = 1; gu8HIDIO4Ready = 1; gu8HIDMiscReady = 1; @@ -88,20 +89,30 @@ int _entry(void) { * delay added first. * To make matters worse, if we ever actually call PSoCSetFingerCapacitance it ends up with * huge asymmetry between the two PSoCs, which is just unworkable. + * + * Instead, see the non-blocking logic in the 1ms tick block. */ // uint16_t u16InitialFingerCap = PSoCGetFingerCapacitance(); // if (u16InitialFingerCap != gu16PSoCFingerCap) { - // PSoCSetFingerCapacitance(gu16PSoCFingerCap); + // PSoCSetFingerCapacitance(gu16PSoCFingerCap, 1); // } - static uint32_t su32NowMs = 0; - su32NowMs++; + gbLedIsCustom = LED_FMC_Read(0, NULL); + + gu32NowMs = 0; + uint8_t bPSoCHasTalked = 0; + while (1) { USB_VCOM_Tick(); - if (bPSoCDirty) { - bPSoCDirty = 0; + if (bPSoCDirtyVolatile) { + bPSoCDirtyVolatile = 0; PSoC_PostProcessing(); + + if (!bPSoCHasTalked) { + PSoC_SetFingerCapacitanceFromConfig(1); + bPSoCHasTalked = 1; + } } Slider_TickSerial(); @@ -114,24 +125,17 @@ int _entry(void) { } if (gu8Do1msTick) { - su32NowMs++; + // Update timer + gu32NowMs++; gu8Do1msTick = 0; Slider_Tick1ms(); + USB_VCOM_Tick(); PSoC_DigitalCalc(); UI_Tick(); - // LED_WriteBasicGrounds(); - LED_WriteRGB(); - } /* else { - DelayCycles(45); - }*/ - - // Wait until we know both PSoC are awake and talking before we attempt configure them - if (gu8PSoCSeenData == 0b011) { - PSoC_SetFingerCapacitanceFromConfig(); - gu8PSoCSeenData |= 0b100; + LED_Write(); } FMC_EEPROM_Store(); diff --git a/src/pins.h b/src/pins.h index b4d4b6a..33507a3 100644 --- a/src/pins.h +++ b/src/pins.h @@ -46,16 +46,16 @@ #define USB_MUX_HOST 0 #define USB_MUX_LEDS 1 -#define DIGITAL_FN1_Pos 0 -#define DIGITAL_FN2_Pos 1 +#define DIGITAL_FN2_Pos 0 +#define DIGITAL_FN1_Pos 1 #define DIGITAL_AIR1_Pos 2 #define DIGITAL_AIR2_Pos 3 #define DIGITAL_AIR3_Pos 4 #define DIGITAL_AIR4_Pos 5 #define DIGITAL_AIR5_Pos 6 #define DIGITAL_AIR6_Pos 7 -#define DIGITAL_FN1_Msk (1 << DIGITAL_FN1_Pos) #define DIGITAL_FN2_Msk (1 << DIGITAL_FN2_Pos) +#define DIGITAL_FN1_Msk (1 << DIGITAL_FN1_Pos) #define DIGITAL_AIR1_Msk (1 << DIGITAL_AIR1_Pos) #define DIGITAL_AIR2_Msk (1 << DIGITAL_AIR2_Pos) #define DIGITAL_AIR3_Msk (1 << DIGITAL_AIR3_Pos) diff --git a/src/psoc.c b/src/psoc.c index c3cebef..8afb4c2 100644 --- a/src/psoc.c +++ b/src/psoc.c @@ -1,6 +1,18 @@ #include "tasoller.h" +// For some reason, any level of optimisation +// #pragma GCC diagnostic push +// #pragma GCC diagnostic warning "-Wpedantic" +// #pragma GCC push_options +// #pragma GCC optimize ("O0") + uint16_t gu16PSoCDiff[32] = { 0 }; +// Touch threshold as calculated by SmartSense. Has hysteresis built in. +// 0 is an insane default, so instead we default to 100 which is far less likely to break things :) +uint16_t gu16PSoCThreshold[32] = { + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, + 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, 100, +}; uint32_t gu32PSoCDigital = 0; uint32_t gu32PSoCDigitalPos = 0; @@ -11,13 +23,16 @@ uint16_t gu16PSoCDigitalPos = 0; uint16_t gu16PSoCDigitalNeg = 0; uint16_t gu16PSoCDigitalTrig = 0; -uint8_t gu8PSoCSeenData = 0; +uint32_t gu32LastCapSenseStart = 0; +uint32_t gu32LastCapSenseEnd = 0; -volatile uint8_t bPSoCDirty = 0; +volatile uint8_t bPSoCDirtyVolatile = 0; +volatile uint8_t bForceSliderSend = 0; static void* pu8PsocRxDestination = NULL; static uint8_t pu8PsocRxDestinationLen = 0; -static volatile uint8_t* pu8PsocGotData = NULL; +static volatile uint8_t* volatile pu8PsocGotData = NULL; +static volatile PSoC_CMD_RX eBlockingCommand = _PSoC_CMD_RX_NONE; static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { if (eCmd != PSoC_CMD_RX_SLAVE_DIFF && eCmd != PSoC_CMD_RX_MASTER_DIFF && eCmd != PSoC_CMD_RX_CS_START) { @@ -25,40 +40,76 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { debug++; } - uint8_t u8PsocDataOffset = 0; switch (eCmd) { case PSoC_CMD_RX_REQUEST_FINGER_CAP: - PSoC_SetFingerCapacitanceFromConfig(); + // We're in the IRQ here, so don't block on this! + PSoC_SetFingerCapacitanceFromConfig(0); return; - // We don't care about these + // Debug traces case PSoC_CMD_RX_CS_START: + gu32LastCapSenseStart = gu32NowMs; + return; case PSoC_CMD_RX_CS_END: + gu32LastCapSenseEnd = gu32NowMs; return; case PSoC_CMD_RX_SLAVE_DIFF: - u8PsocDataOffset = 16; - gu8PSoCSeenData |= 1; - // Falls through - case PSoC_CMD_RX_MASTER_DIFF: - if (eCmd == PSoC_CMD_RX_MASTER_DIFF) gu8PSoCSeenData |= 2; + // Request a refresh of our touch thresholds + PSoC_GetDebug(PSoC_CMD_DEBUG_SLAVE_TOUCH_TH, NULL); + if (u8Len != 32) return; for (uint8_t i = 0; i < 16; i++) { - gu16PSoCDiff[u8PsocDataOffset + i] = u8Data[(i * 2)] << 8; - gu16PSoCDiff[u8PsocDataOffset + i] |= u8Data[(i * 2) + 1]; + gu16PSoCDiff[16 + i] = u8Data[(i * 2)] << 8; + gu16PSoCDiff[16 + i] |= u8Data[(i * 2) + 1]; } - bPSoCDirty = 1; + bPSoCDirtyVolatile = 1; + return; + + case PSoC_CMD_RX_MASTER_DIFF: + // Request a refresh of our touch thresholds + PSoC_GetDebug(PSoC_CMD_DEBUG_MASTER_TOUCH_TH, NULL); + + if (u8Len != 32) return; + for (uint8_t i = 0; i < 16; i++) { + gu16PSoCDiff[i] = u8Data[(i * 2)] << 8; + gu16PSoCDiff[i] |= u8Data[(i * 2) + 1]; + } + bPSoCDirtyVolatile = 1; return; // Arbitrary data reception case PSoC_CMD_RX_MASTER_TOUCH_TH: + if (u8Len == 32) { + for (uint8_t i = 0; i < 16; i++) { + gu16PSoCThreshold[i] = u8Data[(i * 2)] << 8; + gu16PSoCThreshold[i] |= u8Data[(i * 2) + 1]; + } + } + goto arbitrary_data_reception; case PSoC_CMD_RX_SLAVE_TOUCH_TH: + if (u8Len == 32) { + for (uint8_t i = 0; i < 16; i++) { + gu16PSoCThreshold[16 + i] = u8Data[(i * 2)] << 8; + gu16PSoCThreshold[16 + i] |= u8Data[(i * 2) + 1]; + } + } + goto arbitrary_data_reception; + case PSoC_CMD_RX_MASTER_FINGER_TH: case PSoC_CMD_RX_MASTER_HYSTERESIS: case PSoC_CMD_RX_SLAVE_FINGER_TH: case PSoC_CMD_RX_SLAVE_HYSTERESIS: case PSoC_CMD_RX_GET_FINGER_CAP: case PSoC_CMD_RX_SET_FINGER_CAP: + case PSoC_CMD_RX_ENABLE_DEBUG: + arbitrary_data_reception: + // Only handle the command if it's what we were actually blocking on + if (eCmd != eBlockingCommand) { + // This was a rogue command! (breakpoint here for PSoC diagnosis stuff) + return; + } + if (pu8PsocRxDestination) { if (u8Len > pu8PsocRxDestinationLen) u8Len = pu8PsocRxDestinationLen; memcpy(pu8PsocRxDestination, u8Data, u8Len); @@ -71,8 +122,84 @@ static void PSoC_HandleRx(PSoC_CMD_RX eCmd, uint8_t u8Len, uint8_t* u8Data) { pu8PsocGotData = NULL; } return; + + // Nonsense, but just to satisfy the switch condition validation :) + case _PSoC_CMD_RX_NONE: + return; } } + +/** + * @brief Returns 1 on successful reception of a command, 0 otherwise + * + * @param eCommand + * @return uint8_t + */ +static volatile uint8_t su8Ready = 0; +static uint8_t PSoC_Await_Command(PSoC_CMD_RX eCommand) { + su8Ready = 0; + pu8PsocGotData = &su8Ready; + + // TODO: What timeout does this actually work out to be? + // With O0, it looks like /33 = 1 second (ie the loop is 33 clocks) + // Haven't tested Os which is what we'd want for prod + uint32_t u32Timeout = (SystemCoreClock / 33) / 16; // 62.5ms + + eBlockingCommand = eCommand; + // Block on reception of the command + while (!su8Ready) { + // Timeout condition + if (--u32Timeout == 0) { + eBlockingCommand = _PSoC_CMD_RX_NONE; + return 0; + } + } + eBlockingCommand = _PSoC_CMD_RX_NONE; + return 1; +} +static uint8_t PSoC_Valid_Len(PSoC_CMD_RX eCmd, uint8_t u8Len) { + switch (eCmd) { + case PSoC_CMD_RX_CS_START: + case PSoC_CMD_RX_CS_END: + // We should never be seeing a length for these! + return 0; + + // Commands that have a payload we need to receive + case PSoC_CMD_RX_SET_FINGER_CAP: + case PSoC_CMD_RX_REQUEST_FINGER_CAP: + case PSoC_CMD_RX_ENABLE_DEBUG: + return u8Len == 2; + case PSoC_CMD_RX_MASTER_DIFF: + case PSoC_CMD_RX_SLAVE_DIFF: + case PSoC_CMD_RX_MASTER_TOUCH_TH: + case PSoC_CMD_RX_SLAVE_TOUCH_TH: + case PSoC_CMD_RX_MASTER_FINGER_TH: + case PSoC_CMD_RX_MASTER_HYSTERESIS: + case PSoC_CMD_RX_SLAVE_FINGER_TH: + case PSoC_CMD_RX_SLAVE_HYSTERESIS: + case PSoC_CMD_RX_GET_FINGER_CAP: + return u8Len == 0x20; + + // Nonsense + case _PSoC_CMD_RX_NONE: + return 0; + } + return 0; +} + +static inline uint8_t PSoC_Validate_Sum(PSoC_CMD_RX eCmd, uint8_t u8Observed, uint8_t u8Received) { + switch (eCmd) { + // These four commands are broken, and their checksum is always zero + case PSoC_CMD_RX_MASTER_FINGER_TH: + case PSoC_CMD_RX_MASTER_HYSTERESIS: + case PSoC_CMD_RX_SLAVE_FINGER_TH: + case PSoC_CMD_RX_SLAVE_HYSTERESIS: + return u8Received == 0; + default: + return u8Received == u8Observed; + } +} + void UART1_IRQHandler(void) { static uint8_t su8RxData[32] = { 0 }; static uint8_t su8State = 0; @@ -88,18 +215,15 @@ void UART1_IRQHandler(void) { switch (su8State) { case 0: - eCmd = u8Data; // For debugging switch (u8Data) { - // Just go away + // Commands with no payload case PSoC_CMD_RX_CS_START: case PSoC_CMD_RX_CS_END: - return; - // Commands with no payload - case PSoC_CMD_RX_SET_FINGER_CAP: - PSoC_HandleRx(u8Data, 0, NULL); + // PSoC_HandleRx(u8Data, 0, NULL); return; // Commands that have a payload we need to receive + case PSoC_CMD_RX_SET_FINGER_CAP: case PSoC_CMD_RX_REQUEST_FINGER_CAP: case PSoC_CMD_RX_MASTER_DIFF: case PSoC_CMD_RX_SLAVE_DIFF: @@ -114,6 +238,11 @@ void UART1_IRQHandler(void) { su8Sum = u8Data; su8State++; return; + + // Unknown command + default: + su8State = 0; + return; } return; case 1: @@ -125,16 +254,28 @@ void UART1_IRQHandler(void) { if (!su8Len) su8State++; // Packets too large for reception need dropped if (su8Len > sizeof su8RxData) su8State = 0; + + if (!PSoC_Valid_Len(eCmd, su8Len)) { + // This isn't the right length for this command, so we know we're out of sync + su8State = 0; + } return; case 2: su8Sum += u8Data; su8RxData[su8Index++] = u8Data; - if (su8Index == su8Len) su8State++; + if (su8Index == su8Len) { + su8State++; + } return; case 3: // Only process packets with a valid checksum - if (su8Sum == u8Data) PSoC_HandleRx(eCmd, su8Len, su8RxData); - su8State = 0; + if (PSoC_Validate_Sum(eCmd, su8Sum, u8Data)) { + PSoC_HandleRx(eCmd, su8Len, su8RxData); + su8State = 0; + } else { + // For breakpoints + su8State = 0; + } return; // If we somehow land in an invalid state (should be impossible), make sure we can recover @@ -144,29 +285,43 @@ void UART1_IRQHandler(void) { } } -static inline void PSoC_Cmd(PSoC_CMD_TX eCmd, uint8_t u8D0, uint8_t u8D1, uint8_t u8Blocking) { - static uint8_t su8Ready = 0; - if (u8Blocking) { - su8Ready = 0; - pu8PsocGotData = &su8Ready; - } +static inline void _UART_Write(uint8_t u8Data) { + while (UART_IS_TX_FULL(UART1)) + ; + UART_WRITE(UART1, u8Data); +} +static inline void PSoC_Cmd(PSoC_CMD_TX eCmd, uint8_t u8D0, uint8_t u8D1, PSoC_CMD_RX eBlocking) { + // The protocol for the PSoCs has no sync byte + // This means it's quite likely we're going to sometimes be parsing in the middle of a packets, + // causing us to potentially lose future packets that we wanted to receive. + // + // To avoid this situation, we're going to repeatedly send the packet on a timeout. + // + // If the PSoC is totally non-responsive this will deadlock. For now that's probably fine. We + // can add a retry counter down the line if it causes problems. + do { + _UART_Write(eCmd); + _UART_Write(2); + _UART_Write(u8D0); + _UART_Write(u8D1); + _UART_Write(eCmd + 2 + u8D0 + u8D1); - static uint8_t u8Packet[5]; - u8Packet[0] = eCmd; - u8Packet[1] = 2; - u8Packet[2] = u8D0; - u8Packet[3] = u8D1; - u8Packet[4] = eCmd + 2 + u8D0 + u8D1; - for (uint8_t i = 0; i < 5; i++) { - while (UART_IS_TX_FULL(UART1)) - ; - UART_WRITE(UART1, u8Packet[i]); - } + if (eBlocking == _PSoC_CMD_RX_NONE) break; + if (PSoC_Await_Command(eBlocking)) break; - if (u8Blocking) { - while (!su8Ready) - ; - } + // The issue with sync bytes goes both ways. Our packets are 5 bytes long, and the PSoC will + // error out if the second byte isn't a 2 (and stop reading), so by ensuring we send an + // odd-length stream we'll be back in sync eventually + // + // It can take quite a few failed packets to bring the PSoC back into sync. Once we have the + // PSoC in sync, it should stay in sync for the duration of the program execution. We + // perform a blocking sensitivity assignment at the start of the program, which is hopefully + // where this resync should ocur if required. + // + // + // ...that's all in theory though. + // In practice it seems if we ever break sync the PSoC will need a hard reset. + } while (1); } uint16_t PSoC_GetFingerCapacitance(void) { @@ -174,27 +329,50 @@ uint16_t PSoC_GetFingerCapacitance(void) { pu8PsocRxDestination = &u16FingerCap; pu8PsocRxDestinationLen = sizeof u16FingerCap; - PSoC_Cmd(PSoC_CMD_TX_GET_FINGER_CAP, 0, 0, 1); + PSoC_Cmd(PSoC_CMD_TX_GET_FINGER_CAP, 0, 0, PSoC_CMD_RX_GET_FINGER_CAP); - return u16FingerCap; + return BYTESWAP_U16(u16FingerCap); } -void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data, volatile uint8_t* pu8Ready) { - pu8PsocGotData = pu8Ready; +void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data) { pu8PsocRxDestination = pu8Data; - pu8PsocRxDestinationLen = 32; - PSoC_Cmd(PSoC_CMD_TX_GET_DEBUG, 0, u8Cmd, 0); + pu8PsocRxDestinationLen = pu8Data ? 32 : 0; + + PSoC_CMD_RX eRet = _PSoC_CMD_RX_NONE; + if (pu8Data != NULL) { + switch (u8Cmd) { + case PSoC_CMD_DEBUG_MASTER_TOUCH_TH: + eRet = PSoC_CMD_RX_MASTER_TOUCH_TH; + break; + case PSoC_CMD_DEBUG_SLAVE_TOUCH_TH: + eRet = PSoC_CMD_RX_SLAVE_TOUCH_TH; + break; + case PSoC_CMD_DEBUG_MASTER_FINGER_TH: + eRet = PSoC_CMD_RX_MASTER_FINGER_TH; + break; + case PSoC_CMD_DEBUG_SLAVE_FINGER_TH: + eRet = PSoC_CMD_RX_SLAVE_FINGER_TH; + break; + case PSoC_CMD_DEBUG_MASTER_HYSTERESIS: + eRet = PSoC_CMD_RX_MASTER_HYSTERESIS; + break; + case PSoC_CMD_DEBUG_SLAVE_HYSTERESIS: + eRet = PSoC_CMD_RX_SLAVE_HYSTERESIS; + break; + } + } + PSoC_Cmd(PSoC_CMD_TX_GET_DEBUG, 0, u8Cmd, eRet); } -void PSoC_SetFingerCapacitance(uint16_t u16FingerCap) { +void PSoC_SetFingerCapacitance(uint16_t u16FingerCap, uint8_t u8Blocking) { if (u16FingerCap > 1023) u16FingerCap = 1023; - // PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff, 1); - PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff, 1); + PSoC_Cmd(PSoC_CMD_TX_SET_FINGER_CAP, u16FingerCap >> 8, u16FingerCap & 0xff, + u8Blocking ? PSoC_CMD_RX_SET_FINGER_CAP : _PSoC_CMD_RX_NONE); } -void PSoC_SetFingerCapacitanceFromConfig(void) { - PSoC_SetFingerCapacitance(PSoC_FINGER_CAP_MIN + (16 - gConfig.u8Sens) * PSoC_FINGER_CAP_STEP); +void PSoC_SetFingerCapacitanceFromConfig(uint8_t u8Blocking) { + PSoC_SetFingerCapacitance(PSoC_FINGER_CAP_MIN + (16 - gConfig.u8Sens) * PSoC_FINGER_CAP_STEP, + u8Blocking); } -void PSoC_EnableDebug(uint8_t u8D1, uint8_t u8D2) { - // TODO: Finish figuring out what values these two bytes need - PSoC_Cmd(PSoC_CMD_TX_ENABLE_DEBUG, u8D1, u8D2, 1); +void PSoC_SetDebug(PSoC_DebugFlag eFlag, uint8_t bEnable) { + PSoC_Cmd(PSoC_CMD_TX_ENABLE_DEBUG, eFlag, bEnable, PSoC_CMD_RX_ENABLE_DEBUG); } uint8_t gu8GroundData[32]; @@ -205,20 +383,33 @@ void PSoC_PostProcessing(void) { uint8_t j = (15 - (i / 2)) * 2 + (i & 1); const uint16_t u16Pad = gu16PSoCDiff[i]; + const uint16_t u16TouchTh = gu16PSoCThreshold[i]; - const uint16_t u16Min = gConfig.u16PSoCScaleMin[i]; - const uint16_t u16Max = gConfig.u16PSoCScaleMax[i]; - - if (u16Pad < u16Min) { - gu8GroundData[j] = 0; - } else if (u16Pad > u16Max) { + // Chunithm defaults to a threshold of 20 + // As such, we need to scale u16Pad such that (0,u16TouchTh)=(0,20) + // + // This can go over 255, so make sure to clamp it + uint16_t u16Val = (u16Pad * 20) / u16TouchTh; + if (u16Val > 255) gu8GroundData[j] = 255; - } else { - // Multiply by 255 first to retain precision - // This can overflow u16, hence the u32 cast pre-mult - gu8GroundData[j] = ((uint32_t)(u16Pad - u16Min) * 255) / (u16Max - u16Min); - } + else + gu8GroundData[j] = u16Val; + + // const uint16_t u16Min = gConfig.u16PSoCScaleMin[i]; + // const uint16_t u16Max = gConfig.u16PSoCScaleMax[i]; + + // if (u16Pad < u16Min) { + // gu8GroundData[j] = 0; + // } else if (u16Pad > u16Max) { + // gu8GroundData[j] = 255; + // } else { + // // Multiply by 255 first to retain precision + // // This can overflow u16, hence the u32 cast pre-mult + // gu8GroundData[j] = ((uint32_t)(u16Pad - u16Min) * 255) / (u16Max - u16Min); + // } } + + bForceSliderSend = 1; } /** * @brief Calculate digital input data from PSoC values @@ -283,3 +474,6 @@ void PSoC_DigitalCalc(void) { } } } + +// #pragma GCC diagnostic pop +// #pragma GCC pop_options diff --git a/src/psoc.h b/src/psoc.h index d40ac06..515aae4 100644 --- a/src/psoc.h +++ b/src/psoc.h @@ -5,22 +5,26 @@ #define PSoC_DATA_SIZE 32 #define PSoC_PACKET_SIZE (2 + PSoC_DATA_SIZE) -// Used for calculation of PSoC_FINGER_CAP_MIN // The difference between CAP and MIN must be divisible by 8 -#define PSoC_FINGER_CAP_MIN 20 // 0.2pF -#define PSoC_DEFAULT_CAP 340 // 3.2pF -// Maximum is CAP*2-MIN*2 ie 6.0pF +// The stupidly high gain kicks in around 0.7pF. It's not directly based on fingerCap though +// so 1.0pF gives us a bit of a margin to ensure we avoid it +#define PSoC_FINGER_CAP_MIN 100 // 1.0pF +#define PSoC_DEFAULT_CAP 500 // 5.0pF +// Maximum is CAP*2-MIN*2 ie 8.0pF #define PSoC_FINGER_CAP_STEP ((PSoC_DEFAULT_CAP - PSoC_FINGER_CAP_MIN) / 8) -typedef enum { +typedef enum : uint8_t { // Host -> PSoC PSoC_CMD_TX_GET_FINGER_CAP = 0xB5, PSoC_CMD_TX_GET_DEBUG = 0xB6, PSoC_CMD_TX_SET_FINGER_CAP = 0xBD, PSoC_CMD_TX_ENABLE_DEBUG = 0xDE, } PSoC_CMD_TX; -typedef enum { +typedef enum : uint8_t { + // We'd use 0x00, but that's used, so 0x01 it is + _PSoC_CMD_RX_NONE = 0x01, + // PSoC -> Host PSoC_CMD_RX_MASTER_DIFF = 0xAD, PSoC_CMD_RX_SLAVE_DIFF = 0xAF, @@ -39,24 +43,28 @@ typedef enum { // Packets that get a direct response PSoC_CMD_RX_GET_FINGER_CAP = PSoC_CMD_TX_GET_FINGER_CAP, PSoC_CMD_RX_SET_FINGER_CAP = PSoC_CMD_TX_SET_FINGER_CAP, + PSoC_CMD_RX_ENABLE_DEBUG = PSoC_CMD_TX_ENABLE_DEBUG, } PSoC_CMD_RX; -typedef enum { +typedef enum : uint8_t { PSoC_CMD_DEBUG_MASTER_TOUCH_TH = 0, PSoC_CMD_DEBUG_SLAVE_TOUCH_TH, PSoC_CMD_DEBUG_MASTER_FINGER_TH, - PSoC_CMD_DEBUG_SLAVE_FINGER_TH, + PSoC_CMD_DEBUG_SLAVE_FINGER_TH, // Broken! PSoC_CMD_DEBUG_MASTER_HYSTERESIS, - PSoC_CMD_DEBUG_SLAVE_HYSTERESIS, + PSoC_CMD_DEBUG_SLAVE_HYSTERESIS, // Broken! } PSoC_CMD_DEBUG; // Difference between the raw count value and the baseline, from SmartSense extern uint16_t gu16PSoCDiff[32]; -// Has the difference data changed? -extern volatile uint8_t bPSoCDirty; +// Has the difference data changed in the interrupt handler? +extern volatile uint8_t bPSoCDirtyVolatile; +// Has the post-processed difference data changed? +extern volatile uint8_t bForceSliderSend; // Used for producing gu32PSoCDigital and gu16PSoCDigital -#define PSoC_INTERNAL_DIGITAL_TH 0 +// (Same threshold as default in Chunithm) +#define PSoC_INTERNAL_DIGITAL_TH 20 // PSoC data, re-interpreted as digital inputs extern uint32_t gu32PSoCDigital; @@ -68,27 +76,16 @@ extern uint16_t gu16PSoCDigitalPos; extern uint16_t gu16PSoCDigitalNeg; extern uint16_t gu16PSoCDigitalTrig; -extern uint8_t gu8PSoCSeenData; - -#define PSoC_RAW_T0 34 -#define PSoC_RAW_T1 91 -#define PSoC_RAW_T2 121 -#define PSoC_RAW_MIN 199 -#define PSoC_SCALE 5 // ie to scale from [0~4ff] to [0~ff] - -#define PSoC_OUT_MIN 20 -#define PSoC_OUT_MAX 255 - -#define PSoC_SCALE_MIN (0 + 128) -#define PSoC_SCALE_MAX (0x4ff - 128 - 256) -#define PSoC_SCALE_RANGE (3) // ie to scale from [min~max] to [0~ff] +// +extern uint32_t gu32LastCapSenseStart; +extern uint32_t gu32LastCapSenseEnd; void PSoC_PostProcessing(void); void PSoC_DigitalCalc(void); -// General usage PSoC commands; return instantly -void PSoC_SetFingerCapacitance(uint16_t u16FingerCap); -void PSoC_SetFingerCapacitanceFromConfig(void); +// General usage PSoC commands +void PSoC_SetFingerCapacitance(uint16_t u16FingerCap, uint8_t u8Blocking); +void PSoC_SetFingerCapacitanceFromConfig(uint8_t u8Blocking); /** * @brief Get the finger capacitance value currently configured on the Master PSoC * @@ -98,13 +95,24 @@ uint16_t PSoC_GetFingerCapacitance(void); /** * @brief Request a debug array from the PSoC pair * - * This call is asynchronous. Use pu8Ready to ensure pu8Data is ready. - * Only one call can be in-flight at once. - * **DO NOT** call PSoCGetFingerCapacitance while waiting on this function. - * * @param u8Cmd The specific debug array to request * @param pu8Data Pointer to array that will receive the data. Must be 32 bytes. - * @param pu8Ready Optional uint8_t to signal the data is ready */ -void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data, volatile uint8_t* pu8Ready); -void PSoC_EnableDebug(uint8_t u8D1, uint8_t u8D2); +void PSoC_GetDebug(PSoC_CMD_DEBUG u8Cmd, uint8_t* pu8Data); + +typedef enum : uint8_t { + // Flag 0 doesn't appear to be used anywhere, nor does it appear to have any effect + PSoC_DebugFlag_0 = 0, + PSoC_DebugFlag_TraceReset = 1, +} PSoC_DebugFlag; +/** + * @brief Enable or disable a debug tracing flag + * + * The reception of debug traces will be recorded in: + * - gu32LastCapSenseStart + * - gu32LastCapSenseEnd + * + * @param eFlag Which flag to set + * @param bEnable Should the flag be enabled or disabled + */ +void PSoC_SetDebug(PSoC_DebugFlag eFlag, uint8_t bEnable); diff --git a/src/slider.c b/src/slider.c index 149e78f..490f095 100644 --- a/src/slider.c +++ b/src/slider.c @@ -10,7 +10,7 @@ static uint8_t su8AutoEnabledRaw = 0; static uint8_t su8AutoEnabledByte = 0; static uint32_t su32SinceLastControlled = 0; -uint8_t gu8GameBrightness = 40; // TODO: Actually make use of this +uint8_t gu8GameBrightness = 40; static uint16_t su16ByteRawSliderOffset = 0; static uint8_t su8ByteRawSliderShift = 0; @@ -23,9 +23,11 @@ typedef enum { SLIDER_PARSE_CHECKSUM, } slider_parse_state; +// Hardware information for a Chunithm slider static const slider_cmd_Tx_hw_info sSliderHwInfo = { "15330 ", 0xA0, "06712", 0xFF, 0x90, 0, 0, }; +void Slider_Exception(uint8_t u8SliderCmd, slider_exception u8Exc); static inline void Slider_Write(uint8_t u8Byte) { if (u8Byte == SLIDER_SYNC || u8Byte == SLIDER_MARK) { @@ -47,11 +49,36 @@ static void Slider_Respond(slider_cmd_Tx u8SliderCmd, const uint8_t* pu8Packet, Slider_Write(-u8Sum); } +uint8_t u8Null64[64] = { 0 }; +static void Slider_Send_Report(void) { + if (gbUIOpen) { + Slider_Respond(SLIDER_CMD_Tx_REPORT, u8Null64, sizeof gu8GroundData); + } else { + Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData); + } +} +static void Slider_Send_Report_Raw(void) { + if (gbUIOpen) { + Slider_Respond(SLIDER_CMD_Tx_RAW, u8Null64, sizeof gu16PSoCDiff); + } else { + Slider_Respond(SLIDER_CMD_Tx_RAW, (void*)gu16PSoCDiff, sizeof gu16PSoCDiff); + } +} +static void Slider_Send_Report_Byte(void) { + slider_cmd_Tx_raw buffer; + for (uint8_t i = 0; i < 32; i++) { + if (gbUIOpen) { + buffer.u16Raw[i] = 0; + } else { + buffer.u16Raw[i] = (gu16PSoCDiff[i] >> su8ByteRawSliderShift) - su16ByteRawSliderOffset; + } + } + Slider_Respond(SLIDER_CMD_Tx_RAW, (void*)&buffer, sizeof buffer); +} static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_t u8NPacket) { switch (u8SliderCmd) { case SLIDER_CMD_Rx_RESET: // These three weren't present previously, but PSoC firmware suggests they should be - // TODO: Validate this against the game! su8AutoEnabled = 0; su8AutoEnabledRaw = 0; su8AutoEnabledByte = 0; @@ -92,7 +119,7 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_ gbLedDataIsControlledExt = 1; su32SinceLastControlled = 0; - memcpy(gu8aControlledExtLedData, &((slider_cmd_Rx_led*)pu8Packet)->aBRG, 3 * 32); + memcpy(gaControlledExtLedData, &((slider_cmd_Rx_led*)pu8Packet)->aBRG, 3 * 32); } // Reprocess this packet as a report request where applicable if (u8SliderCmd == SLIDER_CMD_Rx_REPORT_PING_PONG) { @@ -105,13 +132,13 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_ return; case SLIDER_CMD_Rx_REPORT: - Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData); + Slider_Send_Report(); return; case SLIDER_CMD_Rx_RAW: - // TODO: + Slider_Send_Report_Raw(); return; case SLIDER_CMD_Rx_BRAW: - // TODO: + Slider_Send_Report_Byte(); return; case SLIDER_CMD_Rx_REPORT_ENABLE: @@ -143,6 +170,92 @@ static void Slider_Process(slider_cmd_Rx u8SliderCmd, uint8_t* pu8Packet, uint8_ su8ByteRawSliderShift = ((slider_cmd_Rx_braw_set_shift*)pu8Packet)->u8Shift; Slider_Respond(SLIDER_CMD_Tx_BRAW_SET_SHIFT, NULL, 0); return; + + case SLIDER_CMD_Rx_DEBUG: + uint8_t u8aData[32]; + + switch ((slider_debug_cmd_Rx)pu8Packet[0]) { + case SLIDER_DEBUG_CMD_Rx_GET_FINGER_CAP: + uint16_t u16FingerCap = PSoC_GetFingerCapacitance(); + Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&u16FingerCap, + sizeof u16FingerCap); + break; + case SLIDER_DEBUG_CMD_Rx_TRACE_RESET: + // TODO: Broken. Blocks forever. + PSoC_SetDebug(PSoC_DebugFlag_TraceReset, 1); + Slider_Respond(SLIDER_CMD_Tx_DEBUG, NULL, 0); + break; + case SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_START: + Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gu32LastCapSenseStart, + sizeof gu32LastCapSenseStart); + break; + case SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_END: + Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gu32LastCapSenseEnd, + sizeof gu32LastCapSenseEnd); + break; + + case SLIDER_DEBUG_CMD_Rx_PSoC_REQUEST_DEBUG: + if (u8NPacket == 2) { + PSoC_GetDebug(pu8Packet[1], u8aData); + } + Slider_Respond(SLIDER_CMD_Tx_DEBUG, u8aData, sizeof u8aData); + break; + + case SLIDER_DEBUG_CMD_Rx_HOST_FMC_READ: + if (u8NPacket == 5) { + // We can't use a cast to uint32_t* because of unaligned reads! + uint32_t u32Base = pu8Packet[1]; + u32Base |= pu8Packet[2] << 8; + u32Base |= pu8Packet[3] << 16; + u32Base |= pu8Packet[4] << 24; + memset(u8aData, 0xFF, sizeof u8aData); + FMC_Open(); + FMC_ReadData(u32Base, u32Base + sizeof u8aData, (void*)u8aData); + FMC_Close(); + } + Slider_Respond(SLIDER_CMD_Tx_DEBUG, u8aData, sizeof u8aData); + break; + case SLIDER_DEBUG_CMD_Rx_LED_FMC_READ: + uint32_t u32Data = 0xFFFFFFFF; + if (u8NPacket == 5) { + uint32_t u32Offset = pu8Packet[1]; + u32Offset |= pu8Packet[2] << 8; + u32Offset |= pu8Packet[3] << 16; + u32Offset |= pu8Packet[4] << 24; + + LED_FMC_Read(u32Offset, &u32Data); + } + Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&u32Data, sizeof u32Data); + break; + + case SLIDER_DEBUG_CMD_Rx_HOST_ENTER_LDROM: + SYS_EnterLDROM(); + break; + case SLIDER_DEBUG_CMD_Rx_LED_ENTER_LDROM: + gu8LEDTx[0] = LED_CMD_FMC_ENTER_LDROM; + // The LED firmware checks every 10ms or so + CLK_SysTickLongDelay(15 ms); + SYS_WaitBootloaderLED(); + break; + case SLIDER_DEBUG_CMD_Rx_LED_CHECK: + Slider_Respond(SLIDER_CMD_Tx_DEBUG, (uint8_t*)&gbLedIsCustom, + sizeof gbLedIsCustom); + break; + + case SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL: + Slider_Respond(SLIDER_CMD_Tx_DEBUG, &gu8DigitalButtons, + sizeof gu8DigitalButtons); + break; + + default: + Slider_Exception(u8SliderCmd, SLIDER_EXCEPTION_BUS_ERROR); + break; + } + return; + + default: + Slider_Exception(u8SliderCmd, SLIDER_EXCEPTION_BUS_ERROR); + break; } } void Slider_Exception(uint8_t u8SliderCmd, slider_exception u8Exc) { @@ -233,13 +346,37 @@ void Slider_Tick1ms() { if (++su32SinceLastControlled == 5 * 1000) gbLedDataIsControlledExt = 0; } - if (su8AutoEnabled) { - static uint8_t u8Counter = 0; - // Only actually send an update every 8ms - if (++u8Counter != 8) return; + static uint16_t u16Counter = 0; + /** + * I haven't totally tracked down the source of the interval timer on a real slider. + * That said, from measurement it's 15.365ms or so. The exact time will be based on + * a counter from one of the low speed clocks. + * + * The game makes calls to ReadFile on an 8ms interval, but this isn't the most stable. + * I took a short capture, and my intervals were: + * + * 0% | 3.6ms + * 10% | 7.0ms + * 50% | 8.0ms + * 90% | 9.0ms + * 100% | 14.0ms + * + * This +-1ms appears to be caused by the use of Sleep() to regulate the interval, which + * has an argument precision of 1ms (and an overall precision far worse!). + * + * Chunithm will retry a read four times, at which point it considers the slider to have + * timed out (error 3100). + * + * Based on this, we should be safe to indeed run at a 15ms interval here. + * + * I received one report of a user getting a 3100 when they started the game. I am current + * working on the basis that this is a one-off, however this will need re-addressed if this + * issue becomes widespread. + */ + if (++u16Counter != 15) return; + u16Counter = 0; - u8Counter = 0; - - Slider_Respond(SLIDER_CMD_Tx_REPORT, gu8GroundData, sizeof gu8GroundData); - } + if (su8AutoEnabled) Slider_Send_Report(); + if (su8AutoEnabledRaw) Slider_Send_Report_Raw(); + if (su8AutoEnabledByte) Slider_Send_Report_Byte(); } diff --git a/src/slider.h b/src/slider.h index 11bb6b9..f849b2b 100644 --- a/src/slider.h +++ b/src/slider.h @@ -52,7 +52,7 @@ #define PAD_31_Msk BIT0 #define PAD_32_Msk BIT1 -typedef enum { +typedef enum : uint8_t { // ========================= // Actually used by Chunithm // ========================= @@ -69,9 +69,14 @@ typedef enum { /* Retrieve hardware information (model number, etc.) */ SLIDER_CMD_Rx_HW_INFO = 0xF0, - // ====== - // Autism - // ====== + // ========================================== + // Custom additions, used for debugging, etc. + // ========================================== + SLIDER_CMD_Rx_DEBUG = 0xF1, + + // ===================================================================== + // Required for a complete slider implementation, but unused by the game + // ===================================================================== /* Request a single standard report */ SLIDER_CMD_Rx_REPORT = 0x01, /* Set LED brightness and BRG values, with a report as the response */ @@ -98,7 +103,27 @@ typedef enum { /* Request the CPU status registers */ SLIDER_CMD_Rx_CPU_STATUS = 0xE0, } slider_cmd_Rx; -typedef enum { +typedef enum : uint8_t { + // Actual debugging stuff + SLIDER_DEBUG_CMD_Rx_GET_FINGER_CAP = 0x00, + SLIDER_DEBUG_CMD_Rx_TRACE_RESET = 0x01, + SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_START = 0x02, + SLIDER_DEBUG_CMD_Rx_GET_LAST_CS_END = 0x03, + SLIDER_DEBUG_CMD_Rx_PSoC_REQUEST_DEBUG = 0x04, + + // Flash access + SLIDER_DEBUG_CMD_Rx_HOST_FMC_READ = 0x10, + SLIDER_DEBUG_CMD_Rx_LED_FMC_READ = 0x12, + + // Chip reset + SLIDER_DEBUG_CMD_Rx_HOST_ENTER_LDROM = 0x20, + SLIDER_DEBUG_CMD_Rx_LED_ENTER_LDROM = 0x21, + SLIDER_DEBUG_CMD_Rx_LED_CHECK = 0x22, + + // IO Access + SLIDER_DEBUG_CMD_Rx_LED_GET_DIGITAL = 0x30, +} slider_debug_cmd_Rx; +typedef enum : uint8_t { SLIDER_CMD_Tx_REPORT = 0x01, SLIDER_CMD_Tx_REPORT_DISABLE = 0x04, @@ -112,8 +137,10 @@ typedef enum { SLIDER_CMD_Tx_CPU_STATUS = 0xE0, SLIDER_CMD_Tx_EXCEPTION = 0xEE, SLIDER_CMD_Tx_HW_INFO = 0xF0, + + SLIDER_CMD_Tx_DEBUG = 0xF1, } slider_cmd_Tx; -typedef enum { +typedef enum : uint8_t { SLIDER_EXCEPTION_CHECKSUM = 1, SLIDER_EXCEPTION_BUS_ERROR = 2, } slider_exception; @@ -126,7 +153,7 @@ typedef enum { #define SLIDER_EXCEPTION_CTX_GENERIC 0xED typedef struct __packed { - uint8_t u8Brightness; + uint8_t u8Brightness; // Range: 0~63 struct __packed { uint8_t u8B; uint8_t u8R; @@ -184,6 +211,6 @@ typedef struct __packed { }; } slider_cmd_Tx_cpu_status; -extern uint8_t gu8GameBrightness; // 0~63 +extern uint8_t gu8GameBrightness; // Range: 0~63 void Slider_TickSerial(void); void Slider_Tick1ms(void); diff --git a/src/sys.c b/src/sys.c index 2b0bff1..612b6ae 100644 --- a/src/sys.c +++ b/src/sys.c @@ -17,10 +17,16 @@ void SYS_ResetModule(uint32_t u32ModuleIndex) { ~(1 << (u32ModuleIndex & 0x00ffffff)); } +// Even though we have an external 12MHz crystal, it's never actually used. +// This define toggles if we will even bother configuring it or not. +#define EXT_CLK + void SYS_Init(void) { +#ifdef EXT_CLK // Enable XT1_OUT (PF.0) and XT1_IN (PF.1) SYS->GPF_MFP &= ~(SYS_GPF_MFP_PF0_Msk | SYS_GPF_MFP_PF1_Msk); SYS->GPF_MFP |= SYS_GPF_MFP_PF0_XT1_OUT | SYS_GPF_MFP_PF1_XT1_IN; +#endif // Enable Internal RC 22.1184 MHz clock CLK_EnableXtalRC(CLK_PWRCON_OSC22M_EN_Msk); @@ -29,12 +35,14 @@ void SYS_Init(void) { // Switch HCLK clock source to Internal RC and HCLK source divide 1 CLK_SetHCLK(CLK_CLKSEL0_HCLK_S_HIRC, CLK_CLKDIV_HCLK(1)); +#ifdef EXT_CLK // Enable external XTAL 12 MHz clock CLK_EnableXtalRC(CLK_PWRCON_XTL12M_EN_Msk); CLK_WaitClockReady(CLK_CLKSTATUS_XTL12M_STB_Msk); +#endif // Set core clock - CLK_SetCoreClock(72000000); + CLK_SetCoreClock(72 MHz); // Enable module clocks CLK_EnableModuleClock(UART1_MODULE); @@ -57,6 +65,8 @@ void SYS_Init(void) { PIN_SCL = 1; // Configure our GPIO pins + GPIO_SetMode(_PIN_FN1, GPIO_PMD_QUASI); + GPIO_SetMode(_PIN_FN2, GPIO_PMD_QUASI); GPIO_SetMode(_PIN_EC1, GPIO_PMD_INPUT); GPIO_SetMode(_PIN_RX2, GPIO_PMD_INPUT); GPIO_SetMode(_PIN_RX3, GPIO_PMD_INPUT); @@ -71,26 +81,30 @@ void SYS_Init(void) { PIN_LED_WING_PWR = 1; PIN_LED_GROUND_PWR = 1; - GPIO_SetMode(_PIN_FN2, GPIO_PMD_INPUT); - // TODO: + // If FN2 is depressed, trigger the LED bootloader to enter bootloading mode by pulling both + // PA10 and PA11 low rather than configuring them for I2C (pulling high). + // + // We have 145us to get to this point, because that's how long the LED bootloader is willing to + // wait! if (PIN_FN2 == 0) { PIN_USB_MUX_SEL = USB_MUX_LEDS; PIN_USB_MUX_EN = USB_MUX_ENABLE; + + // Trigger the bootloader GPIO_SetMode(_PIN_SDA, GPIO_PMD_OUTPUT); GPIO_SetMode(_PIN_SCL, GPIO_PMD_OUTPUT); PIN_SDA = 0; PIN_SCL = 0; - return; + } else { + // Set GPA multi-function pins for I2C1 SDA and SCL + SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); + SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL); + SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk); + SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL); + + PIN_USB_MUX_SEL = USB_MUX_HOST; + PIN_USB_MUX_EN = USB_MUX_ENABLE; } - - // Set GPA multi-function pins for I2C1 SDA and SCL - SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); - SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL); - SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk); - SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL); - - PIN_USB_MUX_EN = USB_MUX_ENABLE; - PIN_USB_MUX_SEL = USB_MUX_HOST; } void SYS_ModuleInit(void) { @@ -130,13 +144,46 @@ void SYS_ModuleInit(void) { NVIC_EnableIRQ(USBD_IRQn); } -// Define a dedicated symbol for this, so we can easily trap it with a debugger! -void HardFault_Handler() { +void HardFault_Handler(uint32_t *stack) { + // Extract r0-3, r12, lr, pc, psr, and place them into "stack" + asm("MOVS r0, #4 \n" + "MOV r1, lr \n" + "TST r0, r1 \n" // check LR bit 2 + "BEQ 1f \n" // stack use MSP + "MRS r0, psp \n" // stack use PSP, read PSP + "MOV r1, lr \n" // LR current value + "B 2f \n" + "1: \n" + "MRS r0, msp \n" // LR current value + "2: \n"); + + // Extract out into locals for easy debugging + uint32_t r0 = stack[0]; + uint32_t r1 = stack[1]; + uint32_t r2 = stack[2]; + uint32_t r3 = stack[3]; + uint32_t r12 = stack[4]; + uint32_t lr = stack[5]; + uint32_t pc = stack[6]; + uint32_t psr = stack[7]; + + // Silence unused warnings + (void)r0; + (void)r1; + (void)r2; + (void)r3; + (void)r12; + (void)lr; + (void)pc; + (void)psr; + + // Force a chip reset + SYS->IPRSTC1 = SYS_IPRSTC1_CHIP_RST_Msk; while (1) ; } -void SYS_Bootloader_Check() { +void SYS_Bootloader_Check(void) { FMC_Open(); while (FMC_Read(BOOTLOADER_MAGIC_ADDR) != BOOTLOADER_MAGIC) ; @@ -156,3 +203,88 @@ void TMR0_IRQHandler(void) { } } } + +#define V6M_AIRCR_VECTKEY_DATA 0x05FA0000UL +void __attribute__((noreturn)) SYS_EnterLDROM(void) { + SYS_UnlockReg(); + + // If we use a CPU reset, I2C is left setup and so the LED board will be timing out rather than + // early-NACKS. + // If we use a CHIP reset we aren't guaranteed to land in LDROM because it's based on the CONFIG + // flags, though realistically we are. + // An MCU reset guarantees we end up in LDROM regardless of configuration. + + // Reset to LDROM using a CPU reset + // FMC->ISPCON |= FMC_ISPCON_BS_Msk | FMC_ISPCON_ISPEN_Msk; + // SYS->IPRSTC1 |= SYS_IPRSTC1_CPU_RST_Msk; + + // Reset to LDROM using a CHIP reset + // SYS->IPRSTC1 |= SYS_IPRSTC1_CHIP_RST_Msk; + + // Reset to LDROM using a MCU reset + SYS->RSTSRC = SYS_RSTSRC_RSTS_POR_Msk | SYS_RSTSRC_RSTS_RESET_Msk; + FMC->ISPCON |= FMC_ISPCON_BS_Msk | FMC_ISPCON_ISPEN_Msk; + NVIC_SystemReset(); + + // Trap the processor + while (1) + ; + __builtin_unreachable(); +} + +void SYS_WaitBootloaderLED(void) { + SYS_UnlockReg(); + + // Switch PA10 and PA11 to GPIO so we can pull them low + SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); + SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_GPIO | SYS_GPA_MFP_PA11_GPIO); + SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk); + SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_GPIO | SYS_ALT_MFP_PA11_GPIO); + + // Trigger the bootloader + GPIO_SetMode(_PIN_SDA, GPIO_PMD_OUTPUT); + GPIO_SetMode(_PIN_SCL, GPIO_PMD_OUTPUT); + PIN_SDA = 0; + PIN_SCL = 0; + + // Shutdown I2C + I2C_Close(I2C1); + + // Turn off our USB PHY + USBD->ATTR = 0x650; + NVIC_DisableIRQ(USBD_IRQn); + SYS_ResetModule(USBD_RST); + // Give Windows a moment to notice the disconnection + CLK_SysTickLongDelay(1000 ms); + // Switch the USB connection over the LED microcontroller + PIN_USB_MUX_SEL = USB_MUX_LEDS; + PIN_USB_MUX_EN = USB_MUX_ENABLE; + + u16I2CRxIndex = 0; + // Wait 5 seconds, which is long enough for the LED bootloader to take over + CLK_SysTickLongDelay(5000 ms); + + // Switch PA10 and PA11 back to I2C + SYS->GPA_MFP &= ~(SYS_GPA_MFP_PA10_Msk | SYS_GPA_MFP_PA11_Msk); + SYS->GPA_MFP |= (SYS_GPA_MFP_PA10_I2C1_SDA | SYS_GPA_MFP_PA11_I2C1_SCL); + SYS->ALT_MFP &= ~(SYS_ALT_MFP_PA10_Msk | SYS_ALT_MFP_PA11_Msk); + SYS->ALT_MFP |= (SYS_ALT_MFP_PA10_I2C1_SDA | SYS_ALT_MFP_PA11_I2C1_SCL); + + // Restart the I2C controller + LED_I2C1_Init(); + + // Wait until we start getting data from the LED firmware + while (u16I2CRxIndex == 0) + ; + + // Take back control of USB + PIN_USB_MUX_SEL = USB_MUX_HOST; + PIN_USB_MUX_EN = USB_MUX_ENABLE; + + // Bring our USB PHY back online, along with a delay sufficient for a device reconnect to be + // detected + Tas_USBD_Open(); + Tas_USBD_Init(); + Tas_USBD_Start(); + NVIC_EnableIRQ(USBD_IRQn); +} \ No newline at end of file diff --git a/src/tasoller.h b/src/tasoller.h index 0e0049e..470b933 100644 --- a/src/tasoller.h +++ b/src/tasoller.h @@ -3,39 +3,33 @@ #include #include -#if defined(__CC_ARM) -#elif defined(__GNUC__) -#define __packed __attribute__((packed)) -#else -#error Unknown compiler -#endif +#include "_compiler.h" #define ms *1000 #define kHz *1000 +#define MHz *1000000 -/* === LEDs === */ -#define LED_Tx_BUFFER 254 -// Defined as volatile due to the I2C interrupt reading at random times! -extern volatile uint8_t gu8LEDTx[LED_Tx_BUFFER]; +extern uint32_t gu32NowMs; +extern uint8_t gbUIOpen; -/* === DAO-DRM === */ +#define BYTESWAP_U16(x) ((((x) & 0xFF) << 8) | ((x) >> 8)) + +// === DAO-DRM === // * For now the bootloader check is being left enabled. // * It'll help catch if I break that in my bootloader :P #define ENABLE_BOOTLOADER_CHECK #define BOOTLOADER_MAGIC 0x5555A320 #define BOOTLOADER_MAGIC_ADDR 0x100FF8 -/* === USB definitions === */ -#include "usb_inc/hid.h" -#include "usb_inc/keymap.h" -#include "usb_inc/usb.h" - -/* === Local files === */ +// === Local files === #include "fmc_user.h" #include "led.h" #include "pins.h" #include "psoc.h" #include "slider.h" +#include "usb_def.h" +#include "vcom.h" +#include "io4.h" #define IO4_VENDOR "SEGA INTERACTIVE" // Product description @@ -48,102 +42,26 @@ extern volatile uint8_t gu8LEDTx[LED_Tx_BUFFER]; "6679A;" /* Chip number */ \ "00;" /* Config */ // Functional description -#define _IO4_FUNCTION \ - "GOUT=14_" /* General purpose output (20) */ \ - "ADIN=8,E_" /* ADC input (8) */ \ - "ROTIN=4_" /* Rotary input (4) */ \ - "COININ=2_" /* Coin input (2) */ \ - "SWIN=2,E_" /* Switch inputs (2) */ \ - "UQ1=41,6;" /* Unique function 1 (?) */ +#define _IO4_FUNCTION \ + "GOUT=14_" /* General purpose output (20) */ \ + "ADIN=8,E_" /* ADC input (8) */ \ + "ROTIN=4_" /* Rotary input (4) */ \ + "COININ=2_" /* Coin input (2) */ \ + "SWIN=2,E_" /* Switch inputs (2) */ \ + "UQ1=41,6;" /* Unique 1 (Command: 41h, bytes 6) */ #define IO4_PRODUCT _IO4_PRODUCT _IO4_FUNCTION #define IO4_VID 0x0CA3 #define IO4_PID 0x0021 -#define INCR(x, y) ((x) = (x) < (y) ? (x) + 1 : (y)) -#define DECR(x, y) ((x) = (x) > (y) ? (x)-1 : (y)) -#define MOD_INCR(x, y) ((x) = (x) == ((y)-1) ? 0 : ((x) + 1)) -#define MOD_DECR(x, y) ((x) = (x) == 0 ? ((y)-1) : ((x)-1)) -#define INV(x) ((x) = 1 - (x)) - -extern volatile uint8_t gu8VcomReady; /** * WARNING: This is both used internally and sent verbatim over serial * It **MUST** be 32 bytes of ground data */ extern uint8_t gu8GroundData[32]; -/* === For HID === */ -enum { - HID_REPORT_ID_IO4 = 1, - HID_REPORT_ID_KEYBOARD, - HID_REPORT_ID_DEBUG_A, - HID_REPORT_ID_DEBUG_B, - HID_REPORT_ID_IO4_CMD = 16, -}; - -#define NUM_FN 2 -#define NUM_AIR 6 -#define NUM_GROUND 32 - -typedef struct __packed { - uint8_t bReportId; - uint8_t bKeyboard[NUM_FN + NUM_AIR + NUM_GROUND]; -} hid_report_t; -typedef struct __packed { - uint8_t bReportId; - uint16_t wADC[8]; - uint16_t wRotary[4]; - uint16_t wCoin[2]; - uint16_t wButtons[2]; - uint8_t bSystemStatus; - uint8_t bUsbStatus; - uint8_t bUnique[29]; -} io4_hid_in_t; -typedef struct __packed { - uint8_t bReportId; - uint8_t bCmd; - uint8_t bData[62]; -} io4_hid_out_t; -typedef struct __packed { - uint8_t bReportId; - uint16_t wData[16]; -} debug_hid_report_t; - extern uint8_t gu8DigitalButtons; -// void HID_Tick(); -void USBD_HID_PrepareReport(); -uint8_t *USBD_HID_GetReport(uint8_t u8ReportId, uint32_t *pu32Size); -void USBD_HID_SetReport(volatile uint8_t *pu8EpBuf, uint32_t u32Size); - -// For CDC -typedef struct __packed { - uint32_t u32DTERate; // Baud rate - uint8_t u8CharFormat; // Stop bit - uint8_t u8ParityType; // Parity - uint8_t u8DataBits; // Data bits -} STR_VCOM_LINE_CODING; -extern volatile int8_t gi8BulkOutReady; -extern STR_VCOM_LINE_CODING gLineCoding; -extern uint16_t gCtrlSignal; -extern volatile uint16_t comRbytes; -extern volatile uint16_t comRhead; -extern volatile uint16_t comRtail; -extern volatile uint16_t comTbytes; -extern volatile uint16_t comThead; -extern volatile uint16_t comTtail; -extern volatile uint8_t *gpu8RxBuf; -extern volatile uint32_t gu32RxSize; -extern volatile uint32_t gu32TxSize; -// For HID -extern uint8_t volatile gu8HIDIO4Ready; -extern uint8_t volatile gu8HIDMiscReady; -// General USB control -extern uint8_t volatile g_u8Suspend; -extern uint8_t g_u8Idle; -extern uint8_t g_u8Protocol; - -extern const char *gszVendorInitial; +// === descriptors.c, used in usbd_driver.c === extern const char *gszVendor; extern const char *gszProduct; extern const usb_device_descr_t *gpDeviceDescriptor; @@ -155,128 +73,28 @@ extern const uint32_t gu32UsbHidMiscReportLen; extern const uint8_t *gpu8UsbHidIO4Report; extern const uint8_t *gpu8UsbHidMiscReport; -/*-------------------------------------------------------------*/ - -// Interfaces -enum { - USBD_ITF_CDC_CMD, - USBD_ITF_CDC_DAT, - USBD_ITF_HID_IO4, - USBD_ITF_HID_MISC, - _USBD_ITF_MAX, -}; - -// Endpoint number mapping -#define EP_CTRL_IN EP0 -#define EP_CTRL_OUT EP1 -#define EP_CDC_IN EP2 -#define EP_CDC_OUT EP3 -#define EP_CDC_CMD EP4 -#define EP_HID_IO4_IN EP5 -#define EP_HID_MISC_IN EP6 -#define EP_HID_MISC_OUT EP7 - -#define _USBD_INTSTS(x) USBD_INTSTS_EP##x -#define USBD_INTSTS(x) _USBD_INTSTS(x) - -// Must match the above!! -#define USBD_INTSTS_CTRL_IN USBD_INTSTS(EP_CTRL_IN) -#define USBD_INTSTS_CTRL_OUT USBD_INTSTS(EP_CTRL_OUT) -#define USBD_INTSTS_CDC_IN USBD_INTSTS(EP_CDC_IN) -#define USBD_INTSTS_CDC_OUT USBD_INTSTS(EP_CDC_OUT) -#define USBD_INTSTS_CDC_CMD USBD_INTSTS(EP_CDC_CMD) -#define USBD_INTSTS_HID_IO4_IN USBD_INTSTS(EP_HID_IO4_IN) -#define USBD_INTSTS_HID_MISC_IN USBD_INTSTS(EP_HID_MISC_IN) -#define USBD_INTSTS_HID_MISC_OUT USBD_INTSTS(EP_HID_MISC_OUT) - -#define USBD_CDC_EP_IN (1 | EP_INPUT) -#define USBD_CDC_EP_OUT (2 | EP_OUTPUT) -#define USBD_CDC_EP_CMD (3 | EP_INPUT) -#define USBD_HID_IO4_EP_IN (4 | EP_INPUT) -#define USBD_HID_MISC_EP_IN (5 | EP_INPUT) -#define USBD_HID_MISC_EP_OUT (6 | EP_OUTPUT) - -#define USBD_SETUP_BUF_LEN (8) -#define USBD_CDC_CMD_MAX_SIZE (16) -#define USBD_CDC_IN_MAX_SIZE (64) // Device -> Host -#define USBD_CDC_OUT_MAX_SIZE (64) // Host -> Device -#define USBD_HID_BUF_LEN (64) - -// Endpoint packet max size (cannot total more than 512!) -#define EP0_MAX_PKT_SIZE 64 -#define EP1_MAX_PKT_SIZE 64 -#define EP2_MAX_PKT_SIZE USBD_CDC_IN_MAX_SIZE -#define EP3_MAX_PKT_SIZE USBD_CDC_OUT_MAX_SIZE -#define EP4_MAX_PKT_SIZE USBD_CDC_CMD_MAX_SIZE -#define EP5_MAX_PKT_SIZE USBD_HID_BUF_LEN -#define EP6_MAX_PKT_SIZE USBD_HID_BUF_LEN -#define EP7_MAX_PKT_SIZE USBD_HID_BUF_LEN - -#define SETUP_BUF_BASE 0 -#define SETUP_BUF_LEN 8 - -#if (SETUP_BUF_LEN + EP0_MAX_PKT_SIZE + EP1_MAX_PKT_SIZE + EP2_MAX_PKT_SIZE + EP3_MAX_PKT_SIZE + \ - EP4_MAX_PKT_SIZE + EP5_MAX_PKT_SIZE + EP6_MAX_PKT_SIZE + EP7_MAX_PKT_SIZE) > 512 -#error USB endpoint packet sizes exceeds 512-byte maximum -#endif - -#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN) -#define EP1_BUF_BASE (EP0_BUF_BASE + EP0_MAX_PKT_SIZE) -#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_MAX_PKT_SIZE) -#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_MAX_PKT_SIZE) -#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_MAX_PKT_SIZE) -#define EP5_BUF_BASE (EP4_BUF_BASE + EP4_MAX_PKT_SIZE) -#define EP6_BUF_BASE (EP5_BUF_BASE + EP5_MAX_PKT_SIZE) -#define EP7_BUF_BASE (EP5_BUF_BASE + EP6_MAX_PKT_SIZE) - -// Define Descriptor information -#define HID_IO4_INT_IN_INTERVAL 8 -#define HID_DEFAULT_INT_IN_INTERVAL 1 -#define HID_DEFAULT_INT_OUT_INTERVAL 1 -#define USBD_SELF_POWERED 0 -#define USBD_REMOTE_WAKEUP 0 -#define USBD_MAX_POWER (500 / 2) - -/*-------------------------------------------------------------*/ -#define HEX_NIBBLE(x) ("0123456789ABCDEF"[(x)&0xf]) - +// === sys.c === void SYS_Init(void); void SYS_Bootloader_Check(void); void SYS_ModuleInit(void); +void SYS_EnterLDROM(void); +void SYS_WaitBootloaderLED(void); extern volatile uint8_t gu8Do250usTick; extern volatile uint8_t gu8Do1msTick; -void EP_CDC_CMD_Handler(void); -void EP_CDC_OUT_Handler(void); -void EP_CDC_IN_Handler(void); -void EP_HID_IO4_IN_Handler(void); -void EP_HID_MISC_IN_Handler(void); -void EP_HID_MISC_OUT_Handler(void); - -void USB_VCOM_Write(uint8_t u8Char); -uint16_t USB_VCOM_Available(void); -uint8_t USB_VCOM_Read(void); -void USB_VCOM_Tick(void); -void USB_VCOM_PurgeTx(void); - +// === ui.c === void UI_Tick(void); -void DelayCycles(uint32_t u32Cycles); -#define DelayCycles_Small(x) \ - for (int i = 0; i < (x); i++) __asm__ volatile("" : "+g"(i) : :); +// === Misc === +#define HEX_NIBBLE(x) ("0123456789ABCDEF"[(x)&0xf]) +#define DELAY_CYCLES_2 __asm__ volatile("NOP\nNOP\n"); -// Custom USBD implementation -void Tas_USBD_Open(void); -void Tas_USBD_Init(void); -void Tas_USBD_Start(void); -void Tas_USBD_ClassRequest(void); -void Tas_USBD_GetSetupPacket(usb_setup_t *buf); -void Tas_USBD_ProcessSetupPacket(void); -void Tas_USBD_PrepareCtrlIn(void *pu8Buf, uint32_t u32Size); -void Tas_USBD_CtrlIn(void); -void Tas_USBD_PrepareCtrlOut(void *pu8Buf, uint32_t u32Size, - void (*pCallback)(volatile uint8_t *, uint32_t)); -void Tas_USBD_CtrlOut(void); -void Tas_USBD_SwReset(void); -extern volatile uint8_t *g_usbd_CtrlInPointer; -extern volatile uint32_t g_usbd_CtrlInSize; +#define INCR(x, y) ((x) = (x) < (y) ? (x) + 1 : (y)) +#define DECR(x, y) ((x) = (x) > (y) ? (x)-1 : (y)) +#define MOD_INCR(x, y) ((x) = (x) == ((y)-1) ? 0 : ((x) + 1)) +#define MOD_DECR(x, y) ((x) = (x) == 0 ? ((y)-1) : ((x)-1)) +#define INV(x) ((x) = 1 - (x)) + +#define MS_SINCE(x) \ + ((gu32NowMs >= (x)) ? /* We haven't wrapped yet */ (gu32NowMs - (x)) \ + : /* We've wrapped over (50 days!) */ (gu32NowMs + (0xFFFFFFFF - (x)))) diff --git a/src/ui.c b/src/ui.c index 53fb765..c16e902 100644 --- a/src/ui.c +++ b/src/ui.c @@ -1,7 +1,8 @@ #include "tasoller.h" -#define FN2_HOLD_TIME 250 // ms of FN2 to enter config -#define FN1_HOLD_TIME 2000 // ms of FN1 to enter calibration +#define FN1_HOLD_TIME 250 // ms of FN1 to enter config +#define FN2_TAP_TIME 250 // ms of FN2 to enter service/test +#define FN2_HOLD_TIME 500 // ms of FN2 to enter service/test // All three of these are scaled based on gConfig.u8Sens #define CALIB_HANDS_MIN 100 // If we read less than this, ignore it @@ -12,14 +13,17 @@ #define GREEN 120 #define BLUE 240 +uint8_t gbUIOpen = 0; +static uint8_t u8TestIsActive = 0; + static void UI_TickSensitivity(void) { - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - gaControlledIntLedData[i].u16H = 0; - gaControlledIntLedData[i].u8S = (gConfig.u8Sens - 1) * 16; - gaControlledIntLedData[i].u8V = 0; + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + gaControlledIntLedData[i].h = 0; + gaControlledIntLedData[i].s = (gConfig.u8Sens - 1) * 16; + gaControlledIntLedData[i].v = 0; } - for (uint8_t i = 0; i < gConfig.u8Sens; i++) gaControlledIntLedData[i * 2].u8V = 255; + for (uint8_t i = 0; i < gConfig.u8Sens; i++) gaControlledIntLedData[i * 2].v = 255; // Preferentially allow decreasing of sensitivity, in case it's been taken up too high if (gu32PSoCDigitalPos & PAD_26_Msk && gConfig.u8Sens > 1) { @@ -29,9 +33,9 @@ static void UI_TickSensitivity(void) { } } +static uint8_t su8SensTimeout = 0; static void UI_TickSettings(void) { // If either of the sensitivity settings are being changed, just render that - static uint8_t su8SensTimeout = 0; if (gu16PSoCDigital & CELL_12_Msk) { su8SensTimeout = 150; UI_TickSensitivity(); @@ -41,27 +45,35 @@ static void UI_TickSettings(void) { UI_TickSensitivity(); // Save the sensitivity on exit - if (su8SensTimeout == 0) PSoC_SetFingerCapacitanceFromConfig(); + if (su8SensTimeout == 0) PSoC_SetFingerCapacitanceFromConfig(1); return; } - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - gaControlledIntLedData[i].u16H = 0; - gaControlledIntLedData[i].u8S = 255; - gaControlledIntLedData[i].u8V = 0; + static uint8_t u8Pulser = 0; + static uint8_t u8PulserDir = 0; + if (u8PulserDir) { + if (--u8Pulser == 0) u8PulserDir = 0; + } else { + if (++u8Pulser == 255) u8PulserDir = 1; + } + + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + gaControlledIntLedData[i].h = 0; + gaControlledIntLedData[i].s = 255; + gaControlledIntLedData[i].v = 0; } { // LED colour control - gaControlledIntLedData[LED_CELL_0].u16H = gConfig.u16HueWingLeft; - gaControlledIntLedData[LED_CELL_0].u8V = 255; - gaControlledIntLedData[LED_CELL_1].u16H = gConfig.u16HueGround; - gaControlledIntLedData[LED_CELL_1].u8V = 255; - gaControlledIntLedData[LED_DIVIDER_1_2].u16H = gConfig.u16HueGroundActive; - gaControlledIntLedData[LED_DIVIDER_1_2].u8V = 255; - gaControlledIntLedData[LED_CELL_2].u16H = gConfig.u16HueGround; - gaControlledIntLedData[LED_CELL_2].u8V = 255; - gaControlledIntLedData[LED_CELL_3].u16H = gConfig.u16HueWingRight; - gaControlledIntLedData[LED_CELL_3].u8V = 255; + gaControlledIntLedData[LED_CELL_0].h = gConfig.u16HueWingLeft; + gaControlledIntLedData[LED_CELL_0].v = 255; + gaControlledIntLedData[LED_CELL_1].h = gConfig.u16HueGround; + gaControlledIntLedData[LED_CELL_1].v = 255; + gaControlledIntLedData[LED_DIVIDER_1_2].h = gConfig.u16HueGroundActive; + gaControlledIntLedData[LED_DIVIDER_1_2].v = 255; + gaControlledIntLedData[LED_CELL_2].h = gConfig.u16HueGround; + gaControlledIntLedData[LED_CELL_2].v = 255; + gaControlledIntLedData[LED_CELL_3].h = gConfig.u16HueWingRight; + gaControlledIntLedData[LED_CELL_3].v = 255; if (gu32PSoCDigitalTrig & PAD_1_Msk) MOD_INCR(gConfig.u16HueWingLeft, LED_HUE_MAX); if (gu32PSoCDigitalTrig & PAD_2_Msk) MOD_DECR(gConfig.u16HueWingLeft, LED_HUE_MAX); @@ -77,27 +89,27 @@ static void UI_TickSettings(void) { static uint16_t su16Hue = 0; MOD_INCR(su16Hue, LED_HUE_MAX * 5); if (gConfig.bEnableRainbow) { - gaControlledIntLedData[LED_CELL_5].u16H = su16Hue / 5; - gaControlledIntLedData[LED_CELL_5].u8V = 255; + gaControlledIntLedData[LED_CELL_5].h = su16Hue / 5; + gaControlledIntLedData[LED_CELL_5].v = 255; } else { - gaControlledIntLedData[LED_CELL_5].u8S = 0; - gaControlledIntLedData[LED_CELL_5].u8V = 255; + gaControlledIntLedData[LED_CELL_5].s = 0; + gaControlledIntLedData[LED_CELL_5].v = 255; } if (gu16PSoCDigitalPos & CELL_5_Msk) INV(gConfig.bEnableRainbow); } { // Brightness if (gConfig.u8LedGroundBrightness) { - gaControlledIntLedData[LED_CELL_6].u8S = 0; - gaControlledIntLedData[LED_CELL_6].u8V = gConfig.u8LedGroundBrightness; + gaControlledIntLedData[LED_CELL_6].s = 0; + gaControlledIntLedData[LED_CELL_6].v = gConfig.u8LedGroundBrightness; } else { - gaControlledIntLedData[LED_CELL_6].u8V = 255; + gaControlledIntLedData[LED_CELL_6].v = 255; } if (gConfig.u8LedWingBrightness) { - gaControlledIntLedData[LED_CELL_7].u8S = 0; - gaControlledIntLedData[LED_CELL_7].u8V = gConfig.u8LedWingBrightness; + gaControlledIntLedData[LED_CELL_7].s = 0; + gaControlledIntLedData[LED_CELL_7].v = gConfig.u8LedWingBrightness; } else { - gaControlledIntLedData[LED_CELL_7].u8V = 255; + gaControlledIntLedData[LED_CELL_7].v = 255; } if (gu32PSoCDigitalTrig & PAD_13_Msk) INCR(gConfig.u8LedGroundBrightness, 255); @@ -105,145 +117,201 @@ static void UI_TickSettings(void) { if (gu32PSoCDigitalTrig & PAD_15_Msk) INCR(gConfig.u8LedWingBrightness, 255); if (gu32PSoCDigitalTrig & PAD_16_Msk) DECR(gConfig.u8LedWingBrightness, 0); } - // [Cell 8,9,10,11 no function] + // [Cell 8 no function] + + { // Consumer control + gaControlledIntLedData[LED_CELL_9].s = 0; + gaControlledIntLedData[LED_CELL_9].v = 255; + if (u32EnterPressStarted != 0xFFFFFFFF) { + gaControlledIntLedData[LED_CELL_10].s = 0; + gaControlledIntLedData[LED_CELL_10].v = u8Pulser; + } else { + gaControlledIntLedData[LED_CELL_10].s = 0; + gaControlledIntLedData[LED_CELL_10].v = 255; + } + + if (gu32PSoCDigital & PAD_20_Msk) { + u16RequestedConsumerControl = MEDIA_VOLUME_DOWN; + } else if (gu32PSoCDigital & PAD_19_Msk) { + u16RequestedConsumerControl = MEDIA_VOLUME_UP; + } else { + u16RequestedConsumerControl = 0; + } + + if (gu16PSoCDigitalPos & CELL_10_Msk) { + if (u32EnterPressStarted == 0xFFFFFFFF) u32EnterPressStarted = gu32NowMs; + } + } + + // [Cell 11 no function] + { // Sensitivity control (handled in dedicated function) - gaControlledIntLedData[LED_CELL_12].u8S = (gConfig.u8Sens - 1) * 16; - gaControlledIntLedData[LED_CELL_12].u8V = 255; + gaControlledIntLedData[LED_CELL_12].s = (gConfig.u8Sens - 1) * 16; + gaControlledIntLedData[LED_CELL_12].v = 255; } - // [Cell 13 no function] + // [Cell 13,14 no function] { // Mode switching - gaControlledIntLedData[LED_CELL_14].u16H = gConfig.bEnableKeyboard ? GREEN : RED; - gaControlledIntLedData[LED_CELL_14].u8V = 255; - gaControlledIntLedData[LED_CELL_15].u16H = gConfig.bEnableIO4 ? GREEN : RED; - gaControlledIntLedData[LED_CELL_15].u8V = 255; + gaControlledIntLedData[LED_CELL_15].h = gConfig.bEnableKeyboard ? GREEN : RED; + gaControlledIntLedData[LED_CELL_15].v = 255; - if (gu16PSoCDigitalPos & CELL_14_Msk) INV(gConfig.bEnableKeyboard); - if (gu16PSoCDigitalPos & CELL_15_Msk) INV(gConfig.bEnableIO4); + if (gu16PSoCDigitalPos & CELL_15_Msk) INV(gConfig.bEnableKeyboard); } } -static inline void _FillControlled(uint16_t u16H, uint8_t u8Fill) { - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - gaControlledIntLedData[i].u16H = u16H; - gaControlledIntLedData[i].u8S = 255; - gaControlledIntLedData[i].u8V = i < u8Fill ? 255 : 0; +static uint32_t su32EnteredTestMenuAt = 0; +static void UI_TickServiceTest(void) { + uint8_t u8V = 0; + // Zero out the LED data + for (uint8_t i = 0; i < LED_NUM_GROUND_LOGICAL; i++) { + gaControlledIntLedData[i].h = 0; + gaControlledIntLedData[i].s = 0; + gaControlledIntLedData[i].v = 0; } -} -static uint8_t bCalibrationActive = 0; -static void UI_TickCalibration(void) { - static uint16_t u16Timer = 0; - static uint16_t su16MaxNoHands[32] = { 0 }; - static uint16_t su16MaxHands[32] = { 0 }; - - if (!bCalibrationActive) { - // Calibration start - u16Timer = 0; - bCalibrationActive = 1; - memset(su16MaxNoHands, 0, sizeof su16MaxNoHands); - memset(su16MaxHands, 0, sizeof su16MaxHands); - } - // Only tick the timer when we're using it, to avoid overflow - if (u16Timer < 5000) u16Timer++; - - if (u16Timer < 3000) { - // Flash red for the first 3 seconds - _FillControlled(RED, ((u16Timer / 250) & 1) ? 0 : LED_NUM_GROUND); - } else if (u16Timer < 4000) { - // Fill up the cells with blue - _FillControlled(BLUE, (u16Timer - 3000) / (1000 / LED_NUM_GROUND)); - for (uint8_t i; i < 32; i++) { - su16MaxNoHands[i] = Maximum(su16MaxNoHands[i], gu16PSoCDiff[i]); - } - } else if (u16Timer < 5000) { - // Flash green for the next second - _FillControlled(GREEN, ((u16Timer / 250) & 1) ? 0 : LED_NUM_GROUND); - } else { - for (uint8_t i = 0; i < 32; i++) { - // As well as the raw minimum, force a small constant threshold minimum too - su16MaxHands[i] = Maximum(su16MaxHands[i], gu16PSoCDiff[i] + (gConfig.u8Sens * 5)); + uint8_t u8ForceTest = 0; + if (u8TestIsActive) { + // Send a test button, to trigger entry to the test menu + if (MS_SINCE(su32EnteredTestMenuAt) < 100) { + gu16IO4ForceButtons |= IO4_BUTTON_TEST; + u8ForceTest = 1; } - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - gaControlledIntLedData[i].u8S = 255; - gaControlledIntLedData[i].u8V = 255; - } - - uint16_t u16CalibMin = CALIB_HANDS_MIN + (gConfig.u8Sens * 50); - uint16_t u16CalibReq = CALIB_HANDS_REQ + (gConfig.u8Sens * 50); - uint16_t u16CalibMax = CALIB_HANDS_MAX + (gConfig.u8Sens * 50); - - uint8_t bOk = 1; - // Iterate over the cells - for (uint8_t i = 0; i < 16; i++) { - if (su16MaxHands[i * 2] < u16CalibMin || su16MaxHands[i * 2 + 1] < u16CalibMin || - su16MaxHands[i * 2] < su16MaxNoHands[i * 2] || - su16MaxHands[i * 2 + 1] < su16MaxNoHands[i * 2 + 1]) { - // Not enough data - gaControlledIntLedData[i * 2].u8V = 0; - bOk = 0; - } else if (su16MaxHands[i * 2] < u16CalibReq || su16MaxHands[i * 2 + 1] < u16CalibReq) { - // Data is too low - gaControlledIntLedData[i * 2].u16H = RED; - bOk = 0; - } else if (su16MaxHands[i * 2] < u16CalibMax || su16MaxHands[i * 2 + 1] < u16CalibMax) { - // We've got enough, but it could be better - uint16_t u16Min = Minimum(su16MaxHands[i * 2], su16MaxHands[i * 2 + 1]); - gaControlledIntLedData[i * 2].u16H = - ((u16Min - u16CalibReq) * GREEN) / (u16CalibMax - u16CalibReq); + // Implement the on-screen buttons + { // Down + if (gu16PSoCDigital & (CELL_0_Msk | CELL_1_Msk | CELL_2_Msk)) { + u8V = 200; } else { - // More than enough data - gaControlledIntLedData[i * 2].u16H = GREEN; + u8V = 50; } + gaControlledIntLedData[LED_CELL_0].v = u8V; + gaControlledIntLedData[LED_DIVIDER_0_1].v = u8V; + gaControlledIntLedData[LED_CELL_1].v = u8V; + gaControlledIntLedData[LED_DIVIDER_1_2].v = u8V; + gaControlledIntLedData[LED_CELL_2].v = u8V; } - - for (uint8_t i = 0; i < LED_NUM_GROUND; i++) { - if (i % 2 == 1) { - gaControlledIntLedData[i].u16H = bOk ? GREEN : RED; + gaControlledIntLedData[LED_DIVIDER_2_3].v = 255; + { // Up + if (gu16PSoCDigital & (CELL_3_Msk | CELL_4_Msk | CELL_5_Msk)) { + u8V = 200; + } else { + u8V = 50; } + gaControlledIntLedData[LED_CELL_3].v = u8V; + gaControlledIntLedData[LED_DIVIDER_3_4].v = u8V; + gaControlledIntLedData[LED_CELL_4].v = u8V; + gaControlledIntLedData[LED_DIVIDER_4_5].v = u8V; + gaControlledIntLedData[LED_CELL_5].v = u8V; } + gaControlledIntLedData[LED_DIVIDER_5_6].v = 255; - // Calibration complete - if (gu8DigitalButtons & DIGITAL_FN1_Msk) { - bCalibrationActive = 0; - - if (bOk) { - memcpy(gConfig.u16PSoCScaleMin, su16MaxNoHands, sizeof su16MaxNoHands); - memcpy(gConfig.u16PSoCScaleMax, su16MaxHands, sizeof su16MaxHands); - bConfigDirty = 1; + gaControlledIntLedData[LED_DIVIDER_12_13].v = 255; + { // OK + if (gu16PSoCDigital & (CELL_13_Msk | CELL_14_Msk | CELL_15_Msk)) { + u8V = 200; + } else { + u8V = 50; } + gaControlledIntLedData[LED_CELL_13].v = u8V; + gaControlledIntLedData[LED_DIVIDER_13_14].v = u8V; + gaControlledIntLedData[LED_CELL_14].v = u8V; + gaControlledIntLedData[LED_DIVIDER_14_15].v = u8V; + gaControlledIntLedData[LED_CELL_15].v = u8V; } } + + // Implement our custom buttons for test and service + gaControlledIntLedData[LED_DIVIDER_5_6].v = 255; + { // Test + if (gu16PSoCDigital & (CELL_6_Msk | CELL_7_Msk)) { + u8V = 200; + gu16IO4ForceButtons |= IO4_BUTTON_TEST; + } else { + u8V = 50; + // Don't un-press the test button if we're forcing it on + if (!u8ForceTest) gu16IO4ForceButtons &= ~IO4_BUTTON_TEST; + } + gaControlledIntLedData[LED_CELL_6].v = u8V; + gaControlledIntLedData[LED_DIVIDER_6_7].v = u8V; + gaControlledIntLedData[LED_CELL_7].v = u8V; + } + gaControlledIntLedData[LED_DIVIDER_7_8].v = 255; + { // Service + if (gu16PSoCDigital & (CELL_8_Msk | CELL_9_Msk)) { + u8V = 200; + gu16IO4ForceButtons |= IO4_BUTTON_SERVICE; + } else { + u8V = 50; + gu16IO4ForceButtons &= ~IO4_BUTTON_SERVICE; + } + gaControlledIntLedData[LED_CELL_8].v = u8V; + gaControlledIntLedData[LED_DIVIDER_8_9].v = u8V; + gaControlledIntLedData[LED_CELL_9].v = u8V; + } + gaControlledIntLedData[LED_DIVIDER_9_10].v = 255; } void UI_Tick(void) { - static uint8_t u8Fn2Held = 0; - if (gu8DigitalButtons & DIGITAL_FN2_Msk) { - if (u8Fn2Held < FN2_HOLD_TIME) u8Fn2Held++; + // Handle hold trigger on FN1 + static uint8_t u8Fn1Held = 0; + if (gu8DigitalButtons & DIGITAL_FN1_Msk) { + if (u8Fn1Held < FN1_HOLD_TIME) u8Fn1Held++; } else { // We released the button after holding it for long enough to be in the configuration UI, so // assume something changed - if (u8Fn2Held >= FN2_HOLD_TIME) { + if (u8Fn1Held >= FN1_HOLD_TIME) { + // If FN2 was released while still in sensitivity adjustment, make sure the changes save + if (su8SensTimeout) { + PSoC_SetFingerCapacitanceFromConfig(1); + su8SensTimeout = 0; + } + bConfigDirty = 1; } - u8Fn2Held = 0; + u16RequestedConsumerControl = 0; + u32EnterPressStarted = 0; + u8Fn1Held = 0; } - static uint16_t u16Fn1Held = 0; - if (gu8DigitalButtons & DIGITAL_FN1_Msk) { - if (u16Fn1Held < FN1_HOLD_TIME) u16Fn1Held++; + // Handle double tap trigger on FN2 + static uint32_t u32LastFn2 = 0; + static uint8_t u8LastDB = 0; + const uint8_t u8PostDb = gu8DigitalButtons & (~u8LastDB); + if (u8PostDb & DIGITAL_FN2_Msk) { + if (u32LastFn2 && MS_SINCE(u32LastFn2) < FN2_TAP_TIME) { + u8TestIsActive = !u8TestIsActive; + + if (u8TestIsActive) { + su32EnteredTestMenuAt = gu32NowMs; + gu16IO4ForceButtons = IO4_BUTTON_TEST; + } else { + su32EnteredTestMenuAt = 0; + gu16IO4ForceButtons = 0; + } + } + u32LastFn2 = gu32NowMs; + } + u8LastDB = gu8DigitalButtons; + + // Handle hold trigger on FN2 + static uint16_t u16Fn2Held = 0; + if (gu8DigitalButtons & DIGITAL_FN2_Msk) { + if (u16Fn2Held < FN2_HOLD_TIME) u16Fn2Held++; } else { - u16Fn1Held = 0; + u16Fn2Held = 0; } - if (bCalibrationActive || u16Fn1Held >= FN1_HOLD_TIME) { - gbLedDataIsControlledInt = 1; - UI_TickCalibration(); - } else if (u8Fn2Held >= FN2_HOLD_TIME) { + // Render the appropriate UI based on what's being done + if (u8Fn1Held >= FN1_HOLD_TIME) { gbLedDataIsControlledInt = 1; + gbUIOpen = 1; UI_TickSettings(); + } else if (u16Fn2Held >= FN2_HOLD_TIME || u8TestIsActive) { + gbLedDataIsControlledInt = 1; + gbUIOpen = 0; + UI_TickServiceTest(); } else { gbLedDataIsControlledInt = 0; + gbUIOpen = 0; } } diff --git a/src/usb_def.h b/src/usb_def.h new file mode 100644 index 0000000..2442029 --- /dev/null +++ b/src/usb_def.h @@ -0,0 +1,129 @@ +#pragma once +#pragma once + +#include + +#define USB_STATE_FLOATING 2 +#define USB_STATE_SUSPEND 1 + +// USB definition files +#include "hid_def.h" +#include "usb_inc/hid.h" +#include "usb_inc/keymap.h" +#include "usb_inc/usb.h" + +// Interfaces +enum : uint8_t { + USBD_ITF_CDC_CMD, + USBD_ITF_CDC_DAT, + USBD_ITF_HID_IO4, + USBD_ITF_HID_MISC, + _USBD_ITF_MAX, +}; + +// Endpoint number mapping +#define EP_CTRL_IN EP0 +#define EP_CTRL_OUT EP1 +#define EP_CDC_IN EP2 +#define EP_CDC_OUT EP3 +#define EP_CDC_CMD EP4 +#define EP_HID_IO4_IN EP5 +#define EP_HID_MISC_IN EP6 +#define EP_HID_MISC_OUT EP7 + +#define _USBD_INTSTS(x) USBD_INTSTS_EP##x +#define USBD_INTSTS(x) _USBD_INTSTS(x) + +// Must match the above!! +#define USBD_INTSTS_CTRL_IN USBD_INTSTS(EP_CTRL_IN) +#define USBD_INTSTS_CTRL_OUT USBD_INTSTS(EP_CTRL_OUT) +#define USBD_INTSTS_CDC_IN USBD_INTSTS(EP_CDC_IN) +#define USBD_INTSTS_CDC_OUT USBD_INTSTS(EP_CDC_OUT) +#define USBD_INTSTS_CDC_CMD USBD_INTSTS(EP_CDC_CMD) +#define USBD_INTSTS_HID_IO4_IN USBD_INTSTS(EP_HID_IO4_IN) +#define USBD_INTSTS_HID_MISC_IN USBD_INTSTS(EP_HID_MISC_IN) +#define USBD_INTSTS_HID_MISC_OUT USBD_INTSTS(EP_HID_MISC_OUT) + +#define USBD_CDC_EP_IN (1 | EP_INPUT) +#define USBD_CDC_EP_OUT (2 | EP_OUTPUT) +#define USBD_CDC_EP_CMD (3 | EP_INPUT) +#define USBD_HID_IO4_EP_IN (4 | EP_INPUT) +#define USBD_HID_MISC_EP_IN (5 | EP_INPUT) +#define USBD_HID_MISC_EP_OUT (6 | EP_OUTPUT) + +#define USBD_SETUP_BUF_LEN (8) +#define USBD_CDC_CMD_MAX_SIZE (16) +#define USBD_CDC_IN_MAX_SIZE (64) // Device -> Host +#define USBD_CDC_OUT_MAX_SIZE (64) // Host -> Device +#define USBD_HID_BUF_LEN (64) + +_Static_assert(USBD_HID_BUF_LEN >= sizeof(hid_kbd_report_t) && + USBD_HID_BUF_LEN >= sizeof(hid_consumer_report_t) && + USBD_HID_BUF_LEN >= sizeof(io4_hid_in_t) && + USBD_HID_BUF_LEN >= sizeof(io4_hid_out_t), + "HID USB buffer insufficient size for possible reports"); + +// Endpoint packet max size (cannot total more than 512!) +#define EP0_MAX_PKT_SIZE 64 +#define EP1_MAX_PKT_SIZE 64 +#define EP2_MAX_PKT_SIZE USBD_CDC_IN_MAX_SIZE +#define EP3_MAX_PKT_SIZE USBD_CDC_OUT_MAX_SIZE +#define EP4_MAX_PKT_SIZE USBD_CDC_CMD_MAX_SIZE +#define EP5_MAX_PKT_SIZE USBD_HID_BUF_LEN +#define EP6_MAX_PKT_SIZE USBD_HID_BUF_LEN +#define EP7_MAX_PKT_SIZE USBD_HID_BUF_LEN + +#define SETUP_BUF_BASE 0 +#define SETUP_BUF_LEN 8 + +_Static_assert((SETUP_BUF_LEN + EP0_MAX_PKT_SIZE + EP1_MAX_PKT_SIZE + EP2_MAX_PKT_SIZE + + EP3_MAX_PKT_SIZE + EP4_MAX_PKT_SIZE + EP5_MAX_PKT_SIZE + EP6_MAX_PKT_SIZE + + EP7_MAX_PKT_SIZE) <= 512, + "USB endpoint packet sizes exceeds 512-byte maximum"); + +#define EP0_BUF_BASE (SETUP_BUF_BASE + SETUP_BUF_LEN) +#define EP1_BUF_BASE (EP0_BUF_BASE + EP0_MAX_PKT_SIZE) +#define EP2_BUF_BASE (EP1_BUF_BASE + EP1_MAX_PKT_SIZE) +#define EP3_BUF_BASE (EP2_BUF_BASE + EP2_MAX_PKT_SIZE) +#define EP4_BUF_BASE (EP3_BUF_BASE + EP3_MAX_PKT_SIZE) +#define EP5_BUF_BASE (EP4_BUF_BASE + EP4_MAX_PKT_SIZE) +#define EP6_BUF_BASE (EP5_BUF_BASE + EP5_MAX_PKT_SIZE) +#define EP7_BUF_BASE (EP5_BUF_BASE + EP6_MAX_PKT_SIZE) + +// Define Descriptor information +#define HID_IO4_INT_IN_INTERVAL 8 +#define HID_DEFAULT_INT_IN_INTERVAL 1 +#define HID_DEFAULT_INT_OUT_INTERVAL 1 +#define USBD_SELF_POWERED_Pos 6 +#define USBD_SELF_POWERED_Msk (1 << USBD_SELF_POWERED_Pos) +#define USBD_REMOTE_WAKEUP_Pos 5 +#define USBD_REMOTE_WAKEUP_Msk (1 << USBD_REMOTE_WAKEUP_Pos) +#define USBD_MAX_POWER (500 / 2) + +// Endpoint handler functions (usbd_user.c) +void EP_CDC_OUT_Handler(void); +void EP_CDC_IN_Handler(void); +void EP_HID_IO4_IN_Handler(void); +void EP_HID_MISC_IN_Handler(void); +void EP_HID_MISC_OUT_Handler(void); + +// Custom USBD implementation (usbd_driver.c) +void Tas_USBD_Open(void); +void Tas_USBD_Init(void); +void Tas_USBD_Start(void); +void Tas_USBD_ClassRequest(void); +void Tas_USBD_GetSetupPacket(usb_setup_t *buf); +void Tas_USBD_ProcessSetupPacket(void); +void Tas_USBD_PrepareCtrlIn(void *pu8Buf, uint32_t u32Size); +void Tas_USBD_CtrlIn(void); +void Tas_USBD_PrepareCtrlOut(void *pu8Buf, uint32_t u32Size, + void (*pCallback)(volatile uint8_t *, uint32_t)); +void Tas_USBD_CtrlOut(void); +void Tas_USBD_SwReset(void); +extern volatile uint8_t *g_usbd_CtrlInPointer; +extern volatile uint32_t g_usbd_CtrlInSize; + +// General USB control +extern volatile uint8_t g_u8UsbState; +extern uint8_t g_u8Idle; +extern uint8_t g_u8Protocol; diff --git a/src/usb_inc/keymap.h b/src/usb_inc/keymap.h index 6a1dd88..651559c 100644 --- a/src/usb_inc/keymap.h +++ b/src/usb_inc/keymap.h @@ -622,22 +622,8 @@ static const uint16_t _asciimap[] = { // The following characters belong to ISO-8859-15 // ! The first 16 values here are used for the numpad - KEYPAD_0, - KEYPAD_1, - KEYPAD_2, - KEYPAD_3, - KEYPAD_4, - KEYPAD_5, - KEYPAD_6, - KEYPAD_7, - KEYPAD_8, - KEYPAD_9, - KEYPAD_ADD, - KEYPAD_SUBTRACT, - KEYPAD_MULTIPLY, - KEYPAD_DIVIDE, - KEYPAD_ENTER, - KEYPAD_DOT, + KEYPAD_0, KEYPAD_1, KEYPAD_2, KEYPAD_3, KEYPAD_4, KEYPAD_5, KEYPAD_6, KEYPAD_7, KEYPAD_8, + KEYPAD_9, KEYPAD_ADD, KEYPAD_SUBTRACT, KEYPAD_MULTIPLY, KEYPAD_DIVIDE, KEYPAD_ENTER, KEYPAD_DOT, // KEY_RESERVED, // 128 - Unused // KEY_RESERVED, // 129 - Unused // KEY_RESERVED, // 130 - Unused @@ -767,3 +753,425 @@ static const uint16_t _asciimap[] = { KEY_RESERVED, // 254 - Thorn KEY_RESERVED, // 255 - 'y' Umlaut }; + +enum ConsumerKeycode : uint16_t { + // Some keys might only work with linux + CONSUMER_POWER = 0x30, + CONSUMER_SLEEP = 0x32, + + MEDIA_RECORD = 0xB2, + MEDIA_FAST_FORWARD = 0xB3, + MEDIA_REWIND = 0xB4, + MEDIA_NEXT = 0xB5, + MEDIA_PREVIOUS = 0xB6, + MEDIA_PREV = 0xB6, // Alias + MEDIA_STOP = 0xB7, + MEDIA_PLAY_PAUSE = 0xCD, + MEDIA_PAUSE = 0xB0, + + MEDIA_VOLUME_MUTE = 0xE2, + MEDIA_VOL_MUTE = 0xE2, // Alias + MEDIA_VOLUME_UP = 0xE9, + MEDIA_VOL_UP = 0xE9, // Alias + MEDIA_VOLUME_DOWN = 0xEA, + MEDIA_VOL_DOWN = 0xEA, // Alias + + CONSUMER_BRIGHTNESS_UP = 0x006F, + CONSUMER_BRIGHTNESS_DOWN = 0x0070, + + CONSUMER_SCREENSAVER = 0x19e, + + CONSUMER_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x182, + CONSUMER_CONTROL_CONFIGURATION = 0x183, + CONSUMER_EMAIL_READER = 0x18A, + CONSUMER_CALCULATOR = 0x192, + CONSUMER_EXPLORER = 0x194, + + CONSUMER_BROWSER_HOME = 0x223, + CONSUMER_BROWSER_BACK = 0x224, + CONSUMER_BROWSER_FORWARD = 0x225, + CONSUMER_BROWSER_REFRESH = 0x227, + CONSUMER_BROWSER_BOOKMARKS = 0x22A, + + // Consumer_Page_(0x0C) 0x15 + HID_CONSUMER_UNASSIGNED = 0x00, + HID_CONSUMER_NUMERIC_KEY_PAD = 0x02, // HID type NARY + HID_CONSUMER_PROGRAMMABLE_BUTTONS = 0x03, // HID type NARY + HID_CONSUMER_MICROPHONE_CA = 0x04, + HID_CONSUMER_HEADPHONE_CA = 0x05, + HID_CONSUMER_GRAPHIC_EQUALIZER_CA = 0x06, + // Reserved = 0x07-1F + HID_CONSUMER_PLUS_10 = 0x20, // HID type OSC + HID_CONSUMER_PLUS_100 = 0x21, // HID type OSC + HID_CONSUMER_AM_SLASH_PM = 0x22, // HID type OSC + // Reserved = 0x23-3F + HID_CONSUMER_POWER = 0x30, // HID type OOC + HID_CONSUMER_RESET = 0x31, // HID type OSC + HID_CONSUMER_SLEEP = 0x32, // HID type OSC + HID_CONSUMER_SLEEP_AFTER = 0x33, // HID type OSC + HID_CONSUMER_SLEEP_MODE = 0x34, // HID type RTC + HID_CONSUMER_ILLUMINATION = 0x35, // HID type OOC + HID_CONSUMER_FUNCTION_BUTTONS = 0x36, // HID type NARY + // Reserved = 0x37-3F + HID_CONSUMER_MENU = 0x40, // HID type OOC + HID_CONSUMER_MENU_PICK = 0x41, // HID type OSC + HID_CONSUMER_MENU_UP = 0x42, // HID type OSC + HID_CONSUMER_MENU_DOWN = 0x43, // HID type OSC + HID_CONSUMER_MENU_LEFT = 0x44, // HID type OSC + HID_CONSUMER_MENU_RIGHT = 0x45, // HID type OSC + HID_CONSUMER_MENU_ESCAPE = 0x46, // HID type OSC + HID_CONSUMER_MENU_VALUE_INCREASE = 0x47, // HID type OSC + HID_CONSUMER_MENU_VALUE_DECREASE = 0x48, // HID type OSC + // Reserved 0x49-5F + HID_CONSUMER_DATA_ON_SCREEN = 0x60, // HID type OOC + HID_CONSUMER_CLOSED_CAPTION = 0x61, // HID type OOC + HID_CONSUMER_CLOSED_CAPTION_SELECT = 0x62, // HID type OSC + HID_CONSUMER_VCR_SLASH_TV = 0x63, // HID type OOC + HID_CONSUMER_BROADCAST_MODE = 0x64, // HID type OSC + HID_CONSUMER_SNAPSHOT = 0x65, // HID type OSC + HID_CONSUMER_STILL = 0x66, // HID type OSC + // Reserved 0x67-7F + HID_CONSUMER_SELECTION = 0x80, // HID type NARY + HID_CONSUMER_ASSIGN_SELECTION = 0x81, // HID type OSC + HID_CONSUMER_MODE_STEP = 0x82, // HID type OSC + HID_CONSUMER_RECALL_LAST = 0x83, // HID type OSC + HID_CONSUMER_ENTER_CHANNEL = 0x84, // HID type OSC + HID_CONSUMER_ORDER_MOVIE = 0x85, // HID type OSC + HID_CONSUMER_CHANNEL = 0x86, // HID type LC + HID_CONSUMER_MEDIA_SELECTION = 0x87, // HID type NARY + HID_CONSUMER_MEDIA_SELECT_COMPUTER = 0x88, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_TV = 0x89, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_WWW = 0x8A, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_DVD = 0x8B, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_TELEPHONE = 0x8C, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_PROGRAM_GUIDE = 0x8D, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_VIDEO_PHONE = 0x8E, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_GAMES = 0x8F, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_MESSAGES = 0x90, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_CD = 0x91, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_VCR = 0x92, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_TUNER = 0x93, // HID type SEL + HID_CONSUMER_QUIT = 0x94, // HID type OSC + HID_CONSUMER_HELP = 0x95, // HID type OOC + HID_CONSUMER_MEDIA_SELECT_TAPE = 0x96, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_CABLE = 0x97, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_SATELLITE = 0x98, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_SECURITY = 0x99, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_HOME = 0x9A, // HID type SEL + HID_CONSUMER_MEDIA_SELECT_CALL = 0x9B, // HID type SEL + HID_CONSUMER_CHANNEL_INCREMENT = 0x9C, // HID type OSC + HID_CONSUMER_CHANNEL_DECREMENT = 0x9D, // HID type OSC + HID_CONSUMER_MEDIA_SELECT_SAP = 0x9E, // HID type SEL + // Reserved 0x9F + HID_CONSUMER_VCR_PLUS = 0xA0, // HID type OSC + HID_CONSUMER_ONCE = 0xA1, // HID type OSC + HID_CONSUMER_DAILY = 0xA2, // HID type OSC + HID_CONSUMER_WEEKLY = 0xA3, // HID type OSC + HID_CONSUMER_MONTHLY = 0xA4, // HID type OSC + // Reserved 0xA5-AF + HID_CONSUMER_PLAY = 0xB0, // HID type OOC + HID_CONSUMER_PAUSE = 0xB1, // HID type OOC + HID_CONSUMER_RECORD = 0xB2, // HID type OOC + HID_CONSUMER_FAST_FORWARD = 0xB3, // HID type OOC + HID_CONSUMER_REWIND = 0xB4, // HID type OOC + HID_CONSUMER_SCAN_NEXT_TRACK = 0xB5, // HID type OSC + HID_CONSUMER_SCAN_PREVIOUS_TRACK = 0xB6, // HID type OSC + HID_CONSUMER_STOP = 0xB7, // HID type OSC + HID_CONSUMER_EJECT = 0xB8, // HID type OSC + HID_CONSUMER_RANDOM_PLAY = 0xB9, // HID type OOC + HID_CONSUMER_SELECT_DISC = 0xBA, // HID type NARY + HID_CONSUMER_ENTER_DISC_MC = 0xBB, + HID_CONSUMER_REPEAT = 0xBC, // HID type OSC + HID_CONSUMER_TRACKING = 0xBD, // HID type LC + HID_CONSUMER_TRACK_NORMAL = 0xBE, // HID type OSC + HID_CONSUMER_SLOW_TRACKING = 0xBF, // HID type LC + HID_CONSUMER_FRAME_FORWARD = 0xC0, // HID type RTC + HID_CONSUMER_FRAME_BACK = 0xC1, // HID type RTC + HID_CONSUMER_MARK = 0xC2, // HID type OSC + HID_CONSUMER_CLEAR_MARK = 0xC3, // HID type OSC + HID_CONSUMER_REPEAT_FROM_MARK = 0xC4, // HID type OOC + HID_CONSUMER_RETURN_TO_MARK = 0xC5, // HID type OSC + HID_CONSUMER_SEARCH_MARK_FORWARD = 0xC6, // HID type OSC + HID_CONSUMER_SEARCH_MARK_BACKWARDS = 0xC7, // HID type OSC + HID_CONSUMER_COUNTER_RESET = 0xC8, // HID type OSC + HID_CONSUMER_SHOW_COUNTER = 0xC9, // HID type OSC + HID_CONSUMER_TRACKING_INCREMENT = 0xCA, // HID type RTC + HID_CONSUMER_TRACKING_DECREMENT = 0xCB, // HID type RTC + HID_CONSUMER_STOP_SLASH_EJECT = 0xCC, // HID type OSC + HID_CONSUMER_PLAY_SLASH_PAUSE = 0xCD, // HID type OSC + HID_CONSUMER_PLAY_SLASH_SKIP = 0xCE, // HID type OSC + // Reserved 0xCF-DF + HID_CONSUMER_VOLUME = 0xE0, // HID type LC + HID_CONSUMER_BALANCE = 0xE1, // HID type LC + HID_CONSUMER_MUTE = 0xE2, // HID type OOC + HID_CONSUMER_BASS = 0xE3, // HID type LC + HID_CONSUMER_TREBLE = 0xE4, // HID type LC + HID_CONSUMER_BASS_BOOST = 0xE5, // HID type OOC + HID_CONSUMER_SURROUND_MODE = 0xE6, // HID type OSC + HID_CONSUMER_LOUDNESS = 0xE7, // HID type OOC + HID_CONSUMER_MPX = 0xE8, // HID type OOC + HID_CONSUMER_VOLUME_INCREMENT = 0xE9, // HID type RTC + HID_CONSUMER_VOLUME_DECREMENT = 0xEA, // HID type RTC + // Reserved 0xEB-EF + HID_CONSUMER_SPEED_SELECT = 0xF0, // HID type OSC + HID_CONSUMER_PLAYBACK_SPEED = 0xF1, // HID type NARY + HID_CONSUMER_STANDARD_PLAY = 0xF2, // HID type SEL + HID_CONSUMER_LONG_PLAY = 0xF3, // HID type SEL + HID_CONSUMER_EXTENDED_PLAY = 0xF4, // HID type SEL + HID_CONSUMER_SLOW = 0xF5, // HID type OSC + // Reserved 0xF6-FF + HID_CONSUMER_FAN_ENABLE = 0x100, // HID type OOC + HID_CONSUMER_FAN_SPEED = 0x101, // HID type LC + HID_CONSUMER_LIGHT_ENABLE = 0x102, // HID type OOC + HID_CONSUMER_LIGHT_ILLUMINATION_LEVEL = 0x103, // HID type LC + HID_CONSUMER_CLIMATE_CONTROL_ENABLE = 0x104, // HID type OOC + HID_CONSUMER_ROOM_TEMPERATURE = 0x105, // HID type LC + HID_CONSUMER_SECURITY_ENABLE = 0x106, // HID type OOC + HID_CONSUMER_FIRE_ALARM = 0x107, // HID type OSC + HID_CONSUMER_POLICE_ALARM = 0x108, // HID type OSC + HID_CONSUMER_PROXIMITY = 0x109, // HID type LC + HID_CONSUMER_MOTION = 0x10A, // HID type OSC + HID_CONSUMER_DURESS_ALARM = 0x10B, // HID type OSC + HID_CONSUMER_HOLDUP_ALARM = 0x10C, // HID type OSC + HID_CONSUMER_MEDICAL_ALARM = 0x10D, // HID type OSC + // Reserved 0x10E-14F + HID_CONSUMER_BALANCE_RIGHT = 0x150, // HID type RTC + HID_CONSUMER_BALANCE_LEFT = 0x151, // HID type RTC + HID_CONSUMER_BASS_INCREMENT = 0x152, // HID type RTC + HID_CONSUMER_BASS_DECREMENT = 0x153, // HID type RTC + HID_CONSUMER_TREBLE_INCREMENT = 0x154, // HID type RTC + HID_CONSUMER_TREBLE_DECREMENT = 0x155, // HID type RTC + // Reserved 0x156-15F + HID_CONSUMER_SPEAKER_SYSTEM = 0x160, // HID type CL + HID_CONSUMER_CHANNEL_LEFT = 0x161, // HID type CL + HID_CONSUMER_CHANNEL_RIGHT = 0x162, // HID type CL + HID_CONSUMER_CHANNEL_CENTER = 0x163, // HID type CL + HID_CONSUMER_CHANNEL_FRONT = 0x164, // HID type CL + HID_CONSUMER_CHANNEL_CENTER_FRONT = 0x165, // HID type CL + HID_CONSUMER_CHANNEL_SIDE = 0x166, // HID type CL + HID_CONSUMER_CHANNEL_SURROUND = 0x167, // HID type CL + HID_CONSUMER_CHANNEL_LOW_FREQUENCY_ENHANCEMENT = 0x168, // HID type CL + HID_CONSUMER_CHANNEL_TOP = 0x169, // HID type CL + HID_CONSUMER_CHANNEL_UNKNOWN = 0x16A, // HID type CL + // Reserved 0x16B-16F + HID_CONSUMER_SUB_CHANNEL = 0x170, // HID type LC + HID_CONSUMER_SUB_CHANNEL_INCREMENT = 0x171, // HID type OSC + HID_CONSUMER_SUB_CHANNEL_DECREMENT = 0x172, // HID type OSC + HID_CONSUMER_ALTERNATE_AUDIO_INCREMENT = 0x173, // HID type OSC + HID_CONSUMER_ALTERNATE_AUDIO_DECREMENT = 0x174, // HID type OSC + // Reserved 0x175-17F + HID_CONSUMER_APPLICATION_LAUNCH_BUTTONS = 0x180, // HID type NARY + HID_CONSUMER_AL_LAUNCH_BUTTON_CONFIGURATION_TOOL = 0x181, // HID type SEL + HID_CONSUMER_AL_PROGRAMMABLE_BUTTON_CONFIGURATION = 0x182, // HID type SEL + HID_CONSUMER_AL_CONSUMER_CONTROL_CONFIGURATION = 0x183, // HID type SEL + HID_CONSUMER_AL_WORD_PROCESSOR = 0x184, // HID type SEL + HID_CONSUMER_AL_TEXT_EDITOR = 0x185, // HID type SEL + HID_CONSUMER_AL_SPREADSHEET = 0x186, // HID type SEL + HID_CONSUMER_AL_GRAPHICS_EDITOR = 0x187, // HID type SEL + HID_CONSUMER_AL_PRESENTATION_APP = 0x188, // HID type SEL + HID_CONSUMER_AL_DATABASE_APP = 0x189, // HID type SEL + HID_CONSUMER_AL_EMAIL_READER = 0x18A, // HID type SEL + HID_CONSUMER_AL_NEWSREADER = 0x18B, // HID type SEL + HID_CONSUMER_AL_VOICEMAIL = 0x18C, // HID type SEL + HID_CONSUMER_AL_CONTACTS_SLASH_ADDRESS_BOOK = 0x18D, // HID type SEL + HID_CONSUMER_AL_CALENDAR_SLASH_SCHEDULE = 0x18E, // HID type SEL + HID_CONSUMER_AL_TASK_SLASH_PROJECT_MANAGER = 0x18F, // HID type SEL + HID_CONSUMER_AL_LOG_SLASH_JOURNAL_SLASH_TIMECARD = 0x190, // HID type SEL + HID_CONSUMER_AL_CHECKBOOK_SLASH_FINANCE = 0x191, // HID type SEL + HID_CONSUMER_AL_CALCULATOR = 0x192, // HID type SEL + HID_CONSUMER_AL_A_SLASH_V_CAPTURE_SLASH_PLAYBACK = 0x193, // HID type SEL + HID_CONSUMER_AL_LOCAL_MACHINE_BROWSER = 0x194, // HID type SEL + HID_CONSUMER_AL_LAN_SLASH_WAN_BROWSER = 0x195, // HID type SEL + HID_CONSUMER_AL_INTERNET_BROWSER = 0x196, // HID type SEL + HID_CONSUMER_AL_REMOTE_NETWORKING_SLASH_ISP_CONNECT = 0x197, // HID type SEL + HID_CONSUMER_AL_NETWORK_CONFERENCE = 0x198, // HID type SEL + HID_CONSUMER_AL_NETWORK_CHAT = 0x199, // HID type SEL + HID_CONSUMER_AL_TELEPHONY_SLASH_DIALER = 0x19A, // HID type SEL + HID_CONSUMER_AL_LOGON = 0x19B, // HID type SEL + HID_CONSUMER_AL_LOGOFF = 0x19C, // HID type SEL + HID_CONSUMER_AL_LOGON_SLASH_LOGOFF = 0x19D, // HID type SEL + HID_CONSUMER_AL_TERMINAL_LOCK_SLASH_SCREENSAVER = 0x19E, // HID type SEL + HID_CONSUMER_AL_CONTROL_PANEL = 0x19F, // HID type SEL + HID_CONSUMER_AL_COMMAND_LINE_PROCESSOR_SLASH_RUN = 0x1A0, // HID type SEL + HID_CONSUMER_AL_PROCESS_SLASH_TASK_MANAGER = 0x1A1, // HID type SEL + HID_CONSUMER_AL_SELECT_TASK_SLASH_APPLICATION = 0x1A2, // HID type SEL + HID_CONSUMER_AL_NEXT_TASK_SLASH_APPLICATION = 0x1A3, // HID type SEL + HID_CONSUMER_AL_PREVIOUS_TASK_SLASH_APPLICATION = 0x1A4, // HID type SEL + HID_CONSUMER_AL_PREEMPTIVE_HALT_TASK_SLASH_APPLICATION = 0x1A5, // HID type SEL + HID_CONSUMER_AL_INTEGRATED_HELP_CENTER = 0x1A6, // HID type SEL + HID_CONSUMER_AL_DOCUMENTS = 0x1A7, // HID type SEL + HID_CONSUMER_AL_THESAURUS = 0x1A8, // HID type SEL + HID_CONSUMER_AL_DICTIONARY = 0x1A9, // HID type SEL + HID_CONSUMER_AL_DESKTOP = 0x1AA, // HID type SEL + HID_CONSUMER_AL_SPELL_CHECK = 0x1AB, // HID type SEL + HID_CONSUMER_AL_GRAMMAR_CHECK = 0x1AC, // HID type SEL + HID_CONSUMER_AL_WIRELESS_STATUS = 0x1AD, // HID type SEL + HID_CONSUMER_AL_KEYBOARD_LAYOUT = 0x1AE, // HID type SEL + HID_CONSUMER_AL_VIRUS_PROTECTION = 0x1AF, // HID type SEL + HID_CONSUMER_AL_ENCRYPTION = 0x1B0, // HID type SEL + HID_CONSUMER_AL_SCREEN_SAVER = 0x1B1, // HID type SEL + HID_CONSUMER_AL_ALARMS = 0x1B2, // HID type SEL + HID_CONSUMER_AL_CLOCK = 0x1B3, // HID type SEL + HID_CONSUMER_AL_FILE_BROWSER = 0x1B4, // HID type SEL + HID_CONSUMER_AL_POWER_STATUS = 0x1B5, // HID type SEL + HID_CONSUMER_AL_IMAGE_BROWSER = 0x1B6, // HID type SEL + HID_CONSUMER_AL_AUDIO_BROWSER = 0x1B7, // HID type SEL + HID_CONSUMER_AL_MOVIE_BROWSER = 0x1B8, // HID type SEL + HID_CONSUMER_AL_DIGITAL_RIGHTS_MANAGER = 0x1B9, // HID type SEL + HID_CONSUMER_AL_DIGITAL_WALLET = 0x1BA, // HID type SEL + // _Reserved 0x1BB + HID_CONSUMER_AL_INSTANT_MESSAGING = 0x1BC, // HID type SEL + HID_CONSUMER_AL_OEM_FEATURES_SLASH__TIPS_SLASH_TUTORIAL_BROWSER = 0x1BD, // HID type SEL + HID_CONSUMER_AL_OEM_HELP = 0x1BE, // HID type SEL + HID_CONSUMER_AL_ONLINE_COMMUNITY = 0x1BF, // HID type SEL + HID_CONSUMER_AL_ENTERTAINMENT_CONTENT_BROWSER = 0x1C0, // HID type SEL + HID_CONSUMER_AL_ONLINE_SHOPPING_BROWSER = 0x1C1, // HID type SEL + HID_CONSUMER_AL_SMARTCARD_INFORMATION_SLASH_HELP = 0x1C2, // HID type SEL + HID_CONSUMER_AL_MARKET_MONITOR_SLASH_FINANCE_BROWSER = 0x1C3, // HID type SEL + HID_CONSUMER_AL_CUSTOMIZED_CORPORATE_NEWS_BROWSER = 0x1C4, // HID type SEL + HID_CONSUMER_AL_ONLINE_ACTIVITY_BROWSER = 0x1C5, // HID type SEL + HID_CONSUMER_AL_RESEARCH_SLASH_SEARCH_BROWSER = 0x1C6, // HID type SEL + HID_CONSUMER_AL_AUDIO_PLAYER = 0x1C7, // HID type SEL + // Reserved 0x1C8-1FF + HID_CONSUMER_GENERIC_GUI_APPLICATION_CONTROLS = 0x200, // HID type NARY + HID_CONSUMER_AC_NEW = 0x201, // HID type SEL + HID_CONSUMER_AC_OPEN = 0x202, // HID type SEL + HID_CONSUMER_AC_CLOSE = 0x203, // HID type SEL + HID_CONSUMER_AC_EXIT = 0x204, // HID type SEL + HID_CONSUMER_AC_MAXIMIZE = 0x205, // HID type SEL + HID_CONSUMER_AC_MINIMIZE = 0x206, // HID type SEL + HID_CONSUMER_AC_SAVE = 0x207, // HID type SEL + HID_CONSUMER_AC_PRINT = 0x208, // HID type SEL + HID_CONSUMER_AC_PROPERTIES = 0x209, // HID type SEL + HID_CONSUMER_AC_UNDO = 0x21A, // HID type SEL + HID_CONSUMER_AC_COPY = 0x21B, // HID type SEL + HID_CONSUMER_AC_CUT = 0x21C, // HID type SEL + HID_CONSUMER_AC_PASTE = 0x21D, // HID type SEL + HID_CONSUMER_AC_SELECT_ALL = 0x21E, // HID type SEL + HID_CONSUMER_AC_FIND = 0x21F, // HID type SEL + HID_CONSUMER_AC_FIND_AND_REPLACE = 0x220, // HID type SEL + HID_CONSUMER_AC_SEARCH = 0x221, // HID type SEL + HID_CONSUMER_AC_GO_TO = 0x222, // HID type SEL + HID_CONSUMER_AC_HOME = 0x223, // HID type SEL + HID_CONSUMER_AC_BACK = 0x224, // HID type SEL + HID_CONSUMER_AC_FORWARD = 0x225, // HID type SEL + HID_CONSUMER_AC_STOP = 0x226, // HID type SEL + HID_CONSUMER_AC_REFRESH = 0x227, // HID type SEL + HID_CONSUMER_AC_PREVIOUS_LINK = 0x228, // HID type SEL + HID_CONSUMER_AC_NEXT_LINK = 0x229, // HID type SEL + HID_CONSUMER_AC_BOOKMARKS = 0x22A, // HID type SEL + HID_CONSUMER_AC_HISTORY = 0x22B, // HID type SEL + HID_CONSUMER_AC_SUBSCRIPTIONS = 0x22C, // HID type SEL + HID_CONSUMER_AC_ZOOM_IN = 0x22D, // HID type SEL + HID_CONSUMER_AC_ZOOM_OUT = 0x22E, // HID type SEL + HID_CONSUMER_AC_ZOOM = 0x22F, // HID type LC + HID_CONSUMER_AC_FULL_SCREEN_VIEW = 0x230, // HID type SEL + HID_CONSUMER_AC_NORMAL_VIEW = 0x231, // HID type SEL + HID_CONSUMER_AC_VIEW_TOGGLE = 0x232, // HID type SEL + HID_CONSUMER_AC_SCROLL_UP = 0x233, // HID type SEL + HID_CONSUMER_AC_SCROLL_DOWN = 0x234, // HID type SEL + HID_CONSUMER_AC_SCROLL = 0x235, // HID type LC + HID_CONSUMER_AC_PAN_LEFT = 0x236, // HID type SEL + HID_CONSUMER_AC_PAN_RIGHT = 0x237, // HID type SEL + HID_CONSUMER_AC_PAN = 0x238, // HID type LC + HID_CONSUMER_AC_NEW_WINDOW = 0x239, // HID type SEL + HID_CONSUMER_AC_TILE_HORIZONTALLY = 0x23A, // HID type SEL + HID_CONSUMER_AC_TILE_VERTICALLY = 0x23B, // HID type SEL + HID_CONSUMER_AC_FORMAT = 0x23C, // HID type SEL + HID_CONSUMER_AC_EDIT = 0x23D, // HID type SEL + HID_CONSUMER_AC_BOLD = 0x23E, // HID type SEL + HID_CONSUMER_AC_ITALICS = 0x23F, // HID type SEL + HID_CONSUMER_AC_UNDERLINE = 0x240, // HID type SEL + HID_CONSUMER_AC_STRIKETHROUGH = 0x241, // HID type SEL + HID_CONSUMER_AC_SUBSCRIPT = 0x242, // HID type SEL + HID_CONSUMER_AC_SUPERSCRIPT = 0x243, // HID type SEL + HID_CONSUMER_AC_ALL_CAPS = 0x244, // HID type SEL + HID_CONSUMER_AC_ROTATE = 0x245, // HID type SEL + HID_CONSUMER_AC_RESIZE = 0x246, // HID type SEL + HID_CONSUMER_AC_FLIP_HORIZONTAL = 0x247, // HID type SEL + HID_CONSUMER_AC_FLIP_VERTICAL = 0x248, // HID type SEL + HID_CONSUMER_AC_MIRROR_HORIZONTAL = 0x249, // HID type SEL + HID_CONSUMER_AC_MIRROR_VERTICAL = 0x24A, // HID type SEL + HID_CONSUMER_AC_FONT_SELECT = 0x24B, // HID type SEL + HID_CONSUMER_AC_FONT_COLOR = 0x24C, // HID type SEL + HID_CONSUMER_AC_FONT_SIZE = 0x24D, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_LEFT = 0x24E, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_CENTER_H = 0x24F, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_RIGHT = 0x250, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_BLOCK_H = 0x251, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_TOP = 0x252, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_CENTER_V = 0x253, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_BOTTOM = 0x254, // HID type SEL + HID_CONSUMER_AC_JUSTIFY_BLOCK_V = 0x255, // HID type SEL + HID_CONSUMER_AC_INDENT_DECREASE = 0x256, // HID type SEL + HID_CONSUMER_AC_INDENT_INCREASE = 0x257, // HID type SEL + HID_CONSUMER_AC_NUMBERED_LIST = 0x258, // HID type SEL + HID_CONSUMER_AC_RESTART_NUMBERING = 0x259, // HID type SEL + HID_CONSUMER_AC_BULLETED_LIST = 0x25A, // HID type SEL + HID_CONSUMER_AC_PROMOTE = 0x25B, // HID type SEL + HID_CONSUMER_AC_DEMOTE = 0x25C, // HID type SEL + HID_CONSUMER_AC_YES = 0x25D, // HID type SEL + HID_CONSUMER_AC_NO = 0x25E, // HID type SEL + HID_CONSUMER_AC_CANCEL = 0x25F, // HID type SEL + HID_CONSUMER_AC_CATALOG = 0x260, // HID type SEL + HID_CONSUMER_AC_BUY_SLASH_CHECKOUT = 0x261, // HID type SEL + HID_CONSUMER_AC_ADD_TO_CART = 0x262, // HID type SEL + HID_CONSUMER_AC_EXPAND = 0x263, // HID type SEL + HID_CONSUMER_AC_EXPAND_ALL = 0x264, // HID type SEL + HID_CONSUMER_AC_COLLAPSE = 0x265, // HID type SEL + HID_CONSUMER_AC_COLLAPSE_ALL = 0x266, // HID type SEL + HID_CONSUMER_AC_PRINT_PREVIEW = 0x267, // HID type SEL + HID_CONSUMER_AC_PASTE_SPECIAL = 0x268, // HID type SEL + HID_CONSUMER_AC_INSERT_MODE = 0x269, // HID type SEL + HID_CONSUMER_AC_DELETE = 0x26A, // HID type SEL + HID_CONSUMER_AC_LOCK = 0x26B, // HID type SEL + HID_CONSUMER_AC_UNLOCK = 0x26C, // HID type SEL + HID_CONSUMER_AC_PROTECT = 0x26D, // HID type SEL + HID_CONSUMER_AC_UNPROTECT = 0x26E, // HID type SEL + HID_CONSUMER_AC_ATTACH_COMMENT = 0x26F, // HID type SEL + HID_CONSUMER_AC_DELETE_COMMENT = 0x270, // HID type SEL + HID_CONSUMER_AC_VIEW_COMMENT = 0x271, // HID type SEL + HID_CONSUMER_AC_SELECT_WORD = 0x272, // HID type SEL + HID_CONSUMER_AC_SELECT_SENTENCE = 0x273, // HID type SEL + HID_CONSUMER_AC_SELECT_PARAGRAPH = 0x274, // HID type SEL + HID_CONSUMER_AC_SELECT_COLUMN = 0x275, // HID type SEL + HID_CONSUMER_AC_SELECT_ROW = 0x276, // HID type SEL + HID_CONSUMER_AC_SELECT_TABLE = 0x277, // HID type SEL + HID_CONSUMER_AC_SELECT_OBJECT = 0x278, // HID type SEL + HID_CONSUMER_AC_REDO_SLASH_REPEAT = 0x279, // HID type SEL + HID_CONSUMER_AC_SORT = 0x27A, // HID type SEL + HID_CONSUMER_AC_SORT_ASCENDING = 0x27B, // HID type SEL + HID_CONSUMER_AC_SORT_DESCENDING = 0x27C, // HID type SEL + HID_CONSUMER_AC_FILTER = 0x27D, // HID type SEL + HID_CONSUMER_AC_SET_CLOCK = 0x27E, // HID type SEL + HID_CONSUMER_AC_VIEW_CLOCK = 0x27F, // HID type SEL + HID_CONSUMER_AC_SELECT_TIME_ZONE = 0x280, // HID type SEL + HID_CONSUMER_AC_EDIT_TIME_ZONES = 0x281, // HID type SEL + HID_CONSUMER_AC_SET_ALARM = 0x282, // HID type SEL + HID_CONSUMER_AC_CLEAR_ALARM = 0x283, // HID type SEL + HID_CONSUMER_AC_SNOOZE_ALARM = 0x284, // HID type SEL + HID_CONSUMER_AC_RESET_ALARM = 0x285, // HID type SEL + HID_CONSUMER_AC_SYNCHRONIZE = 0x286, // HID type SEL + HID_CONSUMER_AC_SEND_SLASH_RECEIVE = 0x287, // HID type SEL + HID_CONSUMER_AC_SEND_TO = 0x288, // HID type SEL + HID_CONSUMER_AC_REPLY = 0x289, // HID type SEL + HID_CONSUMER_AC_REPLY_ALL = 0x28A, // HID type SEL + HID_CONSUMER_AC_FORWARD_MSG = 0x28B, // HID type SEL + HID_CONSUMER_AC_SEND = 0x28C, // HID type SEL + HID_CONSUMER_AC_ATTACH_FILE = 0x28D, // HID type SEL + HID_CONSUMER_AC_UPLOAD = 0x28E, // HID type SEL + HID_CONSUMER_AC_DOWNLOAD_SAVE_TARGET_AS = 0x28F, // HID type SEL + HID_CONSUMER_AC_SET_BORDERS = 0x290, // HID type SEL + HID_CONSUMER_AC_INSERT_ROW = 0x291, // HID type SEL + HID_CONSUMER_AC_INSERT_COLUMN = 0x292, // HID type SEL + HID_CONSUMER_AC_INSERT_FILE = 0x293, // HID type SEL + HID_CONSUMER_AC_INSERT_PICTURE = 0x294, // HID type SEL + HID_CONSUMER_AC_INSERT_OBJECT = 0x295, // HID type SEL + HID_CONSUMER_AC_INSERT_SYMBOL = 0x296, // HID type SEL + HID_CONSUMER_AC_SAVE_AND_CLOSE = 0x297, // HID type SEL + HID_CONSUMER_AC_RENAME = 0x298, // HID type SEL + HID_CONSUMER_AC_MERGE = 0x299, // HID type SEL + HID_CONSUMER_AC_SPLIT = 0x29A, // HID type SEL + HID_CONSUMER_AC_DISRIBUTE_HORIZONTALLY = 0x29B, // HID type SEL + HID_CONSUMER_AC_DISTRIBUTE_VERTICALLY = 0x29C, // HID type SEL +}; diff --git a/src/usb_inc/usb.h b/src/usb_inc/usb.h index 19d90a5..3d39fd3 100644 --- a/src/usb_inc/usb.h +++ b/src/usb_inc/usb.h @@ -1,6 +1,8 @@ #pragma once #include +#include "../_compiler.h" + // USB U16 helpers #define U16(_high, _low) ((uint16_t)(((_high) << 8) | (_low))) #define U16_HIGH(_u16) ((uint8_t)(((_u16) >> 8) & 0x00ff)) diff --git a/src/usbd_driver.c b/src/usbd_driver.c index 8e85458..9fa0943 100644 --- a/src/usbd_driver.c +++ b/src/usbd_driver.c @@ -26,7 +26,7 @@ static uint8_t su8VendorCount = 0; void Tas_USBD_Open(void) { g_usbd_CtrlMaxPktSize = gpDeviceDescriptor->bMaxPacketSize0; USBD->ATTR = 0x650; // Disable D+ and USB controller - CLK_SysTickLongDelay(3000 ms); + CLK_SysTickLongDelay(1000 ms); USBD->ATTR = 0x7D0; USBD_SET_SE0(); } @@ -189,18 +189,10 @@ static inline void Tas_USBD_GetDescriptor(void) { break; case USB_STRING_VENDOR: { - const char *szVendor; - if (su8VendorCount < 2) { - szVendor = gszVendorInitial; - su8VendorCount++; - } else { - szVendor = gszVendor; - } - - uint8_t u8Len = strlen(szVendor); + uint8_t u8Len = strlen(gszVendor); u8Str[0] = 2 + u8Len * 2; for (uint8_t i = 0; i < u8Len; i++) { - u8Str[2 + i * 2] = szVendor[i]; + u8Str[2 + i * 2] = gszVendor[i]; u8Str[2 + i * 2 + 1] = 0; } break; @@ -215,8 +207,10 @@ static inline void Tas_USBD_GetDescriptor(void) { break; } case USB_STRING_SERIAL: { - // TODO: I think it might be a u16 then two u8s? - // Need to check the TRM + // The unique ID is technically 3 words, but I'm pretty sure only the last one + // really changes. The TRM has no details regarding this. + // There's no harm using all three as our serial, so to stay on the safe side + // that's what we do. uint32_t u32serial; FMC_Open(); diff --git a/src/usbd_user.c b/src/usbd_user.c index f557f6b..e8bed35 100644 --- a/src/usbd_user.c +++ b/src/usbd_user.c @@ -1,16 +1,27 @@ #include "tasoller.h" -uint8_t volatile g_u8Suspend = 0; +uint8_t volatile g_u8UsbState = 0; +uint8_t volatile gu8VComDTEPresent = 0; uint8_t g_u8Idle = 0; uint8_t g_u8Protocol = 0; uint8_t gHidSetReport[64]; STR_VCOM_LINE_CODING gLineCoding = { 0, 0, 0, 0 }; -uint16_t gCtrlSignal; uint32_t volatile g_u32OutToggle = 0; +#define STALL_CONTROL \ + do { \ + USBD_SetStall(EP_CTRL_IN); \ + USBD_SetStall(EP_CTRL_OUT); \ + } while (0) +#define CONTROL_IN_DONE \ + do { \ + USBD_SET_DATA1(EP_CTRL_IN); \ + USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); \ + } while (0) + void USBD_IRQHandler(void) { uint32_t u32IntSts = USBD_GET_INT_FLAG(); uint32_t u32State = USBD_GET_BUS_STATE(); @@ -21,8 +32,10 @@ void USBD_IRQHandler(void) { if (USBD_IS_ATTACHED()) { USBD_ENABLE_USB(); + g_u8UsbState &= ~USB_STATE_FLOATING; } else { USBD_DISABLE_USB(); + g_u8UsbState |= USB_STATE_FLOATING; } } @@ -38,17 +51,15 @@ void USBD_IRQHandler(void) { USBD_ENABLE_USB(); Tas_USBD_SwReset(); g_u32OutToggle = 0; - g_u8Suspend = 0; + g_u8UsbState &= ~USB_STATE_SUSPEND; } if (u32State & USBD_STATE_SUSPEND) { - // Enter power down to wait USB attached; enable USB but disable PHY - g_u8Suspend = 1; USBD_DISABLE_PHY(); + g_u8UsbState |= USB_STATE_SUSPEND; } if (u32State & USBD_STATE_RESUME) { - // Enable USB and enable PHY USBD_ENABLE_USB(); - g_u8Suspend = 0; + g_u8UsbState &= ~USB_STATE_SUSPEND; } } @@ -173,30 +184,28 @@ void Tas_USBD_ClassRequest(void) { default: // Setup error, stall the device - USBD_SetStall(EP_CTRL_IN); - USBD_SetStall(EP_CTRL_OUT); + STALL_CONTROL; break; } } else { // Host to device switch (setup.bRequest) { case SET_CONTROL_LINE_STATE: - // TODO: Use bit[0] (DTR) to identify connection state - // Is RTS worth using? - if (setup.wIndex == USBD_ITF_CDC_CMD) gCtrlSignal = setup.wValue; - - // Status stage - USBD_SET_DATA1(EP_CTRL_IN); - USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); + if (setup.wIndex == USBD_ITF_CDC_CMD) { + gu8VComDTEPresent = setup.wValue & 1; + CONTROL_IN_DONE; + } else { + STALL_CONTROL; + } break; case SET_LINE_CODING: - if (setup.setLineCoding.wInterface == USBD_ITF_CDC_CMD) + if (setup.setLineCoding.wInterface == USBD_ITF_CDC_CMD) { Tas_USBD_PrepareCtrlOut(&gLineCoding, sizeof gLineCoding, NULL); - - // Status stage - USBD_SET_DATA1(EP_CTRL_IN); - USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); + CONTROL_IN_DONE; + } else { + STALL_CONTROL; + } break; case SET_REPORT: @@ -216,22 +225,17 @@ void Tas_USBD_ClassRequest(void) { // // Status stage // Tas_USBD_PrepareCtrlIn(NULL, 0); } - USBD_SET_DATA1(EP_CTRL_IN); - USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); + CONTROL_IN_DONE; break; case SET_IDLE: g_u8Idle = setup.hidSetIdle.bDuration; - // Status stage - USBD_SET_DATA1(EP_CTRL_IN); - USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); + CONTROL_IN_DONE; break; case SET_PROTOCOL: g_u8Protocol = setup.hidSetProtocol.wProtocol; - // Status stage - USBD_SET_DATA1(EP_CTRL_IN); - USBD_SET_PAYLOAD_LEN(EP_CTRL_IN, 0); + CONTROL_IN_DONE; break; default: diff --git a/src/vcom.c b/src/vcom.c index 1decb3a..7cbb9f8 100644 --- a/src/vcom.c +++ b/src/vcom.c @@ -3,7 +3,8 @@ #define BUF_SIZE_RX 512 #define BUF_SIZE_TX 512 -volatile uint8_t gu8VcomReady = 0; +volatile uint8_t gu8VcomDTEPresent = 0; +volatile uint8_t gu8VComReady = 0; static volatile uint8_t gau8ComRbuf[BUF_SIZE_RX]; static volatile uint16_t gu16ComRbytes = 0; @@ -26,10 +27,10 @@ void _USB_VCOM_Tick_Tx(void) { uint32_t u32Len; if (gu32TxSize != 0) return; - // Check wether we have new COM Rx data to send to USB or not + // Check whether we have new COM Rx data to send to USB or not if (!gu16ComRbytes) { - // Prepare a zero packet if previous packet size is USBD_CDC_IN_MAX_SIZE and - // no more data to send at this moment to note Host the transfer has been done + // Prepare a zero packet if previous packet size is USBD_CDC_IN_MAX_SIZE and no more data to + // send at this moment to notify to the Host that the transfer has been done u32Len = USBD_GET_PAYLOAD_LEN(EP_CDC_IN); if (u32Len == USBD_CDC_IN_MAX_SIZE) USBD_SET_PAYLOAD_LEN(EP_CDC_IN, 0); return; diff --git a/src/vcom.h b/src/vcom.h new file mode 100644 index 0000000..42da425 --- /dev/null +++ b/src/vcom.h @@ -0,0 +1,25 @@ +#pragma once + +#include + +typedef struct __packed { + uint32_t u32DTERate; // Baud rate + uint8_t u8CharFormat; // Stop bit + uint8_t u8ParityType; // Parity + uint8_t u8DataBits; // Data bits +} STR_VCOM_LINE_CODING; +extern STR_VCOM_LINE_CODING gLineCoding; +extern volatile int8_t gi8BulkOutReady; + +extern volatile uint8_t *gpu8RxBuf; +extern volatile uint32_t gu32RxSize; +extern volatile uint32_t gu32TxSize; + +extern volatile uint8_t gu8VComDTEPresent; +extern volatile uint8_t gu8VComReady; + +void USB_VCOM_Write(uint8_t u8Char); +uint16_t USB_VCOM_Available(void); +uint8_t USB_VCOM_Read(void); +void USB_VCOM_Tick(void); +void USB_VCOM_PurgeTx(void);