| /* ========================================================================== |
| * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $ |
| * $Revision: #101 $ |
| * $Date: 2012/08/10 $ |
| * $Change: 2047372 $ |
| * |
| * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, |
| * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless |
| * otherwise expressly agreed to in writing between Synopsys and you. |
| * |
| * The Software IS NOT an item of Licensed Software or Licensed Product under |
| * any End User Software License Agreement or Agreement for Licensed Product |
| * with Synopsys or any supplement thereto. You are permitted to use and |
| * redistribute this Software in source and binary forms, with or without |
| * modification, provided that redistributions of source code must retain this |
| * notice. You may not view, use, disclose, copy or distribute this file or |
| * any information contained herein except pursuant to this license grant from |
| * Synopsys. If you do not agree with this notice, including the disclaimer |
| * below, then you are not authorized to use the Software. |
| * |
| * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS 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. |
| * ========================================================================== */ |
| #ifndef DWC_HOST_ONLY |
| |
| /** @file |
| * This file implements PCD Core. All code in this file is portable and doesn't |
| * use any OS specific functions. |
| * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code> |
| * header file, which can be used to implement OS specific PCD interface. |
| * |
| * An important function of the PCD is managing interrupts generated |
| * by the DWC_otg controller. The implementation of the DWC_otg device |
| * mode interrupt service routines is in dwc_otg_pcd_intr.c. |
| * |
| * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). |
| * @todo Does it work when the request size is greater than DEPTSIZ |
| * transfer size |
| * |
| */ |
| #include "global.h" |
| #include "dwc_otg_pcd.h" |
| |
| __align(4) const dwc_device_descriptor_t device_desc = |
| { |
| sizeof(dwc_device_descriptor_t), |
| DEVICE_DESCRIPTOR, |
| 0x0200, // usb 2.0 //usb 1.1 |
| 0x00, |
| 0x00, |
| 0x00, |
| CONTROL_64, // ×î´ó°ü8×Ö½Ú |
| USB_VENDOR_ID, |
| USB_PRODUCT_ID, |
| PRODUCT_RELEASE_NUMBER, |
| 0x01, // manufacturer descriptionË÷ÒýºÅ |
| 0x02, // product descriptionË÷ÒýºÅ |
| 0x03, // serial number descriptionË÷ÒýºÅ |
| 0x01 // ÅäÖÃÊý |
| }; |
| |
| __align(4) const dwc_dev_qual_descriptor_t dev_qual_desc = |
| { |
| sizeof(dwc_dev_qual_descriptor_t), |
| 0x06, |
| 0x0200, // usb 2.0 |
| VERDOR_SPECIFIC, |
| 0x00, |
| 0x00, |
| 64, |
| 0x01, // ÅäÖÃÊý |
| 0x0 |
| }; |
| |
| |
| |
| __align(4) const dwc_langid_descriptor_t tLanguage= |
| { |
| STRING_DESCRIPTOR_SIZE(1), |
| STRING_DESCRIPTOR, |
| LANGID_US_ENGLISH |
| }; |
| |
| __align(4) const dwc_string_descriptor_t tManufacture= |
| { |
| STRING_DESCRIPTOR_SIZE(11), |
| STRING_DESCRIPTOR, |
| UNICODE('Z'), UNICODE('T'), UNICODE('E'), UNICODE(' '), UNICODE('C'), |
| UNICODE('o'), UNICODE(','), UNICODE('L'), UNICODE('t'), UNICODE('d'), |
| UNICODE('.'), |
| }; |
| |
| __align(4) const dwc_string_descriptor_t tProduct= |
| { |
| STRING_DESCRIPTOR_SIZE(14), |
| STRING_DESCRIPTOR, |
| UNICODE('Z'), UNICODE('T'), UNICODE('E'), UNICODE(' '), UNICODE('B'), |
| UNICODE('o'), UNICODE('o'), UNICODE('t'), UNICODE('L'), UNICODE('o'), |
| UNICODE('a'), UNICODE('d'), UNICODE('e'), UNICODE('r') |
| }; |
| |
| __align(4) const dwc_string_descriptor_t tSN= |
| { |
| STRING_DESCRIPTOR_SIZE(16), |
| STRING_DESCRIPTOR, |
| UNICODE('z'), UNICODE('t'), UNICODE('e'), UNICODE('&'), UNICODE('u'), |
| UNICODE('s'), UNICODE('b'), UNICODE('B'), UNICODE('o'), UNICODE('o'), |
| UNICODE('t'), UNICODE('l'), UNICODE('o'), UNICODE('d'), UNICODE('e'), |
| UNICODE('r') |
| }; |
| |
| __align(4) const dwc_string_descriptor_t tIfc0Name= |
| { |
| STRING_DESCRIPTOR_SIZE(9), |
| STRING_DESCRIPTOR, |
| UNICODE('B'), UNICODE('o'), UNICODE('o'), UNICODE('t'), UNICODE('l'), |
| UNICODE('o'), UNICODE('d'), UNICODE('e'), UNICODE('r') |
| }; |
| |
| __align(4) const dwc_string_descriptor_t tIfc1Name= |
| { |
| STRING_DESCRIPTOR_SIZE(6), |
| STRING_DESCRIPTOR, |
| UNICODE('S'), UNICODE('e'), UNICODE('r'), UNICODE('i'), UNICODE('a'), |
| UNICODE('l') |
| }; |
| |
| __align(4) const dwc_string_descriptor_t * const pStrDescIdx[]= |
| { |
| (dwc_string_descriptor_t *)(&tLanguage), |
| (dwc_string_descriptor_t *)&tManufacture, |
| (dwc_string_descriptor_t *)&tProduct, |
| (dwc_string_descriptor_t *)&tSN, |
| (dwc_string_descriptor_t *)&tIfc0Name, |
| (dwc_string_descriptor_t *)&tIfc1Name, |
| }; |
| |
| |
| static void do_get_descriptor(dwc_otg_pcd_t * pcd,uint32_t *buff,uint32_t len) |
| { |
| |
| dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; |
| pcd->ep0_pending = 1; |
| ep0->dwc_ep.start_xfer_buff =(uint8_t*) buff; |
| ep0->dwc_ep.xfer_buff =(uint8_t*)buff; |
| ep0->dwc_ep.xfer_len = len; |
| ep0->dwc_ep.xfer_count = 0; |
| ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; |
| dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); |
| |
| } |
| |
| extern void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val); |
| |
| int dwc_setup(dwc_otg_pcd_t * pcd, usb_device_request_t * ctrl) |
| { |
| uint8_t byIdx; |
| uint32_t len = 0; |
| uint16_t value; |
| byIdx = (uint8_t)(ctrl->wValue &0xff); |
| value = ctrl->wValue>>8; |
| |
| if( USB_DT_DEVICE ==value ) |
| { |
| len = MIN( sizeof(dwc_device_descriptor_t), ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)&device_desc,len); |
| } |
| else if( USB_DT_CONFIG==value ) |
| { |
| len = MIN( sizeof(dwc_config_all_t), ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)&g_config_desc,len); |
| } |
| |
| else if( USB_DT_STRING ==value) |
| { |
| len = MIN(((dwc_string_descriptor_t*)(pStrDescIdx[byIdx]))->bLength, ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)pStrDescIdx[byIdx],len); |
| } |
| |
| else if( USB_DT_INTERFACE == value) |
| { |
| len = MIN(((dwc_string_descriptor_t*)(pStrDescIdx[byIdx]))->bLength, ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)pStrDescIdx[byIdx],len); |
| } |
| else if( ENDPOINT_DESCRIPTOR == value) |
| { |
| len = MIN(((dwc_string_descriptor_t*)(pStrDescIdx[byIdx]))->bLength, ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)pStrDescIdx[byIdx],len); |
| } |
| else if( USB_DT_DEVICE_QUALIFIER ==value) |
| { |
| len = MIN(sizeof(dwc_dev_qual_descriptor_t),ctrl->wLength); |
| do_get_descriptor(pcd,(uint32_t*)&dev_qual_desc,len); |
| } |
| else |
| { |
| ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); |
| } |
| |
| return 0; |
| } |
| |
| |
| static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep, |
| uint32_t is_in, uint32_t ep_num) |
| { |
| /* Init EP structure */ |
| pcd_ep->desc = 0; |
| pcd_ep->pcd = pcd; |
| pcd_ep->stopped = 1; |
| pcd_ep->queue_sof = 0; |
| |
| /* Init DWC ep structure */ |
| pcd_ep->dwc_ep.is_in = is_in; |
| pcd_ep->dwc_ep.num = ep_num; |
| pcd_ep->dwc_ep.active = 0; |
| pcd_ep->dwc_ep.tx_fifo_num = 0; |
| /* Control until ep is actvated */ |
| pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_BULK; |
| pcd_ep->dwc_ep.maxpacket = 512; |
| pcd_ep->dwc_ep.maxxfer = 65536; |
| |
| pcd_ep->dwc_ep.start_xfer_buff = 0; |
| pcd_ep->dwc_ep.xfer_buff = 0; |
| pcd_ep->dwc_ep.xfer_len = 0; |
| pcd_ep->dwc_ep.xfer_count = 0; |
| pcd_ep->dwc_ep.sent_zlp = 0; |
| pcd_ep->dwc_ep.total_len = 0; |
| } |
| |
| /** |
| * Initialize ep's |
| */ |
| static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd) |
| { |
| int i; |
| uint32_t hwcfg1; |
| dwc_otg_pcd_ep_t *ep; |
| int in_ep_cntr, out_ep_cntr; |
| uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps; |
| uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps; |
| |
| /** |
| * Initialize the EP0 structure. |
| */ |
| pcd->ep0.dwc_ep.pPara = (void*)(&global.g_in_pPara[1]); |
| |
| ep = &pcd->ep0; |
| dwc_otg_pcd_init_ep(pcd, ep, 0, 0); |
| |
| in_ep_cntr = 0; |
| hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3; |
| for (i = 1; in_ep_cntr < num_in_eps; i++) |
| { |
| if ((hwcfg1 & 0x1) == 0) |
| { |
| pcd->in_ep[in_ep_cntr].dwc_ep.pPara = (void*)(&global.g_in_pPara[in_ep_cntr]); |
| ep = &pcd->in_ep[in_ep_cntr]; |
| in_ep_cntr++; |
| /** |
| * @todo NGS: Add direction to EP, based on contents |
| * of HWCFG1. Need a copy of HWCFG1 in pcd structure? |
| * sprintf(";r |
| */ |
| dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i); |
| } |
| hwcfg1 >>= 2; |
| } |
| |
| out_ep_cntr = 0; |
| hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2; |
| for (i = 1; out_ep_cntr < num_out_eps; i++) |
| { |
| if ((hwcfg1 & 0x1) == 0) |
| { |
| pcd->out_ep[out_ep_cntr].dwc_ep.pPara = (void*)(&global.g_out_pPara[out_ep_cntr]); |
| ep = &pcd->out_ep[out_ep_cntr]; |
| out_ep_cntr++; |
| /** |
| * @todo NGS: Add direction to EP, based on contents |
| * of HWCFG1. Need a copy of HWCFG1 in pcd structure? |
| * sprintf(";r |
| */ |
| dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i); |
| } |
| hwcfg1 >>= 2; |
| } |
| if(NEED_ENUM == global.g_enum) |
| { |
| pcd->ep0state = EP0_DISCONNECT; |
| } |
| else |
| { |
| pcd->ep0state = EP0_IDLE; |
| |
| } |
| pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE; |
| pcd->ep0.dwc_ep.type = 0; |
| } |
| |
| /** |
| * This function initialized the PCD portion of the driver. |
| * |
| */ |
| dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if) |
| { |
| dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *)&global.g_dwc_otg_pcd_tp; |
| int i; |
| uint8_t* pt_dwc_otg_pcd_t = (uint8_t*)&global.g_dwc_otg_pcd_tp; |
| for(i = 0;i<sizeof(dwc_otg_pcd_t);i++) |
| { |
| pt_dwc_otg_pcd_t[i] = 0; |
| } |
| |
| pcd->core_if = core_if; |
| |
| pcd->setup_pkt = global.g_u_setup_pkt; |
| pcd->status_buf = &global.g_status_buf; |
| dwc_otg_pcd_reinit(pcd); |
| |
| return pcd; |
| |
| |
| } |
| |
| |
| /******************************************************************************/ |
| |
| #endif /* DWC_HOST_ONLY */ |