1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
RIOT/drivers/include/periph/usbdev.h
Gunar Schorcht d675cd7290 drivers/periph_usbdev: define USBDEV_SET_ADDR_AFTER_STATUS
The address in the USB device can be set either directly after the SETUP stage on receipt of the `SET ADDRESS Request` or after the associated status stage. When the USB device address has to be set depends on the hardware. If `USBDEV_SET_ADDR_AFTER_STATUS` has the value 1 (default), the address is only set in the USB device after the status stage. Overwrite it with 0 in `periph_cpu.h` to set the address already directly after the SETUP stage.
2023-04-16 17:14:08 +02:00

702 lines
21 KiB
C

/*
* Copyright (C) 2018 Koen Zandberg <koen@bergzand.net>
*
* 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 drivers_periph_usbdev usbdev - USB Device Driver API
* @ingroup drivers_periph
* @brief This is a generic low-level USB driver interface
* @{
*
* # About
*
* usbdev specifies a common USB device API for low level USB interfaces. The
* interface is split in a definition for the USB interface hardware and for
* individual USB endpoints.
*
* # Design goals
*
* 1. Support for multiple instances on a single board
* 2. Interface optimized for MCU peripheral interfaces.
*
* # Details
*
* The driver interface is split in two separate parts. One part is a global USB
* device interface, the other is an endpoint control API.
*
* The usb device driver can manage parts of the USB interface itself such as
* the pull up resistor state or the USB speed.
*
* The endpoint control API manages a single endpoint. This allows for a modular
* approach where multiple USB functions/interfaces can be multiplexed over a
* single USB interface. Each interface can be implemented as a separate module.
* The interface handler does not have to care about the usb device itself or
* it's endpoint number. It can simply request an available endpoint from the
* usb device with the @ref usbdev_new_ep function.
*
* Each interface handler can request multiple endpoints from the usbdev device.
* It must supply the expected maximum endpoint transfer size and other
* properties of the endpoint. A pointer to an usbdev endpoint is returned if
* an endpoint fitting the requirements is available.
*
* Data transmission is done via the @ref usbdev_ep_xmit function. A buffer and
* length must be supplied. Received data from the host ends up at this buffer
* automatically by the low level drivers after reception. For reception the
* maximum expected transfer length must be supplied, equal to the maximum
* endpoint transfer size. The data received can be less than this.
*
* For transmitting data back to the host, a similar approach is used. The data
* to be transmitted is supplied as buffer and the @ref usbdev_ep_xmit function
* is called with the buffer and the size of the data.
*
* To ensure that the data buffers adhere to the restrictions of the low level
* USB peripheral memory interface, the specific @ref usbdev_ep_buf_t data type
* must be used. It behaves as a regular `uint8_t` byte buffer, but is
* instantiated with the attributes to ensure that the low level DMA interface
* can use it.
*
* A callback function is required for signalling events from the driver. The
* @ref USBDEV_EVENT_ESR is special in that it indicates that the USB peripheral
* had an interrupt that needs to be serviced in a non-interrupt context. This
* requires the usbdev_esr function to be called. In the case that the event is
* signalled via the @ref usbdev_t::epcb callback, the @ref usbdev_ep_esr
* should be called to service the endpoint specific events from the
* peripheral.
*
* @file
* @brief Definitions low-level USB driver interface
*
* @author Koen Zandberg <koen@bergzand.net>
*/
#ifndef PERIPH_USBDEV_H
#define PERIPH_USBDEV_H
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include "assert.h"
#include "periph_conf.h"
#include "usb.h"
#include "usb/usbopt.h"
#ifdef __cplusplus
extern "C" {
#endif
/**
* @brief usbdev_t forward declaration
*/
typedef struct usbdev usbdev_t;
/**
* @brief usbdev_ep_t forward declaration
*/
typedef struct usbdev_ep usbdev_ep_t;
/**
* @brief USBDEV endpoint buffer CPU-specific requirements
*
* Can be overridden by periph_cpu if needed by the USB peripheral DMA.
*/
#ifndef USBDEV_CPU_DMA_REQUIREMENTS
#define USBDEV_CPU_DMA_REQUIREMENTS
#endif
/**
* @brief Instantiation type for usbdev endpoint buffers
*
* Functions passing around pointers to these buffers can still use `uint8_t`
* for the argument type.
*
* Example usage:
*
* ```
* usbdev_ep_buf_t buffer[64];
* ```
*
* @note This is a define and not a typedef so that the above works. With a
* typedef it would instantiate an array of aligned uint8_t and not an aligned
* array of uint8_t (assuming the requirement is alignment).
*/
#define usbdev_ep_buf_t USBDEV_CPU_DMA_REQUIREMENTS uint8_t
/**
* @brief USBDEV specific requirement for setting the device address
*
* The address in the USB device can be set either directly after the SETUP
* stage on receipt of the `SET ADDRESS Request` or after the associated status
* stage. When the USB device address has to be set depends on the hardware.
* If `USBDEV_CPU_SET_ADDR_AFTER_STATUS` has the value 1 (default), the address
* is only set in the USB device after the status stage. Overwrite it with 0
* in `periph_cpu.h` to set the address already directly after the SETUP stage.
*/
#ifndef USBDEV_CPU_SET_ADDR_AFTER_STATUS
#define USBDEV_CPU_SET_ADDR_AFTER_STATUS 1
#endif
/**
* @brief Number of USB IN and OUT endpoints allocated
*
* Configures the number of endpoints allocated. An equal number of IN and OUT
* endpoints are allocated
*/
#ifndef USBDEV_NUM_ENDPOINTS
#define USBDEV_NUM_ENDPOINTS 8
#endif
/**
* @brief List of event types that can be send from the device driver to the
* upper layer
*/
typedef enum {
/**
* @brief Driver needs it's ESR (event service routine) handled
*/
USBDEV_EVENT_ESR = 1,
/**
* @brief Host connection detected
*
* A host has connected to the device. Detection should happen by detecting
* the USB power, but other detection mechanisms might be used.
*/
USBDEV_EVENT_HOST_CONNECT,
/**
* @brief Host disconnected from the device
*
* Similar to @ref USBDEV_EVENT_HOST_CONNECT, except that the host
* disconnected instead.
*/
USBDEV_EVENT_HOST_DISCONNECT,
/**
* @brief Line reset occurred
*
* A line reset is a host initiated USB reset to the peripheral
*
* The peripheral driver must clears the following settings before
* emitting this event:
* - Device address
* - Endpoint configuration, including stall settings
*/
USBDEV_EVENT_RESET,
/**
* @brief Start of Frame received
*
* A Start of Frame (SoF) packet is received from the host.
*/
USBDEV_EVENT_SOF,
/**
* @brief USB suspend condition active
*/
USBDEV_EVENT_SUSPEND,
/**
* @brief USB suspend condition no longer active
*/
USBDEV_EVENT_RESUME,
/**
* @brief Transaction completed event.
*
* An endpoint must emit this event after a transaction with the host
* occurred to indicate that the data in the buffer is used or new
* depending on the endpoint direction
*/
USBDEV_EVENT_TR_COMPLETE,
/**
* @brief Transaction stall event.
*
* An endpoint should emit this event after a stall reply has been
* transmitted to the host
*/
USBDEV_EVENT_TR_STALL,
/**
* @brief Transaction fail event.
*
* @deprecated This event is deprecated as only a limited number of devices
* implement and doesn't offer value for upper layer code. This
* event will be removed after the 2022.07 release.
*
* An endpoint should emit this event after a nack reply to the host.
*/
USBDEV_EVENT_TR_FAIL,
/* expand list if required */
} usbdev_event_t;
/**
* @brief Event callback for signaling usbdev event to upper layers
*
* @param[in] usbdev usbdev context
* @param[in] event type of the event
*/
typedef void (*usbdev_event_cb_t)(usbdev_t *usbdev, usbdev_event_t event);
/**
* @brief Event callback for signaling endpoint events to upper layers
*
* @param[in] ep usbdev endpoint context
* @param[in] event type of the event
*/
typedef void (*usbdev_ep_event_cb_t)(usbdev_ep_t *ep,
usbdev_event_t event);
/**
* @brief usbdev device descriptor
*/
struct usbdev {
const struct usbdev_driver *driver; /**< usbdev driver struct */
usbdev_event_cb_t cb; /**< Event callback supplied by
* upper layer */
usbdev_ep_event_cb_t epcb; /**< Endpoint event callback for
* upper layer */
void *context; /**< Ptr to the thread context */
};
/**
* @brief usbdev endpoint descriptor
*/
struct usbdev_ep {
usbdev_t *dev; /**< USB device this endpoint belongs to */
size_t len; /**< Endpoint configured max transfer size in bytes */
usb_ep_dir_t dir; /**< Endpoint direction */
usb_ep_type_t type; /**< Endpoint type */
uint8_t num; /**< Endpoint number */
};
/**
* @brief usbdev driver functions
*
* Helpers (such as @ref usbdev_init) are provided and should be used instead.
* Directly calling these functions is not recommended.
*/
typedef struct usbdev_driver {
/**
* @brief Initialize the USB peripheral device
*
* This initializes the USB device but must not enable the USB pull up.
*
* @param[in] dev USB device descriptor
*/
void (*init)(usbdev_t *usbdev);
/**
* @brief Retrieve an USB endpoint of the specified type
*
* requesting an endpoint of @ref USB_EP_TYPE_CONTROL must always return
* endpoint 0 of the specified direction
*
* @param[in] dev USB device descriptor
* @param[in] type USB endpoint type
* @param[in] dir USB endpoint direction
* @param[in] size USB endpoint buffer size
*
* @return ptr to the new USB endpoint descriptor
* @return NULL on error
*/
usbdev_ep_t *(*new_ep)(usbdev_t *dev, usb_ep_type_t type, usb_ep_dir_t dir, size_t size);
/**
* @brief Get an option value from a given usb device
*
* @param[in] dev USB device descriptor
* @param[in] opt option type
* @param[out] value pointer to store the option's value in
* @param[in] max_len maximal amount of byte that fit into @p value
*
* @return number of bytes written to @p value
* @return `< 0` on error 0
*/
int (*get)(usbdev_t *usbdev, usbopt_t opt,
void *value, size_t max_len);
/**
* @brief Set an option value for a given usb device
*
* @param[in] dev USB device descriptor
* @param[in] opt option type
* @param[in] value value to set
* @param[in] value_len the length of @p value
*
* @return number of bytes used from @p value
* @return `< 0` on error
*/
int (*set)(usbdev_t *usbdev, usbopt_t opt,
const void *value, size_t value_len);
/**
* @brief a driver's user-space event service handler
*
* This function will be called from a USB stack's loop when being
* notified by usbdev_event_isr.
*
* @param[in] dev USB device descriptor
*/
void (*esr)(usbdev_t *dev);
/**
* @brief Stall both OUT and IN packets on endpoint 0 until a setup packet
* is received.
*
* @note The stall condition should be cleared automatically either by
* hardware or by the usbdev implementation after receiving a setup packet.
*/
void (*ep0_stall)(usbdev_t *usbdev);
/**
* @brief Initialize the USB endpoint
*
* This initializes the USB endpoint with the settings from the
* @ref usbdev_ep.
*
* @param[in] ep USB endpoint descriptor
*/
void (*ep_init)(usbdev_ep_t *ep);
/**
* @brief Enable or disable the stall condition on the USB endpoint
*
* After clearing the stall condition on the endpoint, the usb peripheral
* must reinitialize the data toggle to DATA0.
*
* @note For enabling stall on endpoint 0 @ref usbdev_driver_t::ep0_stall
* must be used.
*
* @pre (ep->num != 0)
*
* @param[in] ep USB endpoint descriptor
* @param[in] enable True to set stall, false to disable stall
*/
void (*ep_stall)(usbdev_ep_t *ep, bool enable);
/**
* @brief Get an option value from a given usb device endpoint
*
* @param[in] ep USB endpoint descriptor
* @param[in] opt option type
* @param[out] value pointer to store the option's value in
* @param[in] max_len maximum number of byte that fit into @p value
*
* @return number of bytes written to @p value
* @return `< 0` on error
*/
int (*ep_get)(usbdev_ep_t *ep, usbopt_ep_t opt,
void *value, size_t max_len);
/**
* @brief Set an option value for a given usb device endpoint
*
* @param[in] ep USB endpoint descriptor
* @param[in] opt option type
* @param[in] value pointer to the value
* @param[in] value_len the length of @p value
*
* @return number of bytes used from @p value
* @return `< 0` on error
*/
int (*ep_set)(usbdev_ep_t *ep, usbopt_ep_t opt,
const void *value, size_t value_len);
/**
* @brief an endpoint's user-space event handler
*
* Must be called in response to an @ref USBDEV_EVENT_ESR event in
* userspace context.
*
* @param[in] ep USB endpoint descriptor to service
*/
void (*ep_esr)(usbdev_ep_t *ep);
/**
* @brief Transmit a data buffer
*
* Ownership of the @p buf is transferred to the usbdev device after calling
* this. Do not modify (or unallocate) the buffer between calling this and
* when it is released via the @ref USBDEV_EVENT_TR_COMPLETE event.
*
* This clears the stall setting in the endpoint if that is enabled.
*
* @note The @p buf passed here must have been declared as
* @ref usbdev_ep_buf_t before so that DMA restrictions are applied to it
*
* @param[in] ep USB endpoint descriptor
* @param[inout] buf Buffer with the data to transfer
* @param[in] len (Max) Length of the data to transfer
*/
int (*xmit)(usbdev_ep_t *ep, uint8_t *buf, size_t len);
} usbdev_driver_t;
/**
* @brief Low level USB peripheral driver initialization
*
* This function prepares all usbdev peripherals available for initialization
*/
void usbdev_init_lowlevel(void);
/**
* @brief Retrieve usbdev context from the peripheral
*
* @param num usbdev peripheral number to retrieve
*
* @returns the usbdev context at index @p num
*/
usbdev_t *usbdev_get_ctx(unsigned num);
/**
* @brief Initialize the USB peripheral device
*
* @see @ref usbdev_driver_t::init
*
* @pre `(dev != NULL)`
*
* @param dev usb device to initialize
*/
static inline void usbdev_init(usbdev_t *dev)
{
assert(dev);
dev->driver->init(dev);
}
/**
* @brief Retrieve an USB endpoint of the specified type
*
* @see @ref usbdev_driver_t::new_ep
*
* @pre `(dev != NULL)`
*
* @param[in] dev USB device descriptor
* @param[in] type USB endpoint type
* @param[in] dir USB endpoint direction
* @param[in] size Maximum USB endpoint buffer size used
*
* @return ptr to the new USB endpoint descriptor
* @return NULL on error
*/
static inline usbdev_ep_t * usbdev_new_ep(usbdev_t *dev, usb_ep_type_t type,
usb_ep_dir_t dir, size_t size)
{
assert(dev);
return dev->driver->new_ep(dev, type, dir, size);
}
/**
* @brief Get an option value from a given usb device
*
* @see @ref usbdev_driver_t::get
*
* @pre `(dev != NULL)`
*
* @param[in] dev USB device descriptor
* @param[in] opt option type
* @param[out] value pointer to store the option's value in
* @param[in] max_len maximal amount of byte that fit into @p value
*
* @return number of bytes written to @p value
* @return `< 0` on error 0
*/
static inline int usbdev_get(usbdev_t *dev, usbopt_t opt,
void *value, size_t max_len)
{
assert(dev);
return dev->driver->get(dev, opt, value, max_len);
}
/**
* @brief Set an option value for a given usb device
*
* @see @ref usbdev_driver_t::set
*
* @pre `(dev != NULL)`
*
* @param[in] dev USB device descriptor
* @param[in] opt option type
* @param[in] value value to set
* @param[in] value_len the length of @p value
*
* @return number of bytes used from @p value
* @return `< 0` on error
*/
static inline int usbdev_set(usbdev_t *dev, usbopt_t opt,
const void *value, size_t value_len)
{
assert(dev);
return dev->driver->set(dev, opt, value, value_len);
}
/**
* @brief a driver's user-space event service handler
*
* @see @ref usbdev_driver_t::esr
*
* @pre `(dev != NULL)`
*
* @param[in] dev USB device descriptor
*/
static inline void usbdev_esr(usbdev_t *dev)
{
assert(dev);
dev->driver->esr(dev);
}
/**
* @brief Stall both OUT and IN packets on endpoint 0 until a setup packet
* is received.
*
* @see @ref usbdev_driver_t::ep0_stall
*
* @note The stall condition is automatically cleared after receiving a
* setup packet.
*
* @pre `(dev != NULL)`
*
* @param[in] dev USB device descriptor
*/
static inline void usbdev_ep0_stall(usbdev_t *dev)
{
assert(dev);
dev->driver->ep0_stall(dev);
}
/**
* @brief Initialize the USB endpoint
*
* @see @ref usbdev_driver_t::ep_init
*
* @pre `(ep != NULL)`
* @pre `(ep->dev != NULL)`
*
* @param[in] ep USB endpoint descriptor
*/
static inline void usbdev_ep_init(usbdev_ep_t *ep)
{
assert(ep);
assert(ep->dev);
ep->dev->driver->ep_init(ep);
}
/**
* @brief Enable or disable the stall condition on the USB endpoint
*
* @note For enabling stall on endpoint 0 @ref usbdev_driver_t::ep0_stall
* must be used.
*
* @see @ref usbdev_driver_t::ep_stall
*
* @pre (ep->num != 0)
*
* @param[in] ep USB endpoint descriptor
* @param[in] enable True to set stall, false to disable stall
*/
static inline void usbdev_ep_stall(usbdev_ep_t *ep, bool enable)
{
assert(ep);
assert(ep->dev);
ep->dev->driver->ep_stall(ep, enable);
}
/**
* @brief Get an option value from a given usb device endpoint
*
* @see @ref usbdev_driver_t::ep_get
*
* @pre `(ep != NULL)`
* @pre `(ep->dev != NULL)`
*
* @param[in] ep USB endpoint descriptor
* @param[in] opt option type
* @param[out] value pointer to store the option's value in
* @param[in] max_len maximum number of byte that fit into @p value
*
* @return number of bytes written to @p value
* @return `< 0` on error
*/
static inline int usbdev_ep_get(usbdev_ep_t *ep, usbopt_ep_t opt,
void *value, size_t max_len)
{
assert(ep);
assert(ep->dev);
return ep->dev->driver->ep_get(ep, opt, value, max_len);
}
/**
* @brief Set an option value for a given usb device endpoint
*
* @see @ref usbdev_driver_t::ep_set
*
* @pre `(ep != NULL)`
* @pre `(ep->dev != NULL)`
*
* @param[in] ep USB endpoint descriptor
* @param[in] opt option type
* @param[in] value pointer to the value
* @param[in] value_len the length of @p value
*
* @return number of bytes used from @p value
* @return `< 0` on error
*/
static inline int usbdev_ep_set(usbdev_ep_t *ep, usbopt_ep_t opt,
const void *value, size_t value_len)
{
assert(ep);
assert(ep->dev);
return ep->dev->driver->ep_set(ep, opt, value, value_len);
}
/**
* @brief an endpoint's user-space event handler
*
* @see @ref usbdev_driver_t::ep_esr
*
* @pre `(ep != NULL)`
* @pre `(ep->dev != NULL)`
*
* @param[in] ep USB endpoint descriptor to service
*/
static inline void usbdev_ep_esr(usbdev_ep_t *ep)
{
assert(ep);
assert(ep->dev);
ep->dev->driver->ep_esr(ep);
}
/**
* @brief Submit a buffer for a USB endpoint transmission
*
* When dealing with an OUT type endpoint, @p len must be the maximum allowed
* transfer size for the endpoint. The host is allowed to transfer fewer bytes
* than @p len.
*
* @see @ref usbdev_driver_t::xmit
*
* @pre `(ep != NULL)`
* @pre `(ep->dev != NULL)`
*
* @param[in] ep USB endpoint descriptor
* @param[inout] buf Buffer to submit for transmission
* @param[in] len length of the buffer in bytes to be transmitted or received
*/
static inline int usbdev_ep_xmit(usbdev_ep_t *ep, uint8_t *buf, size_t len)
{
assert(ep);
assert(ep->dev);
return ep->dev->driver->xmit(ep, buf, len);
}
#ifdef __cplusplus
}
#endif
#endif /* PERIPH_USBDEV_H */
/** @} */