diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 723e2f6e..9e03d737 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -235,6 +235,7 @@ set(CPPSRC apps/ui_encoders.cpp apps/ui_fileman.cpp apps/ui_flash_utility.cpp + apps/ui_sd_over_usb.cpp apps/ui_freqman.cpp apps/ui_jammer.cpp apps/ui_keyfob.cpp diff --git a/firmware/application/apps/ui_sd_over_usb.cpp b/firmware/application/apps/ui_sd_over_usb.cpp new file mode 100644 index 00000000..82da9d34 --- /dev/null +++ b/firmware/application/apps/ui_sd_over_usb.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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 "ui_sd_over_usb.hpp" +#include "portapack_shared_memory.hpp" + +namespace ui { + +SdOverUsbView::SdOverUsbView(NavigationView& nav) : nav_ (nav) { + add_children({ + &labels, + &button_run + }); + + button_run.on_select = [this](Button&) { + 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 }, + bitmap_icon_hackrf, + ui::Color::yellow(), + ui::Color::black() + ); + + sdcDisconnect(&SDCD1); + sdcStop(&SDCD1); + + portapack::shutdown(true); + m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false); + m0_halt(); + /* will not return*/ + }; +} + +void SdOverUsbView::focus() { + button_run.focus(); +} + +} /* namespace ui */ diff --git a/firmware/application/apps/ui_sd_over_usb.hpp b/firmware/application/apps/ui_sd_over_usb.hpp new file mode 100644 index 00000000..9df6421a --- /dev/null +++ b/firmware/application/apps/ui_sd_over_usb.hpp @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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. + */ + +#ifndef __UI_SD_OVER_USB_H__ +#define __UI_SD_OVER_USB_H__ + +#include "ui_widget.hpp" +#include "ui_navigation.hpp" +#include "string_format.hpp" +#include "ff.h" +#include "baseband_api.hpp" +#include "core_control.hpp" + +#include + +namespace ui { + +class SdOverUsbView : public View { +public: + SdOverUsbView(NavigationView& nav); + + void focus() override; + + std::string title() const override { return "Flash Utility"; }; + +private: + NavigationView& nav_; + + Labels labels { + { { 3 * 8, 2 * 16 }, "Click Run to start the", Color::white() }, + { { 3 * 8, 3 * 16 }, "USB Mass Storage Mode.", Color::white() }, + { { 3 * 8, 5 * 16 }, "It can take up to 20s", Color::white() }, + { { 3 * 8, 6 * 16 }, "for the drive to be", Color::white() }, + { { 3 * 8, 7 * 16 }, "available.", Color::white() }, + }; + + Button button_run { + { 9 * 8, 15 * 16, 12 * 8, 3 * 16 }, + "Run" + }; +}; + +} /* namespace ui */ + +#endif /*__UI_SD_OVER_USB_H__*/ diff --git a/firmware/application/portapack.cpp b/firmware/application/portapack.cpp index edf064e1..c7237171 100644 --- a/firmware/application/portapack.cpp +++ b/firmware/application/portapack.cpp @@ -515,11 +515,13 @@ bool init() { return true; } -void shutdown() { +void shutdown(const bool leave_screen_on) { gpdma::controller.disable(); - backlight()->off(); - display.shutdown(); + if (!leave_screen_on) { + backlight()->off(); + display.shutdown(); + } radio::disable(); audio::shutdown(); diff --git a/firmware/application/portapack.hpp b/firmware/application/portapack.hpp index a34d93a6..3fbc4b8d 100644 --- a/firmware/application/portapack.hpp +++ b/firmware/application/portapack.hpp @@ -63,7 +63,7 @@ void set_antenna_bias(const bool v); bool get_antenna_bias(); bool init(); -void shutdown(); +void shutdown(const bool leave_screen_on = false); Backlight* backlight(); diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 8a778d49..179e734f 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -71,6 +71,7 @@ #include "ui_view_wav.hpp" #include "ui_whipcalc.hpp" #include "ui_flash_utility.hpp" +#include "ui_sd_over_usb.hpp" //#include "acars_app.hpp" #include "ais_app.hpp" @@ -614,8 +615,8 @@ UtilitiesMenuView::UtilitiesMenuView(NavigationView& nav) { { "Antenna length", ui::Color::green(), &bitmap_icon_tools_antenna, [&nav](){ nav.push(); } }, { "Wipe SD card", ui::Color::red(), &bitmap_icon_tools_wipesd, [&nav](){ nav.push(); } }, - { "Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav](){ nav.push(); } }, - + { "Flash Utility", ui::Color::red(), &bitmap_icon_temperature, [&nav](){ nav.push(); } }, + { "SD over USB", ui::Color::yellow(), &bitmap_icon_hackrf, [&nav](){ nav.push(); } }, }); set_max_rows(2); // allow wider buttons } diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index 76065cdd..0d6e6947 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -187,7 +187,7 @@ set(AOPT) set(TOPT "-mthumb -DTHUMB") # Define C warning options here -set(CWARN "-Wall -Wextra -Wstrict-prototypes") +set(CWARN "-Wall -Wextra") # Define C++ warning options here set(CPPWARN "-Wall -Wextra") @@ -515,6 +515,53 @@ set(MODE_CPPSRC ) DeclareTargets(PFUT flash_utility) +### SD over USB + +set(MODE_INCDIR + ${HACKRF_PATH}/firmware + ${HACKRF_PATH}/firmware/common + ${HACKRF_PATH}/firmware/libopencm3/include +) +set(MODE_CPPSRC + sd_over_usb/proc_sd_over_usb.cpp + + sd_over_usb/scsi.c + sd_over_usb/diskio.c + sd_over_usb/sd_over_usb.c + sd_over_usb/usb_descriptor.c + sd_over_usb/hackrf_core.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/hackrf_usb/usb_endpoint.c + ${HACKRF_PATH}/firmware/common/usb_request.c + ${HACKRF_PATH}/firmware/common/usb_standard_request.c + ${HACKRF_PATH}/firmware/common/platform_detect.c + ${HACKRF_PATH}/firmware/common/gpio_lpc.c + ${HACKRF_PATH}/firmware/common/firmware_info.c + ${HACKRF_PATH}/firmware/common/si5351c.c + ${HACKRF_PATH}/firmware/common/i2c_bus.c + ${HACKRF_PATH}/firmware/common/mixer.c + ${HACKRF_PATH}/firmware/common/clkin.c + ${HACKRF_PATH}/firmware/common/spi_bus.c + ${HACKRF_PATH}/firmware/common/sgpio.c + ${HACKRF_PATH}/firmware/common/rf_path.c + ${HACKRF_PATH}/firmware/common/i2c_lpc.c + ${HACKRF_PATH}/firmware/common/spi_ssp.c + ${HACKRF_PATH}/firmware/common/rffc5071_spi.c + ${HACKRF_PATH}/firmware/common/rffc5071.c + ${HACKRF_PATH}/firmware/common/clkin.c + ${HACKRF_PATH}/firmware/common/gpdma.c + + ${HACKRF_PATH}/firmware/libopencm3/lib/cm3/nvic.c + ${HACKRF_PATH}/firmware/libopencm3/lib/cm3/sync.c + ${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/scu.c + ${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/timer.c + ${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/i2c.c +) +DeclareTargets(PUSB sd_over_usb) + ### HackRF "factory" firmware add_custom_command( diff --git a/firmware/baseband/halconf.h b/firmware/baseband/halconf.h index d79a158e..3f24c699 100755 --- a/firmware/baseband/halconf.h +++ b/firmware/baseband/halconf.h @@ -119,7 +119,7 @@ * @brief Enables the SDC subsystem. */ #if !defined(HAL_USE_SDC) || defined(__DOXYGEN__) -#ifdef BASEBAND_flash_utility +#if defined(BASEBAND_flash_utility) || defined(BASEBAND_sd_over_usb) #define HAL_USE_SDC TRUE #else #define HAL_USE_SDC FALSE diff --git a/firmware/baseband/sd_over_usb/diskio.c b/firmware/baseband/sd_over_usb/diskio.c new file mode 100644 index 00000000..e3d5f850 --- /dev/null +++ b/firmware/baseband/sd_over_usb/diskio.c @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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 "diskio.h" + +#include "ch.h" +#include "hal.h" + +uint32_t get_capacity(void) { + return mmcsdGetCardCapacity(&SDCD1); +} + +bool_t read_block(uint32_t startblk, uint8_t *buf, uint32_t n) { + return sdcRead(&SDCD1, startblk, buf, n); +} + +bool_t write_block(uint32_t startblk, uint8_t *buf, uint32_t n) { + return sdcWrite(&SDCD1, startblk, buf, n); +} diff --git a/firmware/baseband/sd_over_usb/diskio.h b/firmware/baseband/sd_over_usb/diskio.h new file mode 100644 index 00000000..2dde43e7 --- /dev/null +++ b/firmware/baseband/sd_over_usb/diskio.h @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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. + */ + +#ifndef __DISKIO_H__ +#define __DISKIO_H__ + +#include +#include + +uint32_t get_capacity(void); +bool_t read_block(uint32_t startblk, uint8_t *buf, uint32_t n); +bool_t write_block(uint32_t startblk, uint8_t *buf, uint32_t n); + +#endif /* __DISKIO_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/hackrf_core.c b/firmware/baseband/sd_over_usb/hackrf_core.c new file mode 100644 index 00000000..bf20577c --- /dev/null +++ b/firmware/baseband/sd_over_usb/hackrf_core.c @@ -0,0 +1,1084 @@ +/* + * 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 "hackrf_core.h" +#include "hackrf_ui.h" +#include "sgpio.h" +#include "si5351c.h" +#include "spi_ssp.h" +#include "max283x.h" +#include "max5864.h" +#include "max5864_target.h" +#include "w25q80bv.h" +#include "w25q80bv_target.h" +#include "i2c_bus.h" +#include "i2c_lpc.h" +#include "cpld_jtag.h" +#include "platform_detect.h" +#include "clkin.h" +#include +#include +#include +#include + +#ifdef HACKRF_ONE + #include "portapack.h" +#endif + +#include "gpio_lpc.h" + +/* GPIO Output PinMux */ +static struct gpio_t gpio_led[] = { + GPIO(2, 1), + GPIO(2, 2), + GPIO(2, 8), +#ifdef RAD1O + GPIO(5, 26), +#endif +}; + +// clang-format off +static struct gpio_t gpio_1v8_enable = GPIO(3, 6); + +/* MAX283x GPIO (XCVR_CTL) PinMux */ +static struct gpio_t gpio_max283x_select = GPIO(0, 15); + +/* MAX5864 SPI chip select (AD_CS) GPIO PinMux */ +static struct gpio_t gpio_max5864_select = GPIO(2, 7); + +/* RFFC5071 GPIO serial interface PinMux */ +// #ifdef RAD1O +// static struct gpio_t gpio_rffc5072_select = GPIO(2, 13); +// static struct gpio_t gpio_rffc5072_clock = GPIO(5, 6); +// static struct gpio_t gpio_rffc5072_data = GPIO(3, 3); +// static struct gpio_t gpio_rffc5072_reset = GPIO(2, 14); +// #endif + +/* RF supply (VAA) control */ +#ifdef HACKRF_ONE +static struct gpio_t gpio_vaa_disable = GPIO(2, 9); +#endif +#ifdef RAD1O +static struct gpio_t gpio_vaa_enable = GPIO(2, 9); +#endif + +static struct gpio_t gpio_w25q80bv_hold = GPIO(1, 14); +static struct gpio_t gpio_w25q80bv_wp = GPIO(1, 15); +static struct gpio_t gpio_w25q80bv_select = GPIO(5, 11); + +/* RF switch control */ +#ifdef HACKRF_ONE +static struct gpio_t gpio_hp = GPIO(2, 0); +static struct gpio_t gpio_lp = GPIO(2, 10); +static struct gpio_t gpio_tx_mix_bp = GPIO(2, 11); +static struct gpio_t gpio_no_mix_bypass = GPIO(1, 0); +static struct gpio_t gpio_rx_mix_bp = GPIO(2, 12); +static struct gpio_t gpio_tx_amp = GPIO(2, 15); +static struct gpio_t gpio_tx = GPIO(5, 15); +static struct gpio_t gpio_mix_bypass = GPIO(5, 16); +static struct gpio_t gpio_rx = GPIO(5, 5); +static struct gpio_t gpio_no_tx_amp_pwr = GPIO(3, 5); +static struct gpio_t gpio_amp_bypass = GPIO(0, 14); +static struct gpio_t gpio_rx_amp = GPIO(1, 11); +static struct gpio_t gpio_no_rx_amp_pwr = GPIO(1, 12); +#endif +#ifdef RAD1O +static struct gpio_t gpio_tx_rx_n = GPIO(1, 11); +static struct gpio_t gpio_tx_rx = GPIO(0, 14); +static struct gpio_t gpio_by_mix = GPIO(1, 12); +static struct gpio_t gpio_by_mix_n = GPIO(2, 10); +static struct gpio_t gpio_by_amp = GPIO(1, 0); +static struct gpio_t gpio_by_amp_n = GPIO(5, 5); +static struct gpio_t gpio_mixer_en = GPIO(5, 16); +static struct gpio_t gpio_low_high_filt = GPIO(2, 11); +static struct gpio_t gpio_low_high_filt_n = GPIO(2, 12); +static struct gpio_t gpio_tx_amp = GPIO(2, 15); +static struct gpio_t gpio_rx_lna = GPIO(5, 15); +#endif + +/* CPLD JTAG interface GPIO pins */ +static struct gpio_t gpio_cpld_tdo = GPIO(5, 18); +static struct gpio_t gpio_cpld_tck = GPIO(3, 0); +#if (defined HACKRF_ONE || defined RAD1O) +static struct gpio_t gpio_cpld_tms = GPIO(3, 4); +static struct gpio_t gpio_cpld_tdi = GPIO(3, 1); +#else +static struct gpio_t gpio_cpld_tms = GPIO(3, 1); +static struct gpio_t gpio_cpld_tdi = GPIO(3, 4); +#endif + +#ifdef HACKRF_ONE +static struct gpio_t gpio_cpld_pp_tms = GPIO(1, 1); +static struct gpio_t gpio_cpld_pp_tdo = GPIO(1, 8); +#endif + +/* other CPLD interface GPIO pins */ +static struct gpio_t gpio_hw_sync_enable = GPIO(5, 12); +static struct gpio_t gpio_q_invert = GPIO(0, 13); + +/* HackRF One r9 */ +#ifdef HACKRF_ONE +static struct gpio_t gpio_h1r9_rx = GPIO(0, 7); +static struct gpio_t gpio_h1r9_1v8_enable = GPIO(2, 9); +static struct gpio_t gpio_h1r9_vaa_disable = GPIO(3, 6); +static struct gpio_t gpio_h1r9_hw_sync_enable = GPIO(5, 5); +#endif +// clang-format on + +i2c_bus_t i2c0 = { + .obj = (void*) I2C0_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +i2c_bus_t i2c1 = { + .obj = (void*) I2C1_BASE, + .start = i2c_lpc_start, + .stop = i2c_lpc_stop, + .transfer = i2c_lpc_transfer, +}; + +// const i2c_lpc_config_t i2c_config_si5351c_slow_clock = { +// .duty_cycle_count = 15, +// }; + +const i2c_lpc_config_t i2c_config_si5351c_fast_clock = { + .duty_cycle_count = 255, +}; + +si5351c_driver_t clock_gen = { + .bus = &i2c0, + .i2c_address = 0x60, +}; + +const ssp_config_t ssp_config_max283x = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_16BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, + .gpio_select = &gpio_max283x_select, +}; + +const ssp_config_t ssp_config_max5864 = { + /* FIXME speed up once everything is working reliably */ + /* + // Freq About 0.0498MHz / 49.8KHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + const uint8_t serial_clock_rate = 32; + const uint8_t clock_prescale_rate = 128; + */ + // Freq About 4.857MHz => Freq = PCLK / (CPSDVSR * [SCR+1]) with PCLK=PLL1=204MHz + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 21, + .clock_prescale_rate = 2, + .gpio_select = &gpio_max5864_select, +}; + +spi_bus_t spi_bus_ssp1 = { + .obj = (void*) SSP1_BASE, + .config = &ssp_config_max5864, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +max283x_driver_t max283x = {}; + +max5864_driver_t max5864 = { + .bus = &spi_bus_ssp1, + .target_init = max5864_target_init, +}; + +const ssp_config_t ssp_config_w25q80bv = { + .data_bits = SSP_DATA_8BITS, + .serial_clock_rate = 2, + .clock_prescale_rate = 2, + .gpio_select = &gpio_w25q80bv_select, +}; + +spi_bus_t spi_bus_ssp0 = { + .obj = (void*) SSP0_BASE, + .config = &ssp_config_w25q80bv, + .start = spi_ssp_start, + .stop = spi_ssp_stop, + .transfer = spi_ssp_transfer, + .transfer_gather = spi_ssp_transfer_gather, +}; + +w25q80bv_driver_t spi_flash = { + .bus = &spi_bus_ssp0, + .gpio_hold = &gpio_w25q80bv_hold, + .gpio_wp = &gpio_w25q80bv_wp, + .target_init = w25q80bv_target_init, +}; + +sgpio_config_t sgpio_config = { + .gpio_q_invert = &gpio_q_invert, + .gpio_hw_sync_enable = &gpio_hw_sync_enable, + .slice_mode_multislice = true, +}; + +rf_path_t rf_path = { + .switchctrl = 0, +#ifdef HACKRF_ONE + .gpio_hp = &gpio_hp, + .gpio_lp = &gpio_lp, + .gpio_tx_mix_bp = &gpio_tx_mix_bp, + .gpio_no_mix_bypass = &gpio_no_mix_bypass, + .gpio_rx_mix_bp = &gpio_rx_mix_bp, + .gpio_tx_amp = &gpio_tx_amp, + .gpio_tx = &gpio_tx, + .gpio_mix_bypass = &gpio_mix_bypass, + .gpio_rx = &gpio_rx, + .gpio_no_tx_amp_pwr = &gpio_no_tx_amp_pwr, + .gpio_amp_bypass = &gpio_amp_bypass, + .gpio_rx_amp = &gpio_rx_amp, + .gpio_no_rx_amp_pwr = &gpio_no_rx_amp_pwr, +#endif +#ifdef RAD1O + .gpio_tx_rx_n = &gpio_tx_rx_n, + .gpio_tx_rx = &gpio_tx_rx, + .gpio_by_mix = &gpio_by_mix, + .gpio_by_mix_n = &gpio_by_mix_n, + .gpio_by_amp = &gpio_by_amp, + .gpio_by_amp_n = &gpio_by_amp_n, + .gpio_mixer_en = &gpio_mixer_en, + .gpio_low_high_filt = &gpio_low_high_filt, + .gpio_low_high_filt_n = &gpio_low_high_filt_n, + .gpio_tx_amp = &gpio_tx_amp, + .gpio_rx_lna = &gpio_rx_lna, +#endif +}; + +jtag_gpio_t jtag_gpio_cpld = { + .gpio_tms = &gpio_cpld_tms, + .gpio_tck = &gpio_cpld_tck, + .gpio_tdi = &gpio_cpld_tdi, + .gpio_tdo = &gpio_cpld_tdo, +#ifdef HACKRF_ONE + .gpio_pp_tms = &gpio_cpld_pp_tms, + .gpio_pp_tdo = &gpio_cpld_pp_tdo, +#endif +}; + +jtag_t jtag_cpld = { + .gpio = &jtag_gpio_cpld, +}; + +void delay(uint32_t duration) +{ + uint32_t i; + + for (i = 0; i < duration; i++) { + __asm__("nop"); + } +} + +void delay_us_at_mhz(uint32_t us, uint32_t mhz) +{ + // The loop below takes 3 cycles per iteration. + uint32_t loop_iterations = (us * mhz) / 3; + asm volatile("start%=:\n" + " subs %[ITERATIONS], #1\n" // 1 cycle + " bpl start%=\n" // 2 cycles + : + : [ITERATIONS] "r"(loop_iterations)); +} + +/* GCD algo from wikipedia */ +/* http://en.wikipedia.org/wiki/Greatest_common_divisor */ +static uint32_t gcd(uint32_t u, uint32_t v) +{ + int s; + + if (!u || !v) { + return u | v; + } + + for (s = 0; !((u | v) & 1); s++) { + u >>= 1; + v >>= 1; + } + + while (!(u & 1)) { + u >>= 1; + } + + do { + while (!(v & 1)) { + v >>= 1; + } + + if (u > v) { + uint32_t t; + t = v; + v = u; + u = t; + } + + v = v - u; + } while (v); + + return u << s; +} + +bool sample_rate_frac_set(uint32_t rate_num, uint32_t rate_denom) +{ + const uint64_t VCO_FREQ = 800 * 1000 * 1000; /* 800 MHz */ + uint32_t MSx_P1, MSx_P2, MSx_P3; + uint32_t a, b, c; + uint32_t rem; + + hackrf_ui()->set_sample_rate(rate_num / 2); + + /* Find best config */ + a = (VCO_FREQ * rate_denom) / rate_num; + + rem = (VCO_FREQ * rate_denom) - (a * rate_num); + + if (!rem) { + /* Integer mode */ + b = 0; + c = 1; + } else { + /* Fractional */ + uint32_t g = gcd(rem, rate_num); + rem /= g; + rate_num /= g; + + if (rate_num < (1 << 20)) { + /* Perfect match */ + b = rem; + c = rate_num; + } else { + /* Approximate */ + c = (1 << 20) - 1; + b = ((uint64_t) c * (uint64_t) rem) / rate_num; + + g = gcd(b, c); + b /= g; + c /= g; + } + } + + bool streaming = sgpio_cpld_stream_is_enabled(&sgpio_config); + + if (streaming) { + sgpio_cpld_stream_disable(&sgpio_config); + } + + /* Can we enable integer mode ? */ + if (a & 0x1 || b) { + si5351c_set_int_mode(&clock_gen, 0, 0); + } else { + si5351c_set_int_mode(&clock_gen, 0, 1); + } + + /* Final MS values */ + MSx_P1 = 128 * a + (128 * b / c) - 512; + MSx_P2 = (128 * b) % c; + MSx_P3 = c; + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, MSx_P1, MSx_P2, MSx_P3, 0); + } else { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, MSx_P1, MSx_P2, MSx_P3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth(&clock_gen, 1, 0, 0, 0, 0); //p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth(&clock_gen, 2, 0, 0, 0, 0); //p1 doesn't matter + } + + if (streaming) { + sgpio_cpld_stream_enable(&sgpio_config); + } + + return true; +} + +bool sample_rate_set(const uint32_t sample_rate_hz) +{ + uint32_t p1 = 4608; + uint32_t p2 = 0; + uint32_t p3 = 0; + + switch (sample_rate_hz) { + case 8000000: + p1 = SI_INTDIV(50); // 800MHz / 50 = 16 MHz (SGPIO), 8 MHz (codec) + break; + + case 9216000: + // 43.40277777777778: a = 43; b = 29; c = 72 + p1 = 5043; + p2 = 40; + p3 = 72; + break; + + case 10000000: + p1 = SI_INTDIV(40); // 800MHz / 40 = 20 MHz (SGPIO), 10 MHz (codec) + break; + + case 12288000: + // 32.552083333333336: a = 32; b = 159; c = 288 + p1 = 3654; + p2 = 192; + p3 = 288; + break; + + case 12500000: + p1 = SI_INTDIV(32); // 800MHz / 32 = 25 MHz (SGPIO), 12.5 MHz (codec) + break; + + case 16000000: + p1 = SI_INTDIV(25); // 800MHz / 25 = 32 MHz (SGPIO), 16 MHz (codec) + break; + + case 18432000: + // 21.70138888889: a = 21; b = 101; c = 144 + p1 = 2265; + p2 = 112; + p3 = 144; + break; + + case 20000000: + p1 = SI_INTDIV(20); // 800MHz / 20 = 40 MHz (SGPIO), 20 MHz (codec) + break; + + default: + return false; + } + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* + * On HackRF One r9 all sample clocks are externally derived + * from MS1/CLK1 operating at twice the sample rate. + */ + si5351c_configure_multisynth(&clock_gen, 1, p1, p2, p3, 0); + } else { + /* + * On other platforms the clock generator produces three + * different sample clocks, all derived from multisynth 0. + */ + /* MS0/CLK0 is the source for the MAX5864/CPLD (CODEC_CLK). */ + si5351c_configure_multisynth(&clock_gen, 0, p1, p2, p3, 1); + + /* MS0/CLK1 is the source for the CPLD (CODEC_X2_CLK). */ + si5351c_configure_multisynth( + &clock_gen, + 1, + p1, + 0, + 1, + 0); //p1 doesn't matter + + /* MS0/CLK2 is the source for SGPIO (CODEC_X2_CLK) */ + si5351c_configure_multisynth( + &clock_gen, + 2, + p1, + 0, + 1, + 0); //p1 doesn't matter + } + + return true; +} + +bool baseband_filter_bandwidth_set(const uint32_t bandwidth_hz) +{ + uint32_t bandwidth_hz_real; + bandwidth_hz_real = max283x_set_lpf_bandwidth(&max283x, bandwidth_hz); + + if (bandwidth_hz_real) { + hackrf_ui()->set_filter_bw(bandwidth_hz_real); + } + + return bandwidth_hz_real != 0; +} + +/* +Configure PLL1 (Main MCU Clock) to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. +This function shall be called after cpu_clock_init(). +*/ +static void cpu_clock_pll1_max_speed(void) +{ + uint32_t reg_val; + + /* This function implements the sequence recommended in: + * UM10503 Rev 2.4 (Aug 2018), section 13.2.1.1, page 167. */ + + /* 1. Select the IRC as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_IRC) | CGU_BASE_M4_CLK_AUTOBLOCK(1); + CGU_BASE_M4_CLK = reg_val; + + /* 2. Enable the crystal oscillator. */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK; + + /* 3. Wait 250us. */ + delay_us_at_mhz(250, 12); + + /* 4. Set the AUTOBLOCK bit. */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_AUTOBLOCK(1); + + /* 5. Reconfigure PLL1 to produce the final output frequency, with the + * crystal oscillator as clock source. */ + reg_val = CGU_PLL1_CTRL; + // clang-format off + reg_val &= ~( CGU_PLL1_CTRL_CLK_SEL_MASK | + CGU_PLL1_CTRL_PD_MASK | + CGU_PLL1_CTRL_FBSEL_MASK | + CGU_PLL1_CTRL_BYPASS_MASK | + CGU_PLL1_CTRL_DIRECT_MASK | + CGU_PLL1_CTRL_PSEL_MASK | + CGU_PLL1_CTRL_MSEL_MASK | + CGU_PLL1_CTRL_NSEL_MASK ); + /* Set PLL1 up to 12MHz * 17 = 204MHz. + * Direct mode: FCLKOUT = FCCO = M*(FCLKIN/N) */ + reg_val |= CGU_PLL1_CTRL_CLK_SEL(CGU_SRC_XTAL) | + CGU_PLL1_CTRL_PSEL(0) | + CGU_PLL1_CTRL_NSEL(0) | + CGU_PLL1_CTRL_MSEL(16) | + CGU_PLL1_CTRL_FBSEL(0) | + CGU_PLL1_CTRL_DIRECT(1); + // clang-format on + CGU_PLL1_CTRL = reg_val; + + /* 6. Wait for PLL1 to lock. */ + while (!(CGU_PLL1_STAT & CGU_PLL1_STAT_LOCK_MASK)) {} + + /* 7. Set the PLL1 P-divider to divide by 2 (DIRECT=0, PSEL=0). */ + CGU_PLL1_CTRL &= ~CGU_PLL1_CTRL_DIRECT_MASK; + + /* 8. Select PLL1 as BASE_M4_CLK source. */ + reg_val = CGU_BASE_M4_CLK; + reg_val &= ~CGU_BASE_M4_CLK_CLK_SEL_MASK; + reg_val |= CGU_BASE_M4_CLK_CLK_SEL(CGU_SRC_PLL1); + CGU_BASE_M4_CLK = reg_val; + + /* 9. Wait 50us. */ + delay_us_at_mhz(50, 102); + + /* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */ + CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK; +} + +/* clock startup for LPC4320 configure PLL1 to max speed (204MHz). +Note: PLL1 clock is used by M4/M0 core, Peripheral, APB1. */ +void cpu_clock_init(void) +{ + /* use IRC as clock source for APB1 (including I2C0) */ + CGU_BASE_APB1_CLK = CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_IRC); + + /* use IRC as clock source for APB3 */ + CGU_BASE_APB3_CLK = CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_IRC); + + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + si5351c_init(&clock_gen); + si5351c_disable_all_outputs(&clock_gen); + si5351c_disable_oeb_pin_control(&clock_gen); + si5351c_power_down_all_clocks(&clock_gen); + si5351c_set_crystal_configuration(&clock_gen); + si5351c_enable_xo_and_ms_fanout(&clock_gen); + si5351c_configure_pll_sources(&clock_gen); + si5351c_configure_pll_multisynth(&clock_gen); + + /* + * Clocks on HackRF One r9: + * CLK0 -> MAX5864/CPLD/SGPIO (sample clocks) + * CLK1 -> RFFC5072/MAX2839 + * CLK2 -> External Clock Output/LPC43xx (power down at boot) + * + * Clocks on other platforms: + * CLK0 -> MAX5864/CPLD + * CLK1 -> CPLD + * CLK2 -> SGPIO + * CLK3 -> External Clock Output (power down at boot) + * CLK4 -> RFFC5072 (MAX2837 on rad1o) + * CLK5 -> MAX2837 (MAX2871 on rad1o) + * CLK6 -> none + * CLK7 -> LPC43xx (uses a 12MHz crystal by default) + */ + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + /* MS0/CLK0 is the reference for both RFFC5071 and MAX2839. */ + si5351c_configure_multisynth( + &clock_gen, + 0, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } else { + /* MS4/CLK4 is the source for the RFFC5071 mixer (MAX2837 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 4, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + /* MS5/CLK5 is the source for the MAX2837 clock input (MAX2871 on rad1o). */ + si5351c_configure_multisynth( + &clock_gen, + 5, + 20 * 128 - 512, + 0, + 1, + 0); /* 800/20 = 40MHz */ + } + + /* MS6/CLK6 is unused. */ + /* MS7/CLK7 is unused. */ + + /* Set to 10 MHz, the common rate between Jawbreaker and HackRF One. */ + sample_rate_set(10000000); + + si5351c_set_clock_source(&clock_gen, PLL_SOURCE_XTAL); + // soft reset + si5351c_reset_pll(&clock_gen); + si5351c_enable_clock_outputs(&clock_gen); + + //FIXME disable I2C + /* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */ + i2c_bus_start(clock_gen.bus, &i2c_config_si5351c_fast_clock); + + /* + * 12MHz clock is entering LPC XTAL1/OSC input now. + * On HackRF One and Jawbreaker, there is a 12 MHz crystal at the LPC. + * Set up PLL1 to run from XTAL1 input. + */ + + //FIXME a lot of the details here should be in a CGU driver + + /* set xtal oscillator to low frequency mode */ + CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_HF_MASK; + + cpu_clock_pll1_max_speed(); + + /* use XTAL_OSC as clock source for APB1 */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for APB3 */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_XTAL); + + /* use XTAL_OSC as clock source for PLL0USB */ + CGU_PLL0USB_CTRL = CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_AUTOBLOCK(1) | + CGU_PLL0USB_CTRL_CLK_SEL(CGU_SRC_XTAL); + while (CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK) {} + + /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */ + /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */ + CGU_PLL0USB_MDIV = 0x06167FFA; + CGU_PLL0USB_NP_DIV = 0x00302062; + CGU_PLL0USB_CTRL |= + (CGU_PLL0USB_CTRL_PD(1) | CGU_PLL0USB_CTRL_DIRECTI(1) | + CGU_PLL0USB_CTRL_DIRECTO(1) | CGU_PLL0USB_CTRL_CLKEN(1)); + + /* power on PLL0USB and wait until stable */ + CGU_PLL0USB_CTRL &= ~CGU_PLL0USB_CTRL_PD_MASK; + while (!(CGU_PLL0USB_STAT & CGU_PLL0USB_STAT_LOCK_MASK)) {} + + /* use PLL0USB as clock source for USB0 */ + CGU_BASE_USB0_CLK = CGU_BASE_USB0_CLK_AUTOBLOCK(1) | + CGU_BASE_USB0_CLK_CLK_SEL(CGU_SRC_PLL0USB); + + /* Switch peripheral clock over to use PLL1 (204MHz) */ + CGU_BASE_PERIPH_CLK = CGU_BASE_PERIPH_CLK_AUTOBLOCK(1) | + CGU_BASE_PERIPH_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB1 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB1_CLK = + CGU_BASE_APB1_CLK_AUTOBLOCK(1) | CGU_BASE_APB1_CLK_CLK_SEL(CGU_SRC_PLL1); + + /* Switch APB3 clock over to use PLL1 (204MHz) */ + CGU_BASE_APB3_CLK = + CGU_BASE_APB3_CLK_AUTOBLOCK(1) | CGU_BASE_APB3_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP0_CLK = + CGU_BASE_SSP0_CLK_AUTOBLOCK(1) | CGU_BASE_SSP0_CLK_CLK_SEL(CGU_SRC_PLL1); + + CGU_BASE_SSP1_CLK = + CGU_BASE_SSP1_CLK_AUTOBLOCK(1) | CGU_BASE_SSP1_CLK_CLK_SEL(CGU_SRC_PLL1); + +#if (defined JAWBREAKER || defined HACKRF_ONE) + /* Disable unused clocks */ + /* Start with PLLs */ + CGU_PLL0AUDIO_CTRL = CGU_PLL0AUDIO_CTRL_PD(1); + + /* Dividers */ + CGU_IDIVA_CTRL = CGU_IDIVA_CTRL_PD(1); + CGU_IDIVB_CTRL = CGU_IDIVB_CTRL_PD(1); + CGU_IDIVC_CTRL = CGU_IDIVC_CTRL_PD(1); + CGU_IDIVD_CTRL = CGU_IDIVD_CTRL_PD(1); + CGU_IDIVE_CTRL = CGU_IDIVE_CTRL_PD(1); + + /* Base clocks */ + CGU_BASE_SPIFI_CLK = CGU_BASE_SPIFI_CLK_PD(1); /* SPIFI is only used at boot */ + CGU_BASE_USB1_CLK = CGU_BASE_USB1_CLK_PD(1); /* USB1 is not exposed on HackRF */ + CGU_BASE_PHY_RX_CLK = CGU_BASE_PHY_RX_CLK_PD(1); + CGU_BASE_PHY_TX_CLK = CGU_BASE_PHY_TX_CLK_PD(1); + CGU_BASE_LCD_CLK = CGU_BASE_LCD_CLK_PD(1); + CGU_BASE_VADC_CLK = CGU_BASE_VADC_CLK_PD(1); + //CGU_BASE_SDIO_CLK = CGU_BASE_SDIO_CLK_PD(1); // << BREAKS + CGU_BASE_UART0_CLK = CGU_BASE_UART0_CLK_PD(1); + CGU_BASE_UART1_CLK = CGU_BASE_UART1_CLK_PD(1); + CGU_BASE_UART2_CLK = CGU_BASE_UART2_CLK_PD(1); + CGU_BASE_UART3_CLK = CGU_BASE_UART3_CLK_PD(1); + CGU_BASE_OUT_CLK = CGU_BASE_OUT_CLK_PD(1); + CGU_BASE_AUDIO_CLK = CGU_BASE_AUDIO_CLK_PD(1); + CGU_BASE_CGU_OUT0_CLK = CGU_BASE_CGU_OUT0_CLK_PD(1); + CGU_BASE_CGU_OUT1_CLK = CGU_BASE_CGU_OUT1_CLK_PD(1); + + /* Disable unused peripheral clocks */ + CCU1_CLK_APB1_CAN1_CFG = 0; + CCU1_CLK_APB1_I2S_CFG = 0; + CCU1_CLK_APB1_MOTOCONPWM_CFG = 0; + CCU1_CLK_APB3_ADC0_CFG = 0; + CCU1_CLK_APB3_ADC1_CFG = 0; + CCU1_CLK_APB3_CAN0_CFG = 0; + CCU1_CLK_APB3_DAC_CFG = 0; + //CCU1_CLK_M4_DMA_CFG = 0; + CCU1_CLK_M4_EMC_CFG = 0; + CCU1_CLK_M4_EMCDIV_CFG = 0; + CCU1_CLK_M4_ETHERNET_CFG = 0; + CCU1_CLK_M4_LCD_CFG = 0; + CCU1_CLK_M4_QEI_CFG = 0; + CCU1_CLK_M4_RITIMER_CFG = 0; + // CCU1_CLK_M4_SCT_CFG = 0; + //CCU1_CLK_M4_SDIO_CFG = 0; // << BREAKS + CCU1_CLK_M4_SPIFI_CFG = 0; + CCU1_CLK_M4_TIMER0_CFG = 0; + //CCU1_CLK_M4_TIMER1_CFG = 0; + //CCU1_CLK_M4_TIMER2_CFG = 0; + CCU1_CLK_M4_TIMER3_CFG = 0; + CCU1_CLK_M4_UART1_CFG = 0; + CCU1_CLK_M4_USART0_CFG = 0; + CCU1_CLK_M4_USART2_CFG = 0; + CCU1_CLK_M4_USART3_CFG = 0; + CCU1_CLK_M4_USB1_CFG = 0; + CCU1_CLK_M4_VADC_CFG = 0; + // CCU1_CLK_SPIFI_CFG = 0; + // CCU1_CLK_USB1_CFG = 0; + // CCU1_CLK_VADC_CFG = 0; + // CCU2_CLK_APB0_UART1_CFG = 0; + // CCU2_CLK_APB0_USART0_CFG = 0; + // CCU2_CLK_APB2_USART2_CFG = 0; + // CCU2_CLK_APB2_USART3_CFG = 0; + // CCU2_CLK_APLL_CFG = 0; + // CCU2_CLK_SDIO_CFG = 0; +#endif + + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + clkin_detect_init(); + } +} + +clock_source_t activate_best_clock_source(void) +{ +#ifdef HACKRF_ONE + /* Ensure PortaPack reference oscillator is off while checking for external clock input. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(false); + } +#endif + + clock_source_t source = CLOCK_SOURCE_HACKRF; + + /* Check for external clock input. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_EXTERNAL; + } else { +#ifdef HACKRF_ONE + /* Enable PortaPack reference oscillator (if present), and check for valid clock. */ + if (portapack_reference_oscillator && portapack()) { + portapack_reference_oscillator(true); + delay(510000); /* loop iterations @ 204MHz for >10ms for oscillator to enable. */ + if (si5351c_clkin_signal_valid(&clock_gen)) { + source = CLOCK_SOURCE_PORTAPACK; + } else { + portapack_reference_oscillator(false); + } + } +#endif + /* No external or PortaPack clock was found. Use HackRF Si5351C crystal. */ + } + + si5351c_set_clock_source( + &clock_gen, + (source == CLOCK_SOURCE_HACKRF) ? PLL_SOURCE_XTAL : PLL_SOURCE_CLKIN); + hackrf_ui()->set_clock_source(source); + return source; +} + +void ssp1_set_mode_max283x(void) +{ + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); +} + +void ssp1_set_mode_max5864(void) +{ + spi_bus_start(max5864.bus, &ssp_config_max5864); +} + +void pin_setup(void) +{ + /* Configure all GPIO as Input (safe state) */ + //gpio_init(); + + /* TDI and TMS pull-ups are required in all JTAG-compliant devices. + * + * The HackRF CPLD is always present, so let the CPLD pull up its TDI and TMS. + * + * The PortaPack may not be present, so pull up the PortaPack TMS pin from the + * microcontroller. + * + * TCK is recommended to be held low, so use microcontroller pull-down. + * + * TDO is undriven except when in Shift-IR or Shift-DR phases. + * Use the microcontroller to pull down to keep from floating. + * + * LPC43xx pull-up and pull-down resistors are approximately 53K. + */ +#ifdef HACKRF_ONE + scu_pinmux(SCU_PINMUX_PP_TMS, SCU_GPIO_PUP | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_PP_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); +#endif + scu_pinmux(SCU_PINMUX_CPLD_TMS, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_CPLD_TDI, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_CPLD_TDO, SCU_GPIO_PDN | SCU_CONF_FUNCTION4); + scu_pinmux(SCU_PINMUX_CPLD_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + + /* Configure SCU Pin Mux as GPIO */ + scu_pinmux(SCU_PINMUX_LED1, SCU_GPIO_NOPULL); + scu_pinmux(SCU_PINMUX_LED2, SCU_GPIO_NOPULL); + scu_pinmux(SCU_PINMUX_LED3, SCU_GPIO_NOPULL); +#ifdef RAD1O + scu_pinmux(SCU_PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4); +#endif + + /* Configure USB indicators */ +#ifdef JAWBREAKER + scu_pinmux(SCU_PINMUX_USB_LED0, SCU_CONF_FUNCTION3); + scu_pinmux(SCU_PINMUX_USB_LED1, SCU_CONF_FUNCTION3); +#endif + + gpio_output(&gpio_led[0]); + gpio_output(&gpio_led[1]); + gpio_output(&gpio_led[2]); +#ifdef RAD1O + gpio_output(&gpio_led[3]); +#endif + + disable_1v8_power(); + if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#ifdef HACKRF_ONE + gpio_output(&gpio_h1r9_1v8_enable); + scu_pinmux(SCU_H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); +#endif + } else { + gpio_output(&gpio_1v8_enable); + scu_pinmux(SCU_PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0); + } + +#ifdef HACKRF_ONE + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_output(&gpio_h1r9_vaa_disable); + } else { + gpio_output(&gpio_vaa_disable); + } +#endif + +#ifdef RAD1O + /* Safe state: start with VAA turned off: */ + disable_rf_power(); + + /* Configure RF power supply (VAA) switch control signal as output */ + gpio_output(&gpio_vaa_enable); + + /* Disable unused clock outputs. They generate noise. */ + scu_pinmux(CLK0, SCU_CLK_IN | SCU_CONF_FUNCTION7); + scu_pinmux(CLK2, SCU_CLK_IN | SCU_CONF_FUNCTION7); + + scu_pinmux(SCU_PINMUX_GPIO3_10, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + scu_pinmux(SCU_PINMUX_GPIO3_11, SCU_GPIO_PDN | SCU_CONF_FUNCTION0); + +#endif + + /* enable input on SCL and SDA pins */ + SCU_SFSI2C0 = SCU_I2C0_NOMINAL; + + spi_bus_start(&spi_bus_ssp1, &ssp_config_max283x); + + mixer_bus_setup(&mixer); + +#ifdef HACKRF_ONE + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + rf_path.gpio_rx = &gpio_h1r9_rx; + sgpio_config.gpio_hw_sync_enable = &gpio_h1r9_hw_sync_enable; + } +#endif + rf_path_pin_setup(&rf_path); + + /* Configure external clock in */ + scu_pinmux(SCU_PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1); + + sgpio_configure_pin_functions(&sgpio_config); +} + +void enable_1v8_power(void) +{ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#ifdef HACKRF_ONE + gpio_set(&gpio_h1r9_1v8_enable); +#endif + } else { + gpio_set(&gpio_1v8_enable); + } +} + +void disable_1v8_power(void) +{ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { +#ifdef HACKRF_ONE + gpio_clear(&gpio_h1r9_1v8_enable); +#endif + } else { + gpio_clear(&gpio_1v8_enable); + } +} + +#ifdef HACKRF_ONE +void enable_rf_power(void) +{ + uint32_t i; + + /* many short pulses to avoid one big voltage glitch */ + for (i = 0; i < 1000; i++) { + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(&gpio_h1r9_vaa_disable); + gpio_clear(&gpio_h1r9_vaa_disable); + } else { + gpio_set(&gpio_vaa_disable); + gpio_clear(&gpio_vaa_disable); + } + } +} + +void disable_rf_power(void) +{ + if (detected_platform() == BOARD_ID_HACKRF1_R9) { + gpio_set(&gpio_h1r9_vaa_disable); + } else { + gpio_set(&gpio_vaa_disable); + } +} +#endif + +#ifdef RAD1O +void enable_rf_power(void) +{ + gpio_set(&gpio_vaa_enable); + + /* Let the voltage stabilize */ + delay(1000000); +} + +void disable_rf_power(void) +{ + gpio_clear(&gpio_vaa_enable); +} +#endif + +void led_on(const led_t led) +{ + gpio_set(&gpio_led[led]); +} + +void led_off(const led_t led) +{ + gpio_clear(&gpio_led[led]); +} + +void led_toggle(const led_t led) +{ + gpio_toggle(&gpio_led[led]); +} + +void set_leds(const uint8_t state) +{ + int num_leds = 3; +#ifdef RAD1O + num_leds = 4; +#endif + for (int i = 0; i < num_leds; i++) { + gpio_write(&gpio_led[i], ((state >> i) & 1) == 1); + } +} + +void hw_sync_enable(const hw_sync_mode_t hw_sync_mode) +{ + gpio_write(sgpio_config.gpio_hw_sync_enable, hw_sync_mode == 1); +} + +void halt_and_flash(const uint32_t duration) +{ + /* blink LED1, LED2, and LED3 */ + while (1) { + led_on(LED1); + led_on(LED2); + led_on(LED3); + delay(duration); + led_off(LED1); + led_off(LED2); + led_off(LED3); + delay(duration); + } +} diff --git a/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp new file mode 100644 index 00000000..3645804e --- /dev/null +++ b/firmware/baseband/sd_over_usb/proc_sd_over_usb.cpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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 "ch.h" +#include "hal.h" + +extern "C" { +void start_usb(void); +void irq_usb(void); +void usb_transfer(void); + +CH_IRQ_HANDLER(Vector60) { + irq_usb(); +} +} + +int main() { + sdcStart(&SDCD1, nullptr); + if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1"); + + start_usb(); + + while (true) { + usb_transfer(); + } + + return 0; +} diff --git a/firmware/baseband/sd_over_usb/scsi.c b/firmware/baseband/sd_over_usb/scsi.c new file mode 100644 index 00000000..d5d8b386 --- /dev/null +++ b/firmware/baseband/sd_over_usb/scsi.c @@ -0,0 +1,274 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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 "scsi.h" +#include "diskio.h" + +volatile bool usb_bulk_block_done = false; + +void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) { + usb_bulk_block_done = true; + + (void)user_data; + (void)bytes_transferred; +} + +void usb_send_bulk(void* const data, const uint32_t maximum_length) { + usb_bulk_block_done = false; + + usb_transfer_schedule_block( + &usb_endpoint_bulk_in, + data, + maximum_length, + usb_bulk_block_cb, + NULL); + + while (!usb_bulk_block_done); +} + +void usb_receive_bulk(void* const data, const uint32_t maximum_length) { + usb_bulk_block_done = false; + + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + data, + maximum_length, + usb_bulk_block_cb, + NULL); + + while (!usb_bulk_block_done); +} + +void usb_send_csw(msd_cbw_t *msd_cbw_data, uint8_t status) { + msd_csw_t csw = { + .signature = MSD_CSW_SIGNATURE, + .tag = msd_cbw_data->tag, + .data_residue = 0, + .status = status + }; + + memcpy(&usb_bulk_buffer[0], &csw, sizeof(msd_csw_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(msd_csw_t)); +} + +uint8_t handle_inquiry(msd_cbw_t *msd_cbw_data) { + (void)msd_cbw_data; + + scsi_inquiry_response_t ret = { + 0x00, /* direct access block device */ + 0x80, /* removable */ + 0x00, //0x04, /* SPC-2 */ + 0x00, //0x02, /* response data format */ + 0x20, /* response has 0x20 + 4 bytes */ + 0x00, + 0x00, + 0x00, + "Mayhem", + "Portapack MSD", + {'v','1','.','6'} + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_inquiry_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_inquiry_response_t)); + + return 0; +} + +uint8_t handle_inquiry_serial_number(msd_cbw_t *msd_cbw_data) { + (void)msd_cbw_data; + + scsi_unit_serial_number_inquiry_response_t ret = { + .peripheral = 0x00, + .page_code = 0x80, + .reserved = 0, + .page_length = 0x08, + .serialNumber = "Mayhem" + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_unit_serial_number_inquiry_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_unit_serial_number_inquiry_response_t)); + + return 0; +} + + +uint8_t read_format_capacities(msd_cbw_t *msd_cbw_data) { + uint16_t len = msd_cbw_data->cmd_data[7] << 8 | msd_cbw_data->cmd_data[8]; + + if (len != 0) { + size_t num_blocks = get_capacity(); + + scsi_read_format_capacities_response_t ret = { + .header = {0, 0, 0, 1 * 8 /* num_entries * 8 */}, + .blocknum = {((num_blocks) >> 24)& 0xff, ((num_blocks) >> 16)& 0xff, ((num_blocks) >> 8)& 0xff, num_blocks & 0xff}, + .blocklen = {0b10 /* formated */, 0, (512) >> 8, 0}, + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_read_format_capacities_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_read_format_capacities_response_t)); + } + + return 0; +} + +uint8_t read_capacity10(msd_cbw_t *msd_cbw_data) { + (void)msd_cbw_data; + + size_t num_blocks = get_capacity(); + + scsi_read_capacity10_response_t ret = { + .last_block_addr = cpu_to_be32(num_blocks - 1), + .block_size = cpu_to_be32(512) + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_read_capacity10_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_read_capacity10_response_t)); + + return 0; +} + +uint8_t request_sense(msd_cbw_t *msd_cbw_data) { + (void)msd_cbw_data; + + scsi_sense_response_t ret = { + .byte = { 0x70, 0, SCSI_SENSE_KEY_GOOD, 0, + 0, 0, 0, 8, + 0, 0 ,0 ,0, + SCSI_ASENSE_NO_ADDITIONAL_INFORMATION, SCSI_ASENSEQ_NO_QUALIFIER, 0, 0, + 0, 0 } + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_sense_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_sense_response_t)); + + return 0; +} + +uint8_t mode_sense6(msd_cbw_t *msd_cbw_data) { + (void)msd_cbw_data; + + scsi_mode_sense6_response_t ret = { + .byte = { + sizeof(scsi_mode_sense6_response_t) - 1, + 0, + 0, + 0 } + }; + + memcpy(&usb_bulk_buffer[0], &ret, sizeof(scsi_mode_sense6_response_t)); + usb_send_bulk(&usb_bulk_buffer[0], sizeof(scsi_mode_sense6_response_t)); + + return 0; +} + +static data_request_t decode_data_request(const uint8_t *cmd) { + data_request_t req; + uint32_t lba; + uint16_t blk; + + memcpy(&lba, &cmd[2], sizeof(lba)); + memcpy(&blk, &cmd[7], sizeof(blk)); + + req.first_lba = be32_to_cpu(lba); + req.blk_cnt = be16_to_cpu(blk); + + return req; +} + +uint8_t data_read10(msd_cbw_t *msd_cbw_data) { + data_request_t req = decode_data_request(msd_cbw_data->cmd_data); + + for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { + read_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); + usb_send_bulk(&usb_bulk_buffer[0], 512); + } + + return 0; +} + +uint8_t data_write10(msd_cbw_t *msd_cbw_data) { + data_request_t req = decode_data_request(msd_cbw_data->cmd_data); + + for (size_t block_index = 0; block_index < req.blk_cnt; block_index++) { + usb_receive_bulk(&usb_bulk_buffer[0], 512); + write_block(req.first_lba + block_index, &usb_bulk_buffer[0], 1 /* n blocks */); + } + + return 0; +} + +void scsi_command(msd_cbw_t *msd_cbw_data) { + uint8_t status = 1; + + switch (msd_cbw_data->cmd_data[0]) { + case SCSI_CMD_INQUIRY: + if ((msd_cbw_data->cmd_data[1] & 0b1) && msd_cbw_data->cmd_data[2] == 0x80) { + status = handle_inquiry_serial_number(msd_cbw_data); + } + else if ((msd_cbw_data->cmd_data[1] & 0b11) || msd_cbw_data->cmd_data[2] != 0) { + status = 1; + } + else { + status = handle_inquiry(msd_cbw_data); + } + + break; + + case SCSI_CMD_REQUEST_SENSE: + status = request_sense(msd_cbw_data); + break; + + case SCSI_CMD_READ_CAPACITY_10: + status = read_capacity10(msd_cbw_data); + break; + + case SCSI_CMD_READ_10: + status = data_read10(msd_cbw_data); + break; + + case SCSI_CMD_WRITE_10: + status = data_write10(msd_cbw_data); + break; + + case SCSI_CMD_TEST_UNIT_READY: + status = 0; + break; + + case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL: + status = 0; + break; + + case SCSI_CMD_MODE_SENSE_6: + status = mode_sense6(msd_cbw_data); + break; + + case SCSI_CMD_READ_FORMAT_CAPACITIES: + status = read_format_capacities(msd_cbw_data); + break; + + case SCSI_CMD_VERIFY_10: + status = 0; + break; + } + + usb_send_csw(msd_cbw_data, status); +} \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/scsi.h b/firmware/baseband/sd_over_usb/scsi.h new file mode 100644 index 00000000..9381bdff --- /dev/null +++ b/firmware/baseband/sd_over_usb/scsi.h @@ -0,0 +1,197 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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. + */ + +#ifndef __SCSI_H__ +#define __SCSI_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_detect.h" +#include "hackrf_core.h" +#include "usb_bulk_buffer.h" + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +#define SCSI_CMD_TEST_UNIT_READY 0x00 +#define SCSI_CMD_REQUEST_SENSE 0x03 +#define SCSI_CMD_INQUIRY 0x12 +#define SCSI_CMD_MODE_SENSE_6 0x1A +#define SCSI_CMD_START_STOP_UNIT 0x1B +#define SCSI_CMD_SEND_DIAGNOSTIC 0x1D +#define SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL 0x1E +#define SCSI_CMD_READ_CAPACITY_10 0x25 +#define SCSI_CMD_READ_FORMAT_CAPACITIES 0x23 +#define SCSI_CMD_READ_10 0x28 +#define SCSI_CMD_WRITE_10 0x2A +#define SCSI_CMD_VERIFY_10 0x2F + +#define SCSI_SENSE_KEY_GOOD 0x00 +#define SCSI_SENSE_KEY_RECOVERED_ERROR 0x01 +#define SCSI_SENSE_KEY_NOT_READY 0x02 +#define SCSI_SENSE_KEY_MEDIUM_ERROR 0x03 +#define SCSI_SENSE_KEY_HARDWARE_ERROR 0x04 +#define SCSI_SENSE_KEY_ILLEGAL_REQUEST 0x05 +#define SCSI_SENSE_KEY_UNIT_ATTENTION 0x06 +#define SCSI_SENSE_KEY_DATA_PROTECT 0x07 +#define SCSI_SENSE_KEY_BLANK_CHECK 0x08 +#define SCSI_SENSE_KEY_VENDOR_SPECIFIC 0x09 +#define SCSI_SENSE_KEY_COPY_ABORTED 0x0A +#define SCSI_SENSE_KEY_ABORTED_COMMAND 0x0B +#define SCSI_SENSE_KEY_VOLUME_OVERFLOW 0x0D +#define SCSI_SENSE_KEY_MISCOMPARE 0x0E + +#define SCSI_ASENSE_NO_ADDITIONAL_INFORMATION 0x00 +#define SCSI_ASENSE_LOGICAL_UNIT_NOT_READY 0x04 +#define SCSI_ASENSE_INVALID_FIELD_IN_CDB 0x24 +#define SCSI_ASENSE_NOT_READY_TO_READY_CHANGE 0x28 +#define SCSI_ASENSE_WRITE_PROTECTED 0x27 +#define SCSI_ASENSE_FORMAT_ERROR 0x31 +#define SCSI_ASENSE_INVALID_COMMAND 0x20 +#define SCSI_ASENSE_LBA_OUT_OF_RANGE 0x21 +#define SCSI_ASENSE_MEDIUM_NOT_PRESENT 0x3A + +#define SCSI_ASENSEQ_NO_QUALIFIER 0x00 +#define SCSI_ASENSEQ_FORMAT_COMMAND_FAILED 0x01 +#define SCSI_ASENSEQ_INIT_COMMAND_REQUIRED 0x02 +#define SCSI_ASENSEQ_OPERATION_IN_PROGRESS 0x07 + +#define MSD_CBW_SIGNATURE 0x43425355 +#define MSD_CSW_SIGNATURE 0x53425355 + +#define USB_TRANSFER_SIZE 0x2000 + +typedef struct { + uint32_t signature; + uint32_t tag; + uint32_t data_len; + uint8_t flags; + uint8_t lun; + uint8_t cmd_len; + uint8_t cmd_data[16]; +} __attribute__((packed)) msd_cbw_t; + +typedef struct { + uint8_t peripheral; + uint8_t removable; + uint8_t version; + uint8_t response_data_format; + uint8_t additional_length; + uint8_t sccstp; + uint8_t bqueetc; + uint8_t cmdque; + uint8_t vendorID[8]; + uint8_t productID[16]; + uint8_t productRev[4]; +} scsi_inquiry_response_t; + +typedef struct { + uint32_t signature; + uint32_t tag; + uint32_t data_residue; + uint8_t status; +} __attribute__((packed)) msd_csw_t; + +typedef struct { + uint8_t header[4]; + uint8_t blocknum[4]; + uint8_t blocklen[4]; +} scsi_read_format_capacities_response_t; + +typedef struct { + uint32_t last_block_addr; + uint32_t block_size; +} scsi_read_capacity10_response_t; + +typedef struct { + uint8_t byte[18]; +} scsi_sense_response_t; + +typedef struct { + uint8_t byte[4]; +} scsi_mode_sense6_response_t; + +typedef struct { + uint32_t first_lba; + uint16_t blk_cnt; +} data_request_t; + +typedef struct { + uint8_t peripheral; + uint8_t page_code; + uint8_t reserved; + uint8_t page_length; + uint8_t serialNumber[8]; +} scsi_unit_serial_number_inquiry_response_t; + +static inline uint16_t bswap_16(const uint16_t x) + __attribute__ ((warn_unused_result)) + __attribute__ ((const)) + __attribute__ ((always_inline)); + +static inline uint16_t bswap_16(const uint16_t x) { + uint8_t tmp; + union { uint16_t x; uint8_t b[2]; } data; + + data.x = x; + tmp = data.b[0]; + data.b[0] = data.b[1]; + data.b[1] = tmp; + + return data.x; +} + +static inline uint32_t bswap_32(const uint32_t x) + __attribute__ ((warn_unused_result)) + __attribute__ ((const)) + __attribute__ ((always_inline)); + +static inline uint32_t bswap_32(const uint32_t x) { + uint8_t tmp; + union { uint32_t x; uint8_t b[4]; } data; + + data.x = x; + tmp = data.b[0]; + data.b[0] = data.b[3]; + data.b[3] = tmp; + tmp = data.b[1]; + data.b[1] = data.b[2]; + data.b[2] = tmp; + + return data.x; +} + +#define be16_to_cpu(x) bswap_16(x) +#define be32_to_cpu(x) bswap_32(x) + +#define cpu_to_be16(x) bswap_16(x) +#define cpu_to_be32(x) bswap_32(x) + +void scsi_command(msd_cbw_t *msd_cbw_data); + +#endif /* __SCSI_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.c b/firmware/baseband/sd_over_usb/sd_over_usb.c new file mode 100644 index 00000000..e2863da5 --- /dev/null +++ b/firmware/baseband/sd_over_usb/sd_over_usb.c @@ -0,0 +1,132 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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 "sd_over_usb.h" +#include "scsi.h" + +bool scsi_running = false; + +usb_request_status_t report_max_lun( + usb_endpoint_t* const endpoint, + const usb_transfer_stage_t stage) +{ + if (stage == USB_TRANSFER_STAGE_SETUP) { + endpoint->buffer[0] = 0; + usb_transfer_schedule_block( + endpoint->in, + &endpoint->buffer, + 1, + NULL, + NULL); + + usb_transfer_schedule_ack(endpoint->out); + + scsi_running = true; + } + + return USB_REQUEST_STATUS_OK; +} + +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 == 0xFE) + return report_max_lun(endpoint, stage); + + return status; +} + +const usb_request_handlers_t usb_request_handlers = { + .standard = usb_standard_request, + .class = usb_class_request, + .vendor = 0, + .reserved = 0 +}; + +void usb_configuration_changed(usb_device_t* const device) { + (void)device; + + usb_endpoint_init(&usb_endpoint_bulk_in); + usb_endpoint_init(&usb_endpoint_bulk_out); +} + +void start_usb(void) { + detect_hardware_platform(); + pin_setup(); + cpu_clock_init(); + + 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_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); + + nvic_set_priority(NVIC_USB0_IRQ, 255); + + usb_run(&usb_device); +} + +void stop_usb(void) { + usb_peripheral_reset(); +} + +void irq_usb(void) { + usb0_isr(); +} + +volatile bool transfer_complete = false; +void scsi_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) +{ + (void)user_data; + (void)bytes_transferred; + + transfer_complete = true; +} + +void usb_transfer(void) { + if (scsi_running) { + transfer_complete = false; + usb_transfer_schedule_block( + &usb_endpoint_bulk_out, + &usb_bulk_buffer[0x4000], + USB_TRANSFER_SIZE, + scsi_bulk_transfer_complete, + NULL); + + while (!transfer_complete); + + msd_cbw_t *msd_cbw_data = (msd_cbw_t *) &usb_bulk_buffer[0x4000]; + + if (msd_cbw_data->signature == MSD_CBW_SIGNATURE){ + scsi_command(msd_cbw_data); + } + } +} \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/sd_over_usb.h b/firmware/baseband/sd_over_usb/sd_over_usb.h new file mode 100644 index 00000000..b886c866 --- /dev/null +++ b/firmware/baseband/sd_over_usb/sd_over_usb.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc. + * 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. + */ + +#ifndef __USB_SD_OVER_USB_H__ +#define __USB_SD_OVER_USB_H__ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "platform_detect.h" +#include "hackrf_core.h" +#include "usb_bulk_buffer.h" + +void start_usb(void); +void stop_usb(void); +void irq_usb(void); +void usb_transfer(void); + +#endif /* __USB_SD_OVER_USB_H__ */ \ No newline at end of file diff --git a/firmware/baseband/sd_over_usb/usb_bulk_buffer.h b/firmware/baseband/sd_over_usb/usb_bulk_buffer.h new file mode 100644 index 00000000..3e284143 --- /dev/null +++ b/firmware/baseband/sd_over_usb/usb_bulk_buffer.h @@ -0,0 +1,39 @@ +/* + * 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_BULK_BUFFER_H__ +#define __USB_BULK_BUFFER_H__ + +#include +#include + +#define USB_BULK_BUFFER_SIZE 0x8000 +#define USB_BULK_BUFFER_MASK 0x7FFF + +/* Address of usb_bulk_buffer is set in ldscripts. If you change the name of this + * variable, it won't be where it needs to be in the processor's address space, + * unless you also adjust the ldscripts. + */ +extern uint8_t usb_bulk_buffer[USB_BULK_BUFFER_SIZE]; + +#endif /*__USB_BULK_BUFFER_H__*/ diff --git a/firmware/baseband/sd_over_usb/usb_descriptor.c b/firmware/baseband/sd_over_usb/usb_descriptor.c new file mode 100644 index 00000000..fdba7b1d --- /dev/null +++ b/firmware/baseband/sd_over_usb/usb_descriptor.c @@ -0,0 +1,322 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * + * 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 + +#include "usb_type.h" +#include "usb_descriptor.h" + +#define USB_VENDOR_ID (0x1D50) + +#ifdef HACKRF_ONE + #define USB_PRODUCT_ID (0x6089) +#elif JAWBREAKER + #define USB_PRODUCT_ID (0x604B) +#elif RAD1O + #define USB_PRODUCT_ID (0xCC15) +#else + #define USB_PRODUCT_ID (0xFFFF) +#endif + +#define USB_API_VERSION (0x0107) + +#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 (512) + +#define USB_BULK_IN_EP_ADDR (0x81) +#define USB_BULK_OUT_EP_ADDR (0x02) + +#define USB_STRING_LANGID (0x0409) + +uint8_t usb_descriptor_device[] = { + 18, // bLength + USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // 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 + 0x04, // iSerialNumber + 0x01 // bNumConfigurations +}; + +uint8_t usb_descriptor_device_qualifier[] = { + 10, // bLength + USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType + USB_WORD(0x0200), // bcdUSB + 0x00, // bDeviceClass + 0x00, // bDeviceSubClass + 0x00, // bDeviceProtocol + 64, // bMaxPacketSize0 + 0x01, // bNumOtherSpeedConfigurations + 0x00 // bReserved +}; + +uint8_t usb_descriptor_configuration_full_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 0, // TERMINATOR +}; + +uint8_t usb_descriptor_configuration_high_speed[] = { + 9, // bLength + USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType + USB_WORD(32), // wTotalLength + 0x01, // bNumInterfaces + 0x01, // bConfigurationValue + 0x00, // iConfiguration + 0x80, // bmAttributes: USB-powered + 250, // bMaxPower: 500mA + + 9, // bLength + USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType + 0x00, // bInterfaceNumber + 0x00, // bAlternateSetting + 0x02, // bNumEndpoints + 0x08, // bInterfaceClass: vendor-specific + 0x06, // bInterfaceSubClass + 0x50, // bInterfaceProtocol: vendor-specific + 0x00, // iInterface + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_IN_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no NAK + + 7, // bLength + USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType + USB_BULK_OUT_EP_ADDR, // bEndpointAddress + 0x02, // bmAttributes: BULK + USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize + 0x00, // bInterval: no 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[] = { +#ifdef HACKRF_ONE + 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, + +#elif JAWBREAKER + 36, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'H', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + 'R', 0x00, + 'F', 0x00, + ' ', 0x00, + 'J', 0x00, + 'a', 0x00, + 'w', 0x00, + 'b', 0x00, + 'r', 0x00, + 'e', 0x00, + 'a', 0x00, + 'k', 0x00, + 'e', 0x00, + 'r', 0x00, +#elif RAD1O + 12, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'r', 0x00, + 'a', 0x00, + 'd', 0x00, + '1', 0x00, + 'o', 0x00, +#else + 14, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'H', 0x00, + 'a', 0x00, + 'c', 0x00, + 'k', 0x00, + 'R', 0x00, + 'F', 0x00, +#endif +}; + +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, +}; + +#ifdef DFU_MODE +uint8_t usb_descriptor_string_serial_number[] = { + 30, // bLength + USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType + 'R', 0x00, + 'u', 0x00, + 'n', 0x00, + 'n', 0x00, + 'i', 0x00, + 'n', 0x00, + 'g', 0x00, + 'F', 0x00, + 'r', 0x00, + 'o', 0x00, + 'm', 0x00, + 'R', 0x00, + 'A', 0x00, + 'M', 0x00, +}; +#else +uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN]; +#endif + +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/baseband/sd_over_usb/usb_descriptor.h b/firmware/baseband/sd_over_usb/usb_descriptor.h new file mode 100644 index 00000000..2c2990e0 --- /dev/null +++ b/firmware/baseband/sd_over_usb/usb_descriptor.h @@ -0,0 +1,42 @@ +/* + * Copyright 2012-2022 Great Scott Gadgets + * Copyright 2012 Jared Boone + * + * 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 + +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/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld index 6eb41175..08bcc093 100755 --- a/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld +++ b/firmware/chibios-portapack/os/ports/GCC/ARMCMx/LPC43xx_M4/ld/LPC43xx_M4.ld @@ -25,8 +25,10 @@ MEMORY { flash (rx) : org = 0x00000000, len = 32752 /* Local SRAM @ 0x10080000 */ ram (rwx) : org = 0x10000000, len = 96k /* Local SRAM @ 0x10000000 */ + ram_usb (rwx) : org = 0x20008000, len = 32K } +usb_bulk_buffer = ORIGIN(ram_usb); __ram_start__ = ORIGIN(ram); __ram_size__ = LENGTH(ram); __ram_end__ = __ram_start__ + __ram_size__; diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 709807fb..89679284 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -107,6 +107,7 @@ constexpr image_tag_t image_tag_siggen { 'P', 'S', 'I', 'G' }; constexpr image_tag_t image_tag_sstv_tx { 'P', 'S', 'T', 'X' }; constexpr image_tag_t image_tag_tones { 'P', 'T', 'O', 'N' }; constexpr image_tag_t image_tag_flash_utility { 'P', 'F', 'U', 'T' }; +constexpr image_tag_t image_tag_usb_sd { 'P', 'U', 'S', 'B' }; constexpr image_tag_t image_tag_noop { 'P', 'N', 'O', 'P' };