Add simple USB CDC task.
parent
a83d9d93e8
commit
df338c84c3
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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__ */
|
@ -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);
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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_ */
|
||||||
|
|
Loading…
Reference in New Issue