936f31c3ee03cc5d85457254ea2374afc67f9211
[deb_libcec.git] / src / lib / adapter / TDA995x / TDA995xCECAdapterCommunication.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011-2013 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_TDA995X_API)
36 #include "TDA995xCECAdapterCommunication.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 // these are defined in nxp private header file
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*/
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*/
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*/
67
68
69 CTDA995xCECAdapterCommunication::CTDA995xCECAdapterCommunication(IAdapterCommunicationCallback *callback) :
70 IAdapterCommunication(callback),
71 m_bLogicalAddressChanged(false)
72 {
73 CLockObject lock(m_mutex);
74
75 m_iNextMessage = 0;
76 m_logicalAddresses.Clear();
77 m_dev = new CCDevSocket(CEC_TDA995x_PATH);
78 }
79
80
81 CTDA995xCECAdapterCommunication::~CTDA995xCECAdapterCommunication(void)
82 {
83 Close();
84
85 CLockObject lock(m_mutex);
86 delete m_dev;
87 m_dev = 0;
88 }
89
90
91 bool CTDA995xCECAdapterCommunication::IsOpen(void)
92 {
93 return IsInitialised() && m_dev->IsOpen();
94 }
95
96
97 bool CTDA995xCECAdapterCommunication::Open(uint32_t iTimeoutMs, bool UNUSED(bSkipChecks), bool bStartListening)
98 {
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,
122 "%s: CEC_IOCTL_GET_RAW_MODE not supported. Please update your kernel.", __func__);
123 }
124
125 m_dev->Close();
126 }
127
128 return false;
129 }
130
131
132 void CTDA995xCECAdapterCommunication::Close(void)
133 {
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
143 std::string CTDA995xCECAdapterCommunication::GetError(void) const
144 {
145 std::string strError(m_strError);
146 return strError;
147 }
148
149
150 cec_adapter_message_state CTDA995xCECAdapterCommunication::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;
156
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;
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
214 uint16_t CTDA995xCECAdapterCommunication::GetFirmwareVersion(void)
215 {
216 cec_sw_version vers = { 0 };
217
218 m_dev->Ioctl(CEC_IOCTL_GET_SW_VERSION, &vers);
219
220 return vers.majorVersionNr;
221 }
222
223
224 cec_vendor_id CTDA995xCECAdapterCommunication::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
234 return cec_vendor_id(info.VendorID);
235 }
236
237
238 uint16_t CTDA995xCECAdapterCommunication::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
248 return info.PhysicalAddress;
249 }
250
251
252 cec_logical_addresses CTDA995xCECAdapterCommunication::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
279 return m_logicalAddresses;
280 }
281
282
283 bool CTDA995xCECAdapterCommunication::SetLogicalAddresses(const cec_logical_addresses &addresses)
284 {
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
311 void CTDA995xCECAdapterCommunication::HandleLogicalAddressLost(cec_logical_address UNUSED(oldAddress))
312 {
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
322 void *CTDA995xCECAdapterCommunication::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
336 if (frame.service == CEC_RX_PKT)
337 {
338 cec_command cmd;
339
340 cec_command::Format(
341 cmd, initiator, destination,
342 ( frame.size > 3 ) ? cec_opcode(frame.data[0]) : CEC_OPCODE_NONE);
343
344 for( uint8_t i = 1; i < frame.size-3; i++ )
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;
352 status = ( frame.size > 3 ) ? frame.data[0] : 255;
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();
357 !bHandled && it != m_messages.end(); it++)
358 {
359 bHandled = it->second->CheckMatch(opcode, initiator, destination, status);
360 }
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
372 #endif // HAVE_TDA995X_API