MythTV  master
firewiredevice.cpp
Go to the documentation of this file.
1 
7 // C++ headers
8 #include <algorithm>
9 #include <chrono> // for milliseconds
10 #include <thread> // for sleep_for
11 
12 // Qt headers
13 #include <QMap>
14 
15 // MythTV headers
16 #include "linuxfirewiredevice.h"
17 #if USING_OSX_FIREWIRE
18 #include "darwinfirewiredevice.h"
19 #endif
20 #include "mythlogging.h"
21 #include "mpegtables.h"
22 
23 #define LOC QString("FireDev(%1): ").arg(guid_to_string(m_guid))
24 
25 static void fw_init(QMap<uint64_t,QString> &id_to_model);
26 
27 QMap<uint64_t,QString> FirewireDevice::s_id_to_model;
29 
30 FirewireDevice::FirewireDevice(uint64_t guid, uint subunitid, uint speed) :
31  m_guid(guid), m_subunitid(subunitid),
32  m_speed(speed)
33 {
34 }
35 
37 {
38  if (listener)
39  {
40  auto it = find(m_listeners.begin(), m_listeners.end(), listener);
41  if (it == m_listeners.end())
42  m_listeners.push_back(listener);
43  }
44 
45  LOG(VB_RECORD, LOG_INFO, LOC +
46  QString("AddListener() %1").arg(m_listeners.size()));
47 }
48 
50 {
51  auto it = m_listeners.end();
52 
53  do
54  {
55  it = find(m_listeners.begin(), m_listeners.end(), listener);
56  if (it != m_listeners.end())
57  {
58  m_listeners.erase(it);
59  it = m_listeners.begin();
60  }
61  }
62  while (it != m_listeners.end());
63 
64  LOG(VB_RECORD, LOG_INFO, LOC +
65  QString("RemoveListener() %1").arg(m_listeners.size()));
66 }
67 
69 {
70  QMutexLocker locker(&m_lock);
71 
72  vector<uint8_t> cmd;
73  vector<uint8_t> ret;
74 
75  cmd.push_back(kAVCControlCommand);
77  cmd.push_back(kAVCUnitPowerOpcode);
78  cmd.push_back((on) ? kAVCPowerStateOn : kAVCPowerStateOff);
79 
80  QString cmdStr = (on) ? "on" : "off";
81  LOG(VB_RECORD, LOG_INFO, LOC + QString("Powering %1").arg(cmdStr));
82 
83  if (!SendAVCCommand(cmd, ret, -1))
84  {
85  LOG(VB_GENERAL, LOG_ERR, LOC + "Power on cmd failed (no response)");
86  return false;
87  }
88 
89  if (kAVCAcceptedStatus != ret[0])
90  {
91  LOG(VB_GENERAL, LOG_ERR, LOC + QString("Power %1 failed").arg(cmdStr));
92 
93  return false;
94  }
95 
96  LOG(VB_RECORD, LOG_INFO, LOC +
97  QString("Power %1 cmd sent successfully").arg(cmdStr));
98 
99  return true;
100 }
101 
103 {
104  QMutexLocker locker(&m_lock);
105 
106  vector<uint8_t> cmd;
107  vector<uint8_t> ret;
108 
109  cmd.push_back(kAVCStatusInquiryCommand);
110  cmd.push_back(kAVCSubunitTypeUnit | kAVCSubunitIdIgnore);
111  cmd.push_back(kAVCUnitPowerOpcode);
112  cmd.push_back(kAVCPowerStateQuery);
113 
114  LOG(VB_CHANNEL, LOG_INFO, LOC + "Requesting STB Power State");
115 
116  if (!SendAVCCommand(cmd, ret, -1))
117  {
118  LOG(VB_GENERAL, LOG_ERR, LOC + "Power cmd failed (no response)");
119  return kAVCPowerQueryFailed;
120  }
121 
122  QString loc = LOC + "STB Power State: ";
123 
124  if (ret[0] != kAVCResponseImplemented)
125  {
126  LOG(VB_CHANNEL, LOG_INFO, loc + "Query not implemented");
127  return kAVCPowerUnknown;
128  }
129 
130  // check 1st operand..
131  if (ret[3] == kAVCPowerStateOn)
132  {
133  LOG(VB_CHANNEL, LOG_INFO, loc + "On");
134  return kAVCPowerOn;
135  }
136 
137  if (ret[3] == kAVCPowerStateOff)
138  {
139  LOG(VB_CHANNEL, LOG_INFO, loc + "Off");
140  return kAVCPowerOff;
141  }
142 
143  LOG(VB_GENERAL, LOG_ERR, LOC + "STB Power State: Unknown Response");
144 
145  return kAVCPowerUnknown;
146 }
147 
148 bool FirewireDevice::SetChannel(const QString &panel_model,
149  uint alt_method, uint channel)
150 {
151  LOG(VB_CHANNEL, LOG_INFO, QString("SetChannel(model %1, alt %2, chan %3)")
152  .arg(panel_model).arg(alt_method).arg(channel));
153 
154  QMutexLocker locker(&m_lock);
155  LOG(VB_CHANNEL, LOG_INFO, "SetChannel() -- locked");
156 
157  if (!IsSTBSupported(panel_model))
158  {
159  LOG(VB_GENERAL, LOG_ERR, LOC +
160  QString("Model: '%1' ").arg(panel_model) +
161  "is not supported by internal channel changer.");
162  return false;
163  }
164 
165  int digit[3];
166  digit[0] = (channel % 1000) / 100;
167  digit[1] = (channel % 100) / 10;
168  digit[2] = (channel % 10);
169 
171  {
172  LOG(VB_GENERAL, LOG_ERR, LOC +
173  "SetChannel: Extended subunits are not supported.");
174 
175  return false;
176  }
177 
178  vector<uint8_t> cmd;
179  vector<uint8_t> ret;
180 
181  if ((panel_model.toUpper() == "SA GENERIC") ||
182  (panel_model.toUpper() == "SA4200HD") ||
183  (panel_model.toUpper() == "SA4250HDC"))
184  {
185  if (panel_model.toUpper() == "SA4250HDC")
186  {
187  LOG(VB_GENERAL, LOG_ERR, LOC +
188  "The Scientific Atlanta 4250 HDC is not supported "
189  "\n\t\t\tby any MythTV Firewire channel changer."
190  "At the moment you must use an IR blaster.");
191  }
192 
193  cmd.push_back(kAVCControlCommand);
194  cmd.push_back(kAVCSubunitTypePanel | m_subunitid);
195  cmd.push_back(kAVCPanelPassThrough);
197 
198  cmd.push_back(4); // operand length
199  cmd.push_back((channel>>8) & 0x0f);
200  cmd.push_back(channel & 0xff);
201  cmd.push_back(0x00);
202  cmd.push_back(0x00);
203 
204  if (!SendAVCCommand(cmd, ret, -1))
205  return false;
206 
207  bool press_ok = (kAVCAcceptedStatus == ret[0]);
208 
210  if (!SendAVCCommand(cmd, ret, -1))
211  return false;
212 
213  bool release_ok = (kAVCAcceptedStatus == ret[0]);
214 
215  if (!press_ok && !release_ok)
216  {
217  LOG(VB_GENERAL, LOG_ERR, LOC + "Tuning failed");
218  return false;
219  }
220 
221  SetLastChannel(channel);
222  return true;
223  }
224 
225  // the PACE is obviously not a Motorola channel changer, but the
226  // same commands work for it as the Motorola.
227  bool is_mot = ((panel_model.toUpper().startsWith("DCT-")) ||
228  (panel_model.toUpper().startsWith("DCH-")) ||
229  (panel_model.toUpper().startsWith("DCX-")) ||
230  (panel_model.toUpper().startsWith("QIP-")) ||
231  (panel_model.toUpper().startsWith("MOTO")) ||
232  (panel_model.toUpper().startsWith("PACE-")));
233 
234  if (is_mot && !alt_method)
235  {
236  for (int d : digit)
237  {
238  cmd.clear();
239  cmd.push_back(kAVCControlCommand);
240  cmd.push_back(kAVCSubunitTypePanel | m_subunitid);
241  cmd.push_back(kAVCPanelPassThrough);
242  cmd.push_back((kAVCPanelKey0 + d) | kAVCPanelKeyPress);
243  cmd.push_back(0x00);
244  cmd.push_back(0x00);
245  cmd.push_back(0x00);
246  cmd.push_back(0x00);
247 
248  if (!SendAVCCommand(cmd, ret, -1))
249  return false;
250 
251  std::this_thread::sleep_for(std::chrono::milliseconds(500));
252  }
253 
254  SetLastChannel(channel);
255  return true;
256  }
257 
258  if (is_mot && alt_method)
259  {
260  cmd.push_back(kAVCControlCommand);
261  cmd.push_back(kAVCSubunitTypePanel | m_subunitid);
262  cmd.push_back(kAVCPanelPassThrough);
264 
265  cmd.push_back(4); // operand length
266  cmd.push_back((channel>>8) & 0x0f);
267  cmd.push_back(channel & 0xff);
268  cmd.push_back(0x00);
269  cmd.push_back(0xff);
270 
271  if (!SendAVCCommand(cmd, ret, -1))
272  return false;
273 
274  SetLastChannel(channel);
275  return true;
276  }
277 
278  if (panel_model.toUpper() == "SA3250HD")
279  {
280  cmd.push_back(kAVCControlCommand);
281  cmd.push_back(kAVCSubunitTypePanel | m_subunitid);
282  cmd.push_back(kAVCPanelPassThrough);
284 
285  cmd.push_back(4); // operand length
286  cmd.push_back(0x30 | digit[2]);
287  cmd.push_back(0x30 | digit[1]);
288  cmd.push_back(0x30 | digit[0]);
289  cmd.push_back(0xff);
290 
291  if (!SendAVCCommand(cmd, ret, -1))
292  return false;
293 
294  cmd[5] = 0x30 | digit[0];
295  cmd[6] = 0x30 | digit[1];
296  cmd[7] = 0x30 | digit[2];
297 
298  if (!SendAVCCommand(cmd, ret, -1))
299  return false;
300 
301  SetLastChannel(channel);
302  return true;
303  }
304 
305  return false;
306 }
307 
309  const unsigned char *data, uint dataSize)
310 {
311  if ((dataSize >= TSPacket::kSize) && (data[0] == SYNC_BYTE) &&
312  ((data[1] & 0x1f) == 0) && (data[2] == 0))
313  {
314  ProcessPATPacket(*((const TSPacket*)data));
315  }
316 
317  for (auto & listener : m_listeners)
318  listener->AddData(data, dataSize);
319 }
320 
322 {
323  m_buffer_cleared = (channel == m_last_channel);
324  m_last_channel = channel;
325 
326  LOG(VB_GENERAL, LOG_INFO, QString("SetLastChannel(%1): cleared: %2")
327  .arg(channel).arg(m_buffer_cleared ? "yes" : "no"));
328 }
329 
331 {
332  if (!tspacket.TransportError() && !tspacket.Scrambled() &&
333  tspacket.HasPayload() && tspacket.PayloadStart() && (tspacket.PID() == 0))
334  {
335  PSIPTable pes(tspacket);
336  uint crc = pes.CalcCRC();
337  m_buffer_cleared |= (crc != m_last_crc);
338  m_last_crc = crc;
339 #if 0
340  LOG(VB_RECORD, LOG_DEBUG, LOC +
341  QString("ProcessPATPacket: CRC 0x%1 cleared: %2")
342  .arg(crc,0,16).arg(m_buffer_cleared ? "yes" : "no"));
343 #endif
344  }
345  else
346  {
347  LOG(VB_GENERAL, LOG_ERR, LOC + "Can't handle large PAT's");
348  }
349 }
350 
351 QString FirewireDevice::GetModelName(uint vendor_id, uint model_id)
352 {
353  QMutexLocker locker(&s_static_lock);
354  if (s_id_to_model.empty())
356 
357  QString ret = s_id_to_model[(((uint64_t) vendor_id) << 32) | model_id];
358 
359  if (ret.isEmpty())
360  return "MOTO GENERIC";
361  return ret;
362 }
363 
364 vector<AVCInfo> FirewireDevice::GetSTBList(void)
365 {
366  vector<AVCInfo> list;
367 
368 #ifdef USING_LINUX_FIREWIRE
370 #elif USING_OSX_FIREWIRE
372 #endif
373 
374 //#define DEBUG_AVC_INFO
375 #ifdef DEBUG_AVC_INFO
376  AVCInfo info;
377  info.guid = 0x0016928a7b600001ULL;
378  info.specid = 0x0;
379  info.vendorid = 0x000014f8;
380  info.modelid = 0x00001072;
381  info.firmware_revision = 0x0;
382  info.product_name = "Explorer 4200 HD";
383  list.push_back(info);
384 
385  info.guid = 0xff2145a850e39810ULL;
386  info.specid = 0x0;
387  info.vendorid = 0x000014f8;
388  info.modelid = 0x00000be0;
389  info.firmware_revision = 0x0;
390  info.product_name = "Explorer 3250 HD";
391  list.push_back(info);
392 #endif // DEBUG_AVC_INFO
393 
394  return list;
395 }
396 
397 static void fw_init(QMap<uint64_t,QString> &id_to_model)
398 {
399  const uint64_t sa_vendor_ids[] =
400  {
401  0x0a73, 0x0f21, 0x11e6, 0x14f8, 0x1692, 0x1868,
402  0x1947, 0x1ac3, 0x1bd7, 0x1cea, 0x1e6b, 0x21be,
403  0x223a, 0x22ce, 0x23be, 0x252e,
404  };
405 
406  for (uint64_t vendor_id : sa_vendor_ids)
407  {
408  id_to_model[vendor_id << 32 | 0x0be0] = "SA3250HD";
409  id_to_model[vendor_id << 32 | 0x1072] = "SA4200HD";
410  id_to_model[vendor_id << 32 | 0x10cc] = "SA4250HDC";
411  id_to_model[vendor_id << 32 | 0x22ce] = "SA8300HD";
412  }
413 
414  const uint64_t motorola_vendor_ids[] =
415  {
416  /* DCH-3200, DCX-3200 */
417  0x1c11, 0x1cfb, 0x1fc4, 0x23a3, 0x23ee, 0x25f1,
418  0xfa01, 0x25f1, 0x25f2, 0xcc7d37, 0x946269, 0x6455b1,
419  /* DCX-3432 */
420  0x24a0,
421  /* DCH-3416 */
422  0x1e46,
423  /* DCT-3416 */
424  0x1bdd,
425  /* DCT-3412 */
426  0x159a,
427  /* DCT-6200, DCT-3416 */
428  0x0ce5, 0x0e5c, 0x1225, 0x0f9f, 0x1180,
429  0x12c9, 0x11ae, 0x152f, 0x14e8, 0x16b5, 0x1371,
430  0x19a6, 0x1aad, 0x0b06, 0x195e, 0x10dc,
431  /* DCT-6212 */
432  0x0f9f, 0x152f,
433  /* DCT-6216, 2224 */
434  0x17ee, 0x1a66,
435  /* QIP 6200 */
436  0x211e,
437  /* QIP 7100 */
438  0x2374,
439  /* unknown, see http://standards.ieee.org/regauth/oui/oui.txt */
440  0x04db, 0x0406, 0x0ce5, 0x111a, 0x1225, 0x1404,
441  0x1626, 0x18c0, 0x1ade, 0x1cfb, 0x2040, 0x2180,
442  0x2210, 0x230b, 0x2375, 0x2395, 0x23a2, 0x23ed,
443  0x23ee, 0x23a0, 0x23a1,
444 
445  };
446 
447  for (uint64_t vendor_id : motorola_vendor_ids)
448  {
449  id_to_model[vendor_id << 32 | 0xf740] = "DCX-3200";
450  id_to_model[vendor_id << 32 | 0xf804] = "DCX-3200";
451  id_to_model[vendor_id << 32 | 0xfa03] = "DCX-3200";
452  id_to_model[vendor_id << 32 | 0xfa05] = "DCX-3200";
453  id_to_model[vendor_id << 32 | 0xfa07] = "DCX-3200";
454  id_to_model[vendor_id << 32 | 0x24a1] = "DCX-3200";
455  id_to_model[vendor_id << 32 | 0x2322] = "DCX-3200";
456  id_to_model[vendor_id << 32 | 0xea05] = "DCX-3432";
457  id_to_model[vendor_id << 32 | 0xd330] = "DCH-3200";
458  id_to_model[vendor_id << 32 | 0xb630] = "DCH-3416";
459  id_to_model[vendor_id << 32 | 0x34cb] = "DCT-3412";
460  id_to_model[vendor_id << 32 | 0x346b] = "DCT-3416";
461  id_to_model[vendor_id << 32 | 0xb630] = "DCT-3416";
462  id_to_model[vendor_id << 32 | 0x6200] = "DCT-6200";
463  id_to_model[vendor_id << 32 | 0x620a] = "DCT-6200";
464  id_to_model[vendor_id << 32 | 0x64ca] = "DCT-6212";
465  id_to_model[vendor_id << 32 | 0x64cb] = "DCT-6212";
466  id_to_model[vendor_id << 32 | 0x646b] = "DCT-6216";
467  id_to_model[vendor_id << 32 | 0x8100] = "QIP-7100";
468  id_to_model[vendor_id << 32 | 0x7100] = "QIP-6200";
469  id_to_model[vendor_id << 32 | 0x0001] = "QIP-7100";
470  }
471 
472  const uint64_t pace_vendor_ids[] =
473  {
474  /* PACE 550-HD & 779 */
475  0x1cc3, 0x5094,
476  };
477 
478  for (uint64_t vendor_id : pace_vendor_ids)
479  {
480  id_to_model[vendor_id << 32 | 0x10551] = "PACE-550";
481  id_to_model[vendor_id << 32 | 0x10755] = "PACE-779";
482  }
483 }
484 
485 bool FirewireDevice::IsSTBSupported(const QString &panel_model)
486 {
487  QString model = panel_model.toUpper();
488  return ((model == "DCH-3200") ||
489  (model == "DCH-3416") ||
490  (model == "DCT-3412") ||
491  (model == "DCT-3416") ||
492  (model == "DCT-6200") ||
493  (model == "DCT-6212") ||
494  (model == "DCT-6216") ||
495  (model == "DCX-3200") ||
496  (model == "SA3250HD") ||
497  (model == "SA4200HD") ||
498  (model == "SA4250HDC") ||
499  (model == "SA8300HD") ||
500  (model == "PACE-550") ||
501  (model == "PACE-779") ||
502  (model == "QIP-6200") ||
503  (model == "QIP-7100") ||
504  (model == "SA GENERIC") ||
505  (model == "MOTO GENERIC"));
506 }
Used to access the data of a Transport Stream packet.
Definition: tspacket.h:166
static QString GetModelName(uint vendor_id, uint model_id)
virtual void RemoveListener(TSDataListener *listener)
static pid_list_t::iterator find(const PIDInfoMap &map, pid_list_t &list, pid_list_t::iterator begin, pid_list_t::iterator end, bool find_open)
#define SYNC_BYTE
Definition: tspacket.h:15
FirewireDevice(uint64_t guid, uint subunitid, uint speed)
static QMutex s_static_lock
void ProcessPATPacket(const TSPacket &tspacket)
static vector< AVCInfo > GetSTBList(void)
uint CalcCRC(void) const
Definition: pespacket.cpp:145
virtual void AddListener(TSDataListener *listener)
bool PayloadStart(void) const
Definition: tspacket.h:67
static vector< AVCInfo > GetSTBList(void)
A PSIP table is a variant of a PES packet containing an MPEG, ATSC or DVB table.
Definition: mpegtables.h:386
vector< TSDataListener * > m_listeners
bool HasPayload(void) const
Definition: tspacket.h:94
static const uint16_t * d
static QMap< uint64_t, QString > s_id_to_model
Vendor ID + Model ID to FirewireDevice STB model string.
void SetLastChannel(uint channel)
unsigned int uint
Definition: compat.h:140
virtual bool SendAVCCommand(const vector< uint8_t > &cmd, vector< uint8_t > &result, int retry_cnt)=0
bool TransportError(void) const
Definition: tspacket.h:64
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
static const unsigned int kSize
Definition: tspacket.h:220
static vector< AVCInfo > GetSTBList(void)
unsigned int PID(void) const
Definition: tspacket.h:71
static bool IsSTBSupported(const QString &model)
virtual void BroadcastToListeners(const unsigned char *data, uint dataSize)
virtual bool SetChannel(const QString &panel_model, uint alt_method, uint channel)
bool Scrambled(void) const
Definition: tspacket.h:90
virtual PowerState GetPowerState(void)
#define LOC
FirewireDevice Copyright (c) 2005 by Jim Westfall Distributed as part of MythTV under GPL v2 and late...
static void fw_init(QMap< uint64_t, QString > &id_to_model)
virtual bool SetPowerState(bool on)