in arch/mips/src/pic32mx/pic32mx_usbdev.c [1866:2436]
static void pic32mx_ep0setup(struct pic32mx_usbdev_s *priv)
{
volatile struct usbotg_bdtentry_s *bdt;
struct pic32mx_ep_s *ep0;
struct pic32mx_ep_s *privep;
union wb_u value;
union wb_u index;
union wb_u len;
union wb_u response;
uint16_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];
pic32mx_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;
/* Initialize for the SETUP */
priv->ctrlstate = CTRLSTATE_WAITSETUP;
/* And 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);
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);
/* Dispatch any non-standard requests */
if ((priv->ctrl.type & USB_REQ_TYPE_MASK) != USB_REQ_TYPE_STANDARD)
{
usbtrace(TRACE_INTDECODE(PIC32MX_TRACEINTID_NOSTDREQ),
priv->ctrl.type);
/* Let the class implementation handle all non-standar requests */
pic32mx_dispatchrequest(priv);
dispatched = true;
goto resume_packet_processing; /* Sorry about the goto */
}
/* 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(PIC32MX_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(PIC32MX_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(PIC32MX_TRACEINTID_EPGETSTATUS),
epno);
if (epno >= PIC32MX_NENDPOINTS)
{
usbtrace(
TRACE_DEVERROR(PIC32MX_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(PIC32MX_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(PIC32MX_TRACEERR_BADDEVGETSTATUS), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
break;
case USB_REQ_RECIPIENT_INTERFACE:
{
usbtrace(
TRACE_INTDECODE(PIC32MX_TRACEINTID_IFGETSTATUS), 0);
response.w = 0;
nbytes = 2; /* Response size: 2 bytes */
}
break;
default:
{
usbtrace(TRACE_DEVERROR(PIC32MX_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(PIC32MX_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_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
*/
pic32mx_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 < PIC32MX_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = false;
ret = pic32mx_epstall(&privep->ep, true);
UNUSED(ret);
}
else
{
usbtrace(
TRACE_DEVERROR(PIC32MX_TRACEERR_BADCLEARFEATURE), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
else
{
/* Let the class implementation handle all other recipients. */
pic32mx_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(PIC32MX_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_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
*/
pic32mx_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 < PIC32MX_NENDPOINTS && index.b[MSB] == 0 &&
value.w == USB_FEATURE_ENDPOINTHALT && len.w == 0)
{
privep = &priv->eplist[epno];
privep->halted = true;
ret = pic32mx_epstall(&privep->ep, false);
UNUSED(ret);
}
else
{
usbtrace(TRACE_DEVERROR(PIC32MX_TRACEERR_BADSETFEATURE), 0);
priv->ctrlstate = CTRLSTATE_STALL;
}
}
else
{
/* The class driver handles all recipients
* except recipient=endpoint
*/
pic32mx_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(PIC32MX_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(PIC32MX_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 zero- length 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(PIC32MX_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
*/
pic32mx_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(PIC32MX_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(PIC32MX_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
*/
pic32mx_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(PIC32MX_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(PIC32MX_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
*/
pic32mx_dispatchrequest(priv);
dispatched = true;
}
else
{
usbtrace(TRACE_DEVERROR(PIC32MX_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(PIC32MX_TRACEINTID_GETSETIF),
priv->ctrl.type);
pic32mx_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(PIC32MX_TRACEINTID_SYNCHFRAME), 0);
}
break;
default:
{
usbtrace(TRACE_DEVERROR(PIC32MX_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.
*/
resume_packet_processing:
regval = pic32mx_getreg(PIC32MX_USB_CON);
regval &= ~USB_CON_PKTDIS;
pic32mx_putreg(regval, PIC32MX_USB_CON);
/* 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 (!dispatched && (priv->ctrlstate != CTRLSTATE_STALL))
{
/* 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) */
pic32mx_epwrite(ep0, ep0->bdtin, response.b, nbytes);
priv->ctrlstate = CTRLSTATE_WAITSETUP;
}
/* Did we stall? This might have occurred from the above logic OR the
* stall condition may have been set less obviously in
* pic32mx_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
* 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???.
*/
/* Set up the BDT to accept the next setup command. */
pic32mx_ep0nextsetup(priv);
priv->ctrlstate = CTRLSTATE_WAITSETUP;
}