#ifndef USB_H
#define USB_H

//#include <inttypes.h>
//#include <stdbool.h>

#include "common/integer.h"
#include "timer.h"

/* NAK powers. To save space in endpoint data structure, amount of retries */
/* before giving up and returning 0x4 is stored in bmNakPower as a power of 2.*/
/* The actual nak_limit is then calculated as nak_limit = ( 2^bmNakPower - 1) */
#define USB_NAK_MAX_POWER 16	//NAK binary order maximum value
#define USB_NAK_DEFAULT	  14	//default 16K-1 NAKs before giving up
#define USB_NAK_NOWAIT	  1	//Single NAK stops transfer
#define USB_NAK_NONAK	  0	//Do not count NAKs, stop retrying after USB Timeout

typedef struct {
  uint8_t epAddr;	// Endpoint address 
  uint8_t maxPktSize;	// Maximum packet size
  
  union {
    uint8_t epAttribs;
    
    struct {
      // Send toggle, when zero bmSNDTOG0, bmSNDTOG1 otherwise
      uint8_t bmSndToggle: 1;
      // Send toggle, when zero bmRCVTOG0, bmRCVTOG1 otherwise
      uint8_t bmRcvToggle: 1;
      // Binary order for NAK_LIMIT value
      uint8_t bmNakPower:  6;
    };
  };
} ep_t;

#define USB_NUMDEVICES 8      // number of supported USB devices

/* Common setup data constant combinations  */
#define USB_REQ_GET_DESCR     USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE     //get descriptor request type
#define USB_REQ_SET           USB_SETUP_HOST_TO_DEVICE|USB_SETUP_TYPE_STANDARD|USB_SETUP_RECIPIENT_DEVICE     //set request type for all but 'set feature' and 'set interface'
#define USB_REQ_CL_GET_INTF   USB_SETUP_DEVICE_TO_HOST|USB_SETUP_TYPE_CLASS|USB_SETUP_RECIPIENT_INTERFACE     //get interface request type

#define USB_SETTLE_DELAY 200   // settle delay in milliseconds
#define USB_XFER_TIMEOUT 5000  // USB transfer timeout in milliseconds, per section 9.2.6.1 of USB 2.0 spec
#define USB_RETRY_LIMIT	3      // retry limit for a transfer

/* USB state machine states */
#define USB_STATE_MASK                                      0xf0

#define USB_STATE_DETACHED                                  0x10
#define USB_DETACHED_SUBSTATE_INITIALIZE                    0x11        
#define USB_DETACHED_SUBSTATE_WAIT_FOR_DEVICE               0x12
#define USB_DETACHED_SUBSTATE_ILLEGAL                       0x13
#define USB_ATTACHED_SUBSTATE_SETTLE                        0x20
#define USB_ATTACHED_SUBSTATE_RESET_DEVICE                  0x30    
#define USB_ATTACHED_SUBSTATE_WAIT_RESET_COMPLETE           0x40
#define USB_ATTACHED_SUBSTATE_WAIT_SOF                      0x50
#define USB_ATTACHED_SUBSTATE_GET_DEVICE_DESCRIPTOR_SIZE    0x60
#define USB_STATE_ADDRESSING                                0x70
#define USB_STATE_CONFIGURING                               0x80
#define USB_STATE_RUNNING                                   0x90

/* USB Setup Packet Structure   */
typedef struct {
  union {                          // offset   description
    uint8_t bmRequestType;         //   0      Bit-map of request type
    struct {
      uint8_t    recipient:  5;    //          Recipient of the request
      uint8_t    type:       2;    //          Type of request
      uint8_t    direction:  1;    //          Direction of data X-fer
    } __attribute__((packed));
  } __attribute__((packed)) ReqType_u;
  uint8_t    bRequest;		   //   1      Request
  uint8_t    wValueL;
  uint8_t    wValueH;
  uint8_t    wIndexL;              //   4      Depends on bRequest
  uint8_t    wIndexH;              //   5      Depends on bRequest
  uint8_t    wLengthL;             //   6      Depends on bRequest
  uint8_t    wLengthH;             //   7      Depends on bRequest
} __attribute__((packed)) setup_pkt_t;

