📄 lh7a40x_udc.c
字号:
return 1;
}
static int lh7a40x_handle_get_status(struct lh7a40x_udc *dev,
struct usb_ctrlrequest *ctrl)
{
struct lh7a40x_ep *ep0 = &dev->ep[0];
struct lh7a40x_ep *qep;
int reqtype = (ctrl->bRequestType & USB_RECIP_MASK);
u16 val = 0;
if (reqtype == USB_RECIP_INTERFACE) {
/* This is not supported.
* And according to the USB spec, this one does nothing..
* Just return 0
*/
DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n");
} else if (reqtype == USB_RECIP_DEVICE) {
DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n");
val |= (1 << 0); /* Self powered */
/*val |= (1<<1); *//* Remote wakeup */
} else if (reqtype == USB_RECIP_ENDPOINT) {
int ep_num = (ctrl->wIndex & ~USB_DIR_IN);
DEBUG_SETUP
("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n",
ep_num, ctrl->wLength);
if (ctrl->wLength > 2 || ep_num > 3)
return -EOPNOTSUPP;
qep = &dev->ep[ep_num];
if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0)
&& ep_index(qep) != 0) {
return -EOPNOTSUPP;
}
usb_set_index(ep_index(qep));
/* Return status on next IN token */
switch (qep->ep_type) {
case ep_control:
val =
(usb_read(qep->csr1) & EP0_SEND_STALL) ==
EP0_SEND_STALL;
break;
case ep_bulk_in:
case ep_interrupt:
val =
(usb_read(qep->csr1) & USB_IN_CSR1_SEND_STALL) ==
USB_IN_CSR1_SEND_STALL;
break;
case ep_bulk_out:
val =
(usb_read(qep->csr1) & USB_OUT_CSR1_SEND_STALL) ==
USB_OUT_CSR1_SEND_STALL;
break;
}
/* Back to EP0 index */
usb_set_index(0);
DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num,
ctrl->wIndex, val);
} else {
DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype);
return -EOPNOTSUPP;
}
/* Clear "out packet ready" */
usb_set((EP0_CLR_OUT), USB_EP0_CSR);
/* Put status to FIFO */
lh7a40x_fifo_write(ep0, (u8 *) & val, sizeof(val));
/* Issue "In packet ready" */
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
return 0;
}
/*
* WAIT_FOR_SETUP (OUT_PKT_RDY)
* - read data packet from EP0 FIFO
* - decode command
* - if error
* set EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL bits
* - else
* set EP0_CLR_OUT | EP0_DATA_END bits
*/
static void lh7a40x_ep0_setup(struct lh7a40x_udc *dev, u32 csr)
{
struct lh7a40x_ep *ep = &dev->ep[0];
struct usb_ctrlrequest ctrl;
int i, bytes, is_in;
DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr);
/* Nuke all previous transfers */
nuke(ep, -EPROTO);
/* read control req from fifo (8 bytes) */
bytes = lh7a40x_fifo_read(ep, (unsigned char *)&ctrl, 8);
DEBUG_SETUP("Read CTRL REQ %d bytes\n", bytes);
DEBUG_SETUP("CTRL.bRequestType = %d (is_in %d)\n", ctrl.bRequestType,
ctrl.bRequestType == USB_DIR_IN);
DEBUG_SETUP("CTRL.bRequest = %d\n", ctrl.bRequest);
DEBUG_SETUP("CTRL.wLength = %d\n", ctrl.wLength);
DEBUG_SETUP("CTRL.wValue = %d (%d)\n", ctrl.wValue, ctrl.wValue >> 8);
DEBUG_SETUP("CTRL.wIndex = %d\n", ctrl.wIndex);
/* Set direction of EP0 */
if (likely(ctrl.bRequestType & USB_DIR_IN)) {
ep->bEndpointAddress |= USB_DIR_IN;
is_in = 1;
} else {
ep->bEndpointAddress &= ~USB_DIR_IN;
is_in = 0;
}
dev->req_pending = 1;
/* Handle some SETUP packets ourselves */
switch (ctrl.bRequest) {
case USB_REQ_SET_ADDRESS:
if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE))
break;
DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue);
udc_set_address(dev, ctrl.wValue);
usb_set((EP0_CLR_OUT | EP0_DATA_END), USB_EP0_CSR);
return;
case USB_REQ_GET_STATUS:{
if (lh7a40x_handle_get_status(dev, &ctrl) == 0)
return;
case USB_REQ_CLEAR_FEATURE:
case USB_REQ_SET_FEATURE:
if (ctrl.bRequestType == USB_RECIP_ENDPOINT) {
struct lh7a40x_ep *qep;
int ep_num = (ctrl.wIndex & 0x0f);
/* Support only HALT feature */
if (ctrl.wValue != 0 || ctrl.wLength != 0
|| ep_num > 3 || ep_num < 1)
break;
qep = &dev->ep[ep_num];
if (ctrl.bRequest == USB_REQ_SET_FEATURE) {
DEBUG_SETUP("SET_FEATURE (%d)\n",
ep_num);
lh7a40x_set_halt(&qep->ep, 1);
} else {
DEBUG_SETUP("CLR_FEATURE (%d)\n",
ep_num);
lh7a40x_set_halt(&qep->ep, 0);
}
usb_set_index(0);
/* Reply with a ZLP on next IN token */
usb_set((EP0_CLR_OUT | EP0_DATA_END),
USB_EP0_CSR);
return;
}
break;
}
default:
break;
}
if (likely(dev->driver)) {
/* device-2-host (IN) or no data setup command, process immediately */
spin_unlock(&dev->lock);
i = dev->driver->setup(&dev->gadget, &ctrl);
spin_lock(&dev->lock);
if (i < 0) {
/* setup processing failed, force stall */
DEBUG_SETUP
(" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n",
i);
usb_set_index(0);
usb_set((EP0_CLR_OUT | EP0_DATA_END | EP0_SEND_STALL),
USB_EP0_CSR);
/* ep->stopped = 1; */
dev->ep0state = WAIT_FOR_SETUP;
}
}
}
/*
* DATA_STATE_NEED_ZLP
*/
static void lh7a40x_ep0_in_zlp(struct lh7a40x_udc *dev, u32 csr)
{
DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);
/* c.f. Table 15-14 */
usb_set((EP0_IN_PKT_RDY | EP0_DATA_END), USB_EP0_CSR);
dev->ep0state = WAIT_FOR_SETUP;
}
/*
* handle ep0 interrupt
*/
static void lh7a40x_handle_ep0(struct lh7a40x_udc *dev, u32 intr)
{
struct lh7a40x_ep *ep = &dev->ep[0];
u32 csr;
/* Set index 0 */
usb_set_index(0);
csr = usb_read(USB_EP0_CSR);
DEBUG_EP0("%s: csr = %x\n", __FUNCTION__, csr);
/*
* For overview of what we should be doing see c.f. Chapter 18.1.2.4
* We will follow that outline here modified by our own global state
* indication which provides hints as to what we think should be
* happening..
*/
/*
* if SENT_STALL is set
* - clear the SENT_STALL bit
*/
if (csr & EP0_SENT_STALL) {
DEBUG_EP0("%s: EP0_SENT_STALL is set: %x\n", __FUNCTION__, csr);
usb_clear((EP0_SENT_STALL | EP0_SEND_STALL), USB_EP0_CSR);
nuke(ep, -ECONNABORTED);
dev->ep0state = WAIT_FOR_SETUP;
return;
}
/*
* if a transfer is in progress && IN_PKT_RDY and OUT_PKT_RDY are clear
* - fill EP0 FIFO
* - if last packet
* - set IN_PKT_RDY | DATA_END
* - else
* set IN_PKT_RDY
*/
if (!(csr & (EP0_IN_PKT_RDY | EP0_OUT_PKT_RDY))) {
DEBUG_EP0("%s: IN_PKT_RDY and OUT_PKT_RDY are clear\n",
__FUNCTION__);
switch (dev->ep0state) {
case DATA_STATE_XMIT:
DEBUG_EP0("continue with DATA_STATE_XMIT\n");
lh7a40x_ep0_in(dev, csr);
return;
case DATA_STATE_NEED_ZLP:
DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n");
lh7a40x_ep0_in_zlp(dev, csr);
return;
default:
/* Stall? */
DEBUG_EP0("Odd state!! state = %s\n",
state_names[dev->ep0state]);
dev->ep0state = WAIT_FOR_SETUP;
/* nuke(ep, 0); */
/* usb_set(EP0_SEND_STALL, ep->csr1); */
break;
}
}
/*
* if SETUP_END is set
* - abort the last transfer
* - set SERVICED_SETUP_END_BIT
*/
if (csr & EP0_SETUP_END) {
DEBUG_EP0("%s: EP0_SETUP_END is set: %x\n", __FUNCTION__, csr);
usb_set(EP0_CLR_SETUP_END, USB_EP0_CSR);
nuke(ep, 0);
dev->ep0state = WAIT_FOR_SETUP;
}
/*
* if EP0_OUT_PKT_RDY is set
* - read data packet from EP0 FIFO
* - decode command
* - if error
* set SERVICED_OUT_PKT_RDY | DATA_END bits | SEND_STALL
* - else
* set SERVICED_OUT_PKT_RDY | DATA_END bits
*/
if (csr & EP0_OUT_PKT_RDY) {
DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__,
csr);
switch (dev->ep0state) {
case WAIT_FOR_SETUP:
DEBUG_EP0("WAIT_FOR_SETUP\n");
lh7a40x_ep0_setup(dev, csr);
break;
case DATA_STATE_RECV:
DEBUG_EP0("DATA_STATE_RECV\n");
lh7a40x_ep0_out(dev, csr);
break;
default:
/* send stall? */
DEBUG_EP0("strange state!! 2. send stall? state = %d\n",
dev->ep0state);
break;
}
}
}
static void lh7a40x_ep0_kick(struct lh7a40x_udc *dev, struct lh7a40x_ep *ep)
{
u32 csr;
usb_set_index(0);
csr = usb_read(USB_EP0_CSR);
DEBUG_EP0("%s: %x\n", __FUNCTION__, csr);
/* Clear "out packet ready" */
usb_set(EP0_CLR_OUT, USB_EP0_CSR);
if (ep_is_in(ep)) {
dev->ep0state = DATA_STATE_XMIT;
lh7a40x_ep0_in(dev, csr);
} else {
dev->ep0state = DATA_STATE_RECV;
lh7a40x_ep0_out(dev, csr);
}
}
/* ---------------------------------------------------------------------------
* device-scoped parts of the api to the usb controller hardware
* ---------------------------------------------------------------------------
*/
static int lh7a40x_udc_get_frame(struct usb_gadget *_gadget)
{
u32 frame1 = usb_read(USB_FRM_NUM1); /* Least significant 8 bits */
u32 frame2 = usb_read(USB_FRM_NUM2); /* Most significant 3 bits */
DEBUG("%s, %p\n", __FUNCTION__, _gadget);
return ((frame2 & 0x07) << 8) | (frame1 & 0xff);
}
static int lh7a40x_udc_wakeup(struct usb_gadget *_gadget)
{
/* host may not have enabled remote wakeup */
/*if ((UDCCS0 & UDCCS0_DRWF) == 0)
return -EHOSTUNREACH;
udc_set_mask_UDCCR(UDCCR_RSM); */
return -ENOTSUPP;
}
static const struct usb_gadget_ops lh7a40x_udc_ops = {
.get_frame = lh7a40x_udc_get_frame,
.wakeup = lh7a40x_udc_wakeup,
/* current versions must always be self-powered */
};
static void nop_release(struct device *dev)
{
DEBUG("%s %s\n", __FUNCTION__, dev->bus_id);
}
static struct lh7a40x_udc memory = {
.usb_address = 0,
.gadget = {
.ops = &lh7a40x_udc_ops,
.ep0 = &memory.ep[0].ep,
.name = driver_name,
.dev = {
.bus_id = "gadget",
.release = nop_release,
},
},
/* control endpoint */
.ep[0] = {
.ep = {
.name = ep0name,
.ops = &lh7a40x_ep_ops,
.maxpacket = EP0_PACKETSIZE,
},
.dev = &memory,
.bEndpointAddress = 0,
.bmAttributes = 0,
.ep_type = ep_control,
.fifo = io_p2v(USB_EP0_FIFO),
.csr1 = USB_EP0_CSR,
.csr2 = USB_EP0_CSR,
},
/* first group of endpoints */
.ep[1] = {
.ep = {
.name = "ep1in-bulk",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 1,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_in,
.fifo = io_p2v(USB_EP1_FIFO),
.csr1 = USB_IN_CSR1,
.csr2 = USB_IN_CSR2,
},
.ep[2] = {
.ep = {
.name = "ep2out-bulk",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = 2,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.ep_type = ep_bulk_out,
.fifo = io_p2v(USB_EP2_FIFO),
.csr1 = USB_OUT_CSR1,
.csr2 = USB_OUT_CSR2,
},
.ep[3] = {
.ep = {
.name = "ep3in-int",
.ops = &lh7a40x_ep_ops,
.maxpacket = 64,
},
.dev = &memory,
.bEndpointAddress = USB_DIR_IN | 3,
.bmAttributes = USB_ENDPOINT_XFER_INT,
.ep_type = ep_interrupt,
.fifo = io_p2v(USB_EP3_FIFO),
.csr1 = USB_IN_CSR1,
.csr2 = USB_IN_CSR2,
},
};
/*
* probe - binds to the platform device
*/
static int lh7a40x_udc_probe(struct device *_dev)
{
struct lh7a40x_udc *dev = &memory;
int retval;
DEBUG("%s: %p\n", __FUNCTION__, _dev);
spin_lock_init(&dev->lock);
dev->dev = _dev;
device_initialize(&dev->gadget.dev);
dev->gadget.dev.parent = _dev;
the_controller = dev;
dev_set_drvdata(_dev, dev);
udc_disable(dev);
udc_reinit(dev);
/* irq setup after old hardware state is cleaned up */
retval =
request_irq(IRQ_USBINTR, lh7a40x_udc_irq, SA_INTERRUPT, driver_name,
dev);
if (retval != 0) {
DEBUG(KERN_ERR "%s: can't get irq %i, err %d\n", driver_name,
IRQ_USBINTR, retval);
return -EBUSY;
}
create_proc_files();
return retval;
}
static int lh7a40x_udc_remove(struct device *_dev)
{
struct lh7a40x_udc *dev = _dev->driver_data;
DEBUG("%s: %p\n", __FUNCTION__, dev);
udc_disable(dev);
remove_proc_files();
usb_gadget_unregister_driver(dev->driver);
free_irq(IRQ_USBINTR, dev);
dev_set_drvdata(_dev, 0);
the_controller = 0;
return 0;
}
/*-------------------------------------------------------------------------*/
static struct device_driver udc_driver = {
.name = (char *)driver_name,
.bus = &platform_bus_type,
.probe = lh7a40x_udc_probe,
.remove = lh7a40x_udc_remove
/* FIXME power management support */
/* .suspend = ... disable UDC */
/* .resume = ... re-enable UDC */
};
static int __init udc_init(void)
{
DEBUG("%s: %s version %s\n", __FUNCTION__, driver_name, DRIVER_VERSION);
return driver_register(&udc_driver);
}
static void __exit udc_exit(void)
{
driver_unregister(&udc_driver);
}
module_init(udc_init);
module_exit(udc_exit);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_AUTHOR("Mikko Lahteenmaki, Bo Henriksen");
MODULE_LICENSE("GPL");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -