sync with LibCecSharp
[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-2012 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 CRPiCECAdapterCommunication::CRPiCECAdapterCommunication(IAdapterCommunicationCallback *callback) :
62 IAdapterCommunication(callback),
63 m_logicalAddress(CECDEVICE_UNKNOWN),
64 m_bLogicalAddressChanged(false),
65 m_previousLogicalAddress(CECDEVICE_FREEUSE)
66 {
67 m_queue = new CRPiCECAdapterMessageQueue(this);
68 }
69
70 CRPiCECAdapterCommunication::~CRPiCECAdapterCommunication(void)
71 {
72 delete(m_queue);
73 Close();
74 }
75
76 const char *ToString(const VC_CEC_ERROR_T error)
77 {
78 switch(error)
79 {
80 case VC_CEC_SUCCESS:
81 return "success";
82 case VC_CEC_ERROR_NO_ACK:
83 return "no ack";
84 case VC_CEC_ERROR_SHUTDOWN:
85 return "shutdown";
86 case VC_CEC_ERROR_BUSY:
87 return "device is busy";
88 case VC_CEC_ERROR_NO_LA:
89 return "no logical address";
90 case VC_CEC_ERROR_NO_PA:
91 return "no physical address";
92 case VC_CEC_ERROR_NO_TOPO:
93 return "no topology";
94 case VC_CEC_ERROR_INVALID_FOLLOWER:
95 return "invalid follower";
96 case VC_CEC_ERROR_INVALID_ARGUMENT:
97 return "invalid arg";
98 default:
99 return "unknown";
100 }
101 }
102
103 bool CRPiCECAdapterCommunication::IsInitialised(void)
104 {
105 CLockObject lock(m_mutex);
106 return m_bInitialised;
107 }
108
109 void CRPiCECAdapterCommunication::OnDataReceived(uint32_t header, uint32_t p0, uint32_t p1, uint32_t p2, uint32_t p3)
110 {
111 VC_CEC_NOTIFY_T reason = (VC_CEC_NOTIFY_T)CEC_CB_REASON(header);
112
113 #ifdef CEC_DEBUGGING
114 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);
115 #endif
116
117 switch (reason)
118 {
119 case VC_CEC_RX:
120 // CEC data received
121 {
122 // translate into a VC_CEC_MESSAGE_T
123 VC_CEC_MESSAGE_T message;
124 vc_cec_param2message(header, p0, p1, p2, p3, &message);
125
126 // translate to a cec_command
127 cec_command command;
128 cec_command::Format(command,
129 (cec_logical_address)message.initiator,
130 (cec_logical_address)message.follower,
131 (cec_opcode)CEC_CB_OPCODE(p0));
132
133 // copy parameters
134 for (uint8_t iPtr = 1; iPtr < message.length; iPtr++)
135 command.PushBack(message.payload[iPtr]);
136
137 // send to libCEC
138 m_callback->OnCommandReceived(command);
139 }
140 break;
141 case VC_CEC_TX:
142 {
143 // handle response to a command that was sent earlier
144 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));
145 }
146 break;
147 case VC_CEC_BUTTON_PRESSED:
148 case VC_CEC_REMOTE_PRESSED:
149 {
150 // translate into a cec_command
151 cec_command command;
152 cec_command::Format(command,
153 (cec_logical_address)CEC_CB_INITIATOR(p0),
154 (cec_logical_address)CEC_CB_FOLLOWER(p0),
155 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_PRESSED : CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN);
156 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
157
158 // send to libCEC
159 m_callback->OnCommandReceived(command);
160 }
161 break;
162 case VC_CEC_BUTTON_RELEASE:
163 case VC_CEC_REMOTE_RELEASE:
164 {
165 // translate into a cec_command
166 cec_command command;
167 cec_command::Format(command,
168 (cec_logical_address)CEC_CB_INITIATOR(p0),
169 (cec_logical_address)CEC_CB_FOLLOWER(p0),
170 reason == VC_CEC_BUTTON_PRESSED ? CEC_OPCODE_USER_CONTROL_RELEASE : CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP);
171 command.parameters.PushBack((uint8_t)CEC_CB_OPERAND1(p0));
172
173 // send to libCEC
174 m_callback->OnCommandReceived(command);
175 }
176 break;
177 case VC_CEC_LOGICAL_ADDR:
178 {
179 CLockObject lock(m_mutex);
180 m_previousLogicalAddress = m_logicalAddress;
181 if (CEC_CB_RC(header) == VCHIQ_SUCCESS)
182 {
183 m_bLogicalAddressChanged = true;
184 m_logicalAddress = (cec_logical_address)(p0 & 0xF);
185 LIB_CEC->AddLog(CEC_LOG_DEBUG, "logical address changed to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
186 }
187 else
188 {
189 m_logicalAddress = CECDEVICE_FREEUSE;
190 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to change the logical address, reset to %s (%x)", LIB_CEC->ToString(m_logicalAddress), m_logicalAddress);
191 }
192 m_logicalAddressCondition.Signal();
193 }
194 break;
195 case VC_CEC_LOGICAL_ADDR_LOST:
196 {
197 // the logical address was taken by another device
198 cec_logical_address previousAddress = m_logicalAddress == CECDEVICE_BROADCAST ? m_previousLogicalAddress : m_logicalAddress;
199 m_logicalAddress = CECDEVICE_UNKNOWN;
200
201 // notify libCEC that we lost our LA when the connection was initialised
202 if (m_bInitialised)
203 m_callback->HandleLogicalAddressLost(previousAddress);
204 }
205 break;
206 case VC_CEC_TOPOLOGY:
207 break;
208 default:
209 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignoring unknown reason %x", reason);
210 break;
211 }
212 }
213
214 int CRPiCECAdapterCommunication::InitHostCEC(void)
215 {
216 VCHIQ_INSTANCE_T vchiq_instance;
217 int iResult;
218
219 if ((iResult = vchiq_initialise(&vchiq_instance)) != VCHIQ_SUCCESS)
220 {
221 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
222 CStdString strError;
223 strError.Format("%s - vchiq_initialise failed (%d)", __FUNCTION__, iResult);
224 m_strError = strError;
225 return iResult;
226 }
227 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchiq_initialise succeeded", __FUNCTION__);
228
229 if ((iResult = vchi_initialise(&m_vchi_instance)) != VCHIQ_SUCCESS)
230 {
231 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
232 CStdString strError;
233 strError.Format("%s - vchi_initialise failed (%d)", __FUNCTION__, iResult);
234 m_strError = strError;
235 return iResult;
236 }
237 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_initialise succeeded", __FUNCTION__);
238
239 vchiq_instance = (VCHIQ_INSTANCE_T)m_vchi_instance;
240
241 m_vchi_connection = vchi_create_connection(single_get_func_table(),
242 vchi_mphi_message_driver_func_table());
243
244 if ((iResult = vchi_connect(&m_vchi_connection, 1, m_vchi_instance)) != VCHIQ_SUCCESS)
245 {
246 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
247 CStdString strError;
248 strError.Format("%s - vchi_connect failed (%d)", __FUNCTION__, iResult);
249 m_strError = strError;
250 return iResult;
251 }
252 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vchi_connect succeeded", __FUNCTION__);
253
254 return VCHIQ_SUCCESS;
255 }
256
257 bool CRPiCECAdapterCommunication::Open(uint32_t iTimeoutMs /* = CEC_DEFAULT_CONNECT_TIMEOUT */, bool UNUSED(bSkipChecks) /* = false */, bool bStartListening)
258 {
259 Close();
260
261 if (InitHostCEC() != VCHIQ_SUCCESS)
262 return false;
263
264 if (bStartListening)
265 {
266 // enable passive mode
267 vc_cec_set_passive(true);
268
269 // register the callback
270 vc_cec_register_callback(((CECSERVICE_CALLBACK_T)rpi_cec_callback), (void*)this);
271
272 // release previous LA
273 vc_cec_release_logical_address();
274 if (!m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged, iTimeoutMs))
275 {
276 LIB_CEC->AddLog(CEC_LOG_ERROR, "failed to release the previous LA");
277 return false;
278 }
279
280 // register LA "freeuse"
281 if (RegisterLogicalAddress(CECDEVICE_FREEUSE))
282 {
283 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - vc_cec initialised", __FUNCTION__);
284 CLockObject lock(m_mutex);
285 m_bInitialised = true;
286 }
287 else
288 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s - vc_cec could not be initialised", __FUNCTION__);
289 }
290
291 return true;
292 }
293
294 uint16_t CRPiCECAdapterCommunication::GetPhysicalAddress(void)
295 {
296 uint16_t iPA(CEC_INVALID_PHYSICAL_ADDRESS);
297 if (!IsInitialised())
298 return iPA;
299
300 if (vc_cec_get_physical_address(&iPA) == VCHIQ_SUCCESS)
301 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - physical address = %04x", __FUNCTION__, iPA);
302 else
303 {
304 LIB_CEC->AddLog(CEC_LOG_WARNING, "%s - failed to get the physical address", __FUNCTION__);
305 iPA = CEC_INVALID_PHYSICAL_ADDRESS;
306 }
307
308 return iPA;
309 }
310
311 void CRPiCECAdapterCommunication::Close(void)
312 {
313 {
314 CLockObject lock(m_mutex);
315 if (m_bInitialised)
316 m_bInitialised = false;
317 else
318 return;
319 }
320 UnregisterLogicalAddress();
321
322 // disable passive mode
323 vc_cec_set_passive(false);
324
325 if (!g_bHostInited)
326 {
327 g_bHostInited = false;
328 bcm_host_deinit();
329 }
330 }
331
332 std::string CRPiCECAdapterCommunication::GetError(void) const
333 {
334 std::string strError(m_strError);
335 return strError;
336 }
337
338 cec_adapter_message_state CRPiCECAdapterCommunication::Write(const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool bIsReply)
339 {
340 // ensure that the source LA is registered
341 if (!RegisterLogicalAddress(data.initiator))
342 {
343 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to register logical address %s (%X)", CCECTypeUtils::ToString(data.initiator), data.initiator);
344 return (data.initiator == data.destination) ? ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED : ADAPTER_MESSAGE_STATE_ERROR;
345 }
346
347 if (!data.opcode_set && data.initiator == data.destination)
348 {
349 // registration of the logical address would have failed
350 return ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
351 }
352
353 return m_queue->Write(data, bIsReply) ? ADAPTER_MESSAGE_STATE_SENT_ACKED : ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
354 }
355
356 uint16_t CRPiCECAdapterCommunication::GetFirmwareVersion(void)
357 {
358 return VC_CECSERVICE_VER;
359 }
360
361 cec_logical_address CRPiCECAdapterCommunication::GetLogicalAddress(void)
362 {
363 {
364 CLockObject lock(m_mutex);
365 if (m_logicalAddress != CECDEVICE_UNKNOWN)
366 return m_logicalAddress;
367 }
368
369 CEC_AllDevices_T address;
370 return (vc_cec_get_logical_address(&address) == VCHIQ_SUCCESS) ?
371 (cec_logical_address)address : CECDEVICE_UNKNOWN;
372 }
373
374 bool CRPiCECAdapterCommunication::UnregisterLogicalAddress(void)
375 {
376 CLockObject lock(m_mutex);
377 if (m_logicalAddress == CECDEVICE_UNKNOWN ||
378 m_logicalAddress == CECDEVICE_BROADCAST)
379 return true;
380
381 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - releasing previous logical address", __FUNCTION__);
382 m_bLogicalAddressChanged = false;
383
384 vc_cec_release_logical_address();
385
386 return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged);
387 }
388
389 bool CRPiCECAdapterCommunication::RegisterLogicalAddress(const cec_logical_address address)
390 {
391 {
392 CLockObject lock(m_mutex);
393 if (m_logicalAddress == address)
394 return true;
395 }
396
397 if (!UnregisterLogicalAddress())
398 return false;
399
400 LIB_CEC->AddLog(CEC_LOG_DEBUG, "%s - registering address %x", __FUNCTION__, address);
401
402 CLockObject lock(m_mutex);
403 m_bLogicalAddressChanged = false;
404 vc_cec_poll_address((CEC_AllDevices_T)address);
405
406 // register the new LA
407 int iRetval = vc_cec_set_logical_address((CEC_AllDevices_T)address, (CEC_DEVICE_TYPE_T)CCECTypeUtils::GetType(address), CEC_VENDOR_ID_BROADCOM);
408 if (iRetval != VCHIQ_SUCCESS)
409 {
410 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);
411 return false;
412 }
413
414 return m_logicalAddressCondition.Wait(m_mutex, m_bLogicalAddressChanged);
415 }
416
417 cec_logical_addresses CRPiCECAdapterCommunication::GetLogicalAddresses(void)
418 {
419 cec_logical_addresses addresses; addresses.Clear();
420 cec_logical_address current = GetLogicalAddress();
421 if (current != CECDEVICE_UNKNOWN)
422 addresses.Set(current);
423
424 return addresses;
425 }
426
427 bool CRPiCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
428 {
429 // the current generation RPi only supports 1 LA, so just ensure that the primary address is registered
430 return SupportsSourceLogicalAddress(addresses.primary) &&
431 RegisterLogicalAddress(addresses.primary);
432 }
433
434 void CRPiCECAdapterCommunication::InitHost(void)
435 {
436 if (!g_bHostInited)
437 {
438 g_bHostInited = true;
439 bcm_host_init();
440 }
441 }
442
443 #endif