/**
 * @file hv_drv_UsbCoreUsb.c
 * @brief usb core driver
 * @details This module provide interfaces for usb core.
 * @author HiView SoC Software Team
 * @version 0.0.1
 * @date 20230417
 * @copyright Copyright(c),2022-9, Hiview Software. All rights reserved.
 * @par History:
 * <table>
 * <tr><th>Author                         <th>Date             <th>Details
 * <tr><td>HiView SoC Software Team   <td>20230417       <td>Create and first Version.
 * </table>
 **/

#include "hv_drv_UsbCoreCommonUsb.h"
#include "hv_drv_UsbCoreDevice.h"
#include "hv_drv_UsbCoreHcd.h"
#include "hv_drv_UsbCoreUsb.h"

static int nousb = 0;

/**
 * for external read access to <nousb>
 */
int usb_disabled(void) {
    return nousb;
}

struct usb_interface *usb_ifnum_to_if(const struct usb_device *dev,
                                      unsigned ifnum) {
    struct usb_host_config *config = dev->actconfig;
    int i;

    if (!config)
        return NULL;
    for (i = 0; i < config->desc.bNumInterfaces; i++)
        if (config->interface[i]->altsetting[0].desc.bInterfaceNumber == ifnum)
            return config->interface[i];

    return NULL;
}

struct usb_host_interface *usb_altnum_to_altsetting(
    const struct usb_interface *intf,
    unsigned int altnum) {
    unsigned int i;

    for (i = 0; i < intf->num_altsetting; i++) {
        if (intf->altsetting[i].desc.bAlternateSetting == altnum)
            return &intf->altsetting[i];
    }
    return NULL;
}

int usb_lock_device_for_reset(struct usb_device *udev,
                              const struct usb_interface *iface) {
    if (udev->state == USB_STATE_NOTATTACHED) {
        return -ENODEV;
    }
    if (udev->state == USB_STATE_SUSPENDED) {
        return -EHOSTUNREACH;
    }
    if (iface && (iface->condition == USB_INTERFACE_UNBINDING ||
                  iface->condition == USB_INTERFACE_UNBOUND)) {
        return -EINTR;
    }

    /* try lock device */
    return 0;
}

struct usb_device *usb_alloc_dev(struct usb_device *parent,
                                 struct usb_bus *bus, unsigned port1) {
    struct usb_device *dev;
    struct usb_hcd *hcd = bus_to_hcd(bus);

    dev = (struct usb_device *)USB_MALLOC(sizeof(*dev));
    if (dev == NULL) {
        return NULL;
    }

    HV_MEMSET((void*)&dev->ep0, 0, sizeof(struct usb_host_endpoint));
    HV_MEMSET((void*)dev, 0, sizeof(struct usb_device));

    device_initialize(&dev->dev);
    dev->dev.bus = &usb_bus_type;
    dev->dev.type = &usb_device_type;
    dev->state = USB_STATE_ATTACHED;

    dev->ep0.desc.bLength = USB_DT_ENDPOINT_SIZE;
    dev->ep0.desc.bDescriptorType = USB_DT_ENDPOINT;

    usb_enable_endpoint(dev, &dev->ep0, false);
    dev->can_submit = 1;
    dev->ep0.hcpriv = NULL;

    if (!parent) {  /* root hub */
        dev->dev.parent = bus->controller;
        dev->authorized = 1;
    } else {        /* hub or single device */
        dev->authorized = hcd->authorized_default;
        dev->dev.parent = &parent->dev;
    }

    dev->portnum = port1;
    dev->bus = bus;
    dev->parent = parent;

    dev->product = NULL;
    dev->manufacturer = NULL;
    dev->serial = NULL;

    return dev;
}

static void usb_release_dev(struct device *dev) {
    struct usb_device *udev;
    udev = to_usb_device(dev);

    usb_destroy_configuration(udev);

    if(udev->product != NULL)
    {
        USB_FREE(udev->product);
        udev->product = NULL;
    }
    if(udev->manufacturer != NULL)
    {
        USB_FREE(udev->manufacturer);
        udev->manufacturer = NULL;
    }
    if(udev->serial != NULL)
    {
        USB_FREE(udev->serial);
        udev->serial = NULL;
    }
    if(udev != NULL)
    {
        USB_FREE(udev);
        udev = NULL;
    }
}

struct device_type usb_device_type = {
    "usb_device",    //name
    usb_release_dev, //release
};

int usb_init(void) {
    bus_register(&usb_bus_type);
    usb_register_device_driver(&usb_generic_driver);
    /* register interface driver */
    usb_hub_init();
    return 0;
}

int usb_exit(void) {
    usb_unregister_device_driver(&usb_generic_driver);

    /* Remove hub */
    //usb_hub_deinit();
    return 0;
}

const char *s_str_usb_req[] = {
    "REQ_GET_STATUS",           //0
    "REQ_CLEAR_FEATURE",        //1
    "REQ_2",                    //2
    "REQ_SET_FEATURE",          //3
    "REQ_4",                    //4
    "REQ_SET_ADDRESS",          //5
    "REQ_GET_DESCRIPTOR",       //6
    "REQ_SET_DESCRIPTOR",       //7
    "REQ_GET_CONFIGURATION",    //8
    "REQ_SET_CONFIGURATION",    //9
    "REQ_GET_INTERFACE",        //A
    "REQ_SET_INTERFACE",        //B
    "REQ_SYNCH_FRAME",          //C
    "REQ_SET_ENCRYPTION,"       //D
    "REQ_GET_ENCRYPTION,"       //E
    //"REQ_RPIPE_ABORT,"        //E
    "REQ_SET_HANDSHAKE,"        //F
    //"REQ_RPIPE_RESET,"        //F
    "REQ_GET_HANDSHAKE,"        //10
    "REQ_SET_CONNECTION,"       //11
    "REQ_SET_SECURITY_DATA,"    //12
    "REQ_GET_SECURITY_DATA,"    //13
    "REQ_SET_WUSB_DATA,"        //14
    "REQ_LOOPBACK_DATA_WRITE,"  //15
    "REQ_LOOPBACK_DATA_READ,"   //16
    "REQ_SET_INTERFACE_DS,"     //17
};

const char *usb_str_request(u8 request) {
    if (request == 0xFE) {
        return "US_BULK_GET_MAX_LUN";
    } else if (request == 0xFF) {
        return "US_BULK_RESET_REQUEST";
    } else {
        USB_ASSERT(request <= sizeof(s_str_usb_req) / sizeof(s_str_usb_req[0]));
        return s_str_usb_req[request];
    }
}

const char *usb_str_request_type(u8 requesttype) {
    switch (requesttype) {
    case USB_DIR_IN:
        return "DIR_IN";
    case USB_DIR_OUT:
        return "DIR_OUT";
    case USB_DIR_IN | USB_RT_HUB:
        return "DIR_IN | RT_HUB";
    case USB_DIR_OUT | USB_RT_PORT:
        return "DIR_OUT | RT_PORT";
    case USB_DIR_IN | USB_RT_PORT:
        return "DIR_IN | RT_PORT";
    default:
        USB_ASSERT(false);
    }
    return "";
}
