Add simple USB CDC task.

This commit is contained in:
Richard Barry 2009-08-07 15:30:18 +00:00
parent a83d9d93e8
commit df338c84c3
15 changed files with 2756 additions and 49 deletions

View File

@ -64,7 +64,7 @@
#define configUSE_IDLE_HOOK 0
#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 )
#define configUSE_TICK_HOOK 1
#define configCPU_CLOCK_HZ ( ( unsigned portLONG ) 64000000 )
#define configCPU_CLOCK_HZ ( ( unsigned portLONG ) 99000000 )
#define configTICK_RATE_HZ ( ( portTickType ) 1000 )
#define configMINIMAL_STACK_SIZE ( ( unsigned portSHORT ) 80 )
#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 19 * 1024 ) )
@ -133,9 +133,12 @@ to exclude the API function. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY ( 5 << (8 - configPRIO_BITS) )
/* Priorities passed to NVIC_SetPriority() do not require shifting as the
function does the shifting itself. */
function does the shifting itself. Note these priorities need to be equal to
or lower than configMAX_SYSCALL_INTERRUPT_PRIORITY - therefore the numeric
value needs to be equal to or greater than 5 (on the Cortex M3 the lower the
numeric value the higher the interrupt priority). */
#define configEMAC_INTERRUPT_PRIORITY 5
#define configUSB_INTERRUPT_PRIORITY 6

View File

@ -0,0 +1,459 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
Minimal implementation of a USB serial port, using the CDC class.
This example application simply echoes everything it receives right back
to the host.
Windows:
Extract the usbser.sys file from .cab file in C:\WINDOWS\Driver Cache\i386
and store it somewhere (C:\temp is a good place) along with the usbser.inf
file. Then plug in the LPC176x and direct windows to the usbser driver.
Windows then creates an extra COMx port that you can open in a terminal
program, like hyperterminal. [Note for FreeRTOS users - the required .inf
file is included in the project directory.]
Linux:
The device should be recognised automatically by the cdc_acm driver,
which creates a /dev/ttyACMx device file that acts just like a regular
serial port.
*/
#include "FreeRTOS.h"
#include "queue.h"
#include <stdio.h>
#include <string.h>
#include "usbapi.h"
#include "usbdebug.h"
#include "usbstruct.h"
#include "LPC17xx.h"
#define usbMAX_SEND_BLOCK ( 20 / portTICK_RATE_MS )
#define usbBUFFER_LEN ( 20 )
#define INCREMENT_ECHO_BY 1
#define BAUD_RATE 115200
#define INT_IN_EP 0x81
#define BULK_OUT_EP 0x05
#define BULK_IN_EP 0x82
#define MAX_PACKET_SIZE 64
#define LE_WORD(x) ((x)&0xFF),((x)>>8)
// CDC definitions
#define CS_INTERFACE 0x24
#define CS_ENDPOINT 0x25
#define SET_LINE_CODING 0x20
#define GET_LINE_CODING 0x21
#define SET_CONTROL_LINE_STATE 0x22
// data structure for GET_LINE_CODING / SET_LINE_CODING class requests
typedef struct {
unsigned long dwDTERate;
unsigned char bCharFormat;
unsigned char bParityType;
unsigned char bDataBits;
} TLineCoding;
static TLineCoding LineCoding = {115200, 0, 0, 8};
static unsigned char abBulkBuf[64];
static unsigned char abClassReqData[8];
static xQueueHandle xRxedChars = NULL, xCharsForTx = NULL;
// forward declaration of interrupt handler
void USBIntHandler(void);
static const unsigned char abDescriptors[] = {
// device descriptor
0x12,
DESC_DEVICE,
LE_WORD(0x0101), // bcdUSB
0x02, // bDeviceClass
0x00, // bDeviceSubClass
0x00, // bDeviceProtocol
MAX_PACKET_SIZE0, // bMaxPacketSize
LE_WORD(0xFFFF), // idVendor
LE_WORD(0x0005), // idProduct
LE_WORD(0x0100), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01, // bNumConfigurations
// configuration descriptor
0x09,
DESC_CONFIGURATION,
LE_WORD(67), // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0xC0, // bmAttributes
0x32, // bMaxPower
// control class interface
0x09,
DESC_INTERFACE,
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndPoints
0x02, // bInterfaceClass
0x02, // bInterfaceSubClass
0x01, // bInterfaceProtocol, linux requires value of 1 for the cdc_acm module
0x00, // iInterface
// header functional descriptor
0x05,
CS_INTERFACE,
0x00,
LE_WORD(0x0110),
// call management functional descriptor
0x05,
CS_INTERFACE,
0x01,
0x01, // bmCapabilities = device handles call management
0x01, // bDataInterface
// ACM functional descriptor
0x04,
CS_INTERFACE,
0x02,
0x02, // bmCapabilities
// union functional descriptor
0x05,
CS_INTERFACE,
0x06,
0x00, // bMasterInterface
0x01, // bSlaveInterface0
// notification EP
0x07,
DESC_ENDPOINT,
INT_IN_EP, // bEndpointAddress
0x03, // bmAttributes = intr
LE_WORD(8), // wMaxPacketSize
0x0A, // bInterval
// data class interface descriptor
0x09,
DESC_INTERFACE,
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndPoints
0x0A, // bInterfaceClass = data
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol
0x00, // iInterface
// data EP OUT
0x07,
DESC_ENDPOINT,
BULK_OUT_EP, // bEndpointAddress
0x02, // bmAttributes = bulk
LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
0x00, // bInterval
// data EP in
0x07,
DESC_ENDPOINT,
BULK_IN_EP, // bEndpointAddress
0x02, // bmAttributes = bulk
LE_WORD(MAX_PACKET_SIZE), // wMaxPacketSize
0x00, // bInterval
// string descriptors
0x04,
DESC_STRING,
LE_WORD(0x0409),
0x0E,
DESC_STRING,
'L', 0, 'P', 0, 'C', 0, 'U', 0, 'S', 0, 'B', 0,
0x14,
DESC_STRING,
'U', 0, 'S', 0, 'B', 0, 'S', 0, 'e', 0, 'r', 0, 'i', 0, 'a', 0, 'l', 0,
0x12,
DESC_STRING,
'D', 0, 'E', 0, 'A', 0, 'D', 0, 'C', 0, '0', 0, 'D', 0, 'E', 0,
// terminating zero
0
};
/**
Local function to handle incoming bulk data
@param [in] bEP
@param [in] bEPStatus
*/
static void BulkOut(unsigned char bEP, unsigned char bEPStatus)
{
int i, iLen;
long lHigherPriorityTaskWoken = pdFALSE;
( void ) bEPStatus;
// get data from USB into intermediate buffer
iLen = USBHwEPRead(bEP, abBulkBuf, sizeof(abBulkBuf));
for (i = 0; i < iLen; i++) {
// put into queue
xQueueSendFromISR( xRxedChars, &( abBulkBuf[ i ] ), &lHigherPriorityTaskWoken );
}
portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}
/**
Local function to handle outgoing bulk data
@param [in] bEP
@param [in] bEPStatus
*/
static void BulkIn(unsigned char bEP, unsigned char bEPStatus)
{
int i, iLen;
long lHigherPriorityTaskWoken = pdFALSE;
( void ) bEPStatus;
if (uxQueueMessagesWaitingFromISR( xCharsForTx ) == 0) {
// no more data, disable further NAK interrupts until next USB frame
USBHwNakIntEnable(0);
return;
}
// get bytes from transmit FIFO into intermediate buffer
for (i = 0; i < MAX_PACKET_SIZE; i++) {
if( xQueueReceiveFromISR( xCharsForTx, ( &abBulkBuf[i] ), &lHigherPriorityTaskWoken ) != pdPASS )
{
break;
}
}
iLen = i;
// send over USB
if (iLen > 0) {
USBHwEPWrite(bEP, abBulkBuf, iLen);
}
portEND_SWITCHING_ISR( lHigherPriorityTaskWoken );
}
/**
Local function to handle the USB-CDC class requests
@param [in] pSetup
@param [out] piLen
@param [out] ppbData
*/
static BOOL HandleClassRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
switch (pSetup->bRequest) {
// set line coding
case SET_LINE_CODING:
DBG("SET_LINE_CODING\n");
memcpy((unsigned char *)&LineCoding, *ppbData, 7);
*piLen = 7;
DBG("dwDTERate=%u, bCharFormat=%u, bParityType=%u, bDataBits=%u\n",
LineCoding.dwDTERate,
LineCoding.bCharFormat,
LineCoding.bParityType,
LineCoding.bDataBits);
break;
// get line coding
case GET_LINE_CODING:
DBG("GET_LINE_CODING\n");
*ppbData = (unsigned char *)&LineCoding;
*piLen = 7;
break;
// set control line state
case SET_CONTROL_LINE_STATE:
// bit0 = DTR, bit = RTS
DBG("SET_CONTROL_LINE_STATE %X\n", pSetup->wValue);
break;
default:
return FALSE;
}
return TRUE;
}
/**
Writes one character to VCOM port
@param [in] c character to write
@returns character written, or EOF if character could not be written
*/
int VCOM_putchar(int c)
{
char cc = ( char ) c;
if( xQueueSend( xCharsForTx, &cc, usbMAX_SEND_BLOCK ) == pdPASS )
{
return c;
}
else
{
return EOF;
}
}
/**
Reads one character from VCOM port
@returns character read, or EOF if character could not be read
*/
int VCOM_getchar(void)
{
unsigned char c;
/* Block the task until a character is available. */
xQueueReceive( xRxedChars, &c, portMAX_DELAY );
return c;
}
/**
Interrupt handler
Simply calls the USB ISR
*/
//void USBIntHandler(void)
void USB_IRQHandler(void)
{
USBHwISR();
}
static void USBFrameHandler(unsigned short wFrame)
{
( void ) wFrame;
if( uxQueueMessagesWaitingFromISR( xCharsForTx ) > 0 )
{
// data available, enable NAK interrupt on bulk in
USBHwNakIntEnable(INACK_BI);
}
}
// CodeRed - added CPUcpsie
unsigned long CPUcpsie(void)
{
unsigned long ulRet;
//
// Read PRIMASK and enable interrupts.
//
__asm(" mrs %0, PRIMASK\n"
" cpsie i\n"
" bx lr\n"
: "=r" (ulRet));
//
// The return is handled in the inline assembly, but the compiler will
// still complain if there is not an explicit return here (despite the fact
// that this does not result in any code being produced because of the
// naked attribute).
//
return(ulRet);
}
void vUSBTask( void *pvParameters )
{
int c;
/* Just to prevent compiler warnings about the unused parameter. */
( void ) pvParameters;
DBG("Initialising USB stack\n");
xRxedChars = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
xCharsForTx = xQueueCreate( usbBUFFER_LEN, sizeof( char ) );
if( ( xRxedChars == NULL ) || ( xCharsForTx == NULL ) )
{
/* Not enough heap available to create the buffer queues, can't do
anything so just delete ourselves. */
vTaskDelete( NULL );
}
// initialise stack
USBInit();
// register descriptors
USBRegisterDescriptors(abDescriptors);
// register class request handler
USBRegisterRequestHandler(REQTYPE_TYPE_CLASS, HandleClassRequest, abClassReqData);
// register endpoint handlers
USBHwRegisterEPIntHandler(INT_IN_EP, NULL);
USBHwRegisterEPIntHandler(BULK_IN_EP, BulkIn);
USBHwRegisterEPIntHandler(BULK_OUT_EP, BulkOut);
// register frame handler
USBHwRegisterFrameHandler(USBFrameHandler);
// enable bulk-in interrupts on NAKs
USBHwNakIntEnable(INACK_BI);
DBG("Starting USB communication\n");
NVIC_SetPriority( USB_IRQn, configUSB_INTERRUPT_PRIORITY );
NVIC_EnableIRQ( USB_IRQn );
// connect to bus
DBG("Connecting to USB bus\n");
USBHwConnect(TRUE);
// echo any character received (do USB stuff in interrupt)
for( ;; )
{
c = VCOM_getchar();
if (c != EOF)
{
// Echo character back with INCREMENT_ECHO_BY offset, so for example if
// INCREMENT_ECHO_BY is 1 and 'A' is received, 'B' will be echoed back.
VCOM_putchar(c + INCREMENT_ECHO_BY );
}
}
}

View File

@ -0,0 +1,38 @@
/*****************************************************************************
* type.h: Type definition Header file for NXP LPC17xx Family
* Microprocessors
*
* Copyright(C) 2008, NXP Semiconductor
* All rights reserved.
*
* History
* 2008.08.21 ver 1.00 Prelimnary version, first Release
*
******************************************************************************/
#ifndef __TYPE_H__
#define __TYPE_H__
#ifndef NULL
#define NULL ((void *)0)
#endif
#ifndef FALSE
#define FALSE (0)
#endif
#ifndef TRUE
#define TRUE (1)
#endif
typedef unsigned char BYTE;
typedef unsigned short WORD;
typedef unsigned long DWORD;
typedef unsigned int BOOL;
typedef enum {RESET = 0, SET = !RESET} FlagStatus, ITStatus;
typedef enum {DISABLE = 0, ENABLE = !DISABLE} FunctionalState;
/* Pointer to Function returning Void (any number of parameters) */
typedef void (*PFV)();
#endif /* __TYPE_H__ */

View File

@ -0,0 +1,118 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
@file
*/
#include "usbstruct.h" // for TSetupPacket
/*************************************************************************
USB configuration
**************************************************************************/
#define MAX_PACKET_SIZE0 64 /**< maximum packet size for EP 0 */
/*************************************************************************
USB hardware interface
**************************************************************************/
// endpoint status sent through callback
#define EP_STATUS_DATA (1<<0) /**< EP has data */
#define EP_STATUS_STALLED (1<<1) /**< EP is stalled */
#define EP_STATUS_SETUP (1<<2) /**< EP received setup packet */
#define EP_STATUS_ERROR (1<<3) /**< EP data was overwritten by setup packet */
#define EP_STATUS_NACKED (1<<4) /**< EP sent NAK */
// device status sent through callback
#define DEV_STATUS_CONNECT (1<<0) /**< device just got connected */
#define DEV_STATUS_SUSPEND (1<<2) /**< device entered suspend state */
#define DEV_STATUS_RESET (1<<4) /**< device just got reset */
// interrupt bits for NACK events in USBHwNakIntEnable
// (these bits conveniently coincide with the LPC176x USB controller bit)
#define INACK_CI (1<<1) /**< interrupt on NACK for control in */
#define INACK_CO (1<<2) /**< interrupt on NACK for control out */
#define INACK_II (1<<3) /**< interrupt on NACK for interrupt in */
#define INACK_IO (1<<4) /**< interrupt on NACK for interrupt out */
#define INACK_BI (1<<5) /**< interrupt on NACK for bulk in */
#define INACK_BO (1<<6) /**< interrupt on NACK for bulk out */
BOOL USBHwInit (void);
void USBHwISR (void);
void USBHwNakIntEnable (unsigned char bIntBits);
void USBHwConnect (BOOL fConnect);
void USBHwSetAddress (unsigned char bAddr);
void USBHwConfigDevice (BOOL fConfigured);
// endpoint operations
void USBHwEPConfig (unsigned char bEP, unsigned short wMaxPacketSize);
int USBHwEPRead (unsigned char bEP, unsigned char *pbBuf, int iMaxLen);
int USBHwEPWrite (unsigned char bEP, unsigned char *pbBuf, int iLen);
void USBHwEPStall (unsigned char bEP, BOOL fStall);
unsigned char USBHwEPGetStatus (unsigned char bEP);
/** Endpoint interrupt handler callback */
typedef void (TFnEPIntHandler) (unsigned char bEP, unsigned char bEPStatus);
void USBHwRegisterEPIntHandler (unsigned char bEP, TFnEPIntHandler *pfnHandler);
/** Device status handler callback */
typedef void (TFnDevIntHandler) (unsigned char bDevStatus);
void USBHwRegisterDevIntHandler (TFnDevIntHandler *pfnHandler);
/** Frame event handler callback */
typedef void (TFnFrameHandler)(unsigned short wFrame);
void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler);
/*************************************************************************
USB application interface
**************************************************************************/
// initialise the complete stack, including HW
BOOL USBInit(void);
/** Request handler callback (standard, vendor, class) */
typedef BOOL (TFnHandleRequest)(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData);
void USBRegisterRequestHandler(int iType, TFnHandleRequest *pfnHandler, unsigned char *pbDataStore);
void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler);
/** Descriptor handler callback */
typedef BOOL (TFnGetDescriptor)(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData);
/** Default standard request handler */
BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData);
/** Default EP0 handler */
void USBHandleControlTransfer(unsigned char bEP, unsigned char bEPStat);
/** Descriptor handling */
void USBRegisterDescriptors(const unsigned char *pabDescriptors);
BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData);

View File

@ -0,0 +1,246 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
Control transfer handler.
This module handles control transfers and is normally installed on the
endpoint 0 callback.
Control transfers can be of the following type:
0 Standard;
1 Class;
2 Vendor;
3 Reserved.
A callback can be installed for each of these control transfers using
USBRegisterRequestHandler.
When an OUT request arrives, data is collected in the data store provided
with the USBRegisterRequestHandler call. When the transfer is done, the
callback is called.
When an IN request arrives, the callback is called immediately to either
put the control transfer data in the data store, or to get a pointer to
control transfer data. The data is then packetised and sent to the host.
*/
#include "usbdebug.h"
#include "usbstruct.h"
#include "usbapi.h"
#define MAX_CONTROL_SIZE 128 /**< maximum total size of control transfer data */
#define MAX_REQ_HANDLERS 4 /**< standard, class, vendor, reserved */
static TSetupPacket Setup; /**< setup packet */
static unsigned char *pbData; /**< pointer to data buffer */
static int iResidue; /**< remaining bytes in buffer */
static int iLen; /**< total length of control transfer */
/** Array of installed request handler callbacks */
static TFnHandleRequest *apfnReqHandlers[4] = {NULL, NULL, NULL, NULL};
/** Array of installed request data pointers */
static unsigned char *apbDataStore[4] = {NULL, NULL, NULL, NULL};
/**
Local function to handle a request by calling one of the installed
request handlers.
In case of data going from host to device, the data is at *ppbData.
In case of data going from device to host, the handler can either
choose to write its data at *ppbData or update the data pointer.
@param [in] pSetup The setup packet
@param [in,out] *piLen Pointer to data length
@param [in,out] ppbData Data buffer.
@return TRUE if the request was handles successfully
*/
static BOOL _HandleRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
TFnHandleRequest *pfnHandler;
int iType;
iType = REQTYPE_GET_TYPE(pSetup->bmRequestType);
pfnHandler = apfnReqHandlers[iType];
if (pfnHandler == NULL) {
DBG("No handler for reqtype %d\n", iType);
return FALSE;
}
return pfnHandler(pSetup, piLen, ppbData);
}
/**
Local function to stall the control endpoint
@param [in] bEPStat Endpoint status
*/
static void StallControlPipe(unsigned char bEPStat)
{
unsigned char *pb;
int i;
USBHwEPStall(0x80, TRUE);
// dump setup packet
DBG("STALL on [");
pb = (unsigned char *)&Setup;
for (i = 0; i < 8; i++) {
DBG(" %02x", *pb++);
}
DBG("] stat=%x\n", bEPStat);
}
/**
Sends next chunk of data (possibly 0 bytes) to host
*/
static void DataIn(void)
{
int iChunk;
if( MAX_PACKET_SIZE0 < iResidue )
{
iChunk = MAX_PACKET_SIZE0;
}
else
{
iChunk = iResidue;
}
USBHwEPWrite(0x80, pbData, iChunk);
pbData += iChunk;
iResidue -= iChunk;
}
/**
* Handles IN/OUT transfers on EP0
*
* @param [in] bEP Endpoint address
* @param [in] bEPStat Endpoint status
*/
void USBHandleControlTransfer(unsigned char bEP, unsigned char bEPStat)
{
int iChunk, iType;
if (bEP == 0x00) {
// OUT transfer
if (bEPStat & EP_STATUS_SETUP) {
// setup packet, reset request message state machine
USBHwEPRead(0x00, (unsigned char *)&Setup, sizeof(Setup));
DBG("S%x", Setup.bRequest);
// defaults for data pointer and residue
iType = REQTYPE_GET_TYPE(Setup.bmRequestType);
pbData = apbDataStore[iType];
iResidue = Setup.wLength;
iLen = Setup.wLength;
if ((Setup.wLength == 0) ||
(REQTYPE_GET_DIR(Setup.bmRequestType) == REQTYPE_DIR_TO_HOST)) {
// ask installed handler to process request
if (!_HandleRequest(&Setup, &iLen, &pbData)) {
DBG("_HandleRequest1 failed\n");
StallControlPipe(bEPStat);
return;
}
// send smallest of requested and offered length
if( iLen < Setup.wLength )
{
iResidue = iLen;
}
else
{
iResidue = Setup.wLength;
}
// send first part (possibly a zero-length status message)
DataIn();
}
}
else {
if (iResidue > 0) {
// store data
iChunk = USBHwEPRead(0x00, pbData, iResidue);
if (iChunk < 0) {
StallControlPipe(bEPStat);
return;
}
pbData += iChunk;
iResidue -= iChunk;
if (iResidue == 0) {
// received all, send data to handler
iType = REQTYPE_GET_TYPE(Setup.bmRequestType);
pbData = apbDataStore[iType];
if (!_HandleRequest(&Setup, &iLen, &pbData)) {
DBG("_HandleRequest2 failed\n");
StallControlPipe(bEPStat);
return;
}
// send status to host
DataIn();
}
}
else {
// absorb zero-length status message
iChunk = USBHwEPRead(0x00, NULL, 0);
DBG(iChunk > 0 ? "?" : "");
}
}
}
else if (bEP == 0x80) {
// IN transfer
// send more data if available (possibly a 0-length packet)
DataIn();
}
else {
ASSERT(FALSE);
}
}
/**
Registers a callback for handling requests
@param [in] iType Type of request, e.g. REQTYPE_TYPE_STANDARD
@param [in] *pfnHandler Callback function pointer
@param [in] *pbDataStore Data storage area for this type of request
*/
void USBRegisterRequestHandler(int iType, TFnHandleRequest *pfnHandler, unsigned char *pbDataStore)
{
ASSERT(iType >= 0);
ASSERT(iType < 4);
apfnReqHandlers[iType] = pfnHandler;
apbDataStore[iType] = pbDataStore;
}

View File

@ -0,0 +1,41 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// CodeRed - comment out this printf, as will use real one from stdio.h
// to implement output via semihosting
//int printf(const char *format, ...);
# include <stdio.h>
#ifdef _DEBUG
#define DBG printf
#define ASSERT(x) if(!(x)){DBG("\nAssertion '%s' failed in %s:%s#%d!\n",#x,__FILE__,__FUNCTION__,__LINE__);while(1);}
#else
#define DBG(x ...)
#define ASSERT(x)
#endif

View File

@ -0,0 +1,521 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
USB hardware layer
*/
#include "usbdebug.h"
#include "usbhw_lpc.h"
#include "usbapi.h"
/** Installed device interrupt handler */
static TFnDevIntHandler *_pfnDevIntHandler = NULL;
/** Installed endpoint interrupt handlers */
static TFnEPIntHandler *_apfnEPIntHandlers[16];
/** Installed frame interrupt handlers */
static TFnFrameHandler *_pfnFrameHandler = NULL;
/** convert from endpoint address to endpoint index */
#define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7))
/** convert from endpoint index to endpoint address */
#define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF))
/**
Local function to wait for a device interrupt (and clear it)
@param [in] dwIntr Bitmask of interrupts to wait for
*/
static void Wait4DevInt(unsigned long dwIntr)
{
// wait for specific interrupt
while ((USB->USBDevIntSt & dwIntr) != dwIntr);
// clear the interrupt bits
USB->USBDevIntClr = dwIntr;
}
/**
Local function to send a command to the USB protocol engine
@param [in] bCmd Command to send
*/
static void USBHwCmd(unsigned char bCmd)
{
// clear CDFULL/CCEMTY
USB->USBDevIntClr = CDFULL | CCEMTY;
// write command code
USB->USBCmdCode = 0x00000500 | (bCmd << 16);
Wait4DevInt(CCEMTY);
}
/**
Local function to send a command + data to the USB protocol engine
@param [in] bCmd Command to send
@param [in] bData Data to send
*/
static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)
{
// write command code
USBHwCmd(bCmd);
// write command data
USB->USBCmdCode = 0x00000100 | (bData << 16);
Wait4DevInt(CCEMTY);
}
/**
Local function to send a command to the USB protocol engine and read data
@param [in] bCmd Command to send
@return the data
*/
static unsigned char USBHwCmdRead(unsigned char bCmd)
{
// write command code
USBHwCmd(bCmd);
// get data
USB->USBCmdCode = 0x00000200 | (bCmd << 16);
Wait4DevInt(CDFULL);
return USB->USBCmdData;
}
/**
'Realizes' an endpoint, meaning that buffer space is reserved for
it. An endpoint needs to be realised before it can be used.
From experiments, it appears that a USB reset causes USBReEP to
re-initialise to 3 (= just the control endpoints).
However, a USB bus reset does not disturb the USBMaxPSize settings.
@param [in] idx Endpoint index
@param [in] wMaxPSize Maximum packet size for this endpoint
*/
static void USBHwEPRealize(int idx, unsigned short wMaxPSize)
{
USB->USBReEP |= (1 << idx);
USB->USBEpInd = idx;
USB->USBMaxPSize = wMaxPSize;
Wait4DevInt(EP_RLZED);
}
/**
Enables or disables an endpoint
@param [in] idx Endpoint index
@param [in] fEnable TRUE to enable, FALSE to disable
*/
static void USBHwEPEnable(int idx, BOOL fEnable)
{
USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);
}
/**
Configures an endpoint and enables it
@param [in] bEP Endpoint number
@param [in] wMaxPacketSize Maximum packet size for this EP
*/
void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)
{
int idx;
idx = EP2IDX(bEP);
// realise EP
USBHwEPRealize(idx, wMaxPacketSize);
// enable EP
USBHwEPEnable(idx, TRUE);
}
/**
Registers an endpoint event callback
@param [in] bEP Endpoint number
@param [in] pfnHandler Callback function
*/
void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)
{
int idx;
idx = EP2IDX(bEP);
ASSERT(idx<32);
/* add handler to list of EP handlers */
_apfnEPIntHandlers[idx / 2] = pfnHandler;
/* enable EP interrupt */
USB->USBEpIntEn |= (1 << idx);
USB->USBDevIntEn |= EP_SLOW;
DBG("Registered handler for EP 0x%x\n", bEP);
}
/**
Registers an device status callback
@param [in] pfnHandler Callback function
*/
void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)
{
_pfnDevIntHandler = pfnHandler;
// enable device interrupt
USB->USBDevIntEn |= DEV_STAT;
DBG("Registered handler for device status\n");
}
/**
Registers the frame callback
@param [in] pfnHandler Callback function
*/
void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)
{
_pfnFrameHandler = pfnHandler;
// enable device interrupt
USB->USBDevIntEn |= FRAME;
DBG("Registered handler for frame\n");
}
/**
Sets the USB address.
@param [in] bAddr Device address to set
*/
void USBHwSetAddress(unsigned char bAddr)
{
USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);
}
/**
Connects or disconnects from the USB bus
@param [in] fConnect If TRUE, connect, otherwise disconnect
*/
void USBHwConnect(BOOL fConnect)
{
USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);
}
/**
Enables interrupt on NAK condition
For IN endpoints a NAK is generated when the host wants to read data
from the device, but none is available in the endpoint buffer.
For OUT endpoints a NAK is generated when the host wants to write data
to the device, but the endpoint buffer is still full.
The endpoint interrupt handlers can distinguish regular (ACK) interrupts
from NAK interrupt by checking the bits in their bEPStatus argument.
@param [in] bIntBits Bitmap indicating which NAK interrupts to enable
*/
void USBHwNakIntEnable(unsigned char bIntBits)
{
USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);
}
/**
Gets the status from a specific endpoint.
@param [in] bEP Endpoint number
@return Endpoint status byte (containing EP_STATUS_xxx bits)
*/
unsigned char USBHwEPGetStatus(unsigned char bEP)
{
int idx = EP2IDX(bEP);
return USBHwCmdRead(CMD_EP_SELECT | idx);
}
/**
Sets the stalled property of an endpoint
@param [in] bEP Endpoint number
@param [in] fStall TRUE to stall, FALSE to unstall
*/
void USBHwEPStall(unsigned char bEP, BOOL fStall)
{
int idx = EP2IDX(bEP);
USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);
}
/**
Writes data to an endpoint buffer
@param [in] bEP Endpoint number
@param [in] pbBuf Endpoint data
@param [in] iLen Number of bytes to write
@return TRUE if the data was successfully written or <0 in case of error.
*/
int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)
{
int idx;
idx = EP2IDX(bEP);
// set write enable for specific endpoint
USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);
// set packet length
USB->USBTxPLen = iLen;
// write data
while (USB->USBCtrl & WR_EN) {
USB->USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0];
pbBuf += 4;
}
// select endpoint and validate buffer
USBHwCmd(CMD_EP_SELECT | idx);
USBHwCmd(CMD_EP_VALIDATE_BUFFER);
return iLen;
}
/**
Reads data from an endpoint buffer
@param [in] bEP Endpoint number
@param [in] pbBuf Endpoint data
@param [in] iMaxLen Maximum number of bytes to read
@return the number of bytes available in the EP (possibly more than iMaxLen),
or <0 in case of error.
*/
int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)
{
unsigned int i, idx;
unsigned long dwData, dwLen;
idx = EP2IDX(bEP);
// set read enable bit for specific endpoint
USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);
// wait for PKT_RDY
do {
dwLen = USB->USBRxPLen;
} while ((dwLen & PKT_RDY) == 0);
// packet valid?
if ((dwLen & DV) == 0) {
return -1;
}
// get length
dwLen &= PKT_LNGTH_MASK;
// get data
dwData = 0;
for (i = 0; i < dwLen; i++) {
if ((i % 4) == 0) {
dwData = USB->USBRxData;
}
if ((pbBuf != NULL) && ((int)i < iMaxLen)) {
pbBuf[i] = dwData & 0xFF;
}
dwData >>= 8;
}
// make sure RD_EN is clear
USB->USBCtrl = 0;
// select endpoint and clear buffer
USBHwCmd(CMD_EP_SELECT | idx);
USBHwCmd(CMD_EP_CLEAR_BUFFER);
return dwLen;
}
/**
Sets the 'configured' state.
All registered endpoints are 'realised' and enabled, and the
'configured' bit is set in the device status register.
@param [in] fConfigured If TRUE, configure device, else unconfigure
*/
void USBHwConfigDevice(BOOL fConfigured)
{
// set configured bit
USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);
}
/**
USB interrupt handler
@todo Get all 11 bits of frame number instead of just 8
Endpoint interrupts are mapped to the slow interrupt
*/
void USBHwISR(void)
{
unsigned long dwStatus;
unsigned long dwIntBit;
unsigned char bEPStat, bDevStat, bStat;
int i;
unsigned short wFrame;
// handle device interrupts
dwStatus = USB->USBDevIntSt;
// frame interrupt
if (dwStatus & FRAME) {
// clear int
USB->USBDevIntClr = FRAME;
// call handler
if (_pfnFrameHandler != NULL) {
wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
_pfnFrameHandler(wFrame);
}
}
// device status interrupt
if (dwStatus & DEV_STAT) {
/* Clear DEV_STAT interrupt before reading DEV_STAT register.
This prevents corrupted device status reads, see
LPC2148 User manual revision 2, 25 july 2006.
*/
USB->USBDevIntClr = DEV_STAT;
bDevStat = USBHwCmdRead(CMD_DEV_STATUS);
if (bDevStat & (CON_CH | SUS_CH | RST)) {
// convert device status into something HW independent
bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) |
((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) |
((bDevStat & RST) ? DEV_STATUS_RESET : 0);
// call handler
if (_pfnDevIntHandler != NULL) {
_pfnDevIntHandler(bStat);
}
}
}
// endpoint interrupt
if (dwStatus & EP_SLOW) {
// clear EP_SLOW
USB->USBDevIntClr = EP_SLOW;
// check all endpoints
for (i = 0; i < 32; i++) {
dwIntBit = (1 << i);
if (USB->USBEpIntSt & dwIntBit) {
// clear int (and retrieve status)
USB->USBEpIntClr = dwIntBit;
Wait4DevInt(CDFULL);
bEPStat = USB->USBCmdData;
// convert EP pipe stat into something HW independent
bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) |
((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) |
((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) |
((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) |
((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0);
// call handler
if (_apfnEPIntHandlers[i / 2] != NULL) {
_apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);
}
}
}
}
}
/**
Initialises the USB hardware
@return TRUE if the hardware was successfully initialised
*/
BOOL USBHwInit(void)
{
// P2.9 -> USB_CONNECT
PINCON->PINSEL4 &= ~0x000C0000;
PINCON->PINSEL4 |= 0x00040000;
// P1.18 -> USB_UP_LED
// P1.30 -> VBUS
PINCON->PINSEL3 &= ~0x30000030;
PINCON->PINSEL3 |= 0x20000010;
// P0.29 -> USB_D+
// P0.30 -> USB_D-
PINCON->PINSEL1 &= ~0x3C000000;
PINCON->PINSEL1 |= 0x14000000;
// enable PUSB
SC->PCONP |= (1 << 31);
USB->OTGClkCtrl = 0x12; /* Dev clock, AHB clock enable */
while ((USB->OTGClkSt & 0x12) != 0x12);
// disable/clear all interrupts for now
USB->USBDevIntEn = 0;
USB->USBDevIntClr = 0xFFFFFFFF;
USB->USBDevIntPri = 0;
USB->USBEpIntEn = 0;
USB->USBEpIntClr = 0xFFFFFFFF;
USB->USBEpIntPri = 0;
// by default, only ACKs generate interrupts
USBHwNakIntEnable(0);
return TRUE;
}

View File

@ -0,0 +1,521 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
USB hardware layer
*/
#include "usbdebug.h"
#include "usbhw_lpc.h"
#include "usbapi.h"
/** Installed device interrupt handler */
static TFnDevIntHandler *_pfnDevIntHandler = NULL;
/** Installed endpoint interrupt handlers */
static TFnEPIntHandler *_apfnEPIntHandlers[16];
/** Installed frame interrupt handlers */
static TFnFrameHandler *_pfnFrameHandler = NULL;
/** convert from endpoint address to endpoint index */
#define EP2IDX(bEP) ((((bEP)&0xF)<<1)|(((bEP)&0x80)>>7))
/** convert from endpoint index to endpoint address */
#define IDX2EP(idx) ((((idx)<<7)&0x80)|(((idx)>>1)&0xF))
/**
Local function to wait for a device interrupt (and clear it)
@param [in] dwIntr Bitmask of interrupts to wait for
*/
static void Wait4DevInt(unsigned long dwIntr)
{
// wait for specific interrupt
while ((USB->USBDevIntSt & dwIntr) != dwIntr);
// clear the interrupt bits
USB->USBDevIntClr = dwIntr;
}
/**
Local function to send a command to the USB protocol engine
@param [in] bCmd Command to send
*/
static void USBHwCmd(unsigned char bCmd)
{
// clear CDFULL/CCEMTY
USB->USBDevIntClr = CDFULL | CCEMTY;
// write command code
USB->USBCmdCode = 0x00000500 | (bCmd << 16);
Wait4DevInt(CCEMTY);
}
/**
Local function to send a command + data to the USB protocol engine
@param [in] bCmd Command to send
@param [in] bData Data to send
*/
static void USBHwCmdWrite(unsigned char bCmd, unsigned short bData)
{
// write command code
USBHwCmd(bCmd);
// write command data
USB->USBCmdCode = 0x00000100 | (bData << 16);
Wait4DevInt(CCEMTY);
}
/**
Local function to send a command to the USB protocol engine and read data
@param [in] bCmd Command to send
@return the data
*/
static unsigned char USBHwCmdRead(unsigned char bCmd)
{
// write command code
USBHwCmd(bCmd);
// get data
USB->USBCmdCode = 0x00000200 | (bCmd << 16);
Wait4DevInt(CDFULL);
return USB->USBCmdData;
}
/**
'Realizes' an endpoint, meaning that buffer space is reserved for
it. An endpoint needs to be realised before it can be used.
From experiments, it appears that a USB reset causes USBReEP to
re-initialise to 3 (= just the control endpoints).
However, a USB bus reset does not disturb the USBMaxPSize settings.
@param [in] idx Endpoint index
@param [in] wMaxPSize Maximum packet size for this endpoint
*/
static void USBHwEPRealize(int idx, unsigned short wMaxPSize)
{
USB->USBReEP |= (1 << idx);
USB->USBEpInd = idx;
USB->USBMaxPSize = wMaxPSize;
Wait4DevInt(EP_RLZED);
}
/**
Enables or disables an endpoint
@param [in] idx Endpoint index
@param [in] fEnable TRUE to enable, FALSE to disable
*/
static void USBHwEPEnable(int idx, BOOL fEnable)
{
USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fEnable ? 0 : EP_DA);
}
/**
Configures an endpoint and enables it
@param [in] bEP Endpoint number
@param [in] wMaxPacketSize Maximum packet size for this EP
*/
void USBHwEPConfig(unsigned char bEP, unsigned short wMaxPacketSize)
{
int idx;
idx = EP2IDX(bEP);
// realise EP
USBHwEPRealize(idx, wMaxPacketSize);
// enable EP
USBHwEPEnable(idx, TRUE);
}
/**
Registers an endpoint event callback
@param [in] bEP Endpoint number
@param [in] pfnHandler Callback function
*/
void USBHwRegisterEPIntHandler(unsigned char bEP, TFnEPIntHandler *pfnHandler)
{
int idx;
idx = EP2IDX(bEP);
ASSERT(idx<32);
/* add handler to list of EP handlers */
_apfnEPIntHandlers[idx / 2] = pfnHandler;
/* enable EP interrupt */
USB->USBEpIntEn |= (1 << idx);
USB->USBDevIntEn |= EP_SLOW;
DBG("Registered handler for EP 0x%x\n", bEP);
}
/**
Registers an device status callback
@param [in] pfnHandler Callback function
*/
void USBHwRegisterDevIntHandler(TFnDevIntHandler *pfnHandler)
{
_pfnDevIntHandler = pfnHandler;
// enable device interrupt
USB->USBDevIntEn |= DEV_STAT;
DBG("Registered handler for device status\n");
}
/**
Registers the frame callback
@param [in] pfnHandler Callback function
*/
void USBHwRegisterFrameHandler(TFnFrameHandler *pfnHandler)
{
_pfnFrameHandler = pfnHandler;
// enable device interrupt
USB->USBDevIntEn |= FRAME;
DBG("Registered handler for frame\n");
}
/**
Sets the USB address.
@param [in] bAddr Device address to set
*/
void USBHwSetAddress(unsigned char bAddr)
{
USBHwCmdWrite(CMD_DEV_SET_ADDRESS, DEV_EN | bAddr);
}
/**
Connects or disconnects from the USB bus
@param [in] fConnect If TRUE, connect, otherwise disconnect
*/
void USBHwConnect(BOOL fConnect)
{
USBHwCmdWrite(CMD_DEV_STATUS, fConnect ? CON : 0);
}
/**
Enables interrupt on NAK condition
For IN endpoints a NAK is generated when the host wants to read data
from the device, but none is available in the endpoint buffer.
For OUT endpoints a NAK is generated when the host wants to write data
to the device, but the endpoint buffer is still full.
The endpoint interrupt handlers can distinguish regular (ACK) interrupts
from NAK interrupt by checking the bits in their bEPStatus argument.
@param [in] bIntBits Bitmap indicating which NAK interrupts to enable
*/
void USBHwNakIntEnable(unsigned char bIntBits)
{
USBHwCmdWrite(CMD_DEV_SET_MODE, bIntBits);
}
/**
Gets the status from a specific endpoint.
@param [in] bEP Endpoint number
@return Endpoint status byte (containing EP_STATUS_xxx bits)
*/
unsigned char USBHwEPGetStatus(unsigned char bEP)
{
int idx = EP2IDX(bEP);
return USBHwCmdRead(CMD_EP_SELECT | idx);
}
/**
Sets the stalled property of an endpoint
@param [in] bEP Endpoint number
@param [in] fStall TRUE to stall, FALSE to unstall
*/
void USBHwEPStall(unsigned char bEP, BOOL fStall)
{
int idx = EP2IDX(bEP);
USBHwCmdWrite(CMD_EP_SET_STATUS | idx, fStall ? EP_ST : 0);
}
/**
Writes data to an endpoint buffer
@param [in] bEP Endpoint number
@param [in] pbBuf Endpoint data
@param [in] iLen Number of bytes to write
@return TRUE if the data was successfully written or <0 in case of error.
*/
int USBHwEPWrite(unsigned char bEP, unsigned char *pbBuf, int iLen)
{
int idx;
idx = EP2IDX(bEP);
// set write enable for specific endpoint
USB->USBCtrl = WR_EN | ((bEP & 0xF) << 2);
// set packet length
USB->USBTxPLen = iLen;
// write data
while (USB->USBCtrl & WR_EN) {
USB->USBTxData = (pbBuf[3] << 24) | (pbBuf[2] << 16) | (pbBuf[1] << 8) | pbBuf[0];
pbBuf += 4;
}
// select endpoint and validate buffer
USBHwCmd(CMD_EP_SELECT | idx);
USBHwCmd(CMD_EP_VALIDATE_BUFFER);
return iLen;
}
/**
Reads data from an endpoint buffer
@param [in] bEP Endpoint number
@param [in] pbBuf Endpoint data
@param [in] iMaxLen Maximum number of bytes to read
@return the number of bytes available in the EP (possibly more than iMaxLen),
or <0 in case of error.
*/
int USBHwEPRead(unsigned char bEP, unsigned char *pbBuf, int iMaxLen)
{
unsigned int i, idx;
unsigned long dwData, dwLen;
idx = EP2IDX(bEP);
// set read enable bit for specific endpoint
USB->USBCtrl = RD_EN | ((bEP & 0xF) << 2);
// wait for PKT_RDY
do {
dwLen = USB->USBRxPLen;
} while ((dwLen & PKT_RDY) == 0);
// packet valid?
if ((dwLen & DV) == 0) {
return -1;
}
// get length
dwLen &= PKT_LNGTH_MASK;
// get data
dwData = 0;
for (i = 0; i < dwLen; i++) {
if ((i % 4) == 0) {
dwData = USB->USBRxData;
}
if ((pbBuf != NULL) && (i < iMaxLen)) {
pbBuf[i] = dwData & 0xFF;
}
dwData >>= 8;
}
// make sure RD_EN is clear
USB->USBCtrl = 0;
// select endpoint and clear buffer
USBHwCmd(CMD_EP_SELECT | idx);
USBHwCmd(CMD_EP_CLEAR_BUFFER);
return dwLen;
}
/**
Sets the 'configured' state.
All registered endpoints are 'realised' and enabled, and the
'configured' bit is set in the device status register.
@param [in] fConfigured If TRUE, configure device, else unconfigure
*/
void USBHwConfigDevice(BOOL fConfigured)
{
// set configured bit
USBHwCmdWrite(CMD_DEV_CONFIG, fConfigured ? CONF_DEVICE : 0);
}
/**
USB interrupt handler
@todo Get all 11 bits of frame number instead of just 8
Endpoint interrupts are mapped to the slow interrupt
*/
void USBHwISR(void)
{
unsigned long dwStatus;
unsigned long dwIntBit;
unsigned char bEPStat, bDevStat, bStat;
int i;
unsigned short wFrame;
// handle device interrupts
dwStatus = USB->USBDevIntSt;
// frame interrupt
if (dwStatus & FRAME) {
// clear int
USB->USBDevIntClr = FRAME;
// call handler
if (_pfnFrameHandler != NULL) {
wFrame = USBHwCmdRead(CMD_DEV_READ_CUR_FRAME_NR);
_pfnFrameHandler(wFrame);
}
}
// device status interrupt
if (dwStatus & DEV_STAT) {
/* Clear DEV_STAT interrupt before reading DEV_STAT register.
This prevents corrupted device status reads, see
LPC2148 User manual revision 2, 25 july 2006.
*/
USB->USBDevIntClr = DEV_STAT;
bDevStat = USBHwCmdRead(CMD_DEV_STATUS);
if (bDevStat & (CON_CH | SUS_CH | RST)) {
// convert device status into something HW independent
bStat = ((bDevStat & CON) ? DEV_STATUS_CONNECT : 0) |
((bDevStat & SUS) ? DEV_STATUS_SUSPEND : 0) |
((bDevStat & RST) ? DEV_STATUS_RESET : 0);
// call handler
if (_pfnDevIntHandler != NULL) {
_pfnDevIntHandler(bStat);
}
}
}
// endpoint interrupt
if (dwStatus & EP_SLOW) {
// clear EP_SLOW
USB->USBDevIntClr = EP_SLOW;
// check all endpoints
for (i = 0; i < 32; i++) {
dwIntBit = (1 << i);
if (USB->USBEpIntSt & dwIntBit) {
// clear int (and retrieve status)
USB->USBEpIntClr = dwIntBit;
Wait4DevInt(CDFULL);
bEPStat = USB->USBCmdData;
// convert EP pipe stat into something HW independent
bStat = ((bEPStat & EPSTAT_FE) ? EP_STATUS_DATA : 0) |
((bEPStat & EPSTAT_ST) ? EP_STATUS_STALLED : 0) |
((bEPStat & EPSTAT_STP) ? EP_STATUS_SETUP : 0) |
((bEPStat & EPSTAT_EPN) ? EP_STATUS_NACKED : 0) |
((bEPStat & EPSTAT_PO) ? EP_STATUS_ERROR : 0);
// call handler
if (_apfnEPIntHandlers[i / 2] != NULL) {
_apfnEPIntHandlers[i / 2](IDX2EP(i), bStat);
}
}
}
}
}
/**
Initialises the USB hardware
@return TRUE if the hardware was successfully initialised
*/
BOOL USBHwInit(void)
{
// P2.9 -> USB_CONNECT
PINCON->PINSEL4 &= ~0x000C0000;
PINCON->PINSEL4 |= 0x00040000;
// P1.18 -> USB_UP_LED
// P1.30 -> VBUS
PINCON->PINSEL3 &= ~0x30000030;
PINCON->PINSEL3 |= 0x20000010;
// P0.29 -> USB_D+
// P0.30 -> USB_D-
PINCON->PINSEL1 &= ~0x3C000000;
PINCON->PINSEL1 |= 0x14000000;
// enable PUSB
SC->PCONP |= (1 << 31);
USB->OTGClkCtrl = 0x12; /* Dev clock, AHB clock enable */
while ((USB->OTGClkSt & 0x12) != 0x12);
// disable/clear all interrupts for now
USB->USBDevIntEn = 0;
USB->USBDevIntClr = 0xFFFFFFFF;
USB->USBDevIntPri = 0;
USB->USBEpIntEn = 0;
USB->USBEpIntClr = 0xFFFFFFFF;
USB->USBEpIntPri = 0;
// by default, only ACKs generate interrupts
USBHwNakIntEnable(0);
return TRUE;
}

View File

@ -0,0 +1,149 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
Hardware definitions for the LPC176x USB controller
These are private to the usbhw module
*/
// CodeRed - pull in defines from NXP header file
//#include "NXP\LPC17xx\LPC17xx.h"
#include "LPC17xx.h"
// CodeRed - these registers have been renamed on LPC176x
#define USBReEP USBReEp
#define OTG_CLK_CTRL USBClkCtrl
#define OTG_CLK_STAT USBClkSt
/* USBIntSt bits */
#define USB_INT_REQ_LP (1<<0)
#define USB_INT_REQ_HP (1<<1)
#define USB_INT_REQ_DMA (1<<2)
#define USB_need_clock (1<<8)
#define EN_USB_BITS (1<<31)
/* USBDevInt... bits */
#define FRAME (1<<0)
#define EP_FAST (1<<1)
#define EP_SLOW (1<<2)
#define DEV_STAT (1<<3)
#define CCEMTY (1<<4)
#define CDFULL (1<<5)
#define RxENDPKT (1<<6)
#define TxENDPKT (1<<7)
#define EP_RLZED (1<<8)
#define ERR_INT (1<<9)
/* USBRxPLen bits */
#define PKT_LNGTH (1<<0)
#define PKT_LNGTH_MASK 0x3FF
#define DV (1<<10)
#define PKT_RDY (1<<11)
/* USBCtrl bits */
#define RD_EN (1<<0)
#define WR_EN (1<<1)
#define LOG_ENDPOINT (1<<2)
/* protocol engine command codes */
/* device commands */
#define CMD_DEV_SET_ADDRESS 0xD0
#define CMD_DEV_CONFIG 0xD8
#define CMD_DEV_SET_MODE 0xF3
#define CMD_DEV_READ_CUR_FRAME_NR 0xF5
#define CMD_DEV_READ_TEST_REG 0xFD
#define CMD_DEV_STATUS 0xFE /* read/write */
#define CMD_DEV_GET_ERROR_CODE 0xFF
#define CMD_DEV_READ_ERROR_STATUS 0xFB
/* endpoint commands */
#define CMD_EP_SELECT 0x00
#define CMD_EP_SELECT_CLEAR 0x40
#define CMD_EP_SET_STATUS 0x40
#define CMD_EP_CLEAR_BUFFER 0xF2
#define CMD_EP_VALIDATE_BUFFER 0xFA
/* set address command */
#define DEV_ADDR (1<<0)
#define DEV_EN (1<<7)
/* configure device command */
#define CONF_DEVICE (1<<0)
/* set mode command */
#define AP_CLK (1<<0)
#define INAK_CI (1<<1)
#define INAK_CO (1<<2)
#define INAK_II (1<<3)
#define INAK_IO (1<<4)
#define INAK_BI (1<<5)
#define INAK_BO (1<<6)
/* set get device status command */
#define CON (1<<0)
#define CON_CH (1<<1)
#define SUS (1<<2)
#define SUS_CH (1<<3)
#define RST (1<<4)
/* get error code command */
// ...
/* Select Endpoint command read bits */
#define EPSTAT_FE (1<<0)
#define EPSTAT_ST (1<<1)
#define EPSTAT_STP (1<<2)
#define EPSTAT_PO (1<<3)
#define EPSTAT_EPN (1<<4)
#define EPSTAT_B1FULL (1<<5)
#define EPSTAT_B2FULL (1<<6)
/* CMD_EP_SET_STATUS command */
#define EP_ST (1<<0)
#define EP_DA (1<<5)
#define EP_RF_MO (1<<6)
#define EP_CND_ST (1<<7)
/* read error status command */
#define PID_ERR (1<<0)
#define UEPKT (1<<1)
#define DCRC (1<<2)
#define TIMEOUT (1<<3)
#define EOP (1<<4)
#define B_OVRN (1<<5)
#define BTSTF (1<<6)
#define TGL_ERR (1<<7)

View File

@ -0,0 +1,82 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
USB stack initialisation
*/
#include "usbdebug.h"
#include "usbapi.h"
/** data storage area for standard requests */
static unsigned char abStdReqData[8];
/**
USB reset handler
@param [in] bDevStatus Device status
*/
static void HandleUsbReset(unsigned char bDevStatus)
{
if (bDevStatus & DEV_STATUS_RESET) {
DBG("\n!");
}
}
/**
Initialises the USB hardware and sets up the USB stack by
installing default callbacks.
@return TRUE if initialisation was successful
*/
BOOL USBInit(void)
{
// init hardware
USBHwInit();
// register bus reset handler
USBHwRegisterDevIntHandler(HandleUsbReset);
// register control transfer handler on EP0
USBHwRegisterEPIntHandler(0x00, USBHandleControlTransfer);
USBHwRegisterEPIntHandler(0x80, USBHandleControlTransfer);
// setup control endpoints
USBHwEPConfig(0x00, MAX_PACKET_SIZE0);
USBHwEPConfig(0x80, MAX_PACKET_SIZE0);
// register standard request handler
USBRegisterRequestHandler(REQTYPE_TYPE_STANDARD, USBHandleStandardRequest, abStdReqData);
return TRUE;
}

View File

@ -0,0 +1,430 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/** @file
Standard request handler.
This modules handles the 'chapter 9' processing, specifically the
standard device requests in table 9-3 from the universal serial bus
specification revision 2.0
Specific types of devices may specify additional requests (for example
HID devices add a GET_DESCRIPTOR request for interfaces), but they
will not be part of this module.
@todo some requests have to return a request error if device not configured:
@todo GET_INTERFACE, GET_STATUS, SET_INTERFACE, SYNCH_FRAME
@todo this applies to the following if endpoint != 0:
@todo SET_FEATURE, GET_FEATURE
*/
#include "usbdebug.h"
#include "usbstruct.h"
#include "usbapi.h"
#define MAX_DESC_HANDLERS 4 /**< device, interface, endpoint, other */
/* general descriptor field offsets */
#define DESC_bLength 0 /**< length offset */
#define DESC_bDescriptorType 1 /**< descriptor type offset */
/* config descriptor field offsets */
#define CONF_DESC_wTotalLength 2 /**< total length offset */
#define CONF_DESC_bConfigurationValue 5 /**< configuration value offset */
#define CONF_DESC_bmAttributes 7 /**< configuration characteristics */
/* interface descriptor field offsets */
#define INTF_DESC_bAlternateSetting 3 /**< alternate setting offset */
/* endpoint descriptor field offsets */
#define ENDP_DESC_bEndpointAddress 2 /**< endpoint address offset */
#define ENDP_DESC_wMaxPacketSize 4 /**< maximum packet size offset */
/** Currently selected configuration */
static unsigned char bConfiguration = 0;
/** Installed custom request handler */
static TFnHandleRequest *pfnHandleCustomReq = NULL;
/** Pointer to registered descriptors */
static const unsigned char *pabDescrip = NULL;
/**
Registers a pointer to a descriptor block containing all descriptors
for the device.
@param [in] pabDescriptors The descriptor byte array
*/
void USBRegisterDescriptors(const unsigned char *pabDescriptors)
{
pabDescrip = pabDescriptors;
}
/**
Parses the list of installed USB descriptors and attempts to find
the specified USB descriptor.
@param [in] wTypeIndex Type and index of the descriptor
@param [in] wLangID Language ID of the descriptor (currently unused)
@param [out] *piLen Descriptor length
@param [out] *ppbData Descriptor data
@return TRUE if the descriptor was found, FALSE otherwise
*/
BOOL USBGetDescriptor(unsigned short wTypeIndex, unsigned short wLangID, int *piLen, unsigned char **ppbData)
{
unsigned char bType, bIndex;
unsigned char *pab;
int iCurIndex;
ASSERT(pabDescrip != NULL);
bType = GET_DESC_TYPE(wTypeIndex);
bIndex = GET_DESC_INDEX(wTypeIndex);
pab = (unsigned char *)pabDescrip;
iCurIndex = 0;
while (pab[DESC_bLength] != 0) {
if (pab[DESC_bDescriptorType] == bType) {
if (iCurIndex == bIndex) {
// set data pointer
*ppbData = pab;
// get length from structure
if (bType == DESC_CONFIGURATION) {
// configuration descriptor is an exception, length is at offset 2 and 3
*piLen = (pab[CONF_DESC_wTotalLength]) |
(pab[CONF_DESC_wTotalLength + 1] << 8);
}
else {
// normally length is at offset 0
*piLen = pab[DESC_bLength];
}
return TRUE;
}
iCurIndex++;
}
// skip to next descriptor
pab += pab[DESC_bLength];
}
// nothing found
DBG("Desc %x not found!\n", wTypeIndex);
return FALSE;
}
/**
Configures the device according to the specified configuration index and
alternate setting by parsing the installed USB descriptor list.
A configuration index of 0 unconfigures the device.
@param [in] bConfigIndex Configuration index
@param [in] bAltSetting Alternate setting number
@todo function always returns TRUE, add stricter checking?
@return TRUE if successfully configured, FALSE otherwise
*/
static BOOL USBSetConfiguration(unsigned char bConfigIndex, unsigned char bAltSetting)
{
unsigned char *pab;
unsigned char bCurConfig, bCurAltSetting;
unsigned char bEP;
unsigned short wMaxPktSize;
ASSERT(pabDescrip != NULL);
if (bConfigIndex == 0) {
// unconfigure device
USBHwConfigDevice(FALSE);
}
else {
// configure endpoints for this configuration/altsetting
pab = (unsigned char *)pabDescrip;
bCurConfig = 0xFF;
bCurAltSetting = 0xFF;
while (pab[DESC_bLength] != 0) {
switch (pab[DESC_bDescriptorType]) {
case DESC_CONFIGURATION:
// remember current configuration index
bCurConfig = pab[CONF_DESC_bConfigurationValue];
break;
case DESC_INTERFACE:
// remember current alternate setting
bCurAltSetting = pab[INTF_DESC_bAlternateSetting];
break;
case DESC_ENDPOINT:
if ((bCurConfig == bConfigIndex) &&
(bCurAltSetting == bAltSetting)) {
// endpoint found for desired config and alternate setting
bEP = pab[ENDP_DESC_bEndpointAddress];
wMaxPktSize = (pab[ENDP_DESC_wMaxPacketSize]) |
(pab[ENDP_DESC_wMaxPacketSize + 1] << 8);
// configure endpoint
USBHwEPConfig(bEP, wMaxPktSize);
}
break;
default:
break;
}
// skip to next descriptor
pab += pab[DESC_bLength];
}
// configure device
USBHwConfigDevice(TRUE);
}
return TRUE;
}
/**
Local function to handle a standard device request
@param [in] pSetup The setup packet
@param [in,out] *piLen Pointer to data length
@param [in,out] ppbData Data buffer.
@return TRUE if the request was handled successfully
*/
static BOOL HandleStdDeviceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
unsigned char *pbData = *ppbData;
switch (pSetup->bRequest) {
case REQ_GET_STATUS:
// bit 0: self-powered
// bit 1: remote wakeup = not supported
pbData[0] = 0;
pbData[1] = 0;
*piLen = 2;
break;
case REQ_SET_ADDRESS:
USBHwSetAddress(pSetup->wValue);
break;
case REQ_GET_DESCRIPTOR:
DBG("D%x", pSetup->wValue);
return USBGetDescriptor(pSetup->wValue, pSetup->wIndex, piLen, ppbData);
case REQ_GET_CONFIGURATION:
// indicate if we are configured
pbData[0] = bConfiguration;
*piLen = 1;
break;
case REQ_SET_CONFIGURATION:
if (!USBSetConfiguration(pSetup->wValue & 0xFF, 0)) {
DBG("USBSetConfiguration failed!\n");
return FALSE;
}
// configuration successful, update current configuration
bConfiguration = pSetup->wValue & 0xFF;
break;
case REQ_CLEAR_FEATURE:
case REQ_SET_FEATURE:
if (pSetup->wValue == FEA_REMOTE_WAKEUP) {
// put DEVICE_REMOTE_WAKEUP code here
}
if (pSetup->wValue == FEA_TEST_MODE) {
// put TEST_MODE code here
}
return FALSE;
case REQ_SET_DESCRIPTOR:
DBG("Device req %d not implemented\n", pSetup->bRequest);
return FALSE;
default:
DBG("Illegal device req %d\n", pSetup->bRequest);
return FALSE;
}
return TRUE;
}
/**
Local function to handle a standard interface request
@param [in] pSetup The setup packet
@param [in,out] *piLen Pointer to data length
@param [in] ppbData Data buffer.
@return TRUE if the request was handled successfully
*/
static BOOL HandleStdInterfaceReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
unsigned char *pbData = *ppbData;
switch (pSetup->bRequest) {
case REQ_GET_STATUS:
// no bits specified
pbData[0] = 0;
pbData[1] = 0;
*piLen = 2;
break;
case REQ_CLEAR_FEATURE:
case REQ_SET_FEATURE:
// not defined for interface
return FALSE;
case REQ_GET_INTERFACE: // TODO use bNumInterfaces
// there is only one interface, return n-1 (= 0)
pbData[0] = 0;
*piLen = 1;
break;
case REQ_SET_INTERFACE: // TODO use bNumInterfaces
// there is only one interface (= 0)
if (pSetup->wValue != 0) {
return FALSE;
}
*piLen = 0;
break;
default:
DBG("Illegal interface req %d\n", pSetup->bRequest);
return FALSE;
}
return TRUE;
}
/**
Local function to handle a standard endpoint request
@param [in] pSetup The setup packet
@param [in,out] *piLen Pointer to data length
@param [in] ppbData Data buffer.
@return TRUE if the request was handled successfully
*/
static BOOL HandleStdEndPointReq(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
unsigned char *pbData = *ppbData;
switch (pSetup->bRequest) {
case REQ_GET_STATUS:
// bit 0 = endpointed halted or not
pbData[0] = (USBHwEPGetStatus(pSetup->wIndex) & EP_STATUS_STALLED) ? 1 : 0;
pbData[1] = 0;
*piLen = 2;
break;
case REQ_CLEAR_FEATURE:
if (pSetup->wValue == FEA_ENDPOINT_HALT) {
// clear HALT by unstalling
USBHwEPStall(pSetup->wIndex, FALSE);
break;
}
// only ENDPOINT_HALT defined for endpoints
return FALSE;
case REQ_SET_FEATURE:
if (pSetup->wValue == FEA_ENDPOINT_HALT) {
// set HALT by stalling
USBHwEPStall(pSetup->wIndex, TRUE);
break;
}
// only ENDPOINT_HALT defined for endpoints
return FALSE;
case REQ_SYNCH_FRAME:
DBG("EP req %d not implemented\n", pSetup->bRequest);
return FALSE;
default:
DBG("Illegal EP req %d\n", pSetup->bRequest);
return FALSE;
}
return TRUE;
}
/**
Default handler for standard ('chapter 9') requests
If a custom request handler was installed, this handler is called first.
@param [in] pSetup The setup packet
@param [in,out] *piLen Pointer to data length
@param [in] ppbData Data buffer.
@return TRUE if the request was handled successfully
*/
BOOL USBHandleStandardRequest(TSetupPacket *pSetup, int *piLen, unsigned char **ppbData)
{
// try the custom request handler first
if ((pfnHandleCustomReq != NULL) && pfnHandleCustomReq(pSetup, piLen, ppbData)) {
return TRUE;
}
switch (REQTYPE_GET_RECIP(pSetup->bmRequestType)) {
case REQTYPE_RECIP_DEVICE: return HandleStdDeviceReq(pSetup, piLen, ppbData);
case REQTYPE_RECIP_INTERFACE: return HandleStdInterfaceReq(pSetup, piLen, ppbData);
case REQTYPE_RECIP_ENDPOINT: return HandleStdEndPointReq(pSetup, piLen, ppbData);
default: return FALSE;
}
}
/**
Registers a callback for custom device requests
In USBHandleStandardRequest, the custom request handler gets a first
chance at handling the request before it is handed over to the 'chapter 9'
request handler.
This can be used for example in HID devices, where a REQ_GET_DESCRIPTOR
request is sent to an interface, which is not covered by the 'chapter 9'
specification.
@param [in] pfnHandler Callback function pointer
*/
void USBRegisterCustomReqHandler(TFnHandleRequest *pfnHandler)
{
pfnHandleCustomReq = pfnHandler;
}

View File

@ -0,0 +1,119 @@
/*
LPCUSB, an USB device driver for LPC microcontrollers
Copyright (C) 2006 Bertrik Sikken (bertrik@sikken.nl)
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/**
Definitions of structures of standard USB packets
*/
#ifndef _USBSTRUCT_H_
#define _USBSTRUCT_H_
// CodeRed - include the LPCUSB type.h file rather than NXP one directly
#include "type.h"
/** setup packet definitions */
typedef struct {
unsigned char bmRequestType; /**< characteristics of the specific request */
unsigned char bRequest; /**< specific request */
unsigned short wValue; /**< request specific parameter */
unsigned short wIndex; /**< request specific parameter */
unsigned short wLength; /**< length of data transfered in data phase */
} TSetupPacket;
#define REQTYPE_GET_DIR(x) (((x)>>7)&0x01)
#define REQTYPE_GET_TYPE(x) (((x)>>5)&0x03)
#define REQTYPE_GET_RECIP(x) ((x)&0x1F)
#define REQTYPE_DIR_TO_DEVICE 0
#define REQTYPE_DIR_TO_HOST 1
#define REQTYPE_TYPE_STANDARD 0
#define REQTYPE_TYPE_CLASS 1
#define REQTYPE_TYPE_VENDOR 2
#define REQTYPE_TYPE_RESERVED 3
#define REQTYPE_RECIP_DEVICE 0
#define REQTYPE_RECIP_INTERFACE 1
#define REQTYPE_RECIP_ENDPOINT 2
#define REQTYPE_RECIP_OTHER 3
/* standard requests */
#define REQ_GET_STATUS 0x00
#define REQ_CLEAR_FEATURE 0x01
#define REQ_SET_FEATURE 0x03
#define REQ_SET_ADDRESS 0x05
#define REQ_GET_DESCRIPTOR 0x06
#define REQ_SET_DESCRIPTOR 0x07
#define REQ_GET_CONFIGURATION 0x08
#define REQ_SET_CONFIGURATION 0x09
#define REQ_GET_INTERFACE 0x0A
#define REQ_SET_INTERFACE 0x0B
#define REQ_SYNCH_FRAME 0x0C
/* class requests HID */
#define HID_GET_REPORT 0x01
#define HID_GET_IDLE 0x02
#define HID_GET_PROTOCOL 0x03
#define HID_SET_REPORT 0x09
#define HID_SET_IDLE 0x0A
#define HID_SET_PROTOCOL 0x0B
/* feature selectors */
#define FEA_ENDPOINT_HALT 0x00
#define FEA_REMOTE_WAKEUP 0x01
#define FEA_TEST_MODE 0x02
/*
USB descriptors
*/
/** USB descriptor header */
typedef struct {
unsigned char bLength; /**< descriptor length */
unsigned char bDescriptorType; /**< descriptor type */
} TUSBDescHeader;
#define DESC_DEVICE 1
#define DESC_CONFIGURATION 2
#define DESC_STRING 3
#define DESC_INTERFACE 4
#define DESC_ENDPOINT 5
#define DESC_DEVICE_QUALIFIER 6
#define DESC_OTHER_SPEED 7
#define DESC_INTERFACE_POWER 8
#define DESC_HID_HID 0x21
#define DESC_HID_REPORT 0x22
#define DESC_HID_PHYSICAL 0x23
#define GET_DESC_TYPE(x) (((x)>>8)&0xFF)
#define GET_DESC_INDEX(x) ((x)&0xFF)
#endif /* _USBSTRUCT_H_ */

