summaryrefslogtreecommitdiff
path: root/openpicc/os/usb/USB-CDC.c
diff options
context:
space:
mode:
authorhenryk <henryk@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2007-11-06 20:26:48 +0000
committerhenryk <henryk@6dc7ffe9-61d6-0310-9af1-9938baff3ed1>2007-11-06 20:26:48 +0000
commit633c646ab36368caf6eaeedd326d9f1835196afd (patch)
treefdd0f60747745d3e528c0f5d8f8895b26fb79633 /openpicc/os/usb/USB-CDC.c
parent3b41196cb6b64cf6ba8ba41d6995428f73d4764a (diff)
Initial import of FreeRTOS code for OpenPICC
git-svn-id: https://svn.openpcd.org:2342/trunk@311 6dc7ffe9-61d6-0310-9af1-9938baff3ed1
Diffstat (limited to 'openpicc/os/usb/USB-CDC.c')
-rw-r--r--openpicc/os/usb/USB-CDC.c953
1 files changed, 953 insertions, 0 deletions
diff --git a/openpicc/os/usb/USB-CDC.c b/openpicc/os/usb/USB-CDC.c
new file mode 100644
index 0000000..4496c55
--- /dev/null
+++ b/openpicc/os/usb/USB-CDC.c
@@ -0,0 +1,953 @@
+/*
+ FreeRTOS.org V4.2.1 - Copyright (C) 2003-2007 Richard Barry.
+
+ This file is part of the FreeRTOS.org distribution.
+
+ FreeRTOS.org is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ FreeRTOS.org is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with FreeRTOS.org; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+ A special exception to the GPL can be applied should you wish to distribute
+ a combined work that includes FreeRTOS.org, without being obliged to provide
+ the source code for any proprietary components. See the licensing section
+ of http://www.FreeRTOS.org for full details of how and when the exception
+ can be applied.
+
+ ***************************************************************************
+ See http://www.FreeRTOS.org for documentation, latest information, license
+ and contact details. Please ensure to read the configuration and relevant
+ port sections of the online documentation.
+
+ Also see http://www.SafeRTOS.com for an IEC 61508 compliant version along
+ with commercial development and support options.
+ ***************************************************************************
+*/
+
+/*
+ USB Communications Device Class driver.
+ Implements task vUSBCDCTask and provides an Abstract Control Model serial
+ interface. Control is through endpoint 0, device-to-host notification is
+ provided by interrupt-in endpoint 3, and raw data is transferred through
+ bulk endpoints 1 and 2.
+
+ - developed from original FreeRTOS HID example by Scott Miller
+ - modified to support 3.2 GCC by najay
+*/
+
+/* Standard includes. */
+#include <string.h>
+#include <stdio.h>
+
+/* Demo board includes. */
+#include <board.h>
+
+/* Scheduler includes. */
+#include <FreeRTOS.h>
+#include <task.h>
+#include <queue.h>
+
+/* Demo app includes. */
+#include <USB-CDC.h>
+#include <descriptors.h>
+
+#define usbNO_BLOCK ( ( portTickType ) 0 )
+
+/* Reset all endpoints */
+static void prvResetEndPoints (void);
+
+/* Clear pull up resistor to detach device from host */
+static void vDetachUSBInterface (void);
+
+/* Set up interface and initialize variables */
+static void vInitUSBInterface (void);
+
+/* Handle control endpoint events. */
+static void prvProcessEndPoint0Interrupt (xISRStatus * pxMessage);
+
+/* Handle standard device requests. */
+static void prvHandleStandardDeviceRequest (xUSB_REQUEST * pxRequest);
+
+/* Handle standard interface requests. */
+static void prvHandleStandardInterfaceRequest (xUSB_REQUEST * pxRequest);
+
+/* Handle endpoint requests. */
+static void prvHandleStandardEndPointRequest (xUSB_REQUEST * pxRequest);
+
+/* Handle class interface requests. */
+static void prvHandleClassInterfaceRequest (xUSB_REQUEST * pxRequest);
+
+/* Prepare control data transfer. prvSendNextSegment starts transfer. */
+static void prvSendControlData (unsigned portCHAR * pucData,
+ unsigned portSHORT usRequestedLength,
+ unsigned portLONG ulLengthLeftToSend,
+ portLONG lSendingDescriptor);
+
+/* Send next segment of data for the control transfer */
+static void prvSendNextSegment (void);
+
+/* Send stall - used to respond to unsupported requests */
+static void prvSendStall (void);
+
+/* Send a zero-length (null) packet */
+static void prvSendZLP (void);
+
+/* Handle requests for standard interface descriptors */
+static void prvGetStandardInterfaceDescriptor (xUSB_REQUEST * pxRequest);
+
+/*------------------------------------------------------------*/
+
+/* File scope static variables */
+static unsigned portCHAR ucUSBConfig = (unsigned portCHAR) 0;
+static unsigned portLONG ulReceivedAddress = (unsigned portLONG) 0;
+static eDRIVER_STATE eDriverState = eNOTHING;
+
+/* Incoming and outgoing control data structures */
+static xCONTROL_MESSAGE pxControlTx;
+static xCONTROL_MESSAGE pxControlRx;
+
+/* Queue holding pointers to pending messages */
+xQueueHandle xUSBInterruptQueue;
+
+/* Queues used to hold received characters, and characters waiting to be
+transmitted. Rx queue must be larger than FIFO size. */
+static xQueueHandle xRxCDC;
+static xQueueHandle xTxCDC;
+
+/* Line coding - 115,200 baud, N-8-1 */
+static const unsigned portCHAR pxLineCoding[] =
+ { 0x00, 0xC2, 0x01, 0x00, 0x00, 0x00, 0x08 };
+
+/* Status variables. */
+static unsigned portCHAR ucControlState;
+static unsigned int uiCurrentBank;
+
+
+/*------------------------------------------------------------*/
+
+
+void
+vUSBCDCTask (void *pvParameters)
+{
+ xISRStatus *pxMessage;
+ unsigned portLONG ulStatus;
+ unsigned portLONG ulRxBytes;
+ unsigned portCHAR ucByte;
+ portBASE_TYPE xByte;
+
+ (void) pvParameters;
+
+ /* Disconnect USB device from hub. For debugging - causes host to register reset */
+ portENTER_CRITICAL ();
+ vDetachUSBInterface ();
+ portEXIT_CRITICAL ();
+
+ vTaskDelay (portTICK_RATE_MS * 60);
+
+ /* Init USB interface */
+ portENTER_CRITICAL ();
+ vInitUSBInterface ();
+ portEXIT_CRITICAL ();
+
+ /* Main task loop. Process incoming endpoint 0 interrupts, handle data transfers. */
+
+ for (;;)
+ {
+ /* Look for data coming from the ISR. */
+ if (xQueueReceive (xUSBInterruptQueue, &pxMessage, usbSHORTEST_DELAY))
+ {
+ if (pxMessage->ulISR & AT91C_UDP_EPINT0)
+ {
+ /* All endpoint 0 interrupts are handled here. */
+ prvProcessEndPoint0Interrupt (pxMessage);
+ }
+
+ if (pxMessage->ulISR & AT91C_UDP_ENDBUSRES)
+ {
+ /* End of bus reset - reset the endpoints and de-configure. */
+ prvResetEndPoints ();
+ }
+ }
+
+ /* See if we're ready to send and receive data. */
+ if (eDriverState == eREADY_TO_SEND && ucControlState)
+ {
+ if ((!
+ (AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_2] & AT91C_UDP_TXPKTRDY))
+ && uxQueueMessagesWaiting (xTxCDC))
+ {
+ for (xByte = 0; xByte < 64; xByte++)
+ {
+ if (!xQueueReceive (xTxCDC, &ucByte, 0))
+ {
+ /* No data buffered to transmit. */
+ break;
+ }
+
+ /* Got a byte to transmit. */
+ AT91C_BASE_UDP->UDP_FDR[usbEND_POINT_2] = ucByte;
+ }
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_2] |= AT91C_UDP_TXPKTRDY;
+ }
+
+ /* Check for incoming data (host-to-device) on endpoint 1. */
+ while (AT91C_BASE_UDP->
+ UDP_CSR[usbEND_POINT_1] & (AT91C_UDP_RX_DATA_BK0 |
+ AT91C_UDP_RX_DATA_BK1))
+ {
+ ulRxBytes =
+ (AT91C_BASE_UDP->
+ UDP_CSR[usbEND_POINT_1] >> 16) & usbRX_COUNT_MASK;
+
+ /* Only process FIFO if there's room to store it in the queue */
+ if (ulRxBytes <
+ (USB_CDC_QUEUE_SIZE - uxQueueMessagesWaiting (xRxCDC)))
+ {
+ while (ulRxBytes--)
+ {
+ ucByte = AT91C_BASE_UDP->UDP_FDR[usbEND_POINT_1];
+ xQueueSend (xRxCDC, &ucByte, 0);
+ }
+
+ /* Release the FIFO */
+ portENTER_CRITICAL ();
+ {
+ ulStatus = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_1];
+ usbCSR_CLEAR_BIT (&ulStatus, uiCurrentBank);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_1] = ulStatus;
+ }
+ portEXIT_CRITICAL ();
+
+ /* Re-enable endpoint 1's interrupts */
+ AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT1;
+
+ /* Update the current bank in use */
+ if (uiCurrentBank == AT91C_UDP_RX_DATA_BK0)
+ {
+ uiCurrentBank = AT91C_UDP_RX_DATA_BK1;
+ }
+ else
+ {
+ uiCurrentBank = AT91C_UDP_RX_DATA_BK0;
+ }
+
+ }
+ else
+ {
+ break;
+ }
+ }
+ }
+ }
+}
+
+/*------------------------------------------------------------*/
+
+void
+vUSBSendByte (portCHAR cByte)
+{
+ /* Queue the byte to be sent. The USB task will send it. */
+ xQueueSend (xTxCDC, &cByte, usbNO_BLOCK);
+}
+
+/*------------------------------------------------------------*/
+
+portLONG
+vUSBRecvByte (portCHAR *cByte, portLONG size, portTickType xTicksToWait)
+{
+ portLONG res;
+ if(size<=0 || !cByte || !xRxCDC)
+ return 0;
+
+ res=0;
+ while(size-- && xQueueReceive(xRxCDC, cByte++, xTicksToWait))
+ res++;
+
+ return res;
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvSendZLP (void)
+{
+ unsigned portLONG ulStatus;
+
+ /* Wait until the FIFO is free - even though we are not going to use it.
+ THERE IS NO TIMEOUT HERE! */
+ while (AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] & AT91C_UDP_TXPKTRDY)
+ {
+ vTaskDelay (usbSHORTEST_DELAY);
+ }
+
+ portENTER_CRITICAL ();
+ {
+ /* Cancel any further pending data */
+ pxControlTx.ulTotalDataLength = pxControlTx.ulNextCharIndex;
+
+ /* Set the TXPKTRDY bit to cause a transmission with no data. */
+ ulStatus = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0];
+ usbCSR_SET_BIT (&ulStatus, AT91C_UDP_TXPKTRDY);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] = ulStatus;
+ }
+ portEXIT_CRITICAL ();
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvSendStall (void)
+{
+ unsigned portLONG ulStatus;
+
+ portENTER_CRITICAL ();
+ {
+ /* Force a stall by simply setting the FORCESTALL bit in the CSR. */
+ ulStatus = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0];
+ usbCSR_SET_BIT (&ulStatus, AT91C_UDP_FORCESTALL);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] = ulStatus;
+ }
+ portEXIT_CRITICAL ();
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvResetEndPoints (void)
+{
+ unsigned portLONG ulTemp;
+
+ eDriverState = eJUST_RESET;
+ ucControlState = 0;
+
+ /* Reset all the end points. */
+ AT91C_BASE_UDP->UDP_RSTEP = usbEND_POINT_RESET_MASK;
+ AT91C_BASE_UDP->UDP_RSTEP = (unsigned portLONG) 0x00;
+
+ /* Enable data to be sent and received. */
+ AT91C_BASE_UDP->UDP_FADDR = AT91C_UDP_FEN;
+
+ /* Repair the configuration end point. */
+ portENTER_CRITICAL ();
+ {
+ ulTemp = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0];
+ usbCSR_SET_BIT (&ulTemp,
+ ((unsigned portLONG) (AT91C_UDP_EPEDS |
+ AT91C_UDP_EPTYPE_CTRL)));
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] = ulTemp;
+ AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT0;
+ }
+ portEXIT_CRITICAL ();
+ uiCurrentBank = AT91C_UDP_RX_DATA_BK0;
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvProcessEndPoint0Interrupt (xISRStatus * pxMessage)
+{
+ static xUSB_REQUEST xRequest;
+ unsigned portLONG ulRxBytes;
+
+ /* Get number of bytes received, if any */
+ ulRxBytes = pxMessage->ulCSR0 >> 16;
+ ulRxBytes &= usbRX_COUNT_MASK;
+
+ if (pxMessage->ulCSR0 & AT91C_UDP_TXCOMP)
+ {
+ /* We received a TX complete interrupt. What we do depends on
+ what we sent to get this interrupt. */
+
+ if (eDriverState == eJUST_GOT_CONFIG)
+ {
+ /* We sent an acknowledgement of a SET_CONFIG request. We
+ are now at the end of the enumeration.
+
+ TODO: Config 0 sets unconfigured state, should enter Address state.
+ Request for unsupported config should stall. */
+ AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_CONFG;
+
+ /* Set up endpoints */
+ portENTER_CRITICAL ();
+ {
+ unsigned portLONG ulTemp;
+
+ /* Set endpoint 1 to bulk-out */
+ ulTemp = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_1];
+ usbCSR_SET_BIT (&ulTemp,
+ AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_OUT);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_1] = ulTemp;
+ AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT1;
+ /* Set endpoint 2 to bulk-in */
+ ulTemp = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_2];
+ usbCSR_SET_BIT (&ulTemp,
+ AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_BULK_IN);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_2] = ulTemp;
+ AT91C_BASE_UDP->UDP_IER = AT91C_UDP_EPINT2;
+ /* Set endpoint 3 to interrupt-in, enable it, and enable interrupts */
+ ulTemp = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_3];
+ usbCSR_SET_BIT (&ulTemp,
+ AT91C_UDP_EPEDS | AT91C_UDP_EPTYPE_INT_IN);
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_3] = ulTemp;
+ /*AT91F_UDP_EnableIt( AT91C_BASE_UDP, AT91C_UDP_EPINT3 ); */
+ }
+ portEXIT_CRITICAL ();
+
+ eDriverState = eREADY_TO_SEND;
+ }
+ else if (eDriverState == eJUST_GOT_ADDRESS)
+ {
+ /* We sent an acknowledgement of a SET_ADDRESS request. Move
+ to the addressed state. */
+ if (ulReceivedAddress != (unsigned portLONG) 0)
+ {
+ AT91C_BASE_UDP->UDP_GLBSTATE = AT91C_UDP_FADDEN;
+ }
+ else
+ {
+ AT91C_BASE_UDP->UDP_GLBSTATE = 0;
+ }
+
+ AT91C_BASE_UDP->UDP_FADDR = (AT91C_UDP_FEN | ulReceivedAddress);
+ eDriverState = eNOTHING;
+ }
+ else
+ {
+ /* The TXCOMP was not for any special type of transmission. See
+ if there is any more data to send. */
+ prvSendNextSegment ();
+ }
+ }
+
+ if (pxMessage->ulCSR0 & AT91C_UDP_RX_DATA_BK0)
+ {
+ /* Received a control data packet. May be a 0-length ACK or a data stage. */
+ unsigned portCHAR ucBytesToGet;
+
+ /* Got data. Cancel any outgoing data. */
+ pxControlTx.ulNextCharIndex = pxControlTx.ulTotalDataLength;
+
+ /* Determine how many bytes we need to receive. */
+ ucBytesToGet =
+ pxControlRx.ulTotalDataLength - pxControlRx.ulNextCharIndex;
+ if (ucBytesToGet > ulRxBytes)
+ {
+ ucBytesToGet = ulRxBytes;
+ }
+
+ /* If we're not expecting any data, it's an ack - just quit now. */
+ if (!ucBytesToGet)
+ {
+ return;
+ }
+
+ /* Get the required data and update the index. */
+ memcpy (pxControlRx.ucBuffer, pxMessage->ucFifoData, ucBytesToGet);
+ pxControlRx.ulNextCharIndex += ucBytesToGet;
+ }
+
+ if (pxMessage->ulCSR0 & AT91C_UDP_RXSETUP)
+ {
+ /* Received a SETUP packet. May be followed by data packets. */
+
+ if (ulRxBytes >= usbEXPECTED_NUMBER_OF_BYTES)
+ {
+ /* Create an xUSB_REQUEST variable from the raw bytes array. */
+
+ xRequest.ucReqType = pxMessage->ucFifoData[usbREQUEST_TYPE_INDEX];
+ xRequest.ucRequest = pxMessage->ucFifoData[usbREQUEST_INDEX];
+
+ xRequest.usValue = pxMessage->ucFifoData[usbVALUE_HIGH_BYTE];
+ xRequest.usValue <<= 8;
+ xRequest.usValue |= pxMessage->ucFifoData[usbVALUE_LOW_BYTE];
+
+ xRequest.usIndex = pxMessage->ucFifoData[usbINDEX_HIGH_BYTE];
+ xRequest.usIndex <<= 8;
+ xRequest.usIndex |= pxMessage->ucFifoData[usbINDEX_LOW_BYTE];
+
+ xRequest.usLength = pxMessage->ucFifoData[usbLENGTH_HIGH_BYTE];
+ xRequest.usLength <<= 8;
+ xRequest.usLength |= pxMessage->ucFifoData[usbLENGTH_LOW_BYTE];
+
+ pxControlRx.ulNextCharIndex = 0;
+ if (!(xRequest.ucReqType & 0x80)) /* Host-to-Device transfer, may need to get data first */
+ {
+ if (xRequest.usLength > usbMAX_CONTROL_MESSAGE_SIZE)
+ {
+ /* Too big! No space for control data, stall and abort. */
+ prvSendStall ();
+ return;
+ }
+
+ pxControlRx.ulTotalDataLength = xRequest.usLength;
+ }
+ else
+ {
+ /* We're sending the data, don't wait for any. */
+ pxControlRx.ulTotalDataLength = 0;
+ }
+ }
+ }
+
+ /* See if we've got a pending request and all its associated data ready */
+ if ((pxMessage->ulCSR0 & (AT91C_UDP_RX_DATA_BK0 | AT91C_UDP_RXSETUP))
+ && (pxControlRx.ulNextCharIndex >= pxControlRx.ulTotalDataLength))
+ {
+ unsigned portCHAR ucRequest;
+
+ /* Manipulate the ucRequestType and the ucRequest parameters to
+ generate a zero based request selection. This is just done to
+ break up the requests into subsections for clarity. The
+ alternative would be to have more huge switch statement that would
+ be difficult to optimise. */
+ ucRequest = ((xRequest.ucReqType & 0x60) >> 3);
+ ucRequest |= (xRequest.ucReqType & 0x03);
+
+ switch (ucRequest)
+ {
+ case usbSTANDARD_DEVICE_REQUEST:
+ /* Standard Device request */
+ prvHandleStandardDeviceRequest (&xRequest);
+ break;
+
+ case usbSTANDARD_INTERFACE_REQUEST:
+ /* Standard Interface request */
+ prvHandleStandardInterfaceRequest (&xRequest);
+ break;
+
+ case usbSTANDARD_END_POINT_REQUEST:
+ /* Standard Endpoint request */
+ prvHandleStandardEndPointRequest (&xRequest);
+ break;
+
+ case usbCLASS_INTERFACE_REQUEST:
+ /* Class Interface request */
+ prvHandleClassInterfaceRequest (&xRequest);
+ break;
+
+ default: /* This is not something we want to respond to. */
+ prvSendStall ();
+ }
+ }
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvGetStandardDeviceDescriptor (xUSB_REQUEST * pxRequest)
+{
+ /* The type is in the high byte. Return whatever has been requested. */
+ switch ((pxRequest->usValue & 0xff00) >> 8)
+ {
+ case usbDESCRIPTOR_TYPE_DEVICE:
+ prvSendControlData ((unsigned portCHAR *) &pxDeviceDescriptor,
+ pxRequest->usLength, sizeof (pxDeviceDescriptor),
+ pdTRUE);
+ break;
+
+ case usbDESCRIPTOR_TYPE_CONFIGURATION:
+ prvSendControlData ((unsigned portCHAR *) &(pxConfigDescriptor),
+ pxRequest->usLength, sizeof (pxConfigDescriptor),
+ pdTRUE);
+ break;
+
+ case usbDESCRIPTOR_TYPE_STRING:
+
+ /* The index to the string descriptor is the lower byte. */
+ switch (pxRequest->usValue & 0xff)
+ {
+ case usbLANGUAGE_STRING:
+ prvSendControlData ((unsigned portCHAR *)
+ &pxLanguageStringDescriptor,
+ pxRequest->usLength,
+ sizeof (pxLanguageStringDescriptor), pdTRUE);
+ break;
+
+ case usbMANUFACTURER_STRING:
+ prvSendControlData ((unsigned portCHAR *)
+ &pxManufacturerStringDescriptor,
+ pxRequest->usLength,
+ sizeof (pxManufacturerStringDescriptor),
+ pdTRUE);
+ break;
+
+ case usbPRODUCT_STRING:
+ prvSendControlData ((unsigned portCHAR *)
+ &pxProductStringDescriptor, pxRequest->usLength,
+ sizeof (pxProductStringDescriptor), pdTRUE);
+ break;
+
+ default:
+ prvSendStall ();
+ break;
+ }
+ break;
+
+ default:
+ prvSendStall ();
+ break;
+ }
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvHandleStandardDeviceRequest (xUSB_REQUEST * pxRequest)
+{
+ unsigned portSHORT usStatus = 0;
+
+ switch (pxRequest->ucRequest)
+ {
+ case usbGET_STATUS_REQUEST:
+ /* Just send two byte dummy status. */
+ prvSendControlData ((unsigned portCHAR *) &usStatus, sizeof (usStatus),
+ sizeof (usStatus), pdFALSE);
+ break;
+
+ case usbGET_DESCRIPTOR_REQUEST:
+ /* Send device descriptor */
+ prvGetStandardDeviceDescriptor (pxRequest);
+ break;
+
+ case usbGET_CONFIGURATION_REQUEST:
+ /* Send selected device configuration */
+ prvSendControlData ((unsigned portCHAR *) &ucUSBConfig,
+ sizeof (ucUSBConfig), sizeof (ucUSBConfig),
+ pdFALSE);
+ break;
+
+ case usbSET_FEATURE_REQUEST:
+ prvSendZLP ();
+ break;
+
+ case usbSET_ADDRESS_REQUEST:
+ /* Get assigned address and send ack, but don't implement new address until we get a TXCOMP */
+ prvSendZLP ();
+ eDriverState = eJUST_GOT_ADDRESS;
+ ulReceivedAddress = (unsigned portLONG) pxRequest->usValue;
+ break;
+
+ case usbSET_CONFIGURATION_REQUEST:
+ /* Ack SET_CONFIGURATION request, but don't implement until TXCOMP */
+ ucUSBConfig = (unsigned portCHAR) (pxRequest->usValue & 0xff);
+ eDriverState = eJUST_GOT_CONFIG;
+ prvSendZLP ();
+ break;
+
+ default:
+ /* Any unsupported request results in a STALL response. */
+ prvSendStall ();
+ break;
+ }
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvHandleClassInterfaceRequest (xUSB_REQUEST * pxRequest)
+{
+ switch (pxRequest->ucRequest)
+ {
+ case usbSEND_ENCAPSULATED_COMMAND:
+ prvSendStall ();
+ break;
+
+ case usbGET_ENCAPSULATED_RESPONSE:
+ prvSendStall ();
+ break;
+
+ case usbSET_LINE_CODING:
+ /* Set line coding - baud rate, data bits, parity, stop bits */
+ prvSendZLP ();
+ memcpy ((void *) pxLineCoding, pxControlRx.ucBuffer,
+ sizeof (pxLineCoding));
+ break;
+
+ case usbGET_LINE_CODING:
+ /* Get line coding */
+ prvSendControlData ((unsigned portCHAR *) &pxLineCoding,
+ pxRequest->usLength, sizeof (pxLineCoding),
+ pdFALSE);
+ break;
+
+ case usbSET_CONTROL_LINE_STATE:
+ /* D0: 1=DTR, 0=No DTR, D1: 1=Activate Carrier, 0=Deactivate carrier (RTS, half-duplex) */
+ prvSendZLP ();
+ ucControlState = pxRequest->usValue;
+ break;
+
+ default:
+ prvSendStall ();
+ break;
+ }
+}
+
+/*------------------------------------------------------------*/
+
+static void
+prvGetStandardInterfaceDescriptor (xUSB_REQUEST * pxRequest)
+{
+ switch ((pxRequest->usValue & (unsigned portSHORT) 0xff00) >> 8)
+ {
+ default:
+ prvSendStall ();
+ break;
+ }
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+prvHandleStandardInterfaceRequest (xUSB_REQUEST * pxRequest)
+{
+ unsigned portSHORT usStatus = 0;
+
+ switch (pxRequest->ucRequest)
+ {
+ case usbGET_STATUS_REQUEST:
+ /* Send dummy 2 bytes. */
+ prvSendControlData ((unsigned portCHAR *) &usStatus, sizeof (usStatus),
+ sizeof (usStatus), pdFALSE);
+ break;
+
+ case usbGET_DESCRIPTOR_REQUEST:
+ prvGetStandardInterfaceDescriptor (pxRequest);
+ break;
+
+ /* This minimal implementation does not respond to these. */
+ case usbGET_INTERFACE_REQUEST:
+ case usbSET_FEATURE_REQUEST:
+ case usbSET_INTERFACE_REQUEST:
+
+ default:
+ prvSendStall ();
+ break;
+ }
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+prvHandleStandardEndPointRequest (xUSB_REQUEST * pxRequest)
+{
+ switch (pxRequest->ucRequest)
+ {
+ /* This minimal implementation does not expect to respond to these. */
+ case usbGET_STATUS_REQUEST:
+ case usbCLEAR_FEATURE_REQUEST:
+ case usbSET_FEATURE_REQUEST:
+
+ default:
+ prvSendStall ();
+ break;
+ }
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+vDetachUSBInterface (void)
+{
+ /* Setup the PIO for the USB pull up resistor. */
+ AT91C_BASE_PIOA->PIO_PER = AT91C_PIO_PA16;
+ AT91C_BASE_PIOA->PIO_OER = AT91C_PIO_PA16;
+
+
+ /* Disable pull up */
+ AT91C_BASE_PIOA->PIO_SODR = AT91C_PIO_PA16;
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+vInitUSBInterface (void)
+{
+ extern void (vUSB_ISR) (void);
+
+ /* Create the queue used to communicate between the USB ISR and task. */
+ xUSBInterruptQueue =
+ xQueueCreate (usbQUEUE_LENGTH + 1, sizeof (xISRStatus *));
+
+ /* Create the queues used to hold Rx and Tx characters. */
+ xRxCDC =
+ xQueueCreate (USB_CDC_QUEUE_SIZE,
+ (unsigned portCHAR) sizeof (signed portCHAR));
+ xTxCDC =
+ xQueueCreate (USB_CDC_QUEUE_SIZE + 1,
+ (unsigned portCHAR) sizeof (signed portCHAR));
+
+ if ((!xUSBInterruptQueue) || (!xRxCDC) || (!xTxCDC))
+ {
+ /* Not enough RAM to create queues!. */
+ return;
+ }
+
+ /* Initialise a few state variables. */
+ pxControlTx.ulNextCharIndex = (unsigned portLONG) 0;
+ pxControlRx.ulNextCharIndex = (unsigned portLONG) 0;
+ ucUSBConfig = (unsigned portCHAR) 0;
+ eDriverState = eNOTHING;
+ ucControlState = 0;
+ uiCurrentBank = AT91C_UDP_RX_DATA_BK0;
+
+
+ /* HARDWARE SETUP */
+
+ /* Set the PLL USB Divider */
+ AT91C_BASE_CKGR->CKGR_PLLR |= AT91C_CKGR_USBDIV_1;
+
+ /* Enables the 48MHz USB clock UDPCK and System Peripheral USB Clock. */
+ AT91C_BASE_PMC->PMC_SCER = AT91C_PMC_UDP;
+ AT91C_BASE_PMC->PMC_PCER = (1 << AT91C_ID_UDP);
+
+ /* Setup the PIO for the USB pull up resistor. */
+ AT91C_BASE_PIOA->PIO_PER = AT91C_PIO_PA16;
+ AT91C_BASE_PIOA->PIO_OER = AT91C_PIO_PA16;
+
+
+ /* Start without the pullup - this will get set at the end of this
+ function. */
+ AT91C_BASE_PIOA->PIO_SODR = AT91C_PIO_PA16;
+
+
+ /* When using the USB debugger the peripheral registers do not always get
+ set to the correct default values. To make sure set the relevant registers
+ manually here. */
+ AT91C_BASE_UDP->UDP_IDR = (unsigned portLONG) 0xffffffff;
+ AT91C_BASE_UDP->UDP_ICR = (unsigned portLONG) 0xffffffff;
+ AT91C_BASE_UDP->UDP_CSR[0] = (unsigned portLONG) 0x00;
+ AT91C_BASE_UDP->UDP_CSR[1] = (unsigned portLONG) 0x00;
+ AT91C_BASE_UDP->UDP_CSR[2] = (unsigned portLONG) 0x00;
+ AT91C_BASE_UDP->UDP_CSR[3] = (unsigned portLONG) 0x00;
+ AT91C_BASE_UDP->UDP_GLBSTATE = 0;
+ AT91C_BASE_UDP->UDP_FADDR = 0;
+
+ /* Enable the transceiver. */
+ AT91C_UDP_TRANSCEIVER_ENABLE = 0;
+
+ /* Enable the USB interrupts - other interrupts get enabled as the
+ enumeration process progresses. */
+ AT91F_AIC_ConfigureIt (AT91C_ID_UDP, usbINTERRUPT_PRIORITY,
+ AT91C_AIC_SRCTYPE_INT_HIGH_LEVEL,
+ (void (*)(void)) vUSB_ISR);
+ AT91C_BASE_AIC->AIC_IECR = 0x1 << AT91C_ID_UDP;
+
+
+ /* Wait a short while before making our presence known. */
+ vTaskDelay (usbINIT_DELAY);
+ AT91C_BASE_PIOA->PIO_CODR = AT91C_PIO_PA16;
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+prvSendControlData (unsigned portCHAR * pucData,
+ unsigned portSHORT usRequestedLength,
+ unsigned portLONG ulLengthToSend,
+ portLONG lSendingDescriptor)
+{
+ if (((unsigned portLONG) usRequestedLength < ulLengthToSend))
+ {
+ /* Cap the data length to that requested. */
+ ulLengthToSend = (unsigned portSHORT) usRequestedLength;
+ }
+ else if ((ulLengthToSend < (unsigned portLONG) usRequestedLength)
+ && lSendingDescriptor)
+ {
+ /* We are sending a descriptor. If the descriptor is an exact
+ multiple of the FIFO length then it will have to be terminated
+ with a NULL packet. Set the state to indicate this if
+ necessary. */
+ if ((ulLengthToSend % usbFIFO_LENGTH) == 0)
+ {
+ eDriverState = eSENDING_EVEN_DESCRIPTOR;
+ }
+ }
+
+ /* Here we assume that the previous message has been sent. THERE IS NO
+ BUFFER OVERFLOW PROTECTION HERE.
+
+ Copy the data to send into the buffer as we cannot send it all at once
+ (if it is greater than 8 bytes in length). */
+ memcpy (pxControlTx.ucBuffer, pucData, ulLengthToSend);
+
+ /* Reinitialise the buffer index so we start sending from the start of
+ the data. */
+ pxControlTx.ulTotalDataLength = ulLengthToSend;
+ pxControlTx.ulNextCharIndex = (unsigned portLONG) 0;
+
+ /* Send the first 8 bytes now. The rest will get sent in response to
+ TXCOMP interrupts. */
+ prvSendNextSegment ();
+}
+
+/*-----------------------------------------------------------*/
+
+static void
+prvSendNextSegment (void)
+{
+ volatile unsigned portLONG ulNextLength, ulStatus, ulLengthLeftToSend;
+
+ /* Is there any data to send? */
+ if (pxControlTx.ulTotalDataLength > pxControlTx.ulNextCharIndex)
+ {
+ ulLengthLeftToSend =
+ pxControlTx.ulTotalDataLength - pxControlTx.ulNextCharIndex;
+
+ /* We can only send 8 bytes to the fifo at a time. */
+ if (ulLengthLeftToSend > usbFIFO_LENGTH)
+ {
+ ulNextLength = usbFIFO_LENGTH;
+ }
+ else
+ {
+ ulNextLength = ulLengthLeftToSend;
+ }
+
+ /* Wait until we can place data in the fifo. THERE IS NO TIMEOUT
+ HERE! */
+ while (AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] & AT91C_UDP_TXPKTRDY)
+ {
+ vTaskDelay (usbSHORTEST_DELAY);
+ }
+
+ /* Write the data to the FIFO. */
+ while (ulNextLength > (unsigned portLONG) 0)
+ {
+ AT91C_BASE_UDP->UDP_FDR[usbEND_POINT_0] =
+ pxControlTx.ucBuffer[pxControlTx.ulNextCharIndex];
+
+ ulNextLength--;
+ pxControlTx.ulNextCharIndex++;
+ }
+
+ /* Start the transmission. */
+ portENTER_CRITICAL ();
+ {
+ ulStatus = AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0];
+ usbCSR_SET_BIT (&ulStatus, ((unsigned portLONG) 0x10));
+ AT91C_BASE_UDP->UDP_CSR[usbEND_POINT_0] = ulStatus;
+ }
+ portEXIT_CRITICAL ();
+ }
+ else
+ {
+ /* There is no data to send. If we were sending a descriptor and the
+ descriptor was an exact multiple of the max packet size then we need
+ to send a null to terminate the transmission. */
+ if (eDriverState == eSENDING_EVEN_DESCRIPTOR)
+ {
+ prvSendZLP ();
+ eDriverState = eNOTHING;
+ }
+ }
+}
personal git repositories of Harald Welte. Your mileage may vary