1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/sys/include/usb/usbus.h

794 lines
26 KiB
C

/*
* Copyright (C) 2018 Koen Zandberg
*
* This file is subject to the terms and conditions of the GNU Lesser
* General Public License v2.1. See the file LICENSE in the top level
* directory for more details.
*/
/**
* @defgroup usb_usbus USBUS device and endpoint manager
* @ingroup usb
* @brief USBUS (Universal Serial Bus Unified Stack), USB device
* management interface
*
* @{
*
* @file
* @brief USBUS basic interface
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef USB_USBUS_H
#define USB_USBUS_H
#include <stdint.h>
#include <stdlib.h>
#include "clist.h"
#include "event.h"
#include "sched.h"
#include "modules.h"
#include "msg.h"
#include "thread.h"
#include "usb.h"
#include "periph/usbdev.h"
#include "usb/descriptor.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @defgroup usb_usbus_conf USBUS compile time configurations
* @ingroup usb_conf
* @{
*/
/**
* @brief USBUS thread stack size
*/
#ifndef USBUS_STACKSIZE
#define USBUS_STACKSIZE (THREAD_STACKSIZE_DEFAULT)
#endif
/**
* @brief USBUS thread priority
*/
#ifndef USBUS_PRIO
#define USBUS_PRIO (THREAD_PRIORITY_MAIN - 6)
#endif
/**
* @brief USBUS auto attach setting
*
* When set to 1, the USBUS thread will automatically enable the USB pull-up
* resistor after initializing the thread. This will signal to the host
* that the USB peripheral is ready for use.
*/
#ifndef CONFIG_USBUS_AUTO_ATTACH
/* Check for Kconfig usage */
#if !IS_ACTIVE(CONFIG_MODULE_USBUS)
#define CONFIG_USBUS_AUTO_ATTACH 1
#endif
#endif
/**
* @brief USBUS MSC auto MTD setting
*
* When set to 1, the USBUS MSC module will automatically create a LUN for
* each MTD device defined in `board.h`.
*/
#ifndef CONFIG_USBUS_MSC_AUTO_MTD
#define CONFIG_USBUS_MSC_AUTO_MTD 1
#endif
/**
* @brief USBUS endpoint 0 buffer size
*
* This configures the buffer size of the control endpoint. Unless you transfer
* large amount of data often over the control endpoint, a minimal size should
* be sufficient
*/
#if IS_ACTIVE(CONFIG_USBUS_EP0_SIZE_8)
#define CONFIG_USBUS_EP0_SIZE 8
#elif IS_ACTIVE(CONFIG_USBUS_EP0_SIZE_16)
#define CONFIG_USBUS_EP0_SIZE 16
#elif IS_ACTIVE(CONFIG_USBUS_EP0_SIZE_32)
#define CONFIG_USBUS_EP0_SIZE 32
#elif IS_ACTIVE(CONFIG_USBUS_EP0_SIZE_64)
#define CONFIG_USBUS_EP0_SIZE 64
#endif
#ifndef CONFIG_USBUS_EP0_SIZE
#define CONFIG_USBUS_EP0_SIZE 64
#endif
/** @} */
/**
* @brief USBUS thread name
*/
#define USBUS_TNAME "usbus"
/**
* @name USBUS thread flags
*
* Thread flags used by the USBUS thread. @ref THREAD_FLAG_EVENT is also used,
* but defined elsewhere
* @{
*/
#define USBUS_THREAD_FLAG_USBDEV (0x02) /**< usbdev esr needs handling */
#define USBUS_THREAD_FLAG_USBDEV_EP (0x04) /**< One or more endpoints requires
servicing */
/** @} */
/**
* @name USBUS handler subscription flags
*
* @{
*/
#define USBUS_HANDLER_FLAG_RESET (0x0001) /**< Report reset event */
#define USBUS_HANDLER_FLAG_SOF (0x0002) /**< Report SOF events */
#define USBUS_HANDLER_FLAG_SUSPEND (0x0004) /**< Report suspend events */
#define USBUS_HANDLER_FLAG_RESUME (0x0008) /**< Report resume from suspend */
#define USBUS_HANDLER_FLAG_TR_STALL (0x0020) /**< Report transfer stall complete */
/** @} */
/**
* @name USBUS URB flags
*
* @{
*/
/**
* @brief End the URB with a zero length packet if the URB is a full number of
* USB transfers in length
*/
#define USBUS_URB_FLAG_AUTO_ZLP (0x0001)
/**
* @brief URB needs a zero length packet and it is requested
* @internal
*/
#define USBUS_URB_FLAG_NEEDS_ZLP (0x1000)
/**
* @brief URB must be cancelled after the next finished xmit
* @internal
*/
#define USBUS_URB_FLAG_CANCELLED (0x2000)
/** @} */
/**
* @brief USB handler events
*/
typedef enum {
USBUS_EVENT_USB_RESET, /**< USB reset event */
USBUS_EVENT_USB_SOF, /**< USB start of frame received */
USBUS_EVENT_USB_SUSPEND, /**< USB suspend condition detected */
USBUS_EVENT_USB_RESUME, /**< USB resume condition detected */
} usbus_event_usb_t;
/**
* @brief USB endpoint transfer status events
*/
typedef enum {
USBUS_EVENT_TRANSFER_COMPLETE, /**< Transfer successfully completed */
USBUS_EVENT_TRANSFER_FAIL, /**< Transfer nack replied by peripheral */
USBUS_EVENT_TRANSFER_STALL, /**< Transfer stall replied by peripheral */
} usbus_event_transfer_t;
/**
* @brief state machine states for the global USBUS thread
*/
typedef enum {
USBUS_STATE_DISCONNECT, /**< Device is disconnected from the host */
USBUS_STATE_RESET, /**< Reset condition received */
USBUS_STATE_ADDR, /**< Address configured */
USBUS_STATE_CONFIGURED, /**< Peripheral is configured */
USBUS_STATE_SUSPEND, /**< Peripheral is suspended by the host */
} usbus_state_t;
/**
* @brief USBUS control request state machine
*/
typedef enum {
USBUS_CONTROL_REQUEST_STATE_READY, /**< Ready for new control request */
USBUS_CONTROL_REQUEST_STATE_INDATA, /**< Request received with expected
DATA IN stage */
USBUS_CONTROL_REQUEST_STATE_OUTACK, /**< Expecting a zero-length ack OUT
request from the host */
USBUS_CONTROL_REQUEST_STATE_OUTDATA, /**< Data OUT expected */
USBUS_CONTROL_REQUEST_STATE_INACK, /**< Expecting a zero-length ack IN
request from the host */
} usbus_control_request_state_t;
/**
* @brief USBUS string type
*/
typedef struct usbus_string {
struct usbus_string *next; /**< Ptr to the next registered string */
const char *str; /**< C string to use as content */
uint16_t idx; /**< USB string index */
} usbus_string_t;
/**
* @brief USBUS context forward declaration
*/
typedef struct usbus usbus_t;
/**
* @brief USBUS event handler forward declaration
*/
typedef struct usbus_handler usbus_handler_t;
/**
* @brief descriptor length types for USB descriptor generators
*/
typedef enum {
USBUS_DESCR_LEN_FIXED, /**< Descriptor always generates a fixed length */
USBUS_DESCR_LEN_FUNC, /**< Descriptor length is calculated by a function */
} usbus_descr_len_type_t;
/**
* @brief USBUS descriptor generator function pointers
*/
typedef struct {
/**
* @brief function pointer to format the descriptor content of this
* descriptor generator. The content of this descriptor is prefixes before
* the descriptor of the object it is part of.
*
* @param usbus The usbus context
* @param arg Additional argument for the descriptor generator
*
* @return Length of the generated descriptor
*/
size_t (*fmt_pre_descriptor)(usbus_t *usbus, void *arg);
/**
* @brief function pointer to format the descriptor content of this
* descriptor generator. The content of this descriptor is appended after
* the descriptor of the object it is part of.
*
* @param usbus The usbus context
* @param arg Additional argument for the descriptor generator
*
* @return Length of the generated descriptor
*/
size_t (*fmt_post_descriptor)(usbus_t *usbus, void *arg);
union {
/**
* @brief USBUS generic descriptor generator generated length
*
* Must return the total length of the descriptors that will be
* generated by @ref fmt_pre_descriptor and @ref fmt_post_descriptor
* This function is used when @ref len_type is set to
* @ref USBUS_DESCR_LEN_FUNC.
*
* @param usbus The usbus context
* @param arg Additional argument for the descriptor generators
*
* @return Length of the generated descriptors
*/
size_t (*get_descriptor_len)(usbus_t *usbus, void *arg);
/**
* @brief Fixed total length of the generated descriptors
*
* Must return the total length of the descriptors that will be
* generated by the @ref fmt_pre_descriptor and @ref fmt_post_descriptor.
* This value is used when @ref len_type is set to
* @ref USBUS_DESCR_LEN_FIXED.
*/
size_t fixed_len;
} len; /**< Fixed or generated length of the descriptor */
usbus_descr_len_type_t len_type; /**< Either USBUS_DESCR_LEN_FIXED or USBUS_DESCR_LEN_FUNC */
} usbus_descr_gen_funcs_t;
/**
* @brief USBUS descriptor generator
*
* The functions are called to allow custom modules to define their own
* descriptors in addition to the USB descriptor. The top level (@ref usbus_t), the
* interface (@ref usbus_interface_t), interface alternative settings
* (@ref usbus_interface_alt_t) and endpoints (@ref usbus_endpoint_t) allow for
* generating additional descriptors
*/
typedef struct usbus_descr_gen {
struct usbus_descr_gen *next; /**< ptr to the next descriptor generator */
const usbus_descr_gen_funcs_t *funcs; /**< Function pointers */
void *arg; /**< Extra context argument for the
descriptor functions */
} usbus_descr_gen_t;
/**
* @brief USBUS endpoint context
*/
typedef struct usbus_endpoint {
struct usbus_endpoint *next; /**< Next endpoint in the
@ref usbus_interface_t list of
endpoints */
usbus_descr_gen_t *descr_gen; /**< Linked list of optional additional
descriptor generators */
usbdev_ep_t *ep; /**< ptr to the matching usbdev endpoint */
#ifdef MODULE_USBUS_URB
clist_node_t urb_list; /**< clist of urbs */
#endif
uint16_t maxpacketsize; /**< Max packet size of this endpoint */
uint8_t interval; /**< Poll interval for interrupt endpoints */
bool active; /**< If the endpoint should be activated after
reset */
bool halted; /**< Endpoint is halted */
} usbus_endpoint_t;
/**
* @brief USBUS USB request/response block
*/
typedef struct usbus_urb {
clist_node_t list; /**< clist block in the queue */
uint32_t flags; /**< Transfer flags */
uint8_t *buf; /**< Pointer to the (aligned) buffer */
size_t len; /**< Length of the data */
size_t transferred; /**< amount transferred, only valid for OUT */
} usbus_urb_t;
/**
* @brief USBUS interface alternative setting
*
* Used for specifying alternative interfaces for an @ref usbus_interface_t
*/
typedef struct usbus_interface_alt {
struct usbus_interface_alt *next; /**< Next alternative setting */
usbus_descr_gen_t *descr_gen; /**< Linked list of optional additional
descriptor generators */
usbus_endpoint_t *ep; /**< List of associated endpoints for
this alternative setting */
usbus_string_t *descr; /**< Descriptor string */
} usbus_interface_alt_t;
/**
* @brief USBUS interface
*/
typedef struct usbus_interface {
struct usbus_interface *next; /**< Next interface (set by USBUS during
registration) */
usbus_descr_gen_t *descr_gen; /**< Linked list of optional additional
descriptor generators */
usbus_endpoint_t *ep; /**< Linked list of endpoints belonging
to this interface */
struct usbus_interface_alt *alts; /**< List of alt settings */
usbus_handler_t *handler; /**< Handlers for this interface */
usbus_string_t *descr; /**< Descriptor string */
uint16_t idx; /**< Interface index, (set by USBUS
during registration */
uint8_t class; /**< USB interface class */
uint8_t subclass; /**< USB interface subclass */
uint8_t protocol; /**< USB interface protocol */
} usbus_interface_t;
/**
* @brief USBUS event handler function pointers
*/
typedef struct usbus_handler_driver {
/**
* @brief Initialize the event handler
*
* This function is called in the USBUS thread context to initialize the
* event handler
*
* @param usbus USBUS context
* @param handler handler context
*/
void (*init)(usbus_t * usbus, struct usbus_handler *handler);
/**
* @brief event handler function
*
* This function is passed USBUS events
*
* @param usbus USBUS context
* @param handler handler context
* @param event @ref usbus_event_usb_t event to handle
*/
void (*event_handler)(usbus_t * usbus, struct usbus_handler *handler,
usbus_event_usb_t event);
/**
* @brief transfer handler function
*
* This function receives transfer based events
*
* @param usbus USBUS context
* @param handler handler context
* @param ep usbdev endpoint that triggered the event
* @param event @ref usbus_event_transfer_t event
*/
void (*transfer_handler)(usbus_t * usbus, struct usbus_handler *handler,
usbdev_ep_t *ep, usbus_event_transfer_t event);
/**
* @brief control request handler function
*
* This function receives USB control requests from the USBUS stack.
*
* @param usbus USBUS context
* @param handler handler context
* @param state control request state
* @param setup setup packet
*
* @return Size of the returned data when the request is handled
* @return negative to have the stack return an USB stall to the
* host
* @return zero when the request is not handled by this handler
*/
int (*control_handler)(usbus_t * usbus, struct usbus_handler *handler,
usbus_control_request_state_t state,
usb_setup_t *request);
} usbus_handler_driver_t;
/**
* @brief USBUS handler struct
*
* Inherit from this struct for custom USB functionality
*/
struct usbus_handler {
struct usbus_handler *next; /**< List of handlers (to be used by
@ref usbus_t) */
const usbus_handler_driver_t *driver; /**< driver for this handler */
usbus_interface_t *iface; /**< Interface this handler belongs
to */
uint32_t flags; /**< Report flags */
};
/**
* @brief USBUS context struct
*/
struct usbus {
usbus_string_t manuf; /**< Manufacturer string */
usbus_string_t product; /**< Product string */
usbus_string_t config; /**< Configuration string */
usbus_string_t serial; /**< serial string */
usbus_endpoint_t ep_out[USBDEV_NUM_ENDPOINTS]; /**< USBUS OUT endpoints */
usbus_endpoint_t ep_in[USBDEV_NUM_ENDPOINTS]; /**< USBUS IN endpoints */
event_queue_t queue; /**< Event queue */
usbdev_t *dev; /**< usb phy device of the usb manager */
usbus_handler_t *control; /**< Ptr to the control endpoint handler */
usbus_descr_gen_t *descr_gen; /**< Linked list of top level descriptor
generators */
usbus_string_t *strings; /**< List of descriptor strings */
usbus_interface_t *iface; /**< List of USB interfaces */
usbus_handler_t *handlers; /**< List of event callback handlers */
uint32_t ep_events; /**< bitflags with endpoint event state */
kernel_pid_t pid; /**< PID of the usb manager's thread */
uint16_t str_idx; /**< Number of strings registered */
usbus_state_t state; /**< Current state */
usbus_state_t pstate; /**< state to recover to from suspend */
uint8_t addr; /**< Address of the USB peripheral */
bool wakeup_enabled; /**< Remote wakeup device feature status */
#ifndef CONFIG_USB_SERIAL_STR
/**
* @brief Hex representation of the device serial number
*/
char serial_str[2 * CONFIG_USB_SERIAL_BYTE_LENGTH + 1];
#endif
};
/**
* @brief Submit an event to the usbus thread
*
* @param usbus USBUS context
* @param event event to post
*/
static inline void usbus_event_post(usbus_t *usbus, event_t *event)
{
event_post(&usbus->queue, event);
}
/**
* @brief Add a string descriptor to the USBUS thread context
*
* @param[in] usbus USBUS context
* @param[in] desc string descriptor context
* @param[in] str C string to use
*
* @return Index of the string descriptor
*/
uint16_t usbus_add_string_descriptor(usbus_t *usbus, usbus_string_t *desc,
const char *str);
/**
* @brief Add an interface to the USBUS thread context
*
* @param[in] usbus USBUS context
* @param[in] iface USB interface to add
*
* @return interface index
*/
uint16_t usbus_add_interface(usbus_t *usbus, usbus_interface_t *iface);
/**
* @brief Add alternate settings to a given interface
*
* @param[in] iface USB interface
* @param[in] alt alternate settings interface to add
*/
void usbus_add_interface_alt(usbus_interface_t *iface,
usbus_interface_alt_t *alt);
/**
* @brief Find an endpoint from an interface based on the endpoint properties
*
* This iterates over the endpoints in an interface and will return the first
* endpoint from the interface matching the @p type and @p dir. It will return
* NULL when no matching endpoint is found.
*
* @param[in] interface interface to look in
* @param[in] type endpoint type to match
* @param[in] dir endpoint direction to match
*
* @return ptr to the first matching endpoint
* @return NULL when no endpoint is found
*/
usbus_endpoint_t *usbus_interface_find_endpoint(usbus_interface_t *interface,
usb_ep_type_t type,
usb_ep_dir_t dir);
/**
* @brief Add an endpoint to the specified interface
*
* An @ref usbdev_ep_t is requested from the low level peripheral matching the
* type, direction and buffer length.
*
* @param[in] usbus USBUS context
* @param[in] iface USB interface to add the endpoint to
* @param[in] type USB endpoint type
* @param[in] dir USB endpoint direction
* @param[in] len Buffer space for the endpoint to allocate
*
* @return Pointer to the endpoint struct
* @return NULL when no endpoint available
*/
usbus_endpoint_t *usbus_add_endpoint(usbus_t *usbus, usbus_interface_t *iface,
usb_ep_type_t type, usb_ep_dir_t dir,
size_t len);
/**
* @brief Add a generator for generating additional top level USB descriptor
* content
*
* @param[in] usbus USBUS context
* @param[in] descr_gen descriptor generator to add
*/
void usbus_add_conf_descriptor(usbus_t *usbus, usbus_descr_gen_t *descr_gen);
/**
* @brief Add an event handler to the USBUS context
*
* The handler must also belong to an interface
* (@ref usbus_interface_t::handler must point to @p handler) for
* transfer event callbacks to work.
*
* @param[in] usbus USBUS context
* @param[in] handler event handler to register
*/
void usbus_register_event_handler(usbus_t *usbus, usbus_handler_t *handler);
/**
* @brief Initialize an USBUS context.
*
* @param[in] usbus context to initialize
* @param[in] usbdev usbdev peripheral to use by USBUS
*/
void usbus_init(usbus_t *usbus, usbdev_t *usbdev);
/**
* @brief Get the maximum supported bulk endpoint transfer size based on the enumeration speed
*
* Should only be called after enumeration has finished by the peripheral. Calling this in response
* to the @ref USBUS_EVENT_USB_RESET is valid
*/
size_t usbus_max_bulk_endpoint_size(usbus_t *usbus);
/**
* @brief Get the maximum supported interrupt endpoint transfer size based on the enumeration speed
*
* Should only be called after enumeration has finished by the peripheral. Calling this in response
* to the @ref USBUS_EVENT_USB_RESET is valid
*/
size_t usbus_max_interrupt_endpoint_size(usbus_t *usbus);
/**
* @brief Create and start the USBUS thread
*
* @param[in] stack The stack for the USBUS thread.
* @param[in] stacksize Size of @p stack.
* @param[in] priority Priority for the USBUS thread.
* @param[in] name Name for the USBUS thread May be NULL.
* @param[in] usbus context to start the thread for
*/
void usbus_create(char *stack, int stacksize, char priority,
const char *name, usbus_t *usbus);
/**
* @brief Initialize a new URB.
*
* Must be called before submitting an URB to an endpoint.
*
* @note When using this for OUT endpoints, the buffer size and the @p len
* argument must allow for a whole number of max length transfers.
*
* @note Requires the `usbus_urb` module.
*
* @param[in] urb URB to submit
* @param[in] buf Buffer to store or transmit the data from
* @param[in] len Length of @p buf in bytes
* @param[in] flags Flags to set for the URB such as @ref USBUS_URB_FLAG_AUTO_ZLP
*/
static inline void usbus_urb_init(usbus_urb_t *urb,
uint8_t *buf,
size_t len,
uint32_t flags)
{
urb->buf = buf;
urb->len = len;
urb->flags = flags;
urb->transferred = 0;
}
/**
* @brief Submit an URB to an endpoint.
*
* @note Requires the `usbus_urb` module.
*
* @param[in] usbus USBUS context
* @param[in] endpoint USBUS endpoint the URB is queued to
* @param[in] urb URB to submit
*/
void usbus_urb_submit(usbus_t *usbus, usbus_endpoint_t *endpoint, usbus_urb_t *urb);
/**
* @brief Cancel and already queued URB.
*
* The URB will be cancelled after the next transmission if the URB is already
* partially completed. It is up to the handler code to gracefully handle the
* partially aborted transfer on the endpoint pipe.
*
* The callback will be called if the URB was already started and cancelled
* while (partially) transferred
*
* @note Requires the `usbus_urb` module.
*
* @param[in] usbus USBUS context
* @param[in] endpoint USBUS endpoint the URB is queued to
* @param[in] urb URB to cancel
*
* @returns 0 if the URB is partially completed
* 1 if the URB was not yet started
* -1 if the URB was not found in the endpoint queue
*/
int usbus_urb_cancel(usbus_t *usbus, usbus_endpoint_t *endpoint, usbus_urb_t *urb);
/**
* @brief Set the halt condition on an endpoint.
*
* The endpoint will respond with stall to all packets and must explicitly be
* cleared by the host by clearing the halt condition or switching interfaces
*/
void usbus_endpoint_halt(usbus_endpoint_t *ep);
/**
* @brief Clear the halt condition on an endpoint
*
* @note Must only be used when the endpoint is halted and when the host issues
* a SetInterface request on the interface containing the endpoint
*/
void usbus_endpoint_clear_halt(usbus_endpoint_t *ep);
/**
* @brief Enable an endpoint
*
* @note must only be used before the usb peripheral is attached to the host
*
* @param[in] ep endpoint to enable
*/
static inline void usbus_enable_endpoint(usbus_endpoint_t *ep)
{
ep->active = true;
}
/**
* @brief Disable an endpoint
*
* @note must only be used before the usb peripheral is attached to the host
*
* @param[in] ep endpoint to disable
*/
static inline void usbus_disable_endpoint(usbus_endpoint_t *ep)
{
ep->active = false;
}
/**
* @brief enable a specific handler flag
*
* @param[in] handler handler to enable the flag for
* @param[in] flag flag to enable
*/
static inline void usbus_handler_set_flag(usbus_handler_t *handler,
uint32_t flag)
{
handler->flags |= flag;
}
/**
* @brief disable a specific handler flag
*
* @param[in] handler handler to disable the flag for
* @param[in] flag flag to disable
*/
static inline void usbus_handler_remove_flag(usbus_handler_t *handler,
uint32_t flag)
{
handler->flags &= ~flag;
}
/**
* @brief check if a specific handler flag is set
*
* @param[in] handler handler to check for flag
* @param[in] flag flag to check
*
* @return true if the flag is set for this handler
*/
static inline bool usbus_handler_isset_flag(usbus_handler_t *handler,
uint32_t flag)
{
return handler->flags & flag;
}
/**
* @brief enable an URB flag
*
* @param[in] urb URB to enable the flag for
* @param[in] flag flag to enable
*/
static inline void usbus_urb_set_flag(usbus_urb_t *urb,
uint32_t flag)
{
urb->flags |= flag;
}
/**
* @brief disable an URB flag
*
* @param[in] urb URB to disable the flag for
* @param[in] flag flag to disable
*/
static inline void usbus_urb_remove_flag(usbus_urb_t *urb,
uint32_t flag)
{
urb->flags &= ~flag;
}
/**
* @brief check if an URB flag is set
*
* @param[in] urb URB to check for flag
* @param[in] flag flag to check
*
* @return true if the flag is set for this URB
*/
static inline bool usbus_urb_isset_flag(usbus_urb_t *urb,
uint32_t flag)
{
return urb->flags & flag;
}
#ifdef __cplusplus
}
#endif
#endif /* USB_USBUS_H */
/** @} */