]> git.sur5r.net Git - freertos/blob - FreeRTOS/Demo/CORTEX_A5_SAMA5D3x_Xplained_IAR/AtmelFiles/usb/device/cdc-serial/CDCDSerialPort.c
SAMA5D3 demo: Add CDC driver code and use CDC to create a simple command console.
[freertos] / FreeRTOS / Demo / CORTEX_A5_SAMA5D3x_Xplained_IAR / AtmelFiles / usb / device / cdc-serial / CDCDSerialPort.c
1 /* ----------------------------------------------------------------------------\r
2  *         ATMEL Microcontroller Software Support\r
3  * ----------------------------------------------------------------------------\r
4  * Copyright (c) 2008, Atmel Corporation\r
5  *\r
6  * All rights reserved.\r
7  *\r
8  * Redistribution and use in source and binary forms, with or without\r
9  * modification, are permitted provided that the following conditions are met:\r
10  *\r
11  * - Redistributions of source code must retain the above copyright notice,\r
12  * this list of conditions and the disclaimer below.\r
13  *\r
14  * Atmel's name may not be used to endorse or promote products derived from\r
15  * this software without specific prior written permission.\r
16  *\r
17  * DISCLAIMER: THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR\r
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF\r
19  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE\r
20  * DISCLAIMED. IN NO EVENT SHALL ATMEL BE LIABLE FOR ANY DIRECT, INDIRECT,\r
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT\r
22  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,\r
23  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF\r
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING\r
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,\r
26  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\r
27  * ----------------------------------------------------------------------------\r
28  */\r
29 \r
30 /**\file\r
31  *  Implementation of the CDCDSerialPort class methods.\r
32  */\r
33 \r
34 /** \addtogroup usbd_cdc\r
35  *@{\r
36  */\r
37 \r
38 /*------------------------------------------------------------------------------\r
39  *         Headers\r
40  *------------------------------------------------------------------------------*/\r
41 \r
42 #include <CDCDSerialPort.h>\r
43 #include <CDCDescriptors.h>\r
44 #include <USBLib_Trace.h>\r
45 \r
46 /*------------------------------------------------------------------------------\r
47  *         Types\r
48  *------------------------------------------------------------------------------*/\r
49 \r
50 /** Parse data extention for descriptor parsing  */\r
51 typedef struct _CDCDParseData {\r
52     /** Pointer to CDCDSerialPort instance */\r
53     CDCDSerialPort * pCdcd;\r
54     /** Pointer to found interface descriptor */\r
55     USBInterfaceDescriptor * pIfDesc;\r
56     \r
57 } CDCDParseData;\r
58 \r
59 /*------------------------------------------------------------------------------\r
60  *         Internal variables\r
61  *------------------------------------------------------------------------------*/\r
62 \r
63 /** Line coding values */\r
64 static CDCLineCoding lineCoding;\r
65 \r
66 /*------------------------------------------------------------------------------\r
67  *         Internal functions\r
68  *------------------------------------------------------------------------------*/\r
69 \r
70 /**\r
71  * Parse descriptors: Interface, Bulk IN/OUT, Interrupt IN.\r
72  * \param desc Pointer to descriptor list.\r
73  * \param arg  Argument, pointer to AUDDParseData instance.\r
74  */\r
75 static uint32_t _Interfaces_Parse(USBGenericDescriptor *pDesc,\r
76                                   CDCDParseData * pArg)\r
77 {\r
78     CDCDSerialPort *pCdcd = pArg->pCdcd;\r
79 \r
80     /* Not a valid descriptor */\r
81     if (pDesc->bLength == 0)\r
82         return USBRC_PARAM_ERR;\r
83 \r
84     /* Find interface descriptor */\r
85     if (pDesc->bDescriptorType == USBGenericDescriptor_INTERFACE) {\r
86         USBInterfaceDescriptor *pIf = (USBInterfaceDescriptor*)pDesc;\r
87 \r
88         /* Obtain interface from descriptor */\r
89         if (pCdcd->bInterfaceNdx == 0xFF) {\r
90             /* First interface is communication */\r
91             if (pIf->bInterfaceClass ==\r
92                 CDCCommunicationInterfaceDescriptor_CLASS) {\r
93                 pCdcd->bInterfaceNdx = pIf->bInterfaceNumber;\r
94                 pCdcd->bNumInterface = 2;\r
95             }\r
96             /* Only data interface */\r
97             else if(pIf->bInterfaceClass == CDCDataInterfaceDescriptor_CLASS) {\r
98                 pCdcd->bInterfaceNdx = pIf->bInterfaceNumber;\r
99                 pCdcd->bNumInterface = 1;\r
100             }\r
101             pArg->pIfDesc = pIf;\r
102         }\r
103         else if (pCdcd->bInterfaceNdx <= pIf->bInterfaceNumber\r
104             &&   pCdcd->bInterfaceNdx + pCdcd->bNumInterface\r
105                                        > pIf->bInterfaceNumber) {\r
106             pArg->pIfDesc = pIf;\r
107         }\r
108     }\r
109 \r
110     /* Parse valid interfaces */\r
111     if (pArg->pIfDesc == 0)\r
112         return 0;\r
113 \r
114     /* Find endpoint descriptors */\r
115     if (pDesc->bDescriptorType == USBGenericDescriptor_ENDPOINT) {\r
116         USBEndpointDescriptor *pEp = (USBEndpointDescriptor*)pDesc;\r
117         switch(pEp->bmAttributes & 0x3) {\r
118             case USBEndpointDescriptor_INTERRUPT:\r
119                 if (pEp->bEndpointAddress & 0x80)\r
120                     pCdcd->bIntInPIPE = pEp->bEndpointAddress & 0x7F;\r
121                 break;\r
122             case USBEndpointDescriptor_BULK:\r
123                 if (pEp->bEndpointAddress & 0x80)\r
124                     pCdcd->bBulkInPIPE = pEp->bEndpointAddress & 0x7F;\r
125                 else\r
126                     pCdcd->bBulkOutPIPE = pEp->bEndpointAddress;\r
127         }\r
128     }\r
129 \r
130     if (    pCdcd->bInterfaceNdx != 0xFF\r
131         &&  pCdcd->bBulkInPIPE != 0\r
132         &&  pCdcd->bBulkOutPIPE != 0)\r
133         return USBRC_FINISHED;\r
134 \r
135     return 0;\r
136 }\r
137 \r
138 /**\r
139  * Callback function which should be invoked after the data of a\r
140  * SetLineCoding request has been retrieved. Sends a zero-length packet\r
141  * to the host for acknowledging the request.\r
142  * \param pCdcd Pointer to CDCDSerialPort instance.\r
143  */\r
144 static void _SetLineCodingCallback(CDCDSerialPort * pCdcd)\r
145 {\r
146     uint32_t exec = 1;\r
147     if (pCdcd->fEventHandler) {\r
148         uint32_t rc = pCdcd->fEventHandler(\r
149                                         CDCDSerialPortEvent_SETLINECODING,\r
150                                         (uint32_t)(&lineCoding),\r
151                                         pCdcd->pArg);\r
152         if (rc == USBD_STATUS_SUCCESS) {\r
153             pCdcd->lineCoding.dwDTERate   = lineCoding.dwDTERate;\r
154             pCdcd->lineCoding.bCharFormat = lineCoding.bCharFormat;\r
155             pCdcd->lineCoding.bParityType = lineCoding.bParityType;\r
156             pCdcd->lineCoding.bDataBits   = lineCoding.bDataBits;\r
157         }\r
158         else\r
159             exec = 0;\r
160     }\r
161     if (exec)   USBD_Write(0, 0, 0, 0, 0);\r
162     else        USBD_Stall(0);\r
163 }\r
164 \r
165 /**\r
166  * Receives new line coding information from the USB host.\r
167  * \param pCdcd Pointer to CDCDSerialPort instance.\r
168  */\r
169 static void _SetLineCoding(CDCDSerialPort * pCdcd)\r
170 {\r
171     TRACE_INFO_WP("sLineCoding ");\r
172 \r
173     USBD_Read(0,\r
174               (void *) & (lineCoding),\r
175               sizeof(CDCLineCoding),\r
176               (TransferCallback)_SetLineCodingCallback,\r
177               (void*)pCdcd);\r
178 }\r
179 \r
180 /**\r
181  * Sends the current line coding information to the host through Control\r
182  * endpoint 0.\r
183  * \param pCdcd Pointer to CDCDSerialPort instance.\r
184  */\r
185 static void _GetLineCoding(CDCDSerialPort * pCdcd)\r
186 {\r
187     TRACE_INFO_WP("gLineCoding ");\r
188 \r
189     USBD_Write(0,\r
190                (void *) &(pCdcd->lineCoding),\r
191                sizeof(CDCLineCoding),\r
192                0,\r
193                0);\r
194 }\r
195 \r
196 /**\r
197  * Changes the state of the serial driver according to the information\r
198  * sent by the host via a SetControlLineState request, and acknowledges\r
199  * the request with a zero-length packet.\r
200  * \param pCdcd Pointer to CDCDSerialPort instance.\r
201  * \param request Pointer to a USBGenericRequest instance.\r
202  */\r
203 static void _SetControlLineState(\r
204     CDCDSerialPort * pCdcd,\r
205     const USBGenericRequest *request)\r
206 {\r
207   #if (TRACE_LEVEL >= TRACE_LEVEL_INFO)\r
208     uint8_t DTR, RTS;\r
209 \r
210     DTR = ((request->wValue & CDCControlLineState_DTR) > 0);\r
211     RTS = ((request->wValue & CDCControlLineState_RTS) > 0);\r
212     TRACE_INFO_WP("sControlLineState(%d, %d) ", DTR, RTS);\r
213   #endif\r
214 \r
215     pCdcd->bControlLineState = (uint8_t)request->wValue;\r
216     USBD_Write(0, 0, 0, 0, 0);\r
217 \r
218     if (pCdcd->fEventHandler)\r
219         pCdcd->fEventHandler(CDCDSerialPortEvent_SETCONTROLLINESTATE,\r
220
221                              (uint32_t)pCdcd->bControlLineState,\r
222                              pCdcd->pArg);\r
223 }\r
224 \r
225 /*------------------------------------------------------------------------------\r
226  *         Exported functions\r
227  *------------------------------------------------------------------------------*/\r
228 \r
229 /**\r
230  * Initializes the USB Device CDC serial port function.\r
231  * \param pCdcd Pointer to CDCDSerialPort instance.\r
232  * \param pUsbd Pointer to USBDDriver instance.\r
233  * \param fEventHandler Pointer to event handler function.\r
234  * \param firstInterface First interface index for the function\r
235  *                       (0xFF to parse from descriptors).\r
236  * \param numInterface   Number of interfaces for the function.\r
237  */\r
238 void CDCDSerialPort_Initialize(CDCDSerialPort * pCdcd,\r
239                                USBDDriver * pUsbd,\r
240                                CDCDSerialPortEventHandler fEventHandler,\r
241                                void * pArg,\r
242                                uint8_t firstInterface,uint8_t numInterface)\r
243 {\r
244     TRACE_INFO("CDCDSerialPort_Initialize\n\r");\r
245 \r
246     /* Initialize event handler */\r
247     pCdcd->fEventHandler = fEventHandler;\r
248     pCdcd->pArg = pArg;\r
249 \r
250     /* Initialize USB Device Driver interface */\r
251     pCdcd->pUsbd = pUsbd;\r
252     pCdcd->bInterfaceNdx = firstInterface;\r
253     pCdcd->bNumInterface = numInterface;\r
254     pCdcd->bIntInPIPE   = 0;\r
255     pCdcd->bBulkInPIPE  = 0;\r
256     pCdcd->bBulkOutPIPE = 0;\r
257 \r
258     /* Initialize Abstract Control Model attributes */\r
259     pCdcd->bControlLineState = 0;\r
260     pCdcd->wSerialState      = 0;\r
261     CDCLineCoding_Initialize(&(pCdcd->lineCoding),\r
262                              115200,\r
263                              CDCLineCoding_ONESTOPBIT,\r
264                              CDCLineCoding_NOPARITY,\r
265                              8);\r
266 }\r
267 \r
268 /**\r
269  * Parse CDC Serial Port information for CDCDSerialPort instance.\r
270  * Accepted interfaces:\r
271  * - Communication Interface + Data Interface\r
272  * - Data Interface ONLY\r
273  * \param pCdcd        Pointer to CDCDSerialPort instance.\r
274  * \param pDescriptors Pointer to descriptor list.\r
275  * \param dwLength     Descriptor list size in bytes.\r
276  */\r
277 USBGenericDescriptor *CDCDSerialPort_ParseInterfaces(\r
278     CDCDSerialPort *pCdcd,\r
279     USBGenericDescriptor *pDescriptors,\r
280     uint32_t dwLength)\r
281 {\r
282     CDCDParseData parseData;\r
283 \r
284     parseData.pCdcd   = pCdcd;\r
285     parseData.pIfDesc = 0;\r
286 \r
287     return USBGenericDescriptor_Parse(\r
288                     pDescriptors, dwLength,\r
289                     (USBDescriptorParseFunction)_Interfaces_Parse,\r
290                     &parseData);\r
291 }\r
292 \r
293 \r
294 /**\r
295  * Handles CDC-specific SETUP requests. Should be called from a\r
296  * re-implementation of USBDCallbacks_RequestReceived() method.\r
297  * \param pCdcd Pointer to CDCDSerialPort instance.\r
298  * \param request Pointer to a USBGenericRequest instance.\r
299  * \return USBRC_SUCCESS if request handled, otherwise error.\r
300  */\r
301 uint32_t CDCDSerialPort_RequestHandler(\r
302     CDCDSerialPort *pCdcd,\r
303     const USBGenericRequest *request)\r
304 {\r
305     if (USBGenericRequest_GetType(request) != USBGenericRequest_CLASS)\r
306         return USBRC_PARAM_ERR;\r
307 \r
308     TRACE_INFO_WP("Cdcs ");\r
309 \r
310     /* Validate interface */\r
311     if (request->wIndex >= pCdcd->bInterfaceNdx &&\r
312         request->wIndex < pCdcd->bInterfaceNdx + pCdcd->bNumInterface) {\r
313     }\r
314     else {\r
315         return USBRC_PARAM_ERR;\r
316     }\r
317 \r
318     /* Handle the request */\r
319     switch (USBGenericRequest_GetRequest(request)) {\r
320 \r
321         case CDCGenericRequest_SETLINECODING:\r
322 \r
323             _SetLineCoding(pCdcd);\r
324             break;\r
325 \r
326         case CDCGenericRequest_GETLINECODING:\r
327 \r
328             _GetLineCoding(pCdcd);\r
329             break;\r
330 \r
331         case CDCGenericRequest_SETCONTROLLINESTATE:\r
332 \r
333             _SetControlLineState(pCdcd, request);\r
334             break;\r
335 \r
336         default:\r
337 \r
338             return USBRC_PARAM_ERR;\r
339     }\r
340 \r
341     return USBRC_SUCCESS;\r
342 }\r
343 \r
344 /**\r
345  * Receives data from the host through the virtual COM port created by\r
346  * the CDC device serial driver. This function behaves like USBD_Read.\r
347  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
348  * \param pData  Pointer to the data buffer to put received data.\r
349  * \param dwSize Size of the data buffer in bytes.\r
350  * \param fCallback Optional callback function to invoke when the transfer\r
351  *                  finishes.\r
352  * \param pArg      Optional argument to the callback function.\r
353  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;\r
354  *         otherwise, the corresponding error code.\r
355  */\r
356 uint32_t CDCDSerialPort_Read(const CDCDSerialPort * pCdcd,\r
357                           void * pData,uint32_t dwSize,\r
358                           TransferCallback fCallback,void * pArg)\r
359 {\r
360     if (pCdcd->bBulkOutPIPE == 0)\r
361         return USBRC_PARAM_ERR;\r
362 \r
363     return USBD_Read(pCdcd->bBulkOutPIPE,\r
364                      pData, dwSize,\r
365                      fCallback, pArg);\r
366 }\r
367 \r
368 /**\r
369  * Sends a data buffer through the virtual COM port created by the CDC\r
370  * device serial driver. This function behaves exactly like USBD_Write.\r
371  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
372  * \param pData  Pointer to the data buffer to send.\r
373  * \param dwSize Size of the data buffer in bytes.\r
374  * \param fCallback Optional callback function to invoke when the transfer\r
375  *                  finishes.\r
376  * \param pArg      Optional argument to the callback function.\r
377  * \return USBD_STATUS_SUCCESS if the read operation has been started normally;\r
378  *         otherwise, the corresponding error code.\r
379  */\r
380 uint32_t CDCDSerialPort_Write(const CDCDSerialPort * pCdcd,\r
381                                    void * pData, uint32_t dwSize,\r
382                                    TransferCallback fCallback, void * pArg)\r
383 {\r
384     if (pCdcd->bBulkInPIPE == 0)\r
385         return USBRC_PARAM_ERR;\r
386 \r
387     return USBD_Write(pCdcd->bBulkInPIPE,\r
388                       pData, dwSize,\r
389                       fCallback, pArg);\r
390 }\r
391 \r
392 /**\r
393  * Returns the current control line state of the RS-232 line.\r
394  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
395  */\r
396 uint8_t CDCDSerialPort_GetControlLineState(const CDCDSerialPort * pCdcd)\r
397 {\r
398     return pCdcd->bControlLineState;\r
399 }\r
400 \r
401 /**\r
402  * Copy current line coding settings to pointered space.\r
403  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
404  * \param pLineCoding Pointer to CDCLineCoding instance.\r
405  */\r
406 void CDCDSerialPort_GetLineCoding(const CDCDSerialPort * pCdcd,\r
407                                   CDCLineCoding* pLineCoding)\r
408 {\r
409     if (pLineCoding) {\r
410         pLineCoding->dwDTERate   = pCdcd->lineCoding.dwDTERate;\r
411         pLineCoding->bCharFormat = pCdcd->lineCoding.bCharFormat;\r
412         pLineCoding->bParityType = pCdcd->lineCoding.bParityType;\r
413         pLineCoding->bDataBits   = pCdcd->lineCoding.bDataBits;\r
414     }\r
415 }\r
416 \r
417 /**\r
418  * Returns the current status of the RS-232 line.\r
419  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
420  */\r
421 uint16_t CDCDSerialPort_GetSerialState(const CDCDSerialPort * pCdcd)\r
422 {\r
423     return pCdcd->wSerialState;\r
424 }\r
425 \r
426 /**\r
427  * Sets the current serial state of the device to the given value.\r
428  * \param pCdcd  Pointer to CDCDSerialPort instance.\r
429  * \param wSerialState  New device state.\r
430  */\r
431 void CDCDSerialPort_SetSerialState(CDCDSerialPort * pCdcd,\r
432                                    uint16_t wSerialState)\r
433 {\r
434     if (pCdcd->bIntInPIPE == 0)\r
435         return;\r
436 \r
437     /* If new state is different from previous one, send a notification to the\r
438        host */\r
439     if (pCdcd->wSerialState != wSerialState) {\r
440 \r
441         pCdcd->wSerialState = wSerialState;\r
442         USBD_Write(pCdcd->bIntInPIPE,\r
443                    &(pCdcd->wSerialState),\r
444                    2,\r
445                    0,\r
446                    0);\r
447 \r
448         /* Reset one-time flags */\r
449         pCdcd->wSerialState &= ~(CDCSerialState_OVERRUN\r
450                               | CDCSerialState_PARITY\r
451                               | CDCSerialState_FRAMING\r
452                               | CDCSerialState_RINGSIGNAL\r
453                               | CDCSerialState_BREAK);\r
454     }\r
455 }\r
456 \r
457 /**@}*/\r
458 \r