diff --git a/include/controller.h b/include/controller.h index c164fa84d9..0c9f145c17 100755 --- a/include/controller.h +++ b/include/controller.h @@ -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"))) diff --git a/include/joybus.h b/include/joybus.h index 4f7bac9566..3d0eb156e5 100644 --- a/include/joybus.h +++ b/include/joybus.h @@ -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, diff --git a/src/controller.c b/src/controller.c index 5a9d8ecdae..87637e8ed5 100755 --- a/src/controller.c +++ b/src/controller.c @@ -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); } /** diff --git a/src/joybus.c b/src/joybus.c index af93472e32..1dd4235b15 100644 --- a/src/joybus.c +++ b/src/joybus.c @@ -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, @@ -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; @@ -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; @@ -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 */ diff --git a/src/joybus_accessory.c b/src/joybus_accessory.c index cc1bd0d4b5..4efcb1c47b 100644 --- a/src/joybus_accessory.c +++ b/src/joybus_accessory.c @@ -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); } @@ -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); } @@ -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); } /** @@ -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 */ diff --git a/src/joybus_commands.h b/src/joybus_commands.h index 9f2aa952d8..00e966884e 100644 --- a/src/joybus_commands.h +++ b/src/joybus_commands.h @@ -142,50 +142,22 @@ extern "C" { */ typedef struct __attribute__((packed)) joybus_cmd_n64_accessory_read_port_s { - /** @brief Length of send data in bytes. (0x03) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x21) */ - uint8_t recv_len; - /// @cond - union + /** @brief "N64 Accessory Read" command send data */ + struct __attribute__((packed)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x03]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_ACCESSORY_READ) */ - uint8_t command; - /** @brief Accessory address with 5-bit checksum. */ - uint16_t addr_checksum; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond - /// @cond - union + /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_ACCESSORY_READ) */ + uint8_t command; + /** @brief Accessory address with 5-bit checksum. */ + uint16_t addr_checksum; + } send; + /** @brief "N64 Accessory Read" command receive data */ + struct __attribute__((packed)) { - /// @endcond - /** @brief Raw recv_data bytes. */ - uint8_t recv_bytes[0x21]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief 32-byte payload of data read from the accessory. */ - uint8_t data[JOYBUS_ACCESSORY_DATA_SIZE]; - /** @brief CRC8 checksum of the data for verification. */ - uint8_t data_crc; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + /** @brief 32-byte payload of data read from the accessory. */ + uint8_t data[JOYBUS_ACCESSORY_DATA_SIZE]; + /** @brief CRC8 checksum of the data for verification. */ + uint8_t data_crc; + } recv; } joybus_cmd_n64_accessory_read_port_t; /** @@ -213,43 +185,23 @@ typedef joybus_cmd_n64_accessory_read_port_t joybus_cmd_gba_link_cable_read_port */ typedef struct __attribute__((packed)) joybus_cmd_n64_accessory_write_port_s { - /** @brief Length of send data in bytes. (0x23) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x01) */ - uint8_t recv_len; - /// @cond - union + /** @brief "N64 Accessory Write" command send data */ + struct __attribute__((packed)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x23]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_ACCESSORY_WRITE) */ - uint8_t command; - /** @brief Accessory address with 5-bit checksum. */ - uint16_t addr_checksum; - /** @brief 32-byte payload of data to write to the accessory. */ - uint8_t data[JOYBUS_ACCESSORY_DATA_SIZE]; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond - /// @cond - union + /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_ACCESSORY_WRITE) */ + uint8_t command; + /** @brief Accessory address with 5-bit checksum. */ + uint16_t addr_checksum; + /** @brief 32-byte payload of data to write to the accessory. */ + uint8_t data[JOYBUS_ACCESSORY_DATA_SIZE]; + + } send; + /** @brief "N64 Accessory Write" command receive data */ + struct __attribute__((packed)) { - /// @endcond - /** @brief Raw recv_data bytes. */ - uint8_t recv_bytes[0x01]; /** @brief CRC8 checksum of the written data for verification. */ uint8_t data_crc; - /// @cond - }; - /// @endcond + } recv; } joybus_cmd_n64_accessory_write_port_t; /** @@ -277,41 +229,20 @@ typedef joybus_cmd_n64_accessory_write_port_t joybus_cmd_gba_link_cable_write_po */ typedef struct __attribute__((packed)) joybus_cmd_identify_port_s { - /** @brief Length of send data in bytes. (0x01) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x03) */ - uint8_t recv_len; - /// @cond - union + /** @brief "Identify" command send data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x01]; /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_IDENTIFY) */ uint8_t command; - /// @cond - }; - /// @endcond - /// @cond - union + } send; + /** @brief "Identify" command receive data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw recv_data bytes. */ - uint8_t recv_bytes[0x03]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief Joybus device identifier */ - uint16_t identifier; - /** @brief Joybus device status byte */ - uint8_t status; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + /** @brief Joybus device identifier */ + uint16_t identifier; + /** @brief Joybus device status byte */ + uint8_t status; + } recv; } joybus_cmd_identify_port_t; /** @@ -330,46 +261,30 @@ typedef joybus_cmd_identify_port_t joybus_cmd_reset_port_t; */ typedef struct __attribute__((packed)) joybus_cmd_n64_game_id_s { - /** @brief Length of send data in bytes. (0x0A) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x01) */ - uint8_t recv_len; - /// @cond - union + /** @brief "N64 Game ID" command send data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x0B]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_GAME_ID) */ - uint8_t command; - /** @brief ROM check code (ROM header bytes 0x10-0x17). - * - * 64-bit check code calculated on 1 Mbyte of ROM contents starting from offset 0x1000. - * - * Sometimes these 8 bytes are referred to as "CRC HI/LO" or "CRC1/2". - */ - uint64_t rom_check_code; - /** @brief Media category code (ROM header byte 0x3B) */ - uint8_t media_format; - /** @brief Region code (ROM header byte 0x3E) */ - uint8_t region_code; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_GAME_ID) */ + uint8_t command; + /** @brief ROM check code (ROM header bytes 0x10-0x17). + * + * 64-bit check code calculated on 1 Mbyte of ROM contents starting from offset 0x1000. + * + * Sometimes these 8 bytes are referred to as "CRC HI/LO" or "CRC1/2". + */ + uint64_t rom_check_code; + /** @brief Media category code (ROM header byte 0x3B) */ + uint8_t media_format; + /** @brief Region code (ROM header byte 0x3E) */ + uint8_t region_code; + } send; /** - * @brief Raw recv_data bytes (unused) + * @brief (unused) * * As per PixelFX's documentation, N64Digital does not respond to this command. * N64Digital only acts as a bus sniffer. */ - uint8_t recv_bytes[0x01]; + uint8_t recv; } joybus_cmd_n64_game_id_t; /** @@ -379,55 +294,34 @@ typedef struct __attribute__((packed)) joybus_cmd_n64_game_id_s */ typedef struct __attribute__((packed)) joybus_cmd_n64_controller_read_port_s { - /** @brief Length of send data in bytes. (0x01) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x04) */ - uint8_t recv_len; - /// @cond - union + /** @brief "N64 Controller Read" command send data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x01]; /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_N64_CONTROLLER_READ) */ uint8_t command; - /// @cond - }; - /// @endcond - /// @cond - union + } send; + /** @brief "N64 Controller Read" command receive data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw recv_data bytes. */ - uint8_t recv_bytes[0x04]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - unsigned a : 1; ///< A button. - unsigned b : 1; ///< B button. - unsigned z : 1; ///< Z button. - unsigned start : 1; ///< Start button. - unsigned d_up : 1; ///< D-Pad up. - unsigned d_down : 1; ///< D-Pad down. - unsigned d_left : 1; ///< D-Pad left. - unsigned d_right : 1; ///< D-Pad right. - unsigned reset : 1; ///< Reset flag. - unsigned : 1; ///< Unused padding. - unsigned l : 1; ///< L shoulder button. - unsigned r : 1; ///< R shoulder button. - unsigned c_up : 1; ///< C-Pad up. - unsigned c_down : 1; ///< C-Pad down. - unsigned c_left : 1; ///< C-Pad left. - unsigned c_right : 1; ///< C-Pad right. - signed stick_x : 8; ///< Analog stick X-axis. - signed stick_y : 8; ///< Analog stick Y-axis. - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + unsigned a : 1; ///< A button. + unsigned b : 1; ///< B button. + unsigned z : 1; ///< Z button. + unsigned start : 1; ///< Start button. + unsigned d_up : 1; ///< D-Pad up. + unsigned d_down : 1; ///< D-Pad down. + unsigned d_left : 1; ///< D-Pad left. + unsigned d_right : 1; ///< D-Pad right. + unsigned reset : 1; ///< Reset flag. + unsigned : 1; ///< Unused padding. + unsigned l : 1; ///< L shoulder button. + unsigned r : 1; ///< R shoulder button. + unsigned c_up : 1; ///< C-Pad up. + unsigned c_down : 1; ///< C-Pad down. + unsigned c_left : 1; ///< C-Pad left. + unsigned c_right : 1; ///< C-Pad right. + signed stick_x : 8; ///< Analog stick X-axis. + signed stick_y : 8; ///< Analog stick Y-axis. + } recv; } joybus_cmd_n64_controller_read_port_t; /** @@ -437,69 +331,41 @@ typedef struct __attribute__((packed)) joybus_cmd_n64_controller_read_port_s */ typedef struct __attribute__((packed)) joybus_cmd_gcn_controller_read_port_s { - /** @brief Length of send data in bytes. (0x03) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x08) */ - uint8_t recv_len; - /// @cond - union + /** @brief "GameCube Controller Read" command send data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw send_data bytes. */ - uint8_t send_bytes[0x03]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ) */ - uint8_t command; - /** @brief GameCube controller mode (0-3) */ - uint8_t mode; - /** @brief Rumble motor control */ - uint8_t rumble; - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond - /// @cond - union + /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ) */ + uint8_t command; + /** @brief GameCube controller mode (0-3) */ + uint8_t mode; + /** @brief Rumble motor control */ + uint8_t rumble; + } send; + /** @brief "GameCube Controller Read" command receive data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw recv_data bytes. */ - uint8_t recv_bytes[0x08]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - unsigned : 2; ///< Unused padding. - unsigned check_origin : 1; ///< Origin check flag. - unsigned start : 1; ///< Start button. - unsigned y : 1; ///< Y button. - unsigned x : 1; ///< X button. - unsigned b : 1; ///< B button. - unsigned a : 1; ///< A button. - unsigned use_origin : 1; ///< Origin use flag. - unsigned l : 1; ///< L button. - unsigned r : 1; ///< R button. - unsigned z : 1; ///< Z button. - unsigned d_up : 1; ///< D-Pad up. - unsigned d_down : 1; ///< D-Pad down. - unsigned d_right : 1; ///< D-Pad right. - unsigned d_left : 1; ///< D-Pad left. - unsigned stick_x : 8; ///< Analog stick X-axis. - unsigned stick_y : 8; ///< Analog stick Y-axis. - unsigned cstick_x : 8; ///< Analog C-Stick X-axis. - unsigned cstick_y : 8; ///< Analog C-Stick Y-axis. - unsigned analog_l : 8; ///< Analog L trigger. - unsigned analog_r : 8; ///< Analog R trigger. - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + unsigned : 2; ///< Unused padding. + unsigned check_origin : 1; ///< Origin check flag. + unsigned start : 1; ///< Start button. + unsigned y : 1; ///< Y button. + unsigned x : 1; ///< X button. + unsigned b : 1; ///< B button. + unsigned a : 1; ///< A button. + unsigned use_origin : 1; ///< Origin use flag. + unsigned l : 1; ///< L button. + unsigned r : 1; ///< R button. + unsigned z : 1; ///< Z button. + unsigned d_up : 1; ///< D-Pad up. + unsigned d_down : 1; ///< D-Pad down. + unsigned d_right : 1; ///< D-Pad right. + unsigned d_left : 1; ///< D-Pad left. + unsigned stick_x : 8; ///< Analog stick X-axis. + unsigned stick_y : 8; ///< Analog stick Y-axis. + unsigned cstick_x : 8; ///< Analog C-Stick X-axis. + unsigned cstick_y : 8; ///< Analog C-Stick Y-axis. + unsigned analog_l : 8; ///< Analog L trigger. + unsigned analog_r : 8; ///< Analog R trigger. + } recv; } joybus_cmd_gcn_controller_read_port_t; /** @@ -509,60 +375,39 @@ typedef struct __attribute__((packed)) joybus_cmd_gcn_controller_read_port_s */ typedef struct __attribute__((packed)) joybus_cmd_gcn_controller_read_long_port_s { - /** @brief Length of send data in bytes. (0x01) */ - uint8_t send_len; - /** @brief Length of recv data in bytes. (0x0A) */ - uint8_t recv_len; - /// @cond - union + /** @brief "GameCube Controller Long Read" command send data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw send_data bytes */ - uint8_t send_bytes[0x01]; /** @brief Joybus command ID (#JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ_LONG) */ uint8_t command; - /// @cond - }; - /// @endcond - /// @cond - union + } send; + /** @brief "GameCube Controller Long Read" command receive data */ + struct __attribute__((__packed__)) { - /// @endcond - /** @brief Raw recv_data bytes */ - uint8_t recv_bytes[0x0A]; - /// @cond - struct __attribute__((__packed__)) - { - /// @endcond - unsigned : 2; ///< Unused padding. - unsigned check_origin : 1; ///< Check origin flag. - unsigned start : 1; ///< Start button. - unsigned y : 1; ///< Y button. - unsigned x : 1; ///< X button. - unsigned b : 1; ///< B button. - unsigned a : 1; ///< A button. - unsigned use_origin : 1; ///< Use origin flag. - unsigned l : 1; ///< L button. - unsigned r : 1; ///< R button. - unsigned z : 1; ///< Z button. - unsigned d_up : 1; ///< D-Pad up. - unsigned d_down : 1; ///< D-Pad down. - unsigned d_right : 1; ///< D-Pad right. - unsigned d_left : 1; ///< D-Pad left. - unsigned stick_x : 8; ///< Analog stick X-axis. - unsigned stick_y : 8; ///< Analog stick Y-axis. - unsigned cstick_x : 8; ///< Analog C-Stick X-axis. - unsigned cstick_y : 8; ///< Analog C-Stick Y-axis. - unsigned analog_l : 8; ///< Analog L trigger. - unsigned analog_r : 8; ///< Analog R trigger. - unsigned analog_a : 8; ///< Analog A button. - unsigned analog_b : 8; ///< Analog B button. - /// @cond - }; - /// @endcond - /// @cond - }; - /// @endcond + unsigned : 2; ///< Unused padding. + unsigned check_origin : 1; ///< Check origin flag. + unsigned start : 1; ///< Start button. + unsigned y : 1; ///< Y button. + unsigned x : 1; ///< X button. + unsigned b : 1; ///< B button. + unsigned a : 1; ///< A button. + unsigned use_origin : 1; ///< Use origin flag. + unsigned l : 1; ///< L button. + unsigned r : 1; ///< R button. + unsigned z : 1; ///< Z button. + unsigned d_up : 1; ///< D-Pad up. + unsigned d_down : 1; ///< D-Pad down. + unsigned d_right : 1; ///< D-Pad right. + unsigned d_left : 1; ///< D-Pad left. + unsigned stick_x : 8; ///< Analog stick X-axis. + unsigned stick_y : 8; ///< Analog stick Y-axis. + unsigned cstick_x : 8; ///< Analog C-Stick X-axis. + unsigned cstick_y : 8; ///< Analog C-Stick Y-axis. + unsigned analog_l : 8; ///< Analog L trigger. + unsigned analog_r : 8; ///< Analog R trigger. + unsigned analog_a : 8; ///< Analog A button. + unsigned analog_b : 8; ///< Analog B button. + } recv; } joybus_cmd_gcn_controller_read_long_port_t; /** diff --git a/src/joybus_internal.h b/src/joybus_internal.h index 17084a2dbb..a726a45086 100644 --- a/src/joybus_internal.h +++ b/src/joybus_internal.h @@ -8,12 +8,77 @@ #define __LIBDRAGON_JOYBUS_INTERNAL_H #include +#include /** * @addtogroup joybus * @{ */ +/** + * @brief Execute a Joybus command synchronously on the given port. + * + * This function is intended to be called internally by the + * #joybus_cmd_exec macro. + * + * @param port + * The Joybus port (0-4) to send the command to. + * @param send_len + * Number of bytes in the send_data request payload + * (including command ID). + * @param recv_len + * Number of bytes in the recv_data response payload. + * @param[in] send_data + * Buffer of send_len bytes to send to the Joybus device + * (including command ID byte). + * @param[out] recv_data + * Buffer of recv_len bytes for the reply from the Joybus device. + */ +inline void __joybus_cmd_exec( + int port, + size_t send_len, + size_t recv_len, + const uint8_t *send_data, + uint8_t *recv_data +) +{ + uint8_t input[JOYBUS_BLOCK_SIZE] = {0}; + uint8_t output[JOYBUS_BLOCK_SIZE] = {0}; + size_t i = port; + // Set the command metadata + input[i++] = send_len; + input[i++] = recv_len; + // Copy the send_data into the input buffer + memcpy(&input[i], send_data, send_len); + i += send_len + recv_len; + // Close out the Joybus operation block + input[i] = 0xFE; + input[sizeof(input) - 1] = 0x01; + // Execute the Joybus operation + joybus_exec(input, output); + // Copy recv_data from the output buffer + memcpy(recv_data, &output[i - recv_len], recv_len); +} + +/** + * @brief Execute a Joybus command struct synchronously. + * + * This macro is a convenience wrapper around #__joybus_cmd_exec + * that automatically sets the send_len and recv_len based on the + * size of the send and recv fields of the command struct. + * + * @param port The Joybus port to execute the command on. + * @param[in,out] cmd The command struct to execute with. + */ +#define joybus_cmd_exec(port, cmd) \ + __joybus_cmd_exec( \ + port, \ + sizeof(cmd.send), \ + sizeof(cmd.recv), \ + (void *)&cmd.send, \ + (void *)&cmd.recv \ + ) + /** @brief Callback function signature for #joybus_exec_async */ typedef void (*joybus_callback_t)(uint64_t *out_dwords, void *ctx); diff --git a/src/joypad.c b/src/joypad.c index ca536bf7e8..cf0372a3f0 100644 --- a/src/joypad.c +++ b/src/joypad.c @@ -159,42 +159,42 @@ static void joypad_device_reset(joypad_port_t port, joybus_identifier_t identifi /** * @brief Convert a "N64 Controller Read" command response to Joypad inputs. * - * @param[in] recv_cmd "N64 Controller Read" command response. + * @param[in] cmd "N64 Controller Read" command response. * @return Joypad inputs structure (#joypad_inputs_t). */ static joypad_inputs_t joypad_inputs_from_n64_controller_read( - const joybus_cmd_n64_controller_read_port_t *recv_cmd + const joybus_cmd_n64_controller_read_port_t *cmd ) { // Emulate analog C-stick based on digital C-buttons - int c_x_direction = recv_cmd->c_right - recv_cmd->c_left; - int c_y_direction = recv_cmd->c_down - recv_cmd->c_up; + int c_x_direction = cmd->recv.c_right - cmd->recv.c_left; + int c_y_direction = cmd->recv.c_down - cmd->recv.c_up; int cstick_x = c_x_direction * JOYPAD_RANGE_GCN_CSTICK_MAX; int cstick_y = c_y_direction * JOYPAD_RANGE_GCN_CSTICK_MAX; // Emulate analog triggers based on digital shoulder buttons - int analog_l = recv_cmd->l ? JOYPAD_RANGE_GCN_TRIGGER_MAX : 0; - int analog_r = recv_cmd->r ? JOYPAD_RANGE_GCN_TRIGGER_MAX : 0; + int analog_l = cmd->recv.l ? JOYPAD_RANGE_GCN_TRIGGER_MAX : 0; + int analog_r = cmd->recv.r ? JOYPAD_RANGE_GCN_TRIGGER_MAX : 0; return (joypad_inputs_t){ - .a = recv_cmd->a, - .b = recv_cmd->b, - .z = recv_cmd->z, - .start = recv_cmd->start, - .d_up = recv_cmd->d_up, - .d_down = recv_cmd->d_down, - .d_left = recv_cmd->d_left, - .d_right = recv_cmd->d_right, + .a = cmd->recv.a, + .b = cmd->recv.b, + .z = cmd->recv.z, + .start = cmd->recv.start, + .d_up = cmd->recv.d_up, + .d_down = cmd->recv.d_down, + .d_left = cmd->recv.d_left, + .d_right = cmd->recv.d_right, .y = 0, .x = 0, - .l = recv_cmd->l, - .r = recv_cmd->r, - .c_up = recv_cmd->c_up, - .c_down = recv_cmd->c_down, - .c_left = recv_cmd->c_left, - .c_right = recv_cmd->c_right, - .stick_x = recv_cmd->stick_x, - .stick_y = recv_cmd->stick_y, + .l = cmd->recv.l, + .r = cmd->recv.r, + .c_up = cmd->recv.c_up, + .c_down = cmd->recv.c_down, + .c_left = cmd->recv.c_left, + .c_right = cmd->recv.c_right, + .stick_x = cmd->recv.stick_x, + .stick_y = cmd->recv.stick_y, .cstick_x = cstick_x, .cstick_y = cstick_y, .analog_l = analog_l, @@ -205,12 +205,12 @@ static joypad_inputs_t joypad_inputs_from_n64_controller_read( /** * @brief Convert a "GameCube Controller Read" command response to Joypad inputs. * - * @param[in] recv_cmd "GameCube Controller Read" command response. + * @param[in] cmd "GameCube Controller Read" command response. * @param[in] origin Pointer to GameCube controller origin structure (or NULL for defaults). * @return Joypad inputs structure (#joypad_inputs_t). */ static joypad_inputs_t joypad_inputs_from_gcn_controller_read( - const joybus_cmd_gcn_controller_read_port_t *recv_cmd, + const joybus_cmd_gcn_controller_read_port_t *cmd, const joypad_gcn_origin_t *origin ) { @@ -222,26 +222,26 @@ static joypad_inputs_t joypad_inputs_from_gcn_controller_read( static const int cstick_threshold = JOYPAD_RANGE_GCN_CSTICK_MAX / 2; // Bias the analog values with the corresponding origin - int stick_x = CLAMP_ANALOG_STICK(recv_cmd->stick_x - origin->stick_x); - int stick_y = CLAMP_ANALOG_STICK(recv_cmd->stick_y - origin->stick_y); - int cstick_x = CLAMP_ANALOG_STICK(recv_cmd->cstick_x - origin->cstick_x); - int cstick_y = CLAMP_ANALOG_STICK(recv_cmd->cstick_y - origin->cstick_y); - int analog_l = CLAMP_ANALOG_TRIGGER(recv_cmd->analog_l - origin->analog_l); - int analog_r = CLAMP_ANALOG_TRIGGER(recv_cmd->analog_r - origin->analog_r); + int stick_x = CLAMP_ANALOG_STICK(cmd->recv.stick_x - origin->stick_x); + int stick_y = CLAMP_ANALOG_STICK(cmd->recv.stick_y - origin->stick_y); + int cstick_x = CLAMP_ANALOG_STICK(cmd->recv.cstick_x - origin->cstick_x); + int cstick_y = CLAMP_ANALOG_STICK(cmd->recv.cstick_y - origin->cstick_y); + int analog_l = CLAMP_ANALOG_TRIGGER(cmd->recv.analog_l - origin->analog_l); + int analog_r = CLAMP_ANALOG_TRIGGER(cmd->recv.analog_r - origin->analog_r); return (joypad_inputs_t){ - .a = recv_cmd->a, - .b = recv_cmd->b, - .z = recv_cmd->z, - .start = recv_cmd->start, - .d_up = recv_cmd->d_up, - .d_down = recv_cmd->d_down, - .d_left = recv_cmd->d_left, - .d_right = recv_cmd->d_right, - .y = recv_cmd->y, - .x = recv_cmd->x, - .l = recv_cmd->l, - .r = recv_cmd->r, + .a = cmd->recv.a, + .b = cmd->recv.b, + .z = cmd->recv.z, + .start = cmd->recv.start, + .d_up = cmd->recv.d_up, + .d_down = cmd->recv.d_down, + .d_left = cmd->recv.d_left, + .d_right = cmd->recv.d_right, + .y = cmd->recv.y, + .x = cmd->recv.x, + .l = cmd->recv.l, + .r = cmd->recv.r, .c_up = cstick_y > +cstick_threshold, .c_down = cstick_y < -cstick_threshold, .c_left = cstick_x < -cstick_threshold, @@ -268,12 +268,13 @@ static void joypad_gcn_controller_rumble_toggle(joypad_port_t port, bool active) if (joypad_read_input_valid) { // Set rumble active flag on cached GameCube controller read command - size_t cmd_offset = joypad_read_input_offsets[port]; - joybus_cmd_gcn_controller_read_port_t *read_cmd; - read_cmd = (void *)&joypad_read_input[cmd_offset]; - assert(read_cmd->send_len == sizeof(read_cmd->send_bytes)); - assert(read_cmd->command == JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ); - read_cmd->rumble = active; + const size_t cmd_offset = joypad_read_input_offsets[port]; + const size_t send_len_offset = cmd_offset + JOYBUS_COMMAND_OFFSET_SEND_LEN; + joybus_cmd_gcn_controller_read_port_t *cmd; + assert(joypad_read_input[send_len_offset] == sizeof(cmd->send)); + cmd = (void *)&joypad_read_input[cmd_offset + JOYBUS_COMMAND_METADATA_SIZE]; + assert(cmd->send.command == JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ); + cmd->send.rumble = active; } } @@ -286,7 +287,7 @@ static void joypad_gcn_controller_rumble_toggle(joypad_port_t port, bool active) static void joypad_gcn_origin_callback(uint64_t *out_dwords, void *ctx) { const uint8_t *out_bytes = (void *)out_dwords; - const joybus_cmd_gcn_controller_origin_port_t *recv_cmd; + const joybus_cmd_gcn_controller_origin_port_t *cmd; size_t i = 0; JOYPAD_PORT_FOREACH (port) @@ -300,20 +301,20 @@ static void joypad_gcn_origin_callback(uint64_t *out_dwords, void *ctx) else if (joypad_devices_hot[port].style != JOYPAD_STYLE_GCN) { // Skip this port - i += sizeof(*recv_cmd); + i += JOYBUS_COMMAND_METADATA_SIZE + sizeof(*cmd); } else { - recv_cmd = (void *)&out_bytes[i]; - i += sizeof(*recv_cmd); + cmd = (void *)&out_bytes[i + JOYBUS_COMMAND_METADATA_SIZE]; + i += JOYBUS_COMMAND_METADATA_SIZE + sizeof(*cmd); joypad_origins_hot[port] = (joypad_gcn_origin_t){ - .stick_x = recv_cmd->stick_x, - .stick_y = recv_cmd->stick_y, - .cstick_x = recv_cmd->cstick_x, - .cstick_y = recv_cmd->cstick_y, - .analog_l = recv_cmd->analog_l, - .analog_r = recv_cmd->analog_r, + .stick_x = cmd->recv.stick_x, + .stick_y = cmd->recv.stick_y, + .cstick_x = cmd->recv.cstick_x, + .cstick_y = cmd->recv.cstick_y, + .analog_l = cmd->recv.analog_l, + .analog_r = cmd->recv.analog_r, }; } } @@ -333,21 +334,22 @@ static void joypad_gcn_origin_check_async(void) uint8_t * const input = (void *)joypad_gcn_origin_input; if (!joypad_gcn_origin_input_valid) { - const joybus_cmd_gcn_controller_origin_port_t send_cmd = { - .send_len = sizeof(send_cmd.send_bytes), - .recv_len = sizeof(send_cmd.recv_bytes), + const joybus_cmd_gcn_controller_origin_port_t cmd = { .send = { .command = JOYBUS_COMMAND_ID_GCN_CONTROLLER_ORIGIN, - }; - const size_t recv_offset = offsetof(typeof(send_cmd), recv_bytes); + }}; + const size_t recv_offset = offsetof(typeof(cmd), recv); size_t i = 0; // Populate the Joybus commands on each port memset(input, 0, JOYBUS_BLOCK_SIZE); JOYPAD_PORT_FOREACH (port) { + // Set the command metadata + input[i++] = sizeof(cmd.send); + input[i++] = sizeof(cmd.recv); // Micro-optimization: Minimize copy length - memcpy(&input[i], &send_cmd, recv_offset); - i += sizeof(send_cmd); + memcpy(&input[i], &cmd, recv_offset); + i += sizeof(cmd); } // Close out the Joybus operation block @@ -369,7 +371,7 @@ static void joypad_gcn_origin_check_async(void) static void joypad_identify_callback(uint64_t *out_dwords, void *ctx) { const uint8_t *out_bytes = (void *)out_dwords; - const joybus_cmd_identify_port_t *recv_cmd; + const joybus_cmd_identify_port_t *cmd; volatile joypad_device_hot_t *device; volatile joypad_accessory_t *accessory; bool devices_changed = false; @@ -379,10 +381,10 @@ static void joypad_identify_callback(uint64_t *out_dwords, void *ctx) { device = &joypad_devices_hot[port]; accessory = &joypad_accessories_hot[port]; - recv_cmd = (void *)&out_bytes[i]; - i += sizeof(*recv_cmd); + cmd = (void *)&out_bytes[i + JOYBUS_COMMAND_METADATA_SIZE]; + i += JOYBUS_COMMAND_METADATA_SIZE + sizeof(*cmd); - joybus_identifier_t identifier = recv_cmd->identifier; + joybus_identifier_t identifier = cmd->recv.identifier; if (joypad_identifiers_hot[port] != identifier) { // The identifier has changed; reset device state @@ -406,7 +408,7 @@ static void joypad_identify_callback(uint64_t *out_dwords, void *ctx) { device->style = JOYPAD_STYLE_N64; uint8_t prev_accessory_status = accessory->status; - uint8_t accessory_status = recv_cmd->status & JOYBUS_IDENTIFY_STATUS_ACCESSORY_MASK; + uint8_t accessory_status = cmd->recv.status & JOYBUS_IDENTIFY_STATUS_ACCESSORY_MASK; // Work-around third-party controllers that don't correctly report accessory status bool accessory_absent = ( accessory_status == JOYBUS_IDENTIFY_STATUS_ACCESSORY_ABSENT || @@ -460,21 +462,22 @@ static void joypad_identify_async(bool reset) // Reset invalidates the cached input block if (!joypad_identify_input_valid || reset) { - const joybus_cmd_identify_port_t send_cmd = { - .send_len = sizeof(send_cmd.send_bytes), - .recv_len = sizeof(send_cmd.recv_bytes), + const joybus_cmd_identify_port_t cmd = { .send = { .command = reset ? JOYBUS_COMMAND_ID_RESET : JOYBUS_COMMAND_ID_IDENTIFY, - }; - const size_t recv_offset = offsetof(typeof(send_cmd), recv_bytes); + } }; + const size_t recv_offset = offsetof(typeof(cmd), recv); size_t i = 0; // Populate the Joybus commands on each port memset(input, 0, JOYBUS_BLOCK_SIZE); JOYPAD_PORT_FOREACH (port) { + // Set the command metadata + input[i++] = sizeof(cmd.send); + input[i++] = sizeof(cmd.recv); // Micro-optimization: Minimize copy length - memcpy(&input[i], &send_cmd, recv_offset); - i += sizeof(send_cmd); + memcpy(&input[i], &cmd, recv_offset); + i += sizeof(cmd); } // Close out the Joybus operation block @@ -526,32 +529,34 @@ static void joypad_read_async(void) if (device->style == JOYPAD_STYLE_GCN) { - const joybus_cmd_gcn_controller_read_port_t send_cmd = { - .send_len = sizeof(send_cmd.send_bytes), - .recv_len = sizeof(send_cmd.recv_bytes), + const joybus_cmd_gcn_controller_read_port_t cmd = { .send = { .command = JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ, .mode = 3, // Most-compatible analog mode .rumble = device->rumble_active, - }; + } }; + // 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, recv_offset); + i += sizeof(cmd); } else if ( identifier == JOYBUS_IDENTIFIER_N64_CONTROLLER || identifier == JOYBUS_IDENTIFIER_N64_MOUSE ) { - const joybus_cmd_n64_controller_read_port_t send_cmd = { - .send_len = sizeof(send_cmd.send_bytes), - .recv_len = sizeof(send_cmd.recv_bytes), + const joybus_cmd_n64_controller_read_port_t cmd = { .send = { .command = JOYBUS_COMMAND_ID_N64_CONTROLLER_READ, - }; + } }; + // 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, recv_offset); + i += sizeof(cmd); } else { @@ -597,29 +602,13 @@ static void joypad_vi_interrupt_callback(void) */ joypad_inputs_t joypad_read_n64_inputs_sync(joypad_port_t port) { - uint8_t input[JOYBUS_BLOCK_SIZE] = {0}; - uint8_t output[JOYBUS_BLOCK_SIZE] = {0}; - size_t i = port; - const joybus_cmd_n64_controller_read_port_t send_cmd = { - .send_len = sizeof(send_cmd.send_bytes), - .recv_len = sizeof(send_cmd.recv_bytes), + joybus_cmd_n64_controller_read_port_t cmd = { .send = { .command = JOYBUS_COMMAND_ID_N64_CONTROLLER_READ, - }; - // 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; - - joybus_exec(input, output); - const joybus_cmd_n64_controller_read_port_t *recv_cmd = - (void *)&output[port]; + } }; + joybus_cmd_exec(port, cmd); - return joypad_inputs_from_n64_controller_read(recv_cmd); + return joypad_inputs_from_n64_controller_read(&cmd); } /** @@ -753,9 +742,9 @@ void joypad_scan(void) if (command_id == JOYBUS_COMMAND_ID_N64_CONTROLLER_READ) { - const joybus_cmd_n64_controller_read_port_t *recv_cmd; - recv_cmd = (void *)&output[i]; - i += sizeof(*recv_cmd); + const joybus_cmd_n64_controller_read_port_t *cmd; + cmd = (void *)&output[i + JOYBUS_COMMAND_METADATA_SIZE]; + i += JOYBUS_COMMAND_METADATA_SIZE + sizeof(*cmd); // N64 Mouse uses the same read command as a Controller if (identifiers[port] == JOYBUS_IDENTIFIER_N64_MOUSE) @@ -768,20 +757,20 @@ void joypad_scan(void) } device->previous = device->current; - device->current = joypad_inputs_from_n64_controller_read(recv_cmd); + device->current = joypad_inputs_from_n64_controller_read(cmd); } else if (command_id == JOYBUS_COMMAND_ID_GCN_CONTROLLER_READ) { // Normalize GameCube controller read response - const joybus_cmd_gcn_controller_read_port_t *recv_cmd; - recv_cmd = (void *)&output[i]; - i += sizeof(*recv_cmd); + const joybus_cmd_gcn_controller_read_port_t *cmd; + cmd = (void *)&output[i + JOYBUS_COMMAND_METADATA_SIZE]; + i += JOYBUS_COMMAND_METADATA_SIZE + sizeof(*cmd); - if (recv_cmd->check_origin) check_origins = true; + if (cmd->recv.check_origin) { check_origins = true; } device->style = JOYPAD_STYLE_GCN; device->previous = device->current; - device->current = joypad_inputs_from_gcn_controller_read(recv_cmd, &origins[port]); + device->current = joypad_inputs_from_gcn_controller_read(cmd, &origins[port]); } else { diff --git a/src/joypad_accessory.c b/src/joypad_accessory.c index cea0eec4d2..f232214860 100644 --- a/src/joypad_accessory.c +++ b/src/joypad_accessory.c @@ -30,7 +30,7 @@ static void joypad_transfer_pak_wait_timer_callback(int ovfl, void *ctx); * @brief Determine whether the accessory read command was successful. Retry if necessary. * * @param port Joypad port number (#joypad_port_t) - * @param[in] recv_cmd Joybus "N64 Accessory Read" command output + * @param[in] cmd Joybus "N64 Accessory Read" command output * @param[in] retry_callback Callback to use if the command needs to be retried. * @param[in,out] retry_ctx Context to pass to the retry callback. * @@ -39,14 +39,14 @@ static void joypad_transfer_pak_wait_timer_callback(int ovfl, void *ctx); */ static bool joypad_accessory_check_read_crc_error( joypad_port_t port, - const joybus_cmd_n64_accessory_read_port_t *recv_cmd, + const joybus_cmd_n64_accessory_read_port_t *cmd, joybus_callback_t retry_callback, void *retry_ctx ) { volatile joypad_device_hot_t *device = &joypad_devices_hot[port]; volatile joypad_accessory_t *accessory = &joypad_accessories_hot[port]; - int crc_status = joybus_accessory_compare_data_crc(recv_cmd->data, recv_cmd->data_crc); + int crc_status = joybus_accessory_compare_data_crc(cmd->recv.data, cmd->recv.data_crc); switch (crc_status) { case JOYBUS_ACCESSORY_IO_STATUS_OK: @@ -74,7 +74,7 @@ static bool joypad_accessory_check_read_crc_error( // Retry: Bad communication with the accessory accessory->retries = retries + 1; accessory->error = JOYPAD_ACCESSORY_ERROR_PENDING; - uint16_t retry_addr = recv_cmd->addr_checksum; + uint16_t retry_addr = cmd->send.addr_checksum; retry_addr &= JOYBUS_ACCESSORY_ADDR_MASK_OFFSET; joybus_accessory_read_async( port, retry_addr, @@ -105,7 +105,7 @@ static bool joypad_accessory_check_read_crc_error( * @brief Determine whether the accessory write command was successful. Retry if necessary. * * @param port Joypad port number (#joypad_port_t) - * @param[in] recv_cmd Joybus "N64 Accessory Write" command output + * @param[in] cmd Joybus "N64 Accessory Write" command output * @param[in] retry_callback Callback to use if the command needs to be retried. * @param[in,out] retry_ctx Context to pass to the retry callback. * @@ -114,14 +114,14 @@ static bool joypad_accessory_check_read_crc_error( */ static bool joypad_accessory_write_crc_error_check( joypad_port_t port, - const joybus_cmd_n64_accessory_write_port_t *recv_cmd, + const joybus_cmd_n64_accessory_write_port_t *cmd, joybus_callback_t retry_callback, void *retry_ctx ) { volatile joypad_device_hot_t *device = &joypad_devices_hot[port]; volatile joypad_accessory_t *accessory = &joypad_accessories_hot[port]; - int crc_status = joybus_accessory_compare_data_crc(recv_cmd->data, recv_cmd->data_crc); + int crc_status = joybus_accessory_compare_data_crc(cmd->send.data, cmd->recv.data_crc); switch (crc_status) { case JOYBUS_ACCESSORY_IO_STATUS_OK: @@ -151,10 +151,10 @@ static bool joypad_accessory_write_crc_error_check( // Intentionally preserve accessory status in this case accessory->retries = retries + 1; accessory->error = JOYPAD_ACCESSORY_ERROR_PENDING; - uint16_t retry_addr = recv_cmd->addr_checksum; + uint16_t retry_addr = cmd->send.addr_checksum; retry_addr &= JOYBUS_ACCESSORY_ADDR_MASK_OFFSET; joybus_accessory_write_async( - port, retry_addr, recv_cmd->data, + port, retry_addr, cmd->send.data, retry_callback, retry_ctx ); return true; @@ -253,9 +253,10 @@ static void joypad_accessory_detect_read_callback(uint64_t *out_dwords, void *ct } uint8_t write_data[JOYBUS_ACCESSORY_DATA_SIZE]; - const joybus_cmd_n64_accessory_read_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_read_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_accessory_detect_read_callback; - if (joypad_accessory_check_read_crc_error(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_check_read_crc_error(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } @@ -263,7 +264,7 @@ static void joypad_accessory_detect_read_callback(uint64_t *out_dwords, void *ct { // Compare the expected label with what was actually read back for (size_t i = 0; i < sizeof(write_data); ++i) write_data[i] = i; - if (memcmp(recv_cmd->data, write_data, sizeof(write_data)) == 0) + if (memcmp(cmd->recv.data, write_data, sizeof(write_data)) == 0) { // Success: Label write persisted; this appears to be a Controller Pak accessory->state = JOYPAD_ACCESSORY_STATE_IDLE; @@ -284,7 +285,7 @@ static void joypad_accessory_detect_read_callback(uint64_t *out_dwords, void *ct } else if (state == JOYPAD_ACCESSORY_STATE_DETECT_RUMBLE_PROBE_READ) { - uint8_t probe_value = recv_cmd->data[0]; + uint8_t probe_value = cmd->recv.data[0]; if (probe_value == JOYBUS_ACCESSORY_PROBE_RUMBLE_PAK) { // Success: Probe reports that this is a Rumble Pak @@ -313,7 +314,7 @@ static void joypad_accessory_detect_read_callback(uint64_t *out_dwords, void *ct } else if (state == JOYPAD_ACCESSORY_STATE_DETECT_TRANSFER_PROBE_READ) { - uint8_t probe_value = recv_cmd->data[0]; + uint8_t probe_value = cmd->recv.data[0]; if (probe_value == JOYBUS_ACCESSORY_PROBE_TRANSFER_PAK_ON) { // Step 4C: Write probe value to turn off Transfer Pak @@ -341,7 +342,7 @@ static void joypad_accessory_detect_read_callback(uint64_t *out_dwords, void *ct } else if (state == JOYPAD_ACCESSORY_STATE_DETECT_SNAP_PROBE_READ) { - uint8_t probe_value = recv_cmd->data[0]; + uint8_t probe_value = cmd->recv.data[0]; if (probe_value == JOYBUS_ACCESSORY_PROBE_SNAP_STATION) { // Success: Probe reports that this is a Snap Station @@ -375,9 +376,10 @@ static void joypad_accessory_detect_write_callback(uint64_t *out_dwords, void *c return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_write_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_write_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_accessory_detect_write_callback; - if (joypad_accessory_write_crc_error_check(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_write_crc_error_check(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } @@ -507,9 +509,10 @@ static void joypad_rumble_pak_motor_write_callback(uint64_t *out_dwords, void *c return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_write_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_write_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_rumble_pak_motor_write_callback; - if (!joypad_accessory_write_crc_error_check(port, recv_cmd, retry_callback, ctx)) + if (!joypad_accessory_write_crc_error_check(port, cmd, retry_callback, ctx)) { accessory->state = JOYPAD_ACCESSORY_STATE_IDLE; } @@ -554,15 +557,16 @@ static void joypad_transfer_pak_enable_read_callback(uint64_t *out_dwords, void return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_read_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_read_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_enable_read_callback; - if (joypad_accessory_check_read_crc_error(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_check_read_crc_error(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } else if (state == JOYPAD_ACCESSORY_STATE_TRANSFER_ENABLE_STATUS_READ) { - accessory->transfer_pak_status.raw = recv_cmd->data[0]; + accessory->transfer_pak_status.raw = cmd->recv.data[0]; accessory->state = JOYPAD_ACCESSORY_STATE_IDLE; } } @@ -584,15 +588,16 @@ static void joypad_transfer_pak_enable_write_callback(uint64_t *out_dwords, void return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_write_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_write_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_enable_write_callback; - if (joypad_accessory_write_crc_error_check(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_write_crc_error_check(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } else if (state == JOYPAD_ACCESSORY_STATE_TRANSFER_ENABLE_PROBE_WRITE) { - if (recv_cmd->data[0] == JOYBUS_ACCESSORY_PROBE_TRANSFER_PAK_ON) + if (cmd->send.data[0] == JOYBUS_ACCESSORY_PROBE_TRANSFER_PAK_ON) { accessory->state = JOYPAD_ACCESSORY_STATE_TRANSFER_ENABLE_PROBE_WAIT; timer_link_t *timer = accessory->transfer_pak_wait_timer; @@ -660,15 +665,16 @@ static void joypad_transfer_pak_load_read_callback(uint64_t *out_dwords, void *c return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_read_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_read_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_load_read_callback; - if (joypad_accessory_check_read_crc_error(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_check_read_crc_error(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } else if (state == JOYPAD_ACCESSORY_STATE_TRANSFER_LOAD_STATUS_READ) { - joybus_transfer_pak_status_t status = { .raw = recv_cmd->data[0] }; + joybus_transfer_pak_status_t status = { .raw = cmd->recv.data[0] }; accessory->transfer_pak_status = status; if (!status.access || !status.power) { @@ -697,7 +703,7 @@ static void joypad_transfer_pak_load_read_callback(uint64_t *out_dwords, void *c } else if (state == JOYPAD_ACCESSORY_STATE_TRANSFER_LOAD_DATA_READ) { - memcpy(io->cursor, recv_cmd->data, JOYBUS_ACCESSORY_DATA_SIZE); + memcpy(io->cursor, cmd->recv.data, JOYBUS_ACCESSORY_DATA_SIZE); uint8_t *cursor = io->cursor += JOYBUS_ACCESSORY_DATA_SIZE; uint16_t tpak_addr = io->tpak_addr += JOYBUS_ACCESSORY_DATA_SIZE; uint16_t cart_addr = io->cart_addr += JOYBUS_ACCESSORY_DATA_SIZE; @@ -760,9 +766,10 @@ static void joypad_transfer_pak_load_write_callback(uint64_t *out_dwords, void * return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_write_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_write_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_load_write_callback; - if (joypad_accessory_write_crc_error_check(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_write_crc_error_check(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } @@ -832,15 +839,16 @@ static void joypad_transfer_pak_store_read_callback(uint64_t *out_dwords, void * return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_read_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_read_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_store_read_callback; - if (joypad_accessory_check_read_crc_error(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_check_read_crc_error(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! } else if (state == JOYPAD_ACCESSORY_STATE_TRANSFER_STORE_STATUS_READ) { - joybus_transfer_pak_status_t status = { .raw = recv_cmd->data[0] }; + joybus_transfer_pak_status_t status = { .raw = cmd->recv.data[0] }; accessory->transfer_pak_status = status; if (!status.access || !status.power) { @@ -887,9 +895,10 @@ static void joypad_transfer_pak_store_write_callback(uint64_t *out_dwords, void return; // Unexpected accessory state! } - const joybus_cmd_n64_accessory_write_port_t *recv_cmd = (void *)&out_bytes[port]; + const joybus_cmd_n64_accessory_write_port_t *cmd = + (void *)&out_bytes[port + JOYBUS_COMMAND_METADATA_SIZE]; joybus_callback_t retry_callback = joypad_transfer_pak_store_write_callback; - if (joypad_accessory_write_crc_error_check(port, recv_cmd, retry_callback, ctx)) + if (joypad_accessory_write_crc_error_check(port, cmd, retry_callback, ctx)) { return; // Accessory communication error! }