Skip to content

Commit 5ebc8bc

Browse files
committed
Refactor command handling
1 parent aa63707 commit 5ebc8bc

9 files changed

Lines changed: 112 additions & 121 deletions

File tree

firmware/libsi/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ cmake_minimum_required(VERSION "3.21")
55
project(si LANGUAGES C)
66

77
# Define the library
8-
add_library(si STATIC "src/crc8.c" "src/commands.c" "src/device/gc_controller.c")
8+
add_library(si STATIC "src/crc8.c" "src/device/commands.c" "src/device/gc_controller.c")
99

1010
# Configure SI peripheral selection
1111
if(DEFINED SI_RX_TIMER_IDX)

firmware/libsi/include/si/commands.h renamed to firmware/libsi/include/si/device/commands.h

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
#include <stdbool.h>
44
#include <stdint.h>
55

6-
#include "si.h"
6+
#include "si/si.h"
77

88
/**
99
* Function type for command handlers.
@@ -14,7 +14,7 @@
1414
*
1515
* @return 0 on success, negative error code on failure
1616
*/
17-
typedef int (*si_command_handler_fn)(const uint8_t *command, si_callback_fn callback, void *context);
17+
typedef int (*si_command_handler_fn)(const uint8_t *command, si_complete_cb_t callback, void *context);
1818

1919
/**
2020
* Register a command handler for commands from an SI host.
@@ -27,27 +27,9 @@ typedef int (*si_command_handler_fn)(const uint8_t *command, si_callback_fn call
2727
void si_command_register(uint8_t command, uint8_t length, si_command_handler_fn handler, void *context);
2828

2929
/**
30-
* Get the expected length of an SI command.
31-
*
32-
* @param command the command to check
33-
*
34-
* @return the expected length of the command, in bytes, or 0 if the command is unknown
35-
*/
36-
uint8_t si_command_get_length(uint8_t command);
37-
38-
/**
39-
* Get the command handler for an SI command.
40-
*
41-
* @param command the command to check
42-
*
43-
* @return the command handler function, or NULL if the command is unknown
44-
*/
45-
si_command_handler_fn si_command_get_handler(uint8_t command);
46-
47-
/**
48-
* Process an SI command.
49-
*
30+
* Process a single SI command on the bus.
5031
*
32+
* This will read a command from the SI bus and call the registered handler.
5133
*/
5234
void si_command_process();
5335

firmware/libsi/include/si/si.h

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141

4242
#pragma once
4343

44+
#include <stdbool.h>
4445
#include <stddef.h>
4546
#include <stdint.h>
4647

@@ -118,12 +119,21 @@ enum {
118119
SI_ERR_TRANSFER_TIMEOUT,
119120
};
120121

122+
/**
123+
* Function type for per-byte callbacks during receive operations.
124+
*
125+
* @param byte the received byte
126+
* @param byte_index the zero-based index of the byte (0 for first byte)
127+
* @return true to continue the transfer, false to stop it
128+
*/
129+
typedef bool (*si_byte_cb_t)(uint8_t byte, uint8_t byte_index);
130+
121131
/**
122132
* Function type for transfer completion callbacks.
123133
*
124134
* @param result 0 on success, negative error code on failure
125135
*/
126-
typedef void (*si_callback_fn)(int result);
136+
typedef void (*si_complete_cb_t)(int result_or_bytes_read);
127137

128138
/**
129139
* Initialize the SI bus.
@@ -143,24 +153,26 @@ void si_init(uint8_t port, uint8_t pin, uint8_t mode, uint32_t rx_freq, uint32_t
143153
* @param length the length of the data
144154
* @param callback function to call when the transfer is complete
145155
*/
146-
void si_write_bytes(const uint8_t *data, uint8_t length, si_callback_fn callback);
156+
void si_write_bytes(const uint8_t *data, uint8_t length, si_complete_cb_t callback);
147157

148158
/**
149159
* Read data from the SI bus.
150160
*
151161
* @param buffer the buffer to read into
152-
* @param length the number of bytes expected
153-
* @param callback function to call when the transfer is complete
162+
* @param max_length the maximum number of bytes to read (buffer size)
163+
* @param byte_callback optional function to call for each received byte (can be NULL)
164+
* @param complete_callback function to call when the transfer is complete
154165
*/
155-
void si_read_bytes(uint8_t *buffer, uint8_t length, si_callback_fn callback);
166+
void si_read_bytes(uint8_t *buffer, uint8_t max_length, si_byte_cb_t byte_callback, si_complete_cb_t complete_callback);
156167

