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
38 /** Installed device interrupt handler */
\r
39 static TFnDevIntHandler *_pfnDevIntHandler = NULL;
\r
40 /** Installed endpoint interrupt handlers */
\r
41 static TFnEPIntHandler *_apfnEPIntHandlers[16];
\r
42 /** Installed frame interrupt handlers */
\r
43 static TFnFrameHandler *_pfnFrameHandler = NULL;
\r
45 /** convert from endpoint address to endpoint index */
\r
46 #define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7))
\r
47 /** convert from endpoint index to endpoint address */
\r
48 #define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF))
\r
53 Local function to wait for a device interrupt (and clear it)
\r
55 @param [in] dwIntr Bitmask of interrupts to wait for
\r
57 static void Wait4DevInt(unsigned long dwIntr)
\r
59 // wait for specific interrupt
\r
60 while ((USB->USBDevIntSt & dwIntr) != dwIntr);
\r
61 // clear the interrupt bits
\r
62 USB->USBDevIntClr = dwIntr;
\r
67 Local function to send a command to the USB protocol engine
\r
69 @param [in] bCmd Command to send
\r
71 static void USBHwCmd(unsigned char bCmd)
\r
73 // clear CDFULL/CCEMTY
\r
74 USB->USBDevIntClr = CDFULL | CCEMTY;
\r
75 // write command code
\r
76 USB->USBCmdCode = 0x00000500 | (bCmd << 16);
\r
77 Wait4DevInt(CCEMTY);
\r
82 Local function to send a command + data to the USB protocol engine
\r
84 @param [in] bCmd Command to send
\r
85 @param [in] bData Data to send
\r
87 static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)
\r
89 // write command code
\r
92 // write command data
\r
93 USB->USBCmdCode = 0x00000100 | (bData << 16);
\r
94 Wait4DevInt(CCEMTY);
\r
99 Local function to send a command to the USB protocol engine and read data
\r
101 @param [in] bCmd Command to send
\r
105 static unsigned char USBHwCmdRead(unsigned char bCmd)
\r
107 // write command code
\r
111 USB->USBCmdCode = 0x00000200 | (bCmd << 16);
\r
112 Wait4DevInt(CDFULL);
\r
113 return USB->USBCmdData;
\r
118 'Realizes' an endpoint, meaning that buffer space is reserved for
\r
119 it. An endpoint needs to be realised before it can be used.
\r
121 From experiments, it appears that a USB reset causes USBReEP to
\r
122 re-initialise to 3 (= just the control endpoints).
\r
123 However, a USB bus reset does not disturb the USBMaxPSize settings.
\r
125 @param [in] idx Endpoint index
\r
126 @param [in] wMaxPSize Maximum packet size for this endpoint
\r
128 static void USBHwEPRealize(int idx, unsigned short wMaxPSize)
\r
130 USB->USBReEP |= (1 << idx);
\r
131 USB->USBEpInd = idx;
\r
132 USB->USBMaxPSize = wMaxPSize;
\r
133 Wait4DevInt(EP_RLZED);
\r
138 Enables or disables an endpoint
\r
140 @param [in] idx Endpoint index
\r
141 @param [in] fEnable TRUE to enable, FALSE to disable
\r
143 static void USBHwEPEnable(int idx, BOOL fEnable)
\r
145 USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);
\r
150 Configures an endpoint and enables it
\r
152 @param [in] bEP Endpoint number
\r
153 @param [in] wMaxPacketSize Maximum packet size for this EP
\r
155 void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)
\r
162 USBHwEPRealize(idx, wMaxPacketSize);
\r
165 USBHwEPEnable(idx, TRUE);
\r
170 Registers an endpoint event callback
\r
172 @param [in] bEP Endpoint number
\r
173 @param [in] pfnHandler Callback function
\r
175 void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)
\r
183 /* add handler to list of EP handlers */
\r
184 _apfnEPIntHandlers[idx / 2] = pfnHandler;
\r
186 /* enable EP interrupt */
\r
187 USB->USBEpIntEn |= (1 << idx);
\r
188 USB->USBDevIntEn |= EP_SLOW;
\r
190 DBG("Registered handler for EP 0x%x\n", bEP);
\r
195 Registers an device status callback
\r
197 @param [in] pfnHandler Callback function
\r
199 void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)
\r
201 _pfnDevIntHandler = pfnHandler;
\r
203 // enable device interrupt
\r
204 USB->USBDevIntEn |= DEV_STAT;
\r
206 DBG("Registered handler for device status\n");
\r
211 Registers the frame callback
\r
213 @param [in] pfnHandler Callback function
\r
215 void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)
\r
217 _pfnFrameHandler = pfnHandler;
\r
219 // enable device interrupt
\r
220 USB->USBDevIntEn |= FRAME;
\r
222 DBG("Registered handler for frame\n");
\r
227 Sets the USB address.
\r
229 @param [in] bAddr Device address to set
\r
231 void USBHwSetAddress(unsigned char bAddr)
\r
233 USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);
\r
238 Connects or disconnects from the USB bus
\r
240 @param [in] fConnect If TRUE, connect, otherwise disconnect
\r
242 void USBHwConnect(BOOL fConnect)
\r
244 USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);
\r
249 Enables interrupt on NAK condition
\r
251 For IN endpoints a NAK is generated when the host wants to read data
\r
252 from the device, but none is available in the endpoint buffer.
\r
253 For OUT endpoints a NAK is generated when the host wants to write data
\r
254 to the device, but the endpoint buffer is still full.
\r
256 The endpoint interrupt handlers can distinguish regular (ACK) interrupts
\r
257 from NAK interrupt by checking the bits in their bEPStatus argument.
\r
259 @param [in] bIntBits Bitmap indicating which NAK interrupts to enable
\r
261 void USBHwNakIntEnable(unsigned char bIntBits)
\r
263 USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);
\r
268 Gets the status from a specific endpoint.
\r
270 @param [in] bEP Endpoint number
\r
271 @return Endpoint status byte (containing EP_STATUS_xxx bits)
\r
273 unsigned char USBHwEPGetStatus(unsigned char bEP)
\r
275 int idx = EP2IDX(bEP);
\r
277 return USBHwCmdRead(CMD_EP_SELECT | idx);
\r
282 Sets the stalled property of an endpoint
\r
284 @param [in] bEP Endpoint number
\r
285 @param [in] fStall TRUE to stall, FALSE to unstall
\r
287 void USBHwEPStall(unsigned char bEP, BOOL fStall)
\r
289 int idx = EP2IDX(bEP);
\r
291 USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);
\r
296 Writes data to an endpoint buffer
\r
298 @param [in] bEP Endpoint number
\r
299 @param [in] pbBuf Endpoint data
\r
300 @param [in] iLen Number of bytes to write
\r
302 @return TRUE if the data was successfully written or <0 in case of error.
\r
304 int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)
\r
310 // set write enable for specific endpoint
\r
311 USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);
\r
313 // set packet length
\r
314 USB->USBTxPLen = iLen;
\r
317 while (USB->USBCtrl & WR_EN) {
\r
318 USB->USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0];
\r
322 // select endpoint and validate buffer
\r
323 USBHwCmd(CMD_EP_SELECT | idx);
\r
324 USBHwCmd(CMD_EP_VALIDATE_BUFFER);
\r
331 Reads data from an endpoint buffer
\r
333 @param [in] bEP Endpoint number
\r
334 @param [in] pbBuf Endpoint data
\r
335 @param [in] iMaxLen Maximum number of bytes to read
\r
337 @return the number of bytes available in the EP (possibly more than iMaxLen),
\r
338 or <0 in case of error.
\r
340 int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)
\r
342 unsigned int i, idx;
\r
343 unsigned long dwData, dwLen;
\r
347 // set read enable bit for specific endpoint
\r
348 USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);
\r
350 // wait for PKT_RDY
\r
352 dwLen = USB->USBRxPLen;
\r
353 } while ((dwLen & PKT_RDY) == 0);
\r
356 if ((dwLen & DV) == 0) {
\r
361 dwLen &= PKT_LNGTH_MASK;
\r
365 for (i = 0; i < dwLen; i++) {
\r
366 if ((i % 4) == 0) {
\r
367 dwData = USB->USBRxData;
\r
369 if ((pbBuf != NULL) && ((int)i < iMaxLen)) {
\r
370 pbBuf[i] = dwData & 0xFF;
\r
375 // make sure RD_EN is clear
\r
378 // select endpoint and clear buffer
\r
379 USBHwCmd(CMD_EP_SELECT | idx);
\r
380 USBHwCmd(CMD_EP_CLEAR_BUFFER);
\r
387 Sets the 'configured' state.
\r
389 All registered endpoints are 'realised' and enabled, and the
\r
390 'configured' bit is set in the device status register.
\r
392 @param [in] fConfigured If TRUE, configure device, else unconfigure
\r
394 void USBHwConfigDevice(BOOL fConfigured)
\r
396 // set configured bit
\r
397 USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);
\r
402 USB interrupt handler
\r
404 @todo Get all 11 bits of frame number instead of just 8
\r
406 Endpoint interrupts are mapped to the slow interrupt
\r
408 void USBHwISR(void)
\r
410 unsigned long dwStatus;
\r
411 unsigned long dwIntBit;
\r
412 unsigned char bEPStat, bDevStat, bStat;
\r
414 unsigned short wFrame;
\r
416 // handle device interrupts
\r
417 dwStatus = USB->USBDevIntSt;
\r
420 if (dwStatus & FRAME) {
\r
422 USB->USBDevIntClr = FRAME;
\r
424 if (_pfnFrameHandler != NULL) {
\r
425 wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
\r
426 _pfnFrameHandler(wFrame);
\r
430 // device status interrupt
\r
431 if (dwStatus & DEV_STAT) {
\r
432 /* Clear DEV_STAT interrupt before reading DEV_STAT register.
\r
433 This prevents corrupted device status reads, see
\r
434 LPC2148 User manual revision 2, 25 july 2006.
\r
436 USB->USBDevIntClr = DEV_STAT;
\r
437 bDevStat = USBHwCmdRead(CMD_DEV_STATUS);
\r
438 if (bDevStat & (CON_CH | SUS_CH | RST)) {
\r
439 // convert device status into something HW independent
\r
440 bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) |
\r
441 ((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) |
\r
442 ((bDevStat & RST) ? DEV_STATUS_RESET : 0);
\r
444 if (_pfnDevIntHandler != NULL) {
\r
445 _pfnDevIntHandler(bStat);
\r
450 // endpoint interrupt
\r
451 if (dwStatus & EP_SLOW) {
\r
453 USB->USBDevIntClr = EP_SLOW;
\r
454 // check all endpoints
\r
455 for (i = 0; i < 32; i++) {
\r
456 dwIntBit = (1 << i);
\r
457 if (USB->USBEpIntSt & dwIntBit) {
\r
458 // clear int (and retrieve status)
\r
459 USB->USBEpIntClr = dwIntBit;
\r
460 Wait4DevInt(CDFULL);
\r
461 bEPStat = USB->USBCmdData;
\r
462 // convert EP pipe stat into something HW independent
\r
463 bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) |
\r
464 ((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) |
\r
465 ((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) |
\r
466 ((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) |
\r
467 ((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0);
\r
469 if (_apfnEPIntHandlers[i / 2] != NULL) {
\r
470 _apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);
\r
480 Initialises the USB hardware
\r
483 @return TRUE if the hardware was successfully initialised
\r
485 BOOL USBHwInit(void)
\r
487 // P2.9 -> USB_CONNECT
\r
488 PINCON->PINSEL4 &= ~0x000C0000;
\r
489 PINCON->PINSEL4 |= 0x00040000;
\r
491 // P1.18 -> USB_UP_LED
\r
493 PINCON->PINSEL3 &= ~0x30000030;
\r
494 PINCON->PINSEL3 |= 0x20000010;
\r
498 PINCON->PINSEL1 &= ~0x3C000000;
\r
499 PINCON->PINSEL1 |= 0x14000000;
\r
502 SC->PCONP |= (1 << 31);
\r
504 USB->OTGClkCtrl = 0x12; /* Dev clock, AHB clock enable */
\r
505 while ((USB->OTGClkSt & 0x12) != 0x12);
\r
507 // disable/clear all interrupts for now
\r
508 USB->USBDevIntEn = 0;
\r
509 USB->USBDevIntClr = 0xFFFFFFFF;
\r
510 USB->USBDevIntPri = 0;
\r
512 USB->USBEpIntEn = 0;
\r
513 USB->USBEpIntClr = 0xFFFFFFFF;
\r
514 USB->USBEpIntPri = 0;
\r
516 // by default, only ACKs generate interrupts
\r
517 USBHwNakIntEnable(0);
\r