#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/ioctl.h>
#include <sys/socket.h>

#include <linux/sockios.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <linux/ip.h>
#include <linux/udp.h>
#include <linux/if.h>

#define ENTER do { fprintf(stderr, "%s:%d - enter\n", \
__FUNCTION__, __LINE__); } while (0)

typedef unsigned int uint;

/* ioctl ops */
enum {
   OP_INFO,OP_READ_REG,OP_WRITE_REG,OP_MDIO_READ,OP_MDIO_WRITE
};


static void
ndelay(long nsec)
{
	struct timespec ts;
	ts.tv_sec = 0;
	ts.tv_nsec = nsec;
	nanosleep(ts, NULL);
}

static void
udelay(int usec)
{
    struct timeval tv;

    tv.tv_sec = 0;
    tv.tv_usec = usec;
    select(0, NULL, NULL, NULL, &tv);
}



int sd;
struct ifreq req;
uint data[3];

static uint
INFO(uint reg)
{
    data[0] = OP_INFO;
    data[1] = reg;
    if (ioctl(sd, SIOCDEVPRIVATE+1, &req) < 0) {
        fprintf(stderr, "get_info 0x%x (%d) reg failed\n", reg, reg);
        exit(1);      
    }
    //printf("read_reg(%x)=%x\n", reg, data[2]);
    return data[2];
}


static uint
READ_REG(uint reg)
{
    data[0] = OP_READ_REG;
    data[1] = reg;
    if (ioctl(sd, SIOCDEVPRIVATE+1, &req) < 0) {
        fprintf(stderr, "reading 0x%x (%d) reg failed\n", reg, reg);
        exit(1);      
    }
//    printf("read_reg(%x)=%x\n", reg, data[2]);
    return data[2];
}

static void
WRITE_REG(uint reg, uint val)
{
    data[0] = OP_WRITE_REG;
    data[1] = reg;
    data[2] = val;
    if (ioctl(sd, SIOCDEVPRIVATE+1, &req) < 0) {
        fprintf(stderr, "writing 0x%x (%d) reg with 0x%x (%d) failed\n",
              reg, reg, val, val);
        exit(1);      
    }
//    printf("write_reg=0x%x val=0x%x)\n", reg, val);
}

/*========================*/
/*
Werite bit

1.	Set CEB low;
2.	Empty read ;
3.	Wait 1us(tCSRST);
4.	Set RSTB low;
5.	Set RSTB high;
6.	Empty read;
7.	Wait 1us(tRS);
8.	Set valid A (address);
9.	Set DLE high and Valid data;
10.	Set WEB low;
11.	Set WEB high;
12.	Set DLE low, data could be unset;
13.	Set PGMEN high - initiates automatic cycle;
14.	Wait 16.5us(tPGM);
15.	Test if Auto Cycle is done
a.	Read OTP_STAT[18];
16.	Wait 100ns(tPGSR);
17.	Set PFMVFY high;
18.	Empty read;
19.	Wait 1us(tPVSR);
20.	Set READEN high;
21.	Wait 100ns(tREADEN);
22.	Read "read data"
a.	Read OTP_CTRL[7:0];
b.	Verify if data OK;
23.	Set READEN low;
24.	Empty read;
25.	Wait 100ns(tPVHR);
26.	Set PGMVFY low.

Read  byte

1.	Set CEB low;
2.	Empty read ;
3.	Wait 1us(tCSRST);
4.	Set RSTB low and valid address;
5.	Set RSTB high;
6.	Empty read;
7.	Wait 1us(tRSRD);
8.	Set READEN high;
9.	Wait 100ns(tREADEN);
10.	Read "read data"
a.	Read OTP_CTRL[7:0];
11.	Set Next Valid address;
12.	Wait 100ns(tACC);
13.	Read next "read data"
a.	Read OTP_CTRL[7:0];
14.	Set READEN low;
15.	Empty read;
16.	Set CEB high;
17.	Empty read;



*/



#ifndef OTP_CTRL

