Line 1: |
Line 1: |
− | #REDIRECT [[IR Services#IR Service "ir:USER"]]
| + | This service was added with [[2.0.0-2]]. It employs a custom packet format with obfuscation and error detection. |
| + | |
| + | = Command set = |
| + | When sending data, SendIrnop is used when the size is <=0xFC, otherwise SendIrnopLarge is used. |
| + | |
| + | {| class="wikitable" border="1" |
| + | |- |
| + | ! Command Header |
| + | ! Available since system version |
| + | ! Description |
| + | |- |
| + | | 0x00010182 |
| + | | [[2.0.0-2]] |
| + | | InitializeIrnop |
| + | |- |
| + | | 0x00020000 |
| + | | [[2.0.0-2]] |
| + | | FinalizeIrnop |
| + | |- |
| + | | 0x00030000 |
| + | | [[2.0.0-2]] |
| + | | ClearReceiveBuffer |
| + | |- |
| + | | 0x00040000 |
| + | | [[2.0.0-2]] |
| + | | ClearSendBuffer |
| + | |- |
| + | | 0x0005.... |
| + | | [[2.0.0-2]] |
| + | | WaitConnection |
| + | |- |
| + | | 0x00060040 |
| + | | [[2.0.0-2]] |
| + | | RequireConnection (u8 input) |
| + | |- |
| + | | 0x0007.... |
| + | | [[2.0.0-2]] |
| + | | AutoConnection |
| + | |- |
| + | | 0x0008.... |
| + | | [[2.0.0-2]] |
| + | | AnyConnection |
| + | |- |
| + | | 0x00090000 |
| + | | [[2.0.0-2]] |
| + | | Disconnect |
| + | |- |
| + | | 0x000A0000 |
| + | | [[2.0.0-2]] |
| + | | GetReceiveEvent (writes event handle to cmdreply[3]) |
| + | |- |
| + | | 0x000B0000 |
| + | | [[2.0.0-2]] |
| + | | GetSendEvent (writes event handle to cmdreply[3]) |
| + | |- |
| + | | 0x000C0000 |
| + | | [[2.0.0-2]] |
| + | | GetConnectionStatusEvent (writes event handle to cmdreply[3]) |
| + | |- |
| + | | 0x000D0042 |
| + | | [[2.0.0-2]] |
| + | | SendIrnop (u32 size, ((Size<<14) <nowiki>|</nowiki> 2), inbufptr) |
| + | |- |
| + | | 0x000E0042 |
| + | | [[2.0.0-2]] |
| + | | SendIrnopLarge (u32 size, ((Size<<8) <nowiki>|</nowiki> 10), inbufptr) |
| + | |- |
| + | | 0x000F.... |
| + | | [[2.0.0-2]] |
| + | | ReceiveIrnop |
| + | |- |
| + | | 0x0010.... |
| + | | [[2.0.0-2]] |
| + | | ReceiveIrnopLarge |
| + | |- |
| + | | 0x0011.... |
| + | | [[2.0.0-2]] |
| + | | GetLatestReceiveErrorResult |
| + | |- |
| + | | 0x0012.... |
| + | | [[2.0.0-2]] |
| + | | GetLatestSendErrorResult |
| + | |- |
| + | | 0x0013.... |
| + | | [[2.0.0-2]] |
| + | | GetConnectionStatus |
| + | |- |
| + | | 0x0014.... |
| + | | [[2.0.0-2]] |
| + | | GetTryingToConnectStatus |
| + | |- |
| + | | 0x0015.... |
| + | | [[2.0.0-2]] |
| + | | GetReceiveSizeFreeAndUsed |
| + | |- |
| + | | 0x0016.... |
| + | | [[2.0.0-2]] |
| + | | GetSendSizeFreeAndUsed |
| + | |- |
| + | | 0x0017.... |
| + | | [[2.2.0-X]] |
| + | | GetConnectionRole |
| + | |- |
| + | | 0x00180182 |
| + | | [[2.2.0-X]] |
| + | | InitializeIrnopShared (u32, u32, u32, u32, u32, u8, 0, handle) |
| + | |- |
| + | | 0x00190040 |
| + | | [[2.2.0-X]] |
| + | | ReleaseReceivedData (32bit_value input) |
| + | |- |
| + | | 0x001A0040 |
| + | | [[2.2.0-X]] |
| + | | SetOwnMachineId (u8 input) |
| + | |} |
| + | |
| + | = Protocol description = |
| + | Packets are sent using IrDA-SIR, with a 8N1 encoding (eight data bits, one stop bit, without parity). Each one is formed by a 2-byte header, a varint with the payload size, an obfuscated payload, and trailing error detection byte. |
| + | |
| + | == Packet header == |
| + | The packet header is fixed and consists in a synchronization byte (0xA5), followed by a unused (possibly RFU) zero byte. After these two hardcoded bytes, there's a varint representing the payload size, which may use one byte or two, depending on the how big the payload is. |
| + | |
| + | * For payloads with less than 64 bytes, the third byte represents the payload size. |
| + | * For packets with up to 16383 bytes, the size is split in two bytes, with the third byte being the upper 6 bits of the payload size, OR'd with 0x40, and the fourth being the lower eight bits of the payload size |
| + | |
| + | For packets with less than 64 bytes: |
| + | {| class="wikitable" border="1" |
| + | ! Sync |
| + | ! RFU |
| + | ! Size |
| + | |- |
| + | | 0xA5 |
| + | | 0x00 |
| + | | size |
| + | |} |
| + | |
| + | For packets with up to 16383 bytes: |
| + | {| class="wikitable" border="1" |
| + | ! Sync |
| + | ! RFU |
| + | ! Size (1) |
| + | ! Size (2) |
| + | |- |
| + | | 0xA5 |
| + | | 0x00 |
| + | | (size >> 8) <nowiki>|</nowiki> 0x40 |
| + | | size & 0xFF |
| + | |} |
| + | |
| + | In C: |
| + | <nowiki>uint8_t * setPacketHeader(uint8_t * buffer, size_t payloadSize) { |
| + | assert(payloadSize < 16384); |
| + | |
| + | buffer[0] = 0xA5; |
| + | buffer[1] = 0x00; |
| + | |
| + | if (payloadSize < 64) { |
| + | buffer[2] = payloadSize; |
| + | buffer += 3; |
| + | } else { |
| + | buffer[2] = 0x40 | (payloadSize >> 8); |
| + | buffer[3] = payloadSize; |
| + | buffer += 4; |
| + | } |
| + | |
| + | return buffer; |
| + | }</nowiki> |
| + | |
| + | == Payload == |
| + | The payload is obfuscated using a XOR-based encryption. In C: |
| + | <nowiki>void payloadObfuscate(const void * voidplain, void * voidcipher, size_t size) { |
| + | uint16_t * plain = (uint16_t *) voidplain; |
| + | uint16_t * cipher = (uint16_t *) voidcipher; |
| + | size_t halfCount = size / sizeof(uint16_t); |
| + | |
| + | uint16_t xorval = htobe16(0xE963); |
| + | size_t i; |
| + | |
| + | for (i = 0; i < halfCount; i++) { |
| + | xorval ^= plain[i]; |
| + | cipher[i] = xorval; |
| + | } |
| + | } |
| + | |
| + | void payloadDeobfuscate(const void * voidcipher, void * voidplain, size_t size) { |
| + | uint16_t * cipher = (uint16_t *) voidcipher; |
| + | uint16_t * plain = (uint16_t *) voidplain; |
| + | size_t halfCount = size / sizeof(uint16_t); |
| + | |
| + | if (halfCount) { |
| + | size_t i; |
| + | for (i = halfCount - 1; i > 0; i--) { |
| + | plain[i] = cipher[i] ^ cipher[i - 1]; |
| + | } |
| + | |
| + | plain[0] = cipher[0] ^ htobe16(0xE963); |
| + | } |
| + | }</nowiki> |
| + | |
| + | == Error detection == |
| + | The trailing error detection byte is calculated using [[CRC-8-CCITT]] <b>over the whole packet</b> (both the header and the payload) |