#ifndef usb_h
#define usb_h

#include "swis.h"
#include "stdint.h"

/*
 *  Class Specific (CS) descriptors
 */
#define USB_CS_UNDEFINED                    0x20
#define USB_CS_DEVICE_DESCRIPTOR            0x21
#define USB_CS_CONFIGURATION_DESCRIPTOR     0x22
#define USB_CS_STRING_DESCRIPTOR            0x23
#define USB_CS_INTERFACE_DESCRIPTOR         0x24
#define USB_CS_ENDPOINT_DESCRIPTOR          0x25

#define USB_AUDIO_CLASS                     0x1
#define USB_CDC_CONTROL_CLASS               0x2
#define USB_HID_CLASS                       0x3
#define USB_PHYSICAL_CLASS                  0x5
#define USB_IMAGE_CLASS                     0x6
#define USB_PRINTER_CLASS                   0x7
#define USB_MASS_STORAGE_CLASS              0x8
#define USB_HUB_CLASS                       0x9
#define USB_CDC_DATA_CLASS                  0xA
#define USB_SMART_CARD_CLASS                0xB
#define USB_CONTENT_SECURITY_CLASS          0xD
#define USB_VIDEO_CLASS                     0xE
#define USB_PERSONAL_HEALTHCARE_CLASS       0xF
#define USB_AUDIO_VIDEO_DEVICES_CLASS       0x10
#define USB_DIAGNOSTIC_DEVICE_CLASS         0xDC
#define USB_WIRELESS_CONTROLLER_CLASS       0xE0
#define USB_MISCELLANEOUS_CLASS             0xEF
#define USB_APPLICATION_SPECIFIC_CLASS      0xFE
#define USB_VENDOR_SPECIFIC_CLASS           0xFF



typedef struct USBDevice USBDevice;

#define USB_DescriptorBase \
    uint8_t             bLength;\
    uint8_t             bDescriptorType

#define USB_DescriptorSubBase \
    USB_DescriptorBase;\
    uint8_t             bDescriptorSubType

typedef __packed struct
{
    USB_DescriptorSubBase;
    uint8_t             data[];
} USB_Descriptor;

/*
 * Note: packed structures give error when passing to void 
 */

typedef __packed struct
{
    USB_DescriptorBase;
    uint16_t            bcdUSB;
    uint8_t             bDeviceClass;
    uint8_t             bDeviceSubClass;
    uint8_t             bDeviceProtocol;
    uint8_t             bMaxPacketSize;
    uint16_t            idVendor;
    uint16_t            idProduct;
    uint16_t            bcdDevice;
    uint8_t             iManufacturer;
    uint8_t             iProduct;
    uint8_t             iSerialNumber;
    uint8_t             bNumConfigurations;
} USB_DeviceDescriptor;
#define USB_DEVICE_DESCRIPTOR           1

typedef  __packed struct
{
    USB_DescriptorBase;
    uint16_t            wTotalLength;
    uint8_t             bNumInterfaces;
    uint8_t             bConfigurationValue;
    uint8_t             iConfiguration;
    uint8_t             bmAttributes;
    uint8_t             bMaxPower;
    USB_Descriptor*     data[];
} USB_ConfigurationDescriptor;
#define USB_CONFIGURATION_DESCRIPTOR    2

typedef __packed struct
{
    USB_DescriptorBase;
    uint16_t            str[1];
} USB_StringDescriptor;
#define USB_STRING_DESCRIPTOR           3

typedef __packed struct
{
    USB_DescriptorBase;
    uint8_t             bInterfaceNumber;
    uint8_t             bAlternateSetting;
    uint8_t             bNumEndpoints;
    uint8_t             bInterfaceClass;
    uint8_t             bInterfaceSubClass;
    uint8_t             bInterfaceProtocol;
    uint8_t             iInterface;
} USB_InterfaceDescriptor;
#define USB_INTERFACE_DESCRIPTOR        4

typedef __packed struct
{
    USB_DescriptorBase;
    uint8_t             bEndpointAddress;
    uint8_t             bmAttributes;
    uint16_t            wMaxPacketSize;
    uint8_t             bInterval;
} USB_EndpointDescriptor;
#define USB_ENDPOINT_DESCRIPTOR         5

typedef __packed struct
{
    USB_DescriptorBase;
    uint8_t             bFirstInterface;
    uint8_t             bInterfaceCount;
    uint8_t             bFunctionClass;
    uint8_t             bFunctionSubClass;
    uint8_t             bFunctionProtocol;
    uint8_t             iFunction;
} USB_InterfaceAssociationDescriptor;
#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR         0x0b


/* A USB device is completely described by the block (or list of blocks)
   pointed to by R2 in the USB service call
   The USBServiceCall includes a copy of the device descriptor, followed
   immediately by zero or more descriptors. A length byte of zero marks the
   end of the block, which is then padded with zeros to achieve word
   alignment */

typedef struct USB_ServiceCall
{
    uint16_t                sclen;          // sum length of the block including
                                            // the appended descriptors
    uint16_t                descOffset;     // offset to the descriptors
    char                    deviceName[20]; // device name as appears in DeviceFS
                                            // e.g.USBD37  NULL terminated
    uint8_t                 busNumber;      // bus number, 0-255
    uint8_t                 deviceAddress;  // usb address,1-127
    uint8_t                 hostAddress;    // usb address of upstream port 0-127
    uint8_t                 hostPort;       // port on host address
    uint8_t                 speed;          // device speed
                                            // 1:     low speed (1.5Mbps)
                                            // 2:     full speed (12Mbps)
                                            // 3:     high speed (480bps)
                                            // others:reserved
    uint8_t                 spare1;         // spare, set NULL
    uint8_t                 spare2;         // spare, set NULL
    uint8_t                 spare3;         // spare, set NULL
    USB_DeviceDescriptor    dDesc;          // device descriptor
                                            // followed immediately by zero or more
                                            // descriptors (with no word alignment
                                            // gaps). Followed by a 0 byte.
} USB_ServiceCall;

typedef struct USB_ConnectedDeviceList 
{
    struct USB_ConnectedDeviceList*      next;         // pointer to next, or NULL for no more
    USB_ServiceCall                      svc;          // data as per 'Attach' service call
}USB_ConnectedDeviceList;


#define \
  USB_control(device, bmRequestType, bRequest, wValue,wIndex,wLength,dataout)\
     _swix(DeviceFS_CallDevice,_INR(0,6),(1u<<31),(device),0,((bmRequestType) | ((bRequest)<<8) | ((wValue)<<16)),\
                                                                 ((wIndex) | ((wLength)<<16)),(dataout),0)


/*
 * use free to destroy descriptor after use
 * device may be the device driver's handle or a pointer to the device name.
 */
USB_StringDescriptor* USB_makeStringDescriptor(USBDevice* device,int index);

_kernel_oserror* USB_getDeviceHandle(const char* name, USBDevice** handle);
_kernel_oserror* USB_setNoPad(USBDevice* device,int fsHandle,int on);
_kernel_oserror* USB_setShort(USBDevice* device,int fsHandle,int on);

USB_ConnectedDeviceList* USB_makeConnectedDeviceList(void);
void                     USB_destroyConnectedDeviceList(USB_ConnectedDeviceList*);
USB_Descriptor*          USB_getDescriptorList(USB_ServiceCall* item);

#endif
