This shows you the differences between two versions of the page.
— |
mantis:ide_simulator:protocol [2024/09/23 04:49] (current) colin created |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== Host Control Protocol ====== | ||
+ | |||
+ | The device can be controlled over the ATAPI interface. This is achieved by using commands in the " | ||
+ | |||
+ | Note that all commands are subject to change in firmware updates. | ||
+ | |||
+ | All structs are expected to be packed with something like <code cpp># | ||
+ | |||
+ | The example code here assumes you have a function " | ||
+ | <code cpp>bool SendPACKET(const uint8_t* cmd, int cmdLength, int cmdExtra, uint8_t* response, int respLength);</ | ||
+ | |||
+ | ===== Detecting the device ===== | ||
+ | |||
+ | The device can be detected by issuing a standard INQUIRY (0x12) command over ATAPI. An example INQUIRY packet in code might appear as: | ||
+ | <code cpp> | ||
+ | const uint8_t SCSI_INQUIRY = 0x12; | ||
+ | uint8_t response[36]; | ||
+ | const uint8_t cmd[12] = {SCSI_INQUIRY, | ||
+ | bool found = SendPACKET(cmd, | ||
+ | </ | ||
+ | Sending this via the ATA " | ||
+ | <code cpp> | ||
+ | found &= memcmp(resp + 8, " | ||
+ | </ | ||
+ | |||
+ | If any subsequent commands fail, it can be assumed that the firmware is too old to support the host commands, and the user can be prompted to install an update. | ||
+ | |||
+ | ===== Getting device state ===== | ||
+ | |||
+ | <code cpp># | ||
+ | |||
+ | ^ Request packet format | ||
+ | ^0 ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9 ^10 ^11 ^ | ||
+ | |TATTIEBOGLE_SYSTEM_STATE |response length high |response length low |0 |0 |0 |0 |0 |0 |0 |0 |0 | | ||
+ | |||
+ | This command retrieves the " | ||
+ | <code cpp> | ||
+ | typedef struct { | ||
+ | char name[50]; | ||
+ | char version[50]; | ||
+ | char date[12]; | ||
+ | char notes[50]; | ||
+ | } FIRMWARE_DATA; | ||
+ | |||
+ | typedef struct { | ||
+ | |||
+ | uint32_t magic; | ||
+ | #define TB_MAGIC (' | ||
+ | uint32_t model; | ||
+ | #define TB_MODEL_V1 (' | ||
+ | #define TB_MODEL_V2 (' | ||
+ | uint16_t hardware_revision; | ||
+ | uint8_t serialNo[16]; | ||
+ | uint16_t flags; | ||
+ | #define TB_STATE_FLAG_SD_PRESENT (1 << 0) | ||
+ | #define TB_STATE_FLAG_BOOTABLE_BANK0 (1 << 1) | ||
+ | #define TB_STATE_FLAG_BOOTABLE_BANK1 (1 << 2) | ||
+ | #define TB_STATE_FLAG_SELECTED_BANK0 (1 << 3) | ||
+ | #define TB_STATE_FLAG_SELECTED_BANK1 (1 << 4) | ||
+ | #define TB_STATE_FLAG_ACTIVE_BANK0 (1 << 5) | ||
+ | #define TB_STATE_FLAG_ACTIVE_BANK1 (1 << 6) | ||
+ | #define TB_STATE_FLAG_VALID_BANK0 (1 << 7) | ||
+ | #define TB_STATE_FLAG_VALID_BANK1 (1 << 8) | ||
+ | uint16_t usb[2]; | ||
+ | #define TB_STATE_USB_MODE_MASK (0x03) | ||
+ | #define TB_STATE_USB_MODE_NONE (0x00) | ||
+ | #define TB_STATE_USB_MODE_HOST (0x01) | ||
+ | #define TB_STATE_USB_MODE_DEVICE (0x02) | ||
+ | #define TB_STATE_USB_SPEED_MASK (0x03 << 2) | ||
+ | #define TB_STATE_USB_SPEED_LOW (0x01 << 2) | ||
+ | #define TB_STATE_USB_SPEED_FULL (0x02 << 2) | ||
+ | #define TB_STATE_USB_SPEED_HIGH (0x03 << 2) | ||
+ | #define TB_STATE_USB_DRIVER_SHIFT (4) | ||
+ | #define TB_STATE_USB_GET_DRIVER(x) ((int16_t)( \ | ||
+ | ((x) >> TB_STATE_USB_DRIVER_SHIFT) | \ | ||
+ | (((x) & 0x8000) ? (0xF << 12) : 0) \ | ||
+ | )) | ||
+ | |||
+ | FIRMWARE_DATA firmwares[2]; | ||
+ | } TATTIEBOGLE_SYSTEM_STATE_RESPONSE; | ||
+ | </ | ||
+ | On devices which don't support flash banks, the output of the firmware A/B fields may instead represent the bootloader/ | ||
+ | |||
+ | Retrieving this struct is much the same as the INQUIRY: | ||
+ | <code cpp> | ||
+ | int sim_get_status(TATTIEBOGLE_SYSTEM_STATE_RESPONSE* state) | ||
+ | { | ||
+ | uint16_t len = sizeof(*state); | ||
+ | uint8_t cmd[12] = {TATTIEBOGLE_SYSTEM_STATE, | ||
+ | return SendPACKET(cmd, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Setting and listing a directory ===== | ||
+ | |||
+ | <code cpp> | ||
+ | #define TATTIEBOGLE_DIR_LISTING | ||
+ | </ | ||
+ | |||
+ | This command can both set the current directory, and list the current directory' | ||
+ | |||
+ | ^ Request packet format | ||
+ | ^0 ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9 ^10 ^11 ^ | ||
+ | |TATTIEBOGLE_DIR_LISTING |response length high |response length low |request length high |request length low |request port |0 |0 |0 |0 |0 |0 | | ||
+ | |||
+ | ==== Setting directory ==== | ||
+ | |||
+ | The directory can be set by providing the name of the directory as a data part of the command. This involves setting the " | ||
+ | |||
+ | <code cpp> | ||
+ | #define DEVICE_SD | ||
+ | #define DEVICE_USB0 | ||
+ | #define DEVICE_USB1 | ||
+ | </ | ||
+ | |||
+ | Note that specific devices may only support certain ports. The system state request should indicate which ports are active, if any. | ||
+ | |||
+ | The request is followed by the actual data for the packet, which is the requested path. Note that since IDE is 16-bit, the requested path must be even, so if it's an odd number of characters, padding must be added. | ||
+ | |||
+ | Example: | ||
+ | <code cpp> | ||
+ | bool sim_set_directory(unsigned char port, const char *dir) | ||
+ | { | ||
+ | uint16_t outlen = (strlen(dir) + 1) & ~1; // must be even | ||
+ | uint8_t cmd[12 + outlen]; | ||
+ | memset(cmd, 0, sizeof(cmd)); | ||
+ | cmd[0] = TATTIEBOGLE_DIR_LISTING; | ||
+ | cmd[3] = outlen >> 8; | ||
+ | cmd[4] = outlen & 0xFF; | ||
+ | cmd[5] = port; | ||
+ | memcpy(cmd + 12, dir, outlen); | ||
+ | return SendPACKET(cmd, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ==== Reading the directory ==== | ||
+ | |||
+ | The directory can be read by setting the " | ||
+ | |||
+ | Example: | ||
+ | <code cpp> | ||
+ | bool sim_read_directory(unsigned char *response, int len) | ||
+ | { | ||
+ | char cmd[12] = {TATTIEBOGLE_DIR_LISTING, | ||
+ | return SendPACKET(cmd, | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | This request can be repeated to continue reading contents of a directory exceeding the size of a single 2Kb request. | ||
+ | |||
+ | Example: | ||
+ | <code cpp> | ||
+ | static char data[2048]; | ||
+ | while (sim_read_directory(data, | ||
+ | for (int i = 0; i < (sizeof(data) - sizeof(TATTIEBOGLE_DIR_LISTING_RESPONSE)); | ||
+ | TATTIEBOGLE_DIR_LISTING_RESPONSE *entry = (TATTIEBOGLE_DIR_LISTING_RESPONSE*)(data + i); | ||
+ | if (entry-> | ||
+ | break; | ||
+ | // Use ' | ||
+ | i += sizeof(*entry) + entry-> | ||
+ | } | ||
+ | } | ||
+ | </ | ||
+ | |||
+ | ===== Selecting a new image ===== | ||
+ | |||
+ | <code cpp># | ||
+ | |||
+ | ^ Request packet format | ||
+ | ^0 ^1 ^2 ^3 ^4 ^5 ^6 ^7 ^8 ^9 ^10 ^11 ^ | ||
+ | |TATTIEBOGLE_SELECT_IMAGE |0 |0 |request length high |request length low |request port |0 |0 |0 |0 |0 |0 | | ||
+ | |||
+ | This command works the same as the "set directory" | ||
+ | |||
+ | Example: | ||
+ | <code cpp> | ||
+ | bool sim_set_image(unsigned char port, const char* dir) | ||
+ | { | ||
+ | unsigned short outlen = (strlen(dir) + 1) & ~1; // must be even | ||
+ | uint8_t cmd[12 + outlen]; | ||
+ | memset(cmd, 0, sizeof(cmd)); | ||
+ | cmd[0] = TATTIEBOGLE_SELECT_IMAGE; | ||
+ | cmd[3] = outlen >> 8; | ||
+ | cmd[4] = outlen & 0xFF; | ||
+ | cmd[5] = port; | ||
+ | memcpy(cmd + 12, dir, outlen); | ||
+ | return SendPACKET(cmd, | ||
+ | } | ||
+ | </ | ||