📄 fsl_usb2_udc.c
字号:
if (!_ep) { return; } else { ep = container_of(_ep, struct fsl_ep, ep); if (!ep->desc) return; } ep_num = ep_index(ep); ep_dir = ep_is_in(ep) ? USB_SEND : USB_RECV; if (ep_num == 0) bits = (1 << 16) | 1; else if (ep_dir == USB_SEND) bits = 1 << (16 + ep_num); else bits = 1 << ep_num; timeout = jiffies + FSL_UDC_FLUSH_TIMEOUT; do { fsl_writel(bits, &dr_regs->endptflush); /* Wait until flush complete */ while (fsl_readl(&dr_regs->endptflush)) { if (time_after(jiffies, timeout)) { ERR("ep flush timeout\n"); return; } cpu_relax(); } /* See if we need to flush again */ } while (fsl_readl(&dr_regs->endptstatus) & bits);}static struct usb_ep_ops fsl_ep_ops = { .enable = fsl_ep_enable, .disable = fsl_ep_disable, .alloc_request = fsl_alloc_request, .free_request = fsl_free_request, .queue = fsl_ep_queue, .dequeue = fsl_ep_dequeue, .set_halt = fsl_ep_set_halt, .fifo_flush = fsl_ep_fifo_flush, /* flush fifo */};/*------------------------------------------------------------------------- Gadget Driver Layer Operations-------------------------------------------------------------------------*//*---------------------------------------------------------------------- * Get the current frame number (from DR frame_index Reg ) *----------------------------------------------------------------------*/static int fsl_get_frame(struct usb_gadget *gadget){ return (int)(fsl_readl(&dr_regs->frindex) & USB_FRINDEX_MASKS);}/*----------------------------------------------------------------------- * Tries to wake up the host connected to this gadget -----------------------------------------------------------------------*/static int fsl_wakeup(struct usb_gadget *gadget){ struct fsl_udc *udc = container_of(gadget, struct fsl_udc, gadget); u32 portsc; /* Remote wakeup feature not enabled by host */ if (!udc->remote_wakeup) return -ENOTSUPP; portsc = fsl_readl(&dr_regs->portsc1); /* not suspended? */ if (!(portsc & PORTSCX_PORT_SUSPEND)) return 0; /* trigger force resume */ portsc |= PORTSCX_PORT_FORCE_RESUME; fsl_writel(portsc, &dr_regs->portsc1); return 0;}static int can_pullup(struct fsl_udc *udc){ return udc->driver && udc->softconnect && udc->vbus_active;}/* Notify controller that VBUS is powered, Called by whatever detects VBUS sessions */static int fsl_vbus_session(struct usb_gadget *gadget, int is_active){ struct fsl_udc *udc; unsigned long flags; udc = container_of(gadget, struct fsl_udc, gadget); spin_lock_irqsave(&udc->lock, flags); VDBG("VBUS %s\n", is_active ? "on" : "off"); udc->vbus_active = (is_active != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), &dr_regs->usbcmd); else fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), &dr_regs->usbcmd); spin_unlock_irqrestore(&udc->lock, flags); return 0;}/* constrain controller's VBUS power usage * This call is used by gadget drivers during SET_CONFIGURATION calls, * reporting how much power the device may consume. For example, this * could affect how quickly batteries are recharged. * * Returns zero on success, else negative errno. */static int fsl_vbus_draw(struct usb_gadget *gadget, unsigned mA){ struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); if (udc->transceiver) return otg_set_power(udc->transceiver, mA); return -ENOTSUPP;}/* Change Data+ pullup status * this func is used by usb_gadget_connect/disconnet */static int fsl_pullup(struct usb_gadget *gadget, int is_on){ struct fsl_udc *udc; udc = container_of(gadget, struct fsl_udc, gadget); udc->softconnect = (is_on != 0); if (can_pullup(udc)) fsl_writel((fsl_readl(&dr_regs->usbcmd) | USB_CMD_RUN_STOP), &dr_regs->usbcmd); else fsl_writel((fsl_readl(&dr_regs->usbcmd) & ~USB_CMD_RUN_STOP), &dr_regs->usbcmd); return 0;}/* defined in gadget.h */static struct usb_gadget_ops fsl_gadget_ops = { .get_frame = fsl_get_frame, .wakeup = fsl_wakeup,/* .set_selfpowered = fsl_set_selfpowered, */ /* Always selfpowered */ .vbus_session = fsl_vbus_session, .vbus_draw = fsl_vbus_draw, .pullup = fsl_pullup,};/* Set protocol stall on ep0, protocol stall will automatically be cleared on new transaction */static void ep0stall(struct fsl_udc *udc){ u32 tmp; /* must set tx and rx to stall at the same time */ tmp = fsl_readl(&dr_regs->endptctrl[0]); tmp |= EPCTRL_TX_EP_STALL | EPCTRL_RX_EP_STALL; fsl_writel(tmp, &dr_regs->endptctrl[0]); udc->ep0_state = WAIT_FOR_SETUP; udc->ep0_dir = 0;}/* Prime a status phase for ep0 */static int ep0_prime_status(struct fsl_udc *udc, int direction){ struct fsl_req *req = udc->status_req; struct fsl_ep *ep; int status = 0; if (direction == EP_DIR_IN) udc->ep0_dir = USB_DIR_IN; else udc->ep0_dir = USB_DIR_OUT; ep = &udc->eps[0]; udc->ep0_state = WAIT_FOR_OUT_STATUS; req->ep = ep; req->req.length = 0; req->req.status = -EINPROGRESS; req->req.actual = 0; req->req.complete = NULL; req->dtd_count = 0; if (fsl_req_to_dtd(req) == 0) status = fsl_queue_td(ep, req); else return -ENOMEM; if (status) ERR("Can't queue ep0 status request \n"); list_add_tail(&req->queue, &ep->queue); return status;}static inline int udc_reset_ep_queue(struct fsl_udc *udc, u8 pipe){ struct fsl_ep *ep = get_ep_by_pipe(udc, pipe); if (!ep->name) return 0; nuke(ep, -ESHUTDOWN); return 0;}/* * ch9 Set address */static void ch9setaddress(struct fsl_udc *udc, u16 value, u16 index, u16 length){ /* Save the new address to device struct */ udc->device_address = (u8) value; /* Update usb state */ udc->usb_state = USB_STATE_ADDRESS; /* Status phase */ if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc);}/* * ch9 Get status */static void ch9getstatus(struct fsl_udc *udc, u8 request_type, u16 value, u16 index, u16 length){ u16 tmp = 0; /* Status, cpu endian */ struct fsl_req *req; struct fsl_ep *ep; int status = 0; ep = &udc->eps[0]; if ((request_type & USB_RECIP_MASK) == USB_RECIP_DEVICE) { /* Get device status */ tmp = 1 << USB_DEVICE_SELF_POWERED; tmp |= udc->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP; } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_INTERFACE) { /* Get interface status */ /* We don't have interface information in udc driver */ tmp = 0; } else if ((request_type & USB_RECIP_MASK) == USB_RECIP_ENDPOINT) { /* Get endpoint status */ struct fsl_ep *target_ep; target_ep = get_ep_by_pipe(udc, get_pipe_by_windex(index)); /* stall if endpoint doesn't exist */ if (!target_ep->desc) goto stall; tmp = dr_ep_get_stall(ep_index(target_ep), ep_is_in(target_ep)) << USB_ENDPOINT_HALT; } udc->ep0_dir = USB_DIR_IN; /* Borrow the per device status_req */ req = udc->status_req; /* Fill in the reqest structure */ *((u16 *) req->req.buf) = cpu_to_le16(tmp); req->ep = ep; req->req.length = 2; req->req.status = -EINPROGRESS; req->req.actual = 0; req->req.complete = NULL; req->dtd_count = 0; /* prime the data phase */ if ((fsl_req_to_dtd(req) == 0)) status = fsl_queue_td(ep, req); else /* no mem */ goto stall; if (status) { ERR("Can't respond to getstatus request \n"); goto stall; } list_add_tail(&req->queue, &ep->queue); udc->ep0_state = DATA_STATE_XMIT; return;stall: ep0stall(udc);}static void setup_received_irq(struct fsl_udc *udc, struct usb_ctrlrequest *setup){ u16 wValue = le16_to_cpu(setup->wValue); u16 wIndex = le16_to_cpu(setup->wIndex); u16 wLength = le16_to_cpu(setup->wLength); udc_reset_ep_queue(udc, 0); /* We process some stardard setup requests here */ switch (setup->bRequest) { case USB_REQ_GET_STATUS: /* Data+Status phase from udc */ if ((setup->bRequestType & (USB_DIR_IN | USB_TYPE_MASK)) != (USB_DIR_IN | USB_TYPE_STANDARD)) break; ch9getstatus(udc, setup->bRequestType, wValue, wIndex, wLength); return; case USB_REQ_SET_ADDRESS: /* Status phase from udc */ if (setup->bRequestType != (USB_DIR_OUT | USB_TYPE_STANDARD | USB_RECIP_DEVICE)) break; ch9setaddress(udc, wValue, wIndex, wLength); return; case USB_REQ_CLEAR_FEATURE: case USB_REQ_SET_FEATURE: /* Status phase from udc */ { int rc = -EOPNOTSUPP; if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_ENDPOINT | USB_TYPE_STANDARD)) { int pipe = get_pipe_by_windex(wIndex); struct fsl_ep *ep; if (wValue != 0 || wLength != 0 || pipe > udc->max_ep) break; ep = get_ep_by_pipe(udc, pipe); spin_unlock(&udc->lock); rc = fsl_ep_set_halt(&ep->ep, (setup->bRequest == USB_REQ_SET_FEATURE) ? 1 : 0); spin_lock(&udc->lock); } else if ((setup->bRequestType & (USB_RECIP_MASK | USB_TYPE_MASK)) == (USB_RECIP_DEVICE | USB_TYPE_STANDARD)) { /* Note: The driver has not include OTG support yet. * This will be set when OTG support is added */ if (!gadget_is_otg(&udc->gadget)) break; else if (setup->bRequest == USB_DEVICE_B_HNP_ENABLE) udc->gadget.b_hnp_enable = 1; else if (setup->bRequest == USB_DEVICE_A_HNP_SUPPORT) udc->gadget.a_hnp_support = 1; else if (setup->bRequest == USB_DEVICE_A_ALT_HNP_SUPPORT) udc->gadget.a_alt_hnp_support = 1; else break; rc = 0; } else break; if (rc == 0) { if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc); } return; } default: break; } /* Requests handled by gadget */ if (wLength) { /* Data phase from gadget, status phase from udc */ udc->ep0_dir = (setup->bRequestType & USB_DIR_IN) ? USB_DIR_IN : USB_DIR_OUT; spin_unlock(&udc->lock); if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff) < 0) ep0stall(udc); spin_lock(&udc->lock); udc->ep0_state = (setup->bRequestType & USB_DIR_IN) ? DATA_STATE_XMIT : DATA_STATE_RECV; } else { /* No data phase, IN status from gadget */ udc->ep0_dir = USB_DIR_IN; spin_unlock(&udc->lock); if (udc->driver->setup(&udc->gadget, &udc->local_setup_buff) < 0) ep0stall(udc); spin_lock(&udc->lock); udc->ep0_state = WAIT_FOR_OUT_STATUS; }}/* Process request for Data or Status phase of ep0 * prime status phase if needed */static void ep0_req_complete(struct fsl_udc *udc, struct fsl_ep *ep0, struct fsl_req *req){ if (udc->usb_state == USB_STATE_ADDRESS) { /* Set the new address */ u32 new_address = (u32) udc->device_address; fsl_writel(new_address << USB_DEVICE_ADDRESS_BIT_POS, &dr_regs->deviceaddr); } done(ep0, req, 0); switch (udc->ep0_state) { case DATA_STATE_XMIT: /* receive status phase */ if (ep0_prime_status(udc, EP_DIR_OUT)) ep0stall(udc); break; case DATA_STATE_RECV: /* send status phase */ if (ep0_prime_status(udc, EP_DIR_IN)) ep0stall(udc); break; case WAIT_FOR_OUT_STATUS: udc->ep0_state = WAIT_FOR_SETUP; break; case WAIT_FOR_SETUP: ERR("Unexpect ep0 packets \n"); break; default: ep0stall(udc); break; }}/* Tripwire mechanism to ensure a setup packet payload is extracted without * being corrupted by another incoming setup packet */static void tripwire_handler(struct fsl_udc *udc, u8 ep_num, u8 *buffer_ptr){ u32 temp; struct ep_queue_head *qh; qh = &udc->ep_qh[ep_num * 2 + EP_DIR_OUT]; /* Clear bit in ENDPTSETUPSTAT */ temp = fsl_readl(&dr_regs->endptsetupstat); fsl_writel(temp | (1 << ep_num), &dr_regs->endptsetupstat); /* while a hazard exists when setup package arrives */ do { /* Set Setup Tripwire */ temp = fsl_readl(&dr_regs->usbcmd); fsl_writel(temp | USB_CMD_SUTW, &dr_regs->usbcmd); /* Copy the setup packet to local buffer */ memcpy(buffer_ptr, (u8 *) qh->setup_buffer, 8); } while (!(fsl_readl(&dr_regs->usbcmd) & USB_CMD_SUTW)); /* Clear Setup Tripwire */ temp = fsl_readl(&dr_regs->usbcmd); fsl_writel(temp & ~USB_CMD_SUTW, &dr_regs->usbcmd);}/* process-ep_req(): free the completed Tds for this req */static int process_ep_req(struct fsl_udc *udc, int pipe, struct fsl_req *curr_req){ struct ep_td_struct *curr_td; int td_complete, actual, remaining_length, j, tmp; int status = 0; int errors = 0; struct ep_queue_head *curr_qh = &udc->ep_qh[pipe]; int direction = pipe % 2; curr_td = curr_req->head; td_complete = 0; actual = curr_req->req.length; for (j = 0; j < curr_req->dtd_count; j++) { remaining_length = (le32_to_cpu(curr_td->size_ioc_sts) & DTD_PACKET_SIZE) >> DTD_LENGTH_BIT_POS; actual -= remaining_length; if ((errors = le32_to_cpu(curr_td->size_ioc_sts) & DTD_ERROR_MASK)) { if (errors & DTD_STATUS_HALTED) { ERR("dTD error %08x QH=%d\n", errors, pipe); /* Clear the errors and Halt condition */ tmp = le32_to_cpu(curr_qh->size_ioc_int_sts); tmp &= ~errors; curr_qh->size_ioc_int_sts = cpu_to_le32(tmp);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -