/* Calculate next state for EP0 */
ep->ep0.state = ep0_state_data;
ep->ep0.dir_in = !!(crq.bRequestType & USB_DIR_IN);
/* If this is the vHub, we handle requests differently */
std_req_rc = std_req_driver; if (ep->dev == NULL) { if ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
std_req_rc = ast_vhub_std_hub_request(ep, &crq); elseif ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_CLASS)
std_req_rc = ast_vhub_class_hub_request(ep, &crq); else
std_req_rc = std_req_stall;
} elseif ((crq.bRequestType & USB_TYPE_MASK) == USB_TYPE_STANDARD)
std_req_rc = ast_vhub_std_dev_request(ep, &crq);
/* Act upon result */ switch(std_req_rc) { case std_req_complete: goto complete; case std_req_stall: goto stall; case std_req_driver: break; case std_req_data: return;
}
/* Pass request up to the gadget driver */ if (WARN_ON(!ep->dev)) goto stall; if (ep->dev->driver) {
EPDBG(ep, "forwarding to gadget...\n");
spin_unlock(&ep->vhub->lock);
rc = ep->dev->driver->setup(&ep->dev->gadget, &crq);
spin_lock(&ep->vhub->lock);
EPDBG(ep, "driver returned %d\n", rc);
} else {
EPDBG(ep, "no gadget for request !\n");
} if (rc >= 0) return;
/* Are we getting more than asked ? */ if (len > remain) {
EPDBG(ep, "receiving too much (ovf: %d) !\n",
len - remain);
len = remain;
rc = -EOVERFLOW;
}
/* Hardware return wrong data len */ if (len < ep->ep.maxpacket && len != remain) {
EPDBG(ep, "using expected data len instead\n");
len = remain;
}
switch(ep->ep0.state) { case ep0_state_token: /* There should be no request queued in that state... */ if (req) {
dev_warn(dev, "request present while in TOKEN state\n");
ast_vhub_nuke(ep, -EINVAL);
}
dev_warn(dev, "ack while in TOKEN state\n");
stall = true; break; case ep0_state_data: /* Check the state bits corresponding to our direction */ if ((ep->ep0.dir_in && (stat & VHUB_EP0_TX_BUFF_RDY)) ||
(!ep->ep0.dir_in && (stat & VHUB_EP0_RX_BUFF_RDY)) ||
(ep->ep0.dir_in != in_ack)) { /* In that case, ignore interrupt */
dev_warn(dev, "irq state mismatch"); break;
} /* *Weareindataphaseandthere'snorequest,somethingis *wrong,stall
*/ if (!req) {
dev_warn(dev, "data phase, no request\n");
stall = true; break;
}
/* We have a request, handle data transfers */ if (ep->ep0.dir_in)
ast_vhub_ep0_do_send(ep, req); else
ast_vhub_ep0_do_receive(ep, req, VHUB_EP0_RX_LEN(stat)); return; case ep0_state_status: /* Nuke stale requests */ if (req) {
dev_warn(dev, "request present while in STATUS state\n");
ast_vhub_nuke(ep, -EINVAL);
}
/* *Ifthestatusphasecompleteswiththewrongack,stall *theendpointjustincase,toabortwhateverthehost *wasdoing.
*/ if (ep->ep0.dir_in == in_ack) {
dev_warn(dev, "status direction mismatch\n");
stall = true;
} break; case ep0_state_stall: /* *Thereshouldn'tbeanyrequestleft,butnukejustincase *otherwisethestalerequestwillblocksubsequentones
*/
ast_vhub_nuke(ep, -EIO); break;
}
/* Reset to token state or stall */ if (stall) {
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_stall;
} else
ep->ep0.state = ep0_state_token;
}
/* Not endpoint 0 ? */ if (WARN_ON(ep->d_idx != 0)) return -EINVAL;
/* Disabled device */ if (ep->dev && !ep->dev->enabled) return -ESHUTDOWN;
/* Data, no buffer and not internal ? */ if (u_req->length && !u_req->buf && !req->internal) {
dev_warn(dev, "Request with no buffer !\n"); return -EINVAL;
}
/* EP0 can only support a single request at a time */ if (!list_empty(&ep->queue) ||
ep->ep0.state == ep0_state_token ||
ep->ep0.state == ep0_state_stall) {
dev_warn(dev, "EP0: Request in wrong state\n");
EPVDBG(ep, "EP0: list_empty=%d state=%d\n",
list_empty(&ep->queue), ep->ep0.state);
spin_unlock_irqrestore(&vhub->lock, flags); return -EBUSY;
}
/* Add request to list and kick processing if empty */
list_add_tail(&req->queue, &ep->queue);
if (ep->ep0.dir_in) { /* IN request, send data */
ast_vhub_ep0_do_send(ep, req);
} elseif (u_req->length == 0) { /* 0-len request, send completion as rx */
EPVDBG(ep, "0-length rx completion\n");
ep->ep0.state = ep0_state_status;
writel(VHUB_EP0_TX_BUFF_RDY, ep->ep0.ctlstat);
ast_vhub_done(ep, req, 0);
} else { /* OUT request, start receiver */
ast_vhub_ep0_rx_prime(ep);
}
/* We do stall the EP to clean things up in HW */
writel(VHUB_EP0_CTRL_STALL, ep->ep0.ctlstat);
ep->ep0.state = ep0_state_status;
ep->ep0.dir_in = false;
rc = 0;
}
spin_unlock_irqrestore(&vhub->lock, flags); return rc;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.