View File

@ -1,7 +1,7 @@
<!DOCTYPE CrossStudio_Project_File>
<solution Name="RTOSDemo" version="2">
<project Name="RTOSDemo">
<configuration Name="Common" Target="LPC1768" arm_architecture="v7M" arm_core_type="Cortex-M3" arm_linker_heap_size="128" arm_linker_process_stack_size="0" arm_linker_stack_size="128" arm_simulator_memory_simulation_filename="$(TargetsDir)/LPC1000/LPC1000SimulatorMemory.dll" arm_simulator_memory_simulation_parameter="0x80000;0x8000;0x8000" arm_target_debug_interface_type="ADIv5" arm_target_loader_parameter="12000000" c_enforce_ansi_checking="No" c_only_additional_options="-Wall;-Wextra" c_preprocessor_definitions="PACK_STRUCT_END=__attribute((packed));ALIGN_STRUCT_END=__attribute((aligned(4)))" c_user_include_directories="$(TargetsDir)/LPC1000/include;../../Source/include;../Common/include;../../Source/portable/GCC/ARM_CM3;../Common/ethernet/uIP/uip-1.0/uip;.;./webserver;./USBHost/Include" link_include_startup_code="No" linker_additional_files="$(TargetsDir)/LPC1000/lib/liblpc1000$(LibExt)$(LIB);$(TargetsDir)/LPC1000/lib/cmsis$(LibExt)$(LIB)" linker_memory_map_file="$(TargetsDir)/LPC1000/LPC1768_MemoryMap.xml" linker_printf_fmt_level="int" linker_printf_width_precision_supported="No" linker_section_placement_file="$(ProjectDir)/flash_placement.xml" oscillator_frequency="12MHz" project_directory="" project_type="Executable" property_groups_file_path="$(TargetsDir)/LPC1000/propertyGroups.xml"/>
<configuration Name="Common" Target="LPC1768" arm_architecture="v7M" arm_core_type="Cortex-M3" arm_linker_heap_size="128" arm_linker_process_stack_size="0" arm_linker_stack_size="128" arm_simulator_memory_simulation_filename="$(TargetsDir)/LPC1000/LPC1000SimulatorMemory.dll" arm_simulator_memory_simulation_parameter="0x80000;0x8000;0x8000" arm_target_debug_interface_type="ADIv5" arm_target_loader_parameter="12000000" c_enforce_ansi_checking="No" c_only_additional_options="-Wall;-Wextra" c_preprocessor_definitions="PACK_STRUCT_END=__attribute((packed));ALIGN_STRUCT_END=__attribute((aligned(4)));CONFIGURE_USB;FULL_SPEED" c_user_include_directories="$(TargetsDir)/LPC1000/include;../../Source/include;../Common/include;../../Source/portable/GCC/ARM_CM3;../Common/ethernet/uIP/uip-1.0/uip;.;./webserver;./LPCUSB" link_include_startup_code="No" linker_additional_files="$(TargetsDir)/LPC1000/lib/liblpc1000$(LibExt)$(LIB);$(TargetsDir)/LPC1000/lib/cmsis$(LibExt)$(LIB)" linker_memory_map_file="$(TargetsDir)/LPC1000/LPC1768_MemoryMap.xml" linker_printf_fmt_level="int" linker_printf_width_precision_supported="No" linker_section_placement_file="$(ProjectDir)/flash_placement.xml" oscillator_frequency="12MHz" project_directory="" project_type="Executable" property_groups_file_path="$(TargetsDir)/LPC1000/propertyGroups.xml"/>
<configuration Name="RAM" Placement="RAM" linker_section_placement_file="$(StudioDir)/targets/Cortex_M/ram_placement.xml" target_reset_script="SRAMReset()"/>
<configuration Name="Flash" Placement="Flash" arm_target_flash_loader_file_path="$(TargetsDir)/LPC1000/Release/Loader_rpc.elf" arm_target_flash_loader_type="LIBMEM RPC Loader" linker_patch_build_command="$(StudioDir)/bin/crossscript &quot;load(\&quot;$(TargetsDir)/LPC1000/LPC1000_LinkPatch.js\&quot;);patch(\&quot;$(TargetPath)\&quot;);&quot;" linker_section_placement_file="$(ProjectDir)/flash_placement.xml" target_reset_script="FLASHReset()"/>
<folder Name="Source Files">
@ -41,6 +41,13 @@
</folder>
<file file_name="ParTest.c"/>
<file file_name="printf-stdarg.c"/>
<folder Name="LPCUSB">
<file file_name="LPCUSB/usbstdreq.c"/>
<file file_name="LPCUSB/USB_CDC.c"/>
<file file_name="LPCUSB/usbcontrol.c"/>
<file file_name="LPCUSB/usbhw_lpc.c"/>
<file file_name="LPCUSB/usbinit.c"/>
</folder>
</folder>
<folder Name="System Files">
<file file_name="$(StudioDir)/source/thumb_crt0.s"/>

View File

@ -22,6 +22,9 @@
<ProjectSessionItem path="RTOSDemo" name="unnamed" />
<ProjectSessionItem path="RTOSDemo;RTOSDemo" name="unnamed" />
<ProjectSessionItem path="RTOSDemo;RTOSDemo;Source Files" name="unnamed" />
<ProjectSessionItem path="RTOSDemo;RTOSDemo;Source Files;FreeRTOS" name="unnamed" />
<ProjectSessionItem path="RTOSDemo;RTOSDemo;Source Files;LPCUSB" name="unnamed" />
<ProjectSessionItem path="RTOSDemo;RTOSDemo;System Files" name="unnamed" />
</Project>
<Register1>
<RegisterWindow openNodes="CPU;CPU/xPSR;CPU/CFBP;CPU/CFBP/CONTROL[0];CPU/CFBP/CONTROL[1]" binaryNodes="" unsignedNodes="" visibleGroups="CPU" decimalNodes="" octalNodes="" asciiNodes="" />
@ -52,7 +55,7 @@
<Watches active="0" update="Never" />
</Watch4>
<Files>
<SessionOpenFile useTextEdit="1" useBinaryEdit="0" codecName="Latin1" x="10" debugPath="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\main.c" y="304" path="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\main.c" left="0" selected="1" name="unnamed" top="0" />
<SessionOpenFile useTextEdit="1" useBinaryEdit="0" codecName="Latin1" x="0" debugPath="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\main.c" y="141" path="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\main.c" left="0" selected="1" name="unnamed" top="138" />
</Files>
<ARMCrossStudioWindow activeProject="RTOSDemo" autoConnectTarget="USB CrossConnect for ARM" debugSearchFileMap="" fileDialogInitialDirectory="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\UsbHost\Uart" fileDialogDefaultFilter="*.*" autoConnectCapabilities="388991" debugSearchPath="" buildConfiguration="THUMB Flash Debug" />
<ARMCrossStudioWindow activeProject="RTOSDemo" autoConnectTarget="USB CrossConnect for ARM" debugSearchFileMap="" fileDialogInitialDirectory="C:\E\Dev\FreeRTOS\WorkingCopy3\Demo\CORTEX_LPC1768_GCC_Rowley\LPCUSB" fileDialogDefaultFilter="*.*" autoConnectCapabilities="388991" debugSearchPath="" buildConfiguration="THUMB Flash Debug" />
</session>

