mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2024-12-29 04:50:03 +01:00
usbus: Initial work to a unified USB stack
This commit is contained in:
parent
35af9b9fb7
commit
74e0b5b85b
@ -832,6 +832,12 @@ ifneq (,$(filter tlsf-malloc,$(USEMODULE)))
|
||||
USEPKG += tlsf
|
||||
endif
|
||||
|
||||
ifneq (,$(filter usbus,$(USEMODULE)))
|
||||
FEATURES_REQUIRED += periph_usbdev
|
||||
USEMODULE += core_thread_flags
|
||||
USEMODULE += event
|
||||
endif
|
||||
|
||||
ifneq (,$(filter uuid,$(USEMODULE)))
|
||||
USEMODULE += hashes
|
||||
USEMODULE += random
|
||||
|
@ -145,6 +145,9 @@ endif
|
||||
ifneq (,$(filter bluetil_%,$(USEMODULE)))
|
||||
DIRS += net/ble/bluetil
|
||||
endif
|
||||
ifneq (,$(filter usbus usbus_%,$(USEMODULE)))
|
||||
DIRS += usb/usbus
|
||||
endif
|
||||
|
||||
DIRS += $(dir $(wildcard $(addsuffix /Makefile, $(USEMODULE))))
|
||||
|
||||
|
533
sys/include/usb/usbus.h
Normal file
533
sys/include/usb/usbus.h
Normal file
@ -0,0 +1,533 @@
|
||||
/*
|
||||
* 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 "kernel_types.h"
|
||||
#include "msg.h"
|
||||
#include "thread.h"
|
||||
|
||||
#include "usb.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "usb/descriptor.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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 thread name
|
||||
*/
|
||||
#define USBUS_TNAME "usbus"
|
||||
|
||||
/**
|
||||
* @brief USBUS auto attach setting
|
||||
*
|
||||
* When set, 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 USBUS_AUTO_ATTACH
|
||||
#define USBUS_AUTO_ATTACH (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
|
||||
*/
|
||||
#ifndef USBUS_EP0_SIZE
|
||||
#define USBUS_EP0_SIZE 64
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @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_FAIL (0x0010) /**< Report transfer fail */
|
||||
#define USBUS_HANDLER_FLAG_TR_STALL (0x0020) /**< Report transfer stall complete */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @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 succesfully 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 setup request state machine
|
||||
*/
|
||||
typedef enum {
|
||||
USBUS_SETUPRQ_READY, /**< Ready for new setup request */
|
||||
USBUS_SETUPRQ_INDATA, /**< Request received with expected DATA IN stage */
|
||||
USBUS_SETUPRQ_OUTACK, /**< Expecting a zero-length ack out request
|
||||
from the host */
|
||||
USBUS_SETUPRQ_OUTDATA, /**< Data OUT expected */
|
||||
USBUS_SETUPRQ_INACK, /**< Expecting a zero-length ack in request
|
||||
from the host */
|
||||
} usbus_setuprq_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 Header length types for USB descriptor generators
|
||||
*/
|
||||
typedef enum {
|
||||
USBUS_HDR_LEN_FIXED, /**< Header always generates a fixed length */
|
||||
USBUS_HDR_LEN_FUNC, /**< Header length is calculated by a function */
|
||||
} usbus_hdr_len_type_t;
|
||||
|
||||
/**
|
||||
* @brief USBUS header generator function pointers
|
||||
*/
|
||||
typedef struct {
|
||||
/**
|
||||
* @brief function pointer to retrieve the header content of this header
|
||||
* generator
|
||||
*
|
||||
* @param usbus The usbus context
|
||||
* @param arg Additional argument for the header generator
|
||||
*
|
||||
* @return Length of the generated header
|
||||
*/
|
||||
size_t (*get_header)(usbus_t *usbus, void *arg);
|
||||
union {
|
||||
/**
|
||||
* @brief USBUS generic header generator generated length
|
||||
*
|
||||
* Must return the length of the header that will be generated by
|
||||
* @ref get_header
|
||||
*
|
||||
* @param usbus The usbus context
|
||||
* @param arg Additional argument for the header generator
|
||||
*
|
||||
* @return Length of the generated header
|
||||
*/
|
||||
size_t (*get_header_len)(usbus_t *usbus, void *arg);
|
||||
size_t fixed_len; /**< length of the header if it is a fixed length */
|
||||
} len; /**< Fixed or generated length of the header */
|
||||
usbus_hdr_len_type_t len_type; /**< Either USBUS_HDR_LEN_FIXED or USBUS_HDR_LEN_FUNC */
|
||||
} usbus_hdr_gen_funcs_t;
|
||||
|
||||
/**
|
||||
* @brief USBUS header generator
|
||||
*
|
||||
* The functions are called to allow custom modules to define their own
|
||||
* headers 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 headers
|
||||
*/
|
||||
typedef struct usbus_hdr_gen {
|
||||
struct usbus_hdr_gen *next; /**< ptr to the next header generator */
|
||||
const usbus_hdr_gen_funcs_t *funcs; /**< Function pointers */
|
||||
void *arg; /**< Extra context argument for the
|
||||
headers functions */
|
||||
} usbus_hdr_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_hdr_gen_t *hdr_gen; /**< Optional additional header generator */
|
||||
usbdev_ep_t *ep; /**< ptr to the matching usbdev endpoint */
|
||||
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 */
|
||||
} usbus_endpoint_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_hdr_gen_t *hdr_gen; /**< Optional additional header
|
||||
generator */
|
||||
usbus_endpoint_t *ep; /**< List of associated endpoints for
|
||||
this alternative setting */
|
||||
} usbus_interface_alt_t;
|
||||
|
||||
/**
|
||||
* @brief USBUS interface
|
||||
*/
|
||||
typedef struct usbus_interface {
|
||||
struct usbus_interface *next; /**< Next interface (set by USBUS during
|
||||
registration) */
|
||||
usbus_hdr_gen_t *hdr_gen; /**< Optional additional header
|
||||
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 setup request handler function
|
||||
*
|
||||
* This function receives USB setup requests from the USBUS stack.
|
||||
*
|
||||
* @param usbus USBUS context
|
||||
* @param handler handler context
|
||||
* @param state setup 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 (*setup_handler)(usbus_t * usbus, struct usbus_handler *handler,
|
||||
usbus_setuprq_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_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_hdr_gen_t *hdr_gen; /**< Top level header 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 */
|
||||
};
|
||||
|
||||
/**
|
||||
* @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 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] hdr_gen Header generator to add
|
||||
*/
|
||||
void usbus_add_conf_descriptor(usbus_t *usbus, usbus_hdr_gen_t *hdr_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 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 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;
|
||||
}
|
||||
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* USB_USBUS_H */
|
||||
/** @} */
|
107
sys/include/usb/usbus/control.h
Normal file
107
sys/include/usb/usbus/control.h
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright (C) 2019 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup usb_usbus
|
||||
* @brief USBUS control endpoint module
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief USBUS control endpoint module interface
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#ifndef USB_USBUS_CONTROL_H
|
||||
#define USB_USBUS_CONTROL_H
|
||||
|
||||
#include "usb/usbus.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* @brief helper struct to divide control messages in multiple parts
|
||||
*/
|
||||
typedef struct {
|
||||
size_t start; /**< Start offset of the current part */
|
||||
size_t cur; /**< Current position in the message */
|
||||
size_t len; /**< Length of the full message */
|
||||
size_t transfered; /**< Number of bytes transfered */
|
||||
size_t reqlen; /**< Maximum length of the request */
|
||||
} usbus_control_slicer_t;
|
||||
|
||||
/**
|
||||
* @brief Endpoint zero event handler
|
||||
*/
|
||||
typedef struct {
|
||||
usbus_handler_t handler; /**< Inherited generic handler */
|
||||
usb_setup_t setup; /**< Last received setup packet */
|
||||
usbus_setuprq_state_t setup_state; /**< Setup request state machine */
|
||||
usbus_control_slicer_t slicer; /**< Slicer for multipart control
|
||||
messages */
|
||||
usbdev_ep_t *out; /**< EP0 out endpoint */
|
||||
usbdev_ep_t *in; /**< EP0 in endpoint */
|
||||
} usbus_control_handler_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize the control endpoint handler
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
* @param[in] handler control handler to initialize
|
||||
*/
|
||||
void usbus_control_init(usbus_t *usbus, usbus_control_handler_t *handler);
|
||||
|
||||
/**
|
||||
* @brief Helper function for adding bytes to the current control message part
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
* @param[in] buf Buffer to add bytes from
|
||||
* @param[in] len Length of @p buf
|
||||
*
|
||||
* @return Actual number of bytes written
|
||||
*/
|
||||
size_t usbus_control_slicer_put_bytes(usbus_t *usbus, const uint8_t *buf,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* @brief Helper function for adding single bytes to the current control
|
||||
* message part
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
* @param[in] c byte to add
|
||||
*
|
||||
* @return Actual number of bytes written
|
||||
*/
|
||||
size_t usbus_control_slicer_put_char(usbus_t *usbus, char c);
|
||||
|
||||
/**
|
||||
* @brief Helper function to signal the end of the control message
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
*/
|
||||
void usbus_control_slicer_ready(usbus_t *usbus);
|
||||
|
||||
/**
|
||||
* @brief Initialize the next slice of the control message
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
*
|
||||
* @return 1 when there is a next slice
|
||||
* @return 0 when the data is fully transfered
|
||||
*/
|
||||
int usbus_control_slicer_nextslice(usbus_t *usbus);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* USB_USBUS_CONTROL_H */
|
||||
/** @} */
|
54
sys/include/usb/usbus/fmt.h
Normal file
54
sys/include/usb/usbus/fmt.h
Normal file
@ -0,0 +1,54 @@
|
||||
/*
|
||||
* 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_fmt USBUS descriptor formatter functions
|
||||
* @ingroup usb_usbus
|
||||
*
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief USBUS descriptor formatter functions
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
*/
|
||||
|
||||
#ifndef USB_USBUS_FMT_H
|
||||
#define USB_USBUS_FMT_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include "usb/usbus.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief generator for the USB configuration descriptor
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
*
|
||||
* @return the generated descriptor size in bytes
|
||||
*/
|
||||
size_t usbus_fmt_hdr_conf(usbus_t *usbus);
|
||||
|
||||
/**
|
||||
* @brief generator for the USB device descriptor
|
||||
*
|
||||
* @param[in] usbus USBUS context
|
||||
*
|
||||
* @return the generated descriptor size in bytes
|
||||
*/
|
||||
size_t usbus_fmt_hdr_dev(usbus_t *usbus);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif /* USB_USBUS_FMT_H */
|
||||
/** @} */
|
4
sys/usb/usbus/Makefile
Normal file
4
sys/usb/usbus/Makefile
Normal file
@ -0,0 +1,4 @@
|
||||
SRCS := usbus.c
|
||||
SRCS += usbus_hdrs.c
|
||||
|
||||
include $(RIOTBASE)/Makefile.base
|
361
sys/usb/usbus/usbus.c
Normal file
361
sys/usb/usbus/usbus.c
Normal file
@ -0,0 +1,361 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup usb_usbus
|
||||
* @{
|
||||
* @file
|
||||
* @brief USBUS USB manager thread, handles USB interaction
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include "bitarithm.h"
|
||||
#include "event.h"
|
||||
#include "thread.h"
|
||||
#include "thread_flags.h"
|
||||
#include "periph/usbdev.h"
|
||||
#include "usb/descriptor.h"
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/fmt.h"
|
||||
#include "usb/usbus/control.h"
|
||||
|
||||
#include "usb.h"
|
||||
#include "cpu.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#define _USBUS_MSG_QUEUE_SIZE (16)
|
||||
|
||||
/* Forward declaration of the generic USBUS event callback */
|
||||
static void _event_cb(usbdev_t *usbdev, usbdev_event_t event);
|
||||
/* Forward declaration of the endpoint USBUS event callback */
|
||||
static void _event_ep_cb(usbdev_ep_t *ep, usbdev_event_t event);
|
||||
|
||||
static void *_usbus_thread(void *args);
|
||||
|
||||
void usbus_init(usbus_t *usbus, usbdev_t *usbdev)
|
||||
{
|
||||
memset(usbus, 0, sizeof(usbus_t));
|
||||
usbus->dev = usbdev;
|
||||
}
|
||||
|
||||
void usbus_create(char *stack, int stacksize, char priority,
|
||||
const char *name, usbus_t *usbus)
|
||||
{
|
||||
int res = thread_create(stack, stacksize, priority, THREAD_CREATE_STACKTEST,
|
||||
_usbus_thread, (void *)usbus, name);
|
||||
|
||||
(void)res;
|
||||
assert(res > 0);
|
||||
}
|
||||
|
||||
uint16_t usbus_add_string_descriptor(usbus_t *usbus, usbus_string_t *desc,
|
||||
const char *str)
|
||||
{
|
||||
desc->next = usbus->strings;
|
||||
usbus->strings = desc;
|
||||
desc->idx = usbus->str_idx++;
|
||||
desc->str = str;
|
||||
DEBUG("usbus: Adding string descriptor number %u for: \"%s\"\n", desc->idx,
|
||||
str);
|
||||
return desc->idx;
|
||||
}
|
||||
|
||||
void usbus_add_conf_descriptor(usbus_t *usbus, usbus_hdr_gen_t *hdr_gen)
|
||||
{
|
||||
hdr_gen->next = usbus->hdr_gen;
|
||||
usbus->hdr_gen = hdr_gen;
|
||||
}
|
||||
|
||||
static usbus_handler_t *_ep_to_handler(usbus_t *usbus, usbdev_ep_t *ep)
|
||||
{
|
||||
if (ep->num == 0) {
|
||||
return usbus->handlers;
|
||||
}
|
||||
for (usbus_interface_t *iface = usbus->iface; iface; iface = iface->next) {
|
||||
for (usbus_endpoint_t *pep = iface->ep; pep; pep = pep->next) {
|
||||
if (pep->ep == ep) {
|
||||
return iface->handler;
|
||||
}
|
||||
}
|
||||
for (usbus_interface_alt_t *alt = iface->alts; alt; alt = alt->next) {
|
||||
for (usbus_endpoint_t *pep = alt->ep; pep; pep = pep->next) {
|
||||
if (pep->ep == ep) {
|
||||
return iface->handler;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
uint16_t usbus_add_interface(usbus_t *usbus, usbus_interface_t *iface)
|
||||
{
|
||||
/* While it is possible to us clist.h here, this results in less flash
|
||||
* usages. Furthermore, the O(1) append is not really necessary as this is
|
||||
* only used at init */
|
||||
uint16_t idx = 0;
|
||||
usbus_interface_t *last = usbus->iface;
|
||||
|
||||
if (last) {
|
||||
idx++;
|
||||
while (last->next) {
|
||||
last = last->next;
|
||||
idx++;
|
||||
}
|
||||
last->next = iface;
|
||||
}
|
||||
else {
|
||||
usbus->iface = iface;
|
||||
}
|
||||
iface->idx = idx;
|
||||
return idx;
|
||||
}
|
||||
|
||||
void usbus_register_event_handler(usbus_t *usbus, usbus_handler_t *handler)
|
||||
{
|
||||
/* See note above for reasons against clist.h */
|
||||
usbus_handler_t *last = usbus->handlers;
|
||||
|
||||
if (last) {
|
||||
while (last->next) {
|
||||
last = last->next;
|
||||
}
|
||||
last->next = handler;
|
||||
}
|
||||
else {
|
||||
usbus->handlers = handler;
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
usbus_endpoint_t *ep = NULL;
|
||||
usbdev_ep_t *usbdev_ep = usbdev_new_ep(usbus->dev, type, dir, len);
|
||||
|
||||
if (usbdev_ep) {
|
||||
ep = dir == USB_EP_DIR_IN ? &usbus->ep_in[usbdev_ep->num]
|
||||
: &usbus->ep_out[usbdev_ep->num];
|
||||
ep->maxpacketsize = usbdev_ep->len;
|
||||
ep->ep = usbdev_ep;
|
||||
if (iface) {
|
||||
ep->next = iface->ep;
|
||||
iface->ep = ep;
|
||||
}
|
||||
}
|
||||
return ep;
|
||||
}
|
||||
|
||||
static inline uint32_t _get_ep_bitflag(usbdev_ep_t *ep)
|
||||
{
|
||||
/* Endpoint activity bit flag, lower USBDEV_NUM_ENDPOINTS bits are
|
||||
* useb as OUT endpoint flags, upper bit are IN endpoints */
|
||||
return 1 << ((ep->dir == USB_EP_DIR_IN ? USBDEV_NUM_ENDPOINTS
|
||||
: 0x00) + ep->num);
|
||||
}
|
||||
|
||||
static uint32_t _get_and_reset_ep_events(usbus_t *usbus)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
uint32_t res = usbus->ep_events;
|
||||
|
||||
usbus->ep_events = 0;
|
||||
irq_restore(state);
|
||||
return res;
|
||||
}
|
||||
|
||||
static void _signal_handlers(usbus_t *usbus, uint16_t flag,
|
||||
uint16_t msg)
|
||||
{
|
||||
for (usbus_handler_t *handler = usbus->handlers;
|
||||
handler; handler = handler->next) {
|
||||
if (handler->flags & flag) {
|
||||
handler->driver->event_handler(usbus, handler, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _usbus_init_handlers(usbus_t *usbus)
|
||||
{
|
||||
for (usbus_handler_t *handler = usbus->handlers;
|
||||
handler; handler = handler->next) {
|
||||
handler->driver->init(usbus, handler);
|
||||
}
|
||||
}
|
||||
|
||||
static void *_usbus_thread(void *args)
|
||||
{
|
||||
usbus_t *usbus = (usbus_t *)args;
|
||||
usbus_control_handler_t ep0_handler;
|
||||
|
||||
event_queue_init(&usbus->queue);
|
||||
|
||||
usbus->control = &ep0_handler.handler;
|
||||
|
||||
usbus_control_init(usbus, &ep0_handler);
|
||||
|
||||
usbdev_t *dev = usbus->dev;
|
||||
usbus->pid = sched_active_pid;
|
||||
usbus->addr = 0;
|
||||
usbus->iface = NULL;
|
||||
usbus->str_idx = 1;
|
||||
DEBUG("usbus: starting thread %i\n", sched_active_pid);
|
||||
/* setup the link-layer's message queue */
|
||||
/* register the event callback with the device driver */
|
||||
dev->cb = _event_cb;
|
||||
dev->epcb = _event_ep_cb;
|
||||
/* initialize low-level driver */
|
||||
dev->context = usbus;
|
||||
usbdev_init(dev);
|
||||
|
||||
usbus_add_string_descriptor(usbus, &usbus->config,
|
||||
USB_CONFIG_CONFIGURATION_STR);
|
||||
usbus_add_string_descriptor(usbus, &usbus->product, USB_CONFIG_PRODUCT_STR);
|
||||
usbus_add_string_descriptor(usbus, &usbus->manuf, USB_CONFIG_MANUF_STR);
|
||||
|
||||
usbus->state = USBUS_STATE_DISCONNECT;
|
||||
|
||||
/* Initialize handlers */
|
||||
_usbus_init_handlers(usbus);
|
||||
|
||||
#if (USBUS_AUTO_ATTACH)
|
||||
static const usbopt_enable_t _enable = USBOPT_ENABLE;
|
||||
usbdev_set(dev, USBOPT_ATTACH, &_enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
#endif
|
||||
|
||||
while (1) {
|
||||
thread_flags_t flags = thread_flags_wait_any(
|
||||
USBUS_THREAD_FLAG_USBDEV |
|
||||
USBUS_THREAD_FLAG_USBDEV_EP |
|
||||
THREAD_FLAG_EVENT
|
||||
);
|
||||
if (flags & USBUS_THREAD_FLAG_USBDEV) {
|
||||
usbdev_esr(dev);
|
||||
}
|
||||
if (flags & USBUS_THREAD_FLAG_USBDEV_EP) {
|
||||
uint32_t events = _get_and_reset_ep_events(usbus);
|
||||
while (events) {
|
||||
unsigned num = bitarithm_lsb(events);
|
||||
events &= ~(1 << num);
|
||||
if (num < USBDEV_NUM_ENDPOINTS) {
|
||||
/* OUT endpoint */
|
||||
usbdev_ep_esr(usbus->ep_out[num].ep);
|
||||
}
|
||||
else {
|
||||
/* IN endpoint */
|
||||
usbdev_ep_esr(usbus->ep_in[num - USBDEV_NUM_ENDPOINTS].ep);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
if (flags & THREAD_FLAG_EVENT) {
|
||||
event_t *event = event_get(&usbus->queue);
|
||||
if (event) {
|
||||
event->handler(event);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* USB event callback */
|
||||
static void _event_cb(usbdev_t *usbdev, usbdev_event_t event)
|
||||
{
|
||||
usbus_t *usbus = (usbus_t *)usbdev->context;
|
||||
|
||||
if (event == USBDEV_EVENT_ESR) {
|
||||
thread_flags_set((thread_t *)thread_get(usbus->pid),
|
||||
USBUS_THREAD_FLAG_USBDEV);
|
||||
}
|
||||
else {
|
||||
usbus_event_usb_t msg;
|
||||
uint16_t flag;
|
||||
switch (event) {
|
||||
case USBDEV_EVENT_RESET:
|
||||
usbus->state = USBUS_STATE_RESET;
|
||||
usbus->addr = 0;
|
||||
usbdev_set(usbus->dev, USBOPT_ADDRESS, &usbus->addr,
|
||||
sizeof(uint8_t));
|
||||
flag = USBUS_HANDLER_FLAG_RESET;
|
||||
msg = USBUS_EVENT_USB_RESET;
|
||||
break;
|
||||
case USBDEV_EVENT_SUSPEND:
|
||||
DEBUG("usbus: USB suspend detected\n");
|
||||
usbus->pstate = usbus->state;
|
||||
usbus->state = USBUS_STATE_SUSPEND;
|
||||
flag = USBUS_HANDLER_FLAG_SUSPEND;
|
||||
msg = USBUS_EVENT_USB_SUSPEND;
|
||||
break;
|
||||
case USBDEV_EVENT_RESUME:
|
||||
DEBUG("usbus: USB resume detected\n");
|
||||
usbus->state = usbus->pstate;
|
||||
flag = USBUS_HANDLER_FLAG_RESUME;
|
||||
msg = USBUS_EVENT_USB_RESUME;
|
||||
break;
|
||||
default:
|
||||
DEBUG("usbus: unhandled event %x\n", event);
|
||||
return;
|
||||
}
|
||||
_signal_handlers(usbus, flag, msg);
|
||||
}
|
||||
}
|
||||
|
||||
/* USB generic endpoint callback */
|
||||
static void _event_ep_cb(usbdev_ep_t *ep, usbdev_event_t event)
|
||||
{
|
||||
usbus_t *usbus = (usbus_t *)ep->dev->context;
|
||||
|
||||
if (event == USBDEV_EVENT_ESR) {
|
||||
assert(irq_is_in());
|
||||
usbus->ep_events |= _get_ep_bitflag(ep);
|
||||
thread_flags_set((thread_t *)thread_get(usbus->pid),
|
||||
USBUS_THREAD_FLAG_USBDEV_EP);
|
||||
}
|
||||
else {
|
||||
usbus_handler_t *handler = _ep_to_handler(usbus, ep);
|
||||
if (handler) {
|
||||
switch (event) {
|
||||
case USBDEV_EVENT_TR_COMPLETE:
|
||||
handler->driver->transfer_handler(usbus, handler, ep,
|
||||
USBUS_EVENT_TRANSFER_COMPLETE);
|
||||
break;
|
||||
case USBDEV_EVENT_TR_FAIL:
|
||||
if (usbus_handler_isset_flag(handler,
|
||||
USBUS_HANDLER_FLAG_TR_FAIL)) {
|
||||
handler->driver->transfer_handler(usbus, handler, ep,
|
||||
USBUS_EVENT_TRANSFER_FAIL);
|
||||
}
|
||||
break;
|
||||
case USBDEV_EVENT_TR_STALL:
|
||||
if (usbus_handler_isset_flag(handler,
|
||||
USBUS_HANDLER_FLAG_TR_STALL)) {
|
||||
handler->driver->transfer_handler(usbus, handler, ep,
|
||||
USBUS_EVENT_TRANSFER_STALL);
|
||||
static const usbopt_enable_t disable = USBOPT_DISABLE;
|
||||
usbdev_ep_set(ep, USBOPT_EP_STALL, &disable,
|
||||
sizeof(usbopt_enable_t));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("unhandled event: %x\n", event);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
426
sys/usb/usbus/usbus_control.c
Normal file
426
sys/usb/usbus/usbus_control.c
Normal file
@ -0,0 +1,426 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup usb_usbus
|
||||
* @{
|
||||
* @file
|
||||
* @brief USBUS control endpoint handling
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @}
|
||||
*/
|
||||
#include "periph/usbdev.h"
|
||||
#include "usb/descriptor.h"
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/fmt.h"
|
||||
#include "usb/usbus/control.h"
|
||||
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
static void _init(usbus_t *usbus, usbus_handler_t *handler);
|
||||
static void _handler_ep0_event(usbus_t *usbus, usbus_handler_t *handler,
|
||||
usbus_event_usb_t event);
|
||||
static void _handler_ep0_transfer(usbus_t *usbus, usbus_handler_t *handler,
|
||||
usbdev_ep_t *ep, usbus_event_transfer_t event);
|
||||
|
||||
const usbus_handler_driver_t _ep0_driver = {
|
||||
.init = _init,
|
||||
.event_handler = _handler_ep0_event,
|
||||
.transfer_handler = _handler_ep0_transfer,
|
||||
};
|
||||
|
||||
void usbus_control_init(usbus_t *usbus, usbus_control_handler_t *handler)
|
||||
{
|
||||
handler->handler.driver = &_ep0_driver;
|
||||
|
||||
/* Ensure that ep0 is the first handler */
|
||||
handler->handler.next = usbus->handlers;
|
||||
usbus->handlers = &handler->handler;
|
||||
}
|
||||
|
||||
static void _activate_endpoints(usbus_t *usbus)
|
||||
{
|
||||
for (usbus_interface_t *iface = usbus->iface; iface; iface = iface->next) {
|
||||
for (usbus_endpoint_t *ep = iface->ep; ep; ep = ep->next) {
|
||||
if (ep->active) {
|
||||
static const usbopt_enable_t enable = USBOPT_ENABLE;
|
||||
usbdev_ep_set(ep->ep, USBOPT_EP_ENABLE, &enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
DEBUG("usbus_control: activated endpoint %d, dir %s\n",
|
||||
ep->ep->num,
|
||||
ep->ep->dir == USB_EP_DIR_OUT ? "out" : "in");
|
||||
}
|
||||
}
|
||||
for (usbus_interface_alt_t *alt = iface->alts; alt; alt = alt->next) {
|
||||
for (usbus_endpoint_t *ep = alt->ep; ep; ep = ep->next) {
|
||||
if (ep->active) {
|
||||
static const usbopt_enable_t enable = USBOPT_ENABLE;
|
||||
usbdev_ep_set(ep->ep, USBOPT_EP_ENABLE, &enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
DEBUG("usbus_control: activated endpoint %d, dir %s\n",
|
||||
ep->ep->num,
|
||||
ep->ep->dir == USB_EP_DIR_OUT ? "out" : "in");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t _cpy_str_to_utf16(usbus_t *usbus, const char *str)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (*str) {
|
||||
usbus_control_slicer_put_char(usbus, *str);
|
||||
usbus_control_slicer_put_char(usbus, 0);
|
||||
len += 2; /* Two bytes added each iteration */
|
||||
str++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static usbus_string_t *_get_descriptor(usbus_t *usbus, uint16_t idx)
|
||||
{
|
||||
for (usbus_string_t *str = usbus->strings; str; str = str->next) {
|
||||
if (str->idx == idx) {
|
||||
return str;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int _req_status(usbus_t *usbus)
|
||||
{
|
||||
uint8_t status[2];
|
||||
|
||||
memset(status, 0, sizeof(status));
|
||||
usbus_control_slicer_put_bytes(usbus, status, sizeof(status));
|
||||
return sizeof(status);
|
||||
}
|
||||
|
||||
static int _req_str(usbus_t *usbus, uint16_t idx)
|
||||
{
|
||||
/* Return an error condition by default */
|
||||
int res = -1;
|
||||
|
||||
/* Language ID must only be supported if there are string descriptors
|
||||
* available */
|
||||
if (usbus->strings) {
|
||||
if (idx == 0) {
|
||||
usb_descriptor_string_t desc;
|
||||
desc.type = USB_TYPE_DESCRIPTOR_STRING;
|
||||
desc.length = sizeof(uint16_t) + sizeof(usb_descriptor_string_t);
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&desc, sizeof(desc));
|
||||
/* Only one language ID supported */
|
||||
uint16_t us = USB_CONFIG_DEFAULT_LANGID;
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&us, sizeof(uint16_t));
|
||||
res = 1;
|
||||
}
|
||||
else {
|
||||
usb_descriptor_string_t desc;
|
||||
desc.type = USB_TYPE_DESCRIPTOR_STRING;
|
||||
usbus_string_t *str = _get_descriptor(usbus, idx);
|
||||
if (str) {
|
||||
desc.length = sizeof(usb_descriptor_string_t);
|
||||
desc.length += 2 * strlen(str->str); /* USB strings are UTF-16 */
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&desc,
|
||||
sizeof(desc));
|
||||
_cpy_str_to_utf16(usbus, str->str);
|
||||
res = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _req_dev(usbus_t *usbus)
|
||||
{
|
||||
return usbus_fmt_hdr_dev(usbus);
|
||||
}
|
||||
|
||||
static int _req_config(usbus_t *usbus)
|
||||
{
|
||||
return usbus_fmt_hdr_conf(usbus);
|
||||
}
|
||||
|
||||
static int _req_dev_qualifier(usbus_t *usbus)
|
||||
{
|
||||
usb_speed_t speed = USB_SPEED_LOW;
|
||||
|
||||
usbus->dev->driver->get(usbus->dev, USBOPT_MAX_SPEED, &speed,
|
||||
sizeof(usb_speed_t));
|
||||
if (speed == USB_SPEED_HIGH) {
|
||||
/* TODO: implement device qualifier support (only required
|
||||
* for High speed) */
|
||||
}
|
||||
/* Signal a stall condition */
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int _req_descriptor(usbus_t *usbus, usb_setup_t *pkt)
|
||||
{
|
||||
uint8_t type = pkt->value >> 8;
|
||||
uint8_t idx = (uint8_t)pkt->value;
|
||||
|
||||
/* Decode descriptor type */
|
||||
switch (type) {
|
||||
case USB_TYPE_DESCRIPTOR_DEVICE:
|
||||
return _req_dev(usbus);
|
||||
case USB_TYPE_DESCRIPTOR_CONFIGURATION:
|
||||
return _req_config(usbus);
|
||||
case USB_TYPE_DESCRIPTOR_STRING:
|
||||
return _req_str(usbus, idx);
|
||||
case USB_TYPE_DESCRIPTOR_DEV_QUALIFIER:
|
||||
return _req_dev_qualifier(usbus);
|
||||
default:
|
||||
DEBUG("usbus: unknown descriptor request %u, signalling stall\n",
|
||||
type);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
static int _recv_dev_setup(usbus_t *usbus, usb_setup_t *pkt)
|
||||
{
|
||||
usbus_control_handler_t *ep0 = (usbus_control_handler_t *)usbus->control;
|
||||
int res = -1;
|
||||
|
||||
if (usb_setup_is_read(pkt)) {
|
||||
switch (pkt->request) {
|
||||
case USB_SETUP_REQ_GET_STATUS:
|
||||
res = _req_status(usbus);
|
||||
break;
|
||||
case USB_SETUP_REQ_GET_DESCRIPTOR:
|
||||
res = _req_descriptor(usbus, pkt);
|
||||
break;
|
||||
default:
|
||||
DEBUG("usbus: Unknown read request %u\n", pkt->request);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
switch (pkt->request) {
|
||||
case USB_SETUP_REQ_SET_ADDRESS:
|
||||
DEBUG("usbus_control: Setting address\n");
|
||||
usbus->addr = (uint8_t)pkt->value;
|
||||
res = 0;
|
||||
break;
|
||||
case USB_SETUP_REQ_SET_CONFIGURATION:
|
||||
/* Nothing configuration dependent to do here, only one
|
||||
* configuration supported */
|
||||
usbus->state = USBUS_STATE_CONFIGURED;
|
||||
_activate_endpoints(usbus);
|
||||
res = 0;
|
||||
break;
|
||||
default:
|
||||
DEBUG("usbus: Unknown write request %u\n", pkt->request);
|
||||
break;
|
||||
}
|
||||
/* Signal zero-length packet */
|
||||
usbdev_ep_ready(ep0->in, 0);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
static int _recv_interface_setup(usbus_t *usbus, usb_setup_t *pkt)
|
||||
{
|
||||
usbus_control_handler_t *ep0_handler =
|
||||
(usbus_control_handler_t *)usbus->control;
|
||||
uint16_t destination = pkt->index & 0x0f;
|
||||
|
||||
/* Find interface handler */
|
||||
for (usbus_interface_t *iface = usbus->iface; iface; iface = iface->next) {
|
||||
if (destination == iface->idx &&
|
||||
iface->handler->driver->setup_handler) {
|
||||
return iface->handler->driver->setup_handler(usbus, iface->handler,
|
||||
ep0_handler->setup_state,
|
||||
pkt);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static void _recv_setup(usbus_t *usbus, usbus_control_handler_t *handler)
|
||||
{
|
||||
usb_setup_t *pkt = &handler->setup;
|
||||
|
||||
DEBUG("usbus_control: Received setup %x %x @ %d\n", pkt->type,
|
||||
pkt->request, pkt->length);
|
||||
if (usb_setup_is_read(pkt)) {
|
||||
handler->setup_state = USBUS_SETUPRQ_INDATA;
|
||||
}
|
||||
else {
|
||||
if (pkt->length) {
|
||||
handler->setup_state = USBUS_SETUPRQ_OUTDATA;
|
||||
usbdev_ep_ready(handler->out, 0);
|
||||
}
|
||||
else {
|
||||
handler->setup_state = USBUS_SETUPRQ_INACK;
|
||||
usbdev_ep_ready(handler->in, 0);
|
||||
}
|
||||
}
|
||||
uint8_t destination = pkt->type & USB_SETUP_REQUEST_RECIPIENT_MASK;
|
||||
int res = 0;
|
||||
switch (destination) {
|
||||
case USB_SETUP_REQUEST_RECIPIENT_DEVICE:
|
||||
res = _recv_dev_setup(usbus, pkt);
|
||||
break;
|
||||
case USB_SETUP_REQUEST_RECIPIENT_INTERFACE:
|
||||
res = _recv_interface_setup(usbus, pkt);
|
||||
break;
|
||||
default:
|
||||
DEBUG("usbus_control: Unhandled setup request\n");
|
||||
}
|
||||
if (res < 0) {
|
||||
/* Signal stall to indicate unsupported (USB 2.0 spec 9.6.2 */
|
||||
static const usbopt_enable_t enable = USBOPT_ENABLE;
|
||||
usbdev_ep_set(handler->in, USBOPT_EP_STALL, &enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
handler->setup_state = USBUS_SETUPRQ_READY;
|
||||
}
|
||||
else if (res) {
|
||||
usbus_control_slicer_ready(usbus);
|
||||
}
|
||||
}
|
||||
|
||||
static void _usbus_config_ep0(usbus_control_handler_t *ep0_handler)
|
||||
{
|
||||
DEBUG("usbus_control: Enabling EP0\n");
|
||||
static const usbopt_enable_t enable = USBOPT_ENABLE;
|
||||
usbdev_ep_init(ep0_handler->in);
|
||||
usbdev_ep_init(ep0_handler->out);
|
||||
usbdev_ep_set(ep0_handler->in, USBOPT_EP_ENABLE, &enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
usbdev_ep_set(ep0_handler->out, USBOPT_EP_ENABLE, &enable,
|
||||
sizeof(usbopt_enable_t));
|
||||
usbdev_ep_ready(ep0_handler->out, 0);
|
||||
}
|
||||
|
||||
static void _init(usbus_t *usbus, usbus_handler_t *handler)
|
||||
{
|
||||
DEBUG("usbus_control: Initializing EP0\n");
|
||||
usbus_control_handler_t *ep0_handler = (usbus_control_handler_t *)handler;
|
||||
usbus_handler_set_flag(handler, USBUS_HANDLER_FLAG_RESET);
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_READY;
|
||||
|
||||
ep0_handler->in = usbus_add_endpoint(usbus, NULL, USB_EP_TYPE_CONTROL,
|
||||
USB_EP_DIR_IN, USBUS_EP0_SIZE)->ep;
|
||||
ep0_handler->out = usbus_add_endpoint(usbus, NULL, USB_EP_TYPE_CONTROL,
|
||||
USB_EP_DIR_OUT, USBUS_EP0_SIZE)->ep;
|
||||
}
|
||||
|
||||
static int _handle_tr_complete(usbus_t *usbus,
|
||||
usbus_control_handler_t *ep0_handler,
|
||||
usbdev_ep_t *ep)
|
||||
{
|
||||
switch (ep0_handler->setup_state) {
|
||||
case USBUS_SETUPRQ_INACK:
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (usbus->addr && usbus->state == USBUS_STATE_RESET) {
|
||||
usbdev_set(usbus->dev, USBOPT_ADDRESS, &usbus->addr,
|
||||
sizeof(usbus->addr));
|
||||
/* Address configured */
|
||||
usbus->state = USBUS_STATE_ADDR;
|
||||
}
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_READY;
|
||||
}
|
||||
break;
|
||||
case USBUS_SETUPRQ_OUTACK:
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
memset(&ep0_handler->slicer, 0, sizeof(usbus_control_slicer_t));
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_READY;
|
||||
}
|
||||
break;
|
||||
case USBUS_SETUPRQ_INDATA:
|
||||
if (ep->dir == USB_EP_DIR_IN) {
|
||||
if (usbus_control_slicer_nextslice(usbus)) {
|
||||
_recv_setup(usbus, ep0_handler);
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_INDATA;
|
||||
}
|
||||
else {
|
||||
/* Ready out ZLP */
|
||||
usbdev_ep_ready(ep0_handler->out, 0);
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_OUTACK;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case USBUS_SETUPRQ_OUTDATA:
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
/* Ready in ZLP */
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_INACK;
|
||||
size_t len = 0;
|
||||
usbdev_ep_get(ep, USBOPT_EP_AVAILABLE, &len, sizeof(size_t));
|
||||
DEBUG("Expected len: %d, received: %d\n",
|
||||
ep0_handler->setup.length, len);
|
||||
if (ep0_handler->setup.length == len) {
|
||||
DEBUG("DATA complete\n");
|
||||
usbdev_ep_ready(ep0_handler->in, 0);
|
||||
}
|
||||
/* Flush OUT buffer */
|
||||
usbdev_ep_ready(ep0_handler->out, 0);
|
||||
}
|
||||
else {
|
||||
DEBUG("usbus_control: Invalid state OUTDATA with IN request\n");
|
||||
}
|
||||
break;
|
||||
case USBUS_SETUPRQ_READY:
|
||||
if (ep->dir == USB_EP_DIR_OUT) {
|
||||
memset(&ep0_handler->slicer, 0, sizeof(usbus_control_slicer_t));
|
||||
memcpy(&ep0_handler->setup, ep0_handler->out->buf,
|
||||
sizeof(usb_setup_t));
|
||||
ep0_handler->slicer.reqlen = ep0_handler->setup.length;
|
||||
_recv_setup(usbus, ep0_handler);
|
||||
}
|
||||
else {
|
||||
DEBUG("usbus_control: invalid state, READY with IN request\n");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DEBUG("usbus_control: Invalid state\n");
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* USB endpoint 0 callback */
|
||||
static void _handler_ep0_event(usbus_t *usbus, usbus_handler_t *handler,
|
||||
usbus_event_usb_t event)
|
||||
{
|
||||
usbus_control_handler_t *ep0_handler = (usbus_control_handler_t *)handler;
|
||||
|
||||
(void)usbus;
|
||||
switch (event) {
|
||||
case USBUS_EVENT_USB_RESET:
|
||||
DEBUG("usbus_control: Reset event triggered\n");
|
||||
ep0_handler->setup_state = USBUS_SETUPRQ_READY;
|
||||
_usbus_config_ep0(ep0_handler);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void _handler_ep0_transfer(usbus_t *usbus, usbus_handler_t *handler,
|
||||
usbdev_ep_t *ep, usbus_event_transfer_t event)
|
||||
{
|
||||
usbus_control_handler_t *ep0_handler = (usbus_control_handler_t *)handler;
|
||||
|
||||
switch (event) {
|
||||
case USBUS_EVENT_TRANSFER_COMPLETE:
|
||||
_handle_tr_complete(usbus, ep0_handler, ep);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
101
sys/usb/usbus/usbus_control_slicer.c
Normal file
101
sys/usb/usbus/usbus_control_slicer.c
Normal file
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup usb_usbus
|
||||
* @{
|
||||
* @file
|
||||
* @brief USBUS multipart control message handling
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include "periph/usbdev.h"
|
||||
#include "usb/usbus.h"
|
||||
#include "usb/usbus/control.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
int usbus_control_slicer_nextslice(usbus_t *usbus)
|
||||
{
|
||||
usbus_control_handler_t *ep0 = (usbus_control_handler_t *)usbus->control;
|
||||
usbus_control_slicer_t *bldr = &ep0->slicer;
|
||||
size_t end = bldr->start + ep0->in->len;
|
||||
|
||||
if (bldr->cur > end && bldr->start < bldr->reqlen &&
|
||||
bldr->transfered < bldr->reqlen) {
|
||||
bldr->start += ep0->in->len;
|
||||
bldr->cur = 0;
|
||||
bldr->len = 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t usbus_control_slicer_put_bytes(usbus_t *usbus, const uint8_t *buf,
|
||||
size_t len)
|
||||
{
|
||||
usbus_control_handler_t *ep0 = (usbus_control_handler_t *)usbus->control;
|
||||
usbus_control_slicer_t *bldr = &ep0->slicer;
|
||||
size_t end = bldr->start + ep0->in->len;
|
||||
size_t byte_len = 0; /* Length of the string to copy */
|
||||
|
||||
/* Calculate start offset of the supplied bytes */
|
||||
size_t byte_offset =
|
||||
(bldr->start > bldr->cur) ? bldr->start - bldr->cur : 0;
|
||||
|
||||
/* Check for string before or beyond window */
|
||||
if ((bldr->cur >= end) || (byte_offset > len)) {
|
||||
bldr->cur += len;
|
||||
return 0;
|
||||
}
|
||||
/* Check if string is over the end of the window */
|
||||
if ((bldr->cur + len) >= end) {
|
||||
byte_len = end - (bldr->cur + byte_offset);
|
||||
}
|
||||
else {
|
||||
byte_len = len - byte_offset;
|
||||
}
|
||||
size_t start_offset = bldr->cur - bldr->start + byte_offset;
|
||||
bldr->cur += len;
|
||||
bldr->len += byte_len;
|
||||
memcpy(ep0->in->buf + start_offset, buf + byte_offset, byte_len);
|
||||
return byte_len;
|
||||
}
|
||||
|
||||
size_t usbus_control_slicer_put_char(usbus_t *usbus, char c)
|
||||
{
|
||||
usbus_control_handler_t *ep0 = (usbus_control_handler_t *)usbus->control;
|
||||
usbus_control_slicer_t *bldr = &ep0->slicer;
|
||||
size_t end = bldr->start + ep0->in->len;
|
||||
|
||||
/* Only copy the char if it is within the window */
|
||||
if ((bldr->start <= bldr->cur) && (bldr->cur < end)) {
|
||||
uint8_t *pos = ep0->in->buf + bldr->cur - bldr->start;
|
||||
*pos = c;
|
||||
bldr->cur++;
|
||||
bldr->len++;
|
||||
return 1;
|
||||
}
|
||||
bldr->cur++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void usbus_control_slicer_ready(usbus_t *usbus)
|
||||
{
|
||||
usbus_control_handler_t *ep0 = (usbus_control_handler_t *)usbus->control;
|
||||
usbus_control_slicer_t *bldr = &ep0->slicer;
|
||||
size_t len = bldr->len;
|
||||
|
||||
len = len < bldr->reqlen - bldr->start ? len : bldr->reqlen - bldr->start;
|
||||
bldr->transfered += len;
|
||||
usbdev_ep_ready(ep0->in, len);
|
||||
}
|
282
sys/usb/usbus/usbus_fmt.c
Normal file
282
sys/usb/usbus/usbus_fmt.c
Normal file
@ -0,0 +1,282 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @ingroup usb_usbus_fmt
|
||||
* @{
|
||||
* @file
|
||||
* @brief USBUS protocol message formatting functions
|
||||
*
|
||||
* @author Koen Zandberg <koen@bergzand.net>
|
||||
* @}
|
||||
*/
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include "usb/descriptor.h"
|
||||
#include "usb/usbus/fmt.h"
|
||||
#include "usb/usbus/control.h"
|
||||
|
||||
static size_t _num_ifaces(usbus_t *usbus)
|
||||
{
|
||||
size_t num = 0;
|
||||
|
||||
for (usbus_interface_t *iface = usbus->iface;
|
||||
iface;
|
||||
iface = iface->next) {
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static size_t _num_endpoints(usbus_interface_t *iface)
|
||||
{
|
||||
size_t num = 0;
|
||||
|
||||
for (usbus_endpoint_t *ep = iface->ep;
|
||||
ep; ep = ep->next) {
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static uint8_t _type_to_attribute(usbus_endpoint_t *ep)
|
||||
{
|
||||
switch (ep->ep->type) {
|
||||
case USB_EP_TYPE_CONTROL:
|
||||
return 0x00;
|
||||
case USB_EP_TYPE_ISOCHRONOUS:
|
||||
return 0x01;
|
||||
case USB_EP_TYPE_BULK:
|
||||
return 0x02;
|
||||
case USB_EP_TYPE_INTERRUPT:
|
||||
return 0x03;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
return 0x00;
|
||||
}
|
||||
|
||||
static size_t _num_endpoints_alt(usbus_interface_alt_t *alt)
|
||||
{
|
||||
size_t num = 0;
|
||||
|
||||
for (usbus_endpoint_t *ep = alt->ep;
|
||||
ep; ep = ep->next) {
|
||||
num++;
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
static inline size_t call_get_header_len(usbus_t *usbus, usbus_hdr_gen_t *hdr)
|
||||
{
|
||||
return hdr->funcs->len_type == USBUS_HDR_LEN_FIXED ?
|
||||
hdr->funcs->len.fixed_len :
|
||||
hdr->funcs->len.get_header_len(usbus, hdr->arg);
|
||||
}
|
||||
|
||||
static size_t _hdr_gen_size(usbus_t *usbus, usbus_hdr_gen_t *hdr)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (; hdr; hdr = hdr->next) {
|
||||
len += call_get_header_len(usbus, hdr);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t _ep_size(usbus_t *usbus, usbus_endpoint_t *ep)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (; ep; ep = ep->next) {
|
||||
len += sizeof(usb_descriptor_endpoint_t);
|
||||
len += _hdr_gen_size(usbus, ep->hdr_gen);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t _alt_size(usbus_t *usbus, usbus_interface_alt_t *alt)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (; alt; alt = alt->next) {
|
||||
len += sizeof(usb_descriptor_interface_t);
|
||||
len += _hdr_gen_size(usbus, alt->hdr_gen);
|
||||
len += _ep_size(usbus, alt->ep);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t _hdrs_config_size(usbus_t *usbus)
|
||||
{
|
||||
size_t len = sizeof(usb_descriptor_configuration_t);
|
||||
|
||||
len += _hdr_gen_size(usbus, usbus->hdr_gen);
|
||||
for (usbus_interface_t *iface = usbus->iface;
|
||||
iface;
|
||||
iface = iface->next) {
|
||||
len += sizeof(usb_descriptor_interface_t);
|
||||
len += _hdr_gen_size(usbus, iface->hdr_gen);
|
||||
len += _ep_size(usbus, iface->ep);
|
||||
len += _alt_size(usbus, iface->alts);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static inline size_t call_get_header(usbus_t *usbus, usbus_hdr_gen_t *hdr)
|
||||
{
|
||||
return hdr->funcs->get_header(usbus, hdr->arg);
|
||||
}
|
||||
|
||||
static size_t _hdrs_fmt_additional(usbus_t *usbus, usbus_hdr_gen_t *hdr)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (; hdr; hdr = hdr->next) {
|
||||
len += call_get_header(usbus, hdr);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t _hdrs_fmt_hdrs(usbus_t *usbus)
|
||||
{
|
||||
return _hdrs_fmt_additional(usbus, usbus->hdr_gen);
|
||||
}
|
||||
|
||||
static size_t _hdrs_fmt_endpoints(usbus_t *usbus, usbus_endpoint_t *ep)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
while (ep) {
|
||||
usb_descriptor_endpoint_t usb_ep;
|
||||
memset(&usb_ep, 0, sizeof(usb_descriptor_endpoint_t));
|
||||
usb_ep.length = sizeof(usb_descriptor_endpoint_t);
|
||||
usb_ep.type = USB_TYPE_DESCRIPTOR_ENDPOINT;
|
||||
usb_ep.address = ep->ep->num;
|
||||
if (ep->ep->dir == USB_EP_DIR_IN) {
|
||||
usb_ep.address |= 0x80;
|
||||
}
|
||||
usb_ep.attributes = _type_to_attribute(ep);
|
||||
usb_ep.max_packet_size = ep->maxpacketsize;
|
||||
usb_ep.interval = ep->interval;
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&usb_ep,
|
||||
sizeof(usb_descriptor_endpoint_t));
|
||||
_hdrs_fmt_additional(usbus, ep->hdr_gen);
|
||||
len += usb_ep.length;
|
||||
/* iterate to next endpoint */
|
||||
ep = ep->next;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static void _hdrs_fmt_iface(usbus_interface_t *iface,
|
||||
usb_descriptor_interface_t *usb_iface)
|
||||
{
|
||||
memset(usb_iface, 0, sizeof(usb_descriptor_interface_t));
|
||||
usb_iface->length = sizeof(usb_descriptor_interface_t);
|
||||
usb_iface->type = USB_TYPE_DESCRIPTOR_INTERFACE;
|
||||
usb_iface->interface_num = iface->idx;
|
||||
usb_iface->class = iface->class;
|
||||
usb_iface->subclass = iface->subclass;
|
||||
usb_iface->protocol = iface->protocol;
|
||||
}
|
||||
|
||||
static size_t _hdrs_fmt_iface_alts(usbus_t *usbus, usbus_interface_t *iface)
|
||||
{
|
||||
size_t len = 0;
|
||||
uint8_t alts = 1;
|
||||
|
||||
for (usbus_interface_alt_t *alt = iface->alts;
|
||||
alt;
|
||||
alt = alt->next) {
|
||||
usb_descriptor_interface_t usb_iface;
|
||||
_hdrs_fmt_iface(iface, &usb_iface);
|
||||
usb_iface.alternate_setting = alts++;
|
||||
usb_iface.num_endpoints = _num_endpoints_alt(alt);
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&usb_iface,
|
||||
sizeof(usb_descriptor_interface_t));
|
||||
len += _hdrs_fmt_additional(usbus, alt->hdr_gen);
|
||||
len += _hdrs_fmt_endpoints(usbus, alt->ep);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
static size_t _hdrs_fmt_ifaces(usbus_t *usbus)
|
||||
{
|
||||
size_t len = 0;
|
||||
|
||||
for (usbus_interface_t *iface = usbus->iface;
|
||||
iface;
|
||||
iface = iface->next) {
|
||||
usb_descriptor_interface_t usb_iface;
|
||||
_hdrs_fmt_iface(iface, &usb_iface);
|
||||
usb_iface.num_endpoints = _num_endpoints(iface);
|
||||
if (iface->descr) {
|
||||
usb_iface.idx = iface->descr->idx;
|
||||
}
|
||||
else {
|
||||
usb_iface.idx = 0;
|
||||
}
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&usb_iface,
|
||||
sizeof(usb_descriptor_interface_t));
|
||||
len += sizeof(usb_descriptor_interface_t);
|
||||
len += _hdrs_fmt_additional(usbus, iface->hdr_gen);
|
||||
len += _hdrs_fmt_endpoints(usbus, iface->ep);
|
||||
len += _hdrs_fmt_iface_alts(usbus, iface);
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t usbus_fmt_hdr_conf(usbus_t *usbus)
|
||||
{
|
||||
size_t len = 0;
|
||||
usb_descriptor_configuration_t conf;
|
||||
|
||||
memset(&conf, 0, sizeof(usb_descriptor_configuration_t));
|
||||
conf.length = sizeof(usb_descriptor_configuration_t);
|
||||
conf.type = USB_TYPE_DESCRIPTOR_CONFIGURATION;
|
||||
conf.total_length = sizeof(usb_descriptor_configuration_t);
|
||||
conf.val = 1;
|
||||
conf.attributes = USB_CONF_ATTR_RESERVED;
|
||||
if (USB_CONFIG_SELF_POWERED) {
|
||||
conf.attributes |= USB_CONF_ATTR_SELF_POWERED;
|
||||
}
|
||||
/* TODO: upper bound */
|
||||
/* USB max power is reported in increments of 2 mA */
|
||||
conf.max_power = USB_CONFIG_MAX_POWER / 2;
|
||||
conf.num_interfaces = _num_ifaces(usbus);
|
||||
len += sizeof(usb_descriptor_configuration_t);
|
||||
conf.total_length = _hdrs_config_size(usbus);
|
||||
conf.idx = usbus->config.idx;
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&conf, sizeof(conf));
|
||||
len += _hdrs_fmt_hdrs(usbus);
|
||||
len += _hdrs_fmt_ifaces(usbus);
|
||||
return len;
|
||||
}
|
||||
|
||||
size_t usbus_fmt_hdr_dev(usbus_t *usbus)
|
||||
{
|
||||
usb_descriptor_device_t desc;
|
||||
|
||||
memset(&desc, 0, sizeof(usb_descriptor_device_t));
|
||||
desc.length = sizeof(usb_descriptor_device_t);
|
||||
desc.type = USB_TYPE_DESCRIPTOR_DEVICE;
|
||||
desc.bcd_usb = USB_CONFIG_SPEC_BCDVERSION;
|
||||
desc.max_packet_size = USBUS_EP0_SIZE;
|
||||
desc.vendor_id = USB_CONFIG_VID;
|
||||
desc.product_id = USB_CONFIG_PID;
|
||||
desc.manufacturer_idx = usbus->manuf.idx;
|
||||
desc.product_idx = usbus->product.idx;
|
||||
/* USBUS supports only a single config at the moment */
|
||||
desc.num_configurations = 1;
|
||||
usbus_control_slicer_put_bytes(usbus, (uint8_t *)&desc,
|
||||
sizeof(usb_descriptor_device_t));
|
||||
return sizeof(usb_descriptor_device_t);
|
||||
}
|
Loading…
Reference in New Issue
Block a user