7a4e019d0b7fdfb2a48968f9ff185c54c82ce2f0
[deb_libcec.git] / src / lib / CECProcessor.cpp
1 /*
2 * This file is part of the libCEC(R) library.
3 *
4 * libCEC(R) is Copyright (C) 2011 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 "CECProcessor.h"
34
35 #include "adapter/AdapterMessage.h"
36 #include "devices/CECBusDevice.h"
37 #include "devices/CECAudioSystem.h"
38 #include "devices/CECPlaybackDevice.h"
39 #include "devices/CECRecordingDevice.h"
40 #include "devices/CECTuner.h"
41 #include "devices/CECTV.h"
42 #include "implementations/CECCommandHandler.h"
43 #include "LibCEC.h"
44
45 using namespace CEC;
46 using namespace std;
47 using namespace PLATFORM;
48
49 CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, cec_logical_address iLogicalAddress /* = CECDEVICE_PLAYBACKDEVICE1 */, uint16_t iPhysicalAddress /* = CEC_DEFAULT_PHYSICAL_ADDRESS*/) :
50 m_bStarted(false),
51 m_bInitialised(false),
52 m_iHDMIPort(CEC_DEFAULT_HDMI_PORT),
53 m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE),
54 m_lastInitiator(CECDEVICE_UNKNOWN),
55 m_strDeviceName(strDeviceName),
56 m_controller(controller),
57 m_bMonitor(false),
58 m_iStandardLineTimeout(3),
59 m_iRetryLineTimeout(3),
60 m_iLastTransmission(0)
61 {
62 m_communication = new CAdapterCommunication(this);
63 m_logicalAddresses.Clear();
64 m_logicalAddresses.Set(iLogicalAddress);
65 m_types.clear();
66 for (int iPtr = 0; iPtr <= 16; iPtr++)
67 m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr, iPtr == iLogicalAddress ? iPhysicalAddress : 0);
68 }
69
70 CCECProcessor::CCECProcessor(CLibCEC *controller, const char *strDeviceName, const cec_device_type_list &types) :
71 m_bStarted(false),
72 m_bInitialised(false),
73 m_iHDMIPort(CEC_DEFAULT_HDMI_PORT),
74 m_iBaseDevice((cec_logical_address)CEC_DEFAULT_BASE_DEVICE),
75 m_strDeviceName(strDeviceName),
76 m_types(types),
77 m_controller(controller),
78 m_bMonitor(false),
79 m_iStandardLineTimeout(3),
80 m_iRetryLineTimeout(3),
81 m_iLastTransmission(0)
82 {
83 m_communication = new CAdapterCommunication(this);
84 m_logicalAddresses.Clear();
85 for (int iPtr = 0; iPtr < 16; iPtr++)
86 {
87 switch(iPtr)
88 {
89 case CECDEVICE_AUDIOSYSTEM:
90 m_busDevices[iPtr] = new CCECAudioSystem(this, (cec_logical_address) iPtr, 0xFFFF);
91 break;
92 case CECDEVICE_PLAYBACKDEVICE1:
93 case CECDEVICE_PLAYBACKDEVICE2:
94 case CECDEVICE_PLAYBACKDEVICE3:
95 m_busDevices[iPtr] = new CCECPlaybackDevice(this, (cec_logical_address) iPtr, 0xFFFF);
96 break;
97 case CECDEVICE_RECORDINGDEVICE1:
98 case CECDEVICE_RECORDINGDEVICE2:
99 case CECDEVICE_RECORDINGDEVICE3:
100 m_busDevices[iPtr] = new CCECRecordingDevice(this, (cec_logical_address) iPtr, 0xFFFF);
101 break;
102 case CECDEVICE_TUNER1:
103 case CECDEVICE_TUNER2:
104 case CECDEVICE_TUNER3:
105 case CECDEVICE_TUNER4:
106 m_busDevices[iPtr] = new CCECTuner(this, (cec_logical_address) iPtr, 0xFFFF);
107 break;
108 case CECDEVICE_TV:
109 m_busDevices[iPtr] = new CCECTV(this, (cec_logical_address) iPtr, 0);
110 break;
111 default:
112 m_busDevices[iPtr] = new CCECBusDevice(this, (cec_logical_address) iPtr, 0xFFFF);
113 break;
114 }
115 }
116 }
117
118 CCECProcessor::~CCECProcessor(void)
119 {
120 m_bStarted = false;
121 m_startCondition.Broadcast();
122 StopThread();
123
124 delete m_communication;
125 m_communication = NULL;
126 m_controller = NULL;
127 for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
128 delete m_busDevices[iPtr];
129 }
130
131 bool CCECProcessor::OpenConnection(const char *strPort, uint16_t iBaudRate, uint32_t iTimeoutMs)
132 {
133 bool bReturn(false);
134 CLockObject lock(m_mutex);
135 if (!m_communication)
136 {
137 CLibCEC::AddLog(CEC_LOG_ERROR, "no connection handler found");
138 return bReturn;
139 }
140
141 /* check for an already opened connection */
142 if (m_communication->IsOpen())
143 {
144 CLibCEC::AddLog(CEC_LOG_ERROR, "connection already opened");
145 return bReturn;
146 }
147
148 /* open a new connection */
149 if ((bReturn = m_communication->Open(strPort, iBaudRate, iTimeoutMs)) == false)
150 CLibCEC::AddLog(CEC_LOG_ERROR, "could not open a connection");
151
152 /* try to ping the adapter */
153 if ((bReturn = m_communication->PingAdapter()) == false)
154 CLibCEC::AddLog(CEC_LOG_ERROR, "the adapter does not respond correctly");
155
156 uint16_t iFirmwareVersion = m_communication->GetFirmwareVersion();
157 if ((bReturn = (iFirmwareVersion != CEC_FW_VERSION_UNKNOWN)) == false)
158 m_controller->AddLog(CEC_LOG_ERROR, "the adapter is running an unknown firmware version");
159
160 CLibCEC::AddLog(CEC_LOG_NOTICE, "CEC Adapter firmware version: %d", iFirmwareVersion);
161
162 return bReturn;
163 }
164
165 void CCECProcessor::SetInitialised(bool bSetTo /* = true */)
166 {
167 CLockObject lock(m_mutex);
168 m_bInitialised = bSetTo;
169 }
170
171 bool CCECProcessor::Initialise(void)
172 {
173 bool bReturn(false);
174 {
175 CLockObject lock(m_mutex);
176 if (!m_logicalAddresses.IsEmpty())
177 m_logicalAddresses.Clear();
178
179 if (!FindLogicalAddresses())
180 {
181 CLibCEC::AddLog(CEC_LOG_ERROR, "could not detect our logical addresses");
182 return bReturn;
183 }
184
185 /* only set our OSD name for the primary device */
186 m_busDevices[m_logicalAddresses.primary]->m_strDeviceName = m_strDeviceName;
187 }
188
189 /* get the vendor id from the TV, so we are using the correct handler */
190 m_busDevices[CECDEVICE_TV]->RequestVendorId();
191 ReplaceHandlers();
192
193 if ((bReturn = SetHDMIPort(m_iBaseDevice, m_iHDMIPort, true)) == false)
194 CLibCEC::AddLog(CEC_LOG_ERROR, "unable to set HDMI port %d on %s (%x)", m_iHDMIPort, ToString(m_iBaseDevice), (uint8_t)m_iBaseDevice);
195
196 SetInitialised(bReturn);
197
198 return bReturn;
199 }
200
201 bool CCECProcessor::Start(const char *strPort, uint16_t iBaudRate /* = 38400 */, uint32_t iTimeoutMs /* = 10000 */)
202 {
203 bool bReturn(false);
204
205 {
206 CLockObject lock(m_mutex);
207 if (!OpenConnection(strPort, iBaudRate, iTimeoutMs))
208 return bReturn;
209
210 /* create the processor thread */
211 if (!CreateThread() || !m_startCondition.Wait(m_mutex) || !m_bStarted)
212 {
213 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
214 return bReturn;
215 }
216 }
217
218 if ((bReturn = Initialise()) == false)
219 {
220 CLibCEC::AddLog(CEC_LOG_ERROR, "could not create a processor thread");
221 StopThread(true);
222 }
223 else
224 {
225 CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
226 }
227
228 return bReturn;
229 }
230
231 bool CCECProcessor::TryLogicalAddress(cec_logical_address address)
232 {
233 if (m_busDevices[address]->TryLogicalAddress())
234 {
235 m_logicalAddresses.Set(address);
236 return true;
237 }
238
239 return false;
240 }
241
242 bool CCECProcessor::FindLogicalAddressRecordingDevice(void)
243 {
244 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'recording device'");
245 return TryLogicalAddress(CECDEVICE_RECORDINGDEVICE1) ||
246 TryLogicalAddress(CECDEVICE_RECORDINGDEVICE2) ||
247 TryLogicalAddress(CECDEVICE_RECORDINGDEVICE3);
248 }
249
250 bool CCECProcessor::FindLogicalAddressTuner(void)
251 {
252 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'tuner'");
253 return TryLogicalAddress(CECDEVICE_TUNER1) ||
254 TryLogicalAddress(CECDEVICE_TUNER2) ||
255 TryLogicalAddress(CECDEVICE_TUNER3) ||
256 TryLogicalAddress(CECDEVICE_TUNER4);
257 }
258
259 bool CCECProcessor::FindLogicalAddressPlaybackDevice(void)
260 {
261 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'playback device'");
262 return TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE1) ||
263 TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE2) ||
264 TryLogicalAddress(CECDEVICE_PLAYBACKDEVICE3);
265 }
266
267 bool CCECProcessor::FindLogicalAddressAudioSystem(void)
268 {
269 CLibCEC::AddLog(CEC_LOG_DEBUG, "detecting logical address for type 'audio'");
270 return TryLogicalAddress(CECDEVICE_AUDIOSYSTEM);
271 }
272
273 bool CCECProcessor::ChangeDeviceType(cec_device_type from, cec_device_type to)
274 {
275 bool bChanged(false);
276
277 CLibCEC::AddLog(CEC_LOG_NOTICE, "changing device type '%s' into '%s'", ToString(from), ToString(to));
278
279 CLockObject lock(m_mutex);
280 CCECBusDevice *previousDevice = GetDeviceByType(from);
281 m_logicalAddresses.primary = CECDEVICE_UNKNOWN;
282
283 for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
284 {
285 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
286 continue;
287
288 if (m_types.types[iPtr] == from)
289 {
290 bChanged = true;
291 m_types.types[iPtr] = to;
292 }
293 else if (m_types.types[iPtr] == to && bChanged)
294 {
295 m_types.types[iPtr] = CEC_DEVICE_TYPE_RESERVED;
296 }
297 }
298
299 if (bChanged)
300 {
301 FindLogicalAddresses();
302
303 CCECBusDevice *newDevice = GetDeviceByType(to);
304 if (previousDevice && newDevice)
305 {
306 newDevice->SetDeviceStatus(CEC_DEVICE_STATUS_HANDLED_BY_LIBCEC);
307 previousDevice->SetDeviceStatus(CEC_DEVICE_STATUS_UNKNOWN);
308
309 newDevice->SetCecVersion(previousDevice->GetCecVersion(false));
310 previousDevice->SetCecVersion(CEC_VERSION_UNKNOWN);
311
312 newDevice->SetMenuLanguage(previousDevice->GetMenuLanguage(false));
313 cec_menu_language lang;
314 lang.device = previousDevice->GetLogicalAddress();
315 for (unsigned int iPtr = 0; iPtr < 4; iPtr++)
316 lang.language[iPtr] = '?';
317 lang.language[3] = 0;
318 previousDevice->SetMenuLanguage(lang);
319
320 newDevice->SetMenuState(previousDevice->GetMenuState());
321 previousDevice->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
322
323 newDevice->SetOSDName(previousDevice->GetOSDName(false));
324 previousDevice->SetOSDName(ToString(previousDevice->GetLogicalAddress()));
325
326 newDevice->SetPhysicalAddress(previousDevice->GetPhysicalAddress(false));
327 previousDevice->SetPhysicalAddress(0xFFFF);
328
329 newDevice->SetPowerStatus(previousDevice->GetPowerStatus(false));
330 previousDevice->SetPowerStatus(CEC_POWER_STATUS_UNKNOWN);
331
332 newDevice->SetVendorId(previousDevice->GetVendorId(false));
333 previousDevice->SetVendorId(CEC_VENDOR_UNKNOWN);
334
335 if ((from == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || from == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
336 (to == CEC_DEVICE_TYPE_PLAYBACK_DEVICE || to == CEC_DEVICE_TYPE_RECORDING_DEVICE))
337 {
338 ((CCECPlaybackDevice *) newDevice)->SetDeckControlMode(((CCECPlaybackDevice *) previousDevice)->GetDeckControlMode());
339 ((CCECPlaybackDevice *) previousDevice)->SetDeckControlMode(CEC_DECK_CONTROL_MODE_STOP);
340
341 ((CCECPlaybackDevice *) newDevice)->SetDeckStatus(((CCECPlaybackDevice *) previousDevice)->GetDeckStatus());
342 ((CCECPlaybackDevice *) previousDevice)->SetDeckStatus(CEC_DECK_INFO_STOP);
343 }
344 }
345 }
346
347 return true;
348 }
349
350 bool CCECProcessor::FindLogicalAddresses(void)
351 {
352 bool bReturn(true);
353 m_logicalAddresses.Clear();
354
355 for (unsigned int iPtr = 0; iPtr < 5; iPtr++)
356 {
357 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RESERVED)
358 continue;
359
360 CLibCEC::AddLog(CEC_LOG_DEBUG, "%s - device %d: type %d", __FUNCTION__, iPtr, m_types.types[iPtr]);
361
362 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_RECORDING_DEVICE)
363 bReturn &= FindLogicalAddressRecordingDevice();
364 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_TUNER)
365 bReturn &= FindLogicalAddressTuner();
366 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_PLAYBACK_DEVICE)
367 bReturn &= FindLogicalAddressPlaybackDevice();
368 if (m_types.types[iPtr] == CEC_DEVICE_TYPE_AUDIO_SYSTEM)
369 bReturn &= FindLogicalAddressAudioSystem();
370 }
371
372 if (bReturn)
373 SetAckMask(m_logicalAddresses.AckMask());
374
375 return bReturn;
376 }
377
378 void CCECProcessor::ReplaceHandlers(void)
379 {
380 for (uint8_t iPtr = 0; iPtr <= CECDEVICE_PLAYBACKDEVICE3; iPtr++)
381 m_busDevices[iPtr]->ReplaceHandler(m_bInitialised);
382 }
383
384 void *CCECProcessor::Process(void)
385 {
386 bool bParseFrame(false);
387 cec_command command;
388 CCECAdapterMessage msg;
389
390 {
391 CLockObject lock(m_mutex);
392 m_bStarted = true;
393 CLibCEC::AddLog(CEC_LOG_DEBUG, "processor thread started");
394 m_startCondition.Signal();
395 }
396
397 while (!IsStopped())
398 {
399 ReplaceHandlers();
400 command.Clear();
401 msg.Clear();
402
403 {
404 CLockObject lock(m_mutex);
405 if (m_commandBuffer.Pop(command))
406 {
407 bParseFrame = true;
408 }
409 else if (m_communication->IsOpen() && m_communication->Read(msg, 50))
410 {
411 if ((bParseFrame = (ParseMessage(msg) && !IsStopped())) == true)
412 command = m_currentframe;
413 }
414 }
415
416 if (bParseFrame)
417 ParseCommand(command);
418 bParseFrame = false;
419
420 Sleep(5);
421
422 m_controller->CheckKeypressTimeout();
423 }
424
425 if (m_communication)
426 m_communication->Close();
427
428 return NULL;
429 }
430
431 bool CCECProcessor::SetActiveSource(cec_device_type type /* = CEC_DEVICE_TYPE_RESERVED */)
432 {
433 bool bReturn(false);
434
435 if (!IsRunning())
436 return bReturn;
437
438 cec_logical_address addr = m_logicalAddresses.primary;
439
440 if (type != CEC_DEVICE_TYPE_RESERVED)
441 {
442 for (uint8_t iPtr = 0; iPtr <= 11; iPtr++)
443 {
444 if (m_logicalAddresses[iPtr] && m_busDevices[iPtr]->m_type == type)
445 {
446 addr = (cec_logical_address) iPtr;
447 break;
448 }
449 }
450 }
451
452 m_busDevices[addr]->SetActiveSource();
453 if (m_busDevices[addr]->GetPhysicalAddress(false) != 0xFFFF)
454 {
455 bReturn = m_busDevices[addr]->TransmitActiveSource();
456
457 if (bReturn && (m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_PLAYBACK_DEVICE ||
458 m_busDevices[addr]->GetType() == CEC_DEVICE_TYPE_RECORDING_DEVICE) &&
459 m_busDevices[addr]->GetHandler()->SendDeckStatusUpdateOnActiveSource())
460 {
461 bReturn = ((CCECPlaybackDevice *)m_busDevices[addr])->TransmitDeckStatus(CECDEVICE_TV);
462 }
463 }
464
465 return bReturn;
466 }
467
468 bool CCECProcessor::SetActiveSource(uint16_t iStreamPath)
469 {
470 bool bReturn(false);
471
472 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamPath);
473 if (device)
474 {
475 device->SetActiveSource();
476 bReturn = true;
477 }
478
479 return bReturn;
480 }
481
482 void CCECProcessor::SetStandardLineTimeout(uint8_t iTimeout)
483 {
484 CLockObject lock(m_mutex);
485 m_iStandardLineTimeout = iTimeout;
486 }
487
488 void CCECProcessor::SetRetryLineTimeout(uint8_t iTimeout)
489 {
490 CLockObject lock(m_mutex);
491 m_iRetryLineTimeout = iTimeout;
492 }
493
494 bool CCECProcessor::SetActiveView(void)
495 {
496 return SetActiveSource(m_types.IsEmpty() ? CEC_DEVICE_TYPE_RESERVED : m_types[0]);
497 }
498
499 bool CCECProcessor::SetDeckControlMode(cec_deck_control_mode mode, bool bSendUpdate /* = true */)
500 {
501 bool bReturn(false);
502
503 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
504 if (device)
505 {
506 ((CCECPlaybackDevice *) device)->SetDeckControlMode(mode);
507 if (bSendUpdate)
508 ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
509 bReturn = true;
510 }
511
512 return bReturn;
513 }
514
515 bool CCECProcessor::SetDeckInfo(cec_deck_info info, bool bSendUpdate /* = true */)
516 {
517 bool bReturn(false);
518
519 CCECBusDevice *device = GetDeviceByType(CEC_DEVICE_TYPE_PLAYBACK_DEVICE);
520 if (device)
521 {
522 ((CCECPlaybackDevice *) device)->SetDeckStatus(info);
523 if (bSendUpdate)
524 ((CCECPlaybackDevice *) device)->TransmitDeckStatus(CECDEVICE_TV);
525 bReturn = true;
526 }
527
528 return bReturn;
529 }
530
531 bool CCECProcessor::SetHDMIPort(cec_logical_address iBaseDevice, uint8_t iPort, bool bForce /* = false */)
532 {
533 bool bReturn(false);
534 CLockObject lock(m_mutex);
535
536 m_iBaseDevice = iBaseDevice;
537 m_iHDMIPort = iPort;
538 if (!m_bStarted && !bForce)
539 return true;
540
541 CLibCEC::AddLog(CEC_LOG_DEBUG, "setting HDMI port to %d on device %s (%d)", iPort, ToString(iBaseDevice), (int)iBaseDevice);
542
543 uint16_t iPhysicalAddress(0);
544 if (iBaseDevice > CECDEVICE_TV)
545 {
546 lock.Unlock();
547 iPhysicalAddress = m_busDevices[iBaseDevice]->GetPhysicalAddress();
548 lock.Lock();
549 }
550
551 if (iPhysicalAddress < 0xffff)
552 {
553 if (iPhysicalAddress == 0)
554 iPhysicalAddress += 0x1000 * iPort;
555 else if (iPhysicalAddress % 0x1000 == 0)
556 iPhysicalAddress += 0x100 * iPort;
557 else if (iPhysicalAddress % 0x100 == 0)
558 iPhysicalAddress += 0x10 * iPort;
559 else if (iPhysicalAddress % 0x10 == 0)
560 iPhysicalAddress += iPort;
561
562 bReturn = true;
563 }
564
565 if (!bReturn)
566 CLibCEC::AddLog(CEC_LOG_ERROR, "failed to set the physical address");
567 else
568 {
569 lock.Unlock();
570 SetPhysicalAddress(iPhysicalAddress);
571 }
572
573 return bReturn;
574 }
575
576 bool CCECProcessor::PhysicalAddressInUse(uint16_t iPhysicalAddress)
577 {
578 for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
579 {
580 if (m_busDevices[iPtr]->GetPhysicalAddress(false) == iPhysicalAddress)
581 return true;
582 }
583 return false;
584 }
585
586 bool CCECProcessor::TransmitInactiveSource(void)
587 {
588 if (!IsRunning())
589 return false;
590
591 if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary])
592 return m_busDevices[m_logicalAddresses.primary]->TransmitInactiveSource();
593 return false;
594 }
595
596 void CCECProcessor::LogOutput(const cec_command &data)
597 {
598 CStdString strTx;
599 strTx.Format("<< %02x", ((uint8_t)data.initiator << 4) + (uint8_t)data.destination);
600 if (data.opcode_set)
601 strTx.AppendFormat(":%02x", (uint8_t)data.opcode);
602
603 for (uint8_t iPtr = 0; iPtr < data.parameters.size; iPtr++)
604 strTx.AppendFormat(":%02x", data.parameters[iPtr]);
605 CLibCEC::AddLog(CEC_LOG_TRAFFIC, strTx.c_str());
606 }
607
608 bool CCECProcessor::SetLogicalAddress(cec_logical_address iLogicalAddress)
609 {
610 CLockObject lock(m_mutex);
611 if (m_logicalAddresses.primary != iLogicalAddress)
612 {
613 CLibCEC::AddLog(CEC_LOG_NOTICE, "<< setting primary logical address to %1x", iLogicalAddress);
614 m_logicalAddresses.primary = iLogicalAddress;
615 m_logicalAddresses.Set(iLogicalAddress);
616 return SetAckMask(m_logicalAddresses.AckMask());
617 }
618
619 return true;
620 }
621
622 bool CCECProcessor::SetMenuState(cec_menu_state state, bool bSendUpdate /* = true */)
623 {
624 for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
625 {
626 if (m_logicalAddresses[iPtr])
627 m_busDevices[iPtr]->SetMenuState(state);
628 }
629
630 if (bSendUpdate)
631 m_busDevices[m_logicalAddresses.primary]->TransmitMenuState(CECDEVICE_TV);
632
633 return true;
634 }
635
636 bool CCECProcessor::SetPhysicalAddress(uint16_t iPhysicalAddress, bool bSendUpdate /* = true */)
637 {
638 bool bSendActiveView(false);
639 bool bReturn(false);
640 cec_logical_addresses sendUpdatesTo;
641
642 {
643 CLockObject lock(m_mutex);
644 if (!m_logicalAddresses.IsEmpty())
645 {
646 bool bWasActiveSource(false);
647 for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
648 if (m_logicalAddresses[iPtr])
649 {
650 bWasActiveSource |= m_busDevices[iPtr]->IsActiveSource();
651 m_busDevices[iPtr]->SetInactiveSource();
652 m_busDevices[iPtr]->SetPhysicalAddress(iPhysicalAddress);
653 if (bSendUpdate)
654 sendUpdatesTo.Set((cec_logical_address)iPtr);
655 }
656
657 bSendActiveView = bWasActiveSource && bSendUpdate;
658 bReturn = true;
659 }
660 }
661
662 for (uint8_t iPtr = 0; iPtr < 15; iPtr++)
663 if (sendUpdatesTo[iPtr])
664 m_busDevices[iPtr]->TransmitPhysicalAddress();
665
666 if (bSendActiveView)
667 SetActiveView();
668
669 return bReturn;
670 }
671
672 bool CCECProcessor::SwitchMonitoring(bool bEnable)
673 {
674 CLibCEC::AddLog(CEC_LOG_NOTICE, "== %s monitoring mode ==", bEnable ? "enabling" : "disabling");
675
676 {
677 CLockObject lock(m_mutex);
678 m_bMonitor = bEnable;
679 }
680
681 if (bEnable)
682 return SetAckMask(0);
683 else
684 return SetAckMask(m_logicalAddresses.AckMask());
685 }
686
687 bool CCECProcessor::PollDevice(cec_logical_address iAddress)
688 {
689 if (iAddress != CECDEVICE_UNKNOWN && m_busDevices[iAddress])
690 {
691 return m_logicalAddresses.primary == CECDEVICE_UNKNOWN ?
692 m_busDevices[iAddress]->TransmitPoll(iAddress) :
693 m_busDevices[m_logicalAddresses.primary]->TransmitPoll(iAddress);
694 }
695 return false;
696 }
697
698 uint8_t CCECProcessor::VolumeUp(bool bSendRelease /* = true */)
699 {
700 uint8_t status = 0;
701 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
702 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeUp(bSendRelease);
703
704 return status;
705 }
706
707 uint8_t CCECProcessor::VolumeDown(bool bSendRelease /* = true */)
708 {
709 uint8_t status = 0;
710 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
711 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->VolumeDown(bSendRelease);
712
713 return status;
714 }
715
716 uint8_t CCECProcessor::MuteAudio(bool bSendRelease /* = true */)
717 {
718 uint8_t status = 0;
719 if (IsPresentDevice(CECDEVICE_AUDIOSYSTEM))
720 status = ((CCECAudioSystem *)m_busDevices[CECDEVICE_AUDIOSYSTEM])->MuteAudio(bSendRelease);
721
722 return status;
723 }
724
725 CCECBusDevice *CCECProcessor::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress, bool bRefresh /* = false */) const
726 {
727 if (m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false) == iPhysicalAddress)
728 return m_busDevices[m_logicalAddresses.primary];
729
730 CCECBusDevice *device = NULL;
731 for (unsigned int iPtr = 0; iPtr < 16; iPtr++)
732 {
733 if (m_busDevices[iPtr]->GetPhysicalAddress(bRefresh) == iPhysicalAddress)
734 {
735 device = m_busDevices[iPtr];
736 break;
737 }
738 }
739
740 return device;
741 }
742
743 CCECBusDevice *CCECProcessor::GetDeviceByType(cec_device_type type) const
744 {
745 CCECBusDevice *device = NULL;
746
747 for (uint8_t iPtr = 0; iPtr < 16; iPtr++)
748 {
749 if (m_busDevices[iPtr]->m_type == type && m_logicalAddresses[iPtr])
750 {
751 device = m_busDevices[iPtr];
752 break;
753 }
754 }
755
756 return device;
757 }
758
759 CCECBusDevice *CCECProcessor::GetPrimaryDevice(void) const
760 {
761 CCECBusDevice *device(NULL);
762 cec_logical_address primary = m_logicalAddresses.primary;
763 if (primary != CECDEVICE_UNKNOWN)
764 device = m_busDevices[primary];
765 return device;
766 }
767
768 cec_version CCECProcessor::GetDeviceCecVersion(cec_logical_address iAddress)
769 {
770 return m_busDevices[iAddress]->GetCecVersion();
771 }
772
773 cec_osd_name CCECProcessor::GetDeviceOSDName(cec_logical_address iAddress)
774 {
775 CStdString strOSDName = m_busDevices[iAddress]->GetOSDName();
776 cec_osd_name retVal;
777
778 snprintf(retVal.name, sizeof(retVal.name), "%s", strOSDName.c_str());
779 retVal.device = iAddress;
780
781 return retVal;
782 }
783
784 bool CCECProcessor::GetDeviceMenuLanguage(cec_logical_address iAddress, cec_menu_language *language)
785 {
786 if (m_busDevices[iAddress])
787 {
788 *language = m_busDevices[iAddress]->GetMenuLanguage();
789 return (strcmp(language->language, "???") != 0);
790 }
791 return false;
792 }
793
794 uint64_t CCECProcessor::GetDeviceVendorId(cec_logical_address iAddress)
795 {
796 if (m_busDevices[iAddress])
797 return m_busDevices[iAddress]->GetVendorId();
798 return false;
799 }
800
801 uint16_t CCECProcessor::GetDevicePhysicalAddress(cec_logical_address iAddress)
802 {
803 if (m_busDevices[iAddress])
804 return m_busDevices[iAddress]->GetPhysicalAddress(false);
805 return false;
806 }
807
808 cec_power_status CCECProcessor::GetDevicePowerStatus(cec_logical_address iAddress)
809 {
810 if (m_busDevices[iAddress])
811 return m_busDevices[iAddress]->GetPowerStatus();
812 return CEC_POWER_STATUS_UNKNOWN;
813 }
814
815 cec_logical_address CCECProcessor::GetActiveSource(void)
816 {
817 for (uint8_t iPtr = 0; iPtr <= 11; iPtr++)
818 {
819 if (m_busDevices[iPtr]->IsActiveSource())
820 return (cec_logical_address)iPtr;
821 }
822
823 return CECDEVICE_UNKNOWN;
824 }
825
826 bool CCECProcessor::IsActiveSource(cec_logical_address iAddress)
827 {
828 return m_busDevices[iAddress]->IsActiveSource();
829 }
830
831 bool CCECProcessor::Transmit(const cec_command &data)
832 {
833 bool bReturn(false);
834 LogOutput(data);
835
836 CCECAdapterMessage *output = new CCECAdapterMessage(data);
837
838 /* set the number of retries */
839 if (data.opcode == CEC_OPCODE_NONE)
840 output->maxTries = 1;
841 else if (data.initiator != CECDEVICE_BROADCAST)
842 output->maxTries = m_busDevices[data.initiator]->GetHandler()->GetTransmitRetries() + 1;
843
844 bReturn = Transmit(output);
845
846 /* set to "not present" on failed ack */
847 if (output->state == ADAPTER_MESSAGE_STATE_SENT_NOT_ACKED &&
848 output->Destination() != CECDEVICE_BROADCAST)
849 m_busDevices[output->Destination()]->SetDeviceStatus(CEC_DEVICE_STATUS_NOT_PRESENT);
850
851 delete output;
852 return bReturn;
853 }
854
855 bool CCECProcessor::Transmit(CCECAdapterMessage *output)
856 {
857 bool bReturn(false);
858 CLockObject lock(m_mutex);
859 {
860 if (!m_communication)
861 return bReturn;
862
863 m_iLastTransmission = GetTimeMs();
864 m_communication->SetLineTimeout(m_iStandardLineTimeout);
865 output->tries = 0;
866
867 do
868 {
869 if (output->tries > 0)
870 m_communication->SetLineTimeout(m_iRetryLineTimeout);
871 bReturn = m_communication->Write(output);
872 }while (!bReturn && output->transmit_timeout > 0 && output->NeedsRetry() && ++output->tries < output->maxTries);
873 }
874
875 m_communication->SetLineTimeout(m_iStandardLineTimeout);
876
877 return bReturn;
878 }
879
880 void CCECProcessor::TransmitAbort(cec_logical_address address, cec_opcode opcode, cec_abort_reason reason /* = CEC_ABORT_REASON_UNRECOGNIZED_OPCODE */)
881 {
882 CLibCEC::AddLog(CEC_LOG_DEBUG, "<< transmitting abort message");
883
884 cec_command command;
885 // TODO
886 cec_command::Format(command, m_logicalAddresses.primary, address, CEC_OPCODE_FEATURE_ABORT);
887 command.parameters.PushBack((uint8_t)opcode);
888 command.parameters.PushBack((uint8_t)reason);
889
890 Transmit(command);
891 }
892
893 bool CCECProcessor::ParseMessage(const CCECAdapterMessage &msg)
894 {
895 bool bEom(false);
896 bool bIsError(msg.IsError());
897
898 if (msg.IsEmpty())
899 return bEom;
900
901 switch(msg.Message())
902 {
903 case MSGCODE_FRAME_START:
904 {
905 m_currentframe.Clear();
906 if (msg.Size() >= 2)
907 {
908 m_currentframe.initiator = msg.Initiator();
909 m_currentframe.destination = msg.Destination();
910 m_currentframe.ack = msg.IsACK();
911 m_currentframe.eom = msg.IsEOM();
912 }
913 if (m_currentframe.ack == 0x1)
914 {
915 m_lastInitiator = m_currentframe.initiator;
916 m_busDevices[m_lastInitiator]->GetHandler()->HandlePoll(m_currentframe.initiator, m_currentframe.destination);
917 }
918 }
919 break;
920 case MSGCODE_RECEIVE_FAILED:
921 {
922 if (m_lastInitiator != CECDEVICE_UNKNOWN)
923 bIsError = m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed();
924 }
925 break;
926 case MSGCODE_FRAME_DATA:
927 {
928 if (msg.Size() >= 2)
929 {
930 m_currentframe.PushBack(msg[1]);
931 m_currentframe.eom = msg.IsEOM();
932 }
933 bEom = msg.IsEOM();
934 }
935 break;
936 default:
937 break;
938 }
939
940 CLibCEC::AddLog(bIsError ? CEC_LOG_WARNING : CEC_LOG_DEBUG, msg.ToString());
941 return bEom;
942 }
943
944 void CCECProcessor::ParseCommand(cec_command &command)
945 {
946 CStdString dataStr;
947 dataStr.Format(">> %1x%1x:%02x", command.initiator, command.destination, command.opcode);
948 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
949 dataStr.AppendFormat(":%02x", (unsigned int)command.parameters[iPtr]);
950 CLibCEC::AddLog(CEC_LOG_TRAFFIC, dataStr.c_str());
951
952 if (!m_bMonitor && command.initiator >= CECDEVICE_TV && command.initiator <= CECDEVICE_BROADCAST)
953 m_busDevices[(uint8_t)command.initiator]->HandleCommand(command);
954 }
955
956 cec_logical_addresses CCECProcessor::GetActiveDevices(void)
957 {
958 cec_logical_addresses addresses;
959 addresses.Clear();
960 for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
961 {
962 if (m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
963 addresses.Set((cec_logical_address) iPtr);
964 }
965 return addresses;
966 }
967
968 bool CCECProcessor::IsPresentDevice(cec_logical_address address)
969 {
970 return m_busDevices[address]->GetStatus() == CEC_DEVICE_STATUS_PRESENT;
971 }
972
973 bool CCECProcessor::IsPresentDeviceType(cec_device_type type)
974 {
975 for (unsigned int iPtr = 0; iPtr < 15; iPtr++)
976 {
977 if (m_busDevices[iPtr]->GetType() == type && m_busDevices[iPtr]->GetStatus() == CEC_DEVICE_STATUS_PRESENT)
978 return true;
979 }
980
981 return false;
982 }
983
984 uint16_t CCECProcessor::GetPhysicalAddress(void) const
985 {
986 if (!m_logicalAddresses.IsEmpty() && m_busDevices[m_logicalAddresses.primary])
987 return m_busDevices[m_logicalAddresses.primary]->GetPhysicalAddress(false);
988 return false;
989 }
990
991 void CCECProcessor::SetCurrentButton(cec_user_control_code iButtonCode)
992 {
993 m_controller->SetCurrentButton(iButtonCode);
994 }
995
996 void CCECProcessor::AddCommand(const cec_command &command)
997 {
998 m_controller->AddCommand(command);
999 }
1000
1001 void CCECProcessor::AddKey(cec_keypress &key)
1002 {
1003 m_controller->AddKey(key);
1004 }
1005
1006 void CCECProcessor::AddKey(void)
1007 {
1008 m_controller->AddKey();
1009 }
1010
1011 bool CCECProcessor::SetAckMask(uint16_t iMask)
1012 {
1013 return m_communication->SetAckMask(iMask);
1014 }
1015
1016 bool CCECProcessor::TransmitKeypress(cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
1017 {
1018 return m_busDevices[iDestination]->TransmitKeypress(key, bWait);
1019 }
1020
1021 bool CCECProcessor::TransmitKeyRelease(cec_logical_address iDestination, bool bWait /* = true */)
1022 {
1023 return m_busDevices[iDestination]->TransmitKeyRelease(bWait);
1024 }
1025
1026 const char *CCECProcessor::ToString(const cec_device_type type)
1027 {
1028 switch (type)
1029 {
1030 case CEC_DEVICE_TYPE_AUDIO_SYSTEM:
1031 return "audio system";
1032 case CEC_DEVICE_TYPE_PLAYBACK_DEVICE:
1033 return "playback device";
1034 case CEC_DEVICE_TYPE_RECORDING_DEVICE:
1035 return "recording device";
1036 case CEC_DEVICE_TYPE_RESERVED:
1037 return "reserved";
1038 case CEC_DEVICE_TYPE_TUNER:
1039 return "tuner";
1040 case CEC_DEVICE_TYPE_TV:
1041 return "TV";
1042 default:
1043 return "unknown";
1044 }
1045 }
1046
1047 const char *CCECProcessor::ToString(const cec_menu_state state)
1048 {
1049 switch (state)
1050 {
1051 case CEC_MENU_STATE_ACTIVATED:
1052 return "activated";
1053 case CEC_MENU_STATE_DEACTIVATED:
1054 return "deactivated";
1055 default:
1056 return "unknown";
1057 }
1058 }
1059
1060 const char *CCECProcessor::ToString(const cec_version version)
1061 {
1062 switch (version)
1063 {
1064 case CEC_VERSION_1_2:
1065 return "1.2";
1066 case CEC_VERSION_1_2A:
1067 return "1.2a";
1068 case CEC_VERSION_1_3:
1069 return "1.3";
1070 case CEC_VERSION_1_3A:
1071 return "1.3a";
1072 case CEC_VERSION_1_4:
1073 return "1.4";
1074 default:
1075 return "unknown";
1076 }
1077 }
1078
1079 const char *CCECProcessor::ToString(const cec_power_status status)
1080 {
1081 switch (status)
1082 {
1083 case CEC_POWER_STATUS_ON:
1084 return "on";
1085 case CEC_POWER_STATUS_STANDBY:
1086 return "standby";
1087 case CEC_POWER_STATUS_IN_TRANSITION_ON_TO_STANDBY:
1088 return "in transition from on to standby";
1089 case CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON:
1090 return "in transition from standby to on";
1091 default:
1092 return "unknown";
1093 }
1094 }
1095
1096 const char *CCECProcessor::ToString(const cec_logical_address address)
1097 {
1098 switch(address)
1099 {
1100 case CECDEVICE_AUDIOSYSTEM:
1101 return "Audio";
1102 case CECDEVICE_BROADCAST:
1103 return "Broadcast";
1104 case CECDEVICE_FREEUSE:
1105 return "Free use";
1106 case CECDEVICE_PLAYBACKDEVICE1:
1107 return "Playback 1";
1108 case CECDEVICE_PLAYBACKDEVICE2:
1109 return "Playback 2";
1110 case CECDEVICE_PLAYBACKDEVICE3:
1111 return "Playback 3";
1112 case CECDEVICE_RECORDINGDEVICE1:
1113 return "Recorder 1";
1114 case CECDEVICE_RECORDINGDEVICE2:
1115 return "Recorder 2";
1116 case CECDEVICE_RECORDINGDEVICE3:
1117 return "Recorder 3";
1118 case CECDEVICE_RESERVED1:
1119 return "Reserved 1";
1120 case CECDEVICE_RESERVED2:
1121 return "Reserved 2";
1122 case CECDEVICE_TUNER1:
1123 return "Tuner 1";
1124 case CECDEVICE_TUNER2:
1125 return "Tuner 2";
1126 case CECDEVICE_TUNER3:
1127 return "Tuner 3";
1128 case CECDEVICE_TUNER4:
1129 return "Tuner 4";
1130 case CECDEVICE_TV:
1131 return "TV";
1132 default:
1133 return "unknown";
1134 }
1135 }
1136
1137 const char *CCECProcessor::ToString(const cec_deck_control_mode mode)
1138 {
1139 switch (mode)
1140 {
1141 case CEC_DECK_CONTROL_MODE_SKIP_FORWARD_WIND:
1142 return "skip forward wind";
1143 case CEC_DECK_CONTROL_MODE_EJECT:
1144 return "eject";
1145 case CEC_DECK_CONTROL_MODE_SKIP_REVERSE_REWIND:
1146 return "reverse rewind";
1147 case CEC_DECK_CONTROL_MODE_STOP:
1148 return "stop";
1149 default:
1150 return "unknown";
1151 }
1152 }
1153
1154 const char *CCECProcessor::ToString(const cec_deck_info status)
1155 {
1156 switch (status)
1157 {
1158 case CEC_DECK_INFO_PLAY:
1159 return "play";
1160 case CEC_DECK_INFO_RECORD:
1161 return "record";
1162 case CEC_DECK_INFO_PLAY_REVERSE:
1163 return "play reverse";
1164 case CEC_DECK_INFO_STILL:
1165 return "still";
1166 case CEC_DECK_INFO_SLOW:
1167 return "slow";
1168 case CEC_DECK_INFO_SLOW_REVERSE:
1169 return "slow reverse";
1170 case CEC_DECK_INFO_FAST_FORWARD:
1171 return "fast forward";
1172 case CEC_DECK_INFO_FAST_REVERSE:
1173 return "fast reverse";
1174 case CEC_DECK_INFO_NO_MEDIA:
1175 return "no media";
1176 case CEC_DECK_INFO_STOP:
1177 return "stop";
1178 case CEC_DECK_INFO_SKIP_FORWARD_WIND:
1179 return "info skip forward wind";
1180 case CEC_DECK_INFO_SKIP_REVERSE_REWIND:
1181 return "info skip reverse rewind";
1182 case CEC_DECK_INFO_INDEX_SEARCH_FORWARD:
1183 return "info index search forward";
1184 case CEC_DECK_INFO_INDEX_SEARCH_REVERSE:
1185 return "info index search reverse";
1186 case CEC_DECK_INFO_OTHER_STATUS:
1187 return "other";
1188 default:
1189 return "unknown";
1190 }
1191 }
1192
1193 const char *CCECProcessor::ToString(const cec_opcode opcode)
1194 {
1195 switch (opcode)
1196 {
1197 case CEC_OPCODE_ACTIVE_SOURCE:
1198 return "active source";
1199 case CEC_OPCODE_IMAGE_VIEW_ON:
1200 return "image view on";
1201 case CEC_OPCODE_TEXT_VIEW_ON:
1202 return "text view on";
1203 case CEC_OPCODE_INACTIVE_SOURCE:
1204 return "inactive source";
1205 case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
1206 return "request active source";
1207 case CEC_OPCODE_ROUTING_CHANGE:
1208 return "routing change";
1209 case CEC_OPCODE_ROUTING_INFORMATION:
1210 return "routing information";
1211 case CEC_OPCODE_SET_STREAM_PATH:
1212 return "set stream path";
1213 case CEC_OPCODE_STANDBY:
1214 return "standby";
1215 case CEC_OPCODE_RECORD_OFF:
1216 return "record off";
1217 case CEC_OPCODE_RECORD_ON:
1218 return "record on";
1219 case CEC_OPCODE_RECORD_STATUS:
1220 return "record status";
1221 case CEC_OPCODE_RECORD_TV_SCREEN:
1222 return "record tv screen";
1223 case CEC_OPCODE_CLEAR_ANALOGUE_TIMER:
1224 return "clear analogue timer";
1225 case CEC_OPCODE_CLEAR_DIGITAL_TIMER:
1226 return "clear digital timer";
1227 case CEC_OPCODE_CLEAR_EXTERNAL_TIMER:
1228 return "clear external timer";
1229 case CEC_OPCODE_SET_ANALOGUE_TIMER:
1230 return "set analogue timer";
1231 case CEC_OPCODE_SET_DIGITAL_TIMER:
1232 return "set digital timer";
1233 case CEC_OPCODE_SET_EXTERNAL_TIMER:
1234 return "set external timer";
1235 case CEC_OPCODE_SET_TIMER_PROGRAM_TITLE:
1236 return "set timer program title";
1237 case CEC_OPCODE_TIMER_CLEARED_STATUS:
1238 return "timer cleared status";
1239 case CEC_OPCODE_TIMER_STATUS:
1240 return "timer status";
1241 case CEC_OPCODE_CEC_VERSION:
1242 return "cec version";
1243 case CEC_OPCODE_GET_CEC_VERSION:
1244 return "get cec version";
1245 case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
1246 return "give physical address";
1247 case CEC_OPCODE_GET_MENU_LANGUAGE:
1248 return "get menu language";
1249 case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
1250 return "report physical address";
1251 case CEC_OPCODE_SET_MENU_LANGUAGE:
1252 return "set menu language";
1253 case CEC_OPCODE_DECK_CONTROL:
1254 return "deck control";
1255 case CEC_OPCODE_DECK_STATUS:
1256 return "deck status";
1257 case CEC_OPCODE_GIVE_DECK_STATUS:
1258 return "give deck status";
1259 case CEC_OPCODE_PLAY:
1260 return "play";
1261 case CEC_OPCODE_GIVE_TUNER_DEVICE_STATUS:
1262 return "give tuner status";
1263 case CEC_OPCODE_SELECT_ANALOGUE_SERVICE:
1264 return "select analogue service";
1265 case CEC_OPCODE_SELECT_DIGITAL_SERVICE:
1266 return "set digital service";
1267 case CEC_OPCODE_TUNER_DEVICE_STATUS:
1268 return "tuner device status";
1269 case CEC_OPCODE_TUNER_STEP_DECREMENT:
1270 return "tuner step decrement";
1271 case CEC_OPCODE_TUNER_STEP_INCREMENT:
1272 return "tuner step increment";
1273 case CEC_OPCODE_DEVICE_VENDOR_ID:
1274 return "device vendor id";
1275 case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
1276 return "give device vendor id";
1277 case CEC_OPCODE_VENDOR_COMMAND:
1278 return "vendor command";
1279 case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
1280 return "vendor command with id";
1281 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
1282 return "vendor remote button down";
1283 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
1284 return "vendor remote button up";
1285 case CEC_OPCODE_SET_OSD_STRING:
1286 return "set osd string";
1287 case CEC_OPCODE_GIVE_OSD_NAME:
1288 return "give osd name";
1289 case CEC_OPCODE_SET_OSD_NAME:
1290 return "set osd name";
1291 case CEC_OPCODE_MENU_REQUEST:
1292 return "menu request";
1293 case CEC_OPCODE_MENU_STATUS:
1294 return "menu status";
1295 case CEC_OPCODE_USER_CONTROL_PRESSED:
1296 return "user control pressed";
1297 case CEC_OPCODE_USER_CONTROL_RELEASE:
1298 return "user control release";
1299 case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
1300 return "give device power status";
1301 case CEC_OPCODE_REPORT_POWER_STATUS:
1302 return "report power status";
1303 case CEC_OPCODE_FEATURE_ABORT:
1304 return "feature abort";
1305 case CEC_OPCODE_ABORT:
1306 return "abort";
1307 case CEC_OPCODE_GIVE_AUDIO_STATUS:
1308 return "give audio status";
1309 case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
1310 return "give audio mode status";
1311 case CEC_OPCODE_REPORT_AUDIO_STATUS:
1312 return "report audio status";
1313 case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
1314 return "set system audio mode";
1315 case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
1316 return "system audio mode request";
1317 case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
1318 return "system audio mode status";
1319 case CEC_OPCODE_SET_AUDIO_RATE:
1320 return "set audio rate";
1321 default:
1322 return "UNKNOWN";
1323 }
1324 }
1325
1326 const char *CCECProcessor::ToString(const cec_system_audio_status mode)
1327 {
1328 switch(mode)
1329 {
1330 case CEC_SYSTEM_AUDIO_STATUS_ON:
1331 return "on";
1332 case CEC_SYSTEM_AUDIO_STATUS_OFF:
1333 return "off";
1334 default:
1335 return "unknown";
1336 }
1337 }
1338
1339 const char *CCECProcessor::ToString(const cec_audio_status UNUSED(status))
1340 {
1341 // TODO this is a mask
1342 return "TODO";
1343 }
1344
1345 const char *CCECProcessor::ToString(const cec_vendor_id vendor)
1346 {
1347 switch (vendor)
1348 {
1349 case CEC_VENDOR_SAMSUNG:
1350 return "Samsung";
1351 case CEC_VENDOR_LG:
1352 return "LG";
1353 case CEC_VENDOR_PANASONIC:
1354 return "Panasonic";
1355 case CEC_VENDOR_PIONEER:
1356 return "Pioneer";
1357 case CEC_VENDOR_ONKYO:
1358 return "Onkyo";
1359 case CEC_VENDOR_YAMAHA:
1360 return "Yamaha";
1361 case CEC_VENDOR_PHILIPS:
1362 return "Philips";
1363 case CEC_VENDOR_SONY:
1364 return "Sony";
1365 default:
1366 return "Unknown";
1367 }
1368 }
1369
1370 void *CCECBusScan::Process(void)
1371 {
1372 CCECBusDevice *device(NULL);
1373 uint8_t iCounter(0);
1374
1375 while (!IsStopped())
1376 {
1377 if (++iCounter < 10)
1378 {
1379 Sleep(1000);
1380 continue;
1381 }
1382 for (unsigned int iPtr = 0; iPtr <= 11 && !IsStopped(); iPtr++)
1383 {
1384 device = m_processor->m_busDevices[iPtr];
1385 WaitUntilIdle();
1386 if (device && device->GetStatus(true) == CEC_DEVICE_STATUS_PRESENT)
1387 {
1388 WaitUntilIdle();
1389 if (!IsStopped())
1390 device->GetVendorId();
1391
1392 WaitUntilIdle();
1393 if (!IsStopped())
1394 device->GetPowerStatus(true);
1395 }
1396 }
1397 }
1398
1399 return NULL;
1400 }
1401
1402 void CCECBusScan::WaitUntilIdle(void)
1403 {
1404 if (IsStopped())
1405 return;
1406
1407 int32_t iWaitTime = 3000 - (int32_t)(GetTimeMs() - m_processor->GetLastTransmission());
1408 while (iWaitTime > 0)
1409 {
1410 Sleep(iWaitTime);
1411 iWaitTime = 3000 - (int32_t)(GetTimeMs() - m_processor->GetLastTransmission());
1412 }
1413 }
1414
1415 bool CCECProcessor::StartBootloader(void)
1416 {
1417 return m_communication->StartBootloader();
1418 }
1419
1420 bool CCECProcessor::PingAdapter(void)
1421 {
1422 return m_communication->PingAdapter();
1423 }
1424
1425 void CCECProcessor::HandlePoll(cec_logical_address initiator, cec_logical_address destination)
1426 {
1427 m_busDevices[initiator]->GetHandler()->HandlePoll(initiator, destination);
1428 m_lastInitiator = initiator;
1429 }
1430
1431 bool CCECProcessor::HandleReceiveFailed(void)
1432 {
1433 return m_lastInitiator != CECDEVICE_UNKNOWN &&
1434 !m_busDevices[m_lastInitiator]->GetHandler()->HandleReceiveFailed();
1435 }