/*
* common USB definitions.
*/
#define dprint          if(debug)print
#define ddprint         if(debug>1)print
#define deprint         if(debug || ep->debug)print
#define ddeprint        if(debug>1 || ep->debug>1)print

#define GET2(p)         ((((p)[1]&0xFF)<<8)|((p)[0]&0xFF))
#define PUT2(p,v)       {((p)[0] = (v)); ((p)[1] = (v)>>8);}

typedef struct Udev Udev;       /* USB device */
typedef struct Ep Ep;           /* Endpoint */
typedef struct Hci Hci;         /* Host Controller Interface */
typedef struct Hciimpl Hciimpl; /* Link to the controller impl. */

enum
{
       /* fundamental constants */
       Ndeveps = 32,           /* max nb. of endpoints per device (16 in + 16 out) */

       /* tunable parameters */
       Nhcis   = 16,           /* max nb. of HCIs */
       Neps    = 64,           /* max nb. of endpoints */
       Maxctllen = 32*1024, /* max allowed sized for ctl. xfers; see Maxdevconf */
       Xfertmout = 2000,       /* default request time out (ms) */

       /* transfer types. keep this order */
       Tnone = 0,              /* no tranfer type configured */
       Tctl,                   /* wr req + rd/wr data + wr/rd sts */
       Tiso,                   /* stream rd or wr (real time) */
       Tbulk,                  /* stream rd or wr */
       Tintr,                  /* msg rd or wr */
       Nttypes,                /* number of transfer types */

       Epmax   = 0xF,          /* max ep. addr */
       Devmax  = 0x7F,         /* max dev. addr */

       /* Speeds */
       Fullspeed = 0,
       Lowspeed,
       Highspeed,
       Nospeed,
       Superspeed,

       /* request type */
       Rh2d = 0<<7,
       Rd2h = 1<<7,
       Rstd = 0<<5,
       Rclass =  1<<5,
       Rdev = 0,
       Rep = 2,
       Rother = 3,

       /* req offsets */
       Rtype   = 0,
       Rreq    = 1,
       Rvalue  = 2,
       Rindex  = 4,
       Rcount  = 6,
       Rsetuplen = 8,

       /* standard requests */
       Rgetstatus      = 0,
       Rclearfeature   = 1,
       Rsetfeature     = 3,
       Rsetaddr        = 5,
       Rgetdesc        = 6,

       /* device states */
       Dconfig  = 0,           /* configuration in progress */
       Denabled,               /* address assigned */
       Ddetach,                /* device is detached */
       Dreset,                 /* its port is being reset */

       /* (root) Hub reply to port status (reported to usbd) */
       HPpresent       = 0x1,
       HPenable        = 0x2,
       HPsuspend       = 0x4,
       HPovercurrent   = 0x8,
       HPreset         = 0x10,
       HPpower         = 0x100,
       HPslow          = 0x200,
       HPhigh          = 0x400,
       HPstatuschg     = 0x10000,
       HPchange        = 0x20000,
};

/*
* Services provided by the driver.
* epopen allocates hardware structures to prepare the endpoint
* for I/O. This happens when the user opens the data file.
* epclose releases them. This happens when the data file is closed.
* epwrite tries to write the given bytes, waiting until all of them
* have been written (or failed) before returning; but not for Iso.
* epread does the same for reading.
* It can be assumed that endpoints are DMEXCL but concurrent
* read/writes may be issued and the controller must take care.
* For control endpoints, device-to-host requests must be followed by
* a read of the expected length if needed.
* The port requests are called when usbd issues commands for root
* hubs. Port status must return bits as a hub request would do.
* Toggle handling and other details are left for the controller driver
* to avoid mixing too much the controller and the comon device.
* While an endpoint is closed, its toggles are saved in the Ep struct.
*/
struct Hciimpl
{
       void    *aux;                           /* for controller info */
       void    (*init)(Hci*);                  /* init. controller */
       void    (*dump)(Hci*);                  /* debug */
       void    (*interrupt)(Ureg*, void*);     /* service interrupt */
       void    (*epopen)(Ep*);                 /* prepare ep. for I/O */
       void    (*epclose)(Ep*);                /* terminate I/O on ep. */
       long    (*epread)(Ep*,void*,long);      /* transmit data for ep */
       long    (*epwrite)(Ep*,void*,long);     /* receive data for ep */
       char*   (*seprintep)(char*,char*,Ep*);  /* debug */
       int     (*portenable)(Hci*, int, int);  /* enable/disable port */
       int     (*portreset)(Hci*, int, int);   /* set/clear port reset */
       int     (*portstatus)(Hci*, int);       /* get port status */
       void    (*shutdown)(Hci*);              /* shutdown for reboot */
       void    (*debug)(Hci*, int);            /* set/clear debug flag */
};

