#define _GNU_SOURCE
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/ioctl.h>

#include "dev/pci/sis1100pci_var.h"
#include "commands.h"

static int
send_command(int p, int code, int sequence, int data)
{
    struct sis1100_command command;
    int res;
    command.data[0]=code;
    command.data[1]=sequence;
    command.data[2]=data;

    res=ioctl(p, SIS1100_COMMAND_WRITE, &command);
    if (res) perror("SIS1100_COMMAND_WRITE");
    return res;
}

static int
read_command(int p, int* code, int* sequence, int* data)
{
    struct sis1100_command command;
    int res;
    res=ioctl(p, SIS1100_COMMAND_READ, &command);
    if (res) {
        perror("SIS1100_COMMAND_READ");
        return res;
    }
    *code=command.data[0];
    *sequence=command.data[1];
    *data=command.data[2];
    return res;
}

static int
do_command(int p, int* code, int* data)
{
    int sequence, res;

    res=read_command(p, code, &sequence, data);
    if (res) return res;
    res=send_command(p, comm_ack, sequence, 0);
    if (res) return res;
    return 0;
}

static
int do_write(int p, int size)
{
    int i, res;
    unsigned char* buf;

    /*printf("do_write(p=%d, size=%d)\n", p, size);*/
    buf=malloc(size);
    if (!buf) {
        perror("malloc");
        return -1;
    }
    for (i=0; i<size; i++) buf[i]=i;
    res=write(p, buf, size);
    if (res!=size) {
        if (res<0)
            perror("write");
        else
            fprintf(stderr, "write: res=%d\n", res);
        return -1;
    }
    return 0;
}

static
int do_read(int p, int size)
{
    int res, i;
    unsigned char* buf;
    printf("do_read(p=%d, size=%d)\n", p, size);
    buf=malloc(size);
    if (!buf) {
        perror("malloc");
        return -1;
    }
    res=read(p, buf, size);
    if (res!=size) {
        if (res<0)
            perror("read");
        else
            fprintf(stderr, "read: res=%d\n", res);
        return -1;
    }
    for (i=0; i<size; i++) printf("%02x ", buf[i]);
    printf("\n");
    return 0;
}

int main(int argc, char* argv[])
{
    int p, code, data;
    struct sis1100_ident ident;

    if (argc!=2)
        {
        fprintf(stderr, "usage: %s path\n", argv[0]);
        return 1;
        }

    if ((p=open(argv[1], O_RDWR, 0))<0) {
        fprintf(stderr, "open \"%s\": %s\n", argv[1], strerror(errno));
        return 1;
    }

    if (ioctl(p, SIS1100_IDENT, &ident)<0) {
        fprintf(stderr, "ioctl(SIS1100_IDENT): %s\n", strerror(errno));
        return 2;
    }

    printf("local device:\n");
    printf("  hw_type   : %d\n",   ident.local.hw_type);
    printf("  hw_version: %d\n",   ident.local.hw_version);
    printf("  fw_type   : %d\n",   ident.local.fw_type);
    printf("  fw_version: %d\n\n", ident.local.fw_version);
    printf("remote device:\n");
    if (ident.remote_online) {
        printf("  online.\n");
    } else {
        printf("  not online.\n");
        return 0; 
    }

    do {
        if (do_command(p, &code, &data)) {
            printf("do_command failed\n");
            return 1;
        }
        /*printf("got command %d data %d\n", code, data);*/
        if ((data<20) || ((data%400)==0)) printf("size=%d\n", data);
        switch (code) {
            case comm_write:
                do_write(p, data);
                break;
            case comm_read:
                do_read(p, data);
                break;
        }
    }
    while (code!=-1);

    if (close(p)<0) {
        fprintf(stderr, "close: %s\n", strerror(errno));
        return 2;
    }
    return 0;
}
