+libcec (1.8.2-1) unstable; urgency=low
+
+ * changed/added:
+ * p8: match to the response provided by the firmware when checking
+ responses (added in firmware v2 rev6)
+
+ * fixed:
+ * windows: update the error message when an error occurs while writing to
+ the serial port
+ * delayed source switch time was reset, resulting in an attempt every
+ second until it succeeded
+ * ensure that the vendor commands are always sent for panasonic, and that
+ the deck status for lg isn't reset. fixes some buttons not working after
+ a second or delayed source switch
+ * added guard so ReplaceHandler() doesn't accidently try to replace a
+ handler for the broadcast address
+ * wait until the commandhandler is replaced before registering a client,
+ so we don't register a client and replace it directly afterwards if the
+ tv doesn't support the device type
+ * ensure that the command handler of the tv is replaced before registering
+ a client, or it might result in a double eeprom write attempt
+ * p8: don't disable controlled mode when switching to monitoring mode
+ * p8: do delayed eeprom writes async or it'll block processing other input
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Tue, 3 Aug 2012 02:35:00 +0100
+
libcec (1.8.1-1) unstable; urgency=low
* changed/added:
+libcec (1.8.2-1) unstable; urgency=low
+
+ * changed/added:
+ * p8: match to the response provided by the firmware when checking
+ responses (added in firmware v2 rev6)
+
+ * fixed:
+ * windows: update the error message when an error occurs while writing to
+ the serial port
+ * delayed source switch time was reset, resulting in an attempt every
+ second until it succeeded
+ * ensure that the vendor commands are always sent for panasonic, and that
+ the deck status for lg isn't reset. fixes some buttons not working after
+ a second or delayed source switch
+ * added guard so ReplaceHandler() doesn't accidently try to replace a
+ handler for the broadcast address
+ * wait until the commandhandler is replaced before registering a client,
+ so we don't register a client and replace it directly afterwards if the
+ tv doesn't support the device type
+ * ensure that the command handler of the tv is replaced before registering
+ a client, or it might result in a double eeprom write attempt
+ * p8: don't disable controlled mode when switching to monitoring mode
+ * p8: do delayed eeprom writes async or it'll block processing other input
+
+ -- Pulse-Eight Packaging <packaging@pulse-eight.com> Tue, 3 Aug 2012 02:35:00 +0100
+
libcec (1.8.1-1) unstable; urgency=low
* changed/added:
#include "cectypes.h"
-#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_8_0
+#define LIBCEC_VERSION_CURRENT CEC_SERVER_VERSION_1_8_1
namespace CEC
{
CEC_CLIENT_VERSION_1_7_0 = 0x1700,
CEC_CLIENT_VERSION_1_7_1 = 0x1701,
CEC_CLIENT_VERSION_1_7_2 = 0x1702,
- CEC_CLIENT_VERSION_1_8_0 = 0x1800
+ CEC_CLIENT_VERSION_1_8_0 = 0x1800,
+ CEC_CLIENT_VERSION_1_8_1 = 0x1801
} cec_client_version;
typedef enum cec_server_version
CEC_SERVER_VERSION_1_7_0 = 0x1700,
CEC_SERVER_VERSION_1_7_1 = 0x1701,
CEC_SERVER_VERSION_1_7_2 = 0x1702,
- CEC_SERVER_VERSION_1_8_0 = 0x1800
+ CEC_SERVER_VERSION_1_8_0 = 0x1800,
+ CEC_SERVER_VERSION_1_8_1 = 0x1801
} cec_server_version;
typedef struct libcec_configuration
Config = new LibCECConfiguration();
Config.DeviceTypes.Types[0] = CecDeviceType.RecordingDevice;
Config.DeviceName = "CEC Tester";
- Config.ClientVersion = CecClientVersion.Version1_8_0;
+ Config.ClientVersion = CecClientVersion.Version1_8_1;
Config.SetCallbacks(this);
LogLevel = (int)CecLogLevel.All;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.8.0.0")]
-[assembly: AssemblyFileVersion("1.8.0.0")]
+[assembly: AssemblyVersion("1.8.1.0")]
+[assembly: AssemblyFileVersion("1.8.1.0")]
[assembly:AssemblyTrademarkAttribute("")];
[assembly:AssemblyCultureAttribute("")];
-[assembly:AssemblyVersionAttribute("1.8.0.0")];
+[assembly:AssemblyVersionAttribute("1.8.1.0")];
[assembly:ComVisible(false)];
[assembly:CLSCompliantAttribute(true)];
Version1_7_0 = 0x1700,
Version1_7_1 = 0x1701,
Version1_7_2 = 0x1702,
- Version1_8_0 = 0x1800
+ Version1_8_0 = 0x1800,
+ Version1_8_1 = 0x1801
};
public enum class CecServerVersion
Version1_7_0 = 0x1700,
Version1_7_1 = 0x1701,
Version1_7_2 = 0x1702,
- Version1_8_0 = 0x1800
+ Version1_8_0 = 0x1800,
+ Version1_8_1 = 0x1801
};
public ref class CecAdapter
Config.DeviceTypes.Types[0] = CecDeviceType.RecordingDevice;
Config.DeviceName = "CEC Config";
Config.GetSettingsFromROM = true;
- Config.ClientVersion = CecClientVersion.Version1_8_0;
+ Config.ClientVersion = CecClientVersion.Version1_8_1;
Callbacks = new CecCallbackWrapper(this);
Config.SetCallbacks(Callbacks);
LoadXMLConfiguration(ref Config);
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
-[assembly: AssemblyVersion("1.8.0.0")]
-[assembly: AssemblyFileVersion("1.8.0.0")]
+[assembly: AssemblyVersion("1.8.1.0")]
+[assembly: AssemblyFileVersion("1.8.1.0")]
g_config.Clear();
snprintf(g_config.strDeviceName, 13, "CEC-config");
g_config.callbackParam = NULL;
- g_config.clientVersion = (uint32_t)CEC_CLIENT_VERSION_1_6_2;
+ g_config.clientVersion = (uint32_t)CEC_CLIENT_VERSION_1_8_1;
g_callbacks.CBCecLogMessage = &CecLogMessage;
g_callbacks.CBCecKeyPress = &CecKeyPress;
g_callbacks.CBCecCommand = &CecCommand;
if (m_processor)
{
- if (bEnable)
- return m_processor->UnregisterClient(this);
- else
- {
- m_configuration.bMonitorOnly = false;
- return m_processor->RegisterClient(this);
- }
+ m_processor->SwitchMonitoring(bEnable);
+ m_configuration.bMonitorOnly = bEnable;
+ return bEnable ? true: m_processor->RegisterClient(this);
}
return false;
m_libcec(libcec),
m_iStandardLineTimeout(3),
m_iRetryLineTimeout(3),
- m_iLastTransmission(0)
+ m_iLastTransmission(0),
+ m_bMonitor(true)
{
m_busDevices = new CCECDeviceMap(this);
}
// ensure that we know the vendor id of the TV
CCECBusDevice *tv = GetTV();
+ cec_vendor_id tvVendor = CEC_VENDOR_UNKNOWN;
if (m_communication->SupportsSourceLogicalAddress(CECDEVICE_UNREGISTERED))
- tv->GetVendorId(CECDEVICE_UNREGISTERED);
+ tvVendor = tv->GetVendorId(CECDEVICE_UNREGISTERED);
else if (m_communication->SupportsSourceLogicalAddress(CECDEVICE_FREEUSE))
- tv->GetVendorId(CECDEVICE_FREEUSE);
+ tvVendor = tv->GetVendorId(CECDEVICE_FREEUSE);
+
+ // wait until the handler is replaced, to avoid double registrations
+ if (tvVendor != CEC_VENDOR_UNKNOWN &&
+ CCECCommandHandler::HasSpecificHandler(tvVendor))
+ {
+ while (!tv->ReplaceHandler(false))
+ CEvent::Sleep(5);
+ }
// get the configuration from the client
m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion));
if (SetLogicalAddresses(addresses))
{
// no more clients left, disable controlled mode
- if (addresses.IsEmpty())
+ if (addresses.IsEmpty() && !m_bMonitor)
m_communication->SetControlledMode(false);
return true;
m_communication->IsRunningLatestFirmware() :
true;
}
+
+void CCECProcessor::SwitchMonitoring(bool bSwitchTo)
+{
+ {
+ CLockObject lock(m_mutex);
+ m_bMonitor = bSwitchTo;
+ }
+ if (bSwitchTo)
+ UnregisterClients();
+}
bool TryLogicalAddress(cec_logical_address address, cec_version libCECSpecVersion = CEC_VERSION_1_4);
bool IsRunningLatestFirmware(void);
- private:
+ void SwitchMonitoring(bool bSwitchTo);
+
+ private:
bool OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening = true);
void SetCECInitialised(bool bSetTo = true);
CCECInputBuffer m_inBuffer;
CCECDeviceMap * m_busDevices;
std::map<cec_logical_address, CCECClient *> m_clients;
+ bool m_bMonitor;
};
};
return "1.7.2";
case CEC_CLIENT_VERSION_1_8_0:
return "1.8.0";
+ case CEC_CLIENT_VERSION_1_8_1:
+ return "1.8.1";
default:
return "Unknown";
}
return "1.7.2";
case CEC_SERVER_VERSION_1_8_0:
return "1.8.0";
+ case CEC_SERVER_VERSION_1_8_1:
+ return "1.8.1";
default:
return "Unknown";
}
// firmware version 2
#define CEC_LATEST_ADAPTER_FW_VERSION 2
-// firmware date Thu Apr 26 20:14:49 2012 +0000
-#define CEC_LATEST_ADAPTER_FW_DATE 0x5009F0A3
+// firmware date Thu Aug 2 08:31:24 UTC 2012
+#define CEC_LATEST_ADAPTER_FW_DATE 0x501a4b0c
+
+#define CEC_FW_DATE_EXTENDED_RESPONSE 0x501a4b0c
#define LIB_CEC m_callback->GetLib()
m_lastPollDestination(CECDEVICE_UNKNOWN),
m_bInitialised(false),
m_pingThread(NULL),
+ m_eepromWriteThread(NULL),
m_commands(NULL),
- m_adapterMessageQueue(NULL),
- m_iLastEepromWrite(0),
- m_iScheduleEepromWrite(0)
+ m_adapterMessageQueue(NULL)
{
m_logicalAddresses.Clear();
for (unsigned int iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
}
else if (bStartListening)
{
- /* start a ping thread, that will ping the adapter every 15 seconds
- if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
- m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
- if (m_pingThread->CreateThread())
+ /* start the eeprom write thread, that handles all eeprom writes async */
+ m_eepromWriteThread = new CAdapterEepromWriteThread(this);
+ if (!m_eepromWriteThread->CreateThread())
{
- bConnectionOpened = true;
+ bConnectionOpened = false;
+ LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create the eeprom write thread");
}
else
{
- bConnectionOpened = false;
- LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
+ /* start a ping thread, that will ping the adapter every 15 seconds
+ if it doesn't receive any ping for 30 seconds, it'll switch to auto mode */
+ m_pingThread = new CAdapterPingThread(this, CEC_ADAPTER_PING_TIMEOUT);
+ if (m_pingThread->CreateThread())
+ {
+ bConnectionOpened = true;
+ }
+ else
+ {
+ bConnectionOpened = false;
+ LIB_CEC->AddLog(CEC_LOG_ERROR, "could not create a ping thread");
+ }
}
}
m_adapterMessageQueue->Clear();
+ /* stop and delete the write thread */
+ if (m_eepromWriteThread)
+ m_eepromWriteThread->Stop();
+ DELETE_AND_NULL(m_eepromWriteThread);
+
/* stop and delete the ping thread */
DELETE_AND_NULL(m_pingThread);
CCECAdapterMessage msg;
LIB_CEC->AddLog(CEC_LOG_DEBUG, "communication thread started");
- bool bWriteEeprom(false);
while (!IsStopped())
{
/* read from the serial port */
break;
}
- // check if we need to do another eeprom write
- {
- CLockObject lock(m_mutex);
- int64_t iNow = GetTimeMs();
- if (m_iScheduleEepromWrite > 0 &&
- m_iScheduleEepromWrite >= iNow)
- {
- m_iScheduleEepromWrite = 0;
- m_iLastEepromWrite = iNow;
- bWriteEeprom = true;
- }
- }
-
- if (bWriteEeprom)
- {
- LIB_CEC->AddLog(CEC_LOG_DEBUG, "updating the eeprom (scheduled)");
- bWriteEeprom = false;
- if (!m_commands->WriteEEPROM())
- {
- // failed, retry later
- CLockObject lock(m_mutex);
- m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
- }
- }
-
/* TODO sleep 5 ms so other threads can get a lock */
Sleep(5);
}
uint32_t CUSBCECAdapterCommunication::GetFirmwareBuildDate(void)
{
- return IsOpen() ? m_commands->RequestBuildDate() : m_commands ? m_commands->GetPersistedBuildDate() : 0;
+ uint32_t iBuildDate(0);
+ if (m_commands)
+ iBuildDate = m_commands->GetPersistedBuildDate();
+ if (iBuildDate == 0 && IsOpen())
+ iBuildDate = m_commands->RequestBuildDate();
+
+ return iBuildDate;
+}
+
+bool CUSBCECAdapterCommunication::ProvidesExtendedResponse(void)
+{
+ uint32_t iBuildDate(0);
+ if (m_commands)
+ iBuildDate = m_commands->GetPersistedBuildDate();
+
+ return iBuildDate >= CEC_FW_DATE_EXTENDED_RESPONSE;
}
bool CUSBCECAdapterCommunication::IsRunningLatestFirmware(void)
{
- return GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION &&
- GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE;
+ return GetFirmwareBuildDate() >= CEC_LATEST_ADAPTER_FW_DATE &&
+ GetFirmwareVersion() >= CEC_LATEST_ADAPTER_FW_VERSION;
}
bool CUSBCECAdapterCommunication::PersistConfiguration(const libcec_configuration &configuration)
{
- if (IsOpen())
- {
- // returns true when something changed
- if (m_commands->PersistConfiguration(configuration))
- {
- {
- CLockObject lock(m_mutex);
- uint64_t iNow = GetTimeMs();
- if (iNow - m_iLastEepromWrite < CEC_ADAPTER_EEPROM_WRITE_INTERVAL)
- {
- // if there was more than 1 write within the last 30 seconds, schedule another one
- if (m_iScheduleEepromWrite == 0)
- m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
- return true;
- }
- else
- {
- m_iLastEepromWrite = iNow;
- }
- }
-
- if (!m_commands->WriteEEPROM())
- {
- // write failed, retry later
- CLockObject lock(m_mutex);
- m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
- }
- }
- }
- return IsOpen() ? m_commands->PersistConfiguration(configuration) : false;
+ return IsOpen() ?
+ m_commands->PersistConfiguration(configuration) && m_eepromWriteThread->Write() :
+ false;
}
bool CUSBCECAdapterCommunication::GetConfiguration(libcec_configuration &configuration)
}
}
- Sleep(500);
+ Sleep(5);
+ }
+ return NULL;
+}
+
+void CAdapterEepromWriteThread::Stop(void)
+{
+ StopThread(-1);
+ {
+ CLockObject lock(m_mutex);
+ if (m_iScheduleEepromWrite > 0)
+ m_com->LIB_CEC->AddLog(CEC_LOG_WARNING, "write thread stopped while a write was queued");
+ m_condition.Signal();
+ }
+ StopThread();
+}
+
+void *CAdapterEepromWriteThread::Process(void)
+{
+ while (!IsStopped())
+ {
+ CLockObject lock(m_mutex);
+ if ((m_iScheduleEepromWrite > 0 && m_iScheduleEepromWrite < GetTimeMs()) ||
+ m_condition.Wait(m_mutex, m_bWrite, 100))
+ {
+ m_bWrite = false;
+ if (m_com->m_commands->WriteEEPROM())
+ {
+ m_iLastEepromWrite = GetTimeMs();
+ m_iScheduleEepromWrite = 0;
+ }
+ else
+ {
+ m_iScheduleEepromWrite = GetTimeMs() + CEC_ADAPTER_EEPROM_WRITE_RETRY;
+ }
+ }
}
return NULL;
}
+
+bool CAdapterEepromWriteThread::Write(void)
+{
+ CLockObject lock(m_mutex);
+ if (m_iScheduleEepromWrite == 0)
+ {
+ int64_t iNow = GetTimeMs();
+ if (m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL > iNow)
+ {
+ m_com->LIB_CEC->AddLog(CEC_LOG_DEBUG, "delaying eeprom write by %ld ms", m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL - iNow);
+ m_iScheduleEepromWrite = m_iLastEepromWrite + CEC_ADAPTER_EEPROM_WRITE_INTERVAL;
+ }
+ else
+ {
+ m_bWrite = true;
+ m_condition.Signal();
+ }
+ }
+ return true;
+}
{
class CCECProcessor;
class CAdapterPingThread;
+ class CAdapterEepromWriteThread;
class CUSBCECAdapterCommands;
class CCECAdapterMessageQueue;
class CCECAdapterMessage;
{
friend class CUSBCECAdapterCommands;
friend class CCECAdapterMessageQueue;
+ friend class CAdapterEepromWriteThread;
public:
/*!
bool SupportsSourceLogicalAddress(const cec_logical_address UNUSED(address)) { return true; }
///}
+ bool ProvidesExtendedResponse(void);
+
void *Process(void);
private:
bool m_bInitialised; /**< true when the connection is initialised, false otherwise */
bool m_bWaitingForAck[15]; /**< array in which we store from which devices we're expecting acks */
CAdapterPingThread * m_pingThread; /**< ping thread, that pings the adapter every 15 seconds */
+ CAdapterEepromWriteThread * m_eepromWriteThread; /**< eeprom writes are done async */
CUSBCECAdapterCommands * m_commands; /**< commands that can be sent to the adapter */
CCECAdapterMessageQueue * m_adapterMessageQueue; /**< the incoming and outgoing message queue */
cec_logical_addresses m_logicalAddresses; /**< the logical address list that this instance is using */
- int64_t m_iLastEepromWrite; /**< last time that this instance did an eeprom write */
- int64_t m_iScheduleEepromWrite; /**< in case there were more than 2 changes within 30 seconds, do another write at this time */
+ };
+
+ class CAdapterEepromWriteThread : public PLATFORM::CThread
+ {
+ public:
+ CAdapterEepromWriteThread(CUSBCECAdapterCommunication *com) :
+ m_com(com),
+ m_bWrite(false),
+ m_iLastEepromWrite(0),
+ m_iScheduleEepromWrite(0) {}
+ virtual ~CAdapterEepromWriteThread(void) {}
+
+ bool Write(void);
+ void* Process(void);
+ void Stop(void);
+ private:
+ CUSBCECAdapterCommunication *m_com;
+ bool m_bWrite;
+ PLATFORM::CCondition<bool> m_condition;
+ PLATFORM::CMutex m_mutex;
+ int64_t m_iLastEepromWrite; /**< last time that this instance did an eeprom write */
+ int64_t m_iScheduleEepromWrite; /**< in case there were more than 2 changes within 30 seconds, do another write at this time */
};
class CAdapterPingThread : public PLATFORM::CThread
m_timeout(iTimeout){}
virtual ~CAdapterPingThread(void) {}
- virtual void* Process(void);
+ void* Process(void);
private:
CUSBCECAdapterCommunication *m_com;
PLATFORM::CTimeout m_timeout;
strMsg.AppendFormat(" %02x %s", At(2), IsEOM() ? "eom" : "");
break;
default:
- for (uint8_t iPtr = 2; iPtr < Size(); iPtr++)
- if (At(iPtr) != MSGEND)
- strMsg.AppendFormat(" %02x", At(iPtr));
+ if (Size() >= 2 && (Message() == MSGCODE_COMMAND_ACCEPTED || Message() == MSGCODE_COMMAND_REJECTED))
+ strMsg.AppendFormat(": %s", ToString((cec_adapter_messagecode)At(2)));
+ else
+ {
+ for (uint8_t iPtr = 2; iPtr < Size(); iPtr++)
+ if (At(iPtr) != MSGEND)
+ strMsg.AppendFormat(" %02x", At(iPtr));
+ }
break;
}
}
MSGCODE_NOTHING;
}
+cec_adapter_messagecode CCECAdapterMessage::ResponseTo(void) const
+{
+ return packet.size >= 3 ?
+ (cec_adapter_messagecode) (packet.At(2) & ~(MSGCODE_FRAME_EOM | MSGCODE_FRAME_ACK)) :
+ MSGCODE_NOTHING;
+}
+
bool CCECAdapterMessage::IsTranmission(void) const
{
cec_adapter_messagecode msgCode = Message();
*/
cec_adapter_messagecode Message(void) const;
+ /*!
+ * @return The messagecode (if provided) that this message is responding to
+ */
+ cec_adapter_messagecode ResponseTo(void) const;
+
/*!
* @return True when this message is a transmission, false otherwise.
*/
return m_message->Message();
}
-bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg)
+bool CCECAdapterMessageQueueEntry::IsResponseOld(const CCECAdapterMessage &msg)
{
cec_adapter_messagecode msgCode = msg.Message();
+
return msgCode == MessageCode() ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TIMEOUT_ERROR) ||
msgCode == MSGCODE_COMMAND_ACCEPTED ||
msgCode == MSGCODE_COMMAND_REJECTED ||
- (m_message->IsTranmission() && msgCode == MSGCODE_HIGH_ERROR) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_LOW_ERROR) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_RECEIVE_FAILED) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_LINE) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_ACK) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE) ||
- (m_message->IsTranmission() && msgCode == MSGCODE_TRANSMIT_SUCCEEDED);
+ (m_message->IsTranmission() && (msgCode == MSGCODE_TIMEOUT_ERROR ||
+ msgCode == MSGCODE_HIGH_ERROR ||
+ msgCode == MSGCODE_LOW_ERROR ||
+ msgCode == MSGCODE_RECEIVE_FAILED ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_LINE ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_ACK ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE ||
+ msgCode == MSGCODE_TRANSMIT_SUCCEEDED));
+}
+
+bool CCECAdapterMessageQueueEntry::IsResponse(const CCECAdapterMessage &msg)
+{
+ cec_adapter_messagecode thisMsgCode = m_message->Message();
+ cec_adapter_messagecode msgCode = msg.Message();
+ cec_adapter_messagecode msgResponse = msg.ResponseTo();
+
+ // msgcode matches, always a response
+ if (msgCode == MessageCode())
+ return true;
+
+ if (!ProvidesExtendedResponse())
+ return IsResponseOld(msg);
+
+ // response without a msgcode
+ if (msgResponse == MSGCODE_NOTHING)
+ {
+ m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, "no response code received");
+ return true;
+ }
+
+ // commands that only repond with accepted/rejected
+ if (thisMsgCode == MSGCODE_PING ||
+ thisMsgCode == MSGCODE_SET_ACK_MASK ||
+ thisMsgCode == MSGCODE_SET_CONTROLLED ||
+ thisMsgCode == MSGCODE_SET_AUTO_ENABLED ||
+ thisMsgCode == MSGCODE_SET_DEFAULT_LOGICAL_ADDRESS ||
+ thisMsgCode == MSGCODE_SET_LOGICAL_ADDRESS_MASK ||
+ thisMsgCode == MSGCODE_SET_PHYSICAL_ADDRESS ||
+ thisMsgCode == MSGCODE_SET_DEVICE_TYPE ||
+ thisMsgCode == MSGCODE_SET_HDMI_VERSION ||
+ thisMsgCode == MSGCODE_SET_OSD_NAME ||
+ thisMsgCode == MSGCODE_WRITE_EEPROM ||
+ thisMsgCode == MSGCODE_TRANSMIT_IDLETIME)
+ return thisMsgCode == msgResponse;
+
+ if (!m_message->IsTranmission())
+ {
+ m_queue->m_com->m_callback->GetLib()->AddLog(CEC_LOG_WARNING, "FIXME! not a transmission");
+ return false;
+ }
+
+ return ((msgCode == MSGCODE_COMMAND_ACCEPTED || msgCode == MSGCODE_COMMAND_REJECTED) &&
+ (msgResponse == MSGCODE_TRANSMIT_ACK_POLARITY || msgResponse == MSGCODE_TRANSMIT || msgResponse == MSGCODE_TRANSMIT_EOM)) ||
+ msgCode == MSGCODE_TIMEOUT_ERROR ||
+ msgCode == MSGCODE_HIGH_ERROR ||
+ msgCode == MSGCODE_LOW_ERROR ||
+ msgCode == MSGCODE_RECEIVE_FAILED ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_LINE ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_ACK ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_DATA ||
+ msgCode == MSGCODE_TRANSMIT_FAILED_TIMEOUT_LINE ||
+ msgCode == MSGCODE_TRANSMIT_SUCCEEDED;
}
const char *CCECAdapterMessageQueueEntry::ToString(void) const
return true;
}
+bool CCECAdapterMessageQueueEntry::ProvidesExtendedResponse(void)
+{
+ return m_queue && m_queue->ProvidesExtendedResponse();
+}
+
CCECAdapterMessageQueue::CCECAdapterMessageQueue(CUSBCECAdapterCommunication *com) :
PLATFORM::CThread(),
m_com(com),
return bReturn;
}
+
+bool CCECAdapterMessageQueue::ProvidesExtendedResponse(void)
+{
+ return m_com && m_com->ProvidesExtendedResponse();
+}
* @return True when it's a response, false otherwise.
*/
bool IsResponse(const CCECAdapterMessage &msg);
+ bool IsResponseOld(const CCECAdapterMessage &msg);
/*!
* @return The command that was sent in human readable form.
*/
void Signal(void);
+ bool ProvidesExtendedResponse(void);
+
CCECAdapterMessageQueue * m_queue;
CCECAdapterMessage * m_message; /**< the message that was sent */
uint8_t m_iPacketsLeft; /**< the amount of acks that we're waiting on */
*/
bool Write(CCECAdapterMessage *msg);
+ bool ProvidesExtendedResponse(void);
+
virtual void *Process(void);
private:
bool CCECBusDevice::ReplaceHandler(bool bActivateSource /* = true */)
{
+ if (m_iLogicalAddress == CECDEVICE_BROADCAST)
+ return false;
+
bool bInitHandler(false);
{
CLockObject lock(m_mutex);
bool m_bReplaceHandler;
cec_menu_state m_menuState;
bool m_bActiveSource;
- uint64_t m_iLastActive;
- uint64_t m_iLastPowerStateUpdate;
+ int64_t m_iLastActive;
+ int64_t m_iLastPowerStateUpdate;
cec_version m_cecVersion;
cec_bus_device_status m_deviceStatus;
std::set<cec_opcode> m_unsupportedFeatures;
LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
}
-
- // clear previous pending active source command
- m_iActiveSourcePending = 0;
}
// update the power state and menu state
m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
- m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); // TODO: LG
+ m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED);
+
+ // vendor specific hook
+ VendorPreActivateSourceHook();
// power on the TV
- bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
+ bool bActiveSourceFailed(false);
+ if (m_processor->GetDevice(CECDEVICE_TV)->GetPowerStatus(m_busDevice->GetLogicalAddress()) != CEC_POWER_STATUS_ON)
+ bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
// check if we're allowed to switch sources
bool bSourceSwitchAllowed = SourceSwitchAllowed();
{
LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
CLockObject lock(m_mutex);
- m_iActiveSourcePending = GetTimeMs() + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
+ if (m_iActiveSourcePending == 0)
+ m_iActiveSourcePending = GetTimeMs() + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
return false;
}
+ else
+ {
+ CLockObject lock(m_mutex);
+ // clear previous pending active source command
+ m_iActiveSourcePending = 0;
+ }
// mark the handler as initialised
CLockObject lock(m_mutex);
virtual int HandleVendorRemoteButtonUp(const cec_command & UNUSED(command)) { return CEC_ABORT_REASON_REFUSED; }
virtual void UnhandledCommand(const cec_command &command, const cec_abort_reason reason);
+ virtual void VendorPreActivateSourceHook(void) {};
+
virtual size_t GetMyDevices(std::vector<CCECBusDevice *> &devices) const;
virtual CCECBusDevice *GetDevice(cec_logical_address iLogicalAddress) const;
virtual CCECBusDevice *GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const;
return CCECCommandHandler::PowerOn(iInitiator, iDestination);
}
+
+void CSLCommandHandler::VendorPreActivateSourceHook(void)
+{
+ CCECPlaybackDevice *device = m_busDevice->AsPlaybackDevice();
+ if (device)
+ device->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG);
+}
void SetSLInitialised(void);
bool ActiveSourceSent(void);
+ void VendorPreActivateSourceHook(void);
+
bool m_bSLEnabled;
bool m_bActiveSourceSent;
PLATFORM::CTimeout m_resetPowerState;
#define ToString(p) LIB_CEC->ToString(p)
// wait this amount of ms before trying to switch sources after receiving the message from the TV that it's powered on
-#define SOURCE_SWITCH_DELAY_MS 1000
+#define SOURCE_SWITCH_DELAY_MS 3000
CVLCommandHandler::CVLCommandHandler(CCECBusDevice *busDevice,
int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */,
int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */,
int64_t iActiveSourcePending /* = 0 */) :
CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending),
- m_iPowerUpEventReceived(0)
+ m_iPowerUpEventReceived(0),
+ m_bCapabilitiesSent(false)
{
m_vendorId = CEC_VENDOR_PANASONIC;
}
if (primary->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE)
return m_processor->GetPrimaryClient()->ChangeDeviceType(CEC_DEVICE_TYPE_RECORDING_DEVICE, CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
-
- m_processor->GetTV()->RequestPowerStatus(primary->GetLogicalAddress(), false);
}
return CCECCommandHandler::InitHandler();
command.parameters[2] != 0x45)
return CEC_ABORT_REASON_INVALID_OPERAND;
+ // XXX this is also sent when the TV is powered off
+#if 0
if (command.initiator == CECDEVICE_TV &&
command.parameters.At(3) == VL_UNKNOWN1)
{
// mark the TV as powered on
m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON);
}
- else if (command.initiator == CECDEVICE_TV &&
+ else
+#endif
+ if (command.initiator == CECDEVICE_TV &&
command.destination == CECDEVICE_BROADCAST &&
command.parameters.At(3) == VL_POWER_CHANGE)
{
}
// mark the TV as powered on
m_processor->GetTV()->SetPowerStatus(CEC_POWER_STATUS_ON);
+
+ // send capabilties
+ SendVendorCommandCapabilities(m_processor->GetLogicalAddress(), command.initiator);
}
else if (command.parameters.At(4) == VL_POWERED_DOWN)
{
return CCECCommandHandler::HandleStandby(command);
}
+void CVLCommandHandler::VendorPreActivateSourceHook(void)
+{
+ bool bTransmit(false);
+ {
+ CLockObject lock(m_mutex);
+ bTransmit = m_bCapabilitiesSent;
+ }
+ if (bTransmit)
+ SendVendorCommandCapabilities(m_processor->GetLogicalAddress(), CECDEVICE_TV);
+}
+
+void CVLCommandHandler::SendVendorCommandCapabilities(const cec_logical_address initiator, const cec_logical_address destination)
+{
+ cec_command response;
+ cec_command::Format(response, initiator, destination, CEC_OPCODE_VENDOR_COMMAND);
+ uint8_t iResponseData[] = {0x10, 0x02, 0xFF, 0xFF, 0x00, 0x05, 0x05, 0x45, 0x55, 0x5c, 0x58, 0x32};
+ response.PushArray(12, iResponseData);
+
+ if (Transmit(response, false, true))
+ {
+ if (PowerUpEventReceived())
+ {
+ CLockObject lock(m_mutex);
+ m_bCapabilitiesSent = true;
+ }
+ }
+}
+
int CVLCommandHandler::HandleVendorCommand(const cec_command &command)
{
// some vendor command voodoo that will enable more buttons on the remote
command.parameters[1] == 0x01 &&
command.parameters[2] == 0x05)
{
- cec_command response;
- cec_command::Format(response, command.destination, command.initiator, CEC_OPCODE_VENDOR_COMMAND);
- uint8_t iResponseData[] = {0x10, 0x02, 0xFF, 0xFF, 0x00, 0x05, 0x05, 0x45, 0x55, 0x5c, 0x58, 0x32};
- response.PushArray(12, iResponseData);
-
- Transmit(response, false, true);
-
+ SendVendorCommandCapabilities(m_processor->GetLogicalAddress(), command.initiator);
return COMMAND_HANDLED;
}
return CCECCommandHandler::HandleSystemAudioModeRequest(command);
}
+
+int CVLCommandHandler::HandleReportPowerStatus(const cec_command &command)
+{
+ if (command.initiator == m_busDevice->GetLogicalAddress() &&
+ command.parameters.size == 1 &&
+ (cec_power_status)command.parameters[0] == CEC_POWER_STATUS_ON)
+ {
+ CLockObject lock(m_mutex);
+ if (m_iPowerUpEventReceived == 0)
+ m_iPowerUpEventReceived = GetTimeMs();
+ }
+
+ return CCECCommandHandler::HandleReportPowerStatus(command);
+}
bool SourceSwitchAllowed(void);
- private:
+ protected:
+ void VendorPreActivateSourceHook(void);
+ void SendVendorCommandCapabilities(const cec_logical_address initiator, const cec_logical_address destination);
+ int HandleReportPowerStatus(const cec_command &command);
+
PLATFORM::CMutex m_mutex;
uint64_t m_iPowerUpEventReceived;
+ bool m_bCapabilitiesSent;
};
};
ssize_t CSerialSocket::Write(void* data, size_t len)
{
- return IsOpen() ? SerialSocketWrite(m_socket, &m_iError, data, len) : -1;
+ if (IsOpen())
+ {
+ ssize_t iReturn = SerialSocketWrite(m_socket, &m_iError, data, len);
+ if (iReturn != len)
+ {
+ m_strError = "unable to write to the serial port";
+ FormatWindowsError(GetLastError(), m_strError);
+ }
+ return iReturn;
+ }
+ return -1;
}
ssize_t CSerialSocket::Read(void* data, size_t len, uint64_t iTimeoutMs /* = 0 */)
return false;
}
+ m_strError.clear();
m_bIsOpen = true;
return m_bIsOpen;
}
using namespace std;
using namespace PLATFORM;
-#define CEC_CONFIG_VERSION CEC_CLIENT_VERSION_1_8_0;
+#define CEC_CONFIG_VERSION CEC_CLIENT_VERSION_1_8_1;
#include <cecloader.h>