157168
/**
158169
* Read a single command from the SI bus.
159170
*
160171
* @param buffer the buffer to read into
172+
* @param max_length the maximum buffer size
161173
* @param callback function to call when the command has been read
162174
*/
163-
void si_read_command(uint8_t *buffer, si_callback_fn callback);
175+
void si_read_command(uint8_t *buffer, uint8_t max_length, si_complete_cb_t callback);
164176

165177
/**
166178
* Wait for the SI bus to be idle.
Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
#include "si/commands.h"
1+
#include "si/device/commands.h"
22

33
#define COMMAND_TABLE_SIZE 18
44

@@ -16,12 +16,21 @@ struct command_entry {
1616
void *context;
1717
};
1818

19-
static uint8_t bus_state = BUS_STATE_UNKNOWN;
19+
// Command table for registered SI commands
2020
static struct command_entry command_table[COMMAND_TABLE_SIZE] = {0};
21+
static struct command_entry *current_command = NULL;
22+
23+
// Current bus state
24+
static uint8_t bus_state = BUS_STATE_UNKNOWN;
25+
26+
// Buffer for reading/writing commands
2127
static uint8_t command_buffer[SI_BLOCK_SIZE];
28+
29+
// Automatically transition back to reading commands
2230
static bool auto_tx_rx_transition = false;
2331

2432
// Vaguely context-aware hash function
33+
// We're hashing to reduce memory usage while keeping O(1) lookup time in most cases
2534
static inline uint8_t hash_command(uint8_t command)
2635
{
2736
// Reserve hash slots for universal commands
@@ -62,65 +71,67 @@ static void on_tx_complete(int result)
6271
// Command handler RX completion callback
6372
static void on_rx_complete(int result)
6473
{
65-
if (result == 0) {
66-
// Look up the command in the table
67-
struct command_entry *command = find_command(command_buffer[0]);
68-
if (command && command->handler) {
69-
// Call the command handler
70-
bus_state = BUS_STATE_BUSY;
71-
command->handler(command_buffer, on_tx_complete, command->context);
72-
return;
73-
}
74+
// If we successfully read a command and have a handler, call it
75+
if (result >= 0 && current_command && current_command->handler) {
76+
current_command->handler(command_buffer, on_tx_complete, current_command->context);
77+
return;
7478
}
7579

76-
// Error during command read or handler not found
80+
// Otherwise, there was either an error during the read, or handler not found
7781
bus_state = BUS_STATE_ERROR;
7882

7983
// Transition back to RX mode if auto transition is enabled
8084
if (auto_tx_rx_transition)
8185
si_command_process();
8286
}
8387

84-
void si_command_register(uint8_t command, uint8_t length, si_command_handler_fn handler, void *context)
88+
static bool command_byte_cb(uint8_t byte, uint8_t byte_index)
8589
{
86-
uint8_t index = hash_command(command);
90+
// If this is the first byte, look up the command entry
91+
if (byte_index == 0) {
92+
current_command = find_command(byte);
93+
if (current_command == NULL) {
94+
return false; // Stop reading on unknown command
95+
}
96+
}
8797

88-
// Linear probing for collision resolution
89-
while (command_table[index].handler != NULL && command_table[index].command != command) {
90-
index = (index + 1) % COMMAND_TABLE_SIZE;
98+
if (byte_index == current_command->length - 1) {
99+
return false; // Stop reading after the expected length
91100
}
92101

93-
command_table[index].command = command;
94-
command_table[index].length = length;
95-
command_table[index].handler = handler;
96-
command_table[index].context = context;
102+
return true;
97103
}
98104

99-
uint8_t si_command_get_length(uint8_t command)
105+
void si_command_register(uint8_t command, uint8_t length, si_command_handler_fn handler, void *context)
100106
{
101-
struct command_entry *entry = find_command(command);
102-
if (entry == NULL)
103-
return 0;
104-
105-
return entry->length;
106-
}
107+
uint8_t index = hash_command(command);
107108

108-
si_command_handler_fn si_command_get_handler(uint8_t command)
109-
{
110-
struct command_entry *entry = find_command(command);
111-
if (entry == NULL)
112-
return NULL;
109+
// Linear probing for collision resolution
110+
// If the slot is already occupied, find the next available slot
111+
// This has high clustering, but is simple and effective for our use case
112+
while (command_table[index].handler != NULL && command_table[index].command != command) {
113+
index = (index + 1) % COMMAND_TABLE_SIZE;
114+
}
113115

114-
return entry->handler;
116+
// Store the command entry
117+
command_table[index] = (struct command_entry){
118+
.command = command,
119+
.length = length,
120+
.handler = handler,
121+
.context = context,
122+
};
115123
}
116124

117125
void si_command_process()
118126
{
119127
if (bus_state != BUS_STATE_IDLE)
120128
si_await_bus_idle();
121129

130+
// Initialize command reading context
131+
current_command = NULL;
132+
122133
bus_state = BUS_STATE_BUSY;
123-
si_read_command(command_buffer, on_rx_complete);
134+
si_read_bytes(command_buffer, SI_BLOCK_SIZE, command_byte_cb, on_rx_complete);
124135
}
125136

126137
void si_command_processing_enable()

firmware/libsi/src/device/gc_controller.c

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#include <string.h>
22

3-
#include "si/commands.h"
3+
#include "si/device/commands.h"
44
#include "si/device/gc_controller.h"
55

66
/*
@@ -74,7 +74,7 @@ static uint8_t *pack_input_state(struct si_device_gc_input_state *src, uint8_t a
7474
* Command: {0x00}
7575
* Response: A 3-byte device info.
7676
*/
77-
static int handle_info(const uint8_t *command, si_callback_fn callback, void *context)
77+
static int handle_info(const uint8_t *command, si_complete_cb_t callback, void *context)
7878
{
7979
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
8080

@@ -90,7 +90,7 @@ static int handle_info(const uint8_t *command, si_callback_fn callback, void *co
9090
* Command: {0xFF}
9191
* Response: A 3-byte device info.
9292
*/
93-
static int handle_reset(const uint8_t *command, si_callback_fn callback, void *context)
93+
static int handle_reset(const uint8_t *command, si_complete_cb_t callback, void *context)
9494
{
9595
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
9696

@@ -108,7 +108,7 @@ static int handle_reset(const uint8_t *command, si_callback_fn callback, void *c
108108
* Command: {0x40, analog_mode, motor_state}
109109
* Response: An 8-byte packed input state, see `pack_input_state` for details
110110
*/
111-
static int handle_short_poll(const uint8_t *command, si_callback_fn callback, void *context)
111+
static int handle_short_poll(const uint8_t *command, si_complete_cb_t callback, void *context)
112112
{
113113
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
114114

@@ -150,7 +150,7 @@ static int handle_short_poll(const uint8_t *command, si_callback_fn callback, vo
150150
* Command: {0x41}
151151
* Response: A 10-byte input state representing the current origin.
152152
*/
153-
static int handle_read_origin(const uint8_t *command, si_callback_fn callback, void *context)
153+
static int handle_read_origin(const uint8_t *command, si_complete_cb_t callback, void *context)
154154
{
155155
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
156156

@@ -174,7 +174,7 @@ static int handle_read_origin(const uint8_t *command, si_callback_fn callback, v
174174
* Command: {0x42, 0x00, 0x00}
175175
* Response: A 10-byte input state representing the current origin.
176176
*/
177-
static int handle_calibrate(const uint8_t *command, si_callback_fn callback, void *context)
177+
static int handle_calibrate(const uint8_t *command, si_complete_cb_t callback, void *context)
178178
{
179179
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
180180

@@ -205,7 +205,7 @@ static int handle_calibrate(const uint8_t *command, si_callback_fn callback, voi
205205
*
206206
* NOTE: This command is not used by any games, but is included for completeness.
207207
*/
208-
static int handle_long_poll(const uint8_t *command, si_callback_fn callback, void *context)
208+
static int handle_long_poll(const uint8_t *command, si_complete_cb_t callback, void *context)
209209
{
210210
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
211211

@@ -238,7 +238,7 @@ static int handle_long_poll(const uint8_t *command, si_callback_fn callback, voi
238238
* Command: {0x4D, 0x??, 0x??} - 2nd and 3rd bytes seem to differ every time
239239
* Response: 8 bytes of zeroes.
240240
*/
241-
static int handle_probe_device(const uint8_t *command, si_callback_fn callback, void *context)
241+
static int handle_probe_device(const uint8_t *command, si_complete_cb_t callback, void *context)
242242
{
243243
// Respond with 8 bytes of zeroes
244244
uint8_t response[8] = {0};
@@ -255,7 +255,7 @@ static int handle_probe_device(const uint8_t *command, si_callback_fn callback,
255255
* Command: {0x4E, wireless_id_h | SI_WIRELESS_FIX_ID, wireless_id_l}
256256
* Response: A 3-byte device info.
257257
*/
258-
static int handle_fix_device(const uint8_t *command, si_callback_fn callback, void *context)
258+
static int handle_fix_device(const uint8_t *command, si_complete_cb_t callback, void *context)
259259
{
260260
struct si_device_gc_controller *device = (struct si_device_gc_controller *)context;
261261

0 commit comments

Comments
 (0)