/**
* \file
*
* \brief SAM USB Driver.
*
* Copyright (C) 2014-2016 Atmel Corporation. All rights reserved.
*
* \asf_license_start
*
* \page License
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* 3. The name of Atmel may not be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* 4. This software may only be redistributed and used in connection with an
* Atmel microcontroller product.
*
* THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
* EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Atmel Support
*/
#include
#include "usb.h"
/** Fields definition from a LPM TOKEN */
#define USB_LPM_ATTRIBUT_BLINKSTATE_MASK (0xF << 0)
#define USB_LPM_ATTRIBUT_HIRD_MASK (0xF << 4)
#define USB_LPM_ATTRIBUT_REMOTEWAKE_MASK (1 << 8)
#define USB_LPM_ATTRIBUT_BLINKSTATE(value) ((value & 0xF) << 0)
#define USB_LPM_ATTRIBUT_HIRD(value) ((value & 0xF) << 4)
#define USB_LPM_ATTRIBUT_REMOTEWAKE(value) ((value & 1) << 8)
#define USB_LPM_ATTRIBUT_BLINKSTATE_L1 USB_LPM_ATTRIBUT_BLINKSTATE(1)
/**
* \brief Mask selecting the index part of an endpoint address
*/
#define USB_EP_ADDR_MASK 0x0f
/**
* \brief Endpoint transfer direction is IN
*/
#define USB_EP_DIR_IN 0x80
/**
* \brief Endpoint transfer direction is OUT
*/
#define USB_EP_DIR_OUT 0x00
/**
* \name USB SRAM data containing pipe descriptor table
* The content of the USB SRAM can be :
* - modified by USB hardware interface to update pipe status.
* Thereby, it is read by software.
* - modified by USB software to control pipe.
* Thereby, it is read by hardware.
* This data section is volatile.
*
* @{
*/
COMPILER_PACK_SET(1)
COMPILER_WORD_ALIGNED
union {
UsbDeviceDescriptor usb_endpoint_table[USB_EPT_NUM];
#if !SAML22
UsbHostDescriptor usb_pipe_table[USB_PIPE_NUM];
#endif
} usb_descriptor_table;
COMPILER_PACK_RESET()
/** @} */
/**
* \brief Local USB module instance
*/
static struct usb_module *_usb_instances;
#if !SAML22
/**
* \brief Host pipe callback structure variable
*/
static struct usb_pipe_callback_parameter pipe_callback_para;
#endif
/* Device LPM callback variable */
static uint32_t device_callback_lpm_wakeup_enable;
/**
* \brief Device endpoint callback parameter variable, used to transfer info to UDD wrapper layer
*/
static struct usb_endpoint_callback_parameter ep_callback_para;
/**
* \internal USB Device IRQ Mask Bits Map
*/
static const uint16_t _usb_device_irq_bits[USB_DEVICE_CALLBACK_N] = {
USB_DEVICE_INTFLAG_SOF,
USB_DEVICE_INTFLAG_EORST,
USB_DEVICE_INTFLAG_WAKEUP | USB_DEVICE_INTFLAG_EORSM | USB_DEVICE_INTFLAG_UPRSM,
USB_DEVICE_INTFLAG_RAMACER,
USB_DEVICE_INTFLAG_SUSPEND,
USB_DEVICE_INTFLAG_LPMNYET,
USB_DEVICE_INTFLAG_LPMSUSP,
};
/**
* \internal USB Device IRQ Mask Bits Map
*/
static const uint8_t _usb_endpoint_irq_bits[USB_DEVICE_EP_CALLBACK_N] = {
USB_DEVICE_EPINTFLAG_TRCPT_Msk,
USB_DEVICE_EPINTFLAG_TRFAIL_Msk,
USB_DEVICE_EPINTFLAG_RXSTP,
USB_DEVICE_EPINTFLAG_STALL_Msk
};
#if !SAML22
/**
* \brief Bit mask for pipe job busy status
*/
uint32_t host_pipe_job_busy_status = 0;
/**
* \brief Registers a USB host callback
*
* Registers a callback function which is implemented by the user.
*
* \note The callback must be enabled by \ref usb_host_enable_callback,
* in order for the interrupt handler to call it when the conditions for the
* callback type is met.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
* \param[in] callback_func Pointer to callback function
*
* \return Status of the registration operation.
* \retval STATUS_OK The callback was registered successfully.
*/
enum status_code usb_host_register_callback(struct usb_module *module_inst,
enum usb_host_callback callback_type,
usb_host_callback_t callback_func)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(callback_func);
/* Register callback function */
module_inst->host_callback[callback_type] = callback_func;
/* Set the bit corresponding to the callback_type */
module_inst->host_registered_callback_mask |= (1 << callback_type);
return STATUS_OK;
}
/**
* \brief Unregisters a USB host callback
*
* Unregisters an asynchronous callback implemented by the user. Removing it
* from the internal callback registration table.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the de-registration operation.
* \retval STATUS_OK The callback was unregistered successfully.
*/
enum status_code usb_host_unregister_callback(struct usb_module *module_inst,
enum usb_host_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
/* Unregister callback function */
module_inst->host_callback[callback_type] = NULL;
/* Clear the bit corresponding to the callback_type */
module_inst->host_registered_callback_mask &= ~(1 << callback_type);
return STATUS_OK;
}
/**
* \brief Enables USB host callback generation for a given type.
*
* Enables asynchronous callbacks for a given logical type.
* This must be called before USB host generate callback events.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback enable operation.
* \retval STATUS_OK The callback was enabled successfully.
*/
enum status_code usb_host_enable_callback(struct usb_module *module_inst,
enum usb_host_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
/* Enable callback */
module_inst->host_enabled_callback_mask |= (1 << callback_type);
if (callback_type == USB_HOST_CALLBACK_SOF) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_HSOF;
}
if (callback_type == USB_HOST_CALLBACK_RESET) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_RST;
}
if (callback_type == USB_HOST_CALLBACK_WAKEUP) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_WAKEUP;
}
if (callback_type == USB_HOST_CALLBACK_DNRSM) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_DNRSM;
}
if (callback_type == USB_HOST_CALLBACK_UPRSM) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_UPRSM;
}
if (callback_type == USB_HOST_CALLBACK_RAMACER) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_RAMACER;
}
if (callback_type == USB_HOST_CALLBACK_CONNECT) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_DCONN;
}
if (callback_type == USB_HOST_CALLBACK_DISCONNECT) {
module_inst->hw->HOST.INTENSET.reg = USB_HOST_INTENSET_DDISC;
}
return STATUS_OK;
}
/**
* \brief Disables USB host callback generation for a given type.
*
* Disables asynchronous callbacks for a given logical type.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback disable operation.
* \retval STATUS_OK The callback was disabled successfully.
*/
enum status_code usb_host_disable_callback(struct usb_module *module_inst,
enum usb_host_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
/* Disable callback */
module_inst->host_enabled_callback_mask &= ~(1 << callback_type);
if (callback_type == USB_HOST_CALLBACK_SOF) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_HSOF;
}
if (callback_type == USB_HOST_CALLBACK_RESET) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_RST;
}
if (callback_type == USB_HOST_CALLBACK_WAKEUP) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_WAKEUP;
}
if (callback_type == USB_HOST_CALLBACK_DNRSM) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_DNRSM;
}
if (callback_type == USB_HOST_CALLBACK_UPRSM) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_UPRSM;
}
if (callback_type == USB_HOST_CALLBACK_RAMACER) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_RAMACER;
}
if (callback_type == USB_HOST_CALLBACK_CONNECT) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_DCONN;
}
if (callback_type == USB_HOST_CALLBACK_DISCONNECT) {
module_inst->hw->HOST.INTENCLR.reg = USB_HOST_INTENCLR_DDISC;
}
return STATUS_OK;
}
/**
* \brief Initializes an USB host pipe configuration structure to defaults.
*
* Initializes a given USB host pipe configuration structure to a
* set of known default values. This function should be called on all new
* instances of these configuration structures before being modified by the
* user application.
*
* The default configuration is as follows:
* \li device address is 0
* \li endpoint address is 0
* \li pipe type is control
* \li interval is 0
* \li pipe size is 8
*
* \param[out] ep_config Configuration structure to initialize to default values
*/
void usb_host_pipe_get_config_defaults(struct usb_host_pipe_config *ep_config)
{
/* Sanity check arguments */
Assert(ep_config);
/* Write default config to config struct */
ep_config->device_address = 0;
ep_config->endpoint_address = 0;
ep_config->pipe_type = USB_HOST_PIPE_TYPE_CONTROL;
ep_config->binterval = 0;
ep_config->size = 8;
}
/**
* \brief Writes an USB host pipe configuration to the hardware module.
*
* Writes out a given configuration of an USB host pipe
* configuration to the hardware module. If the pipe is already configured,
* the new configuration will replace the existing one.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] ep_config Configuration settings for the pipe
*
* \return Status of the host pipe configuration operation.
* \retval STATUS_OK The host pipe was configured successfully.
*/
enum status_code usb_host_pipe_set_config(struct usb_module *module_inst, uint8_t pipe_num,
struct usb_host_pipe_config *ep_config)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
Assert(ep_config);
/* set pipe config */
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.BK = 0;
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE = ep_config->pipe_type;
module_inst->hw->HOST.HostPipe[pipe_num].BINTERVAL.reg =
ep_config->binterval;
if (ep_config->endpoint_address == 0) {
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_SETUP;
} else if (ep_config->endpoint_address & USB_EP_DIR_IN) {
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_IN;
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSSET.reg =
USB_HOST_PSTATUSSET_BK0RDY;
} else {
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_OUT;
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSCLR.reg =
USB_HOST_PSTATUSCLR_BK0RDY;
}
memset((uint8_t *)&usb_descriptor_table.usb_pipe_table[pipe_num], 0,
sizeof(usb_descriptor_table.usb_pipe_table[0]));
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].CTRL_PIPE.bit.PDADDR =
ep_config->device_address;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].CTRL_PIPE.bit.PEPNUM =
ep_config->endpoint_address & USB_EP_ADDR_MASK;
if (ep_config->size == 1023) {
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.SIZE = 0x07;
} else {
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.SIZE =
(32 - clz(((uint32_t)min(max(ep_config->size, 8), 1024) << 1) - 1) - 1 - 3);
}
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_num);
return STATUS_OK;
}
/**
* \brief Gets an USB host pipe configuration.
*
* Gets out the configuration of an USB host pipe from the hardware module.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[out] ep_config Configuration settings for the pipe
*
* \return Status of the get host pipe configuration operation.
* \retval STATUS_OK The host pipe configuration was read successfully.
*/
enum status_code usb_host_pipe_get_config(struct usb_module *module_inst, uint8_t pipe_num,
struct usb_host_pipe_config *ep_config)
{
uint32_t size;
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
Assert(ep_config);
/* get pipe config from setting register */
ep_config->device_address =
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].CTRL_PIPE.bit.PDADDR;
ep_config->endpoint_address =
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].CTRL_PIPE.bit.PEPNUM;
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN ==
USB_HOST_PIPE_TOKEN_IN) {
ep_config->endpoint_address |= USB_EP_DIR_IN;
}
ep_config->pipe_type = (enum usb_host_pipe_type)module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE;
ep_config->binterval =
module_inst->hw->HOST.HostPipe[pipe_num].BINTERVAL.reg;
size = usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.SIZE;
if (size == 0x07) {
ep_config->size = 1023;
} else {
ep_config->size = (8 << size);
}
return STATUS_OK;
}
/**
* \brief Registers a USB host pipe callback
*
* Registers a callback function which is implemented by the user.
*
* \note The callback must be enabled by \ref usb_host_pipe_enable_callback,
* in order for the interrupt handler to call it when the conditions for the
* callback type is met.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] callback_type Callback type given by an enum
* \param[in] callback_func Pointer to callback function
*
* \return Status of the registration operation.
* \retval STATUS_OK The callback was registered successfully.
*/
enum status_code usb_host_pipe_register_callback(
struct usb_module *module_inst, uint8_t pipe_num,
enum usb_host_pipe_callback callback_type,
usb_host_pipe_callback_t callback_func)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(pipe_num < USB_PIPE_NUM);
Assert(callback_func);
/* Register callback function */
module_inst->host_pipe_callback[pipe_num][callback_type] = callback_func;
/* Set the bit corresponding to the callback_type */
module_inst->host_pipe_registered_callback_mask[pipe_num] |= (1 << callback_type);
return STATUS_OK;
}
/**
* \brief Unregisters a USB host pipe callback
*
* Unregisters an asynchronous callback implemented by the user. Removing it
* from the internal callback registration table.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the de-registration operation.
* \retval STATUS_OK The callback was unregistered successfully.
*/
enum status_code usb_host_pipe_unregister_callback(
struct usb_module *module_inst, uint8_t pipe_num,
enum usb_host_pipe_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(pipe_num < USB_PIPE_NUM);
/* Unregister callback function */
module_inst->host_pipe_callback[pipe_num][callback_type] = NULL;
/* Clear the bit corresponding to the callback_type */
module_inst->host_pipe_registered_callback_mask[pipe_num] &= ~(1 << callback_type);
return STATUS_OK;
}
/**
* \brief Enables USB host pipe callback generation for a given type.
*
* Enables asynchronous callbacks for a given logical type.
* This must be called before USB host pipe generate callback events.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback enable operation.
* \retval STATUS_OK The callback was enabled successfully.
*/
enum status_code usb_host_pipe_enable_callback(
struct usb_module *module_inst, uint8_t pipe_num,
enum usb_host_pipe_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
/* Enable callback */
module_inst->host_pipe_enabled_callback_mask[pipe_num] |= (1 << callback_type);
if (callback_type == USB_HOST_PIPE_CALLBACK_TRANSFER_COMPLETE) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENSET.reg = USB_HOST_PINTENSET_TRCPT_Msk;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_ERROR) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENSET.reg =
USB_HOST_PINTENSET_TRFAIL | USB_HOST_PINTENSET_PERR;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_SETUP) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENSET.reg = USB_HOST_PINTENSET_TXSTP;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_STALL) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENSET.reg = USB_HOST_PINTENSET_STALL;
}
return STATUS_OK;
}
/**
* \brief Disables USB host callback generation for a given type.
*
* Disables asynchronous callbacks for a given logical type.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback disable operation.
* \retval STATUS_OK The callback was disabled successfully.
*/
enum status_code usb_host_pipe_disable_callback(
struct usb_module *module_inst, uint8_t pipe_num,
enum usb_host_pipe_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
/* Enable callback */
module_inst->host_pipe_enabled_callback_mask[pipe_num] &= ~(1 << callback_type);
if (callback_type == USB_HOST_PIPE_CALLBACK_TRANSFER_COMPLETE) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENCLR.reg = USB_HOST_PINTENCLR_TRCPT_Msk;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_ERROR) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENCLR.reg =
USB_HOST_PINTENCLR_TRFAIL| USB_HOST_PINTENCLR_PERR;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_SETUP) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENCLR.reg = USB_HOST_PINTENCLR_TXSTP;
}
if (callback_type == USB_HOST_PIPE_CALLBACK_STALL) {
module_inst->hw->HOST.HostPipe[pipe_num].PINTENCLR.reg = USB_HOST_PINTENCLR_STALL;
}
return STATUS_OK;
}
/**
* \brief Sends the setup package.
*
* Sends the setup package.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] buf Pointer to data buffer
*
* \return Status of the setup operation.
* \retval STATUS_OK The setup job was set successfully.
* \retval STATUS_BUSY The pipe is busy.
* \retval STATUS_ERR_NOT_INITIALIZED The pipe has not been configured.
*/
enum status_code usb_host_pipe_setup_job(struct usb_module *module_inst,
uint8_t pipe_num, uint8_t *buf)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
if (host_pipe_job_busy_status & (1 << pipe_num)) {
return STATUS_BUSY;
}
/* Set busy status */
host_pipe_job_busy_status |= 1 << pipe_num;
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE ==
USB_HOST_PIPE_TYPE_DISABLE) {
return STATUS_ERR_NOT_INITIALIZED;
}
/* get pipe config from setting register */
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].ADDR.reg = (uint32_t)buf;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
8;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE =
0;
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_SETUP;
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
usb_host_pipe_unfreeze(module_inst, pipe_num);
return STATUS_OK;
}
/**
* \brief USB host pipe read job.
*
* USB host pipe read job by set and start an in transaction transfer.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] buf Pointer to data buffer
* \param[in] buf_size Data buffer size
* \note The buffer length should not larger than 0x3FFF
*
* \return Status of the setting operation.
* \retval STATUS_OK The read job was set successfully.
* \retval STATUS_BUSY The pipe is busy.
* \retval STATUS_ERR_NOT_INITIALIZED The pipe has not been configured.
*/
enum status_code usb_host_pipe_read_job(struct usb_module *module_inst,
uint8_t pipe_num, uint8_t *buf, uint32_t buf_size)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
if (host_pipe_job_busy_status & (1 << pipe_num)) {
return STATUS_BUSY;
}
/* Set busy status */
host_pipe_job_busy_status |= 1 << pipe_num;
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE ==
USB_HOST_PIPE_TYPE_DISABLE) {
return STATUS_ERR_NOT_INITIALIZED;
}
/* get pipe config from setting register */
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].ADDR.reg = (uint32_t)buf;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
0;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE =
buf_size;
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_IN;
/* Start transfer */
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSCLR.reg = USB_HOST_PSTATUSCLR_BK0RDY;
usb_host_pipe_unfreeze(module_inst, pipe_num);
return STATUS_OK;
}
/**
* \brief USB host pipe write job.
*
* USB host pipe write job by set and start an out transaction transfer.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] buf Pointer to data buffer
* \param[in] buf_size Data buffer size
* \note The buffer length should not larger than 0x3FFF
*
* \return Status of the setting operation.
* \retval STATUS_OK The write job was set successfully.
* \retval STATUS_BUSY The pipe is busy.
* \retval STATUS_ERR_NOT_INITIALIZED The pipe has not been configured.
*/
enum status_code usb_host_pipe_write_job(struct usb_module *module_inst,
uint8_t pipe_num, uint8_t *buf, uint32_t buf_size)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
if (host_pipe_job_busy_status & (1 << pipe_num)) {
return STATUS_BUSY;
}
/* Set busy status */
host_pipe_job_busy_status |= 1 << pipe_num;
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE ==
USB_HOST_PIPE_TYPE_DISABLE) {
return STATUS_ERR_NOT_INITIALIZED;
}
/* get pipe config from setting register */
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].ADDR.reg = (uint32_t)buf;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT =
buf_size;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE =
0;
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTOKEN =
USB_HOST_PIPE_TOKEN_OUT;
/* Start transfer */
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
usb_host_pipe_unfreeze(module_inst, pipe_num);
return STATUS_OK;
}
/**
* \brief USB host abort a pipe job.
*
* USB host pipe abort job by freeze the pipe.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
*
* \return Status of the setting operation.
* \retval STATUS_OK The abort job was set successfully.
* \retval STATUS_ERR_NOT_INITIALIZED The pipe has not been configured.
*/
enum status_code usb_host_pipe_abort_job(struct usb_module *module_inst, uint8_t pipe_num)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE ==
USB_HOST_PIPE_TYPE_DISABLE) {
return STATUS_ERR_NOT_INITIALIZED;
}
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSSET.reg = USB_HOST_PSTATUSSET_PFREEZE;
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_num);
return STATUS_OK;
}
/**
* \brief Sends the LPM package.
*
* Sends the LPM package.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] b_remotewakeup Remote wake up flag
* \param[in] hird Host Initiated Resume Duration
*
* \return Status of the setup operation.
* \retval STATUS_OK The setup job was set successfully.
* \retval STATUS_BUSY The pipe is busy.
* \retval STATUS_ERR_NOT_INITIALIZED The pipe has not been configured.
*/
enum status_code usb_host_pipe_lpm_job(struct usb_module *module_inst,
uint8_t pipe_num, bool b_remotewakeup, uint8_t hird)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(pipe_num < USB_PIPE_NUM);
if (host_pipe_job_busy_status & (1 << pipe_num)) {
return STATUS_BUSY;
}
/* Set busy status */
host_pipe_job_busy_status |= 1 << pipe_num;
if (module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE ==
USB_HOST_PIPE_TYPE_DISABLE) {
return STATUS_ERR_NOT_INITIALIZED;
}
module_inst->hw->HOST.HostPipe[pipe_num].PCFG.bit.PTYPE =
USB_HOST_PIPE_TYPE_EXTENDED;
/* get pipe config from setting register */
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].EXTREG.bit.SUBPID = 0x3;
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].EXTREG.bit.VARIABLE =
USB_LPM_ATTRIBUT_REMOTEWAKE(b_remotewakeup) |
USB_LPM_ATTRIBUT_HIRD(hird) |
USB_LPM_ATTRIBUT_BLINKSTATE_L1;
module_inst->hw->HOST.HostPipe[pipe_num].PSTATUSSET.reg = USB_HOST_PSTATUSSET_BK0RDY;
usb_host_pipe_unfreeze(module_inst, pipe_num);
return STATUS_OK;
}
/**
* \internal
* \brief Function called by USB interrupt to manage USB host interrupts
*
* USB host interrupt events are split into four sections:
* - USB line events
* (Device dis/connection, SOF, reset, resume, wakeup, error)
* - Pipe events
* (End of data transfer, setup, stall, error)
*/
static void _usb_host_interrupt_handler(void)
{
uint32_t pipe_int;
uint32_t flags;
/* Manage pipe interrupts */
pipe_int = ctz(_usb_instances->hw->HOST.PINTSMRY.reg);
if (pipe_int < 32) {
/* pipe interrupts */
/* get interrupt flags */
flags = _usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg;
/* host pipe transfer complete interrupt */
if (flags & USB_HOST_PINTFLAG_TRCPT_Msk) {
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_int);
/* clear the flag */
_usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg =
USB_HOST_PINTFLAG_TRCPT_Msk;
if(_usb_instances->host_pipe_enabled_callback_mask[pipe_int] &
(1 << USB_HOST_PIPE_CALLBACK_TRANSFER_COMPLETE)) {
pipe_callback_para.pipe_num = pipe_int;
if (_usb_instances->hw->HOST.HostPipe[pipe_int].PCFG.bit.PTOKEN ==
USB_HOST_PIPE_TOKEN_IN) {
/* in */
pipe_callback_para.transfered_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
pipe_callback_para.required_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE;
usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
} else {
/* out */
pipe_callback_para.transfered_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE;
pipe_callback_para.required_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
if (0 == pipe_callback_para.transfered_size) {
pipe_callback_para.transfered_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.BYTE_COUNT;
}
}
(_usb_instances->host_pipe_callback[pipe_int]
[USB_HOST_PIPE_CALLBACK_TRANSFER_COMPLETE])(_usb_instances, &pipe_callback_para);
}
}
/* host pipe transfer fail interrupt */
if (flags & USB_HOST_PINTFLAG_TRFAIL) {
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_int);
/* clear the flag */
_usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg =
USB_HOST_PINTFLAG_TRFAIL;
}
/* host pipe error interrupt */
if (flags & USB_HOST_PINTFLAG_PERR) {
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_int);
/* clear the flag */
_usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg =
USB_HOST_PINTFLAG_PERR;
if(_usb_instances->host_pipe_enabled_callback_mask[pipe_int] &
(1 << USB_HOST_PIPE_CALLBACK_ERROR)) {
pipe_callback_para.pipe_num = pipe_int;
pipe_callback_para.pipe_error_status =
usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].STATUS_PIPE.reg & 0x1F;
(_usb_instances->host_pipe_callback[pipe_int]
[USB_HOST_PIPE_CALLBACK_ERROR])(_usb_instances, &pipe_callback_para);
}
}
/* host pipe transmitted setup interrupt */
if (flags & USB_HOST_PINTFLAG_TXSTP) {
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_int);
/* clear the flag */
_usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg =
USB_HOST_PINTFLAG_TXSTP;
if(_usb_instances->host_pipe_enabled_callback_mask[pipe_int] &
(1 << USB_HOST_PIPE_CALLBACK_SETUP)) {
pipe_callback_para.pipe_num = pipe_int;
pipe_callback_para.transfered_size = usb_descriptor_table.usb_pipe_table[pipe_int].HostDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE;
(_usb_instances->host_pipe_callback[pipe_int]
[USB_HOST_PIPE_CALLBACK_SETUP])(_usb_instances, NULL);
}
}
/* host pipe stall interrupt */
if (flags & USB_HOST_PINTFLAG_STALL) {
/* Clear busy status */
host_pipe_job_busy_status &= ~(1 << pipe_int);
/* clear the flag */
_usb_instances->hw->HOST.HostPipe[pipe_int].PINTFLAG.reg =
USB_HOST_PINTFLAG_STALL;
if(_usb_instances->host_pipe_enabled_callback_mask[pipe_int] &
(1 << USB_HOST_PIPE_CALLBACK_STALL)) {
pipe_callback_para.pipe_num = pipe_int;
(_usb_instances->host_pipe_callback[pipe_int]
[USB_HOST_PIPE_CALLBACK_STALL])(_usb_instances, &pipe_callback_para);
}
}
} else {
/* host interrupts */
/* get interrupt flags */
flags = _usb_instances->hw->HOST.INTFLAG.reg;
/* host SOF interrupt */
if (flags & USB_HOST_INTFLAG_HSOF) {
/* clear the flag */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_HSOF;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_SOF)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_SOF])(_usb_instances);
}
}
/* host reset interrupt */
if (flags & USB_HOST_INTFLAG_RST) {
/* Clear busy status */
host_pipe_job_busy_status = 0;
/* clear the flag */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RST;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_RESET)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_RESET])(_usb_instances);
}
}
/* host upstream resume interrupts */
if (flags & USB_HOST_INTFLAG_UPRSM) {
/* clear the flags */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_UPRSM;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_UPRSM)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_UPRSM])(_usb_instances);
}
}
/* host downstream resume interrupts */
if (flags & USB_HOST_INTFLAG_DNRSM) {
/* clear the flags */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DNRSM;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_DNRSM)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_DNRSM])(_usb_instances);
}
}
/* host wakeup interrupts */
if (flags & USB_HOST_INTFLAG_WAKEUP) {
/* clear the flags */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_WAKEUP;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_WAKEUP)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_WAKEUP])(_usb_instances);
}
}
/* host ram access interrupt */
if (flags & USB_HOST_INTFLAG_RAMACER) {
/* Clear busy status */
host_pipe_job_busy_status = 0;
/* clear the flag */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_RAMACER;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_RAMACER)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_RAMACER])(_usb_instances);
}
}
/* host connect interrupt */
if (flags & USB_HOST_INTFLAG_DCONN) {
/* Clear busy status */
host_pipe_job_busy_status = 0;
/* clear the flag */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DCONN;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_CONNECT)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_CONNECT])(_usb_instances);
}
}
/* host disconnect interrupt */
if (flags & USB_HOST_INTFLAG_DDISC) {
/* Clear busy status */
host_pipe_job_busy_status = 0;
/* clear the flag */
_usb_instances->hw->HOST.INTFLAG.reg = USB_HOST_INTFLAG_DDISC;
if(_usb_instances->host_enabled_callback_mask & (1 << USB_HOST_CALLBACK_DISCONNECT)) {
(_usb_instances->host_callback[USB_HOST_CALLBACK_DISCONNECT])(_usb_instances);
}
}
}
}
/**
* \brief Sets USB host pipe auto ZLP setting value
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] pipe_num Pipe to configure
* \param[in] value Auto ZLP setting value, \c true to enable
*
*/
void usb_host_pipe_set_auto_zlp(struct usb_module *module_inst, uint8_t pipe_num, bool value)
{
Assert(module_inst);
usb_descriptor_table.usb_pipe_table[pipe_num].HostDescBank[0].PCKSIZE.bit.AUTO_ZLP = value;
}
#endif
/**
* \brief Registers a USB device callback
*
* Registers a callback function which is implemented by the user.
*
* \note The callback must be enabled by \ref usb_device_enable_callback,
* in order for the interrupt handler to call it when the conditions for the
* callback type is met.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
* \param[in] callback_func Pointer to callback function
*
* \return Status of the registration operation.
* \retval STATUS_OK The callback was registered successfully.
*/
enum status_code usb_device_register_callback(struct usb_module *module_inst,
enum usb_device_callback callback_type,
usb_device_callback_t callback_func)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(callback_func);
/* Register callback function */
module_inst->device_callback[callback_type] = callback_func;
/* Set the bit corresponding to the callback_type */
module_inst->device_registered_callback_mask |= _usb_device_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Unregisters a USB device callback
*
* Unregisters an asynchronous callback implemented by the user. Removing it
* from the internal callback registration table.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the de-registration operation.
* \retval STATUS_OK The callback was unregistered successfully.
*/
enum status_code usb_device_unregister_callback(struct usb_module *module_inst,
enum usb_device_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
/* Unregister callback function */
module_inst->device_callback[callback_type] = NULL;
/* Clear the bit corresponding to the callback_type */
module_inst->device_registered_callback_mask &= ~_usb_device_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Enables USB device callback generation for a given type.
*
* Enables asynchronous callbacks for a given logical type.
* This must be called before USB device generate callback events.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback enable operation.
* \retval STATUS_OK The callback was enabled successfully.
*/
enum status_code usb_device_enable_callback(struct usb_module *module_inst,
enum usb_device_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
/* clear related flag */
module_inst->hw->DEVICE.INTFLAG.reg = _usb_device_irq_bits[callback_type];
/* Enable callback */
module_inst->device_enabled_callback_mask |= _usb_device_irq_bits[callback_type];
module_inst->hw->DEVICE.INTENSET.reg = _usb_device_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Disables USB device callback generation for a given type.
*
* Disables asynchronous callbacks for a given logical type.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback disable operation.
* \retval STATUS_OK The callback was disabled successfully.
*/
enum status_code usb_device_disable_callback(struct usb_module *module_inst,
enum usb_device_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
/* Disable callback */
module_inst->device_enabled_callback_mask &= ~_usb_device_irq_bits[callback_type];
module_inst->hw->DEVICE.INTENCLR.reg = _usb_device_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Registers a USB device endpoint callback
*
* Registers a callback function which is implemented by the user.
*
* \note The callback must be enabled by \ref usb_device_endpoint_enable_callback,
* in order for the interrupt handler to call it when the conditions for the
* callback type is met.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] ep_num Endpoint to configure
* \param[in] callback_type Callback type given by an enum
* \param[in] callback_func Pointer to callback function
*
* \return Status of the registration operation.
* \retval STATUS_OK The callback was registered successfully.
*/
enum status_code usb_device_endpoint_register_callback(
struct usb_module *module_inst, uint8_t ep_num,
enum usb_device_endpoint_callback callback_type,
usb_device_endpoint_callback_t callback_func)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(ep_num < USB_EPT_NUM);
Assert(callback_func);
/* Register callback function */
module_inst->device_endpoint_callback[ep_num][callback_type] = callback_func;
/* Set the bit corresponding to the callback_type */
module_inst->device_endpoint_registered_callback_mask[ep_num] |= _usb_endpoint_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Unregisters a USB device endpoint callback
*
* Unregisters an callback implemented by the user. Removing it
* from the internal callback registration table.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] ep_num Endpoint to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the de-registration operation.
* \retval STATUS_OK The callback was unregistered successfully.
*/
enum status_code usb_device_endpoint_unregister_callback(
struct usb_module *module_inst, uint8_t ep_num,
enum usb_device_endpoint_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(ep_num < USB_EPT_NUM);
/* Unregister callback function */
module_inst->device_endpoint_callback[ep_num][callback_type] = NULL;
/* Clear the bit corresponding to the callback_type */
module_inst->device_endpoint_registered_callback_mask[ep_num] &= ~_usb_endpoint_irq_bits[callback_type];
return STATUS_OK;
}
/**
* \brief Enables USB device endpoint callback generation for a given type.
*
* Enables callbacks for a given logical type.
* This must be called before USB device pipe generate callback events.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] ep Endpoint to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback enable operation.
* \retval STATUS_OK The callback was enabled successfully.
*/
enum status_code usb_device_endpoint_enable_callback(
struct usb_module *module_inst, uint8_t ep,
enum usb_device_endpoint_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
Assert(ep_num < USB_EPT_NUM);
/* Enable callback */
module_inst->device_endpoint_enabled_callback_mask[ep_num] |= _usb_endpoint_irq_bits[callback_type];
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRCPT) {
if (ep_num == 0) { // control endpoint
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0 | USB_DEVICE_EPINTENSET_TRCPT1;
} else if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRCPT0;
}
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL) {
if (ep_num == 0) { // control endpoint
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL0 | USB_DEVICE_EPINTENSET_TRFAIL1;
} else if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_TRFAIL0;
}
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_RXSTP) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_RXSTP;
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_STALL) {
if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_STALL1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENSET.reg = USB_DEVICE_EPINTENSET_STALL0;
}
}
return STATUS_OK;
}
/**
* \brief Disables USB device endpoint callback generation for a given type.
*
* Disables callbacks for a given logical type.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] ep Endpoint to configure
* \param[in] callback_type Callback type given by an enum
*
* \return Status of the callback disable operation.
* \retval STATUS_OK The callback was disabled successfully.
*/
enum status_code usb_device_endpoint_disable_callback(
struct usb_module *module_inst, uint8_t ep,
enum usb_device_endpoint_callback callback_type)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
Assert(ep_num < USB_EPT_NUM);
/* Enable callback */
module_inst->device_endpoint_enabled_callback_mask[ep_num] &= ~_usb_endpoint_irq_bits[callback_type];
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRCPT) {
if (ep_num == 0) { // control endpoint
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0 | USB_DEVICE_EPINTENCLR_TRCPT1;
} else if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRCPT0;
}
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL) {
if (ep_num == 0) { // control endpoint
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL0 | USB_DEVICE_EPINTENCLR_TRFAIL1;
} else if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_TRFAIL0;
}
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_RXSTP) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_RXSTP;
}
if (callback_type == USB_DEVICE_ENDPOINT_CALLBACK_STALL) {
if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_STALL1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTENCLR.reg = USB_DEVICE_EPINTENCLR_STALL0;
}
}
return STATUS_OK;
}
/**
* \brief Initializes an USB device endpoint configuration structure to defaults.
*
* Initializes a given USB device endpoint configuration structure to a
* set of known default values. This function should be called on all new
* instances of these configuration structures before being modified by the
* user application.
*
* The default configuration is as follows:
* \li endpoint address is 0
* \li endpoint size is 8 bytes
* \li auto_zlp is false
* \li endpoint type is control
*
* \param[out] ep_config Configuration structure to initialize to default values
*/
void usb_device_endpoint_get_config_defaults(struct usb_device_endpoint_config *ep_config)
{
/* Sanity check arguments */
Assert(ep_config);
/* Write default config to config struct */
ep_config->ep_address = 0;
ep_config->ep_size = USB_ENDPOINT_8_BYTE;
ep_config->auto_zlp = false;
ep_config->ep_type = USB_DEVICE_ENDPOINT_TYPE_CONTROL;
}
/**
* \brief Writes an USB device endpoint configuration to the hardware module.
*
* Writes out a given configuration of an USB device endpoint
* configuration to the hardware module. If the pipe is already configured,
* the new configuration will replace the existing one.
*
* \param[in] module_inst Pointer to USB software instance struct
* \param[in] ep_config Configuration settings for the endpoint
*
* \return Status of the device endpoint configuration operation
* \retval STATUS_OK The device endpoint was configured successfully
* \retval STATUS_ERR_DENIED The endpoint address is already configured
*/
enum status_code usb_device_endpoint_set_config(struct usb_module *module_inst,
struct usb_device_endpoint_config *ep_config)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(ep_config);
uint8_t ep_num = ep_config->ep_address & USB_EP_ADDR_MASK;
uint8_t ep_bank = (ep_config->ep_address & USB_EP_DIR_IN) ? 1 : 0;
switch (ep_config->ep_type) {
case USB_DEVICE_ENDPOINT_TYPE_DISABLE:
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(0) | USB_DEVICE_EPCFG_EPTYPE1(0);
return STATUS_OK;
case USB_DEVICE_ENDPOINT_TYPE_CONTROL:
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0 && \
(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg = USB_DEVICE_EPCFG_EPTYPE0(1) | USB_DEVICE_EPCFG_EPTYPE1(1);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
} else {
return STATUS_ERR_DENIED;
}
if (true == ep_config->auto_zlp) {
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP;
} else {
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP;
}
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.SIZE = ep_config->ep_size;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.SIZE = ep_config->ep_size;
return STATUS_OK;
case USB_DEVICE_ENDPOINT_TYPE_ISOCHRONOUS:
if (ep_bank) {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(2);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
} else {
return STATUS_ERR_DENIED;
}
} else {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(2);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
} else {
return STATUS_ERR_DENIED;
}
}
break;
case USB_DEVICE_ENDPOINT_TYPE_BULK:
if (ep_bank) {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(3);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
} else {
return STATUS_ERR_DENIED;
}
} else {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(3);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
} else {
return STATUS_ERR_DENIED;
}
}
break;
case USB_DEVICE_ENDPOINT_TYPE_INTERRUPT:
if (ep_bank) {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE1_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE1(4);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
} else {
return STATUS_ERR_DENIED;
}
} else {
if ((module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg & USB_DEVICE_EPCFG_EPTYPE0_Msk) == 0){
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.reg |= USB_DEVICE_EPCFG_EPTYPE0(4);
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
} else {
return STATUS_ERR_DENIED;
}
}
break;
default:
break;
}
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.bit.SIZE = ep_config->ep_size;
if (true == ep_config->auto_zlp) {
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.reg |= USB_DEVICE_PCKSIZE_AUTO_ZLP;
} else {
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[ep_bank].PCKSIZE.reg &= ~USB_DEVICE_PCKSIZE_AUTO_ZLP;
}
return STATUS_OK;
}
/**
* \brief Check if current endpoint is configured
*
* \param module_inst Pointer to USB software instance struct
* \param ep Endpoint address (direction & number)
*
* \return \c true if endpoint is configured and ready to use
*/
bool usb_device_endpoint_is_configured(struct usb_module *module_inst, uint8_t ep)
{
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
uint8_t flag;
if (ep & USB_EP_DIR_IN) {
flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE1);
} else {
flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE0);
}
return ((enum usb_device_endpoint_type)(flag) != USB_DEVICE_ENDPOINT_TYPE_DISABLE);
}
/**
* \brief Abort ongoing job on the endpoint
*
* \param module_inst Pointer to USB software instance struct
* \param ep Endpoint address
*/
void usb_device_endpoint_abort_job(struct usb_module *module_inst, uint8_t ep)
{
uint8_t ep_num;
ep_num = ep & USB_EP_ADDR_MASK;
// Stop transfer
if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK1RDY;
// Eventually ack a transfer occur during abort
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK0RDY;
// Eventually ack a transfer occur during abort
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
}
}
/**
* \brief Check if endpoint is halted
*
* \param module_inst Pointer to USB software instance struct
* \param ep Endpoint address
*
* \return \c true if the endpoint is halted
*/
bool usb_device_endpoint_is_halted(struct usb_module *module_inst, uint8_t ep)
{
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
if (ep & USB_EP_DIR_IN) {
return (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ1);
} else {
return (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ0);
}
}
/**
* \brief Halt the endpoint (send STALL)
*
* \param module_inst Pointer to USB software instance struct
* \param ep Endpoint address
*/
void usb_device_endpoint_set_halt(struct usb_module *module_inst, uint8_t ep)
{
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
// Stall endpoint
if (ep & USB_EP_DIR_IN) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ1;
} else {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_STALLRQ0;
}
}
/**
* \brief Clear endpoint halt state
*
* \param module_inst Pointer to USB software instance struct
* \param ep Endpoint address
*/
void usb_device_endpoint_clear_halt(struct usb_module *module_inst, uint8_t ep)
{
uint8_t ep_num = ep & USB_EP_ADDR_MASK;
if (ep & USB_EP_DIR_IN) {
if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ1) {
// Remove stall request
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ1;
if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL1) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL1;
// The Stall has occurred, then reset data toggle
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_DTGLIN;
}
}
} else {
if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUS.reg & USB_DEVICE_EPSTATUSSET_STALLRQ0) {
// Remove stall request
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_STALLRQ0;
if (module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL0) {
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL0;
// The Stall has occurred, then reset data toggle
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSSET_DTGLOUT;
}
}
}
}
/**
* \brief Start write buffer job on a endpoint
*
* \param module_inst Pointer to USB module instance
* \param ep_num Endpoint number
* \param pbuf Pointer to buffer
* \param buf_size Size of buffer
*
* \return Status of procedure
* \retval STATUS_OK Job started successfully
* \retval STATUS_ERR_DENIED Endpoint is not ready
*/
enum status_code usb_device_endpoint_write_buffer_job(struct usb_module *module_inst,uint8_t ep_num,
uint8_t* pbuf, uint32_t buf_size)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(ep_num < USB_EPT_NUM);
uint8_t flag;
flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE1);
if ((enum usb_device_endpoint_type)(flag) == USB_DEVICE_ENDPOINT_TYPE_DISABLE) {
return STATUS_ERR_DENIED;
};
/* get endpoint configuration from setting register */
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].ADDR.reg = (uint32_t)pbuf;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.MULTI_PACKET_SIZE = 0;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT = buf_size;
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSSET.reg = USB_DEVICE_EPSTATUSSET_BK1RDY;
return STATUS_OK;
}
/**
* \brief Start read buffer job on a endpoint
*
* \param module_inst Pointer to USB module instance
* \param ep_num Endpoint number
* \param pbuf Pointer to buffer
* \param buf_size Size of buffer
*
* \return Status of procedure
* \retval STATUS_OK Job started successfully
* \retval STATUS_ERR_DENIED Endpoint is not ready
*/
enum status_code usb_device_endpoint_read_buffer_job(struct usb_module *module_inst,uint8_t ep_num,
uint8_t* pbuf, uint32_t buf_size)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
Assert(ep_num < USB_EPT_NUM);
uint8_t flag;
flag = (uint8_t)(module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPCFG.bit.EPTYPE0);
if ((enum usb_device_endpoint_type)(flag) == USB_DEVICE_ENDPOINT_TYPE_DISABLE) {
return STATUS_ERR_DENIED;
};
/* get endpoint configuration from setting register */
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].ADDR.reg = (uint32_t)pbuf;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = buf_size;
usb_descriptor_table.usb_endpoint_table[ep_num].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
module_inst->hw->DEVICE.DeviceEndpoint[ep_num].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
return STATUS_OK;
}
/**
* \brief Start setup packet read job on a endpoint
*
* \param module_inst Pointer to USB device module instance
* \param pbuf Pointer to buffer
*
* \return Status of procedure
* \retval STATUS_OK Job started successfully
* \retval STATUS_ERR_DENIED Endpoint is not ready
*/
enum status_code usb_device_endpoint_setup_buffer_job(struct usb_module *module_inst,
uint8_t* pbuf)
{
/* Sanity check arguments */
Assert(module_inst);
Assert(module_inst->hw);
/* get endpoint configuration from setting register */
usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].ADDR.reg = (uint32_t)pbuf;
usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE = 8;
usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT = 0;
module_inst->hw->DEVICE.DeviceEndpoint[0].EPSTATUSCLR.reg = USB_DEVICE_EPSTATUSCLR_BK0RDY;
return STATUS_OK;
}
static void _usb_device_interrupt_handler(void)
{
uint16_t ep_inst;
uint16_t flags, flags_run;
ep_inst = _usb_instances->hw->DEVICE.EPINTSMRY.reg;
/* device interrupt */
if (0 == ep_inst) {
int i;
/* get interrupt flags */
flags = _usb_instances->hw->DEVICE.INTFLAG.reg;
flags_run = flags &
_usb_instances->device_enabled_callback_mask &
_usb_instances->device_registered_callback_mask;
for (i = 0; i < USB_DEVICE_CALLBACK_N; i ++) {
if (flags & _usb_device_irq_bits[i]) {
_usb_instances->hw->DEVICE.INTFLAG.reg =
_usb_device_irq_bits[i];
}
if (flags_run & _usb_device_irq_bits[i]) {
if (i == USB_DEVICE_CALLBACK_LPMSUSP) {
device_callback_lpm_wakeup_enable =
usb_descriptor_table.usb_endpoint_table[0].DeviceDescBank[0].EXTREG.bit.VARIABLE
& USB_LPM_ATTRIBUT_REMOTEWAKE_MASK;
}
(_usb_instances->device_callback[i])(_usb_instances, &device_callback_lpm_wakeup_enable);
}
}
} else {
/* endpoint interrupt */
for (uint8_t i = 0; i < USB_EPT_NUM; i++) {
if (ep_inst & (1 << i)) {
flags = _usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg;
flags_run = flags &
_usb_instances->device_endpoint_enabled_callback_mask[i] &
_usb_instances->device_endpoint_registered_callback_mask[i];
// endpoint transfer stall interrupt
if (flags & USB_DEVICE_EPINTFLAG_STALL_Msk) {
if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL1) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL1;
ep_callback_para.endpoint_address = USB_EP_DIR_IN | i;
} else if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_STALL0) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_STALL0;
ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i;
}
if (flags_run & USB_DEVICE_EPINTFLAG_STALL_Msk) {
(_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_STALL])(_usb_instances,&ep_callback_para);
}
return;
}
// endpoint received setup interrupt
if (flags & USB_DEVICE_EPINTFLAG_RXSTP) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_RXSTP;
if(_usb_instances->device_endpoint_enabled_callback_mask[i] & _usb_endpoint_irq_bits[USB_DEVICE_ENDPOINT_CALLBACK_RXSTP]) {
ep_callback_para.received_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT);
(_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_RXSTP])(_usb_instances,&ep_callback_para);
}
return;
}
// endpoint transfer complete interrupt
if (flags & USB_DEVICE_EPINTFLAG_TRCPT_Msk) {
if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT1) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT1;
ep_callback_para.endpoint_address = USB_EP_DIR_IN | i;
ep_callback_para.sent_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].PCKSIZE.bit.BYTE_COUNT);
} else if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT0) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRCPT0;
ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i;
ep_callback_para.received_bytes = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.BYTE_COUNT);
ep_callback_para.out_buffer_size = (uint16_t)(usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].PCKSIZE.bit.MULTI_PACKET_SIZE);
}
if(flags_run & USB_DEVICE_EPINTFLAG_TRCPT_Msk) {
(_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_TRCPT])(_usb_instances,&ep_callback_para);
}
return;
}
// endpoint transfer fail interrupt
if (flags & USB_DEVICE_EPINTFLAG_TRFAIL_Msk) {
if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRFAIL1) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL1;
if (usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].STATUS_BK.reg & USB_DEVICE_STATUS_BK_ERRORFLOW) {
usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[1].STATUS_BK.reg &= ~USB_DEVICE_STATUS_BK_ERRORFLOW;
}
ep_callback_para.endpoint_address = USB_EP_DIR_IN | i;
if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT1) {
return;
}
} else if(_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRFAIL0) {
_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg = USB_DEVICE_EPINTFLAG_TRFAIL0;
if (usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].STATUS_BK.reg & USB_DEVICE_STATUS_BK_ERRORFLOW) {
usb_descriptor_table.usb_endpoint_table[i].DeviceDescBank[0].STATUS_BK.reg &= ~USB_DEVICE_STATUS_BK_ERRORFLOW;
}
ep_callback_para.endpoint_address = USB_EP_DIR_OUT | i;
if (_usb_instances->hw->DEVICE.DeviceEndpoint[i].EPINTFLAG.reg & USB_DEVICE_EPINTFLAG_TRCPT0) {
return;
}
}
if(flags_run & USB_DEVICE_EPINTFLAG_TRFAIL_Msk) {
(_usb_instances->device_endpoint_callback[i][USB_DEVICE_ENDPOINT_CALLBACK_TRFAIL])(_usb_instances,&ep_callback_para);
}
return;
}
}
}
}
}
/**
* \brief Enable the USB module peripheral
*
* \param module_inst pointer to USB module instance
*/
void usb_enable(struct usb_module *module_inst)
{
Assert(module_inst);
Assert(module_inst->hw);
module_inst->hw->DEVICE.CTRLA.reg |= USB_CTRLA_ENABLE;
while (module_inst->hw->DEVICE.SYNCBUSY.reg == USB_SYNCBUSY_ENABLE);
}
/**
* \brief Disable the USB module peripheral
*
* \param module_inst pointer to USB module instance
*/
void usb_disable(struct usb_module *module_inst)
{
Assert(module_inst);
Assert(module_inst->hw);
module_inst->hw->DEVICE.INTENCLR.reg = USB_DEVICE_INTENCLR_MASK;
module_inst->hw->DEVICE.INTFLAG.reg = USB_DEVICE_INTFLAG_MASK;
module_inst->hw->DEVICE.CTRLA.reg &= ~USB_CTRLA_ENABLE;
while (module_inst->hw->DEVICE.SYNCBUSY.reg == USB_SYNCBUSY_ENABLE);
}
/**
* \brief Interrupt handler for the USB module.
*/
void USB_Handler(void)
{
if (_usb_instances->hw->DEVICE.CTRLA.bit.MODE) {
#if !SAML22
/*host mode ISR */
_usb_host_interrupt_handler();
#endif
} else {
/*device mode ISR */
_usb_device_interrupt_handler();
}
}
/**
* \brief Get the default USB module settings
*
* \param[out] module_config Configuration structure to initialize to default values
*/
void usb_get_config_defaults(struct usb_config *module_config)
{
Assert(module_config);
/* Sanity check arguments */
Assert(module_config);
/* Write default configuration to config struct */
module_config->select_host_mode = 0;
module_config->run_in_standby = 1;
module_config->source_generator = GCLK_GENERATOR_3;
module_config->speed_mode = USB_SPEED_FULL;
}
#define NVM_USB_PAD_TRANSN_POS 45
#define NVM_USB_PAD_TRANSN_SIZE 5
#define NVM_USB_PAD_TRANSP_POS 50
#define NVM_USB_PAD_TRANSP_SIZE 5
#define NVM_USB_PAD_TRIM_POS 55
#define NVM_USB_PAD_TRIM_SIZE 3
/**
* \brief Initializes USB module instance
*
* Enables the clock and initializes the USB module, based on the given
* configuration values.
*
* \param[in,out] module_inst Pointer to the software module instance struct
* \param[in] hw Pointer to the USB hardware module
* \param[in] module_config Pointer to the USB configuration options struct
*
* \return Status of the initialization procedure.
*
* \retval STATUS_OK The module was initialized successfully
*/
enum status_code usb_init(struct usb_module *module_inst, Usb *const hw,
struct usb_config *module_config)
{
/* Sanity check arguments */
Assert(hw);
Assert(module_inst);
Assert(module_config);
uint32_t i,j;
uint32_t pad_transn, pad_transp, pad_trim;
struct system_pinmux_config pin_config;
struct system_gclk_chan_config gclk_chan_config;
#if !SAML22
host_pipe_job_busy_status = 0;
#endif
_usb_instances = module_inst;
/* Associate the software module instance with the hardware module */
module_inst->hw = hw;
/* Turn on the digital interface clock */
system_apb_clock_set_mask(SYSTEM_CLOCK_APB_APBB, MCLK_APBBMASK_USB);
/* Set up the USB DP/DN pins */
system_pinmux_get_config_defaults(&pin_config);
pin_config.mux_position = MUX_PA24G_USB_DM;
system_pinmux_pin_set_config(PIN_PA24G_USB_DM, &pin_config);
pin_config.mux_position = MUX_PA25G_USB_DP;
system_pinmux_pin_set_config(PIN_PA25G_USB_DP, &pin_config);
/* Setup clock for module */
system_gclk_chan_get_config_defaults(&gclk_chan_config);
gclk_chan_config.source_generator = module_config->source_generator;
system_gclk_chan_set_config(USB_GCLK_ID, &gclk_chan_config);
system_gclk_chan_enable(USB_GCLK_ID);
/* Reset */
hw->DEVICE.CTRLA.bit.SWRST = 1;
while (hw->DEVICE.SYNCBUSY.bit.SWRST) {
/* Sync wait */
}
/* Load Pad Calibration */
pad_transn =( *((uint32_t *)(NVMCTRL_OTP4)
+ (NVM_USB_PAD_TRANSN_POS / 32))
>> (NVM_USB_PAD_TRANSN_POS % 32))
& ((1 << NVM_USB_PAD_TRANSN_SIZE) - 1);
if (pad_transn == 0x1F) {
pad_transn = 5;
}
hw->DEVICE.PADCAL.bit.TRANSN = pad_transn;
pad_transp =( *((uint32_t *)(NVMCTRL_OTP4)
+ (NVM_USB_PAD_TRANSP_POS / 32))
>> (NVM_USB_PAD_TRANSP_POS % 32))
& ((1 << NVM_USB_PAD_TRANSP_SIZE) - 1);
if (pad_transp == 0x1F) {
pad_transp = 29;
}
hw->DEVICE.PADCAL.bit.TRANSP = pad_transp;
pad_trim =( *((uint32_t *)(NVMCTRL_OTP4)
+ (NVM_USB_PAD_TRIM_POS / 32))
>> (NVM_USB_PAD_TRIM_POS % 32))
& ((1 << NVM_USB_PAD_TRIM_SIZE) - 1);
if (pad_trim == 0x7) {
pad_trim = 3;
}
hw->DEVICE.PADCAL.bit.TRIM = pad_trim;
/* Set the configuration */
hw->DEVICE.CTRLA.bit.MODE = module_config->select_host_mode;
hw->DEVICE.CTRLA.bit.RUNSTDBY = module_config->run_in_standby;
hw->DEVICE.DESCADD.reg = (uint32_t)(&usb_descriptor_table.usb_endpoint_table[0]);
if (USB_SPEED_FULL == module_config->speed_mode) {
module_inst->hw->DEVICE.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_FS_Val;
} else if(USB_SPEED_LOW == module_config->speed_mode) {
module_inst->hw->DEVICE.CTRLB.bit.SPDCONF = USB_DEVICE_CTRLB_SPDCONF_LS_Val;
}
memset((uint8_t *)(&usb_descriptor_table.usb_endpoint_table[0]), 0,
sizeof(usb_descriptor_table.usb_endpoint_table));
#if !SAML22
/* callback related init */
for (i = 0; i < USB_HOST_CALLBACK_N; i++) {
module_inst->host_callback[i] = NULL;
};
for (i = 0; i < USB_PIPE_NUM; i++) {
for (j = 0; j < USB_HOST_PIPE_CALLBACK_N; j++) {
module_inst->host_pipe_callback[i][j] = NULL;
}
};
module_inst->host_registered_callback_mask = 0;
module_inst->host_enabled_callback_mask = 0;
for (i = 0; i < USB_PIPE_NUM; i++) {
module_inst->host_pipe_registered_callback_mask[i] = 0;
module_inst->host_pipe_enabled_callback_mask[i] = 0;
}
#endif
/* device callback related */
for (i = 0; i < USB_DEVICE_CALLBACK_N; i++) {
module_inst->device_callback[i] = NULL;
}
for (i = 0; i < USB_EPT_NUM; i++) {
for(j = 0; j < USB_DEVICE_EP_CALLBACK_N; j++) {
module_inst->device_endpoint_callback[i][j] = NULL;
}
}
module_inst->device_registered_callback_mask = 0;
module_inst->device_enabled_callback_mask = 0;
for (j = 0; j < USB_EPT_NUM; j++) {
module_inst->device_endpoint_registered_callback_mask[j] = 0;
module_inst->device_endpoint_enabled_callback_mask[j] = 0;
}
/* Enable interrupts for this USB module */
system_interrupt_enable(SYSTEM_INTERRUPT_MODULE_USB);
return STATUS_OK;
}