/* * (C) Copyright 2007-2013 * Allwinner Technology Co., Ltd. * Jerry Wang * * See file CREDITS for list of people who contributed to this * project. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 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 * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, * MA 02111-1307 USA */ #include "usb_base.h" #include #include #include //#include #include #include #include "usb_dma_control.h" #define SUNXI_USB_EP0_BUFFER_SIZE (512) #define HIGH_SPEED_EP_MAX_PACKET_SIZE (512) #define FULL_SPEED_EP_MAX_PACKET_SIZE (64) #define BULK_FIFOSIZE (512) #define SUNXI_USB_CTRL_EP_INDEX 0 #define SUNXI_USB_BULK_IN_EP_INDEX 1 /* tx */ #define SUNXI_USB_BULK_OUT_EP_INDEX 2 /* rx */ enum usb_device_speed { USB_SPEED_UNKNOWN = 0, /* enumerating */ USB_SPEED_LOW, USB_SPEED_FULL, /* usb 1.1 */ USB_SPEED_HIGH, /* usb 2.0 */ USB_SPEED_WIRELESS, /* wireless (usb 2.5) */ USB_SPEED_SUPER, /* usb 3.0 */ USB_SPEED_RESERVED }; static uchar sunxi_usb_ep0_buffer[SUNXI_USB_EP0_BUFFER_SIZE]; sunxi_udc_t sunxi_udc_source; static sunxi_ubuf_t sunxi_ubuf; sunxi_usb_setup_req_t *sunxi_udev_active; static uint usb_dma_trans_unaliged_bytes; static uchar *usb_dma_trans_unaligned_buf; static int __usb_read_ep0_data(void *buffer, uint data_type); //static int __usb_read_fifo(void *buffer, unsigned int buffer_size); static int __usb_write_fifo(uchar *buffer, unsigned int buffer_size); static void __usb_bulk_ep_reset (void); static void __usb_clear_all_irq(void); static void __usb_writecomplete(__hdle hUSB, u32 ep_type, u32 complete); static void __usb_readcomplete(__hdle hUSB, u32 ep_type, u32 complete); static void __usb_recv_by_dma_isr(void *p_arg); static void __usb_send_by_dma_isr(void *p_arg); static int __sunxi_udc_set_address(uchar address); static int eptx_send_op(void); static int eprx_recv_op(void); static int ep0_recv_op(void); extern int fastboot_data_flag; extern volatile int sunxi_usb_burn_from_boot_init; #ifdef CONFIG_SUNXI_USB_DETECT extern volatile int sunxi_usb_detect_flag; #endif /* for fastboot */ __weak int get_fastboot_data_flag(void) { return 0; } /* for usb burn */ __weak void set_usb_burn_boot_init_flag(int flag ) { } /* for usb config */ __weak void otg_phy_config(void) { printf("weak:otg_phy_config\n"); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ void sunxi_usb_irq(void *data) { u8 misc_irq = 0; u16 tx_irq = 0; u16 rx_irq = 0; u32 dma_irq = 0; u32 old_ep_idx = 0; /* Save index */ old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); /* Read status registers */ misc_irq = USBC_INT_MiscPending(sunxi_udc_source.usbc_hd); tx_irq = USBC_INT_EpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX); rx_irq = USBC_INT_EpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); dma_irq = usb_dma_int_query(); /* RESET */ if(misc_irq & USBC_INTUSB_RESET) { sunxi_usb_dbg("IRQ: reset\n"); USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_RESET); __usb_clear_all_irq(); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, 0); USBC_Dev_SetAddress_default(sunxi_udc_source.usbc_hd); sunxi_udc_source.address = 0; //default value sunxi_udc_source.speed = USB_SPEED_HIGH; //default value usb_dma_stop(sunxi_udc_source.dma_recv_channal); usb_dma_stop(sunxi_udc_source.dma_send_channal); usb_dma_set_pktlen(sunxi_udc_source.dma_send_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE); usb_dma_set_pktlen(sunxi_udc_source.dma_recv_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE); sunxi_ubuf.rx_ready_for_data = 0; sunxi_udev_active->state_reset(); return ; } /* RESUME 暂时不处理,仅仅清理中断*/ if (misc_irq & USBC_INTUSB_RESUME) { sunxi_usb_dbg("IRQ: resume\n"); /* clear interrupt */ USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_RESUME); } /* SUSPEND */ if (misc_irq & USBC_INTUSB_SUSPEND) { sunxi_usb_dbg("IRQ: suspend\n"); /* clear interrupt */ USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_SUSPEND); } /* DISCONNECT */ if(misc_irq & USBC_INTUSB_DISCONNECT) { sunxi_usb_dbg("IRQ: disconnect\n"); USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_DISCONNECT); return ; } #if 1 /* SOF */ if(misc_irq & USBC_INTUSB_SOF) { sunxi_usb_dbg("IRQ: SOF\n"); #ifdef CONFIG_SUNXI_USB_DETECT sunxi_usb_detect_flag = 1; #endif set_usb_burn_boot_init_flag(1); USBC_INT_DisableUsbMiscUint(sunxi_udc_source.usbc_hd, USBC_INTUSB_SOF); USBC_INT_ClearMiscPending(sunxi_udc_source.usbc_hd, USBC_INTUSB_SOF); } #endif /* ep0 */ if (tx_irq & (1 << SUNXI_USB_CTRL_EP_INDEX) ) { sunxi_usb_dbg("IRQ: EP0\n"); USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX); //中断内完成ep0处理 ep0_recv_op(); } /* tx endpoint data transfers */ if (tx_irq & (1 << SUNXI_USB_BULK_IN_EP_INDEX)) { sunxi_usb_dbg("tx irq occur\n"); /* Clear the interrupt bit by setting it to 1 */ USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_BULK_IN_EP_INDEX); eptx_send_op(); } /* rx endpoint data transfers */ if (rx_irq & (1 << SUNXI_USB_BULK_OUT_EP_INDEX)) { sunxi_usb_dbg("rx irq occur\n"); /* Clear the interrupt bit by setting it to 1 */ USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX); eprx_recv_op(); } if(dma_irq & (1 << SUNXI_USB_BULK_IN_EP_INDEX)) { sunxi_usb_dbg("tx dma\n"); __usb_send_by_dma_isr(NULL); } if(dma_irq & (1 << SUNXI_USB_BULK_OUT_EP_INDEX)) { sunxi_usb_dbg("rx dma\n"); __usb_recv_by_dma_isr(NULL); } usb_dma_int_clear(); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx); return ; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : usb初始化动作,完成后,即可开启中断后,使用中断处理程序 * * ************************************************************************************************************ */ int sunxi_usb_init(int delaytime) { uint reg_val = 0; if(sunxi_udev_active->state_init()) { printf("sunxi usb err: fail to init usb device\n"); return -1; } //预先关闭usb中断 irq_disable(AW_IRQ_USB_OTG); //初始化 sunxi_udc用到的资源 memset(&sunxi_udc_source, 0, sizeof(sunxi_udc_t)); //获取控制器地址资源 sunxi_udc_source.usbc_hd = USBC_open_otg(0); if(sunxi_udc_source.usbc_hd == 0) { printf("sunxi usb err : USBC_open_otg failed\n"); return -1; } usb_dma_init(sunxi_udc_source.usbc_hd); //断开usb USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_OFF); //预先关闭usb时钟 usb_close_clock(); //延时 delaytime ms printf("delay time %d\n", delaytime); __msdelay(delaytime); //申请DMA资源 sunxi_udc_source.dma_send_channal = usb_dma_request(); if(!sunxi_udc_source.dma_send_channal) { printf("sunxi usb err : unable to request dma for usb send data\n"); goto __sunxi_usb_init_fail; } sunxi_usb_dbg("dma send ch %d\n", sunxi_udc_source.dma_send_channal); sunxi_udc_source.dma_recv_channal = usb_dma_request(); if(!sunxi_udc_source.dma_recv_channal) { printf("sunxi usb err : unable to request dma for usb receive data\n"); goto __sunxi_usb_init_fail; } sunxi_usb_dbg("dma recv ch %d\n", sunxi_udc_source.dma_recv_channal); sunxi_udc_source.address = 0; sunxi_udc_source.speed = USB_SPEED_HIGH; sunxi_udc_source.bulk_ep_max = HIGH_SPEED_EP_MAX_PACKET_SIZE; sunxi_udc_source.fifo_size = BULK_FIFOSIZE; sunxi_udc_source.bulk_in_addr = 100; sunxi_udc_source.bulk_out_addr = sunxi_udc_source.bulk_in_addr + sunxi_udc_source.fifo_size * 2; //内存资源 memset(&sunxi_ubuf, 0, sizeof(sunxi_ubuf_t)); sunxi_ubuf.rx_base_buffer = (uchar *)memalign(CONFIG_SYS_CACHELINE_SIZE, 1024); if(!sunxi_ubuf.rx_base_buffer) { printf("%s:alloc memory fail\n", __func__); goto __sunxi_usb_init_fail; } sunxi_ubuf.rx_req_buffer = sunxi_ubuf.rx_base_buffer; usb_open_clock(); //设置为device模式 USBC_ForceId(sunxi_udc_source.usbc_hd, USBC_ID_TYPE_DEVICE); //设置VBUS为高 USBC_ForceVbusValid(sunxi_udc_source.usbc_hd, USBC_VBUS_TYPE_HIGH); USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_OFF); //soft connect USBC_EnableDpDmPullUp(sunxi_udc_source.usbc_hd); USBC_EnableIdPullUp(sunxi_udc_source.usbc_hd); //选择使用PIO模式搬移数据 USBC_SelectBus(sunxi_udc_source.usbc_hd, USBC_IO_TYPE_PIO, 0, 0); //映射SRAM buffer USBC_ConfigFIFO_Base(sunxi_udc_source.usbc_hd, 0, 0); // USBC_EnhanceSignal(sunxi_udc_source.usbc_hd); //默认采用高速模式传输 #ifdef CONFIG_USB_1_1_DEVICE USBC_Dev_ConfigTransferMode(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_TS_MODE_FS); #else USBC_Dev_ConfigTransferMode(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_TS_MODE_HS); #endif //配置发送端dma资源 usb_dma_setting(sunxi_udc_source.dma_send_channal, USB_DMA_FROM_DRAM_TO_HOST, SUNXI_USB_BULK_IN_EP_INDEX); usb_dma_set_pktlen(sunxi_udc_source.dma_send_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE); //配置接收端dma资源 usb_dma_setting(sunxi_udc_source.dma_recv_channal, USB_DMA_FROM_HOST_TO_DRAM, SUNXI_USB_BULK_OUT_EP_INDEX); usb_dma_set_pktlen(sunxi_udc_source.dma_recv_channal, HIGH_SPEED_EP_MAX_PACKET_SIZE); /* disable all interrupt */ USBC_INT_DisableUsbMiscAll(sunxi_udc_source.usbc_hd); USBC_INT_DisableEpAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); USBC_INT_DisableEpAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX); /* 开启 reset、resume、suspend中断 */ USBC_INT_EnableUsbMiscUint(sunxi_udc_source.usbc_hd, USBC_INTUSB_SUSPEND | USBC_INTUSB_RESUME \ | USBC_INTUSB_RESET | USBC_INTUSB_SOF); /* enbale ep0_tx_irq */ USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX); __usb_bulk_ep_reset(); USBC_Dev_ConectSwitch(sunxi_udc_source.usbc_hd, USBC_DEVICE_SWITCH_ON); irq_install_handler(AW_IRQ_USB_OTG, sunxi_usb_irq, NULL); irq_enable(AW_IRQ_USB_OTG); /* sun8iw10p1 spec default value is not correct, bit 1 should be 0 */ reg_val = readl(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL); reg_val &= ~(0x01<<1); writel(reg_val, SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL); #if defined(CONFIG_SUNXI_NCAT) reg_val = readl(SUNXI_USBOTG_BASE+USBC_REG_o_PHYCTL); reg_val &= ~(0x01<state_exit(); memset(&sunxi_ubuf, 0, sizeof(sunxi_ubuf_t)); return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static void __usb_send_by_dma_isr(void *p_arg) { sunxi_udev_active->dma_tx_isr(p_arg); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static void __usb_recv_by_dma_isr(void *p_arg) { u32 old_ep_idx; old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); //选择RXEP //选择使用IO方式搬运数据 sunxi_usb_dbg("select io mode to transfer data\n"); USBC_Dev_ClearEpDma(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); if(usb_dma_trans_unaliged_bytes) { uint fifo, this_len; this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, usb_dma_trans_unaligned_buf); __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //返回状态 usb_dma_trans_unaliged_bytes = 0; } //如果当前dma传输的不是完整包,则需要手动清除中断 if(sunxi_ubuf.request_size % sunxi_udc_source.bulk_ep_max) { USBC_Dev_ReadDataStatus(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //printf("clear rx pending manually\n"); } USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx); sunxi_udev_active->dma_rx_isr(p_arg); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static void __usb_clear_all_irq(void) { USBC_INT_ClearEpPendingAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX); USBC_INT_ClearEpPendingAll(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); USBC_INT_ClearMiscPendingAll(sunxi_udc_source.usbc_hd); } /* ******************************************************************************* * __usb_readcomplete * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static void __usb_readcomplete(__hdle hUSB, u32 ep_type, u32 complete) { USBC_Dev_ReadDataStatus(hUSB, ep_type, complete); if(ep_type == USBC_EP_TYPE_EP0) { /* clear data end */ if(complete) { USBC_Dev_Ctrl_ClearSetupEnd(hUSB); } /* clear irq */ USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX); } return; } /* ******************************************************************************* * __usb_writecomplete * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static void __usb_writecomplete(__hdle hUSB, u32 ep_type, u32 complete) { USBC_Dev_WriteDataStatus(hUSB, ep_type, complete); /* wait for tx packet sent out */ while(USBC_Dev_IsWriteDataReady(hUSB, ep_type)); if(ep_type == USBC_EP_TYPE_EP0) { /* clear data end */ if(complete) { USBC_Dev_Ctrl_ClearSetupEnd(hUSB); } /* clear irq */ USBC_INT_ClearEpPending(hUSB, USBC_EP_TYPE_TX, SUNXI_USB_CTRL_EP_INDEX); } return; } /* ******************************************************************************* * __usb_bulk_ep_reset * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static void __usb_bulk_ep_reset (void) { u8 old_ep_index = 0; old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); /* tx */ USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX); USBC_Dev_ConfigEp(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_EP_TYPE_TX, 1, sunxi_udc_source.bulk_ep_max & 0x7ff); USBC_ConfigFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, 1, sunxi_udc_source.fifo_size, (uint)sunxi_udc_source.bulk_out_addr); USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, SUNXI_USB_BULK_IN_EP_INDEX); /* rx */ USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); USBC_Dev_ConfigEp(sunxi_udc_source.usbc_hd, USBC_TS_TYPE_BULK, USBC_EP_TYPE_RX, 1, sunxi_udc_source.bulk_ep_max & 0x7ff); USBC_ConfigFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1, sunxi_udc_source.fifo_size, (uint)sunxi_udc_source.bulk_in_addr); USBC_INT_EnableEp(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index); return; } /* ******************************************************************************* * __usb_read_ep0_data * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static int __usb_read_ep0_data(void *buffer, uint data_type) { u32 fifo_count = 0; u32 fifo = 0; int ret = 0; u32 old_ep_index = 0; old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX); fifo_count = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0); if(!data_type) { if(fifo_count != 8 ) { printf("err: ep0 fifo_count %d is not 8\n", fifo_count); return -1; } } USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, fifo_count, (void *)buffer); __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index); return ret; } /* ******************************************************************************* * __sunxi_udc_set_address * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static int __sunxi_udc_set_address(uchar address) { USBC_Dev_SetAddress(sunxi_udc_source.usbc_hd, address); if(USBC_Dev_QueryTransferMode(sunxi_udc_source.usbc_hd) == USBC_TS_MODE_HS) { sunxi_udc_source.speed = USB_SPEED_HIGH; sunxi_udc_source.fifo_size = HIGH_SPEED_EP_MAX_PACKET_SIZE; sunxi_usb_dbg("usb speed: HIGH\n"); } else { sunxi_udc_source.speed = USB_SPEED_FULL; sunxi_udc_source.fifo_size = FULL_SPEED_EP_MAX_PACKET_SIZE; sunxi_usb_dbg("usb speed: FULL\n"); } return SUNXI_USB_REQ_SUCCESSED; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ #if 0 static int __usb_read_fifo(void *buffer, unsigned int buffer_size) { u32 old_ep_idx = 0; u32 fifo = 0; u32 transfered = 0; u32 left = 0; u32 this_len; old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); //选择当前EP fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); //选择fifo left = buffer_size; if(left) { while(left) { if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX)) { this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); this_len = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer + transfered); transfered += this_len; left -= this_len; __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //返回状态 } } USBC_INT_ClearEpPending(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, SUNXI_USB_BULK_OUT_EP_INDEX); } else { if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX)) { this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); this_len = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer); transfered = this_len; __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //返回状态 } else { sunxi_usb_dbg("sunxi usb rxdata not ready\n"); } } USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx); sunxi_usb_dbg("read bytes 0x%x\n", transfered); return transfered; } #endif /* ******************************************************************************* * fastboot_tx_status * * Description: * void * * Parameters: * void * * Return value: * void * * note: * void * ******************************************************************************* */ static int __usb_write_fifo(uchar *buffer, unsigned int buffer_size) { u32 old_ep_idx = 0; u32 fifo = 0; u32 transfered = 0; u32 left = 0; u32 this_len; /* Save index */ old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX); left = buffer_size; fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_IN_EP_INDEX); while(left) { this_len = MIN(sunxi_udc_source.fifo_size, left); this_len = USBC_WritePacket(sunxi_udc_source.usbc_hd, fifo, this_len, buffer + transfered); transfered += this_len; left -= this_len; __usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_TX, 1); } USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx); return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ void sunxi_udc_ep_reset(void) { __usb_bulk_ep_reset(); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_start_recv_by_dma(void* mem_base, uint length) { uint old_ep_idx; ulong mem_buf = (ulong)mem_base; usb_dma_trans_unaliged_bytes = length & (sizeof(int) - 1); length &= ~(sizeof(int) - 1); usb_dma_trans_unaligned_buf = (uchar *)mem_buf + length; old_ep_idx = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); //选择当前EP //usb控制器选择dma传输方式 USBC_Dev_ConfigEpDma(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); //刷掉cache flush_cache(mem_buf, ALIGN(length, 64)); //使能dma传输 sunxi_ubuf.request_size = length; sunxi_usb_dbg("dma start 0x%lx, length 0x%x\n", mem_buf, length); usb_dma_start(sunxi_udc_source.dma_recv_channal, mem_buf, length); //恢复EP USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_idx); //恢复原有EP return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ void sunxi_udc_send_setup(uint bLength, void *buffer) { u32 fifo = 0; if(!bLength) { /* sent zero packet */ __usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1); } else { fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX); USBC_WritePacket(sunxi_udc_source.usbc_hd, fifo, bLength, (void *)buffer); __usb_writecomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1); } } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_set_configuration(int config_param) { return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_set_address(uchar address) { sunxi_udc_source.address = address; USBC_Dev_ReadDataStatus(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0, 1); return SUNXI_USB_REQ_SUCCESSED; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_send_data(void *buffer, unsigned int buffer_size) { sunxi_ubuf.rx_ready_for_data = 0; return __usb_write_fifo((uchar *)buffer, buffer_size); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_get_ep_max(void) { return sunxi_udc_source.bulk_ep_max; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_get_ep_in_type(void) { return (0x80 | SUNXI_USB_BULK_IN_EP_INDEX); } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_udc_get_ep_out_type(void) { return SUNXI_USB_BULK_OUT_EP_INDEX; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static int ep0_recv_op(void) { u32 old_ep_index = 0; int ret = 0; static uint ep0_stage = 0; if(!ep0_stage) { memset(&sunxi_udc_source.standard_reg, 0, sizeof(struct usb_device_request)); } old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_CTRL_EP_INDEX); //clear stall status if(USBC_Dev_IsEpStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0)) { printf("ERR: handle_ep0: ep0 stall\n"); USBC_Dev_EpClearStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0); ret = -1; goto __ep0_recv_op_err; } //clear setup end if (USBC_Dev_Ctrl_IsSetupEnd(sunxi_udc_source.usbc_hd)) { USBC_Dev_Ctrl_ClearSetupEnd(sunxi_udc_source.usbc_hd); } //检查读ep0数据是否完成 if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0)) { uint status; if(!ep0_stage) { status = __usb_read_ep0_data(&sunxi_udc_source.standard_reg, ep0_stage); } else { status = __usb_read_ep0_data(sunxi_usb_ep0_buffer, ep0_stage); } if(status!= 0) { printf("sunxi usb err: read_request failed\n"); ret = -1; goto __ep0_recv_op_err; } } else //此情况通常由于ep0发送空包引起,可以不处理 { sunxi_usb_dbg("sunxi usb msg: ep0 rx data is not ready\n"); if(sunxi_udc_source.address) { __sunxi_udc_set_address(sunxi_udc_source.address&0xff); printf("set address 0x%x ok\n", sunxi_udc_source.address); sunxi_udc_source.address = 0; } goto __ep0_recv_op_err; } /* Check data */ if(USB_REQ_TYPE_STANDARD == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_TYPE_MASK)) { ret = SUNXI_USB_REQ_UNMATCHED_COMMAND; /* standard */ switch(sunxi_udc_source.standard_reg.bRequest) { case USB_REQ_GET_STATUS: // 0x00 { /* device-to-host */ if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_STATUS, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer); } break; } case USB_REQ_CLEAR_FEATURE: // 0x01 { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_CLEAR_FEATURE, &sunxi_udc_source.standard_reg, NULL); } break; } case USB_REQ_SET_FEATURE: // 0x03 { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_FEATURE, &sunxi_udc_source.standard_reg, NULL); } break; } case USB_REQ_SET_ADDRESS: // 0x05 { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { /* receiver is device */ ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_ADDRESS, &sunxi_udc_source.standard_reg, NULL); } } break; } case USB_REQ_GET_DESCRIPTOR: // 0x06 { /* device-to-host */ if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_DESCRIPTOR, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer); } } break; } case USB_REQ_SET_DESCRIPTOR: // 0x07 { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { //there is some problem ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_DESCRIPTOR, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer); } } break; } case USB_REQ_GET_CONFIGURATION: // 0x08 { /* device-to-host */ if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_CONFIGURATION, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer); } } break; } case USB_REQ_SET_CONFIGURATION: // 0x09 { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_CONFIGURATION, &sunxi_udc_source.standard_reg, NULL); } } break; } case USB_REQ_GET_INTERFACE: // 0x0a { /* device-to-host */ if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_DEVICE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_GET_INTERFACE, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer); } } break; } case USB_REQ_SET_INTERFACE: // 0x0b { /* host-to-device */ if(USB_DIR_OUT == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_INTERFACE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { ret = sunxi_udev_active->standard_req_op(USB_REQ_SET_INTERFACE, &sunxi_udc_source.standard_reg, NULL); } } break; } case USB_REQ_SYNCH_FRAME: // 0x0b { /* device-to-host */ if(USB_DIR_IN == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_DIRECTION_MASK)) { if(USB_RECIP_INTERFACE == (sunxi_udc_source.standard_reg.bmRequestType & USB_REQ_RECIPIENT_MASK)) { //there is some problem if(!ep0_stage) { ep0_stage = 1; } else { ret = sunxi_udev_active->standard_req_op(USB_REQ_SYNCH_FRAME, &sunxi_udc_source.standard_reg, NULL); ep0_stage = 0; } } } break; } default: { printf("sunxi usb err: unknown usb out request to device\n"); USBC_Dev_EpSendStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0); ret = SUNXI_USB_REQ_DEVICE_NOT_SUPPORTED; ep0_stage = 0; break; } } } else { /* Non-Standard Req */ printf("non standard req\n"); ret = sunxi_udev_active->nonstandard_req_op(USB_REQ_GET_STATUS, &sunxi_udc_source.standard_reg, sunxi_usb_ep0_buffer, ep0_stage); if(ret == SUNXI_USB_REQ_DATA_HUNGRY) { ep0_stage = 1; } else if(ret == SUNXI_USB_REQ_SUCCESSED) { ep0_stage = 0; } else if(ret < 0) { ep0_stage = 0; printf("err: unkown bmRequestType(%d)\n", sunxi_udc_source.standard_reg.bmRequestType); USBC_Dev_EpSendStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_EP0); } } __ep0_recv_op_err: USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index); return ret; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static int eprx_recv_op(void) { uint old_ep_index; uint this_len; uint fifo; old_ep_index = USBC_GetActiveEp(sunxi_udc_source.usbc_hd); USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); if (USBC_Dev_IsEpStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX)) { USBC_Dev_EpClearStall(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); printf("sunxi ubs read error: usb rx ep is busy already\n"); } else { if(USBC_Dev_IsReadDataReady(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX)) { this_len = USBC_ReadLenFromFifo(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX); if(get_fastboot_data_flag() == 1) { fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); sunxi_ubuf.rx_req_length = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, sunxi_ubuf.rx_req_buffer); sunxi_ubuf.rx_req_buffer += this_len; sunxi_usb_dbg("special read ep bytes 0x%x\n", sunxi_ubuf.rx_req_length); __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //返回状态 } else if(!sunxi_ubuf.rx_ready_for_data) { fifo = USBC_SelectFIFO(sunxi_udc_source.usbc_hd, SUNXI_USB_BULK_OUT_EP_INDEX); memset(sunxi_ubuf.rx_req_buffer, 0, 64); sunxi_ubuf.rx_req_length = USBC_ReadPacket(sunxi_udc_source.usbc_hd, fifo, this_len, sunxi_ubuf.rx_req_buffer); sunxi_ubuf.rx_ready_for_data = 1; sunxi_usb_dbg("read ep bytes 0x%x\n", sunxi_ubuf.rx_req_length); __usb_readcomplete(sunxi_udc_source.usbc_hd, USBC_EP_TYPE_RX, 1); //返回状态 } else { sunxi_usb_dbg("eprx do nothing and left it to dma\n"); } } else { sunxi_usb_dbg("sunxi usb rxdata not ready\n"); } } USBC_SelectActiveEp(sunxi_udc_source.usbc_hd, old_ep_index); return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ static int eptx_send_op(void) { return 0; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ void sunxi_usb_main_loop(int delaytime) { int ret; if(sunxi_usb_init(delaytime)) { printf("usb init fail\n"); sunxi_usb_exit(); return ; } printf("usb init ok\n"); while(1) { ret = sunxi_udev_active->state_loop(&sunxi_ubuf); if(ret) { break; } if(ctrlc()) { break; } } printf("exit usb\n"); sunxi_usb_exit(); sunxi_update_subsequent_processing(ret); return ; } /* ************************************************************************************************************ * * function * * name : * * parmeters : * * return : * * note : * * ************************************************************************************************************ */ int sunxi_usb_extern_loop(void) { return sunxi_udev_active->state_loop(&sunxi_ubuf); }