CECDEVICE_BROADCAST = 15
} cec_logical_address;
+ typedef enum cec_opcode
+ {
+ CEC_OPCODE_ACTIVE_SOURCE = 0x82,
+ CEC_OPCODE_IMAGE_VIEW_ON = 0x04,
+ CEC_OPCODE_TEXT_VIEW_ON = 0x0D,
+ CEC_OPCODE_INACTIVE_SOURCE = 0x9D,
+ CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85,
+ CEC_OPCODE_ROUTING_CHANGE = 0x80,
+ CEC_OPCODE_ROUTING_INFORMATION = 0x81,
+ CEC_OPCODE_SET_STREAM_PATH = 0x86,
+ CEC_OPCODE_STANDBY = 0x36,
+ CEC_OPCODE_RECORD_OFF = 0x0B,
+ CEC_OPCODE_RECORD_ON = 0x09,
+ CEC_OPCODE_RECORD_STATUS = 0x0A,
+ CEC_OPCODE_RECORD_TV_SCREEN = 0x0F,
+ CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33,
+ CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99,
+ CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1,
+ CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34,
+ CEC_OPCODE_SET_DIGITAL_TIMER = 0x97,
+ CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2,
+ CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67,
+ CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43,
+ CEC_OPCODE_TIMER_STATUS = 0x35,
+ CEC_OPCODE_CEC_VERSION = 0x9E,
+ CEC_OPCODE_GET_CEC_VERSION = 0x9F,
+ CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83,
+ CEC_OPCODE_GET_MENU_LANGUAGE = 0x91,
+ CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84,
+ CEC_OPCODE_SET_MENU_LANGUAGE = 0x32,
+ CEC_OPCODE_DECK_CONTROL = 0x42,
+ CEC_OPCODE_DECK_STATUS = 0x1B,
+ CEC_OPCODE_GIVE_DECK_STATUS = 0x1A,
+ CEC_OPCODE_PLAY = 0x41,
+ CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08,
+ CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92,
+ CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93,
+ CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07,
+ CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06,
+ CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05,
+ CEC_OPCODE_DEVICE_VENDOR_ID = 0x87,
+ CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C,
+ CEC_OPCODE_VENDOR_COMMAND = 0x89,
+ CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0,
+ CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
+ CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B,
+ CEC_OPCODE_SET_OSD_STRING = 0x64,
+ CEC_OPCODE_GIVE_OSD_NAME = 0x46,
+ CEC_OPCODE_SET_OSD_NAME = 0x47,
+ CEC_OPCODE_MENU_REQUEST = 0x8D,
+ CEC_OPCODE_MENU_STATUS = 0x8E,
+ CEC_OPCODE_USER_CONTROL_PRESSED = 0x44,
+ CEC_OPCODE_USER_CONTROL_RELEASE = 0x45,
+ CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F,
+ CEC_OPCODE_REPORT_POWER_STATUS = 0x90,
+ CEC_OPCODE_FEATURE_ABORT = 0x00,
+ CEC_OPCODE_ABORT = 0xFF,
+ CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71,
+ CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
+ CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A,
+ CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72,
+ CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70,
+ CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E,
+ CEC_OPCODE_SET_AUDIO_RATE = 0x9A
+ } cec_opcode;
+
typedef enum cec_log_level
{
CEC_LOG_DEBUG = 0,
std::string comm;
} cec_device;
+ typedef struct cec_command
+ {
+ cec_logical_address source;
+ cec_logical_address destination;
+ cec_opcode opcode;
+ cec_frame parameters;
+ } cec_command;
+
//default physical address 1.0.0.0
#define CEC_DEFAULT_PHYSICAL_ADDRESS 0x1000
extern DECLSPEC bool cec_get_next_keypress(cec_keypress *key);
#endif
+/*!
+ * @brief Get the next CEC command that was received by the adapter.
+ * @param action The next command.
+ * @return True when a command was passed, false otherwise.
+ */
+#ifdef __cplusplus
+extern DECLSPEC bool cec_get_next_command(CEC::cec_command *command);
+#else
+extern DECLSPEC bool cec_get_next_command(cec_command *command);
+#endif
+
/*!
* @brief Transmit a frame on the CEC line.
* @param data The frame to send.
*/
virtual bool GetNextKeypress(cec_keypress *key) = 0;
+ /*!
+ * @see cec_get_next_command
+ */
+ virtual bool GetNextCommand(cec_command *command) = 0;
+
/*!
* @see cec_transmit
*/
#include <string>
#include "CECExports.h"
-typedef enum
-{
- CEC_OPCODE_ACTIVE_SOURCE = 0x82,
- CEC_OPCODE_IMAGE_VIEW_ON = 0x04,
- CEC_OPCODE_TEXT_VIEW_ON = 0x0D,
- CEC_OPCODE_INACTIVE_SOURCE = 0x9D,
- CEC_OPCODE_REQUEST_ACTIVE_SOURCE = 0x85,
- CEC_OPCODE_ROUTING_CHANGE = 0x80,
- CEC_OPCODE_ROUTING_INFORMATION = 0x81,
- CEC_OPCODE_SET_STREAM_PATH = 0x86,
- CEC_OPCODE_STANDBY = 0x36,
- CEC_OPCODE_RECORD_OFF = 0x0B,
- CEC_OPCODE_RECORD_ON = 0x09,
- CEC_OPCODE_RECORD_STATUS = 0x0A,
- CEC_OPCODE_RECORD_TV_SCREEN = 0x0F,
- CEC_OPCODE_CLEAR_ANALOGUE_TIMER = 0x33,
- CEC_OPCODE_CLEAR_DIGITAL_TIMER = 0x99,
- CEC_OPCODE_CLEAR_EXTERNAL_TIMER = 0xA1,
- CEC_OPCODE_SET_ANALOGUE_TIMER = 0x34,
- CEC_OPCODE_SET_DIGITAL_TIMER = 0x97,
- CEC_OPCODE_SET_EXTERNAL_TIMER = 0xA2,
- CEC_OPCODE_SET_TIMER_PROGRAM_TITLE = 0x67,
- CEC_OPCODE_TIMER_CLEARED_STATUS = 0x43,
- CEC_OPCODE_TIMER_STATUS = 0x35,
- CEC_OPCODE_CEC_VERSION = 0x9E,
- CEC_OPCODE_GET_CEC_VERSION = 0x9F,
- CEC_OPCODE_GIVE_PHYSICAL_ADDRESS = 0x83,
- CEC_OPCODE_GET_MENU_LANGUAGE = 0x91,
- CEC_OPCODE_REPORT_PHYSICAL_ADDRESS = 0x84,
- CEC_OPCODE_SET_MENU_LANGUAGE = 0x32,
- CEC_OPCODE_DECK_CONTROL = 0x42,
- CEC_OPCODE_DECK_STATUS = 0x1B,
- CEC_OPCODE_GIVE_DECK_STATUS = 0x1A,
- CEC_OPCODE_PLAY = 0x41,
- CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS = 0x08,
- CEC_OPCODE_SELECT_ANALOGUE_SERVICE = 0x92,
- CEC_OPCODE_SELECT_DIGITAL_SERVICE = 0x93,
- CEC_OPCODE_TUNER_DEVICE_STATUS = 0x07,
- CEC_OPCODE_TUNER_STEP_DECREMENT = 0x06,
- CEC_OPCODE_TUNER_STEP_INCREMENT = 0x05,
- CEC_OPCODE_DEVICE_VENDOR_ID = 0x87,
- CEC_OPCODE_GIVE_DEVICE_VENDOR_ID = 0x8C,
- CEC_OPCODE_VENDOR_COMMAND = 0x89,
- CEC_OPCODE_VENDOR_COMMAND_WITH_ID = 0xA0,
- CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN = 0x8A,
- CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP = 0x8B,
- CEC_OPCODE_SET_OSD_STRING = 0x64,
- CEC_OPCODE_GIVE_OSD_NAME = 0x46,
- CEC_OPCODE_SET_OSD_NAME = 0x47,
- CEC_OPCODE_MENU_REQUEST = 0x8D,
- CEC_OPCODE_MENU_STATUS = 0x8E,
- CEC_OPCODE_USER_CONTROL_PRESSED = 0x44,
- CEC_OPCODE_USER_CONTROL_RELEASE = 0x45,
- CEC_OPCODE_GIVE_DEVICE_POWER_STATUS = 0x8F,
- CEC_OPCODE_REPORT_POWER_STATUS = 0x90,
- CEC_OPCODE_FEATURE_ABORT = 0x00,
- CEC_OPCODE_ABORT = 0xFF,
- CEC_OPCODE_GIVE_AUDIO_STATUS = 0x71,
- CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS = 0x7D,
- CEC_OPCODE_REPORT_AUDIO_STATUS = 0x7A,
- CEC_OPCODE_SET_SYSTEM_AUDIO_MODE = 0x72,
- CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST = 0x70,
- CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS = 0x7E,
- CEC_OPCODE_SET_AUDIO_RATE = 0x9A
-} ECecOpcode;
-
typedef enum
{
CEC_ABORT_REASON_UNRECOGNIZED_OPCODE = 0,
{
return m_keyBuffer.Pop(*key);
}
+
+bool CCECParser::GetNextCommand(cec_command *command)
+{
+ return m_commandBuffer.Pop(*command);
+}
//@}
-void CCECParser::TransmitAbort(cec_logical_address address, ECecOpcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
+void CCECParser::TransmitAbort(cec_logical_address address, cec_opcode opcode, ECecAbortReason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
{
AddLog(CEC_LOG_DEBUG, "transmitting abort message");
cec_frame frame;
return;
vector<uint8_t> tx;
- ECecOpcode opCode = (ECecOpcode) m_currentframe[1];
+ cec_opcode opCode = (cec_opcode) m_currentframe[1];
if (destination == (uint16_t) m_iLogicalAddress)
{
switch(opCode)
AddKey();
break;
default:
+ cec_frame params = m_currentframe;
+ params.erase(params.begin(), params.begin() + 2);
+ AddCommand((cec_logical_address) initiator, (cec_logical_address) destination, opCode, ¶ms);
break;
}
}
}
}
+void CCECParser::AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters)
+{
+ cec_command command;
+ command.source = source;
+ command.destination = destination;
+ command.opcode = opcode;
+ if (parameters)
+ command.parameters = *parameters;
+ m_commandBuffer.Push(command);
+}
+
int CCECParser::GetMinVersion(void)
{
return CEC_MIN_VERSION;
virtual bool SetInactiveView(void);
virtual bool GetNextLogMessage(cec_log_message *message);
virtual bool GetNextKeypress(cec_keypress *key);
+ virtual bool GetNextCommand(cec_command *command);
virtual bool Transmit(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 5000);
virtual bool SetLogicalAddress(cec_logical_address iLogicalAddress);
virtual bool SetAckMask(uint16_t iMask);
bool Process(void);
protected:
virtual bool TransmitFormatted(const cec_frame &data, bool bWaitForAck = true, int64_t iTimeout = (int64_t) 2000);
- virtual void TransmitAbort(cec_logical_address address, ECecOpcode opcode, ECecAbortReason reason = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
+ 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 ReportMenuState(cec_logical_address address = CECDEVICE_TV, bool bActive = true);
private:
void AddKey(void);
+ void AddCommand(cec_logical_address source, cec_logical_address destination, cec_opcode opcode, cec_frame *parameters);
void AddLog(cec_log_level level, const std::string &strMessage);
bool WaitForAck(int64_t iTimeout = (int64_t) 1000);
bool ReadFromDevice(int iTimeout);
CecBuffer<cec_frame> m_frameBuffer;
CecBuffer<cec_log_message> m_logBuffer;
CecBuffer<cec_keypress> m_keyBuffer;
+ CecBuffer<cec_command> m_commandBuffer;
std::string m_strDeviceName;
pthread_t m_thread;
CMutex m_mutex;
return false;
}
+bool cec_get_next_command(cec_command *command)
+{
+ if (cec_parser)
+ return cec_parser->GetNextCommand(command);
+ return false;
+}
+
bool cec_transmit(const CEC::cec_frame &data, bool bWaitForAck /* = true */, int64_t iTimeout /* = 2000 */)
{
if (cec_parser)
struct CecBuffer
{
public:
- CecBuffer(void) {}
+ CecBuffer(int iMaxSize = 100) {}
virtual ~CecBuffer(void) {}
- void Push(_BType entry)
+ bool Push(_BType entry)
{
- CLockObject lock(&mutex);
- buffer.push(entry);
+ CLockObject lock(&m_mutex);
+ if (m_buffer.size() == m_maxSize)
+ return false;
+
+ m_buffer.push(entry);
+ return true;
}
bool Pop(_BType &entry)
{
bool bReturn(false);
- CLockObject lock(&mutex);
- if (buffer.size() > 0)
+ CLockObject lock(&m_mutex);
+ if (m_buffer.size() > 0)
{
- entry = buffer.front();
- buffer.pop();
+ entry = m_buffer.front();
+ m_buffer.pop();
bReturn = true;
}
return bReturn;
}
private:
- std::queue<_BType> buffer;
- CMutex mutex;
+ int m_maxSize;
+ std::queue<_BType> m_buffer;
+ CMutex m_mutex;
};
"parameters:" << endl <<
"\t-h --help Shows this help text" << endl <<
"\t-l --list-devices List all devices on this system" << endl <<
- "\t[COM PORT] The com port to connect to. If no COM port is given, the client tries to connect to the first device that is detected" << endl;
+ "\t[COM PORT] The com port to connect to. If no COM port is given, the client tries to connect to the first device that is detected" << endl <<
+ endl <<
+ "Type 'h' or 'help' and press enter after starting the client to display all available commands" << endl;
+}
+
+void show_console_help(void)
+{
+ cout << endl <<
+ "================================================================================" << endl <<
+ "Available commands:" << endl <<
+ 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 <<
+ "am {ackmack} change the ackmask of the CEC adapter." << endl <<
+ "[am 10] ackmask 0x10 (logical address 4)" << 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 <<
+ "[h] or [help] show this help." << endl <<
+ "[q] or [quit] to quit the CEC test client and switch off all connected CEC devices." << endl <<
+ "================================================================================" << endl;
}
int main (int argc, char *argv[])
{
parser->StartBootloader();
}
+ else if (command == "h" || command == "help")
+ {
+ show_console_help();
+ }
else if (command == "q" || command == "quit")
{
bContinue = false;