Ticket #7671: sa_control.cc

File sa_control.cc, 16.1 KB (added by anonymous, 14 years ago)
Line 
1/*
2 * sa_control - control for Scientific Atlanta Cable Boxes, including
3 * the 4250 HDC which is unsupported in sa3250ch.c 
4 * Based on sa3250ch.c, firewiredevice.{h.cpp}, and linuxavcinfo.{h,cpp}
5 *
6 * Martin Purschke ( mpurschke (at) gmail.com ) 11/28/2009
7 *
8 * allows to change the channel, query the power status, and turn
9 * the power on and off
10 *
11 * Usage: sa_control [-v] [-q] [ channel | on | off ]
12 *      -h         : this help
13 *      -v         : verbose, multiple -v for more verbosity
14 *      -q         : query power status, also sets exit status
15 *       <channel> : set channel, implies power on
16 *       on        : switch on
17 *       off       : switch off
18 * Examples:
19 * sa_control -q
20 * sa_control -q -v
21 * sa_control -q -v -v
22 * sa_control 702
23 * sa_control off
24 *
25 * Build it with
26 * g++ -o sa_control sa_control.cc  -lrom1394 -lavc1394 -lraw1394
27 *
28 * This program is free software; you can redistribute it and/or modify
29 * it under the terms of the GNU General Public License as published by
30 * the Free Software Foundation; either version 2 of the License, or
31 * (at your option) any later version.
32 *
33 * This program is distributed in the hope that it will be useful,
34 * but WITHOUT ANY WARRANTY; without even the implied warranty of
35 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36 * GNU General Public License for more details.
37 *
38 * You should have received a copy of the GNU General Public License
39 * along with this program; if not, write to the Free Software Foundation,
40 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
41 */
42
43#include <libavc1394/rom1394.h>
44#include <libavc1394/avc1394.h>
45#include <libraw1394/raw1394.h>
46#include <sys/types.h>
47#include <getopt.h>
48#include <errno.h>
49#include <stdlib.h>
50#include <string.h>
51
52#include <iostream>
53#include <iomanip>
54#include <vector>
55
56using namespace std;
57
58#define COMMAND_CHANNEL 1
59#define COMMAND_POWER   2
60#define COMMAND_QUERY   3
61
62typedef enum
63  {
64    kAVCPowerOn,
65    kAVCPowerOff,
66    kAVCPowerUnknown,
67    kAVCPowerQueryFailed,
68  } PowerState;
69
70// AVC commands
71typedef enum
72  {
73    kAVCControlCommand         = 0x00,
74    kAVCStatusInquiryCommand   = 0x01,
75    kAVCSpecificInquiryCommand = 0x02,
76    kAVCNotifyCommand          = 0x03,
77    kAVCGeneralInquiryCommand  = 0x04,
78
79    kAVCNotImplementedStatus   = 0x08,
80    kAVCAcceptedStatus         = 0x09,
81    kAVCRejectedStatus         = 0x0a,
82    kAVCInTransitionStatus     = 0x0b,
83    kAVCImplementedStatus      = 0x0c,
84    kAVCChangedStatus          = 0x0d,
85
86    kAVCInterimStatus          = 0x0f,
87    kAVCResponseImplemented    = 0x0c,
88  } IEEE1394Command;
89
90// AVC unit addresses
91typedef enum
92  {
93    kAVCSubunitId0                = 0x00,
94    kAVCSubunitId1                = 0x01,
95    kAVCSubunitId2                = 0x02,
96    kAVCSubunitId3                = 0x03,
97    kAVCSubunitId4                = 0x04,
98    kAVCSubunitIdExtended         = 0x05,
99    kAVCSubunitIdIgnore           = 0x07,
100
101    kAVCSubunitTypeVideoMonitor   = (0x00 << 3),
102    kAVCSubunitTypeAudio          = (0x01 << 3),
103    kAVCSubunitTypePrinter        = (0x02 << 3),
104    kAVCSubunitTypeDiscRecorder   = (0x03 << 3),
105    kAVCSubunitTypeTapeRecorder   = (0x04 << 3),
106    kAVCSubunitTypeTuner          = (0x05 << 3),
107    kAVCSubunitTypeCA             = (0x06 << 3),
108    kAVCSubunitTypeVideoCamera    = (0x07 << 3),
109    kAVCSubunitTypePanel          = (0x09 << 3),
110    kAVCSubunitTypeBulletinBoard  = (0x0a << 3),
111    kAVCSubunitTypeCameraStorage  = (0x0b << 3),
112    kAVCSubunitTypeMusic          = (0x0c << 3),
113    kAVCSubunitTypeVendorUnique   = (0x1c << 3),
114    kAVCSubunitTypeExtended       = (0x1e << 3),
115    kAVCSubunitTypeUnit           = (0x1f << 3),
116  } IEEE1394UnitAddress;
117
118// AVC opcode
119typedef enum
120  {
121    // Unit
122    kAVCUnitPlugInfoOpcode               = 0x02,
123    kAVCUnitDigitalOutputOpcode          = 0x10,
124    kAVCUnitDigitalInputOpcode           = 0x11,
125    kAVCUnitChannelUsageOpcode           = 0x12,
126    kAVCUnitOutputPlugSignalFormatOpcode = 0x18,
127    kAVCUnitInputPlugSignalFormatOpcode  = 0x19,
128    kAVCUnitConnectAVOpcode              = 0x20,
129    kAVCUnitDisconnectAVOpcode           = 0x21,
130    kAVCUnitConnectionsOpcode            = 0x22,
131    kAVCUnitConnectOpcode                = 0x24,
132    kAVCUnitDisconnectOpcode             = 0x25,
133    kAVCUnitUnitInfoOpcode               = 0x30,
134    kAVCUnitSubunitInfoOpcode            = 0x31,
135    kAVCUnitSignalSourceOpcode           = 0x1a,
136    kAVCUnitPowerOpcode                  = 0xb2,
137
138    // Common Unit + Subunit
139    kAVCCommonOpenDescriptorOpcode       = 0x08,
140    kAVCCommonReadDescriptorOpcode       = 0x09,
141    kAVCCommonWriteDescriptorOpcode      = 0x0A,
142    kAVCCommonSearchDescriptorOpcode     = 0x0B,
143    kAVCCommonObjectNumberSelectOpcode   = 0x0D,
144    kAVCCommonPowerOpcode                = 0xB2,
145    kAVCCommonReserveOpcode              = 0x01,
146    kAVCCommonPlugInfoOpcode             = 0x02,
147    kAVCCommonVendorDependentOpcode      = 0x00,
148
149    // Panel
150    kAVCPanelPassThrough                 = 0x7c,
151  } IEEE1394Opcode;
152
153// AVC param 0
154typedef enum
155  {
156    kAVCPowerStateOn           = 0x70,
157    kAVCPowerStateOff          = 0x60,
158    kAVCPowerStateQuery        = 0x7f,
159  } IEEE1394UnitPowerParam0;
160
161typedef enum
162  {
163    kAVCPanelKeySelect          = 0x00,
164    kAVCPanelKeyUp              = 0x01,
165    kAVCPanelKeyDown            = 0x02,
166    kAVCPanelKeyLeft            = 0x03,
167    kAVCPanelKeyRight           = 0x04,
168    kAVCPanelKeyRightUp         = 0x05,
169    kAVCPanelKeyRightDown       = 0x06,
170    kAVCPanelKeyLeftUp          = 0x07,
171    kAVCPanelKeyLeftDown        = 0x08,
172    kAVCPanelKeyRootMenu        = 0x09,
173    kAVCPanelKeySetupMenu       = 0x0A,
174    kAVCPanelKeyContentsMenu    = 0x0B,
175    kAVCPanelKeyFavoriteMenu    = 0x0C,
176    kAVCPanelKeyExit            = 0x0D,
177
178    kAVCPanelKey0               = 0x20,
179    kAVCPanelKey1               = 0x21,
180    kAVCPanelKey2               = 0x22,
181    kAVCPanelKey3               = 0x23,
182    kAVCPanelKey4               = 0x24,
183    kAVCPanelKey5               = 0x25,
184    kAVCPanelKey6               = 0x26,
185    kAVCPanelKey7               = 0x27,
186    kAVCPanelKey8               = 0x28,
187    kAVCPanelKey9               = 0x29,
188    kAVCPanelKeyDot             = 0x2A,
189    kAVCPanelKeyEnter           = 0x2B,
190    kAVCPanelKeyClear           = 0x2C,
191
192    kAVCPanelKeyChannelUp       = 0x30,
193    kAVCPanelKeyChannelDown     = 0x31,
194    kAVCPanelKeyPreviousChannel = 0x32,
195    kAVCPanelKeySoundSelect     = 0x33,
196    kAVCPanelKeyInputSelect     = 0x34,
197    kAVCPanelKeyDisplayInfo     = 0x35,
198    kAVCPanelKeyHelp            = 0x36,
199    kAVCPanelKeyPageUp          = 0x37,
200    kAVCPanelKeyPageDown        = 0x38,
201
202    kAVCPanelKeyPower           = 0x40,
203    kAVCPanelKeyVolumeUp        = 0x41,
204    kAVCPanelKeyVolumeDown      = 0x42,
205    kAVCPanelKeyMute            = 0x43,
206    kAVCPanelKeyPlay            = 0x44,
207    kAVCPanelKeyStop            = 0x45,
208    kAVCPanelKeyPause           = 0x46,
209    kAVCPanelKeyRecord          = 0x47,
210    kAVCPanelKeyRewind          = 0x48,
211    kAVCPanelKeyFastForward     = 0x49,
212    kAVCPanelKeyEject           = 0x4a,
213    kAVCPanelKeyForward         = 0x4b,
214    kAVCPanelKeyBackward        = 0x4c,
215
216    kAVCPanelKeyAngle           = 0x50,
217    kAVCPanelKeySubPicture      = 0x51,
218
219    kAVCPanelKeyTuneFunction    = 0x67,
220
221    kAVCPanelKeyPress           = 0x00,
222    kAVCPanelKeyRelease         = 0x80,
223
224  } IEEE1394PanelPassThroughParam0;
225
226
227
228
229
230/* SA3250HD IDs */
231/* WARNING: Please update firewiredevice.cpp when adding to this list. */
232
233#define SA_VENDOR_ID1           0x00000a73
234#define SA_VENDOR_ID2           0x00000f21
235#define SA_VENDOR_ID3           0x000011e6
236#define SA_VENDOR_ID4           0x000014f8
237#define SA_VENDOR_ID5           0x00001692
238#define SA_VENDOR_ID6           0x00001868
239#define SA_VENDOR_ID7           0x00001947
240#define SA_VENDOR_ID8           0x00001ac3
241#define SA_VENDOR_ID9           0x00001bd7
242#define SA_VENDOR_ID10          0x00001cea
243#define SA_VENDOR_ID11          0x00001e6b
244#define SA_VENDOR_ID12          0x000021be
245#define SA_VENDOR_ID13          0x0000223a
246#define SA_VENDOR_ID14          0x000022ce
247#define SA_VENDOR_ID15          0x000023be
248#define SA_VENDOR_ID16          0x0000252e
249
250#define SA3250HD_MODEL_ID1      0x00000be0
251#define SA4200HD_MODEL_ID1      0x00001072
252#define SA4250HDC_MODEL_ID1     0x000010cc
253#define SA8300HD_MODEL_ID1      0x000022ce
254
255
256#define STARTING_NODE 0
257
258int verbose = 0;
259
260void exitmsg()
261{
262  cout << "Usage: sa_control [-v] [-q] [ <channel> | on | off ]" << endl;
263  cout << "       sa_control -h for help" << endl;
264  exit(1);
265}
266
267void exithelp()
268{
269  cout << "Usage: sa_control [-v] [-q] [ <channel> | on | off ]" << endl;
270  cout << "       -h         : this help" << endl;
271  cout << "       -v         : verbose, multiple -v for more verbosity" << endl;
272  cout << "       -q         : query power status, also sets exit status" << endl;
273  cout << "        <channel> : set channel, implies power on" << endl;
274  cout << "        on        : switch on" << endl;
275  cout << "        off       : switch off" << endl;
276  cout << "Examples:" << endl;
277  cout << " sa_control -q" << endl;
278  cout << " sa_control 702" << endl;
279  cout << " sa_control off" << endl;
280  exit(0);
281}
282
283
284// adapted from linuxavcinfo.cpp
285
286int SendAVCCommand(const vector<uint8_t>  &_cmd,
287                   vector<uint8_t>        &result,
288                   int                     retry_cnt,
289                   raw1394handle_t &fw_handle
290                   )
291{
292  retry_cnt = (retry_cnt < 0) ? 2 : retry_cnt;
293 
294  result.clear();
295 
296  int node =1;
297 
298  vector<uint8_t> cmd = _cmd;
299  while (cmd.size() & 0x3)
300    {
301      cmd.push_back(0x00);
302    }
303 
304  if (cmd.size() > 4096)
305    return 1;
306 
307  uint32_t cmdbuf[1024];
308  if (verbose >2)
309    {
310      for (uint i = 0; i < cmd.size(); i++)
311        {
312          std::cout << "avc command " << setw(3)<< i << "  0x" << hex << (unsigned int) cmd[i] << dec << std::endl;
313        }
314    }
315 
316  for (uint i = 0; i < cmd.size(); i+=4)
317    {
318      cmdbuf[i>>2] = cmd[i]<<24 | cmd[i+1]<<16 | cmd[i+2]<<8 | cmd[i+3];
319    }
320  uint result_length = 0;
321 
322 
323  uint32_t *ret = avc1394_transaction_block(fw_handle, node, cmdbuf, cmd.size() >> 2, retry_cnt);
324  result_length = cmd.size() >> 2;
325 
326  if (!ret)
327    return 1;
328 
329  for (uint i = 0; i < result_length; i++)
330    {
331      result.push_back((ret[i]>>24) & 0xff);
332      result.push_back((ret[i]>>16) & 0xff);
333      result.push_back((ret[i]>>8)  & 0xff);
334      result.push_back((ret[i])     & 0xff);
335    }
336 
337  avc1394_transaction_block_close(fw_handle);
338 
339  return 0;
340}
341
342
343
344
345int SetPower (const int onoff, raw1394handle_t &handle)
346{
347  vector<uint8_t> cmd;
348  vector<uint8_t> ret;
349 
350  cmd.push_back(kAVCControlCommand);
351  cmd.push_back(kAVCSubunitTypeUnit | kAVCSubunitIdIgnore);
352  cmd.push_back(kAVCUnitPowerOpcode);
353  cmd.push_back((onoff) ? kAVCPowerStateOn : kAVCPowerStateOff);
354
355 
356  if (SendAVCCommand(cmd, ret, -1, handle))
357    {
358      std::cout <<  "Power command failed (no response)" << std::endl;
359      return -1;
360    }
361 
362  return 0;
363}
364
365int QueryPower(raw1394handle_t &handle)
366{
367   
368  vector<uint8_t> cmd;
369  vector<uint8_t> ret;
370 
371  cmd.push_back(kAVCStatusInquiryCommand);
372  cmd.push_back(kAVCSubunitTypeUnit | kAVCSubunitIdIgnore);
373  cmd.push_back(kAVCUnitPowerOpcode);
374  cmd.push_back(kAVCPowerStateQuery);
375 
376  if (SendAVCCommand(cmd, ret, -1, handle))
377    {
378      return -1;
379    }
380 
381  if (ret[0] != kAVCResponseImplemented)
382    {
383      return -2;
384    }
385 
386  // check 1st operand..
387  if (ret[3] == kAVCPowerStateOn)
388    {
389      return 1;
390    }
391 
392    if (ret[3] == kAVCPowerStateOff)
393      {
394        return 0;
395      }
396
397    return -3;
398
399}
400
401int ChangeChannel(const int channelnumber, raw1394handle_t &handle)
402{
403
404  // power on if off
405  if ( !QueryPower(handle) )
406    {
407      SetPower(1,handle);
408      sleep(1);
409    }
410
411  vector<uint8_t> cmd;
412  vector<uint8_t> ret;
413 
414  cmd.push_back(kAVCControlCommand);
415  cmd.push_back(kAVCSubunitTypePanel);
416  cmd.push_back(kAVCPanelPassThrough);
417  cmd.push_back(kAVCPanelKeyTuneFunction | kAVCPanelKeyPress); 
418  cmd.push_back(4); // operand length
419  cmd.push_back((channelnumber>>8) & 0x0f);
420  cmd.push_back(channelnumber & 0xff);
421  cmd.push_back(0x00);
422  cmd.push_back(0x00);
423 
424  if (SendAVCCommand(cmd, ret, -1, handle))
425    {
426      std::cout <<  "Channel change command failed (no response)" << std::endl;
427      return -1;
428    }
429 
430  return 0;
431}
432
433
434int main (int argc, char *argv[])
435{
436   rom1394_directory dir;
437   int device = -1;
438   int single = 0;
439   int i;
440   int channelnumber;
441   int onoff;
442
443   if (argc < 2)
444     exitmsg();
445
446   extern char *optarg;
447   extern int optind;
448
449   int main_command = COMMAND_CHANNEL;  // assume our job is to change a channel
450
451   char c;
452   while ((c = getopt(argc, argv, "vqh")) != EOF)
453     {
454       switch (c)
455         {
456           
457         case 'h':
458           exithelp();
459           break;
460           
461         case 'q':   // query
462           main_command =COMMAND_QUERY;  // job is to query the power state
463           break;
464
465         case 'v':   // verbose
466           verbose++;
467           break;
468         }
469     }
470   
471   if ( main_command != COMMAND_QUERY )
472     {
473       if ( optind >= argc) exitmsg();
474       if ( strcmp ( argv[optind], "on") ==0 )
475         {
476           onoff =1;
477           main_command =COMMAND_POWER;  // job is to power on or off
478         }
479       else if (strcmp ( argv[optind], "off") ==0 )
480         {
481           onoff =0;
482           main_command =COMMAND_POWER;  // job is to power on or off
483         }
484       else
485         {
486           if ( !sscanf(argv[optind], "%d", &channelnumber) ) exitmsg();
487           main_command =COMMAND_CHANNEL;  // job is to power on or off
488         }
489     }
490   
491   raw1394handle_t handle = raw1394_new_handle();
492   
493   if (!handle)
494     {
495       if (!errno)
496         {
497           fprintf(stderr, "Not Compatible!\n");
498         }
499       else
500         {
501           perror("Couldn't get 1394 handle");
502           fprintf(stderr, "Is ieee1394, driver, and raw1394 loaded?\n");
503         }
504       exit(1);
505     }
506   
507   
508   if (raw1394_set_port(handle, 0) < 0)
509     {
510       perror("couldn't set port");
511       raw1394_destroy_handle(handle);
512       exit(1);
513     }
514   
515   int nc = raw1394_get_nodecount(handle);
516   for (i=STARTING_NODE; i < nc; ++i)
517     {
518       if (rom1394_get_directory(handle, i, &dir) < 0)
519         {
520           fprintf(stderr,"error reading config rom directory for node %d\n", i);
521           raw1394_destroy_handle(handle);
522           exit(1);
523         }
524       
525       /* WARNING: Please update firewiredevice.cpp when adding to this list. */
526       if (((dir.vendor_id == SA_VENDOR_ID1)  ||
527            (dir.vendor_id == SA_VENDOR_ID2)  ||
528            (dir.vendor_id == SA_VENDOR_ID3)  ||
529            (dir.vendor_id == SA_VENDOR_ID4)  ||
530            (dir.vendor_id == SA_VENDOR_ID5)  ||
531            (dir.vendor_id == SA_VENDOR_ID6)  ||
532            (dir.vendor_id == SA_VENDOR_ID7)  ||
533            (dir.vendor_id == SA_VENDOR_ID8)  ||
534            (dir.vendor_id == SA_VENDOR_ID9)  ||
535            (dir.vendor_id == SA_VENDOR_ID10) ||
536            (dir.vendor_id == SA_VENDOR_ID11) ||
537            (dir.vendor_id == SA_VENDOR_ID12) ||
538            (dir.vendor_id == SA_VENDOR_ID13) ||
539            (dir.vendor_id == SA_VENDOR_ID14) ||
540            (dir.vendor_id == SA_VENDOR_ID15) ||
541            (dir.vendor_id == SA_VENDOR_ID16)) &&
542           ((dir.model_id == SA3250HD_MODEL_ID1)  ||
543            (dir.model_id == SA4200HD_MODEL_ID1)  ||
544            (dir.model_id == SA4250HDC_MODEL_ID1) ||
545            (dir.model_id == SA8300HD_MODEL_ID1)))
546         {
547           if (verbose > 1)
548             {
549               cout << "Vendor id = 0x" << hex << dir.vendor_id << "  Model id = 0x" << dir.model_id << dec << endl;
550             }
551           
552           device = i;
553           break;
554         }
555     }
556   
557   
558   if (device == -1)
559     {
560       cout << "Could not find a cable box on bus" << endl;
561       raw1394_destroy_handle(handle);
562       return 1;
563     }
564   
565   int returnvalue = 0;
566   
567   switch ( main_command)
568     {
569       
570     case COMMAND_CHANNEL: // change channel
571       if (verbose)
572         {
573           cout << "Setting channel to " << channelnumber << endl;
574         }
575       returnvalue = ChangeChannel(channelnumber, handle);
576       break;
577       
578     case COMMAND_POWER: // change channel
579       if (verbose)
580         {
581           cout << "Turning power  " << ( (onoff) ? "on" : "off" ) << endl;
582         }
583       returnvalue = SetPower(onoff, handle);
584       break;
585       
586     case COMMAND_QUERY: // change channel
587       int status = QueryPower(handle);
588       if ( status < 0)
589         {
590           cout << "Error executing query" << endl;
591           exit(1);
592         }
593       
594       if (verbose)
595         {
596           cout << "Power is " << ((status) ? "on" : "off")  << endl;
597         }
598       else
599         {
600           cout  << ((status) ? "on" : "off")  << endl;
601         }
602       returnvalue = (status)? 0 : 1;
603
604     }
605
606   raw1394_destroy_handle(handle);
607   
608   return returnvalue;
609}
610
611
612