| /* usb pcd driver */ |
| /* |
| * (C) Copyright 2010 Amlogic, Inc |
| * |
| * Victor Wan, victor.wan@amlogic.com, |
| * 2010-03-24 @ Shanghai |
| * |
| */ |
| #include "../v2_burning_i.h" |
| #include "platform.h" |
| #include "usb_ch9.h" |
| #include "dwc_pcd.h" |
| #include "dwc_pcd_irq.h" |
| #include "usb_pcd.h" |
| |
| #define COMPILE_TYPE_CHK(expr, t) typedef char t[(expr) ? 1 : -1] |
| |
| static int do_bulk_cmd(char* cmd); |
| |
| //one DWC_BLK_MAX_LEN <==> one dwc_otg_ep_req_start |
| #ifdef USE_FULL_SPEED |
| #define DWC_BLK_MAX_LEN (6*BULK_EP_MPS) |
| #else |
| #define DWC_BLK_MAX_LEN (8*BULK_EP_MPS) |
| #endif// #ifdef USE_FULL_SPEED |
| #define DWC_BLK_LEN(leftSz) ((leftSz) >= DWC_BLK_MAX_LEN ? DWC_BLK_MAX_LEN : \ |
| (leftSz >= BULK_EP_MPS ? ((leftSz/BULK_EP_MPS)*BULK_EP_MPS): leftSz)) |
| #define DWC_BLK_NUM(totalTransLen) ( (totalTransLen/DWC_BLK_MAX_LEN) + \ |
| ( (totalTransLen & (DWC_BLK_MAX_LEN-1)) >= BULK_EP_MPS ? 1 : 0 ) +\ |
| ( (totalTransLen & (BULK_EP_MPS-1)) ? 1 : 0 ) ) |
| |
| #define DRIVER_VENDOR_ID 0x1B8E //Amlogic's VerdorID |
| #define DRIVER_PRODUCT_ID 0xC003 |
| #define DRIVER_VERSION 0x0100 |
| |
| |
| #define STRING_MANUFACTURER 1 |
| #define STRING_PRODUCT 2 |
| #define STRING_SERIAL 3 |
| #define STRING_CONFIG 4 |
| #define STRING_INTERFACE 5 |
| static const struct usb_device_descriptor |
| device_desc = { |
| sizeof device_desc, //__u8 bLength; |
| USB_DT_DEVICE, //__u8 bDescriptorType; |
| #ifdef USE_FULL_SPEED |
| __constant_cpu_to_le16(0x0110), //__u16 bcdUSB; |
| #else |
| __constant_cpu_to_le16(0x0200), //__u16 bcdUSB; |
| #endif |
| USB_CLASS_PER_INTERFACE, //__u8 bDeviceClass; |
| 0, //__u8 bDeviceSubClass; |
| 0, //__u8 bDeviceProtocol; |
| 64, //__u8 bMaxPacketSize0; |
| __constant_cpu_to_le16(DRIVER_VENDOR_ID), //__u16 idVendor; |
| __constant_cpu_to_le16(DRIVER_PRODUCT_ID), //__u16 idProduct; |
| __constant_cpu_to_le16(0x0007), //__u16 bcdDevice; |
| 0/*STRING_MANUFACTURER*/, //__u8 iManufacturer; |
| 0/*STRING_PRODUCT*/, //__u8 iProduct; |
| 0/*STRING_SERIAL*/, //__u8 iSerialNumber; |
| 1 //__u8 bNumConfigurations; |
| }; |
| |
| #define INTF_CONFIG_DESC_LEN 23 |
| #define TOTAL_CONFIG_DESC_LEN (INTF_CONFIG_DESC_LEN + USB_DT_CONFIG_SIZE) |
| |
| #pragma pack(push, 1) |
| struct _ConfigDesTotal_s{ |
| struct usb_config_descriptor configDesc; |
| char intfDesc[INTF_CONFIG_DESC_LEN] ; |
| }; |
| #pragma pack(pop) |
| COMPILE_TYPE_CHK(TOTAL_CONFIG_DESC_LEN == sizeof(struct _ConfigDesTotal_s), a); |
| |
| static const struct _ConfigDesTotal_s _configDescTotal = { |
| .configDesc = |
| { |
| USB_DT_CONFIG_SIZE, //__u8 bLength; |
| USB_DT_CONFIG, //__u8 bDescriptorType; |
| TOTAL_CONFIG_DESC_LEN, //__u16 wTotalLength; |
| 1, //__u8 bNumInterfaces; |
| 1, //__u8 bConfigurationValue; |
| 0,//STRING_CONFIG, //__u8 iConfiguration; |
| USB_CONFIG_ATT_ONE | |
| USB_CONFIG_ATT_SELFPOWER, //__u8 bmAttributes; |
| 1 //__u8 MaxPower; |
| }, |
| |
| .intfDesc = |
| { |
| 0x09,// length |
| 0x04,//USB_DT_INTERFACE |
| 0x00,//bInterfaceNumber |
| 0x00,//bAlternateSetting |
| 0x02,//bNumEndpoints |
| 0xFF,//bInterfaceClass, 0xFF = USB_CLASS_VENDOR_SPEC |
| 0x00,//bInterfaceSubClass |
| 0x00,//bInterfaceProtocol |
| 0x00,//iInterface |
| |
| 0x07,//Length |
| 0x05,//USB_DT_ENDPOINT |
| 0x80 | BULK_IN_EP_NUM,// 1 -- IN |
| 0x02,// Bulk |
| #ifndef USE_FULL_SPEED |
| 0x00,// |
| 0x02,// 64 bytes MPS |
| #else |
| 0x40,// |
| 0x00,// 64 bytes MPS |
| #endif |
| 0x00, |
| |
| 0x07,//Length |
| 0x05,//USB_DT_ENDPOINT |
| 0x00 | BULK_OUT_EP_NUM,// 2 -- OUT |
| 0x02,// Bulk |
| #ifndef USE_FULL_SPEED |
| 0x00,// |
| 0x02,// 64 bytes MPS |
| #else |
| 0x40,// |
| 0x00,// 64 bytes MPS |
| #endif |
| 0x00 |
| } |
| }; |
| |
| /*static const struct usb_config_descriptor* config_desc = &_configDescTotal.configDesc;*/ |
| /*static const unsigned char* intf_desc = &_configDescTotal.intfDesc;*/ |
| |
| #define DT_STRING_ID_LEN 4 |
| static const char dt_string_id[DT_STRING_ID_LEN]={ |
| DT_STRING_ID_LEN, |
| USB_DT_STRING, |
| 0x09, |
| 0x04, |
| }; |
| #define DT_STRING_VID_LEN 16 |
| static const char dt_string_vid[DT_STRING_VID_LEN]= |
| { |
| DT_STRING_VID_LEN, |
| USB_DT_STRING, |
| 'A', |
| 0, |
| 'm', |
| 0, |
| 'l', |
| 0, |
| 'o', |
| 0, |
| 'g', |
| 0, |
| 'i', |
| 0, |
| 'c', |
| 0 |
| }; |
| |
| #define _PLATFORM_CHIP_INDEX '8' |
| |
| #define DT_STRING_PID_LEN 16 |
| static const char dt_string_pid[DT_STRING_PID_LEN]= |
| { |
| DT_STRING_PID_LEN, |
| USB_DT_STRING, |
| 'M', |
| 0, |
| _PLATFORM_CHIP_INDEX, |
| 0, |
| '-', |
| 0, |
| 'C', |
| 0, |
| 'H', |
| 0, |
| 'I', |
| 0, |
| 'P', |
| 0 |
| }; |
| |
| #define DT_STRING_SERIAL_LEN 18 |
| static const char dt_string_serial[DT_STRING_SERIAL_LEN]= |
| { |
| DT_STRING_SERIAL_LEN, |
| USB_DT_STRING, |
| '2', |
| 0, |
| '0', |
| 0, |
| '1', |
| 0, |
| '1', |
| 0, |
| '0', |
| 0, |
| '4', |
| 0, |
| '0', |
| 0, |
| '6', |
| 0 |
| }; |
| |
| static char _resultInfo[AM_BULK_REPLY_LEN] = {0}; |
| /*static char _bulkCmdBuf[AM_BULK_REPLY_LEN] = {0};*/ |
| /*#define buff ((char*)PCD_BUFF)*/ |
| static volatile char _pcd_buff[512] ; |
| #define buff _pcd_buff |
| |
| int usb_pcd_init(void) |
| { |
| flush_cache((unsigned long)buff, 512); |
| return dwc_core_init(); |
| } |
| |
| static unsigned int need_check_timeout; |
| static unsigned int time_out_val;//wati time for pc enumerate |
| static unsigned int _auto_burn_time_out_base = 0; |
| // |
| //wait time for gintsts_data_t.b.sofintr, 1/2 time_out_val, 1/2 * 700 in trunk |
| static unsigned int time_out_wait_sof; |
| static unsigned int _sofintr_not_occur; |
| |
| void usb_parameter_init(int time_out) |
| { |
| /* clear utimer */ |
| need_check_timeout=get_timer(0); |
| if (time_out) |
| { |
| time_out_val = time_out; // wait PC GetDescriptor command for 1us changed by Elvis |
| |
| } |
| else |
| { |
| time_out_val = USB_ROM_CONN_TIMEOUT; // wait PC GetDescriptor command |
| } |
| time_out_wait_sof = time_out_val >> 1; |
| _sofintr_not_occur = 1; |
| //printf("need_check_timeout %d, time_out_val %d, time_out %d\n", need_check_timeout, time_out_val, time_out); |
| |
| //Added by Sam Wu |
| optimus_buf_manager_init(16*1024); |
| optimus_download_init(); |
| return; |
| } |
| |
| int usb_pcd_irq(void) |
| { |
| if (need_check_timeout) |
| { |
| unsigned curTime = get_timer(need_check_timeout); |
| if (curTime > time_out_wait_sof && _sofintr_not_occur) { |
| ERR("noSof\n"); |
| dwc_otg_power_off_phy(); |
| return 2; |
| } |
| if (curTime > time_out_val) { |
| dwc_otg_power_off_phy(); |
| ERR("Try connect time out %u, %u, %u\n", curTime, time_out_val, need_check_timeout); |
| return 2;// return to other device boot |
| } |
| } |
| if (_auto_burn_time_out_base) { |
| unsigned waitIdentifyTime = get_timer(_auto_burn_time_out_base); |
| unsigned timeout = simple_strtoul(getenv(_ENV_TIME_OUT_TO_AUTO_BURN), NULL, 0); |
| if (waitIdentifyTime > timeout) { |
| ERR("waitIdentifyTime(%u) > timeout(%u)\n", waitIdentifyTime, timeout); |
| _auto_burn_time_out_base = 0;//clear it to allow enter burning next time |
| return __LINE__; |
| } |
| } |
| |
| return dwc_otg_irq(); |
| } |
| |
| void start_bulk_transfer(pcd_struct_t *_pcd) |
| { |
| _pcd->bulk_lock = 1; // TODO : add lock code. |
| dwc_otg_ep_req_start(_pcd,_pcd->bulk_out?BULK_OUT_EP_NUM:BULK_IN_EP_NUM); |
| } |
| /** |
| * This functions delegates the setup command to the gadget driver. |
| */ |
| void do_gadget_setup( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl) |
| { |
| int value; |
| u16 w_index = ctrl->wIndex; |
| u16 w_value = ctrl->wValue; |
| u16 w_length = ctrl->wLength; |
| |
| /* usually this just stores reply data in the pre-allocated ep0 buffer, |
| * but config change events will also reconfigure hardware. */ |
| switch (ctrl->bRequest) |
| { |
| case USB_REQ_GET_DESCRIPTOR: |
| { |
| if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) |
| break; |
| //time_out_val = USB_ROM_DRIVER_TIMEOUT;// Wait SetConfig (PC install driver OK) |
| /*need_check_timeout = 0;*/ |
| switch (w_value >> 8) |
| { |
| case USB_DT_DEVICE: |
| DBG("get dev desc\n"); |
| value = min(w_length, (u16) sizeof device_desc); |
| /*usb_memcpy(buff, (char*)&device_desc, value);*/ |
| /* |
| *memcpy((void*)buff, &device_desc, value); |
| *_pcd->buf = buff; |
| */ |
| _pcd->buf = (volatile char*)&device_desc; |
| _pcd->length = value; |
| break; |
| case USB_DT_DEVICE_QUALIFIER: |
| DBG("get dev qual\n"); |
| break; |
| case USB_DT_OTHER_SPEED_CONFIG: |
| DBG("--get other speed configuration descriptor\n"); |
| DBG("--other speed configuration descriptor length :%d\n", value); |
| break; |
| case USB_DT_CONFIG: |
| { |
| USB_DBG("--get configuration descriptor: size %d\n",w_length); |
| if (w_length > USB_DT_CONFIG_SIZE) {//request USB_DT_CONFIG_SIZE only |
| value = TOTAL_CONFIG_DESC_LEN; |
| } |
| else{ |
| value = w_length; |
| } |
| _pcd->buf = (volatile char*)&_configDescTotal; |
| _pcd->length = value; |
| /*printf("--get configuration descriptor: size %d, ret length :%d\n\n", w_length, value); */ |
| printf("Get DT cfg\n"); |
| |
| } |
| break; |
| |
| case USB_DT_STRING: |
| { |
| const char* str = NULL; |
| |
| /*DBG("--get string Desc: id %d\n", w_value & 0xff);*/ |
| printf("Get str %x\n", w_value); |
| switch (w_value & 0xff) |
| { |
| case 0: // IDs |
| /*usb_memcpy(buff,(void*)dt_string_id,DT_STRING_ID_LEN);*/ |
| str = dt_string_id; |
| break; |
| |
| case 1: // STRING_MANUFACTURER |
| /*usb_memcpy(buff,(void*)dt_string_vid,DT_STRING_VID_LEN);*/ |
| str = dt_string_vid; |
| break; |
| |
| case 2://STRING_PRODUCT |
| /*usb_memcpy(buff,(void*)dt_string_pid,DT_STRING_PID_LEN);*/ |
| str = dt_string_pid; |
| break; |
| |
| case 3://STRING_SERIAL |
| /*usb_memcpy(buff,(void*)dt_string_serial,DT_STRING_SERIAL_LEN);*/ |
| str = dt_string_serial; |
| break; |
| |
| default: |
| USB_ERR("Error string id!\n"); |
| /*buff[0] = 0;*/ |
| str = NULL; |
| break; |
| } |
| _pcd->buf = (char*)str; |
| _pcd->length = str[0]; |
| /*printf("--get str DESC: id %d ,return length %d\n", (w_value & 0xff), _pcd->length);*/ |
| } |
| break; |
| |
| } |
| |
| } |
| break; |
| |
| case USB_REQ_SET_CONFIGURATION: |
| { |
| if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | |
| USB_RECIP_DEVICE)) |
| break; |
| printf("set CFG\n"); |
| _pcd->buf = 0; |
| _pcd->length = 0; |
| _pcd->request_config = 1; /* Configuration changed */ |
| need_check_timeout = 0; |
| if (OPTIMUS_WORK_MODE_USB_UPDATE == optimus_work_mode_get()) {//not booting from usb |
| if (getenv(_ENV_TIME_OUT_TO_AUTO_BURN))_auto_burn_time_out_base = get_timer(0) ; |
| } |
| } |
| break; |
| |
| case USB_REQ_GET_CONFIGURATION: |
| if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) |
| break; |
| printf("get CFG\n"); |
| buff[0] = 1; |
| _pcd->buf = buff; |
| _pcd->length = 1; |
| break; |
| |
| default: |
| USB_ERR("--un handled standard req %02x.%02x v%04x i%04x l%u\n", |
| ctrl->bRequestType, ctrl->bRequest, w_value, w_index, w_length); |
| } |
| |
| w_index = 0; //remove compile warning |
| return ; |
| } |
| |
| /** |
| * This functions delegates vendor ctrl request. |
| */ |
| void do_vendor_request( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl) |
| { |
| unsigned long value =0; |
| u16 w_index = ctrl->wIndex; |
| u16 w_value = ctrl->wValue; |
| u16 w_length = ctrl->wLength; |
| |
| usb_set_reply_cmd_id(0);//clear reply |
| switch (ctrl->bRequest) |
| { |
| case AM_REQ_WRITE_MEM: |
| if (ctrl->bRequestType != |
| (USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE)) |
| break; |
| USB_DBG("--am req write memory\n"); |
| value = (w_value << 16) + w_index; |
| USB_DBG("addr = 0x%08X, size = %d\n\n",value,w_length); |
| _pcd->buf = (char *)value; // copy to dst memory directly |
| _pcd->length = w_length; |
| break; |
| |
| case AM_REQ_READ_MEM: |
| if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| uint64_t memAddr = w_value;//w_value is short 16, cannot left shit!!! |
| memAddr <<= 16; |
| memAddr += w_index; |
| /*printf("Copy from 0x%llx to %p at len %d\n", memAddr, buff, w_length);*/ |
| memcpy((void*)buff,(char*)memAddr,w_length); |
| |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| break; |
| |
| case AM_REQ_READ_AUX: |
| if (ctrl->bRequestType != (USB_DIR_IN | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| /*unsigned int data = 0;*/ |
| value = (w_value << 16) + w_index; |
| |
| //data = _lr(value); |
| /**(unsigned int *)(unsigned)buff = data;*/ |
| memset((char*)buff, 0, sizeof(unsigned)); |
| |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| break; |
| |
| case AM_REQ_FILL_MEM: |
| case AM_REQ_WRITE_AUX: |
| case AM_REQ_MODIFY_MEM: |
| if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| break; |
| |
| case AM_REQ_RUN_IN_ADDR: |
| if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| value = (w_value << 16) + w_index; |
| USB_DBG("--am req run in addr %p\n\n",value); |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| break; |
| |
| case AM_REQ_WR_LARGE_MEM: |
| value = 1; |
| case AM_REQ_RD_LARGE_MEM: |
| USB_DBG("--am req large %s mem \n\n",value?"write":"read"); |
| |
| _pcd->bulk_len = w_value; // block length |
| _pcd->bulk_num = w_index; // number of block |
| _pcd->buf = buff; //EP0 command data buffer |
| _pcd->length = w_length; //EP0 command data length |
| /*FIXME: cann't delay when writelargeMem, or it will fail??*/ |
| /* |
| *printf("f(%s)L%d: bulk_len=0x%x, bulk_num=%d, bufAddr=0x%x, len=0x%x\n", |
| * __func__, __LINE__, _pcd->bulk_len, _pcd->bulk_num, _pcd->buf, _pcd->length); |
| */ |
| break; |
| |
| case AM_REQ_DOWNLOAD: |
| { |
| value = 1;//is out |
| _pcd->bulk_len = w_value;//block length |
| _pcd->bulk_num = w_index;//how many times to transfer |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| if (65535 == w_index && 0x20 == w_length) |
| usb_set_reply_cmd_id(AM_REQ_DOWNLOAD); |
| } |
| break; |
| |
| case AM_REQ_UPLOAD: |
| { |
| _pcd->buf = _resultInfo; |
| _pcd->length = w_length;//assert that lenght == 16 ???? |
| |
| optimus_buf_manager_get_command_data_for_upload_transfer((u8*)_resultInfo, w_length); |
| } |
| break; |
| |
| |
| case AM_REQ_IDENTIFY_HOST: |
| { |
| static const char id[4] = |
| { |
| [0] = USB_ROM_VER_MAJOR, |
| [1] = USB_ROM_VER_MINOR, |
| [2] = USB_ROM_STAGE_MAJOR, |
| [3] = USB_ROM_STAGE_MINOR, |
| }; |
| |
| _pcd->buf = (char*)id; |
| _pcd->length = w_length;//FIXME:asset w_length == 4 ?? |
| _auto_burn_time_out_base = 0;//get burning tool identify command, so clear the timeout flag |
| printf("\nID[%d]\n", _pcd->buf[3]); |
| } |
| break; |
| |
| case AM_REQ_BULKCMD://Added by Sam |
| { |
| _pcd->bulk_len = w_value; // block length |
| _pcd->bulk_num = w_index; // number of block |
| _pcd->buf = buff; //EP0 command data buffer, out buffer after set_up command |
| _pcd->length = w_length; //EP0 command data length |
| /*printf("blkc:len %d\n", _pcd->length);*/ |
| _pcd->cmdtype.setup_complete = 1; |
| if (2 == w_index) usb_set_reply_cmd_id(AM_REQ_BULKCMD) ; |
| } |
| break; |
| |
| case AM_REQ_TPL_CMD: |
| { |
| _pcd->buf = buff; |
| _pcd->length = w_length; |
| } |
| break; |
| |
| case AM_REQ_TPL_STAT: |
| { |
| _pcd->buf = _resultInfo; |
| _pcd->length = w_length; |
| } |
| break; |
| |
| default: |
| USB_ERR("--unknown vendor req %02x.%02x v%04x i%04x l%u\n", |
| ctrl->bRequestType, ctrl->bRequest, |
| w_value, w_index, w_length); |
| break; |
| } |
| |
| return; |
| } |
| |
| /* |
| * This function will be called after a whole SETUP-OUT-IN transfer. |
| */ |
| void do_vendor_out_complete( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl) |
| { |
| unsigned long value = 0; |
| u16 w_index = ctrl->wIndex; |
| u16 w_value = ctrl->wValue; |
| u16 w_length = ctrl->wLength; |
| void (*fp)(void); |
| volatile char * buf; |
| |
| //USB_DBG("do_vendor_out_complete(0x%x)\n", ctrl->bRequest); |
| switch (ctrl->bRequest) |
| { |
| case AM_REQ_WRITE_MEM: |
| if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| USB_DBG("--am req write memory completed\n\n"); |
| break; |
| |
| case AM_REQ_FILL_MEM: |
| buf = (char*)_pcd->buf; |
| unsigned long addr,i; |
| for (i = 0; i < _pcd->length; i+=8) { |
| addr = *((unsigned int *)&buf[i]) ; |
| value = *((unsigned int *)&buf[i+4]) ; |
| *(unsigned int*)addr = value; |
| } |
| break; |
| |
| case AM_REQ_WRITE_AUX: |
| buf = _pcd->buf; |
| unsigned int data =0; |
| |
| data = *((unsigned int *)&buf[0]) ; //reg value |
| value = (w_value << 16) + w_index; //aux reg |
| |
| dwc_write_reg32(value, data); |
| break; |
| |
| case AM_REQ_MODIFY_MEM: |
| do_modify_memory(w_value,(char*)_pcd->buf); |
| break; |
| |
| case AM_REQ_RUN_IN_ADDR: |
| if (ctrl->bRequestType != (USB_DIR_OUT | USB_TYPE_VENDOR | |
| USB_RECIP_DEVICE)) |
| break; |
| value = (w_value << 16) + w_index; |
| USB_DBG("run addr = 0x%08X\n",value); |
| fp = (void(*)(void))value; |
| dwc_otg_power_off_phy(); |
| fp(); |
| break; |
| |
| case AM_REQ_WR_LARGE_MEM: |
| value = 1; // is_out = 1 |
| case AM_REQ_RD_LARGE_MEM: |
| { |
| const unsigned* intBuf = (unsigned*)buff; |
| _pcd->bulk_out = (char)value; // read or write |
| _pcd->bulk_buf = (char*)(u64)intBuf[0]; |
| _pcd->bulk_data_len = intBuf[1]; // data length |
| |
| //added by Sam.Wu |
| _pcd->bulk_xfer_len = 0; |
| _pcd->xferNeedReply = 0; |
| |
| _pcd->bulk_len = DWC_BLK_LEN(_pcd->bulk_data_len); |
| _pcd->bulk_num = DWC_BLK_NUM(_pcd->bulk_data_len); |
| memcpy(&this_pcd[value ? BULK_OUT_EP_NUM : BULK_IN_EP_NUM], _pcd, sizeof(pcd_struct_t)); |
| start_bulk_transfer(_pcd); |
| } |
| break; |
| |
| case AM_REQ_DOWNLOAD: |
| value = 2;//2 to differ from write_large_mem |
| { |
| const unsigned* intBuf = (unsigned*)buff; |
| unsigned isPktResended = intBuf[0]; |
| unsigned sequenceNo = intBuf[2]; |
| _pcd->bulk_out = value; |
| _pcd->bulk_data_len = intBuf[1]; |
| _pcd->bulk_xfer_len = 0; |
| _pcd->origiSum = intBuf[3]; |
| _pcd->isPktResended = isPktResended; |
| if (0x20 == _pcd->length) {//new protocol |
| _pcd->dataChkSumAlg = intBuf[4] & 0xffff; |
| _pcd->xferAckLen = intBuf[4] >> 16; |
| } |
| else{ |
| _pcd->dataChkSumAlg = WRITE_MEDIA_CKC_ALG_ADDSUM; |
| _pcd->xferAckLen = AM_BULK_REPLY_LEN; |
| } |
| |
| if (!isPktResended) //request next buffer slot only if transfer packet not re-sended packet |
| { |
| int ret = optimus_buf_manager_get_buf_for_bulk_transfer(&_pcd->bulk_buf, _pcd->bulk_data_len, sequenceNo, NULL); |
| if (ret) _pcd->bulk_data_len = _pcd->bulk_len = _pcd->bulk_num = 0; |
| } |
| |
| _pcd->sequenceNo = sequenceNo; |
| |
| _pcd->bulk_len = DWC_BLK_LEN(_pcd->bulk_data_len); |
| _pcd->bulk_num = DWC_BLK_NUM(_pcd->bulk_data_len); |
| _pcd->xferNeedReply= 1;/////// |
| memcpy(&this_pcd[BULK_OUT_EP_NUM], _pcd, sizeof(pcd_struct_t)); |
| start_bulk_transfer(_pcd); |
| } |
| break; |
| |
| case AM_REQ_TPL_CMD: |
| /* this is an example for any command */ |
| if (!w_index) {/* assume subcode is 0 */ |
| } |
| else if(w_index == 1){ |
| char cmd[CMD_BUFF_SIZE]; |
| memcpy(cmd, (void*)buff, CMD_BUFF_SIZE); |
| if (strncmp(cmd,"bootm",(sizeof("bootm")-1)) == 0) { |
| dwc_otg_pullup(0);//disconnect |
| } |
| printf("tplcmd[%s]\n", cmd); |
| optimus_working(cmd, _resultInfo); |
| } |
| break; |
| |
| case AM_REQ_BULKCMD: |
| { |
| memcpy(&this_pcd[BULK_IN_EP_NUM], _pcd, sizeof(pcd_struct_t)); |
| do_bulk_cmd((char*)buff); |
| } |
| break; |
| |
| default: |
| USB_ERR("--unknown vendor req comp %02x.%02x v%04x i%04x l%u\n", |
| ctrl->bRequestType, ctrl->bRequest, |
| w_value, w_index, w_length); |
| } |
| w_length = 0;//remove compile warning |
| return; |
| } |
| |
| /* |
| * This function will be called after a whole SETUP-IN-OUT transfer. |
| */ |
| void do_vendor_in_complete( pcd_struct_t *_pcd, struct usb_ctrlrequest * ctrl) |
| { |
| u16 w_index = ctrl->wIndex; |
| u16 w_value = ctrl->wValue; |
| u16 w_length = ctrl->wLength; |
| |
| //USB_DBG("do_vendor_out_complete(0x%x)\n", ctrl->bRequest); |
| switch (ctrl->bRequest) |
| { |
| case AM_REQ_IDENTIFY_HOST: |
| case AM_REQ_TPL_STAT: |
| case AM_REQ_READ_MEM: |
| break; |
| |
| case USB_REQ_GET_DESCRIPTOR: |
| break; |
| |
| case AM_REQ_UPLOAD: |
| { |
| //command foramt: [4-7]dataLen, other don't care now |
| const u8* cmdData = (const u8*)_resultInfo; |
| const u32 sequenceNo = *(unsigned*)(cmdData + 8); |
| const u32 leftDataLen = *(unsigned*)(cmdData + 4); |
| int ret = 0; |
| |
| _pcd->bulk_out = 0; |
| _pcd->bulk_xfer_len = 0; |
| _pcd->bulk_data_len = leftDataLen; |
| |
| _pcd->bulk_len = DWC_BLK_LEN(leftDataLen); |
| _pcd->bulk_num = DWC_BLK_NUM(leftDataLen); |
| |
| ret = optimus_buf_manager_get_buf_for_bulk_transfer(&_pcd->bulk_buf, _pcd->bulk_data_len, sequenceNo, _resultInfo); |
| if (ret) _pcd->bulk_data_len = _pcd->bulk_len = _pcd->bulk_num = 0;//respond 0 byte data to stop transfer |
| |
| DWN_DBG("up 0x:len %x, buf %p, bulk_num %d\n", _pcd->bulk_data_len, _pcd->bulk_buf, _pcd->bulk_num); |
| _pcd->xferNeedReply= 1;/////// |
| memcpy(&this_pcd[BULK_IN_EP_NUM], _pcd, sizeof(pcd_struct_t)); |
| start_bulk_transfer(_pcd); |
| } |
| break; |
| |
| default: |
| USB_ERR("--unknown vendor req comp %02x.%02x v%04x i%04x l%u\n", |
| ctrl->bRequestType, ctrl->bRequest, |
| w_value, w_index, w_length); |
| } |
| w_length = 0;//remove compile warning |
| return; |
| } |
| |
| |
| int bulk_transfer_reply(const pcd_struct_t* _pcd) |
| { |
| static int _mediaErr = 0; |
| char* replyBuf = _resultInfo; |
| const void* data = _pcd->bulk_buf; |
| const unsigned len = _pcd->bulk_xfer_len; |
| const unsigned sequenceNo = _pcd->sequenceNo; |
| int isOut = !_pcd->bulk_out;//handshake direction is opposite |
| const u32 origiSum = _pcd->origiSum; |
| unsigned genSum = 0; |
| int ret = 0; |
| const int isPktResended = _pcd->isPktResended; |
| |
| dwc_otg_bulk_ep_enable(!isOut);//enable the bulk in ep first before optimus_working!! |
| |
| if(0 == _pcd->sequenceNo |
| && !isPktResended)//media error can't resolved by reseneded, only memory(dram) error can |
| { |
| _mediaErr = 0;//set error none if download a new file |
| } |
| |
| if (_mediaErr) { |
| sprintf(replyBuf, "media error %d at seq %d, resend %d\n", _mediaErr, sequenceNo, isPktResended); |
| DWN_ERR(replyBuf); |
| ret = __LINE__; |
| } |
| else |
| {//check sum for usb ---> mem |
| const int checkSumAlg = _pcd->dataChkSumAlg; |
| if (WRITE_MEDIA_CKC_ALG_ADDSUM == checkSumAlg) |
| { |
| genSum = add_sum(data, len); |
| } |
| if ( origiSum != genSum && WRITE_MEDIA_CKC_ALG_NONE != checkSumAlg) |
| { |
| sprintf(replyBuf, "Alg[%x]0x: genSum %x != org %x at seq %x, len %x, resend %d\n", |
| checkSumAlg, genSum, origiSum, sequenceNo, len, isPktResended); |
| DWN_ERR(replyBuf); |
| ret = __LINE__; |
| } |
| } |
| |
| if (!ret) //add-sum checked ok and no media error |
| { |
| _mediaErr = optimus_buf_manager_report_transfer_complete(_pcd->bulk_xfer_len, replyBuf); |
| if (_mediaErr) { |
| sprintf(replyBuf, "media error %d at seq %d\n", _mediaErr, sequenceNo); |
| DWN_ERR(replyBuf); |
| ret = __LINE__; |
| } |
| } |
| |
| if (!ret) |
| { |
| memcpy(replyBuf, "OK!!", 4); |
| } |
| |
| //PCD fields needed for reply |
| //see implementation of dwc_otg_ep_req_start |
| pcd_struct_t* reply_pcd = &this_pcd[isOut ? BULK_OUT_EP_NUM : BULK_IN_EP_NUM]; |
| memcpy(reply_pcd, _pcd, sizeof(pcd_struct_t)); |
| reply_pcd->bulk_data_len = _pcd->xferAckLen;//total length <= 4k |
| reply_pcd->bulk_len = DWC_BLK_LEN(reply_pcd->bulk_data_len);//total length <= 4k |
| reply_pcd->bulk_num = DWC_BLK_NUM(reply_pcd->bulk_data_len); |
| reply_pcd->bulk_buf = replyBuf; |
| reply_pcd->bulk_xfer_len = 0; |
| reply_pcd->bulk_out = isOut; |
| |
| DWN_DBG("Down replyBuf %s\n", replyBuf); |
| dwc_otg_ep_req_start(reply_pcd, isOut?BULK_OUT_EP_NUM:BULK_IN_EP_NUM); |
| |
| return ret; |
| } |
| |
| /* |
| * This function will be called after a whole bulk out transfer. |
| * Attention That All bulk transfer will reach here after completed!! |
| */ |
| void do_bulk_complete( pcd_struct_t *_pcd) |
| { |
| const int bRequest = _pcd->setup_pkt.req.bRequest; |
| _pcd->bulk_lock = 0; |
| _pcd->bulk_num--; |
| /*_pcd->bulk_data_len -= _pcd->xfer_len;*/ |
| _pcd->bulk_xfer_len += _pcd->xfer_len; |
| const int leftDataLen = _pcd->bulk_data_len - _pcd->bulk_xfer_len; |
| |
| //Transfer NEXT block (at most 4K) |
| switch (bRequest) |
| { |
| case AM_REQ_DOWNLOAD : |
| { |
| //called after xferNeedReply |
| if (!_pcd->xferNeedReply) return; |
| } |
| case AM_REQ_UPLOAD : |
| case AM_REQ_WR_LARGE_MEM: |
| case AM_REQ_RD_LARGE_MEM: |
| case AM_REQ_BULKCMD://Need start transfer more than once when full-speed mode |
| { |
| /*if ( !_pcd->bulk_out)printf("leftDataLen 0x%x\n", leftDataLen);*/ |
| if (leftDataLen) //if earlier packet length is 0, no next xfer here!! |
| { |
| if (leftDataLen < 0) { |
| DWN_WRN("total 0x%x < tranferred 0x%x\n", _pcd->bulk_data_len, _pcd->bulk_xfer_len); |
| return ; |
| } |
| _pcd->bulk_len = DWC_BLK_LEN(leftDataLen); |
| start_bulk_transfer(_pcd); |
| |
| return;///////////////// |
| } |
| } |
| break; |
| default: |
| DWN_WRN("Unhandled bulk bRequest 0x%x, leftDataLen %d\n", bRequest, leftDataLen); |
| DWN_WRN("bulk_xfer_len=%d, bulk_data_len=%d\n", _pcd->bulk_xfer_len, _pcd->bulk_data_len); |
| } |
| |
| //update states if all blocks (at most 64k) transferred end |
| if (_pcd->xferNeedReply && 0 == leftDataLen) //get download command and is out |
| { |
| _pcd->xferNeedReply = 0;//// |
| |
| switch (bRequest) |
| { |
| case AM_REQ_DOWNLOAD : |
| bulk_transfer_reply(_pcd); |
| break; |
| case AM_REQ_UPLOAD : |
| optimus_buf_manager_report_transfer_complete(_pcd->bulk_xfer_len, NULL); |
| break; |
| |
| case AM_REQ_BULKCMD://Need start transfer more than once when full-speed mode |
| break; |
| default: |
| DWN_WRN("Unhandled xferNeedReply for bulk bRequest 0x%x\n", bRequest); |
| } |
| } |
| |
| return; |
| } |
| |
| |
| static int bulk_cmd_reply(const char* replyBuf) |
| { |
| static char _bulk_replyBuf[AM_BULK_REPLY_LEN]; |
| memset(_bulk_replyBuf, 0, AM_BULK_REPLY_LEN); |
| memcpy(_bulk_replyBuf, replyBuf, strnlen(replyBuf, AM_BULK_REPLY_LEN - 1)); |
| |
| pcd_struct_t* pcd = &this_pcd[BULK_IN_EP_NUM]; |
| //PCD fields needed for reply |
| //see implementation of dwc_otg_ep_req_start |
| pcd->bulk_data_len = AM_BULK_REPLY_LEN;/// |
| pcd->bulk_len = DWC_BLK_LEN(pcd->bulk_data_len); |
| pcd->bulk_num = DWC_BLK_NUM(pcd->bulk_data_len); |
| pcd->bulk_xfer_len = 0; |
| pcd->bulk_buf = _bulk_replyBuf; |
| pcd->bulk_out = 0;////////////////////////////// |
| DWN_DBG("reply..[%s]\n", _bulk_replyBuf); |
| dwc_otg_ep_req_start(pcd, BULK_IN_EP_NUM);//bulk in to reply result, it is ALWAYS IN |
| |
| return 0; |
| } |
| |
| static int do_bulk_cmd(char* cmd) |
| { |
| int ret = 0; |
| const int is_in = 1; |
| |
| ret = dwc_otg_bulk_ep_enable(is_in);//enable the bulk in ep first before optimus_working!! |
| |
| printf("BULKcmd[%s]\n", cmd); |
| ret = optimus_working(cmd, _resultInfo); |
| |
| ret = bulk_cmd_reply(_resultInfo); |
| |
| return ret; |
| } |
| |
| void do_modify_memory(u16 opcode, char *inbuff) |
| { |
| unsigned int *mem,*mem2; |
| unsigned int data,mask; |
| |
| mem = *(unsigned int**)&inbuff[0]; |
| data = *(unsigned int*)&inbuff[4]; |
| mask = *(unsigned int*)&inbuff[8]; |
| mem2= *(unsigned int**)&inbuff[12]; |
| |
| switch (opcode) { |
| case 0: //*mem = data |
| *mem = data; |
| break; |
| |
| case 1:// *mem = (data & mask) |
| *mem = data & mask; |
| break; |
| |
| case 2:// *mem =(*mem | mask) |
| *mem = *mem | mask; |
| break; |
| |
| case 3:// *mem = (data & (~mask)) |
| *mem = (data & (~mask)); |
| break; |
| |
| case 4:// *mem = (data & mask) |(*mem & ~mask) |
| *mem = (data & mask) |(*mem & ~mask); |
| break; |
| |
| case 5:// *mem = *mem2 |
| *mem = *mem2; |
| break; |
| |
| case 6:// *mem = (*mem2 & mask) |
| *mem = (*mem2 & mask); |
| break; |
| |
| case 7:// while(data--) {*mem++ = *mem2++} |
| while (data--) { |
| *mem++ = *mem2++; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| } |
| |
| static int _cbReplyCmdId = 0; |
| static unsigned _cbUnReportedSz = 0; |
| void usb_set_reply_cmd_id(const int cmdId) |
| { |
| _cbReplyCmdId = cmdId; |
| _cbUnReportedSz = 0; |
| } |
| |
| extern int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(void); |
| static int cb_4_bulk_in_reply(const char* replyInf) |
| { |
| gintsts_data_t gintr_status; |
| gintr_status.d32 = dwc_read_reg32(DWC_REG_GINTSTS); |
| if (!gintr_status.b.nptxfempty) return 0; |
| if (gintr_status.b.intokenrx) DWN_MSG("intokenrx\n") ;//FIXME:try wait pc in-nak, must never catch it! |
| |
| bulk_cmd_reply(replyInf); |
| dwc_otg_pcd_handle_np_tx_fifo_empty_intr(); |
| |
| #if 0 |
| //clear 'xfercompl' intr as not needed to handle for bulk_cmd_reply |
| diepint_data_t diepint = { 0 }; |
| diepint.d32 = dwc_read_reg32( DWC_REG_IN_EP_INTR(BULK_IN_EP_NUM)) & dwc_read_reg32(DWC_REG_DAINTMSK); |
| /*if(diepint.b.xfercompl)DWN_MSG("xfercompl\n");*/ |
| diepint.b.xfercompl = 1; |
| dwc_write_reg32(DWC_REG_IN_EP_INTR(BULK_IN_EP_NUM), diepint.d32); |
| #endif |
| return 0; |
| } |
| |
| static int cb_usb_bulk_in_reply_busy(void) |
| { |
| switch (_cbReplyCmdId) |
| { |
| case AM_REQ_DOWNLOAD: |
| return cb_4_bulk_in_reply("Continue:32"); |
| case AM_REQ_BULKCMD: |
| return cb_4_bulk_in_reply("Continue:34"); |
| default: |
| return 0; |
| } |
| return 0; |
| } |
| |
| //1, add @_timeElaspedBeforeLastReport to remember elapsed time, to avoid depend on long period timer, |
| // i.e, assume that timer is short cycle, such as only 5 seconds a cycle |
| //2, |
| int platform_busy_increase_un_reported_size(const unsigned nBytes) |
| { |
| static unsigned long _lastReportTick = 0; |
| static unsigned _timeElaspedBeforeLastReport = 0; |
| const int ReportTimePeriod = 16 * 1000;//16 seconds |
| //Add @ReportMinLen to 'avoid report busy when data ended', or it blocked in dwc_irq_pcd.c |
| const int ReportMinLen = OPTIMUS_SHA1SUM_BUFFER_LEN;//8M |
| |
| if (!_cbReplyCmdId) return 0; |
| |
| if (!_cbUnReportedSz) { |
| _lastReportTick = get_timer(0);//start time re-init |
| _timeElaspedBeforeLastReport = 0; |
| } |
| unsigned long timePeriod = get_timer(_lastReportTick); |
| |
| _cbUnReportedSz += nBytes; |
| //Maybe timer is not long enough cycle, so add another variable to remember the elapsed period |
| if (timePeriod > 2000) { |
| _timeElaspedBeforeLastReport += timePeriod; |
| _lastReportTick += timePeriod; |
| } |
| DWN_DBG("_timeElaspedBeforeLastReport=%u, _unReportedLen=%u\n", _timeElaspedBeforeLastReport, _cbUnReportedSz); |
| if (_timeElaspedBeforeLastReport < ReportTimePeriod || _cbUnReportedSz < ReportMinLen) return 0; |
| |
| _timeElaspedBeforeLastReport = 0; |
| _cbUnReportedSz = 0; |
| DWN_DBG("_lastReportTick=%lu\n", _lastReportTick); |
| return cb_usb_bulk_in_reply_busy(); |
| } |
| |