Firewire Tester

From MythTV Official Wiki
(Redirected from Firewire tester)
Jump to: navigation, search


Author Jim Westfall
Description Test FireWire connectivity and stabilize broadcast FireWire connections.
Supports


firewire_tester is used to help determine which type of connection to use with mythtv. It can also help to stabilize a broadcast connection, which is sometimes required after rebooting your backend host or set top box (STB).

Jim Westfall ( jwestfall a surrealistic d net )

Usage

firewire_tester <action> -n <node> [-P <port>] [-r <n>] [-v]
 Actions: (one is required)
    -b          - test broadcast connection
    -p          - test p2p connection
    -B          - attempt to fix/stabilize broadcast connection
    -R          - reset the firewire bus
 Options
    -n <node>   - firewire node, required
    -P <port>   - firewire port, default 0
    -r <n>      - run action <n> times, default 1
    -o          - output mpeg2 stream to stdout using p2p connection
    -v          - verbose

There are 2 types of connections that can be used by mythtv to talk to the STB to transfer mpeg2 data. These are peer 2 peer (P2P) and broadcast. You will need to determine which works best with your STB.

Best bet is to start with trying P2P. First thing to do is change the channel on your STB to a known non-encrypted channel, then run the tester multiple times testing a P2P connection. If they all come back successful then have mythtv use P2P. If you find that it fails, then move onto testing broadcast. Start by doing the -B action to attempt fix/stabilize broadcast connection. Once it has then test broadcast a few more times with -b action.

example STB on node 0 that works best with a broadcast connection.

- test P2P connection a few times

$ ./firewire_tester -p -n 0 -r 5
Action: Test P2P connection 5 times, node 0, channel 0
P2P: Testing...Failed
P2P: Testing...Success, 46 packets received
P2P: Testing...Success, 164 packets received
P2P: Testing...Failed
P2P: Testing...Failed

- 2 out of 5 failed, not good. - now use the -B option to try and fix/stabilize broadcast connection

$ ./firewire_tester -B -n 0
Action: Attempt to fix broadcast connection 1 times, node 0
Broadcast: Testing...Failed
P2P: Testing...Failed
P2P: Testing...Success, 58 packets received
Broadcast: Testing...Failed
P2P: Testing...Failed
P2P: Testing...Success, 48 packets received
Broadcast: Testing...Success, 75 packets
Broadcast: Testing...Success, 49 packets
Broadcast: Testing...Success, 42 packets
Broadcast: Testing...Success, 31 packets
Broadcast: Testing...Success, 112 packets
Broadcast Fix: Success

- worked! do a few more broadcast connection tests

$ ./firewire_tester -b -n 0 -r 5
Action: Test broadcast 5 times, node 0, channel 63
Broadcast: Testing...Success, 42 packets
Broadcast: Testing...Success, 112 packets
Broadcast: Testing...Success, 27 packets
Broadcast: Testing...Success, 78 packets
Broadcast: Testing...Success, 103 packets

- looks good, configure mythtv to use broadcast connection type.


Script.png firewire_tester.c

/*
 *  firewire_tester
 *  Copyright (c) 2006 by Jim Westfall
 *  Distributed as part of MythTV under GPL v2 and later.
 *
 *  $ gcc -Wall -o firewire_tester firewire_tester.c -liec61883 -lraw1394
 */

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/select.h>
#include <libraw1394/raw1394.h>
#include <libiec61883/iec61883.h>

#define ACTION_NONE        -1
#define ACTION_TEST_BCAST   0
#define ACTION_TEST_P2P     1
#define ACTION_FIX_BCAST    2
#define ACTION_RESET_BUS    3
#define ACTION_OUTPUT_P2P   4

#define SYNC_BYTE           0x47
#define MIN_PACKETS         25
#define MAX_NODATA          10

#define VERBOSE(args...)    do { if (verbose) printf(args); } while (0)

int verbose = 0;
int sync_failed = 0;
int nodata = 0;
int run = 0;

