Merge branch 'master' into release
[deb_libcec.git] / src / lib / adapter / CuBox / NxpCECAdapterCommunication.cpp
CommitLineData
4d3a7562 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
51b611bc 35#if defined(HAVE_TDA995X_API)
4d3a7562 36#include "NxpCECAdapterCommunication.h"
37
38#include "lib/CECTypeUtils.h"
39#include "lib/LibCEC.h"
40#include "lib/platform/sockets/cdevsocket.h"
41#include "lib/platform/util/StdString.h"
42#include "lib/platform/util/buffer.h"
43
44extern "C" {
45#define __cec_h__
46#include <../comps/tmdlHdmiCEC/inc/tmdlHdmiCEC_Types.h>
47#include <../tda998x_ioctl.h>
48}
49
50using namespace std;
51using namespace CEC;
52using namespace PLATFORM;
53
54#include "AdapterMessageQueue.h"
55
56#define LIB_CEC m_callback->GetLib()
57
4d3a7562 58// these are defined in nxp private header file
51b611bc 59#define CEC_MSG_SUCCESS 0x00 /*Message transmisson Succeed*/
60#define CEC_CSP_OFF_STATE 0x80 /*CSP in Off State*/
61#define CEC_BAD_REQ_SERVICE 0x81 /*Bad .req service*/
4d3a7562 62#define CEC_MSG_FAIL_UNABLE_TO_ACCESS 0x82 /*Message transmisson failed: Unable to access CEC line*/
63#define CEC_MSG_FAIL_ARBITRATION_ERROR 0x83 /*Message transmisson failed: Arbitration error*/
64#define CEC_MSG_FAIL_BIT_TIMMING_ERROR 0x84 /*Message transmisson failed: Bit timming error*/
51b611bc 65#define CEC_MSG_FAIL_DEST_NOT_ACK 0x85 /*Message transmisson failed: Destination Address not aknowledged*/
66#define CEC_MSG_FAIL_DATA_NOT_ACK 0x86 /*Message transmisson failed: Databyte not acknowledged*/
4d3a7562 67
68
51b611bc 69CNxpCECAdapterCommunication::CNxpCECAdapterCommunication(IAdapterCommunicationCallback *callback) :
4d3a7562 70 IAdapterCommunication(callback),
71 m_bLogicalAddressChanged(false)
72{
4d3a7562 73 CLockObject lock(m_mutex);
74
75 m_iNextMessage = 0;
76 m_logicalAddresses.Clear();
51b611bc 77 m_dev = new CCDevSocket(CEC_TDA995x_PATH);
4d3a7562 78}
79
80
81CNxpCECAdapterCommunication::~CNxpCECAdapterCommunication(void)
82{
4d3a7562 83 Close();
84
85 CLockObject lock(m_mutex);
86 delete m_dev;
87 m_dev = 0;
88}
89
90
91bool CNxpCECAdapterCommunication::IsOpen(void)
92{
93 return IsInitialised() && m_dev->IsOpen();
94}
95
96
51b611bc 97bool CNxpCECAdapterCommunication::Open(uint32_t iTimeoutMs, bool UNUSED(bSkipChecks), bool bStartListening)
4d3a7562 98{
4d3a7562 99 if (m_dev->Open(iTimeoutMs))
100 {
101 unsigned char raw_mode = 0xff;
102
103 if (m_dev->Ioctl(CEC_IOCTL_GET_RAW_MODE, &raw_mode) == 0)
104 {
105 raw_mode = 1;
106 if (m_dev->Ioctl(CEC_IOCTL_SET_RAW_MODE, &raw_mode) == 0)
107 {
108 if (!bStartListening || CreateThread())
109 return true;
110 }
111 else
112 {
113 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_SET_RAW_MODE failed !", __func__);
114 }
115
116 raw_mode = 0;
117 m_dev->Ioctl(CEC_IOCTL_SET_RAW_MODE, &raw_mode);
118 }
119 else
120 {
121 LIB_CEC->AddLog(CEC_LOG_ERROR,
51b611bc 122 "%s: CEC_IOCTL_GET_RAW_MODE not supported. Please update your kernel.", __func__);
4d3a7562 123 }
124
125 m_dev->Close();
126 }
127
128 return false;
129}
130
131
132void CNxpCECAdapterCommunication::Close(void)
133{
4d3a7562 134 StopThread(0);
135
136 unsigned char raw_mode = 0;
137 m_dev->Ioctl(CEC_IOCTL_SET_RAW_MODE, &raw_mode);
138
139 m_dev->Close();
140}
141
142
143std::string CNxpCECAdapterCommunication::GetError(void) const
144{
145 std::string strError(m_strError);
146 return strError;
147}
148
149
150cec_adapter_message_state CNxpCECAdapterCommunication::Write(
151 const cec_command &data, bool &UNUSED(bRetry), uint8_t UNUSED(iLineTimeout), bool UNUSED(bIsReply))
152{
153 cec_frame frame;
154 CAdapterMessageQueueEntry *entry;
155 cec_adapter_message_state rc = ADAPTER_MESSAGE_STATE_ERROR;
51b611bc 156
4d3a7562 157 if ((size_t)data.parameters.size + data.opcode_set > sizeof(frame.data))
158 {
159 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: data size too large !", __func__);
160 return ADAPTER_MESSAGE_STATE_ERROR;
161 }
162
163 frame.size = 0;
164 frame.service = 0;
165 frame.addr = (data.initiator << 4) | (data.destination & 0x0f);
166
167 if (data.opcode_set)
168 {
169 frame.data[0] = data.opcode;
170 frame.size++;
171
172 memcpy(&frame.data[frame.size], data.parameters.data, data.parameters.size);
173 frame.size += data.parameters.size;
174 }
175
176 frame.size += 3;
177
178 entry = new CAdapterMessageQueueEntry(data);
179
180 m_messageMutex.Lock();
181 uint32_t msgKey = ++m_iNextMessage;
182 m_messages.insert(make_pair(msgKey, entry));
183
184 if (m_dev->Write((char *)&frame, sizeof(frame)) == sizeof(frame))
185 {
186 m_messageMutex.Unlock();
187
188 if (entry->Wait(CEC_DEFAULT_TRANSMIT_WAIT))
189 {
190 uint32_t status = entry->Result();
191
192 if (status == CEC_MSG_FAIL_DEST_NOT_ACK)
193 rc = ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED;
194 else if (status == CEC_MSG_SUCCESS)
195 rc = ADAPTER_MESSAGE_STATE_SENT_ACKED;
4d3a7562 196 }
197 else
198 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: command timed out !", __func__);
199
200 m_messageMutex.Lock();
201 }
202 else
203 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: write failed !", __func__);
204
205 m_messages.erase(msgKey);
206 m_messageMutex.Unlock();
207
208 delete entry;
209
210 return rc;
211}
212
213
214uint16_t CNxpCECAdapterCommunication::GetFirmwareVersion(void)
215{
216 cec_sw_version vers = { 0 };
217
218 m_dev->Ioctl(CEC_IOCTL_GET_SW_VERSION, &vers);
219
4d3a7562 220 return (vers.majorVersionNr * 100) + vers.minorVersionNr;
221}
222
223
224cec_vendor_id CNxpCECAdapterCommunication::GetVendorId(void)
225{
226 cec_raw_info info;
227
228 if (m_dev->Ioctl(CEC_IOCTL_GET_RAW_INFO, &info) != 0)
229 {
230 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_GET_RAW_INFO failed !", __func__);
231 return CEC_VENDOR_LG;
232 }
233
4d3a7562 234 return cec_vendor_id(info.VendorID);
235}
236
237
238uint16_t CNxpCECAdapterCommunication::GetPhysicalAddress(void)
239{
240 cec_raw_info info;
241
242 if (m_dev->Ioctl(CEC_IOCTL_GET_RAW_INFO, &info) != 0)
243 {
244 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_GET_RAW_INFO failed !", __func__);
245 return CEC_INVALID_PHYSICAL_ADDRESS;
246 }
247
4d3a7562 248 return info.PhysicalAddress;
249}
250
251
252cec_logical_addresses CNxpCECAdapterCommunication::GetLogicalAddresses(void)
253{
254 CLockObject lock(m_mutex);
255
256 if (m_bLogicalAddressChanged || m_logicalAddresses.IsEmpty() )
257 {
258 cec_raw_info info;
259
260 m_logicalAddresses.Clear();
261
262 if (m_dev->Ioctl(CEC_IOCTL_GET_RAW_INFO, &info) != 0)
263 {
264 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_GET_RAW_INFO failed !", __func__);
265 }
266 else if (info.LogicalAddress != CECDEVICE_UNREGISTERED)
267 {
268 m_logicalAddresses.Set(cec_logical_address(info.LogicalAddress));
269
270 for (int la = CECDEVICE_TV; la < CECDEVICE_BROADCAST; la++)
271 {
272 m_logicalAddresses.Set(cec_logical_address(la));
273 }
274 }
275
276 m_bLogicalAddressChanged = false;
277 }
278
4d3a7562 279 return m_logicalAddresses;
280}
281
282
283bool CNxpCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
284{
4d3a7562 285 unsigned char log_addr = addresses.primary;
286
287 if (m_dev->Ioctl(CEC_IOCTL_RX_ADDR, &log_addr) != 0)
288 {
289 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_RX_ADDR failed !", __func__);
290 return false;
291 }
292
293 cec_rx_mask all_addresses;
294
295 all_addresses.SwitchOn = addresses.AckMask() & 0x7fff;
296 all_addresses.SwitchOff = ~all_addresses.SwitchOn;
297
298 if (all_addresses.SwitchOn != (1 << addresses.primary) &&
299 m_dev->Ioctl(CEC_IOCTL_SET_RX_ADDR_MASK, &all_addresses) != 0)
300 {
301 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_SET_RX_ADDR_MASK failed !", __func__);
302 return false;
303 }
304
305 m_bLogicalAddressChanged = true;
306
307 return true;
308}
309
310
51b611bc 311void CNxpCECAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress))
4d3a7562 312{
4d3a7562 313 unsigned char log_addr = CECDEVICE_BROADCAST;
314
315 if (m_dev->Ioctl(CEC_IOCTL_RX_ADDR, &log_addr) != 0)
316 {
317 LIB_CEC->AddLog(CEC_LOG_ERROR, "%s: CEC_IOCTL_RX_ADDR failed !", __func__);
318 }
319}
320
321
322void *CNxpCECAdapterCommunication::Process(void)
323{
324 bool bHandled;
325 cec_frame frame;
326 uint32_t opcode, status;
327 cec_logical_address initiator, destination;
328
329 while (!IsStopped())
330 {
331 if (m_dev->Read((char *)&frame, sizeof(frame), 500) == sizeof(frame))
332 {
333 initiator = cec_logical_address(frame.addr >> 4);
334 destination = cec_logical_address(frame.addr & 0x0f);
335
4d3a7562 336 if (frame.service == CEC_RX_PKT)
337 {
51b611bc 338 cec_command cmd;
4d3a7562 339
51b611bc 340 cec_command::Format(
341 cmd, initiator, destination,
4d3a7562 342 ( frame.size > 3 ) ? cec_opcode(frame.data[0]) : CEC_OPCODE_NONE);
343
51b611bc 344 for( uint8_t i = 1; i < frame.size-3; i++ )
4d3a7562 345 cmd.parameters.PushBack(frame.data[i]);
346
347 m_callback->OnCommandReceived(cmd);
348 }
349 else if (frame.service == CEC_ACK_PKT)
350 {
351 bHandled = false;
51b611bc 352 status = ( frame.size > 3 ) ? frame.data[0] : 255;
4d3a7562 353 opcode = ( frame.size > 4 ) ? frame.data[1] : (uint32_t)CEC_OPCODE_NONE;
354
355 m_messageMutex.Lock();
356 for (map<uint32_t, CAdapterMessageQueueEntry *>::iterator it = m_messages.begin();
51b611bc 357 !bHandled && it != m_messages.end(); it++)
358 {
4d3a7562 359 bHandled = it->second->CheckMatch(opcode, initiator, destination, status);
51b611bc 360 }
4d3a7562 361 m_messageMutex.Unlock();
362
363 if (!bHandled)
364 LIB_CEC->AddLog(CEC_LOG_WARNING, "%s: unhandled response received !", __func__);
365 }
366 }
367 }
368
369 return 0;
370}
371
51b611bc 372#endif // HAVE_TDA995X_API