/** * \file * * \brief SAM USB host HPL * * Copyright (c) 2016-2018 Microchip Technology Inc. and its subsidiaries. * * \asf_license_start * * \page License * * Subject to your compliance with these terms, you may use Microchip * software and any derivatives exclusively with Microchip products. * It is your responsibility to comply with third party license terms applicable * to your use of third party software (including open source software) that * may accompany Microchip software. * * THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES, * WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE, * INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY, * AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE * LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL * LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE * SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE * POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT * ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY * RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY, * THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE. * * \asf_license_stop * */ #ifndef _HPL_USB_HOST_H_INCLUDED #define _HPL_USB_HOST_H_INCLUDED #include #include #include "hpl_usb_config.h" #ifdef __cplusplus extern "C" { #endif /** Driver version */ #define USB_H_VERSION 0x00000001 /** * @brief USB HCD callback types */ enum usb_h_cb_type { /** SOF generated */ USB_H_CB_SOF, /** Root Hub change detected */ USB_H_CB_ROOTHUB_CHANGE, /** Number of USB HCD callback types */ USB_H_CB_N }; /** * @brief USB HCD resource strategy */ enum usb_h_rsc_strategy { /** Normal resource allocation, e.g., * 1 bank for interrupt endpoint, * 2 bank for bulk endpoint and normal iso endpoint, * 3 bank for iso high bandwidth endpoint. */ USB_H_RSC_NORMAL = false, /** Minimal resource allocation, e.g., only 1 bank for bulk endpoints */ USB_H_RSC_MINIMAL = true }; /** * @brief USB HCD pipe states */ enum usb_h_pipe_state { /** Pipe is free to allocate */ USB_H_PIPE_S_FREE = 0x00, /** Pipe is in configuration */ USB_H_PIPE_S_CFG = 0x01, /** Pipe is allocated and idle */ USB_H_PIPE_S_IDLE = 0x02, /** Pipe in control setup stage */ USB_H_PIPE_S_SETUP = 0x03, /** Pipe in data IN stage */ USB_H_PIPE_S_DATI = 0x05, /** Pipe in data OUT stage */ USB_H_PIPE_S_DATO = 0x06, /** Pipe in data IN ZLP stage */ USB_H_PIPE_S_ZLPI = 0x07, /** Pipe in data OUT ZLP stage */ USB_H_PIPE_S_ZLPO = 0x08, /** Pipe in control status IN stage */ USB_H_PIPE_S_STATI = 0x09, /** Pipe in control status OUT stage */ USB_H_PIPE_S_STATO = 0x0A, /** Taken by physical pipe (in process) */ USB_H_PIPE_S_TAKEN = 0x10 }; /** * @brief USB HCD status code */ enum usb_h_status { /** OK */ USB_H_OK = ERR_NONE, /** Busy */ USB_H_BUSY = ERR_BUSY, /** Denied */ USB_H_DENIED = ERR_DENIED, /** Timeout */ USB_H_TIMEOUT = ERR_TIMEOUT, /** Abort */ USB_H_ABORT = ERR_ABORTED, /** Stall protocol */ USB_H_STALL = ERR_PROTOCOL, /** Transfer reset by pipe re-configure */ USB_H_RESET = ERR_REQ_FLUSHED, /** Argument error */ USB_H_ERR_ARG = ERR_INVALID_ARG, /** Operation not supported */ USB_H_ERR_UNSP_OP = ERR_UNSUPPORTED_OP, /** No resource */ USB_H_ERR_NO_RSC = ERR_NO_RESOURCE, /** Not initialized */ USB_H_ERR_NOT_INIT = ERR_NOT_INITIALIZED, /** Some general error */ USB_H_ERR = ERR_IO }; /** Forward declare for pipe structure */ struct usb_h_pipe; /** Forward declare for driver descriptor structure */ struct usb_h_desc; /** * \brief Prototyping USB HCD callback of SOF */ typedef void (*usb_h_cb_sof_t)(struct usb_h_desc *drv); /** * \brief Prototyping USB HCD callback of root hub changing. * According to the bitmap size, max port number is 31. */ typedef void (*usb_h_cb_roothub_t)(struct usb_h_desc *drv, uint8_t port, uint8_t ftr); /** * Prototyping USB HCD callback of pipe transfer done. * For control pipe, it's forced to call even if there is no transfer * in progress, since the pipe size could be changed at run time. */ typedef void (*usb_h_pipe_cb_xfer_t)(struct usb_h_pipe *pipe); /** Access to max packet size of a pipe */ #define usb_h_pipe_max_pkt_size(p) (p->max_pkt_size) /** Access to device address of a pipe */ #define usb_h_pipe_dev_addr(p) (p->dev) /** Access to endpoint address of a pipe */ #define usb_h_pipe_ep_addr(p) (p->ep) /** Access to state of a pipe */ #define usb_h_pipe_state(p) (p->x.general.state) /** Access to status of a pipe */ #define usb_h_pipe_status(p) (p->x.general.status) /** * @brief USB Host Controller device structure */ struct usb_h_desc { /** Pointer to hardware base */ void *hw; /** Pointer to private data for Host Controller driver */ void *prvt; /** Interrupt handling descriptor */ struct _irq_descriptor irq; /** Callback of SOF */ usb_h_cb_sof_t sof_cb; /** Callback of root hub change */ usb_h_cb_roothub_t rh_cb; #if CONF_USB_H_INST_OWNER_SP /** Extension for the driver owner (upper layer user) */ void *owner; #endif }; /** * @brief Transfer descriptor for control transfer * * Timing in USB 2.0 spec.: * - 9.2.6.1 : USB sets an upper limit of 5 seconds as the upper limit for any * command to be processed. * - 9.2.6.3 : if a device receives a SetAddress() request, the device must be * able to complete processing of the request and be able to * successfully complete the Status stage of the request within * 50 ms. * After successful completion of the Status stage, the device is * allowed a SetAddress() recovery interval of 2 ms. At the end of * this interval, the device must be able to accept Setup packets * addressed to the new address. * - 9.2.6.4 : For standard device requests that require no Data stage, a device * must be able to complete the request and be able to successfully * complete the Status stage of the request within 50 ms of receipt * of the request. This limitation applies to requests to the * device, interface, or endpoint. * For standard device requests that require data stage transfer to * the host, the device must be able to return the first data packet * to the host within 500 ms of receipt of the request. For * subsequent data packets, if any, the device must be able to * return them within 500 ms of successful completion of the * transmission of the previous packet. The device must then be * able to successfully complete the status stage within 50 ms after * returning the last data packet. * For standard device requests that require a data stage transfer * to the device, the 5-second limit applies. * - 9.2.6.5 : Unless specifically exempted in the class document, all * class-specific requests must meet the timing limitations for * standard device requests. * * Conclusion: * 1. Whole request with data: 5 seconds * 2. Whole request without data: 50 ms * 3. Data packets: 500 ms */ struct usb_h_ctrl_xfer { /** Pointer to transfer data */ uint8_t *data; /** Pointer to setup packet */ uint8_t *setup; /** Expected transfer size */ uint16_t size; /** Transfer count */ uint16_t count; /** Timeout for request, -1 if disable timeout */ int16_t req_timeout; /** Timeout between packets * (500ms for data and 50ms for status), -1 if disabled */ int16_t pkt_timeout; /** Packet size during transfer (<= allocate max packet size) */ uint16_t pkt_size; /** Transfer state */ uint8_t state; /** Last transfer status */ int8_t status; }; /** * @brief Transfer descriptor for bulk / interrupt / iso transfer */ struct usb_h_bulk_int_iso_xfer { /** Expected transfer size */ uint32_t size; /** Transfer count */ uint32_t count; /** Pointer to transfer data */ uint8_t *data; /** Reserved */ uint16_t reserved[3]; /** Transfer state */ uint8_t state; /** Last transfer status */ int8_t status; }; /** * @brief Transfer descriptor for periodic high bandwidth transfer */ struct usb_h_high_bw_xfer { /** Expected transfer size */ uint32_t size; /** Transfer count */ uint32_t count; /** Pointer to transfer data */ uint8_t *data; /** Micro frame packet sizes */ uint16_t pkt_size[3]; /** Transfer state */ uint8_t state; /** Last transfer status */ int8_t status; }; /** * @brief General transfer descriptor */ struct usb_h_xfer { /** Reserved for different transfer */ union { uint16_t u16[9]; uint8_t u8[18]; } reserved; /** Transfer state */ uint8_t state; /** Last transfer status */ int8_t status; }; /** * @brief USB Host Controller Driver Pipe structure */ struct usb_h_pipe { /** Pointer to the USB Host Controller Driver */ struct usb_h_desc *hcd; /** Pointer to the callback for transfer done */ usb_h_pipe_cb_xfer_t done; #if CONF_USB_H_INST_OWNER_SP /** Pointer to the pipe owner */ void *owner; #endif /** Endpoint max packet size (bits 10..0) */ uint16_t max_pkt_size; /** Device address */ uint8_t dev; /** Endpoint address */ uint8_t ep; /** Endpoint interval */ uint8_t interval; /** Endpoint type: Control, Isochronous, Bulk or Interrupt */ uint8_t type; /** Current toggle (driver dependent) */ uint8_t toggle; /** Endpoint number of banks (HW dependent) */ uint8_t bank : 2; /** Transfer speed (HW dependent) */ uint8_t speed : 2; /** High bandwidth periodic out */ uint8_t high_bw_out : 1; /** Uses DMA (on transfer) */ uint8_t dma : 1; /** Transfer ZLP support */ uint8_t zlp : 1; /** Transfer periodic */ uint8_t periodic_start : 1; /** Transfer status */ union { /** General transfer info */ struct usb_h_xfer general; /** Control transfer status */ struct usb_h_ctrl_xfer ctrl; /** Bulk interrupt iso transfer status */ struct usb_h_bulk_int_iso_xfer bii; /** Periodic high bandwidth transfer status */ struct usb_h_high_bw_xfer hbw; } x; }; /** * @brief USB HCD Initialization * * @param drv Pointer to the HCD driver instance * @param[in] hw Pointer to hardware base * @param[in] prvt The private driver data (implement specific) * * @return Operation result status * @retval ERR_DENIED Hardware has been enabled * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_init(struct usb_h_desc *drv, void *hw, void *prvt); /** * @brief USB HCD de-initialization * * @param drv The driver */ void _usb_h_deinit(struct usb_h_desc *drv); /** * @brief USB HCD enable * * @param drv The driver */ void _usb_h_enable(struct usb_h_desc *drv); /** * @brief USB HCD disable * * @param drv The driver */ void _usb_h_disable(struct usb_h_desc *drv); /** * @brief Register callbacks for USB HCD * * @param drv The driver * @param[in] type The callback type * @param[in] cb The callback function entry * * @return Operation result status * @retval ERR_INVALID_ARG Argument error * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_register_callback(struct usb_h_desc *drv, enum usb_h_cb_type type, FUNC_PTR cb); /** * @brief Return current frame number * * @param drv The driver * * @return current frame number */ uint16_t _usb_h_get_frame_n(struct usb_h_desc *drv); /** * @brief Return current micro frame number * * @param drv The driver * * @return current micro frame number */ uint8_t _usb_h_get_microframe_n(struct usb_h_desc *drv); /** * @brief Suspend the USB bus * * @param drv The driver */ void _usb_h_suspend(struct usb_h_desc *drv); /** * @brief Resume the USB bus * * @param drv The driver */ void _usb_h_resume(struct usb_h_desc *drv); /* Root hub related APIs */ /** * \brief Reset the root hub port * * \param[in,out] drv Pointer to the USB HCD driver * \param[in] port Root hub port, ignored if there is only one port */ void _usb_h_rh_reset(struct usb_h_desc *drv, uint8_t port); /** * \brief Suspend the root hub port * * \param[in,out] drv Pointer to the USB HCD driver * \param[in] port Root hub port, ignored if there is only one port */ void _usb_h_rh_suspend(struct usb_h_desc *drv, uint8_t port); /** * \brief Resume the root hub port * * \param[in,out] drv Pointer to the USB HCD driver * \param[in] port Root hub port, ignored if there is only one port */ void _usb_h_rh_resume(struct usb_h_desc *drv, uint8_t port); /** * \brief Root hub or port feature status check * * Check USB Spec. for hub status and feature selectors. * * \param[in] drv Pointer to the USB HCD driver * \param[in] port Set to 0 to get hub status, otherwise to get port status * \param[in] ftr Hub feature/status selector * (0: connection, 2: suspend, 4: reset, 9: LS, 10: HS) * * \return \c true if the status bit is 1 */ bool _usb_h_rh_check_status(struct usb_h_desc *drv, uint8_t port, uint8_t ftr); /* Pipe transfer functions */ /** * @brief Allocate a pipe for USB host communication * * @param drv The USB HCD driver * @param[in] dev The device address * @param[in] ep The endpoint address * @param[in] max_pkt_size The endpoint maximum packet size * @param[in] attr The endpoint attribute * @param[in] interval The endpoint interval * (bInterval of USB Endpoint Descriptor) * @param[in] speed The transfer speed of the endpoint * @param[in] minimum_rsc Minimum the resource usage, \sa usb_h_rsc_strategy * * @return Pointer to allocated pipe structure instance * @retval NULL allocation fail */ struct usb_h_pipe *_usb_h_pipe_allocate(struct usb_h_desc *drv, uint8_t dev, uint8_t ep, uint16_t max_pkt_size, uint8_t attr, uint8_t interval, uint8_t speed, bool minimum_rsc); /** * @brief Free an allocated pipe * * @param pipe The pipe * * @return Operation result status * @retval ERR_BUSY Pipe is busy, use \ref _usb_h_pipe_abort to abort * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_pipe_free(struct usb_h_pipe *pipe); /** * @brief Modify parameters of an allocated control pipe * * @param pipe The pipe * @param[in] dev The device address * @param[in] ep The endpoint address * @param[in] max_pkt_size The maximum packet size, must be equal or * less than allocated size * @param[in] speed The working speed * * @return Operation result status * @retval ERR_NOT_INITIALIZED Pipe is not allocated * @retval ERR_BUSY Pipe is busy transferring * @retval ERR_INVALID_ARG Argument error * @retval ERR_UNSUPPORTED_OP Pipe is not control pipe * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_pipe_set_control_param(struct usb_h_pipe *pipe, uint8_t dev, uint8_t ep, uint16_t max_pkt_size, uint8_t speed); /** * @brief Register transfer callback on a pipe * * @param pipe The pipe * @param[in] cb Transfer callback function * * @return Operation result status * @retval ERR_INVALID_ARG Argument error * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_pipe_register_callback(struct usb_h_pipe *pipe, usb_h_pipe_cb_xfer_t cb); /** * @brief Issue a control transfer (request) on a pipe * * \note When there is data stage, timeout between data packets is 500ms, the * timeout between last data packet and the status packet is 50ms. * * @param pipe The pipe * @param[in] setup Pointer to the setup packet * @param[in,out] data Pointer to the data buffer * @param[in] length The data length * @param[in] timeout Timeout for whole request in ms * * @return Operation result status * @retval ERR_NOT_INITIALIZED Pipe is not allocated * @retval ERR_BUSY Pipe is busy transferring * @retval ERR_INVALID_ARG Argument error * @retval ERR_UNSUPPORTED_OP Pipe is not control pipe * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_control_xfer(struct usb_h_pipe *pipe, uint8_t *setup, uint8_t *data, uint16_t length, int16_t timeout); /** * @brief Issue a bulk / interrupt / iso transfer on a pipe * * @param pipe The pipe * @param[in,out] data Pointer to the data buffer * @param[in] length The data length * @param[in] auto_zlp Auto append ZLP for OUT * * @return Operation result status * @retval ERR_NOT_INITIALIZED Pipe is not allocated * @retval ERR_BUSY Pipe is busy transferring * @retval ERR_INVALID_ARG Argument error * @retval ERR_UNSUPPORTED_OP Pipe is control pipe * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_bulk_int_iso_xfer(struct usb_h_pipe *pipe, uint8_t *data, uint32_t length, bool auto_zlp); /** * @brief Issue a periodic high bandwidth output on a pipe * * @param pipe The pipe * @param[in,out] data Pointer to the data buffer * @param[in] length The data length * @param[in] trans_pkt_size The transaction packet sizes in a micro frame, * 0 to use endpoint max packet size * * @return Operation result status * @retval ERR_NOT_INITIALIZED Pipe is not allocated * @retval ERR_BUSY Pipe is busy transferring * @retval ERR_INVALID_ARG Argument error * @retval ERR_UNSUPPORTED_OP Pipe is not high bandwidth periodic pipe, or * DMA feature not enabled, or * high bandwidth not enabled * @retval ERR_NONE Operation done successfully */ int32_t _usb_h_high_bw_out(struct usb_h_pipe *pipe, uint8_t *data, uint32_t length, uint16_t trans_pkt_size[3]); /** * @brief Check if pipe is busy transferring * * @param pipe The pipe * * @return \c true if pipe is busy */ bool _usb_h_pipe_is_busy(struct usb_h_pipe *pipe); /** * @brief Abort pending transfer on a pipe * * @param pipe The pipe */ void _usb_h_pipe_abort(struct usb_h_pipe *pipe); #ifdef __cplusplus } #endif #endif /* _HPL_USB_HOST_H_INCLUDED */