/**
 * @file hv_drv_UsbCoreMessage.c
 * @brief usb core message driver
 * @details This module provide interfaces for usb core message.
 * @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_UsbCoreUsb.h"
#include "hv_drv_UsbCoreQuirks.h"
#include "hv_drv_UsbCoreHcd.h"

static int utf16s_to_utf8s(const u16 *pwcs, int inlen, u8 *s, int maxout);

static void usb_api_blocking_completion(struct urb *urb) {
    completion_complete((completion *) urb->context);
}

/*
 * Starts urb and waits for completion or timeout. Note that this call
 * is NOT interruptible. Many device driver i/o requests should be
 * interruptible and therefore these drivers should implement their
 * own interruptible routines.
 */
int usb_start_wait_urb(struct urb *urb, int timeout, int *actual_length) {
    int retval = 0;
    completion done;
    boolean waited;

    done.isr_caller = is_root_hub(urb->dev)?0:1;
    init_completion(&done);

    urb->context = &done;
    urb->actual_length = 0;
    retval = usb_submit_urb(urb);
    if (unlikely(retval)) {
        goto out;
    }

    waited = wait_for_completion_timeout(&done, timeout);

    retval = (urb->status == -ENOENT ? -ETIMEDOUT : urb->status);
    if (waited) {
        retval = usb_kill_urb(urb);
    }

out:
    destroy_complete(&done);
    if (actual_length)
        *actual_length = urb->actual_length;

    usb_free_urb(urb);
    return retval;
}

/*-------------------------------------------------------------------*/
/* returns status (negative) or length (positive) */
static int usb_internal_control_msg(struct usb_device *usb_dev,
                                    unsigned int pipe,
                                    struct usb_ctrlrequest *cmd,
                                    void *data, int len, int timeout) {
    struct urb *urb;
    int retv = 0;
    int length = 0;

    urb = usb_alloc_urb();
    if (!urb)
        return -ENOMEM;

    usb_fill_control_urb(urb, usb_dev, pipe, (unsigned char *) cmd, data,
                         len, usb_api_blocking_completion, NULL);

    retv = usb_start_wait_urb(urb, timeout, &length);

    if (retv < 0)
        return retv;
    else
        return length;
}

int usb_control_msg(struct usb_device *dev, unsigned int pipe, u8 request,
                    u8 requesttype, u16 value, u16 index, void *data,
                    u16 size, int timeout) {
    struct usb_ctrlrequest dr;

    dr.bRequestType = requesttype;
    dr.bRequest = request;
    dr.wValue = value;
    dr.wIndex = index;
    dr.wLength = size;

    return usb_internal_control_msg(dev, pipe, &dr, data, size, timeout);
}

int usb_get_descriptor(struct usb_device *dev, unsigned char type,
                       unsigned char index, void *buf, int size) {
    int i;
    int result;

    HV_MEMSET(buf, 0, size);   /* Make sure we parse really received data */

    for (i = 0; i < 3; ++i) {
        /* retry on length 0 or error; some devices are flakey */
        result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                                 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                                 (type << 8) + index, 0, buf, size,
                                 USB_CTRL_GET_TIMEOUT);

        if (result == -ENODEV || result == -EIO) {
            return result;
        }

        if (result <= 0 && result != -ETIMEDOUT) {
            continue;
        }

        if (result > 1 && ((u8 *)buf)[1] != type) {
            result = -ENODATA;
            continue;
        }
        break;
    }
    return result;
}

static int usb_get_string(struct usb_device *dev, unsigned short langid,
                          unsigned char index, void *buf, int size) {
    int i;
    int result;

    for (i = 0; i < 3; ++i) {
        /* retry on length 0 or stall; some devices are flakey */
        result = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                                 USB_REQ_GET_DESCRIPTOR, USB_DIR_IN,
                                 (USB_DT_STRING << 8) + index, langid, buf, size,
                                 USB_CTRL_GET_TIMEOUT);

        if (result == -EIO
                || result == -ENODEV
                || result == -ETIMEDOUT) {
            return result;
        }

        if (result == 0 || result == -EPIPE)
            continue;
        if (result > 1 && ((u8 *) buf)[1] != USB_DT_STRING) {
            result = -ENODATA;
            continue;
        }

        break;
    }
    return result;
}

