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