// Additional Error Codes
#define USB_ERROR_INVALID_MAX_PKT_SIZE			    0xDA
#define USB_DEV_CONFIG_ERROR_DEVICE_NOT_SUPPORTED           0xDB
#define USB_ERROR_CONFIGURAION_SIZE_MISMATCH                0xDC
#define USB_ERROR_TRANSFER_TIMEOUT			    0xFF

struct usb_device_entry;

typedef struct {
  uint8_t (*init)(struct usb_device_entry *);
  uint8_t (*release)(struct usb_device_entry *);
  uint8_t (*poll)(struct usb_device_entry *);
} usb_device_class_config_t;

#include "hub.h"
#include "hid.h"

// entry used for list of connected devices
typedef struct usb_device_entry {
  const usb_device_class_config_t *class;  // pointer to class hadlers
  ep_t ep0;                            // information about endpoint 0
  uint8_t bAddress;	                   // device address
  uint8_t parent;                          // parent device address
  uint8_t port;
  bool lowspeed;
  char volatile * host_addr;

  union {
    usb_hub_info_t hub_info;
    usb_hid_info_t hid_info;
  };
} usb_device_t;

struct usb_host
{
	uint8_t usb_task_state;
        unsigned char * addr;
        msec_t poll;
        msec_t delay;
};

#define USB_CLASS_USE_CLASS_INFO          0x00    // Use Class Info in the Interface Descriptors
#define USB_CLASS_AUDIO                   0x01    // Audio
#define USB_CLASS_COM_AND_CDC_CTRL        0x02    // Communications and CDC Control
#define USB_CLASS_HID                     0x03    // HID
#define USB_CLASS_PHYSICAL                0x05    // Physical
#define USB_CLASS_IMAGE                   0x06    // Image
#define USB_CLASS_PRINTER                 0x07    // Printer
#define USB_CLASS_MASS_STORAGE            0x08    // Mass Storage
#define USB_CLASS_HUB                     0x09    // Hub
#define USB_CLASS_CDC_DATA                0x0a    // CDC-Data
#define USB_CLASS_SMART_CARD              0x0b    // Smart-Card
#define USB_CLASS_CONTENT_SECURITY        0x0d    // Content Security
#define USB_CLASS_VIDEO                   0x0e    // Video
#define USB_CLASS_PERSONAL_HEALTH         0x0f    // Personal Healthcare
#define USB_CLASS_DIAGNOSTIC_DEVICE       0xdc    // Diagnostic Device
#define USB_CLASS_WIRELESS_CTRL           0xe0    // Wireless Controller
#define USB_CLASS_MISC                    0xef    // Miscellaneous
#define USB_CLASS_APP_SPECIFIC            0xfe    // Application Specific
#define USB_CLASS_VENDOR_SPECIFIC         0xff    // Vendor Specific

/*******************************************************************************/
/***                                                                         ***/
/***                       USB chapter 9 structures                          ***/
/***                                                                         ***/
/*******************************************************************************/

/* Device descriptor structure */
typedef struct {
  uint8_t  bLength;            // Length of this descriptor.
  uint8_t  bDescriptorType;    // DEVICE descriptor type (USB_DESCRIPTOR_DEVICE).
  uint8_t bcdUSBL;	       // USB Spec Release Number (BCD).
  uint8_t bcdUSBH;	       // USB Spec Release Number (BCD).
  uint8_t  bDeviceClass;       // Class code (assigned by the USB-IF). 0xFF-Vendor specific.
  uint8_t  bDeviceSubClass;    // Subclass code (assigned by the USB-IF).
  uint8_t  bDeviceProtocol;    // Protocol code (assigned by the USB-IF). 0xFF-Vendor specific.
  uint8_t  bMaxPacketSize0;    // Maximum packet size for endpoint 0.
  uint8_t idVendorL;	       // Vendor ID (assigned by the USB-IF).
  uint8_t idVendorH;	       // Vendor ID (assigned by the USB-IF).
  uint8_t idProductL;	       // Product ID (assigned by the manufacturer).
  uint8_t idProductH;	       // Product ID (assigned by the manufacturer).
  uint8_t bcdDeviceL;	       // Device release number (BCD).
  uint8_t bcdDeviceH;	       // Device release number (BCD).
  uint8_t  iManufacturer;      // Index of String Descriptor describing the manufacturer.
  uint8_t  iProduct;           // Index of String Descriptor describing the product.
  uint8_t  iSerialNumber;      // Index of String Descriptor with the device's serial number.
  uint8_t  bNumConfigurations; // Number of possible configurations.
} __attribute__((packed)) usb_device_descriptor_t;

