Changing channels on a FIOS QIP7100 via Firewire from MythTV (6200ch)

Posted by james on Feb. 27, 2009

I've had a few requests for this script, so I've decided to post the file 6200ch.c file I modified to get it to work with MythTV (full writeup here).

You can download my modified 6200ch.c file, or copy and paste from below:
/*
 * 6200ch - an external channel changer for Motorola DCT-6200 Tuner
 *
 * Copyright 2004,2005 by Stacey D. Son
 *
 * 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
#include
#include
#include
#include
#include
#include
#include //for LLONG_MAX
#include
#include // 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_MODEL_ID1 0x0000d330

#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_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 PACE_VENDOR_ID1 0x00005094 /* 550 & 779 */
#define PACE550_MODEL_ID1 0x00010551
#define PACE779_MODEL_ID1 0x00010755

#define QIP7100_VENDOR_ID1 0x00002180
#define QIP7100_MODEL_ID1 0x00008100

#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 usage()
{
   fprintf(stderr, "Usage: 6200ch [-v] [-s] [-n NODE] [-g GUID] [-p PORT] "
           "\n");
   fprintf(stderr, "-v print additional verbose output\n");
   fprintf(stderr, "-s use single packet method\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");
   exit(1);
}

int main (int argc, char *argv[])
{
   rom1394_directory dir;
   int device = -1;
   int i;
   int verbose = 0;
   int single_packet = 0;
   int bGUID=0;
   octlet_t cli_GUID=0LL;
   octlet_t node_GUID=0LL;
   quadlet_t cmd[2];
   int chn = 550;
   int use_four_channel = 0;

   /* some people experience crashes when starting on node 1 */
   int starting_node = STARTING_NODE;
   int starting_port = STARTING_PORT;
   int c;
   int index;

   if (argc < 2)
      usage();

   opterr = 0;
   while ((c = getopt(argc, argv, "vsg:n:p:4")) != -1)
   {
       switch (c) {
       case 'v':
           verbose = 1;
           break;
       case 's':
           single_packet = 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_channel = 1;
           break;
       default:
           fprintf(stderr, "incorrect command line arguments\n");
           usage();
       }
   }

   /* print out usage message if not enough arguments */
   if (optind != argc-1) {
       usage();
   }
   
   if (single_packet && use_four_channel) {
       fprintf(stderr, "Can't use both -s and -4 together.\n");
       usage();
   }

   /* the last argument is the channel number */
   chn = atoi(argv[optind]);

#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 Compatable!\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 == 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 == DCT6412_VENDOR_ID1) ||
            (dir.vendor_id == DCT6412_VENDOR_ID2) ||
            (dir.vendor_id == DCT6416_VENDOR_ID1) ||
            (dir.vendor_id == DCT6416_VENDOR_ID2) ||
            (dir.vendor_id == PACE_VENDOR_ID1) ||
            (dir.vendor_id == QIP7100_VENDOR_ID1)) &&
           ((dir.model_id == DCH3200_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 == PACE550_MODEL_ID1) ||
            (dir.model_id == PACE779_MODEL_ID1) ||
            (dir.model_id == QIP7100_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 (single_packet)
       set_chan_fast(handle, device, verbose, chn);
   else
       if (use_four_channel)
           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 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 = 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]);

   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);
}

/* vim: set expandtab tabstop=4 shiftwidth=4: */