Author | Tokens | Token Proportion | Commits | Commit Proportion |
---|---|---|---|---|
Chunfeng Yun | 4173 | 99.95% | 10 | 83.33% |
Greg Kroah-Hartman | 2 | 0.05% | 2 | 16.67% |
Total | 4175 | 12 |
// SPDX-License-Identifier: GPL-2.0 /* * mtu3_gadget_ep0.c - MediaTek USB3 DRD peripheral driver ep0 handling * * Copyright (c) 2016 MediaTek Inc. * * Author: Chunfeng.Yun <chunfeng.yun@mediatek.com> */ #include <linux/iopoll.h> #include <linux/usb/composite.h> #include "mtu3.h" #include "mtu3_debug.h" #include "mtu3_trace.h" /* ep0 is always mtu3->in_eps[0] */ #define next_ep0_request(mtu) next_request((mtu)->ep0) /* for high speed test mode; see USB 2.0 spec 7.1.20 */ static const u8 mtu3_test_packet[53] = { /* implicit SYNC then DATA0 to start */ /* JKJKJKJK x9 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* JJKKJJKK x8 */ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, /* JJJJKKKK x8 */ 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, 0xee, /* JJJJJJJKKKKKKK x8 */ 0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* JJJJJJJK x8 */ 0x7f, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, /* JKKKKKKK x10, JK */ 0xfc, 0x7e, 0xbf, 0xdf, 0xef, 0xf7, 0xfb, 0xfd, 0x7e, /* implicit CRC16 then EOP to end */ }; static char *decode_ep0_state(struct mtu3 *mtu) { switch (mtu->ep0_state) { case MU3D_EP0_STATE_SETUP: return "SETUP"; case MU3D_EP0_STATE_TX: return "IN"; case MU3D_EP0_STATE_RX: return "OUT"; case MU3D_EP0_STATE_TX_END: return "TX-END"; case MU3D_EP0_STATE_STALL: return "STALL"; default: return "??"; } } static void ep0_req_giveback(struct mtu3 *mtu, struct usb_request *req) { mtu3_req_complete(mtu->ep0, req, 0); } static int forward_to_driver(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) __releases(mtu->lock) __acquires(mtu->lock) { int ret; if (!mtu->gadget_driver) return -EOPNOTSUPP; spin_unlock(&mtu->lock); ret = mtu->gadget_driver->setup(&mtu->g, setup); spin_lock(&mtu->lock); dev_dbg(mtu->dev, "%s ret %d\n", __func__, ret); return ret; } static void ep0_write_fifo(struct mtu3_ep *mep, const u8 *src, u16 len) { void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0; u16 index = 0; dev_dbg(mep->mtu->dev, "%s: ep%din, len=%d, buf=%p\n", __func__, mep->epnum, len, src); if (len >= 4) { iowrite32_rep(fifo, src, len >> 2); index = len & ~0x03; } if (len & 0x02) { writew(*(u16 *)&src[index], fifo); index += 2; } if (len & 0x01) writeb(src[index], fifo); } static void ep0_read_fifo(struct mtu3_ep *mep, u8 *dst, u16 len) { void __iomem *fifo = mep->mtu->mac_base + U3D_FIFO0; u32 value; u16 index = 0; dev_dbg(mep->mtu->dev, "%s: ep%dout len=%d buf=%p\n", __func__, mep->epnum, len, dst); if (len >= 4) { ioread32_rep(fifo, dst, len >> 2); index = len & ~0x03; } if (len & 0x3) { value = readl(fifo); memcpy(&dst[index], &value, len & 0x3); } } static void ep0_load_test_packet(struct mtu3 *mtu) { /* * because the length of test packet is less than max packet of HS ep0, * write it into fifo directly. */ ep0_write_fifo(mtu->ep0, mtu3_test_packet, sizeof(mtu3_test_packet)); } /* * A. send STALL for setup transfer without data stage: * set SENDSTALL and SETUPPKTRDY at the same time; * B. send STALL for other cases: * set SENDSTALL only. */ static void ep0_stall_set(struct mtu3_ep *mep0, bool set, u32 pktrdy) { struct mtu3 *mtu = mep0->mtu; void __iomem *mbase = mtu->mac_base; u32 csr; /* EP0_SENTSTALL is W1C */ csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; if (set) csr |= EP0_SENDSTALL | pktrdy; else csr = (csr & ~EP0_SENDSTALL) | EP0_SENTSTALL; mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); mtu->delayed_status = false; mtu->ep0_state = MU3D_EP0_STATE_SETUP; dev_dbg(mtu->dev, "ep0: %s STALL, ep0_state: %s\n", set ? "SEND" : "CLEAR", decode_ep0_state(mtu)); } static int ep0_queue(struct mtu3_ep *mep0, struct mtu3_request *mreq); static void ep0_dummy_complete(struct usb_ep *ep, struct usb_request *req) {} static void ep0_set_sel_complete(struct usb_ep *ep, struct usb_request *req) { struct mtu3_request *mreq; struct mtu3 *mtu; struct usb_set_sel_req sel; memcpy(&sel, req->buf, sizeof(sel)); mreq = to_mtu3_request(req); mtu = mreq->mtu; dev_dbg(mtu->dev, "u1sel:%d, u1pel:%d, u2sel:%d, u2pel:%d\n", sel.u1_sel, sel.u1_pel, sel.u2_sel, sel.u2_pel); } /* queue data stage to handle 6 byte SET_SEL request */ static int ep0_set_sel(struct mtu3 *mtu, struct usb_ctrlrequest *setup) { int ret; u16 length = le16_to_cpu(setup->wLength); if (unlikely(length != 6)) { dev_err(mtu->dev, "%s wrong wLength:%d\n", __func__, length); return -EINVAL; } mtu->ep0_req.mep = mtu->ep0; mtu->ep0_req.request.length = 6; mtu->ep0_req.request.buf = mtu->setup_buf; mtu->ep0_req.request.complete = ep0_set_sel_complete; ret = ep0_queue(mtu->ep0, &mtu->ep0_req); return ret < 0 ? ret : 1; } static int ep0_get_status(struct mtu3 *mtu, const struct usb_ctrlrequest *setup) { struct mtu3_ep *mep = NULL; int handled = 1; u8 result[2] = {0, 0}; u8 epnum = 0; int is_in; switch (setup->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: result[0] = mtu->is_self_powered << USB_DEVICE_SELF_POWERED; result[0] |= mtu->may_wakeup << USB_DEVICE_REMOTE_WAKEUP; if (mtu->g.speed >= USB_SPEED_SUPER) { result[0] |= mtu->u1_enable << USB_DEV_STAT_U1_ENABLED; result[0] |= mtu->u2_enable << USB_DEV_STAT_U2_ENABLED; } dev_dbg(mtu->dev, "%s result=%x, U1=%x, U2=%x\n", __func__, result[0], mtu->u1_enable, mtu->u2_enable); break; case USB_RECIP_INTERFACE: break; case USB_RECIP_ENDPOINT: epnum = (u8) le16_to_cpu(setup->wIndex); is_in = epnum & USB_DIR_IN; epnum &= USB_ENDPOINT_NUMBER_MASK; if (epnum >= mtu->num_eps) { handled = -EINVAL; break; } if (!epnum) break; mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum; if (!mep->desc) { handled = -EINVAL; break; } if (mep->flags & MTU3_EP_STALL) result[0] |= 1 << USB_ENDPOINT_HALT; break; default: /* class, vendor, etc ... delegate */ handled = 0; break; } if (handled > 0) { int ret; /* prepare a data stage for GET_STATUS */ dev_dbg(mtu->dev, "get_status=%x\n", *(u16 *)result); memcpy(mtu->setup_buf, result, sizeof(result)); mtu->ep0_req.mep = mtu->ep0; mtu->ep0_req.request.length = 2; mtu->ep0_req.request.buf = &mtu->setup_buf; mtu->ep0_req.request.complete = ep0_dummy_complete; ret = ep0_queue(mtu->ep0, &mtu->ep0_req); if (ret < 0) handled = ret; } return handled; } static int handle_test_mode(struct mtu3 *mtu, struct usb_ctrlrequest *setup) { void __iomem *mbase = mtu->mac_base; int handled = 1; u32 value; switch (le16_to_cpu(setup->wIndex) >> 8) { case TEST_J: dev_dbg(mtu->dev, "TEST_J\n"); mtu->test_mode_nr = TEST_J_MODE; break; case TEST_K: dev_dbg(mtu->dev, "TEST_K\n"); mtu->test_mode_nr = TEST_K_MODE; break; case TEST_SE0_NAK: dev_dbg(mtu->dev, "TEST_SE0_NAK\n"); mtu->test_mode_nr = TEST_SE0_NAK_MODE; break; case TEST_PACKET: dev_dbg(mtu->dev, "TEST_PACKET\n"); mtu->test_mode_nr = TEST_PACKET_MODE; break; default: handled = -EINVAL; goto out; } mtu->test_mode = true; /* no TX completion interrupt, and need restart platform after test */ if (mtu->test_mode_nr == TEST_PACKET_MODE) ep0_load_test_packet(mtu); /* send status before entering test mode. */ value = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; mtu3_writel(mbase, U3D_EP0CSR, value | EP0_SETUPPKTRDY | EP0_DATAEND); /* wait for ACK status sent by host */ readl_poll_timeout_atomic(mbase + U3D_EP0CSR, value, !(value & EP0_DATAEND), 100, 5000); mtu3_writel(mbase, U3D_USB2_TEST_MODE, mtu->test_mode_nr); mtu->ep0_state = MU3D_EP0_STATE_SETUP; out: return handled; } static int ep0_handle_feature_dev(struct mtu3 *mtu, struct usb_ctrlrequest *setup, bool set) { void __iomem *mbase = mtu->mac_base; int handled = -EINVAL; u32 lpc; switch (le16_to_cpu(setup->wValue)) { case USB_DEVICE_REMOTE_WAKEUP: mtu->may_wakeup = !!set; handled = 1; break; case USB_DEVICE_TEST_MODE: if (!set || (mtu->g.speed != USB_SPEED_HIGH) || (le16_to_cpu(setup->wIndex) & 0xff)) break; handled = handle_test_mode(mtu, setup); break; case USB_DEVICE_U1_ENABLE: if (mtu->g.speed < USB_SPEED_SUPER || mtu->g.state != USB_STATE_CONFIGURED) break; lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) lpc |= SW_U1_REQUEST_ENABLE; else lpc &= ~SW_U1_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u1_enable = !!set; handled = 1; break; case USB_DEVICE_U2_ENABLE: if (mtu->g.speed < USB_SPEED_SUPER || mtu->g.state != USB_STATE_CONFIGURED) break; lpc = mtu3_readl(mbase, U3D_LINK_POWER_CONTROL); if (set) lpc |= SW_U2_REQUEST_ENABLE; else lpc &= ~SW_U2_REQUEST_ENABLE; mtu3_writel(mbase, U3D_LINK_POWER_CONTROL, lpc); mtu->u2_enable = !!set; handled = 1; break; default: handled = -EINVAL; break; } return handled; } static int ep0_handle_feature(struct mtu3 *mtu, struct usb_ctrlrequest *setup, bool set) { struct mtu3_ep *mep; int handled = -EINVAL; int is_in; u16 value; u16 index; u8 epnum; value = le16_to_cpu(setup->wValue); index = le16_to_cpu(setup->wIndex); switch (setup->bRequestType & USB_RECIP_MASK) { case USB_RECIP_DEVICE: handled = ep0_handle_feature_dev(mtu, setup, set); break; case USB_RECIP_INTERFACE: /* superspeed only */ if (value == USB_INTRF_FUNC_SUSPEND && mtu->g.speed >= USB_SPEED_SUPER) { /* * forward the request because function drivers * should handle it */ handled = 0; } break; case USB_RECIP_ENDPOINT: epnum = index & USB_ENDPOINT_NUMBER_MASK; if (epnum == 0 || epnum >= mtu->num_eps || value != USB_ENDPOINT_HALT) break; is_in = index & USB_DIR_IN; mep = (is_in ? mtu->in_eps : mtu->out_eps) + epnum; if (!mep->desc) break; handled = 1; /* ignore request if endpoint is wedged */ if (mep->wedged) break; mtu3_ep_stall_set(mep, set); break; default: /* class, vendor, etc ... delegate */ handled = 0; break; } return handled; } /* * handle all control requests can be handled * returns: * negative errno - error happened * zero - need delegate SETUP to gadget driver * positive - already handled */ static int handle_standard_request(struct mtu3 *mtu, struct usb_ctrlrequest *setup) { void __iomem *mbase = mtu->mac_base; enum usb_device_state state = mtu->g.state; int handled = -EINVAL; u32 dev_conf; u16 value; value = le16_to_cpu(setup->wValue); /* the gadget driver handles everything except what we must handle */ switch (setup->bRequest) { case USB_REQ_SET_ADDRESS: /* change it after the status stage */ mtu->address = (u8) (value & 0x7f); dev_dbg(mtu->dev, "set address to 0x%x\n", mtu->address); dev_conf = mtu3_readl(mbase, U3D_DEVICE_CONF); dev_conf &= ~DEV_ADDR_MSK; dev_conf |= DEV_ADDR(mtu->address); mtu3_writel(mbase, U3D_DEVICE_CONF, dev_conf); if (mtu->address) usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS); else usb_gadget_set_state(&mtu->g, USB_STATE_DEFAULT); handled = 1; break; case USB_REQ_SET_CONFIGURATION: if (state == USB_STATE_ADDRESS) { usb_gadget_set_state(&mtu->g, USB_STATE_CONFIGURED); } else if (state == USB_STATE_CONFIGURED) { /* * USB2 spec sec 9.4.7, if wValue is 0 then dev * is moved to addressed state */ if (!value) usb_gadget_set_state(&mtu->g, USB_STATE_ADDRESS); } handled = 0; break; case USB_REQ_CLEAR_FEATURE: handled = ep0_handle_feature(mtu, setup, 0); break; case USB_REQ_SET_FEATURE: handled = ep0_handle_feature(mtu, setup, 1); break; case USB_REQ_GET_STATUS: handled = ep0_get_status(mtu, setup); break; case USB_REQ_SET_SEL: handled = ep0_set_sel(mtu, setup); break; case USB_REQ_SET_ISOCH_DELAY: handled = 1; break; default: /* delegate SET_CONFIGURATION, etc */ handled = 0; } return handled; } /* receive an data packet (OUT) */ static void ep0_rx_state(struct mtu3 *mtu) { struct mtu3_request *mreq; struct usb_request *req; void __iomem *mbase = mtu->mac_base; u32 maxp; u32 csr; u16 count = 0; dev_dbg(mtu->dev, "%s\n", __func__); csr = mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS; mreq = next_ep0_request(mtu); req = &mreq->request; /* read packet and ack; or stall because of gadget driver bug */ if (req) { void *buf = req->buf + req->actual; unsigned int len = req->length - req->actual; /* read the buffer */ count = mtu3_readl(mbase, U3D_RXCOUNT0); if (count > len) { req->status = -EOVERFLOW; count = len; } ep0_read_fifo(mtu->ep0, buf, count); req->actual += count; csr |= EP0_RXPKTRDY; maxp = mtu->g.ep0->maxpacket; if (count < maxp || req->actual == req->length) { mtu->ep0_state = MU3D_EP0_STATE_SETUP; dev_dbg(mtu->dev, "ep0 state: %s\n", decode_ep0_state(mtu)); csr |= EP0_DATAEND; } else { req = NULL; } } else { csr |= EP0_RXPKTRDY | EP0_SENDSTALL; dev_dbg(mtu->dev, "%s: SENDSTALL\n", __func__); } mtu3_writel(mbase, U3D_EP0CSR, csr); /* give back the request if have received all data */ if (req) ep0_req_giveback(mtu, req); } /* transmitting to the host (IN) */ static void ep0_tx_state(struct mtu3 *mtu) { struct mtu3_request *mreq = next_ep0_request(mtu); struct usb_request *req; u32 csr; u8 *src; u32 count; u32 maxp; dev_dbg(mtu->dev, "%s\n", __func__); if (!mreq) return; maxp = mtu->g.ep0->maxpacket; req = &mreq->request; /* load the data */ src = (u8 *)req->buf + req->actual; count = min(maxp, req->length - req->actual); if (count) ep0_write_fifo(mtu->ep0, src, count); dev_dbg(mtu->dev, "%s act=%d, len=%d, cnt=%d, maxp=%d zero=%d\n", __func__, req->actual, req->length, count, maxp, req->zero); req->actual += count; if ((count < maxp) || ((req->actual == req->length) && !req->zero)) mtu->ep0_state = MU3D_EP0_STATE_TX_END; /* send it out, triggering a "txpktrdy cleared" irq */ csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_TXPKTRDY); dev_dbg(mtu->dev, "%s ep0csr=0x%x\n", __func__, mtu3_readl(mtu->mac_base, U3D_EP0CSR)); } static void ep0_read_setup(struct mtu3 *mtu, struct usb_ctrlrequest *setup) { struct mtu3_request *mreq; u32 count; u32 csr; csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; count = mtu3_readl(mtu->mac_base, U3D_RXCOUNT0); ep0_read_fifo(mtu->ep0, (u8 *)setup, count); dev_dbg(mtu->dev, "SETUP req%02x.%02x v%04x i%04x l%04x\n", setup->bRequestType, setup->bRequest, le16_to_cpu(setup->wValue), le16_to_cpu(setup->wIndex), le16_to_cpu(setup->wLength)); /* clean up any leftover transfers */ mreq = next_ep0_request(mtu); if (mreq) ep0_req_giveback(mtu, &mreq->request); if (le16_to_cpu(setup->wLength) == 0) { ; /* no data stage, nothing to do */ } else if (setup->bRequestType & USB_DIR_IN) { mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr | EP0_SETUPPKTRDY | EP0_DPHTX); mtu->ep0_state = MU3D_EP0_STATE_TX; } else { mtu3_writel(mtu->mac_base, U3D_EP0CSR, (csr | EP0_SETUPPKTRDY) & (~EP0_DPHTX)); mtu->ep0_state = MU3D_EP0_STATE_RX; } } static int ep0_handle_setup(struct mtu3 *mtu) __releases(mtu->lock) __acquires(mtu->lock) { struct usb_ctrlrequest setup; struct mtu3_request *mreq; void __iomem *mbase = mtu->mac_base; int handled = 0; ep0_read_setup(mtu, &setup); trace_mtu3_handle_setup(&setup); if ((setup.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD) handled = handle_standard_request(mtu, &setup); dev_dbg(mtu->dev, "handled %d, ep0_state: %s\n", handled, decode_ep0_state(mtu)); if (handled < 0) goto stall; else if (handled > 0) goto finish; handled = forward_to_driver(mtu, &setup); if (handled < 0) { stall: dev_dbg(mtu->dev, "%s stall (%d)\n", __func__, handled); ep0_stall_set(mtu->ep0, true, le16_to_cpu(setup.wLength) ? 0 : EP0_SETUPPKTRDY); return 0; } finish: if (mtu->test_mode) { ; /* nothing to do */ } else if (handled == USB_GADGET_DELAYED_STATUS) { /* handle the delay STATUS phase till receive ep_queue on ep0 */ mtu->delayed_status = true; } else if (le16_to_cpu(setup.wLength) == 0) { /* no data stage */ mtu3_writel(mbase, U3D_EP0CSR, (mtu3_readl(mbase, U3D_EP0CSR) & EP0_W1C_BITS) | EP0_SETUPPKTRDY | EP0_DATAEND); /* complete zlp request directly */ mreq = next_ep0_request(mtu); if (mreq && !mreq->request.length) ep0_req_giveback(mtu, &mreq->request); } return 0; } irqreturn_t mtu3_ep0_isr(struct mtu3 *mtu) { void __iomem *mbase = mtu->mac_base; struct mtu3_request *mreq; u32 int_status; irqreturn_t ret = IRQ_NONE; u32 csr; u32 len; int_status = mtu3_readl(mbase, U3D_EPISR); int_status &= mtu3_readl(mbase, U3D_EPIER); mtu3_writel(mbase, U3D_EPISR, int_status); /* W1C */ /* only handle ep0's */ if (!(int_status & (EP0ISR | SETUPENDISR))) return IRQ_NONE; /* abort current SETUP, and process new one */ if (int_status & SETUPENDISR) mtu->ep0_state = MU3D_EP0_STATE_SETUP; csr = mtu3_readl(mbase, U3D_EP0CSR); dev_dbg(mtu->dev, "%s csr=0x%x\n", __func__, csr); /* we sent a stall.. need to clear it now.. */ if (csr & EP0_SENTSTALL) { ep0_stall_set(mtu->ep0, false, 0); csr = mtu3_readl(mbase, U3D_EP0CSR); ret = IRQ_HANDLED; } dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); mtu3_dbg_trace(mtu->dev, "ep0_state %s", decode_ep0_state(mtu)); switch (mtu->ep0_state) { case MU3D_EP0_STATE_TX: /* irq on clearing txpktrdy */ if ((csr & EP0_FIFOFULL) == 0) { ep0_tx_state(mtu); ret = IRQ_HANDLED; } break; case MU3D_EP0_STATE_RX: /* irq on set rxpktrdy */ if (csr & EP0_RXPKTRDY) { ep0_rx_state(mtu); ret = IRQ_HANDLED; } break; case MU3D_EP0_STATE_TX_END: mtu3_writel(mbase, U3D_EP0CSR, (csr & EP0_W1C_BITS) | EP0_DATAEND); mreq = next_ep0_request(mtu); if (mreq) ep0_req_giveback(mtu, &mreq->request); mtu->ep0_state = MU3D_EP0_STATE_SETUP; ret = IRQ_HANDLED; dev_dbg(mtu->dev, "ep0_state: %s\n", decode_ep0_state(mtu)); break; case MU3D_EP0_STATE_SETUP: if (!(csr & EP0_SETUPPKTRDY)) break; len = mtu3_readl(mbase, U3D_RXCOUNT0); if (len != 8) { dev_err(mtu->dev, "SETUP packet len %d != 8 ?\n", len); break; } ep0_handle_setup(mtu); ret = IRQ_HANDLED; break; default: /* can't happen */ ep0_stall_set(mtu->ep0, true, 0); WARN_ON(1); break; } return ret; } static int mtu3_ep0_enable(struct usb_ep *ep, const struct usb_endpoint_descriptor *desc) { /* always enabled */ return -EINVAL; } static int mtu3_ep0_disable(struct usb_ep *ep) { /* always enabled */ return -EINVAL; } static int ep0_queue(struct mtu3_ep *mep, struct mtu3_request *mreq) { struct mtu3 *mtu = mep->mtu; mreq->mtu = mtu; mreq->request.actual = 0; mreq->request.status = -EINPROGRESS; dev_dbg(mtu->dev, "%s %s (ep0_state: %s), len#%d\n", __func__, mep->name, decode_ep0_state(mtu), mreq->request.length); switch (mtu->ep0_state) { case MU3D_EP0_STATE_SETUP: case MU3D_EP0_STATE_RX: /* control-OUT data */ case MU3D_EP0_STATE_TX: /* control-IN data */ break; default: dev_err(mtu->dev, "%s, error in ep0 state %s\n", __func__, decode_ep0_state(mtu)); return -EINVAL; } if (mtu->delayed_status) { u32 csr; mtu->delayed_status = false; csr = mtu3_readl(mtu->mac_base, U3D_EP0CSR) & EP0_W1C_BITS; csr |= EP0_SETUPPKTRDY | EP0_DATAEND; mtu3_writel(mtu->mac_base, U3D_EP0CSR, csr); /* needn't giveback the request for handling delay STATUS */ return 0; } if (!list_empty(&mep->req_list)) return -EBUSY; list_add_tail(&mreq->list, &mep->req_list); /* sequence #1, IN ... start writing the data */ if (mtu->ep0_state == MU3D_EP0_STATE_TX) ep0_tx_state(mtu); return 0; } static int mtu3_ep0_queue(struct usb_ep *ep, struct usb_request *req, gfp_t gfp) { struct mtu3_ep *mep; struct mtu3_request *mreq; struct mtu3 *mtu; unsigned long flags; int ret = 0; if (!ep || !req) return -EINVAL; mep = to_mtu3_ep(ep); mtu = mep->mtu; mreq = to_mtu3_request(req); spin_lock_irqsave(&mtu->lock, flags); ret = ep0_queue(mep, mreq); spin_unlock_irqrestore(&mtu->lock, flags); return ret; } static int mtu3_ep0_dequeue(struct usb_ep *ep, struct usb_request *req) { /* we just won't support this */ return -EINVAL; } static int mtu3_ep0_halt(struct usb_ep *ep, int value) { struct mtu3_ep *mep; struct mtu3 *mtu; unsigned long flags; int ret = 0; if (!ep || !value) return -EINVAL; mep = to_mtu3_ep(ep); mtu = mep->mtu; dev_dbg(mtu->dev, "%s\n", __func__); spin_lock_irqsave(&mtu->lock, flags); if (!list_empty(&mep->req_list)) { ret = -EBUSY; goto cleanup; } switch (mtu->ep0_state) { /* * stalls are usually issued after parsing SETUP packet, either * directly in irq context from setup() or else later. */ case MU3D_EP0_STATE_TX: case MU3D_EP0_STATE_TX_END: case MU3D_EP0_STATE_RX: case MU3D_EP0_STATE_SETUP: ep0_stall_set(mtu->ep0, true, 0); break; default: dev_dbg(mtu->dev, "ep0 can't halt in state %s\n", decode_ep0_state(mtu)); ret = -EINVAL; } cleanup: spin_unlock_irqrestore(&mtu->lock, flags); return ret; } const struct usb_ep_ops mtu3_ep0_ops = { .enable = mtu3_ep0_enable, .disable = mtu3_ep0_disable, .alloc_request = mtu3_alloc_request, .free_request = mtu3_free_request, .queue = mtu3_ep0_queue, .dequeue = mtu3_ep0_dequeue, .set_halt = mtu3_ep0_halt, };
Information contained on this website is for historical information purposes only and does not indicate or represent copyright ownership.
Created with Cregit http://github.com/cregit/cregit
Version 2.0-RC1