/***************************************************************************
 *             __________               __   ___.
 *   Open      \______   \ ____   ____ |  | _\_ |__   _______  ___
 *   Source     |       _//  _ \_/ ___\|  |/ /| __ \ /  _ \  \/  /
 *   Jukebox    |    |   (  <_> )  \___|    < | \_\ (  <_> > <  <
 *   Firmware   |____|_  /\____/ \___  >__|_ \|___  /\____/__/\_ \
 *                     \/            \/     \/    \/            \/
 *
 * Copyright (C) 2008 Frank Gevaerts
 *
 * 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 2
 * of the License, or (at your option) any later version.
 *
 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
 * KIND, either express or implied.
 *
 ****************************************************************************/

#ifndef _USB_CLASS_DRIVER_H_
#define _USB_CLASS_DRIVER_H_

#include "usb_ch9.h"
#include <stdbool.h>
#include <stddef.h>
#include <string.h>

/* Common api, implemented by all class drivers */

struct usb_class_driver_ep_allocation {
    uint8_t ep;      /* by core, allocated ep. > 0 are valid but can be 0 if optional==true */
    uint8_t type:2;  /* by driver, required ep type. USB_ENDPOINT_XFER_* */
    uint8_t dir:1;   /* by driver, required ep dir. DIR_{IN,OUT} */
    bool optional:1; /* by driver, set true to mark this requirement to be optional */
    int16_t mps;     /* by driver, desired max packet size, or -1 for device driver default */
};

struct usb_class_driver {
    /* First some runtime data */

    /* there are three possible runtime states for class driver:
     * !enabled          -> disabled, invisible from host
     * enabled && !error -> enabled, healthy
     * enabled && error  -> recognized by host, but failed */
    bool enabled;
    bool error;

    int first_interface;
    int last_interface;

    /* Driver api starts here */

    /* Set this to true if the driver needs exclusive disk access (e.g. usb storage) */
    bool needs_exclusive_storage;

    /* Set this to true if the driver needs to enable cpu boost */
    bool needs_cpu_boost;

    /* USB config number this driver belongs to */
    uint8_t config;

    /* Endpoint allocation state table */
    uint8_t ep_allocs_size;
    struct usb_class_driver_ep_allocation* ep_allocs;

    /* Tells the driver what its first interface number will be. The driver
       returns the number of the first available interface for the next driver
       (i.e. a driver with one interface will return interface+1)
       A driver must have at least one interface
       Mandatory function */
    int (*set_first_interface)(int interface);

    /* Asks the driver to put the interface descriptor and all other
       needed descriptor for this driver at dest.
       Returns the number of bytes taken by these descriptors.
       Mandatory function */
    int (*get_config_descriptor)(unsigned char *dest, int max_packet_size);

    /* Tells the driver that a usb connection has been set up and is now
       ready to use.
       Returns 0 on success and -1 on error.
       Optional function */
    int (*init_connection)(void);

    /* Initialises the driver. This can be called multiple times,
       and should not perform any action that can disturb other threads
       (like getting the audio buffer)
       Optional function */
    void (*init)(void);

    /* Tells the driver that the usb connection is no longer active
       Optional function */
    void (*disconnect)(void);

    /* Tells the driver that a usb transfer has been completed. Note that "dir"
       is relative to the host
       Optional function */
    void (*transfer_complete)(int ep, int dir, int status, int length);

    /* Similar to transfer_complete but called directly instead of going through
     * the usb queue. Since it might be called in an interrupt context,
     * processing should be kept to a minimum. This is mainly intended for
     * isochronous transfers.
     * The function must return true if it handled the completion, and false
     * otherwise so that it is dispatched to the normal handler
     * Optional function */
    bool (*fast_transfer_complete)(int ep, int dir, int status, int length);

    /* Tells the driver that a control request has come in. If the driver is
       able to handle it, it should ack the request, and return true. Otherwise
       it should return false.
       Optional function */
    bool (*control_request)(struct usb_ctrlrequest* req, uint8_t* reqdata, size_t reqdata_size);

#ifdef HAVE_HOTSWAP
    /* Tells the driver that a hotswappable disk/card was inserted or
       extracted
       Optional function */
    void (*notify_hotswap)(int volume, bool inserted);
#endif

    /* Tells the driver to select an alternate setting for a specific interface.
     * Returns 0 on success and -1 on error.
     * Mandatory function if alternate interface support is needed */
    int (*set_interface)(int interface, int alt_setting);

    /* Asks the driver what is the current alternate setting for a specific interface.
     * Returns value on success and -1 on error.
     * Mandatory function if alternate interface support is needed */
    int (*get_interface)(int interface);

    /* Invoked by USB_NOTIFY_CLASS_DRIVER
       Optional function */
    void (*notify_event)(intptr_t data);
};

#define PACK_DATA(dest, data) pack_data(dest, &(data), sizeof(data))
static inline void pack_data(uint8_t **dest, const void *data, size_t size)
{
    memcpy(*dest, data, size);
    *dest += size;
}
#endif
