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