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
55 #include "usbdebug.h"
\r
56 #include "usbstruct.h"
\r
58 #include "LPC17xx.h"
\r
60 #define usbMAX_SEND_BLOCK ( 20 / portTICK_RATE_MS )
\r
61 #define usbBUFFER_LEN ( 20 )
\r
63 #define INCREMENT_ECHO_BY 1
\r
64 #define BAUD_RATE 115200
\r
66 #define INT_IN_EP 0x81
\r
67 #define BULK_OUT_EP 0x05
\r
68 #define BULK_IN_EP 0x82
\r
70 #define MAX_PACKET_SIZE 64
\r
72 #define LE_WORD(x) ((x)&0xFF),((x)>>8)
\r
75 #define CS_INTERFACE 0x24
\r
76 #define CS_ENDPOINT 0x25
\r
78 #define SET_LINE_CODING 0x20
\r
79 #define GET_LINE_CODING 0x21
\r
80 #define SET_CONTROL_LINE_STATE 0x22
\r
82 // data structure for GET_LINE_CODING / SET_LINE_CODING class requests
\r
84 unsigned long dwDTERate;
\r
85 unsigned char bCharFormat;
\r
86 unsigned char bParityType;
\r
87 unsigned char bDataBits;
\r
90 static TLineCoding LineCoding = {115200, 0, 0, 8};
\r
91 static unsigned char abBulkBuf[64];
\r
92 static unsigned char abClassReqData[8];
\r
94 static xQueueHandle xRxedChars = NULL, xCharsForTx = NULL;
\r
96 // forward declaration of interrupt handler
\r
97 void USBIntHandler(void);
\r
99 static const unsigned char abDescriptors[] = {
\r
101 // device descriptor
\r
104 LE_WORD(0x0101), // bcdUSB
\r
105 0x02, // bDeviceClass
\r
106 0x00, // bDeviceSubClass
\r
107 0x00, // bDeviceProtocol
\r
108 MAX_PACKET_SIZE0, // bMaxPacketSize
\r
109 LE_WORD(0xFFFF), // idVendor
\r
110 LE_WORD(0x0005), // idProduct
\r
111 LE_WORD(0x0100), // bcdDevice
\r
112 0x01, // iManufacturer
\r
114 0x03, // iSerialNumber
\r
115 0x01, // bNumConfigurations
\r
117 // configuration descriptor
\r
119 DESC_CONFIGURATION,
\r
120 LE_WORD(67), // wTotalLength
\r
121 0x02, // bNumInterfaces
\r
122 0x01, // bConfigurationValue
\r
123 0x00, // iConfiguration
\r
124 0xC0, // bmAttributes
\r
126 // control class interface
\r
129 0x00, // bInterfaceNumber
\r
130 0x00, // bAlternateSetting
\r
131 0x01, // bNumEndPoints
\r
132 0x02, // bInterfaceClass
\r
133 0x02, // bInterfaceSubClass
\r
134 0x01, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
\r
135 0x00, // iInterface
\r
136 // header functional descriptor
\r
141 // call management functional descriptor
\r
145 0x01, // bmCapabilities = device handles call management
\r
146 0x01, // bDataInterface
\r
147 // ACM functional descriptor
\r
151 0x02, // bmCapabilities
\r
152 // union functional descriptor
\r
156 0x00, // bMasterInterface
\r
157 0x01, // bSlaveInterface0
\r
161 INT_IN_EP, // bEndpointAddress
\r
162 0x03, // bmAttributes = intr
\r
163 LE_WORD(8), // wMaxPacketSize
\r
165 // data class interface descriptor
\r
168 0x01, // bInterfaceNumber
\r
169 0x00, // bAlternateSetting
\r
170 0x02, // bNumEndPoints
\r
171 0x0A, // bInterfaceClass = data
\r
172 0x00, // bInterfaceSubClass
\r
173 0x00, // bInterfaceProtocol
\r
174 0x00, // iInterface
\r
178 BULK_OUT_EP, // bEndpointAddress
\r
179 0x02, // bmAttributes = bulk
\r
180 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
\r
185 BULK_IN_EP, // bEndpointAddress
\r
186 0x02, // bmAttributes = bulk
\r
187 LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
\r
190 // string descriptors
\r
197 'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,
\r
201 'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0,
\r
205 'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,
\r
207 // terminating zero
\r
213 Local function to handle incoming bulk data
\r
216 @param [in] bEPStatus
\r
218 static void BulkOut(unsigned char bEP, unsigned char bEPStatus)
\r
221 long lHigherPriorityTaskWoken = pdFALSE;
\r
223 ( void ) bEPStatus;
\r
225 // get data from USB into intermediate buffer
\r
226 iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf));
\r
227 for (i = 0; i < iLen; i++) {
\r
229 xQueueSendFromISR( xRxedChars, &( abBulkBuf[ i ] ), &lHigherPriorityTaskWoken );
\r
232 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
237 Local function to handle outgoing bulk data
\r
240 @param [in] bEPStatus
\r
242 static void BulkIn(unsigned char bEP, unsigned char bEPStatus)
\r
245 long lHigherPriorityTaskWoken = pdFALSE;
\r
247 ( void ) bEPStatus;
\r
249 if (uxQueueMessagesWaitingFromISR( xCharsForTx ) == 0) {
\r
250 // no more data, disable further NAK interrupts until next USB frame
\r
251 USBHwNakIntEnable(0);
\r
255 // get bytes from transmit FIFO into intermediate buffer
\r
256 for (i = 0; i < MAX_PACKET_SIZE; i++) {
\r
257 if( xQueueReceiveFromISR( xCharsForTx, ( &abBulkBuf[i] ), &lHigherPriorityTaskWoken ) != pdPASS )
\r
266 USBHwEPWrite(bEP, abBulkBuf, iLen);
\r
269 portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
\r
274 Local function to handle the USB-CDC class requests
\r
278 @param [out] ppbData
\r
280 static BOOL HandleClassRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
\r
282 switch (pSetup->bRequest) {
\r
285 case SET_LINE_CODING:
\r
286 DBG("SET_LINE_CODING\n");
\r
287 memcpy((unsigned char *)&LineCoding, *ppbData, 7);
\r
289 DBG("dwDTERate=%u, bCharFormat=%u, bParityType=%u, bDataBits=%u\n",
\r
290 LineCoding.dwDTERate,
\r
291 LineCoding.bCharFormat,
\r
292 LineCoding.bParityType,
\r
293 LineCoding.bDataBits);
\r
297 case GET_LINE_CODING:
\r
298 DBG("GET_LINE_CODING\n");
\r
299 *ppbData = (unsigned char *)&LineCoding;
\r
303 // set control line state
\r
304 case SET_CONTROL_LINE_STATE:
\r
305 // bit0 = DTR, bit = RTS
\r
306 DBG("SET_CONTROL_LINE_STATE %X\n", pSetup->wValue);
\r
317 Writes one character to VCOM port
\r
319 @param [in] c character to write
\r
320 @returns character written, or EOF if character could not be written
\r
322 int VCOM_putchar(int c)
\r
324 char cc = ( char ) c;
\r
326 if( xQueueSend( xCharsForTx, &cc, usbMAX_SEND_BLOCK ) == pdPASS )
\r
338 Reads one character from VCOM port
\r
340 @returns character read, or EOF if character could not be read
\r
342 int VCOM_getchar(void)
\r
346 /* Block the task until a character is available. */
\r
347 xQueueReceive( xRxedChars, &c, portMAX_DELAY );
\r
355 Simply calls the USB ISR
\r
357 //void USBIntHandler(void)
\r
358 void USB_IRQHandler(void)
\r
364 static void USBFrameHandler(unsigned short wFrame)
\r
368 if( uxQueueMessagesWaitingFromISR( xCharsForTx ) > 0 )
\r
370 // data available, enable NAK interrupt on bulk in
\r
371 USBHwNakIntEnable(INACK_BI);
\r
375 // CodeRed - added CPUcpsie
\r
377 unsigned long CPUcpsie(void)
\r
379 unsigned long ulRet;
\r
382 // Read PRIMASK and enable interrupts.
\r
384 __asm(" mrs %0, PRIMASK\n"
\r
390 // The return is handled in the inline assembly, but the compiler will
\r
391 // still complain if there is not an explicit return here (despite the fact
\r
392 // that this does not result in any code being produced because of the
\r
393 // naked attribute).
\r
398 void vUSBTask( void *pvParameters )
\r
402 /* Just to prevent compiler warnings about the unused parameter. */
\r
403 ( void ) pvParameters;
\r
404 DBG("Initialising USB stack\n");
\r
406 xRxedChars = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
\r
407 xCharsForTx = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
\r
409 if( ( xRxedChars == NULL ) || ( xCharsForTx == NULL ) )
\r
411 /* Not enough heap available to create the buffer queues, can't do
\r
412 anything so just delete ourselves. */
\r
413 vTaskDelete( NULL );
\r
417 // initialise stack
\r
420 // register descriptors
\r
421 USBRegisterDescriptors(abDescriptors);
\r
423 // register class request handler
\r
424 USBRegisterRequestHandler(REQTYPE_TYPE_CLASS, HandleClassRequest, abClassReqData);
\r
426 // register endpoint handlers
\r
427 USBHwRegisterEPIntHandler(INT_IN_EP, NULL);
\r
428 USBHwRegisterEPIntHandler(BULK_IN_EP, BulkIn);
\r
429 USBHwRegisterEPIntHandler(BULK_OUT_EP, BulkOut);
\r
431 // register frame handler
\r
432 USBHwRegisterFrameHandler(USBFrameHandler);
\r
434 // enable bulk-in interrupts on NAKs
\r
435 USBHwNakIntEnable(INACK_BI);
\r
437 DBG("Starting USB communication\n");
\r
439 NVIC_SetPriority( USB_IRQn, configUSB_INTERRUPT_PRIORITY );
\r
440 NVIC_EnableIRQ( USB_IRQn );
\r
444 DBG("Connecting to USB bus\n");
\r
445 USBHwConnect(TRUE);
\r
447 // echo any character received (do USB stuff in interrupt)
\r
450 c = VCOM_getchar();
\r
453 // Echo character back with INCREMENT_ECHO_BY offset, so for example if
\r
454 // INCREMENT_ECHO_BY is 1 and 'A' is received, 'B' will be echoed back.
\r
455 VCOM_putchar(c + INCREMENT_ECHO_BY );
\r