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
34 #include "usbdebug.h"
\r
35 #include "usbhw_lpc.h"
\r
39 // comment out the following line if you don't want to use debug LEDs
\r
43 /** Installed device interrupt handler */
\r
44 static TFnDevIntHandler *_pfnDevIntHandler = NULL;
\r
45 /** Installed endpoint interrupt handlers */
\r
46 static TFnEPIntHandler *_apfnEPIntHandlers[16];
\r
47 /** Installed frame interrupt handlers */
\r
48 static TFnFrameHandler *_pfnFrameHandler = NULL;
\r
50 /** convert from endpoint address to endpoint index */
\r
51 #define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7))
\r
52 /** convert from endpoint index to endpoint address */
\r
53 #define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF))
\r
58 Local function to wait for a device interrupt (and clear it)
\r
60 @param [in] dwIntr Bitmask of interrupts to wait for
\r
62 static void Wait4DevInt(unsigned long dwIntr)
\r
64 // wait for specific interrupt
\r
65 while ((LPC_USB->USBDevIntSt & dwIntr) != dwIntr);
\r
66 // clear the interrupt bits
\r
67 LPC_USB->USBDevIntClr = dwIntr;
\r
72 Local function to send a command to the USB protocol engine
\r
74 @param [in] bCmd Command to send
\r
76 static void USBHwCmd(unsigned char bCmd)
\r
78 // clear CDFULL/CCEMTY
\r
79 LPC_USB->USBDevIntClr = CDFULL | CCEMTY;
\r
80 // write command code
\r
81 LPC_USB->USBCmdCode = 0x00000500 | (bCmd << 16);
\r
82 Wait4DevInt(CCEMTY);
\r
87 Local function to send a command + data to the USB protocol engine
\r
89 @param [in] bCmd Command to send
\r
90 @param [in] bData Data to send
\r
92 static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)
\r
94 // write command code
\r
97 // write command data
\r
98 LPC_USB->USBCmdCode = 0x00000100 | (bData << 16);
\r
99 Wait4DevInt(CCEMTY);
\r
104 Local function to send a command to the USB protocol engine and read data
\r
106 @param [in] bCmd Command to send
\r
110 static unsigned char USBHwCmdRead(unsigned char bCmd)
\r
112 // write command code
\r
116 LPC_USB->USBCmdCode = 0x00000200 | (bCmd << 16);
\r
117 Wait4DevInt(CDFULL);
\r
118 return LPC_USB->USBCmdData;
\r
123 'Realizes' an endpoint, meaning that buffer space is reserved for
\r
124 it. An endpoint needs to be realised before it can be used.
\r
126 From experiments, it appears that a USB reset causes USBReEP to
\r
127 re-initialise to 3 (= just the control endpoints).
\r
128 However, a USB bus reset does not disturb the USBMaxPSize settings.
\r
130 @param [in] idx Endpoint index
\r
131 @param [in] wMaxPSize Maximum packet size for this endpoint
\r
133 static void USBHwEPRealize(int idx, unsigned short wMaxPSize)
\r
135 LPC_USB->USBReEP |= (1 << idx);
\r
136 LPC_USB->USBEpInd = idx;
\r
137 LPC_USB->USBMaxPSize = wMaxPSize;
\r
138 Wait4DevInt(EP_RLZED);
\r
143 Enables or disables an endpoint
\r
145 @param [in] idx Endpoint index
\r
146 @param [in] fEnable TRUE to enable, FALSE to disable
\r
148 static void USBHwEPEnable(int idx, BOOL fEnable)
\r
150 USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);
\r
155 Configures an endpoint and enables it
\r
157 @param [in] bEP Endpoint number
\r
158 @param [in] wMaxPacketSize Maximum packet size for this EP
\r
160 void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)
\r
167 USBHwEPRealize(idx, wMaxPacketSize);
\r
170 USBHwEPEnable(idx, TRUE);
\r
175 Registers an endpoint event callback
\r
177 @param [in] bEP Endpoint number
\r
178 @param [in] pfnHandler Callback function
\r
180 void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)
\r
188 /* add handler to list of EP handlers */
\r
189 _apfnEPIntHandlers[idx / 2] = pfnHandler;
\r
191 /* enable EP interrupt */
\r
192 LPC_USB->USBEpIntEn |= (1 << idx);
\r
193 LPC_USB->USBDevIntEn |= EP_SLOW;
\r
195 DBG("Registered handler for EP 0x%x\n", bEP);
\r
200 Registers an device status callback
\r
202 @param [in] pfnHandler Callback function
\r
204 void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)
\r
206 _pfnDevIntHandler = pfnHandler;
\r
208 // enable device interrupt
\r
209 LPC_USB->USBDevIntEn |= DEV_STAT;
\r
211 DBG("Registered handler for device status\n");
\r
216 Registers the frame callback
\r
218 @param [in] pfnHandler Callback function
\r
220 void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)
\r
222 _pfnFrameHandler = pfnHandler;
\r
224 // enable device interrupt
\r
225 LPC_USB->USBDevIntEn |= FRAME;
\r
227 DBG("Registered handler for frame\n");
\r
232 Sets the USB address.
\r
234 @param [in] bAddr Device address to set
\r
236 void USBHwSetAddress(unsigned char bAddr)
\r
238 USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);
\r
243 Connects or disconnects from the USB bus
\r
245 @param [in] fConnect If TRUE, connect, otherwise disconnect
\r
247 void USBHwConnect(BOOL fConnect)
\r
249 USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);
\r
254 Enables interrupt on NAK condition
\r
256 For IN endpoints a NAK is generated when the host wants to read data
\r
257 from the device, but none is available in the endpoint buffer.
\r
258 For OUT endpoints a NAK is generated when the host wants to write data
\r
259 to the device, but the endpoint buffer is still full.
\r
261 The endpoint interrupt handlers can distinguish regular (ACK) interrupts
\r
262 from NAK interrupt by checking the bits in their bEPStatus argument.
\r
264 @param [in] bIntBits Bitmap indicating which NAK interrupts to enable
\r
266 void USBHwNakIntEnable(unsigned char bIntBits)
\r
268 USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);
\r
273 Gets the status from a specific endpoint.
\r
275 @param [in] bEP Endpoint number
\r
276 @return Endpoint status byte (containing EP_STATUS_xxx bits)
\r
278 unsigned char USBHwEPGetStatus(unsigned char bEP)
\r
280 int idx = EP2IDX(bEP);
\r
282 return USBHwCmdRead(CMD_EP_SELECT | idx);
\r
287 Sets the stalled property of an endpoint
\r
289 @param [in] bEP Endpoint number
\r
290 @param [in] fStall TRUE to stall, FALSE to unstall
\r
292 void USBHwEPStall(unsigned char bEP, BOOL fStall)
\r
294 int idx = EP2IDX(bEP);
\r
296 USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);
\r
301 Writes data to an endpoint buffer
\r
303 @param [in] bEP Endpoint number
\r
304 @param [in] pbBuf Endpoint data
\r
305 @param [in] iLen Number of bytes to write
\r
307 @return TRUE if the data was successfully written or <0 in case of error.
\r
309 int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)
\r
315 // set write enable for specific endpoint
\r
316 LPC_USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);
\r
318 // set packet length
\r
319 LPC_USB->USBTxPLen = iLen;
\r
322 while (LPC_USB->USBCtrl & WR_EN) {
\r
323 LPC_USB->USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0];
\r
327 // select endpoint and validate buffer
\r
328 USBHwCmd(CMD_EP_SELECT | idx);
\r
329 USBHwCmd(CMD_EP_VALIDATE_BUFFER);
\r
336 Reads data from an endpoint buffer
\r
338 @param [in] bEP Endpoint number
\r
339 @param [in] pbBuf Endpoint data
\r
340 @param [in] iMaxLen Maximum number of bytes to read
\r
342 @return the number of bytes available in the EP (possibly more than iMaxLen),
\r
343 or <0 in case of error.
\r
345 int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)
\r
348 unsigned long dwData, dwLen;
\r
352 // set read enable bit for specific endpoint
\r
353 LPC_USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);
\r
355 // wait for PKT_RDY
\r
357 dwLen = LPC_USB->USBRxPLen;
\r
358 } while ((dwLen & PKT_RDY) == 0);
\r
361 if ((dwLen & DV) == 0) {
\r
366 dwLen &= PKT_LNGTH_MASK;
\r
370 for (i = 0; i < dwLen; i++) {
\r
371 if ((i % 4) == 0) {
\r
372 dwData = LPC_USB->USBRxData;
\r
374 if ((pbBuf != NULL) && (i < iMaxLen)) {
\r
375 pbBuf[i] = dwData & 0xFF;
\r
380 // make sure RD_EN is clear
\r
381 LPC_USB->USBCtrl = 0;
\r
383 // select endpoint and clear buffer
\r
384 USBHwCmd(CMD_EP_SELECT | idx);
\r
385 USBHwCmd(CMD_EP_CLEAR_BUFFER);
\r
392 Sets the 'configured' state.
\r
394 All registered endpoints are 'realised' and enabled, and the
\r
395 'configured' bit is set in the device status register.
\r
397 @param [in] fConfigured If TRUE, configure device, else unconfigure
\r
399 void USBHwConfigDevice(BOOL fConfigured)
\r
401 // set configured bit
\r
402 USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);
\r
407 USB interrupt handler
\r
409 @todo Get all 11 bits of frame number instead of just 8
\r
411 Endpoint interrupts are mapped to the slow interrupt
\r
413 void USBHwISR(void)
\r
415 unsigned long dwStatus;
\r
416 unsigned long dwIntBit;
\r
417 unsigned char bEPStat, bDevStat, bStat;
\r
419 unsigned short wFrame;
\r
421 // handle device interrupts
\r
422 dwStatus = LPC_USB->USBDevIntSt;
\r
425 if (dwStatus & FRAME) {
\r
427 LPC_USB->USBDevIntClr = FRAME;
\r
429 if (_pfnFrameHandler != NULL) {
\r
430 wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
\r
431 _pfnFrameHandler(wFrame);
\r
435 // device status interrupt
\r
436 if (dwStatus & DEV_STAT) {
\r
437 /* Clear DEV_STAT interrupt before reading DEV_STAT register.
\r
438 This prevents corrupted device status reads, see
\r
439 LPC2148 User manual revision 2, 25 july 2006.
\r
441 LPC_USB->USBDevIntClr = DEV_STAT;
\r
442 bDevStat = USBHwCmdRead(CMD_DEV_STATUS);
\r
443 if (bDevStat & (CON_CH | SUS_CH | RST)) {
\r
444 // convert device status into something HW independent
\r
445 bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) |
\r
446 ((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) |
\r
447 ((bDevStat & RST) ? DEV_STATUS_RESET : 0);
\r
449 if (_pfnDevIntHandler != NULL) {
\r
450 _pfnDevIntHandler(bStat);
\r
455 // endpoint interrupt
\r
456 if (dwStatus & EP_SLOW) {
\r
458 LPC_USB->USBDevIntClr = EP_SLOW;
\r
459 // check all endpoints
\r
460 for (i = 0; i < 32; i++) {
\r
461 dwIntBit = (1 << i);
\r
462 if (LPC_USB->USBEpIntSt & dwIntBit) {
\r
463 // clear int (and retrieve status)
\r
464 LPC_USB->USBEpIntClr = dwIntBit;
\r
465 Wait4DevInt(CDFULL);
\r
466 bEPStat = LPC_USB->USBCmdData;
\r
467 // convert EP pipe stat into something HW independent
\r
468 bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) |
\r
469 ((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) |
\r
470 ((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) |
\r
471 ((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) |
\r
472 ((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0);
\r
474 if (_apfnEPIntHandlers[i / 2] != NULL) {
\r
475 _apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);
\r
485 Initialises the USB hardware
\r
488 @return TRUE if the hardware was successfully initialised
\r
490 BOOL USBHwInit(void)
\r
492 // P2.9 -> USB_CONNECT
\r
493 LPC_PINCON->PINSEL4 &= ~0x000C0000;
\r
494 LPC_PINCON->PINSEL4 |= 0x00040000;
\r
496 // P1.18 -> USB_UP_LED
\r
498 LPC_PINCON->PINSEL3 &= ~0x30000030;
\r
499 LPC_PINCON->PINSEL3 |= 0x20000010;
\r
503 LPC_PINCON->PINSEL1 &= ~0x3C000000;
\r
504 LPC_PINCON->PINSEL1 |= 0x14000000;
\r
507 LPC_SC->PCONP |= (1 << 31);
\r
509 LPC_USB->OTGClkCtrl = 0x12; /* Dev clock, AHB clock enable */
\r
510 while ((LPC_USB->OTGClkSt & 0x12) != 0x12);
\r
512 // disable/clear all interrupts for now
\r
513 LPC_USB->USBDevIntEn = 0;
\r
514 LPC_USB->USBDevIntClr = 0xFFFFFFFF;
\r
515 LPC_USB->USBDevIntPri = 0;
\r
517 LPC_USB->USBEpIntEn = 0;
\r
518 LPC_USB->USBEpIntClr = 0xFFFFFFFF;
\r
519 LPC_USB->USBEpIntPri = 0;
\r
521 // by default, only ACKs generate interrupts
\r
522 USBHwNakIntEnable(0);
\r