2 * This file is part of the libCEC(R) library.
4 * libCEC(R) is Copyright (C) 2011-2012 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 CRPiCECAdapterFindNewLogicalAddress::CRPiCECAdapterFindNewLogicalAddress(CRPiCECAdapterCommunication
* communication
, const cec_logical_address address
) :
62 m_communication(communication
),
63 m_address(address
) { }
65 void *CRPiCECAdapterFindNewLogicalAddress::Process(void)
67 cec_logical_address
newAddress(CECDEVICE_UNKNOWN
);
68 for (unsigned int iLA
= CECDEVICE_RECORDINGDEVICE1
; newAddress
== CECDEVICE_UNKNOWN
&& iLA
< CECDEVICE_BROADCAST
; iLA
++)
70 if (CCECTypeUtils::GetType((cec_logical_address
)iLA
) == CCECTypeUtils::GetType(m_address
) &&
71 m_communication
->SupportsSourceLogicalAddress((cec_logical_address
)iLA
) &&
72 m_communication
->RegisterLogicalAddress((cec_logical_address
)iLA
))
73 newAddress
= (cec_logical_address
)iLA
;
76 m_communication
->m_callback
->HandleLogicalAddressLost(m_address
, newAddress
);
80 CRPiCECAdapterCommunication::CRPiCECAdapterCommunication(IAdapterCommunicationCallback
*callback
) :
81 IAdapterCommunication(callback
),
82 m_logicalAddress(CECDEVICE_UNKNOWN
),
83 m_bLogicalAddressChanged(false),
86 m_queue
= new CRPiCECAdapterMessageQueue(this);
89 CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void)
93 m_laLost
->StopThread();
101 const char *ToString(const VC_CEC_ERROR_T error
)
107 case VC_CEC_ERROR_NO_ACK
:
109 case VC_CEC_ERROR_SHUTDOWN
:
111 case VC_CEC_ERROR_BUSY
:
112 return "device is busy";
113 case VC_CEC_ERROR_NO_LA
:
114 return "no logical address";
115 case VC_CEC_ERROR_NO_PA
:
116 return "no physical address";
117 case VC_CEC_ERROR_NO_TOPO
:
118 return "no topology";
119 case VC_CEC_ERROR_INVALID_FOLLOWER
:
120 return "invalid follower";
121 case VC_CEC_ERROR_INVALID_ARGUMENT
:
122 return "invalid arg";
128 bool CRPiCECAdapterCommunication::IsInitialised(void)
130 CLockObject
lock(m_mutex
);
131 return m_bInitialised
;
134 void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header
, uint32_t p0
, uint32_t p1
, uint32_t p2
, uint32_t p3
)
136 VC_CEC_NOTIFY_T reason
= (VC_CEC_NOTIFY_T
)CEC_CB_REASON(header
);
138 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
);
145 // translate into a VC_CEC_MESSAGE_T
146 VC_CEC_MESSAGE_T message
;
147 vc_cec_param2message(header
, p0
, p1
, p2
, p3
, &message
);
149 // translate to a cec_command
151 cec_command::Format(command
,
152 (cec_logical_address
)message
.initiator
,
153 (cec_logical_address
)message
.follower
,
154 (cec_opcode
)CEC_CB_OPCODE(p0
));
157 for (uint8_t iPtr
= 1; iPtr
< message
.length
; iPtr
++)
158 command
.PushBack(message
.payload
[iPtr
]);
161 m_callback
->OnCommandReceived(command
);
166 // handle response to a command that was sent earlier
167 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
));
170 case VC_CEC_BUTTON_PRESSED
:
172 // translate into a cec_command
174 cec_command::Format(command
, (cec_logical_address
)CEC_CB_INITIATOR(p0
), (cec_logical_address
)CEC_CB_FOLLOWER(p0
), CEC_OPCODE_USER_CONTROL_PRESSED
);
175 command
.parameters
.PushBack((uint8_t)CEC_CB_OPERAND1(p0
));
178 m_callback
->OnCommandReceived(command
);
181 case VC_CEC_BUTTON_RELEASE
:
183 // translate into a cec_command
185 cec_command::Format(command
, (cec_logical_address
)CEC_CB_INITIATOR(p0
), (cec_logical_address
)CEC_CB_FOLLOWER(p0
), CEC_OPCODE_USER_CONTROL_RELEASE
);
186 command
.parameters
.PushBack((uint8_t)CEC_CB_OPERAND1(p0
));
189 m_callback
->OnCommandReceived(command
);
192 case VC_CEC_LOGICAL_ADDR
:
194 CLockObject
lock(m_mutex
);
195 if (CEC_CB_RC(header
) == VCHIQ_SUCCESS
)
197 m_bLogicalAddressChanged
= true;
198 m_logicalAddress
= (cec_logical_address
)(p0
& 0xF);
199 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "logical address changed to %s (%x)", LIB_CEC
->ToString(m_logicalAddress
), m_logicalAddress
);
203 m_logicalAddress
= CECDEVICE_BROADCAST
;
204 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "failed to change the logical address, reset to %s (%x)", LIB_CEC
->ToString(m_logicalAddress
), m_logicalAddress
);
206 m_logicalAddressCondition
.Signal();
209 case VC_CEC_LOGICAL_ADDR_LOST
:
211 // the logical address was taken by another device
212 cec_logical_address previousAddress
= m_logicalAddress
;
213 m_logicalAddress
= CECDEVICE_UNKNOWN
;
215 if (m_laLost
&& !m_laLost
->IsRunning())
217 m_laLost
= new CRPiCECAdapterFindNewLogicalAddress(this, previousAddress
);
219 m_laLost
->CreateThread();
222 case VC_CEC_TOPOLOGY
:
223 case VC_CEC_REMOTE_PRESSED
:
224 case VC_CEC_REMOTE_RELEASE
:
227 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "ignoring unknown reason %x", reason
);
232 int CRPiCECAdapterCommunication::InitHostCEC(void)
234 VCHIQ_INSTANCE_T vchiq_instance
;
237 if ((iResult
= vchiq_initialise(&vchiq_instance
)) != VCHIQ_SUCCESS
)
239 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchiq_initialise failed (%d)", __FUNCTION__
, iResult
);
241 strError
.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__
, iResult
);
242 m_strError
= strError
;
245 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchiq_initialise succeeded", __FUNCTION__
);
247 if ((iResult
= vchi_initialise(&m_vchi_instance
)) != VCHIQ_SUCCESS
)
249 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchi_initialise failed (%d)", __FUNCTION__
, iResult
);
251 strError
.Format("%s - vchi_initialise failed (%d)", __FUNCTION__
, iResult
);
252 m_strError
= strError
;
255 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchi_initialise succeeded", __FUNCTION__
);
257 vchiq_instance
= (VCHIQ_INSTANCE_T
)m_vchi_instance
;
259 m_vchi_connection
= vchi_create_connection(single_get_func_table(),
260 vchi_mphi_message_driver_func_table());
262 if ((iResult
= vchi_connect(&m_vchi_connection
, 1, m_vchi_instance
)) != VCHIQ_SUCCESS
)
264 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vchi_connect failed (%d)", __FUNCTION__
, iResult
);
266 strError
.Format("%s - vchi_connect failed (%d)", __FUNCTION__
, iResult
);
267 m_strError
= strError
;
270 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vchi_connect succeeded", __FUNCTION__
);
272 return VCHIQ_SUCCESS
;
275 bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs
/* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks
) /* = false */, bool bStartListening
)
279 if (InitHostCEC() != VCHIQ_SUCCESS
)
284 // enable passive mode
285 vc_cec_set_passive(true);
287 // register the callback
288 vc_cec_register_callback(((CECSERVICE_CALLBACK_T
)rpi_cec_callback
), (void*)this);
290 // release previous LA
291 vc_cec_release_logical_address();
292 if (!m_logicalAddressCondition
.Wait(m_mutex
, m_bLogicalAddressChanged
, iTimeoutMs
))
294 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "failed to release the previous LA");
298 // register LA "freeuse"
299 if (RegisterLogicalAddress(CECDEVICE_FREEUSE
))
301 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - vc_cec initialised", __FUNCTION__
);
302 CLockObject
lock(m_mutex
);
303 m_bInitialised
= true;
306 LIB_CEC
->AddLog(CEC_LOG_ERROR
, "%s - vc_cec could not be initialised", __FUNCTION__
);
312 uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
314 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS
);
315 if (!IsInitialised())
318 if (vc_cec_get_physical_address(&iPA
) == VCHIQ_SUCCESS
)
319 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - physical address = %04x", __FUNCTION__
, iPA
);
322 LIB_CEC
->AddLog(CEC_LOG_WARNING
, "%s - failed to get the physical address", __FUNCTION__
);
323 iPA
= CEC_INVALID_PHYSICAL_ADDRESS
;
329 void CRPiCECAdapterCommunication::Close(void)
332 CLockObject
lock(m_mutex
);
334 m_bInitialised
= false;
338 UnregisterLogicalAddress();
340 // disable passive mode
341 vc_cec_set_passive(false);
345 g_bHostInited
= false;
350 std::string
CRPiCECAdapterCommunication::GetError(void) const
352 std::string
strError(m_strError
);
356 cec_adapter_message_state
CRPiCECAdapterCommunication::Write(const cec_command
&data
, bool &UNUSED(bRetry
), uint8_t UNUSED(iLineTimeout
), bool bIsReply
)
358 // ensure that the source LA is registered
359 if (!RegisterLogicalAddress(data
.initiator
))
361 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "failed to register logical address %s (%X)", CCECTypeUtils::ToString(data
.initiator
), data
.initiator
);
362 return (data
.initiator
== data
.destination
) ? ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED
: ADAPTER_MESSAGE_STATE_ERROR
;
365 if (!data
.opcode_set
&& data
.initiator
== data
.destination
)
367 // registration of the logical address would have failed
368 return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED
;
371 return m_queue
->Write(data
, bIsReply
) ? ADAPTER_MESSAGE_STATE_SENT_ACKED
: ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED
;
374 uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
376 return VC_CECSERVICE_VER
;
379 cec_logical_address
CRPiCECAdapterCommunication::GetLogicalAddress(void)
382 CLockObject
lock(m_mutex
);
383 if (m_logicalAddress
!= CECDEVICE_UNKNOWN
)
384 return m_logicalAddress
;
387 CEC_AllDevices_T address
;
388 return (vc_cec_get_logical_address(&address
) == VCHIQ_SUCCESS
) ?
389 (cec_logical_address
)address
: CECDEVICE_UNKNOWN
;
392 bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
394 CLockObject
lock(m_mutex
);
395 if (m_logicalAddress
== CECDEVICE_UNKNOWN
||
396 m_logicalAddress
== CECDEVICE_BROADCAST
)
399 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - releasing previous logical address", __FUNCTION__
);
400 m_bLogicalAddressChanged
= false;
402 vc_cec_release_logical_address();
404 return m_logicalAddressCondition
.Wait(m_mutex
, m_bLogicalAddressChanged
);
407 bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address
)
410 CLockObject
lock(m_mutex
);
411 if (m_logicalAddress
== address
)
415 if (!UnregisterLogicalAddress())
418 LIB_CEC
->AddLog(CEC_LOG_DEBUG
, "%s - registering address %x", __FUNCTION__
, address
);
420 CLockObject
lock(m_mutex
);
421 m_bLogicalAddressChanged
= false;
422 vc_cec_poll_address((CEC_AllDevices_T
)address
);
424 // register the new LA
425 int iRetval
= vc_cec_set_logical_address((CEC_AllDevices_T
)address
, (CEC_DEVICE_TYPE_T
)CCECTypeUtils::GetType(address
), CEC_VENDOR_ID_BROADCOM
);
426 if (iRetval
!= VCHIQ_SUCCESS
)
428 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
);
432 return m_logicalAddressCondition
.Wait(m_mutex
, m_bLogicalAddressChanged
);
435 cec_logical_addresses
CRPiCECAdapterCommunication::GetLogicalAddresses(void)
437 cec_logical_addresses addresses
; addresses
.Clear();
438 cec_logical_address current
= GetLogicalAddress();
439 if (current
!= CECDEVICE_UNKNOWN
)
440 addresses
.Set(current
);
445 bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses
&addresses
)
447 // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered
448 return SupportsSourceLogicalAddress(addresses
.primary
) &&
449 RegisterLogicalAddress(addresses
.primary
);
452 void CRPiCECAdapterCommunication::InitHost(void)
456 g_bHostInited
= true;