/**
 * @file hv_drv_UsbCoreGeneric.c
 * @brief usb core generic driver
 * @details This module provide interfaces for usb core generic.
 * @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_UsbCoreDevice.h"
#include "hv_drv_UsbCoreCommonUsb.h"

#include "hv_drv_UsbCoreHcd.h"
#include "hv_drv_UsbCoreUsb.h"

int usb_choose_configuration(struct usb_device *udev) {
    int i = 0;
    int num_configs;
    struct usb_host_config *c, *best;

    best = NULL;
    c = udev->config;
    num_configs = udev->descriptor.bNumConfigurations;

    for (i = 0; i < num_configs; (i++, c++)) {
        struct usb_interface_descriptor *desc = NULL;
        if (c->desc.bNumInterfaces > 0) {
            desc = &c->intf_cache[0]->altsetting->desc;
        }

        if (udev->descriptor.bDeviceClass != USB_CLASS_VENDOR_SPEC
                && (desc && desc->bInterfaceClass != USB_CLASS_VENDOR_SPEC)) {
            best = c;
            break;
        } else if (!best) {
            best = c;
        }
    }

    if (best) {
        i = best->desc.bConfigurationValue;
    } else {
        i = -1;
        USB_LOGE("no configuration chosen from %d.\n", num_configs);
    }

    return i;
}

static int generic_probe(struct usb_device *udev) {
    int  err, c;
    if (udev->authorized == 0) {
        USB_LOGE("%s, Device is not authorized for usage.\n", __FUNCTION__);
    } else {
        c = usb_choose_configuration(udev);
        if (c >= 0) {
            USB_LOGV("generic_probe:usb_set_configuration.\n");
            err = usb_set_configuration(udev, c);
            USB_LOGV("generic_probe:usb_set_configuration finished.\n");
            if (err) {
                USB_LOGE("Can not set config %d, error %d.\n", c, err);
            }
        }
    }

    return 0;
}

static void generic_disconnect(struct usb_device *udev) {
    if (udev && udev->actconfig) {
        usb_set_configuration(udev, -1);
    }
}

struct usb_device_driver usb_generic_driver = {
    "usb generic",      //name
    generic_probe,      //probe
    generic_disconnect, //disconnect
};