static void usb_try_string_workarounds(unsigned char *buf, int *length) {
    int newlength, oldlength = *length;

    for (newlength = 2; newlength + 1 < oldlength; newlength += 2) {
        if (buf[newlength + 1])
            break;
    }

    if (newlength > 2) {
        buf[0] = newlength;
        *length = newlength;
    }
}
static int usb_string_sub(struct usb_device *dev, unsigned int langid,
                          unsigned int index, unsigned char *buf) {
    int rc;

    if (!dev->is_connect) {
        return -EINVAL;
    }

    rc = usb_get_string(dev, langid, index, buf, 255);

    /* If that failed try to read the descriptor length, then
    * ask for just that many bytes */
    if (rc < 2) {
        rc = usb_get_string(dev, langid, index, buf, 2);

        if (rc == 2)
            rc = usb_get_string(dev, langid, index, buf, buf[0]);
    }

    if (rc >= 2) {
        if (!buf[0] && !buf[1])
            usb_try_string_workarounds(buf, &rc);

        /* There might be extra junk at the end of the descriptor */
        if (buf[0] < rc)
            rc = buf[0];

        rc = rc - (rc & 1); /* force a multiple of two */
    }

    if (rc < 2)
        rc = (rc < 0 ? rc : -EINVAL);

    return rc;
}

static int usb_get_langid(struct usb_device *dev, unsigned char *tbuf) {
    int err;

    if (dev->have_langid)
        return 0;

    if (dev->string_langid < 0)
        return -EPIPE;

    err = usb_string_sub(dev, 0, 0, tbuf);

    /* If the string was reported but is malformed, default to english
    * (0x0409) */
    if (err == -ENODATA || (err > 0 && err < 4)) {
        dev->string_langid = 0x0409;
        dev->have_langid = 1;
        return 0;
    }

    /* In case of all other errors, we assume the device is not able to
    * deal with strings at all. Set string_langid to -1 in order to
    * prevent any string to be retrieved from the device */
    if (err < 0) {
        dev->string_langid = -1;
        return -EPIPE;
    }

    /* always use the first langid listed */
    dev->string_langid = tbuf[2] | (tbuf[3] << 8);
    dev->have_langid = 1;
    return 0;
}

int usb_string(struct usb_device *dev, int index, char *buf, size_t size) {
    unsigned char tbuf[256];
    int err;

    if (!dev->is_connect) {
        return -ENODEV;
    }

    if (dev->state == USB_STATE_SUSPENDED)
        return -EHOSTUNREACH;

    if (size <= 0 || !buf || !index)
        return -EINVAL;

    buf[0] = 0;
    HV_MEMSET(tbuf, 0, sizeof(tbuf));

    err = usb_get_langid(dev, tbuf);

    if (err < 0)
        goto errout;

    err = usb_string_sub(dev, dev->string_langid, index, tbuf);

    if (err < 0)
        goto errout;

    size--;     /* leave room for trailing NULL char in output buffer */
    err = utf16s_to_utf8s((u16 *) &tbuf[2], (err - 2) / 2, (u8*)buf, size);
    buf[err] = 0;

    if (tbuf[1] != USB_DT_STRING)
        USB_LOGV("wrong descriptor type %02x for string %d (\"%s\")\n",
                tbuf[1], index, buf);

errout:
    return err;
}

/* one UTF-8-encoded 16-bit character has at most three bytes */
#define MAX_USB_STRING_SIZE (127 * 3 + 1)

/**
 * usb_cache_string - read a string descriptor and cache it for later use
 * @udev: the device whose string descriptor is being read
 * @index: the descriptor index
 *
 * Returns a pointer to a kmalloc'ed buffer containing the descriptor string,
 * or NULL if the index is 0 or the string could not be read.
 */
char *usb_cache_string(struct usb_device *usb_dev, int index) {
    char buf[MAX_USB_STRING_SIZE];
    char *smallbuf = NULL;
    int len;

    if (!usb_dev->is_connect) {
        return NULL;
    }

    if (index <= 0)
        return NULL;

    HV_MEMSET(buf, 0, sizeof(buf));

    len = usb_string(usb_dev, index, buf, MAX_USB_STRING_SIZE);

    if (len > 0) {
        smallbuf = (char *)USB_MALLOC(++len);

        if (!smallbuf)
            return NULL;

        USB_MEMCPY(smallbuf, buf, len);
    }

    return smallbuf;
}

