mirror of
https://github.com/niellun/FastCarPlay.git
synced 2026-06-07 09:38:25 +02:00
Encrypted protocol, autoconnect, logging refactor
This commit is contained in:
@@ -0,0 +1,110 @@
|
||||
#include "aes_cipher.h"
|
||||
|
||||
#include <openssl/aes.h>
|
||||
#include <openssl/evp.h>
|
||||
#include <stdexcept>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <ctime>
|
||||
|
||||
AESCipher::AESCipher(const std::string &baseKey)
|
||||
: _baseKey(baseKey)
|
||||
{
|
||||
std::srand(std::time(nullptr));
|
||||
_seed = static_cast<uint32_t>(std::rand());
|
||||
|
||||
if (_baseKey.size() != keyLength)
|
||||
{
|
||||
std::cerr << "[Cypher] Error: base key must be exactly 16 bytes" << std::endl;
|
||||
throw std::invalid_argument("Base key must be exactly 16 bytes");
|
||||
}
|
||||
|
||||
for (uint8_t i = 0; i < keyLength; ++i)
|
||||
{
|
||||
_encKey[i] = static_cast<uint8_t>(_baseKey[(_seed + i) % keyLength]);
|
||||
}
|
||||
|
||||
_initVec.fill(0);
|
||||
_initVec[1] = static_cast<uint8_t>(_seed);
|
||||
_initVec[4] = static_cast<uint8_t>(_seed >> 8);
|
||||
_initVec[9] = static_cast<uint8_t>(_seed >> 16);
|
||||
_initVec[12] = static_cast<uint8_t>(_seed >> 24);
|
||||
}
|
||||
|
||||
bool AESCipher::Encrypt(uint8_t *data, uint16_t length) const
|
||||
{
|
||||
if (!data || length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx)
|
||||
{
|
||||
std::cerr << "[Cypher] Failed to create cipher context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_EncryptInit_ex(ctx.get(), EVP_aes_128_cfb(), nullptr, _encKey.data(), _initVec.data()) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption initialization failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> temp(new uint8_t[length + AES_BLOCK_SIZE]);
|
||||
int out_len = 0;
|
||||
if (EVP_EncryptUpdate(ctx.get(), temp.get(), &out_len, data, length) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption failed during update" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int final_len = 0;
|
||||
if (EVP_EncryptFinal_ex(ctx.get(), temp.get() + out_len, &final_len) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Encryption failed during final" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy_n(temp.get(), length, data);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AESCipher::Decrypt(uint8_t *data, uint16_t length) const
|
||||
{
|
||||
if (!data || length == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ctx = std::unique_ptr<EVP_CIPHER_CTX, decltype(&EVP_CIPHER_CTX_free)>(EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free);
|
||||
if (!ctx)
|
||||
{
|
||||
std::cerr << "[Cypher] Failed to create cipher context" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (EVP_DecryptInit_ex(ctx.get(), EVP_aes_128_cfb(), nullptr, _encKey.data(), _initVec.data()) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption initialization failed" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<uint8_t[]> temp(new uint8_t[length + AES_BLOCK_SIZE]);
|
||||
int out_len = 0;
|
||||
if (EVP_DecryptUpdate(ctx.get(), temp.get(), &out_len, data, length) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption failed during update" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
int final_len = 0;
|
||||
if (EVP_DecryptFinal_ex(ctx.get(), temp.get() + out_len, &final_len) != 1)
|
||||
{
|
||||
std::cerr << "[Cypher] Decryption failed during final" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::copy_n(temp.get(), length, data);
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
#ifndef SRC_AES_CIPHER
|
||||
#define SRC_AES_CIPHER
|
||||
|
||||
#include <array>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
class AESCipher
|
||||
{
|
||||
public:
|
||||
static constexpr size_t keyLength = 16;
|
||||
|
||||
AESCipher(const std::string &base_key);
|
||||
~AESCipher() = default;
|
||||
|
||||
bool Encrypt(uint8_t *data, uint16_t length) const;
|
||||
bool Decrypt(uint8_t *data, uint16_t length) const;
|
||||
|
||||
uint32_t Seed() const { return _seed; }
|
||||
const std::string& Key() const { return _baseKey; }
|
||||
|
||||
private:
|
||||
std::string _baseKey;
|
||||
uint32_t _seed;
|
||||
std::array<uint8_t, keyLength> _encKey;
|
||||
std::array<uint8_t, keyLength> _initVec;
|
||||
};
|
||||
|
||||
#endif /* SRC_AES_CIPHER */
|
||||
+87
-54
@@ -4,12 +4,22 @@
|
||||
#include <iostream>
|
||||
#include <condition_variable>
|
||||
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
#include "settings.h"
|
||||
|
||||
Connector::Connector(uint16_t videoPadding)
|
||||
: _videoPadding(videoPadding)
|
||||
{
|
||||
try
|
||||
{
|
||||
_cipher = new AESCipher(ENCRYPTION_BASE);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
_cipher = nullptr;
|
||||
}
|
||||
|
||||
int result = libusb_init(&_context);
|
||||
if (result < 0)
|
||||
throw std::runtime_error(std::string("Can't initialise USB: ") + libusb_error_name(result));
|
||||
@@ -60,13 +70,13 @@ void Connector::stop()
|
||||
|
||||
bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
|
||||
{
|
||||
status("Searching for device");
|
||||
status("Searching for dongle");
|
||||
|
||||
std::cout << "Creating device handle" << std::endl;
|
||||
_device = libusb_open_device_with_vid_pid(_context, vendor_id, product_id);
|
||||
if (!_device)
|
||||
{
|
||||
status("Can't find device");
|
||||
std::cout << "[Connection] Failed to create device handle - no device" << std::endl;
|
||||
status("Can't find dongle");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -81,26 +91,38 @@ bool Connector::connect(uint16_t vendor_id, uint16_t product_id)
|
||||
|
||||
bool Connector::link()
|
||||
{
|
||||
status("Linking device");
|
||||
int usbres = 0;
|
||||
status("Linking dongle");
|
||||
|
||||
std::cout << "Reset device" << std::endl;
|
||||
if (libusb_reset_device(_device) < 0)
|
||||
usbres = libusb_reset_device(_device);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't reset device: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Set configuration" << std::endl;
|
||||
if (libusb_set_configuration(_device, 1) < 0)
|
||||
usbres = libusb_set_configuration(_device, 1);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't set configuration: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Claim interface" << std::endl;
|
||||
if (libusb_claim_interface(_device, 0) < 0)
|
||||
usbres = libusb_claim_interface(_device, 0);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't claim interface: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << "Get config descriptor" << std::endl;
|
||||
libusb_device *dev = libusb_get_device(_device);
|
||||
struct libusb_config_descriptor *config = nullptr;
|
||||
|
||||
if (libusb_get_active_config_descriptor(dev, &config) < 0)
|
||||
usbres = libusb_get_active_config_descriptor(dev, &config);
|
||||
if (usbres < 0)
|
||||
{
|
||||
std::cout << "[Connection] Can't get config: " << libusb_error_name(usbres) << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 0; i < config->interface[0].altsetting[0].bNumEndpoints; i++)
|
||||
{
|
||||
@@ -108,18 +130,14 @@ bool Connector::link()
|
||||
if ((ep->bEndpointAddress & LIBUSB_ENDPOINT_DIR_MASK) == LIBUSB_ENDPOINT_IN)
|
||||
{
|
||||
_endpoint_in = ep->bEndpointAddress;
|
||||
std::cout << "Found input endpoint" << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
_endpoint_out = ep->bEndpointAddress;
|
||||
std::cout << "Found output endpoint" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Free config descriptor" << std::endl;
|
||||
libusb_free_config_descriptor(config);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -135,58 +153,56 @@ void Connector::release()
|
||||
|
||||
void Connector::status(const char *status)
|
||||
{
|
||||
std::cout << status << std::endl;
|
||||
if (_protocol)
|
||||
_protocol->onStatus(status);
|
||||
}
|
||||
|
||||
void Connector::write_uint32_le(uint8_t *dst, uint32_t value)
|
||||
{
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
dst[2] = (value >> 16) & 0xFF;
|
||||
dst[3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
int Connector::send(int cmd)
|
||||
int Connector::send(int cmd, bool encrypt, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (!_connected)
|
||||
return 0;
|
||||
|
||||
int transferred;
|
||||
uint8_t buffer[16];
|
||||
uint32_t magic = MAGIC;
|
||||
encrypt = encrypt && _ecnrypt;
|
||||
|
||||
write_uint32_le(&buffer[0], 0x55AA55AA);
|
||||
write_uint32_le(&buffer[4], 0);
|
||||
write_uint32_le(&buffer[8], cmd);
|
||||
write_uint32_le(&buffer[12], ~cmd);
|
||||
if (encrypt && data && size > 0)
|
||||
{
|
||||
if (_cipher->Encrypt(data, size))
|
||||
magic = MAGIC_ENC;
|
||||
}
|
||||
|
||||
std::unique_lock<std::mutex> lock(_write_mutex);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, buffer, 16, &transferred, 0);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
int Connector::send(int cmd, uint8_t *data, uint32_t size)
|
||||
{
|
||||
if (!_connected)
|
||||
return 0;
|
||||
|
||||
int transferred;
|
||||
uint8_t buffer[16];
|
||||
|
||||
write_uint32_le(&buffer[0], 0x55AA55AA);
|
||||
write_uint32_le(&buffer[0], magic);
|
||||
write_uint32_le(&buffer[4], size);
|
||||
write_uint32_le(&buffer[8], cmd);
|
||||
write_uint32_le(&buffer[12], ~cmd);
|
||||
|
||||
std::unique_lock<std::mutex> lock(_write_mutex);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, buffer, 16, &transferred, 0);
|
||||
libusb_bulk_transfer(_device, _endpoint_out, data, size, &transferred, 0);
|
||||
if (data && size > 0)
|
||||
libusb_bulk_transfer(_device, _endpoint_out, data, size, &transferred, 0);
|
||||
|
||||
return transferred;
|
||||
}
|
||||
|
||||
void Connector::setEncryption(bool enabled)
|
||||
{
|
||||
if (!enabled)
|
||||
{
|
||||
_ecnrypt = false;
|
||||
return;
|
||||
}
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Connection] Can't enable encryption: cypher initialisation failed" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::cout << "[Connection] Encryption enabled" << std::endl;
|
||||
_ecnrypt = true;
|
||||
}
|
||||
|
||||
void Connector::read_loop()
|
||||
{
|
||||
std::mutex mtx;
|
||||
@@ -196,7 +212,7 @@ void Connector::read_loop()
|
||||
uint8_t *data;
|
||||
|
||||
// Set thread name
|
||||
setThreadName( "protocol-reader");
|
||||
setThreadName("protocol-reader");
|
||||
while (_active)
|
||||
{
|
||||
if (!_connected)
|
||||
@@ -211,6 +227,7 @@ void Connector::read_loop()
|
||||
|
||||
if (result == LIBUSB_ERROR_NO_DEVICE)
|
||||
{
|
||||
std::cout << "[Connection] Device disconnected" << std::endl;
|
||||
if (_protocol)
|
||||
_protocol->onDevice(false);
|
||||
_connected = false;
|
||||
@@ -235,13 +252,29 @@ void Connector::read_loop()
|
||||
}
|
||||
|
||||
if (!_protocol)
|
||||
free(data);
|
||||
else
|
||||
{
|
||||
if (padding > 0)
|
||||
std::fill(data + header.length, data + header.length + padding, 0);
|
||||
_protocol->onData(header.type, header.length, data);
|
||||
free(data);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (header.magic == MAGIC_ENC)
|
||||
{
|
||||
|
||||
if (!_cipher)
|
||||
{
|
||||
std::cout << "[Connection] Received encrypted command " << header.type <<" but cipher is not initialised" << std::endl;
|
||||
continue;
|
||||
}
|
||||
if (!_cipher->Decrypt(data, header.length))
|
||||
{
|
||||
std::cout << "[Connection] Can't decrypt command " << header.type << std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (padding > 0)
|
||||
std::fill(data + header.length, data + header.length + padding, 0);
|
||||
_protocol->onData(header.type, header.length, data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +291,7 @@ void Connector::write_loop()
|
||||
_connected = connect(Settings::vendorid, Settings::productid);
|
||||
if (_connected)
|
||||
{
|
||||
status("Starting device");
|
||||
status("Initialising dongle");
|
||||
if (_protocol)
|
||||
_protocol->onDevice(true);
|
||||
|
||||
|
||||
+12
-7
@@ -9,11 +9,14 @@
|
||||
#include <string>
|
||||
|
||||
#include "helper/iprotocol.h"
|
||||
#include "aes_cipher.h"
|
||||
|
||||
#define READ_TIMEOUT 5000
|
||||
#define ENCRYPTION_BASE "SkBRDy3gmrw1ieH0"
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct Header {
|
||||
struct Header
|
||||
{
|
||||
uint32_t magic;
|
||||
uint32_t length;
|
||||
uint32_t type;
|
||||
@@ -31,10 +34,10 @@ public:
|
||||
void start(IProtocol *protocol);
|
||||
void stop();
|
||||
|
||||
int send(int cmd, uint8_t *data, uint32_t size);
|
||||
int send(int cmd);
|
||||
int send(int cmd, bool encrypt = true, uint8_t *data = nullptr, uint32_t size = 0);
|
||||
void setEncryption(bool enabled);
|
||||
|
||||
static void write_uint32_le(uint8_t *dst, uint32_t value);
|
||||
AESCipher *Cypher() const { return _cipher; };
|
||||
|
||||
private:
|
||||
void read_loop();
|
||||
@@ -43,14 +46,15 @@ private:
|
||||
bool connect(uint16_t vendor_id, uint16_t product_id);
|
||||
bool link();
|
||||
void release();
|
||||
|
||||
void status(const char* status);
|
||||
|
||||
void status(const char *status);
|
||||
|
||||
libusb_context *_context = nullptr;
|
||||
libusb_device_handle *_device = nullptr;
|
||||
uint8_t _endpoint_in;
|
||||
uint8_t _endpoint_out;
|
||||
bool _connected;
|
||||
std::atomic<bool> _ecnrypt = false;
|
||||
|
||||
std::thread _read_thread;
|
||||
std::thread _write_thread;
|
||||
@@ -59,7 +63,8 @@ private:
|
||||
|
||||
u_int16_t _videoPadding;
|
||||
|
||||
IProtocol* _protocol = nullptr;
|
||||
IProtocol *_protocol = nullptr;
|
||||
AESCipher *_cipher = nullptr;
|
||||
};
|
||||
|
||||
#endif /* SRC_CONNECTOR */
|
||||
|
||||
+11
-13
@@ -52,18 +52,18 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
result = avcodec_alloc_context3(codec);
|
||||
if (!result)
|
||||
{
|
||||
std::cout << "Can't load HW codec " << codec->name << ": out of memory" << std::endl;
|
||||
std::cout << "[Video] Can't load HW codec " << codec->name << ": out of memory" << std::endl;
|
||||
break;
|
||||
}
|
||||
|
||||
int ret = avcodec_open2(result, codec, nullptr);
|
||||
if (ret == 0)
|
||||
{
|
||||
std::cout << "Using HW decoder: " << codec->name << std::endl;
|
||||
std::cout << "[Video] Using HW decoder: " << codec->name << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::cout << "Can't load HW codec " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
std::cout << "[Video] Can't load HW codec " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
avcodec_free_context(&result);
|
||||
}
|
||||
|
||||
@@ -71,33 +71,33 @@ AVCodecContext *Decoder::load_codec(AVCodecID codec_id)
|
||||
codec = avcodec_find_decoder(codec_id);
|
||||
if (!codec)
|
||||
{
|
||||
std::cout << "Decoder not found for codec id " << codec_id << std::endl;
|
||||
std::cout << "[Video] Decoder not found for codec id " << codec_id << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
result = avcodec_alloc_context3(codec);
|
||||
if (!result)
|
||||
{
|
||||
std::cout << "Failed to allocate context for codec id " << codec_id << std::endl;
|
||||
std::cout << "[Video] Failed to allocate context for codec id " << codec_id << std::endl;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int ret = avcodec_open2(result, codec, nullptr);
|
||||
if (ret < 0)
|
||||
{
|
||||
std::cout << "Failed to open software decoder " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
std::cout << "[Video] Failed to open software decoder " << codec->name << ": " << Error::avErrorText(ret) << std::endl;
|
||||
avcodec_free_context(&result);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::cout << "Using SW decoder " << codec->name << std::endl;
|
||||
std::cout << "[Video] Using SW decoder " << codec->name << std::endl;
|
||||
return result;
|
||||
}
|
||||
|
||||
void Decoder::runner()
|
||||
{
|
||||
// Set thread name
|
||||
setThreadName( "video-decoding");
|
||||
setThreadName("video-decoding");
|
||||
|
||||
// Load codec context
|
||||
AVCodecContext *context = load_codec(_codecId);
|
||||
@@ -127,15 +127,13 @@ void Decoder::runner()
|
||||
avcodec_free_context(&context);
|
||||
|
||||
if (_status.error())
|
||||
std::cout << "Video decoder error: " << _status.message() << std::endl;
|
||||
std::cout << "[Video] Decoder error: " << _status.message() << std::endl;
|
||||
}
|
||||
|
||||
void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPacket *packet, AVFrame *frame)
|
||||
{
|
||||
uint32_t counter = 0;
|
||||
|
||||
std::cout << "Video decoder loop started" << std::endl;
|
||||
|
||||
// Main decoding loop; runs until global_quit flag is set
|
||||
while (_active)
|
||||
{
|
||||
@@ -182,14 +180,14 @@ void Decoder::loop(AVCodecContext *context, AVCodecParserContext *parser, AVPack
|
||||
int send_ret = avcodec_send_packet(context, packet);
|
||||
if (send_ret != 0)
|
||||
{
|
||||
std::cout << "Error decoding packet: " << Error::avErrorText(send_ret) << std::endl;
|
||||
std::cout << "[Video] Can't decode packet: " << Error::avErrorText(send_ret) << std::endl;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Receive decoded frames
|
||||
while (avcodec_receive_frame(context, frame) == 0 && _active)
|
||||
{
|
||||
AVFrame* out = _vb->write(counter++);
|
||||
AVFrame *out = _vb->write(counter++);
|
||||
av_frame_unref(out);
|
||||
av_frame_move_ref(out, frame);
|
||||
_vb->commit();
|
||||
|
||||
@@ -21,4 +21,12 @@ inline void disable_cout()
|
||||
std::cout.setstate(std::ios_base::failbit);
|
||||
}
|
||||
|
||||
inline void write_uint32_le(uint8_t *dst, uint32_t value)
|
||||
{
|
||||
dst[0] = value & 0xFF;
|
||||
dst[1] = (value >> 8) & 0xFF;
|
||||
dst[2] = (value >> 16) & 0xFF;
|
||||
dst[3] = (value >> 24) & 0xFF;
|
||||
}
|
||||
|
||||
#endif /* SRC_HELPER_FUNCTIONS */
|
||||
|
||||
@@ -0,0 +1,57 @@
|
||||
#ifndef SRC_HELPER_PROTOCOL_CONST
|
||||
#define SRC_HELPER_PROTOCOL_CONST
|
||||
|
||||
#define MAGIC 0x55aa55aa
|
||||
#define MAGIC_ENC 0x55bb55bb
|
||||
|
||||
#define CMD_OPEN 1
|
||||
#define CMD_PLUGGED 2
|
||||
#define CMD_STATE 3
|
||||
#define CMD_UNPLUGGED 4
|
||||
#define CMD_TOUCH 5
|
||||
#define CMD_VIDEO_DATA 6
|
||||
#define CMD_AUDIO_DATA 7
|
||||
#define CMD_CONTROL 8
|
||||
#define CMD_UNKNOWN_9 9
|
||||
#define CMD_APP_INFO 10
|
||||
#define CMD_BLUETOOTH_INFO 13
|
||||
#define CMD_WIFI_INFO 14
|
||||
#define CMD_DEVICE_LIST 18
|
||||
#define CMD_JSON_CONTROL 25
|
||||
#define CMD_MANUFACTURER 20
|
||||
#define CMD_UNKNOWN_38 38
|
||||
#define CMD_SEND_FILE 153
|
||||
#define CMD_UNKNOWN_136 136
|
||||
#define CMD_DAYNIGHT 162
|
||||
#define CMD_HEARTBEAT 170
|
||||
#define CMD_VERSION 204
|
||||
#define CMD_ENCRYPTION 240
|
||||
|
||||
struct ProtocolCmdEntry
|
||||
{
|
||||
int cmd;
|
||||
const char *name;
|
||||
};
|
||||
|
||||
const ProtocolCmdEntry protocolCmdList[] = {
|
||||
{CMD_OPEN, "Open"},
|
||||
{CMD_PLUGGED, "Plugged"},
|
||||
{CMD_STATE, "State"},
|
||||
{CMD_UNPLUGGED, "Unplugged"},
|
||||
{CMD_TOUCH, "Touch"},
|
||||
{CMD_VIDEO_DATA, "Video"},
|
||||
{CMD_AUDIO_DATA, "Audio"},
|
||||
{CMD_CONTROL, "Control Bin"},
|
||||
{CMD_APP_INFO, "AppInfo"},
|
||||
{CMD_BLUETOOTH_INFO, "Bluetooth Info"},
|
||||
{CMD_WIFI_INFO, "WiFi Info"},
|
||||
{CMD_DEVICE_LIST, "Device List"},
|
||||
{CMD_JSON_CONTROL, "Control JSON"},
|
||||
{CMD_MANUFACTURER, "Manufacturer"},
|
||||
{CMD_SEND_FILE, "File"},
|
||||
{CMD_DAYNIGHT, "DeyNight Mode"},
|
||||
{CMD_HEARTBEAT, "Heartbeat"},
|
||||
{CMD_VERSION, "Version"},
|
||||
{CMD_ENCRYPTION, "Encryption"}};
|
||||
|
||||
#endif /* SRC_HELPER_PROTOCOL_CONST */
|
||||
+4
-4
@@ -207,7 +207,7 @@ void application()
|
||||
DrawText(textFont, status);
|
||||
SDL_RenderPresent(renderer);
|
||||
|
||||
std::cout << " > Application loop" << std::endl;
|
||||
std::cout << "[Main] Loop" << std::endl;
|
||||
Renderer videoRenderer(renderer);
|
||||
bool dirty = true;
|
||||
bool connected = false;
|
||||
@@ -276,7 +276,7 @@ void application()
|
||||
SDL_Delay(frameDelay - frameTime); // Sleep only the remaining time
|
||||
}
|
||||
}
|
||||
std::cout << " > Application stopping" << std::endl;
|
||||
std::cout << "[Main] Stopping" << std::endl;
|
||||
SDL_HideWindow(window);
|
||||
}
|
||||
|
||||
@@ -343,9 +343,9 @@ int main(int argc, char **argv)
|
||||
renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
|
||||
if (renderer)
|
||||
{
|
||||
std::cout << " > Application started" << std::endl;
|
||||
std::cout << "[Main] Started" << std::endl;
|
||||
application();
|
||||
std::cout << " > Application finish" << std::endl;
|
||||
std::cout << "[Main] Finish" << std::endl;
|
||||
SDL_DestroyRenderer(renderer);
|
||||
}
|
||||
else
|
||||
|
||||
+1
-1
@@ -99,7 +99,7 @@ void PcmAudio::runner()
|
||||
device = SDL_OpenAudioDevice(nullptr, 0, &spec, nullptr, 0);
|
||||
if (device == 0)
|
||||
{
|
||||
std::cerr << "Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
std::cerr << "[Audio] Failed to open audio: " << SDL_GetError() << std::endl;
|
||||
continue;
|
||||
}
|
||||
// Calculate new buffer target: 0.5s
|
||||
|
||||
+88
-55
@@ -1,6 +1,10 @@
|
||||
#include "protocol.h"
|
||||
#include "helper/protocol_const.h"
|
||||
#include "helper/functions.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <iomanip>
|
||||
#include <cctype>
|
||||
|
||||
Protocol::Protocol(uint16_t width, uint16_t height, uint16_t fps, uint16_t padding)
|
||||
: connector(padding),
|
||||
@@ -22,25 +26,12 @@ Protocol::~Protocol()
|
||||
|
||||
const char *Protocol::cmdString(int cmd)
|
||||
{
|
||||
switch (cmd)
|
||||
for (const ProtocolCmdEntry &entry : protocolCmdList)
|
||||
{
|
||||
case CMD_OPEN:
|
||||
return "Open";
|
||||
case CMD_PLUGGED:
|
||||
return "Plugged";
|
||||
case CMD_UNPLUGGED:
|
||||
return "Unplugged";
|
||||
case CMD_TOUCH:
|
||||
return "Touch";
|
||||
case CMD_VIDEO_DATA:
|
||||
return "Video";
|
||||
case CMD_AUDIO_DATA:
|
||||
return "Audio";
|
||||
case CMD_SEND_FILE:
|
||||
return "File";
|
||||
default:
|
||||
return "Unknown";
|
||||
if (entry.cmd == cmd)
|
||||
return entry.name;
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
void Protocol::start(StatusCallback onStatus)
|
||||
@@ -57,15 +48,15 @@ void Protocol::stop()
|
||||
void Protocol::sendInit(int width, int height, int fps)
|
||||
{
|
||||
uint8_t buf[28];
|
||||
Connector::write_uint32_le(&buf[0], width);
|
||||
Connector::write_uint32_le(&buf[4], height);
|
||||
Connector::write_uint32_le(&buf[8], fps);
|
||||
Connector::write_uint32_le(&buf[12], 5);
|
||||
Connector::write_uint32_le(&buf[16], 49152);
|
||||
Connector::write_uint32_le(&buf[20], 2);
|
||||
Connector::write_uint32_le(&buf[24], 2);
|
||||
write_uint32_le(&buf[0], width);
|
||||
write_uint32_le(&buf[4], height);
|
||||
write_uint32_le(&buf[8], fps);
|
||||
write_uint32_le(&buf[12], 5);
|
||||
write_uint32_le(&buf[16], 49152);
|
||||
write_uint32_le(&buf[20], 2);
|
||||
write_uint32_le(&buf[24], 2);
|
||||
|
||||
connector.send(1, buf, 28);
|
||||
connector.send(1, true, buf, 28);
|
||||
}
|
||||
|
||||
void Protocol::sendKey(int key)
|
||||
@@ -73,9 +64,9 @@ void Protocol::sendKey(int key)
|
||||
printf("Send key %d", key);
|
||||
|
||||
uint8_t buf[4];
|
||||
Connector::write_uint32_le(&buf[0], key);
|
||||
write_uint32_le(&buf[0], key);
|
||||
|
||||
connector.send(8, buf, 4);
|
||||
connector.send(8, false, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const char *value)
|
||||
@@ -96,28 +87,28 @@ void Protocol::sendFile(const char *filename, const char *value)
|
||||
void Protocol::sendFile(const char *filename, int value)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
Connector::write_uint32_le(buf, value);
|
||||
write_uint32_le(buf, value);
|
||||
sendFile(filename, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendClick(float x, float y, bool down)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
Connector::write_uint32_le(buf, down ? 14 : 16);
|
||||
Connector::write_uint32_le(buf + 4, int(10000 * x));
|
||||
Connector::write_uint32_le(buf + 8, int(10000 * y));
|
||||
Connector::write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, buf, 16);
|
||||
write_uint32_le(buf, down ? 14 : 16);
|
||||
write_uint32_le(buf + 4, int(10000 * x));
|
||||
write_uint32_le(buf + 8, int(10000 * y));
|
||||
write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendMove(float dx, float dy)
|
||||
{
|
||||
uint8_t buf[16];
|
||||
Connector::write_uint32_le(buf, 15);
|
||||
Connector::write_uint32_le(buf + 4, int(10000 * dx));
|
||||
Connector::write_uint32_le(buf + 8, int(10000 * dy));
|
||||
Connector::write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, buf, 16);
|
||||
write_uint32_le(buf, 15);
|
||||
write_uint32_le(buf + 4, int(10000 * dx));
|
||||
write_uint32_le(buf + 8, int(10000 * dy));
|
||||
write_uint32_le(buf + 12, 0);
|
||||
connector.send(5, false, buf, 16);
|
||||
}
|
||||
|
||||
void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t length)
|
||||
@@ -131,7 +122,7 @@ void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t leng
|
||||
uint8_t *buf = result.data();
|
||||
|
||||
// 1) filename length (LE)
|
||||
Connector::write_uint32_le(buf, fn_len);
|
||||
write_uint32_le(buf, fn_len);
|
||||
buf += 4;
|
||||
|
||||
// 2) filename bytes (including the '\0')
|
||||
@@ -139,13 +130,33 @@ void Protocol::sendFile(const char *filename, const uint8_t *data, uint32_t leng
|
||||
buf += fn_len;
|
||||
|
||||
// 3) content length (LE)
|
||||
Connector::write_uint32_le(buf, length);
|
||||
write_uint32_le(buf, length);
|
||||
buf += 4;
|
||||
|
||||
// 4) content bytes
|
||||
std::memcpy(buf, data, length);
|
||||
|
||||
connector.send(CMD_SEND_FILE, result.data(), total);
|
||||
connector.send(CMD_SEND_FILE, true, result.data(), total);
|
||||
}
|
||||
|
||||
void Protocol::sendInt(uint32_t cmd, uint32_t value, bool encryption)
|
||||
{
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, value);
|
||||
connector.send(cmd, encryption, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::sendEncryption()
|
||||
{
|
||||
AESCipher *cypher = connector.Cypher();
|
||||
if (!cypher)
|
||||
{
|
||||
std::cout << "[Protocol] Can't enable encryption: cypher is not initalised";
|
||||
return;
|
||||
}
|
||||
uint8_t buf[4];
|
||||
write_uint32_le(buf, cypher->Seed());
|
||||
connector.send(CMD_ENCRYPTION, false, buf, 4);
|
||||
}
|
||||
|
||||
void Protocol::onStatus(const char *status)
|
||||
@@ -158,14 +169,24 @@ void Protocol::onDevice(bool connected)
|
||||
{
|
||||
if (connected)
|
||||
{
|
||||
if (Settings::encryption)
|
||||
sendEncryption();
|
||||
sendInit(_width, _height, _fps);
|
||||
sendFile("/tmp/night_mode", 0); // 0==day, 1==night
|
||||
if (Settings::dpi > 0)
|
||||
sendFile("/tmp/screen_dpi", Settings::dpi);
|
||||
sendFile("/etc/android_work_mode", 1);
|
||||
sendFile("/tmp/night_mode", 2); // 0==day, 1==night, 2==???
|
||||
sendFile("/tmp/hand_drive_mode", 0); // 0==left, 1==right
|
||||
sendFile("/tmp/charge_mode", 0);
|
||||
sendFile("/etc/box_name", "CarPlay");
|
||||
if (Settings::autoconnect)
|
||||
sendInt(CMD_CONTROL, 1002);
|
||||
if (Settings::encryption)
|
||||
sendEncryption();
|
||||
}
|
||||
else
|
||||
{
|
||||
connector.setEncryption(false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -178,7 +199,7 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
{
|
||||
if (length <= 20)
|
||||
break;
|
||||
videoData.pushDiscard( std::make_unique<Message>(data, length, 20));
|
||||
videoData.pushDiscard(std::make_unique<Message>(data, length, 20));
|
||||
dispose = false;
|
||||
break;
|
||||
}
|
||||
@@ -222,6 +243,13 @@ void Protocol::onData(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
phoneConnected = false;
|
||||
break;
|
||||
}
|
||||
case CMD_ENCRYPTION:
|
||||
{
|
||||
if (length == 0)
|
||||
connector.setEncryption(true);
|
||||
print_message(cmd, length, data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
print_message(cmd, length, data);
|
||||
@@ -236,7 +264,7 @@ void Protocol::print_ints(uint32_t length, uint8_t *data, uint16_t max)
|
||||
{
|
||||
if (data && length >= 4)
|
||||
{
|
||||
printf(" > ");
|
||||
std::cout << " > ";
|
||||
size_t count = length / 4;
|
||||
for (size_t i = 0; (i < count) & (i < max); ++i)
|
||||
{
|
||||
@@ -245,9 +273,9 @@ void Protocol::print_ints(uint32_t length, uint8_t *data, uint16_t max)
|
||||
((uint32_t)data[i * 4 + 1] << 8) |
|
||||
((uint32_t)data[i * 4 + 2] << 16) |
|
||||
((uint32_t)data[i * 4 + 3] << 24);
|
||||
printf("%u ", val);
|
||||
std::cout << val;
|
||||
}
|
||||
printf("\n");
|
||||
std::cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -255,25 +283,30 @@ void Protocol::print_bytes(uint32_t length, uint8_t *data, uint16_t max)
|
||||
{
|
||||
if (data && length >= 4)
|
||||
{
|
||||
printf(" > ");
|
||||
std::cout << " > ";
|
||||
for (size_t i = 0; (i < length) & (i < max); ++i)
|
||||
{
|
||||
printf("%d ", data[i]);
|
||||
std::cout << data[i];
|
||||
}
|
||||
printf("\n");
|
||||
std::cout << endl;
|
||||
}
|
||||
}
|
||||
|
||||
void Protocol::print_message(uint32_t cmd, uint32_t length, uint8_t *data)
|
||||
{
|
||||
printf("Cmd: %-3u %-10s Size: %-6u > ", cmd, cmdString(cmd), length);
|
||||
std::cout << "> "
|
||||
<< std::setw(3) << std::right << static_cast<unsigned>(cmd)
|
||||
<< std::setw(8) << std::left << ("[" + std::to_string(length) + "]")
|
||||
<< std::setw(15) << std::left << cmdString(cmd);
|
||||
|
||||
if (data && length > 0)
|
||||
for (size_t i = 0; i < 40 && i < length; ++i)
|
||||
{
|
||||
for (size_t i = 0; i < 50 && i < length; ++i)
|
||||
{
|
||||
char ch = static_cast<char>(data[i]);
|
||||
printf("%c", isprint(ch) ? ch : '.');
|
||||
std::cout << (std::isprint(static_cast<unsigned char>(ch)) ? ch : '.');
|
||||
}
|
||||
// for (int i = 0; i < length && i < 10; i++)
|
||||
// printf("%-4u", data[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
+3
-9
@@ -7,15 +7,6 @@
|
||||
#include "settings.h"
|
||||
#include "connector.h"
|
||||
|
||||
#define MAGIC 0x55aa55aa
|
||||
|
||||
#define CMD_OPEN 1
|
||||
#define CMD_PLUGGED 2
|
||||
#define CMD_UNPLUGGED 4
|
||||
#define CMD_TOUCH 5
|
||||
#define CMD_VIDEO_DATA 6
|
||||
#define CMD_AUDIO_DATA 7
|
||||
#define CMD_SEND_FILE 153
|
||||
|
||||
class Protocol : public IProtocol
|
||||
{
|
||||
@@ -48,6 +39,9 @@ public:
|
||||
bool phoneConnected;
|
||||
|
||||
private:
|
||||
void sendInt(uint32_t cmd, uint32_t value, bool encryption = true);
|
||||
void sendEncryption();
|
||||
|
||||
void onStatus(const char *status) override;
|
||||
void onDevice(bool connected) override;
|
||||
void onData(uint32_t cmd, uint32_t length, uint8_t *data) override;
|
||||
|
||||
+5
-2
@@ -10,6 +10,7 @@ public:
|
||||
static inline Setting<int> vendorid{"vendor-id", 4884};
|
||||
static inline Setting<int> productid{"product-id ", 5408};
|
||||
static inline Setting<bool> fullscreen{"fullscreen", true};
|
||||
static inline Setting<int> dpi{"dpi", 0};
|
||||
static inline Setting<int> width{"width", 720};
|
||||
static inline Setting<int> height{"height", 576};
|
||||
static inline Setting<int> fps{"fps", 60};
|
||||
@@ -18,8 +19,10 @@ public:
|
||||
static inline Setting<int> sourceFps{"source-fps", 30};
|
||||
static inline Setting<bool> logging{"logging", false};
|
||||
static inline Setting<int> scaler{"scaler", 2};
|
||||
static inline Setting<int> queue{"queue-size", 32};
|
||||
static inline Setting<int> fontSize{"font-size", 30};
|
||||
static inline Setting<int> queue{"queue-size", 32};
|
||||
static inline Setting<int> fontSize{"font-size", 30};
|
||||
static inline Setting<bool> encryption{"encryption", false};
|
||||
static inline Setting<bool> autoconnect{"autoconnect", true};
|
||||
|
||||
static void load(const std::string &filename);
|
||||
static void print();
|
||||
|
||||
Reference in New Issue
Block a user