diff --git a/firmware/CMakeLists.txt b/firmware/CMakeLists.txt index 071f0f74..1d34e960 100644 --- a/firmware/CMakeLists.txt +++ b/firmware/CMakeLists.txt @@ -54,6 +54,7 @@ add_custom_target( add_custom_target( program + COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary) COMMAND sleep 3s COMMAND hackrf_spiflash -i -R -w ${FIRMWARE_FILENAME} diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index b93efc02..90922617 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -113,6 +113,17 @@ set(CSRC ${BOARDSRC} ${FATFSSRC} firmware_info.c + usb_serial_cdc.c + usb_serial_descriptor.c + usb_serial_endpoints.c + usb_serial_io.c + ${HACKRF_PATH}/firmware/common/usb.c + ${HACKRF_PATH}/firmware/common/usb_queue.c + ${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c + ${HACKRF_PATH}/firmware/common/usb_request.c + ${HACKRF_PATH}/firmware/common/usb_standard_request.c + ${CHIBIOS}/os/various/shell.c + ${CHIBIOS}/os/various/chprintf.c ) # C++ sources that can be compiled in ARM or THUMB mode depending on the global @@ -191,6 +202,9 @@ set(CPPSRC log_file.cpp metadata_file.cpp portapack.cpp + usb_serial_shell.cpp + usb_serial_event.cpp + usb_serial.cpp qrcodegen.cpp radio.cpp receiver_model.cpp @@ -363,6 +377,9 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC ${HALINC} ${PLATFORMINC} ${BOARDINC} ${FATFSINC} ${CHIBIOS}/os/various + ${HACKRF_PATH}/firmware/libopencm3/include + ${HACKRF_PATH}/firmware/common + ${HACKRF_PATH}/firmware ui hw apps diff --git a/firmware/application/event_m0.cpp b/firmware/application/event_m0.cpp index d4deef8b..99e5e2ae 100644 --- a/firmware/application/event_m0.cpp +++ b/firmware/application/event_m0.cpp @@ -113,6 +113,7 @@ void EventDispatcher::run() { while (is_running) { const auto events = wait(); dispatch(events); + portapack::usb_serial.dispatch(); } } diff --git a/firmware/application/halconf.h b/firmware/application/halconf.h index 23af6593..23b40040 100755 --- a/firmware/application/halconf.h +++ b/firmware/application/halconf.h @@ -285,7 +285,7 @@ * buffers. */ #if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__) -#define SERIAL_BUFFERS_SIZE 16 +#define SERIAL_BUFFERS_SIZE 64 #endif /*===========================================================================*/ diff --git a/firmware/application/irq_controls.cpp b/firmware/application/irq_controls.cpp index cd1e7f24..08ff13c0 100644 --- a/firmware/application/irq_controls.cpp +++ b/firmware/application/irq_controls.cpp @@ -122,6 +122,8 @@ static bool touch_update() { } static uint8_t switches_raw = 0; +static uint8_t injected_switch = 0; +static uint8_t injected_encoder = 0; /* The raw data is not packed in a way that makes looping over it easy. * One option would be an accessor helper (RawSwitch). Another option @@ -170,8 +172,11 @@ static bool encoder_update(const uint8_t raw) { static bool encoder_read() { const auto delta = encoder.update( - encoder_debounce[0].state(), - encoder_debounce[1].state()); + encoder_debounce[0].state() | (injected_encoder == 1), + encoder_debounce[1].state() | (injected_encoder == 2)); + + if (injected_encoder > 0) + injected_encoder = 0; if (delta != 0) { encoder_position += delta; @@ -186,10 +191,10 @@ void timer0_callback(GPTDriver* const) { if (touch_update()) event_mask |= EVT_MASK_TOUCH; switches_raw = swizzled_switches(); - if (switches_update(switches_raw)) + if (switches_update(switches_raw) || (injected_switch > 0)) event_mask |= EVT_MASK_SWITCHES; - if (encoder_update(switches_raw) && encoder_read()) + if (encoder_update(switches_raw) || encoder_read()) event_mask |= EVT_MASK_ENCODER; /* Signal event loop */ @@ -238,6 +243,13 @@ SwitchesState get_switches_state() { for (size_t i = 0; i < result.size(); i++) result[i] = switch_debounce[i].state(); + if (injected_switch > 0 && injected_switch <= 6) { + result[injected_switch - 1] = 1; + injected_switch = 0xff; + } else if (injected_switch == 0xff) { + injected_switch = 0x00; + } + return result; } @@ -277,5 +289,12 @@ uint8_t switches() { return switches_raw; } +void inject_switch(uint8_t button) { + if (button <= 6) + injected_switch = button; + else if (button > 6) + injected_encoder = button - 6; +} + } // namespace debug } // namespace control diff --git a/firmware/application/irq_controls.hpp b/firmware/application/irq_controls.hpp index a158b138..79482fe7 100644 --- a/firmware/application/irq_controls.hpp +++ b/firmware/application/irq_controls.hpp @@ -56,6 +56,7 @@ namespace control { namespace debug { uint8_t switches(); +void inject_switch(uint8_t); } // namespace debug } // namespace control diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index f9033a3d..8813d30b 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -70,6 +70,7 @@ lcd::ILI9341 display; I2C i2c0(&I2CD0); SPI ssp1(&SPID2); +portapack::USBSerial usb_serial; si5351::Si5351 clock_generator{ i2c0, hackrf::one::si5351_i2c_address}; @@ -368,7 +369,7 @@ static void shutdown_base() { * * XTAL_OSC = powered down * - * PLL0USB = powered down + * PLL0USB = XTAL, 480 MHz * PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz * PLL1 = * OG: GP_CLKIN * 10 = 200 MHz @@ -464,6 +465,8 @@ bool init() { /* Remove /2P divider from PLL1 output to achieve full speed */ cgu::pll1::direct(); + usb_serial.initialize(); + i2c0.start(i2c_config_fast_clock); chThdSleepMilliseconds(10); diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index 0bf33be5..a9cbebd5 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -31,6 +31,7 @@ #include "si5351.hpp" #include "lcd_ili9341.hpp" #include "backlight.hpp" +#include "usb_serial.hpp" #include "radio.hpp" #include "clock_manager.hpp" @@ -46,6 +47,7 @@ extern lcd::ILI9341 display; extern I2C i2c0; extern SPI ssp1; +extern portapack::USBSerial usb_serial; extern si5351::Si5351 clock_generator; extern ClockManager clock_manager; diff --git a/firmware/application/usb_serial.cpp b/firmware/application/usb_serial.cpp new file mode 100644 index 00000000..a1a8bd92 --- /dev/null +++ b/firmware/application/usb_serial.cpp @@ -0,0 +1,97 @@ +extern "C" { +#include "usb_serial_io.h" +#include "usb_serial_cdc.h" +} + +#include "usb_serial_shell.hpp" +#include "usb_serial.hpp" +#include "portapack.hpp" + +#include +#include + +namespace portapack { + +void USBSerial::initialize() { + enable_xtal(); + disable_pll0(); + + setup_pll0(); + enable_pll0(); + + setup_usb_clock(); + setup_usb_serial_controller(); + + init_serial_usb_driver(&SUSBD1); + shellInit(); +} + +void USBSerial::dispatch() { + if (!connected) + return; + + if (shell_created == false) { + shell_created = true; + create_shell(); + } + + bulk_out_receive(); +} + +void USBSerial::on_channel_opened() { + connected = true; +} + +void USBSerial::on_channel_closed() { + connected = false; +} + +void USBSerial::enable_xtal() { + LPC_CGU->XTAL_OSC_CTRL.ENABLE = 0; + LPC_CGU->XTAL_OSC_CTRL.HF = 0; +} + +void USBSerial::disable_pll0() { + LPC_CGU->PLL0USB_CTRL.PD = 1; + LPC_CGU->PLL0USB_CTRL.AUTOBLOCK = 1; + + LPC_CGU->PLL0USB_CTRL.BYPASS = 0; + LPC_CGU->PLL0USB_CTRL.DIRECTI = 0; + LPC_CGU->PLL0USB_CTRL.DIRECTO = 0; + LPC_CGU->PLL0USB_CTRL.CLKEN = 0; + LPC_CGU->PLL0USB_CTRL.FRM = 0; + LPC_CGU->PLL0USB_CTRL.CLK_SEL = 0x06; // 12MHz internal XTAL +} + +void USBSerial::setup_pll0() { + /* use XTAL_OSC as clock source for PLL0USB */ + + while (LPC_CGU->PLL0USB_STAT.LOCK) { + } + + // /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ + // /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ + LPC_CGU->PLL0USB_MDIV = 0x06167FFA; + LPC_CGU->PLL0USB_NP_DIV = 0x00302062; + LPC_CGU->PLL0USB_CTRL.PD = 1; + LPC_CGU->PLL0USB_CTRL.DIRECTI = 1; + LPC_CGU->PLL0USB_CTRL.DIRECTO = 1; + LPC_CGU->PLL0USB_CTRL.CLKEN = 1; +} + +void USBSerial::enable_pll0() { + // /* power on PLL0USB and wait until stable */ + LPC_CGU->PLL0USB_CTRL.PD = 0; + + while (!LPC_CGU->PLL0USB_STAT.LOCK) { + } +} + +void USBSerial::setup_usb_clock() { + /* use PLL0USB as clock source for USB0 */ + LPC_CGU->BASE_USB0_CLK.AUTOBLOCK = 1; + LPC_CGU->BASE_USB0_CLK.CLK_SEL = 0x07; + LPC_CGU->BASE_USB0_CLK.PD = 0; +} + +} // namespace portapack diff --git a/firmware/application/usb_serial.hpp b/firmware/application/usb_serial.hpp new file mode 100644 index 00000000..f4f50ee3 --- /dev/null +++ b/firmware/application/usb_serial.hpp @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +namespace portapack { + +class USBSerial { + public: + void initialize(); + void dispatch(); + void on_channel_opened(); + void on_channel_closed(); + + private: + void enable_xtal(); + void disable_pll0(); + void setup_pll0(); + void enable_pll0(); + + void setup_usb_clock(); + + bool connected{false}; + bool shell_created{false}; +}; + +} // namespace portapack \ No newline at end of file diff --git a/firmware/application/usb_serial_cdc.c b/firmware/application/usb_serial_cdc.c new file mode 100644 index 00000000..8671b116 --- /dev/null +++ b/firmware/application/usb_serial_cdc.c @@ -0,0 +1,143 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_cdc.h" +#include "usb_serial_endpoints.h" +#include "usb_serial_event.hpp" + +extern void usb0_isr(void); +CH_IRQ_HANDLER(USB0_IRQHandler) { + CH_IRQ_PROLOGUE(); + usb0_isr(); + CH_IRQ_EPILOGUE(); +} + +uint32_t __ldrex(volatile uint32_t* addr) { + __disable_irq(); + return *addr; +} + +uint32_t __strex(uint32_t val, volatile uint32_t* addr) { + (void)val; + (void)addr; + + *addr = val; + __enable_irq(); + return 0; +} + +void nvic_enable_irq(uint8_t irqn) { + NVIC_ISER(irqn / 32) = (1 << (irqn % 32)); +} + +void usb_configuration_changed(usb_device_t* const device) { + (void)device; + + usb_endpoint_init(&usb_endpoint_int_in); + usb_endpoint_init(&usb_endpoint_bulk_in); + usb_endpoint_init(&usb_endpoint_bulk_out); +} + +void setup_usb_serial_controller(void) { + usb_set_configuration_changed_cb(usb_configuration_changed); + usb_peripheral_reset(); + + usb_device_init(0, &usb_device); + + usb_queue_init(&usb_endpoint_control_out_queue); + usb_queue_init(&usb_endpoint_control_in_queue); + usb_queue_init(&usb_endpoint_int_in_queue); + usb_queue_init(&usb_endpoint_bulk_out_queue); + usb_queue_init(&usb_endpoint_bulk_in_queue); + + usb_endpoint_init(&usb_endpoint_control_out); + usb_endpoint_init(&usb_endpoint_control_in); + + usb_run(&usb_device); +} + +const usb_request_handlers_t usb_request_handlers = { + .standard = usb_standard_request, + .class = usb_class_request, + .vendor = 0, + .reserved = 0}; + +usb_request_status_t usb_class_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + usb_request_status_t status = USB_REQUEST_STATUS_STALL; + + volatile uint8_t request = endpoint->setup.request; + + if (request == 0x21) // GET LINE CODING REQUEST + return usb_get_line_coding_request(endpoint, stage); + + if (request == 0x22) // SET CONTROL LINE STATE REQUEST + return usb_set_control_line_state_request(endpoint, stage); + + if (request == 0x20) // SET LINE CODING REQUEST + return usb_set_line_coding_request(endpoint, stage); + + return USB_REQUEST_STATUS_OK; + + return status; +} + +usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 0, + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + usb_transfer_schedule_ack(endpoint->out); + } + + return USB_REQUEST_STATUS_OK; +} +usb_request_status_t usb_set_control_line_state_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + if (endpoint->setup.value == 3) { + on_channel_opened(); + } else { + on_channel_closed(); + } + + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_set_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + usb_transfer_schedule_block( + endpoint->out, + &endpoint->buffer, + 32, + NULL, + NULL); + } else if (stage == USB_TRANSFER_STAGE_DATA) { + usb_transfer_schedule_ack(endpoint->in); + } + + return USB_REQUEST_STATUS_OK; +} diff --git a/firmware/application/usb_serial_cdc.h b/firmware/application/usb_serial_cdc.h new file mode 100644 index 00000000..ee6f3239 --- /dev/null +++ b/firmware/application/usb_serial_cdc.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +#ifndef __cplusplus +#pragma GCC diagnostic push +// external code, so ignore warnings +#pragma GCC diagnostic ignored "-Wstrict-prototypes" + +#include +#include +#include +#include +#include + +#pragma GCC diagnostic pop + +usb_request_status_t usb_class_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_set_control_line_state_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +usb_request_status_t usb_set_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage); +#endif + +void setup_usb_serial_controller(void); diff --git a/firmware/application/usb_serial_descriptor.c b/firmware/application/usb_serial_descriptor.c new file mode 100644 index 00000000..dbdbfee3 --- /dev/null +++ b/firmware/application/usb_serial_descriptor.c @@ -0,0 +1,339 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_descriptor.h" + +#include "usb_serial_endpoints.h" + +#define USB_VENDOR_ID (0x1D50) +#define USB_PRODUCT_ID (0x6018) +#define USB_API_VERSION (0x0100) + +#define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF) +#define USB_MAX_PACKET0 (64) +#define USB_MAX_PACKET_BULK_FS (64) +#define USB_MAX_PACKET_BULK_HS (64) +#define USB_STRING_LANGID (0x0409) + +uint8_t usb_descriptor_device[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0xef, // bDeviceClass + 0x02, // bDeviceSubClass + 0x01, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + USB_WORD(USB_VENDOR_ID), // idVendor + USB_WORD(USB_PRODUCT_ID), // idProduct + USB_WORD(USB_API_VERSION), // bcdDevice + 0x01, // iManufacturer + 0x02, // iProduct + 0x03, // iSerialNumber + 0x01 // bNumConfigurations +}; + +uint8_t usb_descriptor_device_qualifier[] = { + 10, // bLength + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0xef, // bDeviceClass + 0x02, // bDeviceSubClass + 0x01, // bDeviceProtocol + USB_MAX_PACKET0, // bMaxPacketSize0 + 0x01, // bNumOtherSpeedConfigurations + 0x00 // bReserved +}; + +uint8_t usb_descriptor_configuration_full_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD((16 + 8 + 9 + 5 + 5 + 4 + 5 + 9 + 7 + 7)), // wTotalLength + 0x02, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 8, // bLength + 0xb, // bDescriptorType + 0, + 2, + 0x02, + 0x02, + 0x00, + 4, + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints + 0x02, // bInterfaceClass: vendor-specific + 0x02, // bInterfaceSubClass + 0x00, // bInterfaceProtocol: vendor-specific + 0x04, // iInterface + + 5, + 0x24, // ACM + 0x00, + USB_WORD(0x0110), + + 5, + 0x24, + 0x01, + 0x00, + 0x01, + + 4, + 0x24, + 0x02, + 0x02, + + 5, + 0x24, + 0x06, + 0x00, + 0x01, + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_INT_IN_EP_ADDR, // bEndpointAddress + 0x03, // bmAttributes: BULK + USB_WORD(16), // wMaxPacketSize + 0xFF, // bInterval: no NAK + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x0a, // bInterfaceClass: vendor-specific + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x01, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x01, // bInterval: NAK + + 0, // TERMINATOR +}; + +uint8_t usb_descriptor_configuration_high_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD((16 + 8 + 9 + 5 + 5 + 4 + 5 + 9 + 7 + 7)), // wTotalLength + 0x02, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 8, // bLength + 0xb, // bDescriptorType + 0, + 2, + 0x02, + 0x02, + 0x00, + 4, + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x01, // bNumEndpoints + 0x02, // bInterfaceClass: vendor-specific + 0x02, // bInterfaceSubClass + 0x00, // bInterfaceProtocol: vendor-specific + 0x04, // iInterface + + 5, + 0x24, + 0x00, + USB_WORD(0x0110), + + 5, + 0x24, + 0x01, + 0x00, + 0x01, + + 4, + 0x24, + 0x02, + 0x02, + + 5, + 0x24, + 0x06, + 0x00, + 0x01, + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_INT_IN_EP_ADDR, // bEndpointAddress + 0x03, // bmAttributes: BULK + USB_WORD(16), // wMaxPacketSize + 0xFF, // bInterval: no NAK + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x01, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x0a, // bInterfaceClass: vendor-specific + 0x00, // bInterfaceSubClass + 0x00, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x01, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x01, // bInterval: NAK + + 0, // TERMINATOR +}; + +uint8_t usb_descriptor_string_languages[] = { + 0x04, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + USB_WORD(USB_STRING_LANGID), // wLANGID +}; + +// clang-format off +uint8_t usb_descriptor_string_manufacturer[] = { + 40, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'G', 0x00, + 'r', 0x00, + 'e', 0x00, + 'a', 0x00, + 't', 0x00, + ' ', 0x00, + 'S', 0x00, + 'c', 0x00, + 'o', 0x00, + 't', 0x00, + 't', 0x00, + ' ', 0x00, + 'G', 0x00, + 'a', 0x00, + 'd', 0x00, + 'g', 0x00, + 'e', 0x00, + 't', 0x00, + 's', 0x00, + }; + + uint8_t usb_descriptor_string_product[] = { + 43, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'P', 0x00, + 'o', 0x00, + 'r', 0x00, + 't', 0x00, + 'a', 0x00, + 'P', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + ' ', 0x00, + 'M', 0x00, + 'a', 0x00, + 'y', 0x00, + 'h', 0x00, + 'e', 0x00, + 'm', 0x00, + }; + + uint8_t usb_descriptor_string_config_description[] = { + 24, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'T', 0x00, + 'r', 0x00, + 'a', 0x00, + 'n', 0x00, + 's', 0x00, + 'c', 0x00, + 'e', 0x00, + 'i', 0x00, + 'v', 0x00, + 'e', 0x00, + 'r', 0x00, + }; + + uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; + + uint8_t* usb_descriptor_strings[] = { + usb_descriptor_string_languages, + usb_descriptor_string_manufacturer, + usb_descriptor_string_product, + usb_descriptor_string_config_description, + usb_descriptor_string_serial_number, + 0, // TERMINATOR + }; + + uint8_t wcid_string_descriptor[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'M', 0x00, + 'S', 0x00, + 'F', 0x00, + 'T', 0x00, + '1', 0x00, + '0', 0x00, + '0', 0x00, + USB_WCID_VENDOR_REQ, // vendor request code for further descriptor + 0x00 + }; + + uint8_t wcid_feature_descriptor[] = { + 0x28, 0x00, 0x00, 0x00, // bLength + USB_WORD(0x0100), // WCID version + USB_WORD(0x0004), // WICD descriptor index + 0x01, // bNumSections + 0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved + 0x00, // bInterfaceNumber + 0x01, // Reserved + 'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID + 0x00,0x00,0x00,0x00,0x00,0x00 // Reserved + }; diff --git a/firmware/application/usb_serial_descriptor.h b/firmware/application/usb_serial_descriptor.h new file mode 100644 index 00000000..ebc073b7 --- /dev/null +++ b/firmware/application/usb_serial_descriptor.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +extern uint8_t usb_descriptor_device[]; +extern uint8_t usb_descriptor_device_qualifier[]; +extern uint8_t usb_descriptor_configuration_full_speed[]; +extern uint8_t usb_descriptor_configuration_high_speed[]; +extern uint8_t usb_descriptor_string_languages[]; +extern uint8_t usb_descriptor_string_manufacturer[]; +extern uint8_t usb_descriptor_string_product[]; + +#define USB_DESCRIPTOR_STRING_SERIAL_LEN 32 +#define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \ + (USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */ +extern uint8_t usb_descriptor_string_serial_number[]; + +extern uint8_t* usb_descriptor_strings[]; + +#define USB_WCID_VENDOR_REQ 0x19 +extern uint8_t wcid_string_descriptor[]; +extern uint8_t wcid_feature_descriptor[]; diff --git a/firmware/application/usb_serial_endpoints.c b/firmware/application/usb_serial_endpoints.c new file mode 100644 index 00000000..32290585 --- /dev/null +++ b/firmware/application/usb_serial_endpoints.c @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_endpoints.h" + +#pragma GCC diagnostic push +// external code, so ignore warnings +#pragma GCC diagnostic ignored "-Wstrict-prototypes" + +#include +#include +#include +#include +#include + +#pragma GCC diagnostic pop + +usb_endpoint_t usb_endpoint_control_out = { + .address = USB_CONTROL_OUT_EP_ADDR, + .device = &usb_device, + .in = &usb_endpoint_control_in, + .out = &usb_endpoint_control_out, + .setup_complete = usb_setup_complete, + .transfer_complete = usb_control_out_complete, +}; +USB_DEFINE_QUEUE(usb_endpoint_control_out, 4); + +usb_endpoint_t usb_endpoint_control_in = { + .address = USB_CONTROL_IN_EP_ADDR, + .device = &usb_device, + .in = &usb_endpoint_control_in, + .out = &usb_endpoint_control_out, + .setup_complete = 0, + .transfer_complete = usb_control_in_complete, +}; +static USB_DEFINE_QUEUE(usb_endpoint_control_in, 4); + +usb_endpoint_t usb_endpoint_int_in = { + .address = USB_INT_IN_EP_ADDR, + .device = &usb_device, + .in = &usb_endpoint_int_in, + .out = 0, + .setup_complete = 0, + .transfer_complete = usb_queue_transfer_complete}; +static USB_DEFINE_QUEUE(usb_endpoint_int_in, 1); + +usb_endpoint_t usb_endpoint_bulk_in = { + .address = USB_BULK_IN_EP_ADDR, + .device = &usb_device, + .in = &usb_endpoint_bulk_in, + .out = 0, + .setup_complete = 0, + .transfer_complete = usb_queue_transfer_complete}; +static USB_DEFINE_QUEUE(usb_endpoint_bulk_in, 1); + +usb_endpoint_t usb_endpoint_bulk_out = { + .address = USB_BULK_OUT_EP_ADDR, + .device = &usb_device, + .in = 0, + .out = &usb_endpoint_bulk_out, + .setup_complete = 0, + .transfer_complete = usb_queue_transfer_complete}; +static USB_DEFINE_QUEUE(usb_endpoint_bulk_out, 1); diff --git a/firmware/application/usb_serial_endpoints.h b/firmware/application/usb_serial_endpoints.h new file mode 100644 index 00000000..48ebaea2 --- /dev/null +++ b/firmware/application/usb_serial_endpoints.h @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +#include +#include + +#define USB_CONTROL_IN_EP_ADDR (0x80) +#define USB_CONTROL_OUT_EP_ADDR (0x00) + +#define USB_INT_IN_EP_ADDR (0x82) + +#define USB_BULK_OUT_EP_ADDR (0x01) +#define USB_BULK_IN_EP_ADDR (0x81) + +extern usb_endpoint_t usb_endpoint_control_out; +extern USB_DECLARE_QUEUE(usb_endpoint_control_out); + +extern usb_endpoint_t usb_endpoint_control_in; +extern USB_DECLARE_QUEUE(usb_endpoint_control_in); + +extern usb_endpoint_t usb_endpoint_int_in; +extern USB_DECLARE_QUEUE(usb_endpoint_int_in); + +extern usb_endpoint_t usb_endpoint_bulk_in; +extern USB_DECLARE_QUEUE(usb_endpoint_bulk_in); + +extern usb_endpoint_t usb_endpoint_bulk_out; +extern USB_DECLARE_QUEUE(usb_endpoint_bulk_out); diff --git a/firmware/application/usb_serial_event.cpp b/firmware/application/usb_serial_event.cpp new file mode 100644 index 00000000..ff2c37b2 --- /dev/null +++ b/firmware/application/usb_serial_event.cpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_event.hpp" + +#include "portapack.hpp" + +extern "C" { +void on_channel_opened() { + portapack::usb_serial.on_channel_opened(); +} + +void on_channel_closed() { + portapack::usb_serial.on_channel_closed(); +} +} diff --git a/firmware/application/usb_serial_event.hpp b/firmware/application/usb_serial_event.hpp new file mode 100644 index 00000000..a3e40afb --- /dev/null +++ b/firmware/application/usb_serial_event.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +#ifdef __cplusplus +extern "C" { +#endif +void on_channel_opened(void); +void on_channel_closed(void); +#ifdef __cplusplus +} +#endif diff --git a/firmware/application/usb_serial_io.c b/firmware/application/usb_serial_io.c new file mode 100644 index 00000000..1b6aef14 --- /dev/null +++ b/firmware/application/usb_serial_io.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_io.h" + +#include "usb_serial_endpoints.h" + +#pragma GCC diagnostic push +// external code, so ignore warnings +#pragma GCC diagnostic ignored "-Wstrict-prototypes" + +#include +#include +#include +#include +#include + +#pragma GCC diagnostic pop + +#include +#include + +SerialUSBDriver SUSBD1; + +void bulk_out_receive(void) { + int ret; + do { + ret = usb_transfer_schedule( + &usb_endpoint_bulk_out, + &usb_endpoint_bulk_out.buffer[0], + 32, + serial_bulk_transfer_complete, + NULL); + + } while (ret != -1); +} + +void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) { + (void)user_data; + + chSysLockFromIsr(); + + for (unsigned int i = 0; i < bytes_transferred; i++) { + msg_t ret; + do { + ret = chIQPutI(&SUSBD1.iqueue, usb_endpoint_bulk_out.buffer[i]); + if (ret == Q_FULL) + chThdSleepMilliseconds(1); + + } while (ret == Q_FULL); + } + + chSysUnlockFromIsr(); +} + +static void onotify(GenericQueue* qp) { + SerialUSBDriver* sdp = chQGetLink(qp); + int n = chOQGetFullI(&sdp->oqueue); + if (n > 0) { + for (int i = 0; i < n; i++) { + usb_endpoint_bulk_in.buffer[i] = chOQGetI(&sdp->oqueue); + } + + int ret; + chSysUnlock(); + do { + ret = usb_transfer_schedule( + &usb_endpoint_bulk_in, + &usb_endpoint_bulk_in.buffer[0], + n, + NULL, + NULL); + + if (ret == -1) + chThdSleepMilliseconds(1); + + } while (ret == -1); + chSysLock(); + } +} + +static size_t write(void* ip, const uint8_t* bp, size_t n) { + return chOQWriteTimeout(&((SerialUSBDriver*)ip)->oqueue, bp, + n, TIME_INFINITE); +} + +static size_t read(void* ip, uint8_t* bp, size_t n) { + return chIQReadTimeout(&((SerialUSBDriver*)ip)->iqueue, bp, + n, TIME_INFINITE); +} + +static msg_t put(void* ip, uint8_t b) { + return chOQPutTimeout(&((SerialUSBDriver*)ip)->oqueue, b, TIME_INFINITE); +} + +static msg_t get(void* ip) { + return chIQGetTimeout(&((SerialUSBDriver*)ip)->iqueue, TIME_INFINITE); +} + +static msg_t putt(void* ip, uint8_t b, systime_t timeout) { + return chOQPutTimeout(&((SerialUSBDriver*)ip)->oqueue, b, timeout); +} + +static msg_t gett(void* ip, systime_t timeout) { + return chIQGetTimeout(&((SerialUSBDriver*)ip)->iqueue, timeout); +} + +static size_t writet(void* ip, const uint8_t* bp, size_t n, systime_t time) { + return chOQWriteTimeout(&((SerialUSBDriver*)ip)->oqueue, bp, n, time); +} + +static size_t readt(void* ip, uint8_t* bp, size_t n, systime_t time) { + return chIQReadTimeout(&((SerialUSBDriver*)ip)->iqueue, bp, n, time); +} + +static const struct SerialUSBDriverVMT vmt = { + write, read, put, get, + putt, gett, writet, readt}; + +void init_serial_usb_driver(SerialUSBDriver* sdp) { + sdp->vmt = &vmt; + chIQInit(&sdp->iqueue, sdp->ib, SERIAL_BUFFERS_SIZE, NULL, sdp); + chOQInit(&sdp->oqueue, sdp->ob, SERIAL_BUFFERS_SIZE, onotify, sdp); +} diff --git a/firmware/application/usb_serial_io.h b/firmware/application/usb_serial_io.h new file mode 100644 index 00000000..6f69375a --- /dev/null +++ b/firmware/application/usb_serial_io.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +struct SerialUSBDriverVMT { + _base_asynchronous_channel_methods +}; + +struct SerialUSBDriver { + /** @brief Virtual Methods Table.*/ + const struct SerialUSBDriverVMT* vmt; + InputQueue iqueue; /* Output queue.*/ + OutputQueue oqueue; /* Input circular buffer.*/ + uint8_t ib[SERIAL_BUFFERS_SIZE]; /* Output circular buffer.*/ + uint8_t ob[SERIAL_BUFFERS_SIZE]; +}; + +typedef struct SerialUSBDriver SerialUSBDriver; + +extern SerialUSBDriver SUSBD1; + +void init_serial_usb_driver(SerialUSBDriver* sdp); +void bulk_out_receive(void); +void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred); diff --git a/firmware/application/usb_serial_shell.cpp b/firmware/application/usb_serial_shell.cpp new file mode 100644 index 00000000..b8a9c673 --- /dev/null +++ b/firmware/application/usb_serial_shell.cpp @@ -0,0 +1,403 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_serial_shell.hpp" +#include "event_m0.hpp" +#include "baseband_api.hpp" +#include "core_control.hpp" +#include "bitmap.hpp" +#include "png_writer.hpp" +#include "irq_controls.hpp" + +#include "usb_serial_io.h" +#include "ff.h" +#include "chprintf.h" + +#include +#include +#include +#include + +#define SHELL_WA_SIZE THD_WA_SIZE(1024 * 3) +#define palOutputPad(port, pad) (LPC_GPIO->DIR[(port)] |= 1 << (pad)) + +static void cmd_reboot(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)chp; + (void)argc; + (void)argv; + + m4_request_shutdown(); + chThdSleepMilliseconds(50); + + LPC_RGU->RESET_CTRL[0] = (1 << 0); +} + +static void cmd_dfu(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)chp; + (void)argc; + (void)argv; + + m4_request_shutdown(); + chThdSleepMilliseconds(50); + + LPC_SCU->SFSP2_8 = (LPC_SCU->SFSP2_8 & ~(7)) | 4; + palOutputPad(5, 7); + palSetPad(5, 7); + + LPC_RGU->RESET_CTRL[0] = (1 << 0); +} + +static void cmd_hackrf(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)chp; + (void)argc; + (void)argv; + + m4_request_shutdown(); + chThdSleepMilliseconds(50); + EventDispatcher::request_stop(); +} + +static void cmd_sd_over_usb(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)chp; + (void)argc; + (void)argv; + + ui::Painter painter; + painter.fill_rectangle( + {0, 0, portapack::display.width(), portapack::display.height()}, + ui::Color::black()); + + painter.draw_bitmap( + {portapack::display.width() / 2 - 8, portapack::display.height() / 2 - 8}, + ui::bitmap_icon_hackrf, + ui::Color::yellow(), + ui::Color::black()); + + sdcDisconnect(&SDCD1); + sdcStop(&SDCD1); + + m4_request_shutdown(); + chThdSleepMilliseconds(50); + portapack::shutdown(true); + m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false); + m0_halt(); +} + +std::filesystem::path path_from_string8(char* path) { + std::wstring_convert, char16_t> conv; + return conv.from_bytes(path); +} + +static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n"); + return; + } + + auto path = path_from_string8(argv[0]); + size_t filename_length = strlen(argv[0]); + + if (!std::filesystem::file_exists(path)) { + chprintf(chp, "file not found.\r\n"); + return; + } + + std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (filename_length + 1) * 2); + + m4_request_shutdown(); + chThdSleepMilliseconds(50); + m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false); + m0_halt(); +} + +static void cmd_screenshot(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)argc; + (void)argv; + + ensure_directory("SCREENSHOTS"); + auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG"); + + if (path.empty()) + return; + + PNGWriter png; + auto error = png.create(path); + if (error) + return; + + for (int i = 0; i < ui::screen_height; i++) { + std::array row; + portapack::display.read_pixels({0, i, ui::screen_width, 1}, row); + png.write_scanline(row); + } + + chprintf(chp, "generated %s\r\n", path.string().c_str()); +} + +static void cmd_write_memory(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 2) { + chprintf(chp, "usage: write_memory
\r\n"); + chprintf(chp, "example: write_memory 0x40004008 0x00000002\r\n"); + return; + } + + int value_length = strlen(argv[1]); + if (value_length != 10 && value_length != 4) { + chprintf(chp, "usage: write_memory
\r\n"); + chprintf(chp, "example: write_memory 0x40004008 0x00000002\r\n"); + return; + } + + uint32_t address = (uint32_t)strtol(argv[0], NULL, 16); + uint32_t value = (uint32_t)strtol(argv[1], NULL, 16); + + if (value_length == 10) { + uint32_t* data_pointer = (uint32_t*)address; + *data_pointer = value; + } else { + uint8_t* data_pointer = (uint8_t*)address; + *data_pointer = (uint8_t)value; + } + + chprintf(chp, "ok\r\n"); +} + +static void cmd_read_memory(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: read_memory 0x40004008\r\n"); + return; + } + + int address = (int)strtol(argv[0], NULL, 16); + uint32_t* data_pointer = (uint32_t*)address; + + chprintf(chp, "%x\r\n", *data_pointer); +} + +static void cmd_button(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: button 1\r\n"); + return; + } + + int button = (int)strtol(argv[0], NULL, 10); + if (button < 1 || button > 8) { + chprintf(chp, "usage: button \r\n"); + return; + } + + control::debug::inject_switch(button); + + chprintf(chp, "ok\r\n"); +} + +static void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: ls /\r\n"); + return; + } + + auto path = path_from_string8(argv[0]); + + for (const auto& entry : std::filesystem::directory_iterator(path, "*")) { + if (std::filesystem::is_directory(entry.status())) { + chprintf(chp, "%s/\r\n", entry.path().string().c_str()); + } else if (std::filesystem::is_regular_file(entry.status())) { + chprintf(chp, "%s\r\n", entry.path().string().c_str()); + } else { + chprintf(chp, "%s *\r\n", entry.path().string().c_str()); + } + } +} + +static void cmd_sd_delete(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: rm \r\n"); + return; + } + + auto path = path_from_string8(argv[0]); + + if (!std::filesystem::file_exists(path)) { + chprintf(chp, "file not found.\r\n"); + return; + } + + delete_file(path); + + chprintf(chp, "ok\r\n"); +} + +File* shell_file = nullptr; + +static void cmd_sd_open(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: open \r\n"); + return; + } + + if (shell_file != nullptr) { + chprintf(chp, "file already open\r\n"); + return; + } + + auto path = path_from_string8(argv[0]); + shell_file = new File(); + shell_file->open(path, false, true); + + chprintf(chp, "ok\r\n"); +} + +static void cmd_sd_seek(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: seek \r\n"); + return; + } + + if (shell_file == nullptr) { + chprintf(chp, "no open file\r\n"); + return; + } + + int address = (int)strtol(argv[0], NULL, 10); + shell_file->seek(address); + + chprintf(chp, "ok\r\n"); +} + +static void cmd_sd_close(BaseSequentialStream* chp, int argc, char* argv[]) { + (void)argv; + + if (argc != 0) { + chprintf(chp, "usage: close\r\n"); + return; + } + + if (shell_file == nullptr) { + chprintf(chp, "no open file\r\n"); + return; + } + + delete shell_file; + shell_file = nullptr; + + chprintf(chp, "ok\r\n"); +} + +static void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]) { + if (argc != 1) { + chprintf(chp, "usage: read \r\n"); + return; + } + + if (shell_file == nullptr) { + chprintf(chp, "no open file\r\n"); + return; + } + + int size = (int)strtol(argv[0], NULL, 10); + + uint8_t buffer[16]; + + do { + File::Size bytes_to_read = size > 16 ? 16 : size; + auto bytes_read = shell_file->read(buffer, bytes_to_read); + if (bytes_read.is_error()) { + chprintf(chp, "error %d\r\n", bytes_read.error()); + return; + } + + for (size_t i = 0; i < bytes_read.value(); i++) + chprintf(chp, "%02X", buffer[i]); + + chprintf(chp, "\r\n"); + + if (bytes_to_read != bytes_read.value()) + return; + + size -= bytes_to_read; + } while (size > 0); +} + +static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) { + const char* usage = "usage: write 0123456789ABCDEF\r\n"; + if (argc != 1) { + chprintf(chp, usage); + return; + } + + if (shell_file == nullptr) { + chprintf(chp, "no open file\r\n"); + return; + } + + size_t data_string_len = strlen(argv[0]); + if (data_string_len % 2 != 0) { + chprintf(chp, usage); + return; + } + + for (size_t i = 0; i < data_string_len; i++) { + char c = argv[0][i]; + if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) { + chprintf(chp, usage); + return; + } + } + + char buffer[3] = {0, 0, 0}; + + for (size_t i = 0; i < data_string_len / 2; i++) { + buffer[0] = argv[0][i * 2]; + buffer[1] = argv[0][i * 2 + 1]; + uint8_t value = (uint8_t)strtol(buffer, NULL, 16); + shell_file->write(&value, 1); + } + + chprintf(chp, "ok\r\n"); +} + +static const ShellCommand commands[] = { + {"reboot", cmd_reboot}, + {"dfu", cmd_dfu}, + {"hackrf", cmd_hackrf}, + {"sd_over_usb", cmd_sd_over_usb}, + {"flash", cmd_flash}, + {"screenshot", cmd_screenshot}, + {"write_memory", cmd_write_memory}, + {"read_memory", cmd_read_memory}, + {"button", cmd_button}, + {"ls", cmd_sd_list_dir}, + {"rm", cmd_sd_delete}, + {"open", cmd_sd_open}, + {"seek", cmd_sd_seek}, + {"close", cmd_sd_close}, + {"read", cmd_sd_read}, + {"write", cmd_sd_write}, + {NULL, NULL}}; + +static const ShellConfig shell_cfg1 = { + (BaseSequentialStream*)&SUSBD1, + commands}; + +void create_shell() { + shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO); +} diff --git a/firmware/application/usb_serial_shell.hpp b/firmware/application/usb_serial_shell.hpp new file mode 100644 index 00000000..bcfed055 --- /dev/null +++ b/firmware/application/usb_serial_shell.hpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2023 Bernd Herzog + * + * This file is part of PortaPack. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#pragma once + +#include "ch.h" +#include "hal.h" + +#include "shell.h" + +void create_shell(void); diff --git a/firmware/baseband/proc_flash_utility.cpp b/firmware/baseband/proc_flash_utility.cpp index 9de74270..32cbd50c 100644 --- a/firmware/baseband/proc_flash_utility.cpp +++ b/firmware/baseband/proc_flash_utility.cpp @@ -58,6 +58,8 @@ int main() { f_close(&firmware_file); + LPC_RGU->RESET_CTRL[0] = (1 << 0); + while (1) __WFE(); diff --git a/firmware/baseband/sd_over_usb/scsi.c b/firmware/baseband/sd_over_usb/scsi.c index 9484e4ef..c20e0aa0 100644 --- a/firmware/baseband/sd_over_usb/scsi.c +++ b/firmware/baseband/sd_over_usb/scsi.c @@ -22,6 +22,9 @@ #include "scsi.h" #include "diskio.h" +#include +#include +#include volatile bool usb_bulk_block_done = false; @@ -281,6 +284,17 @@ void scsi_command(msd_cbw_t* msd_cbw_data) { case SCSI_CMD_VERIFY_10: status = 0; break; + + case SCSI_CMD_START_STOP_UNIT: + SCU_SFSP2_8 = (SCU_SFSP2_8 & ~(7)) | 4; + struct gpio_t dfu = GPIO(5, 7); + gpio_output(&dfu); + gpio_clear(&dfu); + + delay(50 * 40800); + + RESET_CTRL0 = (1 << 0); + break; } usb_send_csw(msd_cbw_data, status); diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.c b/firmware/baseband/sd_over_usb/sd_over_usb.c index 4b84ca73..f75107af 100644 --- a/firmware/baseband/sd_over_usb/sd_over_usb.c +++ b/firmware/baseband/sd_over_usb/sd_over_usb.c @@ -36,7 +36,7 @@ usb_request_status_t report_max_lun( 1, NULL, NULL); - + } else if (stage == USB_TRANSFER_STAGE_DATA) { usb_transfer_schedule_ack(endpoint->out); scsi_running = true; diff --git a/firmware/tools/copy_external_apps.sh b/firmware/tools/copy_external_apps.sh index 15669dc3..bd807c66 100755 --- a/firmware/tools/copy_external_apps.sh +++ b/firmware/tools/copy_external_apps.sh @@ -26,6 +26,15 @@ try=1 while [ $try -le 180 ] do + if [ -c /dev/ttyACM0 ]; + then + echo "" > /dev/ttyACM0 + sleep 1 + echo "sd_over_usb" > /dev/ttyACM0 + sleep 5 + fi + + if ls /dev/disk/by-id/*Portapack*part1 1> /dev/null 2>&1; then disk=$(ls /dev/disk/by-id/*Portapack*part1) part=$(readlink -f $disk) diff --git a/firmware/tools/enter_mode.sh b/firmware/tools/enter_mode.sh new file mode 100755 index 00000000..fe4b5499 --- /dev/null +++ b/firmware/tools/enter_mode.sh @@ -0,0 +1,35 @@ +#!/bin/bash + +# +# Copyright (C) 2023 Bernd Herzog +# +# This file is part of PortaPack. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2, or (at your option) +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; see the file COPYING. If not, write to +# the Free Software Foundation, Inc., 51 Franklin Street, +# Boston, MA 02110-1301, USA. +# + +# This script will copy compiled external apps to the Portapack. + +mode=$1 + +echo "entering mode $mode" + +if [ -c /dev/ttyACM0 ]; +then + echo "" > /dev/ttyACM0 + sleep 1 + echo "$mode" > /dev/ttyACM0 +fi diff --git a/hackrf-portapack/CMakeLists.txt b/hackrf-portapack/CMakeLists.txt index 15209a76..ffa7c398 100644 --- a/hackrf-portapack/CMakeLists.txt +++ b/hackrf-portapack/CMakeLists.txt @@ -43,7 +43,6 @@ set(SRC_M4 ${PATH_HACKRF_USB}/usb_descriptor.c ${PATH_HACKRF_USB}/usb_device.c ${PATH_HACKRF_USB}/usb_endpoint.c - ${PATH_HACKRF_USB}/usb_api_board_info.c ${PATH_HACKRF_USB}/usb_api_cpld.c ${PATH_HACKRF_USB}/usb_api_m0_state.c ${PATH_HACKRF_USB}/usb_api_register.c @@ -71,6 +70,7 @@ set(SRC_M0 ${PATH_HACKRF_USB}/sgpio_m0.s) if(BOARD STREQUAL "HACKRF_ONE") SET(SRC_M4 ${SRC_M4} + usb_api_board_info.c portapack.c "${PATH_HACKRF_FIRMWARE_COMMON}/ui_portapack.c" ) diff --git a/hackrf-portapack/usb_api_board_info.c b/hackrf-portapack/usb_api_board_info.c new file mode 100644 index 00000000..199c3b3a --- /dev/null +++ b/hackrf-portapack/usb_api_board_info.c @@ -0,0 +1,189 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * Copyright 2013 Benjamin Vernoux + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#include "usb_api_board_info.h" +#include "platform_detect.h" +#include "firmware_info.h" + +#include +#include +#include +#include +#include +#include + +#include +#include + +#ifdef HACKRF_ONE +#include "gpio_lpc.h" +static struct gpio_t gpio_h1r9_clkout_en = GPIO(0, 9); +static struct gpio_t gpio_h1r9_mcu_clk_en = GPIO(0, 8); +static struct gpio_t gpio_h1r9_rx = GPIO(0, 7); +#endif + +usb_request_status_t usb_vendor_request_read_board_id( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + endpoint->buffer[0] = detected_platform(); + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_read_version_string( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + uint8_t length; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + length = (uint8_t)strlen(firmware_info.version_string); + // The USB peripheral doesn't seem to be able to read directly from flash, + // so copy the version string into ram first. + memcpy(&endpoint->buffer, + firmware_info.version_string, + sizeof(firmware_info.version_string)); + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + length, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +static read_partid_serialno_t read_partid_serialno; + +usb_request_status_t usb_vendor_request_read_partid_serialno( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + uint8_t length; + iap_cmd_res_t iap_cmd_res; + + if (stage == USB_TRANSFER_STAGE_SETUP) { + /* Read IAP Part Number Identification */ + iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_PART_ID_NO; + iap_cmd_call(&iap_cmd_res); + if (iap_cmd_res.status_res.status_ret != CMD_SUCCESS) { + return USB_REQUEST_STATUS_STALL; + } + + read_partid_serialno.part_id[0] = iap_cmd_res.status_res.iap_result[0]; + read_partid_serialno.part_id[1] = iap_cmd_res.status_res.iap_result[1]; + + /* Read IAP Serial Number Identification */ + iap_cmd_res.cmd_param.command_code = IAP_CMD_READ_SERIAL_NO; + iap_cmd_call(&iap_cmd_res); + if (iap_cmd_res.status_res.status_ret != CMD_SUCCESS) { + return USB_REQUEST_STATUS_STALL; + } + + read_partid_serialno.serial_no[0] = iap_cmd_res.status_res.iap_result[0]; + read_partid_serialno.serial_no[1] = iap_cmd_res.status_res.iap_result[1]; + read_partid_serialno.serial_no[2] = iap_cmd_res.status_res.iap_result[2]; + read_partid_serialno.serial_no[3] = iap_cmd_res.status_res.iap_result[3]; + + length = (uint8_t)sizeof(read_partid_serialno_t); + usb_transfer_schedule_block( + endpoint->in, + &read_partid_serialno, + length, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_reset( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { +#ifdef HACKRF_ONE + /* + * Set boot pins as inputs so that the bootloader reads them + * correctly after the reset. + */ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_input(&gpio_h1r9_mcu_clk_en); + gpio_input(&gpio_h1r9_clkout_en); + gpio_input(&gpio_h1r9_rx); + } +#endif + usb_transfer_schedule_ack(endpoint->in); + delay(50 * 40800); + + SCU_SFSP2_8 = (SCU_SFSP2_8 & ~(7)) | 4; + struct gpio_t dfu = GPIO(5, 7); + gpio_output(&dfu); + gpio_clear(&dfu); + + wwdt_reset(100000); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_read_board_rev( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + endpoint->buffer[0] = detected_revision(); + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} + +usb_request_status_t usb_vendor_request_read_supported_platform( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) { + if (stage == USB_TRANSFER_STAGE_SETUP) { + uint32_t platform = supported_platform(); + endpoint->buffer[0] = (platform >> 24) & 0xff; + endpoint->buffer[1] = (platform >> 16) & 0xff; + endpoint->buffer[2] = (platform >> 8) & 0xff; + endpoint->buffer[3] = platform & 0xff; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 4, + NULL, + NULL); + usb_transfer_schedule_ack(endpoint->out); + } + return USB_REQUEST_STATUS_OK; +} diff --git a/hackrf-portapack/usb_api_board_info.h b/hackrf-portapack/usb_api_board_info.h new file mode 100644 index 00000000..663d25d8 --- /dev/null +++ b/hackrf-portapack/usb_api_board_info.h @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * Copyright 2013 Benjamin Vernoux + * + * This file is part of HackRF. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; see the file COPYING. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, + * Boston, MA 02110-1301, USA. + */ + +#ifndef __USB_API_BOARD_INFO_H__ +#define __USB_API_BOARD_INFO_H__ + +#include + +#include +#include + +typedef struct { + uint32_t part_id[2]; + uint32_t serial_no[4]; +} read_partid_serialno_t; + +usb_request_status_t usb_vendor_request_read_board_id( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_read_version_string( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_read_partid_serialno( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_reset( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_read_board_rev( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); +usb_request_status_t usb_vendor_request_read_supported_platform( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage); + +#endif /* end of include guard: __USB_API_BOARD_INFO_H__ */