]> git.sur5r.net Git - freertos/blob - Demo/CORTEX_LPC1768_GCC_RedSuite/src/LPCUSB/usbhw_lpc.c
Update the Red Suite LPC1768 project and source to use the latest version of the...
[freertos] / Demo / CORTEX_LPC1768_GCC_RedSuite / src / LPCUSB / usbhw_lpc.c
1 /*\r
2         LPCUSB, an USB device driver for LPC microcontrollers   \r
3         Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)\r
4 \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
7 \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
15 \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
26 */\r
27 \r
28 \r
29 /** @file\r
30         USB hardware layer\r
31  */\r
32 \r
33 \r
34 #include "usbdebug.h"\r
35 #include "usbhw_lpc.h"\r
36 #include "usbapi.h"\r
37 \r
38 #ifdef DEBUG\r
39 // comment out the following line if you don't want to use debug LEDs\r
40 //#define DEBUG_LED\r
41 #endif\r
42 \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
49 \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
54 \r
55 \r
56 \r
57 /**\r
58         Local function to wait for a device interrupt (and clear it)\r
59                 \r
60         @param [in]     dwIntr          Bitmask of interrupts to wait for       \r
61  */\r
62 static void Wait4DevInt(unsigned long dwIntr)\r
63 {\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
68 }\r
69 \r
70 \r
71 /**\r
72         Local function to send a command to the USB protocol engine\r
73                 \r
74         @param [in]     bCmd            Command to send\r
75  */\r
76 static void USBHwCmd(unsigned char bCmd)\r
77 {\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
83 }\r
84 \r
85 \r
86 /**\r
87         Local function to send a command + data to the USB protocol engine\r
88                 \r
89         @param [in]     bCmd            Command to send\r
90         @param [in]     bData           Data to send\r
91  */\r
92 static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)\r
93 {\r
94         // write command code\r
95         USBHwCmd(bCmd);\r
96 \r
97         // write command data\r
98         LPC_USB->USBCmdCode = 0x00000100 | (bData << 16);\r
99         Wait4DevInt(CCEMTY);\r
100 }\r
101 \r
102 \r
103 /**\r
104         Local function to send a command to the USB protocol engine and read data\r
105                 \r
106         @param [in]     bCmd            Command to send\r
107 \r
108         @return the data\r
109  */\r
110 static unsigned char USBHwCmdRead(unsigned char bCmd)\r
111 {\r
112         // write command code\r
113         USBHwCmd(bCmd);\r
114         \r
115         // get data\r
116         LPC_USB->USBCmdCode = 0x00000200 | (bCmd << 16);\r
117         Wait4DevInt(CDFULL);\r
118         return LPC_USB->USBCmdData;\r
119 }\r
120 \r
121 \r
122 /**\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
125                 \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
129                 \r
130         @param [in]     idx                     Endpoint index\r
131         @param [in] wMaxPSize   Maximum packet size for this endpoint\r
132  */\r
133 static void USBHwEPRealize(int idx, unsigned short wMaxPSize)\r
134 {\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
139 }\r
140 \r
141 \r
142 /**\r
143         Enables or disables an endpoint\r
144                 \r
145         @param [in]     idx             Endpoint index\r
146         @param [in]     fEnable TRUE to enable, FALSE to disable\r
147  */\r
148 static void USBHwEPEnable(int idx, BOOL fEnable)\r
149 {\r
150         USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);\r
151 }\r
152 \r
153 \r
154 /**\r
155         Configures an endpoint and enables it\r
156                 \r
157         @param [in]     bEP                             Endpoint number\r
158         @param [in]     wMaxPacketSize  Maximum packet size for this EP\r
159  */\r
160 void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)\r
161 {\r
162         int idx;\r
163         \r
164         idx = EP2IDX(bEP);\r
165         \r
166         // realise EP\r
167         USBHwEPRealize(idx, wMaxPacketSize);\r
168 \r
169         // enable EP\r
170         USBHwEPEnable(idx, TRUE);\r
171 }\r
172 \r
173 \r
174 /**\r
175         Registers an endpoint event callback\r
176                 \r
177         @param [in]     bEP                             Endpoint number\r
178         @param [in]     pfnHandler              Callback function\r
179  */\r
180 void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)\r
181 {\r
182         int idx;\r
183         \r
184         idx = EP2IDX(bEP);\r
185 \r
186         ASSERT(idx<32);\r
187 \r
188         /* add handler to list of EP handlers */\r
189         _apfnEPIntHandlers[idx / 2] = pfnHandler;\r
190         \r
191         /* enable EP interrupt */\r
192         LPC_USB->USBEpIntEn |= (1 << idx);\r
193         LPC_USB->USBDevIntEn |= EP_SLOW;\r
194         \r
195         DBG("Registered handler for EP 0x%x\n", bEP);\r
196 }\r
197 \r
198 \r
199 /**\r
200         Registers an device status callback\r
201                 \r
202         @param [in]     pfnHandler      Callback function\r
203  */\r
204 void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)\r
205 {\r
206         _pfnDevIntHandler = pfnHandler;\r
207         \r
208         // enable device interrupt\r
209         LPC_USB->USBDevIntEn |= DEV_STAT;\r
210 \r
211         DBG("Registered handler for device status\n");\r
212 }\r
213 \r
214 \r
215 /**\r
216         Registers the frame callback\r
217                 \r
218         @param [in]     pfnHandler      Callback function\r
219  */\r
220 void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)\r
221 {\r
222         _pfnFrameHandler = pfnHandler;\r
223         \r
224         // enable device interrupt\r
225         LPC_USB->USBDevIntEn |= FRAME;\r
226 \r
227         DBG("Registered handler for frame\n");\r
228 }\r
229 \r
230 \r
231 /**\r
232         Sets the USB address.\r
233                 \r
234         @param [in]     bAddr           Device address to set\r
235  */\r
236 void USBHwSetAddress(unsigned char bAddr)\r
237 {\r
238         USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);\r
239 }\r
240 \r
241 \r
242 /**\r
243         Connects or disconnects from the USB bus\r
244                 \r
245         @param [in]     fConnect        If TRUE, connect, otherwise disconnect\r
246  */\r
247 void USBHwConnect(BOOL fConnect)\r
248 {\r
249         USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);\r
250 }\r
251 \r
252 \r
253 /**\r
254         Enables interrupt on NAK condition\r
255                 \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
260         \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
263         \r
264         @param [in]     bIntBits        Bitmap indicating which NAK interrupts to enable\r
265  */\r
266 void USBHwNakIntEnable(unsigned char bIntBits)\r
267 {\r
268         USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);\r
269 }\r
270 \r
271 \r
272 /**\r
273         Gets the status from a specific endpoint.\r
274                 \r
275         @param [in]     bEP             Endpoint number\r
276         @return Endpoint status byte (containing EP_STATUS_xxx bits)\r
277  */\r
278 unsigned char   USBHwEPGetStatus(unsigned char bEP)\r
279 {\r
280         int idx = EP2IDX(bEP);\r
281 \r
282         return USBHwCmdRead(CMD_EP_SELECT | idx);\r
283 }\r
284 \r
285 \r
286 /**\r
287         Sets the stalled property of an endpoint\r
288                 \r
289         @param [in]     bEP             Endpoint number\r
290         @param [in]     fStall  TRUE to stall, FALSE to unstall\r
291  */\r
292 void USBHwEPStall(unsigned char bEP, BOOL fStall)\r
293 {\r
294         int idx = EP2IDX(bEP);\r
295 \r
296         USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);\r
297 }\r
298 \r
299 \r
300 /**\r
301         Writes data to an endpoint buffer\r
302                 \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
306                         \r
307         @return TRUE if the data was successfully written or <0 in case of error.\r
308 */\r
309 int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)\r
310 {\r
311         int idx;\r
312         \r
313         idx = EP2IDX(bEP);\r
314         \r
315         // set write enable for specific endpoint\r
316         LPC_USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);\r
317         \r
318         // set packet length\r
319         LPC_USB->USBTxPLen = iLen;\r
320         \r
321         // write data\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
324                 pbBuf += 4;\r
325         }\r
326 \r
327         // select endpoint and validate buffer\r
328         USBHwCmd(CMD_EP_SELECT | idx);\r
329         USBHwCmd(CMD_EP_VALIDATE_BUFFER);\r
330         \r
331         return iLen;\r
332 }\r
333 \r
334 \r
335 /**\r
336         Reads data from an endpoint buffer\r
337                 \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
341                         \r
342         @return the number of bytes available in the EP (possibly more than iMaxLen),\r
343         or <0 in case of error.\r
344  */\r
345 int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)\r
346 {\r
347         int i, idx;\r
348         unsigned long   dwData, dwLen;\r
349         \r
350         idx = EP2IDX(bEP);\r
351         \r
352         // set read enable bit for specific endpoint\r
353         LPC_USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);\r
354         \r
355         // wait for PKT_RDY\r
356         do {\r
357                 dwLen = LPC_USB->USBRxPLen;\r
358         } while ((dwLen & PKT_RDY) == 0);\r
359         \r
360         // packet valid?\r
361         if ((dwLen & DV) == 0) {\r
362                 return -1;\r
363         }\r
364         \r
365         // get length\r
366         dwLen &= PKT_LNGTH_MASK;\r
367         \r
368         // get data\r
369         dwData = 0;\r
370         for (i = 0; i < dwLen; i++) {\r
371                 if ((i % 4) == 0) {\r
372                         dwData = LPC_USB->USBRxData;\r
373                 }\r
374                 if ((pbBuf != NULL) && (i < iMaxLen)) {\r
375                         pbBuf[i] = dwData & 0xFF;\r
376                 }\r
377                 dwData >>= 8;\r
378         }\r
379 \r
380         // make sure RD_EN is clear\r
381         LPC_USB->USBCtrl = 0;\r
382 \r
383         // select endpoint and clear buffer\r
384         USBHwCmd(CMD_EP_SELECT | idx);\r
385         USBHwCmd(CMD_EP_CLEAR_BUFFER);\r
386         \r
387         return dwLen;\r
388 }\r
389 \r
390 \r
391 /**\r
392         Sets the 'configured' state.\r
393                 \r
394         All registered endpoints are 'realised' and enabled, and the\r
395         'configured' bit is set in the device status register.\r
396                 \r
397         @param [in]     fConfigured     If TRUE, configure device, else unconfigure\r
398  */\r
399 void USBHwConfigDevice(BOOL fConfigured)\r
400 {\r
401         // set configured bit\r
402         USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);\r
403 }\r
404 \r
405 \r
406 /**\r
407         USB interrupt handler\r
408                 \r
409         @todo Get all 11 bits of frame number instead of just 8\r
410 \r
411         Endpoint interrupts are mapped to the slow interrupt\r
412  */\r
413 void USBHwISR(void)\r
414 {\r
415         unsigned long   dwStatus;\r
416         unsigned long dwIntBit;\r
417         unsigned char   bEPStat, bDevStat, bStat;\r
418         int i;\r
419         unsigned short  wFrame;\r
420 \r
421         // handle device interrupts\r
422         dwStatus = LPC_USB->USBDevIntSt;\r
423         \r
424         // frame interrupt\r
425         if (dwStatus & FRAME) {\r
426                 // clear int\r
427                 LPC_USB->USBDevIntClr = FRAME;\r
428                 // call handler\r
429                 if (_pfnFrameHandler != NULL) {\r
430                         wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);\r
431                         _pfnFrameHandler(wFrame);\r
432                 }\r
433         }\r
434         \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
440                 */\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
448                         // call handler\r
449                         if (_pfnDevIntHandler != NULL) {\r
450                                 _pfnDevIntHandler(bStat);\r
451                         }\r
452                 }\r
453         }\r
454         \r
455         // endpoint interrupt\r
456         if (dwStatus & EP_SLOW) {\r
457                 // clear 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
473                                 // call handler\r
474                                 if (_apfnEPIntHandlers[i / 2] != NULL) {\r
475                                         _apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);\r
476                                 }\r
477                         }\r
478                 }\r
479         }\r
480 }\r
481 \r
482 \r
483 \r
484 /**\r
485         Initialises the USB hardware\r
486                 \r
487                 \r
488         @return TRUE if the hardware was successfully initialised\r
489  */\r
490 BOOL USBHwInit(void)\r
491 {\r
492         // P2.9 -> USB_CONNECT\r
493         LPC_PINCON->PINSEL4 &= ~0x000C0000;\r
494         LPC_PINCON->PINSEL4 |= 0x00040000;\r
495 \r
496         // P1.18 -> USB_UP_LED\r
497         // P1.30 -> VBUS\r
498         LPC_PINCON->PINSEL3 &= ~0x30000030;\r
499         LPC_PINCON->PINSEL3 |= 0x20000010;\r
500 \r
501         // P0.29 -> USB_D+\r
502         // P0.30 -> USB_D-\r
503         LPC_PINCON->PINSEL1 &= ~0x3C000000;\r
504         LPC_PINCON->PINSEL1 |= 0x14000000;\r
505 \r
506         // enable PUSB\r
507         LPC_SC->PCONP |= (1 << 31);\r
508 \r
509         LPC_USB->OTGClkCtrl = 0x12;                       /* Dev clock, AHB clock enable  */\r
510         while ((LPC_USB->OTGClkSt & 0x12) != 0x12);\r
511         \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
516 \r
517         LPC_USB->USBEpIntEn = 0;\r
518         LPC_USB->USBEpIntClr = 0xFFFFFFFF;\r
519         LPC_USB->USBEpIntPri = 0;\r
520 \r
521         // by default, only ACKs generate interrupts\r
522         USBHwNakIntEnable(0);\r
523 \r
524         return TRUE;\r
525 }\r
526 \r