#include <t_services.h>
#include <string.h>
#include "kernel_id.h"
#include "player.h"
#include <cqnxparm.h>

#define	EP_ISO	1
#define	FEATURE_UNIT	0x09

#define	ISO_UNIT	192

/*
 * For 48.0KHz, we'll send 192 bytes every 1ms.
 * For 44.1KHz sampling, we'll send following bytes every 10ms
 *  0	176
 *  1	176
 *  2	176
 *  3	176
 *  4	176
 *  5	176
 *  6	176
 *  7	176
 *  8	180
 *  9	176
 *
 * Total 1764 bytes in 10ms
 */

struct pcmb_desc {
  int index;		/* Current buffer index */
  int putpos;	/* Put pointer */
  UB *putptr;
  int count_10ms;
  int copy_count;
  int frames;
};

#define	NUM_PCMINDEX	32

static void usb_vol_set(int val);
static void usb_rate_set(int rate);
static void usb_pcm_output(pcm_data *pcm);
static void usb_init_output(void);
static void usb_stop_output(void);
static void usb_restart_output(void);

static devfunc usb_dev_funcs = {
  usb_vol_set, usb_rate_set, usb_pcm_output,
  usb_init_output, usb_stop_output, usb_restart_output
};

static int usbvols[] = {
  /*   0       1       2       3       4       5 */
  0xe3a0, 0xe3a0, 0xe400, 0xe4c0, 0xe4c0, 0xe580,
  0xe640, 0xe700, 0xe7c0, 0xe880, 0xe940
};

static int in_dma, in_halt;
static int abort_play, iso_loop;
static int defvol = 0xe3a0;
static int minvol = 0xe3a0;
static int maxvol = 0xe940;

/*
 *  Audio PCM buffer is located at memory pointed by UserBuffer.
 *  We need 192bytes for each buffer and there are 32 periodic
 *  list slots, so total 192 * 32 = 6144 bytes space is required.
 */
static struct pcmb_desc pcmbufstate;

#define	PORT1_INS	(1 << 0)
#define	PORT1_REM	(1 << 1)
#define	PORT2_INS	(1 << 2)
#define	PORT2_REM	(1 << 3)
#define	PORT_FLAGS	0xff

#include "Host/usbhost_lpc2388.c"
#include "Host/usbhost_audio.c"

devfunc *usb_func_init()
{
    return &usb_dev_funcs;
}

static void pcmb_init()
{
    int i;
    struct pcmb_desc *pcm = &pcmbufstate;

    pcm->putptr = (UB *)UserBuffer;
    pcm->index = pcm->putpos = 0;
    pcm->frames = pcm->copy_count = pcm->count_10ms = 0;
    abort_play = iso_loop = 0;
    in_halt = in_dma = 0;
    for (i = 0; i < NUM_TD; i++)
        sig_sem(SEM_PCMISO);
}

void usb_set_params();

void host_task()
{
    FLGPTN ptn;
    ER err;

    Host_Init();
    for (;;) {
        err = wai_flg(FLG_USBHUB, PORT_FLAGS, TWF_ORW, &ptn);
        if (ptn & (PORT1_INS|PORT2_INS)) {
            /* Device is connected. Wait for 10ms */
            err = twai_flg(FLG_USBHUB, PORT_FLAGS, TWF_ORW, &ptn, 10);
            if (err == E_TMOUT) {
syslog(LOG_NOTICE, "Inserted %x, %x", ptn, HcRhPortStatus1);
                Host_EnumDev(0);
                select_usb_output();
                usb_set_params();
            }
        }
        if ((err == E_OK) && (ptn & (PORT1_REM|PORT2_REM))) {
            /* Device is removed */
            HcControl &= ~(OR_CONTROL_IE|OR_CONTROL_PLE);
syslog(LOG_NOTICE, "removed %x, %x, %x", ptn,
                HcRhPortStatus1, HcRhPortStatus2);
        }
    }
}

#ifdef DEBUG_COMMAND
static int get_if(int index)
{
    Host_CtrlRecv(USB_DEVICE_TO_HOST | USB_RECIPIENT_INTERFACE,
        GET_INTERFACE, 0, index, 1, ParamBuffer);
    return ParamBuffer[0];
}
#endif

