2 LPCUSB, an USB device driver for LPC microcontrollers
\r
3 Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
\r
5 Redistribution and use in source and binary forms, with or without
\r
6 modification, are permitted provided that the following conditions are met:
\r
8 1. Redistributions of source code must retain the above copyright
\r
9 notice, this list of conditions and the following disclaimer.
\r
10 2. Redistributions in binary form must reproduce the above copyright
\r
11 notice, this list of conditions and the following disclaimer in the
\r
12 documentation and/or other materials provided with the distribution.
\r
13 3. The name of the author may not be used to endorse or promote products
\r
14 derived from this software without specific prior written permission.
\r
16 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
\r
17 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
\r
18 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
\r
19 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
\r
20 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
\r
21 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
\r
22 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
\r
23 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
\r
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
\r
25 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
\r
29 Minimal implementation of a USB serial port, using the CDC class.
\r
30 This example application simply echoes everything it receives right back
\r
34 Extract the usbser.sys file from .cab file in C:\WINDOWS\Driver Cache\i386
\r
35 and store it somewhere (C:\temp is a good place) along with the usbser.inf
\r
36 file. Then plug in the LPC176x and direct windows to the usbser driver.
\r
37 Windows then creates an extra COMx port that you can open in a terminal
\r
38 program, like hyperterminal. [Note for FreeRTOS users - the required .inf
\r
39 file is included in the project directory.]
\r
42 The device should be recognised automatically by the cdc_acm driver,
\r
43 which creates a /dev/ttyACMx device file that acts just like a regular
\r
48 #include "FreeRTOS.h"
\r
56 #include "usbdebug.h"
\r
57 #include "usbstruct.h"
\r
59 #include "LPC17xx.h"
\r
61 #define usbMAX_SEND_BLOCK ( 20 / portTICK_RATE_MS )
\r
62 #define usbBUFFER_LEN ( 20 )
\r
64 #define INCREMENT_ECHO_BY 1
\r
65 #define BAUD_RATE 115200
\r
67 #define INT_IN_EP 0x81
\r
68 #define BULK_OUT_EP 0x05
\r
69 #define BULK_IN_EP 0x82
\r
71 #define MAX_PACKET_SIZE 64
\r
73 #define LE_WORD(x) ((x)&0xFF),((x)>>8)
\r
76 #define CS_INTERFACE 0x24
\r
77 #define CS_ENDPOINT 0x25
\r
79 #define SET_LINE_CODING 0x20
\r
80 #define GET_LINE_CODING 0x21
\r
81 #define SET_CONTROL_LINE_STATE 0x22
\r
83 // data structure for GET_LINE_CODING / SET_LINE_CODING class requests
\r
85 unsigned long dwDTERate;
\r
86 unsigned char bCharFormat;
\r
87 unsigned char bParityType;
\r
88 unsigned char bDataBits;
\r
91 static TLineCoding LineCoding = {115200, 0, 0, 8};
\r
92 static unsigned char abBulkBuf[64];
\r
93 static unsigned char abClassReqData[8];
\r
95 static xQueueHandle xRxedChars = NULL, xCharsForTx = NULL;
\r
97 // forward declaration of interrupt handler
\r
98 void USBIntHandler(void);
\r
100 static const unsigned char abDescriptors[] = {
\r
102 // device descriptor
\r
105 LE_WORD(0x0101), // bcdUSB
\r
106 0x02, // bDeviceClass
\r
107 0x00, // bDeviceSubClass
\r
108 0x00, // bDeviceProtocol
\r
109 MAX_PACKET_SIZE0, // bMaxPacketSize
\r
110 LE_WORD(0xFFFF), // idVendor
\r
111 LE_WORD(0x0005), // idProduct
\r
112 LE_WORD(0x0100), // bcdDevice
\r
113 0x01, // iManufacturer
\r
115 0x03, // iSerialNumber
\r
116 0x01, // bNumConfigurations
\r
118 // configuration descriptor
\r
120 DESC_CONFIGURATION,
\r
121 LE_WORD(67), // wTotalLength
\r
122 0x02, // bNumInterfaces
\r
123 0x01, // bConfigurationValue
\r
124 0x00, // iConfiguration
\r
125 0xC0, // bmAttributes
\r
127 // control class interface
\r
130 0x00, // bInterfaceNumber
\r
131 0x00, // bAlternateSetting
\r
132 0x01, // bNumEndPoints
\r
133 0x02, // bInterfaceClass
\r
134 0x02, // bInterfaceSubClass
\r
135 0x01, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
\r
136 0x00, // iInterface
\r
137 // header functional descriptor
\r
142 // call management functional descriptor
\r
146 0x01, // bmCapabilities = device handles call management
\r
147 0x01, // bDataInterface
\r
148 // ACM functional descriptor
\r
152 0x02, // bmCapabilities
\r
153 // union functional descriptor
\r
157 0x00, // bMasterInterface
\r
158 0x01, // bSlaveInterface0
\r
162 INT_IN_EP, // bEndpointAddress
\r
163 0x03, // bmAttributes = intr
\r
164 LE_WORD(8), // wMaxPacketSize
\r
166 // data class interface descriptor
\r
169 0x01, // bInterfaceNumber
\r
170 0x00, // bAlternateSetting
\r
171 0x02, // bNumEndPoints
\r
172 0x0A, // bInterfaceClass = data
\r
173 0x00, // bInterfaceSubClass
\r
174 0x00, // bInterfaceProtocol
\r
175 0x00, // iInterface
\r
179 BULK_OUT_EP, // bEndpointAddress
\r
180 0x02, // bmAttributes = bulk
\r
181 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
\r
186 BULK_IN_EP, // bEndpointAddress
\r
187 0x02, // bmAttributes = bulk
\r
188 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
\r
191 // string descriptors
\r
198 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,
\r
202 'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0,
\r
206 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,
\r
208 // terminating zero
\r
214 Local function to handle incoming bulk data
\r
217 @param [in] bEPStatus
\r
219 static void BulkOut(unsigned char bEP, unsigned char bEPStatus)
\r
222 long lHigherPriorityTaskWoken = pdFALSE;
\r
224 ( void ) bEPStatus;
\r
226 // get data from USB into intermediate buffer
\r
227 iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf));
\r
228 for (i = 0; i < iLen; i++) {
\r
230 xQueueSendFromISR( xRxedChars, &( abBulkBuf[ i ] ), &lHigherPriorityTaskWoken );
\r
233 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
238 Local function to handle outgoing bulk data
\r
241 @param [in] bEPStatus
\r
243 static void BulkIn(unsigned char bEP, unsigned char bEPStatus)
\r
246 long lHigherPriorityTaskWoken = pdFALSE;
\r
248 ( void ) bEPStatus;
\r
250 if (uxQueueMessagesWaitingFromISR( xCharsForTx ) == 0) {
\r
251 // no more data, disable further NAK interrupts until next USB frame
\r
252 USBHwNakIntEnable(0);
\r
256 // get bytes from transmit FIFO into intermediate buffer
\r
257 for (i = 0; i < MAX_PACKET_SIZE; i++) {
\r
258 if( xQueueReceiveFromISR( xCharsForTx, ( &abBulkBuf[i] ), &lHigherPriorityTaskWoken ) != pdPASS )
\r
267 USBHwEPWrite(bEP, abBulkBuf, iLen);
\r
270 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
275 Local function to handle the USB-CDC class requests
\r
279 @param [out] ppbData
\r
281 static BOOL HandleClassRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
283 switch (pSetup->bRequest) {
\r
286 case SET_LINE_CODING:
\r
287 DBG("SET_LINE_CODING\n");
\r
288 memcpy((unsigned char *)&LineCoding, *ppbData, 7);
\r
290 DBG("dwDTERate=%u, bCharFormat=%u, bParityType=%u, bDataBits=%u\n",
\r
291 LineCoding.dwDTERate,
\r
292 LineCoding.bCharFormat,
\r
293 LineCoding.bParityType,
\r
294 LineCoding.bDataBits);
\r
298 case GET_LINE_CODING:
\r
299 DBG("GET_LINE_CODING\n");
\r
300 *ppbData = (unsigned char *)&LineCoding;
\r
304 // set control line state
\r
305 case SET_CONTROL_LINE_STATE:
\r
306 // bit0 = DTR, bit = RTS
\r
307 DBG("SET_CONTROL_LINE_STATE %X\n", pSetup->wValue);
\r
318 Writes one character to VCOM port
\r
320 @param [in] c character to write
\r
321 @returns character written, or EOF if character could not be written
\r
323 int VCOM_putchar(int c)
\r
325 char cc = ( char ) c;
\r
327 if( xQueueSend( xCharsForTx, &cc, usbMAX_SEND_BLOCK ) == pdPASS )
\r
339 Reads one character from VCOM port
\r
341 @returns character read, or EOF if character could not be read
\r
343 int VCOM_getchar(void)
\r
347 /* Block the task until a character is available. */
\r
348 xQueueReceive( xRxedChars, &c, portMAX_DELAY );
\r
356 Simply calls the USB ISR
\r
358 //void USBIntHandler(void)
\r
359 void USB_IRQHandler(void)
\r
365 static void USBFrameHandler(unsigned short wFrame)
\r
369 if( uxQueueMessagesWaitingFromISR( xCharsForTx ) > 0 )
\r
371 // data available, enable NAK interrupt on bulk in
\r
372 USBHwNakIntEnable(INACK_BI);
\r
376 // CodeRed - added CPUcpsie
\r
378 unsigned long CPUcpsie(void)
\r
380 unsigned long ulRet;
\r
383 // Read PRIMASK and enable interrupts.
\r
385 __asm(" mrs %0, PRIMASK\n"
\r
391 // The return is handled in the inline assembly, but the compiler will
\r
392 // still complain if there is not an explicit return here (despite the fact
\r
393 // that this does not result in any code being produced because of the
\r
394 // naked attribute).
\r
399 void vUSBTask( void *pvParameters )
\r
403 /* Just to prevent compiler warnings about the unused parameter. */
\r
404 ( void ) pvParameters;
\r
405 DBG("Initialising USB stack\n");
\r
407 xRxedChars = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
\r
408 xCharsForTx = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
\r
410 if( ( xRxedChars == NULL ) || ( xCharsForTx == NULL ) )
\r
412 /* Not enough heap available to create the buffer queues, can't do
\r
413 anything so just delete ourselves. */
\r
414 vTaskDelete( NULL );
\r
418 // initialise stack
\r
421 // register descriptors
\r
422 USBRegisterDescriptors(abDescriptors);
\r
424 // register class request handler
\r
425 USBRegisterRequestHandler(REQTYPE_TYPE_CLASS, HandleClassRequest, abClassReqData);
\r
427 // register endpoint handlers
\r
428 USBHwRegisterEPIntHandler(INT_IN_EP, NULL);
\r
429 USBHwRegisterEPIntHandler(BULK_IN_EP, BulkIn);
\r
430 USBHwRegisterEPIntHandler(BULK_OUT_EP, BulkOut);
\r
432 // register frame handler
\r
433 USBHwRegisterFrameHandler(USBFrameHandler);
\r
435 // enable bulk-in interrupts on NAKs
\r
436 USBHwNakIntEnable(INACK_BI);
\r
438 DBG("Starting USB communication\n");
\r
440 NVIC_SetPriority( USB_IRQn, configUSB_INTERRUPT_PRIORITY );
\r
441 NVIC_EnableIRQ( USB_IRQn );
\r
445 DBG("Connecting to USB bus\n");
\r
446 USBHwConnect(TRUE);
\r
448 // echo any character received (do USB stuff in interrupt)
\r
451 c = VCOM_getchar();
\r
454 // Echo character back with INCREMENT_ECHO_BY offset, so for example if
\r
455 // INCREMENT_ECHO_BY is 1 and 'A' is received, 'B' will be echoed back.
\r
456 VCOM_putchar(c + INCREMENT_ECHO_BY );
\r