int usb_get_device_descriptor(struct usb_device *dev, unsigned int size) {
    struct usb_device_descriptor *desc;
    int ret;

    desc = &dev->descriptor;

    if (size > sizeof(*desc)) {
        return -EINVAL;
    }

    HV_MEMSET(desc, 0, sizeof(*desc));
    ret = usb_get_descriptor(dev, USB_DT_DEVICE, 0, desc, size);

    if (ret < 0)
        HV_MEMSET(&dev->descriptor, 0, size);

    return ret;
}

/**
 * usb_get_status - issues a GET_STATUS call
 * @dev: the device whose status is being checked
 * @type: USB_RECIP_*; for device, interface, or endpoint
 * @target: zero (for device), else interface or endpoint number
 * @data: pointer to two bytes of bitmap data
 * Context: !in_interrupt ()
 *
 * Returns device, interface, or endpoint status.  Normally only of
 * interest to see if the device is self powered, or has enabled the
 * remote wakeup facility; or whether a bulk or interrupt endpoint
 * is halted ("stalled").
 *
 * Bits in these status bitmaps are set using the SET_FEATURE request,
 * and cleared using the CLEAR_FEATURE request.  The usb_clear_halt()
 * function should be used to clear halt ("stall") status.
 *
 * This call is synchronous, and may not be used in an interrupt context.
 *
 * Returns the number of bytes received on success, or else the status code
 * returned by the underlying usb_control_msg() call.
 */
int usb_get_status(struct usb_device *dev, int type, int target, void *data) {
    int ret;
    u16 status;

    ret = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0),
                          USB_REQ_GET_STATUS, USB_DIR_IN | type, 0, target, &status,
                          sizeof(status), USB_CTRL_GET_TIMEOUT);

    if (ret == 2) {
        *(u16 *)data = status;
        ret = 0;
    } else if (ret >= 0) {
        ret = -EIO;
    }
    return ret;
}

int usb_clear_halt(struct usb_device *dev, int pipe) {
    int result;
    int endp = usb_pipeendpoint(pipe);

    if (usb_pipein(pipe))
        endp |= USB_DIR_IN;

    /* we don't care if it wasn't halted first. in fact some devices
     * (like some ibmcam model 1 units) seem to expect hosts to make
     * this request for iso endpoints, which can't halt!
     */
    result = usb_control_msg(dev, usb_sndctrlpipe(dev, 0),
                             USB_REQ_CLEAR_FEATURE, USB_RECIP_ENDPOINT,
                             USB_ENDPOINT_HALT, endp, NULL, 0,
                             USB_CTRL_SET_TIMEOUT);

    /* don't un-halt or force to DATA0 except on success */
    if (result < 0)
        return result;

    usb_reset_endpoint(dev, endp);

    return 0;
}

/* TODO!! */
void usb_disable_endpoint(struct usb_device *dev, unsigned int epaddr,
                          boolean reset_hardware) {
    unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
    struct usb_host_endpoint *ep;

    if (!dev)
        return;

    if (usb_endpoint_out(epaddr)) {
        ep = dev->ep_out[epnum];
        if (reset_hardware)
            dev->ep_out[epnum] = NULL;
    } else {
        ep = dev->ep_in[epnum];
        if (reset_hardware)
            dev->ep_in[epnum] = NULL;
    }

    if (ep) {
        ep->enabled = 0;
    }
}

/**
 * usb_reset_endpoint - Reset an endpoint's state.
 * @dev: the device whose endpoint is to be reset
 * @epaddr: the endpoint's address.  Endpoint number for output,
 *  endpoint number + USB_DIR_IN for input
 *
 * Resets any host-side endpoint state such as the toggle bit,
 * sequence number or current window.
 */
void usb_reset_endpoint(struct usb_device *dev, unsigned int epaddr) {
    unsigned int epnum = epaddr & USB_ENDPOINT_NUMBER_MASK;
    struct usb_host_endpoint *ep;

    if (usb_endpoint_out(epaddr))
        ep = dev->ep_out[epnum];
    else
        ep = dev->ep_in[epnum];

    if (ep) {
        usb_hcd_reset_endpoint(dev, ep);
    }
}

/**
 * usb_disable_interface -- Disable all endpoints for an interface
 * @dev: the device whose interface is being disabled
 * @intf: pointer to the interface descriptor
 * @reset_hardware: flag to erase any endpoint state stored in the
 *  controller hardware
 *
 * Disables all the endpoints for the interface's current altsetting.
 */
void usb_disable_interface(struct usb_device *dev, struct usb_interface *intf,
                           boolean reset_hardware) {
    struct usb_host_interface *alt = intf->cur_altsetting;
    int i;

    for (i = 0; i < alt->desc.bNumEndpoints; ++i) {
        usb_disable_endpoint(dev, alt->endpoint[i].desc.bEndpointAddress,
                             reset_hardware);
    }
}

void usb_enable_endpoint(struct usb_device *dev, struct usb_host_endpoint *ep, boolean reset_ep) {
    int epnum = usb_endpoint_num(&ep->desc);
    int is_out = usb_endpoint_dir_out(&ep->desc);
    int is_control = usb_endpoint_xfer_control(&ep->desc);

    ep->hcpriv = NULL;
    
    if (reset_ep)
        usb_hcd_reset_endpoint(dev, ep);
    if (is_out || is_control)
        dev->ep_out[epnum] = ep;
    if (!is_out || is_control)
        dev->ep_in[epnum] = ep;
    ep->enabled = 1;
    
}


/*
 * usb_enable_interface - Enable all the endpoints for an interface
 * @dev: the device whose interface is being enabled
 * @intf: pointer to the interface descriptor
 * @reset_eps: flag to reset the endpoints' state
 *
 * Enables all the endpoints for the interface's current altsetting.
 */
void usb_enable_interface(struct usb_device *udev,
                          struct usb_interface *intf, boolean reset_eps) {
    struct usb_host_interface *alt = intf->cur_altsetting;
    int i;

    for (i = 0; i < alt->desc.bNumEndpoints; ++i)
        usb_enable_endpoint(udev, &alt->endpoint[i], reset_eps);
}

void usb_disable_device(struct usb_device *udev, int skip_ep0) {
    int i = 0;
    if (udev->actconfig) {
        for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
            udev->actconfig->interface[i]->unregistering = 1;
        }

        for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
            struct usb_interface    *interface;

            /* remove this interface if it has been registered */
            interface = udev->actconfig->interface[i];
            if (!device_is_registered(&interface->dev))
                continue;
            device_del(&interface->dev);
        }

        /* Now that the interfaces are unbound, nobody should
         * try to access them.
         */
        for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
            put_device(&udev->actconfig->interface[i]->dev);
            udev->actconfig->interface[i] = NULL;
        }

        udev->actconfig = NULL;
        if (udev->state == USB_STATE_CONFIGURED)
            usb_set_device_state(udev, USB_STATE_ADDRESS);
    }

    for (i = skip_ep0; i < 16; ++i) {
        usb_disable_endpoint(udev, i, true);
        usb_disable_endpoint(udev, i + USB_DIR_IN, true);
    }
}

int usb_set_configuration(struct usb_device *udev, int configuration) {
    int i, ret;
    struct usb_host_config *cp = NULL;
    struct usb_interface **new_interfaces = NULL;
    int n, nintf;

    if (udev->authorized == 0 || configuration == -1)
        configuration = 0;
    else {
        for (i = 0; i < udev->descriptor.bNumConfigurations; i++) {
            if (udev->config[i].desc.bConfigurationValue ==
                    configuration) {
                cp = &udev->config[i];
                break;
            }
        }
    }
    if ((!cp && configuration != 0))
        return -EINVAL;

    /* The USB spec says configuration 0 means unconfigured.
     * But if a device includes a configuration numbered 0,
     * we will accept it as a correctly configured state.
     * Use -1 if you really want to unconfigure the device.
     */
    if (cp && configuration == 0)
        USB_LOGV("config 0 descriptor??\n");

    /* Allocate memory for new interfaces before doing anything else,
     * so that if we run out then nothing will have changed. */
    n = nintf = 0;
    if (cp) {
        nintf = cp->desc.bNumInterfaces;
        new_interfaces = USB_MALLOC(nintf * sizeof(*new_interfaces));
        if (!new_interfaces) {
            USB_LOGE("Out of memory\n");
            return -ENOMEM;
        }
        HV_MEMSET((void*)(new_interfaces), 0, (nintf * sizeof(*new_interfaces)));

        for (; n < nintf; ++n) {
            new_interfaces[n] = USB_MALLOC(
                                    sizeof(struct usb_interface));
            if (!new_interfaces[n]) {
                USB_LOGE("Out of memory\n");
                ret = -ENOMEM;
                // free_interfaces:
                while (--n >= 0) {
                    if(new_interfaces[n] != NULL)
                    {
                        USB_FREE(new_interfaces[n]);
                        new_interfaces[n] = NULL;
                    }
                }
                if(new_interfaces != NULL)
                {
                    USB_FREE(new_interfaces);
                    new_interfaces = NULL;
                }
                return ret;
            }
            HV_MEMSET((void*)(new_interfaces[n]), 0, sizeof(struct usb_interface));
        }
    }

    /* if it's already configured, clear out old state first.
     * getting rid of old interfaces means unbinding their drivers.
     */
    if (udev->state != USB_STATE_ADDRESS)
        usb_disable_device(udev, 1);    /* Skip ep0 */


    
    /*
     * Initialize the new interface structures and the
     * hc/hcd/usbcore interface/endpoint state.
     */
    for (i = 0; i < nintf; ++i) {
        struct usb_interface_cache *intfc;
        struct usb_interface *intf;
        struct usb_host_interface *alt;

        cp->interface[i] = intf = new_interfaces[i];
        intfc = cp->intf_cache[i];
        intf->altsetting = intfc->altsetting;
        intf->num_altsetting = intfc->num_altsetting;

        alt = usb_altnum_to_altsetting(intf, 0);

        /* No altsetting 0?  We'll assume the first altsetting.
         * We could use a GetInterface call, but if a device is
         * so non-compliant that it doesn't have altsetting 0
         * then I wouldn't trust its reply anyway.
         */
        if (!alt)
            alt = &intf->altsetting[0];

        intf->cur_altsetting = alt;
        usb_enable_interface(udev, intf, true);
        intf->dev.parent = &udev->dev;
        intf->dev.driver = NULL;
        intf->dev.bus = &usb_bus_type;
        intf->dev.type = &usb_if_device_type;
    }
    if(new_interfaces != NULL)
    {
        USB_FREE(new_interfaces);
        new_interfaces = NULL;
    }

    ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                          USB_REQ_SET_CONFIGURATION, 0, configuration, 0,
                          NULL, 0, USB_CTRL_SET_TIMEOUT);
    if (ret < 0 && cp) {
        /*
         * All the old state is gone, so what else can we do?
         * The device is probably useless now anyway.
         */
        for (i = 0; i < nintf; ++i) {
            usb_disable_interface(udev, cp->interface[i], true);
            cp->interface[i] = NULL;
        }
        cp = NULL;
    }

    udev->actconfig = cp;

    if (!cp) {
        usb_set_device_state(udev, USB_STATE_ADDRESS);

        return ret;
    }
    usb_set_device_state(udev, USB_STATE_CONFIGURED);

    /* Now that all the interfaces are set up, register them
     * to trigger binding of drivers to interfaces.  probe()
     * routines may install different altsettings and may
     * claim() any interfaces not yet bound.  Many class drivers
     * need that: CDC, audio, video, etc.
     */
    for (i = 0; i < nintf; ++i) {
        struct usb_interface *intf = cp->interface[i];
    
        ret = device_add(&intf->dev);
        if (ret != 0) {
            continue;
        }
    }

    return 0;
}

int usb_set_interface(struct usb_device *udev, int ifnum, int alternate) {
    struct usb_interface *iface;
    struct usb_host_interface *alt;
    int ret, manual = 0;

    if (udev->state == USB_STATE_SUSPENDED) {
        return -EHOSTUNREACH;
    }

    iface = usb_ifnum_to_if(udev, ifnum);
    if (!iface) {
        return -EINVAL;
    }

    if (iface->unregistering) {
        return -ENODEV;
    }

    alt = usb_altnum_to_altsetting(iface, alternate);
    if (!alt) {
        return -EINVAL;
    }

    if (udev->quirks & USB_QUIRK_NO_SET_INTF) {
        ret = -EPIPE;
    } else {
        ret = usb_control_msg(udev, usb_sndctrlpipe(udev, 0),
                              USB_REQ_SET_INTERFACE, USB_RECIP_INTERFACE,
                              alternate, ifnum, NULL, 0, USB_CTRL_SET_TIMEOUT);
    }

    if (ret == -EPIPE && iface->num_altsetting == 1) {
        manual = 1;
    } else if (ret < 0) {
        return ret;
    }

    if (iface->cur_altsetting != alt) {
        ;   /* FIXME: may do something? */
    }
    usb_disable_interface(udev, iface, true);
    iface->cur_altsetting = alt;

    if (manual) {
        unsigned int epaddr;
        unsigned int pipe;
        int i;
        for (i = 0; i < alt->desc.bNumEndpoints; i++) {
            epaddr = alt->endpoint[i].desc.bEndpointAddress;
            pipe = __create_pipe(udev,
                                 USB_ENDPOINT_NUMBER_MASK & epaddr) |
                   (usb_endpoint_out(epaddr) ?
                    USB_DIR_OUT : USB_DIR_IN);

            usb_clear_halt(udev, pipe);
        }
    }

    usb_enable_interface(udev, iface, true);

    return 0;
}

