Ticket #7671: sa_control.4.cc

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

new version with more fixes - md5sum b4e8a6eb45fd3a15d89eace2d182a930

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