/** * Create the device files */ void dwc_otg_attr_create (struct device *_dev) { int retval; retval = device_create_file(_dev, &dev_attr_regoffset); retval += device_create_file(_dev, &dev_attr_regvalue); retval += device_create_file(_dev, &dev_attr_mode); retval += device_create_file(_dev, &dev_attr_hnpcapable); retval += device_create_file(_dev, &dev_attr_srpcapable); retval += device_create_file(_dev, &dev_attr_hnp); retval += device_create_file(_dev, &dev_attr_srp); retval += device_create_file(_dev, &dev_attr_buspower); retval += device_create_file(_dev, &dev_attr_bussuspend); retval += device_create_file(_dev, &dev_attr_busconnected); retval += device_create_file(_dev, &dev_attr_gotgctl); retval += device_create_file(_dev, &dev_attr_gusbcfg); retval += device_create_file(_dev, &dev_attr_grxfsiz); retval += device_create_file(_dev, &dev_attr_gnptxfsiz); retval += device_create_file(_dev, &dev_attr_gpvndctl); retval += device_create_file(_dev, &dev_attr_ggpio); retval += device_create_file(_dev, &dev_attr_guid); retval += device_create_file(_dev, &dev_attr_gsnpsid); retval += device_create_file(_dev, &dev_attr_devspeed); retval += device_create_file(_dev, &dev_attr_enumspeed); retval += device_create_file(_dev, &dev_attr_hptxfsiz); retval += device_create_file(_dev, &dev_attr_hprt0); retval += device_create_file(_dev, &dev_attr_remote_wakeup); retval += device_create_file(_dev, &dev_attr_regdump); retval += device_create_file(_dev, &dev_attr_hcddump); retval += device_create_file(_dev, &dev_attr_hcd_frrem); retval += device_create_file(_dev, &dev_attr_rd_reg_test); retval += device_create_file(_dev, &dev_attr_wr_reg_test); if(retval != 0) { DWC_PRINT("cannot create sysfs device files.\n"); // DWC_PRINT("killing own sysfs device files!\n"); dwc_otg_attr_remove(_dev); } }
/** * This function handles the Connector ID Status Change Interrupt. It * reads the OTG Interrupt Register (GOTCTL) to determine whether this * is a Device to Host Mode transition or a Host Mode to Device * Transition. * * This only occurs when the cable is connected/removed from the PHY * connector. * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * _core_if) { uint32_t count = 0; gintsts_data_t gintsts = {.d32 = 0}; gintmsk_data_t gintmsk = {.d32 = 0}; gotgctl_data_t gotgctl = {.d32 = 0}; /* * Need to disable SOF interrupt immediately. If 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.b.sofintr = 1; dwc_modify_reg32(&_core_if->core_global_regs->gintmsk, gintmsk.d32, 0); DWC_DEBUGPL(DBG_CIL, " ++Connector ID Status Change Interrupt++ (%s)\n", (dwc_otg_is_host_mode(_core_if) ? "Host" : "Device")); gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); /* B-Device connector (Device Mode) */ if (gotgctl.b.conidsts) { /* Wait for switch to device mode. */ while (!dwc_otg_is_device_mode(_core_if)) { DWC_PRINT("Waiting for Peripheral Mode, Mode=%s\n", (dwc_otg_is_host_mode(_core_if) ? "Host" : "Peripheral")); MDELAY(100); if (++count > 10000) *(uint32_t *) NULL = 0; } _core_if->op_state = B_PERIPHERAL; dwc_otg_core_init(_core_if); dwc_otg_enable_global_interrupts(_core_if); pcd_start(_core_if); } else { /* A-Device connector (Host Mode) */ while (!dwc_otg_is_host_mode(_core_if)) { DWC_PRINT("Waiting for Host Mode, Mode=%s\n", (dwc_otg_is_host_mode(_core_if) ? "Host" : "Peripheral")); MDELAY(100); if (++count > 10000) *(uint32_t *) NULL = 0; } _core_if->op_state = A_HOST; /* * Initialize the Core for Host mode. */ dwc_otg_core_init(_core_if); dwc_otg_enable_global_interrupts(_core_if); hcd_start(_core_if); } /* Set flag and clear interrupt */ gintsts.b.conidstschng = 1; dwc_write_reg32(&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that a device is initiating the Session * Request Protocol to request the host to turn on bus power so a new * session can begin. The handler responds by turning on bus power. If * the DWC_otg controller is in low power mode, the handler brings the * controller out of low power mode before turning on bus power. * * @param _core_if Programming view of DWC_otg controller. */ int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * _core_if) { gintsts_data_t gintsts; #ifndef DWC_HOST_ONLY hprt0_data_t hprt0; DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); if (dwc_otg_is_device_mode(_core_if)) { DWC_PRINT("SRP: Device mode\n"); } else { DWC_PRINT("SRP: Host mode\n"); /* Turn on the port power bit. */ hprt0.d32 = dwc_otg_read_hprt0(_core_if); hprt0.b.prtpwr = 1; dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); /* Start the Connection timer. So a message can be displayed * if connect does not occur within 10 seconds. */ hcd_session_start(_core_if); } #endif /* */ /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.sessreqintr = 1; dwc_write_reg32(&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that the DWC_otg controller has detected a * resume or remote wakeup sequence. If the DWC_otg controller is in * low power mode, the handler must brings the controller out of low * power mode. The controller automatically begins resume * signaling. The handler schedules a time to stop resume signaling. */ int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * _core_if) { gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY,"++Resume and Remote Wakeup Detected Interrupt++\n"); if (dwc_otg_is_device_mode(_core_if)) { dctl_data_t dctl = {.d32 = 0}; DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dsts)); #ifdef PARTIAL_POWER_DOWN if (_core_if->hwcfg4.b.power_optimiz) { pcgcctl_data_t power = {.d32 = 0}; power.d32 = dwc_read_reg32(_core_if->pcgcctl); DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", power.d32); power.b.stoppclk = 0; dwc_write_reg32(_core_if->pcgcctl, power.d32); power.b.pwrclmp = 0; dwc_write_reg32(_core_if->pcgcctl, power.d32); power.b.rstpdwnmodule = 0; dwc_write_reg32(_core_if->pcgcctl, power.d32); } #endif /* */ /* Clear the Remote Wakeup Signalling */ dctl.b.rmtwkupsig = 1; dwc_modify_reg32(&_core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); if (_core_if->pcd_cb && _core_if->pcd_cb->resume_wakeup) { _core_if->pcd_cb->resume_wakeup(_core_if->pcd_cb->p); } } else { /* * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms * so that OPT tests pass with all PHYs). */ hprt0_data_t hprt0 = {.d32 = 0}; pcgcctl_data_t pcgcctl = {.d32 = 0}; /* Restart the Phy Clock */ pcgcctl.b.stoppclk = 1; dwc_modify_reg32(_core_if->pcgcctl, pcgcctl.d32, 0); UDELAY(10); /* Now wait for 70 ms. */ hprt0.d32 = dwc_otg_read_hprt0(_core_if); DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32); MDELAY(70); hprt0.b.prtres = 0; /* Resume */ dwc_write_reg32(_core_if->host_if->hprt0, hprt0.d32); DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n", dwc_read_reg32(_core_if->host_if->hprt0)); } /* Clear interrupt */ gintsts.d32 = 0; gintsts.b.wkupintr = 1; dwc_write_reg32(&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that a device has been disconnected from * the root port. */ int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * _core_if) { gintsts_data_t gintsts; printk(KERN_ERR " Disconnect Detected Interrupt++ (%s) %s\n", (dwc_otg_is_host_mode(_core_if) ? "Host" : "Device"), op_state_str(_core_if)); DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", (dwc_otg_is_host_mode(_core_if) ? "Host" : "Device"), op_state_str(_core_if)); /** @todo Consolidate this if statement. */ #ifndef DWC_HOST_ONLY if (_core_if->op_state == B_HOST) { /* If in device mode Disconnect and stop the HCD, then * start the PCD. */ hcd_disconnect(_core_if); pcd_start(_core_if); _core_if->op_state = B_PERIPHERAL; } else if (dwc_otg_is_device_mode(_core_if)) { gotgctl_data_t gotgctl = {.d32 = 0}; gotgctl.d32 = dwc_read_reg32(&_core_if->core_global_regs->gotgctl); if (gotgctl.b.hstsethnpen == 1) { /* Do nothing, if HNP in process the OTG * interrupt "Host Negotiation Detected" * interrupt will do the mode switch. */ } else if (gotgctl.b.devhnpen == 0) { /* If in device mode Disconnect and stop the HCD, then * start the PCD. */ hcd_disconnect(_core_if); pcd_start(_core_if); _core_if->op_state = B_PERIPHERAL; } else { DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n"); } } else { if (_core_if->op_state == A_HOST) { /* A-Cable still connected but device disconnected. */ hcd_disconnect(_core_if); } } #endif /* */ gintsts.d32 = 0; gintsts.b.disconnect = 1; dwc_write_reg32(&_core_if->core_global_regs->gintsts, gintsts.d32); return 1; } /** * This interrupt indicates that SUSPEND state has been detected on * the USB. * * For HNP the USB Suspend interrupt signals the change from * "a_peripheral" to "a_host". * * When power management is enabled the core will be put in low power * mode. */ int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * _core_if) { dsts_data_t dsts; gintsts_data_t gintsts; DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n"); if (dwc_otg_is_device_mode(_core_if)) { /* Check the Device status register to determine if the Suspend * state is active. */ dsts.d32 = dwc_read_reg32(&_core_if->dev_if->dev_global_regs->dsts); DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " "HWCFG4.power Optimize=%d\n", dsts.b.suspsts, _core_if->hwcfg4.b.power_optimiz); #ifdef PARTIAL_POWER_DOWN /** @todo Add a module parameter for power management. */ if (dsts.b.suspsts && _core_if->hwcfg4.b.power_optimiz) { pcgcctl_data_t power = {.d32 = 0}; DWC_DEBUGPL(DBG_CIL, "suspend\n"); power.b.pwrclmp = 1; dwc_write_reg32(_core_if->pcgcctl, power.d32); power.b.rstpdwnmodule = 1; dwc_modify_reg32(_core_if->pcgcctl, 0, power.d32); power.b.stoppclk = 1; dwc_modify_reg32(_core_if->pcgcctl, 0, power.d32); } else { DWC_DEBUGPL(DBG_ANY, "disconnect?\n"); } #endif /* */ /* PCD callback for suspend. */ pcd_suspend(_core_if); } else { if (_core_if->op_state == A_PERIPHERAL) {
/** * dwc_otg_device_start * * Start device mode on the chip. */ int dwc_otg_device_start(dwc_otg_device_t *_dev) { int ret = 0, i; diepmsk_data_t diepmsk = { .d32 = 0 }; doepmsk_data_t doepmsk = { .d32 = 0 }; if(!_dev) { DWC_ERROR("%s passed a null device structure pointer!\n", __func__); return -EINVAL; } DWC_VERBOSE("%s(%p)\n", __func__, _dev); // Enable Interrupts if(dwc_otg_device_enable_interrupts(_dev)) DWC_WARNING("Failed to enable device interrupts.\n"); // Clear D(I|O)PCTLs. for(i = 0; i < _dev->core->num_eps; i++) { dwc_otg_write_reg32(&_dev->core->in_ep_registers[i]->diepctl, 0); dwc_otg_write_reg32(&_dev->core->out_ep_registers[i]->doepctl, 0); } // Enable EP Interrupts diepmsk.b.xfercompl = 1; diepmsk.b.ahberr = 1; diepmsk.b.timeout = 1; diepmsk.b.epdisabled = 1; diepmsk.b.inepnakeff = 1; dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32); doepmsk.b.xfercompl = 1; doepmsk.b.setup = 1; doepmsk.b.back2backsetup = 1; doepmsk.b.epdisabled = 1; dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32); // Reset the device. dwc_otg_device_usb_reset(_dev); // We're all set up, tell the usb_gadget framework // that we exist. ret = usb_gadget_register_controller(_dev); if(ret) { DWC_ERROR("Failed to register controller.\n"); return ret; } return 0; } /** * dwc_otg_device_stop * * Stop device mode on the chip. */ void dwc_otg_device_stop(dwc_otg_device_t *_dev) { if(!_dev) { DWC_WARNING("%s passed a null device structure pointer!\n", __func__); return; } if(dwc_otg_device_disable_interrupts(_dev)) DWC_WARNING("Failed to disable device interrupts.\n"); } /** * dwc_otg_device_enable_interrupts * * enables interrupts. */ int dwc_otg_device_enable_interrupts(dwc_otg_device_t *_dev) { gintmsk_data_t gintmsk = { .d32 = 0 }; DWC_VERBOSE("%s(%p)\n", __func__, _dev); gintmsk.b.usbreset = 1; gintmsk.b.usbsuspend = 1; gintmsk.b.disconnect = 1; gintmsk.b.inepintr = 1; gintmsk.b.outepintr = 1; gintmsk.b.enumdone = 1; gintmsk.b.otgintr = 1; gintmsk.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, 0, gintmsk.d32); return 0; } /** * dwc_otg_device_disable_interrupts * * disables interrupts. */ int dwc_otg_device_disable_interrupts(dwc_otg_device_t *_dev) { gintmsk_data_t gintmsk = { .d32 = 0 }; DWC_VERBOSE("%s(%p)\n", __func__, _dev); gintmsk.b.usbreset = 1; gintmsk.b.usbsuspend = 1; gintmsk.b.disconnect = 1; gintmsk.b.inepintr = 1; gintmsk.b.outepintr = 1; gintmsk.b.enumdone = 1; gintmsk.b.otgintr = 1; gintmsk.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintmsk, gintmsk.d32, 0); return 0; } /** * dwc_otg_device_usb_reset * * Resets the USB connection and re-enables EP0. */ int dwc_otg_device_usb_reset(dwc_otg_device_t *_dev) { diepmsk_data_t diepmsk = { .d32 = 0 }; doepmsk_data_t doepmsk = { .d32 = 0 }; dcfg_data_t dcfg = { .d32 = 0 }; daint_data_t daint = { .d32 = 0 }; unsigned long flags; DWC_VERBOSE("%s(%p)\n", __func__, _dev); spin_lock_irqsave(&_dev->core->lock, flags); // Clear Device Address dcfg.d32 = dwc_otg_read_reg32(&_dev->core->device_registers->dcfg); dcfg.b.devaddr = 0; dcfg.b.epmscnt = 1; // TODO: if shared fifo dwc_otg_write_reg32(&_dev->core->device_registers->dcfg, dcfg.d32); // Reset global NAK registers. _dev->core->global_in_nak_count = 0; _dev->core->global_out_nak_count = 0; dwc_otg_core_clear_global_in_nak(_dev->core); dwc_otg_core_clear_global_out_nak(_dev->core); // Reset EPs dwc_otg_core_ep_reset(_dev->core); // Enable interrupts on EP0 daint.b.inep0 = 1; daint.b.outep0 = 1; dwc_otg_write_reg32(&_dev->core->device_registers->daintmsk, daint.d32); // Clear EP0 interrupts dwc_otg_write_reg32(&_dev->core->in_ep_registers[0]->diepint, 0xffffffff); dwc_otg_write_reg32(&_dev->core->out_ep_registers[0]->doepint, 0xffffffff); // Enable EP Interrupts diepmsk.b.xfercompl = 1; diepmsk.b.ahberr = 1; diepmsk.b.timeout = 1; diepmsk.b.epdisabled = 1; diepmsk.b.inepnakeff = 1; dwc_otg_write_reg32(&_dev->core->device_registers->diepmsk, diepmsk.d32); doepmsk.b.xfercompl = 1; doepmsk.b.setup = 1; doepmsk.b.back2backsetup = 1; doepmsk.b.epdisabled = 1; dwc_otg_write_reg32(&_dev->core->device_registers->doepmsk, doepmsk.d32); spin_unlock_irqrestore(&_dev->core->lock, flags); // Notify gadget driver if(dwc_otg_gadget_driver && dwc_otg_gadget_driver->resume) dwc_otg_gadget_driver->resume(&dwc_otg_gadget); return 0; } /** The EP0 USB descriptor! */ static struct usb_endpoint_descriptor ep0_descriptor = { .bLength = USB_DT_ENDPOINT_SIZE, .bDescriptorType = USB_DT_ENDPOINT, .bEndpointAddress = 0, .bmAttributes = 0, }; /** The OUT request for EP0. */ static dwc_otg_core_request_t ep0_out_request = { .request_type = DWC_EP_TYPE_CONTROL, .direction = DWC_OTG_REQUEST_OUT, .completed_handler = &dwc_otg_device_complete_ep0, .dont_free = 1, .buffer_length = 64, .dma_buffer = NULL, }; /** The IN request for EP0. */ static dwc_otg_core_request_t ep0_in_request = { .request_type = DWC_EP_TYPE_CONTROL, .direction = DWC_OTG_REQUEST_IN, .dont_free = 1, .buffer_length = 64, .dma_buffer = NULL, }; /** * The device USB IRQ handler. */ irqreturn_t dwc_otg_device_irq(int _irq, void *_dev) { dwc_otg_device_t *dev = (dwc_otg_device_t*)_dev; dwc_otg_core_t *core = dev->core; gintsts_data_t gintsts = { .d32 = 0 }; gintsts_data_t gintclr = { .d32 = 0 }; gintsts.d32 = dwc_otg_read_reg32(&core->registers->gintsts) & dwc_otg_read_reg32(&core->registers->gintmsk); DWC_VERBOSE("%s(%d, %p) gintsts=0x%08x\n", __func__, _irq, _dev, gintsts.d32); if(gintsts.b.otgintr) { gotgint_data_t gotgint; gotgint.d32 = dwc_otg_read_reg32(&core->registers->gotgint); DWC_DEBUG("otgintr 0x%08x\n", gotgint.d32); if(gotgint.b.sesenddet) { dcfg_data_t dcfg = { .d32 = dwc_otg_read_reg32(&core->device_registers->dcfg) }; DWC_DEBUG("session end detected\n"); dcfg.b.nzstsouthshk = 1; dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32); DWC_DEBUG("DCFG=0x%08x\n", dcfg.d32); schedule_work(&dev->disconnect_work); } // Clear OTG interrupts dwc_otg_write_reg32(&core->registers->gotgint, 0xffffffff); gintclr.b.otgintr = 1; } if(gintsts.b.enumdone) { int dwcSpeed; int i; dsts_data_t dsts; DWC_DEBUG("enumdone\n"); // Enable EP0. dwc_otg_core_enable_ep(core, &core->endpoints[0], &ep0_descriptor); //ep0_in_request.queued = 0; //ep0_out_request.queued = 0; // Listen for setup packets on EP0. dwc_otg_device_receive_ep0(_dev); // Tell gadget driver our top speed. dsts.d32 = dwc_otg_read_reg32(&core->device_registers->dsts); switch (dsts.b.enumspd) { case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: dwc_otg_gadget.speed = USB_SPEED_HIGH; dwcSpeed = DWC_OTG_HIGH_SPEED; break; case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: dwc_otg_gadget.speed = USB_SPEED_FULL; dwcSpeed = DWC_OTG_FULL_SPEED; break; case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: dwc_otg_gadget.speed = USB_SPEED_LOW; dwcSpeed = DWC_OTG_LOW_SPEED; break; } // Update the endpoint speeds. for(i = 1; i < core->num_eps; i++) core->endpoints[i].speed = dwcSpeed; gintclr.b.enumdone = 1; } if(gintsts.b.usbreset) { DWC_DEBUG("usbreset\n"); //schedule_work(&dev->reset_work); dwc_otg_device_usb_reset(dev); gintclr.b.usbreset = 1; } if(gintsts.b.usbsuspend) { DWC_DEBUG("usbsuspend\n"); schedule_work(&dev->suspend_work); gintclr.b.usbsuspend = 1; } if(gintsts.b.disconnect) { DWC_DEBUG("disconnect\n"); schedule_work(&dev->disconnect_work); gintclr.b.disconnect = 1; } if(gintsts.b.epmismatch) { dtknq1_data_t t1; int t2, t3, t4; int i; int num_tokens; int doneEPs = 0; dwc_otg_core_request_t *req; DWC_ERROR("EP Mismatch.\n"); dwc_otg_core_set_global_in_nak(dev->core); for(i = 0; i < dev->core->num_eps; i++) { dwc_otg_core_ep_t *ep = &core->endpoints[i]; req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) { dwc_otg_core_cancel_ep(dev->core, ep); } } dwc_otg_core_clear_global_in_nak(dev->core); t1.d32 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr1); t2 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr2); t3 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr3_dthrctl); t4 = dwc_otg_read_reg32(&dev->core->device_registers->dtknqr4_fifoemptymsk); num_tokens = min((int)dev->core->hwcfg2.b.dev_token_q_depth, (int)t1.b.intknwptr); DWC_PRINT("Requeing %d requests.\n", num_tokens); for(i = 0; i < num_tokens; i++) { int ep; #define GET_BITS(x, start, len) (((x) << ((sizeof(x)*8)-(len)-(start))) >> ((sizeof(x)*8)-(len))) if(i < 6) ep = GET_BITS((int)t1.b.epnums0_5, 8 + (i*4), 4); else if(i < 14) ep = GET_BITS(t2, (i-6)*4, 4); else if(i < 21) ep = GET_BITS(t3, (i-14)*4, 4); else if(i < 30) ep = GET_BITS(t4, (i-21)*4, 4); else break; if(doneEPs & (1 << ep)) break; DWC_ERROR("Requeing %d.\n", ep); req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) dwc_otg_core_start_request(core, req); else DWC_ERROR("Tried to requeue invalid request %p.\n", req); doneEPs |= (1 << ep); } for(i = 0; i < dev->core->num_eps; i++) { if(doneEPs & (1 << i)) continue; req = list_first_entry(&core->endpoints[i].transfer_queue, dwc_otg_core_request_t, queue_pointer); if(req && req->direction == DWC_OTG_REQUEST_IN) dwc_otg_core_start_request(core, req); //else // DWC_ERROR("Tried to requeue invalid request %p.\n", req); } gintclr.b.epmismatch = 1; } if(gintsts.b.inepintr || gintsts.b.outepintr) { daint_data_t daint = { .d32 = 0 }; int i; DWC_DEBUG("epint\n"); daint.d32 = dwc_otg_read_reg32(&core->device_registers->daint) & dwc_otg_read_reg32(&core->device_registers->daintmsk); for(i = 0; i < core->num_eps; i++) { if(daint.ep.in & 1) dwc_otg_device_handle_in_interrupt(dev, i); if(daint.ep.out & 1) dwc_otg_device_handle_out_interrupt(dev, i); daint.ep.out >>= 1; daint.ep.in >>= 1; } } // Clear the interrupts gintsts.d32 &= ~gintclr.d32; dwc_otg_write_reg32(&core->registers->gintsts, gintclr.d32); return gintsts.d32 != 0 ? IRQ_NONE : IRQ_HANDLED; } /** * The in endpoint interrupt handler. */ irqreturn_t dwc_otg_device_handle_in_interrupt(dwc_otg_device_t *_dev, int _ep) { dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep]; diepint_data_t depint = { .d32 = 0 }; diepint_data_t depclr = { .d32 = 0 }; DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep); depint.d32 = dwc_otg_read_reg32(&ep->in_registers->diepint) & dwc_otg_read_reg32(&_dev->core->device_registers->diepmsk); if(!ep->exists) { DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep); return -ENXIO; } if(depint.b.inepnakeff) { DWC_DEBUG("in nak eff %s\n", ep->name); complete_all(&ep->nakeff_completion); init_completion(&ep->nakeff_completion); depclr.b.inepnakeff = 1; } if(depint.b.epdisabled) { DWC_DEBUG("in epdisabled %s\n", ep->name); complete_all(&ep->disabled_completion); init_completion(&ep->disabled_completion); depclr.b.epdisabled = 1; } /*if(depint.b.intknepmis) { gintsts_data_t gintsts = { .d32 = 0 }; DWC_DEBUG("in tn mis\n"); gintsts.b.epmismatch = 1; dwc_otg_modify_reg32(&_dev->core->registers->gintsts, gintsts.d32, 0); }*/ if(depint.b.intktxfemp) { DWC_DEBUG("in tx f emp\n"); // Do nothing... ;_; depclr.b.intktxfemp = 1; } if(depint.b.timeout) { DWC_DEBUG("in timeout %s\n", ep->name); // Do something!? // Cancel sending?! depclr.b.timeout = 1; } if(depint.b.ahberr) { DWC_DEBUG("in ahberr\n"); // Do nothing again... :'( depclr.b.ahberr = 1; } if(depint.b.xfercompl) { dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); DWC_DEBUG("in xfercompl %s\n", ep->name); if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_IN) { DWC_ERROR("XferCompl received when we weren't transferring anything!\n"); } else dwc_otg_core_complete_request(_dev->core, req); depclr.b.xfercompl = 1; } // Clear interrupts dwc_otg_write_reg32(&ep->in_registers->diepint, depclr.d32); return 0; } /** * The out endpoint interrupt handler. */ irqreturn_t dwc_otg_device_handle_out_interrupt(dwc_otg_device_t *_dev, int _ep) { dwc_otg_core_ep_t *ep = &_dev->core->endpoints[_ep]; doepint_data_t depint = { .d32 = 0 }; doepint_data_t depclr = { .d32 = 0 }; DWC_VERBOSE("%s(%p, %d)\n", __func__, _dev, _ep); depint.d32 = dwc_otg_read_reg32(&ep->out_registers->doepint) & dwc_otg_read_reg32(&_dev->core->device_registers->doepmsk); if(!ep->exists) { DWC_ERROR("%s called on non-existant in endpoint %d.\n", __func__, _ep); return -ENXIO; } if(depint.b.setup) { dwc_otg_core_request_t *req; // We received a setup packet, yaaay! DWC_VERBOSE("setup\n"); // If we had a setup packet, set the flag on the // request that says it was prepended by a setup // token. req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); if(!list_empty(&ep->transfer_queue) && req->direction == DWC_OTG_REQUEST_OUT) { req->setup = 1; dwc_otg_core_complete_request(_dev->core, req); } else DWC_ERROR("Setup Packet Received without us initiating a transfer!\n"); depclr.b.setup = 1; } if(depint.b.back2backsetup) { // Do nothing DWC_DEBUG("back2backsetup\n"); depclr.b.back2backsetup = 1; } if(depint.b.epdisabled) { DWC_DEBUG("out epdisabled\n"); complete_all(&ep->disabled_completion); init_completion(&ep->disabled_completion); depclr.b.epdisabled = 1; } if(depint.b.ahberr) { // Do nothing again... :'( DWC_DEBUG("out ahberr\n"); depclr.b.ahberr = 1; } if(depint.b.outtknepdis) { // Nothing to do again! Wah... :''( DWC_DEBUG("out tkn epdis\n"); depclr.b.outtknepdis = 1; } if(depint.b.xfercompl) { dwc_otg_core_request_t *req = list_first_entry(&ep->transfer_queue, dwc_otg_core_request_t, queue_pointer); DWC_DEBUG("out xfercompl\n"); // Yay! :D if(list_empty(&ep->transfer_queue) || req->direction != DWC_OTG_REQUEST_OUT) { DWC_ERROR("XferCompl received when we weren't transferring anything!\n"); } else dwc_otg_core_complete_request(_dev->core, req); depclr.b.xfercompl = 1; } // Clear interrupts dwc_otg_write_reg32(&ep->out_registers->doepint, depclr.d32); return 0; } /** * dwc_otg_device_receive_ep0 */ void dwc_otg_device_receive_ep0(dwc_otg_device_t *_dev) { ep0_out_request.data = _dev; ep0_out_request.length = 8; //ep0_out_request.buffer_length; if(ep0_out_request.dma_buffer == NULL) { ep0_out_request.dma_buffer = dma_alloc_coherent(NULL, ep0_out_request.buffer_length, &ep0_out_request.dma_address, GFP_KERNEL); if(!ep0_out_request.dma_buffer) { DWC_ERROR("Failed to allocate setup buffer for EP0!\n"); return; } // As we've allocated a DMA buffer, we don't // need another. :D -- Ricky26 ep0_out_request.buffer = ep0_out_request.dma_buffer; // Setup the in request, as we might need it as a response. ep0_in_request.length = ep0_in_request.buffer_length; ep0_in_request.dma_buffer = dma_alloc_coherent(NULL, ep0_in_request.buffer_length, &ep0_in_request.dma_address, GFP_KERNEL); if(!ep0_in_request.dma_buffer) { DWC_ERROR("Failed to allocate in buffer for EP0!\n"); return; } ep0_in_request.buffer = ep0_in_request.dma_buffer; } DWC_VERBOSE("%s: enqueue (%p):%d\n", __func__, &ep0_out_request, ep0_out_request.length); dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_out_request); } /** * dwc_otg_device_send_ep0 */ void dwc_otg_device_send_ep0(dwc_otg_device_t *_dev) { ep0_in_request.data = _dev; dwc_otg_core_enqueue_request(_dev->core, &_dev->core->endpoints[0], &ep0_in_request); } /** * dwc_otg_device_complete_ep0 * * This is called to complete an interrupt on EP0. */ void dwc_otg_device_complete_ep0(dwc_otg_core_request_t *_req) { dwc_otg_core_t *core = _req->core; dwc_otg_device_t *dev = (dwc_otg_device_t*)_req->data; if(_req->cancelled) { DWC_VERBOSE("Shutting down EP0 control channel.\n"); return; } if(_req->setup) { struct usb_ctrlrequest *packet = (struct usb_ctrlrequest*)_req->buffer; #if defined(VERBOSE)&&defined(DEBUG) uint8_t *b = (uint8_t*)packet; DWC_PRINT("EP0 Sent us a setup packet! :3\n"); DWC_PRINT("%02x %02x %02x %02x %02x %02x %02x %02x\n", b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); DWC_PRINT("RT %d, R %d, w %d\n", packet->bRequestType & 0x7F, packet->bRequest, packet->wIndex); #endif if((packet->bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) { int set = 0; switch(packet->bRequest) { case USB_REQ_GET_STATUS: { uint16_t *result = (uint16_t*)ep0_in_request.buffer; switch (packet->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: *result = 0x1 | (1 << dev->remote_wakeup); // Self powered, remote wakeup enabled. break; case USB_RECIP_INTERFACE: *result = 0; break; case USB_RECIP_ENDPOINT: { // TODO: check this! int epnum = packet->wIndex & 0xf; if(epnum < core->num_eps && list_empty(&core->endpoints[epnum].transfer_queue)) { *result = 0; break; } *result = 1; } break; } ep0_in_request.length = 2; dwc_otg_device_send_ep0(dev); } goto exit; case USB_REQ_SET_FEATURE: set = 1; // fall through case USB_REQ_CLEAR_FEATURE: switch (packet->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: switch(packet->wValue) { case USB_DEVICE_REMOTE_WAKEUP: dev->remote_wakeup = set; break; } break; case USB_RECIP_ENDPOINT: { int ep = packet->wValue; if(ep == 0 || ep > core->num_eps) goto stall; dwc_otg_core_stall_ep(core, &core->endpoints[ep], set); } break; } goto send_zlp; case USB_REQ_SET_ADDRESS: { dcfg_data_t dcfg = { .d32 = 0 }; dcfg.d32 = dwc_otg_read_reg32(&core->device_registers->dcfg); dcfg.b.devaddr = packet->wValue; dwc_otg_write_reg32(&core->device_registers->dcfg, dcfg.d32); DWC_VERBOSE("Received address %d.\n", dcfg.b.devaddr); } goto send_zlp; } } // If we got here we don't know how to handle // the setup packet, so let's ask the gadget driver to // do it, and then blame it if it doesn't work... DWC_VERBOSE("Passing setup packet to gadget driver.\n"); if(dwc_otg_gadget_driver == NULL) { DWC_WARNING("No gadget driver yet, stalling.\n"); goto stall; } if(dwc_otg_gadget_driver->setup(&dwc_otg_gadget, packet)) { DWC_ERROR("Got a setup packet we don't handle properly yet.\n"); goto stall; } goto exit; } else if(ep0_out_request.length == 0) { DWC_VERBOSE("Received a ZLP from host.\n"); } else { DWC_ERROR("EP0 sent us some shit we don't know how to handle.\n"); } goto exit; stall: //dwc_otg_core_stall_ep(core, &core->endpoints[0], 1); { depctl_data_t depctl = { .d32 = dwc_otg_read_reg32(&core->in_ep_registers[0]->diepctl) }; depctl.b.stall = 1; depctl.b.cnak = 1; dwc_otg_write_reg32(&core->in_ep_registers[0]->diepctl, depctl.d32); } goto exit; send_zlp: ep0_in_request.length = 0; dwc_otg_device_send_ep0(dev); exit: dwc_otg_device_receive_ep0(dev); }