/**
 ******************************************************************************
 * @addtogroup PIOS PIOS Core hardware abstraction layer
 * @{
 * @addtogroup PIOS_USB_DESC USB Descriptor definitions
 * @brief USB Descriptor definitions for HID and CDC
 * @{
 *
 * @file       pios_usb_desc_hid_cdc.c
 * @author     The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010.
 * @brief      USB Descriptor definitions for HID and CDC
 * @see        The GNU Public License (GPL) Version 3
 *
 *****************************************************************************/
/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
 * for more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 */

#include "pios.h"

#ifdef PIOS_INCLUDE_USB

#include "pios_usb_desc_hid_cdc_priv.h" /* exported API */
#include "pios_usb_defs.h" /* struct usb_*, USB_* */
#include "pios_usb_board_data.h" /* PIOS_USB_BOARD_* */
#include "pios_usbhook.h" /* PIOS_USBHOOK_Register* */
#include "pios_usb_hid.h" /* PIOS_USB_HID_Register* */

static const struct usb_device_desc device_desc = {
    .bLength = sizeof(struct usb_device_desc),
    .bDescriptorType    = USB_DESC_TYPE_DEVICE,
    .bcdUSB  = htousbs(0x0200),
    .bDeviceClass    = 0xef,
    .bDeviceSubClass = 0x02,
    .bDeviceProtocol = 0x01,
    .bMaxPacketSize0 = 64, /* Must be 64 for high-speed devices */
    .idVendor      = htousbs(USB_VENDOR_ID_OPENPILOT),
    .idProduct     = htousbs(PIOS_USB_BOARD_PRODUCT_ID),
    .bcdDevice     = htousbs(PIOS_USB_BOARD_DEVICE_VER),
    .iManufacturer = USB_STRING_DESC_VENDOR,
    .iProduct      = USB_STRING_DESC_PRODUCT,
    .iSerialNumber = USB_STRING_DESC_SERIAL,
    .bNumConfigurations = 1,
};

static const uint8_t hid_report_desc[89] = {
    HID_GLOBAL_ITEM_2(HID_TAG_GLOBAL_USAGE_PAGE),
    0x9C,                                         0xFF, /* Usage Page 0xFF9C (Vendor Defined) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x01,                                         /* Usage ID 0x0001 (0x01-0x1F uaually for top-level collections) */

    HID_MAIN_ITEM_1(HID_TAG_MAIN_COLLECTION),
    0x01,                                         /* Application */

    /* Device -> Host emulated serial channel */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_ID),
    0x01,                                         /* OpenPilot emulated serial channel (Device -> Host) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x02,
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_LOGICAL_MIN),
    0x00,                                         /* Values range from min = 0x00 */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_LOGICAL_MAX),
    0xFF,                                         /* Values range to max = 0xFF */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_SIZE),
    0x08,                                         /* 8 bits wide */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_CNT),
    PIOS_USB_BOARD_HID_DATA_LENGTH - 1,           /* Need to leave room for a report ID */
    HID_MAIN_ITEM_1(HID_TAG_MAIN_INPUT),
    0x03,                                         /* Variable, Constant (read-only) */

    /* Host -> Host emulated serial channel */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_ID),
    0x02,                                         /* OpenPilot emulated Serial Channel (Host -> Device) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x02,
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_LOGICAL_MIN),
    0x00,                                         /* Values range from min = 0x00 */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_LOGICAL_MAX),
    0xFF,                                         /* Values range to max = 0xFF */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_SIZE),
    0x08,                                         /* 8 bits wide */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_CNT),
    PIOS_USB_BOARD_HID_DATA_LENGTH - 1,           /* Need to leave room for a report ID */
    HID_MAIN_ITEM_1(HID_TAG_MAIN_OUTPUT),
    0x82,                                         /* Volatile, Variable */

    HID_MAIN_ITEM_0(HID_TAG_MAIN_ENDCOLLECTION),

/* 36 bytes to here */

    /* Emulate a Joystick */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_USAGE_PAGE),
    0x01,                                         /* Usage Page 0x01 (Generic Desktop Controls) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x04,                                         /* Usage ID 0x0004 (Joystick) */

    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_LOGICAL_MIN),
    0x00,                                         /* Values range from min = 0x00 */
    HID_GLOBAL_ITEM_4(HID_TAG_GLOBAL_LOGICAL_MAX),
    0xFF,                                         0xFF,0x00, 0x00, /* Values range to max = 0x0000FFFF */

    HID_MAIN_ITEM_1(HID_TAG_MAIN_COLLECTION),
    0x01,                                         /* Application */
    HID_MAIN_ITEM_1(HID_TAG_MAIN_COLLECTION),
    0x00,                                         /* Physical */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_ID),
    0x03,                                         /* OpenPilot Emulated joystick */

    /* X + Y controls */

    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x30,                                         /* Usage ID 0x00010030 (Generic Desktop: X) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x31,                                         /* Usage ID 0x00010031 (Generic Desktop: Y) */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_SIZE),
    0x10,                                         /* 16 bits wide */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_CNT),
    2,
    HID_MAIN_ITEM_1(HID_TAG_MAIN_INPUT),
    0x82,                                         /* Data, Var, Abs, Vol */

    /* Y + Rx controls */

    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x32,                                         /* Usage ID 0x00010032 (Generic Desktop: Z) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x33,                                         /* Usage ID 0x00010031 (Generic Desktop: Rx) */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_SIZE),
    0x10,                                         /* 16 bits wide */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_CNT),
    2,
    HID_MAIN_ITEM_1(HID_TAG_MAIN_INPUT),
    0x82,                                         /* Data, Var, Abs, Vol */

    /* Ry, Rz, Slider + Dial controls */

    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x34,                                         /* Usage ID 0x00010034 (Generic Desktop: Ry) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x35,                                         /* Usage ID 0x00010035 (Generic Desktop: Rz) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x36,                                         /* Usage ID 0x00010036 (Generic Desktop: Slider) */
    HID_LOCAL_ITEM_1(HID_TAG_LOCAL_USAGE),
    0x37,                                         /* Usage ID 0x00010037 (Generic Desktop: Dial) */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_SIZE),
    0x10,                                         /* 16 bits wide */
    HID_GLOBAL_ITEM_1(HID_TAG_GLOBAL_REPORT_CNT),
    4,
    HID_MAIN_ITEM_1(HID_TAG_MAIN_INPUT),
    0x82,                                         /* Data, Var, Abs, Vol */

    HID_MAIN_ITEM_0(HID_TAG_MAIN_ENDCOLLECTION),

    HID_MAIN_ITEM_0(HID_TAG_MAIN_ENDCOLLECTION),

/* 89 bytes to here */
};

struct usb_config_hid_cdc {
    struct usb_configuration_desc         config;
    struct usb_interface_association_desc iad;
    struct usb_interface_desc             cdc_control_if;
    struct usb_cdc_header_func_desc       cdc_header;
    struct usb_cdc_callmgmt_func_desc     cdc_callmgmt;
    struct usb_cdc_acm_func_desc          cdc_acm;
    struct usb_cdc_union_func_desc        cdc_union;
    struct usb_endpoint_desc cdc_mgmt_in;
    struct usb_interface_desc             cdc_data_if;
    struct usb_endpoint_desc cdc_in;
    struct usb_endpoint_desc cdc_out;
    struct usb_interface_desc             hid_if;
    struct usb_hid_desc hid;
    struct usb_endpoint_desc hid_in;
    struct usb_endpoint_desc hid_out;
} __attribute__((packed));

static const struct usb_config_hid_cdc config_hid_cdc = {
    .config                   = {
        .bLength              = sizeof(struct usb_configuration_desc),
        .bDescriptorType     = USB_DESC_TYPE_CONFIGURATION,
        .wTotalLength        = htousbs(sizeof(struct usb_config_hid_cdc)),
        .bNumInterfaces      = 3,
        .bConfigurationValue = 1,
        .iConfiguration      = 0,
        .bmAttributes        = 0xC0,
        .bMaxPower            = 250 / 2,                                   /* in units of 2ma */
    },
    .iad                      = {
        .bLength              = sizeof(struct usb_interface_association_desc),
        .bDescriptorType   = USB_DESC_TYPE_IAD,
        .bFirstInterface   = 0,
        .bInterfaceCount   = 2,
        .bFunctionClass    = USB_INTERFACE_CLASS_CDC, /* Communication */
        .bFunctionSubClass = USB_CDC_DESC_SUBTYPE_ABSTRACT_CTRL, /* Abstract Control Model */
        .bFunctionProtocol = 1, /* V.25ter, Common AT commands */
        .iInterface           = 0,
    },
    .cdc_control_if           = {
        .bLength              = sizeof(struct usb_interface_desc),
        .bDescriptorType    = USB_DESC_TYPE_INTERFACE,
        .bInterfaceNumber   = 0,
        .bAlternateSetting  = 0,
        .bNumEndpoints      = 1,
        .bInterfaceClass    = USB_INTERFACE_CLASS_CDC,
        .bInterfaceSubClass = USB_CDC_DESC_SUBTYPE_ABSTRACT_CTRL, /* Abstract Control Model */
        .nInterfaceProtocol = 1,       /* V.25ter, Common AT commands */
        .iInterface           = 0,
    },
    .cdc_header               = {
        .bLength = sizeof(struct usb_cdc_header_func_desc),
        .bDescriptorType      = USB_DESC_TYPE_CLASS_SPECIFIC,
        .bDescriptorSubType   = USB_CDC_DESC_SUBTYPE_HEADER,
        .bcdCDC  = htousbs(0x0110),
    },
    .cdc_callmgmt             = {
        .bLength              = sizeof(struct usb_cdc_callmgmt_func_desc),
        .bDescriptorType    = USB_DESC_TYPE_CLASS_SPECIFIC,
        .bDescriptorSubType = USB_CDC_DESC_SUBTYPE_CALLMGMT,
        .bmCapabilities     = 0x00, /* No call handling */
        .bDataInterface     = 1,
    },
    .cdc_acm                  = {
        .bLength              = sizeof(struct usb_cdc_acm_func_desc),
        .bDescriptorType    = USB_DESC_TYPE_CLASS_SPECIFIC,
        .bDescriptorSubType = USB_CDC_DESC_SUBTYPE_ABSTRACT_CTRL,
        .bmCapabilities     = 0x02, /* line coding and serial state */
    },
    .cdc_union                = {
        .bLength              = sizeof(struct usb_cdc_union_func_desc),
        .bDescriptorType    = USB_DESC_TYPE_CLASS_SPECIFIC,
        .bDescriptorSubType = USB_CDC_DESC_SUBTYPE_UNION,
        .bMasterInterface   = 0,
        .bSlaveInterface    = 1,
    },
    .cdc_mgmt_in              = {
        .bLength              = sizeof(struct usb_endpoint_desc),
        .bDescriptorType  = USB_DESC_TYPE_ENDPOINT,
        .bEndpointAddress = USB_EP_IN(2),
        .bmAttributes     = USB_EP_ATTR_TT_INTERRUPT,
        .wMaxPacketSize   = htousbs(PIOS_USB_BOARD_CDC_MGMT_LENGTH),
        .bInterval            = 4,                                      /* ms */
    },
    .cdc_data_if              = {
        .bLength              = sizeof(struct usb_interface_desc),
        .bDescriptorType    = USB_DESC_TYPE_INTERFACE,
        .bInterfaceNumber   = 1,
        .bAlternateSetting  = 0,
        .bNumEndpoints      = 2,
        .bInterfaceClass    = USB_INTERFACE_CLASS_DATA,
        .bInterfaceSubClass = 0,
        .nInterfaceProtocol = 0, /* No class specific protocol */
        .iInterface           = 0,
    },
    .cdc_in                   = {
        .bLength              = sizeof(struct usb_endpoint_desc),
        .bDescriptorType  = USB_DESC_TYPE_ENDPOINT,
        .bEndpointAddress = USB_EP_IN(3),
        .bmAttributes     = USB_EP_ATTR_TT_BULK,
        .wMaxPacketSize   = htousbs(PIOS_USB_BOARD_CDC_DATA_LENGTH),
        .bInterval            = 0,                                      /* ms */
    },
    .cdc_out                  = {
        .bLength              = sizeof(struct usb_endpoint_desc),
        .bDescriptorType  = USB_DESC_TYPE_ENDPOINT,
        .bEndpointAddress = USB_EP_OUT(3),
        .bmAttributes     = USB_EP_ATTR_TT_BULK,      /* Bulk */
        .wMaxPacketSize   = htousbs(PIOS_USB_BOARD_CDC_DATA_LENGTH),
        .bInterval            = 0,                                      /* ms */
    },
    .hid_if                   = {
        .bLength              = sizeof(struct usb_interface_desc),
        .bDescriptorType    = USB_DESC_TYPE_INTERFACE,
        .bInterfaceNumber   = 2,
        .bAlternateSetting  = 0,
        .bNumEndpoints      = 2,
        .bInterfaceClass    = USB_INTERFACE_CLASS_HID,
        .bInterfaceSubClass = 0, /* no boot */
        .nInterfaceProtocol = 0, /* none */
        .iInterface           = 0,
    },
    .hid                      = {
        .bLength = sizeof(struct usb_hid_desc),
        .bDescriptorType      = USB_DESC_TYPE_HID,
        .bcdHID  = htousbs(0x0110),
        .bCountryCode    = 0,
        .bNumDescriptors = 1,
        .bClassDescriptorType = USB_DESC_TYPE_REPORT,
        .wItemLength     = htousbs(sizeof(hid_report_desc)),
    },
    .hid_in                   = {
        .bLength              = sizeof(struct usb_endpoint_desc),
        .bDescriptorType  = USB_DESC_TYPE_ENDPOINT,
        .bEndpointAddress = USB_EP_IN(1),
        .bmAttributes     = USB_EP_ATTR_TT_INTERRUPT,
        .wMaxPacketSize   = htousbs(PIOS_USB_BOARD_HID_DATA_LENGTH),
        .bInterval            = 4,                                      /* ms */
    },
    .hid_out                  = {
        .bLength              = sizeof(struct usb_endpoint_desc),
        .bDescriptorType  = USB_DESC_TYPE_ENDPOINT,
        .bEndpointAddress = USB_EP_OUT(1),
        .bmAttributes     = USB_EP_ATTR_TT_INTERRUPT,
        .wMaxPacketSize   = htousbs(PIOS_USB_BOARD_HID_DATA_LENGTH),
        .bInterval            = 4,                                      /* ms */
    },
};

int32_t PIOS_USB_DESC_HID_CDC_Init(void)
{
    PIOS_USBHOOK_RegisterConfig(1, (uint8_t *)&config_hid_cdc, sizeof(config_hid_cdc));

    PIOS_USBHOOK_RegisterDevice((uint8_t *)&device_desc, sizeof(device_desc));

    PIOS_USB_HID_RegisterHidDescriptor((uint8_t *)&(config_hid_cdc.hid), sizeof(config_hid_cdc.hid));
    PIOS_USB_HID_RegisterHidReport((uint8_t *)hid_report_desc, sizeof(hid_report_desc));

    return 0;
}

#endif /* PIOS_INCLUDE_USB */