/* Configuration descriptor structure */
typedef struct {
  uint8_t bLength;             // Length of this descriptor.
  uint8_t bDescriptorType;     // CONFIGURATION descriptor type (USB_DESCRIPTOR_CONFIGURATION).
  uint8_t wTotalLengthL;       // Total length of all descriptors for this configuration.
  uint8_t wTotalLengthH;       // Total length of all descriptors for this configuration.
  uint8_t bNumInterfaces;      // Number of interfaces in this configuration.
  uint8_t bConfigurationValue; // Value of this configuration (1 based).
  uint8_t iConfiguration;      // Index of String Descriptor describing the configuration.
  uint8_t bmAttributes;        // Configuration characteristics.
  uint8_t bMaxPower;           // Maximum power consumed by this configuration.
} __attribute__((packed)) usb_configuration_descriptor_t;

/* Interface descriptor structure */
typedef struct {
  uint8_t bLength;               // Length of this descriptor.
  uint8_t bDescriptorType;       // INTERFACE descriptor type (USB_DESCRIPTOR_INTERFACE).
  uint8_t bInterfaceNumber;      // Number of this interface (0 based).
  uint8_t bAlternateSetting;     // Value of this alternate interface setting.
  uint8_t bNumEndpoints;         // Number of endpoints in this interface.
  uint8_t bInterfaceClass;       // Class code (assigned by the USB-IF).  0xFF-Vendor specific.
  uint8_t bInterfaceSubClass;    // Subclass code (assigned by the USB-IF).
  uint8_t bInterfaceProtocol;    // Protocol code (assigned by the USB-IF).  0xFF-Vendor specific.
  uint8_t iInterface;            // Index of String Descriptor describing the interface.
} __attribute__((packed)) usb_interface_descriptor_t;

/* Endpoint descriptor structure */
typedef struct {
  uint8_t bLength;               // Length of this descriptor.
  uint8_t bDescriptorType;       // ENDPOINT descriptor type (USB_DESCRIPTOR_ENDPOINT).
  uint8_t bEndpointAddress;      // Endpoint address. Bit 7 indicates direction (0=OUT, 1=IN).
  uint8_t bmAttributes;          // Endpoint transfer type.
  uint8_t wMaxPacketSize[2];     // Maximum packet size.
  uint8_t bInterval;             // Polling interval in frames.
} __attribute__((packed)) usb_endpoint_descriptor_t;

/* Standard Device Requests */
#define USB_REQUEST_GET_STATUS                  0       // Standard Device Request - GET STATUS
#define USB_REQUEST_CLEAR_FEATURE               1       // Standard Device Request - CLEAR FEATURE
#define USB_REQUEST_SET_FEATURE                 3       // Standard Device Request - SET FEATURE
#define USB_REQUEST_SET_ADDRESS                 5       // Standard Device Request - SET ADDRESS
#define USB_REQUEST_GET_DESCRIPTOR              6       // Standard Device Request - GET DESCRIPTOR
#define USB_REQUEST_SET_DESCRIPTOR              7       // Standard Device Request - SET DESCRIPTOR
#define USB_REQUEST_GET_CONFIGURATION           8       // Standard Device Request - GET CONFIGURATION
#define USB_REQUEST_SET_CONFIGURATION           9       // Standard Device Request - SET CONFIGURATION
#define USB_REQUEST_GET_INTERFACE               10      // Standard Device Request - GET INTERFACE
#define USB_REQUEST_SET_INTERFACE               11      // Standard Device Request - SET INTERFACE
#define USB_REQUEST_SYNCH_FRAME                 12      // Standard Device Request - SYNCH FRAME

