fix assertion on XBMC exit
[deb_libcec.git] / src / lib / adapter / RPi / RPiCECAdapterCommunication.cpp
CommitLineData
29104708
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
16f47961 4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
29104708
LOK
5 * libCEC(R) is an original work, containing original code.
6 *
7 * libCEC(R) is a trademark of Pulse-Eight Limited.
8 *
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.
13 *
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.
18 *
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.
22 *
23 *
24 * Alternatively, you can license this library under a commercial license,
25 * please contact Pulse-Eight Licensing for more information.
26 *
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/
31 */
32
33#include "env.h"
34
35#if defined(HAVE_RPI_API)
36#include "RPiCECAdapterCommunication.h"
37
38extern "C" {
39#include <bcm_host.h>
40}
41
42#include "lib/CECTypeUtils.h"
43#include "lib/LibCEC.h"
44#include "lib/platform/util/StdString.h"
45#include "RPiCECAdapterMessageQueue.h"
46
47using namespace CEC;
48using namespace PLATFORM;
49
50#define LIB_CEC m_callback->GetLib()
51
52static bool g_bHostInited = false;
53
54// callback for the RPi CEC service
55void rpi_cec_callback(void *callback_data, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3, uint32_t p4)
56{
57 if (callback_data)
58 static_cast<CRPiCECAdapterCommunication *>(callback_data)->OnDataReceived(p0, p1, p2, p3, p4);
59}
60
49ba42d4
LOK
61// callback for the TV service
62void rpi_tv_callback(void *callback_data, uint32_t reason, uint32_t p0, uint32_t p1)
63{
64 if (callback_data)
65 static_cast<CRPiCECAdapterCommunication *>(callback_data)->OnTVServiceCallback(reason, p0, p1);
66}
67
29104708
LOK
68CRPiCECAdapterCommunication::CRPiCECAdapterCommunication(IAdapterCommunicationCallback *callback) :
69 IAdapterCommunication(callback),
70 m_logicalAddress(CECDEVICE_UNKNOWN),
d1a2ed70 71 m_bLogicalAddressChanged(false),
057497c6
LOK
72 m_previousLogicalAddress(CECDEVICE_FREEUSE),
73 m_bLogicalAddressRegistered(false)
29104708
LOK
74{
75 m_queue = new CRPiCECAdapterMessageQueue(this);
76}
77
78CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void)
79{
80 delete(m_queue);
81 Close();
82}
83
84const char *ToString(const VC_CEC_ERROR_T error)
85{
86 switch(error)
87 {
88 case VC_CEC_SUCCESS:
89 return "success";
90 case VC_CEC_ERROR_NO_ACK:
91 return "no ack";
92 case VC_CEC_ERROR_SHUTDOWN:
93 return "shutdown";
94 case VC_CEC_ERROR_BUSY:
95 return "device is busy";
96 case VC_CEC_ERROR_NO_LA:
97 return "no logical address";
98 case VC_CEC_ERROR_NO_PA:
99 return "no physical address";
100 case VC_CEC_ERROR_NO_TOPO:
101 return "no topology";
102 case VC_CEC_ERROR_INVALID_FOLLOWER:
103 return "invalid follower";
104 case VC_CEC_ERROR_INVALID_ARGUMENT:
105 return "invalid arg";
106 default:
107 return "unknown";
108 }
109}
110
111bool CRPiCECAdapterCommunication::IsInitialised(void)
112{
113 CLockObject lock(m_mutex);
114 return m_bInitialised;
115}
116
49ba42d4
LOK
117void CRPiCECAdapterCommunication::OnTVServiceCallback(uint32_t reason, uint32_t UNUSED(p0), uint32_t UNUSED(p1))
118{
119 switch(reason)
120 {
49ba42d4
LOK
121 case VC_HDMI_ATTACHED:
122 {
123 uint16_t iNewAddress = GetPhysicalAddress();
124 m_callback->HandlePhysicalAddressChanged(iNewAddress);
125 break;
126 }
c66fdf1f 127 case VC_HDMI_UNPLUGGED:
49ba42d4
LOK
128 case VC_HDMI_DVI:
129 case VC_HDMI_HDMI:
130 case VC_HDMI_HDCP_UNAUTH:
131 case VC_HDMI_HDCP_AUTH:
132 case VC_HDMI_HDCP_KEY_DOWNLOAD:
133 case VC_HDMI_HDCP_SRM_DOWNLOAD:
134 default:
135 break;
136 }
137}
138
29104708
LOK
139void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3)
140{
141 VC_CEC_NOTIFY_T reason = (VC_CEC_NOTIFY_T)CEC_CB_REASON(header);
142
b509ba1a 143#ifdef CEC_DEBUGGING
29104708 144 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);
b509ba1a 145#endif
29104708
LOK
146
147 switch (reason)
148 {
149 case VC_CEC_RX:
150 // CEC data received
151 {
152 // translate into a VC_CEC_MESSAGE_T
153 VC_CEC_MESSAGE_T message;
154 vc_cec_param2message(header, p0, p1, p2, p3, &message);
155
156 // translate to a cec_command
157 cec_command command;
158 cec_command::Format(command,
159 (cec_logical_address)message.initiator,
160 (cec_logical_address)message.follower,
161 (cec_opcode)CEC_CB_OPCODE(p0));
162
163 // copy parameters
164 for (uint8_t iPtr = 1; iPtr < message.length; iPtr++)
165 command.PushBack(message.payload[iPtr]);
166
167 // send to libCEC
168 m_callback->OnCommandReceived(command);
169 }
170 break;
171 case VC_CEC_TX:
172 {
173 // handle response to a command that was sent earlier
174 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));
175 }
176 break;
177 case VC_CEC_BUTTON_PRESSED:
3b106c7d 178 case VC_CEC_REMOTE_PRESSED:
29104708
LOK
179 {
180 // translate into a cec_command
181 cec_command command;
3b106c7d
LOK
182 cec_command::Format(command,
183 (cec_logical_address)CEC_CB_INITIATOR(p0),
184 (cec_logical_address)CEC_CB_FOLLOWER(p0),
185 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_PRESSED : CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN);
29104708
LOK
186 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
187
188 // send to libCEC
189 m_callback->OnCommandReceived(command);
190 }
191 break;
192 case VC_CEC_BUTTON_RELEASE:
3b106c7d 193 case VC_CEC_REMOTE_RELEASE:
29104708
LOK
194 {
195 // translate into a cec_command
196 cec_command command;
3b106c7d
LOK
197 cec_command::Format(command,
198 (cec_logical_address)CEC_CB_INITIATOR(p0),
199 (cec_logical_address)CEC_CB_FOLLOWER(p0),
200 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_RELEASE : CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP);
29104708
LOK
201 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
202
203 // send to libCEC
204 m_callback->OnCommandReceived(command);
205 }
206 break;
207 case VC_CEC_LOGICAL_ADDR:
208 {
209 CLockObject lock(m_mutex);
d1a2ed70 210 m_previousLogicalAddress = m_logicalAddress;
29104708
LOK
211 if (CEC_CB_RC(header) == VCHIQ_SUCCESS)
212 {
213 m_bLogicalAddressChanged = true;
214 m_logicalAddress = (cec_logical_address)(p0 & 0xF);
215 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address changed to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
216 }
217 else
218 {
d1a2ed70 219 m_logicalAddress = CECDEVICE_FREEUSE;
29104708
LOK
220 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to change the logical address, reset to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
221 }
222 m_logicalAddressCondition.Signal();
223 }
224 break;
7d27bafc
LOK
225 case VC_CEC_LOGICAL_ADDR_LOST:
226 {
227 // the logical address was taken by another device
d1a2ed70 228 cec_logical_address previousAddress = m_logicalAddress == CECDEVICE_BROADCAST ? m_previousLogicalAddress : m_logicalAddress;
7d27bafc 229 m_logicalAddress = CECDEVICE_UNKNOWN;
f60ee8b3 230
40119e0c 231 // notify libCEC that we lost our LA when the connection was initialised
057497c6
LOK
232 bool bNotify(false);
233 {
234 CLockObject lock(m_mutex);
235 bNotify = m_bInitialised && m_bLogicalAddressRegistered;
236 }
237 if (bNotify)
40119e0c 238 m_callback->HandleLogicalAddressLost(previousAddress);
7d27bafc
LOK
239 }
240 break;
29104708 241 case VC_CEC_TOPOLOGY:
29104708
LOK
242 break;
243 default:
244 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignoring unknown reason %x", reason);
245 break;
246 }
247}
248
249int CRPiCECAdapterCommunication::InitHostCEC(void)
250{
251 VCHIQ_INSTANCE_T vchiq_instance;
252 int iResult;
253
254 if ((iResult = vchiq_initialise(&vchiq_instance)) != VCHIQ_SUCCESS)
255 {
256 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
257 CStdString strError;
258 strError.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
259 m_strError = strError;
260 return iResult;
261 }
262 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchiq_initialise succeeded", __FUNCTION__);
263
264 if ((iResult = vchi_initialise(&m_vchi_instance)) != VCHIQ_SUCCESS)
265 {
266 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
267 CStdString strError;
268 strError.Format("%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
269 m_strError = strError;
270 return iResult;
271 }
272 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_initialise succeeded", __FUNCTION__);
273
274 vchiq_instance = (VCHIQ_INSTANCE_T)m_vchi_instance;
275
276 m_vchi_connection = vchi_create_connection(single_get_func_table(),
277 vchi_mphi_message_driver_func_table());
278
279 if ((iResult = vchi_connect(&m_vchi_connection, 1, m_vchi_instance)) != VCHIQ_SUCCESS)
280 {
281 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
282 CStdString strError;
283 strError.Format("%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
284 m_strError = strError;
285 return iResult;
286 }
287 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_connect succeeded", __FUNCTION__);
288
289 return VCHIQ_SUCCESS;
290}
291
6bba6e2c 292bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening)
29104708
LOK
293{
294 Close();
295
296 if (InitHostCEC() != VCHIQ_SUCCESS)
297 return false;
298
299 if (bStartListening)
300 {
301 // enable passive mode
302 vc_cec_set_passive(true);
303
49ba42d4
LOK
304 // register the callbacks
305 vc_cec_register_callback(rpi_cec_callback, (void*)this);
306 vc_tv_register_callback(rpi_tv_callback, (void*)this);
29104708 307
6bba6e2c
LOK
308 // release previous LA
309 vc_cec_release_logical_address();
310 if (!m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs))
311 {
312 LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to release the previous LA");
313 return false;
314 }
315
29104708
LOK
316 // register LA "freeuse"
317 if (RegisterLogicalAddress(CECDEVICE_FREEUSE))
318 {
319 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vc_cec initialised", __FUNCTION__);
320 CLockObject lock(m_mutex);
321 m_bInitialised = true;
322 }
323 else
324 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec could not be initialised", __FUNCTION__);
325 }
326
327 return true;
328}
329
330uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
331{
332 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS);
333 if (!IsInitialised())
334 return iPA;
335
336 if (vc_cec_get_physical_address(&iPA) == VCHIQ_SUCCESS)
337 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address = %04x", __FUNCTION__, iPA);
338 else
339 {
340 LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - failed to get the physical address", __FUNCTION__);
341 iPA = CEC_INVALID_PHYSICAL_ADDRESS;
342 }
343
344 return iPA;
345}
346
347void CRPiCECAdapterCommunication::Close(void)
348{
349 {
350 CLockObject lock(m_mutex);
351 if (m_bInitialised)
352 m_bInitialised = false;
353 else
354 return;
355 }
485660f8
MK
356 if (m_bInitialised)
357 vc_tv_unregister_callback(rpi_tv_callback);
49ba42d4 358
29104708
LOK
359 UnregisterLogicalAddress();
360
361 // disable passive mode
362 vc_cec_set_passive(false);
363
364 if (!g_bHostInited)
365 {
366 g_bHostInited = false;
367 bcm_host_deinit();
368 }
369}
370
371std::string CRPiCECAdapterCommunication::GetError(void) const
372{
373 std::string strError(m_strError);
374 return strError;
375}
376
377cec_adapter_message_state CRPiCECAdapterCommunication::Write(const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool bIsReply)
378{
379 // ensure that the source LA is registered
380 if (!RegisterLogicalAddress(data.initiator))
381 {
382 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to register logical address %s (%X)", CCECTypeUtils::ToString(data.initiator), data.initiator);
383 return (data.initiator == data.destination) ? ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED : ADAPTER_MESSAGE_STATE_ERROR;
384 }
385
38b8eaae 386 if (!m_queue->Write(data, bIsReply))
29104708 387 {
38b8eaae
MK
388 if (!data.opcode_set)
389 {
390 return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
391 }
392
393 return ADAPTER_MESSAGE_STATE_SENT;
29104708
LOK
394 }
395
38b8eaae
MK
396 return ADAPTER_MESSAGE_STATE_SENT_ACKED;
397
29104708
LOK
398}
399
400uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
401{
402 return VC_CECSERVICE_VER;
403}
404
405cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void)
406{
407 {
408 CLockObject lock(m_mutex);
409 if (m_logicalAddress != CECDEVICE_UNKNOWN)
410 return m_logicalAddress;
411 }
412
413 CEC_AllDevices_T address;
414 return (vc_cec_get_logical_address(&address) == VCHIQ_SUCCESS) ?
415 (cec_logical_address)address : CECDEVICE_UNKNOWN;
416}
417
418bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
419{
420 CLockObject lock(m_mutex);
421 if (m_logicalAddress == CECDEVICE_UNKNOWN ||
422 m_logicalAddress == CECDEVICE_BROADCAST)
423 return true;
424
425 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - releasing previous logical address", __FUNCTION__);
057497c6
LOK
426 {
427 CLockObject lock(m_mutex);
428 m_bLogicalAddressRegistered = false;
429 m_bLogicalAddressChanged = false;
430 }
29104708
LOK
431
432 vc_cec_release_logical_address();
433
434 return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged);
435}
436
437bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address)
438{
439 {
440 CLockObject lock(m_mutex);
441 if (m_logicalAddress == address)
442 return true;
443 }
444
445 if (!UnregisterLogicalAddress())
446 return false;
447
448 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - registering address %x", __FUNCTION__, address);
449
450 CLockObject lock(m_mutex);
451 m_bLogicalAddressChanged = false;
452 vc_cec_poll_address((CEC_AllDevices_T)address);
453
454 // register the new LA
455 int iRetval = vc_cec_set_logical_address((CEC_AllDevices_T)address, (CEC_DEVICE_TYPE_T)CCECTypeUtils::GetType(address), CEC_VENDOR_ID_BROADCOM);
456 if (iRetval != VCHIQ_SUCCESS)
457 {
458 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);
459 return false;
460 }
461
057497c6
LOK
462 if (m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged))
463 {
464 m_bLogicalAddressRegistered = true;
465 return true;
466 }
467 return false;
29104708
LOK
468}
469
470cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void)
471{
472 cec_logical_addresses addresses; addresses.Clear();
473 cec_logical_address current = GetLogicalAddress();
474 if (current != CECDEVICE_UNKNOWN)
475 addresses.Set(current);
476
477 return addresses;
478}
479
480bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
481{
482 // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered
483 return SupportsSourceLogicalAddress(addresses.primary) &&
484 RegisterLogicalAddress(addresses.primary);
485}
486
487void CRPiCECAdapterCommunication::InitHost(void)
488{
489 if (!g_bHostInited)
490 {
491 g_bHostInited = true;
492 bcm_host_init();
493 }
494}
495
496#endif