View File

@ -62,6 +62,11 @@
*
* "uIP" task - This is the task that handles the uIP stack. All TCP/IP
* processing is performed in this task.
*
* "USB" task - Enumerates the USB device as a CDC class, then echoes back all
* received characters with a configurable offset (for example, if the offset
* is 1 and 'A' is received then 'B' will be sent back). A dumb terminal such
* as Hyperterminal can be used to talk to the USB task.
*/
/* Scheduler includes. */
@ -116,6 +121,11 @@ static void prvSetupHardware( void );
*/
extern void vuIP_Task( void *pvParameters );
/*
* The task that handles the USB stack.
*/
extern void vUSBTask( void *pvParameters );
/*
* Simply returns the current status message for display on served WEB pages.
*/
@ -145,6 +155,9 @@ int main( void )
vStartRecursiveMutexTasks();
vStartLEDFlashTasks( mainFLASH_TASK_PRIORITY );
/* Create the USB task. */
xTaskCreate( vUSBTask, ( signed char * ) "USB", configMINIMAL_STACK_SIZE, ( void * ) NULL, tskIDLE_PRIORITY, NULL );
/* Create the uIP task. The WEB server runs in this task. */
xTaskCreate( vuIP_Task, ( signed char * ) "uIP", mainBASIC_WEB_STACK_SIZE, ( void * ) NULL, mainUIP_TASK_PRIORITY, NULL );
@ -228,49 +241,6 @@ void prvSetupHardware( void )
/* Disable TPIU. */
PINCON->PINSEL10 = 0;
/* Disconnect the main PLL. */
SC->PLL0CON &= ~PLLCON_PLLC;
SC->PLL0FEED = PLLFEED_FEED1;
SC->PLL0FEED = PLLFEED_FEED2;
while ((SC->PLL0STAT & PLLSTAT_PLLC) != 0);
/* Turn off the main PLL. */
SC->PLL0CON &= ~PLLCON_PLLE;
SC->PLL0FEED = PLLFEED_FEED1;
SC->PLL0FEED = PLLFEED_FEED2;
while ((SC->PLL0STAT & PLLSTAT_PLLE) != 0);
/* No CPU clock divider. */
SC->CCLKCFG = 0;
/* OSCEN. */
SC->SCS = 0x20;
while ((SC->SCS & 0x40) == 0);
/* Use main oscillator. */
SC->CLKSRCSEL = 1;
SC->PLL0CFG = (PLLCFG_MUL16 | PLLCFG_DIV1);
SC->PLL0FEED = PLLFEED_FEED1;
SC->PLL0FEED = PLLFEED_FEED2;
/* Activate the PLL by turning it on then feeding the correct
sequence of bytes. */
SC->PLL0CON = PLLCON_PLLE;
SC->PLL0FEED = PLLFEED_FEED1;
SC->PLL0FEED = PLLFEED_FEED2;
/* 6x CPU clock divider (64 MHz) */
SC->CCLKCFG = 5;
/* Wait for the PLL to lock. */
while ((SC->PLL0STAT & PLLSTAT_PLOCK) == 0);
/* Connect the PLL. */
SC->PLL0CON = PLLCON_PLLC | PLLCON_PLLE;
SC->PLL0FEED = PLLFEED_FEED1;
SC->PLL0FEED = PLLFEED_FEED2;
/* Setup the peripheral bus to be the same as the PLL output (64 MHz). */
SC->PCLKSEL0 = 0x05555555;