mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-08-18 21:53:59 +00:00
Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 167f89df0b | |||
| 1fcd87756e | |||
| 824b53f7d4 | |||
| 86584feb16 | |||
| 47c94dbf26 | |||
| b048b8f4f1 |
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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
@@ -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
@@ -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*/
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
};
|
||||
@@ -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[];
|
||||
|
||||
@@ -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};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,7 +297,6 @@ class BigFrequency : public Widget {
|
||||
|
||||
private:
|
||||
rf::Frequency _frequency;
|
||||
rf::Frequency _previous_frequency{~0LL};
|
||||
|
||||
static constexpr Dim digit_width = 32;
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Superrollo;BADBEEF;1;5
|
||||
@@ -0,0 +1 @@
|
||||
Superrollo;BADBEEF;1;7
|
||||
@@ -0,0 +1 @@
|
||||
Superrollo;BADBEEF;1;3
|
||||
Reference in New Issue
Block a user