/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __USB_USBTMC_H
#define __USB_USBTMC_H

#ifdef __cplusplus
 extern "C" {
#endif

//----------------------------------------------------------------
// Refer to:
// [1] USBTMC Standard, rev. 1.0, 14/04/2003
//     "Universal Serial Bus Test and Measurement Class Specification (USBTMC)"
// [2] USBTMC-USB488 Standard, rev. 1.0, 14/04/2003
//     "Universal Serial Bus Test and Measurement Class, Subclass USB488 Specification (USBTMC-USB488)
//----------------------------------------------------------------

/* Includes ------------------------------------------------------------------*/
#include  "usbd_ioreq.h"
#include  "usb/usb_hooks.h"
#include  "usb/usb_config.h"
#include  "core/config.h"
#include  "drivers/usb/class/common/class_common.h"

#define USBTMC_BULKIO_EP                               0x01  /* Bulk_IN_OUT */
#define USBTMC_INTIO_EP                                0x02  /* Interrupt_IN */
#define USBTMC_BULKIN_EP                               (0x80 | USBTMC_BULKIO_EP)  /* EP1 for data IN */
#define USBTMC_BULKOUT_EP                              (0x7F & USBTMC_BULKIO_EP)  /* EP1 for data OUT */
#define USBTMC_INTERRUPTIN_EP                          (0x80 | USBTMC_INTIO_EP)   /* EP2 for data IN */
#define USBTMC_BENCHMARK_EP                            USBTMC_BULKIO_EP

#define USBTMC_DATAIN_MAX_PACKET_SIZE                  64    // Control-EP size
#define USBTMC_DATAOUT_MAX_PACKET_SIZE                 64    // Control-EP size

// Refer [1], 3.4 Interrupt-IN.
// The Host, after an Interrupt-IN transfer has completed, must interpret the first byte in the next Interrupt-
// IN DATA payload as a new notification, beginning with bNotify1. Note that the USB 2.0 specification,
// section 5.7.3, defines when an interrupt transfer must be considered complete. If no subclass specification
// applies (bInterfaceProtocol = 0, see Table 44), or if the notification is a vendor specific request, the last
// Interrupt-IN transaction must be a short packet (< wMaxPacketSize).
#define USBTMC_INTIN_MAX_PACKET_SIZE                    2    // INT-EP size: in accordance to [2]

#define USBTMC_USE_COMMON_EP0RXREADY                   1
#define USBTMC_USE_COMMON_EP0TXSENT                    1

#if USB_CONTROL_USBTMC_RX_BUFFER < USB_MAX_EP0_SIZE
#error Invalid USB_CONTROL_USBTMC_RX_BUFFER buffer size. Must be at least USB_MAX_EP0_SIZE bytes.
#endif
#if USB_CONTROL_USBTMC_TX_BUFFER < USB_MAX_EP0_SIZE
#error Invalid USB_CONTROL_USBTMC_TX_BUFFER buffer size. Must be at least USB_MAX_EP0_SIZE bytes.
#endif

/*---------------------------------------------------------------------*/
/*  USBTMC interface definitions                                       */
/*---------------------------------------------------------------------*/

typedef struct
{
    USBD_HandleTypeDef * pdev; // for @USBD_USBTMC_DataIn_BeginSend()
    sUSBTransfer_t ep0_tx_transfer; // Control-IN EP
    sUSBTransfer_t ep0_rx_transfer; // Control-OUT EP
    sUSBTransfer_t ep1_tx_transfer; // Bulk-IN EP
    sUSBTransfer_t ep1_rx_transfer; // Bulk-OUT EP
    sUSBTransfer_t ep2_tx_transfer; // Interrupt-IN EP

    uint8_t ep0_tx_buffer[ USB_CONTROL_USBTMC_TX_BUFFER ];   // Control-IN EP
    uint8_t ep0_rx_buffer[ USB_CONTROL_USBTMC_RX_BUFFER ];   // Control-OUT EP
    uint8_t ep1_tx_buffer[ USB_BULK_USBTMC_TX_BUFFER ];      // Bulk-IN EP
    uint8_t ep1_rx_buffer[ USB_BULK_USBTMC_RX_BUFFER ];      // Bulk-OUT EP
    uint8_t ep2_tx_buffer[ USB_INTERRUPT_USBTMC_TX_BUFFER ]; // Interrupt-IN EP
}
USBD_USBTMC_HandleTypeDef;

static inline size_t usbd_usbtmc_get_control_tx_transfer_size()
{
   return sizeof(((USBD_USBTMC_HandleTypeDef*)0)->ep0_tx_buffer);
}

static inline size_t usbd_usbtmc_get_control_rx_transfer_size()
{
   return sizeof(((USBD_USBTMC_HandleTypeDef*)0)->ep0_rx_buffer);
}

static inline size_t usbd_usbtmc_get_bulk_tx_transfer_size()
{
   return sizeof(((USBD_USBTMC_HandleTypeDef*)0)->ep1_tx_buffer);
}

static inline size_t usbd_usbtmc_get_bulk_rx_transfer_size()
{
   return sizeof(((USBD_USBTMC_HandleTypeDef*)0)->ep1_rx_buffer);
}

static inline size_t usbd_usbtmc_get_interrupt_tx_transfer_size()
{
   return sizeof(((USBD_USBTMC_HandleTypeDef*)0)->ep2_tx_buffer);
}

#if CONFIG_USB_USBTMC_ENABLE
extern const USBD_COMMON_ItfTypeDef USBD_Interface_Usbtmc_UsbInterface;
#endif

extern const USBD_ClassTypeDef  USBD_USBTMC;
#define USBD_USBTMC_CLASS    &USBD_USBTMC

sUSBTransfer_t * USBD_USBTMC_GetDataTxTransferHandle();        // Bulk-IN (USBTMC)
sUSBTransfer_t * USBD_USBTMC_GetDataRxTransferHandle();        // Bulk-OUT (USBTMC)
sUSBTransfer_t * USBD_USBTMC_GetInterruptTxTransferHandle();   // Interrupt-IN (USBTMC)

const USBD_DescriptorsTypeDef * USBD_USBTMC_GetDescriptorHandlers();

// USBD_USBTMC_DataIn_BeginSend
// Initiates the first packet sending using virtual DataIN event
// @bTxAlreadyPrepared - if the TX-transfer is already prepared to transmission, do not reset transfer, do not call TX-handler
uint8_t USBD_USBTMC_DataIn_BeginSend( uint8_t epnum, bool bTxAlreadyPrepared );

// USBD_USBTMC_DataIn_ZeroSend
// Queue zero packet in EP IN
uint8_t USBD_USBTMC_DataIn_ZeroSend( uint8_t epnum );

// USBD_USBTMC_ClearEP()
// Restore specfied halted data-endpoint by it's address and reset the buffer
// Recover the Rx-transfer for specified endpoint
// Restarts the receiving for the specified endpoint
void USBD_USBTMC_ClearEP( uint8_t epaddr );

// USBD_USBTMC_StallEP()
// Halt specfied data-endpoint by it's address and reset the buffer
void USBD_USBTMC_StallEP( uint8_t epaddr );

// USBD_USBTMC_NakEP()
// Reset hardware EP buffer and setup the end-point to Not-acknowlage mode
// No data will be returned by EP util the data will be queued again
void USBD_USBTMC_NakEP( uint8_t epaddr );

// USBD_USBTMC_GetTxQueuedSize
// Returns amound of bytes queued in USBTMC IN endpoint.
// @epaddr - USBTMC EP-IN number
// @pCount - pointer to the value to store the packet length, can not be NULL
// Return: true if there is a queued packet in EP-IN, the length is stored in @pCount
//         false if there no queued packet in EP-IN, @pCount has not been modified
bool USBD_USBTMC_GetTxQueuedSize( uint8_t epaddr, uint32_t * pCount );

#ifdef __cplusplus
}
#endif

#endif  /* __USB_USBTMC_H */
