SAMV71 Xplained Ultra Software Package 1.5

CDCDEEMPort.c

Go to the documentation of this file.
00001 /* ---------------------------------------------------------------------------- */
00002 /*                  Atmel Microcontroller Software Support                      */
00003 /*                       SAM Software Package License                           */
00004 /* ---------------------------------------------------------------------------- */
00005 /* Copyright (c) 2015, Atmel Corporation                                        */
00006 /*                                                                              */
00007 /* All rights reserved.                                                         */
00008 /*                                                                              */
00009 /* Redistribution and use in source and binary forms, with or without           */
00010 /* modification, are permitted provided that the following condition is met:    */
00011 /*                                                                              */
00012 /* - Redistributions of source code must retain the above copyright notice,     */
00013 /* this list of conditions and the disclaimer below.                            */
00014 /*                                                                              */
00015 /* Atmel's name may not be used to endorse or promote products derived from     */
00016 /* this software without specific prior written permission.                     */
00017 /*                                                                              */
00018 /* DISCLAIMER:  THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR   */
00019 /* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF */
00020 /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE   */
00021 /* DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,      */
00022 /* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT */
00023 /* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,  */
00024 /* OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF    */
00025 /* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING         */
00026 /* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, */
00027 /* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                           */
00028 /* ---------------------------------------------------------------------------- */
00029 
00030 /**\file
00031  *  Implementation of the CDCDEEMPort class methods.
00032  */
00033 
00034 /** \addtogroup usbd_cdc
00035  *@{
00036  */
00037 
00038 /*------------------------------------------------------------------------------
00039  *         Headers
00040  *------------------------------------------------------------------------------*/
00041 
00042 #include <CDCDEEMPort.h>
00043 #include <CDCDescriptors.h>
00044 #include <USBLib_Trace.h>
00045 #include <string.h>
00046 #include "../../../../utils/utility.h"
00047 
00048 /*------------------------------------------------------------------------------
00049  *         Types
00050  *------------------------------------------------------------------------------*/
00051 
00052 /** Parse data extention for descriptor parsing  */
00053 typedef struct _CDCDParseData {
00054     /** Pointer to CDCDEEMPort instance */
00055     CDCDEEMPort *pCdcd;
00056     /** Pointer to found interface descriptor */
00057     USBInterfaceDescriptor *pIfDesc;
00058 
00059 } CDCDParseData;
00060 
00061 /*------------------------------------------------------------------------------
00062  *         Internal variables
00063  *------------------------------------------------------------------------------*/
00064 
00065 /** Maximum packet size in bytes */
00066 #define DATAPACKETSIZE (1536)
00067 
00068 /** Size in bytes of the buffer used for reading data from USB */
00069 #define DATABUFFERSIZE (DATAPACKETSIZE+2)
00070 
00071 /** Number of transmit buffers */
00072 #define USB_TX_BUFFERS 64
00073 
00074 /** Buffer for storing incoming USB data. */
00075 static uint8_t gRxBuffer[DATABUFFERSIZE];
00076 
00077 /** Buffer for storing outgoing USB data. */
00078 static uint8_t gUsbTxBuffers[USB_TX_BUFFERS * DATAPACKETSIZE];
00079 static uint32_t gUsbTxBufferSizes[USB_TX_BUFFERS];
00080 static int gUsbTxHeadIdx = 0;
00081 static int gUsbTxTailIdx = 0;
00082 static uint8_t gUsbTxMutex = 0;
00083 
00084 // TODO temp
00085 static TransferCallback gRxCallback;
00086 static void *gRxCallbackArg;
00087 static void *gRxData;
00088 static uint32_t gRxDataSize;
00089 
00090 /*------------------------------------------------------------------------------
00091  *         Internal functions
00092  *------------------------------------------------------------------------------*/
00093 
00094 /**
00095  * Parse descriptors: Interface, Bulk IN/OUT, Interrupt IN.
00096  * \param desc Pointer to descriptor list.
00097  * \param arg  Argument, pointer to AUDDParseData instance.
00098  */
00099 static uint32_t _Interfaces_Parse(USBGenericDescriptor *pDesc,
00100                                   CDCDParseData *pArg)
00101 {
00102     CDCDEEMPort *pCdcd = pArg->pCdcd;
00103 
00104     /* Not a valid descriptor */
00105     if (pDesc->bLength == 0)
00106         return USBRC_PARAM_ERR;
00107 
00108     /* Find interface descriptor */
00109     if (pDesc->bDescriptorType == USBGenericDescriptor_INTERFACE) {
00110         USBInterfaceDescriptor *pIf = (USBInterfaceDescriptor *)pDesc;
00111 
00112         /* Obtain interface from descriptor */
00113         if (pCdcd->bInterfaceNdx == 0xFF) {
00114             /* First interface is communication */
00115             if (pIf->bInterfaceClass ==
00116                 CDCCommunicationInterfaceDescriptor_CLASS) {
00117                 pCdcd->bInterfaceNdx = pIf->bInterfaceNumber;
00118                 pCdcd->bNumInterface = 1;
00119             }
00120 
00121             pArg->pIfDesc = pIf;
00122         } else if (pCdcd->bInterfaceNdx <= pIf->bInterfaceNumber
00123                 && pCdcd->bInterfaceNdx + pCdcd->bNumInterface
00124                     > pIf->bInterfaceNumber)
00125             pArg->pIfDesc = pIf;
00126     }
00127 
00128     /* Parse valid interfaces */
00129     if (pArg->pIfDesc == 0)
00130         return 0;
00131 
00132     /* Find endpoint descriptors */
00133     if (pDesc->bDescriptorType == USBGenericDescriptor_ENDPOINT) {
00134         USBEndpointDescriptor *pEp = (USBEndpointDescriptor *)pDesc;
00135 
00136         switch (pEp->bmAttributes & 0x3) {
00137         case USBEndpointDescriptor_BULK:
00138             if (pEp->bEndpointAddress & 0x80)
00139                 pCdcd->bBulkInPIPE = pEp->bEndpointAddress & 0x7F;
00140             else {
00141                 pCdcd->bBulkOutPIPE = pEp->bEndpointAddress;
00142                 pCdcd->wBulkOutMaxPacketSize = pEp->wMaxPacketSize;
00143             }
00144         }
00145     }
00146 
00147     if (pCdcd->bInterfaceNdx != 0xFF
00148             &&  pCdcd->bBulkInPIPE != 0
00149             &&  pCdcd->bBulkOutPIPE != 0)
00150         return USBRC_FINISHED;
00151 
00152     return 0;
00153 }
00154 
00155 /*----------------------------------------------------------------------------
00156  * Callback invoked when data has been received on the USB.
00157  *----------------------------------------------------------------------------*/
00158 static void _TransferCallback(const CDCDEEMPort *pCdcd,
00159                               uint8_t status,
00160                               uint32_t received,
00161                               uint32_t remaining)
00162 {
00163     /* Check that data has been received successfully */
00164     if (status == USBD_STATUS_SUCCESS) {
00165         uint32_t offset = 0;
00166 
00167         /* Check if bytes have been discarded */
00168         if ((received == DATAPACKETSIZE) && (remaining > 0)) {
00169             TRACE_WARNING("_UsbDataReceived: %u bytes discarded\n\r",
00170                           (unsigned int)remaining);
00171         }
00172 
00173         while (received >= 2) {
00174             uint16_t header;
00175 
00176             header = gRxBuffer[offset] + (gRxBuffer[offset + 1] << 8);
00177             offset += 2;
00178             received -= 2;
00179 
00180             if (header & (1 << 15)) {
00181                 // bit 14: reserved
00182                 if (header & (1 << 14))
00183                     break;
00184 
00185                 int cmd = (header >> 11) & 7; // bits 11..13: command
00186                 unsigned int param = header & 0x7ff;   // bits 0..10: parameter
00187 
00188                 switch (cmd) {
00189                 case 0:
00190                     // Echo
00191                     TRACE_WARNING("Received Echo EEM command\n\r");
00192 
00193                     if (received < param)
00194                         break;
00195 
00196                     header = (1 << 15) + (1 << 11);
00197 
00198                     TRACE_WARNING("Sending EchoReply EEM command\n\r");
00199                     gRxBuffer[offset - 2] = header & 0xff;
00200                     gRxBuffer[offset - 1] = (header >> 8) & 0xff;
00201 
00202                     while (CDCDEEMPort_Write(pCdcd, &gRxBuffer[offset - 2],
00203                                              param + 2, NULL, 0) != USBD_STATUS_SUCCESS);
00204 
00205                     break;
00206 
00207                 case 1:
00208                     // EchoReply
00209                     TRACE_WARNING("Unexpected EchoReply EEM command received\n\r");
00210                     break;
00211 
00212                 case 2:
00213                     // SuspendHint
00214                     TRACE_WARNING("Unexpected SuspendHint EEM command received\n\r");
00215                     break;
00216 
00217                 case 3:
00218                     // ResponseHint
00219                     TRACE_WARNING("Unexpected ResponseHint EEM command received\n\r");
00220                     break;
00221 
00222                 case 4:
00223                     // ResponseCompleteHint
00224                     TRACE_WARNING("Unexpected ResponseCompleteHint EEM command received\n\r");
00225                     break;
00226 
00227                 case 5:
00228                     // Tickle
00229                     TRACE_WARNING("Unexpected Tickle EEM command received\n\r");
00230                     break;
00231 
00232                 default:
00233                     // unknown command
00234                     TRACE_WARNING("Unexpected unknown EEM command %d received\n\r", cmd);
00235                     break;
00236                 }
00237             } else {
00238                 uint16_t len = header & 0x3fff;
00239 
00240                 if (received < len || len < 4)
00241                     return;
00242 
00243                 // TODO check CRC if present
00244 
00245                 /* Send frame to Callback */
00246                 if (gRxCallback) {
00247                     if (gRxDataSize + 4 > len) {
00248                         memcpy(gRxData, gRxBuffer + offset, len - 4);
00249                         gRxCallback(gRxCallbackArg, USBD_STATUS_SUCCESS, len - 4, 0);
00250                     }
00251                 }
00252 
00253                 offset += len;
00254                 received -= len;
00255             }
00256         }
00257 
00258         // TODO: handle remaining data if any
00259         if (received > 0) {
00260             TRACE_WARNING("_UsbDataReceived: %u bytes ignored\n\r",
00261                           (unsigned int)remaining);
00262         }
00263     } else {
00264         TRACE_WARNING("_UsbDataReceived: Transfer error\n\r");
00265     }
00266 }
00267 
00268 static int _UsbTxBufferPush(void)
00269 {
00270     int idx;
00271 
00272     if (gUsbTxHeadIdx == ((gUsbTxTailIdx - 1 + USB_TX_BUFFERS) % USB_TX_BUFFERS))
00273         return -1; // no buffer available
00274 
00275     idx = gUsbTxHeadIdx;
00276     gUsbTxHeadIdx = (gUsbTxHeadIdx + 1) % USB_TX_BUFFERS;
00277     gUsbTxBufferSizes[idx] = 0;
00278     return idx;
00279 }
00280 
00281 static int _UsbTxBufferPop(void)
00282 {
00283     int idx;
00284 
00285     if (gUsbTxHeadIdx == gUsbTxTailIdx)
00286         return -1; // nothing to get
00287 
00288     if (gUsbTxBufferSizes[gUsbTxTailIdx] == 0)
00289         return -1; // still nothing to get
00290 
00291     idx = gUsbTxTailIdx;
00292     gUsbTxTailIdx = (gUsbTxTailIdx + 1) % USB_TX_BUFFERS;
00293     return idx;
00294 }
00295 
00296 static int _UsbTxBufferPeek(void)
00297 {
00298     if (gUsbTxHeadIdx == gUsbTxTailIdx)
00299         return -1; // nothing to get
00300 
00301     if (gUsbTxBufferSizes[gUsbTxTailIdx] == 0)
00302         return -1; // still nothing to get
00303 
00304     return gUsbTxTailIdx;
00305 }
00306 
00307 static void _FlushUsbTxBuffers(const uint8_t *pipe);
00308 
00309 static void _UsbDataSent(void *pipe,
00310                          uint8_t status,
00311                          uint32_t received,
00312                          uint32_t remaining)
00313 {
00314     int bufIdx;
00315 
00316     // unused args
00317     (void)status;
00318     (void)received;
00319     (void)remaining;
00320 
00321     bufIdx = _UsbTxBufferPop();
00322 
00323     if (bufIdx >= 0) {
00324         TRACE_INFO("%u USB_TX_DEQUEUE(%d)\n\r", (unsigned int)GetTicks(), bufIdx);
00325     }
00326 
00327     // release mutex, to allow for another Flush
00328     __disable_irq();
00329     ReleaseMutex(gUsbTxMutex);
00330     __enable_irq();
00331 
00332     // try to flush more buffers
00333     _FlushUsbTxBuffers((const uint8_t *)pipe);
00334 }
00335 
00336 static void _FlushUsbTxBuffers(const uint8_t *pipe)
00337 {
00338     int bufIdx;
00339     uint32_t timeout = 0x7ff;
00340 
00341     // try to lock mutex to avoid concurrent flushes
00342     __disable_irq();
00343 
00344     if (LockMutex(gUsbTxMutex, timeout) != 0) {
00345         __enable_irq();
00346         return;
00347     }
00348 
00349     __enable_irq();
00350 
00351     if ((bufIdx = _UsbTxBufferPeek()) >= 0) {
00352         TRACE_INFO("%u USB_TX_SEND(%d,%u)\n\r", (unsigned int)GetTicks(), bufIdx,
00353                    (unsigned int)gUsbTxBufferSizes[bufIdx]);
00354 
00355         /* Send buffer through the USB */
00356         while (USBD_Write(*pipe, &gUsbTxBuffers[DATAPACKETSIZE * bufIdx],
00357                           gUsbTxBufferSizes[bufIdx], _UsbDataSent,
00358                           (void *)pipe) != USBD_STATUS_SUCCESS) {}
00359     } else {
00360         // nothing to Flush, release mutex
00361         __disable_irq();
00362         ReleaseMutex(gUsbTxMutex);
00363         __enable_irq();
00364     }
00365 }
00366 
00367 /*------------------------------------------------------------------------------
00368  *         Exported functions
00369  *------------------------------------------------------------------------------*/
00370 
00371 /**
00372  * Initializes the USB Device CDC serial port function.
00373  * \param pCdcd Pointer to CDCDEEMPort instance.
00374  * \param pUsbd Pointer to USBDDriver instance.
00375  * \param fEventHandler Pointer to event handler function.
00376  * \param firstInterface First interface index for the function
00377  *                       (0xFF to parse from descriptors).
00378  * \param numInterface   Number of interfaces for the function.
00379  */
00380 void CDCDEEMPort_Initialize(CDCDEEMPort *pCdcd,
00381                             USBDDriver *pUsbd,
00382                             uint8_t firstInterface, uint8_t numInterface)
00383 {
00384     TRACE_INFO("CDCDEEMPort_Initialize\n\r");
00385 
00386     /* Initialize USB Device Driver interface */
00387     pCdcd->pUsbd = pUsbd;
00388     pCdcd->bInterfaceNdx = firstInterface;
00389     pCdcd->bNumInterface = numInterface;
00390     pCdcd->bBulkInPIPE  = 0;
00391     pCdcd->bBulkOutPIPE = 0;
00392 }
00393 
00394 /**
00395  * Parse CDC EEM Port information for CDCDEEMPort instance.
00396  * Accepted interfaces:
00397  * - Communication Interface + Data Interface
00398  * - Data Interface ONLY
00399  * \param pCdcd        Pointer to CDCDEEMPort instance.
00400  * \param pDescriptors Pointer to descriptor list.
00401  * \param dwLength     Descriptor list size in bytes.
00402  */
00403 USBGenericDescriptor *CDCDEEMPort_ParseInterfaces(
00404     CDCDEEMPort *pCdcd,
00405     USBGenericDescriptor *pDescriptors,
00406     uint32_t dwLength)
00407 {
00408     CDCDParseData parseData;
00409 
00410     parseData.pCdcd   = pCdcd;
00411     parseData.pIfDesc = 0;
00412 
00413     return USBGenericDescriptor_Parse(
00414                pDescriptors, dwLength,
00415                (USBDescriptorParseFunction)_Interfaces_Parse,
00416                &parseData);
00417 }
00418 
00419 
00420 /**
00421  * Handles CDC-specific SETUP requests. Should be called from a
00422  * re-implementation of USBDCallbacks_RequestReceived() method.
00423  * \param pCdcd Pointer to CDCDEEMPort instance.
00424  * \param request Pointer to a USBGenericRequest instance.
00425  * \return USBRC_SUCCESS if request handled, otherwise error.
00426  */
00427 uint32_t CDCDEEMPort_RequestHandler(
00428     CDCDEEMPort *pCdcd,
00429     const USBGenericRequest *request)
00430 {
00431     if (USBGenericRequest_GetType(request) != USBGenericRequest_CLASS)
00432         return USBRC_PARAM_ERR;
00433 
00434     TRACE_INFO_WP("Cdce ");
00435 
00436     /* Validate interface */
00437     if (request->wIndex >= pCdcd->bInterfaceNdx &&
00438         request->wIndex < pCdcd->bInterfaceNdx + pCdcd->bNumInterface) {
00439     } else
00440         return USBRC_PARAM_ERR;
00441 
00442     /* Handle the request */
00443     switch (USBGenericRequest_GetRequest(request)) {
00444 
00445     default:
00446 
00447         return USBRC_PARAM_ERR;
00448     }
00449 
00450     return USBRC_SUCCESS;
00451 }
00452 
00453 /**
00454  * Receives data from the host through the virtual COM port created by
00455  * the CDC device serial driver. This function behaves like USBD_Read.
00456  * \param pCdcd  Pointer to CDCDEEMPort instance.
00457  * \param pData  Pointer to the data buffer to put received data.
00458  * \param dwSize Size of the data buffer in bytes.
00459  * \param fCallback Optional callback function to invoke when the transfer
00460  *                  finishes.
00461  * \param pArg      Optional argument to the callback function.
00462  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;
00463  *         otherwise, the corresponding error code.
00464  */
00465 uint32_t CDCDEEMPort_Read(const CDCDEEMPort *pCdcd,
00466                           void *pData, uint32_t dwSize,
00467                           TransferCallback fCallback, void *pArg)
00468 {
00469     if (pCdcd->bBulkOutPIPE == 0)
00470         return USBRC_PARAM_ERR;
00471 
00472     gRxCallback = fCallback;
00473     gRxCallbackArg = pArg;
00474     gRxData = pData;
00475     gRxDataSize = dwSize;
00476     return USBD_Read(pCdcd->bBulkOutPIPE,
00477                      gRxBuffer, DATABUFFERSIZE,
00478                      (TransferCallback)_TransferCallback, pArg);
00479 }
00480 
00481 /**
00482  * Sends a data buffer through the virtual COM port created by the CDC
00483  * device serial driver. This function behaves exactly like USBD_Write.
00484  *
00485  * TODO batch packets ?
00486  *
00487  * \param pCdcd  Pointer to CDCDEEMPort instance.
00488  * \param pData  Pointer to the data buffer to send.
00489  * \param dwSize Size of the data buffer in bytes.
00490  * \param fCallback Optional callback function to invoke when the transfer
00491  *                  finishes.
00492  * \param pArg      Optional argument to the callback function.
00493  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;
00494  *         otherwise, the corresponding error code.
00495  */
00496 uint32_t CDCDEEMPort_Write(const CDCDEEMPort *pCdcd,
00497                            void *pData, uint32_t dwSize,
00498                            TransferCallback fCallback, void *pArg)
00499 {
00500     int bufIdx;
00501     uint8_t *buffer;
00502     uint32_t len;
00503 
00504     // unused args
00505     (void)fCallback;
00506     (void)pArg;
00507 
00508     if (pCdcd->bBulkInPIPE == 0)
00509         return USBRC_PARAM_ERR;
00510 
00511     bufIdx = _UsbTxBufferPush();
00512 
00513     if (bufIdx >= 0) {
00514         buffer = &gUsbTxBuffers[DATAPACKETSIZE * bufIdx];
00515 
00516         // EEM header
00517         len = dwSize;
00518         uint16_t eemHdr = (len + 4) & 0x3fff;
00519         buffer[0] = eemHdr & 0xff;
00520         buffer[1] = (eemHdr >> 8) & 0xff;
00521         memcpy(buffer + 2, pData, len);
00522         len += 2; // add EEM header length (2 bytes)
00523         buffer[len] = 0xde;
00524         buffer[len + 1] = 0xad;
00525         buffer[len + 2] = 0xbe;
00526         buffer[len + 3] = 0xef;
00527         len += 4; // add CRC length (4 bytes)
00528 
00529         // add zero EEM packet when len % maxTransferSize == 0
00530         if (len % pCdcd->wBulkOutMaxPacketSize == 0) {
00531             memset(buffer + len, 0, 2);
00532             len += 2;
00533         }
00534 
00535         gUsbTxBufferSizes[bufIdx] = len;
00536 
00537         _FlushUsbTxBuffers(&pCdcd->bBulkInPIPE);
00538     } else {
00539         // TODO
00540     }
00541 
00542     return USBRC_SUCCESS;
00543 }
00544 
00545 /**@}*/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines