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