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