static void set_if(int index, int altno)
{
    Host_CtrlSend(USB_HOST_TO_DEVICE | USB_RECIPIENT_INTERFACE,
        SET_INTERFACE, altno, index, 0, NULL);
}

#define	MUTE_CONTROL	1
#define	VOLUME_CONTROL	2
#define	GET_CUR	0x81
#define	GET_MIN	0x82
#define	GET_MAX	0x83
#define	SET_CUR	0x01
#define	SET_MIN	0x02
#define	SET_MAX	0x03

#define	SAMPLING_FREQ_CONTROL	1

#ifdef DEBUG_COMMAND
static int get_mute(int index)
{
    ParamBuffer[0] = 0x55;
    Host_CtrlRecv(GET_FEATURE_UNIT, GET_CUR,
	MUTE_CONTROL << 8, (FEATURE_UNIT<<8) | 0x00, 1, ParamBuffer);
    return ParamBuffer[0];
}

static void set_mute(int index, int val)
{
    ParamBuffer[0] = val & 0xff;
    Host_CtrlSend(SET_FEATURE_UNIT, SET_CUR,
        MUTE_CONTROL << 8, (FEATURE_UNIT<<8) | 0x00, 1, ParamBuffer);
}

static int get_freq(int ep)
{
    int freq;

    Host_CtrlRecv(GET_ENDPOINT_CONTROL, GET_CUR,
        (SAMPLING_FREQ_CONTROL << 8) | 0x00, ep, 3, ParamBuffer);
cons_printf("CUR: (%x %x %x) ",
        ParamBuffer[0], ParamBuffer[1], ParamBuffer[2]);
    freq = (ParamBuffer[2] << 16) | (ParamBuffer[1] << 8) | ParamBuffer[0];
    return freq;
}
#endif

static void set_freq(int ep, int freq)
{
    ParamBuffer[0] = freq & 255;
    ParamBuffer[1] = (freq >> 8) & 255;
    ParamBuffer[2] = (freq >> 16) & 255;
    Host_CtrlSend(SET_ENDPOINT_CONTROL, SET_CUR,
        (SAMPLING_FREQ_CONTROL << 8) | 0x00, ep, 3, ParamBuffer);
}

#ifdef DEBUG_COMMAND
static void get_vol(int chan)
{
    volatile UH *bp = (UH *)ParamBuffer;
    Host_CtrlRecv(GET_FEATURE_UNIT, GET_CUR,
        (VOLUME_CONTROL << 8) | chan, (FEATURE_UNIT << 8) | 0x00, 2, ParamBuffer);
cons_printf("CUR: %d (%x %x) ", *bp, ParamBuffer[0], ParamBuffer[1]);
    Host_CtrlRecv(GET_FEATURE_UNIT, GET_MIN,
        (VOLUME_CONTROL << 8) | chan, (FEATURE_UNIT << 8) | 0x00, 2, ParamBuffer);
cons_printf("MIN: %d (%x %x) ", *bp, ParamBuffer[0], ParamBuffer[1]);
    Host_CtrlRecv(GET_FEATURE_UNIT, GET_MAX,
        (VOLUME_CONTROL << 8) | chan, (FEATURE_UNIT << 8) | 0x00, 2, ParamBuffer);
cons_printf("MAX: %d (%x %x)\n", *bp, ParamBuffer[0], ParamBuffer[1]);
}
#endif

static void set_vol(int chan, int volume)
{
    UH *bp = (UH *)ParamBuffer;

    *bp = volume & 0xffff;
    Host_CtrlSend(SET_FEATURE_UNIT, SET_CUR,
        (VOLUME_CONTROL << 8) | chan, (FEATURE_UNIT << 8) | 0x00, 2, ParamBuffer);
#ifdef XDEBUG
    syslog(LOG_NOTICE, "set_vol called (%d-> %x %x)\n", volume, ParamBuffer[0], ParamBuffer[1]);
#endif
}

