/*
 * A simple program to flash a set of LEDs connected to the parallel port.
 * - Assumes that there's only one port on the PC. 
 * - Assumes that the LEDs are arranged as:
 *
 *              1           5
 *            4   2       8   6
 *              3           7
 *
 * To do so, hack open a printer cable, and wire the anode side of the LEDs to 
 * pins 2-9, and all of the cathodes to pin 18.
 *
 * Copyright 2004 Jon McClintock.  All rights reserved.
 */
#include <asm/io.h>
#include <unistd.h>
#include <stdint.h>
#include <signal.h>
#include <getopt.h>

/* 
 * The I/O address of the parallel port.
 */
#define LPT_BASE 0x378

/*
 * Defines the patterns to flash. Update all three arrays at once.
 * If you add new patterns, be sure to increment the NUMBER_OF_PATTERNS
 * constant.
 */
#define NUMBER_OF_PATTERNS 7 
const unsigned char* patterns[] = {
    "\x00\xFF",
    "\x55\xAA",
    "\x11\x28\x44\x82",
    "\x11\x82\x44\x28",
    "\xEE\xD7\xBB\x7D",
    "\xEE\x7D\xBB\xD7",
    "\x80\x10\x20\x40\x80\x02\x01\x08\x04\x02",
};

/* 
 * The number of bytes in each pattern.
 */
const int pattern_lengths[] = {
        2,
        2,
        4,
        4,
        4,
        4,
        10,
};

/*
 * The names of the patterns. These must be unique.
 */
const char* pattern_names[] = {
    "blink",
    "alternate",
    "up",
    "down",
    "inversedown",
    "inverseup",
    "infinity",
};

/* 
 * A global to indicate whether the mainloop should continue running.
 */
unsigned char keep_running;

/*
 * We trap SIGINT, which is generated when you hit Ctrl-C, so that we can
 * reset the LEDs to all be off when we quit.
 */
void 
sigint_handler(int interrupt)
{
    keep_running = 0;
}

void
usage()
{
    printf("Usage: strobe [-r rate] [-p pattern] [-l port] [-h]\n");
    printf("Where pattern is one of:\n");         

    int i;
    for(i = 0; i < NUMBER_OF_PATTERNS; i++) {
        printf("\t%s\n", pattern_names[i]);
    }
}

int 
main(int argc, char** argv) 
{
    /* The frequency at which to flash, and the correspondingly calculated 
     * period between changing pattern stages. */
    float rate = 10.0, period;

    /* The selected pattern. */
    int pattern = 0;

    /* Print the pattern as we display it. */
    int verbose = 0;

    /* The address or the parallel port. */
    int addr = LPT_BASE;
    
    /* Parse the command-line parameters. */
    while (1) {
        int this_option_optind = optind ? optind : 1;
        int option_index = 0;
        static struct option long_options[] = {
            {"rate",    1, 0, 10},
            {"pattern", 1, 0, 0},
            {"port",    1, 0, LPT_BASE},
            {"verbose", 0, 0, 0},
            {"help",    0, 0, 0},
            {0, 0, 0, 0}
        };

        int c = getopt_long(argc, argv, "r:p:l:hv", 
                            long_options, &option_index);
        if (c == -1)
            break;

        int i;
        switch (c) {
            /* Pattern rate. */
            case 'r':
                sscanf(optarg, "%f", &rate);
                break;

            /* Pattern. */
            case 'p':
                for (i = 0; i < NUMBER_OF_PATTERNS; i++) {
                    if (strncmp(optarg, pattern_names[i], 
                                strlen(pattern_names[i])) == 0) {
                        pattern = i;
                        break;
                    }
                }
                if (i == NUMBER_OF_PATTERNS) {
                    printf("Unknown pattern specified.\n");
                    usage();
                    exit(-1);
                }
                break;

            /* Port to use. */
            case 'l':
                if (strncmp(optarg, "0x", 2) == 0) {
                    addr = strtoul(optarg, NULL, 16);
                } else {
                    addr = strtoul(optarg, NULL, 10);
                }
                break;

            case 'v':
                verbose = 1;
                break;

            case '?':
            case 'h':
            default:
                usage();
                exit(0);
        }
    }

    if (optind < argc) {
        usage();
        exit(-1);
    }

    /* Trap Ctrl-C. */
    signal(SIGINT, sigint_handler);
    
    /* Open the parallel port. */
    printf("Opening port 0x%03x...", addr);
    if (ioperm(addr, 1, 1) != 0) {
        printf("failed.\n");
        printf("Unable to access the parallel port. Make sure you are\n");
        printf("running with root privileges.\n\n");
        exit(-1);
    }
    printf("success.\n");
    
    printf("Strobing pattern '%s' at %f Hz...\n", pattern_names[pattern], rate);
    period = ((1 / rate) * 1000000) / 2;
    
    /* Display the actual pattern. */
    int stage = 0;
    keep_running = 1;
    while (keep_running) {
        for (stage = 0; stage < pattern_lengths[pattern]; stage++) {
            if (verbose) {
                printf("%d - 0x%02x\n", stage, patterns[pattern][stage]);
            }
            outb(patterns[pattern][stage], addr);
            usleep(period);
        }
    }
        
    outb(0x00, addr);
    return 0;
}

