/* pushbroom.c, the controller for the pushbroom device */

#include <sys/types.h>
#include <asm/types.h>
#include <asm/io.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <stdio.h>
#include <ctype.h>
#include <time.h>

/* base for parallel port */
#define LP_BASE 0x378

/* send data byte to io port */
#define LP_SEND(a) outb(~(a), LP_BASE)

/* character table */
char *char_str = " _ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";

__u8 char_mat[38][6] = 
{
    {0x00, 0x00, 0x00, 0x00, 0x00, 0x00},   /* ' ' */
    {0x01, 0x01, 0x01, 0x01, 0x01, 0x00},   /* '_' */
    {0x3f, 0x48, 0x88, 0x48, 0x3f, 0x00},   /* 'A' */
    {0xff, 0x91, 0x91, 0x91, 0x6e, 0x00},   /* 'B' */
    {0x7e, 0x81, 0x81, 0x81, 0x42, 0x00},   /* 'C' */
    {0xff, 0x81, 0x81, 0x42, 0x3c, 0x00},   /* 'D' */
    {0xff, 0x91, 0x91, 0x81, 0x00, 0x00},   /* 'E' */
    {0xff, 0x90, 0x90, 0x80, 0x00, 0x00},   /* 'F' */
    {0x7e, 0x81, 0x89, 0x8a, 0x4f, 0x00},   /* 'G' */
    {0xff, 0x08, 0x08, 0x08, 0xff, 0x00},   /* 'H' */
    {0x81, 0xff, 0x81, 0x00, 0x00, 0x00},   /* 'I' */
    {0x02, 0x01, 0x81, 0xfe, 0x80, 0x00},   /* 'J' */
    {0xff, 0x18, 0x24, 0x42, 0x81, 0x00},   /* 'K' */
    {0xff, 0x01, 0x01, 0x01, 0x00, 0x00},   /* 'L' */
    {0xff, 0x40, 0x30, 0x40, 0xff, 0x00},   /* 'M' */
    {0xff, 0x60, 0x18, 0x06, 0xff, 0x00},   /* 'N' */
    {0x7e, 0x81, 0x81, 0x81, 0x7e, 0x00},   /* 'O' */
    {0xff, 0x88, 0x88, 0x88, 0x70, 0x00},   /* 'P' */
    {0x7e, 0x81, 0x85, 0x82, 0x7d, 0x00},   /* 'Q' */
    {0xff, 0x88, 0x88, 0x8c, 0x73, 0x00},   /* 'R' */
    {0x72, 0x91, 0x91, 0x91, 0x4e, 0x00},   /* 'S' */
    {0x80, 0x80, 0xff, 0x80, 0x80, 0x00},   /* 'T' */
    {0xfe, 0x01, 0x01, 0x01, 0xfe, 0x00},   /* 'U' */
    {0xf8, 0x06, 0x01, 0x06, 0xf8, 0x00},   /* 'V' */
    {0xfc, 0x03, 0x1c, 0x03, 0xfc, 0x00},   /* 'W' */
    {0xc3, 0x24, 0x18, 0x24, 0xc3, 0x00},   /* 'X' */
    {0xe0, 0x10, 0x0f, 0x10, 0xe0, 0x00},   /* 'Y' */
    {0x87, 0x89, 0x91, 0xa1, 0xc1, 0x00},   /* 'Z' */
    {0x7e, 0xe1, 0x99, 0xff, 0x7e, 0x00},   /* '0' */
    {0x41, 0xff, 0x01, 0x00, 0x00, 0x00},   /* '1' */
    {0x63, 0x85, 0x89, 0x91, 0x61, 0x00},   /* '2' */
    {0x42, 0x81, 0x91, 0x91, 0x6e, 0x00},   /* '3' */
    {0x1c, 0x24, 0x44, 0xff, 0x04, 0x00},   /* '4' */
    {0xf2, 0x91, 0x91, 0x91, 0x8e, 0x00},   /* '5' */
    {0x7e, 0x91, 0x91, 0x91, 0x8e, 0x00},   /* '6' */
    {0x80, 0x80, 0x8f, 0xb0, 0xc0, 0x00},   /* '7' */
    {0x6e, 0x91, 0x91, 0x91, 0x6e, 0x00},   /* '8' */
    {0x70, 0x88, 0x88, 0x88, 0xff, 0x00}    /* '9' */
};

/* capture the parallel port's io */
void capture_lp();
/* release the parallel port's io */
void release_lp();

/* output to the lp port using PWM */
void out_pwm(__u8 code1, long int1, __u8 code2, long int2, unsigned long msec);

/* run the lightchaser with delay in milliseconds */
void run_chaser(long interval, long int1, long int2);
/* run the pushbroom with delay in milliseconds */
void run_pushbroom(long interval, long int1, long int2, char *msg);

void sighandle(int s);

int main(int argc, char **argv)
{
    char msg[81], chaser = 0, ch;
    long interval = 25, int1 = 10, int2 = 0;

    signal(SIGINT, sighandle);

    *msg = '\0';

    /* read the command line options */
    while ((ch = getopt(argc, argv, "hlit:p:f:b:")) != -1)
    {
        switch (ch)
        {
        case 'h':
        case '?':
            printf("%s [options]\n\n", argv[0]);
            printf("Options :\n");
            printf("-h       : Shows this help menu\n");
            printf("-l       : Shows a lightchaser\n");
            printf("-t<int>  : Set the time interval for wait in milliseconds\n");
            printf("-f<int>  : Use int/10 intensity on foreground\n");
            printf("-b<int>  : Use int/10 intensity on background\n");
            printf("-p<txt>  : Shows the message on pushbroom\n");
            printf("-i       : Shows a pushbroom message from the line on stdin\n\n");
            
            exit((ch == '?')?1:0);

        case 'l':
            chaser = 1;
            break;

        case 'i':
            fgets(msg, 80, stdin);
            break;

        case 't':
            interval = atoi(optarg);
            break;

        case 'p':
            strncpy(msg, optarg, 80);
            msg[80] = '\0';
            break;

        case 'f':
            int1 = atoi(optarg);
            break;

        case 'b':
            int2 = atoi(optarg);
            break;
	    
        default:
            fprintf(stderr, "Something wrong\n");
            exit(1);
        }
    }

    capture_lp();

    if (chaser)
        run_chaser(interval, int1, int2);
    else if (*msg)
        run_pushbroom(interval, int1, int2, msg);

    LP_SEND(0);
    release_lp();

    return 0;
}

void sighandle(int s)
{
    LP_SEND(0);
    exit(0);
}

void capture_lp()
{
    /* attempt to reserve region in io space */
    if (ioperm(LP_BASE, 3, 1) < 0)
    {
        fprintf(stderr, "Cannot reserve region in io space, aborting...\n");
        exit(1);
    }
}

void release_lp()
{
    /* attempt to release reserved region in io space */
    if (ioperm(LP_BASE, 3, 0) < 0)
    {
        fprintf(stderr, "Cannot release region in io space, aborting...\n");
        exit(1);
    }
}

void out_pwm(__u8 code1, long int1, __u8 code2, long int2, unsigned long msec)
{
    register unsigned long w = msec*100;
    register int i = 0;
    register __u8 res;
    clock_t end = clock() + msec*CLOCKS_PER_SEC/1000;
    
    while (clock() < end)
    {
        res = 0;
        if (int1 > i%10)
            res |= code1;
        if (int2 > i%10)
            res |= code2;

        LP_SEND(res);
        i++;
    }
}

void run_chaser(long interval, long int1, long int2)
{
    register __u8 i = 1;

    while (1)
    {
        out_pwm(i, int1, ~i, int2, interval);

        i <<= 1;
        if (i == 0)
            i = 1;
    }
}

void run_pushbroom(long interval, long int1, long int2, char *msg)
{
    register char *c = NULL;
    register int p = 0, i;

    while (*msg)
    {
        if (p > 5)
        {
            p = 0;
            c = NULL;
            msg++;
        }

        /* Find the char to display */
        while (!c)
        {
            c = strchr(char_str, toupper(*msg));

            if (!c)
            {
                if (*msg)
                    msg++;
                else
                    return;
            }
            else
                i = (int)(c - char_str);
        }

        out_pwm(char_mat[i][p], int1, ~char_mat[i][p], int2, interval);
        p++;
    }
}

