Skip to content

Commit

Permalink
Refactor joybus_command structs
Browse files Browse the repository at this point in the history
  • Loading branch information
meeq committed Sep 6, 2023
1 parent e287660 commit e696f01
Show file tree
Hide file tree
Showing 9 changed files with 441 additions and 563 deletions.
2 changes: 1 addition & 1 deletion include/controller.h
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ typedef struct controller_origin_data

/// @cond

__attribute__((deprecated("use joybus_exec_command instead")))
__attribute__((deprecated("use joybus_exec_raw_command instead")))
void execute_raw_command( int controller, int command, int bytesout, int bytesin, unsigned char *out, unsigned char *in );

__attribute__((deprecated("use joypad_read_n64_inputs_sync instead")))
Expand Down
36 changes: 27 additions & 9 deletions include/joybus.h
Original file line number Diff line number Diff line change
Expand Up @@ -213,16 +213,34 @@ typedef uint16_t joybus_identifier_t;
void joybus_exec( const void * inblock, void * outblock );

/**
* @brief Executes a Joybus command on the given port.
* @brief Executes a Joybus command synchronously on the given port.
*
* @param port Joybus port (0-4).
* @param command_id Joybus command identifier.
* @param send_len Number of bytes in the send payload (not including command ID).
* @param recv_len Number of bytes in the recieve payload.
* @param send_data Buffer of send_len bytes to send in the Joybus command.
* @param recv_data Buffer of recv_len bytes to receive from the Joybus reply.
*/
void joybus_exec_command(
* This function is not a stable feature of the libdragon API and should be
* considered experimental!
*
* The usage of this function will likely change as a result of the ongoing
* effort to integrate the multitasking kernel with asynchronous operations.
*
* @note This function is slow: it blocks until the command completes.
* Calling this function multiple times per frame may cause
* audio and video stuttering.
*
* @param port
* The Joybus port (0-4) to send the command to.
* @param command_id
* Joybus command identifier. See @ref JOYBUS_COMMAND_ID.
* @param send_len
* Number of bytes in the send payload
* (not including command ID).
* @param recv_len
* Number of bytes in the recieve payload.
* @param[in] send_data
* Buffer of send_len bytes to send to the Joybus device
* (not including command ID byte).
* @param[out] recv_data
* Buffer of recv_len bytes for reply from the Joybus device.
*/
void joybus_exec_raw_command(
int port,
uint8_t command_id,
uint8_t send_len,
Expand Down
4 changes: 2 additions & 2 deletions src/controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -212,11 +212,11 @@ void controller_read_gc_origin( struct controller_origin_data * outdata )
* @param[out] in
* The result bytes returned by the operation
*
* @deprecated Use #joybus_exec_command instead
* @deprecated Use #joybus_exec_raw_command instead
*/
void execute_raw_command( int controller, int command, int bytesout, int bytesin, unsigned char *out, unsigned char *in )
{
joybus_exec_command(controller, command, bytesout, bytesin, out, in);
joybus_exec_raw_command(controller, command, bytesout, bytesin, out, in);
}

/**
Expand Down
30 changes: 13 additions & 17 deletions src/joybus.c
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ void joybus_exec( const void * input, void * output )
}
}

void joybus_exec_command(
void joybus_exec_raw_command(
int port,
uint8_t command_id,
uint8_t send_len,
Expand All @@ -337,7 +337,7 @@ void joybus_exec_command(
uint8_t *recv_data
)
{
assertf((port >= 0) && (port < JOYBUS_PORT_COUNT), "Invalid Joybus port");
assertf((port >= 0) && (port < JOYBUS_PORT_COUNT), "Invalid Joybus port %d", port);
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
uint8_t output[JOYBUS_BLOCK_SIZE] = {0};
size_t i = port;
Expand All @@ -347,10 +347,7 @@ void joybus_exec_command(
input[i++] = command_id;
// Copy the send_data into the input buffer
memcpy( &input[i], send_data, send_len );
i += send_len;
// Fill the recv_data with 0xFF
memset( &input[i], 0xFF, recv_len );
i += recv_len;
i += send_len + recv_len;
// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;
Expand All @@ -363,27 +360,26 @@ void joybus_exec_command(
void joybus_send_game_id(uint64_t rom_check_code, uint8_t media_format, uint8_t region_code)
{
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
size_t i = 0;

const joybus_cmd_n64_game_id_t send_cmd = {
.send_len = sizeof(send_cmd.send_bytes),
.recv_len = sizeof(send_cmd.recv_bytes),
const joybus_cmd_n64_game_id_t cmd = { .send = {
.command = JOYBUS_COMMAND_ID_N64_GAME_ID,
.rom_check_code = rom_check_code,
.media_format = media_format,
.region_code = region_code,
};
memcpy(&input[i], &send_cmd, sizeof(send_cmd));
i += sizeof(send_cmd);

} };
size_t i = 0;
// Set the command metadata
input[i++] = sizeof(cmd.send);
input[i++] = sizeof(cmd.recv);
// Copy the send_data into the input buffer
memcpy(&input[i], &cmd, sizeof(cmd));
i += sizeof(cmd);
// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;

// This is a fire-and-forget command with no expected response
joybus_exec_async(input, NULL, NULL);
}

void joybus_clear_game_id(void) { joybus_send_game_id(0, 0, 0); }
void joybus_clear_game_id( void ) { joybus_send_game_id(0, 0, 0); }

/** @} */ /* joybus */
120 changes: 38 additions & 82 deletions src/joybus_accessory.c
Original file line number Diff line number Diff line change
Expand Up @@ -121,23 +121,22 @@ void joybus_accessory_read_async(
)
{
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
size_t i = port;

const joybus_cmd_n64_accessory_read_port_t send_cmd = {
.send_len = sizeof(send_cmd.send_bytes),
.recv_len = sizeof(send_cmd.recv_bytes),
joybus_cmd_n64_accessory_read_port_t cmd = { .send = {
.command = JOYBUS_COMMAND_ID_N64_ACCESSORY_READ,
.addr_checksum = joybus_accessory_calculate_addr_checksum(addr),
};
} };
size_t i = port;
// Set the command metadata
input[i++] = sizeof(cmd.send);
input[i++] = sizeof(cmd.recv);
// Micro-optimization: Minimize copy length
const size_t recv_offset = offsetof(typeof(send_cmd), recv_bytes);
memcpy(&input[i], &send_cmd, recv_offset);
i += sizeof(send_cmd);

const size_t recv_offset = offsetof(typeof(cmd), recv);
memcpy(&input[i], &cmd.send, recv_offset);
i += sizeof(cmd);
// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;

// Execute the Joybus operation asynchronously
joybus_exec_async(input, callback, ctx);
}

Expand All @@ -159,24 +158,23 @@ void joybus_accessory_write_async(
)
{
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
size_t i = port;

const joybus_cmd_n64_accessory_write_port_t send_cmd = {
.send_len = sizeof(send_cmd.send_bytes),
.recv_len = sizeof(send_cmd.recv_bytes),
joybus_cmd_n64_accessory_write_port_t cmd = { .send = {
.command = JOYBUS_COMMAND_ID_N64_ACCESSORY_WRITE,
.addr_checksum = joybus_accessory_calculate_addr_checksum(addr),
};
} };
size_t i = port;
// Set the command metadata
input[i++] = sizeof(cmd.send);
input[i++] = sizeof(cmd.recv);
// Micro-optimization: Minimize copy length
const size_t data_offset = offsetof(typeof(send_cmd), data);
memcpy(&input[i], &send_cmd, data_offset);
memcpy(&input[i + data_offset], data, sizeof(send_cmd.data));
i += sizeof(send_cmd);

const size_t data_offset = offsetof(typeof(cmd.send), data);
memcpy(&input[i], &cmd.send, data_offset);
memcpy(&input[i + data_offset], data, sizeof(cmd.send.data));
i += sizeof(cmd);
// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;

// Execute the Joybus operation asynchronously
joybus_exec_async(input, callback, ctx);
}

Expand All @@ -191,39 +189,18 @@ void joybus_accessory_write_async(
* @retval #JOYBUS_ACCESSORY_IO_STATUS_NO_PAK No accessory is present.
* @retval #JOYBUS_ACCESSORY_IO_STATUS_BAD_CRC The data was not read successfully.
*/
int joybus_accessory_read_sync(
int port,
uint16_t addr,
uint8_t *data
)
int joybus_accessory_read_sync(int port, uint16_t addr, uint8_t *data)
{
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
uint8_t output[JOYBUS_BLOCK_SIZE] = {0};
size_t i = port;

const joybus_cmd_n64_accessory_read_port_t send_cmd = {
.send_len = sizeof(send_cmd.send_bytes),
.recv_len = sizeof(send_cmd.recv_bytes),
joybus_cmd_n64_accessory_read_port_t cmd = { .send = {
.command = JOYBUS_COMMAND_ID_N64_ACCESSORY_READ,
.addr_checksum = joybus_accessory_calculate_addr_checksum(addr),
};
// Micro-optimization: Minimize copy length
const size_t recv_offset = offsetof(typeof(send_cmd), recv_bytes);
memcpy(&input[i], &send_cmd, recv_offset);
i += sizeof(send_cmd);

// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;

// Execute and wait for the Joybus operation
joybus_exec(input, output);
const joybus_cmd_n64_accessory_read_port_t *recv_cmd =
(void *)&output[port];

// Copy the data and check the CRC to see if the read was successful
memcpy(data, recv_cmd->data, sizeof(recv_cmd->data));
return joybus_accessory_compare_data_crc(data, recv_cmd->data_crc);
} };
// Execute the command
joybus_cmd_exec(port, cmd);
// Copy the data from the command receive buffer
memcpy(data, cmd.recv.data, sizeof(cmd.recv.data));
// Check the CRC against the data to see if the read was successful
return joybus_accessory_compare_data_crc(data, cmd.recv.data_crc);
}

/**
Expand All @@ -237,39 +214,18 @@ int joybus_accessory_read_sync(
* @retval #JOYBUS_ACCESSORY_IO_STATUS_NO_PAK No accessory is present.
* @retval #JOYBUS_ACCESSORY_IO_STATUS_BAD_CRC The data was not written successfully.
*/
int joybus_accessory_write_sync(
int port,
uint16_t addr,
const uint8_t *data
)
int joybus_accessory_write_sync(int port, uint16_t addr, const uint8_t *data)
{
uint8_t input[JOYBUS_BLOCK_SIZE] = {0};
uint8_t output[JOYBUS_BLOCK_SIZE] = {0};
size_t i = port;

const joybus_cmd_n64_accessory_write_port_t send_cmd = {
.send_len = sizeof(send_cmd.send_bytes),
.recv_len = sizeof(send_cmd.recv_bytes),
joybus_cmd_n64_accessory_write_port_t cmd = { .send = {
.command = JOYBUS_COMMAND_ID_N64_ACCESSORY_WRITE,
.addr_checksum = joybus_accessory_calculate_addr_checksum(addr),
};
// Micro-optimization: Minimize copy length
const size_t data_offset = offsetof(typeof(send_cmd), data);
memcpy(&input[i], &send_cmd, data_offset);
memcpy(&input[i + data_offset], data, sizeof(send_cmd.data));
i += sizeof(send_cmd);

// Close out the Joybus operation block
input[i] = 0xFE;
input[sizeof(input) - 1] = 0x01;

// Execute and wait for the Joybus operation
joybus_exec(input, output);
const joybus_cmd_n64_accessory_write_port_t *recv_cmd =
(void *)&output[port];

} };
// Copy the data to the command send buffer
memcpy(cmd.send.data, data, sizeof(cmd.send.data));
// Execute the command
joybus_cmd_exec(port, cmd);
// Check the data CRC to see if the write was successful
return joybus_accessory_compare_data_crc(data, recv_cmd->data_crc);
return joybus_accessory_compare_data_crc(data, cmd.recv.data_crc);
}

/** @} */ /* joybus */
Loading

0 comments on commit e696f01

Please sign in to comment.