#define UNICODE_MAX 0x0010ffff
#define PLANE_SIZE  0x00010000

#define SURROGATE_MASK  0xfffff800
#define SURROGATE_PAIR  0x0000d800
#define SURROGATE_LOW   0x00000400
#define SURROGATE_BITS  0x000003ff

struct utf8_table {
    int     cmask;
    int     cval;
    int     shift;
    long    lmask;
    long    lval;
};

static const struct utf8_table utf8_table[] = {
    {0x80,  0x00,   0*6,    0x7F,           0,         /* 1 byte sequence */},
    {0xE0,  0xC0,   1*6,    0x7FF,          0x80,      /* 2 byte sequence */},
    {0xF0,  0xE0,   2*6,    0xFFFF,         0x800,     /* 3 byte sequence */},
    {0xF8,  0xF0,   3*6,    0x1FFFFF,       0x10000,   /* 4 byte sequence */},
    {0xFC,  0xF8,   4*6,    0x3FFFFFF,      0x200000,  /* 5 byte sequence */},
    {0xFE,  0xFC,   5*6,    0x7FFFFFFF,     0x4000000, /* 6 byte sequence */},
    {0,                            /* end of table    */}
};

static int utf32_to_utf8(u32 u, u8 *s, int maxout) {
    unsigned long l;
    int c, nc;
    const struct utf8_table *t;

    if (!s)
        return 0;

    l = u;
    if (l > UNICODE_MAX || (l & SURROGATE_MASK) == SURROGATE_PAIR)
        return -1;

    nc = 0;
    for (t = utf8_table; t->cmask && maxout; t++, maxout--) {
        nc++;
        if ((long)l <= t->lmask) {
            c = t->shift;
            *s = (u8) (t->cval | (l >> c));
            while (c > 0) {
                c -= 6;
                s++;
                *s = (u8) (0x80 | ((l >> c) & 0x3F));
            }
            return nc;
        }
    }
    return -1;
}

static int utf16s_to_utf8s(const u16 *pwcs, int inlen, u8 *s, int maxout) {
    u8 *op;
    int size;
    unsigned long u, v;

    op = s;
    while (inlen > 0 && maxout > 0) {
        u = *pwcs;
        if (!u)
            break;
        pwcs++;
        inlen--;
        if (u > 0x7f) {
            if ((u & SURROGATE_MASK) == SURROGATE_PAIR) {
                if (u & SURROGATE_LOW) {
                    /* Ignore character and move on */
                    continue;
                }
                if (inlen <= 0)
                    break;
                v = *pwcs;
                if ((v & SURROGATE_MASK) != SURROGATE_PAIR ||
                        !(v & SURROGATE_LOW)) {
                    /* Ignore character and move on */
                    continue;
                }
                u = PLANE_SIZE + ((u & SURROGATE_BITS) << 10)
                    + (v & SURROGATE_BITS);
                pwcs++;
                inlen--;
            }
            size = utf32_to_utf8(u, op, maxout);
            if (size == -1) {
                /* Ignore character and move on */
            } else {
                op += size;
                maxout -= size;
            }
        } else {
            *op++ = (u8) u;
            maxout--;
        }
    }
    return op - s;
}

static void usb_release_interface(struct device *dev) {
    struct usb_interface *intf = to_usb_interface(dev);
    if(intf != NULL)
    {
        USB_FREE(intf);
        intf = NULL;
    }
}

struct device_type usb_if_device_type = {
    "usb_device",           //name
    usb_release_interface,  //release
};

