a68c0ec64227ec6aa6f318bc20ecb33e6baf50e0
[deb_libcec.git] / src / lib / adapter / RPi / RPiCECAdapterCommunication.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
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
38 extern "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
47 using namespace CEC;
48 using namespace PLATFORM;
49
50 #define LIB_CEC m_callback->GetLib()
51
52 static bool g_bHostInited = false;
53
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)
56 {
57 if (callback_data)
58 static_cast<CRPiCECAdapterCommunication *>(callback_data)->OnDataReceived(p0, p1, p2, p3, p4);
59 }
60
61 // callback for the TV service
62 void 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
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)
74 {
75 m_queue = new CRPiCECAdapterMessageQueue(this);
76 }
77
78 CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void)
79 {
80 delete(m_queue);
81 Close();
82 }
83
84 const 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
111 bool CRPiCECAdapterCommunication::IsInitialised(void)
112 {
113 CLockObject lock(m_mutex);
114 return m_bInitialised;
115 }
116
117 void CRPiCECAdapterCommunication::OnTVServiceCallback(uint32_t reason, uint32_t UNUSED(p0), uint32_t UNUSED(p1))
118 {
119 switch(reason)
120 {
121 case VC_HDMI_UNPLUGGED:
122 {
123 m_callback->HandlePhysicalAddressChanged(0x1000);
124 break;
125 }
126 case VC_HDMI_ATTACHED:
127 {
128 uint16_t iNewAddress = GetPhysicalAddress();
129 m_callback->HandlePhysicalAddressChanged(iNewAddress);
130 break;
131 }
132 case VC_HDMI_DVI:
133 case VC_HDMI_HDMI:
134 case VC_HDMI_HDCP_UNAUTH:
135 case VC_HDMI_HDCP_AUTH:
136 case VC_HDMI_HDCP_KEY_DOWNLOAD:
137 case VC_HDMI_HDCP_SRM_DOWNLOAD:
138 default:
139 break;
140 }
141 }
142
143 void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3)
144 {
145 VC_CEC_NOTIFY_T reason = (VC_CEC_NOTIFY_T)CEC_CB_REASON(header);
146
147 #ifdef CEC_DEBUGGING
148 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);
149 #endif
150
151 switch (reason)
152 {
153 case VC_CEC_RX:
154 // CEC data received
155 {
156 // translate into a VC_CEC_MESSAGE_T
157 VC_CEC_MESSAGE_T message;
158 vc_cec_param2message(header, p0, p1, p2, p3, &message);
159
160 // translate to a cec_command
161 cec_command command;
162 cec_command::Format(command,
163 (cec_logical_address)message.initiator,
164 (cec_logical_address)message.follower,
165 (cec_opcode)CEC_CB_OPCODE(p0));
166
167 // copy parameters
168 for (uint8_t iPtr = 1; iPtr < message.length; iPtr++)
169 command.PushBack(message.payload[iPtr]);
170
171 // send to libCEC
172 m_callback->OnCommandReceived(command);
173 }
174 break;
175 case VC_CEC_TX:
176 {
177 // handle response to a command that was sent earlier
178 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 }
180 break;
181 case VC_CEC_BUTTON_PRESSED:
182 case VC_CEC_REMOTE_PRESSED:
183 {
184 // translate into a cec_command
185 cec_command command;
186 cec_command::Format(command,
187 (cec_logical_address)CEC_CB_INITIATOR(p0),
188 (cec_logical_address)CEC_CB_FOLLOWER(p0),
189 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_PRESSED : CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN);
190 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
191
192 // send to libCEC
193 m_callback->OnCommandReceived(command);
194 }
195 break;
196 case VC_CEC_BUTTON_RELEASE:
197 case VC_CEC_REMOTE_RELEASE:
198 {
199 // translate into a cec_command
200 cec_command command;
201 cec_command::Format(command,
202 (cec_logical_address)CEC_CB_INITIATOR(p0),
203 (cec_logical_address)CEC_CB_FOLLOWER(p0),
204 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_RELEASE : CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP);
205 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
206
207 // send to libCEC
208 m_callback->OnCommandReceived(command);
209 }
210 break;
211 case VC_CEC_LOGICAL_ADDR:
212 {
213 CLockObject lock(m_mutex);
214 m_previousLogicalAddress = m_logicalAddress;
215 if (CEC_CB_RC(header) == VCHIQ_SUCCESS)
216 {
217 m_bLogicalAddressChanged = true;
218 m_logicalAddress = (cec_logical_address)(p0 & 0xF);
219 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address changed to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
220 }
221 else
222 {
223 m_logicalAddress = CECDEVICE_FREEUSE;
224 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to change the logical address, reset to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
225 }
226 m_logicalAddressCondition.Signal();
227 }
228 break;
229 case VC_CEC_LOGICAL_ADDR_LOST:
230 {
231 // the logical address was taken by another device
232 cec_logical_address previousAddress = m_logicalAddress == CECDEVICE_BROADCAST ? m_previousLogicalAddress : m_logicalAddress;
233 m_logicalAddress = CECDEVICE_UNKNOWN;
234
235 // notify libCEC that we lost our LA when the connection was initialised
236 bool bNotify(false);
237 {
238 CLockObject lock(m_mutex);
239 bNotify = m_bInitialised && m_bLogicalAddressRegistered;
240 }
241 if (bNotify)
242 m_callback->HandleLogicalAddressLost(previousAddress);
243 }
244 break;
245 case VC_CEC_TOPOLOGY:
246 break;
247 default:
248 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignoring unknown reason %x", reason);
249 break;
250 }
251 }
252
253 int 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
296 bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening)
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
308 // register the callbacks
309 vc_cec_register_callback(rpi_cec_callback, (void*)this);
310 vc_tv_register_callback(rpi_tv_callback, (void*)this);
311
312 // release previous LA
313 vc_cec_release_logical_address();
314 if (!m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs))
315 {
316 LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to release the previous LA");
317 return false;
318 }
319
320 // register LA "freeuse"
321 if (RegisterLogicalAddress(CECDEVICE_FREEUSE))
322 {
323 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vc_cec initialised", __FUNCTION__);
324 CLockObject lock(m_mutex);
325 m_bInitialised = true;
326 }
327 else
328 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec could not be initialised", __FUNCTION__);
329 }
330
331 return true;
332 }
333
334 uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
335 {
336 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS);
337 if (!IsInitialised())
338 return iPA;
339
340 if (vc_cec_get_physical_address(&iPA) == VCHIQ_SUCCESS)
341 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address = %04x", __FUNCTION__, iPA);
342 else
343 {
344 LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - failed to get the physical address", __FUNCTION__);
345 iPA = CEC_INVALID_PHYSICAL_ADDRESS;
346 }
347
348 return iPA;
349 }
350
351 void CRPiCECAdapterCommunication::Close(void)
352 {
353 {
354 CLockObject lock(m_mutex);
355 if (m_bInitialised)
356 m_bInitialised = false;
357 else
358 return;
359 }
360 vc_tv_unregister_callback(rpi_tv_callback);
361
362 UnregisterLogicalAddress();
363
364 // disable passive mode
365 vc_cec_set_passive(false);
366
367 if (!g_bHostInited)
368 {
369 g_bHostInited = false;
370 bcm_host_deinit();
371 }
372 }
373
374 std::string CRPiCECAdapterCommunication::GetError(void) const
375 {
376 std::string strError(m_strError);
377 return strError;
378 }
379
380 cec_adapter_message_state CRPiCECAdapterCommunication::Write(const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool bIsReply)
381 {
382 // ensure that the source LA is registered
383 if (!RegisterLogicalAddress(data.initiator))
384 {
385 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to register logical address %s (%X)", CCECTypeUtils::ToString(data.initiator), data.initiator);
386 return (data.initiator == data.destination) ? ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED : ADAPTER_MESSAGE_STATE_ERROR;
387 }
388
389 if (!data.opcode_set && data.initiator == data.destination)
390 {
391 // registration of the logical address would have failed
392 return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
393 }
394
395 return m_queue->Write(data, bIsReply) ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
396 }
397
398 uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
399 {
400 return VC_CECSERVICE_VER;
401 }
402
403 cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void)
404 {
405 {
406 CLockObject lock(m_mutex);
407 if (m_logicalAddress != CECDEVICE_UNKNOWN)
408 return m_logicalAddress;
409 }
410
411 CEC_AllDevices_T address;
412 return (vc_cec_get_logical_address(&address) == VCHIQ_SUCCESS) ?
413 (cec_logical_address)address : CECDEVICE_UNKNOWN;
414 }
415
416 bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
417 {
418 CLockObject lock(m_mutex);
419 if (m_logicalAddress == CECDEVICE_UNKNOWN ||
420 m_logicalAddress == CECDEVICE_BROADCAST)
421 return true;
422
423 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - releasing previous logical address", __FUNCTION__);
424 {
425 CLockObject lock(m_mutex);
426 m_bLogicalAddressRegistered = false;
427 m_bLogicalAddressChanged = false;
428 }
429
430 vc_cec_release_logical_address();
431
432 return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged);
433 }
434
435 bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address)
436 {
437 {
438 CLockObject lock(m_mutex);
439 if (m_logicalAddress == address)
440 return true;
441 }
442
443 if (!UnregisterLogicalAddress())
444 return false;
445
446 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - registering address %x", __FUNCTION__, address);
447
448 CLockObject lock(m_mutex);
449 m_bLogicalAddressChanged = false;
450 vc_cec_poll_address((CEC_AllDevices_T)address);
451
452 // register the new LA
453 int iRetval = vc_cec_set_logical_address((CEC_AllDevices_T)address, (CEC_DEVICE_TYPE_T)CCECTypeUtils::GetType(address), CEC_VENDOR_ID_BROADCOM);
454 if (iRetval != VCHIQ_SUCCESS)
455 {
456 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);
457 return false;
458 }
459
460 if (m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged))
461 {
462 m_bLogicalAddressRegistered = true;
463 return true;
464 }
465 return false;
466 }
467
468 cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void)
469 {
470 cec_logical_addresses addresses; addresses.Clear();
471 cec_logical_address current = GetLogicalAddress();
472 if (current != CECDEVICE_UNKNOWN)
473 addresses.Set(current);
474
475 return addresses;
476 }
477
478 bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
479 {
480 // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered
481 return SupportsSourceLogicalAddress(addresses.primary) &&
482 RegisterLogicalAddress(addresses.primary);
483 }
484
485 void CRPiCECAdapterCommunication::InitHost(void)
486 {
487 if (!g_bHostInited)
488 {
489 g_bHostInited = true;
490 bcm_host_init();
491 }
492 }
493
494 #endif