gadget-kspconsole/unicore-mx/lib/usbd/usbd_private.h
Daniele Lacamera b8a7ffcd31 Initial import
2023-11-27 15:16:45 +01:00

438 lines
12 KiB
C

/**
* @defgroup usbd_private_defines USB Private Structures
*
* @brief <b>Defined Constants and Types for the USB Private Structures</b>
*
* @ingroup USBD_defines
*
* @author @htmlonly &copy; @endhtmlonly 2016
* Kuldeep Singh Dhaka <kuldeepdhaka9@gmail.com>
*
* @author @htmlonly &copy; @endhtmlonly 2010
* Gareth McMullin <gareth@blacksphere.co.nz>
*
* @date 11 September 2016
*
* LGPL License Terms @ref lgpl_license
*/
/*
* This file is part of the unicore-mx project.
*
* Copyright (C) 2010 Gareth McMullin <gareth@blacksphere.co.nz>
* Copyright (C) 2016, 2017 Kuldeep Singh Dhaka <kuldeepdhaka9@gmail.com>
*
* This library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
/**@{*/
#ifndef UNICOREMX_USBD_PRIVATE_H
#define UNICOREMX_USBD_PRIVATE_H
#include <unicore-mx/usbd/usbd.h>
/**
* Compile time configuration: \n
* USBD_URB_COUNT: Number of URB Object to allocate (default: 20) \n
* USBD_ENABLE_TIMEOUT: Define to enable timeout functionality (default: undefined)
*/
#if defined(USBD_URB_COUNT) && (USBD_URB_COUNT < 1)
# error "Sanity check failed!!! go get sleep." \
"USBD_URB_COUNT less than 1 is meaningless in our universe."
#endif
#if !defined(USBD_URB_COUNT)
# define USBD_URB_COUNT 20
#endif
#if defined(USBD_INTEFACE_MAX) && (USBD_INTEFACE_MAX < 0)
# error "Sanity check failed!!! go get sleep." \
"USBD_INTEFACE_MAX less than 0 is meaningless in our universe."
#endif
/**
* Maximum number of interface for memory to allocate.
* The allocated memory is used keep track of the SET_INTERFACE setting
* If the value is 0, no memory is allocated
*
* @note If a value could not be stored
* (due to no memory allocated OR allocate memory not enought to store the value),
* then the following behavour apply:
* - SET_INTEFACE for other than alternate-setting = 0 will always result in STALL.
* - GET_INTEFACE will always result in STALL
*/
#if !defined(USBD_INTERFACE_MAX)
# define USBD_INTERFACE_MAX 8
#endif
#if defined(__DOXYGEN__)
# define USBD_ENABLE_TIMEOUT
#endif
/* define macro "USBD_ENABLE_TIMEOUT" to enable timeout functionality.
* It can help save processing as well as RAM.
* By default - disabled. */
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct usbd_urb {
uint64_t id;
usbd_transfer transfer;
#if defined(USBD_ENABLE_TIMEOUT)
uint64_t timeout_on;
#endif
struct usbd_urb *next;
};
typedef struct usbd_urb usbd_urb;
/** Internal collection of device information. */
struct usbd_device {
/**
* Current configuration of the device.
* If NULL, then the device is in {default, address} state
* If not NULL, then device is in configured state.
*/
const struct usb_config_descriptor *current_config;
#if (USBD_INTERFACE_MAX > 0)
/**
* Store the current alternate setting of interfaces.
* Interface number is used as array index (used to store).
*/
const struct usb_interface_descriptor *current_iface[USBD_INTERFACE_MAX];
#endif
/** Device descriptor and other details. */
const struct usbd_info *info;
struct {
/** invoked on bus-reset */
usbd_generic_callback reset;
/** invoked on bus-suspend */
usbd_generic_callback suspend;
/** invoked on bus-resume */
usbd_generic_callback resume;
/** invoked when sof received */
usbd_generic_callback sof;
/** invoked on SETUP packet */
usbd_setup_callback setup;
/** invoked on SET_CONFIGURATION */
usbd_set_config_callback set_config;
/** invoked on SET_INTERFACE */
usbd_set_interface_callback set_interface;
} callback;
/** Backend */
const struct usbd_backend *backend;
/** Backend configuration */
const struct usbd_backend_config *config;
#if defined(USBD_ENABLE_TIMEOUT)
uint64_t last_poll;
#endif
/**
* @a active - URB that are being processed
* @a waiting - URB that are waiting to be added to @a active once
* the endpoint become free.
*/
struct {
/**
* @a head - Head of the Queue
* @a tail - Tail of the Queue
*/
struct usbd_urb_queue {
usbd_urb *head, *tail;
} active, waiting;
/**
* 1 Means the endpoint is free to be used.
* 0 Means the endpoint is being used.
*
* BITn (where n = 0-15) - Endpoint OUT n
* BIT(n+16) (where n = 0-15) - Endpoint IN n
*
* If the endpoint is bidirectional (control) then,
* the IN and OUT need to be marked together.
*/
uint32_t ep_free;
/** List of unused objects (invalid) and empty shell for transfer */
usbd_urb *unused;
/** Array of URB allocated at compile time */
usbd_urb arr[USBD_URB_COUNT];
uint64_t next_id;
/** Only allow EP0 transfer.
* main use case is, ep_prepare_start and ep_prepare_end block */
bool force_all_new_urb_to_waiting;
} urbs;
#if defined(USBD_DEVICE_EXTRA)
USBD_DEVICE_EXTRA
#endif
};
/* Functions provided by the hardware abstraction. */
struct usbd_backend {
usbd_device * (*init)(const usbd_backend_config *config);
void (*set_address)(usbd_device *dev, uint8_t addr);
uint8_t (*get_address)(usbd_device *dev);
void (*ep_prepare_start)(usbd_device *dev);
void (*ep_prepare)(usbd_device *dev, uint8_t addr, usbd_ep_type type,
uint16_t max_size, uint16_t interval, usbd_ep_flags flags);
void (*ep_prepare_end)(usbd_device *dev);
void (*set_ep_dtog)(usbd_device *dev, uint8_t addr, bool dtog);
bool (*get_ep_dtog)(usbd_device *dev, uint8_t addr);
void (*set_ep_stall)(usbd_device *dev, uint8_t addr, bool stall);
bool (*get_ep_stall)(usbd_device *dev, uint8_t addr);
void (*poll)(usbd_device *dev);
void (*disconnect)(usbd_device *dev, bool disconnected);
void (*enable_sof)(usbd_device *dev, bool enable);
usbd_speed (*get_speed)(usbd_device *dev);
void (*urb_submit)(usbd_device *dev, usbd_urb *urb);
void (*urb_cancel)(usbd_device *dev, usbd_urb *urb);
/* Frame number */
uint16_t (*frame_number)(usbd_device *dev);
/*
* this is to tell usb generic code
* that address need to be set before status-stage.
*/
bool set_address_before_status;
/* somewhere in backend header or code.
* WARNING: this will make the struct size variable.
* (cannot be used for array - anyway no one will be used it for array) */
#if defined(USBD_BACKEND_EXTRA)
USBD_BACKEND_EXTRA
#endif
};
#if defined(USBD_DEBUG)
extern void usbd_log_puts(const char *arg);
extern void usbd_log_printf(const char *fmt, ...)
__attribute__((format(printf, 1, 2)));
# include <inttypes.h>
# define LOG(str) usbd_log_puts(str)
# define LOGF(fmt,...) usbd_log_printf(fmt, ##__VA_ARGS__)
#else
# define LOG(str)
# define LOGF(fmt,...)
#endif
#define NEW_LINE "\n"
#define LOG_LN(str) LOG(str); LOG(NEW_LINE)
#define LOGF_LN(fmt,...) LOGF(fmt, __VA_ARGS__); LOG(NEW_LINE)
#define LOG_CALL LOG("inside "); LOG_LN(__func__);
/** Convert milliseconds to microseconds */
#define MS2US(ms) ((ms) * 1000)
#define DIVIDE_AND_CEIL(divident, divisor) \
((divident) + (divisor) - 1) / (divisor)
/**
* Perform a callback for transfer
* @param[in] t Transfer
* @param[in] status Status of transfer
* @param[in] transferred Length of data transferred
*/
#define TRANSFER_CALLBACK(dev, transfer, status, urb_id) \
if ((transfer)->callback != NULL) { \
(transfer)->callback((dev), (transfer), (status), (urb_id)); \
}
/* Just for readability, nothing special */
#define IS_OUT_ENDPOINT(ep_addr) (!((ep_addr) & 0x80))
#define IS_IN_ENDPOINT(ep_addr) (!!((ep_addr) & 0x80))
#define ENDPOINT_NUMBER(ep_addr) ((ep_addr) & 0x7F)
void usbd_urb_complete(usbd_device *dev, usbd_urb *urb,
usbd_transfer_status status);
void usbd_urb_detach_from_active(usbd_device *dev, usbd_urb *urb);
void usbd_urb_schedule(usbd_device *dev);
usbd_urb *usbd_find_active_urb(usbd_device *dev, uint8_t ep);
void *usbd_urb_get_buffer_pointer(usbd_device *dev, usbd_urb *urb, size_t len);
void usbd_urb_inc_data_pointer(usbd_device *dev, usbd_urb *urb, size_t len);
#if defined(USBD_ENABLE_TIMEOUT)
void usbd_timeout_checkup(usbd_device *dev, uint64_t now);
#endif
void usbd_purge_all_transfer(usbd_device *dev, usbd_transfer_status status);
void usbd_put_all_urb_into_unused(usbd_device *dev);
void usbd_purge_all_non_ep0_transfer(usbd_device *dev,
usbd_transfer_status status);
static inline uint32_t ep_free_mask(uint8_t ep_addr);
static inline void usbd_handle_suspend(usbd_device *dev);
static inline void usbd_handle_resume(usbd_device *dev);
static inline void usbd_handle_sof(usbd_device *dev);
static inline void usbd_handle_setup(usbd_device *dev, uint8_t ep,
const struct usb_setup_data *setup_data);
static inline void usbd_handle_reset(usbd_device *dev);
static inline bool is_ep_free(usbd_device *dev, uint8_t ep_addr);
static inline void mark_ep_as_free(usbd_device *dev, uint8_t ep_addr, bool yes);
/**
* Get the DTOG bit mask for @a ep_addr
* @param[in] ep_addr Endpoint address (including direction)
* @return mask
*/
static inline uint32_t ep_free_mask(uint8_t ep_addr)
{
uint32_t num = ENDPOINT_NUMBER(ep_addr);
if (IS_IN_ENDPOINT(ep_addr)) {
num += 16;
}
return 1 << num;
}
/**
* SUSPEND detected on bus
* @param[in] dev USB Device
*/
static inline void usbd_handle_suspend(usbd_device *dev)
{
LOG_LN("SUSPEND detected!");
if (dev->callback.suspend != NULL) {
dev->callback.suspend(dev);
}
}
/**
* RESUME detected on bus
* @param[in] dev USB Device
*/
static inline void usbd_handle_resume(usbd_device *dev)
{
LOG_LN("RESUME detected!");
if (dev->callback.resume != NULL) {
dev->callback.resume(dev);
}
}
/**
* SOF detected on bus
* @param[in] dev USB Device
*/
static inline void usbd_handle_sof(usbd_device *dev)
{
/* Oh no! dont print anything here... or else... */
if (dev->callback.sof != NULL) {
dev->callback.sof(dev);
}
}
/**
* Called by backend to pass the setup data when a SETUP packet is recevied
* on control endpoint.
* @param[in] dev USB Device
* @param[in] ep Endpoint on which SETUP was received
* @param[in] setup_data Setup Data
* @note setup_data is only expected to be valid till this function do not return.
*/
static inline void usbd_handle_setup(usbd_device *dev, uint8_t ep,
const struct usb_setup_data *setup_data)
{
if (dev->callback.setup != NULL) {
dev->callback.setup(dev, ep, setup_data);
} else if (!ep) {
/* No callback registered, route to ep0 by default */
usbd_ep0_setup(dev, setup_data);
} else {
LOGF_LN("WARNING: Application code need to handle SETUP packet on "
"0x%"PRIx8", (stalling...)", ep);
usbd_set_ep_stall(dev, ENDPOINT_NUMBER(ep) | 0x80, true);
usbd_set_ep_stall(dev, ENDPOINT_NUMBER(ep), true);
}
}
/**
* RESET detected on bus
* @param[in] dev USB Device
*/
static inline void usbd_handle_reset(usbd_device *dev)
{
LOG_LN("RESET detected");
usbd_purge_all_transfer(dev, USBD_ERR_CONN);
dev->urbs.force_all_new_urb_to_waiting = false;
dev->current_config = NULL;
if (dev->callback.reset != NULL) {
dev->callback.reset(dev);
}
}
/**
* Check if the endpoint is free
* @param[in] dev USB Device
* @param[in] ep_addr Endpoint (including direction)
*/
static inline bool is_ep_free(usbd_device *dev, uint8_t ep_addr)
{
return !!(dev->urbs.ep_free & ep_free_mask(ep_addr));
}
/**
* Mark the endpoint as unused on @a yes
* @param[in] dev USB Device
* @param[in] ep_addr Endpoint address (including direction)
* @param[in] yes Yes (if true, mark it as unused)
*/
static inline void mark_ep_as_free(usbd_device *dev, uint8_t ep_addr, bool yes)
{
uint32_t mask = ep_free_mask(ep_addr);
if (yes) {
dev->urbs.ep_free |= mask;
} else {
dev->urbs.ep_free &= ~mask;
}
}
/* Do not appear to belong to the API, so are omitted from docs */
/**@}*/
#endif