2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
5 * libCEC(R) is an original work, containing original code.
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
9 * This program is dual-licensed; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
27 * For more information contact:
28 * Pulse-Eight Licensing <license@pulse-eight.com>
29 * http://www.pulse-eight.com/
30 * http://www.pulse-eight.net/
35 #if defined(HAVE_RPI_API)
36 #include "RPiCECAdapterCommunication.h"
42 #include "lib/CECTypeUtils.h"
43 #include "lib/LibCEC.h"
44 #include "lib/platform/util/StdString.h"
45 #include "RPiCECAdapterMessageQueue.h"
48 using namespace PLATFORM
;
50 #define LIB_CEC m_callback->GetLib()
52 static bool g_bHostInited
= false;
54 // callback for the RPi CEC service
55 void rpi_cec_callback(void *callback_data
, uint32_t p0
, uint32_t p1
, uint32_t p2
, uint32_t p3
, uint32_t p4
)
58 static_cast<CRPiCECAdapterCommunication
*>(callback_data
)->OnDataReceived(p0
, p1
, p2
, p3
, p4
);
61 // callback for the TV service
62 void rpi_tv_callback(void *callback_data
, uint32_t reason
, uint32_t p0
, uint32_t p1
)
65 static_cast<CRPiCECAdapterCommunication
*>(callback_data
)->OnTVServiceCallback(reason
, p0
, p1
);
68 CRPiCECAdapterCommunication::CRPiCECAdapterCommunication(IAdapterCommunicationCallback
*callback
) :
69 IAdapterCommunication(callback
),
70 m_logicalAddress(CECDEVICE_UNKNOWN
),
71 m_bLogicalAddressChanged(false),
72 m_previousLogicalAddress(CECDEVICE_FREEUSE
),
73 m_bLogicalAddressRegistered(false)
75 m_queue
= new CRPiCECAdapterMessageQueue(this);
78 CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void)
81 UnregisterLogicalAddress();
83 vc_cec_set_passive(false);
86 const char *ToString(const VC_CEC_ERROR_T error
)
92 case VC_CEC_ERROR_NO_ACK
:
94 case VC_CEC_ERROR_SHUTDOWN
:
96 case VC_CEC_ERROR_BUSY
:
97 return "device is busy";
98 case VC_CEC_ERROR_NO_LA
:
99 return "no logical address";
100 case VC_CEC_ERROR_NO_PA
:
101 return "no physical address";
102 case VC_CEC_ERROR_NO_TOPO
:
103 return "no topology";
104 case VC_CEC_ERROR_INVALID_FOLLOWER
:
105 return "invalid follower";
106 case VC_CEC_ERROR_INVALID_ARGUMENT
:
107 return "invalid arg";
113 bool CRPiCECAdapterCommunication::IsInitialised(void)
115 CLockObject
lock(m_mutex
);
116 return m_bInitialised
;
119 void CRPiCECAdapterCommunication::OnTVServiceCallback(uint32_t reason
, uint32_t UNUSED(p0
), uint32_t UNUSED(p1
))
123 case VC_HDMI_ATTACHED
:
125 uint16_t iNewAddress
= GetPhysicalAddress();
126 m_callback
->HandlePhysicalAddressChanged(iNewAddress
);
129 case VC_HDMI_UNPLUGGED
:
132 case VC_HDMI_HDCP_UNAUTH
:
133 case VC_HDMI_HDCP_AUTH
:
134 case VC_HDMI_HDCP_KEY_DOWNLOAD
:
135 case VC_HDMI_HDCP_SRM_DOWNLOAD
:
141 void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header
, uint32_t p0
, uint32_t p1
, uint32_t p2
, uint32_t p3
)
143 VC_CEC_NOTIFY_T reason
= (VC_CEC_NOTIFY_T
)CEC_CB_REASON(header
);
146 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "received data: header:%08X p0:%08X p1:%08X p2:%08X p3:%08X reason:%x", header
, p0
, p1
, p2
, p3
, reason
);
154 // translate into a VC_CEC_MESSAGE_T
155 VC_CEC_MESSAGE_T message
;
156 vc_cec_param2message(header
, p0
, p1
, p2
, p3
, &message
);
158 // translate to a cec_command
160 cec_command::Format(command
,
161 (cec_logical_address
)message
.initiator
,
162 (cec_logical_address
)message
.follower
,
163 (cec_opcode
)CEC_CB_OPCODE(p0
));
166 for (uint8_t iPtr
= 1; iPtr
< message
.length
; iPtr
++)
167 command
.PushBack(message
.payload
[iPtr
]);
170 m_callback
->OnCommandReceived(command
);
175 // handle response to a command that was sent earlier
176 m_queue
->MessageReceived((cec_opcode
)CEC_CB_OPCODE(p0
), (cec_logical_address
)CEC_CB_INITIATOR(p0
), (cec_logical_address
)CEC_CB_FOLLOWER(p0
), CEC_CB_RC(header
));
179 case VC_CEC_BUTTON_PRESSED
:
180 case VC_CEC_REMOTE_PRESSED
:
182 // translate into a cec_command
184 cec_command::Format(command
,
185 (cec_logical_address
)CEC_CB_INITIATOR(p0
),
186 (cec_logical_address
)CEC_CB_FOLLOWER(p0
),
187 reason
== VC_CEC_BUTTON_PRESSED
? CEC_OPCODE_USER_CONTROL_PRESSED
: CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN
);
188 command
.parameters
.PushBack((uint8_t)CEC_CB_OPERAND1(p0
));
191 m_callback
->OnCommandReceived(command
);
194 case VC_CEC_BUTTON_RELEASE
:
195 case VC_CEC_REMOTE_RELEASE
:
197 // translate into a cec_command
199 cec_command::Format(command
,
200 (cec_logical_address
)CEC_CB_INITIATOR(p0
),
201 (cec_logical_address
)CEC_CB_FOLLOWER(p0
),
202 reason
== VC_CEC_BUTTON_PRESSED
? CEC_OPCODE_USER_CONTROL_RELEASE
: CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP
);
203 command
.parameters
.PushBack((uint8_t)CEC_CB_OPERAND1(p0
));
206 m_callback
->OnCommandReceived(command
);
209 case VC_CEC_LOGICAL_ADDR
:
211 CLockObject
lock(m_mutex
);
212 m_previousLogicalAddress
= m_logicalAddress
;
213 if (CEC_CB_RC(header
) == VCHIQ_SUCCESS
)
215 m_bLogicalAddressChanged
= true;
216 m_logicalAddress
= (cec_logical_address
)(p0
& 0xF);
217 m_bLogicalAddressRegistered
= true;
218 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "logical address changed to %s (%x)", LIB_CEC
->ToString(m_logicalAddress
), m_logicalAddress
);
222 m_logicalAddress
= CECDEVICE_FREEUSE
;
223 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "failed to change the logical address, reset to %s (%x)", LIB_CEC
->ToString(m_logicalAddress
), m_logicalAddress
);
225 m_logicalAddressCondition
.Signal();
228 case VC_CEC_LOGICAL_ADDR_LOST
:
230 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "logical %s (%x) address lost", LIB_CEC
->ToString(m_logicalAddress
), m_logicalAddress
);
231 // the logical address was taken by another device
232 cec_logical_address previousAddress
= m_logicalAddress
== CECDEVICE_FREEUSE
? m_previousLogicalAddress
: m_logicalAddress
;
233 m_logicalAddress
= CECDEVICE_UNKNOWN
;
235 // notify libCEC that we lost our LA when the connection was initialised
238 CLockObject
lock(m_mutex
);
239 bNotify
= m_bInitialised
&& m_bLogicalAddressRegistered
;
242 m_callback
->HandleLogicalAddressLost(previousAddress
);
245 case VC_CEC_TOPOLOGY
:
248 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "ignoring unknown reason %x", reason
);
253 int CRPiCECAdapterCommunication::InitHostCEC(void)
255 VCHIQ_INSTANCE_T vchiq_instance
;
258 if ((iResult
= vchiq_initialise(&vchiq_instance
)) != VCHIQ_SUCCESS
)
260 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchiq_initialise failed (%d)", __FUNCTION__
, iResult
);
262 strError
.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__
, iResult
);
263 m_strError
= strError
;
266 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchiq_initialise succeeded", __FUNCTION__
);
268 if ((iResult
= vchi_initialise(&m_vchi_instance
)) != VCHIQ_SUCCESS
)
270 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchi_initialise failed (%d)", __FUNCTION__
, iResult
);
272 strError
.Format("%s - vchi_initialise failed (%d)", __FUNCTION__
, iResult
);
273 m_strError
= strError
;
276 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchi_initialise succeeded", __FUNCTION__
);
278 vchiq_instance
= (VCHIQ_INSTANCE_T
)m_vchi_instance
;
280 m_vchi_connection
= vchi_create_connection(single_get_func_table(),
281 vchi_mphi_message_driver_func_table());
283 if ((iResult
= vchi_connect(&m_vchi_connection
, 1, m_vchi_instance
)) != VCHIQ_SUCCESS
)
285 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchi_connect failed (%d)", __FUNCTION__
, iResult
);
287 strError
.Format("%s - vchi_connect failed (%d)", __FUNCTION__
, iResult
);
288 m_strError
= strError
;
291 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchi_connect succeeded", __FUNCTION__
);
293 return VCHIQ_SUCCESS
;
296 bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs
/* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks
) /* = false */, bool bStartListening
)
300 if (InitHostCEC() != VCHIQ_SUCCESS
)
305 // enable passive mode
306 vc_cec_set_passive(true);
308 // register the callbacks
309 vc_cec_register_callback(rpi_cec_callback
, (void*)this);
310 vc_tv_register_callback(rpi_tv_callback
, (void*)this);
312 // register LA "freeuse"
313 if (RegisterLogicalAddress(CECDEVICE_FREEUSE
, iTimeoutMs
))
315 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vc_cec initialised", __FUNCTION__
);
316 CLockObject
lock(m_mutex
);
317 m_bInitialised
= true;
321 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vc_cec could not be initialised", __FUNCTION__
);
329 uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
331 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS
);
332 if (!IsInitialised())
335 if (vc_cec_get_physical_address(&iPA
) == VCHIQ_SUCCESS
)
336 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - physical address = %04x", __FUNCTION__
, iPA
);
339 LIB_CEC
->AddLog(CEC_LOG_WARNING
, "%s - failed to get the physical address", __FUNCTION__
);
340 iPA
= CEC_INVALID_PHYSICAL_ADDRESS
;
346 void CRPiCECAdapterCommunication::Close(void)
348 if (m_bInitialised
) {
349 vc_tv_unregister_callback(rpi_tv_callback
);
350 m_bInitialised
= false;
355 g_bHostInited
= false;
360 std::string
CRPiCECAdapterCommunication::GetError(void) const
362 std::string
strError(m_strError
);
366 cec_adapter_message_state
CRPiCECAdapterCommunication::Write(const cec_command
&data
, bool &bRetry
, uint8_t iLineTimeout
, bool bIsReply
)
368 VC_CEC_ERROR_T vcAnswer
;
369 uint32_t iTimeout
= (data
.transmit_timeout
? data
.transmit_timeout
: iLineTimeout
*1000);
371 cec_adapter_message_state rc
= m_queue
->Write(data
, bRetry
, iTimeout
, bIsReply
, vcAnswer
);
373 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "sending data: result %s", ToString(vcAnswer
));
378 uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
380 return VC_CECSERVICE_VER
;
383 cec_logical_address
CRPiCECAdapterCommunication::GetLogicalAddress(void)
385 CLockObject
lock(m_mutex
);
387 return m_logicalAddress
;
390 bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
392 CLockObject
lock(m_mutex
);
396 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - releasing previous logical address", __FUNCTION__
);
398 CLockObject
lock(m_mutex
);
399 m_bLogicalAddressRegistered
= false;
400 m_bLogicalAddressChanged
= false;
403 vc_cec_release_logical_address();
405 return m_logicalAddressCondition
.Wait(m_mutex
, m_bLogicalAddressChanged
);
408 bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address
, uint32_t iTimeoutMs
)
411 CLockObject
lock(m_mutex
);
412 if ((m_logicalAddress
== address
) && m_bLogicalAddressRegistered
)
416 m_bLogicalAddressChanged
= false;
418 // register the new LA
419 int iRetval
= vc_cec_set_logical_address((CEC_AllDevices_T
)address
, (CEC_DEVICE_TYPE_T
)CCECTypeUtils::GetType(address
), CEC_VENDOR_ID_BROADCOM
);
420 if (iRetval
!= VCHIQ_SUCCESS
)
422 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vc_cec_set_logical_address(%X) returned %s (%d)", __FUNCTION__
, address
, ToString((VC_CEC_ERROR_T
)iRetval
), iRetval
);
423 UnregisterLogicalAddress();
425 else if (m_logicalAddressCondition
.Wait(m_mutex
, m_bLogicalAddressChanged
, iTimeoutMs
))
432 cec_logical_addresses
CRPiCECAdapterCommunication::GetLogicalAddresses(void)
434 cec_logical_addresses addresses
; addresses
.Clear();
435 if (m_bLogicalAddressRegistered
)
436 addresses
.primary
= GetLogicalAddress();
441 bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses
&addresses
)
443 // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered
444 return SupportsSourceLogicalAddress(addresses
.primary
) &&
445 RegisterLogicalAddress(addresses
.primary
);
448 void CRPiCECAdapterCommunication::InitHost(void)
452 g_bHostInited
= true;