Release 2.2.0
[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);
f9c4a2de 81 UnregisterLogicalAddress();
29104708 82 Close();
f9c4a2de 83 vc_cec_set_passive(false);
29104708
LOK
84}
85
86const char *ToString(const VC_CEC_ERROR_T error)
87{
88 switch(error)
89 {
90 case VC_CEC_SUCCESS:
91 return "success";
92 case VC_CEC_ERROR_NO_ACK:
93 return "no ack";
94 case VC_CEC_ERROR_SHUTDOWN:
95 return "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";
108 default:
109 return "unknown";
110 }
111}
112
113bool CRPiCECAdapterCommunication::IsInitialised(void)
114{
115 CLockObject lock(m_mutex);
116 return m_bInitialised;
117}
118
49ba42d4
LOK
119void CRPiCECAdapterCommunication::OnTVServiceCallback(uint32_t reason, uint32_t UNUSED(p0), uint32_t UNUSED(p1))
120{
121 switch(reason)
122 {
49ba42d4
LOK
123 case VC_HDMI_ATTACHED:
124 {
125 uint16_t iNewAddress = GetPhysicalAddress();
126 m_callback->HandlePhysicalAddressChanged(iNewAddress);
127 break;
128 }
c66fdf1f 129 case VC_HDMI_UNPLUGGED:
49ba42d4
LOK
130 case VC_HDMI_DVI:
131 case VC_HDMI_HDMI:
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:
136 default:
137 break;
138 }
139}
140
29104708
LOK
141void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3)
142{
143 VC_CEC_NOTIFY_T reason = (VC_CEC_NOTIFY_T)CEC_CB_REASON(header);
144
b509ba1a 145#ifdef CEC_DEBUGGING
29104708 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);
b509ba1a 147#endif
29104708
LOK
148
149 switch (reason)
150 {
151 case VC_CEC_RX:
152 // CEC data received
153 {
154 // translate into a VC_CEC_MESSAGE_T
155 VC_CEC_MESSAGE_T message;
156 vc_cec_param2message(header, p0, p1, p2, p3, &message);
157
158 // translate to a cec_command
159 cec_command 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));
164
165 // copy parameters
166 for (uint8_t iPtr = 1; iPtr < message.length; iPtr++)
167 command.PushBack(message.payload[iPtr]);
168
169 // send to libCEC
170 m_callback->OnCommandReceived(command);
171 }
172 break;
173 case VC_CEC_TX:
174 {
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));
177 }
178 break;
179 case VC_CEC_BUTTON_PRESSED:
3b106c7d 180 case VC_CEC_REMOTE_PRESSED:
29104708
LOK
181 {
182 // translate into a cec_command
183 cec_command command;
3b106c7d
LOK
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);
29104708
LOK
188 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
189
190 // send to libCEC
191 m_callback->OnCommandReceived(command);
192 }
193 break;
194 case VC_CEC_BUTTON_RELEASE:
3b106c7d 195 case VC_CEC_REMOTE_RELEASE:
29104708
LOK
196 {
197 // translate into a cec_command
198 cec_command command;
3b106c7d
LOK
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);
29104708
LOK
203 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
204
205 // send to libCEC
206 m_callback->OnCommandReceived(command);
207 }
208 break;
209 case VC_CEC_LOGICAL_ADDR:
210 {
211 CLockObject lock(m_mutex);
d1a2ed70 212 m_previousLogicalAddress = m_logicalAddress;
29104708
LOK
213 if (CEC_CB_RC(header) == VCHIQ_SUCCESS)
214 {
215 m_bLogicalAddressChanged = true;
216 m_logicalAddress = (cec_logical_address)(p0 & 0xF);
f9c4a2de 217 m_bLogicalAddressRegistered = true;
29104708
LOK
218 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address changed to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
219 }
220 else
221 {
d1a2ed70 222 m_logicalAddress = CECDEVICE_FREEUSE;
29104708
LOK
223 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to change the logical address, reset to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
224 }
225 m_logicalAddressCondition.Signal();
226 }
227 break;
7d27bafc
LOK
228 case VC_CEC_LOGICAL_ADDR_LOST:
229 {
f9c4a2de 230 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical %s (%x) address lost", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
7d27bafc 231 // the logical address was taken by another device
f9c4a2de 232 cec_logical_address previousAddress = m_logicalAddress == CECDEVICE_FREEUSE ? m_previousLogicalAddress : m_logicalAddress;
7d27bafc 233 m_logicalAddress = CECDEVICE_UNKNOWN;
f60ee8b3 234
40119e0c 235 // notify libCEC that we lost our LA when the connection was initialised
057497c6
LOK
236 bool bNotify(false);
237 {
238 CLockObject lock(m_mutex);
239 bNotify = m_bInitialised && m_bLogicalAddressRegistered;
240 }
241 if (bNotify)
40119e0c 242 m_callback->HandleLogicalAddressLost(previousAddress);
7d27bafc
LOK
243 }
244 break;
29104708 245 case VC_CEC_TOPOLOGY:
29104708
LOK
246 break;
247 default:
248 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignoring unknown reason %x", reason);
249 break;
250 }
251}
252
253int CRPiCECAdapterCommunication::InitHostCEC(void)
254{
255 VCHIQ_INSTANCE_T vchiq_instance;
256 int iResult;
257
258 if ((iResult = vchiq_initialise(&vchiq_instance)) != VCHIQ_SUCCESS)
259 {
260 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
261 CStdString strError;
262 strError.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
263 m_strError = strError;
264 return iResult;
265 }
266 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchiq_initialise succeeded", __FUNCTION__);
267
268 if ((iResult = vchi_initialise(&m_vchi_instance)) != VCHIQ_SUCCESS)
269 {
270 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
271 CStdString strError;
272 strError.Format("%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
273 m_strError = strError;
274 return iResult;
275 }
276 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_initialise succeeded", __FUNCTION__);
277
278 vchiq_instance = (VCHIQ_INSTANCE_T)m_vchi_instance;
279
280 m_vchi_connection = vchi_create_connection(single_get_func_table(),
281 vchi_mphi_message_driver_func_table());
282
283 if ((iResult = vchi_connect(&m_vchi_connection, 1, m_vchi_instance)) != VCHIQ_SUCCESS)
284 {
285 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
286 CStdString strError;
287 strError.Format("%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
288 m_strError = strError;
289 return iResult;
290 }
291 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_connect succeeded", __FUNCTION__);
292
293 return VCHIQ_SUCCESS;
294}
295
6bba6e2c 296bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening)
29104708
LOK
297{
298 Close();
299
300 if (InitHostCEC() != VCHIQ_SUCCESS)
301 return false;
302
303 if (bStartListening)
304 {
305 // enable passive mode
306 vc_cec_set_passive(true);
307
49ba42d4
LOK
308 // register the callbacks
309 vc_cec_register_callback(rpi_cec_callback, (void*)this);
310 vc_tv_register_callback(rpi_tv_callback, (void*)this);
29104708 311
29104708 312 // register LA "freeuse"
f9c4a2de 313 if (RegisterLogicalAddress(CECDEVICE_FREEUSE, iTimeoutMs))
29104708
LOK
314 {
315 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vc_cec initialised", __FUNCTION__);
316 CLockObject lock(m_mutex);
317 m_bInitialised = true;
318 }
319 else
f9c4a2de 320 {
29104708 321 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec could not be initialised", __FUNCTION__);
f9c4a2de
MK
322 return false;
323 }
29104708
LOK
324 }
325
326 return true;
327}
328
329uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
330{
331 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS);
332 if (!IsInitialised())
333 return iPA;
334
335 if (vc_cec_get_physical_address(&iPA) == VCHIQ_SUCCESS)
336 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address = %04x", __FUNCTION__, iPA);
337 else
338 {
339 LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - failed to get the physical address", __FUNCTION__);
340 iPA = CEC_INVALID_PHYSICAL_ADDRESS;
341 }
342
343 return iPA;
344}
345
346void CRPiCECAdapterCommunication::Close(void)
347{
f9c4a2de 348 if (m_bInitialised) {
485660f8 349 vc_tv_unregister_callback(rpi_tv_callback);
f9c4a2de
MK
350 m_bInitialised = false;
351 }
29104708
LOK
352
353 if (!g_bHostInited)
354 {
355 g_bHostInited = false;
356 bcm_host_deinit();
357 }
358}
359
360std::string CRPiCECAdapterCommunication::GetError(void) const
361{
362 std::string strError(m_strError);
363 return strError;
364}
365
f9c4a2de 366cec_adapter_message_state CRPiCECAdapterCommunication::Write(const cec_command &data, bool &bRetry, uint8_t iLineTimeout, bool bIsReply)
29104708 367{
f9c4a2de
MK
368 VC_CEC_ERROR_T vcAnswer;
369 uint32_t iTimeout = (data.transmit_timeout ? data.transmit_timeout : iLineTimeout*1000);
38b8eaae 370
f9c4a2de
MK
371 cec_adapter_message_state rc = m_queue->Write(data, bRetry, iTimeout, bIsReply, vcAnswer);
372#ifdef CEC_DEBUGGING
373 LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending data: result %s", ToString(vcAnswer));
374#endif
375 return rc;
29104708
LOK
376}
377
378uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
379{
380 return VC_CECSERVICE_VER;
381}
382
383cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void)
384{
f9c4a2de 385 CLockObject lock(m_mutex);
29104708 386
f9c4a2de 387 return m_logicalAddress;
29104708
LOK
388}
389
390bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
391{
392 CLockObject lock(m_mutex);
f9c4a2de 393 if (!m_bInitialised)
29104708
LOK
394 return true;
395
396 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - releasing previous logical address", __FUNCTION__);
057497c6
LOK
397 {
398 CLockObject lock(m_mutex);
399 m_bLogicalAddressRegistered = false;
400 m_bLogicalAddressChanged = false;
401 }
29104708
LOK
402
403 vc_cec_release_logical_address();
404
405 return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged);
406}
407
f9c4a2de 408bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address, uint32_t iTimeoutMs)
29104708
LOK
409{
410 {
411 CLockObject lock(m_mutex);
f9c4a2de 412 if ((m_logicalAddress == address) && m_bLogicalAddressRegistered)
29104708
LOK
413 return true;
414 }
415
29104708 416 m_bLogicalAddressChanged = false;
29104708
LOK
417
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)
421 {
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);
f9c4a2de 423 UnregisterLogicalAddress();
29104708 424 }
f9c4a2de 425 else if (m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs))
057497c6 426 {
057497c6
LOK
427 return true;
428 }
429 return false;
29104708
LOK
430}
431
432cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void)
433{
434 cec_logical_addresses addresses; addresses.Clear();
f9c4a2de
MK
435 if (m_bLogicalAddressRegistered)
436 addresses.primary = GetLogicalAddress();
29104708
LOK
437
438 return addresses;
439}
440
441bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
442{
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);
446}
447
448void CRPiCECAdapterCommunication::InitHost(void)
449{
450 if (!g_bHostInited)
451 {
452 g_bHostInited = true;
453 bcm_host_init();
454 }
455}
456
457#endif