in gadget/udc/omap_udc.c [1389:1714]
static void ep0_irq(struct omap_udc *udc, u16 irq_src)
{
struct omap_ep *ep0 = &udc->ep[0];
struct omap_req *req = NULL;
ep0->irqs++;
/* Clear any pending requests and then scrub any rx/tx state
* before starting to handle the SETUP request.
*/
if (irq_src & UDC_SETUP) {
u16 ack = irq_src & (UDC_EP0_TX|UDC_EP0_RX);
nuke(ep0, 0);
if (ack) {
omap_writew(ack, UDC_IRQ_SRC);
irq_src = UDC_SETUP;
}
}
/* IN/OUT packets mean we're in the DATA or STATUS stage.
* This driver uses only uses protocol stalls (ep0 never halts),
* and if we got this far the gadget driver already had a
* chance to stall. Tries to be forgiving of host oddities.
*
* NOTE: the last chance gadget drivers have to stall control
* requests is during their request completion callback.
*/
if (!list_empty(&ep0->queue))
req = container_of(ep0->queue.next, struct omap_req, queue);
/* IN == TX to host */
if (irq_src & UDC_EP0_TX) {
int stat;
omap_writew(UDC_EP0_TX, UDC_IRQ_SRC);
omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
stat = omap_readw(UDC_STAT_FLG);
if (stat & UDC_ACK) {
if (udc->ep0_in) {
/* write next IN packet from response,
* or set up the status stage.
*/
if (req)
stat = write_fifo(ep0, req);
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
if (!req && udc->ep0_pending) {
omap_writew(UDC_EP_SEL, UDC_EP_NUM);
omap_writew(UDC_CLR_EP, UDC_CTRL);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
omap_writew(0, UDC_EP_NUM);
udc->ep0_pending = 0;
} /* else: 6 wait states before it'll tx */
} else {
/* ack status stage of OUT transfer */
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
if (req)
done(ep0, req, 0);
}
req = NULL;
} else if (stat & UDC_STALL) {
omap_writew(UDC_CLR_HALT, UDC_CTRL);
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
} else {
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
}
}
/* OUT == RX from host */
if (irq_src & UDC_EP0_RX) {
int stat;
omap_writew(UDC_EP0_RX, UDC_IRQ_SRC);
omap_writew(UDC_EP_SEL, UDC_EP_NUM);
stat = omap_readw(UDC_STAT_FLG);
if (stat & UDC_ACK) {
if (!udc->ep0_in) {
stat = 0;
/* read next OUT packet of request, maybe
* reactiviting the fifo; stall on errors.
*/
stat = read_fifo(ep0, req);
if (!req || stat < 0) {
omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
udc->ep0_pending = 0;
stat = 0;
} else if (stat == 0)
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
omap_writew(0, UDC_EP_NUM);
/* activate status stage */
if (stat == 1) {
done(ep0, req, 0);
/* that may have STALLed ep0... */
omap_writew(UDC_EP_SEL | UDC_EP_DIR,
UDC_EP_NUM);
omap_writew(UDC_CLR_EP, UDC_CTRL);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
udc->ep0_pending = 0;
}
} else {
/* ack status stage of IN transfer */
omap_writew(0, UDC_EP_NUM);
if (req)
done(ep0, req, 0);
}
} else if (stat & UDC_STALL) {
omap_writew(UDC_CLR_HALT, UDC_CTRL);
omap_writew(0, UDC_EP_NUM);
} else {
omap_writew(0, UDC_EP_NUM);
}
}
/* SETUP starts all control transfers */
if (irq_src & UDC_SETUP) {
union u {
u16 word[4];
struct usb_ctrlrequest r;
} u;
int status = -EINVAL;
struct omap_ep *ep;
/* read the (latest) SETUP message */
do {
omap_writew(UDC_SETUP_SEL, UDC_EP_NUM);
/* two bytes at a time */
u.word[0] = omap_readw(UDC_DATA);
u.word[1] = omap_readw(UDC_DATA);
u.word[2] = omap_readw(UDC_DATA);
u.word[3] = omap_readw(UDC_DATA);
omap_writew(0, UDC_EP_NUM);
} while (omap_readw(UDC_IRQ_SRC) & UDC_SETUP);
#define w_value le16_to_cpu(u.r.wValue)
#define w_index le16_to_cpu(u.r.wIndex)
#define w_length le16_to_cpu(u.r.wLength)
/* Delegate almost all control requests to the gadget driver,
* except for a handful of ch9 status/feature requests that
* hardware doesn't autodecode _and_ the gadget API hides.
*/
udc->ep0_in = (u.r.bRequestType & USB_DIR_IN) != 0;
udc->ep0_set_config = 0;
udc->ep0_pending = 1;
ep0->stopped = 0;
ep0->ackwait = 0;
switch (u.r.bRequest) {
case USB_REQ_SET_CONFIGURATION:
/* udc needs to know when ep != 0 is valid */
if (u.r.bRequestType != USB_RECIP_DEVICE)
goto delegate;
if (w_length != 0)
goto do_stall;
udc->ep0_set_config = 1;
udc->ep0_reset_config = (w_value == 0);
VDBG("set config %d\n", w_value);
/* update udc NOW since gadget driver may start
* queueing requests immediately; clear config
* later if it fails the request.
*/
if (udc->ep0_reset_config)
omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
else
omap_writew(UDC_DEV_CFG, UDC_SYSCON2);
update_otg(udc);
goto delegate;
case USB_REQ_CLEAR_FEATURE:
/* clear endpoint halt */
if (u.r.bRequestType != USB_RECIP_ENDPOINT)
goto delegate;
if (w_value != USB_ENDPOINT_HALT
|| w_length != 0)
goto do_stall;
ep = &udc->ep[w_index & 0xf];
if (ep != ep0) {
if (w_index & USB_DIR_IN)
ep += 16;
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|| !ep->ep.desc)
goto do_stall;
use_ep(ep, 0);
omap_writew(udc->clr_halt, UDC_CTRL);
ep->ackwait = 0;
if (!(ep->bEndpointAddress & USB_DIR_IN)) {
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
ep->ackwait = 1 + ep->double_buf;
}
/* NOTE: assumes the host behaves sanely,
* only clearing real halts. Else we may
* need to kill pending transfers and then
* restart the queue... very messy for DMA!
*/
}
VDBG("%s halt cleared by host\n", ep->name);
goto ep0out_status_stage;
case USB_REQ_SET_FEATURE:
/* set endpoint halt */
if (u.r.bRequestType != USB_RECIP_ENDPOINT)
goto delegate;
if (w_value != USB_ENDPOINT_HALT
|| w_length != 0)
goto do_stall;
ep = &udc->ep[w_index & 0xf];
if (w_index & USB_DIR_IN)
ep += 16;
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC
|| ep == ep0 || !ep->ep.desc)
goto do_stall;
if (use_dma && ep->has_dma) {
/* this has rude side-effects (aborts) and
* can't really work if DMA-IN is active
*/
DBG("%s host set_halt, NYET\n", ep->name);
goto do_stall;
}
use_ep(ep, 0);
/* can't halt if fifo isn't empty... */
omap_writew(UDC_CLR_EP, UDC_CTRL);
omap_writew(UDC_SET_HALT, UDC_CTRL);
VDBG("%s halted by host\n", ep->name);
ep0out_status_stage:
status = 0;
omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
omap_writew(UDC_CLR_EP, UDC_CTRL);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
udc->ep0_pending = 0;
break;
case USB_REQ_GET_STATUS:
/* USB_ENDPOINT_HALT status? */
if (u.r.bRequestType != (USB_DIR_IN|USB_RECIP_ENDPOINT))
goto intf_status;
/* ep0 never stalls */
if (!(w_index & 0xf))
goto zero_status;
/* only active endpoints count */
ep = &udc->ep[w_index & 0xf];
if (w_index & USB_DIR_IN)
ep += 16;
if (!ep->ep.desc)
goto do_stall;
/* iso never stalls */
if (ep->bmAttributes == USB_ENDPOINT_XFER_ISOC)
goto zero_status;
/* FIXME don't assume non-halted endpoints!! */
ERR("%s status, can't report\n", ep->ep.name);
goto do_stall;
intf_status:
/* return interface status. if we were pedantic,
* we'd detect non-existent interfaces, and stall.
*/
if (u.r.bRequestType
!= (USB_DIR_IN|USB_RECIP_INTERFACE))
goto delegate;
zero_status:
/* return two zero bytes */
omap_writew(UDC_EP_SEL|UDC_EP_DIR, UDC_EP_NUM);
omap_writew(0, UDC_DATA);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
omap_writew(UDC_EP_DIR, UDC_EP_NUM);
status = 0;
VDBG("GET_STATUS, interface %d\n", w_index);
/* next, status stage */
break;
default:
delegate:
/* activate the ep0out fifo right away */
if (!udc->ep0_in && w_length) {
omap_writew(0, UDC_EP_NUM);
omap_writew(UDC_SET_FIFO_EN, UDC_CTRL);
}
/* gadget drivers see class/vendor specific requests,
* {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION},
* and more
*/
VDBG("SETUP %02x.%02x v%04x i%04x l%04x\n",
u.r.bRequestType, u.r.bRequest,
w_value, w_index, w_length);
#undef w_value
#undef w_index
#undef w_length
/* The gadget driver may return an error here,
* causing an immediate protocol stall.
*
* Else it must issue a response, either queueing a
* response buffer for the DATA stage, or halting ep0
* (causing a protocol stall, not a real halt). A
* zero length buffer means no DATA stage.
*
* It's fine to issue that response after the setup()
* call returns, and this IRQ was handled.
*/
udc->ep0_setup = 1;
spin_unlock(&udc->lock);
status = udc->driver->setup(&udc->gadget, &u.r);
spin_lock(&udc->lock);
udc->ep0_setup = 0;
}
if (status < 0) {
do_stall:
VDBG("req %02x.%02x protocol STALL; stat %d\n",
u.r.bRequestType, u.r.bRequest, status);
if (udc->ep0_set_config) {
if (udc->ep0_reset_config)
WARNING("error resetting config?\n");
else
omap_writew(UDC_CLR_CFG, UDC_SYSCON2);
}
omap_writew(UDC_STALL_CMD, UDC_SYSCON2);
udc->ep0_pending = 0;
}
}
}