#include <stdio.h>
#include <sys/errno.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/hippi.h>

#define DEFAULT_ULP         0x80
#define MAX_DEV_NAME_LEN    1024
#define MAX_WRITE           0x1000000 /* 16 meg */

extern int errno;

static char *usage = 
"Usage: fptest [-D<device_name] [-I<ifield>] [-U<ulp>] [-n<num packets] [-l<pkt length>] [-s] [-r]\n"
"-D: Device name. Default is /dev/hippi0.\n"
"-I: Ifield. Default is 0, should be set every time.\n"
"-U: ULP. Default is 0x80.\n"
"-n: Number of packets to send. 0 is send forever. Default is 64.\n"
"-l: Length of packet in bytes. Default is 1024.\n"
"-s: Send mode. This is the default.\n"
"-r: Receive mode.\n";


static char *src_errs[] = { 
    "no error",
    "SRC sequence error",
    "SRC lost DSIC",
    "SRC timeout",
    "SRC lost connect",
    "SRC connect rejection",
    "HIPPI interface shutdown",
    "SRC parity error"
};

static char *dst_errs[] = {
    "DST Parity Error",
    "DST LLRC Error",
    "DST Sequence Error",
    "DST Sync Error",
    "DST Illegal Burst",
    "DST SDIC loss",
    "DST Framing Error",
    "DST lost linkrdy in packet",
    "DST No Packet Signal in Connection",
    "DST Data Received with no READY's sent",
    "DST No Burst in Packet",
    "DST Illegal State Transition"
};

int send = 1, fd = -1;

void
die(char * str, ...) {
    va_list ap;

    va_start (ap, str);
    vfprintf(stderr, str, ap);
    va_end (ap);

    exit(1);
}

void
phiperr(int retval) {

    if (retval < 0 && errno == EIO) {
        retval = ioctl(fd, HIPIOCW_ERR);
        if (retval < 0)
            perror("HIPIOCW_ERR ioctl failed\n");
        else
            fprintf(stderr,
                    "HIPIOCW_ERR: %d: %s\n",retval,
                    (send) ? src_errs[retval] : dst_errs[retval]);
    }
    else
        perror("READ/WRITE ERROR");

    exit(1);
}

main(int argc, char **argv)
{
    extern char *optarg;
    extern int optind;
    char *buf;
    char device_name[MAX_DEV_NAME_LEN] = "/dev/hippi0";
    uint32_t num_pkts = 64,
             pkt_len = 0x400,
             ifield = 0,
             ulp = DEFAULT_ULP;
    int i, c;


    if(argc > 1) {
        while ((c = getopt(argc, argv, "D:I:U:n:l:srh")) != EOF) {
            switch (c) {
            case 'D':
                strncpy(device_name, optarg, MAX_DEV_NAME_LEN);
                break;
            case 'I':
                ifield = strtoul(optarg, NULL, 0);
                break;
            case 'U':
                ulp = strtoul(optarg, NULL, 0);
                break;
            case 'n':
                num_pkts = strtoul(optarg, NULL, 0);
                break;
            case 'l':
                pkt_len = strtoul(optarg, NULL, 0);
                break;
            case 's':
                send = 1;
                break;
            case 'r':
                send = 0;
                break;
            case 'h':
                printf(usage);
                exit(0);
            default:
                die(usage);
            }
        }
    }

    if (pkt_len > MAX_WRITE)
        die("ERROR: pkt_len (%d) must be <= %d\n", pkt_len, MAX_WRITE);


    buf = (char*)memalign(8, pkt_len);

    if (!buf) {
        perror("memalign() ");
        exit(1);
    }

    printf("Opening HIPPI-FP dev = %s\n",
           device_name);

    if (send)
        fd = open(device_name, O_WRONLY);
    else
        fd = open(device_name, O_RDONLY);

    if (fd == -1) {
        perror("open() ");
        exit(1);
    }

    printf("Binding to ULP = 0x%x\n", ulp);
    if (ioctl(fd, HIPIOC_BIND_ULP, ulp) < 0)
        die("ERROR: HIPIOC_BIND_ULP failed to bind to ulp\n");

    if (ioctl(fd, HIPIOCW_I, ifield) < 0)
        die("ERROR: HIPIOCW_I failed to set ifield\n");

    printf("%s mode:\n", (send) ? "Send" : "Receive");

    for (i = 1; (num_pkts ? (i < num_pkts + 1) : 1); i++) {
        int retval;

        if (send) {
            if ((retval = write(fd, buf, pkt_len)) != pkt_len) {
                fprintf(stderr, "pkt %d: trouble writing\n", i);
                phiperr(retval);
            }
        }
        else {
            if ((retval = read(fd, buf, 8)) != 8) {
                fprintf(stderr, "pkt %d: trouble reading header\n", i);
                phiperr(retval);
            }
            if ((retval = read(fd, buf, pkt_len)) != pkt_len) {
                fprintf(stderr, "pkt %d: trouble reading data\n", i);
                phiperr(retval);
            }
        }

        if (i%64 == 0)
            printf(".\n%s %d packets\n", (send) ? "Sent" : "Received", i);
        else
            printf(".");
    }
}
