package com.silicondust.libhdhomerun;
public final class HDHomerun_Pkt {
/*
* The discover protocol (UDP port 65001) and control protocol (TCP port 65001)
* both use the same packet based format:
* uint16_t Packet type
* uint16_t Payload length (bytes)
* uint8_t[] Payload data (0-n bytes).
* uint32_t CRC (Ethernet style 32-bit CRC)
*
* All variables are big-endian except for the crc which is little-endian.
*
* Valid values for the packet type are listed below as defines prefixed
* with "HDHOMERUN_TYPE_"
*
* Discovery:
*
* The payload for a discovery request or reply is a simple sequence of
* tag-length-value data:
* uint8_t Tag
* varlen Length
* uint8_t[] Value (0-n bytes)
*
* The length field can be one or two bytes long.
* For a length <= 127 bytes the length is expressed as a single byte. The
* most-significant-bit is clear indicating a single-byte length.
* For a length >= 128 bytes the length is expressed as a sequence of two bytes as follows:
* The first byte is contains the least-significant 7-bits of the length. The
* most-significant bit is then set (add 0x80) to indicate that it is a two byte length.
* The second byte contains the length shifted down 7 bits.
*
* A discovery request packet has a packet type of HDHOMERUN_TYPE_DISCOVER_REQ and should
* contain two tags: HDHOMERUN_TAG_DEVICE_TYPE and HDHOMERUN_TAG_DEVICE_ID.
* The HDHOMERUN_TAG_DEVICE_TYPE value should be set to HDHOMERUN_DEVICE_TYPE_TUNER.
* The HDHOMERUN_TAG_DEVICE_ID value should be set to HDHOMERUN_DEVICE_ID_WILDCARD to match
* all devices, or to the 32-bit device id number to match a single device.
*
* The discovery response packet has a packet type of HDHOMERUN_TYPE_DISCOVER_RPY and has the
* same format as the discovery request packet with the two tags: HDHOMERUN_TAG_DEVICE_TYPE and
* HDHOMERUN_TAG_DEVICE_ID. In the future additional tags may also be returned - unknown tags
* should be skipped and not treated as an error.
*
* Control get/set:
*
* The payload for a control get/set request is a simple sequence of tag-length-value data
* following the same format as for discover packets.
*
* A get request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ and should contain
* the tag: HDHOMERUN_TAG_GETSET_NAME. The HDHOMERUN_TAG_GETSET_NAME value should be a sequence
* of bytes forming a null-terminated string, including the NULL. The TLV length must include
* the NULL character so the length field should be set to strlen(str) + 1.
*
* A set request packet has a packet type of HDHOMERUN_TYPE_GETSET_REQ (same as a get request)
* and should contain two tags: HDHOMERUN_TAG_GETSET_NAME and HDHOMERUN_TAG_GETSET_VALUE.
* The HDHOMERUN_TAG_GETSET_NAME value should be a sequence of bytes forming a null-terminated
* string, including the NULL.
* The HDHOMERUN_TAG_GETSET_VALUE value should be a sequence of bytes forming a null-terminated
* string, including the NULL.
*
* The get and set reply packets have the packet type HDHOMERUN_TYPE_GETSET_RPY and have the same
* format as the set request packet with the two tags: HDHOMERUN_TAG_GETSET_NAME and
* HDHOMERUN_TAG_GETSET_VALUE. A set request is also implicit get request so the updated value is
* returned.
*
* If the device encounters an error handling the get or set request then the get/set reply packet
* will contain the tag HDHOMERUN_TAG_ERROR_MESSAGE. The format of the value is a sequence of
* bytes forming a null-terminated string, including the NULL.
*
* In the future additional tags may also be returned - unknown tags should be skipped and not
* treated as an error.
*
* Security note: The application should not rely on the NULL character being present. The
* application should write a NULL character based on the TLV length to protect the application
* from a potential attack.
*
* Firmware Upgrade:
*
* A firmware upgrade packet has a packet type of HDHOMERUN_TYPE_UPGRADE_REQ and has a fixed format:
* uint32_t Position in bytes from start of file.
* uint8_t[256] Firmware data (256 bytes)
*
* The data must be uploaded in 256 byte chunks and must be uploaded in order.
* The position number is in bytes so will increment by 256 each time.
*
* When all data is uploaded it should be signaled complete by sending another packet of type
* HDHOMERUN_TYPE_UPGRADE_REQ with payload of a single uint32_t with the value 0xFFFFFFFF.
*/
public final static int HDHOMERUN_DISCOVER_UDP_PORT = 65001;
public final static int HDHOMERUN_CONTROL_TCP_PORT = 65001;
public final static int HDHOMERUN_MAX_PACKET_SIZE = 1460;
public final static int HDHOMERUN_MAX_PAYLOAD_SIZE = 1452;
public final static short HDHOMERUN_TYPE_DISCOVER_REQ = 0x0002;
public final static short HDHOMERUN_TYPE_DISCOVER_RPY = 0x0003;
public final static short HDHOMERUN_TYPE_GETSET_REQ = 0x0004;
public final static short HDHOMERUN_TYPE_GETSET_RPY = 0x0005;
public final static short HDHOMERUN_TYPE_UPGRADE_REQ = 0x0006;
public final static short HDHOMERUN_TYPE_UPGRADE_RPY = 0x0007;
public final static byte HDHOMERUN_TAG_DEVICE_TYPE = 0x01;
public final static byte HDHOMERUN_TAG_DEVICE_ID = 0x02;
public final static byte HDHOMERUN_TAG_GETSET_NAME = 0x03;
public final static byte HDHOMERUN_TAG_GETSET_VALUE = 0x04;
public final static byte HDHOMERUN_TAG_GETSET_LOCKKEY = 0x15;
public final static byte HDHOMERUN_TAG_ERROR_MESSAGE = 0x05;
public final static byte HDHOMERUN_TAG_TUNER_COUNT = 0x10;
public final static int HDHOMERUN_DEVICE_TYPE_WILDCARD = 0xFFFFFFFF;
public final static int HDHOMERUN_DEVICE_TYPE_TUNER = 0x00000001;
public final static int HDHOMERUN_DEVICE_ID_WILDCARD = 0xFFFFFFFF;
public final static int HDHOMERUN_MIN_PEEK_LENGTH = 4;
public final static int PKT_BUFFER_SIZE = 3074;
public int posIndex; // ubyte8* damn java
public int startIndex; // ubyte8*
public int endIndex; // ubyte8*
public int limitIndex; // ubyte8*
public byte[] buffer = new byte[PKT_BUFFER_SIZE];
public HDHomerun_Pkt()
{
reset();
}
private static final int BUFFER_START_INDEX = 1024;
public void reset()
{
limitIndex = PKT_BUFFER_SIZE - 4;
startIndex = BUFFER_START_INDEX;
endIndex = startIndex;
posIndex = startIndex;
}
public byte read_u8() // uint8_t
{
return (byte) HDHomerun_OS.getRealUByteVal(buffer[posIndex++]);
}
public short read_u16() // uint16_t
{
short v = 0;
v |= (short) HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 8;
v |= (short) HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 0;
return v;
}
public int read_u32()
{
int v = 0;
v |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 24;
v |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 16;
v |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 8;
v |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 0;
return v;
}
public int read_var_length()
{
int length;
if (posIndex + 1 > endIndex) {
return -1;
}
length = (int) buffer[posIndex++];
if ((length & 0x0080) > 0) {
if (posIndex + 1 > endIndex) {
return -1;
}
length &= 0x007F;
length |= ((int) buffer[posIndex++]) << 7;
}
return length;
}
public int read_tlv(byte[] ptag, int[] plength) // uint8_t
{
if (posIndex + 2 > endIndex) {
return 0;
}
ptag[0] = read_u8();
plength[0] = read_var_length();
if (posIndex + plength[0] > endIndex) {
return 0;
}
return posIndex + plength[0];
}
public void write_u8( byte v) // uint8_t
{
buffer[posIndex++] = v;
if (posIndex > endIndex) {
endIndex = posIndex;
}
}
public void write_u16(short v) // uint16_t
{
buffer[posIndex++] = (byte)(v >> 8);
buffer[posIndex++] = (byte)(v >> 0);
if (posIndex > endIndex) {
endIndex = posIndex;
}
}
public void write_u32(int v) // uint32_t
{
buffer[posIndex++] = (byte)(v >> 24);
buffer[posIndex++] = (byte)(v >> 16);
buffer[posIndex++] = (byte)(v >> 8);
buffer[posIndex++] = (byte)(v >> 0);
if (posIndex > endIndex) {
endIndex = posIndex;
}
}
public void write_var_length(short v) // size_t
{
if (v <= 127) {
buffer[posIndex++] = (byte)v;
} else {
buffer[posIndex++] = (byte)(v | 0x80);
buffer[posIndex++] = (byte)(v >> 7);
}
if (posIndex > endIndex) {
endIndex = posIndex;
}
}
public void write_mem(final byte[] mem, short length) // const void*, size_t
{
for(short cnt = 0; cnt < length; ++cnt)
buffer[posIndex++] = mem[cnt];
if (posIndex > endIndex) {
endIndex = posIndex;
}
}
public int open_frame(short[] ptype) // uint16_t *
{
posIndex = startIndex;
if ((posIndex + 4) > endIndex) {
return 0;
}
ptype[0] = read_u16();
short length = read_u16();
posIndex += length;
if ((posIndex + 4) > endIndex) {
posIndex = startIndex;
return 0;
}
final int calc_crc = calc_crc(buffer, startIndex, posIndex); // uint32_t
int packet_crc = 0; // uint32_t
packet_crc |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 0;
packet_crc |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 8;
packet_crc |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 16;
packet_crc |= HDHomerun_OS.getRealUByteVal(buffer[posIndex++]) << 24;
if (calc_crc != packet_crc) {
return -1;
}
startIndex += 4;
endIndex = (short) (startIndex + length);
posIndex = startIndex;
return 1;
}
public void seal_frame(short frame_type) // uint16_t
{
short length = (short) (endIndex - startIndex);
startIndex -= 4;
posIndex = startIndex;
write_u16(frame_type);
write_u16(length);
int crc = calc_crc(buffer, startIndex, endIndex);
buffer[endIndex++] = (byte)(crc >> 0);
buffer[endIndex++] = (byte)(crc >> 8);
buffer[endIndex++] = (byte)(crc >> 16);
buffer[endIndex++] = (byte)(crc >> 24);
posIndex = startIndex;
}
private int calc_crc(byte[] buffer, int start, int end) // uint32_t
{
int pos = start; // uint8_t *
int crc = 0xFFFFFFFF; // uint32_t
while (pos < end) {
byte x = (byte) ((byte)(crc) ^ buffer[pos++]);
crc = crc >>> 8;
if ((x & 0x01) > 0) crc ^= 0x77073096;
if ((x & 0x02) > 0) crc ^= 0xEE0E612C;
if ((x & 0x04) > 0) crc ^= 0x076DC419;
if ((x & 0x08) > 0) crc ^= 0x0EDB8832;
if ((x & 0x10) > 0) crc ^= 0x1DB71064;
if ((x & 0x20) > 0) crc ^= 0x3B6E20C8;
if ((x & 0x40) > 0) crc ^= 0x76DC4190;
if ((x & 0x80) > 0) crc ^= 0xEDB88320;
}
return crc ^ 0xFFFFFFFF;
}
}