fix(native-usb): prevent transmit-bank starvation and retain battery telemetry
This commit is contained in:
parent
a407e84a39
commit
a699a6920b
16 changed files with 342 additions and 82 deletions
|
|
@ -43,7 +43,7 @@ _Static_assert(PROBE_ROUTER_SLOTS == 3u, "Packed setup owner has three slots");
|
|||
typedef struct {
|
||||
uint8_t owner[128];
|
||||
#if defined(SWITCH2_PROBE_HUB) && SWITCH2_PROBE_HUB
|
||||
uint8_t early_address[2][16];
|
||||
uint8_t early_address[2][256];
|
||||
#endif
|
||||
} routing_table;
|
||||
|
||||
|
|
@ -149,6 +149,9 @@ static void build_table(routing_table* table,
|
|||
#if defined(SWITCH2_PROBE_HUB) && SWITCH2_PROBE_HUB
|
||||
// A unique observed prefix can preselect the SIE sooner. It still compares
|
||||
// the complete hardware address and CRC before accepting the transaction.
|
||||
// Index the four captured line pairs directly. Packing their D+ bits in
|
||||
// the sampling window delays bit 20 even when the prefix is ambiguous.
|
||||
memset(table->early_address, PROBE_ROUTER_UNASSIGNED, sizeof(table->early_address));
|
||||
for (unsigned kind = 0; kind < 2; ++kind) {
|
||||
for (unsigned prefix = 0; prefix < 16; ++prefix) {
|
||||
uint8_t candidate = PROBE_ROUTER_UNASSIGNED;
|
||||
|
|
@ -161,7 +164,10 @@ static void build_table(routing_table* table,
|
|||
}
|
||||
candidate = address;
|
||||
}
|
||||
table->early_address[kind][prefix] = candidate;
|
||||
unsigned raw_prefix = 0u;
|
||||
for (unsigned bit = 0; bit < 4; ++bit)
|
||||
raw_prefix |= ((prefix >> bit) & 1u ? LINE_J : LINE_K) << (2u * bit);
|
||||
table->early_address[kind][raw_prefix] = candidate;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -331,14 +337,12 @@ static bool observe_idle_j(void);
|
|||
#if defined(SWITCH2_PROBE_HUB) && SWITCH2_PROBE_HUB
|
||||
static raw_packet __no_inline_not_in_flash_func(capture_packet)(
|
||||
uint32_t phase, const routing_table* table, bool draining) {
|
||||
prepare_capture:;
|
||||
#else
|
||||
static raw_packet __no_inline_not_in_flash_func(capture_packet)(
|
||||
uint32_t phase, const routing_table* table) {
|
||||
#endif
|
||||
raw_packet result = {0};
|
||||
uint32_t word0 = LINE_K, word1 = 0u, word2 = 0u;
|
||||
uint32_t address_wire = 0u;
|
||||
const uint8_t* decoder = NULL;
|
||||
uint32_t expected_word = 0u;
|
||||
const uint32_t initial_address = usb_hw->dev_addr_ctrl;
|
||||
|
|
@ -351,6 +355,7 @@ static raw_packet __no_inline_not_in_flash_func(capture_packet)(
|
|||
// forcing them into live registers adds spills and unnecessary ORs.
|
||||
__asm volatile ("" : "+r"(word0), "+m"(result) : : "memory");
|
||||
#if defined(SWITCH2_PROBE_HUB) && SWITCH2_PROBE_HUB
|
||||
drain_prepared_capture:;
|
||||
if (draining) {
|
||||
const uint32_t stop = cycles_now() + FS_CLOCK_HZ / 10000u;
|
||||
bool saw_se0 = false;
|
||||
|
|
@ -422,10 +427,16 @@ edge:
|
|||
// The first stored K is the observed SOP above, not an invented SYNC bit.
|
||||
#if defined(SWITCH2_PROBE_HUB) && SWITCH2_PROBE_HUB
|
||||
#define SET_EARLY_DECODER(kind) (early_decoder = table->early_address[kind])
|
||||
#define DISCARD_NON_TOKEN() do { draining = true; goto prepare_capture; } while (0)
|
||||
// PID rejection happens before any address/body samples. Reuse the prepared
|
||||
// frame: rebuilding its stack image here can miss the end of a short ACK/NAK
|
||||
// and the following token. All other result/address accumulators are still zero.
|
||||
#define DISCARD_NON_TOKEN() do { \
|
||||
result.sop = false; word0 = LINE_K; draining = true; \
|
||||
goto drain_prepared_capture; \
|
||||
} while (0)
|
||||
#define ROUTE_EARLY(bit, base) do { \
|
||||
if ((base) + (bit) == 19u && decoder != NULL) { \
|
||||
uint8_t candidate = early_decoder[address_wire]; \
|
||||
uint8_t candidate = early_decoder[word1 & 0xffu]; \
|
||||
if (candidate < 128u) \
|
||||
route_header(table, candidate, word0 == 0x9a56a666u, initial_address, \
|
||||
deadline + 11u * FS_BIT_CYCLES, &result); \
|
||||
|
|
@ -447,10 +458,13 @@ edge:
|
|||
decoder = NULL; \
|
||||
DISCARD_NON_TOKEN(); \
|
||||
} \
|
||||
if ((base) + (bit) >= 16u && (base) + (bit) <= 23u) \
|
||||
address_wire |= (line & 1u) << (bit); \
|
||||
ROUTE_EARLY(bit, base); \
|
||||
if ((base) + (bit) == 23u && decoder != NULL) { \
|
||||
/* Compact observed D+ bits only after all eight symbols exist. */ \
|
||||
uint32_t address_wire = word1 & 0x5555u; \
|
||||
address_wire = (address_wire | (address_wire >> 1u)) & 0x3333u; \
|
||||
address_wire = (address_wire | (address_wire >> 2u)) & 0x0f0fu; \
|
||||
address_wire = (address_wire | (address_wire >> 4u)) & 0xffu; \
|
||||
route_header(table, decoder[address_wire], word0 == 0x9a56a666u, \
|
||||
initial_address, deadline + 7u * FS_BIT_CYCLES, &result); \
|
||||
if (result.late) { result.count = (base) + (bit) + 1u; goto done; } \
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@ int g_sensor_status = -1;
|
|||
uint16_t g_stick_center[2]{2048, 2048};
|
||||
uint16_t g_stick_positive[2]{2047, 2047};
|
||||
uint16_t g_stick_negative[2]{2048, 2048};
|
||||
uint8_t g_power_info = 0x01; // USB powered; no invented source charge or charging state.
|
||||
uint8_t g_power_info = 0x01;
|
||||
uint8_t g_report_counter;
|
||||
uint32_t g_report_serial;
|
||||
uint32_t g_pending_serial;
|
||||
|
|
|
|||
|
|
@ -290,9 +290,7 @@ uint32_t probe_native_gamepad_input_peek_native_report(uint8_t instance, uint32_
|
|||
if (g_report_token == UINT32_MAX) return 0; // Boot-unique, including across children/resets.
|
||||
memset(child.pending_report, 0, sizeof(child.pending_report));
|
||||
child.pending_report[0] = child.counter;
|
||||
// Battery telemetry belongs to the source; external power belongs to
|
||||
// these USB-powered virtual controllers. Unknown is not a full charge,
|
||||
// and the Pico does not charge the wireless source.
|
||||
// Source battery level and the virtual controller's USB power are separate.
|
||||
const unsigned battery_level = (static_cast<unsigned>(g_source.battery) * 9u + 127u) / 255u;
|
||||
child.pending_report[1] = static_cast<uint8_t>((battery_level << 2) | 0x01u);
|
||||
memcpy(child.pending_report + 2, child.input.buttons, sizeof(child.input.buttons));
|
||||
|
|
|
|||
|
|
@ -369,12 +369,12 @@ function(switch2_usb_probe_configure target)
|
|||
endif()
|
||||
if(SWITCH2_PROBE_HUB AND SWITCH2_BRIDGE_FULL_INPUT)
|
||||
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
||||
pico_set_program_version(${target} "0.72-native-usb-power")
|
||||
pico_set_program_version(${target} "0.78-native-tx-bank-trace")
|
||||
else()
|
||||
pico_set_program_version(${target} "0.72-native-gamepad")
|
||||
pico_set_program_version(${target} "0.78-native-gamepad")
|
||||
endif()
|
||||
elseif(SWITCH2_PROBE_HUB)
|
||||
pico_set_program_version(${target} "0.72-native-hub-editor")
|
||||
pico_set_program_version(${target} "0.78-native-hub-tx-bank")
|
||||
elseif(SWITCH2_PROBE_JOIN_CHORD_GATE)
|
||||
if(SWITCH2_PROBE_TRACE_NATIVE_INPUT)
|
||||
pico_set_program_version(${target} "0.37-pair-chord-trace")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue