/* Copyright (C) 2018 RDA Technologies Limited and/or its affiliates("RDA").
 * All rights reserved.
 *
 * This software is supplied "AS IS" without any warranties.
 * RDA assumes no responsibility or liability for the use of the software,
 * conveys no license or title under any patent, copyright, or mask work
 * right to the product. RDA reserves the right to make changes in the
 * software without notification.  RDA also make no representation or
 * warranty that such application will be suitable for the specified use
 * without further testing or modification.
 */

#ifndef _USB__COMPOSITE_DEVICE_H_
#define _USB__COMPOSITE_DEVICE_H_

#ifdef __cplusplus
extern "C" {
#endif

#include <sys/queue.h>
#include <usb/usb_device.h>
#include <stdbool.h>

#include "ql_api_common.h"

struct composite_gadget_s;
struct composite_func_s;

/**
 * @brief Composite gadget driver
 */
typedef struct composite_device_s cops_t;

typedef struct
{
    usbXfer_t *xfer;
    uint8_t *buffer;
    unsigned bufsize;
} copsCtrl_t;

typedef struct
{
    uint32_t (*fill_desc)(struct composite_func_s *f, void *buf, uint32_t size);
    int (*bind)(struct composite_func_s *f, cops_t *cops, udc_t *udc);
    void (*unbind)(struct composite_func_s *f);
    void (*destroy)(struct composite_func_s *f);
    int (*setup)(struct composite_func_s *f, const usb_device_request_t *ctrl);
    int (*set_alt)(struct composite_func_s *f, uint8_t intf, uint8_t alt);
    void (*disable)(struct composite_func_s *f);
    void (*resume)(struct composite_func_s *f);
    void (*suspend)(struct composite_func_s *f);
} copsFuncOpt_t;

struct composite_func_s
{
    uint32_t name;
    copsFuncOpt_t ops;
    TAILQ_ENTRY(composite_func_s) anchor;
    // will be set on function add to composite device
    cops_t *cops;
    udc_t *controller;
#ifdef CONFIG_QUEC_PROJECT_FEATURE
	uint8 port_mode;
#endif
};

/**
 * @brief Usb function adapt composite driver framework
 */
typedef struct composite_func_s copsFunc_t;

#define CHECK_CF_OPS(f, o) ((f) && ((f)->ops.o))

/**
 * @brief composite function fill its interface descriptors
 *
 * @param f     the function
 * @param buf   buffer to fill descriptors
 * @param size  size of the buffer
 * @return
 *      - (-1)      fail
 *      - otherwise the number of bytes actually filled
 */
static inline uint32_t cfFillDesc(copsFunc_t *f, void *buf, uint32_t size)
{
    if (size == 0 || buf == NULL)
        return 0;

    if (!CHECK_CF_OPS(f, fill_desc))
        return 0;

    return f->ops.fill_desc(f, buf, size);
}

/**
 * @brief composite function destroy
 *
 * @param f the composite function
 */
static inline void cfDestroy(copsFunc_t *f)
{
    if (CHECK_CF_OPS(f, destroy))
        return f->ops.destroy(f);
}

/**
 * @brief composite function process setup packet
 *
 * @param f     the function
 * @param ctrl  the setup packet
 * @return
 *      - (-1)  fail
 *      - 0     success
 */
static inline int cfSetup(copsFunc_t *f, const usb_device_request_t *ctrl)
{
    if (CHECK_CF_OPS(f, setup) && ctrl != NULL)
        return f->ops.setup(f, ctrl);
    return -1;
}

/**
 * @brief composite function bind driver
 *
 * @param f     the function
 * @param cops  the composite device
 * @param udc   the udc
 * @return
 *      - (-1)  fail
 *      - 0     success
 */
static inline int cfBind(copsFunc_t *f, cops_t *cops, udc_t *udc)
{
    if (CHECK_CF_OPS(f, bind) && cops && udc)
        return f->ops.bind(f, cops, udc);
    return -1;
}

/**
 * @brief composite function unbind driver
 *
 * @param f the function
 */
static inline void cfUnbind(copsFunc_t *f)
{
    if (CHECK_CF_OPS(f, unbind))
        f->ops.unbind(f);
}

/**
 * @brief composite function set alternate
 *
 * @param f     the function
 * @param intf  the interface number
 * @param alt   alternate setting number
 * @return
 *      - (-1)  fail
 *      - 0     success
 */
static inline int cfSetAlt(copsFunc_t *f, uint8_t intf, uint8_t alt)
{
    if (CHECK_CF_OPS(f, set_alt))
        return f->ops.set_alt(f, intf, alt);
    return -1;
}

/**
 * @breif composite function disable
 *
 * @param f     the function
 */
static inline void cfDisable(copsFunc_t *f)
{
    if (CHECK_CF_OPS(f, disable))
        f->ops.disable(f);
}

/**
 * @breif composite function suspend
 *
 * @param f     the function
 */
static inline void cfSuspend(copsFunc_t *f)
{
    if (CHECK_CF_OPS(f, suspend))
        f->ops.suspend(f);
}

/**
 * @breif composite function resume
 *
 * @param f     the function
 */
static inline void cfResume(copsFunc_t *f)
{
    if (CHECK_CF_OPS(f, resume))
        f->ops.resume(f);
}

/**
 * @brief Create a composite gadget driver
 *
 * @return
 *      - NULL  fail to create the driver
 *      - other the composite driver
 */
cops_t *copsCreate();

/**
 * @brief Destroy a composite gadget driver
 *
 * @param c the composite driver
 */
void copsDestroy(cops_t *c);

/**
 * @brief Add an usb function to the composite driver
 *
 * @param c the composite driver
 * @param f the composite usb function
 * @return
 *      - true  success
 *      - false fail
 */
bool copsAddFunction(cops_t *c, copsFunc_t *f);

/**
 * @brief Add an usb function to the composite driver
 *
 * @param c     the composite driver
 * @param funcs the composite usb function list
 * @param count functions count
 * @return
 *      - true  success
 *      - false fail
 */
bool copsAddFunctions(cops_t *c, copsFunc_t **funcs, unsigned count);

/**
 * @brief Remove all usb function from the composite driver
 *
 * @param c the composite driver
 */
void copsRemoveAllFunction(cops_t *c);

/**
 * @brief cops api for mtp notify vfs add file
 *
 * @param c the composite driver
 */
void copsMtpNotify(cops_t *c);

/**
 * @brief From composite driver get the gadget driver instance
 *
 * @param c the composite driver
 * @return
 *      - NULL  fail
 *      - other the gadget driver instance
 */
udevDrv_t *copsGetGadgetDriver(cops_t *c);

/**
 * @brief Assign an interface number
 *
 * @param c the composite drvice
 * @param f the composite function
 * @return
 *      - (-1)      fail
 *      - [0:15]    the interface number
 */
int copsAssignInterface(cops_t *c, copsFunc_t *f);

/**
 * @brief Remove an interface
 *
 * @param c     the composite device
 * @param index the interface number
 */
void copsRemoveInterface(cops_t *c, uint8_t index);

#ifdef CONFIG_QUEC_PROJECT_FEATURE
int copsAssignInterface_ex(cops_t *c, copsFunc_t *f);
int quec_usb_assign_interface(uint8 intf, cops_t *c, copsFunc_t *f);
#endif

/**
 * @brief Allocate a string id
 * @note Caller must keep the strings room, composite device only store
 *       the address.
 *
 * @param c     the composite device
 * @param ustr  the usb string (caller keep the memory)
 * @return      the string id, 0 means no string
 */
int copsAssignStringId(cops_t *c, usbString_t *ustr);

/**
 * @brief Remove a string, giveback the id
 *
 * @param c     the composite device
 * @prarm ustr  the usb string (caller keep the memory)
 * @return
 *      - 0     always return 0
 */
int copsRemoveString(cops_t *c, usbString_t *ustr);

/**
 * @brief Set vendor id for this composite device
 *
 * @param c     the composite device
 * @param vid   vid
 */
void copsSetVendorId(cops_t *c, uint16_t vid);

/**
 * @brief Set product id for this composite device
 *
 * @param c     the composite device
 * @param pid   pid
 */
void copsSetProductId(cops_t *c, uint16_t pid);

/**
 * @brief get ctrl buffer/xfer
 *
 * @param c     the composite device
 * @return
 *      - NonNull for cops ctrl data
 */
copsCtrl_t *copsGetCtrl(cops_t *c);

#ifdef __cplusplus
}
#endif

#endif
