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