Ticket #7671: sa_control.cc

File sa_control.cc, 16.1 KB (added by anonymous, 16 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