Ticket #2059: sa3250.patch

File sa3250.patch, 13.6 KB (added by chris@…, 18 years ago)

Enhanced sa3250ch command line channel changer, also supports SA4200, patches sa3250ch.c and sa3250ch-README

  • sa3250ch.c

     
    22 * sa3250ch - an external channel changer for SA3250HD Tuner
    33 * Based off 6200ch.c by Stacey D. Son
    44 *
     5 * Additional enhancements and alternate SA3250 and SA4200 channel change
     6 * method added for the MythTV project by Chris Ingrassia <chris@spicecoffee.org>
     7 *
    58 * Copyright 2004,2005 by Stacey D. Son <mythdev@son.org>
    69 * Copyright 2005 Matt Porter <mporter@kernel.crashing.org>
    710 *
     
    2023 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
    2124 */
    2225
    23 #include <libavc1394/rom1394.h>
    24 #include <libavc1394/avc1394.h>
    25 #include <libraw1394/raw1394.h>
     26#include <unistd.h>
    2627#include <sys/types.h>
    2728#include <stdio.h>
    2829#include <errno.h>
    2930#include <stdlib.h>
    3031
     32#include <libavc1394/rom1394.h>
     33#include <libavc1394/avc1394.h>
     34#include <libraw1394/raw1394.h>
     35
     36
     37int verbose = 0;
     38
     39#ifdef RAW1394_V_0_8
     40#   define  GET_1394HANDLE(h)   (h = raw1394_get_handle())
     41#else
     42#   define  GET_1394HANDLE(h)   (h = raw1394_new_handle())
     43#endif
     44
     45#define MAX_PORTS   4   /* Maximum number of 1394 ports to check for devices */
     46
    3147/* SA3250HD IDs */
    3248#define SA3250HD_VENDOR_ID      0x000011e6
    33 #define SA3250HD_VENDOR_ID2     0x000014f8
     49#define SA3250HD_VENDOR_ID2     0x00001692
    3450#define SA3250HD_MODEL_ID       0x00000be0
    3551
    3652#define AVC1394_SA3250_COMMAND_CHANNEL 0x000007c00   /* subunit command */
     
    4056#define CTL_CMD0 AVC1394_CTYPE_CONTROL | AVC1394_SUBUNIT_TYPE_PANEL | \
    4157        AVC1394_SUBUNIT_ID_0 | AVC1394_SA3250_COMMAND_CHANNEL
    4258#define CTL_CMD1 (0x04 << 24)
    43 #define CTL_CMD2 0xff000000
     59#define CTL_CMD2 0x00000000
    4460
    4561#define STARTING_NODE 0
    4662
    4763void usage()
    4864{
    49    fprintf(stderr, "Usage: sa3250ch [-v] <channel_num>\n");
     65   fprintf(stderr, "Usage: sa3250ch [-vsh] [-p <port>] [-n <node>] <channel>\n");
     66   fprintf(stderr, "Available Options:\n");
     67   fprintf(stderr, "\t-v:  Be verbose (useful for debugging)\n");
     68   fprintf(stderr, "\t-f:  Force sending command even if compatible device is not detected\n");
     69   fprintf(stderr, "\t-a:  Just attempt autodetection of device and exit\n");
     70   fprintf(stderr, "\t-s:  Send channel as single command (for SA4200, and some SA3250 models)\n");
     71   fprintf(stderr, "\t-p:  Use port <port> (Default: autodetect)\n");
     72   fprintf(stderr, "\t-n:  Use node <node> (Default: autodetect)\n");
     73   fprintf(stderr, "\t-h:  Print this usage message\n");
    5074   exit(1);
    5175}
    5276
    5377int main (int argc, char *argv[])
    5478{
    55    rom1394_directory dir;
    56    int device = -1;
    57    int i;
    58    int verbose = 0;
    59    quadlet_t cmd[3];
     79   int device = -1, i = 0, chn = 708, st = EXIT_SUCCESS, arg = 0;
     80   int single = 0, port = -1, node = -1, report = 0, force = 0;
    6081   int dig[3];
    61    int chn = 708;
    62 
     82   quadlet_t cmd[3], *response = NULL;
     83   raw1394handle_t handle;
     84 
    6385   if (argc < 2)
    6486      usage();
    6587
    66    if (argc == 3 && argv[1][0] == '-' && argv[1][1] == 'v') {
    67       verbose = 1;
    68       chn = atoi(argv[2]);
    69    } else {
    70       chn = atoi(argv[1]);
     88   while((arg = getopt(argc, argv, "vsafhp:n:")) != -1) {
     89      switch(arg) {
     90          case 'v':
     91            verbose = 1;
     92            printf("Verbose mode on\n");
     93          break;
     94          case 's':
     95            single = 1;
     96          break;
     97          case 'p':
     98            port = atoi(optarg);
     99          break;
     100          case 'n':
     101            node = atoi(optarg);
     102          break;
     103          case 'f':
     104            if(verbose) printf("Force mode on\n");
     105            force = 1;
     106          break;
     107          case 'a':
     108            report = 1;
     109          break;
     110          case 'h':
     111            usage();
     112            goto cleanup;
     113          break;
     114      }
    71115   }
    72116
    73 #ifdef RAW1394_V_0_8
    74    raw1394handle_t handle = raw1394_get_handle();
    75 #else
    76    raw1394handle_t handle = raw1394_new_handle();
    77 #endif
     117   argc -= optind;
     118   argv += optind;
     119 
     120   if((argc != 1) && !report) {
     121      if(verbose) printf("No channel number specified?\n");
     122      usage();
     123      st = EXIT_FAILURE;
     124      goto cleanup;
     125   }
     126 
     127   if(!report) chn = atoi(*argv);
     128 
     129   GET_1394HANDLE(handle);
    78130
    79131   if (!handle) {
    80132      if (!errno) {
     
    83135         perror("Couldn't get 1394 handle");
    84136         fprintf(stderr, "Is ieee1394, driver, and raw1394 loaded?\n");
    85137      }
    86       exit(1);
     138      st = EXIT_FAILURE;
     139      goto cleanup;
    87140   }
    88141
    89    if (raw1394_set_port(handle, 0) < 0) {
    90       perror("couldn't set port");
    91       raw1394_destroy_handle(handle);
    92       exit(1);
     142   if(!locate_device(handle, &port, &node)) {
     143       if(force) {
     144           if(verbose) printf("Compatible AV/C device not found, continuing anyway\n");
     145       } else {
     146           fprintf(stderr, "Unable to locate AV/C 1394 Panel Device\n");
     147           st = EXIT_FAILURE;
     148           goto cleanup;
     149       }
    93150   }
    94151
    95    int nc = raw1394_get_nodecount(handle);
    96    for (i=STARTING_NODE; i < nc; ++i) {
    97       if (rom1394_get_directory(handle, i, &dir) < 0) {
    98          fprintf(stderr,"error reading config rom directory for node %d\n", i);
    99          raw1394_destroy_handle(handle);
    100          exit(1);
    101       }
    102 
    103       if (verbose)
    104          printf("node %d: vendor_id = 0x%08x model_id = 0x%08x\n",
    105                  i, dir.vendor_id, dir.model_id);
    106                
    107       if ((dir.vendor_id == SA3250HD_VENDOR_ID ||
    108            dir.vendor_id == SA3250HD_VENDOR_ID2)  &&
    109           (dir.model_id == SA3250HD_MODEL_ID)) {
    110             device = i;
    111             break;
    112       }
     152   if(report) {
     153       printf("Detected compatible device at port %d and node %d\n",
     154              port, node);
     155       st = EXIT_SUCCESS;
     156       goto cleanup;
    113157   }
    114    
    115    if (device == -1) {
    116         fprintf(stderr, "Could not find SA3250HD on the 1394 bus.\n");
    117         raw1394_destroy_handle(handle);
    118         exit(1);
    119    }
    120158
    121    dig[2] = 0x30 | (chn % 10);
    122    dig[1] = 0x30 | ((chn % 100)  / 10);
    123    dig[0] = 0x30 | ((chn % 1000) / 100);
     159   raw1394_set_port(handle, port);
     160   
     161   if(single) {
     162       if(verbose)
     163        printf("Tuning to channel %d using single command\n", chn);
    124164
    125    cmd[0] = CTL_CMD0 | AVC1394_SA3250_OPERAND_KEY_PRESS;
    126    cmd[1] = CTL_CMD1 | (dig[2] << 16) | (dig[1] << 8) | dig[0];
    127    cmd[2] = CTL_CMD2;
     165       cmd[0] = CTL_CMD0 | AVC1394_SA3250_OPERAND_KEY_PRESS;
     166       cmd[1] = CTL_CMD1 | (chn << 8);
     167       cmd[2] = 0x0;
    128168
    129    if (verbose)
    130       printf("AV/C Command: %d%d%d = cmd0=0x%08x cmd2=0x%08x cmd3=0x%08x\n",
    131             dig[0] & 0xf, dig[1] & 0xf, dig[2] & 0xf, cmd[0], cmd[1], cmd[2]);
     169       if(verbose)
     170            printf("Sending command channel change: 0x%08X 0x%08X\n",
     171                   cmd[0], cmd[1]);
    132172
    133    avc1394_transaction_block(handle, 0, cmd, 3, 1);
    134    cmd[0] = CTL_CMD0 | AVC1394_SA3250_OPERAND_KEY_RELEASE;
    135    cmd[1] = CTL_CMD1 | (dig[0] << 16) | (dig[1] << 8) | dig[2];
    136    cmd[2] = CTL_CMD2;
     173       response = avc1394_transaction_block(handle, 0, cmd, 3, 1);
     174       if(AVC1394_MASK_RESPONSE(response[0]) == AVC1394_RESPONSE_ACCEPTED) {
     175           if(verbose)
     176            printf("Channel change command success: Response: 0x%08X 0x%08X\n",
     177                   response[0], response[1]);
     178       }
     179       else {
     180           fprintf(stderr, "Channel change command failed: Response: 0x%08X 0x%08X\n",
     181                   response[0], response[1]);
     182       }
     183   }
     184   else {
     185       /* Original sa3250ch channel change method */
     186       dig[2] = 0x30 | (chn % 10);
     187       dig[1] = 0x30 | ((chn % 100)  / 10);
     188       dig[0] = 0x30 | ((chn % 1000) / 100);
    137189
    138    if (verbose)
    139       printf("AV/C Command: %d%d%d = cmd0=0x%08x cmd2=0x%08x cmd3=0x%08x\n",
    140             dig[0] & 0xf, dig[1] & 0xf, dig[2] & 0xf, cmd[0], cmd[1], cmd[2]);
     190       if (verbose) {
     191          printf("Digits : [0] : %d [1]: %d [2]: %d\n",
     192                 dig[0], dig[1], dig[2]);
     193       }
     194       cmd[0] = CTL_CMD0 | AVC1394_SA3250_OPERAND_KEY_PRESS;
     195       cmd[1] = CTL_CMD1 | (dig[0] << 16) | (dig[1] << 8) | dig[2];
     196       cmd[2] = CTL_CMD2;
     197       if(verbose)
     198           printf("AV/C Command: %d%d%d = cmd0=0x%08x cmd2=0x%08x cmd3=0x%08x\n",
     199                    dig[0] & 0xf, dig[1] & 0xf, dig[2] & 0xf, cmd[0], cmd[1], cmd[2]);
     200       avc1394_transaction_block(handle, 0, cmd, 3, 1);
     201       cmd[0] = CTL_CMD0 | AVC1394_SA3250_OPERAND_KEY_RELEASE;
     202       if(verbose)
     203           printf("AV/C Command: %d%d%d = cmd0=0x%08x cmd2=0x%08x cmd3=0x%08x\n",
     204                    dig[0] & 0xf, dig[1] & 0xf, dig[2] & 0xf, cmd[0], cmd[1], cmd[2]);
     205       avc1394_transaction_block(handle, 0, cmd, 3, 1);
     206   }
    141207
    142    avc1394_transaction_block(handle, 0, cmd, 3, 1);
     208   cleanup:
     209   if(handle) raw1394_destroy_handle(handle);
     210   
     211   if(verbose) {
     212       printf("%s attempt %s\n",
     213           report ? "Autodetection" : "Channel change",
     214           st ? "failed" : "suceeded");
     215   }
     216   
     217   exit(st);
     218}
    143219
    144    raw1394_destroy_handle(handle);
     220/**
     221 * locate_device - Attempt to locate a compatible AV/C panel device on the 1394
     222 * bus
     223 * @handle: libraw1394 handle
     224 * @port: pointer to detected port number
     225 * @node: pointer to detected node number
     226 *
     227 * Setting the value pointed to by @port or @node to a value >= 0 will limit detection
     228 * to those ports, and this function will simply verify that it thinks a compatible
     229 * device is at that location
     230 *
     231 * Returns: 1 on succesful detection, and sets the value at the locations
     232 * pointed to be @port and @node to the detected ieee1394 port and node, respectively.
     233 * Returns 0 on failure
     234 */
     235int locate_device(raw1394handle_t handle, int *port, int *node) {
     236    int st = 0, nports = 0, i = 0, j = 0;
     237    int found_port = -1, found_node = -1;
     238    struct raw1394_portinfo pinfo[MAX_PORTS];
     239   
     240    if((port && (*port >= 0))) {
     241        found_port = *port;
     242        if(verbose) printf("Using user specified port %d\n", found_port);
     243    }
     244   
     245    if((node && (*node >= 0))) {
     246        found_node = *node;
     247        if(verbose) printf("Using user specified node %d\n", found_node);
     248    }
     249   
     250    if((found_port >= 0) && (found_node >= 0)) {
     251        /* Just verify device type at this port/node combination */
     252        st = verify_device(found_port, found_node);
     253        goto cleanup;
     254    }
     255    /* XXX: I think you have to give raw1394_get_port_info() a nonzero
     256     * maxports argument.  I figure the default in MAX_PORTS should be more
     257     * than sufficient, and you can always specify the port manually, but
     258     * I could be wrong
     259     */
     260    if(found_port < 0) {
     261        if(!(nports = raw1394_get_port_info(handle, pinfo, MAX_PORTS))) {
     262            fprintf(stderr, "Could not find any IEEE 1394 ports!\n");
     263            st = 0;
     264            goto cleanup;
     265        }
     266    } else {
     267        nports = 1;
     268        raw1394_set_port(handle, found_port);
     269        pinfo[0].nodes = raw1394_get_nodecount(handle);
     270        strncpy(pinfo[0].name, "Pre-set port", sizeof(pinfo[0].name));
     271    }
     272   
     273    if(verbose) {
     274        printf("Found %d ports on 1394 Bus\n", nports);
     275    }
     276   
     277    for(i = 0; i < nports; ++i) {
     278        if(verbose) {
     279            printf("Checking port #%d - \'%s\'\n", i, pinfo[i].name);
     280        }
     281        for(j = 0; (j < pinfo[i].nodes) && (found_node < 0); ++j) {
     282            if(verbose) printf("\t Node %d ... ", j);
     283            if(verify_device(i, j)) {
     284                found_node = j;
     285                if(verbose) printf("AV/C Panel/Tuner Device Found\n");
     286                break;               
     287            }
     288            if(verbose) printf("\n");
     289        }
     290        if(found_node >= 0) {
     291            found_port = i;
     292            st = 1;
     293            break;
     294        }
     295    }
     296   
     297    cleanup:
     298    if(found_port >= 0) *port = found_port;
     299    if(found_node >= 0) *node = found_node;
     300    return st;
     301}
    145302
    146    exit(0);
     303int verify_device(int port, int node) {
     304    raw1394handle_t handle;
     305    int panel = 0, tuner = 0;
     306       
     307    GET_1394HANDLE(handle);
     308    raw1394_set_port(handle, port);
     309    panel = avc1394_check_subunit_type(handle, node, AVC1394_SUBUNIT_TYPE_PANEL);
     310    tuner = avc1394_check_subunit_type(handle, node, AVC1394_SUBUNIT_TYPE_TUNER);
     311    raw1394_destroy_handle(handle);
     312    return (panel && tuner);
    147313}
  • sa3250ch-README

     
    11sa3250ch is a small program that changes channels on a Scientific Atlanta
    2 SA3250HD cable box via a 1394 (aka. Firewire) connection. It is based off
     2SA3250HD, and SA4200 cable boxes via a 1394 (aka. Firewire) connection. It is based off
    33of 6200ch by Stacey Son (mythdev@son.org).
    44
    55To use this with mythtv do the following:
     
    2121program and adding to "/usr/local/bin/sa3250ch" to the "External channel
    2222change command" field under "Connect source to Input".
    2323
    24 I'm curious if this works for anybody on other SciAtl boxes when the proper
    25 model IDs are added.
     24Troubleshooting:
     25----------------
    2626
     27(1) Try running the program with the "-v" (verbose) argument first to see if anything
     28    obvious is wrong
     29(2) If for some reason auto-detection of the device fails or you want to circumvent it,
     30    manually specify the port and node with the "-p" and "-n" arguments, respectively.
     31(3) If the channel changes, but is incorrect or you have problems recording or watching LiveTV
     32    after a channel change, try using the alternate method of channel changing by sending
     33    a single command to the box with the "-s" option.
     34
     35If you're still having problems, please include the output of the failed command with the "-v"
     36argument specified when reporting problems.
     37
    2738Matt Porter <mporter@kernel.crashing.org>
     39Chris Ingrassia <chris@spicecoffee.org> (sa3250ch enhancements, "-s" support)
     40 No newline at end of file