summaryrefslogtreecommitdiff
path: root/bsp/at32/libraries/rt_drivers/drv_usbfsh.c
diff options
context:
space:
mode:
Diffstat (limited to 'bsp/at32/libraries/rt_drivers/drv_usbfsh.c')
-rw-r--r--bsp/at32/libraries/rt_drivers/drv_usbfsh.c406
1 files changed, 406 insertions, 0 deletions
diff --git a/bsp/at32/libraries/rt_drivers/drv_usbfsh.c b/bsp/at32/libraries/rt_drivers/drv_usbfsh.c
new file mode 100644
index 0000000000..8a959cff81
--- /dev/null
+++ b/bsp/at32/libraries/rt_drivers/drv_usbfsh.c
@@ -0,0 +1,406 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2023-02-28 leo first version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+#include "usbh_int.h"
+#include "drv_common.h"
+#include "drv_usbfs.h"
+#include "drv_config.h"
+
+#if defined(BSP_USING_HOST_USBFS1) || defined(BSP_USING_HOST_USBFS2)
+
+//#define DRV_DEBUG
+#define LOG_TAG "drv.usb"
+#include <drv_log.h>
+
+static struct rt_completion urb_completion;
+static volatile rt_bool_t connect_status = RT_FALSE;
+static struct at32_usbfs *p_usbfs_instance = NULL;
+
+enum
+{
+#ifdef BSP_USING_HOST_USBFS1
+ USBFS1_INDEX,
+#endif
+#ifdef BSP_USING_HOST_USBFS2
+ USBFS2_INDEX,
+#endif
+};
+
+static struct at32_usbfs usbfsh_config[] = {
+#ifdef BSP_USING_HOST_USBFS1
+ USBFS1_CONFIG,
+#endif
+#ifdef BSP_USING_HOST_USBFS2
+ USBFS2_CONFIG,
+#endif
+};
+
+#ifdef BSP_USING_HOST_USBFS1
+void OTGFS1_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ usbh_irq_handler(p_usbfs_instance->p_otg_core);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+#ifdef BSP_USING_HOST_USBFS2
+void OTGFS2_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ usbh_irq_handler(p_usbfs_instance->p_otg_core);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+void usbh_connect_callback(usbh_core_type *uhost)
+{
+ uhcd_t hcd = (uhcd_t)uhost->pdata;
+ if (!connect_status)
+ {
+ connect_status = RT_TRUE;
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("usb connected\n"));
+ rt_usbh_root_hub_connect_handler(hcd, 1, RT_FALSE);
+ }
+}
+
+void usbh_disconnect_callback(usbh_core_type *uhost)
+{
+ uhcd_t hcd = (uhcd_t)uhost->pdata;
+ if (connect_status)
+ {
+ connect_status = RT_FALSE;
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("usb disconnnect\n"));
+ rt_usbh_root_hub_disconnect_handler(hcd, 1);
+ }
+}
+
+void usbd_notify_urbchange_callback(usbh_core_type *uhost, uint8_t chnum, urb_sts_type sts)
+{
+ rt_completion_done(&urb_completion);
+}
+
+static rt_err_t drv_reset_port(rt_uint8_t port)
+{
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("reset port\n"));
+ usbh_reset_port(&p_usbfs_instance->p_otg_core->host);
+ return RT_EOK;
+}
+
+static int drv_pipe_xfer(upipe_t pipe, rt_uint8_t token, void *buffer, int nbytes, int timeouts)
+{
+ int timeout = timeouts;
+
+ while(1)
+ {
+ if(!connect_status)
+ {
+ return -1;
+ }
+ rt_completion_init(&urb_completion);
+
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].dir = (pipe->ep.bEndpointAddress & 0x80) >> 7;
+
+ if(token == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_SETUP;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+
+ /* endpoint type */
+ switch(pipe->ep.bmAttributes)
+ {
+ /* endpoint is control type */
+ case EPT_CONTROL_TYPE:
+ if((token == 1U) && (((pipe->ep.bEndpointAddress & 0x80) >> 7) == 0U))
+ {
+ if(nbytes == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_out = 1U;
+ }
+ if((&p_usbfs_instance->p_otg_core->host)->hch[pipe->pipe_index].toggle_out == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+ }
+ break;
+ /* endpoint is bulk type */
+ case EPT_BULK_TYPE:
+ if(((pipe->ep.bEndpointAddress & 0x80) >> 7) == 0U)
+ {
+ if( p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_out == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+ }
+ else
+ {
+ if( p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_in == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+ }
+ break;
+ /* endpoint is int type */
+ case EPT_INT_TYPE:
+ if(((pipe->ep.bEndpointAddress & 0x80) >> 7) == 0U)
+ {
+ if( p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_out == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+ }
+ else
+ {
+ if( p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_in == 0U)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA1;
+ }
+ }
+ break;
+ /* endpoint is isoc type */
+ case EPT_ISO_TYPE:
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].data_pid = HCH_PID_DATA0;
+ break;
+
+ default:
+ break;
+ }
+
+ /* set transfer buffer */
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].trans_buf = buffer;
+ /* set transfer len*/
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].trans_len = nbytes;
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].urb_sts = URB_IDLE;
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].ch_num = pipe->pipe_index;
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].trans_count = 0;
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].state = HCH_IDLE;
+
+ /* data in/out for host */
+ usbh_in_out_request((&p_usbfs_instance->p_otg_core->host), pipe->pipe_index);
+
+ rt_completion_wait(&urb_completion, timeout);
+ rt_thread_mdelay(1);
+
+ if(usbh_get_status((&p_usbfs_instance->p_otg_core->host), pipe->pipe_index) == HCH_NAK)
+ {
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("nak\n"));
+ if (pipe->ep.bmAttributes == USB_EP_ATTR_INT)
+ {
+ rt_thread_delay((pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) > 0 ? (pipe->ep.bInterval * RT_TICK_PER_SECOND / 1000) : 1);
+ }
+ usb_hch_halt((&p_usbfs_instance->p_otg_core->host)->usb_reg, pipe->pipe_index);
+
+ usbh_hc_open(&p_usbfs_instance->p_otg_core->host,
+ pipe->pipe_index,
+ pipe->ep.bEndpointAddress,
+ pipe->inst->address,
+ pipe->ep.bmAttributes,
+ pipe->ep.wMaxPacketSize,
+ USB_PRTSPD_FULL_SPEED);
+ continue;
+ }
+ else if (usbh_get_status(&p_usbfs_instance->p_otg_core->host, pipe->pipe_index) == HCH_STALL)
+ {
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("stall\n"));
+ pipe->status = UPIPE_STATUS_STALL;
+ if (pipe->callback != RT_NULL)
+ {
+ pipe->callback(pipe);
+ }
+ return -1;
+ }
+ else if (usbh_get_status(&p_usbfs_instance->p_otg_core->host, pipe->pipe_index) == URB_ERROR)
+ {
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("error\n"));
+ pipe->status = UPIPE_STATUS_ERROR;
+ if (pipe->callback != RT_NULL)
+ {
+ pipe->callback(pipe);
+ }
+ return -1;
+ }
+ else if(URB_DONE == usbh_get_urb_status(&p_usbfs_instance->p_otg_core->host, pipe->pipe_index))
+ {
+ RT_DEBUG_LOG(RT_DEBUG_USB, ("ok\n"));
+ pipe->status = UPIPE_STATUS_OK;
+ if (pipe->callback != RT_NULL)
+ {
+ pipe->callback(pipe);
+ }
+ size_t size = (&p_usbfs_instance->p_otg_core->host)->hch[pipe->pipe_index].trans_count;
+ if (pipe->ep.bEndpointAddress & 0x80)
+ {
+ return size;
+ }
+ else if (pipe->ep.bEndpointAddress & 0x00)
+ {
+ return size;
+ }
+ return nbytes;
+ }
+ continue;
+ }
+}
+
+static rt_uint16_t pipe_index = 0;
+static rt_uint8_t drv_get_free_pipe_index(void)
+{
+ rt_uint8_t idx;
+ for (idx = 1; idx < 16; idx++)
+ {
+ if (!(pipe_index & (0x01 << idx)))
+ {
+ pipe_index |= (0x01 << idx);
+ return idx;
+ }
+ }
+ return 0xff;
+}
+
+static void drv_free_pipe_index(rt_uint8_t index)
+{
+ pipe_index &= ~(0x01 << index);
+}
+
+static rt_err_t drv_open_pipe(upipe_t pipe)
+{
+ pipe->pipe_index = drv_get_free_pipe_index();
+ usbh_hc_open(&p_usbfs_instance->p_otg_core->host,
+ pipe->pipe_index,
+ pipe->ep.bEndpointAddress,
+ pipe->inst->address,
+ pipe->ep.bmAttributes,
+ pipe->ep.wMaxPacketSize,
+ USB_PRTSPD_FULL_SPEED);
+ /* set data0 pid token*/
+ if (p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].dir)
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_in = 0;
+ }
+ else
+ {
+ p_usbfs_instance->p_otg_core->host.hch[pipe->pipe_index].toggle_out = 0;
+ }
+ return RT_EOK;
+}
+
+static rt_err_t drv_close_pipe(upipe_t pipe)
+{
+ usb_hch_halt((&p_usbfs_instance->p_otg_core->host)->usb_reg, pipe->pipe_index);
+ drv_free_pipe_index(pipe->pipe_index);
+ return RT_EOK;
+}
+
+static struct uhcd_ops _uhcd_ops =
+{
+ drv_reset_port,
+ drv_pipe_xfer,
+ drv_open_pipe,
+ drv_close_pipe,
+};
+
+static rt_err_t at32_hcd_init(rt_device_t device)
+{
+ /* usb gpio config */
+ at32_msp_usb_init(device);
+
+ /* enable otgfs irq */
+ nvic_irq_enable(p_usbfs_instance->irqn, 2, 0);
+
+ /* init usb */
+ usbh_init(p_usbfs_instance->p_otg_core,
+ USB_FULL_SPEED_CORE_ID,
+ p_usbfs_instance->id);
+ return RT_EOK;
+}
+
+int at32_usbh_register(void)
+{
+ rt_size_t obj_num;
+ rt_err_t result = 0;
+ int index;
+
+ obj_num = sizeof(usbfsh_config) / sizeof(struct at32_usbfs);
+
+ for (index = 0; index < obj_num; index++) {
+ uhcd_t uhcd = (uhcd_t)rt_malloc(sizeof(struct uhcd));
+ if (uhcd == RT_NULL)
+ {
+ rt_kprintf("uhcd malloc failed\r\n");
+ return -RT_ERROR;
+ }
+ rt_memset((void *)uhcd, 0, sizeof(struct uhcd));
+
+ otg_core_type *p_otg_core = (otg_core_type *)rt_malloc(sizeof(otg_core_type));
+ if (p_otg_core == RT_NULL)
+ {
+ rt_kprintf("otg_core malloc failed\r\n");
+ return -RT_ERROR;
+ }
+ rt_memset((void *)p_otg_core, 0, sizeof(otg_core_type));
+
+ uhcd->parent.type = RT_Device_Class_USBHost;
+ uhcd->parent.init = at32_hcd_init;
+ uhcd->parent.user_data = &(p_otg_core->host);
+
+ uhcd->ops = &_uhcd_ops;
+ uhcd->num_ports = 1;
+ p_otg_core->host.pdata = uhcd;
+ usbfsh_config[index].p_otg_core = p_otg_core;
+
+ result = rt_device_register(&uhcd->parent, usbfsh_config[index].name, RT_DEVICE_FLAG_DEACTIVATE);
+ RT_ASSERT(result == RT_EOK);
+
+ p_usbfs_instance = &usbfsh_config[index];
+
+ result = rt_usb_host_init(usbfsh_config[index].name);
+ RT_ASSERT(result == RT_EOK);
+ }
+
+ return result;
+}
+
+INIT_DEVICE_EXPORT(at32_usbh_register);
+
+#endif