diff --git a/fw/CMakeLists.txt b/fw/CMakeLists.txt index 153d023..535a3cd 100644 --- a/fw/CMakeLists.txt +++ b/fw/CMakeLists.txt @@ -35,10 +35,12 @@ add_executable(fw ptn3460.c tcpm_driver.c tps65185.c + usb_descriptors.c usb_mux.c usb_pd_driver.c usb_pd_policy.c usb_pd_protocol.c + usbapp.c utils.c ) @@ -46,10 +48,14 @@ pico_set_program_name(fw "fw") pico_set_program_version(fw "0.1") pico_enable_stdio_uart(fw 0) -pico_enable_stdio_usb(fw 1) +pico_enable_stdio_usb(fw 0) + +# Make sure TinyUSB can find tusb_config.h +target_include_directories(fw PUBLIC + ${CMAKE_CURRENT_LIST_DIR}) # Add the standard library to the build -target_link_libraries(fw pico_stdlib hardware_sleep) +target_link_libraries(fw pico_stdlib hardware_sleep tinyusb_device tinyusb_board) # Add any user requested libraries target_link_libraries(fw diff --git a/fw/config.h b/fw/config.h index 3b5f98e..f848d23 100644 --- a/fw/config.h +++ b/fw/config.h @@ -61,6 +61,8 @@ #define POWER_GPIO #define POWER_GPIO_VCOM_MEASURE #define INPUT_DVI +#define BOARD_HAS_BUTTON +#define BUTTON_GPIO 2 #else #error "Unknown board revision" #endif diff --git a/fw/fw.c b/fw/fw.c index 945e0da..7bcd02d 100644 --- a/fw/fw.c +++ b/fw/fw.c @@ -1,5 +1,5 @@ // -// Copyright 2022 Wenting Zhang +// Copyright 2024 Wenting Zhang // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal @@ -33,6 +33,10 @@ #include "fpga.h" #include "edid.h" #include "caster.h" +#include "usbapp.h" + +void osd_task(void); // OSD handling task +void usb_pd_task(void); // USB PD handling task int main() { @@ -43,28 +47,50 @@ int main() printf("\n"); printf("Glider\n"); - // TODO: Unify both input options -#if defined(INPUT_DVI) power_init(); + +#ifdef INPUT_DVI edid_init(); - power_enable(true); +#endif - //sleep_run_from_xosc(); - //sleep_goto_dormant_until_edge_high(8); - // https://ghubcoder.github.io/posts/awaking-the-pico/ +#ifdef INPUT_TYPEC + int result = tcpm_init(0); + if (result) + fatal("Failed to initialize TCPC\n"); - fpga_init(); + // int cc1, cc2; + // tcpc_config[0].drv->get_cc(0, &cc1, &cc2); + // printf("CC status %d %d\n", cc1, cc2); - //sleep_ms(5000); - //caster_init(); + ptn3460_init(); + pd_init(0); +#endif - gpio_init(2); - gpio_set_dir(2, GPIO_IN); - gpio_pull_up(2); +#ifdef BOARD_HAS_BUTTON + gpio_init(BUTTON_GPIO); + gpio_set_dir(BUTTON_GPIO, GPIO_IN); + gpio_pull_up(BUTTON_GPIO); +#endif - int mode_max = 6; - int mode = 1; - UPDATE_MODE modes[6] = { + //fpga_init(); + //power_enable(true); + + usbapp_init(); + + while (1) { + osd_task(); + usb_pd_task(); + usbapp_task(); + } + + return 0; +} + +void osd_task(void) { +#ifdef BOARD_HAS_BUTTON + static int mode_max = 6; + static int mode = 1; + const UPDATE_MODE modes[6] = { UM_FAST_MONO_NO_DITHER, UM_FAST_MONO_BAYER, UM_FAST_MONO_BLUE_NOISE, @@ -73,73 +99,48 @@ int main() UM_AUTO_LUT_ERROR_DIFFUSION }; - while (1) { - // - if (gpio_get(2) == 0) { - sleep_ms(20); - if (gpio_get(2) == 0) { - int i = 0; - while (gpio_get(2) == 0) { - i++; - sleep_ms(1); - if (i > 500) - break; - } - if (i > 500) { - // Long press, clear screen - caster_redraw(0,0,1600,1200); - } - else { - // Short press, switch mode - mode++; - if (mode >= mode_max) mode = 0; - caster_setmode(0,0,1600,1200,modes[mode]); - } - while (gpio_get(2) == 0); + if (gpio_get(BUTTON_GPIO) == 0) { + sleep_ms(20); + if (gpio_get(BUTTON_GPIO) == 0) { + int i = 0; + while (gpio_get(BUTTON_GPIO) == 0) { + i++; + sleep_ms(1); + if (i > 500) + break; } - while (gpio_get(2) == 0); - } - } -#elif defined(INPUT_TYPEC) - int result = tcpm_init(0); - if (result) - fatal("Failed to initialize TCPC\n"); - - int cc1, cc2; - tcpc_config[0].drv->get_cc(0, &cc1, &cc2); - printf("CC status %d %d\n", cc1, cc2); - - gpio_init(10); - gpio_put(10, 0); - gpio_set_dir(10, GPIO_OUT); - - power_init(); - power_enable(true); // TODO: should be dependent on DP signal valid - fpga_init(); - - ptn3460_init(); - pd_init(0); - sleep_ms(50); - - extern int dp_enabled; - bool hpd_sent = false; - bool dp_valid = false; - - while (1) { - // TODO: Implement interrupt - fusb302_tcpc_alert(0); - pd_run_state_machine(0); - if (dp_enabled && !hpd_sent && !pd_is_vdm_busy(0)) { - printf("DP enabled\n"); - pd_send_hpd(0, hpd_high); - hpd_sent = true; - } - if (dp_valid != ptn3460_is_valid()) { - dp_valid = ptn3460_is_valid(); - printf(dp_valid ? "Input is valid\n" : "Input is invalid\n"); + if (i > 500) { + // Long press, clear screen + caster_redraw(0,0,1600,1200); + } + else { + // Short press, switch mode + mode++; + if (mode >= mode_max) mode = 0; + caster_setmode(0,0,1600,1200,modes[mode]); + } + while (gpio_get(BUTTON_GPIO) == 0); } + while (gpio_get(BUTTON_GPIO) == 0); } #endif - - return 0; +} + +void usb_pd_task(void) { + extern int dp_enabled; + static bool hpd_sent = false; + static bool dp_valid = false; + + // TODO: Implement interrupt + fusb302_tcpc_alert(0); + pd_run_state_machine(0); + if (dp_enabled && !hpd_sent && !pd_is_vdm_busy(0)) { + printf("DP enabled\n"); + pd_send_hpd(0, hpd_high); + hpd_sent = true; + } + if (dp_valid != ptn3460_is_valid()) { + dp_valid = ptn3460_is_valid(); + printf(dp_valid ? "Input is valid\n" : "Input is invalid\n"); + } } diff --git a/fw/tusb_config.h b/fw/tusb_config.h new file mode 100644 index 0000000..868424e --- /dev/null +++ b/fw/tusb_config.h @@ -0,0 +1,111 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#ifndef _TUSB_CONFIG_H_ +#define _TUSB_CONFIG_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +//-------------------------------------------------------------------- +// COMMON CONFIGURATION +//-------------------------------------------------------------------- + +// defined by board.mk +#ifndef CFG_TUSB_MCU + #error CFG_TUSB_MCU must be defined +#endif + +// RHPort number used for device can be defined by board.mk, default to port 0 +#ifndef BOARD_DEVICE_RHPORT_NUM + #define BOARD_DEVICE_RHPORT_NUM 0 +#endif + +// RHPort max operational speed can defined by board.mk +// Default to Highspeed for MCU with internal HighSpeed PHY (can be port specific), otherwise FullSpeed +#ifndef BOARD_DEVICE_RHPORT_SPEED + #if (CFG_TUSB_MCU == OPT_MCU_LPC18XX || CFG_TUSB_MCU == OPT_MCU_LPC43XX || CFG_TUSB_MCU == OPT_MCU_MIMXRT10XX || \ + CFG_TUSB_MCU == OPT_MCU_NUC505 || CFG_TUSB_MCU == OPT_MCU_CXD56 || CFG_TUSB_MCU == OPT_MCU_SAMX7X) + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_HIGH_SPEED + #else + #define BOARD_DEVICE_RHPORT_SPEED OPT_MODE_FULL_SPEED + #endif +#endif + +// Device mode with rhport and speed defined by board.mk +#if BOARD_DEVICE_RHPORT_NUM == 0 + #define CFG_TUSB_RHPORT0_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#elif BOARD_DEVICE_RHPORT_NUM == 1 + #define CFG_TUSB_RHPORT1_MODE (OPT_MODE_DEVICE | BOARD_DEVICE_RHPORT_SPEED) +#else + #error "Incorrect RHPort configuration" +#endif + +#ifndef CFG_TUSB_OS +#define CFG_TUSB_OS OPT_OS_NONE +#endif + +// CFG_TUSB_DEBUG is defined by compiler in DEBUG build +// #define CFG_TUSB_DEBUG 0 + +/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. + * Tinyusb use follows macros to declare transferring memory so that they can be put + * into those specific section. + * e.g + * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) + * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) + */ +#ifndef CFG_TUSB_MEM_SECTION +#define CFG_TUSB_MEM_SECTION +#endif + +#ifndef CFG_TUSB_MEM_ALIGN +#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) +#endif + +//-------------------------------------------------------------------- +// DEVICE CONFIGURATION +//-------------------------------------------------------------------- + +#ifndef CFG_TUD_ENDPOINT0_SIZE +#define CFG_TUD_ENDPOINT0_SIZE 64 +#endif + +//------------- CLASS -------------// +#define CFG_TUD_HID 1 +#define CFG_TUD_CDC 0 +#define CFG_TUD_MSC 0 +#define CFG_TUD_MIDI 0 +#define CFG_TUD_VENDOR 0 + +// HID buffer size Should be sufficient to hold ID (if any) + Data +#define CFG_TUD_HID_EP_BUFSIZE 16 + +#ifdef __cplusplus + } +#endif + +#endif /* _TUSB_CONFIG_H_ */ diff --git a/fw/usb_descriptors.c b/fw/usb_descriptors.c new file mode 100644 index 0000000..cd48384 --- /dev/null +++ b/fw/usb_descriptors.c @@ -0,0 +1,170 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2019 Ha Thach (tinyusb.org) + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * + */ + +#include "tusb.h" + +/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. + * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. + * + * Auto ProductID layout's Bitmap: + * [MSB] HID | MSC | CDC [LSB] + */ +#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) +#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ + _PID_MAP(MIDI, 3) | _PID_MAP(VENDOR, 4) ) + +//--------------------------------------------------------------------+ +// Device Descriptors +//--------------------------------------------------------------------+ +tusb_desc_device_t const desc_device = +{ + .bLength = sizeof(tusb_desc_device_t), + .bDescriptorType = TUSB_DESC_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = 0x00, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, + + .idVendor = 0xCafe, + .idProduct = USB_PID, + .bcdDevice = 0x0100, + + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + + .bNumConfigurations = 0x01 +}; + +// Invoked when received GET DEVICE DESCRIPTOR +// Application return pointer to descriptor +uint8_t const * tud_descriptor_device_cb(void) +{ + return (uint8_t const *) &desc_device; +} + +//--------------------------------------------------------------------+ +// HID Report Descriptor +//--------------------------------------------------------------------+ + +uint8_t const desc_hid_report[] = +{ + TUD_HID_REPORT_DESC_GENERIC_INOUT(CFG_TUD_HID_EP_BUFSIZE) +}; + +// Invoked when received GET HID REPORT DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_hid_descriptor_report_cb(uint8_t itf) +{ + (void) itf; + return desc_hid_report; +} + +//--------------------------------------------------------------------+ +// Configuration Descriptor +//--------------------------------------------------------------------+ + +enum +{ + ITF_NUM_HID, + ITF_NUM_TOTAL +}; + +#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_HID_INOUT_DESC_LEN) + +#define EPNUM_HID 0x01 + +uint8_t const desc_configuration[] = +{ + // Config number, interface count, string index, total length, attribute, power in mA + TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 100), + + // Interface number, string index, protocol, report descriptor len, EP Out & In address, size & polling interval + TUD_HID_INOUT_DESCRIPTOR(ITF_NUM_HID, 0, HID_ITF_PROTOCOL_NONE, sizeof(desc_hid_report), EPNUM_HID, 0x80 | EPNUM_HID, CFG_TUD_HID_EP_BUFSIZE, 10) +}; + +// Invoked when received GET CONFIGURATION DESCRIPTOR +// Application return pointer to descriptor +// Descriptor contents must exist long enough for transfer to complete +uint8_t const * tud_descriptor_configuration_cb(uint8_t index) +{ + (void) index; // for multiple configurations + return desc_configuration; +} + +//--------------------------------------------------------------------+ +// String Descriptors +//--------------------------------------------------------------------+ + +// array of pointer to string descriptors +char const* string_desc_arr [] = +{ + (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) + "TinyUSB", // 1: Manufacturer + "TinyUSB Device", // 2: Product + "123456", // 3: Serials, should use chip ID +}; + +static uint16_t _desc_str[32]; + +// Invoked when received GET STRING DESCRIPTOR request +// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete +uint16_t const* tud_descriptor_string_cb(uint8_t index, uint16_t langid) +{ + (void) langid; + + uint8_t chr_count; + + if ( index == 0) + { + memcpy(&_desc_str[1], string_desc_arr[0], 2); + chr_count = 1; + }else + { + // Note: the 0xEE index string is a Microsoft OS 1.0 Descriptors. + // https://docs.microsoft.com/en-us/windows-hardware/drivers/usbcon/microsoft-defined-usb-descriptors + + if ( !(index < sizeof(string_desc_arr)/sizeof(string_desc_arr[0])) ) return NULL; + + const char* str = string_desc_arr[index]; + + // Cap at max char + chr_count = (uint8_t) strlen(str); + if ( chr_count > 31 ) chr_count = 31; + + // Convert ASCII string into UTF-16 + for(uint8_t i=0; i +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#include +#include "pico/stdlib.h" +#include "fpga.h" +#include "caster.h" +#include "tusb.h" +#include "usb_descriptors.h" + +void usbapp_init(void) { + tusb_init(); +} + +// Device callbacks +void tud_mount_cb(void) { + +} + +void tud_umount_cb(void) { + +} + +void tud_suspend_cb(bool remote_wakeup_en) { + // TODO: Force Suspend +} + +void tud_resume_cb(void) { + +} + +// Invoked when received GET_REPORT control request +// Application must fill buffer report's content and return its length. +// Return zero will cause the stack to STALL request +uint16_t tud_hid_get_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t* buffer, uint16_t reqlen) +{ + // TODO not Implemented + (void) instance; + (void) report_id; + (void) report_type; + (void) buffer; + (void) reqlen; + + return 0; +} + +// Invoked when received SET_REPORT control request or +// received data on OUT endpoint ( Report ID = 0, Type = 0 ) +void tud_hid_set_report_cb(uint8_t instance, uint8_t report_id, hid_report_type_t report_type, uint8_t const* buffer, uint16_t bufsize) +{ + // This example doesn't use multiple report and report ID + (void) instance; + (void) report_id; + (void) report_type; + + // echo back anything we received from host + tud_hid_report(0, buffer, bufsize); +} + +void usbapp_task(void) { + tud_task(); +} diff --git a/fw/usbapp.h b/fw/usbapp.h new file mode 100644 index 0000000..52ab326 --- /dev/null +++ b/fw/usbapp.h @@ -0,0 +1,25 @@ +// +// Copyright 2024 Wenting Zhang +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +#pragma once + +void usbapp_init(void); +void usbapp_task(void);