struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt) { struct udc_endpoint *ept; unsigned n; unsigned in; if (type == UDC_TYPE_BULK_IN) { in = 1; } else if (type == UDC_TYPE_BULK_OUT) { in = 0; } else { return 0; } for (n = 1; n < 16; n++) { unsigned bit = in ? EPT_TX(n) : EPT_RX(n); if (ept_alloc_table & bit) continue; ept = _udc_endpoint_alloc(n, in, maxpkt); if (ept) ept_alloc_table |= bit; return ept; } return 0; }
struct udc_endpoint *_udc_endpoint_alloc(unsigned num, unsigned in, unsigned max_pkt) { struct udc_endpoint *ept; unsigned cfg; ept = memalign(CACHE_LINE, ROUNDUP(sizeof(*ept), CACHE_LINE)); ept->maxpkt = max_pkt; ept->num = num; ept->in = !!in; ept->req = 0; cfg = CONFIG_MAX_PKT(max_pkt) | CONFIG_ZLT; if (ept->in) { ept->bit = EPT_TX(ept->num); } else { ept->bit = EPT_RX(ept->num); if (num == 0) cfg |= CONFIG_IOS; } ept->head = epts + (num * 2) + (ept->in); ept->head->config = cfg; ept->next = ept_list; ept_list = ept; DBG("ept%d %s @%p/%p max=%d bit=%x\n", num, in ? "in" : "out", ept, ept->head, max_pkt, ept->bit); return ept; }
int usb_loop_poll_hw(struct usb_ep *_ept, int is_rx) { struct msm_endpoint *act_ept, *ept = to_msm_endpoint(_ept); struct usb_info *ui = ept->ui; int done = 0; u32 n; /* Normally there is a read request in the endpoint, wait for new data */ for (;;) { n = readl(USB_USBSTS); writel(n, USB_USBSTS); if (n & STS_UI) /* finished transaction */ break; } /* USB Transaction is complete */ if (n & STS_UI) { n = readl(USB_ENDPTSETUPSTAT); if (n & EPT_RX(0)) handle_setup(ui); n = readl(USB_ENDPTCOMPLETE); writel(n, USB_ENDPTCOMPLETE); while (n) { unsigned bit = __ffs(n); act_ept = ui->ept + bit; if (ept == act_ept) { pr_debug("%s: recv'd right tx %d\n", __func__, bit); done = 1; } else { pr_debug("%s: recv'd extra tx from ept %d (exp %d)\n", __func__, bit, ept->bit); } /* always call the handler for KGDB and other usb functions. * this is to avoid hardware timeout, but can leave a bit * kernel code running when kgdb is invoked to stopped the * kernel. this works quite well with adb but might not * support usb mass storage devices very well. */ handle_endpoint(ui, bit); n = n & (~(1 << bit)); } } return done ? 0 : -EAGAIN; }
static irqreturn_t usb_interrupt(int irq, void *data) { struct usb_info *ui = data; unsigned n; n = readl(USB_USBSTS); writel(n, USB_USBSTS); /* somehow we got an IRQ while in the reset sequence: ignore it */ if (ui->running == 0) return IRQ_HANDLED; if (n & STS_PCI) { switch (readl(USB_PORTSC) & PORTSC_PSPD_MASK) { case PORTSC_PSPD_FS: INFO("msm72k_udc: portchange USB_SPEED_FULL\n"); ui->gadget.speed = USB_SPEED_FULL; break; case PORTSC_PSPD_LS: INFO("msm72k_udc: portchange USB_SPEED_LOW\n"); ui->gadget.speed = USB_SPEED_LOW; break; case PORTSC_PSPD_HS: INFO("msm72k_udc: portchange USB_SPEED_HIGH\n"); ui->gadget.speed = USB_SPEED_HIGH; break; } } if (n & STS_URI) { INFO("msm72k_udc: reset\n"); writel(readl(USB_ENDPTSETUPSTAT), USB_ENDPTSETUPSTAT); writel(readl(USB_ENDPTCOMPLETE), USB_ENDPTCOMPLETE); writel(0xffffffff, USB_ENDPTFLUSH); writel(0, USB_ENDPTCTRL(1)); if (ui->online != 0) { /* marking us offline will cause ept queue attempts ** to fail */ ui->online = 0; flush_all_endpoints(ui); /* XXX: we can't seem to detect going offline, * XXX: so deconfigure on reset for the time being */ if (ui->driver) { printk(KERN_INFO "usb: notify offline\n"); ui->driver->disconnect(&ui->gadget); } } } if (n & STS_SLI) INFO("msm72k_udc: suspend\n"); if (n & STS_UI) { n = readl(USB_ENDPTSETUPSTAT); if (n & EPT_RX(0)) handle_setup(ui); n = readl(USB_ENDPTCOMPLETE); writel(n, USB_ENDPTCOMPLETE); while (n) { unsigned bit = __ffs(n); handle_endpoint(ui, bit); n = n & (~(1 << bit)); } } return IRQ_HANDLED; }
static void handle_setup(struct usb_info *ui) { struct usb_ctrlrequest ctl; struct usb_request *req = ui->setup_req; int ret; /* USB hardware sometimes generate interrupt before * 8 bytes of SETUP packet are written to system memory. * This results in fetching wrong setup_data sometimes. * TODO: Remove below workaround of adding 1us delay once * it gets fixed in hardware. */ udelay(10); memcpy(&ctl, ui->ep0out.head->setup_data, sizeof(ctl)); writel(EPT_RX(0), USB_ENDPTSETUPSTAT); if (ctl.bRequestType & USB_DIR_IN) ui->ep0_dir = USB_DIR_IN; else ui->ep0_dir = USB_DIR_OUT; /* any pending ep0 transactions must be canceled */ flush_endpoint(&ui->ep0out); flush_endpoint(&ui->ep0in); #if 0 INFO("setup: type=%02x req=%02x val=%04x idx=%04x len=%04x\n", ctl.bRequestType, ctl.bRequest, ctl.wValue, ctl.wIndex, ctl.wLength); #endif if ((ctl.bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) == (USB_DIR_IN | USB_TYPE_STANDARD)) { if (ctl.bRequest == USB_REQ_GET_STATUS) { if (ctl.wLength != 2) goto stall; switch (ctl.bRequestType & USB_RECIP_MASK) { case USB_RECIP_ENDPOINT: { struct msm_endpoint *ept; unsigned num = ctl.wIndex & USB_ENDPOINT_NUMBER_MASK; u16 temp = 0; if (num == 0) { memset(req->buf, 0, 2); break; } if (ctl.wIndex & USB_ENDPOINT_DIR_MASK) num += 16; ept = &ui->ep0out + num; temp = usb_ep_get_stall(ept); temp = temp << USB_ENDPOINT_HALT; memcpy(req->buf, &temp, 2); break; } case USB_RECIP_DEVICE: { u16 temp = 0; temp = 1 << USB_DEVICE_SELF_POWERED; temp |= (ui->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP); memcpy(req->buf, &temp, 2); break; } case USB_RECIP_INTERFACE: memset(req->buf, 0, 2); break; default: goto stall; } ep0_setup_send(ui, 2); return; } } if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_ENDPOINT)) { if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) || (ctl.bRequest == USB_REQ_SET_FEATURE)) { if ((ctl.wValue == 0) && (ctl.wLength == 0)) { unsigned num = ctl.wIndex & 0x0f; if (num != 0) { struct msm_endpoint *ept; if (ctl.wIndex & 0x80) num += 16; ept = &ui->ep0out + num; if (ctl.bRequest == USB_REQ_SET_FEATURE) msm72k_set_halt(&ept->ep, 1); else msm72k_set_halt(&ept->ep, 0); } goto ack; } } } if (ctl.bRequestType == (USB_DIR_OUT | USB_TYPE_STANDARD)) { if (ctl.bRequest == USB_REQ_SET_CONFIGURATION) { ui->online = !!ctl.wValue; if (ui->online && ui->usb_connected) ui->usb_connected(1); } else if (ctl.bRequest == USB_REQ_SET_ADDRESS) { /* write address delayed (will take effect ** after the next IN txn) */ writel((ctl.wValue << 25) | (1 << 24), USB_DEVICEADDR); goto ack; } else if (ctl.bRequest == USB_REQ_SET_FEATURE) { switch (ctl.wValue) { case USB_DEVICE_TEST_MODE: switch (ctl.wIndex) { case J_TEST: case K_TEST: case SE0_NAK_TEST: case TST_PKT_TEST: ui->test_mode = ctl.wIndex; goto ack; } goto stall; case USB_DEVICE_REMOTE_WAKEUP: ui->remote_wakeup = 1; goto ack; } } else if ((ctl.bRequest == USB_REQ_CLEAR_FEATURE) && (ctl.wValue == USB_DEVICE_REMOTE_WAKEUP)) { ui->remote_wakeup = 0; goto ack; } } /* delegate if we get here */ if (ui->driver) { ret = ui->driver->setup(&ui->gadget, &ctl); if (ret >= 0) return; } stall: /* stall ep0 on error */ ep0_setup_stall(ui); return; ack: ep0_setup_ack(ui); }
cfg |= CONFIG_IOS; } ept->head = epts + (num * 2) + (ept->in); ept->head->config = cfg; ept->next = ept_list; ept_list = ept; DBG("ept%d %s @%p/%p max=%d bit=%x\n", num, in ? "in" : "out", ept, ept->head, max_pkt, ept->bit); return ept; } static unsigned ept_alloc_table = EPT_TX(0) | EPT_RX(0); struct udc_endpoint *udc_endpoint_alloc(unsigned type, unsigned maxpkt) { struct udc_endpoint *ept; unsigned n; unsigned in; if (type == UDC_TYPE_BULK_IN) { in = 1; } else if (type == UDC_TYPE_BULK_OUT) { in = 0; } else { return 0; }