static int read_packet (unsigned char *tspacket, int len, 
                        unsigned int dropped, void *callback_data)
{
    int *count = (int *)callback_data;

    if (dropped)
    {
        printf("Dropped %d packet(s).\n", dropped);
        return 0;
    }

    if (tspacket[0] != SYNC_BYTE)
    {
        sync_failed = 1;
        return 0;
    }
    nodata = 0;
    *count = *count + 1;
    return 1;
}

static int fwrite_packet (unsigned char *tspacket, int len, 
                        unsigned int dropped, void *callback_data)
{
    int n;
    FILE *fp = (FILE *) callback_data;

    if (dropped)
    {
        fprintf(stderr, "Dropped %d packet(s).\n", dropped);
        return 0;
    }

    if (tspacket[0] != SYNC_BYTE)
    {
        sync_failed = 1;
        return 0;
    }
    nodata = 0;
    n = fwrite(tspacket, sizeof(char), 188, fp);
    //fprintf(stderr, "fwrite_packets: wrote %d of %d\n", n, len);
    return(n == len ? 0 : 1);
}

int test_connection(raw1394handle_t handle, int channel)
{
    int count = 0;
    int retry = 0;
    int fd = raw1394_get_fd(handle);
    iec61883_mpeg2_t mpeg;
    struct timeval tv;
    fd_set rfds;

    sync_failed = 0;
    mpeg = iec61883_mpeg2_recv_init(handle, read_packet, (void*) &count);
    iec61883_mpeg2_recv_start(mpeg, channel);
    while(count < MIN_PACKETS && retry < 2 && !sync_failed 
          && nodata < MAX_NODATA)
    {
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        tv.tv_sec = 1;
        tv.tv_usec = 0;

        if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
        {
             nodata++;
             raw1394_loop_iterate(handle);
        }
        else
        {
            retry++;
        }
    }
    iec61883_mpeg2_recv_stop(mpeg);
    iec61883_mpeg2_close(mpeg);

    if (sync_failed)
        return 0;

    return count;
}

// create and test a p2p connection
// returns 1 on success, 0 on failure
int test_p2p(raw1394handle_t handle, nodeid_t node) {
    int oplug = -1, iplug = -1, bandwidth = -1;
    int channel, count, success = 0;

    printf("P2P: Testing...");
    fflush(stdout);

    // make connection
    channel = iec61883_cmp_connect(handle, node, &oplug,
		raw1394_get_local_id(handle), &iplug, &bandwidth);

    VERBOSE("P2P: Creating, node %d, channel %d\n", node, channel);
    fflush(stdout);

#if 0
    if (iec61883_cmp_create_p2p_output(handle, node | 0xffc0, 0, channel,
                                       1 /* fix me, speed */ ) != 0)
    {
        printf("iec61883_cmp_create_p2p_output failed\n");
        return 0;
    }
#endif

    count = test_connection(handle, channel);
    if (count >= MIN_PACKETS)
    {
        printf("Success, %d packets received\n", count);
        success = 1;
    }
    else
    {
        printf("Failed%s\n", (sync_failed ? " (sync failed)":""));
    }

    VERBOSE("P2P: Disconnecting.\n");
    iec61883_cmp_disconnect(handle, node | 0xffc0, 0,
                            raw1394_get_local_id (handle),
                            -1, channel, 0);
    return success;
}

// create and test a broadcast connection
// returns 1 on success, 0 on failure
int test_broadcast(raw1394handle_t handle, nodeid_t node) {
    int channel, count, success = 0;
    channel = 63 - node;

    VERBOSE("Broadcast: Creating, node %d, channel %d\n", node, channel);

    printf("Broadcast: Testing...");
    fflush(stdout);

    // open connection
    if (iec61883_cmp_create_bcast_output(handle, node | 0xffc0, 0, channel, 
                                         1 /* fix me, speed */ ) != 0)
    {
        printf("iec61883_cmp_create_bcast_output failed\n");
        return 0;
    }
    count = test_connection(handle, channel);

    if (count >= MIN_PACKETS)
    {
        printf("Success, %d packets\n", count);
        success = 1;
    }
    else
    {
        printf("Failed%s\n", (sync_failed ? " (sync failed)":""));
    }

    VERBOSE("Broadcast: Disconnecting.\n");
    iec61883_cmp_disconnect(handle, node | 0xffc0, 0,
                            raw1394_get_local_id (handle),
                            -1, channel, 0);
    return success;
}  

/* 
 *  Attempt to get a reliable broadcast connection initialized
 *  This is done by first attempting multiple p2p connections until data is 
 *  received, once data is seen we then attempt multiple (5) broadcast 
 *  connections to verify the connection is stable.
 *  returns 1 on success, 0 on fail.
 */
int fix_broadcast(raw1394handle_t handle, nodeid_t node) {
    int p2p_retries = 0;
    int bcast_success = 0;

    // see if we even need to fix it
    while (test_broadcast(handle, node))
    {
        bcast_success++;
        if (bcast_success == 5)
        {
            printf("Broadcast Fix: Success (already stable)\n");
            return 1;
        }
    }

    // attempt upto 10 p2p connections looking for data
    while (p2p_retries < 10)
    { 
        if (test_p2p(handle, node))
        {
            // got data from p2p, try a few bcast connections
            bcast_success = 0;
            while (test_broadcast(handle, node))
            {
                bcast_success++;
                if (bcast_success == 5)
                {
                    printf("Broadcast Fix: Success\n");
                    return 1;
                }
            }
        }
        p2p_retries++;
    }
    printf("Broadcast Fix: Failed\n");
    return 0;
}

// create and test a p2p connection, copy packets to stdout
// returns 1 on success, 0 on failure
int output_p2p(raw1394handle_t handle, nodeid_t node) {
    int channel, count = 0, success = 0;
    int oplug = -1, iplug = -1, bandwidth = -1;
    int retry = 0;
    FILE *fp = stdout;
    iec61883_mpeg2_t mpeg;
    struct timeval tv;
    fd_set rfds;
    int fd = raw1394_get_fd(handle);

    sync_failed = 0;

    // make connection
    channel = iec61883_cmp_connect(handle, node, &oplug,
		raw1394_get_local_id(handle), &iplug, &bandwidth);
#if 0
    if (iec61883_cmp_create_p2p_output(handle, node | 0xffc0, 0, channel,
                                       1 /* fix me, speed */ ) != 0)
    {
        fprintf(stderr, "iec61883_cmp_create_p2p_output failed\n");
        return 0;
    }
#endif

    VERBOSE("P2P: Creating, node %d, channel %d\n", node, channel);
    fflush(stdout);

    mpeg = iec61883_mpeg2_recv_init(handle, fwrite_packet, (void*) fp);
    run = 1;
    iec61883_mpeg2_recv_start(mpeg, channel);
    while(run && retry < 2 && !sync_failed && nodata < MAX_NODATA)
    {
        FD_ZERO(&rfds);
        FD_SET(fd, &rfds);
        tv.tv_sec = 1;
        tv.tv_usec = 0;

        if (select(fd + 1, &rfds, NULL, NULL, &tv) > 0)
        {
             nodata++;
             raw1394_loop_iterate(handle);
        }
        else
        {
            retry++;
        }
    }
    iec61883_mpeg2_recv_stop(mpeg);
    iec61883_mpeg2_close(mpeg);

    if (sync_failed)
        return 0;

    return count;

    VERBOSE("P2P: Disconnecting.\n");
    iec61883_cmp_disconnect(handle, node | 0xffc0, 0,
                            raw1394_get_local_id (handle),
                            -1, channel, 0);
    return success;
}

void usage(void) {
    printf("firewire_tester <action> -n <node> [-P <port>] [-r <n>] [-v]\n");
    printf(" Actions: (one is required)\n");
    printf("    -b          - test broadcast connection\n");
    printf("    -p          - test p2p connection\n");
    printf("    -B          - attempt to fix/stabilize broadcast connection\n");
    printf("    -R          - reset the firewire bus\n");
    printf(" Options\n");
    printf("    -n <node>   - firewire node, required\n");
    printf("    -o          - write packets to stdout using p2p connection\n");
    printf("    -P <port>   - firewire port, default 0\n");
    printf("    -r <n>      - run action <n> times, default 1\n");
    printf("    -v          - verbose\n");
    exit(1);
}

int main(int argc, char **argv) {
    raw1394handle_t handle;
    int node = -1;
    int port = 0;
    int runs = 1;
    int c, i, success;
    int action = ACTION_NONE;

    opterr = 0;
    while ((c = getopt(argc, argv, "Bbn:opP:r:Rv")) != -1)
    {
        switch (c) 
        {

            // attempt to get a reliable bcast connection initialize
            case 'B':
                if (action != ACTION_NONE)
                {
                    printf("Invalid command line\n");
                    usage();
                }
                action = ACTION_FIX_BCAST;
                break;

            // test broadcast connection
            case 'b':
                if (action != ACTION_NONE)
                {
                    printf("Invalid command line\n");
                    usage();
                }
                action = ACTION_TEST_BCAST;
                break;

            // set the node, required
            case 'n':
                node = atoi(optarg);
                if (node < 0 || node > 63)
                {
                    printf("Invalid node: %d\n", node);
                    exit(1);
                }
                break;

            // output packets using p2p connection
            case 'o':
                if (action != ACTION_NONE)
                {
                    printf("Invalid command line\n");
                    usage();
                }
                action = ACTION_OUTPUT_P2P;
                break;

            // set the port, optional
            case 'P':
                port = atoi(optarg);
                if (port < 0)
                {
                    printf("Invalid port: %d\n", port);
                }
                break;

            // test a p2p connection
            case 'p':
                if (action != ACTION_NONE)
                {
                    printf("Invalid command line\n");
                    usage();
                }
                action = ACTION_TEST_P2P;
                break;

            case 'R':
                action = ACTION_RESET_BUS;
                break;

            // number of runs
            case 'r':
                runs = atoi(optarg);
                if (runs <= 0)
                {
                    printf("Run amount <= 0\n");
                    usage();
                }
                break;

            // verbose
            case 'v':
                verbose = 1;
                break;

            // bad option
            default:
                printf("Invalid command line\n");
                usage();
                
        }
    }

    if (action == ACTION_NONE)
    {
        usage();
    }

    if (action != ACTION_RESET_BUS && node == -1)
    {
        printf("-n <node> is a required option\n");
        usage();
    }

    VERBOSE("raw1394: Allocating handle, port %d.\n", port);
    handle = raw1394_new_handle_on_port(port);
    if (!handle)
    {
        printf("Failed to create new raw1394 handle on port %d\n", port);
        exit(1);
    }

    success = 0;
    switch (action)
    {
        case ACTION_TEST_BCAST:
            printf("Action: Test broadcast %d times, node %d, channel %d\n", 
                   runs, node, 63 - node);
            for (i = 0;i < runs;i++) 
                success += test_broadcast(handle, node);
            break;
        case ACTION_TEST_P2P:
            printf("Action: Test P2P connection %d times, node %d, channel %d\n",
                   runs, node, node);
            for (i = 0;i < runs;i++)
                success += test_p2p(handle, node);
            break;
        case ACTION_FIX_BCAST:
            printf("Action: Attempt to fix broadcast connection %d times, node %d\n", 
                   runs, node);
            for (i = 0;i < runs;i++)
                success += fix_broadcast(handle, node);
            break;
        case ACTION_RESET_BUS:
            runs = 1;
            printf("Action: Resetting the firewire bus\n");
            if (raw1394_reset_bus_new(handle, RAW1394_LONG_RESET) == 0)
            {
                printf("Bus reset succeeded\n");
                success = 1;
            }
            else
            {
                printf("Bus reset failed: %d\n", errno);
            }
            break;
        case ACTION_OUTPUT_P2P:
            runs = 1;
            fprintf(stderr,
                    "Action: Output P2P packets using node %d, channel %d\n",
                    node, node);
            success = output_p2p(handle, node);
            break;
    }

    VERBOSE("raw1394: Releasing handle.\n");
    raw1394_destroy_handle(handle);
    exit(!(success == runs));
}