/*
 * Copyright (c) 2007-2008 Atheros Communications Inc.
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*                                                                      */
/*  Module Name : apdbg.c                                               */
/*                                                                      */
/*  Abstract                                                            */
/*      Debug tools                                                     */
/*                                                                      */
/*  NOTES                                                               */
/*      None                                                            */
/*                                                                      */
/************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <net/if.h>
#include <netinet/in.h>

#include <linux/sockios.h>

#define ZM_IOCTL_REG_READ           0x01
#define ZM_IOCTL_REG_WRITE          0x02
#define ZM_IOCTL_MEM_DUMP           0x03
#define ZM_IOCTL_REG_DUMP           0x05
#define ZM_IOCTL_TXD_DUMP           0x06
#define ZM_IOCTL_RXD_DUMP           0x07
#define ZM_IOCTL_MEM_READ           0x0B
#define ZM_IOCTL_MEM_WRITE          0x0C
#define ZM_IOCTL_DMA_TEST           0x10
#define ZM_IOCTL_REG_TEST           0x11
#define ZM_IOCTL_TEST               0x80
#define ZM_IOCTL_TALLY              0x81 //CWYang(+)
#define ZM_IOCTL_RTS                0xA0
#define ZM_IOCTL_MIX_MODE           0xA1
#define ZM_IOCTL_FRAG               0xA2
#define ZM_IOCTL_SCAN               0xA3
#define ZM_IOCTL_KEY                0xA4
#define ZM_IOCTL_RATE               0xA5
#define ZM_IOCTL_ENCRYPTION_MODE    0xA6
#define ZM_IOCTL_GET_TXCNT          0xA7
#define ZM_IOCTL_GET_DEAGG_CNT      0xA8
#define ZM_IOCTL_DURATION_MODE      0xA9
#define ZM_IOCTL_SET_AES_KEY        0xAA
#define ZM_IOCTL_SET_AES_MODE       0xAB
#define ZM_IOCTL_SIGNAL_STRENGTH    0xAC //CWYang(+)
#define ZM_IOCTL_SIGNAL_QUALITY     0xAD //CWYang(+)
#define ZM_IOCTL_SET_PIBSS_MODE     0xAE
#define	ZDAPIOCTL                   SIOCDEVPRIVATE

struct zdap_ioctl {
	unsigned short cmd;                /* Command to run */
	unsigned int   addr;                /* Length of the data buffer */
	unsigned int   value;               /* Pointer to the data buffer */
	unsigned char data[0x100];
};

/* Declaration of macro and function for handling WEP Keys */

#if 0

#define SKIP_ELEM { \
    while(isxdigit(*p)) \
        p++; \
}

#define SKIP_DELIMETER { \
    if(*p == ':' || *p == ' ') \
        p++; \
}

#endif

char hex(char);
unsigned char asctohex(char *str);

char *prgname;

int set_ioctl(int sock, struct ifreq *req)
{
    if (ioctl(sock, ZDAPIOCTL, req) < 0) {
        fprintf(stderr, "%s: ioctl(SIOCGIFMAP): %s\n",
                prgname, strerror(errno));
        return -1;
    }

    return 0;
}


int read_reg(int sock, struct ifreq *req)
{
    struct zdap_ioctl *zdreq = 0;

    if (!set_ioctl(sock, req))
        return -1;

    //zdreq = (struct zdap_ioctl *)req->ifr_data;
    //printf( "reg = %4x, value = %4x\n", zdreq->addr, zdreq->value);

    return 0;
}


int read_mem(int sock, struct ifreq *req)
{
    struct zdap_ioctl *zdreq = 0;
    int i;

    if (!set_ioctl(sock, req))
        return -1;

    /*zdreq = (struct zdap_ioctl *)req->ifr_data;
    printf( "dump mem from %x, length = %x\n", zdreq->addr, zdreq->value);

    for (i=0; i<zdreq->value; i++) {
        printf("%02x", zdreq->data[i]);
        printf(" ");

        if ((i>0) && ((i+1)%16 == 0))
            printf("\n");
    }*/

    return 0;
}


