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 | |
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 | |