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