cec: added GetDeviceMenuLanguage()/cec_get_device_menu_language()
[deb_libcec.git] / src / lib / devices / CECBusDevice.cpp
CommitLineData
e9de9629
LOK
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 "CECBusDevice.h"
eafa9d46
LOK
34#include "../CECProcessor.h"
35#include "../implementations/ANCommandHandler.h"
36#include "../implementations/CECCommandHandler.h"
37#include "../implementations/SLCommandHandler.h"
38#include "../platform/timeutils.h"
e9de9629
LOK
39
40using namespace CEC;
41
42CCECBusDevice::CCECBusDevice(CCECProcessor *processor, cec_logical_address iLogicalAddress, uint16_t iPhysicalAddress) :
43 m_iPhysicalAddress(iPhysicalAddress),
44 m_iLogicalAddress(iLogicalAddress),
45 m_processor(processor),
46 m_iVendorId(0),
0ab58650 47 m_iVendorClass(CEC_VENDOR_UNKNOWN),
6a1c0009
LOK
48 m_iLastActive(0),
49 m_cecVersion(CEC_VERSION_UNKNOWN)
e9de9629
LOK
50{
51 m_handler = new CCECCommandHandler(this);
a3269a0a
LOK
52 for (unsigned int iPtr = 0; iPtr < 4; iPtr++)
53 m_menuLanguage.language[iPtr] = '?';
54 m_menuLanguage.language[3] = 0;
55 m_menuLanguage.device = iLogicalAddress;
e9de9629
LOK
56}
57
58CCECBusDevice::~CCECBusDevice(void)
59{
6a1c0009 60 m_condition.Broadcast();
e9de9629
LOK
61 delete m_handler;
62}
63
64cec_logical_address CCECBusDevice::GetMyLogicalAddress(void) const
65{
66 return m_processor->GetLogicalAddress();
67}
68
69uint16_t CCECBusDevice::GetMyPhysicalAddress(void) const
70{
71 return m_processor->GetPhysicalAddress();
72}
73
74void CCECBusDevice::AddLog(cec_log_level level, const CStdString &strMessage)
75{
76 m_processor->AddLog(level, strMessage);
77}
78
a3269a0a
LOK
79void CCECBusDevice::SetMenuLanguage(const cec_menu_language &language)
80{
81 if (language.device == m_iLogicalAddress)
82 {
83 CStdString strLog;
84 strLog.Format("device %d menu language set to '%s'", m_iLogicalAddress, language.language);
85 m_processor->AddLog(CEC_LOG_DEBUG, strLog);
86 m_menuLanguage = language;
87 }
88}
89
6a1c0009
LOK
90void CCECBusDevice::SetCecVersion(cec_version newVersion)
91{
92 CStdString strLog;
93 m_cecVersion = newVersion;
94
95 switch (newVersion)
96 {
97 case CEC_VERSION_1_2:
98 strLog.Format("device %d reports CEC version 1.2", m_iLogicalAddress);
99 break;
100 case CEC_VERSION_1_2A:
101 strLog.Format("device %d reports CEC version 1.2a", m_iLogicalAddress);
102 break;
103 case CEC_VERSION_1_3:
104 strLog.Format("device %d reports CEC version 1.3", m_iLogicalAddress);
105 break;
106 case CEC_VERSION_1_3A:
107 strLog.Format("device %d reports CEC version 1.3a", m_iLogicalAddress);
108 break;
109 default:
110 strLog.Format("device %d reports an unknown CEC version", m_iLogicalAddress);
111 m_cecVersion = CEC_VERSION_UNKNOWN;
112 break;
113 }
114 AddLog(CEC_LOG_DEBUG, strLog);
115}
116
0f23c85c
LOK
117void CCECBusDevice::SetVendorId(const cec_datapacket &data)
118{
119 if (data.size < 3)
120 {
121 AddLog(CEC_LOG_WARNING, "invalid vendor ID received");
122 return;
123 }
124
125 uint64_t iVendorId = ((uint64_t)data[0] << 3) +
126 ((uint64_t)data[1] << 2) +
127 (uint64_t)data[2];
128
129 SetVendorId(iVendorId, data.size >= 4 ? data[3] : 0);
130}
131
2cd8b5d0 132void CCECBusDevice::SetVendorId(uint64_t iVendorId, uint8_t iVendorClass /* = 0 */)
e9de9629 133{
e9de9629
LOK
134 m_iVendorId = iVendorId;
135 m_iVendorClass = iVendorClass;
136
137 switch (iVendorId)
138 {
139 case CEC_VENDOR_SAMSUNG:
0ab58650
LOK
140 if (m_handler->GetVendorId() != CEC_VENDOR_SAMSUNG)
141 {
142 delete m_handler;
143 m_handler = new CANCommandHandler(this);
144 }
e9de9629
LOK
145 break;
146 case CEC_VENDOR_LG:
0ab58650
LOK
147 if (m_handler->GetVendorId() != CEC_VENDOR_LG)
148 {
149 delete m_handler;
150 m_handler = new CSLCommandHandler(this);
151 }
e9de9629
LOK
152 break;
153 default:
0ab58650
LOK
154 if (m_handler->GetVendorId() != CEC_VENDOR_UNKNOWN)
155 {
156 delete m_handler;
157 m_handler = new CCECCommandHandler(this);
158 }
e9de9629
LOK
159 break;
160 }
161
162 CStdString strLog;
0f23c85c 163 strLog.Format("device %d: vendor = %s (%06x) class = %2x", m_iLogicalAddress, GetVendorName(), GetVendorId(), GetVendorClass());
e9de9629
LOK
164 m_processor->AddLog(CEC_LOG_DEBUG, strLog.c_str());
165}
166
167bool CCECBusDevice::HandleCommand(const cec_command &command)
168{
169 CLockObject lock(&m_mutex);
0ab58650 170 m_iLastActive = GetTimeMs();
e9de9629 171 m_handler->HandleCommand(command);
6a1c0009 172 m_condition.Signal();
e9de9629
LOK
173 return true;
174}
175
0ab58650
LOK
176void CCECBusDevice::PollVendorId(void)
177{
178 CLockObject lock(&m_mutex);
0f23c85c
LOK
179 if (m_iLastActive > 0 && m_iLogicalAddress != CECDEVICE_BROADCAST &&
180 m_iVendorId == CEC_VENDOR_UNKNOWN &&
0ab58650
LOK
181 GetTimeMs() - m_iLastActive > 5000)
182 {
183 m_iLastActive = GetTimeMs();
184
185 cec_command command;
186 cec_command::format(command, GetMyLogicalAddress(), GetLogicalAddress(), CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
8d84e2c0
LOK
187 command.ack_timeout = 0;
188 m_processor->Transmit(command);
0ab58650
LOK
189 }
190}
191
4e4be5cf
LOK
192void CCECBusDevice::SetPhysicalAddress(uint16_t iNewAddress, uint16_t iOldAddress /* = 0 */)
193{
194 CStdString strLog;
195 strLog.Format(">> %i changed physical address from %04x to %04x", GetLogicalAddress(), m_iPhysicalAddress, iNewAddress);
196 AddLog(CEC_LOG_DEBUG, strLog.c_str());
197
198 m_iPhysicalAddress = iNewAddress;
199}
200
0f23c85c
LOK
201bool CCECBusDevice::PowerOn(void)
202{
203 CStdString strLog;
204 strLog.Format("<< powering on device with logical address %d", (int8_t)m_iLogicalAddress);
205 AddLog(CEC_LOG_DEBUG, strLog.c_str());
206
207 cec_command command;
208 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_IMAGE_VIEW_ON);
209
210 return m_processor->Transmit(command);
211}
212
213bool CCECBusDevice::Standby(void)
214{
215 CStdString strLog;
216 strLog.Format("<< putting device with logical address %d in standby mode", (int8_t)m_iLogicalAddress);
217 AddLog(CEC_LOG_DEBUG, strLog.c_str());
218
219 cec_command command;
220 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_STANDBY);
221
222 return m_processor->Transmit(command);
223}
224
225bool CCECBusDevice::SetOSDString(cec_display_control duration, const char *strMessage)
226{
227 CStdString strLog;
228 strLog.Format("<< display message '%s'", strMessage);
229 AddLog(CEC_LOG_NOTICE, strLog.c_str());
230
231 cec_command command;
232 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_SET_OSD_STRING);
233 command.parameters.push_back((uint8_t)duration);
234
235 for (unsigned int iPtr = 0; iPtr < strlen(strMessage); iPtr++)
236 command.parameters.push_back(strMessage[iPtr]);
237
238 return m_processor->Transmit(command);
239}
240
241bool CCECBusDevice::ReportCECVersion(void)
242{
243 AddLog(CEC_LOG_NOTICE, "<< reporting CEC version as 1.3a");
244
245 cec_command command;
246 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_CEC_VERSION);
247 command.parameters.push_back(CEC_VERSION_1_3A);
248
249 return m_processor->Transmit(command);
250}
251
252bool CCECBusDevice::ReportDeckStatus(void)
253{
254 // need to support opcodes play and deck control before doing anything with this
255 AddLog(CEC_LOG_NOTICE, "<< deck status requested, feature abort");
256 m_processor->TransmitAbort(m_iLogicalAddress, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
257 return false;
258}
259
260bool CCECBusDevice::ReportMenuState(bool bActive /* = true */)
261{
262 if (bActive)
263 AddLog(CEC_LOG_NOTICE, "<< reporting menu state as active");
264 else
265 AddLog(CEC_LOG_NOTICE, "<< reporting menu state as inactive");
266
267 cec_command command;
268 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_MENU_STATUS);
269 command.parameters.push_back(bActive ? (uint8_t) CEC_MENU_STATE_ACTIVATED : (uint8_t) CEC_MENU_STATE_DEACTIVATED);
270
271 return m_processor->Transmit(command);
272}
273
274bool CCECBusDevice::ReportOSDName(void)
275{
276 const char *osdname = m_processor->GetDeviceName().c_str();
277 CStdString strLog;
278 strLog.Format("<< reporting OSD name as %s", osdname);
279 AddLog(CEC_LOG_NOTICE, strLog.c_str());
280
281 cec_command command;
282 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_SET_OSD_NAME);
283 for (unsigned int iPtr = 0; iPtr < strlen(osdname); iPtr++)
284 command.parameters.push_back(osdname[iPtr]);
285
286 return m_processor->Transmit(command);
287}
288
289bool CCECBusDevice::ReportPowerState(bool bOn /* = true */)
290{
291 if (bOn)
292 AddLog(CEC_LOG_NOTICE, "<< reporting \"On\" power status");
293 else
294 AddLog(CEC_LOG_NOTICE, "<< reporting \"Off\" power status");
295
296 cec_command command;
297 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_REPORT_POWER_STATUS);
298 command.parameters.push_back(bOn ? (uint8_t) CEC_POWER_STATUS_ON : (uint8_t) CEC_POWER_STATUS_STANDBY);
299
300 return m_processor->Transmit(command);
301}
302
303bool CCECBusDevice::ReportVendorID(void)
304{
305 AddLog(CEC_LOG_NOTICE, "<< vendor ID requested, feature abort");
306 m_processor->TransmitAbort(m_iLogicalAddress, CEC_OPCODE_GIVE_DEVICE_VENDOR_ID);
307 return false;
308}
309
310bool CCECBusDevice::BroadcastActiveView(void)
311{
312 AddLog(CEC_LOG_DEBUG, "<< setting active view");
313
314 cec_command command;
82b6ff53 315 cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
0f23c85c
LOK
316 command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
317 command.parameters.push_back(m_iPhysicalAddress & 0xFF);
318
319 return m_processor->Transmit(command);
320}
321
322bool CCECBusDevice::BroadcastInactiveView(void)
323{
324 AddLog(CEC_LOG_DEBUG, "<< setting inactive view");
325
326 cec_command command;
82b6ff53 327 cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_INACTIVE_SOURCE);
0f23c85c
LOK
328 command.parameters.push_back((m_iPhysicalAddress >> 8) & 0xFF);
329 command.parameters.push_back(m_iPhysicalAddress & 0xFF);
330
331 return m_processor->Transmit(command);
332}
333
334bool CCECBusDevice::BroadcastPhysicalAddress(void)
335{
336 CStdString strLog;
337 strLog.Format("<< reporting physical address as %04x", m_iPhysicalAddress);
338 AddLog(CEC_LOG_NOTICE, strLog.c_str());
339
340 cec_command command;
82b6ff53 341 cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_REPORT_PHYSICAL_ADDRESS);
0f23c85c
LOK
342 command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
343 command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
344 command.parameters.push_back((uint8_t) (CEC_DEVICE_TYPE_PLAYBACK_DEVICE));
345
346 return m_processor->Transmit(command);
347}
348
349bool CCECBusDevice::BroadcastActiveSource(void)
350{
351 AddLog(CEC_LOG_NOTICE, "<< broadcasting active source");
352
353 cec_command command;
82b6ff53 354 cec_command::format(command, GetMyLogicalAddress(), CECDEVICE_BROADCAST, CEC_OPCODE_ACTIVE_SOURCE);
0f23c85c
LOK
355 command.parameters.push_back((uint8_t) ((m_iPhysicalAddress >> 8) & 0xFF));
356 command.parameters.push_back((uint8_t) (m_iPhysicalAddress & 0xFF));
357
358 return m_processor->Transmit(command);
359}
360
6a1c0009
LOK
361cec_version CCECBusDevice::GetCecVersion(void)
362{
363 if (m_cecVersion == CEC_VERSION_UNKNOWN)
364 {
365 AddLog(CEC_LOG_NOTICE, "<< requesting CEC version");
366 cec_command command;
367 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_GET_CEC_VERSION);
368 CLockObject lock(&m_mutex);
369 if (m_processor->Transmit(command))
370 m_condition.Wait(&m_mutex, 1000);
371 }
372
373 return m_cecVersion;
374}
375
a3269a0a
LOK
376cec_menu_language &CCECBusDevice::GetMenuLanguage(void)
377{
378 if (!strcmp(m_menuLanguage.language, "???"))
379 {
380 AddLog(CEC_LOG_NOTICE, "<< requesting menu language");
381 cec_command command;
382 cec_command::format(command, GetMyLogicalAddress(), m_iLogicalAddress, CEC_OPCODE_GET_MENU_LANGUAGE);
383 CLockObject lock(&m_mutex);
384 if (m_processor->Transmit(command))
385 m_condition.Wait(&m_mutex, 1000);
386 }
387
388 return m_menuLanguage;
389}
390
e9de9629
LOK
391const char *CCECBusDevice::CECVendorIdToString(const uint64_t iVendorId)
392{
393 switch (iVendorId)
394 {
395 case CEC_VENDOR_SAMSUNG:
396 return "Samsung";
397 case CEC_VENDOR_LG:
398 return "LG";
399 default:
400 return "Unknown";
401 }
402}