#include <ddk.h>
#include <mutex.h>
#include <pci.h>
#include <linux/dmapool.h>
#include <linux/string.h>
#include <syscall.h>
#include "usb.h"

int __stdcall srv_usb(ioctl_t *io);

bool init_hc(hc_t *hc);

static slab_t   qh_slab;

LIST_HEAD( hc_list );
LIST_HEAD( newdev_list );
LIST_HEAD( rq_list );

u32_t drvEntry(int action, char *cmdline)
{
    u32_t   retval;
    hc_t   *hc;
    udev_t *dev;

    int     i;

    if(action != 1)
        return 0;

    if(!dbg_open("/rd/1/drivers/usb.log"))
    {
        printf("Can't open /rd/1/drivers/usb.log\nExit\n");
        return 0;
    }

    if( !FindUSBControllers() ) {
        dbgprintf("no uhci devices found\n");
        return 0;
    };

    hcd_buffer_create();

    qh_slab.available = 256;
    qh_slab.start     = KernelAlloc(4096);
    qh_slab.nextavail = (addr_t)qh_slab.start;
    qh_slab.dma       = GetPgAddr(qh_slab.start);

    qh_t    *p;
    addr_t  dma;

    for (i = 0, p = (qh_t*)qh_slab.start, dma = qh_slab.dma;
         i < 256; i++, p++, dma+= sizeof(qh_t))
    {
       p->qlink  = (addr_t)(p+1);
       p->qelem  = 1;
       p->dma    = dma;
       p->r1     = 0;
    };

    hc = (hc_t*)hc_list.next;

    while( &hc->list != &hc_list)
    {
        hc_t *tmp = hc;
        hc = (hc_t*)hc->list.next;

        if( !init_hc(tmp))
            list_del(&tmp->list);
    };

    dbgprintf("\n");

    dev = (udev_t*)newdev_list.next;
    while( &dev->list != &newdev_list)
    {
        udev_t *tmp = dev;
        dev = (udev_t*)dev->list.next;

        if(tmp->id != 0)
            init_device(tmp);
    }

    while(1)
    {
        udev_t    *dev;
        request_t *rq;
        kevent_t    event;
        u32_t       handle;

        event.code    = 0;
        event.data[0] = 0;

        handle = GetEvent(&event);

//        dbgprintf("event handle 0x%0x code 0x%0x\n",
//                   handle, event.code);

        if(event.code != 0xFF000001)
            continue;

        rq = (request_t*)event.data[0];

//        dbgprintf("rq = 0x%0x\n", rq);

        rq->handler(rq->dev, rq);
    };

    retval = RegService("USB", srv_usb);
    dbgprintf("reg service USB as: %x\n", retval);

    return retval;
};


#define API_VERSION     0x01000100

#define SRV_GETVERSION  0


int __stdcall srv_usb(ioctl_t *io)
{
  u32_t *inp;
  u32_t *outp;

  inp = io->input;
  outp = io->output;

  switch(io->io_code)
  {
    case SRV_GETVERSION:
      if(io->out_size==4)
      {
        *outp = API_VERSION;
        return 0;
      }
      break;


    default:
      return ERR_PARAM;
  };
  return ERR_PARAM;
}


static qh_t* alloc_qh()
{
    if( qh_slab.available )
    {
        qh_t *qh;

        qh_slab.available--;
        qh = (qh_t*)qh_slab.nextavail;
        qh_slab.nextavail = qh->qlink;
        return qh;
     }
     return NULL;
};

static void  free_qh(qh_t *qh)
{
     qh->qlink = qh_slab.nextavail;
     qh_slab.nextavail = (addr_t)qh;
     qh_slab.available++;
};


#include "pci.inc"
#include "detect.inc"
#include "hcd.inc"
#include "hid.inc"