#define OTP_CTRL 0x5710
#define OTP_STAT 0x5720
#define OTP_OPS 0x5730
#define OTP_PAARAM 0x5740
#define OTP_AUTO 0x5750
/**/
#define READ_OTP_CTRL(c) READ_REG(OTP_CTRL)

#define WRITE_OTP_CTRL(c,v) WRITE_REG(OTP_CTRL,(v))
#define READ_OTP_STAT(c) READ_REG(OTP_STAT)

#define OTP_CTRL_D       0xFF 
#define OTP_CTRL_DIN     (1<<8) 
#define OTP_CTRL_A_SET(a) ((0x01FFF&(a))<<9) 
#define OTP_CTRL_CEB     (1<<22)
#define OTP_CTRL_CLE     (1<<23)
#define OTP_CTRL_DLE     (1<<24)
#define OTP_CTRL_PGMEM   (1<<25)
#define OTP_CTRL_WEB     (1<<26)
#define OTP_CTRL_READEN  (1<<27)
#define OTP_CTRL_RSTB    (1<<28) 
#define OTP_CTRL_PGMVFY  (1<<29) 
#define OTP_CTRL_CPUMPEN (1<<30) 
#define OTP_CTRL_LOCK    (1<<31) 

#define OTP_STAT_RDBNK(a)    (0x7&(a)) 
#define OTP_STAT_PGMBNK(a)   (0x7&(a>>3)) 
#define OTP_STAT_BUZY        (1<<6) 
#define OTP_STAT_RDVALID     (1<<7) 
#define OTP_STAT_RDDATA(a)   (0xFF&(a>>8)) 
#define OTP_STAT_PGMERROR    (1<<16) 
#define OTP_STAT_PGMLOCK     (1<<17) 
#define OTP_STAT_PGMEN_DONE  (1<<18) 

#define uDELAY(a) udelay((a))
#endif /*OTP_CTRL*/

/*========================*/


static void
usage()
{
        printf("tconfig: utility to configure and query Tehuti cards\n");
        printf("  tconfig ethN <action> <params>\n");
        printf("  actions are:\n");
        printf("  read_reg <addr> - reads register at <addr>\n");
        printf("  info <i> - reads info <i> \n");
        printf("         1 - io version \n");
        printf("         2 - PHY port (0xFF - no PHY) \n");
 
}

static void
my_exit(char *str)
{
     if (str)
         printf("error: %s\n", str);
     usage();
     exit(1);
}


/**********************************************************/

static int
op_INFO(int argc, char *argv[])
{
    uint reg, val;
  
    reg = (uint) strtol(argv[0], NULL, 0);
    val = INFO(reg);
    printf("0x%x (%d)\n", val, val);
    return 1;
}


static int
op_READ_REG(int argc, char *argv[])
{
    uint reg, val;
    
    reg = (uint) strtol(argv[0], NULL, 0);
    val = READ_REG(reg);
    printf("%d\n", val);
    return 1;
}

typedef int (* op_func)(int argc, char *argv[]);
struct {
    char *name;
    op_func func;
    int params; 
} ops[] = {
    { "info",        op_INFO,1 },
    { "read_reg",    op_READ_REG,1 },
    { NULL, NULL,0 },
};

int
main(int argc, char *argv[])
{
    char *eth = NULL;
    int i;
    
    if (argc < 2) 
        my_exit("no eth specified");
    
    eth = argv[1];
    if (argc == 2) 
        my_exit("nothing asked - nothing done");
    
    if (!(sd = socket(PF_INET, SOCK_DGRAM, 0))) 
        my_exit("cant create socket");

    strcpy(req.ifr_name, eth);
    req.ifr_data = (char *) &data;

    for (i = 0; ops[i].name; i++) {
        if (!strcmp(ops[i].name, argv[2])) {
            if((argc-3)<ops[i].params) my_exit("not enough args");
            else{
              int j= ops[i].func(argc - 3, argv + 3);
              exit(j?0:1);
            }
        }
    }
    printf("%s operation not supported\n", argv[2]); 
    exit(1);
}
