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