static void hcca_dump()
{
    HCCA *phcca = (HCCA *) HcHCCA;
    int i, n;

    n = 0;
    for (i = 0; i < 32; i++) {
        cons_printf("  %2d: %x", i, phcca->IntTable[i]);
        if (++n == 4) {
            cons_printf("\n");
            n = 0;
        }
    }
    cons_printf("HccaFrameNumber: %x\n", phcca->FrameNumber);
    cons_printf("HccaDoneHead: %x\n", phcca->DoneHead);
    cons_printf("\n");
    cons_printf("HcControl: %x\n", HcControl);
    cons_printf("HcCommandStatus: %x\n", HcCommandStatus);
    cons_printf("HcFmInterval: %x\n", HcFmInterval);
    cons_printf("HcPeriodCurrentED: %x\n", HcPeriodCurrentED);
    cons_printf("HcPeriodicStart: %x\n", HcPeriodicStart);
    cons_printf("HcInterruptStatus: %x\n", HcInterruptStatus);
    cons_printf("HcInterruptEnable: %x\n", HcInterruptEnable);
    cons_printf("HcRhStatus: %x\n", HcRhStatus);
    cons_printf("HcRhPortStatus1: %x\n", HcRhPortStatus1);
    cons_printf("HcRhDescA: %x\n", HC_RH_DESCA);
    cons_printf("HcRhDescB: %x\n", HC_RH_DESCB);
}

static void usb_rate_set(int rate)
{
    set_freq(EP_ISO, rate);
}

static void usb_vol_set(int val)
{
    int level;

    if (val < 0) {
        val = 0;
    } else if (val > 10) {
        val = 10;
    }
    level = usbvols[val];

    set_vol(1, level);
    set_vol(2, level);
}

void usb_set_params()
{
    set_vol(1, defvol);
    set_vol(2, defvol);
    set_if(1, 1);
    dly_tsk(100);
}

#ifdef DEBUG_COMMAND
void host_command(int argc, char **argv)
{
    char *cp;
    int ifindex, altif;

    if (argc > 2) {
        cp = argv[1];
        ifindex = atoi(argv[2]);
        if (strncmp(cp, "ifget", 5) == 0) {
            cons_printf("%d\n", get_if(ifindex));
        } else if (strncmp(cp, "volget", 5) == 0) {
            get_vol(ifindex);
        } else if (strncmp(cp, "freqget", 6) == 0) {
            cons_printf("%d\n", get_freq(ifindex));
        } else if (strncmp(cp, "muteget", 6) == 0) {
            cons_printf("%d\n", get_mute(ifindex));
        } else if (strncmp(cp, "periodic", 8) == 0) {
            HcPeriodicStart = ifindex;
            cons_printf("%d (%x)\n", ifindex);
        } else if (strncmp(cp, "ifset", 5) == 0) {
            if (argc > 3) {
                altif = atoi(argv[3]);
                set_if(ifindex, altif);
            } else {
                cons_printf("usage: host ifset index altif\n");
            }
        } else if (strncmp(cp, "freqset", 7) == 0) {
            if (argc > 3) {
                altif = atoi(argv[3]);
                set_freq(ifindex, altif);
            } else {
                cons_printf("usage: host volset index altif\n");
            }
        } else if (strncmp(cp, "muteset", 7) == 0) {
        } else if (strncmp(cp, "volset", 5) == 0) {
            if (argc > 3) {
                altif = atoi(argv[3]);
                set_vol(ifindex, altif);
            } else {
                cons_printf("usage: host volset index altif\n");
            }
        } else if (strncmp(cp, "muteset", 7) == 0) {
            if (argc > 3) {
                altif = atoi(argv[3]);
                set_mute(ifindex, altif);
            } else {
                cons_printf("usage: host muteset index altif\n");
            }
        }
        return;
    } else if (argc > 1) {
        cp = argv[1];
        if (strncmp(cp, "hcca", 4) == 0) {
            hcca_dump();
        } else if (strncmp(cp, "ed", 2) == 0) {
            ed_dump();
        } else if (strncmp(cp, "td", 2) == 0) {
            td_dump();
        } else if (strncmp(cp, "report", 6) == 0) {
            ReadReportDescriptor();
        } else if (strncmp(cp, "setup", 5) == 0) {
            usb_set_params();
        }
        return;
    }
}
#else
void host_command(int argc, char **argv)
{
    struct pcmb_desc *pcm = &pcmbufstate;

    syslog(LOG_NOTICE, "in_dma: %d, in_halt: %d", in_dma, in_halt);
    syslog(LOG_NOTICE, "copy_count: %d, pcm_frames: %d, iso_loop: %d",
        pcm->copy_count, pcm->frames, iso_loop);
    syslog(LOG_NOTICE, "HcControl: %x", HcControl);
}
#endif
