SAMV71 Xplained Ultra Software Package 1.3

CDCDEEMPort.c

Go to the documentation of this file.
00001 /* ----------------------------------------------------------------------------
00002  *         ATMEL Microcontroller Software Support
00003  * ----------------------------------------------------------------------------
00004  * Copyright (c) 2008, Atmel Corporation
00005  *
00006  * All rights reserved.
00007  *
00008  * Redistribution and use in source and binary forms, with or without
00009  * modification, are permitted provided that the following conditions are met:
00010  *
00011  * - Redistributions of source code must retain the above copyright notice,
00012  * this list of conditions and the disclaimer below.
00013  *
00014  * Atmel's name may not be used to endorse or promote products derived from
00015  * this software without specific prior written permission.
00016  *
00017  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR
00018  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
00019  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
00020  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,
00021  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
00022  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
00023  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00024  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00025  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
00026  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00027  * ----------------------------------------------------------------------------
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             pArg->pIfDesc = pIf;
00121         }
00122         else if (pCdcd->bInterfaceNdx <= pIf->bInterfaceNumber
00123             &&   pCdcd->bInterfaceNdx + pCdcd->bNumInterface
00124                                        > pIf->bInterfaceNumber) {
00125             pArg->pIfDesc = pIf;
00126         }
00127     }
00128 
00129     /* Parse valid interfaces */
00130     if (pArg->pIfDesc == 0)
00131         return 0;
00132 
00133     /* Find endpoint descriptors */
00134     if (pDesc->bDescriptorType == USBGenericDescriptor_ENDPOINT) {
00135         USBEndpointDescriptor *pEp = (USBEndpointDescriptor*)pDesc;
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     {
00166         uint32_t offset = 0;
00167 
00168         /* Check if bytes have been discarded */
00169         if ((received == DATAPACKETSIZE) && (remaining > 0))
00170         {
00171             TRACE_WARNING("_UsbDataReceived: %u bytes discarded\n\r",
00172                       (unsigned int)remaining);
00173         }
00174 
00175         while (received >= 2)
00176         {
00177             uint16_t header;
00178 
00179             header = gRxBuffer[offset] + (gRxBuffer[offset + 1] << 8);
00180             offset += 2;
00181             received -= 2;
00182 
00183             if (header & (1 << 15))
00184             {
00185                 // bit 14: reserved
00186                 if (header & (1 << 14))
00187                     break;
00188 
00189                 int cmd = (header >> 11) & 7; // bits 11..13: command
00190                 unsigned int param = header & 0x7ff;   // bits 0..10: parameter
00191 
00192                 switch (cmd)
00193                 {
00194                         case 0:
00195                                 // Echo
00196                                 TRACE_WARNING("Received Echo EEM command\n\r");
00197                                 if (received < param)
00198                                         break;
00199                                 header = (1 << 15) + (1 << 11);
00200 
00201                                 TRACE_WARNING("Sending EchoReply EEM command\n\r");
00202                                 gRxBuffer[offset - 2] = header & 0xff;
00203                                 gRxBuffer[offset - 1] = (header >> 8) & 0xff;
00204                                 while (CDCDEEMPort_Write(pCdcd, &gRxBuffer[offset - 2],
00205                                         param + 2, NULL, 0) != USBD_STATUS_SUCCESS);
00206 
00207                                 break;
00208                         case 1:
00209                                 // EchoReply
00210                                 TRACE_WARNING("Unexpected EchoReply EEM command received\n\r");
00211                                 break;
00212                         case 2:
00213                                 // SuspendHint
00214                                 TRACE_WARNING("Unexpected SuspendHint EEM command received\n\r");
00215                                 break;
00216                         case 3:
00217                                 // ResponseHint
00218                                 TRACE_WARNING("Unexpected ResponseHint EEM command received\n\r");
00219                                 break;
00220                         case 4:
00221                                 // ResponseCompleteHint
00222                                 TRACE_WARNING("Unexpected ResponseCompleteHint EEM command received\n\r");
00223                                 break;
00224                         case 5:
00225                                 // Tickle
00226                                 TRACE_WARNING("Unexpected Tickle EEM command received\n\r");
00227                                 break;
00228                         default:
00229                                 // unknown command
00230                                 TRACE_WARNING("Unexpected unknown EEM command %d received\n\r", cmd);
00231                                 break;
00232                 }
00233             }
00234             else
00235             {
00236                 uint16_t len = header & 0x3fff;
00237 
00238                 if (received < len || len < 4)
00239                        return;
00240 
00241                 // TODO check CRC if present
00242 
00243                 /* Send frame to Callback */
00244                 if (gRxCallback)
00245                 {
00246                     if (gRxDataSize + 4 > len)
00247                     {
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         {
00261             TRACE_WARNING("_UsbDataReceived: %u bytes ignored\n\r", (unsigned int)remaining);
00262         }
00263     }
00264     else
00265     {
00266         TRACE_WARNING("_UsbDataReceived: Transfer error\n\r");
00267     }
00268 }
00269 
00270 static int _UsbTxBufferPush(void)
00271 {
00272     int idx;
00273     if (gUsbTxHeadIdx == ((gUsbTxTailIdx - 1 + USB_TX_BUFFERS) % USB_TX_BUFFERS))
00274         return -1; // no buffer available
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     if (gUsbTxHeadIdx == gUsbTxTailIdx)
00285         return -1; // nothing to get
00286     if (gUsbTxBufferSizes[gUsbTxTailIdx] == 0)
00287         return -1; // still nothing to get
00288     idx = gUsbTxTailIdx;
00289     gUsbTxTailIdx = (gUsbTxTailIdx + 1) % USB_TX_BUFFERS;
00290     return idx;
00291 }
00292 
00293 static int _UsbTxBufferPeek(void)
00294 {
00295     if (gUsbTxHeadIdx == gUsbTxTailIdx)
00296         return -1; // nothing to get
00297     if (gUsbTxBufferSizes[gUsbTxTailIdx] == 0)
00298         return -1; // still nothing to get
00299     return gUsbTxTailIdx;
00300 }
00301 
00302 static void _FlushUsbTxBuffers(const uint8_t *pipe);
00303 
00304 static void _UsbDataSent(void *pipe,
00305                          uint8_t status,
00306                          uint32_t received,
00307                          uint32_t remaining)
00308 {
00309     int bufIdx;
00310 
00311     // unused args
00312     (void)status;
00313     (void)received;
00314     (void)remaining;
00315 
00316     bufIdx = _UsbTxBufferPop();
00317     if (bufIdx >= 0)
00318         TRACE_INFO("%u USB_TX_DEQUEUE(%d)\n\r", (unsigned int)GetTicks(), bufIdx);
00319 
00320     // release mutex, to allow for another Flush
00321     __disable_irq();
00322     ReleaseMutex(gUsbTxMutex);
00323     __enable_irq();
00324 
00325     // try to flush more buffers
00326     _FlushUsbTxBuffers((const uint8_t *)pipe);
00327 }
00328 
00329 static void _FlushUsbTxBuffers(const uint8_t *pipe)
00330 {
00331     int bufIdx;
00332     uint32_t timeout = 0x7ff;
00333 
00334     // try to lock mutex to avoid concurrent flushes
00335     __disable_irq();
00336     if (LockMutex(gUsbTxMutex, timeout) != 0)
00337     {
00338         __enable_irq();
00339         return;
00340     }
00341     __enable_irq();
00342 
00343     if ((bufIdx = _UsbTxBufferPeek()) >= 0)
00344     {
00345         TRACE_INFO("%u USB_TX_SEND(%d,%u)\n\r", (unsigned int)GetTicks(), bufIdx, gUsbTxBufferSizes[bufIdx]);
00346 
00347         /* Send buffer through the USB */
00348         while (USBD_Write(*pipe, &gUsbTxBuffers[DATAPACKETSIZE * bufIdx],
00349                 gUsbTxBufferSizes[bufIdx], _UsbDataSent, (void*)pipe) != USBD_STATUS_SUCCESS) {}
00350     }
00351     else
00352     {
00353         // nothing to Flush, release mutex
00354         __disable_irq();
00355         ReleaseMutex(gUsbTxMutex);
00356         __enable_irq();
00357     }
00358 }
00359 
00360 /*------------------------------------------------------------------------------
00361  *         Exported functions
00362  *------------------------------------------------------------------------------*/
00363 
00364 /**
00365  * Initializes the USB Device CDC serial port function.
00366  * \param pCdcd Pointer to CDCDEEMPort instance.
00367  * \param pUsbd Pointer to USBDDriver instance.
00368  * \param fEventHandler Pointer to event handler function.
00369  * \param firstInterface First interface index for the function
00370  *                       (0xFF to parse from descriptors).
00371  * \param numInterface   Number of interfaces for the function.
00372  */
00373 void CDCDEEMPort_Initialize(CDCDEEMPort * pCdcd,
00374                                USBDDriver * pUsbd,
00375                                uint8_t firstInterface,uint8_t numInterface)
00376 {
00377     TRACE_INFO("CDCDEEMPort_Initialize\n\r");
00378 
00379     /* Initialize USB Device Driver interface */
00380     pCdcd->pUsbd = pUsbd;
00381     pCdcd->bInterfaceNdx = firstInterface;
00382     pCdcd->bNumInterface = numInterface;
00383     pCdcd->bBulkInPIPE  = 0;
00384     pCdcd->bBulkOutPIPE = 0;
00385 }
00386 
00387 /**
00388  * Parse CDC EEM Port information for CDCDEEMPort instance.
00389  * Accepted interfaces:
00390  * - Communication Interface + Data Interface
00391  * - Data Interface ONLY
00392  * \param pCdcd        Pointer to CDCDEEMPort instance.
00393  * \param pDescriptors Pointer to descriptor list.
00394  * \param dwLength     Descriptor list size in bytes.
00395  */
00396 USBGenericDescriptor *CDCDEEMPort_ParseInterfaces(
00397     CDCDEEMPort *pCdcd,
00398     USBGenericDescriptor *pDescriptors,
00399     uint32_t dwLength)
00400 {
00401     CDCDParseData parseData;
00402 
00403     parseData.pCdcd   = pCdcd;
00404     parseData.pIfDesc = 0;
00405 
00406     return USBGenericDescriptor_Parse(
00407                     pDescriptors, dwLength,
00408                     (USBDescriptorParseFunction)_Interfaces_Parse,
00409                     &parseData);
00410 }
00411 
00412 
00413 /**
00414  * Handles CDC-specific SETUP requests. Should be called from a
00415  * re-implementation of USBDCallbacks_RequestReceived() method.
00416  * \param pCdcd Pointer to CDCDEEMPort instance.
00417  * \param request Pointer to a USBGenericRequest instance.
00418  * \return USBRC_SUCCESS if request handled, otherwise error.
00419  */
00420 uint32_t CDCDEEMPort_RequestHandler(
00421     CDCDEEMPort *pCdcd,
00422     const USBGenericRequest *request)
00423 {
00424     if (USBGenericRequest_GetType(request) != USBGenericRequest_CLASS)
00425         return USBRC_PARAM_ERR;
00426 
00427     TRACE_INFO_WP("Cdce ");
00428 
00429     /* Validate interface */
00430     if (request->wIndex >= pCdcd->bInterfaceNdx &&
00431         request->wIndex < pCdcd->bInterfaceNdx + pCdcd->bNumInterface) {
00432     }
00433     else {
00434         return USBRC_PARAM_ERR;
00435     }
00436 
00437     /* Handle the request */
00438     switch (USBGenericRequest_GetRequest(request)) {
00439 
00440         default:
00441 
00442             return USBRC_PARAM_ERR;
00443     }
00444 
00445     return USBRC_SUCCESS;
00446 }
00447 
00448 /**
00449  * Receives data from the host through the virtual COM port created by
00450  * the CDC device serial driver. This function behaves like USBD_Read.
00451  * \param pCdcd  Pointer to CDCDEEMPort instance.
00452  * \param pData  Pointer to the data buffer to put received data.
00453  * \param dwSize Size of the data buffer in bytes.
00454  * \param fCallback Optional callback function to invoke when the transfer
00455  *                  finishes.
00456  * \param pArg      Optional argument to the callback function.
00457  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;
00458  *         otherwise, the corresponding error code.
00459  */
00460 uint32_t CDCDEEMPort_Read(const CDCDEEMPort * pCdcd,
00461                           void * pData, uint32_t dwSize,
00462                           TransferCallback fCallback, void * pArg)
00463 {
00464     if (pCdcd->bBulkOutPIPE == 0)
00465         return USBRC_PARAM_ERR;
00466  
00467     gRxCallback = fCallback;
00468     gRxCallbackArg = pArg;
00469     gRxData = pData;
00470     gRxDataSize = dwSize;
00471     return USBD_Read(pCdcd->bBulkOutPIPE,
00472                      gRxBuffer, DATABUFFERSIZE,
00473                      (TransferCallback)_TransferCallback, pArg);
00474 }
00475 
00476 /**
00477  * Sends a data buffer through the virtual COM port created by the CDC
00478  * device serial driver. This function behaves exactly like USBD_Write.
00479  *
00480  * TODO batch packets ?
00481  *
00482  * \param pCdcd  Pointer to CDCDEEMPort instance.
00483  * \param pData  Pointer to the data buffer to send.
00484  * \param dwSize Size of the data buffer in bytes.
00485  * \param fCallback Optional callback function to invoke when the transfer
00486  *                  finishes.
00487  * \param pArg      Optional argument to the callback function.
00488  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;
00489  *         otherwise, the corresponding error code.
00490  */
00491 uint32_t CDCDEEMPort_Write(const CDCDEEMPort * pCdcd,
00492                                    void * pData, uint32_t dwSize,
00493                                    TransferCallback fCallback, void * pArg)
00494 {
00495     int bufIdx;
00496     uint8_t *buffer;
00497     uint32_t len;
00498 
00499     // unused args
00500     (void)fCallback;
00501     (void)pArg;
00502 
00503     if (pCdcd->bBulkInPIPE == 0)
00504         return USBRC_PARAM_ERR;
00505 
00506     bufIdx = _UsbTxBufferPush();
00507     if (bufIdx >= 0)
00508     {
00509         buffer = &gUsbTxBuffers[DATAPACKETSIZE * bufIdx];
00510 
00511         // EEM header
00512         len = dwSize;
00513         uint16_t eemHdr = (len + 4) & 0x3fff;
00514         buffer[0] = eemHdr & 0xff;
00515         buffer[1] = (eemHdr >> 8) & 0xff;
00516         memcpy(buffer + 2, pData, len);
00517         len += 2; // add EEM header length (2 bytes)
00518         buffer[len] = 0xde;
00519         buffer[len+1] = 0xad;
00520         buffer[len+2] = 0xbe;
00521         buffer[len+3] = 0xef;
00522         len += 4; // add CRC length (4 bytes)
00523 
00524         // add zero EEM packet when len % maxTransferSize == 0
00525         if (len % pCdcd->wBulkOutMaxPacketSize == 0) {
00526             memset(buffer + len, 0, 2);
00527             len += 2;
00528         }
00529 
00530         gUsbTxBufferSizes[bufIdx] = len;
00531 
00532         _FlushUsbTxBuffers(&pCdcd->bBulkInPIPE);
00533     }
00534     else
00535     {
00536         // TODO
00537     }
00538 
00539     return USBRC_SUCCESS;
00540 }
00541 
00542 /**@}*/
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines