6200ch
Author | Stacey Son |
Description | Channel-change script for Motorola DCT-6200 cable box via a 1394 (aka Firewire) connection. |
Supports |
6200ch is a small program that Stacey Son wrote to change channels on a Motorola DCT-6200 cable box via a 1394 (aka. Firewire) connection.
README
I was working on some code to record the video/audio via firewire, however, I have noticed that my cable provider has started to encrypt (5C/DTCP) many of the channels that I care to record. I am currently using a s-video cable, a PVR-250 and this code with mythtv until I figure out something better to do. This works with for DCT-6200 but may be easy to modify for other firewire tuners as well.
To use this with mythtv do the following:
(1) Make sure you have 1394/Firewire drivers installed in your kernel.
(2) Install libraw1394, librom1394 and libavc1394. (On gentoo: "emerge libavc1394").
(3) Compile and install "6200ch":
# cc -std=gnu99 -o 6200ch 6200ch.c -lavc1394 -lrom1394 -lraw1394 # cp 6200ch /usr/local/bin
(4) Connect a 1394/Firewire cable from your computer to your DCT-6200 and test:
# 6200ch <your_favorite_channel_number>
(5) Configure Mythtv to use the channel changer by running the "setup" program and adding to "/usr/local/bin/6200ch" to the "External channel change command" field under "Connect source to Input".
Enjoy!
The Code
/* * 6200ch - an external channel changer for Motorola DCT-6200 Tuner * * Copyright 2004,2005 by Stacey D. Son ( mythdev a son d org ) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include <libavc1394/rom1394.h> #include <libavc1394/avc1394.h> #include <libraw1394/raw1394.h> #include <sys/types.h> #include <stdio.h> #include <errno.h> #include <stdlib.h> #include <limits.h> //for LLONG_MAX #include <getopt.h> #include <unistd.h> // for usleep // Vendor and Model IDs. // NOTE: Some Models have more than one possible Vendor ID // WARNING: Please update firewiredevice.cpp when adding to this list. #define DCH3200_VENDOR_ID1 0x00001c11 #define DCH3200_VENDOR_ID2 0x00001cfb #define DCH3200_VENDOR_ID3 0x00001fc4 #define DCH3200_VENDOR_ID4 0x000023a3 #define DCH3200_MODEL_ID1 0x0000d330 #define DCX3200_MODEL_ID1 0x0000f740 #define DCX3200_MODEL_ID2 0x0000fa07 #define DCX3432_VENDOR_ID1 0x000024a0 #define DCX3432_MODEL_ID1 0x0000ea05 #define DCH3416_VENDOR_ID1 0x00001e46 #define DCH3416_MODEL_ID1 0x0000b630 #define DCT3412_VENDOR_ID1 0x0000159a #define DCT3412_MODEL_ID1 0x000034cb #define DCT3416_VENDOR_ID1 0x000016b5 #define DCT3416_VENDOR_ID2 0x00001bdd #define DCT3416_MODEL_ID1 0x0000346b #define DCT3416_MODEL_ID2 0x0000b630 #define DCT5100_VENDOR_ID1 0x000017ee #define DCT5100_MODEL_ID1 0x0000620a #define DCT6200_VENDOR_ID1 0x00000ce5 #define DCT6200_VENDOR_ID2 0x00000e5c #define DCT6200_VENDOR_ID3 0x00001225 #define DCT6200_VENDOR_ID4 0x00000f9f #define DCT6200_VENDOR_ID5 0x00001180 #define DCT6200_VENDOR_ID6 0x000012c9 #define DCT6200_VENDOR_ID7 0x000011ae #define DCT6200_VENDOR_ID8 0x0000152f #define DCT6200_VENDOR_ID9 0x000014e8 #define DCT6200_VENDOR_ID10 0x000016b5 #define DCT6200_VENDOR_ID11 0x00001371 #define DCT6200_VENDOR_ID12 0x000019a6 #define DCT6200_VENDOR_ID13 0x00001aad #define DCT6200_VENDOR_ID14 0x00000b06 #define DCT6200_VENDOR_ID15 0x0000195e #define DCT6200_VENDOR_ID16 0x000010dc #define DCT6200_SPEC_ID 0x00005068 #define DCT6200_SW_VERSION 0x00010101 #define DCT6200_MODEL_ID1 0x0000620a #define DCT6200_MODEL_ID2 0x00006200 #define DCT6412_VENDOR_ID1 0x00000f9f #define DCT6412_VENDOR_ID2 0x0000152f #define DCT6412_MODEL_ID1 0x000064ca #define DCT6412_MODEL_ID2 0x000064cb #define DCT6416_VENDOR_ID1 0x000017ee #define DCT6416_VENDOR_ID2 0x00001a66 #define DCT6416_MODEL_ID1 0x0000646b #define QIP7100_VENDOR_ID1 0x00002374 #define QIP7100_MODEL_ID1 0x00008100 #define QIP7100_MODEL_ID2 0x00000001 #define QIP6200_VENDOR_ID1 0x0000211e #define QIP6200_MODEL_ID1 0x00007100 #define MOT_UNKNOWN_VENDOR_ID1 0x04db #define MOT_UNKNOWN_VENDOR_ID2 0x0406 #define MOT_UNKNOWN_VENDOR_ID3 0x0ce5 #define MOT_UNKNOWN_VENDOR_ID4 0x111a #define MOT_UNKNOWN_VENDOR_ID5 0x1225 #define MOT_UNKNOWN_VENDOR_ID6 0x1404 #define MOT_UNKNOWN_VENDOR_ID7 0x1626 #define MOT_UNKNOWN_VENDOR_ID8 0x18c0 #define MOT_UNKNOWN_VENDOR_ID9 0x1ade #define MOT_UNKNOWN_VENDOR_ID10 0x1cfb #define MOT_UNKNOWN_VENDOR_ID11 0x2040 #define MOT_UNKNOWN_VENDOR_ID12 0x2180 #define MOT_UNKNOWN_VENDOR_ID13 0x2210 #define MOT_UNKNOWN_VENDOR_ID14 0x230b #define MOT_UNKNOWN_VENDOR_ID15 0x2375 #define MOT_UNKNOWN_VENDOR_ID16 0x2395 #define MOT_UNKNOWN_VENDOR_ID17 0x23a2 #define MOT_UNKNOWN_VENDOR_ID18 0x23ed #define MOT_UNKNOWN_VENDOR_ID19 0x23ee #define MOT_UNKNOWN_VENDOR_ID20 0x23a0 #define MOT_UNKNOWN_VENDOR_ID21 0x23a1 #define PACE_VENDOR_ID1 0x00005094 /* 550 & 779 */ #define PACE_VENDOR_ID2 0x00005094 /* unknown */ #define PACE550_MODEL_ID1 0x00010551 #define PACE779_MODEL_ID1 0x00010755 #define AVC1394_6200_OPERAND_SET 0x20 /* 6200 subunit command operand */ #define CTL_CMD0 AVC1394_CTYPE_CONTROL | AVC1394_SUBUNIT_TYPE_PANEL | \ AVC1394_SUBUNIT_ID_0 | AVC1394_PANEL_COMMAND_PASS_THROUGH | \ AVC1394_6200_OPERAND_SET #define STARTING_NODE 1 /* skip 1394 nodes to avoid error msgs */ #define STARTING_PORT 0 #define RETRY_COUNT_SLOW 1 #define RETRY_COUNT_FAST 0 void set_chan_slow(raw1394handle_t handle, int device, int verbose, int chn); void set_chan_slow_four_digit(raw1394handle_t handle, int device, int verbose, int chn); void set_chan_fast(raw1394handle_t handle, int device, int verbose, int chn); void set_power_fast(raw1394handle_t handle, int device, int verbose); void usage() { fprintf(stderr, "Usage: 6200ch [-v] [-s] [-n NODE] [-g GUID] [-p PORT] " "[-4] <channel_num>\n"); fprintf(stderr, "-v print additional verbose output\n"); fprintf(stderr, "-s use single packet method. Cannot be used with -4.\n"); fprintf(stderr, "-w toggle power state\n"); fprintf(stderr, "-n NODE node to start device scanning on (default:%i)\n", STARTING_NODE); fprintf(stderr, "-p PORT port/adapter to use (default:%i)\n", STARTING_PORT); fprintf(stderr, "-g GUID GUID to use, -n switch, if present, will be ignored.\n"); fprintf(stderr, "-4 Enable 4 digit channel number support. Cannot be used with -s.\n"); exit(1); } int main (int argc, char *argv[]) { rom1394_directory dir; int device = -1; int i; int verbose = 0; int single_packet = 0; int toggle_power = 0; int bGUID=0; octlet_t cli_GUID=0LL; octlet_t node_GUID=0LL; int chn = 0; int use_four_digit = 0; /* some people experience crashes when starting on node 1 */ int starting_node = STARTING_NODE; int starting_port = STARTING_PORT; int c; if (argc < 2) usage(); opterr = 0; while ((c = getopt(argc, argv, "vswg:n:p:4")) != -1) { switch (c) { case 'v': verbose = 1; break; case 's': single_packet = 1; break; case 'w': toggle_power = 1; break; case 'n': starting_node = atoi(optarg); break; case 'g': bGUID=1; starting_node=0; cli_GUID = (octlet_t)strtoll(optarg, (char **)NULL, 16); break; case 'p': starting_port = atoi(optarg); break; case '4': use_four_digit = 1; break; default: fprintf(stderr, "incorrect command line arguments\n"); usage(); } } { /* We cannot use single packet with 4 digits */ if (single_packet && use_four_digit) usage(); } /* print out usage message if not enough arguments */ if (optind == argc-1) { /* the last argument is the channel number */ chn = atoi(argv[optind]); } else if (!toggle_power) { usage(); } #ifdef RAW1394_V_0_8 raw1394handle_t handle = raw1394_get_handle(); #else raw1394handle_t handle = raw1394_new_handle(); #endif if (!handle) { if (!errno) { fprintf(stderr, "Not Compatible!\n"); } else { perror("Couldn't get 1394 handle"); fprintf(stderr, "Is ieee1394, driver, and raw1394 loaded?\n"); } exit(1); } if (raw1394_set_port(handle, starting_port) < 0) { perror("couldn't set port"); raw1394_destroy_handle(handle); exit(1); } if (verbose) printf("starting with node: %d\n", starting_node); int nc = raw1394_get_nodecount(handle); if (bGUID!=0) { if (cli_GUID==0LL || cli_GUID==LLONG_MAX || cli_GUID==LLONG_MIN) { fprintf(stderr, "error parsing GUID command line parameter\n"); exit(1); } } for (i=starting_node; i < nc; ++i) { if (bGUID!=0) { node_GUID=rom1394_get_guid(handle, i); #ifdef DEBUG printf("node=%d, node_GUID=%LX, cli_GUID=%LX\n", i, node_GUID, cli_GUID); #endif if (cli_GUID!=node_GUID) { continue; } } if (rom1394_get_directory(handle, i, &dir) < 0) { fprintf(stderr,"error reading config rom directory for node %d\n", i); raw1394_destroy_handle(handle); exit(1); } if (verbose) printf("node %d: vendor_id = 0x%08x model_id = 0x%08x\n", i, dir.vendor_id, dir.model_id); // WARNING: Please update firewiredevice.cpp when adding to this list. if ( ((dir.vendor_id == DCH3200_VENDOR_ID1) || (dir.vendor_id == DCH3200_VENDOR_ID2) || (dir.vendor_id == DCH3200_VENDOR_ID3) || (dir.vendor_id == DCH3200_VENDOR_ID4) || (dir.vendor_id == DCH3416_VENDOR_ID1) || (dir.vendor_id == DCT3412_VENDOR_ID1) || (dir.vendor_id == DCT3416_VENDOR_ID1) || (dir.vendor_id == DCT3416_VENDOR_ID2) || (dir.vendor_id == DCT5100_VENDOR_ID1) || (dir.vendor_id == DCT6200_VENDOR_ID1) || (dir.vendor_id == DCT6200_VENDOR_ID2) || (dir.vendor_id == DCT6200_VENDOR_ID3) || (dir.vendor_id == DCT6200_VENDOR_ID4) || (dir.vendor_id == DCT6200_VENDOR_ID5) || (dir.vendor_id == DCT6200_VENDOR_ID6) || (dir.vendor_id == DCT6200_VENDOR_ID7) || (dir.vendor_id == DCT6200_VENDOR_ID8) || (dir.vendor_id == DCT6200_VENDOR_ID9) || (dir.vendor_id == DCT6200_VENDOR_ID10) || (dir.vendor_id == DCT6200_VENDOR_ID11) || (dir.vendor_id == DCT6200_VENDOR_ID12) || (dir.vendor_id == DCT6200_VENDOR_ID13) || (dir.vendor_id == DCT6200_VENDOR_ID14) || (dir.vendor_id == DCT6200_VENDOR_ID15) || (dir.vendor_id == DCT6200_VENDOR_ID16) || (dir.vendor_id == DCT6412_VENDOR_ID1) || (dir.vendor_id == DCT6412_VENDOR_ID2) || (dir.vendor_id == DCT6416_VENDOR_ID1) || (dir.vendor_id == DCT6416_VENDOR_ID2) || (dir.vendor_id == DCX3432_VENDOR_ID1) || (dir.vendor_id == QIP7100_VENDOR_ID1) || (dir.vendor_id == QIP6200_VENDOR_ID1) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID1) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID2) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID3) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID4) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID5) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID6) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID7) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID8) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID9) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID10) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID11) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID12) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID13) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID14) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID15) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID16) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID17) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID18) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID19) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID20) || (dir.vendor_id == MOT_UNKNOWN_VENDOR_ID21) || (dir.vendor_id == PACE_VENDOR_ID1) || (dir.vendor_id == PACE_VENDOR_ID2)) && ((dir.model_id == DCH3200_MODEL_ID1) || (dir.model_id == DCX3200_MODEL_ID1) || (dir.model_id == DCX3200_MODEL_ID2) || (dir.model_id == DCX3432_MODEL_ID1) || (dir.model_id == DCH3416_MODEL_ID1) || (dir.model_id == DCT3412_MODEL_ID1) || (dir.model_id == DCT3416_MODEL_ID1) || (dir.model_id == DCT3416_MODEL_ID2) || (dir.model_id == DCT5100_MODEL_ID1) || (dir.model_id == DCT6200_MODEL_ID1) || (dir.model_id == DCT6200_MODEL_ID2) || (dir.model_id == DCT6412_MODEL_ID1) || (dir.model_id == DCT6412_MODEL_ID2) || (dir.model_id == DCT6416_MODEL_ID1) || (dir.model_id == QIP7100_MODEL_ID1) || (dir.model_id == QIP7100_MODEL_ID2) || (dir.model_id == QIP6200_MODEL_ID1) || (dir.model_id == PACE550_MODEL_ID1) || (dir.model_id == PACE779_MODEL_ID1)) ) { if (dir.unit_spec_id != DCT6200_SPEC_ID) fprintf(stderr, "Warning: Unit Spec ID different.\n"); if (dir.unit_sw_version != DCT6200_SW_VERSION) fprintf(stderr, "Warning: Unit Software Version different.\n"); device = i; break; } } if (device == -1) { fprintf(stderr, "Could not find Motorola DCT-6200 on the 1394 bus.\n"); raw1394_destroy_handle(handle); exit(1); } if (toggle_power) set_power_fast(handle, device, verbose); if (chn) { if (single_packet) set_chan_fast(handle, device, verbose, chn); else if (use_four_digit) set_chan_slow_four_digit(handle, device, verbose, chn); else set_chan_slow(handle, device, verbose, chn); } raw1394_destroy_handle(handle); exit(0); } void set_chan_slow(raw1394handle_t handle, int device, int verbose, int chn) { int i; int dig[3]; quadlet_t cmd[2]; dig[2] = (chn % 10); dig[1] = (chn % 100) / 10; dig[0] = (chn % 1000) / 100; if (verbose) printf("AV/C Command: %d%d%d = Op1=0x%08X Op2=0x%08X Op3=0x%08X\n", dig[0], dig[1], dig[2], CTL_CMD0 | dig[0], CTL_CMD0 | dig[1], CTL_CMD0 | dig[2]); for (i=0; i<3; i++) { cmd[0] = CTL_CMD0 | dig[i]; cmd[1] = 0x0; avc1394_transaction_block(handle, device, cmd, 2, RETRY_COUNT_SLOW); usleep(500000); // small delay for button to register } } // Same as set_chan_slow(), but sends 4 digits instead of 3 (for quicker channel changes on QIP6200-2 / QIP7100-1) void set_chan_slow_four_digit(raw1394handle_t handle, int device, int verbose, int chn) { int i; int dig[4]; quadlet_t cmd[2]; if (verbose) printf("chn: %d\n", chn); dig[3] = (chn % 10); dig[2] = (chn % 100) / 10; dig[1] = (chn % 1000) / 100; dig[0] = (chn % 10000) / 1000; if (verbose) printf("AV/C Command: %d%d%d%d = Op1=0x%08X Op2=0x%08X Op3=0x%08X Op4=0x%08X\n", dig[0], dig[1], dig[2], dig[3], CTL_CMD0 | dig[0], CTL_CMD0 | dig[1], CTL_CMD0 | dig[2], CTL_CMD0 | dig[3]); for (i=0; i<4; i++) { if (verbose) printf("Sending digit %d\n", dig[i]); cmd[0] = CTL_CMD0 | dig[i]; cmd[1] = 0x0; avc1394_transaction_block(handle, device, cmd, 2, RETRY_COUNT_SLOW); usleep(500000); // small delay for button to register } } void set_chan_fast(raw1394handle_t handle, int device, int verbose, int chn) { quadlet_t cmd[3]; cmd[0] = CTL_CMD0 | 0x67; cmd[1] = (0x04 << 24) | (chn << 8) | 0x000000FF; cmd[2] = 0xFF << 24; if (verbose) printf("AV/C command for channel %d = 0x%08X %08X %08X\n", chn, cmd[0], cmd[1], cmd[2]); avc1394_transaction_block(handle, device, cmd, 3, RETRY_COUNT_FAST); } void set_power_fast(raw1394handle_t handle, int device, int verbose) { quadlet_t cmd[2]; quadlet_t *response; cmd[0] = ((CTL_CMD0) & 0xffffff00) | AVC1394_PANEL_OPERATION_POWER; cmd[1] = 0; response = avc1394_transaction_block( handle, device, cmd, 2, RETRY_COUNT_FAST); if (verbose) { printf("AV/C command for power = 0x%08X\n", cmd[0]); if (NULL != response) printf(" (%x) 0x%x\n", (*response) >> 24, *response); else printf(" No response\n"); } } /* vim: set expandtab tabstop=4 shiftwidth=4: */
TARGET = 6200ch CFLAGS = -Wall -std=gnu99 PREFIX = /usr/local BINPATH = ${PREFIX}/bin all: $(TARGET) $(TARGET): $(TARGET).c $(CC) $(CFLAGS) -o $(TARGET) $(TARGET).c -lavc1394 -lrom1394 -lraw1394 install: $(TARGET) install -m 0755 $(TARGET) ${BINPATH} uninstall: rm -f ${BINPATH}/$(TARGET) clean: rm -f $(TARGET) distclean: clean