Firmware upgrade using IOT device
Approximatelly from the new year 2025, the SRM endpoint will be changed. If you are using or implementing this protocol, please contact us and we will keep you informed about the changes in advance.
More info in Querying the SRM section.
This document describes how to perform a firmware upgrade using CAN and siliXcon’s SRM (server with firmware repository) for an external developer. It should be regarded as a simple guide allowing to embed the upgrade process into another system. The document doesn’t aim to describe the entire proprietary service protocol, just the selected parts that are necessary for the upgrade process.
All the data in the CAN messages are encoded in little endian - INTEL format
Please ensure that the CAN bus and CAN timing are correctly set. Please do not mix CANH and CANL wires and ensure that the bus is properly terminated with a 120Ohm resistor. All CAN IDs described below are 11 bits.
The firmware size is around 250kb.
Legend
- Host: the 3RD party system (to implement the upgrade process). Have address 7.
- Device: A siliXcon product (display, device, BMS)
- DNC: Do Not Care. This information can have an arbitrary value with no significance for the host.
- SRM: Secure Remote Management (siliXcon’s server with firmware repository)
- Service: a device‘s internal communication object with an independent context. (BL, ID, PWR)
Establishing dialog with the bootloader
First of all, the host needs to put the device into bootloader mode. This is done by resetting the device and sending the SELECT command, as described below.
1. Reset the device
Reset the device using the PWR/reboot command. This command will instruct all devices (or another siliXcon products on the CAN-bus) to reboot. It is necessary to reboot the device in order to establish communication with the bootloader.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
PWR / Reboot | Host -> Device | 0 | 1 |
Payload:
Byte 0 |
---|
Target address |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2. Wait for the ATR
Wait for the ATR (answer to reset). As a response to the REBOOT, the device sends a message called ATR (answer to reset). The host shall wait for this message to ensure the reset took place. If the ATR does not arrive (within a timeout of ~500ms), the host can attempt to reset the device once again. The first 3 bytes must contain the value specified in the table, all other bytes shall not be parsed. At most 3 attempts are recommended, before a failure shall be considered.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / ATR response | Device -> Host | 8*4 + device address Usually 32 (device address 0) | 8 |
Payload:
Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 | Byte 5 | Byte 6 | Byte 7 |
---|---|---|---|---|---|---|---|
0xFF (0x07 for Boootloader version older than 2022) | 0x40 | 0x00 | DNC | DNC | DNC | DNC | DNC |
Host address (broadcast) | RESET | RESPONSE indication | Reset reason |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
3. Do a handshake by issuing the SELECT command.
The bootloader waits for about ~200ms after the ATR is sent, for the SELECT command. If the SELECT command is not received, the bootloader passes control to the main application.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / SELECT | Host -> Device | 8*4 + host address (7 is used as host address) Usually 39 (host with address 7) | 4 |
Payload:
Byte 0 | Byte 1 | Byte 2 | Byte 3 |
---|---|---|---|
0x00 | 0x02 | 0x00 | Session ID (DNC) |
Device address | SELECT command | Device address repeated | X (device increments the X value with each new host‘s command) |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
4. Wait for the SELECT response
Wait for the SELECT response. The bootloader sends this message as a confirmation of successful selection (hand shaken back). If the host doesn’t receive the SELECT response or, the first three bytes contain other than specified values, the host can attempt to send the SELECT command again. At most 3 attempts are recommended, before a failure shall be considered.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / SELECT response | Device -> Host | 8*4 + device address Usually 32 (device address 0) | 3 |
Payload:
| Byte 0 | Byte 1 | Byte 2 | | ------------ | ------ | ------------------- | ----------------------- | | 0x00 | 0x42 | 0x00 | | Host address | SELECT | RESPONSE indication | Device address repeated |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
Retrieving the identifiers
At this step, we assume that the communication with the bootloader was established up to the point where the SELECT response was successfully received.
Get device IDINFO
1. Request device IDINFO
Ask for the device’s IDINFO using the GETINFO command of the ID service. Now, the host shall issue the GETINFO command if the ID service retrieves the IDINFO. IDINFO is a set of string identifiers that the host must afterward send to the online SRM service.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO | Host -> Device | 8*3 + host address (7 is used as host address) Usually 31 (host with address 7) | 3 |
Payload:
Byte 0 | Byte 1 | Byte 2 |
---|---|---|
0x00 | 0x01 | 0x00 |
Device address | GETINFO command | - |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2. Receive IDINFO
Receive IDINFO data from the device. The device will respond with a series of messages to the GETINFO command. The host shall receive all these messages, collect the DATA in an input buffer and once transmission is finished, it should compute the CRC8 and check it against the CRC received in the footer message.
The IDINFO header message indicates the transfer start, contains the number of bytes the device will consequently send (dlen) and is transmitted once.
- CAN
- UART
Header message:
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response | Device -> Host | 8*3 + device address Usually 24 (device address 0) | 8 |
Byte 0 | Byte 1 | Byte 2 | Byte 3 - 6 | Byte 7 |
---|---|---|---|---|
0x07 | 0x41 | 0x10 = success | Dlen LSB - Dlen MSB | Session ID (DNC) |
Host address | GETINFO / RESPONSE indication | Command result | Number of bytes to be transferred, UINT32_T (encoded in little endian) | X (device increments the X value with each new host‘s command) |
Data message
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response data | Device -> Host | 8*3 + device address Usually 24 (device address 0) | Max(8, remaining bytes to be transferred) |
Byte 0 | Byte 1 | Byte 2 - 7 |
---|---|---|
0x07 | 0x71 | Payload |
Host address | GETINFO / DATA indication | The payload (1 – 6 bytes per single message). |
Footer message
The IDINFO footer message is transmitted at the end. It contains the 8-bit CRC of the transferred data. The CRC is computed from the entire data using the attached C-code. If there is a CRC mismatch, not all data were received or either the footer or header message is missing, the host can retry the transaction (by issuing the GETINFO command again).
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response footer | Device -> Host | 8*3 + device address Usually 24 (device address 0) | 5 |
Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|
0x07 | 0x51 | 0x00 | CRC | Session ID (DNC) |
Host address | GETINFO / CMDPART1 footer indication | The data payload (1 – 6 bytes per single message). | The host should compute the CRC8 of the received data chunk and compare it against this value. | X (device increments the X value with each new host‘s command) |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
3. Request device BLINFO
Ask for the device’s BLINFO using the GETINFO command of the BL service. Now, the host shall issue the GETINFO command of the BL service to retrieve the BLINFO. BLINFO is another set of string identifiers that the host must afterward send to the online SRM.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / GETINFO | Host -> Device | 8*4 + host address (7 is used as host address) Usually 39 (host with address 7) | 3 |
Payload:
Byte 0 | Byte 1 | Byte 2 |
---|---|---|
0x00 | 0x03 | 0x00 |
Device address | BL GETINFO command | DNC |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
4. Receive BLINFO
Receive BLINFO data from the device. The device will respond with a series of messages to the GETINFO command. The host shall receive all these messages, collect the DATA in an input buffer and once transmission is finished, it should compute the CRC8 and check it against the CRC received in the footer message. Semantics is described below:
The BLINFO header message indicates the transfer start, contains the number of bytes the device will consequently send (dlen) and is transmitted once.
- CAN
- UART
Header message:
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response | Device -> Host | 8*4 + device address Usually 32 (device address 0) | 8 |
Byte 0 | Byte 1 | Byte 2 | Byte 3 - Byte 6 | Byte 7 |
---|---|---|---|---|
0x07 | 0x43 | 0x10 = success | Dlen LSB - Dlen MSB | Session ID (DNC) |
Host address | GETINFO / RESPONSE indication | Command result | Number of bytes to be transferred, UINT32_T (encoded in little endian) | X (device increments the X value with each new host‘s command) |
Data message
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response data | Device -> Host | 8*4 + device address Usually 32 (device address 0) | Max(8, remaining bytes to be transferred) |
Byte 0 | Byte 1 | Byte 2 - 7 |
---|---|---|
0x07 | 0x73 | Payload |
Host address | GETINFO / DATA indication | The payload (1 – 6 bytes per single message). |
Footer message
The BLINFO footer message is transmitted at the end. It contains the 8-bit CRC of the transferred data. The CRC is computed from the entire data using the attached C-code. If there is a CRC mismatch, not all data were received or either the footer or header message is missing, the host can retry the transaction (by issuing the GETINFO command again).
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
ID / GETINFO response footer | Device -> Host | 8*4 + device address Usually 32 (device address 0) | 5 |
Byte 0 | Byte 1 | Byte 2 | Byte 3 | Byte 4 |
---|---|---|---|---|
0x07 | 0x53 | 0x00 | CRC | Session ID (DNC) |
Host address | GETINFO / CMDPART1 footer indication | The data payload (1 – 6 bytes per single message). | The host should compute the CRC8 of the received data chunk and compare it against this value. | X (device increments the X value with each new host‘s command) |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
Querying the SRM
This is documentation for new SRM server, that will be used from 2025. If you are using or implementing this protocol, please contact us and we will keep you informed about the changes in advance.
As a next step, once the IDINFO and BLINFO are successfully obtained, the host shall invoke the SRM with an UPGRADE query. The SRM is an online querying service that can be used (apart from other functionality) to retrieve the firmware upgrades.
The SRM service is a RESTful service that can be accessed via HTTPS. The host shall send a POST request to the SRM with the following payload (this is example data):
URL: Contact siliXcon support to get early access to the testing server. With the testing server you can test the API before it is released.
blinfo
and idinfo
are encoded using base64 encoding.
{
"query": "UPGRADE-nightly:VECTOR_LYNX_generic v6.0.0-nightly Nov 27 2024",
"tool": {
"name": "my_awesome_iot_device",
"version": "1.2.3"
},
"device": {
"idinfo": "MUE0MQBlc2MzLXNsMWtfNDhleGExMDYwLUMwMABTTC1zZXJwZW50AEVtQkwgIHYyLjUuMiBPY3QgIDIgMjAyMwAyMDM0MzAzODQzNTg1MzEzMDAyOTAwMzcAMEM6U0wA",
"blinfo": "MUE0OAA0MTBGQzI0MQA4MDAwADQwMDAwADEwMDAAN0FENjRCMTkAMjlFMTQAVkVDVE9SX09QSElPTl9nZW5lcmljIHY0LjMuMi10ZXN0aW5nIE9jdCAxNiAyMDI0AA=="
}
}
Field | Description |
---|---|
query | SRM query, More info. |
tool | Information about the host system. It is optional, but it can help with debuging. |
device | idinfo and blinfo retrieved from the device, encoded using base64. |
How to parse the idinfo
and blinfo
to get info about the device?
Using the following Python code, the idinfo
and blinfo
can be decoded nad parsed. This is not needed during the upgrade process, but it can be useful for debugging.
If you succesfully decode the idinfo
and blinfo
, you can be sure that the data was correctly received from the device.
import base64
import pytest
def decode_and_split(enc):
enc = base64.b64decode(enc).decode("utf-8").split("\000")
return enc
def decodeBlInfo(blinfo_base64):
parts = decode_and_split(blinfo_base64)
blinfo = {
"magic": parts[0],
"cpuid": parts[1],
"blsize": parts[2],
"flashsize": parts[3],
"pagesize": parts[4],
"appmagic": parts[5],
"appsize": parts[6],
"swid": parts[7],
}
if blinfo["magic"] != "1A48":
raise ValueError("Invalid magic number")
return blinfo
def decodeIdInfo(idinfo_base64):
parts = decode_and_split(idinfo_base64)
idinfo = {
"magic": parts[0],
"hwid": parts[1],
"Basename": parts[2],
"swid": parts[3],
"uuid": parts[4],
"signature": parts[5],
}
if idinfo["magic"] != "1A41":
raise ValueError("Invalid magic number")
return idinfo
def test_decode_info():
# Test decodeIdInfo
test_idinfo_cases = [
{
"base64_data": "MUE0MQBlc2MzLXNsMWtfNDhleGExMDYwLUMwMABTTC1zZXJwZW50AEVtQkwgIHYyLjUuMiBPY3QgIDIgMjAyMwAyMDM0MzAzODQzNTg1MzEzMDAyOTAwMzcAMEM6U0wA",
"expected": {
"magic": "1A41",
"hwid": "esc3-sl1k_48exa1060-C00",
"Basename": "SL-serpent",
"swid": "EmBL v2.5.2 Oct 2 2023",
"uuid": "203430384358531300290037",
"signature": "0C:SL",
},
}
]
# Test decodeBLInfo
test_blinfo_cases = [
{
"base64_data": "MUE0OAA0MTBGQzI0MQA4MDAwADQwMDAwADEwMDAAN0FENjRCMTkAMjlFMTQAVkVDVE9SX09QSElPTl9nZW5lcmljIHY0LjMuMi10ZXN0aW5nIE9jdCAxNiAyMDI0AA==",
"expected": {
"magic": "1A48",
"cpuid": "410FC241",
"blsize": "8000",
"flashsize": "40000",
"pagesize": "1000",
"appmagic": "7AD64B19",
"appsize": "29E14",
"swid": "VECTOR_OPHION_generic v4.3.2-testing Oct 16 2024",
},
}
]
# Test all idinfo cases
for test_case in test_idinfo_cases:
result = decodeIdInfo(test_case["base64_data"])
expected = test_case["expected"]
for key in expected:
assert result[key] == expected[key], f"Failed on idinfo key {key}"
# Test all blinfo cases
for test_case in test_blinfo_cases:
result = decodeBlInfo(test_case["base64_data"])
expected = test_case["expected"]
for key in expected:
assert result[key] == expected[key], f"Failed on blinfo key {key}"
# Run tests if file is run directly
if __name__ == "__main__":
pytest.main([__file__])
Response
This is an example of the response from the SRM. The response contains the information about the upgrade availability and the secured load. The secured load is encrypted firmware that can be flashed to the device.
{
"query": "UPGRADE-nightly:VECTOR_LYNX_generic v6.0.0-nightly Nov 27 2024",
"server": "Odoo 17 Silixcon",
"current_rom": {
"swid": "VECTOR_LYNX_generic v6.0.0-nightly Nov 27 2024",
"release_type": "nightly",
"available_upgrade": false
},
"result": 0,
"string_result": "OK",
"secured_load": "MjlFMTQAVkV.............."
}
The secured_load
is a base64 encoded string that contains the encrypted firmware. The host shall decode the secured_load
to binnary format and flash it to the device.
Error response examples
TBD
Flashing the received payload to the device
During the flashing process, the host sends the secured_load
as is to the device as payload. The device will decrypt the secured load and flash it to the internal memory.
2. Send LOAD command
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL/LOAD | Host -> Device | 8*4 + host address (7) Usually 39 | 8 |
Payload
Byte 0 | Byte 1 | Byte 2 - 5 | Byte 6-7 |
---|---|---|---|
0x00 | 0x0E | UINT32_T Litte endian | 0x015C |
Device address | Load | Total lenght | Crypto ID |
Response:
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / LOAD | Device -> host | 8*4 + host address (0 is used as host address) Usually 31 (host with address 7) | 7 |
Payload:
Byte 0 | Byte 1 | Byte 2 | Byte 3 - 7 |
---|---|---|---|
0x00 | 0x4E | 0x10 = success | Chunk size |
Host address | BL/LOAD response | Command result | UINT32_T Litte endian |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2.1 Send the firmware payload - SLOW method
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / LOAD | Host -> Device | 8*4 + host address (7 is used as host address) Usually 39 (host with address 7) | 8, or remaining bytes to be transferred |
Payload:
Byte 0 | Byte 1 | Byte 2--7 |
---|---|---|
0x00 | 0x3E | Total lenght of the payload |
Device address | BL/LOAD DATA indication | Payload 0 - 5 |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2.2 Send the firmware payload - FAST method
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / LOAD | Host -> Device | 8*5 + host address (7 is used as host address) Usually 47 (host with address 7) | 8, or remaining bytes to be transferred |
Payload:
Byte 0 - 7 |
---|
Payload 0 - Payload 7 |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2.3 Send the firmware payload - footer
- CAN
- UART
Footer messge:
The LOAD footer message is transmitted at the end of the chunk. It contains the 8-bit CRC of the transferred data. The CRC is computed from the entire data. If there is a CRC mismatch, not all data were received or either the footer or header message is missing, the host can retry to retransmit the data chunk.
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / LOAD | Host -> Device | 8*4 + host address (7 is used as host address) Usually 39 (host with address 7) | 3 |
Payload:
Byte 0 | Byte 1 | Byte 2 |
---|---|---|
0x00 | 0x2E | CRC8 |
Device address | BL/LOAD DATA footer indication | CRC of the transfered chunk |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
2.4 Receive ack
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / LOAD | Device -> host | 8*4 + host address (0 is used as host address) Usually 32 (host with address 0) | 8 |
Payload:
Byte 0 | Byte 1 | Byte 2 | Byte 3 - 6 | Byte 7 |
---|---|---|---|---|
0x07 | 0x4E | 0x10 - send next chunk 0x00 - done other - error | Next chunk size | Session ID |
Host address | BL/LOAD response | Command result | UINT32_T Litte endian | X |
Exit bootloader
A Last step is necessary to exit the bootloader and enter the main program. To perform this step run command needs to be sent. After this, the device should be updated and prepared to work
If the upgrade process is interrupted, or the internal CRC check of the whole FW is incorrect, the device stays in bootloader mode. In this case, it is necessary to repeat the whole process again.
- CAN
- UART
Service / Command | Direction | CAN ID | DLC |
---|---|---|---|
BL / RUN | Host -> Device | 8*4 + host address (7 is used as host address) Usually 39 (host with address 7) | 2 |
Payload:
Byte 0 | Byte 1 |
---|---|
0xFF | 0x01 |
Device address (broadcast) | RUN command |
Device upgrade is possible over all its interfaces, but it needs to be documented first. :-/
Appendix
CRC8 computation
The CRC8 is computed using the following C-code:
static const uint8_t crc8_table[256] = {0x0, 0x7, 0xE, 0x9, 0x1C, 0x1B,
0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D, 0x70, 0x77, 0x7E,
0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4,
0xC3, 0xCA, 0xCD, 0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF,
0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD, 0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5,
0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA, 0xB7, 0xB0, 0xB9, 0xBE,
0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A, 0x27,
0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04,
0x0D, 0x0A, 0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61,
0x66, 0x73, 0x74, 0x7D, 0x7A, 0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4, 0xF9, 0xFE, 0xF7, 0xF0, 0xE5,
0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4, 0x69, 0x6E,
0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43,
0x44, 0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28,
0x3D, 0x3A, 0x33, 0x34, 0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76,
0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63, 0x3E, 0x39, 0x30, 0x37, 0x22, 0x25,
0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13, 0xAE, 0xA9, 0xA0,
0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA,
0xFD, 0xF4, 0xF3
};
uint8_t
crc8(const uint8_t* ptr, int32_t len)
{
uint8_t crc = 0;
len++;
while (--len)
crc = crc8_table[crc ^ *ptr++];
return crc;
}
ACK and Commands results
The result is bitwise, i.e. there can be multiple results at once.
Name | Value | Description |
---|---|---|
TCP_STE_DONE | 0x00 | |
TCP_ERR_GENERIC | 0x08 | |
TCP_ERR_WRONG_NBYTES | 0x09 | |
TCP_ERR_NO_TRANSFER | 0x0A | |
TCP_ERR_CRC | 0x0B | |
TCP_ERR_CHUNK | 0x0C | |
TCP_ERR_ARB_LOST | 0x0D | |
TCP_ERR_SIZE | 0x0E | |
TCP_ERR_UNSUPPORT | 0x0F | |
TCP_STE_PENDING | 0x10 | |
TCP_STE_EXPEDITED | 0x80 |