+libcec (0.6-1) unstable; urgency=low
+
+ * bumped interface version to 6
+ * fixed packet output (reporting the OSD name correctly now)
+ * refactored packet structs: split up in cec commands and adapter messages
+ * fixed i/o timings
+ * added Darwin support (thanks Davilla!)
+ * fixed WaitForAck()
+ * fixed possible deadlock when starting a new thread
+ * implemented vendor id and device class parsing. full detection will follow
+ * added "on" and "standby" commands to the test client
+ * retransmit packets if needed
+ * fix GetTimeMs() on linux
+ * added timestamp to log messages
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Sun, 09 Oct 2011 15:15:00 +0200
+
libcec (0.5-1) unstable; urgency=low
* bumped interface version to 5
* fixed some memory leaks
* reset all structs to default values before doing with them
- -- Pulse-Eight Packaging <packaging@pulse-eight.com> Thu, 07 Oct 2011 22:00:00 +0200
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Fri, 07 Oct 2011 22:00:00 +0200
libcec (0.4-3) unstable; urgency=low
-AC_INIT([libcec], 0:5:0)
+AC_INIT([libcec], 0:6:0)
AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION)
AC_PROG_CXX
AC_PROG_LIBTOOL
-AC_CHECK_LIB([rt], [main],, AC_MSG_ERROR("required library 'rt' is missing"))
+case "${host}" in
+ *-*-linux*)
+ AC_CHECK_LIB([rt], [main],, AC_MSG_ERROR("required library 'rt' is missing"))
+ AC_CHECK_LIB([udev], [main],, AC_MSG_ERROR("required library 'udev' is missing"))
+ REQUIRES="udev"
+ ;;
+ *-apple-darwin*)
+ LIBS+="-framework CoreVideo -framework IOKit"
+ ;;
+esac
+
AC_CHECK_LIB([pthread], [main],, AC_MSG_ERROR("required library 'pthread' is missing"))
-AC_CHECK_LIB([udev], [main],, AC_MSG_ERROR("required library 'udev' is missing"))
CXXFLAGS="-fPIC -Wall -Wextra $CXXFLAGS"
+AC_SUBST(REQUIRES)
AC_CONFIG_FILES([src/lib/libcec.pc])
AC_OUTPUT([Makefile src/lib/Makefile src/testclient/Makefile])
+libcec (0.6-1) unstable; urgency=low
+
+ * bumped interface version to 6
+ * fixed packet output (reporting the OSD name correctly now)
+ * refactored packet structs: split up in cec commands and adapter messages
+ * fixed i/o timings
+ * added Darwin support (thanks Davilla!)
+ * fixed WaitForAck()
+ * fixed possible deadlock when starting a new thread
+ * implemented vendor id and device class parsing. full detection will follow
+ * added "on" and "standby" commands to the test client
+ * retransmit packets if needed
+ * fix GetTimeMs() on linux
+ * added timestamp to log messages
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Sun, 09 Oct 2011 15:15:00 +0200
+
libcec (0.5-1) unstable; urgency=low
* bumped interface version to 5
* fixed some memory leaks
* reset all structs to default values before doing with them
- -- Pulse-Eight Packaging <packaging@pulse-eight.com> Thu, 07 Oct 2011 22:00:00 +0200
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Fri, 07 Oct 2011 22:00:00 +0200
libcec (0.4-3) unstable; urgency=low
extern "C" {
namespace CEC {
#endif
- #define CEC_MIN_VERSION 5
- #define CEC_LIB_VERSION 5
+ #define CEC_MIN_VERSION 6
+ #define CEC_LIB_VERSION 6
#define CEC_SETTLE_DOWN_TIME 1000
#define CEC_BUTTON_TIMEOUT 500
{
char message[1024];
cec_log_level level;
+ int64_t time;
} cec_log_message;
typedef struct cec_keypress
char comm[1024];
} cec_adapter;
- typedef struct cec_frame
+ typedef enum cec_adapter_messagecode
{
- uint8_t data[20];
+ MSGCODE_NOTHING = 0,
+ MSGCODE_PING,
+ MSGCODE_TIMEOUT_ERROR,
+ MSGCODE_HIGH_ERROR,
+ MSGCODE_LOW_ERROR,
+ MSGCODE_FRAME_START,
+ MSGCODE_FRAME_DATA,
+ MSGCODE_RECEIVE_FAILED,
+ MSGCODE_COMMAND_ACCEPTED,
+ MSGCODE_COMMAND_REJECTED,
+ MSGCODE_SET_ACK_MASK,
+ MSGCODE_TRANSMIT,
+ MSGCODE_TRANSMIT_EOM,
+ MSGCODE_TRANSMIT_IDLETIME,
+ MSGCODE_TRANSMIT_ACK_POLARITY,
+ MSGCODE_TRANSMIT_LINE_TIMEOUT,
+ MSGCODE_TRANSMIT_SUCCEEDED,
+ MSGCODE_TRANSMIT_FAILED_LINE,
+ MSGCODE_TRANSMIT_FAILED_ACK,
+ MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
+ MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
+ MSGCODE_FIRMWARE_VERSION,
+ MSGCODE_START_BOOTLOADER,
+ MSGCODE_FRAME_EOM = 0x80,
+ MSGCODE_FRAME_ACK = 0x40,
+ } cec_adapter_messagecode;
+
+ typedef struct cec_datapacket
+ {
+ uint8_t data[100];
uint8_t size;
- void shift(uint8_t num)
+ bool empty(void) const { return size == 0; }
+ bool full(void) const { return size == 100; }
+ uint8_t operator[](uint8_t pos) const { return pos < size ? data[pos] : 0; }
+ uint8_t at(uint8_t pos) const { return pos < size ? data[pos] : 0; }
+
+ void shift(uint8_t iShiftBy)
{
- for (uint8_t iPtr = 0; iPtr < num; iPtr++)
- data[iPtr] = iPtr + num < size ? data[iPtr + num] : 0;
- size -= num;
+ if (iShiftBy >= size)
+ {
+ clear();
+ }
+ else
+ {
+ for (uint8_t iPtr = 0; iPtr < size; iPtr++)
+ data[iPtr] = (iPtr + iShiftBy < size) ? data[iPtr + iShiftBy] : 0;
+ size = (uint8_t) (size - iShiftBy);
+ }
}
void push_back(uint8_t add)
{
- if (size < 20)
+ if (size < 100)
data[size++] = add;
}
memset(data, 0, sizeof(data));
size = 0;
}
- } cec_frame;
+
+ } cec_datapacket;
+
+ typedef struct cec_adapter_message
+ {
+ cec_datapacket packet;
+
+ bool empty(void) const { return packet.empty(); }
+ uint8_t operator[](uint8_t pos) const { return packet[pos]; }
+ uint8_t at(uint8_t pos) const { return packet[pos]; }
+ uint8_t size(void) const { return packet.size; }
+ void clear(void) { packet.clear(); }
+ void shift(uint8_t iShiftBy) { packet.shift(iShiftBy); }
+ void push_back(uint8_t add) { packet.push_back(add); }
+ cec_adapter_messagecode message(void) const { return packet.size >= 1 ? (cec_adapter_messagecode) (packet.at(0) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) : MSGCODE_NOTHING; }
+ bool eom(void) const { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_EOM) != 0 : false; }
+ bool ack(void) const { return packet.size >= 1 ? (packet.at(0) & MSGCODE_FRAME_ACK) != 0 : false; }
+ cec_logical_address initiator(void) const { return packet.size >= 2 ? (cec_logical_address) (packet.at(1) >> 4) : CECDEVICE_UNKNOWN; };
+ cec_logical_address destination(void) const { return packet.size >= 2 ? (cec_logical_address) (packet.at(1) & 0xF) : CECDEVICE_UNKNOWN; };
+ } cec_adapter_message;
typedef struct cec_command
{
- cec_logical_address source;
+ cec_logical_address initiator;
cec_logical_address destination;
+ bool ack;
+ bool eom;
cec_opcode opcode;
- cec_frame parameters;
+ cec_datapacket parameters;
+ bool opcode_set;
+
+ static void format(cec_command &command, cec_logical_address initiator, cec_logical_address destination, cec_opcode opcode)
+ {
+ command.clear();
+ command.initiator = initiator;
+ command.destination = destination;
+ command.opcode = opcode;
+ command.opcode_set = true;
+ }
+
+ void push_back(uint8_t data)
+ {
+ if (!opcode_set)
+ {
+ opcode_set = true;
+ opcode = (cec_opcode) data;
+ }
+ else
+ parameters.push_back(data);
+ }
void clear(void)
{
- source = CECDEVICE_UNKNOWN;
+ initiator = CECDEVICE_UNKNOWN;
destination = CECDEVICE_UNKNOWN;
+ ack = false;
+ eom = false;
+ opcode_set = false;
opcode = CEC_OPCODE_FEATURE_ABORT;
parameters.clear();
};
} cec_command;
+ typedef enum cec_vendor_id
+ {
+ CEC_VENDOR_SAMSUNG = 240,
+ CEC_VENDOR_UNKNOWN = 0
+ } vendor_id;
+
+ static const char *CECVendorIdToString(const uint64_t iVendorId)
+ {
+ switch (iVendorId)
+ {
+ case CEC_VENDOR_SAMSUNG:
+ return "Samsung";
+ default:
+ return "Unknown";
+ }
+ }
+
//default physical address 1.0.0.0
#define CEC_DEFAULT_PHYSICAL_ADDRESS 0x1000
* @return True when the data was sent and acked, false otherwise.
*/
#ifdef __cplusplus
-extern DECLSPEC bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck = true);
+extern DECLSPEC bool cec_transmit(const CEC::cec_command &data, bool bWaitForAck = true);
#else
-extern DECLSPEC bool cec_transmit(const cec_frame &data, bool bWaitForAck = true);
+extern DECLSPEC bool cec_transmit(const cec_command &data, bool bWaitForAck = true);
#endif
/*!
class ICECAdapter
{
public:
+ virtual ~ICECAdapter() {};
/*! @name Adapter methods */
//@{
/*!
/*!
* @see cec_transmit
*/
- virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true) = 0;
+ virtual bool Transmit(const cec_command &data, bool bWaitForAck = true) = 0;
/*!
* @see cec_set_logical_address
CEC_BROADCAST_SYSTEM_OTHER_SYSTEM = 30
} ECecBroadcastSystem;
-typedef enum
-{
- MSGCODE_NOTHING = 0,
- MSGCODE_PING,
- MSGCODE_TIMEOUT_ERROR,
- MSGCODE_HIGH_ERROR,
- MSGCODE_LOW_ERROR,
- MSGCODE_FRAME_START,
- MSGCODE_FRAME_DATA,
- MSGCODE_RECEIVE_FAILED,
- MSGCODE_COMMAND_ACCEPTED,
- MSGCODE_COMMAND_REJECTED,
- MSGCODE_SET_ACK_MASK,
- MSGCODE_TRANSMIT,
- MSGCODE_TRANSMIT_EOM,
- MSGCODE_TRANSMIT_IDLETIME,
- MSGCODE_TRANSMIT_ACK_POLARITY,
- MSGCODE_TRANSMIT_LINE_TIMEOUT,
- MSGCODE_TRANSMIT_SUCCEEDED,
- MSGCODE_TRANSMIT_FAILED_LINE,
- MSGCODE_TRANSMIT_FAILED_ACK,
- MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA,
- MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE,
- MSGCODE_FIRMWARE_VERSION,
- MSGCODE_START_BOOTLOADER,
- MSGCODE_FRAME_EOM = 0x80,
- MSGCODE_FRAME_ACK = 0x40,
-} ECecMessageCode;
-
#define MSGSTART 0xFF
#define MSGEND 0xFE
#define MSGESC 0xFD
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;__WINDOWS__;DLL_EXPORT;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>$(SolutionDir)..\src\lib\platform\pthread_win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
- <DisableSpecificWarnings>4996;4100;4309</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4996;4100;4309;4505</DisableSpecificWarnings>
<TreatWarningAsError>true</TreatWarningAsError>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OutputFile>$(SolutionDir)..\libcec.dll</OutputFile>
- <AdditionalDependencies>%(AdditionalDependencies);setupapi.lib;$(SolutionDir)..\src\lib\platform\pthread_win32\pthreadVC2d.lib</AdditionalDependencies>
+ <AdditionalDependencies>%(AdditionalDependencies);setupapi.lib;$(SolutionDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib</AdditionalDependencies>
<IgnoreSpecificDefaultLibraries>libcmtd</IgnoreSpecificDefaultLibraries>
- <Version>5</Version>
+ <Version>6</Version>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;_CRT_SECURE_NO_WARNINGS;_SCL_SECURE_NO_WARNINGS;_USE_32BIT_TIME_T;_WINSOCKAPI_;__STDC_CONSTANT_MACROS;__WINDOWS__;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<TreatWarningAsError>true</TreatWarningAsError>
- <DisableSpecificWarnings>4100;4309</DisableSpecificWarnings>
+ <DisableSpecificWarnings>4100;4309;4505</DisableSpecificWarnings>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
- <AdditionalDependencies>$(ProjectDir)..\src\lib\platform\pthread_win32\pthreadVC2d.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ <AdditionalDependencies>$(ProjectDir)..\src\lib\platform\pthread_win32\pthreadVC2.lib;%(AdditionalDependencies)</AdditionalDependencies>
<OutputFile>$(SolutionDir)..\cec-client.exe</OutputFile>
</Link>
</ItemDefinitionGroup>
bool CAdapterCommunication::Open(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */)
{
- CLockObject lock(&m_mutex);
+ CLockObject lock(&m_commMutex);
if (!m_port)
{
m_controller->AddLog(CEC_LOG_ERROR, "port is NULL");
void CAdapterCommunication::Close(void)
{
- m_rcvCondition.Broadcast();
-
- CLockObject lock(&m_mutex);
+ CLockObject lock(&m_commMutex);
StopThread();
if (m_inbuf)
m_iInbufSize = 0;
m_iInbufUsed = 0;
}
+
+ m_rcvCondition.Broadcast();
}
void *CAdapterCommunication::Process(void)
while (!IsStopped())
{
- bool bSignal(false);
{
- CLockObject lock(&m_mutex, true);
- if (lock.IsLocked())
- bSignal = ReadFromDevice(50);
+ CLockObject lock(&m_commMutex);
+ ReadFromDevice(100);
}
- if (bSignal)
- m_rcvCondition.Signal();
-
if (!IsStopped())
- Sleep(50);
+ Sleep(5);
}
return NULL;
CStdString strError;
strError.Format("error reading from serial port: %s", m_port->GetError().c_str());
m_controller->AddLog(CEC_LOG_ERROR, strError);
- StopThread(false);
return false;
}
else if (iBytesRead > 0)
void CAdapterCommunication::AddData(uint8_t *data, uint8_t iLen)
{
+ CLockObject lock(&m_bufferMutex);
if (m_iInbufUsed + iLen > m_iInbufSize)
{
m_iInbufSize = m_iInbufUsed + iLen;
memcpy(m_inbuf + m_iInbufUsed, data, iLen);
m_iInbufUsed += iLen;
+
+ m_rcvCondition.Signal();
}
-bool CAdapterCommunication::Write(const cec_frame &data)
+bool CAdapterCommunication::Write(const cec_adapter_message &data)
{
+ CLockObject lock(&m_commMutex);
+ if (m_port->Write(data) != (int32_t) data.size())
{
- CLockObject lock(&m_mutex);
- if (m_port->Write(data) != (int32_t) data.size)
- {
- CStdString strError;
- strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
- m_controller->AddLog(CEC_LOG_ERROR, strError);
- return false;
- }
-
- m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
-
- CCondition::Sleep((uint32_t) data.size * (uint32_t)24 /*data*/ + (uint32_t)5 /*start bit (4.5 ms)*/ + (uint32_t)50 /* to be on the safe side */);
+ CStdString strError;
+ strError.Format("error writing to serial port: %s", m_port->GetError().c_str());
+ m_controller->AddLog(CEC_LOG_ERROR, strError);
+ return false;
}
+ m_controller->AddLog(CEC_LOG_DEBUG, "command sent");
+ CCondition::Sleep((uint32_t) data.size() * (uint32_t)24 /*data*/ + (uint32_t)5 /*start bit (4.5 ms)*/);
+
return true;
}
-bool CAdapterCommunication::Read(cec_frame &msg, uint32_t iTimeout)
+bool CAdapterCommunication::Read(cec_adapter_message &msg, uint32_t iTimeout)
{
- CLockObject lock(&m_mutex);
+ CLockObject lock(&m_bufferMutex);
if (m_iInbufUsed < 1)
{
- if (!m_rcvCondition.Wait(&m_mutex, iTimeout))
+ if (!m_rcvCondition.Wait(&m_bufferMutex, iTimeout))
return false;
}
return false;
m_controller->AddLog(CEC_LOG_DEBUG, "starting the bootloader");
- cec_frame output;
+ cec_adapter_message output;
output.clear();
output.push_back(MSGSTART);
return true;
}
-void CAdapterCommunication::PushEscaped(cec_frame &vec, uint8_t byte)
+void CAdapterCommunication::PushEscaped(cec_adapter_message &vec, uint8_t byte)
{
if (byte >= MSGESC && byte != MSGSTART)
{
strLog.Format("setting ackmask to %2x", iMask);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
- cec_frame output;
+ cec_adapter_message output;
output.clear();
output.push_back(MSGSTART);
return false;
m_controller->AddLog(CEC_LOG_DEBUG, "sending ping");
- cec_frame output;
+ cec_adapter_message output;
output.clear();
output.push_back(MSGSTART);
{
return !IsStopped() && m_port->IsOpen();
}
+
+void CAdapterCommunication::FormatAdapterMessage(const cec_command &command, cec_adapter_message &packet)
+{
+ packet.clear();
+
+ //set ack polarity to high when transmitting to the broadcast address
+ //set ack polarity low when transmitting to any other address
+ packet.push_back(MSGSTART);
+ PushEscaped(packet, MSGCODE_TRANSMIT_ACK_POLARITY);
+ if (command.destination == CECDEVICE_BROADCAST)
+ PushEscaped(packet, CEC_TRUE);
+ else
+ PushEscaped(packet, CEC_FALSE);
+ packet.push_back(MSGEND);
+
+ // add source and destination
+ packet.push_back(MSGSTART);
+ PushEscaped(packet, MSGCODE_TRANSMIT);
+ packet.push_back(((uint8_t)command.initiator << 4) + (uint8_t)command.destination);
+ packet.push_back(MSGEND);
+
+ // add opcode
+ packet.push_back(MSGSTART);
+ PushEscaped(packet, command.parameters.empty() ? (uint8_t)MSGCODE_TRANSMIT_EOM : (uint8_t)MSGCODE_TRANSMIT);
+ packet.push_back((uint8_t) command.opcode);
+ packet.push_back(MSGEND);
+
+ // add parameters
+ for (int8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+ {
+ packet.push_back(MSGSTART);
+
+ if (iPtr == command.parameters.size - 1)
+ PushEscaped(packet, MSGCODE_TRANSMIT_EOM);
+ else
+ PushEscaped(packet, MSGCODE_TRANSMIT);
+
+ PushEscaped(packet, command.parameters[iPtr]);
+
+ packet.push_back(MSGEND);
+ }
+}
+
virtual ~CAdapterCommunication();
bool Open(const char *strPort, uint16_t iBaudRate = 38400, uint32_t iTimeoutMs = 10000);
- bool Read(cec_frame &msg, uint32_t iTimeout = 1000);
- bool Write(const cec_frame &frame);
+ bool Read(cec_adapter_message &msg, uint32_t iTimeout = 1000);
+ bool Write(const cec_adapter_message &frame);
bool PingAdapter(void);
void Close(void);
bool IsOpen(void) const;
bool StartBootloader(void);
bool SetAckMask(uint16_t iMask);
- static void PushEscaped(cec_frame &vec, uint8_t byte);
+ static void PushEscaped(cec_adapter_message &vec, uint8_t byte);
+ static void FormatAdapterMessage(const cec_command &command, cec_adapter_message &packet);
+
private:
void AddData(uint8_t *data, uint8_t iLen);
bool ReadFromDevice(uint32_t iTimeout);
uint8_t* m_inbuf;
int16_t m_iInbufSize;
int16_t m_iInbufUsed;
- CMutex m_mutex;
+ CMutex m_bufferMutex;
+ CMutex m_commMutex;
CCondition m_rcvCondition;
};
};
#include "platform/os-dependent.h"
#include "util/StdString.h"
-#if !defined(__WINDOWS__)
+#if defined(__APPLE__)
+#include <dirent.h>
+#include <sys/param.h>
+#include <IOKit/IOKitLib.h>
+#include <IOKit/IOMessage.h>
+#include <IOKit/IOCFPlugIn.h>
+#include <IOKit/usb/IOUSBLib.h>
+#include <IOKit/serial/IOSerialKeys.h>
+#include <CoreFoundation/CoreFoundation.h>
+
+#elif !defined(__WINDOWS__)
#include <dirent.h>
#include <libudev.h>
#include <poll.h>
{
uint8_t iFound(0);
-#if !defined(__WINDOWS__)
+#if defined(__APPLE__)
+ kern_return_t kresult;
+ char bsdPath[MAXPATHLEN] = {0};
+ io_iterator_t serialPortIterator;
+
+ CFMutableDictionaryRef classesToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
+ if (classesToMatch)
+ {
+ CFDictionarySetValue(classesToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDModemType));
+ kresult = IOServiceGetMatchingServices(kIOMasterPortDefault, classesToMatch, &serialPortIterator);
+ if (kresult == KERN_SUCCESS)
+ {
+ io_object_t serialService;
+ while ((serialService = IOIteratorNext(serialPortIterator)))
+ {
+ int iVendor = 0, iProduct = 0;
+ CFTypeRef bsdPathAsCFString;
+
+ // fetch the device path.
+ bsdPathAsCFString = IORegistryEntryCreateCFProperty(serialService,
+ CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
+ if (bsdPathAsCFString)
+ {
+ // convert the path from a CFString to a C (NUL-terminated) string.
+ CFStringGetCString((CFStringRef)bsdPathAsCFString, bsdPath, MAXPATHLEN - 1, kCFStringEncodingUTF8);
+ CFRelease(bsdPathAsCFString);
+
+ // now walk up the hierarchy until we find the entry with vendor/product IDs
+ io_registry_entry_t parent;
+ CFTypeRef vendorIdAsCFNumber = NULL;
+ CFTypeRef productIdAsCFNumber = NULL;
+ kern_return_t kresult = IORegistryEntryGetParentEntry(serialService, kIOServicePlane, &parent);
+ while (kresult == KERN_SUCCESS)
+ {
+ vendorIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
+ kIOServicePlane, CFSTR(kUSBVendorID), kCFAllocatorDefault, 0);
+ productIdAsCFNumber = IORegistryEntrySearchCFProperty(parent,
+ kIOServicePlane, CFSTR(kUSBProductID), kCFAllocatorDefault, 0);
+ if (vendorIdAsCFNumber && productIdAsCFNumber)
+ {
+ CFNumberGetValue((CFNumberRef)vendorIdAsCFNumber, kCFNumberIntType, &iVendor);
+ CFRelease(vendorIdAsCFNumber);
+ CFNumberGetValue((CFNumberRef)productIdAsCFNumber, kCFNumberIntType, &iProduct);
+ CFRelease(productIdAsCFNumber);
+ IOObjectRelease(parent);
+ break;
+ }
+ io_registry_entry_t oldparent = parent;
+ kresult = IORegistryEntryGetParentEntry(parent, kIOServicePlane, &parent);
+ IOObjectRelease(oldparent);
+ }
+ if (strlen(bsdPath) && iVendor == CEC_VID && iProduct == CEC_PID)
+ {
+ if (!strDevicePath || !strcmp(bsdPath, strDevicePath))
+ {
+ // on darwin, the device path is the same as the comm path.
+ snprintf(deviceList[iFound ].path, sizeof(deviceList[iFound].path), "%s", bsdPath);
+ snprintf(deviceList[iFound++].comm, sizeof(deviceList[iFound].path), "%s", bsdPath);
+ }
+ }
+ }
+ IOObjectRelease(serialService);
+ }
+ }
+ IOObjectRelease(serialPortIterator);
+ }
+
+#elif !defined(__WINDOWS__)
struct udev *udev;
if (!(udev = udev_new()))
return -1;
m_communication(serComm),
m_controller(controller)
{
+ for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
+ m_vendorIds[iPtr] = CEC_VENDOR_UNKNOWN;
+ for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
+ m_vendorClasses[iPtr] = (uint8_t) 0;
}
CCECProcessor::~CCECProcessor(void)
{
m_controller->AddLog(CEC_LOG_DEBUG, "processor thread started");
+ cec_command command;
+ cec_adapter_message msg;
+
while (!IsStopped())
{
bool bParseFrame(false);
- cec_frame msg;
+ command.clear();
msg.clear();
{
bParseFrame = ParseMessage(msg) && !IsStopped();
if (bParseFrame)
- {
- msg.clear();
- msg = m_currentframe;
- }
+ command = m_currentframe;
}
if (bParseFrame)
- ParseCurrentFrame(msg);
+ ParseCommand(command);
m_controller->CheckKeypressTimeout();
if (!IsStopped())
- Sleep(50);
+ Sleep(5);
}
return NULL;
return false;
CStdString strLog;
- strLog.Format("powering on devices with logical address %d", (int8_t)address);
+ strLog.Format("<< powering on device with logical address %d", (int8_t)address);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
- cec_frame frame;
- frame.clear();
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_IMAGE_VIEW_ON);
- return Transmit(frame);
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_IMAGE_VIEW_ON);
+
+ return Transmit(command);
}
bool CCECProcessor::StandbyDevices(cec_logical_address address /* = CECDEVICE_BROADCAST */)
return false;
CStdString strLog;
- strLog.Format("putting all devices with logical address %d in standby mode", (int8_t)address);
+ strLog.Format("<< putting device with logical address %d in standby mode", (int8_t)address);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
- cec_frame frame;
- frame.clear();
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_STANDBY);
- return Transmit(frame);
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_STANDBY);
+
+ return Transmit(command);
}
bool CCECProcessor::SetActiveView(void)
if (!IsRunning())
return false;
- m_controller->AddLog(CEC_LOG_DEBUG, "setting active view");
- cec_frame frame;
- frame.clear();
+ m_controller->AddLog(CEC_LOG_DEBUG, "<< setting active view");
+
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
+ command.parameters.push_back((m_physicaladdress >> 8) & 0xFF);
+ command.parameters.push_back(m_physicaladdress & 0xFF);
- frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST));
- frame.push_back((uint8_t) CEC_OPCODE_ACTIVE_SOURCE);
- frame.push_back((m_physicaladdress >> 8) & 0xFF);
- frame.push_back(m_physicaladdress & 0xFF);
- return Transmit(frame);
+ return Transmit(command);
}
bool CCECProcessor::SetInactiveView(void)
if (!IsRunning())
return false;
- m_controller->AddLog(CEC_LOG_DEBUG, "setting inactive view");
- cec_frame frame;
- frame.clear();
+ m_controller->AddLog(CEC_LOG_DEBUG, "<< setting inactive view");
- frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST));
- frame.push_back((uint8_t) CEC_OPCODE_INACTIVE_SOURCE);
- frame.push_back((m_physicaladdress >> 8) & 0xFF);
- frame.push_back(m_physicaladdress & 0xFF);
- return Transmit(frame);
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_INACTIVE_SOURCE);
+ command.parameters.push_back((m_physicaladdress >> 8) & 0xFF);
+ command.parameters.push_back(m_physicaladdress & 0xFF);
+
+ return Transmit(command);
}
-bool CCECProcessor::Transmit(const cec_frame &data, bool bWaitForAck /* = true */)
+void CCECProcessor::LogOutput(const cec_command &data)
{
CStdString txStr = "transmit ";
- for (unsigned int i = 0; i < data.size; i++)
- txStr.AppendFormat(" %02x", data.data[i]);
+ txStr.AppendFormat(" %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
+ txStr.AppendFormat(" %02x", (uint8_t)data.opcode);
+
+ for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
+ txStr.AppendFormat(" %02x", data.parameters[iPtr]);
m_controller->AddLog(CEC_LOG_DEBUG, txStr.c_str());
+}
- if (data.size == 0)
- {
- m_controller->AddLog(CEC_LOG_WARNING, "transmit buffer is empty");
- return false;
- }
+bool CCECProcessor::Transmit(const cec_command &data, bool bWaitForAck /* = true */)
+{
+ LogOutput(data);
- cec_frame output;
+ cec_adapter_message output;
output.clear();
-
- //set ack polarity to high when transmitting to the broadcast address
- //set ack polarity low when transmitting to any other address
- output.push_back(MSGSTART);
- CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT_ACK_POLARITY);
-
- if ((data.data[0] & 0xF) == 0xF)
- CAdapterCommunication::PushEscaped(output, CEC_TRUE);
- else
- CAdapterCommunication::PushEscaped(output, CEC_FALSE);
-
- output.push_back(MSGEND);
-
- for (int8_t i = 0; i < data.size; i++)
- {
- output.push_back(MSGSTART);
-
- if (i == (int8_t)data.size - 1)
- CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT_EOM);
- else
- CAdapterCommunication::PushEscaped(output, MSGCODE_TRANSMIT);
-
- CAdapterCommunication::PushEscaped(output, data.data[i]);
-
- output.push_back(MSGEND);
- }
+ CAdapterCommunication::FormatAdapterMessage(data, output);
return TransmitFormatted(output, bWaitForAck);
}
bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
{
CStdString strLog;
- strLog.Format("setting logical address to %d", iLogicalAddress);
+ strLog.Format("<< setting logical address to %d", iLogicalAddress);
m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
m_iLogicalAddress = iLogicalAddress;
return m_communication && m_communication->SetAckMask(0x1 << (uint8_t)m_iLogicalAddress);
}
-bool CCECProcessor::TransmitFormatted(const cec_frame &data, bool bWaitForAck /* = true */)
+bool CCECProcessor::TransmitFormatted(const cec_adapter_message &data, bool bWaitForAck /* = true */)
{
CLockObject lock(&m_mutex);
if (!m_communication || !m_communication->Write(data))
return false;
- if (bWaitForAck && !WaitForAck())
+ if (bWaitForAck)
{
- m_controller->AddLog(CEC_LOG_DEBUG, "did not receive ACK");
- return false;
+ uint64_t now = GetTimeMs();
+ uint64_t target = now + 1000;
+ bool bError(false);
+ bool bGotAck(false);
+
+ while (!bGotAck && now < target)
+ {
+ bGotAck = WaitForAck(&bError, (uint32_t) (target - now));
+ now = GetTimeMs();
+
+ if (bError && now < target)
+ {
+ m_controller->AddLog(CEC_LOG_ERROR, "retransmitting previous frame");
+ if (!m_communication->Write(data))
+ return false;
+ }
+ }
}
return true;
void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
{
- m_controller->AddLog(CEC_LOG_DEBUG, "transmitting abort message");
- cec_frame frame;
- frame.clear();
-
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_FEATURE_ABORT);
- frame.push_back((uint8_t) opcode);
- frame.push_back((uint8_t) reason);
- Transmit(frame);
+ m_controller->AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
+
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_FEATURE_ABORT);
+ command.parameters.push_back((uint8_t)opcode);
+ command.parameters.push_back((uint8_t)reason);
+
+ Transmit(command);
}
void CCECProcessor::ReportCECVersion(cec_logical_address address /* = CECDEVICE_TV */)
{
- cec_frame frame;
- frame.clear();
-
- m_controller->AddLog(CEC_LOG_NOTICE, "reporting CEC version as 1.3a");
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_CEC_VERSION);
- frame.push_back((uint8_t) CEC_VERSION_1_3A);
- Transmit(frame);
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting CEC version as 1.3a");
+
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_CEC_VERSION);
+ command.parameters.push_back(CEC_VERSION_1_3A);
+
+ Transmit(command);
}
void CCECProcessor::ReportPowerState(cec_logical_address address /*= CECDEVICE_TV */, bool bOn /* = true */)
{
- cec_frame frame;
- frame.clear();
-
if (bOn)
- m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"On\" power status");
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting \"On\" power status");
else
- m_controller->AddLog(CEC_LOG_NOTICE, "reporting \"Off\" power status");
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting \"Off\" power status");
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_REPORT_POWER_STATUS);
- frame.push_back(bOn ? (uint8_t) CEC_POWER_STATUS_ON : (uint8_t) CEC_POWER_STATUS_STANDBY);
- Transmit(frame);
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_REPORT_POWER_STATUS);
+ command.parameters.push_back(bOn ? (uint8_t) CEC_POWER_STATUS_ON : (uint8_t) CEC_POWER_STATUS_STANDBY);
+
+ Transmit(command);
}
void CCECProcessor::ReportMenuState(cec_logical_address address /* = CECDEVICE_TV */, bool bActive /* = true */)
{
- cec_frame frame;
- frame.clear();
-
if (bActive)
- m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as active");
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting menu state as active");
else
- m_controller->AddLog(CEC_LOG_NOTICE, "reporting menu state as inactive");
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< reporting menu state as inactive");
+
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_MENU_STATUS);
+ command.parameters.push_back(bActive ? (uint8_t) CEC_MENU_STATE_ACTIVATED : (uint8_t) CEC_MENU_STATE_DEACTIVATED);
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_MENU_STATUS);
- frame.push_back(bActive ? (uint8_t) CEC_MENU_STATE_ACTIVATED : (uint8_t) CEC_MENU_STATE_DEACTIVATED);
- Transmit(frame);
+ Transmit(command);
}
void CCECProcessor::ReportVendorID(cec_logical_address address /* = CECDEVICE_TV */)
{
- m_controller->AddLog(CEC_LOG_NOTICE, "vendor ID requested, feature abort");
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< vendor ID requested, feature abort");
TransmitAbort(address, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
}
void CCECProcessor::ReportOSDName(cec_logical_address address /* = CECDEVICE_TV */)
{
- cec_frame frame;
- frame.clear();
-
const char *osdname = m_strDeviceName.c_str();
CStdString strLog;
- strLog.Format("reporting OSD name as %s", osdname);
+ strLog.Format("<< reporting OSD name as %s", osdname);
m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
- frame.push_back(GetSourceDestination(address));
- frame.push_back((uint8_t) CEC_OPCODE_SET_OSD_NAME);
- for (unsigned int i = 0; i < strlen(osdname); i++)
- frame.push_back(osdname[i]);
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, address, CEC_OPCODE_SET_OSD_NAME);
+ for (unsigned int iPtr = 0; iPtr < strlen(osdname); iPtr++)
+ command.parameters.push_back(osdname[iPtr]);
- Transmit(frame);
+ Transmit(command);
}
void CCECProcessor::ReportPhysicalAddress(void)
{
- cec_frame frame;
- frame.clear();
-
CStdString strLog;
- strLog.Format("reporting physical address as %04x", m_physicaladdress);
+ strLog.Format("<< reporting physical address as %04x", m_physicaladdress);
m_controller->AddLog(CEC_LOG_NOTICE, strLog.c_str());
- frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST));
- frame.push_back((uint8_t) CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
- frame.push_back((uint8_t) ((m_physicaladdress >> 8) & 0xFF));
- frame.push_back((uint8_t) (m_physicaladdress & 0xFF));
- frame.push_back((uint8_t) CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
- Transmit(frame);
+
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
+ command.parameters.push_back((uint8_t) ((m_physicaladdress >> 8) & 0xFF));
+ command.parameters.push_back((uint8_t) (m_physicaladdress & 0xFF));
+
+ Transmit(command);
}
void CCECProcessor::BroadcastActiveSource(void)
{
- cec_frame frame;
- frame.clear();
-
- m_controller->AddLog(CEC_LOG_NOTICE, "broadcasting active source");
- frame.push_back(GetSourceDestination(CECDEVICE_BROADCAST));
- frame.push_back((uint8_t) CEC_OPCODE_ACTIVE_SOURCE);
- frame.push_back((uint8_t) ((m_physicaladdress >> 8) & 0xFF));
- frame.push_back((uint8_t) (m_physicaladdress & 0xFF));
- Transmit(frame);
-}
+ m_controller->AddLog(CEC_LOG_NOTICE, "<< broadcasting active source");
-uint8_t CCECProcessor::GetSourceDestination(cec_logical_address destination /* = CECDEVICE_BROADCAST */) const
-{
- return ((uint8_t)m_iLogicalAddress << 4) + (uint8_t)destination;
+ cec_command command;
+ cec_command::format(command, m_iLogicalAddress, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
+ command.parameters.push_back((uint8_t) ((m_physicaladdress >> 8) & 0xFF));
+ command.parameters.push_back((uint8_t) (m_physicaladdress & 0xFF));
+
+ Transmit(command);
}
-bool CCECProcessor::WaitForAck(uint32_t iTimeout /* = 1000 */)
+bool CCECProcessor::WaitForAck(bool *bError, uint32_t iTimeout /* = 1000 */)
{
bool bGotAck(false);
- bool bError(false);
+ *bError = false;
int64_t iNow = GetTimeMs();
int64_t iTargetTime = iNow + (uint64_t) iTimeout;
- while (!bGotAck && !bError && (iTimeout == 0 || iNow < iTargetTime))
+ while (!bGotAck && !*bError && (iTimeout == 0 || iNow < iTargetTime))
{
- cec_frame msg;
+ cec_adapter_message msg;
msg.clear();
- while (!bGotAck && !bError && m_communication->Read(msg, iTimeout))
+ if (!m_communication->Read(msg, iTimeout > 0 ? (int32_t)(iTargetTime - iNow) : 1000))
{
- uint8_t iCode = msg.data[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK);
-
- switch (iCode)
- {
- case MSGCODE_COMMAND_ACCEPTED:
- m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED");
- break;
- case MSGCODE_TRANSMIT_SUCCEEDED:
- m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED");
- // TODO
- bGotAck = true;
- break;
- case MSGCODE_RECEIVE_FAILED:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED");
- bError = true;
- break;
- case MSGCODE_COMMAND_REJECTED:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED");
- bError = true;
- break;
- case MSGCODE_TRANSMIT_FAILED_LINE:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE");
- bError = true;
- break;
- case MSGCODE_TRANSMIT_FAILED_ACK:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK");
- bError = true;
- break;
- case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA");
- bError = true;
- break;
- case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
- m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE");
- bError = true;
- break;
- default:
- m_frameBuffer.Push(msg);
- bGotAck = (msg.data[0] & MSGCODE_FRAME_ACK) != 0;
- break;
- }
iNow = GetTimeMs();
+ continue;
+ }
+
+ switch (msg.message())
+ {
+ case MSGCODE_COMMAND_ACCEPTED:
+ m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_COMMAND_ACCEPTED");
+ break;
+ case MSGCODE_TRANSMIT_SUCCEEDED:
+ m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_TRANSMIT_SUCCEEDED");
+ bGotAck = true;
+ break;
+ case MSGCODE_RECEIVE_FAILED:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_RECEIVE_FAILED");
+ *bError = true;
+ break;
+ case MSGCODE_COMMAND_REJECTED:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_COMMAND_REJECTED");
+ *bError = true;
+ break;
+ case MSGCODE_TRANSMIT_FAILED_LINE:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_LINE");
+ *bError = true;
+ break;
+ case MSGCODE_TRANSMIT_FAILED_ACK:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_ACK");
+ *bError = true;
+ break;
+ case MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA");
+ *bError = true;
+ break;
+ case MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE:
+ m_controller->AddLog(CEC_LOG_WARNING, "MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE");
+ *bError = true;
+ break;
+ default:
+ m_frameBuffer.Push(msg);
+ break;
}
+
+ iNow = GetTimeMs();
}
- return bGotAck && !bError;
+ return bGotAck && !*bError;
}
-bool CCECProcessor::ParseMessage(cec_frame &msg)
+bool CCECProcessor::ParseMessage(cec_adapter_message &msg)
{
bool bReturn(false);
- if (msg.size == 0)
+ if (msg.empty())
return bReturn;
CStdString logStr;
- uint8_t iCode = msg.data[0] & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK);
- bool bEom = (msg.data[0] & MSGCODE_FRAME_EOM) != 0;
- bool bAck = (msg.data[0] & MSGCODE_FRAME_ACK) != 0;
- switch(iCode)
+ switch(msg.message())
{
case MSGCODE_NOTHING:
m_controller->AddLog(CEC_LOG_DEBUG, "MSGCODE_NOTHING");
case MSGCODE_HIGH_ERROR:
case MSGCODE_LOW_ERROR:
{
- if (iCode == MSGCODE_TIMEOUT_ERROR)
+ if (msg.message() == MSGCODE_TIMEOUT_ERROR)
logStr = "MSGCODE_TIMEOUT";
- else if (iCode == MSGCODE_HIGH_ERROR)
+ else if (msg.message() == MSGCODE_HIGH_ERROR)
logStr = "MSGCODE_HIGH_ERROR";
else
logStr = "MSGCODE_LOW_ERROR";
- int iLine = (msg.size >= 3) ? (msg.data[1] << 8) | (msg.data[2]) : 0;
- uint32_t iTime = (msg.size >= 7) ? (msg.data[3] << 24) | (msg.data[4] << 16) | (msg.data[5] << 8) | (msg.data[6]) : 0;
+ int iLine = (msg.size() >= 3) ? (msg[1] << 8) | (msg[2]) : 0;
+ uint32_t iTime = (msg.size() >= 7) ? (msg[3] << 24) | (msg[4] << 16) | (msg[5] << 8) | (msg[6]) : 0;
logStr.AppendFormat(" line:%i", iLine);
logStr.AppendFormat(" time:%u", iTime);
m_controller->AddLog(CEC_LOG_WARNING, logStr.c_str());
{
logStr = "MSGCODE_FRAME_START";
m_currentframe.clear();
- if (msg.size >= 2)
+ if (msg.size() >= 2)
{
- int iInitiator = msg.data[1] >> 4;
- int iDestination = msg.data[1] & 0xF;
- logStr.AppendFormat(" initiator:%u destination:%u ack:%s %s", iInitiator, iDestination, bAck ? "high" : "low", bEom ? "eom" : "");
-
- m_currentframe.push_back(msg.data[1]);
+ logStr.AppendFormat(" initiator:%u destination:%u ack:%s %s", msg.initiator(), msg.destination(), msg.ack() ? "high" : "low", msg.eom() ? "eom" : "");
+ m_currentframe.initiator = msg.initiator();
+ m_currentframe.destination = msg.destination();
+ m_currentframe.ack = msg.ack();
+ m_currentframe.eom = msg.eom();
}
m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str());
}
case MSGCODE_FRAME_DATA:
{
logStr = "MSGCODE_FRAME_DATA";
- if (msg.size >= 2)
+ if (msg.size() >= 2)
{
- uint8_t iData = msg.data[1];
+ uint8_t iData = msg[1];
logStr.AppendFormat(" %02x", iData);
m_currentframe.push_back(iData);
}
m_controller->AddLog(CEC_LOG_DEBUG, logStr.c_str());
}
- if (bEom)
+ if (msg.eom())
bReturn = true;
break;
default:
return bReturn;
}
-void CCECProcessor::ParseCurrentFrame(cec_frame &frame)
+void CCECProcessor::ParseVendorId(cec_logical_address device, const cec_datapacket &data)
{
- uint8_t initiator = frame.data[0] >> 4;
- uint8_t destination = frame.data[0] & 0xF;
+ if (data.size < 3)
+ {
+ m_controller->AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
+ return;
+ }
- CStdString dataStr;
- dataStr.Format("received frame: initiator: %u destination: %u", initiator, destination);
+ uint64_t iVendorId = ((uint64_t)data[0] << 3) +
+ ((uint64_t)data[1] << 2) +
+ (uint64_t)data[2];
+
+ m_vendorIds[(uint8_t)device] = iVendorId;
+ m_vendorClasses[(uint8_t)device] = data.size >= 4 ? data[3] : 0;
+
+ CStdString strLog;
+ strLog.Format("device %d: vendor = %s (%lld) class = %2x", (uint8_t)device, CECVendorIdToString(m_vendorIds[(uint8_t)device]), iVendorId, m_vendorClasses[(uint8_t)device]);
+ m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
+}
- if (frame.size > 1)
+void CCECProcessor::ParseCommand(cec_command &command)
+{
+ CStdString dataStr;
+ dataStr.Format(">> received frame: initiator: %u destination: %u", command.initiator, command.destination);
+ if (command.parameters.size > 1)
{
dataStr += " data:";
- for (unsigned int i = 1; i < frame.size; i++)
- dataStr.AppendFormat(" %02x", frame.data[i]);
+ for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
+ dataStr.AppendFormat(" %02x", (unsigned int)command.parameters[iPtr]);
}
m_controller->AddLog(CEC_LOG_DEBUG, dataStr.c_str());
- if (frame.size <= 1)
- return;
-
- cec_opcode opCode = (cec_opcode) frame.data[1];
- if (destination == (uint16_t) m_iLogicalAddress)
+ if (command.destination == m_iLogicalAddress)
{
- switch(opCode)
+ switch(command.opcode)
{
case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
ReportPhysicalAddress();
- SetActiveView();
break;
case CEC_OPCODE_GIVE_OSD_NAME:
- ReportOSDName((cec_logical_address)initiator);
+ ReportOSDName(command.initiator);
break;
case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
- ReportVendorID((cec_logical_address)initiator);
+ ReportVendorID(command.initiator);
+ break;
+ case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
+ ParseVendorId(command.initiator, command.parameters);
+ TransmitAbort(command.initiator, CEC_OPCODE_VENDOR_COMMAND_WITH_ID);
+ break;
+ case CEC_OPCODE_GIVE_DECK_STATUS:
+ // need to support opcodes play and deck control before doing anything with this
+ TransmitAbort(command.initiator, CEC_OPCODE_GIVE_DECK_STATUS);
break;
case CEC_OPCODE_MENU_REQUEST:
- ReportMenuState((cec_logical_address)initiator);
+ if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_QUERY)
+ ReportMenuState(command.initiator);
break;
case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
- ReportPowerState((cec_logical_address)initiator);
+ ReportPowerState(command.initiator);
+ SetActiveView();
break;
case CEC_OPCODE_GET_CEC_VERSION:
- ReportCECVersion((cec_logical_address)initiator);
+ ReportCECVersion(command.initiator);
break;
case CEC_OPCODE_USER_CONTROL_PRESSED:
- if (frame.size > 2)
+ if (command.parameters.size > 0)
{
m_controller->AddKey();
- if (frame.data[2] <= CEC_USER_CONTROL_CODE_MAX)
- m_controller->SetCurrentButton((cec_user_control_code) frame.data[2]);
+ if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
+ m_controller->SetCurrentButton((cec_user_control_code) command.parameters[0]);
}
break;
case CEC_OPCODE_USER_CONTROL_RELEASE:
m_controller->AddKey();
break;
default:
- cec_frame params = frame;
- params.shift(2);
- m_controller->AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms);
+ m_controller->AddCommand(command);
break;
}
}
- else if (destination == (uint8_t) CECDEVICE_BROADCAST)
+ else if (command.destination == CECDEVICE_BROADCAST)
{
CStdString strLog;
- if (opCode == CEC_OPCODE_REQUEST_ACTIVE_SOURCE)
+ if (command.opcode == CEC_OPCODE_REQUEST_ACTIVE_SOURCE)
{
- strLog.Format("%i requests active source", initiator);
+ strLog.Format(">> %i requests active source", (uint8_t) command.initiator);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
BroadcastActiveSource();
}
- else if (opCode == CEC_OPCODE_SET_STREAM_PATH)
+ else if (command.opcode == CEC_OPCODE_SET_STREAM_PATH)
{
- if (frame.size >= 4)
+ if (command.parameters.size >= 2)
{
- int streamaddr = ((int)frame.data[2] << 8) | ((int)frame.data[3]);
- strLog.Format("%i requests stream path from physical address %04x", initiator, streamaddr);
+ int streamaddr = ((int)command.parameters[0] << 8) | ((int)command.parameters[1]);
+ strLog.Format(">> %i requests stream path from physical address %04x", command.initiator, streamaddr);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
if (streamaddr == m_physicaladdress)
BroadcastActiveSource();
}
else
{
- cec_frame params = frame;
- params.shift(2);
- m_controller->AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms);
+ m_controller->AddCommand(command);
}
}
else
{
CStdString strLog;
- strLog.Format("ignoring frame: destination: %u != %u", destination, (uint16_t)m_iLogicalAddress);
+ strLog.Format("ignoring frame: destination: %u != %u", command.destination, (uint8_t)m_iLogicalAddress);
m_controller->AddLog(CEC_LOG_DEBUG, strLog.c_str());
}
}
virtual bool StandbyDevices(cec_logical_address address = CECDEVICE_BROADCAST);
virtual bool SetActiveView(void);
virtual bool SetInactiveView(void);
- virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true);
+ virtual bool Transmit(const cec_command &data, bool bWaitForAck = true);
virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress);
protected:
- virtual bool TransmitFormatted(const cec_frame &data, bool bWaitForAck = true);
+ virtual bool TransmitFormatted(const cec_adapter_message &data, bool bWaitForAck = true);
virtual void TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
virtual void ReportCECVersion(cec_logical_address address = CECDEVICE_TV);
virtual void ReportPowerState(cec_logical_address address = CECDEVICE_TV, bool bOn = true);
virtual void ReportOSDName(cec_logical_address address = CECDEVICE_TV);
virtual void ReportPhysicalAddress(void);
virtual void BroadcastActiveSource(void);
- virtual uint8_t GetSourceDestination(cec_logical_address destination = CECDEVICE_BROADCAST) const;
private:
- bool WaitForAck(uint32_t iTimeout = 1000);
- bool ParseMessage(cec_frame &msg);
- void ParseCurrentFrame(cec_frame &frame);
+ void LogOutput(const cec_command &data);
+ bool WaitForAck(bool *bError, uint32_t iTimeout = 1000);
+ bool ParseMessage(cec_adapter_message &msg);
+ void ParseCommand(cec_command &command);
+ void ParseVendorId(cec_logical_address device, const cec_datapacket &data);
- cec_frame m_currentframe;
- uint16_t m_physicaladdress;
- cec_logical_address m_iLogicalAddress;
- CecBuffer<cec_frame> m_frameBuffer;
- std::string m_strDeviceName;
- CMutex m_mutex;
- CAdapterCommunication *m_communication;
- CLibCEC *m_controller;
+ cec_command m_currentframe;
+ uint16_t m_physicaladdress;
+ cec_logical_address m_iLogicalAddress;
+ CecBuffer<cec_adapter_message> m_frameBuffer;
+ std::string m_strDeviceName;
+ CMutex m_mutex;
+ CAdapterCommunication *m_communication;
+ CLibCEC *m_controller;
+ uint64_t m_vendorIds[16];
+ uint8_t m_vendorClasses[16];
};
};
using namespace CEC;
CLibCEC::CLibCEC(const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS */) :
+ m_iStartTime(GetTimeMs()),
m_iCurrentButton(CEC_USER_CONTROL_CODE_UNKNOWN),
m_buttontime(0)
{
return m_commandBuffer.Pop(*command);
}
-bool CLibCEC::Transmit(const cec_frame &data, bool bWaitForAck /* = true */)
+bool CLibCEC::Transmit(const cec_command &data, bool bWaitForAck /* = true */)
{
return m_cec ? m_cec->Transmit(data, bWaitForAck) : false;
}
{
cec_log_message message;
message.level = level;
+ message.time = GetTimeMs() - m_iStartTime;
snprintf(message.message, sizeof(message.message), "%s", strMessage.c_str());
m_logBuffer.Push(message);
}
}
}
-void CLibCEC::AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters)
+void CLibCEC::AddCommand(cec_command &command)
{
- cec_command command;
- command.clear();
-
- command.source = source;
- command.destination = destination;
- command.opcode = opcode;
- if (parameters)
- command.parameters = *parameters;
if (m_commandBuffer.Push(command))
{
CStdString strDebug;
- strDebug.Format("stored command '%d' in the command buffer. buffer size = %d", opcode, m_commandBuffer.Size());
+ strDebug.Format("stored command '%2x' in the command buffer. buffer size = %d", command.opcode, m_commandBuffer.Size());
AddLog(CEC_LOG_DEBUG, strDebug);
}
else
virtual bool GetNextKeypress(cec_keypress *key);
virtual bool GetNextCommand(cec_command *command);
- virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true);
+ virtual bool Transmit(const cec_command &data, bool bWaitForAck = true);
virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress);
virtual bool PowerOnDevices(cec_logical_address address = CECDEVICE_TV);
virtual void AddLog(cec_log_level level, const std::string &strMessage);
virtual void AddKey(void);
- virtual void AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters);
+ virtual void AddCommand(cec_command &command);
virtual void CheckKeypressTimeout(void);
virtual void SetCurrentButton(cec_user_control_code iButtonCode);
protected:
+ int64_t m_iStartTime;
cec_user_control_code m_iCurrentButton;
int64_t m_buttontime;
CCECProcessor *m_cec;
return false;
}
-bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck /* = true */)
+bool cec_transmit(const CEC::cec_command &data, bool bWaitForAck /* = true */)
{
if (cec_parser)
return cec_parser->Transmit(data, bWaitForAck);
platform/threads.cpp \
platform/threads.h
-libcec_la_LDFLAGS = -lrt -lpthread -ludev -version-info @VERSION@
+libcec_la_LDFLAGS = @LIBS@ -version-info @VERSION@
Description: CEC interface library
URL: http://www.pulse-eight.com/
Version: @VERSION@
-Requires: udev
+Requires: @REQUIRES@
Libs: -L${libdir} -lcec
Cflags: -I${includedir}
#include "../baudrate.h"
#include "../timeutils.h"
+#if defined(__APPLE__)
+#ifndef XCASE
+#define XCASE 0
+#endif
+#ifndef OLCUC
+#define OLCUC 0
+#endif
+#ifndef IUCLC
+#define IUCLC 0
+#endif
+#endif
using namespace std;
using namespace CEC;
Close();
}
-int8_t CSerialPort::Write(const cec_frame &data)
+int8_t CSerialPort::Write(const cec_adapter_message &data)
{
fd_set port;
int32_t byteswritten = 0;
- while (byteswritten < (int32_t) data.size)
+ while (byteswritten < (int32_t) data.size())
{
FD_ZERO(&port);
FD_SET(m_fd, &port);
return -1;
}
- returnv = write(m_fd, data.data + byteswritten, data.size - byteswritten);
+ returnv = write(m_fd, data.packet.data + byteswritten, data.size() - byteswritten);
if (returnv == -1)
{
m_error = strerror(errno);
// {
// printf("%s write:", m_name.c_str());
// for (int i = 0; i < byteswritten; i++)
-// printf(" %02x", (unsigned int)data.data[i]);
+// printf(" %02x", (unsigned int)data[i]);
//
// printf("\n");
// }
bool IsOpen();
void Close();
- int8_t Write(const cec_frame &data);
+ int8_t Write(const cec_adapter_message &data);
int32_t Read(uint8_t* data, uint32_t len, uint64_t iTimeoutMs = 0);
std::string GetError() { return m_error; }
if (thread)
{
+ CLockObject lock(&thread->m_threadMutex);
thread->m_bRunning = true;
+ lock.Leave();
thread->m_threadCondition.Broadcast();
+
retVal = thread->Process();
+
+ lock.Lock();
thread->m_bRunning = false;
+ lock.Leave();
+ thread->m_threadCondition.Broadcast();
}
return retVal;
*/
#include <stdint.h>
+#if defined(__APPLE__)
+#include <mach/mach_time.h>
+#include <CoreVideo/CVHostTime.h>
+#elif defined(__WINDOWS__)
#include <time.h>
+#else
+#include <sys/time.h>
+#endif
namespace CEC
{
inline int64_t GetTimeMs()
{
- #ifdef __WINDOWS__
+ #if defined(__APPLE__)
+ return (int64_t) (CVGetCurrentHostTime() * 1000 / CVGetHostClockFrequency());
+ #elif defined(__WINDOWS__)
time_t rawtime;
time(&rawtime);
}
return -1;
#else
- struct timespec time;
- clock_gettime(CLOCK_MONOTONIC, &time);
-
- return ((int64_t)time.tv_sec * (int64_t)1000) + (int64_t)time.tv_nsec / (int64_t)1000;
+ timeval time;
+ gettimeofday(&time, NULL);
+ return (int64_t) (time.tv_sec * 1000 + time.tv_usec / 1000);
#endif
}
}
}
-int8_t CSerialPort::Write(const cec_frame &data)
+int8_t CSerialPort::Write(const cec_adapter_message &data)
{
CLockObject lock(&m_mutex);
DWORD iBytesWritten = 0;
if (!m_bIsOpen)
return -1;
- if (!WriteFile(m_handle, data.data, data.size, &iBytesWritten, NULL))
+ if (!WriteFile(m_handle, data.packet.data, data.size(), &iBytesWritten, NULL))
{
m_error = "Error while writing to COM port";
FormatWindowsError(GetLastError(), m_error);
using namespace CEC;
using namespace std;
-#define CEC_TEST_CLIENT_VERSION 5
+#define CEC_TEST_CLIENT_VERSION 6
inline bool HexStrToInt(const std::string& data, uint8_t& value)
switch (message.level)
{
case CEC_LOG_ERROR:
- cout << "ERROR: " << message.message << endl;
+ cout << "ERROR: ";
break;
case CEC_LOG_WARNING:
- cout << "WARNING: " << message.message << endl;
+ cout << "WARNING: ";
break;
case CEC_LOG_NOTICE:
- cout << "NOTICE: " << message.message << endl;
+ cout << "NOTICE: ";
break;
case CEC_LOG_DEBUG:
- cout << "DEBUG: " << message.message << endl;
+ cout << "DEBUG: ";
break;
}
+
+ CStdString strMessageTmp;
+ strMessageTmp.Format("[%16lld]\t%s", message.time, message.message);
+ cout << strMessageTmp.c_str() << endl;
}
}
"tx {bytes} transfer bytes over the CEC line." << endl <<
"[tx 40 00 FF 11 22 33] sends bytes 0x40 0x00 0xFF 0x11 0x22 0x33" << endl <<
endl <<
+ "on {address} power on the device with the given logical address." << endl <<
+ "[on 5] power on a connected audio system" << endl <<
+ endl <<
+ "standby {address} put the device with the given address in standby mode." << endl <<
+ "[standby 0] powers off the TV" << endl <<
+ endl <<
"la {logical_address} change the logical address of the CEC adapter." << endl <<
"[la 4] logical address 4" << endl <<
endl <<
"[ping] send a ping command to the CEC adapter." << endl <<
- "[bl] to let the adapter enter the bootloader, to upgrade the flash rom." << endl <<
+ "[bl] to let the adapter enter the bootloader, to upgrade" << endl <<
+ " the flash rom." << endl <<
+ "[r] reconnect to the CEC adapter." << endl <<
"[h] or [help] show this help." << endl <<
- "[q] or [quit] to quit the CEC test client and switch off all connected CEC devices." << endl <<
+ "[q] or [quit] to quit the CEC test client and switch off all" << endl <<
+ " connected CEC devices." << endl <<
"================================================================================" << endl;
}
{
string strvalue;
uint8_t ivalue;
- cec_frame bytes;
+ cec_command bytes;
bytes.clear();
while (GetWord(input, strvalue) && HexStrToInt(strvalue, ivalue))
parser->Transmit(bytes);
}
+ else if (command == "on")
+ {
+ string strValue;
+ uint8_t iValue = 0;
+ if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
+ {
+ parser->PowerOnDevices((cec_logical_address) iValue);
+ }
+ else
+ {
+ cout << "invalid destination" << endl;
+ }
+ }
+ else if (command == "standby")
+ {
+ string strValue;
+ uint8_t iValue = 0;
+ if (GetWord(input, strValue) && HexStrToInt(strValue, iValue) && iValue <= 0xF)
+ {
+ parser->StandbyDevices((cec_logical_address) iValue);
+ }
+ else
+ {
+ cout << "invalid destination" << endl;
+ }
+ }
else if (command == "la")
{
string strvalue;