static int _complete(dwc_otg_pcd_t *pcd, void *ep_handle, void *req_handle, int32_t status, uint32_t actual) { struct out_request *oreq = (struct out_request *)req_handle; if (ep_handle != NULL) { static_data->usb_ep_complete((int)ep_handle, req_handle, status, actual); return 0; } __DWC_ERROR("%s(): enter \n", __func__); if (oreq == NULL) { return 0; } if (status != 0) { __DWC_ERROR("%s(): status not null: %d \n", __func__, status); return 0; } if (static_data->usb_class_handler(&oreq->out_req, &oreq->data.length, &oreq->data.buf) != 0) { __DWC_ERROR("%s(): Error while processing packet\n", __func__); } return 0; }
/* Allocates and init the pcd */ static int pcd_init(dwc_otg_device_t *otg_dev) { int retval = 0; otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if, &static_data->static_pcd); if (!otg_dev->pcd) { __DWC_ERROR("dwc_otg_pcd_init failed\n"); return -DWC_E_NO_MEMORY; } dwc_otg_pcd_start(otg_dev->pcd, &fops); return retval; }
/* Basic sanity check of the descriptor * Copy the provided descriptor locally*/ static int init_descriptors(usb_device_descriptor_t * dev_desc, usb_config_descriptor_t * conf_desc, uint32_t csize, usb_string_descriptor_t *strings, int num_strings) { static_data->usb_device_descriptor = (usb_device_descriptor_t *) dev_desc; if (static_data->usb_device_descriptor->bNumConfigurations != 1) { __DWC_ERROR("Only one USB config is supported\n"); return -DWC_E_INVALID; } static_data->usb_config_descriptor = (usb_config_descriptor_t *) conf_desc; static_data->config_descriptor_size = csize; static_data->strings_desc = strings; static_data->usb_strings_count = num_strings; return 0; }
/* * This function is called when an lm_device is bound to a * dwc_otg_driver. It creates the driver components required to * control the device (CIL, HCD, and PCD) and it initializes the * device. The driver components are stored in a dwc_otg_device * structure. A reference to the dwc_otg_device is saved in the * lm_device. This allows the driver to access the dwc_otg_device * structure on subsequent calls to driver methods for this device. * */ int usb_driver_init(uint32_t base_addr) { int retval = 0; __DWC_WARN("dwc_otg_driver_init\n"); dwc_otg_device = &static_data->dwc_otg_device_inst; DWC_MEMSET(dwc_otg_device, 0, sizeof(*dwc_otg_device)); /* * Initialize driver data to point to the global DWC_otg * Device structure. */ __DWC_ERROR("dwc_otg_device addr = 0x%p\n", dwc_otg_device); dwc_otg_device->core_if = dwc_otg_cil_init(base_addr); if (!dwc_otg_device->core_if) { __DWC_ERROR("CIL initialization failed!\n"); retval = -DWC_E_NO_MEMORY; goto fail; } #ifdef USB_DEBUG /* * Attempt to ensure this device is really a DWC_otg Controller. * Read and verify the SNPSID register contents. The value should be * 0x45F42XXX or 0x45F42XXX, which corresponds to either "OT2" or "OTG3", * as in "OTG version 2.XX" or "OTG version 3.XX". */ if (((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F542000) && ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != 0x4F543000)) { __DWC_WARN("Bad value for SNPSID: 0x%x\n", dwc_otg_get_gsnpsid(dwc_otg_device->core_if)); retval = -DWC_E_INVALID; goto fail; } #endif /* USB_DEBUG */ /* * Disable the global interrupt until all the interrupt * handlers are installed. */ __DWC_WARN("USB INIT: Disabling global IRQ\n"); dwc_otg_disable_global_interrupts(dwc_otg_device->core_if); /* * Initialize the DWC_otg core. */ __DWC_WARN("USB INIT: Initializing OTG core\n"); dwc_otg_core_init(dwc_otg_device->core_if); /* * Initialize the PCD */ __DWC_WARN("USB INIT: Initializing PCD\n"); retval = pcd_init(dwc_otg_device); if (retval != 0) { __DWC_ERROR("pcd_init failed\n"); dwc_otg_device->pcd = NULL; goto fail; } __DWC_WARN("USB INIT: Enabling global IRQ\n"); dwc_otg_enable_global_interrupts(dwc_otg_device->core_if); __DWC_WARN("DWC core initialized! \n"); return 0; fail: __DWC_ERROR("ERROR! unable to init USB driver\n"); return retval; }
static int _setup(dwc_otg_pcd_t *pcd, uint8_t *bytes) { DWC_DEBUGPL(DBG_PCD, "%s(): enter \n", __func__); const usb_device_request_t *ctrlReq = (usb_device_request_t *)bytes; int value = 0; int idx; usb_device_request_t ctrlConvertedReq; usb_device_request_t *ctrl = &ctrlConvertedReq; ctrl->bmRequestType = (uByte)(ctrlReq->bmRequestType); ctrl->bRequest = (uByte)(ctrlReq->bRequest); ctrl->wValue[0] = ctrlReq->wValue[0]; ctrl->wValue[1] = ctrlReq->wValue[1]; ctrl->wIndex[0] = ctrlReq->wIndex[0]; ctrl->wIndex[1] = ctrlReq->wIndex[1]; ctrl->wLength[0] = ctrlReq->wLength[0]; ctrl->wLength[1] = ctrlReq->wLength[1]; /* * usually this stores reply data in the pre-allocated ep0 buffer, * but config change events will reconfigure hardware. */ static_data->dl_req.data.zero = 0; if (UT_GET_RECIPIENT(ctrl->bmRequestType) == UT_INTERFACE) { if ((UT_GET_DIR(ctrl->bmRequestType) == UT_WRITE) && (UGETW(ctrl->wLength) > 0)) { static_data->dl_req.data.buf = &static_data->ep0_buffer[0]; DWC_MEMCPY(&static_data->dl_req.out_req, ctrl, sizeof(*ctrl)); static_data->dl_req.data.length = DWC_MIN(UGETW( ctrl->wLength), static_data->ep0_buffer_size); // either shorter than asked, or multiple of the MPS require ZLP. static_data->dl_req.data.zero = ((value < UGETW(ctrl->wLength)) && ((value % pcd->ep0.dwc_ep. maxpacket) == 0)) ? 1 : 0; // Elaborate when getting the time. dwc_otg_pcd_ep_queue( pcd, /*void* ep_hanlde */ NULL, static_data->dl_req.data.buf, (dwc_dma_t)static_data->dl_req. data.buf, static_data->dl_req.data.length, static_data->dl_req.data.zero, /*void* req_handle */ &static_data ->dl_req, 0); } else { static_data->dl_req.data.buf = &static_data->ep0_buffer[0]; if (static_data->usb_class_handler(ctrl, &value, &static_data->dl_req .data.buf) != 0) { value = -DWC_E_NOT_SUPPORTED; } } } else { switch (ctrl->bRequest) { case UR_GET_DESCRIPTOR: __DWC_ERROR("ctrl->Length = %d \n", UGETW(ctrl->wLength)); switch (UGETW(ctrl->wValue) >> 8) { case UDESC_DEVICE: value = DWC_MIN(UGETW(ctrl->wLength), USB_DEVICE_DESCRIPTOR_SIZE); static_data->dl_req.data.buf = (uint8_t *) static_data-> usb_device_descriptor; break; case UDESC_CONFIG: value = DWC_MIN(UGETW(ctrl->wLength), UGETW(static_data-> usb_config_descriptor-> wTotalLength)); static_data->dl_req.data.buf = (uint8_t *) static_data-> usb_config_descriptor; break; case UDESC_OTHER_SPEED_CONFIGURATION: __DWC_ERROR ( "UDESC_OTHER_SPEED_CONFIGURATION NOT IMPLEMENTED! \n"); break; case UDESC_STRING: /* wIndex == language code. * this driver only handles one language, you can * add string tables for other languages, using * any UTF-8 characters */ idx = UGETW(ctrl->wValue) & 0xFF; __DWC_ERROR("UDESC_STRING idx = %d \n", idx); __DWC_ERROR( "UDESC_STRING bLength = %d \n", &static_data->strings_desc[idx]. bLength); if (idx > static_data->usb_strings_count) { if (idx == 0xEE) { // Microsoft MTP extension value = 0; } else { value = -DWC_E_INVALID; } } else { value = DWC_MIN(UGETW( ctrl->wLength), static_data-> strings_desc[idx]. bLength); static_data->dl_req.data.buf = (uint8_t *) & static_data->strings_desc[idx]; } break; } break; case UR_SET_CONFIG: static_data->usb_config = UGETW(ctrl->wValue); value = 0; { int i; for (i = 0; i < static_data->usb_num_endpoints; i++) { int ret = dwc_otg_pcd_ep_enable( pcd, (const uint8_t *)&static_data->usb_endpoints[ i], static_data ->usb_endpoints[i]. bEndpointAddress); __DWC_WARN( "Enabled endpoint: %x ret: %d\n", static_data->usb_endpoints[i ].bEndpointAddress, ret); } struct usb_event evt; evt.event = USB_EVENT_SET_CONFIG; evt.event_data.config = static_data->usb_config; if (static_data->usb_event_cb) { static_data->usb_event_cb(&evt); } } break; case UR_GET_CONFIG: static_data->dl_req.data.buf = &static_data->usb_config; value = DWC_MIN(UGETW(ctrl->wLength), 1); break; /* until we add altsetting support, or other interfaces, * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) * and already killed pending endpoint I/O. */ case UR_SET_INTERFACE: value = 0; break; case UR_GET_INTERFACE: __DWC_ERROR("UR_GET_INTERFACE NOT IMPLEMENTED! \n"); goto unknown; break; default: unknown: value = -DWC_E_NOT_SUPPORTED; break; } } /* respond with data transfer before status phase? */ __DWC_ERROR("%s(): req.length = %d - max length is 64 \n", __func__, value); if (value >= 0) { static_data->dl_req.data.length = value; // either shorter than asked, or multiple of the MPS require ZLP. static_data->dl_req.data.zero = ((value < UGETW(ctrl->wLength)) && ((value % pcd->ep0.dwc_ep.maxpacket) == 0)) ? 1 : 0; // Elaborate when getting the time. dwc_otg_pcd_ep_queue(pcd, /*void* ep_hanlde */ NULL, static_data->dl_req.data.buf, (dwc_dma_t)static_data->dl_req.data.buf, static_data->dl_req.data.length, static_data->dl_req.data.zero, /*void* req_handle */ NULL, 0); } /* device either stalls (value < 0) or reports success */ return value; }
/** * This function handles the OTG Interrupts. It reads the OTG * Interrupt Register (GOTGINT) to determine what interrupt has * occurred. * * @param core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if) { dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; gotgint_data_t gotgint; gotgctl_data_t gotgctl; gintmsk_data_t gintmsk; gpwrdn_data_t gpwrdn; gotgint.d32 = DWC_READ_REG32(&global_regs->gotgint); gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32, op_state_str(core_if)); if (gotgint.b.sesenddet) { DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Session End Detected++ (%s)\n", op_state_str(core_if)); gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); if (core_if->op_state == B_HOST) { cil_pcd_start(core_if); core_if->op_state = B_PERIPHERAL; } else { /* If not B_HOST and Device HNP still set. HNP * Did not succeed!*/ if (gotgctl.b.devhnpen) { DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); __DWC_ERROR("Device Not Connected/Responding!\n"); } /* If Session End Detected the B-Cable has * been disconnected. */ /* Reset PCD and Gadget driver to a * clean state. */ core_if->lx_state = DWC_OTG_L0; DWC_SPINUNLOCK(core_if->lock); cil_pcd_stop(core_if); DWC_SPINLOCK(core_if->lock); if (core_if->adp_enable) { if (core_if->power_down == 2) { gpwrdn.d32 = 0; gpwrdn.b.pwrdnswtch = 1; DWC_MODIFY_REG32(&core_if-> core_global_regs-> gpwrdn, gpwrdn.d32, 0); } gpwrdn.d32 = 0; gpwrdn.b.pmuintsel = 1; gpwrdn.b.pmuactv = 1; DWC_MODIFY_REG32(&core_if->core_global_regs-> gpwrdn, 0, gpwrdn.d32); dwc_otg_adp_sense_start(core_if); } } gotgctl.d32 = 0; gotgctl.b.devhnpen = 1; DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); } if (gotgint.b.sesreqsucstschng) { DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Session Reqeust Success Status Change++\n"); gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); if (gotgctl.b.sesreqscs) { if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) { core_if->srp_success = 1; } else { DWC_SPINUNLOCK(core_if->lock); cil_pcd_resume(core_if); DWC_SPINLOCK(core_if->lock); /* Clear Session Request */ gotgctl.d32 = 0; gotgctl.b.sesreq = 1; DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); } } } if (gotgint.b.hstnegsucstschng) { /* Print statements during the HNP interrupt handling * can cause it to fail.*/ gotgctl.d32 = DWC_READ_REG32(&global_regs->gotgctl); /* WA for 3.00a- HW is not setting cur_mode, even sometimes * this does not help*/ if (core_if->snpsid >= OTG_CORE_REV_3_00a) dwc_udelay(100); if (gotgctl.b.hstnegscs) { if (dwc_otg_is_host_mode(core_if)) { core_if->op_state = B_HOST; /* * Need to disable SOF interrupt immediately. * When switching from device to host, the PCD * interrupt handler won't handle the * interrupt if host mode is already set. The * HCD interrupt handler won't get called if * the HCD state is HALT. This means that the * interrupt does not get handled and Linux * complains loudly. */ gintmsk.d32 = 0; gintmsk.b.sofintr = 1; DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0); /* Call callback function with spin lock released */ DWC_SPINUNLOCK(core_if->lock); cil_pcd_stop(core_if); /* * Initialize the Core for Host mode. */ cil_hcd_start(core_if); DWC_SPINLOCK(core_if->lock); core_if->op_state = B_HOST; } } else { gotgctl.d32 = 0; gotgctl.b.hnpreq = 1; gotgctl.b.devhnpen = 1; DWC_MODIFY_REG32(&global_regs->gotgctl, gotgctl.d32, 0); DWC_DEBUGPL(DBG_ANY, "HNP Failed\n"); __DWC_ERROR("Device Not Connected/Responding\n"); } } if (gotgint.b.hstnegdet) { /* The disconnect interrupt is set at the same time as * Host Negotiation Detected. During the mode * switch all interrupts are cleared so the disconnect * interrupt handler will not get executed. */ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Host Negotiation Detected++ (%s)\n", (dwc_otg_is_host_mode(core_if) ? "Host" : "Device")); if (dwc_otg_is_device_mode(core_if)) { DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", core_if->op_state); DWC_SPINUNLOCK(core_if->lock); cil_hcd_disconnect(core_if); cil_pcd_start(core_if); DWC_SPINLOCK(core_if->lock); core_if->op_state = A_PERIPHERAL; } else { /* * Need to disable SOF interrupt immediately. When * switching from device to host, the PCD interrupt * handler won't handle the interrupt if host mode is * already set. The HCD interrupt handler won't get * called if the HCD state is HALT. This means that * the interrupt does not get handled and Linux * complains loudly. */ gintmsk.d32 = 0; gintmsk.b.sofintr = 1; DWC_MODIFY_REG32(&global_regs->gintmsk, gintmsk.d32, 0); DWC_SPINUNLOCK(core_if->lock); cil_pcd_stop(core_if); cil_hcd_start(core_if); DWC_SPINLOCK(core_if->lock); core_if->op_state = A_HOST; } } if (gotgint.b.adevtoutchng) { DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "A-Device Timeout Change++\n"); } if (gotgint.b.debdone) { DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n"); } /* Clear GOTGINT */ DWC_WRITE_REG32(&core_if->core_global_regs->gotgint, gotgint.d32); return 1; }