#define USB_FEATURE_ENDPOINT_HALT               0       // CLEAR/SET FEATURE - Endpoint Halt
#define USB_FEATURE_DEVICE_REMOTE_WAKEUP        1       // CLEAR/SET FEATURE - Device remote wake-up
#define USB_FEATURE_TEST_MODE                   2       // CLEAR/SET FEATURE - Test mode

/* Setup Data Constants */
#define USB_SETUP_HOST_TO_DEVICE                0x00    // Device Request bmRequestType transfer direction - host to device transfer
#define USB_SETUP_DEVICE_TO_HOST                0x80    // Device Request bmRequestType transfer direction - device to host transfer
#define USB_SETUP_TYPE_STANDARD                 0x00    // Device Request bmRequestType type - standard
#define USB_SETUP_TYPE_CLASS                    0x20    // Device Request bmRequestType type - class
#define USB_SETUP_TYPE_VENDOR                   0x40    // Device Request bmRequestType type - vendor
#define USB_SETUP_RECIPIENT_DEVICE              0x00    // Device Request bmRequestType recipient - device
#define USB_SETUP_RECIPIENT_INTERFACE           0x01    // Device Request bmRequestType recipient - interface
#define USB_SETUP_RECIPIENT_ENDPOINT            0x02    // Device Request bmRequestType recipient - endpoint
#define USB_SETUP_RECIPIENT_OTHER               0x03    // Device Request bmRequestType recipient - other

/* USB descriptors  */
#define USB_DESCRIPTOR_DEVICE           0x01    // bDescriptorType for a Device Descriptor.
#define USB_DESCRIPTOR_CONFIGURATION    0x02    // bDescriptorType for a Configuration Descriptor.
#define USB_DESCRIPTOR_STRING           0x03    // bDescriptorType for a String Descriptor.
#define USB_DESCRIPTOR_INTERFACE        0x04    // bDescriptorType for an Interface Descriptor.
#define USB_DESCRIPTOR_ENDPOINT         0x05    // bDescriptorType for an Endpoint Descriptor.
#define USB_DESCRIPTOR_DEVICE_QUALIFIER 0x06    // bDescriptorType for a Device Qualifier.
#define USB_DESCRIPTOR_OTHER_SPEED      0x07    // bDescriptorType for a Other Speed Configuration.
#define USB_DESCRIPTOR_INTERFACE_POWER  0x08    // bDescriptorType for Interface Power.

void usb_init(struct usb_host * host, int portnumber);
void usb_poll(struct usb_host * host);

uint8_t usb_set_addr( usb_device_t *, uint8_t );
uint8_t usb_ctrl_req( usb_device_t *, uint8_t bmReqType, 
		      uint8_t bRequest, uint8_t wValLo, uint8_t wValHi, 
		      uint16_t wInd, uint16_t nbytes, uint8_t* dataptr);
uint8_t usb_get_dev_descr( usb_device_t *, uint16_t nbytes, usb_device_descriptor_t* dataptr );
uint8_t usb_get_conf_descr( usb_device_t *, uint16_t nbytes, uint8_t conf, usb_configuration_descriptor_t* dataptr );
uint8_t usb_set_conf( usb_device_t *dev, uint8_t conf_value );
uint8_t usb_in_transfer( usb_device_t *, ep_t *ep, uint16_t *nbytesptr, uint8_t* data);
uint8_t usb_out_transfer( usb_device_t *, ep_t *ep, uint16_t nbytes, uint8_t* data );
uint8_t usb_release_device(uint8_t parent, uint8_t port);
usb_device_t *usb_get_devices();

#endif // USB_H
