in arch/arm/src/kinetis/kinetis_usbdev.c [1964:2586]
static void khci_ep0setup(struct khci_usbdev_s *priv)
{
volatile struct usbotg_bdtentry_s *bdt;
struct khci_ep_s *ep0;
struct khci_ep_s *privep;
union wb_u value;
union wb_u index;
union wb_u len;
union wb_u response;
uint32_t regval;
bool dispatched = false;
uint8_t epno;
int nbytes = 0; /* Assume zero-length packet */
int ret;
/* Cancel any pending requests. */
ep0 = &priv->eplist[EP0];
khci_cancelrequests(ep0, -EPROTO);
/* Assume NOT stalled; no TX in progress; no RX overrun. Data 0/1 toggling
* is not used on SETUP packets, but any following EP0 IN transfer should
* beginning with DATA1.
*/
ep0->stalled = false;
ep0->rxdata1 = 0;
ep0->txdata1 = 1;
/* Extract the little-endian 16-bit values to host order */
value.w = GETUINT16(priv->ctrl.value);
index.w = GETUINT16(priv->ctrl.index);
len.w = GETUINT16(priv->ctrl.len);
response.w = 0;
/* Check to see if called from the DATA phase of a SETUP Transfer */
if (priv->ctrlstate != CTRLSTATE_SETUP_READY &&
priv->ctrlstate != CTRLSTATE_SETUP_OUT)
{
/* Not the data phase */
uinfo("SETUP: type=%02x req=%02x value=%04x index=%04x len=%04x\n",
priv->ctrl.type, priv->ctrl.req, value.w, index.w, len.w);
/* Is this an setup with OUT and data of length > 0 */
if (USB_REQ_ISOUT(priv->ctrl.type) && len.w > 0)
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_EP0SETUPOUT), len.w);
priv->ep0datlen = 0;
priv->ep0datreq = len.w;
/* At this point priv->ctrl is the setup packet. */
khci_ep0nextsetup(priv);
priv->ctrlstate = CTRLSTATE_SETUP_OUT;
}
else
{
priv->ctrlstate = CTRLSTATE_SETUP_READY;
}
}
if (priv->ctrlstate == CTRLSTATE_SETUP_READY)
{
/* Dispatch any non-standard requests */
if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_NOSTDREQ),
priv->ctrl.type);
/* Let the class implementation handle all non-standard requests */
khci_dispatchrequest(priv);
dispatched = true;
}
else
{
/* Handle standard request. Pick off the things of interest to the
* USB device controller driver; pass what is left to the class
* driver
*/
switch (priv->ctrl.req)
{
case USB_REQ_GETSTATUS:
{
/* type: device-to-host; recipient = device, interface,
* endpoint
* value: 0
* index: zero interface endpoint
* len: 2; data = status
*/
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSTATUS),
priv->ctrl.type);
if (len.w != 2 || (priv->ctrl.type & USB_REQ_DIR_IN) == 0 ||
index.b[MSB] != 0 || value.w != 0)
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADEPGETSTATUS), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
else
{
switch (priv->ctrl.type & USB_REQ_RECIPIENT_MASK)
{
case USB_REQ_RECIPIENT_ENDPOINT:
{
epno = USB_EPNO(index.b[LSB]);
usbtrace(TRACE_INTDECODE(
KHCI_TRACEINTID_EPGETSTATUS), epno);
if (epno >= KHCI_NENDPOINTS)
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADEPGETSTATUS), epno);
priv->ctrlstate = CTRLSTATE_STALL;
}
else
{
privep = &priv->eplist[epno];
response.w = 0; /* Not stalled */
nbytes = 2; /* Response size: 2 bytes */
if (USB_ISEPIN(index.b[LSB]))
{
/* IN endpoint */
bdt = privep->bdtin;
}
else
{
/* OUT endpoint */
bdt = privep->bdtout;
}
/* BSTALL set if stalled */
if ((bdt->status & USB_BDT_BSTALL) != 0)
{
response.b[LSB] = 1; /* Stalled, set bit 0 */
}
}
}
break;
case USB_REQ_RECIPIENT_DEVICE:
{
if (index.w == 0)
{
usbtrace(TRACE_INTDECODE(
KHCI_TRACEINTID_DEVGETSTATUS), 0);
/* Features:
* Remote Wakeup=YES;
* selfpowered=?
*/
response.w = 0;
response.b[LSB] =
(priv->selfpowered <<
USB_FEATURE_SELFPOWERED) |
(priv->rwakeup <<
USB_FEATURE_REMOTEWAKEUP);
nbytes = 2; /* Response size: 2 bytes */
}
else
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADDEVGETSTATUS), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(TRACE_INTDECODE(
KHCI_TRACEINTID_IFGETSTATUS), 0);
response.w = 0;
nbytes = 2; /* Response size: 2 bytes */
}
break;
default:
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADGETSTATUS), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
break;
}
}
}
break;
case USB_REQ_CLEARFEATURE:
{
/* type: host-to-device; recipient = device, interface or
* endpoint
* value: feature selector
* index: zero interface endpoint;
* len: zero, data = none
*/
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_CLEARFEATURE),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
{
/* Disable B device from performing HNP */
#ifdef CONFIG_KINETIS_USBOTG
if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
{
/* Disable HNP */
#warning Missing Logic
}
/* Disable A device HNP support */
else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
{
/* Disable HNP support */
#warning Missing Logic
}
/* Disable alternate HNP support */
else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
{
/* Disable alternate HNP */
#warning Missing Logic
}
else
#endif
/* Disable remote wakeup */
if (value.w == USB_FEATURE_REMOTEWAKEUP)
{
priv->rwakeup = 0;
}
else
{
/* Let the class implementation handle all other
* device features
*/
khci_dispatchrequest(priv);
dispatched = true;
}
}
else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_ENDPOINT)
{
epno = USB_EPNO(index.b[LSB]);
if (epno > 0 && epno <
KHCI_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = false;
ret = khci_epstall(&privep->ep, true);
UNUSED(ret);
}
else
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADCLEARFEATURE), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
else
{
/* Let the class implementation handle all other
* recipients.
*/
khci_dispatchrequest(priv);
dispatched = true;
}
}
break;
case USB_REQ_SETFEATURE:
{
/* type: host-to-device; recipient = device, interface,
* endpoint
* value: feature selector
* index: zero interface endpoint;
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SETFEATURE),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
{
/* Enable B device to perform HNP */
#ifdef CONFIG_KINETIS_USBOTG
if (value.w == USBOTG_FEATURE_B_HNP_ENABLE)
{
/* Enable HNP */
#warning "Missing logic"
}
/* Enable A device HNP supports */
else if (value.w == USBOTG_FEATURE_A_HNP_SUPPORT)
{
/* Enable HNP support */
#warning "Missing logic"
}
/* Another port on the A device supports HNP */
else if (value.w == USBOTG_FEATURE_A_ALT_HNP_SUPPORT)
{
/* Enable alternate HNP */
#warning "Missing logic"
}
else
#endif
if (value.w == USB_FEATURE_REMOTEWAKEUP)
{
priv->rwakeup = 0;
}
else if (value.w == USB_FEATURE_TESTMODE)
{
/* Special case recipient=device test mode */
uinfo("test mode: %d\n", index.w);
}
else
{
/* Let the class implementation handle all other
* device features
*/
khci_dispatchrequest(priv);
dispatched = true;
}
}
else if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_ENDPOINT)
{
/* Handler recipient=endpoint */
epno = USB_EPNO(index.b[LSB]);
if (epno < KHCI_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = true;
ret = khci_epstall(&privep->ep, false);
UNUSED(ret);
}
else
{
usbtrace(TRACE_DEVERROR(
KHCI_TRACEERR_BADSETFEATURE), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
else
{
/* The class driver handles all recipients except
* recipient=endpoint
*/
khci_dispatchrequest(priv);
dispatched = true;
}
}
break;
case USB_REQ_SETADDRESS:
{
/* type: host-to-device; recipient = device
* value: device address
* index: 0
* len: 0; data = none
*/
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_EP0SETUPSETADDRESS),
value.w);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) !=
USB_REQ_RECIPIENT_DEVICE ||
index.w != 0 || len.w != 0 || value.w > 127)
{
usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADSETADDRESS), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
else
{
/* Note that setting of the device address will be
* deferred. A zero-length packet will be sent and the
* device address will be set when the zerolength packet
* transfer completes.
*/
priv->devstate = DEVSTATE_ADDRPENDING;
}
}
break;
case USB_REQ_GETDESCRIPTOR:
/* type: device-to-host; recipient = device
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
case USB_REQ_SETDESCRIPTOR:
/* type: host-to-device; recipient = device
* value: descriptor type and index
* index: 0 or language ID;
* len: descriptor len; data = descriptor
*/
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSETDESC),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE)
{
/* The request seems valid...
* let the class implementation handle it
*/
khci_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADGETSETDESC), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
break;
case USB_REQ_GETCONFIGURATION:
/* type: device-to-host; recipient = device
* value: 0;
* index: 0;
* len: 1; data = configuration value
*/
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE && value.w == 0 &&
index.w == 0 && len.w == 1)
{
/* The request seems valid... let the class
* implementation handle it
*/
khci_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADGETCONFIG), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
break;
case USB_REQ_SETCONFIGURATION:
/* type: host-to-device; recipient = device
* value: configuration value
* index: 0;
* len: 0; data = none
*/
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SETCONFIG),
priv->ctrl.type);
if ((priv->ctrl.type & USB_REQ_RECIPIENT_MASK) ==
USB_REQ_RECIPIENT_DEVICE && index.w == 0 && len.w == 0)
{
/* The request seems valid. Let the class implementation
* handle it
*/
khci_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_BADSETCONFIG), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
break;
case USB_REQ_GETINTERFACE:
/* type: device-to-host; recipient = interface
* value: 0
* index: interface;
* len: 1; data = alt interface
*/
case USB_REQ_SETINTERFACE:
/* type: host-to-device; recipient = interface
* value: alternate setting
* index: interface;
* len: 0; data = none
*/
{
/* Let the class implementation handle the request */
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_GETSETIF),
priv->ctrl.type);
khci_dispatchrequest(priv);
dispatched = true;
}
break;
case USB_REQ_SYNCHFRAME:
/* type: device-to-host; recipient = endpoint
* value: 0
* index: endpoint;
* len: 2; data = frame number
*/
{
usbtrace(TRACE_INTDECODE(KHCI_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(KHCI_TRACEERR_INVALIDCTRLREQ),
priv->ctrl.req);
priv->ctrlstate = CTRLSTATE_STALL;
}
break;
}
}
}
/* PKTDIS bit is set when a Setup Transaction is received. Clear to resume
* packet processing.
*/
regval = khci_getreg(KINETIS_USB0_CTL);
regval &= ~USB_CTL_TXSUSPENDTOKENBUSY;
khci_putreg(regval, KINETIS_USB0_CTL);
/* At this point, the request has been handled and there are three possible
* outcomes:
*
* 1. The setup request was successfully handled above and a response
* packet must be sent (may be a zero length packet).
* 2. The request was successfully handled by the class implementation.
* In case, the EP0 IN response has already been queued and the local
* variable 'dispatched' will be set to true and ctrlstate !=
* CTRLSTATE_STALL;
* 3. An error was detected in either the above logic or by the class
* implementation logic. In either case, priv->state will be set
* CTRLSTATE_STALL to indicate this case.
*
* NOTE:
* Non-standard requests are a special case. They are handled by the
* class implementation and this function returned early above, skipping
* this logic altogether.
*/
if (priv->ctrlstate == CTRLSTATE_SETUP_READY)
{
if (!dispatched)
{
/* The SETUP command was not dispatched to the class driver and the
* SETUP command did not cause a stall. We will respond. First,
* restrict the data length to the length requested in the setup
* packet
*/
if (nbytes > len.w)
{
nbytes = len.w;
}
/* Send the EP0 SETUP response (might be a zero-length packet) */
khci_epwrite(ep0, ep0->bdtin, response.b, nbytes);
}
priv->ctrlstate = CTRLSTATE_WAITSETUP;
}
else if (priv->ctrlstate == CTRLSTATE_STALL)
{
/* Did we stall?
* This might have occurred from the above logic OR the stall condition
* may have been set less obviously in khci_dispatchrequest().
* In either case, we handle the stall condition the same.
*
* However, bad things happen if we try to stall a SETUP packet. So
* lets So lets not. If we wait a bit, things will recover.
* Hmmm.. If we completed the data phase (perhaps by sending a NULL
* packet), then I think we could stall the endpoint and perhaps speed
* things up a bit???.
*/
priv->ctrlstate = CTRLSTATE_WAITSETUP;
}
/* Set up the BDT to accept the next setup command. */
khci_ep0nextsetup(priv);
}