blob: aff6b27bc740effae626f670fdd7e546eff7d6d7 [file] [log] [blame]
/*
* Copyright (c) 2022 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
/**
* @file
* @brief USB host controller (UHC) driver API
*/
#ifndef ZEPHYR_INCLUDE_UHC_H
#define ZEPHYR_INCLUDE_UHC_H
#include <zephyr/kernel.h>
#include <zephyr/device.h>
#include <zephyr/net/buf.h>
#include <zephyr/usb/usb_ch9.h>
#include <zephyr/sys/dlist.h>
/**
* @brief USB host controller (UHC) driver API
* @defgroup uhc_api USB host controller driver API
* @ingroup io_interfaces
* @{
*/
/**
* UHC endpoint buffer info
*
* This structure is mandatory for all UHC request.
* It contains the meta data about the request and FIFOs
* to store net_buf structures for each request.
*
* The members of this structure should not be used
* directly by a higher layer (host stack).
*/
struct uhc_transfer {
/** dlist node */
sys_dnode_t node;
/** FIFO requests to process */
struct k_fifo queue;
/** FIFO to keep completed requests */
struct k_fifo done;
/** Device (peripheral) address */
uint8_t addr;
/** Endpoint to which request is associated */
uint8_t ep;
/** Endpoint attributes (TBD) */
uint8_t attrib;
/** Maximum packet size */
uint16_t mps;
/** Timeout in number of frames */
uint16_t timeout;
/** Flag marks request buffer claimed by the controller */
unsigned int claimed : 1;
/** Flag marks request buffer is queued */
unsigned int queued : 1;
/** Flag marks setup stage of transfer */
unsigned int setup : 1;
/** Transfer owner */
void *owner;
};
/**
* @brief USB host controller event types
*/
enum uhc_event_type {
/** Low speed device connected */
UHC_EVT_DEV_CONNECTED_LS,
/** Full speed device connected */
UHC_EVT_DEV_CONNECTED_FS,
/** High speed device connected */
UHC_EVT_DEV_CONNECTED_HS,
/** Device (peripheral) removed */
UHC_EVT_DEV_REMOVED,
/** Bus reset operation finished */
UHC_EVT_RESETED,
/** Bus suspend operation finished */
UHC_EVT_SUSPENDED,
/** Bus resume operation finished */
UHC_EVT_RESUMED,
/** Remote wakeup signal */
UHC_EVT_RWUP,
/** Endpoint request result event */
UHC_EVT_EP_REQUEST,
/**
* Non-correctable error event, requires attention from higher
* levels or application.
*/
UHC_EVT_ERROR,
};
/**
* USB host controller event
*
* Common structure for all events that originate from
* the UHC driver and are passed to higher layer using
* message queue and a callback (uhc_event_cb_t) provided
* by higher layer during controller initialization (uhc_init).
*/
struct uhc_event {
/** slist node for the message queue */
sys_snode_t node;
/** Event type */
enum uhc_event_type type;
union {
/** Event value */
uint32_t value;
/** Pointer to request used only for UHC_EVT_EP_REQUEST */
struct uhc_transfer *xfer;
};
/** Event status, 0 on success, other (transfer) values on error */
int status;
/** Pointer to controller's device struct */
const struct device *dev;
};
/**
* @typedef uhc_event_cb_t
* @brief Callback to submit UHC event to higher layer.
*
* At the higher level, the event is to be inserted into a message queue.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] event Point to event structure
*
* @return 0 on success, all other values should be treated as error.
*/
typedef int (*uhc_event_cb_t)(const struct device *dev,
const struct uhc_event *const event);
/**
* USB host controller capabilities
*
* This structure is mainly intended for the USB host stack.
*/
struct uhc_device_caps {
/** USB high speed capable controller */
uint32_t hs : 1;
};
/**
* Controller is initialized by uhc_init()
*/
#define UHC_STATUS_INITIALIZED 0
/**
* Controller is enabled and all API functions are available
*/
#define UHC_STATUS_ENABLED 1
/**
* Common UHC driver data structure
*
* Mandatory structure for each UHC controller driver.
* To be implemented as device's private data (device->data).
*/
struct uhc_data {
/** Controller capabilities */
struct uhc_device_caps caps;
/** Driver access mutex */
struct k_mutex mutex;
/** dlist for control transfers */
sys_dlist_t ctrl_xfers;
/** dlist for bulk transfers */
sys_dlist_t bulk_xfers;
/** Callback to submit an UHC event to upper layer */
uhc_event_cb_t event_cb;
/** USB host controller status */
atomic_t status;
/** Driver private data */
void *priv;
};
/**
* @brief Checks whether the controller is initialized.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return true if controller is initialized, false otherwise
*/
static inline bool uhc_is_initialized(const struct device *dev)
{
struct uhc_data *data = dev->data;
return atomic_test_bit(&data->status, UHC_STATUS_INITIALIZED);
}
/**
* @brief Checks whether the controller is enabled.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return true if controller is enabled, false otherwise
*/
static inline bool uhc_is_enabled(const struct device *dev)
{
struct uhc_data *data = dev->data;
return atomic_test_bit(&data->status, UHC_STATUS_ENABLED);
}
/**
* @cond INTERNAL_HIDDEN
*/
struct uhc_api {
int (*lock)(const struct device *dev);
int (*unlock)(const struct device *dev);
int (*init)(const struct device *dev);
int (*enable)(const struct device *dev);
int (*disable)(const struct device *dev);
int (*shutdown)(const struct device *dev);
int (*bus_reset)(const struct device *dev);
int (*sof_enable)(const struct device *dev);
int (*bus_suspend)(const struct device *dev);
int (*bus_resume)(const struct device *dev);
int (*ep_enqueue)(const struct device *dev,
struct uhc_transfer *const xfer);
int (*ep_dequeue)(const struct device *dev,
struct uhc_transfer *const xfer);
};
/**
* @endcond
*/
/**
* @brief Reset USB bus
*
* Perform USB bus reset, controller may emit UHC_EVT_RESETED
* at the end of reset signaling.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EBUSY if the controller is already performing a bus operation
*/
static inline int uhc_bus_reset(const struct device *dev)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
ret = api->bus_reset(dev);
api->unlock(dev);
return ret;
}
/**
* @brief Enable Start of Frame generator
*
* Enable SOF generator.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EALREADY if already enabled
*/
static inline int uhc_sof_enable(const struct device *dev)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
ret = api->sof_enable(dev);
api->unlock(dev);
return ret;
}
/**
* @brief Suspend USB bus
*
* Disable SOF generator and emit UHC_EVT_SUSPENDED event when USB bus
* is suspended.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EALREADY if already suspended
*/
static inline int uhc_bus_suspend(const struct device *dev)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
ret = api->bus_suspend(dev);
api->unlock(dev);
return ret;
}
/**
* @brief Resume USB bus
*
* Signal resume for at least 20ms, emit UHC_EVT_RESUMED at the end of USB
* bus resume signaling. The SoF generator should subsequently start within 3ms.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EBUSY if the controller is already performing a bus operation
*/
static inline int uhc_bus_resume(const struct device *dev)
{
const struct uhc_api *api = dev->api;
int ret;
api->lock(dev);
ret = api->bus_resume(dev);
api->unlock(dev);
return ret;
}
/**
* @brief Allocate UHC transfer
*
* Allocate a new transfer from common transfer pool.
* Transfer has no buffers after allocation, these can be
* requested and assigned separately.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] addr Device (peripheral) address
* @param[in] ep Endpoint address
* @param[in] attrib Endpoint attributes
* @param[in] mps Maximum packet size of the endpoint
* @param[in] timeout Timeout in number of frames
* @param[in] owner Transfer owner
*
* @return pointer to allocated transfer or NULL on error.
*/
struct uhc_transfer *uhc_xfer_alloc(const struct device *dev,
const uint8_t addr,
const uint8_t ep,
const uint8_t attrib,
const uint16_t mps,
const uint16_t timeout,
void *const owner);
/**
* @brief Free UHC transfer and any buffers
*
* Free any buffers and put the transfer back into the transfer pool.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] xfer Pointer to UHC transfer
*
* @return 0 on success, all other values should be treated as error.
*/
int uhc_xfer_free(const struct device *dev,
struct uhc_transfer *const xfer);
/**
* @brief Allocate UHC transfer buffer
*
* Allocate a new buffer from common request buffer pool and
* assign it to the transfer.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] xfer Pointer to UHC transfer
* @param[in] size Size of the request buffer
*
* @return pointer to allocated request or NULL on error.
*/
struct net_buf *uhc_xfer_buf_alloc(const struct device *dev,
struct uhc_transfer *const xfer,
const size_t size);
/**
* @brief Free UHC request buffer
*
* Put the buffer back into the request buffer pool.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] buf Pointer to UHC request buffer
*
* @return 0 on success, all other values should be treated as error.
*/
int uhc_xfer_buf_free(const struct device *dev, struct net_buf *const buf);
/**
* @brief Queue USB host controller transfer
*
* Add transfer to the queue. If the queue is empty, the transfer
* can be claimed by the controller immediately.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] xfer Pointer to UHC transfer
*
* @return 0 on success, all other values should be treated as error.
* @retval -EPERM controller is not initialized
*/
int uhc_ep_enqueue(const struct device *dev, struct uhc_transfer *const xfer);
/**
* @brief Remove a USB host controller transfers from queue
*
* Not implemented yet.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] xfer Pointer to UHC transfer
*
* @return 0 on success, all other values should be treated as error.
* @retval -EPERM controller is not initialized
*/
int uhc_ep_dequeue(const struct device *dev, struct uhc_transfer *const xfer);
/**
* @brief Initialize USB host controller
*
* Initialize USB host controller.
*
* @param[in] dev Pointer to device struct of the driver instance
* @param[in] event_cb Event callback from the higher layer (USB host stack)
*
* @return 0 on success, all other values should be treated as error.
* @retval -EINVAL on parameter error (no callback is passed)
* @retval -EALREADY already initialized
*/
int uhc_init(const struct device *dev, uhc_event_cb_t event_cb);
/**
* @brief Enable USB host controller
*
* Enable powered USB host controller and allow host stack to
* recognize and enumerate devices.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EPERM controller is not initialized
* @retval -EALREADY already enabled
*/
int uhc_enable(const struct device *dev);
/**
* @brief Disable USB host controller
*
* Disable enabled USB host controller.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EALREADY already disabled
*/
int uhc_disable(const struct device *dev);
/**
* @brief Poweroff USB host controller
*
* Shut down the controller completely to reduce energy consumption
* or to change the role of the controller.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return 0 on success, all other values should be treated as error.
* @retval -EALREADY controller is already uninitialized
*/
int uhc_shutdown(const struct device *dev);
/**
* @brief Get USB host controller capabilities
*
* Obtain the capabilities of the controller
* such as high speed (HS), and more.
*
* @param[in] dev Pointer to device struct of the driver instance
*
* @return USB host controller capabilities.
*/
static inline struct uhc_device_caps uhc_caps(const struct device *dev)
{
struct uhc_data *data = dev->data;
return data->caps;
}
/**
* @}
*/
#endif /* ZEPHYR_INCLUDE_UHC_H */