struct Hci
{
       ISAConf;                                /* hardware info */
       int     tbdf;                           /* type+busno+devno+funcno */
       int     ctlrno;                         /* controller number */
       int     nports;                         /* number of ports in hub */
       int     highspeed;                      /* supports highspeed devices */
       uint    superspeed;                     /* bitmap of superspeed ports */
       Hciimpl;                                        /* HCI driver  */
};

/*
* USB endpoint.
* All endpoints are kept in a global array. The first
* block of fields is constant after endpoint creation.
* The rest is configuration information given to all controllers.
* The first endpoint for a device (known as ep0) represents the
* device and is used to configure it and create other endpoints.
* Its QLock also protects per-device data in dev.
* See Hciimpl for clues regarding how this is used by controllers.
*/
struct Ep
{
       Ref;                    /* one per fid (and per dev ep for ep0s) */

       /* const once inited. */
       int     idx;            /* index in global eps array */
       int     nb;             /* endpoint number in device */
       Hci*    hp;             /* HCI it belongs to */
       Udev*   dev;            /* device for the endpoint */
       Ep*     ep0;            /* control endpoint for its device */

       QLock;                  /* protect fields below */
       char*   name;           /* for ep file names at #u/ */
       int     inuse;          /* endpoint is open */
       int     mode;           /* OREAD, OWRITE, or ORDWR */
       int     clrhalt;        /* true if halt was cleared on ep. */
       int     debug;          /* per endpoint debug flag */
       char*   info;           /* for humans to read */
       long    maxpkt;         /* maximum packet size */
       int     ttype;          /* tranfer type */
       ulong   load;           /* in µs, for a transfer of maxpkt bytes */
       void*   aux;            /* for controller specific info */
       u64int  rhrepl;         /* fake root hub replies */
       int     toggle[2];      /* saved toggles (while ep is not in use) */
       long    pollival;       /* poll interval ([µ]frames; intr/iso) */
       long    hz;             /* poll frequency (iso) */
       long    samplesz;       /* sample size (iso) */
       int     ntds;           /* nb. of Tds per µframe */
       int     tmout;          /* 0 or timeout for transfers (ms) */
       int     sampledelay;    /* maximum delay introduced by buffering (iso) */
       int     uframes;        /* uframes mode (iso); 0 = normal behaviour, 1 = return only one uframe per read */
};

/*
* Per-device configuration and cached list of endpoints.
* eps[0]->QLock protects it.
*/
struct Udev
{
       int     nb;             /* USB device number */
       int     state;          /* state for the device */
       int     ishub;          /* hubs can allocate devices */
       int     isroot;         /* is a root hub */
       int     speed;          /* Full/Low/High/Super/No -speed */
       int     hub;            /* device address for the parent hub */
       int     port;           /* port number in the parent hub */
       int     addr;           /* device address */
       int     depth;          /* hub depth from root port */
       int     rootport;       /* port number on root hub */
       int     routestr;       /* route string */

       void    *aux;
       void    (*free)(void*);

       Ep*     eps[Ndeveps];   /* end points for this device (cached) */
};

void    addhcitype(char *type, int (*reset)(Hci*));

extern char *usbmodename[];
extern char Estalled[];

extern char *seprintdata(char*,char*,uchar*,int);