// SPDX-License-Identifier: GPL-2.0+
/*
 * hid.c -- HID Composite driver
 *
 * Based on multi.c
 *
 * Copyright (C) 2010 Fabien Chouteau <fabien.chouteau@barco.com>
 */

#include "hv_drv_UsbErr.h"
#include "hv_drv_UsbGadgetComposite.h"
#include "hv_drv_UsbCh9.h"
#include "hv_vos_Comm.h"

#include "hv_drv_UsbGadgetHid.h"
#include "hv_drv_UsbUtilHid.h"


#define DRIVER_DESC            "HID Gadget"
#define DRIVER_VERSION         "2010/03/16"

/*-------------------------------------------------------------------------*/

#define HIDG_VENDOR_NUM        0x380C    /* Hi-image */
#define HIDG_PRODUCT_NUM       0xa4ac    /* Linux-USB HID gadget */

/*-------------------------------------------------------------------------*/

struct hidg_func_node {
    struct usb_function_instance *fi;
    struct usb_function *f;
    struct list_head node;
    struct hidg_func_descriptor *func;
};

static LIST_HEAD(hidg_func_list);

/*-------------------------------------------------------------------------*/

static struct usb_device_descriptor device_desc = {
    .bLength = sizeof(device_desc),
    .bDescriptorType = USB_DT_DEVICE,

    .bcdUSB = __constant_cpu_to_le16(0x0201),

    .bDeviceClass           = USB_CLASS_PER_INTERFACE,
    .bDeviceSubClass = 0,
    .bDeviceProtocol = 0,
    .bMaxPacketSize0 = 8,

    /* Vendor and product id can be overridden by module parameters.  */
    .idVendor = cpu_to_le16(HIDG_VENDOR_NUM),
    .idProduct = cpu_to_le16(HIDG_PRODUCT_NUM),
    /* .bcdDevice = f(hardware) */
    /* .iManufacturer = DYNAMIC */
    /* .iProduct = DYNAMIC */
    /* NO SERIAL NUMBER */
    .bNumConfigurations     = 1,
};

static const struct usb_descriptor_header *otg_desc[2];

/* string IDs are assigned dynamically */
static struct usb_string strings_dev[] = {
    [USB_GADGET_MANUFACTURER_IDX].s = "hid demo",
    [USB_GADGET_PRODUCT_IDX].s      = DRIVER_DESC,
    [USB_GADGET_SERIAL_IDX].s       = "00001",
    {  } /* end of list */
};

static struct usb_gadget_strings stringtab_dev = {
    .language    = 0x0409,    /* en-us */
    .strings    = strings_dev,
};

static struct usb_gadget_strings *dev_strings[] = {
    &stringtab_dev,
    NULL,
};



/****************************** Configurations ******************************/

static int do_config(struct usb_configuration *c)
{
    struct hidg_func_node *e, *n;
    int status = 0;

#if 0
    if (gadget_is_otg(c->cdev->gadget)) {
        c->descriptors = otg_desc;
        c->bmAttributes |= USB_CONFIG_ATT_WAKEUP;
    }
#endif

    list_for_each_entry(e, &hidg_func_list, node) {
        
        e->f = usb_get_function(e->fi);
        if (IS_ERR(e->f))
            goto put;
        status = usb_add_function(c, e->f);
        if (status < 0) {
            usb_put_function(e->f);
            goto put;
        }
        usb_function_deactivate(e->f);  
        usb_function_activate(e->f);
    }
    
    return 0;
put:
    list_for_each_entry(n, &hidg_func_list, node) {
        if (n == e)
            break;
    //    usb_remove_function(c, n->f);
        usb_put_function(n->f);
    }
    return status;
}

static struct usb_configuration config_driver = {
    .bind           = do_config,
    .label            = "HID Gadget",
    .bConfigurationValue    = 1,
    /* .iConfiguration = DYNAMIC */
    .bmAttributes        = USB_CONFIG_ATT_SELFPOWER,
};

/****************************** Gadget Bind ******************************/

static int hid_bind(struct usb_composite_dev *cdev)
{
    struct usb_gadget *gadget = cdev->gadget;
    struct list_head *tmp;
    struct hidg_func_node *n, *m;
    struct f_hid_opts *hid_opts;
    int status, funcs = 0;

    list_for_each(tmp, &hidg_func_list)
        funcs++;

    if (!funcs)
        return -ENODEV;

    list_for_each_entry(n, &hidg_func_list, node) {
        n->fi = usb_get_function_instance("hid");
        
        if (IS_ERR(n->fi)) {
            status = PTR_ERR(n->fi);
            goto put;
        }
        hid_opts = container_of(n->fi, struct f_hid_opts, func_inst);
        hid_opts->subclass = n->func->subclass;
        hid_opts->protocol = n->func->protocol;
        hid_opts->report_length = n->func->report_length;
        hid_opts->report_desc_length = n->func->report_desc_length;
        hid_opts->report_desc = n->func->report_desc;
    }

    /* Allocate string descriptor numbers ... note that string
     * contents can be overridden by the composite_dev glue.
     */

    status = usb_string_ids_tab(cdev, strings_dev);
    if (status < 0)
        goto put;
    device_desc.iManufacturer = strings_dev[USB_GADGET_MANUFACTURER_IDX].id;
    device_desc.iProduct = strings_dev[USB_GADGET_PRODUCT_IDX].id;
#if 0
    if (gadget_is_otg(gadget) && !otg_desc[0]) {
        struct usb_descriptor_header *usb_desc;

        usb_desc = usb_otg_descriptor_alloc(gadget);
        if (!usb_desc)
            goto put;
        usb_otg_descriptor_init(gadget, usb_desc);
        otg_desc[0] = usb_desc;
        otg_desc[1] = NULL;
    }
#endif
    /* register our configuration */
    status = usb_add_config(cdev, &config_driver);
    if (status < 0)
        goto free_otg_desc;

    //usb_composite_overwrite_options(cdev, &coverwrite);    //lxn del
    dev_info(&gadget->dev, DRIVER_DESC ", version: " DRIVER_VERSION "\n");

    return 0;

free_otg_desc:
    kfree(otg_desc[0]);
    otg_desc[0] = NULL;
put:
    list_for_each_entry(m, &hidg_func_list, node) {
        if (m == n)
            break;
        usb_put_function_instance(m->fi);
    }
    return status;
}

static int hid_unbind(struct usb_composite_dev *cdev)
{
    struct hidg_func_node *n;

    list_for_each_entry(n, &hidg_func_list, node) {
        usb_put_function(n->f);
        usb_put_function_instance(n->fi);
    }

    kfree(otg_desc[0]);
    otg_desc[0] = NULL;

    return 0;
}

static int hidg_plat_driver_probe(struct hidg_func_descriptor *func)
{
    struct hidg_func_node *entry;

    if (!func) {
        dev_err(&pdev->dev, "Platform data missing\n");
        return -ENODEV;
    }

    entry = USB_MALLOC(sizeof(*entry));
    if (!entry)
        return -ENOMEM;
    HV_MEMSET(entry, 0, sizeof(*entry));

    entry->func = func;
    list_add_tail(&entry->node, &hidg_func_list);

    return 0;
}

static int hidg_plat_driver_remove(void)
{
    struct hidg_func_node *e, *n;

    list_for_each_entry_safe(e, n, &hidg_func_list, node) {
        list_del(&e->node);
        kfree(e);
    }

    return 0;
}


/****************************** Some noise ******************************/

static struct usb_composite_driver hidg_driver = {
    .name        = "g_hid",
    .dev        = &device_desc,
    .strings    = dev_strings,
    .max_speed    = USB_SPEED_FULL,
    .bind        = hid_bind,
    .unbind        = hid_unbind,
};



unsigned char mouse_report[] = {
    0x05, 0x01, // USAGE_PAGE (Generic Desktop) // parser->global.usage_page = 0x01;进入Generic Desktop
    0x09, 0x02, // USAGE (Mouse)
    0xa1, 0x01, // COLLECTION (Application)     // 对基于Generic Desktop的Mouse信息进行收集
    0x09, 0x01, // USAGE (Pointer)
    0xa1, 0x00, // COLLECTION (Physical)      // 对基于Generic Desktop的Porinter信息进行收集[luther.gliethttp]
    0x05, 0x09, // USAGE_PAGE (Button)      // parser->global.usage_page = 0x09;进入Button
    0x19, 0x01, // USAGE_MINIMUM (Button 1)
    0x29, 0x03, // USAGE_MAXIMUM (Button 3)
    0x15, 0x00, // LOGICAL_MINIMUM (0)
    0x25, 0x01, // LOGICAL_MAXIMUM (1)
    0x95, 0x03, // REPORT_COUNT (3)
    0x75, 0x01, // REPORT_SIZE (1)
    0x81, 0x02, // INPUT (Data,Var,Abs)     // 生成具有上面4个local属性和2个global属性的Button的Input报告report数据格式,同时复位local[luther.gliethttp]
    0x95, 0x01, // REPORT_COUNT (1)
    0x75, 0x05, // REPORT_SIZE (5)
    0x81, 0x03, // INPUT (Cnst,Var,Abs)     // 生成只有上面2个local.usage[0-1]属性的Button的Input报告report数据格式[luther.gliethttp]
    0x05, 0x01, // USAGE_PAGE (Generic Desktop)// parser->global.usage_page = 0x01;进入Generic Desktop
    0x09, 0x30, // USAGE (X)
    0x09, 0x31, // USAGE (Y)
    0x09, 0x38, // USAGE (Wheel)
    0x15, 0x81, // LOGICAL_MINIMUM (-127)
    0x25, 0x7f, // LOGICAL_MAXIMUM (127)
    0x75, 0x08, // REPORT_SIZE (8)
    0x95, 0x03, // REPORT_COUNT (3)
    0x81, 0x06, // INPUT (Data,Var,Rel)     // 生成hid设备report上报数据格式[luther.gliethttp]
    0xc0,       // END_COLLECTION
    0xc0        // END_COLLECTION
};


static struct hidg_func_descriptor func ={

    .subclass = 0,
    .protocol = 0,
    .report_length = 4,
    .report_desc_length =52,
    .report_desc = (unsigned char*)mouse_report,
};


int hidg_init(void)
{
    int status = 0;

    hidg_plat_driver_probe(&func);
    
    usb_composite_register(&hidg_driver);

    return status;
}


void hidg_cleanup(void)
{
    usb_composite_unregister(&hidg_driver);
    hidg_plat_driver_remove();
}
module_exit(hidg_cleanup);
