SL clean up and attempt to fix issue #176
[deb_libcec.git] / src / lib / implementations / SLCommandHandler.cpp
CommitLineData
e9de9629
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
16f47961 4 * libCEC(R) is Copyright (C) 2011-2013 Pulse-Eight Limited. All rights reserved.
e9de9629
LOK
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
2b44051c 33#include "env.h"
e9de9629 34#include "SLCommandHandler.h"
2b44051c
LOK
35
36#include "lib/platform/util/timeutils.h"
37#include "lib/devices/CECBusDevice.h"
38#include "lib/devices/CECPlaybackDevice.h"
39#include "lib/CECProcessor.h"
40#include "lib/LibCEC.h"
e9de9629
LOK
41
42using namespace CEC;
cc1b9bc4 43using namespace PLATFORM;
e9de9629 44
415e9bad
LOK
45#define SL_COMMAND_TYPE_HDDRECORDER_DISC 0x01
46#define SL_COMMAND_TYPE_VCR 0x02
47#define SL_COMMAND_TYPE_DVDPLAYER 0x03
48#define SL_COMMAND_TYPE_HDDRECORDER_DISC2 0x04
49#define SL_COMMAND_TYPE_HDDRECORDER 0x05
11d13a02 50
5d71f08c
LOK
51#define SL_COMMAND_INIT 0x01
52#define SL_COMMAND_ACK_INIT 0x02
9a0d7b9f 53#define SL_COMMAND_POWER_ON 0x03
0d4c3a7b 54#define SL_COMMAND_CONNECT_REQUEST 0x04
5abb18f3 55#define SL_COMMAND_SET_DEVICE_MODE 0x05
5d71f08c 56#define SL_COMMAND_REQUEST_POWER_STATUS 0xa0
11d13a02 57
004b8382
LOK
58#define LIB_CEC m_busDevice->GetProcessor()->GetLib()
59#define ToString(p) LIB_CEC->ToString(p)
60
060a7b5e
LOK
61CSLCommandHandler::CSLCommandHandler(CCECBusDevice *busDevice,
62 int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */,
63 int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */,
64 int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */,
65 int64_t iActiveSourcePending /* = 0 */) :
66 CCECCommandHandler(busDevice, iTransmitTimeout, iTransmitWait, iTransmitRetries, iActiveSourcePending),
5d71f08c 67 m_bSLEnabled(false)
e9de9629 68{
cf4931be 69 m_vendorId = CEC_VENDOR_LG;
79f01d26 70
6b87ae0c
LOK
71 /* LG devices don't always reply to CEC version requests, so just set it to 1.3a */
72 m_busDevice->SetCecVersion(CEC_VERSION_1_3A);
79f01d26
LOK
73
74 /* LG devices always return "korean" as language */
75 cec_menu_language lang;
76 lang.device = m_busDevice->GetLogicalAddress();
77 snprintf(lang.language, 4, "eng");
78 m_busDevice->SetMenuLanguage(lang);
9f65e017
LOK
79}
80
468a1414
LOK
81bool CSLCommandHandler::InitHandler(void)
82{
83 if (m_bHandlerInited)
84 return true;
85 m_bHandlerInited = true;
86
0b835513
LOK
87 if (m_busDevice->GetLogicalAddress() != CECDEVICE_TV)
88 return true;
89
42d28d15
LOK
90 CCECBusDevice *primary = m_processor->GetPrimaryDevice();
91 if (primary && primary->GetLogicalAddress() != CECDEVICE_UNREGISTERED)
871934d3 92 {
42d28d15
LOK
93 /* imitate LG devices */
94 if (m_busDevice->GetLogicalAddress() != primary->GetLogicalAddress())
95 {
96 primary->SetVendorId(CEC_VENDOR_LG);
97 primary->ReplaceHandler(false);
98 }
871934d3 99 }
468a1414 100
468a1414
LOK
101 return true;
102}
103
9a54dc82 104int CSLCommandHandler::HandleVendorCommand(const cec_command &command)
e54fd7d2 105{
004b8382 106 if (!m_processor->IsHandledByLibCEC(command.destination))
415e9bad
LOK
107 return true;
108
e54fd7d2 109 if (command.parameters.size == 1 &&
5d71f08c 110 command.parameters[0] == SL_COMMAND_INIT)
e54fd7d2 111 {
5d71f08c 112 HandleVendorCommandSLInit(command);
9a54dc82 113 return COMMAND_HANDLED;
e54fd7d2 114 }
9a0d7b9f
LOK
115 else if (command.parameters.size == 2 &&
116 command.parameters[0] == SL_COMMAND_POWER_ON)
117 {
0ecbcd4d 118 HandleVendorCommandPowerOn(command);
9a54dc82 119 return COMMAND_HANDLED;
9a0d7b9f 120 }
797dd7c4 121 else if (command.parameters.size == 2 &&
11d13a02 122 command.parameters[0] == SL_COMMAND_CONNECT_REQUEST)
9902f4e8 123 {
0ecbcd4d 124 HandleVendorCommandSLConnect(command);
9a54dc82 125 return COMMAND_HANDLED;
9902f4e8
LOK
126 }
127 else if (command.parameters.size == 1 &&
0d4c3a7b 128 command.parameters[0] == SL_COMMAND_REQUEST_POWER_STATUS)
9902f4e8 129 {
0ecbcd4d 130 HandleVendorCommandPowerOnStatus(command);
9a54dc82 131 return COMMAND_HANDLED;
9902f4e8 132 }
e54fd7d2 133
9a54dc82 134 return CCECCommandHandler::HandleVendorCommand(command);
e54fd7d2
LOK
135}
136
5d71f08c 137void CSLCommandHandler::HandleVendorCommandSLInit(const cec_command &command)
fe6f8e37 138{
34232c92 139 CCECBusDevice* dev = m_processor->GetDevice(command.destination);
5d71f08c
LOK
140 if (dev && dev->IsHandledByLibCEC())
141 {
142 if (!dev->IsActiveSource())
143 {
144 dev->SetPowerStatus(CEC_POWER_STATUS_STANDBY);
145 dev->TransmitPowerState(command.initiator, true);
146 }
147
148 TransmitVendorCommandSLAckInit(command.destination, command.initiator);
149 }
fe6f8e37
LOK
150}
151
5d71f08c 152void CSLCommandHandler::TransmitVendorCommandSLAckInit(const cec_logical_address iSource, const cec_logical_address iDestination)
5f316715
LOK
153{
154 cec_command response;
fe6f8e37 155 cec_command::Format(response, iSource, iDestination, CEC_OPCODE_VENDOR_COMMAND);
5d71f08c 156 response.PushBack(SL_COMMAND_ACK_INIT);
415e9bad 157 response.PushBack(SL_COMMAND_TYPE_HDDRECORDER);
5f316715 158
2b44051c 159 Transmit(response, false, true);
34232c92 160 SetSLInitialised();
fe6f8e37 161}
5f316715 162
0ecbcd4d 163void CSLCommandHandler::HandleVendorCommandPowerOn(const cec_command &command)
9a0d7b9f 164{
632f5810
LOK
165 if (command.initiator != CECDEVICE_TV)
166 return;
167
842262d8 168 CCECBusDevice *device = m_processor->GetPrimaryDevice();
9a0d7b9f
LOK
169 if (device)
170 {
cc1b9bc4 171 SetSLInitialised();
004b8382 172 device->MarkAsActiveSource();
60383b11 173 device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
2b44051c 174 device->TransmitPowerState(command.initiator, true);
60383b11
LOK
175
176 CEvent::Sleep(2000);
177 device->SetPowerStatus(CEC_POWER_STATUS_ON);
2b44051c
LOK
178 device->TransmitPowerState(command.initiator, false);
179 device->TransmitPhysicalAddress(false);
5d71f08c 180
e8fca5f8
LOK
181 if (device->IsActiveSource())
182 ActivateSource();
468a1414 183 }
5f316715 184}
0ecbcd4d 185void CSLCommandHandler::HandleVendorCommandPowerOnStatus(const cec_command &command)
5f316715
LOK
186{
187 if (command.destination != CECDEVICE_BROADCAST)
188 {
004b8382
LOK
189 CCECBusDevice *device = m_processor->GetPrimaryDevice();
190 if (device)
191 {
192 device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
2b44051c 193 device->TransmitPowerState(command.initiator, true);
004b8382
LOK
194 device->SetPowerStatus(CEC_POWER_STATUS_ON);
195 }
5f316715
LOK
196 }
197}
198
468a1414 199void CSLCommandHandler::HandleVendorCommandSLConnect(const cec_command &command)
8d915412 200{
cc1b9bc4 201 SetSLInitialised();
004b8382 202 TransmitVendorCommandSetDeviceMode(command.destination, command.initiator, CEC_DEVICE_TYPE_RECORDING_DEVICE);
37acf382 203
5d71f08c
LOK
204
205 if (m_processor->IsActiveSource(command.destination) && m_processor->IsHandledByLibCEC(command.destination))
206 {
207 CCECBusDevice* dev = m_processor->GetDevice(command.destination);
208 CCECPlaybackDevice* pb = dev->AsPlaybackDevice();
209 if (pb)
210 pb->TransmitDeckStatus(command.initiator, true);
211 dev->TransmitPowerState(command.initiator, true);
212 }
8d915412 213}
0d4c3a7b 214
5abb18f3 215void CSLCommandHandler::TransmitVendorCommandSetDeviceMode(const cec_logical_address iSource, const cec_logical_address iDestination, const cec_device_type type)
0d4c3a7b 216{
468a1414
LOK
217 cec_command response;
218 cec_command::Format(response, iSource, iDestination, CEC_OPCODE_VENDOR_COMMAND);
5abb18f3
LOK
219 response.PushBack(SL_COMMAND_SET_DEVICE_MODE);
220 response.PushBack((uint8_t)type);
2b44051c 221 Transmit(response, false, true);
0d4c3a7b 222}
fe6f8e37 223
9a54dc82 224int CSLCommandHandler::HandleGiveDeckStatus(const cec_command &command)
9f65e017 225{
9a54dc82
LOK
226 if (!m_processor->CECInitialised() ||
227 !m_processor->IsHandledByLibCEC(command.destination))
228 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
229
230 CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
231 if (!device || command.parameters.size == 0)
232 return CEC_ABORT_REASON_INVALID_OPERAND;
233
aab5cd7a 234 device->SetDeckStatus(CEC_DECK_INFO_OTHER_STATUS_LG);
9a54dc82 235 if (command.parameters[0] == CEC_STATUS_REQUEST_ON)
b818fb5a 236 {
2b44051c 237 device->TransmitDeckStatus(command.initiator, true);
5d71f08c 238 ActivateSource();
9a54dc82
LOK
239 return COMMAND_HANDLED;
240 }
241 else if (command.parameters[0] == CEC_STATUS_REQUEST_ONCE)
242 {
2b44051c 243 device->TransmitDeckStatus(command.initiator, true);
9a54dc82 244 return COMMAND_HANDLED;
b818fb5a 245 }
9f65e017 246
9a54dc82 247 return CCECCommandHandler::HandleGiveDeckStatus(command);
9f65e017 248}
718b3632 249
9a54dc82 250int CSLCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
718b3632 251{
0b8c7eab 252 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination) && command.initiator == CECDEVICE_TV)
718b3632
LOK
253 {
254 CCECBusDevice *device = GetDevice(command.destination);
004b8382 255 if (device && device->GetCurrentPowerStatus() != CEC_POWER_STATUS_ON)
2abe628c 256 {
2b44051c 257 device->TransmitPowerState(command.initiator, true);
2abe628c
LOK
258 device->SetPowerStatus(CEC_POWER_STATUS_ON);
259 }
2abe628c
LOK
260 else
261 {
5d71f08c 262 if (m_resetPowerState.IsSet() && m_resetPowerState.TimeLeft() > 0)
3faa971c 263 {
415e9bad 264 /* TODO assume that we've bugged out. the return button no longer works after this */
004b8382 265 LIB_CEC->AddLog(CEC_LOG_WARNING, "FIXME: LG seems to have bugged out. resetting to 'in transition standby to on'. the return button will not work");
3faa971c 266 device->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
2b44051c 267 device->TransmitPowerState(command.initiator, true);
3faa971c
LOK
268 device->SetPowerStatus(CEC_POWER_STATUS_ON);
269 m_resetPowerState.Init(5000);
270 }
271 else
272 {
2b44051c 273 device->TransmitPowerState(command.initiator, true);
3faa971c
LOK
274 m_resetPowerState.Init(5000);
275 }
b818fb5a 276 }
9a54dc82
LOK
277
278 return COMMAND_HANDLED;
718b3632
LOK
279 }
280
9a54dc82 281 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
2abe628c
LOK
282}
283
9a54dc82 284int CSLCommandHandler::HandleRequestActiveSource(const cec_command &command)
2abe628c 285{
0b8c7eab 286 if (m_processor->CECInitialised())
2abe628c 287 {
5d71f08c
LOK
288 if (!SLInitialised())
289 TransmitVendorCommandSLAckInit(m_processor->GetPrimaryDevice()->GetLogicalAddress(), command.initiator);
290 CCECCommandHandler::HandleRequestActiveSource(command);
2abe628c 291 }
9a54dc82 292 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
718b3632 293}
5c3b1f92 294
9a54dc82 295int CSLCommandHandler::HandleFeatureAbort(const cec_command &command)
5c3b1f92 296{
5d71f08c
LOK
297 CCECBusDevice* primary = m_processor->GetPrimaryDevice();
298 if (command.parameters.size == 0 && primary->GetLogicalAddress() != CECDEVICE_UNKNOWN &&
299 primary->GetCurrentPowerStatus() == CEC_POWER_STATUS_ON && !SLInitialised() &&
632f5810 300 command.initiator == CECDEVICE_TV)
5c3b1f92 301 {
5d71f08c
LOK
302 if (!SLInitialised() && m_processor->IsActiveSource(command.destination))
303 {
304 TransmitVendorCommandSLAckInit(command.destination, command.initiator);
305 return COMMAND_HANDLED;
306 }
5c3b1f92
LOK
307 }
308
309 return CCECCommandHandler::HandleFeatureAbort(command);
310}
311
9a54dc82 312int CSLCommandHandler::HandleStandby(const cec_command &command)
5c3b1f92 313{
5d71f08c 314 ResetSLState();
5c3b1f92 315
cc1b9bc4
LOK
316 return CCECCommandHandler::HandleStandby(command);
317}
5c3b1f92 318
cc1b9bc4
LOK
319void CSLCommandHandler::ResetSLState(void)
320{
004b8382 321 LIB_CEC->AddLog(CEC_LOG_NOTICE, "resetting SL initialised state");
cc1b9bc4
LOK
322 CLockObject lock(m_SLMutex);
323 m_bSLEnabled = false;
cc1b9bc4
LOK
324 m_processor->GetPrimaryDevice()->SetPowerStatus(CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
325}
326
327void CSLCommandHandler::SetSLInitialised(void)
328{
004b8382 329 LIB_CEC->AddLog(CEC_LOG_NOTICE, "SL initialised");
cc1b9bc4
LOK
330 CLockObject lock(m_SLMutex);
331 m_bSLEnabled = true;
332}
333
334bool CSLCommandHandler::SLInitialised(void)
335{
336 CLockObject lock(m_SLMutex);
337 return m_bSLEnabled;
338}
339
632f5810
LOK
340bool CSLCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
341{
342 if (iDestination != CECDEVICE_TV)
343 {
bbc71623 344 /* LG devices only allow themselves to be woken up by the TV with a vendor command */
632f5810 345 cec_command command;
8b5e50ec
LOK
346
347 if (!m_bSLEnabled)
869ff027 348 TransmitVendorID(CECDEVICE_TV, iDestination, CEC_VENDOR_LG, false);
8b5e50ec 349
632f5810
LOK
350 cec_command::Format(command, CECDEVICE_TV, iDestination, CEC_OPCODE_VENDOR_COMMAND);
351 command.PushBack(SL_COMMAND_POWER_ON);
352 command.PushBack(0);
2b44051c 353 return Transmit(command, false, false);
632f5810
LOK
354 }
355
356 return CCECCommandHandler::PowerOn(iInitiator, iDestination);
357}
7b01619d 358
5d71f08c 359bool CSLCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */)
7b01619d 360{
5d71f08c
LOK
361 if (m_busDevice->IsActiveSource() &&
362 m_busDevice->IsHandledByLibCEC())
363 {
364 {
365 CLockObject lock(m_mutex);
366 // check if we need to send a delayed source switch
367 if (bTransmitDelayedCommandsOnly)
368 {
369 if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending)
370 return false;
371
372#ifdef CEC_DEBUGGING
373 LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
374#endif
375 }
376 }
377
378 CCECPlaybackDevice *device = m_busDevice->AsPlaybackDevice();
379 if (device)
380 device->SetDeckStatus(!device->IsActiveSource() ? CEC_DECK_INFO_OTHER_STATUS : CEC_DECK_INFO_OTHER_STATUS_LG);
381
382 // power on the TV
383 CCECBusDevice* tv = m_processor->GetDevice(CECDEVICE_TV);
384 bool bTvPresent = (tv && tv->GetStatus() == CEC_DEVICE_STATUS_PRESENT);
385 bool bActiveSourceFailed(false);
386 if (bTvPresent)
387 {
388 bActiveSourceFailed = !device->TransmitImageViewOn();
389 }
390 else
391 {
392 LIB_CEC->AddLog(CEC_LOG_DEBUG, "TV not present, not sending 'image view on'");
393 }
394
395 // check if we're allowed to switch sources
396 bool bSourceSwitchAllowed = SourceSwitchAllowed();
397 if (!bSourceSwitchAllowed)
398 LIB_CEC->AddLog(CEC_LOG_DEBUG, "source switch is currently not allowed by command handler");
399
400 // switch sources (if allowed)
401 if (!bActiveSourceFailed && bSourceSwitchAllowed)
402 {
403 bActiveSourceFailed = !m_busDevice->TransmitActiveSource(false);
404 }
405
406 // retry later
407 if (bActiveSourceFailed || !bSourceSwitchAllowed)
408 {
409 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
410 int64_t now(GetTimeMs());
411 CLockObject lock(m_mutex);
412 if (m_iActiveSourcePending == 0 || m_iActiveSourcePending < now)
413 m_iActiveSourcePending = now + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
414 return false;
415 }
416 else
417 {
418 CLockObject lock(m_mutex);
419 // clear previous pending active source command
420 m_iActiveSourcePending = 0;
421 }
422
423 // mark the handler as initialised
424 CLockObject lock(m_mutex);
425 m_bHandlerInited = true;
426 }
427 return true;
7b01619d 428}