int main(int argc, char **argv)
{
    int sock;
    int addr, value;
    struct ifreq req;
    char *action = NULL;
    struct zdap_ioctl zdreq;

    prgname = argv[0];

    if (argc < 3) {
        fprintf(stderr,"%s: usage is \"%s <ifname> <operation> [<address>] [<value>]\"\n",
                prgname, prgname);
        fprintf(stderr,"valid operation: read, write, mem, reg,\n");
        fprintf(stderr,"               : txd, rxd, rmem, wmem\n");
        fprintf(stderr,"               : dmat, regt, test\n");

        fprintf(stderr,"    scan, Channel Scan\n");
        fprintf(stderr,"    rts <decimal>, Set RTS Threshold\n");
        fprintf(stderr,"    frag <decimal>, Set Fragment Threshold\n");
        fprintf(stderr,"    rate <0-28>, 0:AUTO, 1-4:CCK, 5-12:OFDM, 13-28:HT\n");
        fprintf(stderr,"    TBD mix <0 or 1>, Set 1 to enable mixed mode\n");
        fprintf(stderr,"    enc, <0-3>, 0=>OPEN, 1=>WEP64, 2=>WEP128, 3=>WEP256\n");
        fprintf(stderr,"    skey <key>, Set WEP key\n");
        fprintf(stderr,"    txcnt, Get TxQ Cnt\n");
        fprintf(stderr,"    dagcnt, Get Deaggregate Cnt\n");
        fprintf(stderr,"    durmode <mode>, Set Duration Mode 0=>HW, 1=>SW\n");
        fprintf(stderr,"    aeskey <user> <key>\n");
        fprintf(stderr,"    aesmode <mode>\n");
        fprintf(stderr,"    wlanmode <0,1> 0:Station mode, 1:PIBSS mode\n");
        fprintf(stderr,"    tal <0,1>, Get Current Tally Info, 0=>read, 1=>read and reset\n");

        exit(1);
    }

    strcpy(req.ifr_name, argv[1]);
    zdreq.addr = 0;
    zdreq.value = 0;

    /* a silly raw socket just for ioctl()ling it */
    sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
    if (sock < 0) {
        fprintf(stderr, "%s: socket(): %s\n", argv[0], strerror(errno));
        exit(1);
    }

    if (argc >= 4)
    {
        sscanf(argv[3], "%x", &addr);
    }

    if (argc >= 5)
    {
        sscanf(argv[4], "%x", &value);
    }

    zdreq.addr = addr;
    zdreq.value = value;

    if (!strcmp(argv[2], "read"))
    {
        zdreq.cmd = ZM_IOCTL_REG_READ;
    }
    else if (!strcmp(argv[2], "mem"))
    {
        zdreq.cmd = ZM_IOCTL_MEM_DUMP;
    }
    else if (!strcmp(argv[2], "write"))
    {
        zdreq.cmd = ZM_IOCTL_REG_WRITE;
    }
    else if (!strcmp(argv[2], "reg"))
    {
        zdreq.cmd = ZM_IOCTL_REG_DUMP;
    }
    else if (!strcmp(argv[2], "txd"))
    {
        zdreq.cmd = ZM_IOCTL_TXD_DUMP;
    }
    else if (!strcmp(argv[2], "rxd"))
    {
        zdreq.cmd = ZM_IOCTL_RXD_DUMP;
    }
    else if (!strcmp(argv[2], "rmem"))
    {
        zdreq.cmd = ZM_IOCTL_MEM_READ;
    }
    else if (!strcmp(argv[2], "wmem"))
    {
        zdreq.cmd = ZM_IOCTL_MEM_WRITE;
    }
    else if (!strcmp(argv[2], "dmat"))
    {
        zdreq.cmd = ZM_IOCTL_DMA_TEST;
    }
    else if (!strcmp(argv[2], "regt"))
    {
        zdreq.cmd = ZM_IOCTL_REG_TEST;
    }
    else if (!strcmp(argv[2], "test"))
    {
        zdreq.cmd = ZM_IOCTL_TEST;
    }
    else if (!strcmp(argv[2], "tal"))
    {
        sscanf(argv[3], "%d", &addr);
        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_TALLY;
    }
    else if (!strcmp(argv[2], "rts"))
    {
        sscanf(argv[3], "%d", &addr);
        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_RTS;
    }
    else if (!strcmp(argv[2], "mix"))
    {
        zdreq.cmd = ZM_IOCTL_MIX_MODE;
    }
    else if (!strcmp(argv[2], "frag"))
    {
        sscanf(argv[3], "%d", &addr);
        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_FRAG;
    }
    else if (!strcmp(argv[2], "scan"))
    {
        zdreq.cmd = ZM_IOCTL_SCAN;
    }
    else if (!strcmp(argv[2], "skey"))
    {
        zdreq.cmd = ZM_IOCTL_KEY;

        if (argc >= 4)
        {
            unsigned char temp[29];
            int i;
            int keyLen;
            int encType;

            keyLen = strlen(argv[3]);

            if (keyLen == 10)
            {
                sscanf(argv[3], "%02x%02x%02x%02x%02x", &temp[0], &temp[1],
                        &temp[2], &temp[3], &temp[4]);
            }
            else if (keyLen == 26)
            {
                sscanf(argv[3], "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                        &temp[0], &temp[1], &temp[2], &temp[3], &temp[4],
                        &temp[5], &temp[6], &temp[7], &temp[8], &temp[9],
                         &temp[10], &temp[11], &temp[12]);
            }
            else if (keyLen == 58)
            {
                sscanf(argv[3], "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
                        &temp[0], &temp[1], &temp[2], &temp[3], &temp[4],
                        &temp[5], &temp[6], &temp[7], &temp[8], &temp[9],
                        &temp[10], &temp[11], &temp[12], &temp[13], &temp[14],
                        &temp[15], &temp[16], &temp[17], &temp[18], &temp[19],
                        &temp[20], &temp[21], &temp[22], &temp[23], &temp[24],
                        &temp[25], &temp[26], &temp[27], &temp[28]);
            }
            else
            {
                fprintf(stderr, "Invalid key length\n");
                exit(1);
            }
            zdreq.addr = keyLen/2;

            for(i=0; i<zdreq.addr; i++)
            {
                zdreq.data[i] = temp[i];
            }
        }
        else
        {
            printf("Error : Key required!\n");
        }
    }
    else if (!strcmp(argv[2], "rate"))
    {
        sscanf(argv[3], "%d", &addr);

        if (addr > 28)
        {
            fprintf(stderr, "Invalid rate, range:0~28\n");
            exit(1);
        }
        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_RATE;
    }
    else if (!strcmp(argv[2], "enc"))
    {
        sscanf(argv[3], "%d", &addr);

        if (addr > 3)
        {
            fprintf(stderr, "Invalid encryption mode, range:0~3\n");
            exit(1);
        }

        if (addr == 2)
        {
            addr = 5;
        }
        else if (addr == 3)
        {
            addr = 6;
        }

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_ENCRYPTION_MODE;
    }
    else if (!strcmp(argv[2], "txcnt"))
    {
        zdreq.cmd = ZM_IOCTL_GET_TXCNT;
    }
    else if (!strcmp(argv[2], "dagcnt"))
    {
        sscanf(argv[3], "%d", &addr);

        if (addr != 0 && addr != 1)
        {
            fprintf(stderr, "The value should be 0 or 1\n");
            exit(0);
        }

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_GET_DEAGG_CNT;
    }
    else if (!strcmp(argv[2], "durmode"))
    {
        sscanf(argv[3], "%d", &addr);

        if (addr != 0 && addr != 1)
        {
            fprintf(stderr, "The Duration mode should be 0 or 1\n");
            exit(0);
        }

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_DURATION_MODE;
    }
    else if (!strcmp(argv[2], "aeskey"))
    {
        unsigned char temp[16];
        int i;

        sscanf(argv[3], "%d", &addr);

        sscanf(argv[4], "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", &temp[0], &temp[1], &temp[2], &temp[3], &temp[4], &temp[5], &temp[6], &temp[7], &temp[8], &temp[9], &temp[10], &temp[11], &temp[12], &temp[13], &temp[14], &temp[15]);

        for(i = 0; i < 16; i++)
        {
            zdreq.data[i] = temp[i];
        }

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_SET_AES_KEY;
    }
    else if (!strcmp(argv[2], "aesmode"))
    {
        sscanf(argv[3], "%d", &addr);

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_SET_AES_MODE;
    }
    else if (!strcmp(argv[2], "wlanmode"))
    {
        sscanf(argv[3], "%d", &addr);

        zdreq.addr = addr;
        zdreq.cmd = ZM_IOCTL_SET_PIBSS_MODE;
    }
    else
    {
	    fprintf(stderr, "error action\n");
        exit(1);
    }

    req.ifr_data = (char *)&zdreq;
    set_ioctl(sock, &req);

fail:
    exit(0);
}

unsigned char asctohex(char *str)
{
    unsigned char value;

    value = hex(*str) & 0x0f;
    value = value << 4;
    str++;
    value |= hex(*str) & 0x0f;

    return value;
}

char hex(char v)
{
    if(isdigit(v))
        return v - '0';
    else if(isxdigit(v))
        return (tolower(v) - 'a' + 10);
    else
        return 0;
}