Merge branch 'master' into release
[deb_libcec.git] / src / lib / implementations / CECCommandHandler.cpp
CommitLineData
e9de9629
LOK
1/*
2 * This file is part of the libCEC(R) library.
3 *
b492c10e 4 * libCEC(R) is Copyright (C) 2011-2012 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
33#include "CECCommandHandler.h"
eafa9d46 34#include "../devices/CECBusDevice.h"
a1f8fb1b 35#include "../devices/CECAudioSystem.h"
28089abc 36#include "../devices/CECPlaybackDevice.h"
004b8382 37#include "../CECClient.h"
387b6f6f 38#include "../CECProcessor.h"
5477a250 39#include "../LibCEC.h"
0d800fe5 40#include "../CECTypeUtils.h"
c9d15485 41#include "../platform/util/util.h"
e9de9629
LOK
42
43using namespace CEC;
8747dd4f 44using namespace std;
f00ff009 45using namespace PLATFORM;
e9de9629 46
004b8382 47#define LIB_CEC m_busDevice->GetProcessor()->GetLib()
0d800fe5 48#define ToString(p) CCECTypeUtils::ToString(p)
004b8382 49
060a7b5e
LOK
50CCECCommandHandler::CCECCommandHandler(CCECBusDevice *busDevice,
51 int32_t iTransmitTimeout /* = CEC_DEFAULT_TRANSMIT_TIMEOUT */,
52 int32_t iTransmitWait /* = CEC_DEFAULT_TRANSMIT_WAIT */,
53 int8_t iTransmitRetries /* = CEC_DEFAULT_TRANSMIT_RETRIES */,
54 int64_t iActiveSourcePending /* = 0 */) :
8fa35473
LOK
55 m_busDevice(busDevice),
56 m_processor(m_busDevice->GetProcessor()),
060a7b5e
LOK
57 m_iTransmitTimeout(iTransmitTimeout),
58 m_iTransmitWait(iTransmitWait),
59 m_iTransmitRetries(iTransmitRetries),
b64db02e 60 m_bHandlerInited(false),
d79b67d8 61 m_bOPTSendDeckStatusUpdateOnActiveSource(false),
960f33c6 62 m_vendorId(CEC_VENDOR_UNKNOWN),
060a7b5e 63 m_iActiveSourcePending(iActiveSourcePending)
e9de9629 64{
8fa35473
LOK
65}
66
e9de9629
LOK
67bool CCECCommandHandler::HandleCommand(const cec_command &command)
68{
d297cbd4
LOK
69 if (command.opcode_set == 0)
70 return HandlePoll(command);
71
9a54dc82 72 int iHandled(CEC_ABORT_REASON_UNRECOGNIZED_OPCODE);
e9de9629 73
5d19989f 74 LIB_CEC->AddCommand(command);
855a3a98 75
6b72afcd 76 switch(command.opcode)
e9de9629 77 {
6b72afcd 78 case CEC_OPCODE_REPORT_POWER_STATUS:
9a54dc82 79 iHandled = HandleReportPowerStatus(command);
6b72afcd
LOK
80 break;
81 case CEC_OPCODE_CEC_VERSION:
9a54dc82 82 iHandled = HandleDeviceCecVersion(command);
6b72afcd
LOK
83 break;
84 case CEC_OPCODE_SET_MENU_LANGUAGE:
9a54dc82 85 iHandled = HandleSetMenuLanguage(command);
6b72afcd
LOK
86 break;
87 case CEC_OPCODE_GIVE_PHYSICAL_ADDRESS:
9a54dc82 88 iHandled = HandleGivePhysicalAddress(command);
6b72afcd 89 break;
fbdea54c 90 case CEC_OPCODE_GET_MENU_LANGUAGE:
9a54dc82 91 iHandled = HandleGiveMenuLanguage(command);
fbdea54c 92 break;
6b72afcd 93 case CEC_OPCODE_GIVE_OSD_NAME:
9a54dc82 94 iHandled = HandleGiveOSDName(command);
6b72afcd
LOK
95 break;
96 case CEC_OPCODE_GIVE_DEVICE_VENDOR_ID:
9a54dc82 97 iHandled = HandleGiveDeviceVendorId(command);
6b72afcd
LOK
98 break;
99 case CEC_OPCODE_DEVICE_VENDOR_ID:
9a54dc82 100 iHandled = HandleDeviceVendorId(command);
6b72afcd
LOK
101 break;
102 case CEC_OPCODE_VENDOR_COMMAND_WITH_ID:
9a54dc82 103 iHandled = HandleDeviceVendorCommandWithId(command);
6b72afcd
LOK
104 break;
105 case CEC_OPCODE_GIVE_DECK_STATUS:
9a54dc82 106 iHandled = HandleGiveDeckStatus(command);
6b72afcd
LOK
107 break;
108 case CEC_OPCODE_DECK_CONTROL:
9a54dc82 109 iHandled = HandleDeckControl(command);
6b72afcd
LOK
110 break;
111 case CEC_OPCODE_MENU_REQUEST:
9a54dc82 112 iHandled = HandleMenuRequest(command);
6b72afcd
LOK
113 break;
114 case CEC_OPCODE_GIVE_DEVICE_POWER_STATUS:
9a54dc82 115 iHandled = HandleGiveDevicePowerStatus(command);
6b72afcd
LOK
116 break;
117 case CEC_OPCODE_GET_CEC_VERSION:
9a54dc82 118 iHandled = HandleGetCecVersion(command);
6b72afcd
LOK
119 break;
120 case CEC_OPCODE_USER_CONTROL_PRESSED:
9a54dc82 121 iHandled = HandleUserControlPressed(command);
6b72afcd
LOK
122 break;
123 case CEC_OPCODE_USER_CONTROL_RELEASE:
9a54dc82 124 iHandled = HandleUserControlRelease(command);
6b72afcd
LOK
125 break;
126 case CEC_OPCODE_GIVE_AUDIO_STATUS:
9a54dc82 127 iHandled = HandleGiveAudioStatus(command);
6b72afcd
LOK
128 break;
129 case CEC_OPCODE_GIVE_SYSTEM_AUDIO_MODE_STATUS:
9a54dc82 130 iHandled = HandleGiveSystemAudioModeStatus(command);
6b72afcd
LOK
131 break;
132 case CEC_OPCODE_SYSTEM_AUDIO_MODE_REQUEST:
9a54dc82 133 iHandled = HandleSystemAudioModeRequest(command);
aa517a0d
LOK
134 break;
135 case CEC_OPCODE_REPORT_AUDIO_STATUS:
9a54dc82 136 iHandled = HandleReportAudioStatus(command);
aa517a0d
LOK
137 break;
138 case CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS:
9a54dc82 139 iHandled = HandleSystemAudioModeStatus(command);
aa517a0d
LOK
140 break;
141 case CEC_OPCODE_SET_SYSTEM_AUDIO_MODE:
9a54dc82 142 iHandled = HandleSetSystemAudioMode(command);
6b72afcd
LOK
143 break;
144 case CEC_OPCODE_REQUEST_ACTIVE_SOURCE:
9a54dc82 145 iHandled = HandleRequestActiveSource(command);
6b72afcd
LOK
146 break;
147 case CEC_OPCODE_SET_STREAM_PATH:
9a54dc82 148 iHandled = HandleSetStreamPath(command);
6b72afcd
LOK
149 break;
150 case CEC_OPCODE_ROUTING_CHANGE:
9a54dc82 151 iHandled = HandleRoutingChange(command);
6b72afcd 152 break;
907bd60f 153 case CEC_OPCODE_ROUTING_INFORMATION:
9a54dc82 154 iHandled = HandleRoutingInformation(command);
907bd60f 155 break;
6b72afcd 156 case CEC_OPCODE_STANDBY:
9a54dc82 157 iHandled = HandleStandby(command);
6b72afcd
LOK
158 break;
159 case CEC_OPCODE_ACTIVE_SOURCE:
9a54dc82 160 iHandled = HandleActiveSource(command);
6b72afcd 161 break;
907bd60f 162 case CEC_OPCODE_REPORT_PHYSICAL_ADDRESS:
9a54dc82 163 iHandled = HandleReportPhysicalAddress(command);
907bd60f 164 break;
15d1a84c 165 case CEC_OPCODE_SET_OSD_NAME:
9a54dc82 166 iHandled = HandleSetOSDName(command);
15d1a84c 167 break;
1a6669b8 168 case CEC_OPCODE_IMAGE_VIEW_ON:
9a54dc82 169 iHandled = HandleImageViewOn(command);
1a6669b8
LOK
170 break;
171 case CEC_OPCODE_TEXT_VIEW_ON:
9a54dc82 172 iHandled = HandleTextViewOn(command);
1a6669b8 173 break;
4d738fe3 174 case CEC_OPCODE_FEATURE_ABORT:
9a54dc82 175 iHandled = HandleFeatureAbort(command);
4d738fe3 176 break;
468a1414 177 case CEC_OPCODE_VENDOR_COMMAND:
9a54dc82
LOK
178 iHandled = HandleVendorCommand(command);
179 break;
180 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_DOWN:
181 iHandled = HandleVendorRemoteButtonDown(command);
182 break;
183 case CEC_OPCODE_VENDOR_REMOTE_BUTTON_UP:
184 iHandled = HandleVendorRemoteButtonUp(command);
468a1414 185 break;
34fe491f
LOK
186 case CEC_OPCODE_PLAY:
187 // libCEC (currently) doesn't need to do anything with this, since player applications handle it
188 // but it should not respond with a feature abort
9a54dc82 189 iHandled = COMMAND_HANDLED;
34fe491f 190 break;
6b72afcd 191 default:
6b72afcd 192 break;
e9de9629
LOK
193 }
194
9a54dc82 195 if (iHandled == COMMAND_HANDLED)
060a7b5e 196 m_busDevice->SignalOpcode((command.opcode == CEC_OPCODE_FEATURE_ABORT && command.parameters.size > 0) ? (cec_opcode)command.parameters[0] : command.opcode);
004b8382 197 else
9a54dc82 198 UnhandledCommand(command, (cec_abort_reason)iHandled);
8fa35473 199
9a54dc82 200 return iHandled == COMMAND_HANDLED;
e9de9629
LOK
201}
202
9a54dc82 203int CCECCommandHandler::HandleActiveSource(const cec_command &command)
be5b0e24
LOK
204{
205 if (command.parameters.size == 2)
206 {
207 uint16_t iAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
004b8382
LOK
208 CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iAddress);
209 if (device)
9a54dc82 210 {
004b8382 211 device->MarkAsActiveSource();
9a54dc82
LOK
212 return COMMAND_HANDLED;
213 }
be5b0e24
LOK
214 }
215
9a54dc82 216 return CEC_ABORT_REASON_INVALID_OPERAND;
be5b0e24
LOK
217}
218
9a54dc82 219int CCECCommandHandler::HandleDeckControl(const cec_command &command)
a9232a79 220{
aa40e0e2
LOK
221 CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
222 if (device && command.parameters.size > 0)
a9232a79 223 {
aa40e0e2 224 device->SetDeckControlMode((cec_deck_control_mode) command.parameters[0]);
9a54dc82 225 return COMMAND_HANDLED;
a9232a79
LOK
226 }
227
9a54dc82 228 return CEC_ABORT_REASON_INVALID_OPERAND;
a9232a79
LOK
229}
230
9a54dc82 231int CCECCommandHandler::HandleDeviceCecVersion(const cec_command &command)
6a1c0009
LOK
232{
233 if (command.parameters.size == 1)
234 {
235 CCECBusDevice *device = GetDevice(command.initiator);
236 if (device)
237 device->SetCecVersion((cec_version) command.parameters[0]);
9a54dc82
LOK
238
239 return COMMAND_HANDLED;
6a1c0009
LOK
240 }
241
9a54dc82 242 return CEC_ABORT_REASON_INVALID_OPERAND;
6a1c0009
LOK
243}
244
866e7584 245int CCECCommandHandler::HandleDeviceVendorCommandWithId(const cec_command & UNUSED(command))
e9de9629 246{
22579015 247 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
248}
249
9a54dc82 250int CCECCommandHandler::HandleDeviceVendorId(const cec_command &command)
e9de9629 251{
9a54dc82
LOK
252 SetVendorId(command);
253 return COMMAND_HANDLED;
e9de9629
LOK
254}
255
9a54dc82 256int CCECCommandHandler::HandleFeatureAbort(const cec_command &command)
4d738fe3 257{
69a1a673
LOK
258 if (command.parameters.size == 2 &&
259 (command.parameters[1] == CEC_ABORT_REASON_UNRECOGNIZED_OPCODE ||
260 command.parameters[1] == CEC_ABORT_REASON_REFUSED))
004b8382 261 m_processor->GetDevice(command.initiator)->SetUnsupportedFeature((cec_opcode)command.parameters[0]);
9a54dc82 262 return COMMAND_HANDLED;
4d738fe3
LOK
263}
264
9a54dc82 265int CCECCommandHandler::HandleGetCecVersion(const cec_command &command)
e9de9629 266{
0b8c7eab 267 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd
LOK
268 {
269 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
270 if (device && device->TransmitCECVersion(command.initiator))
271 return COMMAND_HANDLED;
272 return CEC_ABORT_REASON_INVALID_OPERAND;
6b72afcd 273 }
0f23c85c 274
9a54dc82 275 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
e9de9629
LOK
276}
277
9a54dc82 278int CCECCommandHandler::HandleGiveAudioStatus(const cec_command &command)
a1f8fb1b 279{
0b8c7eab 280 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd 281 {
aa40e0e2 282 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
9a54dc82
LOK
283 if (device && device->TransmitAudioStatus(command.initiator))
284 return COMMAND_HANDLED;
285 return CEC_ABORT_REASON_INVALID_OPERAND;
6b72afcd 286 }
a1f8fb1b 287
9a54dc82 288 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
a1f8fb1b
LOK
289}
290
9a54dc82 291int CCECCommandHandler::HandleGiveDeckStatus(const cec_command &command)
e9de9629 292{
0b8c7eab 293 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd 294 {
aa40e0e2 295 CCECPlaybackDevice *device = CCECBusDevice::AsPlaybackDevice(GetDevice(command.destination));
9a54dc82
LOK
296 if (device && device->TransmitDeckStatus(command.initiator))
297 return COMMAND_HANDLED;
298 return CEC_ABORT_REASON_INVALID_OPERAND;
6b72afcd 299 }
0f23c85c 300
9a54dc82 301 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
e9de9629
LOK
302}
303
9a54dc82 304int CCECCommandHandler::HandleGiveDevicePowerStatus(const cec_command &command)
e9de9629 305{
0b8c7eab 306 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd
LOK
307 {
308 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
309 if (device && device->TransmitPowerState(command.initiator))
310 return COMMAND_HANDLED;
311 return CEC_ABORT_REASON_INVALID_OPERAND;
6b72afcd 312 }
0f23c85c 313
9a54dc82 314 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
315}
316
9a54dc82 317int CCECCommandHandler::HandleGiveDeviceVendorId(const cec_command &command)
e9de9629 318{
0b8c7eab 319 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd
LOK
320 {
321 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
322 if (device && device->TransmitVendorID(command.initiator))
323 return COMMAND_HANDLED;
6b72afcd 324 }
0f23c85c 325
9a54dc82 326 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
327}
328
9a54dc82 329int CCECCommandHandler::HandleGiveOSDName(const cec_command &command)
e9de9629 330{
0b8c7eab 331 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd
LOK
332 {
333 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
334 if (device && device->TransmitOSDName(command.initiator))
335 return COMMAND_HANDLED;
6b72afcd 336 }
0f23c85c 337
9a54dc82 338 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
339}
340
9a54dc82 341int CCECCommandHandler::HandleGivePhysicalAddress(const cec_command &command)
e9de9629 342{
0b8c7eab 343 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
6b72afcd
LOK
344 {
345 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
346 if (device && device->TransmitPhysicalAddress())
347 return COMMAND_HANDLED;
348 return CEC_ABORT_REASON_INVALID_OPERAND;
6b72afcd 349 }
09c10b66 350
9a54dc82 351 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
e9de9629
LOK
352}
353
9a54dc82 354int CCECCommandHandler::HandleGiveMenuLanguage(const cec_command &command)
fbdea54c 355{
0b8c7eab 356 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
fbdea54c
MK
357 {
358 CCECBusDevice *device = GetDevice(command.destination);
9a54dc82
LOK
359 if (device && device->TransmitSetMenuLanguage(command.initiator))
360 return COMMAND_HANDLED;
361 return CEC_ABORT_REASON_INVALID_OPERAND;
fbdea54c
MK
362 }
363
9a54dc82 364 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
fbdea54c
MK
365}
366
9a54dc82 367int CCECCommandHandler::HandleGiveSystemAudioModeStatus(const cec_command &command)
1a6669b8 368{
0b8c7eab 369 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
1a6669b8 370 {
aa40e0e2 371 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
9a54dc82
LOK
372 if (device && device->TransmitSystemAudioModeStatus(command.initiator))
373 return COMMAND_HANDLED;
374 return CEC_ABORT_REASON_INVALID_OPERAND;
1a6669b8
LOK
375 }
376
9a54dc82 377 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
1a6669b8
LOK
378}
379
9a54dc82 380int CCECCommandHandler::HandleImageViewOn(const cec_command &command)
1a6669b8 381{
004b8382 382 m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
9a54dc82 383 return COMMAND_HANDLED;
1a6669b8
LOK
384}
385
9a54dc82 386int CCECCommandHandler::HandleMenuRequest(const cec_command &command)
e9de9629 387{
0b8c7eab 388 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
0f23c85c 389 {
e1804a4e
LOK
390 CCECBusDevice *device = GetDevice(command.destination);
391 if (device)
6b72afcd 392 {
004b8382
LOK
393 CCECClient *client = device->GetClient();
394 if (client)
e1804a4e 395 {
004b8382
LOK
396 if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_ACTIVATE)
397 {
398 if (client->MenuStateChanged(CEC_MENU_STATE_ACTIVATED) == 1)
399 device->SetMenuState(CEC_MENU_STATE_ACTIVATED);
400 }
401 else if (command.parameters[0] == CEC_MENU_REQUEST_TYPE_DEACTIVATE)
402 {
403 if (client->MenuStateChanged(CEC_MENU_STATE_DEACTIVATED) == 1)
404 device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
405 }
e1804a4e 406 }
9a54dc82
LOK
407 if (device->TransmitMenuState(command.initiator))
408 return COMMAND_HANDLED;
6b72afcd 409 }
9a54dc82 410 return CEC_ABORT_REASON_INVALID_OPERAND;
15d1a84c
LOK
411 }
412
9a54dc82 413 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
15d1a84c
LOK
414}
415
d297cbd4
LOK
416bool CCECCommandHandler::HandlePoll(const cec_command &command)
417{
a75e3a5a 418 m_busDevice->HandlePoll(command.destination);
d297cbd4
LOK
419 return true;
420}
421
9a54dc82 422int CCECCommandHandler::HandleReportAudioStatus(const cec_command &command)
15d1a84c
LOK
423{
424 if (command.parameters.size == 1)
425 {
aa40e0e2
LOK
426 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
427 if (device)
6b72afcd 428 {
aa40e0e2 429 device->SetAudioStatus(command.parameters[0]);
9a54dc82 430 return COMMAND_HANDLED;
6b72afcd 431 }
0f23c85c 432 }
9a54dc82 433 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
434}
435
9a54dc82 436int CCECCommandHandler::HandleReportPhysicalAddress(const cec_command &command)
907bd60f 437{
0bfce006 438 if (command.parameters.size == 3)
907bd60f
LOK
439 {
440 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
16b1e052 441 SetPhysicalAddress(command.initiator, iNewAddress);
9a54dc82 442 return COMMAND_HANDLED;
907bd60f 443 }
9a54dc82 444 return CEC_ABORT_REASON_INVALID_OPERAND;
907bd60f
LOK
445}
446
9a54dc82 447int CCECCommandHandler::HandleReportPowerStatus(const cec_command &command)
e55f3f70
LOK
448{
449 if (command.parameters.size == 1)
450 {
451 CCECBusDevice *device = GetDevice(command.initiator);
452 if (device)
9a54dc82 453 {
e55f3f70 454 device->SetPowerStatus((cec_power_status) command.parameters[0]);
9a54dc82
LOK
455 return COMMAND_HANDLED;
456 }
e55f3f70 457 }
9a54dc82 458 return CEC_ABORT_REASON_INVALID_OPERAND;
e55f3f70
LOK
459}
460
9a54dc82 461int CCECCommandHandler::HandleRequestActiveSource(const cec_command &command)
e9de9629 462{
0b8c7eab 463 if (m_processor->CECInitialised())
5f316715 464 {
004b8382
LOK
465 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %i requests active source", (uint8_t) command.initiator);
466 m_processor->GetDevice(command.initiator)->SetPowerStatus(CEC_POWER_STATUS_ON);
8747dd4f 467
5f316715 468 vector<CCECBusDevice *> devices;
f00ff009 469 for (size_t iDevicePtr = 0; iDevicePtr < GetMyDevices(devices); iDevicePtr++)
5f316715 470 devices[iDevicePtr]->TransmitActiveSource();
5f316715 471 }
9a54dc82
LOK
472
473 return COMMAND_HANDLED;
e9de9629
LOK
474}
475
9a54dc82 476int CCECCommandHandler::HandleRoutingChange(const cec_command &command)
e9de9629
LOK
477{
478 if (command.parameters.size == 4)
479 {
480 uint16_t iOldAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
481 uint16_t iNewAddress = ((uint16_t)command.parameters[2] << 8) | ((uint16_t)command.parameters[3]);
e9de9629 482
0f23c85c
LOK
483 CCECBusDevice *device = GetDevice(command.initiator);
484 if (device)
9a54dc82 485 {
9dc04b07 486 device->SetStreamPath(iNewAddress, iOldAddress);
9a54dc82
LOK
487 return COMMAND_HANDLED;
488 }
e9de9629 489 }
9a54dc82
LOK
490
491 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
492}
493
9a54dc82 494int CCECCommandHandler::HandleRoutingInformation(const cec_command &command)
907bd60f
LOK
495{
496 if (command.parameters.size == 2)
497 {
498 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
004b8382
LOK
499 CCECBusDevice *device = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
500 if (device)
9a54dc82 501 {
004b8382 502 device->MarkAsActiveSource();
9a54dc82
LOK
503 return COMMAND_HANDLED;
504 }
907bd60f 505 }
15d1a84c 506
9a54dc82 507 return CEC_ABORT_REASON_INVALID_OPERAND;
907bd60f
LOK
508}
509
9a54dc82 510int CCECCommandHandler::HandleSetMenuLanguage(const cec_command &command)
a3269a0a
LOK
511{
512 if (command.parameters.size == 3)
513 {
514 CCECBusDevice *device = GetDevice(command.initiator);
515 if (device)
516 {
517 cec_menu_language language;
518 language.device = command.initiator;
56701628 519 for (uint8_t iPtr = 0; iPtr < 4; iPtr++)
a3269a0a
LOK
520 language.language[iPtr] = command.parameters[iPtr];
521 language.language[3] = 0;
522 device->SetMenuLanguage(language);
9a54dc82 523 return COMMAND_HANDLED;
15d1a84c
LOK
524 }
525 }
9a54dc82
LOK
526
527 return CEC_ABORT_REASON_INVALID_OPERAND;
15d1a84c
LOK
528}
529
9a54dc82 530int CCECCommandHandler::HandleSetOSDName(const cec_command &command)
15d1a84c
LOK
531{
532 if (command.parameters.size > 0)
533 {
534 CCECBusDevice *device = GetDevice(command.initiator);
535 if (device)
536 {
537 char buf[1024];
538 for (uint8_t iPtr = 0; iPtr < command.parameters.size; iPtr++)
539 buf[iPtr] = (char)command.parameters[iPtr];
540 buf[command.parameters.size] = 0;
541
542 CStdString strName(buf);
543 device->SetOSDName(strName);
6b72afcd 544
9a54dc82 545 return COMMAND_HANDLED;
a3269a0a
LOK
546 }
547 }
9a54dc82
LOK
548
549 return CEC_ABORT_REASON_INVALID_OPERAND;
a3269a0a
LOK
550}
551
9a54dc82 552int CCECCommandHandler::HandleSetStreamPath(const cec_command &command)
e9de9629 553{
9a54dc82
LOK
554 if (!m_processor->CECInitialised())
555 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
556
557 if (command.parameters.size >= 2)
e9de9629 558 {
b6c7bc94 559 uint16_t iStreamAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
bf1b57e0
LOK
560 LIB_CEC->AddLog(CEC_LOG_DEBUG, ">> %s (%x) sets stream path to physical address %04x", ToString(command.initiator), command.initiator, iStreamAddress);
561
562 // a device will only change the stream path when it's powered on
563 m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
72689600
LOK
564
565 /* one of the device handled by libCEC has been made active */
566 CCECBusDevice *device = GetDeviceByPhysicalAddress(iStreamAddress);
004b8382 567 if (device && device->IsHandledByLibCEC())
9a54dc82 568 {
004b8382 569 device->ActivateSource();
9a54dc82
LOK
570 return COMMAND_HANDLED;
571 }
e9de9629 572 }
9a54dc82
LOK
573
574 return CEC_ABORT_REASON_INVALID_OPERAND;
e9de9629
LOK
575}
576
9a54dc82 577int CCECCommandHandler::HandleSystemAudioModeRequest(const cec_command &command)
e5e86c76 578{
0b8c7eab 579 if (m_processor->CECInitialised() && m_processor->IsHandledByLibCEC(command.destination))
e5e86c76 580 {
aa40e0e2
LOK
581 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.destination));
582 if (device)
aa517a0d
LOK
583 {
584 if (command.parameters.size >= 2)
585 {
586 device->SetPowerStatus(CEC_POWER_STATUS_ON);
aa40e0e2 587 device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_ON);
aa517a0d
LOK
588 uint16_t iNewAddress = ((uint16_t)command.parameters[0] << 8) | ((uint16_t)command.parameters[1]);
589 CCECBusDevice *newActiveDevice = GetDeviceByPhysicalAddress(iNewAddress);
590 if (newActiveDevice)
004b8382 591 newActiveDevice->MarkAsActiveSource();
9a54dc82
LOK
592 if (device->TransmitSetSystemAudioMode(command.initiator))
593 return COMMAND_HANDLED;
aa517a0d
LOK
594 }
595 else
596 {
aa40e0e2 597 device->SetSystemAudioModeStatus(CEC_SYSTEM_AUDIO_STATUS_OFF);
9a54dc82
LOK
598 if (device->TransmitSetSystemAudioMode(command.initiator))
599 return COMMAND_HANDLED;
aa517a0d
LOK
600 }
601 }
e5e86c76 602 }
9a54dc82
LOK
603
604 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
e5e86c76
LOK
605}
606
9a54dc82 607int CCECCommandHandler::HandleStandby(const cec_command &command)
4d6b4433
LOK
608{
609 CCECBusDevice *device = GetDevice(command.initiator);
610 if (device)
611 device->SetPowerStatus(CEC_POWER_STATUS_STANDBY);
6b72afcd 612
9a54dc82 613 return COMMAND_HANDLED;
4d6b4433
LOK
614}
615
9a54dc82 616int CCECCommandHandler::HandleSystemAudioModeStatus(const cec_command &command)
868dc71f 617{
aa517a0d 618 if (command.parameters.size == 1)
868dc71f 619 {
aa40e0e2
LOK
620 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
621 if (device)
aa517a0d 622 {
aa40e0e2 623 device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
9a54dc82 624 return COMMAND_HANDLED;
aa517a0d
LOK
625 }
626 }
627
9a54dc82 628 return CEC_ABORT_REASON_INVALID_OPERAND;
aa517a0d
LOK
629}
630
9a54dc82 631int CCECCommandHandler::HandleSetSystemAudioMode(const cec_command &command)
aa517a0d
LOK
632{
633 if (command.parameters.size == 1)
634 {
aa40e0e2
LOK
635 CCECAudioSystem *device = CCECBusDevice::AsAudioSystem(GetDevice(command.initiator));
636 if (device)
aa517a0d 637 {
aa40e0e2 638 device->SetSystemAudioModeStatus((cec_system_audio_status)command.parameters[0]);
9a54dc82 639 return COMMAND_HANDLED;
aa517a0d 640 }
868dc71f
LOK
641 }
642
9a54dc82 643 return CEC_ABORT_REASON_INVALID_OPERAND;
868dc71f
LOK
644}
645
9a54dc82 646int CCECCommandHandler::HandleTextViewOn(const cec_command &command)
cf0ecd85 647{
004b8382 648 m_processor->GetDevice(command.initiator)->MarkAsActiveSource();
9a54dc82 649 return COMMAND_HANDLED;
cf0ecd85
LOK
650}
651
9a54dc82 652int CCECCommandHandler::HandleUserControlPressed(const cec_command &command)
e9de9629 653{
9a54dc82
LOK
654 if (!m_processor->CECInitialised() ||
655 !m_processor->IsHandledByLibCEC(command.destination))
656 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
004b8382 657
9a54dc82
LOK
658 if (command.parameters.size == 0)
659 return CEC_ABORT_REASON_INVALID_OPERAND;
004b8382 660
9a54dc82
LOK
661 CCECBusDevice *device = GetDevice(command.destination);
662 if (!device)
663 return CEC_ABORT_REASON_INVALID_OPERAND;
e33e0d75 664
9a54dc82
LOK
665 CCECClient *client = device->GetClient();
666 if (client)
667 client->AddKey();
e33e0d75 668
9a54dc82
LOK
669 if (command.parameters[0] <= CEC_USER_CONTROL_CODE_MAX)
670 client->SetCurrentButton((cec_user_control_code) command.parameters[0]);
e33e0d75 671
9a54dc82
LOK
672 if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER ||
673 command.parameters[0] == CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION)
674 {
675 bool bPowerOn(true);
676
677 // CEC_USER_CONTROL_CODE_POWER operates as a toggle
678 // assume CEC_USER_CONTROL_CODE_POWER_ON_FUNCTION does not
679 if (command.parameters[0] == CEC_USER_CONTROL_CODE_POWER)
680 {
681 cec_power_status status = device->GetCurrentPowerStatus();
682 bPowerOn = !(status == CEC_POWER_STATUS_ON || status == CEC_POWER_STATUS_IN_TRANSITION_STANDBY_TO_ON);
e9de9629 683 }
e33e0d75 684
9a54dc82
LOK
685 if (bPowerOn)
686 {
687 device->ActivateSource();
688 }
689 else
690 {
691 device->MarkAsInactiveSource();
692 device->TransmitInactiveSource();
693 device->SetMenuState(CEC_MENU_STATE_DEACTIVATED);
694 }
e9de9629 695 }
9a54dc82
LOK
696
697 return COMMAND_HANDLED;
e9de9629
LOK
698}
699
9a54dc82 700int CCECCommandHandler::HandleUserControlRelease(const cec_command &command)
e9de9629 701{
9a54dc82
LOK
702 if (!m_processor->CECInitialised() ||
703 !m_processor->IsHandledByLibCEC(command.destination))
704 return CEC_ABORT_REASON_NOT_IN_CORRECT_MODE_TO_RESPOND;
705
004b8382
LOK
706 CCECClient *client = m_processor->GetClient(command.destination);
707 if (client)
708 client->AddKey();
9a54dc82
LOK
709
710 return COMMAND_HANDLED;
e9de9629
LOK
711}
712
866e7584 713int CCECCommandHandler::HandleVendorCommand(const cec_command & UNUSED(command))
1de6617c 714{
22579015 715 return CEC_ABORT_REASON_INVALID_OPERAND;
1de6617c
LOK
716}
717
9a54dc82 718void CCECCommandHandler::UnhandledCommand(const cec_command &command, const cec_abort_reason reason)
e9de9629 719{
9a54dc82
LOK
720 if (m_processor->IsHandledByLibCEC(command.destination))
721 {
722 LIB_CEC->AddLog(CEC_LOG_DEBUG, "sending abort with opcode %02x and reason '%s' to %s", command.opcode, ToString(reason), ToString(command.initiator));
723 m_processor->TransmitAbort(command.destination, command.initiator, command.opcode, reason);
724 }
0f23c85c
LOK
725}
726
6f14b512 727size_t CCECCommandHandler::GetMyDevices(vector<CCECBusDevice *> &devices) const
8747dd4f 728{
6f14b512 729 size_t iReturn(0);
8747dd4f 730
fcf10e27 731 cec_logical_addresses addresses = m_processor->GetLogicalAddresses();
d2d1660c 732 for (uint8_t iPtr = CECDEVICE_TV; iPtr < CECDEVICE_BROADCAST; iPtr++)
8747dd4f
LOK
733 {
734 if (addresses[iPtr])
735 {
736 devices.push_back(GetDevice((cec_logical_address) iPtr));
737 ++iReturn;
738 }
739 }
740
741 return iReturn;
742}
743
0f23c85c
LOK
744CCECBusDevice *CCECCommandHandler::GetDevice(cec_logical_address iLogicalAddress) const
745{
004b8382 746 return m_processor->GetDevice(iLogicalAddress);
e9de9629 747}
6685ae07
LOK
748
749CCECBusDevice *CCECCommandHandler::GetDeviceByPhysicalAddress(uint16_t iPhysicalAddress) const
750{
fcf10e27 751 return m_processor->GetDeviceByPhysicalAddress(iPhysicalAddress);
6685ae07 752}
181b3475 753
8fa35473 754bool CCECCommandHandler::SetVendorId(const cec_command &command)
181b3475 755{
8fa35473 756 bool bChanged(false);
181b3475
LOK
757 if (command.parameters.size < 3)
758 {
004b8382 759 LIB_CEC->AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
8fa35473 760 return bChanged;
181b3475
LOK
761 }
762
bae71306
LOK
763 uint64_t iVendorId = ((uint64_t)command.parameters[0] << 16) +
764 ((uint64_t)command.parameters[1] << 8) +
181b3475
LOK
765 (uint64_t)command.parameters[2];
766
767 CCECBusDevice *device = GetDevice((cec_logical_address) command.initiator);
768 if (device)
8fa35473
LOK
769 bChanged = device->SetVendorId(iVendorId);
770 return bChanged;
181b3475 771}
5e822b09 772
16b1e052
LOK
773void CCECCommandHandler::SetPhysicalAddress(cec_logical_address iAddress, uint16_t iNewAddress)
774{
004b8382 775 if (!m_processor->IsHandledByLibCEC(iAddress))
16b1e052 776 {
c0152c09
LOK
777 CCECBusDevice *otherDevice = m_processor->GetDeviceByPhysicalAddress(iNewAddress);
778 CCECClient *client = otherDevice ? otherDevice->GetClient() : NULL;
004b8382 779
c0152c09
LOK
780 CCECBusDevice *device = m_processor->GetDevice(iAddress);
781 if (device)
782 device->SetPhysicalAddress(iNewAddress);
783 else
16b1e052 784 {
c0152c09 785 LIB_CEC->AddLog(CEC_LOG_DEBUG, "device with logical address %X not found", iAddress);
16b1e052 786 }
c0152c09
LOK
787
788 /* another device reported the same physical address as ours */
789 if (client)
790 client->ResetPhysicalAddress();
791 }
792 else
793 {
794 LIB_CEC->AddLog(CEC_LOG_DEBUG, "ignore physical address report for device %s (%X) because it's marked as handled by libCEC", ToString(iAddress), iAddress);
16b1e052
LOK
795 }
796}
855a3a98 797
f4698390
LOK
798bool CCECCommandHandler::PowerOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
799{
800 if (iDestination == CECDEVICE_TV)
801 return TransmitImageViewOn(iInitiator, iDestination);
802
803 return TransmitKeypress(iInitiator, iDestination, CEC_USER_CONTROL_CODE_POWER) &&
804 TransmitKeyRelease(iInitiator, iDestination);
805}
806
b64db02e 807bool CCECCommandHandler::TransmitImageViewOn(const cec_logical_address iInitiator, const cec_logical_address iDestination)
8fa35473
LOK
808{
809 cec_command command;
ae693aaa 810 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_IMAGE_VIEW_ON);
8fa35473 811
4fde2fd0 812 return Transmit(command);
8fa35473
LOK
813}
814
815bool CCECCommandHandler::TransmitStandby(const cec_logical_address iInitiator, const cec_logical_address iDestination)
816{
817 cec_command command;
ae693aaa 818 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_STANDBY);
8fa35473 819
4fde2fd0 820 return Transmit(command);
8fa35473
LOK
821}
822
5734016c
LOK
823bool CCECCommandHandler::TransmitRequestActiveSource(const cec_logical_address iInitiator, bool bWaitForResponse /* = true */)
824{
825 cec_command command;
826 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_REQUEST_ACTIVE_SOURCE);
827
828 return Transmit(command, !bWaitForResponse);
829}
830
a75e3a5a 831bool CCECCommandHandler::TransmitRequestCecVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
832{
833 cec_command command;
ae693aaa 834 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_CEC_VERSION);
8fa35473 835
4fde2fd0 836 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
837}
838
a75e3a5a 839bool CCECCommandHandler::TransmitRequestMenuLanguage(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
840{
841 cec_command command;
ae693aaa 842 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GET_MENU_LANGUAGE);
8fa35473 843
4fde2fd0 844 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
845}
846
a75e3a5a 847bool CCECCommandHandler::TransmitRequestOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
848{
849 cec_command command;
ae693aaa 850 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_OSD_NAME);
8fa35473 851
4fde2fd0 852 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
853}
854
a75e3a5a 855bool CCECCommandHandler::TransmitRequestPhysicalAddress(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
856{
857 cec_command command;
ae693aaa 858 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_PHYSICAL_ADDRESS);
8fa35473 859
4fde2fd0 860 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
861}
862
a75e3a5a 863bool CCECCommandHandler::TransmitRequestPowerStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
864{
865 cec_command command;
ae693aaa 866 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_POWER_STATUS);
8fa35473 867
4fde2fd0 868 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
869}
870
a75e3a5a 871bool CCECCommandHandler::TransmitRequestVendorId(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWaitForResponse /* = true */)
8fa35473
LOK
872{
873 cec_command command;
ae693aaa 874 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
8fa35473 875
4fde2fd0 876 return Transmit(command, !bWaitForResponse);
8fa35473
LOK
877}
878
879bool CCECCommandHandler::TransmitActiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
880{
881 cec_command command;
ae693aaa 882 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
8fa35473
LOK
883 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
884 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
885
4fde2fd0 886 return Transmit(command);
8fa35473
LOK
887}
888
889bool CCECCommandHandler::TransmitCECVersion(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_version cecVersion)
890{
891 cec_command command;
ae693aaa 892 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_CEC_VERSION);
8fa35473
LOK
893 command.parameters.PushBack((uint8_t)cecVersion);
894
4fde2fd0 895 return Transmit(command);
8fa35473
LOK
896}
897
898bool CCECCommandHandler::TransmitInactiveSource(const cec_logical_address iInitiator, uint16_t iPhysicalAddress)
899{
900 cec_command command;
ae693aaa 901 cec_command::Format(command, iInitiator, CECDEVICE_TV, CEC_OPCODE_INACTIVE_SOURCE);
8fa35473
LOK
902 command.parameters.PushBack((iPhysicalAddress >> 8) & 0xFF);
903 command.parameters.PushBack(iPhysicalAddress & 0xFF);
904
4fde2fd0 905 return Transmit(command);
8fa35473
LOK
906}
907
908bool CCECCommandHandler::TransmitMenuState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_menu_state menuState)
909{
910 cec_command command;
ae693aaa 911 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_MENU_STATUS);
8fa35473
LOK
912 command.parameters.PushBack((uint8_t)menuState);
913
4fde2fd0 914 return Transmit(command);
8fa35473
LOK
915}
916
917bool CCECCommandHandler::TransmitOSDName(const cec_logical_address iInitiator, const cec_logical_address iDestination, CStdString strDeviceName)
918{
919 cec_command command;
ae693aaa 920 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_NAME);
6f14b512 921 for (size_t iPtr = 0; iPtr < strDeviceName.length(); iPtr++)
8fa35473
LOK
922 command.parameters.PushBack(strDeviceName.at(iPtr));
923
4fde2fd0 924 return Transmit(command);
8fa35473
LOK
925}
926
927bool CCECCommandHandler::TransmitOSDString(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_display_control duration, const char *strMessage)
928{
929 cec_command command;
ae693aaa 930 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_OSD_STRING);
8fa35473
LOK
931 command.parameters.PushBack((uint8_t)duration);
932
6f14b512 933 size_t iLen = strlen(strMessage);
8fa35473
LOK
934 if (iLen > 13) iLen = 13;
935
6f14b512 936 for (size_t iPtr = 0; iPtr < iLen; iPtr++)
8fa35473
LOK
937 command.parameters.PushBack(strMessage[iPtr]);
938
4fde2fd0 939 return Transmit(command);
8fa35473
LOK
940}
941
942bool CCECCommandHandler::TransmitPhysicalAddress(const cec_logical_address iInitiator, uint16_t iPhysicalAddress, cec_device_type type)
943{
944 cec_command command;
ae693aaa 945 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
8fa35473
LOK
946 command.parameters.PushBack((uint8_t) ((iPhysicalAddress >> 8) & 0xFF));
947 command.parameters.PushBack((uint8_t) (iPhysicalAddress & 0xFF));
948 command.parameters.PushBack((uint8_t) (type));
949
4fde2fd0 950 return Transmit(command);
fbdea54c
MK
951}
952
953bool CCECCommandHandler::TransmitSetMenuLanguage(const cec_logical_address iInitiator, const char lang[3])
954{
955 cec_command command;
956 command.Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_SET_MENU_LANGUAGE);
957 command.parameters.PushBack((uint8_t) lang[0]);
958 command.parameters.PushBack((uint8_t) lang[1]);
959 command.parameters.PushBack((uint8_t) lang[2]);
960
4fde2fd0 961 return Transmit(command);
8fa35473
LOK
962}
963
964bool CCECCommandHandler::TransmitPoll(const cec_logical_address iInitiator, const cec_logical_address iDestination)
965{
966 cec_command command;
ae693aaa 967 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_NONE);
8fa35473 968
4fde2fd0 969 return Transmit(command);
8fa35473
LOK
970}
971
972bool CCECCommandHandler::TransmitPowerState(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_power_status state)
973{
974 cec_command command;
ae693aaa 975 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_POWER_STATUS);
8fa35473
LOK
976 command.parameters.PushBack((uint8_t) state);
977
4fde2fd0 978 return Transmit(command);
8fa35473
LOK
979}
980
981bool CCECCommandHandler::TransmitVendorID(const cec_logical_address iInitiator, uint64_t iVendorId)
982{
983 cec_command command;
ae693aaa 984 cec_command::Format(command, iInitiator, CECDEVICE_BROADCAST, CEC_OPCODE_DEVICE_VENDOR_ID);
8fa35473
LOK
985
986 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 16) & 0xFF));
987 command.parameters.PushBack((uint8_t) (((uint64_t)iVendorId >> 8) & 0xFF));
988 command.parameters.PushBack((uint8_t) ((uint64_t)iVendorId & 0xFF));
989
4fde2fd0 990 return Transmit(command);
8fa35473
LOK
991}
992
993bool CCECCommandHandler::TransmitAudioStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, uint8_t state)
994{
995 cec_command command;
ae693aaa 996 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_REPORT_AUDIO_STATUS);
8fa35473
LOK
997 command.parameters.PushBack(state);
998
4fde2fd0 999 return Transmit(command);
8fa35473
LOK
1000}
1001
1002bool CCECCommandHandler::TransmitSetSystemAudioMode(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
1003{
1004 cec_command command;
ae693aaa 1005 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SET_SYSTEM_AUDIO_MODE);
8fa35473
LOK
1006 command.parameters.PushBack((uint8_t)state);
1007
4fde2fd0 1008 return Transmit(command);
8fa35473
LOK
1009}
1010
f42d3e0f
LOK
1011bool CCECCommandHandler::TransmitSetStreamPath(uint16_t iStreamPath)
1012{
1013 cec_command command;
1014 cec_command::Format(command, m_busDevice->GetLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_SET_STREAM_PATH);
1015 command.parameters.PushBack((uint8_t) ((iStreamPath >> 8) & 0xFF));
1016 command.parameters.PushBack((uint8_t) (iStreamPath & 0xFF));
1017
4fde2fd0 1018 return Transmit(command);
f42d3e0f
LOK
1019}
1020
8fa35473
LOK
1021bool CCECCommandHandler::TransmitSystemAudioModeStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_system_audio_status state)
1022{
1023 cec_command command;
ae693aaa 1024 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_SYSTEM_AUDIO_MODE_STATUS);
8fa35473
LOK
1025 command.parameters.PushBack((uint8_t)state);
1026
4fde2fd0 1027 return Transmit(command);
8fa35473
LOK
1028}
1029
1030bool CCECCommandHandler::TransmitDeckStatus(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_deck_info state)
1031{
1032 cec_command command;
ae693aaa 1033 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_DECK_STATUS);
8fa35473
LOK
1034 command.PushBack((uint8_t)state);
1035
4fde2fd0 1036 return Transmit(command);
8fa35473
LOK
1037}
1038
4bec9d79 1039bool CCECCommandHandler::TransmitKeypress(const cec_logical_address iInitiator, const cec_logical_address iDestination, cec_user_control_code key, bool bWait /* = true */)
8fa35473
LOK
1040{
1041 cec_command command;
ae693aaa 1042 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_PRESSED);
8fa35473
LOK
1043 command.parameters.PushBack((uint8_t)key);
1044
4fde2fd0 1045 return Transmit(command, !bWait);
8fa35473
LOK
1046}
1047
4bec9d79 1048bool CCECCommandHandler::TransmitKeyRelease(const cec_logical_address iInitiator, const cec_logical_address iDestination, bool bWait /* = true */)
8fa35473
LOK
1049{
1050 cec_command command;
ae693aaa 1051 cec_command::Format(command, iInitiator, iDestination, CEC_OPCODE_USER_CONTROL_RELEASE);
8fa35473 1052
4fde2fd0 1053 return Transmit(command, !bWait);
8fa35473
LOK
1054}
1055
4fde2fd0 1056bool CCECCommandHandler::Transmit(cec_command &command, bool bSuppressWait /* = false */)
8fa35473 1057{
b64db02e 1058 bool bReturn(false);
4fde2fd0
LOK
1059 cec_opcode expectedResponse(cec_command::GetResponseOpcode(command.opcode));
1060 bool bExpectResponse(expectedResponse != CEC_OPCODE_NONE && !bSuppressWait);
ae693aaa 1061 command.transmit_timeout = m_iTransmitTimeout;
ae693aaa 1062
7e4558f1
LOK
1063 if (command.initiator == CECDEVICE_UNKNOWN)
1064 {
004b8382 1065 LIB_CEC->AddLog(CEC_LOG_ERROR, "not transmitting a command without a valid initiator");
7e4558f1
LOK
1066 return bReturn;
1067 }
1068
ae693aaa 1069 {
6c0ccaa5 1070 uint8_t iTries(0), iMaxTries(!command.opcode_set ? 1 : m_iTransmitRetries + 1);
ec15344c 1071 while (!bReturn && ++iTries <= iMaxTries && !m_busDevice->IsUnsupportedFeature(command.opcode))
b64db02e 1072 {
99666519 1073 if ((bReturn = m_processor->Transmit(command)) == true)
19cbfa8f 1074 {
004b8382 1075 LIB_CEC->AddLog(CEC_LOG_DEBUG, "command transmitted");
4478bc79 1076 if (bExpectResponse)
24dd566c 1077 {
060a7b5e 1078 bReturn = m_busDevice->WaitForOpcode(expectedResponse);
004b8382 1079 LIB_CEC->AddLog(CEC_LOG_DEBUG, bReturn ? "expected response received (%X: %s)" : "expected response not received (%X: %s)", (int)expectedResponse, ToString(expectedResponse));
24dd566c 1080 }
19cbfa8f 1081 }
b64db02e 1082 }
ae693aaa 1083 }
8fa35473 1084
b64db02e 1085 return bReturn;
8fa35473 1086}
83be0701 1087
aa4c0d34 1088bool CCECCommandHandler::ActivateSource(bool bTransmitDelayedCommandsOnly /* = false */)
83be0701 1089{
c4287bcd 1090 if (m_busDevice->IsActiveSource() &&
aa4c0d34 1091 m_busDevice->IsHandledByLibCEC())
c4287bcd 1092 {
b0015449
LOK
1093 {
1094 CLockObject lock(m_mutex);
aa4c0d34
LOK
1095 // check if we need to send a delayed source switch
1096 if (bTransmitDelayedCommandsOnly)
1097 {
1098 if (m_iActiveSourcePending == 0 || GetTimeMs() < m_iActiveSourcePending)
1099 return false;
1100
1101 LIB_CEC->AddLog(CEC_LOG_DEBUG, "transmitting delayed activate source command");
1102 }
1103
1104 // clear previous pending active source command
1105 m_iActiveSourcePending = 0;
b0015449
LOK
1106 }
1107
aa4c0d34 1108 // update the power state and menu state
c4287bcd 1109 m_busDevice->SetPowerStatus(CEC_POWER_STATUS_ON);
aa4c0d34
LOK
1110 m_busDevice->SetMenuState(CEC_MENU_STATE_ACTIVATED); // TODO: LG
1111
1112 // power on the TV
1113 bool bActiveSourceFailed = !m_busDevice->TransmitImageViewOn();
c4287bcd 1114
aa4c0d34
LOK
1115 // check if we're allowed to switch sources
1116 bool bSourceSwitchAllowed = SourceSwitchAllowed();
1117 if (!bSourceSwitchAllowed)
1118 LIB_CEC->AddLog(CEC_LOG_DEBUG, "source switch is currently not allowed by command handler");
dbad810a 1119
aa4c0d34
LOK
1120 // switch sources (if allowed)
1121 if (!bActiveSourceFailed && bSourceSwitchAllowed)
b0015449 1122 {
aa4c0d34
LOK
1123 bActiveSourceFailed = !m_busDevice->TransmitActiveSource() ||
1124 !m_busDevice->TransmitMenuState(CECDEVICE_TV);
1125
1126 // update the deck status for playback devices
1127 if (!bActiveSourceFailed)
1128 {
1129 CCECPlaybackDevice *playbackDevice = m_busDevice->AsPlaybackDevice();
1130 if (playbackDevice && SendDeckStatusUpdateOnActiveSource())
1131 bActiveSourceFailed = !playbackDevice->TransmitDeckStatus(CECDEVICE_TV);
1132 }
dbad810a
LOK
1133 }
1134
aa4c0d34
LOK
1135 // retry later
1136 if (bActiveSourceFailed || !bSourceSwitchAllowed)
dbad810a
LOK
1137 {
1138 LIB_CEC->AddLog(CEC_LOG_DEBUG, "failed to make '%s' the active source. will retry later", m_busDevice->GetLogicalAddressName());
b0015449 1139 CLockObject lock(m_mutex);
29c4a2d1 1140 m_iActiveSourcePending = GetTimeMs() + (int64_t)CEC_ACTIVE_SOURCE_SWITCH_RETRY_TIME_MS;
b0015449
LOK
1141 return false;
1142 }
aa40e0e2 1143
aa4c0d34
LOK
1144 // mark the handler as initialised
1145 CLockObject lock(m_mutex);
c4287bcd 1146 m_bHandlerInited = true;
83be0701 1147 }
7dc58c9f 1148 return true;
83be0701 1149}
b499cf16 1150
060a7b5e 1151void CCECCommandHandler::ScheduleActivateSource(uint64_t iDelay)
b499cf16 1152{
060a7b5e
LOK
1153 CLockObject lock(m_mutex);
1154 m_iActiveSourcePending = GetTimeMs() + iDelay;
b499cf16 1155}