Rx setup (8) 0009010000000000 Setup device request Configured 1-- addressed Rx setup (8) 2120000000000700 Setup interface request Tick 1 -- bytes: 0 -- int, sof, xof = 905 831 96 Tick 2 -- bytes: 0 -- int, sof, xof = 1854 1780 96 Tick 3 -- bytes: 0 -- int, sof, xof = 2803 2729 96 Tick 4 -- bytes: 0 -- int, sof, xof = 3752 3678 96 Tick 5 -- bytes: 0 -- int, sof, xof = 4702 4628 96 Tick 6 -- bytes: 0 -- int, sof, xof = 5651 5577 96 Rx setup (8) 2122030000000000 Setup interface request Rx setup (8) 2122000000000000 Setup interface request Tick 7 -- bytes: 512 -- int, sof, xof = 6627 6531 120The above is a short transcript of my debug log showing the very end of enumeration, then later some activity when I send a 512 byte data packet. Ignore the setup device request at the top. I include that simply because it is the last exchange as part of enumeration.
The first message comes from DCD_HandleRxStatusQueueLevel_ISR() in driver/interrupts.c This is called from USBD_OTG_ISR_Handler() in the same file, which is the overall USB interrupt handler. The specific cause of the interrupt is that the "rxstsqlvl" bit is set, which means that something is in the Rx FIFO.
This routine reads a status "header" word from the fifo, then discovers that the STS_SETUP_UPDT bit in that word is set, indicating that a setup packet (of size 8 bytes) follows. It reads this packet into a special buffer "pdev->dev.setup_packet" set aside for setup packets, then considers its work done!
The next message come from CORE_SetupStage() in library/core.c -- how does it know that a setup packet has been read and is waiting to be processed? This routine is called from DCD_HandleOutEP_ISR() in driver/interrupts.c, which is called from USBD_OTG_ISR_Handler() (the overall USB interrupt handler). It is responding now to a different interrupt cause, namely "outepintr". The endpoint interrupt handler has its own status register and it discovers that the "setup" bit is set, indicating that the setup phase is done for a control endpoint and thus that a setup packet is available, so it calls CORE_SetupStage() to process the setup packet.
A setup packet looks like this:
struct usb_setup_req { uint8_t bmRequest; 0x21 uint8_t bRequest; 0x22 uint16_t wValue; 0x0003 or 0x0000 uint16_t wIndex; 0 uint16_t wLength; 0 };The routine USBD_ParseSetupRequest() is called to move the 8 bytes from the buffer in the pdev area into this structure, performing byte swapping as needed (for the last 3 fields). Then the low 5 bits of the bmRequest field (bmRequest & 0x1f) drives a switch/case and in our case the request is 0x01 for interface. This causes USBD_StdItfReq() to be called -- also in library/core.c
This routine checks the USB status -- if we are not configured, the request is rejected. Index specifies which interface the request is aimed at (here this is interface 0). This is validated, then CLASS_Setup() is called in vcp/cdc.c Now we examine bmRequest again using a mask of 0x60 -- the 0x20 bit indicates that this is a class request, but there is no data (wLength is 0) so we call
VCP_Ctrl(req->bRequest, (uint8_t*)&req->wValue, sizeof(req->wValue));This routine is in vcp/vcp.c The switch is now on the second byte (bRequest). The value 0x22 is "SET_CONTROL_LINE_STATE" and the code executed is:
linecoding.bitrate = (uint32_t)(Buf[0] | (Buf[1] << 8)); VCP_DTRHIGH = (Buf[0] & 0x1); VCP_RTSHIGH = (Buf[0] & 0x2)>>1;It looks to me that (ignoring the linecoding business) the first call with Value "3" sets DTR and RTS high. The second call, with Value "0" sets them low again.
The linecoding information can be set and is also available to be read back, but does nothing whatsoever in the code.
After adding some more messages, I now see this when I send a single 512 byte packet of data to the USB device:
Tick 12 -- bytes: 0 -- int, sof, xof = 11356 11282 96 Rx setup (8) 2122030000000000 Setup interface request DTR/RTS set: 3 zero length status Rx setup (8) 2122000000000000 Setup interface request DTR/RTS set: 0 zero length status Tick 13 -- bytes: 512 -- int, sof, xof = 12332 12236 120
volatile uint8_t VCP_DTRHIGH = 0; volatile uint8_t VCP_RTSHIGH = 0; uint8_t VCPGetDTR(void) { return VCP_DTRHIGH; } uint8_t VCPGetRTS(void) { return VCP_RTSHIGH; }So, we have two variables (which ought to be static) and a pair of accessor routines which are currently never used.
To explain DTR and RTS we need to go back in time. Consider at computer and a modem. The computer is called DTE (data terminal equipment) for this scenario and is responsible for control of the DTR and RTS signals. The modem is called DCE (data communication equipment) and is expected to respond to these signals.
Our USB device acts as the DCE here. When it sees DTR (data terminal ready), it knows that the computer (host) is ready to communicate. It would assert DSR (data set ready) to indicate that it is also ready, but I see nothing dealing with that in our code.
The DTE sends the RTS signal (request to send) and expects to see CTS (clear to send) as a response. This is good old hardware handshaking, but again I see nothing with respect to CTS in our code.
Tom's Computer Info / tom@mmto.org