Ticket #7671: sa_control.3.cc

File sa_control.3.cc, 16.9 KB (added by mpurschke@…, 16 years ago)

new version with the type fix, md5sum c6bf80d64b126d3cc717f11ebf4c5727

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