Compare commits

..

6 Commits

Author SHA1 Message Date
copilot-swe-agent[bot] 167f89df0b Move frequency dirty-check to set() to avoid redundant repaints
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2026-08-18 18:22:23 +00:00
copilot-swe-agent[bot] 1fcd87756e Remove unused _previous_frequency member from BigFrequency and remove paint() guard
Co-authored-by: gullradriel <3157857+gullradriel@users.noreply.github.com>
2026-08-18 18:21:38 +00:00
copilot-swe-agent[bot] 824b53f7d4 Initial plan 2026-08-18 18:17:14 +00:00
rollorentner 86584feb16 Add Superrollo (GW60/HCS361) TX and RX (#3290)
keeloqtx: Superrollo 67-bit transmit mode with rolling counter.
subghzd: GW60 receive decoder (reports as KeeLoq; manufacturer key read from KEELOQKEYS/MFCODES). The manufacturer key is not included; add a Superrollo entry to MFCODES to enable TX/RX.
2026-08-16 20:10:04 +02:00
Bernd Herzog 47c94dbf26 SD Over USB for the Hackrf Pro (#3291)
* enabled sd over usb compilation
* updated hackrf pro usb stack
* fixed hackrf one code path
* fixed hackrf pro code path
* improved performance
* fixed sd card clock
* refactoring
* formatted code
2026-08-16 20:08:19 +02:00
Oleg Belousov b048b8f4f1 Feature/sliding freq (#3280)
* Add sliding-frequency audio receiver tuning
* Move ADS-B receiver to external app
* Move AIS receiver to external app
* Move APRS receiver to external app
* Move APRS transmitter to external app
* Isolate filtered spectrum collection from WFM
* Fixed the issues #3280
* fpga_bridge.c: init registers per reference fpga_init, add quarter-shift mode setter
* radio.hpp: expose cached FPGA quarter-rate shift
* radio.cpp: program FPGA quarter-rate shift together with tuning offset
* tuning.hpp: pass AFE rate and direction to tuning config, add quarter_shift field
* tuning.cpp: import full PRALINE RX/TX tuning tables with quarter-shift offsets
* clock_manager: stop writing bogus RX digital gain, keep quarter shift across rate changes
* receiver_model: port LPF bandwidth from reference auto_bandwidth, use cached quarter shift
* adsb_rx: enable RF amp by default on first run
* ui_geomap.hpp: add hemisphere fields, degrees become magnitude
* ui_geomap: use hemisphere selector for lat/lon sign, fix minute/second wrap carry
* ui_menu: guard select against empty menu
* waterfall_designer: header updates for profile file handling
* waterfall_designer: exception-free profile parsing, CRLF handling, deferred nav callbacks, backup cleanup
* external.ld: scope app section globs to their own object directories
* CMakeLists: relink when external.ld changes
* tools: add external app symbol placement checker
* tools: add guru meditation address lookup script
* tools: add per-function stack usage report script
Co-authored-by: gullradriel <gullradriel@users.noreply.github.com>
2026-08-08 21:47:54 +02:00
26 changed files with 2025 additions and 1349 deletions
+4 -2
View File
@@ -74,7 +74,7 @@ void SubGhzDRecentEntryDetailView::update_data() {
console.writeln("Action: 0x" + to_string_hex(func_code));
}
if (entry_.sensorType == FPS_KEELOQ) {
if (entry_.sensorType == FPS_KEELOQ || entry_.sensorType == FPS_SUPERROLLO) {
console.writeln("Fix: " + to_string_hex(fix));
console.writeln("Encrypted: " + to_string_hex(encrypted));
console.writeln("Manufacturer: " + mf_name);
@@ -243,6 +243,8 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "Ido 11x";
case FPS_INTERTECHNOV3:
return "InterTehcno v3";
case FPS_SUPERROLLO:
return "Superrollo";
case FPS_KEELOQ:
return "KeeLoq";
case FPS_KINGGATESSTYLO4K:
@@ -671,7 +673,7 @@ void SubGhzDRecentEntryDetailView::parseProtocol() {
return;
}
if (entry_.sensorType == FPS_KEELOQ) {
if (entry_.sensorType == FPS_KEELOQ || entry_.sensorType == FPS_SUPERROLLO) {
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, 64);
btn = data_rev >> 60;
+4 -8
View File
@@ -32,10 +32,6 @@
#include "core_control.hpp"
/* Set true to enable additional checks to ensure
* M4 and M0 are synchronized before passing messages. */
static constexpr bool enforce_core_sync = true;
/* Set true to enable check for baseband messages getting stuck.
* This implies the baseband thread is not dequeuing and has probably stalled.
* NB: This check adds a small amout of overhead to the message sending code
@@ -474,7 +470,7 @@ bool is_image_running() {
return baseband_image_running;
}
void run_image(const spi_flash::image_tag_t image_tag) {
void run_image(const spi_flash::image_tag_t image_tag, bool enforce_core_sync) {
if (baseband_image_running) {
chDbgPanic("BBRunning");
}
@@ -487,7 +483,7 @@ void run_image(const spi_flash::image_tag_t image_tag) {
creg::m4txevent::enable();
if constexpr (enforce_core_sync) {
if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u;
while (!shared_memory.baseband_ready && --count)
@@ -498,7 +494,7 @@ void run_image(const spi_flash::image_tag_t image_tag) {
}
}
void run_prepared_image(const uint32_t m4_code) {
void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync) {
if (baseband_image_running) {
chDbgPanic("BBRunning");
}
@@ -511,7 +507,7 @@ void run_prepared_image(const uint32_t m4_code) {
creg::m4txevent::enable();
if constexpr (enforce_core_sync) {
if (enforce_core_sync) {
// Wait up to 3 seconds for baseband to start handling events.
auto count = 3'000u;
while (!shared_memory.baseband_ready && --count)
+2 -2
View File
@@ -131,8 +131,8 @@ void request_beep_stop();
void request_audio_beep(uint32_t freq, uint32_t sample_rate, uint32_t duration_ms);
bool is_image_running();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
void run_prepared_image(const uint32_t m4_code);
void run_image(const portapack::spi_flash::image_tag_t image_tag, bool enforce_core_sync = true);
void run_prepared_image(const uint32_t m4_code, bool enforce_core_sync = true);
void shutdown();
void spectrum_streaming_start();
+5 -9
View File
@@ -407,6 +407,10 @@ set(EXTCPPSRC
external/aprs_tx/main.cpp
external/aprs_tx/ui_aprs_tx.cpp
#sd over usb
external/sdusb/main.cpp
external/sdusb/ui_sd_over_usb.cpp
)
set(EXTAPPLIST
@@ -505,13 +509,5 @@ set(EXTAPPLIST
ais_rx
aprs_rx
aprs_tx
sdusb
)
# sdusb has type conflicts with PRALINE (HackRF Pro) - add only for non-PRALINE builds
if(NOT BOARD STREQUAL "PRALINE")
list(APPEND EXTCPPSRC
external/sdusb/main.cpp
external/sdusb/ui_sd_over_usb.cpp
)
list(APPEND EXTAPPLIST sdusb)
endif()
+38 -2
View File
@@ -34,7 +34,7 @@ void KeeloqTXView::update_hop() {
hop = data.btn << 28 | (apri_serial & 0xFFF) << 16 | data.counter;
} else if (
data.mf_name == "DTM_Neo" || data.mf_name == "FAAC_RC,XT" || data.mf_name == "Mutanco_Mutancode" || data.mf_name == "Came_Space" || data.mf_name == "Genius_Bravo" || data.mf_name == "GSN" || data.mf_name == "Rosh" || data.mf_name == "Rossi" || data.mf_name == "Peccinin" || data.mf_name == "Steelmate" || data.mf_name == "Cardin_S449") {
data.mf_name == "DTM_Neo" || data.mf_name == "FAAC_RC,XT" || data.mf_name == "Mutanco_Mutancode" || data.mf_name == "Came_Space" || data.mf_name == "Genius_Bravo" || data.mf_name == "GSN" || data.mf_name == "Rosh" || data.mf_name == "Rossi" || data.mf_name == "Peccinin" || data.mf_name == "Steelmate" || data.mf_name == "Cardin_S449" || data.mf_name == "Superrollo") {
hop = data.btn << 28 | (data.serial & 0xFFF) << 16 | data.counter;
} else if (
data.mf_name == "NICE_Smilo" || data.mf_name == "NICE_MHOUSE" || data.mf_name == "JCM_Tech") {
@@ -75,6 +75,12 @@ void KeeloqTXView::update_payload() {
}
}
if (data.mf_name == "Superrollo") {
text_payload.set(to_string_hex((uint64_t)(uint32_t)encrypt));
encode_data_gw60((uint32_t)encrypt);
return;
}
payload = (uint64_t)fix << 32 | encrypt;
uint64_t preview_payload = FProtoGeneral::subghz_protocol_blocks_reverse_key(payload, 64);
@@ -83,6 +89,34 @@ void KeeloqTXView::update_payload() {
encode_data();
}
// Superrollo GW60 (HCS361) 67-bit OOK frame at Te=450us.
void KeeloqTXView::encode_data_gw60(uint32_t hop_enc) {
int bits[67];
for (uint32_t i = 0; i < 32; i++) bits[i] = (hop_enc >> i) & 1;
for (uint32_t i = 0; i < 28; i++) bits[32 + i] = (data.serial >> i) & 1;
for (uint32_t i = 0; i < 4; i++) bits[60 + i] = (data.btn >> i) & 1;
bits[64] = 1;
int crc0 = 0, crc1 = 0;
for (uint32_t i = 0; i < 65; i++) {
int new_crc1 = crc0 ^ bits[i];
int new_crc0 = new_crc1 ^ crc1;
crc0 = new_crc0 & 1;
crc1 = new_crc1 & 1;
}
bits[65] = crc0;
bits[66] = crc1;
std::string s{};
for (int i = 0; i < 9; i++) s += "100";
s += "11111111110000000000";
for (int i = 0; i < 67; i++) s += keeloq_fragments[bits[i]];
for (int i = 0; i < 18; i++) s += "0";
encoded_data = s;
pause_duration = 0;
}
void KeeloqTXView::encode_data() {
std::string fragments{};
@@ -206,9 +240,11 @@ void KeeloqTXView::start_tx() {
transmitter_model.enable();
const double te_us = (data.mf_name == "Superrollo") ? 450.0 : 400.0;
baseband::set_ook_data(
bitstream_length,
OOK_SAMPLERATE * (400.0 / 1000000.0),
OOK_SAMPLERATE * (te_us / 1000000.0),
repeat,
pause_duration);
}
@@ -106,6 +106,7 @@ class KeeloqTXView : public View {
std::string encoded_data{};
void encode_data();
void encode_data_gw60(uint32_t hop_enc);
uint32_t repeat = 4;
uint32_t pause_duration = 0;
+1 -1
View File
@@ -46,7 +46,7 @@ SdOverUsbView::SdOverUsbView(NavigationView& nav)
sdcStop(&SDCD1);
portapack::shutdown(true, false);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
baseband::run_prepared_image(portapack::memory::map::m4_code.base(), false);
m0_halt();
/* will not return*/
};
+21 -11
View File
@@ -748,13 +748,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PRTT rtty_tx)
### SD over USB
set(MODE_INCDIR
${HACKRF_PATH}/firmware
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware/libopencm3/include
)
### Morse TX
@@ -794,6 +787,15 @@ set(MODE_CPPSRC
)
DeclareTargets(PTET tetra_rx)
### SD over USB
set(MODE_INCDIR
${HACKRF_PATH}/firmware
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware/libopencm3/include
${HACKRF_PATH}/firmware/hackrf_usb
sd_over_usb/
)
set(MODE_CPPSRC
sd_over_usb/proc_sd_over_usb.cpp
@@ -802,7 +804,9 @@ set(MODE_CPPSRC
sd_over_usb/diskio.c
sd_over_usb/sd_over_usb.c
sd_over_usb/usb_descriptor.c
sd_over_usb/hackrf_core.c
sd_over_usb/usb_api_transceiver.c
sd_over_usb/cpu_clock.c
sd_over_usb/pins.c
${HACKRF_PATH}/firmware/common/adc.c
${HACKRF_PATH}/firmware/common/selftest.c
@@ -829,6 +833,15 @@ set(MODE_CPPSRC
${HACKRF_PATH}/firmware/common/rffc5071_spi.c
${HACKRF_PATH}/firmware/common/rffc5071.c
${HACKRF_PATH}/firmware/common/gpdma.c
${HACKRF_PATH}/firmware/common/clock_gen.c
${HACKRF_PATH}/firmware/common/cpld_jtag.c
${HACKRF_PATH}/firmware/common/leds.c
${HACKRF_PATH}/firmware/common/power.c
${HACKRF_PATH}/firmware/common/clock_io.c
${HACKRF_PATH}/firmware/common/rom_iap.c
${HACKRF_PATH}/firmware/common/w25q80bv.c
${HACKRF_PATH}/firmware/common/w25q80bv_target.c
${HACKRF_PATH}/firmware/libopencm3/lib/cm3/nvic.c
${HACKRF_PATH}/firmware/libopencm3/lib/cm3/sync.c
@@ -836,10 +849,7 @@ set(MODE_CPPSRC
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/timer.c
${HACKRF_PATH}/firmware/libopencm3/lib/lpc43xx/i2c.c
)
# sd_over_usb has type conflicts with PRALINE (HackRF Pro) - disable for now
if(NOT BOARD STREQUAL "PRALINE")
DeclareTargets(PUSB sd_over_usb)
endif()
### HackRF "factory" firmware
+124
View File
@@ -0,0 +1,124 @@
#ifndef __FPROTO_SUPERROLLO_H__
#define __FPROTO_SUPERROLLO_H__
#include "subghzdbase.hpp"
// Superrollo GW60 (HCS361) 67-bit OOK decoder. Reports FPS_SUPERROLLO; SubGhzD
// treats FPS_SUPERROLLO like KeeLoq for decrypt/display (see ui_subghzd.cpp), so
// the "Superrollo" keystore key decrypts it while it stays identifiable in logs.
typedef enum : uint8_t {
SuperrolloStepReset = 0,
SuperrolloStepPreambleLow,
SuperrolloStepSyncLow,
SuperrolloStepSaveDuration,
SuperrolloStepCheckDuration,
} SuperrolloDecoderStep;
class FProtoSubGhzDSuperrollo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSuperrollo() {
sensorType = FPS_SUPERROLLO;
te_short = 450;
te_long = 900;
te_delta = 200;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SuperrolloStepReset:
if (level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
header_count++;
parser_step = SuperrolloStepPreambleLow;
} else if (
(header_count >= 4) &&
(DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
parser_step = SuperrolloStepSyncLow;
} else {
header_count = 0;
}
} else {
header_count = 0;
}
break;
case SuperrolloStepPreambleLow:
if ((!level) && (DURATION_DIFF(duration, te_long) < te_delta)) {
parser_step = SuperrolloStepReset;
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
case SuperrolloStepSyncLow:
if ((!level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
parser_step = SuperrolloStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
case SuperrolloStepSaveDuration:
if (level) {
te_last = duration;
parser_step = SuperrolloStepCheckDuration;
} else if (duration >= te_short * 12) {
endFrame();
}
break;
case SuperrolloStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found)
subghz_protocol_blocks_add_bit(1);
else
decode_count_bit++;
parser_step = SuperrolloStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found)
subghz_protocol_blocks_add_bit(0);
else
decode_count_bit++;
parser_step = SuperrolloStepSaveDuration;
} else if (duration >= te_short * 12) {
endFrame();
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
} else {
parser_step = SuperrolloStepReset;
header_count = 0;
}
break;
}
}
uint16_t header_count = 0;
private:
void endFrame() {
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 3)) {
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
}
parser_step = SuperrolloStepReset;
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
}
};
#endif
@@ -29,6 +29,7 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include "s-ido.hpp"
#include "s-intertechnov3.hpp"
#include "s-keeloq.hpp"
#include "s-superrollo.hpp"
#include "s-kinggates_stylo_4k.hpp"
#include "s-linear.hpp"
#include "s-linear_delta3.hpp"
@@ -85,6 +86,7 @@ class SubGhzDProtos : public FProtoListGeneral {
protos[FPS_IDO] = new FProtoSubGhzDIdo();
protos[FPS_INTERTECHNOV3] = new FProtoSubGhzDIntertechnoV3();
protos[FPS_KEELOQ] = new FProtoSubGhzDKeeLoq();
protos[FPS_SUPERROLLO] = new FProtoSubGhzDSuperrollo();
protos[FPS_KINGGATESSTYLO4K] = new FProtoSubGhzDKinggatesStylo4K();
protos[FPS_LINEAR] = new FProtoSubGhzDLinear();
protos[FPS_LINEARDELTA3] = new FProtoSubGhzDLinearDelta3();
@@ -60,6 +60,7 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
FPS_MARANTEC24,
FPS_HOLTEKHT6P20B,
FPS_RESTAURANT_PAGER,
FPS_SUPERROLLO,
FPS_COUNT
};
+263
View File
@@ -0,0 +1,263 @@
/*
* Copyright 2026 Great Scott Gadgets <info@greatscottgadgets.com>
*
* 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 "cpu_clock.h"
#include <stdint.h>
#include <libopencm3/lpc43xx/cgu.h>
#if defined(IS_JAWBREAKER) || defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
#include <libopencm3/lpc43xx/ccu.h>
#endif
#include "delay.h"
#include "i2c_bus.h"
#include "i2c_lpc.h"
#include "si5351c.h"
/* We start with the CPU clock at 96MHz */
unsigned int cpu_clock_mhz = 96;
/*
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;
/* CPU is now at 12MHz */
cpu_clock_mhz = 12;
/* 2. Enable the crystal oscillator. */
CGU_XTAL_OSC_CTRL &= ~CGU_XTAL_OSC_CTRL_ENABLE_MASK;
/* 3. Wait 250us. */
delay_us(250);
/* 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;
/* CPU is now at 102MHz */
cpu_clock_mhz = 102;
/* 9. Wait 50us. */
delay_us(50);
/* 10. Set the PLL1 P-divider to direct output mode (DIRECT=1). */
CGU_PLL1_CTRL |= CGU_PLL1_CTRL_DIRECT_MASK;
/* CPU is now at 204MHz */
cpu_clock_mhz = 204;
}
/* 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);
// FIXME disable I2C
/* Kick I2C0 down to 400kHz when we switch over to APB1 clock = 204MHz */
i2c_bus_start(si5351c.bus, &i2c_config_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);
#ifdef IS_NOT_RAD1O
if (IS_NOT_RAD1O) {
/* 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(0) | CGU_BASE_SDIO_CLK_AUTOBLOCK(1) | CGU_BASE_SDIO_CLK_CLK_SEL(0x09);
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 = 1;
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
}
@@ -0,0 +1 @@
#pragma once
File diff suppressed because it is too large Load Diff
+254
View File
@@ -0,0 +1,254 @@
/*
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone <jared@sharebrained.com>
* Copyright 2013 Benjamin Vernoux <titanmkd@gmail.com>
*
* 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 "pins.h"
#include <libopencm3/lpc43xx/scu.h>
#include "gpio.h"
#include "leds.h"
#include "platform_detect.h"
#include "platform_gpio.h"
#include "platform_scu.h"
#include "power.h"
#ifdef IS_PRALINE
#include "clock_io.h"
#endif
void pins_shutdown(void) {
/* Configure all GPIO as Input (safe state) */
// gpio_init();
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* 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 IS_EXPANSION_COMPATIBLE
if (IS_EXPANSION_COMPATIBLE) {
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_TCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
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);
}
#endif
/* 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 IS_RAD1O
if (IS_RAD1O) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->PINMUX_LED4, SCU_GPIO_NOPULL | SCU_CONF_FUNCTION0);
}
#endif
/* Configure USB indicators */
#ifdef IS_JAWBREAKER
if (IS_JAWBREAKER) {
scu_pinmux(scu->PINMUX_USB_LED0, SCU_CONF_FUNCTION3);
scu_pinmux(scu->PINMUX_USB_LED1, SCU_CONF_FUNCTION3);
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
// disable_1v2_power();
// disable_3v3aux_power();
// gpio_output(gpio->gpio_1v2_enable);
// gpio_output(gpio->gpio_3v3aux_enable_n);
// scu_pinmux(scu->PINMUX_EN1V2, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
// scu_pinmux(scu->PINMUX_EN3V3_AUX_N, SCU_GPIO_FAST | SCU_CONF_FUNCTION4);
}
#endif
#ifdef IS_NOT_PRALINE
if (IS_NOT_PRALINE) {
disable_1v8_power();
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_1v8_enable);
scu_pinmux(scu->H1R9_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->gpio_1v8_enable);
scu_pinmux(scu->PINMUX_EN1V8, SCU_GPIO_FAST | SCU_CONF_FUNCTION0);
}
#endif
}
#endif
#ifdef IS_H1_OR_PRALINE
if (IS_H1_OR_PRALINE) {
/* Safe state: start with VAA turned off: */
disable_rf_power();
/* Configure RF power supply (VAA) switch control signal as output */
#ifdef IS_H1_R9
if (IS_H1_R9) {
gpio_output(gpio->h1r9_vaa_disable);
}
#endif
#ifdef IS_NOT_H1_R9
if (IS_NOT_H1_R9) {
gpio_output(gpio->vaa_disable);
}
#endif
}
#endif
#ifdef IS_RAD1O
if (IS_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
#ifdef IS_PRALINE
if (IS_PRALINE) {
scu_pinmux(scu->P2_CTRL0, scu->P2_CTRL0_PINCFG);
scu_pinmux(scu->P2_CTRL1, scu->P2_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL0, scu->P1_CTRL0_PINCFG);
scu_pinmux(scu->P1_CTRL1, scu->P1_CTRL1_PINCFG);
scu_pinmux(scu->P1_CTRL2, scu->P1_CTRL2_PINCFG);
scu_pinmux(scu->CLKIN_CTRL, scu->CLKIN_CTRL_PINCFG);
scu_pinmux(scu->AA_EN, scu->AA_EN_PINCFG);
scu_pinmux(scu->TRIGGER_IN, scu->TRIGGER_IN_PINCFG);
scu_pinmux(scu->TRIGGER_OUT, scu->TRIGGER_OUT_PINCFG);
scu_pinmux(scu->PPS_OUT, scu->PPS_OUT_PINCFG);
scu_pinmux(scu->SCT_CLK, scu->SCT_CLK_PINCFG);
scu_pinmux(scu->PINMUX_FPGA_CRESET, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->PINMUX_FPGA_CDONE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->PINMUX_FPGA_SPI_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_CIPO, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_COPI, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->SSP1_SCK, SCU_GPIO_PDN | SCU_CONF_FUNCTION2);
scu_pinmux(scu->XCVR_ENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXENABLE, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_RXHP, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->XCVR_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_LD, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SCLK, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
scu_pinmux(scu->MIXER_SDATA, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_RESETX, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->MIXER_ENBL, SCU_GPIO_PDN | SCU_CONF_FUNCTION0);
scu_pinmux(scu->AD_CS, SCU_GPIO_PDN | SCU_CONF_FUNCTION4);
p2_ctrl_set(P2_SIGNAL_CLK3);
p1_ctrl_set(P1_SIGNAL_CLKIN);
clkin_ctrl_set(CLKIN_SIGNAL_P1);
gpio_output(gpio->p2_ctrl0);
gpio_output(gpio->p2_ctrl1);
gpio_output(gpio->p1_ctrl0);
gpio_output(gpio->p1_ctrl1);
gpio_output(gpio->p1_ctrl2);
gpio_output(gpio->clkin_ctrl);
gpio_output(gpio->pps_out);
gpio_input(gpio->trigger_in);
gpio_input(gpio->trigger_out);
gpio_clear(gpio->fpga_cfg_spi_cs);
gpio_output(gpio->fpga_cfg_spi_cs);
gpio_clear(gpio->fpga_cfg_creset);
gpio_output(gpio->fpga_cfg_creset);
gpio_input(gpio->fpga_cfg_cdone);
gpio_input(gpio->max5864_select);
}
#endif
/* enable input on SCL and SDA pins */
SCU_SFSI2C0 = SCU_I2C0_NOMINAL;
}
/* Run after pins_shutdown() and prior to enabling power supplies. */
void pins_setup(void) {
/* Detect Platform */
const platform_gpio_t* gpio = platform_gpio();
const platform_scu_t* scu = platform_scu();
/* Configure LEDs */
led_off(0);
led_off(1);
led_off(2);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
led_off(3);
}
#endif
gpio_output(gpio->led[0]);
gpio_output(gpio->led[1]);
gpio_output(gpio->led[2]);
#ifdef IS_FOUR_LEDS
if (IS_FOUR_LEDS) {
gpio_output(gpio->led[3]);
}
#endif
/* Configure external clock in */
scu_pinmux(scu->PINMUX_GP_CLKIN, SCU_CLK_IN | SCU_CONF_FUNCTION1);
}
@@ -44,11 +44,11 @@ CH_IRQ_HANDLER(Vector60) {
}
int main() {
start_usb();
sdcStart(&SDCD1, nullptr);
if (sdcConnect(&SDCD1) == CH_FAILED) chDbgPanic("no sd card #1");
start_usb();
while (true) {
usb_transfer();
}
+145 -8
View File
@@ -23,14 +23,35 @@
#include "scsi.h"
#include "diskio.h"
#include "gpio_lpc.h"
#include "delay.h"
#include <libopencm3/lpc43xx/scu.h>
#include <libopencm3/lpc43xx/rgu.h>
#include <libopencm3/lpc43xx/wwdt.h>
#include "string.h"
/* Maximum number of 512-byte blocks transferred per SD command / USB bulk
* transfer. The data region of usb_bulk_buffer is 16 KiB (0x0000..0x3FFF),
* so this must be <= 32. It is split into two equal halves for double
* buffering. */
#define MAX_BLOCKS_PER_TRANSFER 32
#define HALF_BLOCKS (MAX_BLOCKS_PER_TRANSFER / 2)
volatile bool usb_bulk_block_done = false;
void delay(uint32_t duration);
/* Per-buffer-half completion flags for the asynchronous (double-buffered)
* bulk transfers. Index 0 = usb_bulk_buffer[0], index 1 = second half. */
volatile bool usb_bulk_block_done_async[2] = {false, false};
static uint32_t usb_bulk_buffer_index(const void* const data) {
return (data == &usb_bulk_buffer[HALF_BLOCKS * 512]) ? 1 : 0;
}
void usb_bulk_block_cb_async(void* user_data, unsigned int bytes_transferred) {
const uint32_t idx = (uint32_t)(uintptr_t)user_data;
usb_bulk_block_done_async[idx] = true;
(void)bytes_transferred;
}
void usb_bulk_block_cb(void* user_data, unsigned int bytes_transferred) {
usb_bulk_block_done = true;
@@ -65,6 +86,49 @@ void usb_receive_bulk(void* const data, const uint32_t maximum_length) {
while (!usb_bulk_block_done);
}
/* Schedule a bulk IN transfer without waiting for it to complete. The caller
* must later call usb_send_bulk_wait_finish() with the same buffer before
* reusing it. */
void usb_send_bulk_start(void* const data, const uint32_t maximum_length) {
const uint32_t idx = usb_bulk_buffer_index(data);
usb_bulk_block_done_async[idx] = false;
usb_transfer_schedule_block(
&usb_endpoint_bulk_in,
data,
maximum_length,
usb_bulk_block_cb_async,
(void*)(uintptr_t)idx);
}
/* Wait for a bulk IN transfer scheduled by usb_send_bulk_start() to finish. */
void usb_send_bulk_wait_finish(void* const data) {
const uint32_t idx = usb_bulk_buffer_index(data);
while (!usb_bulk_block_done_async[idx]);
}
/* Schedule a bulk OUT transfer without waiting for it to complete. The caller
* must later call usb_receive_bulk_finish() with the same buffer before
* reading from it. */
void usb_receive_bulk_start(void* const data, const uint32_t maximum_length) {
const uint32_t idx = usb_bulk_buffer_index(data);
usb_bulk_block_done_async[idx] = false;
usb_transfer_schedule_block(
&usb_endpoint_bulk_out,
data,
maximum_length,
usb_bulk_block_cb_async,
(void*)(uintptr_t)idx);
}
/* Wait for a bulk OUT transfer scheduled by usb_receive_bulk_start() to
* finish. */
void usb_receive_bulk_finish(void* const data) {
const uint32_t idx = usb_bulk_buffer_index(data);
while (!usb_bulk_block_done_async[idx]);
}
void usb_send_csw(msd_cbw_t* msd_cbw_data, uint8_t status) {
msd_csw_t csw = {
.signature = MSD_CSW_SIGNATURE,
@@ -213,22 +277,95 @@ static data_request_t decode_data_request(const uint8_t* cmd) {
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);
uint32_t lba = req.first_lba;
uint32_t remaining = req.blk_cnt;
uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]};
uint32_t buf_idx = 0;
uint8_t* in_flight = NULL;
if (remaining == 0)
return 0;
/* Read the first chunk and start sending it. */
uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
if (read_block(lba, buf[0], n))
return 1;
usb_send_bulk_start(buf[0], n * 512);
in_flight = buf[0];
lba += n;
remaining -= n;
/* While USB sends the previous chunk, read the next one into the other
* half of the buffer. */
while (remaining > 0) {
buf_idx ^= 1;
n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
if (read_block(lba, buf[buf_idx], n)) {
usb_send_bulk_wait_finish(in_flight);
return 1;
}
usb_send_bulk_wait_finish(in_flight);
usb_send_bulk_start(buf[buf_idx], n * 512);
in_flight = buf[buf_idx];
lba += n;
remaining -= n;
}
usb_send_bulk_wait_finish(in_flight);
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 */);
uint32_t lba = req.first_lba;
uint32_t remaining = req.blk_cnt;
uint8_t* buf[2] = {&usb_bulk_buffer[0], &usb_bulk_buffer[HALF_BLOCKS * 512]};
uint32_t buf_idx = 0;
uint8_t* pending_buf = NULL;
uint32_t pending_lba = 0;
uint32_t pending_n = 0;
if (remaining == 0)
return 0;
/* Start receiving the first chunk. */
uint32_t n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
usb_receive_bulk_start(buf[0], n * 512);
pending_buf = buf[0];
pending_lba = lba;
pending_n = n;
lba += n;
remaining -= n;
/* While USB receives the next chunk, write the previous one to the SD
* card. */
while (remaining > 0) {
buf_idx ^= 1;
n = (remaining > HALF_BLOCKS) ? HALF_BLOCKS : remaining;
usb_receive_bulk_start(buf[buf_idx], n * 512);
usb_receive_bulk_finish(pending_buf);
if (write_block(pending_lba, pending_buf, pending_n)) {
usb_receive_bulk_finish(buf[buf_idx]);
return 1;
}
pending_buf = buf[buf_idx];
pending_lba = lba;
pending_n = n;
lba += n;
remaining -= n;
}
usb_receive_bulk_finish(pending_buf);
if (write_block(pending_lba, pending_buf, pending_n))
return 1;
return 0;
}
@@ -293,7 +430,7 @@ void scsi_command(msd_cbw_t* msd_cbw_data) {
gpio_output(&dfu);
gpio_clear(&dfu);
delay(50 * 40800);
delay_ms(50);
RESET_CTRL0 = (1 << 0);
break;
+79 -7
View File
@@ -23,6 +23,8 @@
#include "sd_over_usb.h"
#include "scsi.h"
#include "usb_descriptor.h"
#include <rom_iap.h>
#include "delay.h"
#include <string.h>
@@ -36,8 +38,14 @@
extern usb_configuration_t* usb_configurations[];
static const usb_device_t usb_device_sd_over_usb = {
.descriptor = usb_descriptor_device,
.descriptor_strings = usb_descriptor_strings,
#ifdef IS_NOT_PRALINE
.descriptor = usb_descriptor_device_hackrf,
.descriptor_strings = usb_descriptor_strings_hackrf_one,
#endif
#ifdef IS_PRALINE
.descriptor = usb_descriptor_device_hackrf,
.descriptor_strings = usb_descriptor_strings_praline,
#endif
.qualifier_descriptor = usb_descriptor_device_qualifier,
.configurations = &usb_configurations,
.configuration = 0,
@@ -91,16 +99,83 @@ void usb_configuration_changed(usb_device_t* const device) {
usb_endpoint_init(&usb_endpoint_bulk_out, false);
}
void usb_set_descriptor_by_serial_number(void) {
iap_cmd_res_t iap_cmd_res;
/* 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) {
usb_descriptor_string_serial_number[0] =
USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN;
usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING;
/* 32 characters of serial number, convert to UTF-16LE */
for (size_t i = 0; i < USB_DESCRIPTOR_STRING_SERIAL_LEN; i++) {
const uint_fast8_t nibble =
(iap_cmd_res.status_res.iap_result[i >> 3] >>
(28 - (i & 7) * 4)) &
0xf;
const char c =
(nibble > 9) ? ('a' + nibble - 10) : ('0' + nibble);
usb_descriptor_string_serial_number[2 + i * 2] = c;
usb_descriptor_string_serial_number[3 + i * 2] = 0x00;
}
} else {
usb_descriptor_string_serial_number[0] = 2;
usb_descriptor_string_serial_number[1] = USB_DESCRIPTOR_TYPE_STRING;
}
}
void start_usb(void) {
// Detect hardware platform before we do anything else.
detect_hardware_platform();
pin_setup();
board_id_t board_id = detected_platform();
pins_shutdown();
sgpio_pin_shutdown(&sgpio_config);
rf_path_pin_shutdown();
if (board_id != BOARD_ID_RAD1O) {
clock_gen_shutdown();
}
delay_ms(10);
pins_setup();
cpld_jtag_pin_setup();
cpu_clock_init();
memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb));
#ifndef DFU_MODE
usb_set_descriptor_by_serial_number();
#endif
usb_set_configuration_changed_cb(usb_configuration_changed);
usb_peripheral_reset();
#ifdef IS_HACKRF_ONE
if (IS_HACKRF_ONE) {
memcpy(&usb_device,
&usb_device_sd_over_usb,
sizeof(usb_device_sd_over_usb));
}
#endif
#ifdef IS_JAWBREAKER
if (IS_JAWBREAKER) {
memcpy(&usb_device,
&usb_device_jawbreaker,
sizeof(usb_device_jawbreaker));
}
#endif
#ifdef IS_RAD1O
if (IS_RAD1O) {
memcpy(&usb_device, &usb_device_rad1o, sizeof(usb_device_rad1o));
}
#endif
#ifdef IS_PRALINE
if (IS_PRALINE) {
memcpy(&usb_device, &usb_device_sd_over_usb, sizeof(usb_device_sd_over_usb));
}
#endif
usb_device_init(0, &usb_device);
usb_queue_init(&usb_endpoint_control_out_queue);
@@ -109,9 +184,6 @@ void start_usb(void) {
usb_queue_init(&usb_endpoint_bulk_in_queue);
usb_endpoint_init(&usb_endpoint_control_out, false);
/* Match the new usb_endpoint_init() contract introduced upstream by
* db73ecbf, control IN needs ZLP for transfers whose length is a
* multiple of the EP0 max packet size, otherwise the host hangs. */
usb_endpoint_init(&usb_endpoint_control_in, true);
nvic_set_priority(NVIC_USB0_IRQ, 255);
@@ -0,0 +1,766 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
* Copyright 2013 Benjamin Vernoux
* Copyright 2024 Bernd Herzog
*
* 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_transceiver.h"
#include <stdbool.h>
#include <stddef.h>
#include <libopencm3/cm3/nvic.h>
#include <libopencm3/lpc43xx/gpdma.h>
#include <libopencm3/lpc43xx/usb.h>
#include <clock_gen.h>
#include <fixed_point.h>
#include <gpdma.h>
#include <hackrf_ui.h>
#include <leds.h>
#include <m0_state.h>
#include <operacake_sctimer.h>
#include <platform_detect.h>
#include <radio.h>
#include <sgpio.h>
#include <streaming.h>
#include <transceiver_mode.h>
#include "common/usb.h"
#include <usb_queue.h>
#include <usb_request.h>
#include <usb_type.h>
#include "usb_buffer.h"
#include "usb_endpoint.h"
#define USB_TRANSFER_SIZE 0x4000
#define DMA_TRANSFER_SIZE 0x2000
#define BUF_HALF_MASK (USB_SAMP_BUFFER_SIZE >> 1)
// Unless we know the host knows our buffer size, we'll avoid leaving TX
// until we've transmitted all bytes sent by the host. This flag is cleared
// when the host requests our buffer size.
bool auto_tx_flush = true;
volatile uint32_t dma_started, dma_pending, usb_started, usb_completed;
typedef struct {
uint32_t freq_mhz;
uint32_t freq_hz;
} set_freq_params_t;
set_freq_params_t set_freq_params;
struct set_freq_explicit_params {
uint64_t if_freq_hz; /* intermediate frequency */
uint64_t lo_freq_hz; /* front-end local oscillator frequency */
uint8_t path; /* image rejection filter path */
};
struct set_freq_explicit_params explicit_params;
typedef struct {
uint32_t freq_hz;
uint32_t divider;
} set_sample_r_params_t;
set_sample_r_params_t set_sample_r_params;
void transceiver_dma_setup(void);
usb_request_status_t usb_vendor_request_set_baseband_filter_bandwidth(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
const uint32_t bandwidth =
(endpoint->setup.index << 16) | endpoint->setup.value;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BB_BANDWIDTH_TX,
bandwidth);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BB_BANDWIDTH_RX,
bandwidth);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_freq(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&set_freq_params,
sizeof(set_freq_params_t),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
const uint64_t freq =
set_freq_params.freq_mhz * 1000000ULL + set_freq_params.freq_hz;
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_RF,
freq * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_IF,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_LO,
RADIO_UNSET);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_IMAGE_REJECT,
RADIO_UNSET);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_freq_explicit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&explicit_params,
sizeof(struct set_freq_explicit_params),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_IF,
explicit_params.if_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_FREQUENCY_LO,
explicit_params.lo_freq_hz * FP_ONE_HZ);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_IMAGE_REJECT,
explicit_params.path);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
/*
* Convert fractional sample rate to units of 1/(2**36) Hz.
*/
static inline fp_28_36_t round_sample_rate(uint64_t num, uint32_t denom) {
uint64_t q1, r1, q2, r2, q3;
if (denom == 0) {
denom = 1;
}
q1 = num / denom;
r1 = num % denom;
q2 = (r1 << 32) / denom;
r2 = (r1 << 32) % denom;
q3 = ((r2 << 4) + (denom >> 1)) / denom;
return (q1 << 36) + (q2 << 4) + q3;
}
usb_request_status_t usb_vendor_request_set_sample_rate_frac(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&set_sample_r_params,
sizeof(set_sample_r_params_t),
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
uint32_t numerator = set_sample_r_params.freq_hz;
uint32_t denominator = set_sample_r_params.divider;
uint64_t value = round_sample_rate(numerator, denominator);
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_SAMPLE_RATE, value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_amp_enable(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_GAIN_TX_RF,
endpoint->setup.value);
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_GAIN_RX_RF,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_lna_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
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_set_vga_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_RX_BB, gain);
endpoint->buffer[0] = RADIO_OK;
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_set_txvga_gain(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint8_t gain = endpoint->setup.index;
radio_reg_write(&radio, RADIO_BANK_REQUESTED, RADIO_GAIN_TX_IF, gain);
endpoint->buffer[0] = RADIO_OK;
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_set_antenna_enable(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
switch (detected_platform()) {
case BOARD_ID_HACKRF1_OG:
case BOARD_ID_HACKRF1_R9:
case BOARD_ID_PRALINE:
// supported
break;
default:
return USB_REQUEST_STATUS_STALL;
}
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_BIAS_TEE,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
static volatile uint32_t _tx_underrun_limit;
static volatile uint32_t _rx_overrun_limit;
volatile transceiver_request_t transceiver_request = {
.mode = TRANSCEIVER_MODE_OFF,
.seq = 0,
};
void transceiver_usb_setup_complete(usb_endpoint_t* const endpoint) {
if (transceiver_request.mode == TRANSCEIVER_MODE_TX &&
endpoint->setup.request == 1 && auto_tx_flush) {
// This is a request to leave TX mode. Do so but NAK for now.
request_transceiver_mode(endpoint->setup.value);
} else {
usb_setup_complete(endpoint);
}
}
// Must be called from an atomic context (normally USB ISR)
void request_transceiver_mode(transceiver_mode_t mode) {
usb_endpoint_flush(&usb_endpoint_bulk_in);
usb_endpoint_flush(&usb_endpoint_bulk_out);
transceiver_request.mode = mode;
transceiver_request.seq++;
}
void transceiver_shutdown(void) {
baseband_streaming_disable(&sgpio_config);
operacake_sctimer_reset_state();
usb_endpoint_flush(&usb_endpoint_bulk_in);
usb_endpoint_flush(&usb_endpoint_bulk_out);
led_off(LED2);
led_off(LED3);
radio_switch_opmode(&radio, TRANSCEIVER_MODE_OFF);
m0_set_mode(M0_MODE_IDLE);
}
void transceiver_startup(const transceiver_mode_t mode) {
dma_started = 0;
dma_pending = 0;
usb_started = 0;
usb_completed = 0;
transceiver_dma_setup();
radio_switch_opmode(&radio, mode);
switch (mode) {
case TRANSCEIVER_MODE_RX_SWEEP:
case TRANSCEIVER_MODE_RX:
led_off(LED3);
led_on(LED2);
m0_set_mode(M0_MODE_RX);
m0_state.shortfall_limit = _rx_overrun_limit;
break;
case TRANSCEIVER_MODE_TX:
led_off(LED2);
led_on(LED3);
m0_set_mode(M0_MODE_TX_START);
m0_state.shortfall_limit = _tx_underrun_limit;
break;
default:
break;
}
activate_best_clock_source();
}
usb_request_status_t usb_vendor_request_set_transceiver_mode(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
switch (endpoint->setup.value) {
case TRANSCEIVER_MODE_OFF:
case TRANSCEIVER_MODE_RX:
case TRANSCEIVER_MODE_TX:
case TRANSCEIVER_MODE_RX_SWEEP:
case TRANSCEIVER_MODE_CPLD_UPDATE:
request_transceiver_mode(endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
return USB_REQUEST_STATUS_OK;
default:
return USB_REQUEST_STATUS_STALL;
}
} else {
return USB_REQUEST_STATUS_OK;
}
}
usb_request_status_t usb_vendor_request_set_hw_sync_mode(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
radio_reg_write(
&radio,
RADIO_BANK_REQUESTED,
RADIO_TRIGGER,
endpoint->setup.value);
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_tx_underrun_limit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value;
_tx_underrun_limit = value;
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_set_rx_overrun_limit(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = (endpoint->setup.index << 16) + endpoint->setup.value;
_rx_overrun_limit = value;
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_vendor_request_get_buffer_size(
usb_endpoint_t* const endpoint,
const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
uint32_t value = USB_SAMP_BUFFER_SIZE + USB_BULK_BUFFER_SIZE;
endpoint->buffer[0] = value & 0xff;
endpoint->buffer[1] = (value & 0xff00) >> 8;
endpoint->buffer[2] = (value & 0xff0000) >> 16;
endpoint->buffer[3] = (value & 0xff000000) >> 24;
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
4,
NULL,
NULL);
usb_transfer_schedule_ack(endpoint->out);
// We now know the host is aware of our buffer size, so it
// can make its own decisions about flushing the buffer.
auto_tx_flush = false;
return USB_REQUEST_STATUS_OK;
}
return USB_REQUEST_STATUS_OK;
}
/* clang-format off */
// Which GPDMA channel to use.
const uint32_t DMA_CHANNEL = 1;
// GPDMA CCONFIG register setting.
const uint32_t DMA_CONFIG =
GPDMA_CCONFIG_FLOWCNTRL(0) // memory-to-memory
| GPDMA_CCONFIG_IE(0) // no error interrupt
| GPDMA_CCONFIG_ITC(1) // terminal count interrupt
| GPDMA_CCONFIG_L(0) // do not lock
| GPDMA_CCONFIG_H(0); // do not halt
// GPDMA CCONTROL register setting (excluding TRANSFERSIZE field).
const uint32_t DMA_CONTROL =
GPDMA_CCONTROL_SBSIZE(7) // 256-transfer src bursts
| GPDMA_CCONTROL_DBSIZE(7) // 256-transfer dst bursts
| GPDMA_CCONTROL_SWIDTH(2) // 32-bit src transfers
| GPDMA_CCONTROL_DWIDTH(2) // 32-bit dst transfers
| GPDMA_CCONTROL_S(0) // AHB Master 0
| GPDMA_CCONTROL_D(1) // AHB Master 1
| GPDMA_CCONTROL_SI(1) // increment source
| GPDMA_CCONTROL_DI(1) // increment destination
| GPDMA_CCONTROL_PROT1(0) // user mode
| GPDMA_CCONTROL_PROT2(0) // not bufferable
| GPDMA_CCONTROL_PROT3(0) // not cacheable
| GPDMA_CCONTROL_I(1); // interrupt enabled
/* clang-format on */
// Called before any sequence of DMA transfers.
void transceiver_dma_setup(void) {
gpdma_controller_enable();
GPDMA_CCONFIG(DMA_CHANNEL) = DMA_CONFIG;
GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL;
GPDMA_CLLI(DMA_CHANNEL) = 0;
GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL);
nvic_enable_irq(NVIC_DMA_IRQ);
}
// Called to start each DMA transfer.
void transceiver_start_dma(void* src, void* dest, size_t size) {
uint32_t num_transfers = size >> 2;
GPDMA_CCONTROL(DMA_CHANNEL) = DMA_CONTROL | num_transfers;
GPDMA_CSRCADDR(DMA_CHANNEL) = (uint32_t)src;
GPDMA_CDESTADDR(DMA_CHANNEL) = (uint32_t)dest;
dma_pending = size;
gpdma_channel_enable(DMA_CHANNEL);
}
// Called when a DMA transfer completes.
void dma_isr(void) {
gpdma_channel_disable(DMA_CHANNEL);
GPDMA_INTTCCLEAR = (1 << DMA_CHANNEL);
m0_state.m4_count += dma_pending;
dma_pending = 0;
}
void transceiver_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
(void)user_data;
usb_completed += bytes_transferred;
}
typedef enum {
DIRECTION_RX,
DIRECTION_TX,
} direction_t;
void start_dma_if_possible(direction_t direction, size_t size) {
if (dma_pending) {
return;
}
uint32_t sampling_completed = m0_state.m0_count;
uint32_t dma_completed = m0_state.m4_count;
uint32_t samp_offset = dma_started & USB_SAMP_BUFFER_MASK;
uint32_t bulk_offset = dma_started & USB_BULK_BUFFER_MASK;
uint32_t data_available, space_in_use, space_available, samp_buf_margin;
uint8_t *dest, *src;
if (direction == DIRECTION_RX) {
data_available = sampling_completed - dma_started;
space_in_use = usb_completed - dma_completed;
space_available = USB_BULK_BUFFER_SIZE - space_in_use;
samp_buf_margin = USB_SAMP_BUFFER_SIZE - data_available;
src = &usb_samp_buffer[samp_offset];
dest = &usb_bulk_buffer[bulk_offset];
} else {
data_available = usb_completed - dma_started;
space_in_use = dma_completed - sampling_completed;
space_available = USB_SAMP_BUFFER_SIZE - space_in_use;
samp_buf_margin = space_in_use;
src = &usb_bulk_buffer[bulk_offset];
dest = &usb_samp_buffer[samp_offset];
}
if (data_available < size || size > space_available) {
return;
}
uint32_t m0_buf_half = sampling_completed & BUF_HALF_MASK;
uint32_t dma_buf_half = dma_started & BUF_HALF_MASK;
bool same_buf_half = m0_buf_half == dma_buf_half;
if (same_buf_half && samp_buf_margin >= (USB_SAMP_BUFFER_SIZE / 2)) {
return;
}
transceiver_start_dma(src, dest, size);
dma_started += size;
}
void start_usb_if_possible(direction_t direction) {
uint32_t bulk_offset = usb_started & USB_BULK_BUFFER_MASK;
uint32_t dma_completed = m0_state.m4_count;
uint32_t bytes_available;
usb_endpoint_t* usb_endpoint;
if (direction == DIRECTION_RX) {
bytes_available = dma_completed - usb_started;
usb_endpoint = &usb_endpoint_bulk_in;
} else {
uint32_t space_used = usb_started - dma_completed;
bytes_available = USB_BULK_BUFFER_SIZE - space_used;
usb_endpoint = &usb_endpoint_bulk_out;
}
if (bytes_available < USB_TRANSFER_SIZE) {
return;
}
usb_transfer_schedule_block(
usb_endpoint,
&usb_bulk_buffer[bulk_offset],
USB_TRANSFER_SIZE,
transceiver_bulk_transfer_complete,
NULL);
usb_started += USB_TRANSFER_SIZE;
}
int8_t saturation_buffer = 0;
uint64_t saturation_buffer_time = 0;
volatile uint64_t systick_counter = 0;
void sys_tick_handler(void) {
systick_counter++;
}
void rx_mode(uint32_t seq) {
transceiver_startup(TRANSCEIVER_MODE_RX);
baseband_streaming_enable(&sgpio_config);
while (transceiver_request.seq == seq) {
start_dma_if_possible(DIRECTION_RX, DMA_TRANSFER_SIZE);
start_usb_if_possible(DIRECTION_RX);
int8_t sample_value = *(
int8_t*)&usb_samp_buffer[m0_state.m0_count & USB_SAMP_BUFFER_MASK];
if (sample_value > saturation_buffer)
saturation_buffer = sample_value;
if (-sample_value > saturation_buffer)
saturation_buffer = -sample_value;
if (saturation_buffer_time + 4 < systick_counter) {
saturation_buffer_time = systick_counter;
hackrf_ui()->set_saturation(saturation_buffer);
saturation_buffer = 0;
}
radio_update(&radio);
}
transceiver_shutdown();
}
void tx_mode(uint32_t seq) {
transceiver_startup(TRANSCEIVER_MODE_TX);
// First, make transfers directly into the sample buffer to fill it.
for (int i = 0; i < (USB_SAMP_BUFFER_SIZE / USB_TRANSFER_SIZE); i++) {
// Set up transfer.
usb_transfer_schedule_block(
&usb_endpoint_bulk_out,
&usb_samp_buffer[usb_started],
USB_TRANSFER_SIZE,
transceiver_bulk_transfer_complete,
NULL);
usb_started += USB_TRANSFER_SIZE;
// Wait for the transfer to complete.
while (usb_completed < usb_started) {
// Handle the host switching modes before filling the buffer.
if (transceiver_request.seq != seq) {
transceiver_shutdown();
return;
}
radio_update(&radio);
}
}
// Sample buffer is now full. Update DMA counters accordingly.
dma_started = USB_SAMP_BUFFER_SIZE;
m0_state.m4_count = USB_SAMP_BUFFER_SIZE;
// Start transmitting samples.
baseband_streaming_enable(&sgpio_config);
// Continue feeding samples to the sample buffer.
while (transceiver_request.seq == seq) {
start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE);
start_usb_if_possible(DIRECTION_TX);
radio_update(&radio);
}
// Host has now requested to stop TX. If we're not auto-flushing, we
// should now stop TX immediately.
if (!auto_tx_flush) {
transceiver_shutdown();
return;
}
// Otherwise, we should now ensure all bytes sent by the host are
// transmitted before we leave TX. First, we should make sure all data
// currently in the USB bulk buffer reaches the sample buffer.
if ((usb_started - usb_completed) > 0) {
// We were part way through a 16KB firmware-side transfer when
// the transceiver mode change request to stop TX was received.
//
// We want to include the contents of that partial transfer in
// the data we move to the sample buffer.
//
// The transfer was already stopped by usb_endpoint_flush(),
// which was called from request_transceiver_mode().
//
// We will not have had a callback, and the transfer descriptor
// (dTD) will not have been updated, since the transfer did not
// complete.
//
// However, as long as we haven't started a new transfer, we
// can retrieve the partial byte count from the transfer
// overlay in the endpoint queue head (dQH) (UM10503 25.9.1).
usb_queue_head_t* const qh =
usb_queue_head(usb_endpoint_bulk_out.address);
unsigned int bytes_remaining =
(qh->total_bytes & USB_TD_DTD_TOKEN_TOTAL_BYTES_MASK) >>
USB_TD_DTD_TOKEN_TOTAL_BYTES_SHIFT;
unsigned int bytes_transferred = USB_TRANSFER_SIZE - bytes_remaining;
usb_completed += bytes_transferred;
}
// Feed the remaining data from the bulk buffer to the sample buffer.
// At this point, we also need to handle the case where there is less data
// to be transferred to the sample buffer than a full-sized DMA transfer.
// Any remainder of less than 4 bytes will be ignored; this is the chunk
// size of our DMA transfers.
while ((usb_completed - m0_state.m4_count) >= 4) {
uint32_t data_available = usb_completed - dma_started;
if (data_available > DMA_TRANSFER_SIZE) {
start_dma_if_possible(DIRECTION_TX, DMA_TRANSFER_SIZE);
} else {
start_dma_if_possible(DIRECTION_TX, data_available);
}
radio_update(&radio);
}
// Wait for the data in the sample buffer to be transmitted.
// Any remainder of less than 32 bytes will be ignored; this is
// the chunk size used by the M0 core to transfer samples to SGPIO.
while ((m0_state.m4_count - m0_state.m0_count) >= 32) {
radio_update(&radio);
}
// All data received from the host has now been transmitted.
// Now we can ACK the control request that took us out of TX mode.
usb_transfer_schedule_ack(usb_endpoint_control_in.in);
transceiver_shutdown();
}
void off_mode(uint32_t seq) {
while (transceiver_request.seq == seq) {
radio_update(&radio);
}
}
+233 -153
View File
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2022 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012-2026 Great Scott Gadgets <info@greatscottgadgets.com>
* Copyright 2012 Jared Boone
*
* This file is part of HackRF.
@@ -20,22 +20,13 @@
* Boston, MA 02110-1301, USA.
*/
#include <stdint.h>
#include "usb_type.h"
#include "usb_descriptor.h"
#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */
#include <stdint.h>
#ifdef HACKRF_ONE
#define USB_PRODUCT_ID (0xa7a8) /* SD card reader */
#elif JAWBREAKER
#define USB_PRODUCT_ID (0x604B)
#elif RAD1O
#define USB_PRODUCT_ID (0xCC15)
#else
#define USB_PRODUCT_ID (0xFFFF)
#endif
#include <usb_type.h>
#define USB_VENDOR_ID (0x0781) /* SanDisk Corp. */
#define USB_API_VERSION (0x0127) /* hardware revision */
@@ -50,7 +41,8 @@
#define USB_STRING_LANGID (0x0409)
uint8_t usb_descriptor_device[] = {
#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
uint8_t usb_descriptor_device_hackrf[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
@@ -59,13 +51,50 @@ uint8_t usb_descriptor_device[] = {
0x00, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor
USB_WORD(USB_PRODUCT_ID), // idProduct
USB_WORD(0xa7a8), // idProduct /* SD card reader */
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
#ifdef IS_JAWBREAKER
uint8_t usb_descriptor_device_jawbreaker[] = {
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(0x604B), // idProduct
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
#ifdef IS_RAD1O
uint8_t usb_descriptor_device_rad1o[] = {
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(0xCC15), // idProduct
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x04, // iSerialNumber
0x01 // bNumConfigurations
};
#endif
uint8_t usb_descriptor_device_qualifier[] = {
10, // bLength
@@ -161,162 +190,213 @@ uint8_t usb_descriptor_string_languages[] = {
// 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,
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
34, // 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,
#ifdef IS_HACKRF_ONE
uint8_t usb_descriptor_string_product_hackrf_one[] = {
34, // 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,
'M', 0x00,
'a', 0x00,
'y', 0x00,
'h', 0x00,
'e', 0x00,
'm', 0x00,
};
#endif
#ifdef IS_PRALINE
uint8_t usb_descriptor_string_product_praline[] = {
34, // 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,
};
#endif
#ifdef IS_JAWBREAKER
uint8_t usb_descriptor_string_product_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,
};
#endif
#ifdef IS_RAD1O
uint8_t usb_descriptor_string_product_rad1o[] = {
12, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'r', 0x00,
'a', 0x00,
'd', 0x00,
'1', 0x00,
'o', 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,
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,
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
#ifdef IS_HACKRF_ONE
uint8_t* usb_descriptor_strings_hackrf_one[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_hackrf_one,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_PRALINE
uint8_t* usb_descriptor_strings_praline[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_praline,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_JAWBREAKER
uint8_t* usb_descriptor_strings_jawbreaker[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_jawbreaker,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
#ifdef IS_RAD1O
uint8_t* usb_descriptor_strings_rad1o[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product_rad1o,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
#endif
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
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
};
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
};
+27 -3
View File
@@ -20,22 +20,46 @@
* Boston, MA 02110-1301, USA.
*/
#pragma once
#include <stdint.h>
extern uint8_t usb_descriptor_device[];
#if defined(IS_HACKRF_ONE) || defined(IS_PRALINE)
extern uint8_t usb_descriptor_device_hackrf[];
#endif
#ifdef IS_JAWBREAKER
extern uint8_t usb_descriptor_device_jawbreaker[];
#endif
#ifdef IS_RAD1O
extern uint8_t usb_descriptor_device_rad1o[];
#endif
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[];
#ifdef IS_HACKRF_ONE
extern uint8_t usb_descriptor_string_product_hackrf_one[];
#endif
#ifdef IS_PRALINE
extern uint8_t usb_descriptor_string_product_praline[];
#endif
#ifdef IS_JAWBREAKER
extern uint8_t usb_descriptor_string_product_jawbreaker[];
#endif
#ifdef IS_RAD1O
extern uint8_t usb_descriptor_string_product_rad1o[];
#endif
#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[];
extern uint8_t* usb_descriptor_strings_hackrf_one[];
extern uint8_t* usb_descriptor_strings_jawbreaker[];
extern uint8_t* usb_descriptor_strings_rad1o[];
extern uint8_t* usb_descriptor_strings_praline[];
#define USB_WCID_VENDOR_REQ 0x19
extern uint8_t wcid_string_descriptor[];
+49 -55
View File
@@ -564,16 +564,14 @@ void LiveDateTime::set_seconds_enabled(bool new_value) {
/* BigFrequency **********************************************************/
BigFrequency::BigFrequency(
Rect parent_rect,
rf::Frequency frequency)
: Widget{parent_rect},
_frequency{frequency} {
}
BigFrequency::BigFrequency(Rect parent_rect, rf::Frequency frequency)
: Widget{parent_rect}, _frequency{frequency} {}
void BigFrequency::set(const rf::Frequency frequency) {
_frequency = frequency;
set_dirty();
if (_frequency != frequency) {
_frequency = frequency;
set_dirty();
}
}
void BigFrequency::paint(Painter& painter) {
@@ -583,63 +581,59 @@ void BigFrequency::paint(Painter& painter) {
Point digit_pos;
ui::Color segment_color;
if (_frequency != _previous_frequency) {
_previous_frequency = _frequency;
rf::Frequency frequency{_frequency};
const auto rect = screen_rect(); // why not use screen_rect() directly for width, ...? it may be too small, but ...
rf::Frequency frequency{_frequency};
const auto rect = screen_rect(); // why not use screen_rect() directly for width, ...? it may be too small, but ...
// Erase
painter.fill_rectangle(
{{0, rect.location().y()}, {screen_width, 52}},
Theme::getInstance()->bg_darkest->background);
// Erase
painter.fill_rectangle(
{{0, rect.location().y()}, {screen_width, 52}},
Theme::getInstance()->bg_darkest->background);
// Prepare digits
if (!frequency) {
digits.fill(10); // ----.---
digit_pos = {(screen_width - ((7 * digit_width) + 8)) / 2, rect.location().y()};
} else {
frequency /= 1000; // GMMM.KKK(uuu)
// Prepare digits
if (!frequency) {
digits.fill(10); // ----.---
digit_pos = {(screen_width - ((7 * digit_width) + 8)) / 2, rect.location().y()};
} else {
frequency /= 1000; // GMMM.KKK(uuu)
for (i = 0; i < 7; i++) {
digits[6 - i] = frequency % 10;
frequency /= 10;
}
// Remove leading zeros
for (i = 0; i < 3; i++) {
if (!digits[i])
digits[i] = 16; // "Don't draw" code
else
break;
}
digit_pos = {(Coord)(screen_width - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()};
for (i = 0; i < 7; i++) {
digits[6 - i] = frequency % 10;
frequency /= 10;
}
segment_color = style().foreground;
// Remove leading zeros
for (i = 0; i < 3; i++) {
if (!digits[i])
digits[i] = 16; // "Don't draw" code
else
break;
}
// Draw
for (i = 0; i < 7; i++) {
digit = digits[i];
digit_pos = {(Coord)(screen_width - ((7 * digit_width) + 8) - (i * digit_width)) / 2, rect.location().y()};
}
if (digit < 16) {
digit_def = segment_font[(uint8_t)digit];
segment_color = style().foreground;
for (size_t s = 0; s < 7; s++) {
if (digit_def & 1)
painter.fill_rectangle({digit_pos + segments[s].location(), segments[s].size()}, segment_color);
digit_def >>= 1;
}
// Draw
for (i = 0; i < 7; i++) {
digit = digits[i];
if (digit < 16) {
digit_def = segment_font[(uint8_t)digit];
for (size_t s = 0; s < 7; s++) {
if (digit_def & 1)
painter.fill_rectangle({digit_pos + segments[s].location(), segments[s].size()}, segment_color);
digit_def >>= 1;
}
}
if (i == 3) {
// Dot
painter.fill_rectangle({digit_pos + Point(34, 48), {4, 4}}, segment_color);
digit_pos += {(digit_width + 8), 0};
} else {
digit_pos += {digit_width, 0};
}
if (i == 3) {
// Dot
painter.fill_rectangle({digit_pos + Point(34, 48), {4, 4}}, segment_color);
digit_pos += {(digit_width + 8), 0};
} else {
digit_pos += {digit_width, 0};
}
}
}
-1
View File
@@ -297,7 +297,6 @@ class BigFrequency : public Widget {
private:
rf::Frequency _frequency;
rf::Frequency _previous_frequency{~0LL};
static constexpr Dim digit_width = 32;
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;5
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;7
+1
View File
@@ -0,0 +1 @@
Superrollo;BADBEEF;1;3