X-Git-Url: https://git.piment-noir.org/?a=blobdiff_plain;f=src%2Flib%2FCECProcessor.cpp;h=df04a237edba78ba755928757ed72c6f0590a1bb;hb=5f2f3609d7ee857ac7e5d03600fbd735e99c892f;hp=435177b6e3bceaeb18119fac58e9d6794c2840b4;hpb=004b83822a351e1fb6e982a9183a12a430b0b769;p=deb_libcec.git diff --git a/src/lib/CECProcessor.cpp b/src/lib/CECProcessor.cpp index 435177b..df04a23 100644 --- a/src/lib/CECProcessor.cpp +++ b/src/lib/CECProcessor.cpp @@ -56,8 +56,6 @@ CCECProcessor::CCECProcessor(CLibCEC *libcec) : m_bInitialised(false), m_communication(NULL), m_libcec(libcec), - m_bMonitor(false), - m_iPreviousAckMask(0), m_iStandardLineTimeout(3), m_iRetryLineTimeout(3), m_iLastTransmission(0) @@ -74,57 +72,77 @@ CCECProcessor::~CCECProcessor(void) bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = CEC_SERIAL_DEFAULT_BAUDRATE */, uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */) { CLockObject lock(m_mutex); + // open a connection if (!OpenConnection(strPort, iBaudRate, iTimeoutMs)) return false; - /* create the processor thread */ + // create the processor thread if (!IsRunning()) { - if (CreateThread()) - m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); - else + if (!CreateThread()) { m_libcec->AddLog(CEC_LOG_ERROR, "could not create a processor thread"); return false; } } - SetCECInitialised(true); - return true; } void CCECProcessor::Close(void) { + // mark as uninitialised SetCECInitialised(false); + + // stop the processor StopThread(); + // close the connection + if (m_communication) + { + delete m_communication; + m_communication = NULL; + } +} + +void CCECProcessor::ResetMembers(void) +{ + // close the connection if (m_communication) { delete m_communication; m_communication = NULL; } + + // reset the other members to the initial state + m_iStandardLineTimeout = 3; + m_iRetryLineTimeout = 3; + m_iLastTransmission = 0; + m_busDevices->ResetDeviceStatus(); } bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs, bool bStartListening /* = true */) { bool bReturn(false); + CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); + + // ensure that a previous connection is closed Close(); + // reset all member to the initial state + ResetMembers(); + + // check whether the Close() method deleted any previous connection + if (m_communication) { - CLockObject lock(m_mutex); - if (m_communication && m_communication->IsOpen()) - { - m_libcec->AddLog(CEC_LOG_ERROR, "connection already opened"); - return true; - } - else if (!m_communication) - m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); + m_libcec->AddLog(CEC_LOG_ERROR, "previous connection could not be closed"); + return bReturn; } - CTimeout timeout(iTimeoutMs > 0 ? iTimeoutMs : CEC_DEFAULT_TRANSMIT_WAIT); + // create a new connection + m_communication = new CUSBCECAdapterCommunication(this, strPort, iBaudRate); - /* open a new connection */ + // open a new connection unsigned iConnectTry(0); while (timeout.TimeLeft() > 0 && (bReturn = m_communication->Open((timeout.TimeLeft() / CEC_CONNECT_TRIES), false, bStartListening)) == false) { @@ -135,6 +153,9 @@ bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint m_libcec->AddLog(CEC_LOG_NOTICE, "connection opened"); + // mark as initialised + SetCECInitialised(true); + return bReturn; } @@ -146,20 +167,25 @@ bool CCECProcessor::CECInitialised(void) void CCECProcessor::SetCECInitialised(bool bSetTo /* = true */) { - CLockObject lock(m_mutex); - m_bInitialised = bSetTo; + { + CLockObject lock(m_mutex); + m_bInitialised = bSetTo; + } if (!bSetTo) UnregisterClients(); } bool CCECProcessor::TryLogicalAddress(cec_logical_address address) { + // find the device CCECBusDevice *device = m_busDevices->At(address); if (device) { + // check if it's already marked as present or used if (device->IsPresent() || device->IsHandledByLibCEC()) return false; + // poll the LA if not SetAckMask(0); return device->TryLogicalAddress(); } @@ -172,6 +198,7 @@ void CCECProcessor::ReplaceHandlers(void) if (!CECInitialised()) return; + // check each device for (CECDEVICEMAP::iterator it = m_busDevices->Begin(); it != m_busDevices->End(); it++) it->second->ReplaceHandler(true); } @@ -186,29 +213,34 @@ void *CCECProcessor::Process(void) m_libcec->AddLog(CEC_LOG_DEBUG, "processor thread started"); cec_command command; - command.Clear(); + // as long as we're not being stopped and the connection is open while (!IsStopped() && m_communication->IsOpen()) { + // wait for a new incoming command, and process it if (m_inBuffer.Pop(command, CEC_PROCESSOR_SIGNAL_WAIT_TIME)) - ParseCommand(command); + ProcessCommand(command); if (CECInitialised()) { - ReplaceHandlers(); - + // check clients for keypress timeouts m_libcec->CheckKeypressTimeout(); + + // check if we need to replace handlers + ReplaceHandlers(); } } return NULL; } -bool CCECProcessor::SetActiveSource(uint16_t iStreamPath) +bool CCECProcessor::ActivateSource(uint16_t iStreamPath) { bool bReturn(false); + // find the device with the given PA CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath); + // and make it the active source when found if (device) bReturn = device->ActivateSource(); else @@ -223,12 +255,24 @@ void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout) m_iStandardLineTimeout = iTimeout; } +uint8_t CCECProcessor::GetStandardLineTimeout(void) +{ + CLockObject lock(m_mutex); + return m_iStandardLineTimeout; +} + void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout) { CLockObject lock(m_mutex); m_iRetryLineTimeout = iTimeout; } +uint8_t CCECProcessor::GetRetryLineTimeout(void) +{ + CLockObject lock(m_mutex); + return m_iRetryLineTimeout; +} + bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) { CCECBusDevice *device = GetDeviceByPhysicalAddress(iPhysicalAddress); @@ -238,41 +282,36 @@ bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress) void CCECProcessor::LogOutput(const cec_command &data) { CStdString strTx; + + // initiator and destination strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination); + + // append the opcode if (data.opcode_set) strTx.AppendFormat(":%02x", (uint8_t)data.opcode); + // append the parameters for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++) strTx.AppendFormat(":%02x", data.parameters[iPtr]); - m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); -} - -bool CCECProcessor::SwitchMonitoring(bool bEnable) -{ - m_libcec->AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling"); - - { - CLockObject lock(m_mutex); - m_bMonitor = bEnable; - m_iPreviousAckMask = m_communication->GetAckMask(); - } - if (bEnable) - return SetAckMask(0); - else - return SetAckMask(m_iPreviousAckMask); + // and log it + m_libcec->AddLog(CEC_LOG_TRAFFIC, strTx.c_str()); } bool CCECProcessor::PollDevice(cec_logical_address iAddress) { - CCECBusDevice *device = m_busDevices->At(iAddress); + // try to find the primary device CCECBusDevice *primary = GetPrimaryDevice(); + // poll the destination, with the primary as source + if (primary) + return primary->TransmitPoll(iAddress); + + // try to find the destination + CCECBusDevice *device = m_busDevices->At(iAddress); + // and poll the destination, with the same LA as source if (device) - { - return primary ? - primary->TransmitPoll(iAddress) : - device->TransmitPoll(iAddress); - } + return device->TransmitPoll(iAddress); + return false; } @@ -320,6 +359,19 @@ bool CCECProcessor::IsActiveSource(cec_logical_address iAddress) bool CCECProcessor::Transmit(const cec_command &data) { + uint8_t iMaxTries(0); + bool bRetry(true); + uint8_t iTries(0); + + // get the current timeout setting + uint8_t iLineTimeout(GetStandardLineTimeout()); + + // reset the state of this message to 'unknown' + cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; + + LogOutput(data); + + // find the initiator device CCECBusDevice *initiator = m_busDevices->At(data.initiator); if (!initiator) { @@ -327,42 +379,35 @@ bool CCECProcessor::Transmit(const cec_command &data) return false; } + // find the destination device, if it's not the broadcast address if (data.destination != CECDEVICE_BROADCAST) { + // check if the device is marked as handled by libCEC CCECBusDevice *destination = m_busDevices->At(data.destination); if (destination && destination->IsHandledByLibCEC()) { + // and reject the command if it's trying to send data to a device that is handled by libCEC m_libcec->AddLog(CEC_LOG_WARNING, "not sending data to myself!"); return false; } } - uint8_t iMaxTries(0); { CLockObject lock(m_mutex); - if (IsStopped()) - return false; - LogOutput(data); m_iLastTransmission = GetTimeMs(); - if (!m_communication || !m_communication->IsOpen()) - { - m_libcec->AddLog(CEC_LOG_ERROR, "cannot transmit command: connection closed"); - return false; - } + // set the number of tries iMaxTries = initiator->GetHandler()->GetTransmitRetries() + 1; } - bool bRetry(true); - uint8_t iTries(0); - uint8_t iLineTimeout = m_iStandardLineTimeout; - cec_adapter_message_state adapterState = ADAPTER_MESSAGE_STATE_UNKNOWN; - + // and try to send the command while (bRetry && ++iTries < iMaxTries) { if (initiator->IsUnsupportedFeature(data.opcode)) return false; - adapterState = m_communication->Write(data, bRetry, iLineTimeout); + adapterState = !IsStopped() && m_communication && m_communication->IsOpen() ? + m_communication->Write(data, bRetry, iLineTimeout) : + ADAPTER_MESSAGE_STATE_ERROR; iLineTimeout = m_iRetryLineTimeout; } @@ -381,8 +426,9 @@ void CCECProcessor::TransmitAbort(cec_logical_address source, cec_logical_addres Transmit(command); } -void CCECProcessor::ParseCommand(const cec_command &command) +void CCECProcessor::ProcessCommand(const cec_command &command) { + // log the command CStdString dataStr; dataStr.Format(">> %1x%1x", command.initiator, command.destination); if (command.opcode_set == 1) @@ -391,12 +437,11 @@ void CCECProcessor::ParseCommand(const cec_command &command) dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]); m_libcec->AddLog(CEC_LOG_TRAFFIC, dataStr.c_str()); - if (!m_bMonitor) - { - CCECBusDevice *device = m_busDevices->At(command.initiator); - if (device) - device->HandleCommand(command); - } + // find the initiator + CCECBusDevice *device = m_busDevices->At(command.initiator); + + if (device) + device->HandleCommand(command); } bool CCECProcessor::IsPresentDevice(cec_logical_address address) @@ -454,6 +499,7 @@ bool CCECProcessor::PowerOnDevice(const cec_logical_address initiator, cec_logic bool CCECProcessor::StartBootloader(const char *strPort /* = NULL */) { bool bReturn(false); + // open a connection if no connection has been opened if (!m_communication && strPort) { IAdapterCommunication *comm = new CUSBCECAdapterCommunication(this, strPort); @@ -511,7 +557,7 @@ bool CCECProcessor::CanPersistConfiguration(void) return m_communication ? m_communication->GetFirmwareVersion() >= 2 : false; } -bool CCECProcessor::PersistConfiguration(libcec_configuration *configuration) +bool CCECProcessor::PersistConfiguration(const libcec_configuration &configuration) { return m_communication ? m_communication->PersistConfiguration(configuration) : false; } @@ -573,16 +619,34 @@ bool CCECProcessor::RegisterClient(CCECClient *client) return false; libcec_configuration &configuration = *client->GetConfiguration(); + + if (configuration.clientVersion >= CEC_CLIENT_VERSION_1_6_3 && configuration.bMonitorOnly == 1) + return true; + + if (!CECInitialised()) + { + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register a new CEC client: CEC processor is not initialised"); + return false; + } + + // unregister the client first if it's already been marked as registered + if (client->IsRegistered()) + UnregisterClient(client); + + // get the configuration from the client m_libcec->AddLog(CEC_LOG_NOTICE, "registering new CEC client - v%s", ToString((cec_client_version)configuration.clientVersion)); + // mark as uninitialised and unregistered client->SetRegistered(false); client->SetInitialised(false); + // get the current ackmask, so we can restore it if polling fails uint16_t iPreviousMask(m_communication->GetAckMask()); // find logical addresses for this client - if (!client->FindLogicalAddresses()) + if (!client->AllocateLogicalAddresses()) { + m_libcec->AddLog(CEC_LOG_ERROR, "failed to register the new CEC client - cannot allocate the requested device types"); SetAckMask(iPreviousMask); return false; } @@ -592,17 +656,17 @@ bool CCECProcessor::RegisterClient(CCECClient *client) m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) { + // replace a previous client CLockObject lock(m_mutex); m_clients.erase((*it)->GetLogicalAddress()); m_clients.insert(make_pair((*it)->GetLogicalAddress(), client)); - client->SetRegistered(true); } // get the settings from the rom if (configuration.bGetSettingsFromROM == 1) { libcec_configuration config; - m_communication->GetConfiguration(&config); + m_communication->GetConfiguration(config); CLockObject lock(m_mutex); if (!config.deviceTypes.IsEmpty()) @@ -612,80 +676,79 @@ bool CCECProcessor::RegisterClient(CCECClient *client) snprintf(configuration.strDeviceName, 13, "%s", config.strDeviceName); } - // set the new ack mask - bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) && - client->Initialise(); - // set the firmware version and build date - configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); + configuration.serverVersion = LIBCEC_VERSION_CURRENT; + configuration.iFirmwareVersion = m_communication->GetFirmwareVersion(); configuration.iFirmwareBuildDate = m_communication->GetFirmwareBuildDate(); - CStdString strLog; - if (bReturn) - strLog = "CEC client registered."; - else - strLog = "failed to register the CEC client."; - strLog.AppendFormat(" libCEC version = %s, client version = %s, firmware version = %d", ToString((cec_server_version)configuration.serverVersion), ToString((cec_client_version)configuration.clientVersion), configuration.iFirmwareVersion); - if (configuration.iFirmwareBuildDate != CEC_FW_BUILD_UNKNOWN) - { - time_t buildTime = (time_t)configuration.iFirmwareBuildDate; - strLog.AppendFormat(", firmware build date: %s", asctime(gmtime(&buildTime))); - strLog = strLog.Left((int)strLog.length() - 1); // strip \n added by asctime - strLog.append(" +0000"); - } + // mark the client as registered + client->SetRegistered(true); - m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog); + // set the new ack mask + bool bReturn = SetAckMask(GetLogicalAddresses().AckMask()) && + // and initialise the client + client->OnRegister(); - if (bReturn) - { - strLog = "Using logical address(es): "; - CECDEVICEVEC devices; - m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); - for (CECDEVICEVEC::iterator it = devices.begin(); it != devices.end(); it++) - strLog.AppendFormat("%s (%X) ", (*it)->GetLogicalAddressName(), (*it)->GetLogicalAddress()); - m_libcec->AddLog(CEC_LOG_NOTICE, strLog); - } + // log the new registration + CStdString strLog; + strLog.Format("%s: %s", bReturn ? "CEC client registered" : "failed to register the CEC client", client->GetConnectionInfo().c_str()); + m_libcec->AddLog(bReturn ? CEC_LOG_NOTICE : CEC_LOG_ERROR, strLog); // display a warning if the firmware can be upgraded - if (!m_communication->IsRunningLatestFirmware()) + if (bReturn && !IsRunningLatestFirmware()) { const char *strUpgradeMessage = "The firmware of this adapter can be upgraded. Please visit http://blog.pulse-eight.com/ for more information."; m_libcec->AddLog(CEC_LOG_WARNING, strUpgradeMessage); - client->Alert(CEC_ALERT_SERVICE_DEVICE, libcec_parameter(strUpgradeMessage)); - } - else - { - m_libcec->AddLog(CEC_LOG_DEBUG, "the adapter is using the latest (known) firmware version"); - } - - if (bReturn) - { - /* get the vendor id from the TV, so we are using the correct handler */ - GetTV()->GetVendorId(configuration.logicalAddresses.primary); + libcec_parameter param; + param.paramData = (void*)strUpgradeMessage; param.paramType = CEC_PARAMETER_TYPE_STRING; + client->Alert(CEC_ALERT_SERVICE_DEVICE, param); } return bReturn; } -void CCECProcessor::UnregisterClient(CCECClient *client) +bool CCECProcessor::UnregisterClient(CCECClient *client) { - CLockObject lock(m_mutex); - libcec_configuration &configuration = *client->GetConfiguration(); - CECDEVICEVEC devices; - m_busDevices->GetByLogicalAddresses(devices, configuration.logicalAddresses); - for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + if (!client) + return false; + + if (client->IsRegistered()) + m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering client: %s", client->GetConnectionInfo().c_str()); + + // notify the client that it will be unregistered + client->OnUnregister(); + { - map::iterator entry = m_clients.find((*it)->GetLogicalAddress()); - if (entry != m_clients.end()) - m_clients.erase(entry); + CLockObject lock(m_mutex); + // find all devices that match the LA's of this client + CECDEVICEVEC devices; + m_busDevices->GetByLogicalAddresses(devices, client->GetConfiguration()->logicalAddresses); + for (CECDEVICEVEC::const_iterator it = devices.begin(); it != devices.end(); it++) + { + // find the client + map::iterator entry = m_clients.find((*it)->GetLogicalAddress()); + // unregister the client + if (entry != m_clients.end()) + m_clients.erase(entry); + + // reset the device status + (*it)->ResetDeviceStatus(); + } } + + // set the new ackmask + return SetAckMask(GetLogicalAddresses().AckMask()); } void CCECProcessor::UnregisterClients(void) { + m_libcec->AddLog(CEC_LOG_NOTICE, "unregistering all CEC clients"); + + vector clients = m_libcec->GetClients(); + for (vector::iterator client = clients.begin(); client != clients.end(); client++) + UnregisterClient(*client); + CLockObject lock(m_mutex); - for (map::iterator client = m_clients.begin(); client != m_clients.end(); client++) - client->second->OnUnregister(); m_clients.clear(); } @@ -707,20 +770,22 @@ CCECClient *CCECProcessor::GetPrimaryClient(void) return NULL; } -CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const +CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) { return m_busDevices->At(GetLogicalAddress()); } -cec_logical_address CCECProcessor::GetLogicalAddress(void) const +cec_logical_address CCECProcessor::GetLogicalAddress(void) { cec_logical_addresses addresses = GetLogicalAddresses(); return addresses.primary; } -cec_logical_addresses CCECProcessor::GetLogicalAddresses(void) const +cec_logical_addresses CCECProcessor::GetLogicalAddresses(void) { + CLockObject lock(m_mutex); cec_logical_addresses addresses; + addresses.Clear(); for (map::const_iterator client = m_clients.begin(); client != m_clients.end(); client++) addresses.Set(client->first); @@ -732,3 +797,10 @@ bool CCECProcessor::IsHandledByLibCEC(const cec_logical_address address) const CCECBusDevice *device = GetDevice(address); return device && device->IsHandledByLibCEC(); } + +bool CCECProcessor::IsRunningLatestFirmware(void) +{ + return m_communication && m_communication->IsOpen() ? + m_communication->IsRunningLatestFirmware() : + true; +}