Line 1: |
Line 1: |
− | This service was added with [[2.0.0-2]]. It employs a custom packet format with obfuscation and error detection.
| + | #REDIRECT [[IR Services#IR Service "ir:USER"]] |
− | | |
− | = 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)
| |