/* * Copyright : (C) 2023 Phytium Information Technology, Inc. * All Rights Reserved. * * This program is OPEN SOURCE software: you can redistribute it and/or modify it * under the terms of the Phytium Public License as published by the Phytium Technology Co.,Ltd, * either version 1.0 of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the Phytium Public License for more details. * * * FilePath: usb_dc_pusb2.c * Date: 2021-08-25 14:53:42 * LastEditTime: 2021-08-26 09:01:26 * Description:  This file is for implementation of PUSB2 port to cherryusb for host mode * * Modify History: * Ver   Who        Date         Changes * ----- ------     --------    -------------------------------------- * 1.0 zhugengyu 2023/7/19 first commit */ #include "usbd_core.h" #include "fpusb2.h" /* Endpoint state */ struct pusb2_dc_ep_state { uint16_t ep_mps; /* Endpoint max packet size */ uint8_t ep_type; /* Endpoint type */ uint8_t ep_stalled; /* Endpoint stall flag */ const struct usb_endpoint_descriptor *desc; FPUsb2DcEp *priv_ep; }; /* Data IN/OUT request */ struct pusb2_dc_request { struct pusb2_dc_ep_state *ep; FPUsb2DcReq *priv_req; int status; }; /* Driver state */ struct pusb2_udc { FPUsb2 pusb2; int speed; FPUsb2Config config; volatile uint8_t dev_addr; int ep0_init_finish; struct pusb2_dc_ep_state in_ep[FPUSB2_DC_EP_NUM]; /*!< IN endpoint parameters*/ struct pusb2_dc_ep_state out_ep[FPUSB2_DC_EP_NUM]; /*!< OUT endpoint parameters */ } g_pusb2_udc; __WEAK void usb_dc_low_level_init(void) { } __WEAK void usb_dc_low_level_deinit(void) { } static void pusb2_dc_init_ep_state(struct pusb2_dc_ep_state *ep_state, FPUsb2DcEp *priv_ep) { /* reset ep state and attach priv ep */ ep_state->ep_mps = 0U; ep_state->ep_type = 0U; ep_state->ep_stalled = 0U; ep_state->desc = NULL; ep_state->priv_ep = priv_ep; } static void pusb2_dc_connect_handler(FPUsb2DcController *instance) { FPUsb2DcDev *dc_dev = NULL; extern void FPUsb2DcNoReset(FPUsb2DcController *instance); FPUsb2DcGetDevInstance(&g_pusb2_udc.pusb2.device_ctrl, &dc_dev); USB_ASSERT(dc_dev); USB_LOG_DBG("%s \n", __func__); usbd_event_reset_handler(); /* update speed and max packet size when connect */ g_pusb2_udc.speed = dc_dev->speed; if (g_pusb2_udc.speed > USB_SPEED_HIGH) { g_pusb2_udc.in_ep[0].ep_mps = 9; g_pusb2_udc.out_ep[0].ep_mps = 9; } else { g_pusb2_udc.in_ep[0].ep_mps = dc_dev->ep0->max_packet; g_pusb2_udc.out_ep[0].ep_mps = dc_dev->ep0->max_packet; } FPUsb2DcNoReset(instance); } static void pusb2_dc_disconnect_handler(FPUsb2DcController *instance) { USB_LOG_DBG("%s \n", __func__); } static void pusb2_dc_resume_handler(FPUsb2DcController *instance) { USB_LOG_DBG("%s \n", __func__); } static uint32_t pusb2_dc_receive_steup_handler(FPUsb2DcController *instance, FUsbSetup *setup) { USB_LOG_DBG("%s 0x%x:0x%x:0x%x:0x%x:0x%x\n", __func__, setup->bmRequestType, setup->bRequest, setup->wIndex, setup->wLength, setup->wValue); usbd_event_ep0_setup_complete_handler((u8 *)setup); return 0; } static void pusb2_dc_suspend_handler(FPUsb2DcController *instance) { USB_LOG_DBG("%s \n", __func__); } static void* pusb2_dc_allocate_request_handler(FPUsb2DcController *instance, uint32_t size) { FPUsb2DcReq * cusbd_req = usb_malloc(size); if (!cusbd_req) { return NULL; } memset(cusbd_req, 0, sizeof(*cusbd_req)); return cusbd_req; } static void pusb2_dc_free_request_handler(FPUsb2DcController *instance, void *usb_request) { if (!usb_request) return; usb_free(usb_request); } static void pusb2_dc_pre_start_handler(FPUsb2DcController *instance) { FPUsb2DcEp *priv_epx = NULL; FPUsb2DcDev *dc_dev = NULL; FDListHead *list; int ep_num; FPUsb2DcGetDevInstance(&g_pusb2_udc.pusb2.device_ctrl, &dc_dev); USB_ASSERT(dc_dev); g_pusb2_udc.speed = dc_dev->max_speed; pusb2_dc_init_ep_state(&g_pusb2_udc.in_ep[0], dc_dev->ep0); pusb2_dc_init_ep_state(&g_pusb2_udc.out_ep[0], dc_dev->ep0); for(list = dc_dev->ep_list.next; list != &dc_dev->ep_list; list = list->next) { priv_epx = (FPUsb2DcEp*)list; ep_num = USB_EP_GET_IDX(priv_epx->address); if (USB_EP_DIR_IS_IN(priv_epx->address)) { pusb2_dc_init_ep_state(&g_pusb2_udc.in_ep[ep_num], priv_epx); } else { pusb2_dc_init_ep_state(&g_pusb2_udc.out_ep[ep_num], priv_epx); } } } static void pusb2_dc_prepare_ctrl_config(FPUsb2Config *config) { *config = *FPUsb2LookupConfig(CONFIG_USBDEV_PUSB2_CTRL_ID); config->mode = FPUSB2_MODE_PERIPHERAL; /* allocate DMA buffer for TRB transfer */ config->trb_mem_addr = usb_align(64U, config->trb_mem_size); USB_ASSERT(config->trb_mem_addr); /* hook up device callbacks */ config->host_cb.givback_request = NULL; config->host_cb.otg_state_change = NULL; config->host_cb.port_status_change = NULL; config->host_cb.set_ep_toggle = NULL; config->host_cb.get_ep_toggle = NULL; config->host_cb.pre_start = NULL; config->device_cb.connect = pusb2_dc_connect_handler; config->device_cb.disconnect= pusb2_dc_disconnect_handler; config->device_cb.resume = pusb2_dc_resume_handler; config->device_cb.setup = pusb2_dc_receive_steup_handler; config->device_cb.suspend = pusb2_dc_suspend_handler; config->device_cb.usb_request_mem_alloc = pusb2_dc_allocate_request_handler; config->device_cb.usb_request_mem_free = pusb2_dc_free_request_handler; config->device_cb.pre_start = pusb2_dc_pre_start_handler; return; } int usb_dc_init(void) { memset(&g_pusb2_udc, 0, sizeof(struct pusb2_udc)); usb_dc_low_level_init(); pusb2_dc_prepare_ctrl_config(&g_pusb2_udc.config); if (FPUSB2_SUCCESS != FPUsb2CfgInitialize(&g_pusb2_udc.pusb2, &g_pusb2_udc.config)) { USB_LOG_ERR("init pusb2 failed \n"); return -1; } USB_LOG_INFO("init pusb2 successed \n"); return 0; } int usb_dc_deinit(void) { usb_dc_low_level_deinit(); FPUsb2DeInitialize(&g_pusb2_udc.pusb2); return 0; } int usbd_set_address(const uint8_t addr) { g_pusb2_udc.dev_addr = addr; return 0; } static struct usb_endpoint_descriptor *usbd_get_ep0_desc(const struct usb_endpoint_descriptor *ep) { static struct usb_endpoint_descriptor ep0_desc; /* Config EP0 mps from speed */ ep0_desc.bEndpointAddress = ep->bEndpointAddress; ep0_desc.bDescriptorType = USB_DESCRIPTOR_TYPE_ENDPOINT; ep0_desc.bmAttributes = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); ep0_desc.wMaxPacketSize = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); ep0_desc.bInterval = 0; ep0_desc.bLength = 7; return &ep0_desc; } int usbd_ep_open(const struct usb_endpoint_descriptor *ep) { uint8_t ep_idx = USB_EP_GET_IDX(ep->bEndpointAddress); struct pusb2_dc_ep_state *ep_state; uint32_t error; if (USB_EP_DIR_IS_OUT(ep->bEndpointAddress)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } ep_state->ep_mps = USB_GET_MAXPACKETSIZE(ep->wMaxPacketSize); ep_state->ep_type = USB_GET_ENDPOINT_TYPE(ep->bmAttributes); ep_state->desc = usbd_get_ep0_desc(ep); USB_ASSERT(ep_state->priv_ep != NULL); USB_LOG_DBG("try to enable ep@0x%x 0x%x:0x%x\n", ep->bEndpointAddress, ep_state->priv_ep, ep_state->desc ); error = FPUsb2DcEpEnable(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, (const FUsbEndpointDescriptor *)ep_state->desc); if (FPUSB2_SUCCESS != error){ USB_LOG_ERR("enable ep-%d failed, error = 0x%x\n", ep->bEndpointAddress, error); return -1; } g_pusb2_udc.ep0_init_finish = 1; return 0; } int usbd_ep_close(const uint8_t ep) { uint8_t ep_idx = USB_EP_GET_IDX(ep); struct pusb2_dc_ep_state *ep_state; if (USB_EP_DIR_IS_OUT(ep)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } ep_state->desc = NULL; if (FPUSB2_SUCCESS != FPUsb2DcEpDisable(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep)){ USB_LOG_ERR("disable ep@0x%x failed\n", ep); return -1; } return 0; } int usbd_ep_set_stall(const uint8_t ep) { uint8_t ep_idx = USB_EP_GET_IDX(ep); struct pusb2_dc_ep_state *ep_state; if (USB_EP_DIR_IS_OUT(ep)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } if (FPUSB2_SUCCESS != FPUsb2DcEpSetHalt(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, 1)){ USB_LOG_ERR("stall ep@0x%x failed\n", ep); return -1; } ep_state->ep_stalled = 1; return 0; } int usbd_ep_clear_stall(const uint8_t ep) { uint8_t ep_idx = USB_EP_GET_IDX(ep); struct pusb2_dc_ep_state *ep_state; if (USB_EP_DIR_IS_OUT(ep)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } if (FPUSB2_SUCCESS != FPUsb2DcEpSetHalt(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, 0)){ USB_LOG_ERR("clear ep@0x%x stall status failed\n", ep); return -1; } ep_state->ep_stalled = 0; return 0; } int usbd_ep_is_stalled(const uint8_t ep, uint8_t *stalled) { uint8_t ep_idx = USB_EP_GET_IDX(ep); struct pusb2_dc_ep_state *ep_state; if (USB_EP_DIR_IS_OUT(ep)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } if (stalled) { *stalled = ep_state->ep_stalled; } return 0; } static struct pusb2_dc_request *pusb2_dc_allocate_request(struct pusb2_dc_ep_state *ep_state) { struct pusb2_dc_request *request = usb_malloc(sizeof(*request)); if (!request) { return NULL; } memset(request, 0, sizeof(*request)); request->ep = ep_state; request->priv_req = NULL; if (FPUSB2_SUCCESS != FPUsb2DcReqAlloc(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, &request->priv_req )){ USB_LOG_ERR("allocate request failed\n"); usb_free(request); return NULL; } return request; } static void pusb2_dc_free_request(struct pusb2_dc_request *request) { USB_ASSERT(request); struct pusb2_dc_ep_state *ep_state = request->ep; FPUsb2DcReqFree(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, request->priv_req); usb_free(request); } void pusb2_dc_callback_complete(FPUsb2DcEp *priv_ep, FPUsb2DcReq *priv_request) { USB_ASSERT(priv_ep && priv_request); struct pusb2_dc_request *request; request = priv_request->context; if (USB_EP_DIR_IS_OUT(priv_ep->address)) { usbd_event_ep_out_complete_handler(priv_ep->address, priv_request->actual); } else { usbd_event_ep_in_complete_handler(priv_ep->address, priv_request->actual); } request->status = priv_request->status; if (request->status != 0) { USB_LOG_ERR("Request failed, status = %d\n", request->status); } pusb2_dc_free_request(request); priv_request->context = NULL; } int pusb2_dc_ep_read_write(const uint8_t ep, uintptr data, uint32_t data_len) { uint8_t ep_idx = USB_EP_GET_IDX(ep); struct pusb2_dc_ep_state *ep_state; struct pusb2_dc_request *request; uint32_t error; if (USB_EP_DIR_IS_OUT(ep)) { ep_state = &g_pusb2_udc.out_ep[ep_idx]; } else { ep_state = &g_pusb2_udc.in_ep[ep_idx]; } request = pusb2_dc_allocate_request(ep_state); if (!request) { USB_LOG_ERR("failed to allocate request !!!\n"); return -1; } request->priv_req->dma = data; request->priv_req->buf = (void *)data; request->priv_req->length = data_len; request->priv_req->complete = pusb2_dc_callback_complete; request->priv_req->context = request; request->priv_req->status = 0; error = FPUsb2DcReqQueue(&g_pusb2_udc.pusb2.device_ctrl, ep_state->priv_ep, request->priv_req); if (FPUSB2_SUCCESS != error){ USB_LOG_ERR("send req to ep@0x%x failed, error = 0x%x\n", ep, error); return -1; } return 0; } int usbd_ep_start_write(const uint8_t ep, const uint8_t *data, uint32_t data_len) { return pusb2_dc_ep_read_write(ep, (uintptr)data, data_len); } int usbd_ep_start_read(const uint8_t ep, uint8_t *data, uint32_t data_len) { return pusb2_dc_ep_read_write(ep, (uintptr)data, data_len); } void USBD_IRQHandler(void) { FPUsb2InterruptHandler(&g_pusb2_udc.pusb2); }