📄 d_usb.c
字号:
//
// Programmer
//
// Date init 14.12.2004
//
// Reviser $Author:: Dkpechri $
//
// Revision date $Date:: 19-07-06 10:02 $
//
// Filename $Workfile:: d_usb.c $
//
// Version $Revision:: 32 $
//
// Archive $Archive:: /LMS2006/Sys01/Main/Firmware/Source/d_usb.c $
//
// Platform C
//
#include "stdconst.h"
#include "m_sched.h"
#include "d_usb.h"
#include "d_usb.r"
#define ENDPOINT_OUT 1 // HOST write
#define ENDPOINT_OUT_SIZE 64
#define ENDPOINT_IN 2 // HOST read
#define ENDPOINT_IN_SIZE 64
#define AT91C_UDP_ISR ((AT91_REG *) 0xFFFB001C) // (UDP) Interrupt Status Register
#define AT91C_RSTC_URSTEN ((unsigned int) 0x1 << 0) // (RSTC) User Reset Enable
// Endpoint Control and Status Registers
#define AT91C_UDP_CSR0 ((AT91_REG *) 0xFFFB0030) // Endpoint 0 Control and Status Register
#define AT91C_UDP_CSR1 ((AT91_REG *) 0xFFFB0034) // Endpoint 1 Control and Status Register
#define AT91C_UDP_CSR2 ((AT91_REG *) 0xFFFB0038) // Endpoint 2 Control and Status Register
#define AT91C_UDP_CSR3 ((AT91_REG *) 0xFFFB003C) // Endpoint 3 Control and Status Register
// Endpoint FIFO Data Registers
#define AT91C_UDP_FDR0 ((AT91_REG *) 0xFFFB0050) // Endpoint 0 FIFO Data Register
#define AT91C_UDP_FDR1 ((AT91_REG *) 0xFFFB0054) // Endpoint 1 FIFO Data Register
#define AT91C_UDP_FDR2 ((AT91_REG *) 0xFFFB0058) // Endpoint 2 FIFO Data Register
#define AT91C_UDP_FDR3 ((AT91_REG *) 0xFFFB005C) // Endpoint 3 FIFO Data Register
const UBYTE DeviceDescriptor[] = {
/* Device descriptor */
0x12, // bLength, size of this descriptor = 18 entries
0x01, // bDescriptorType = 1 = DEVICE
0x00, // bcdUSBL, USB spec. vers. 2.0
0x02, // bcdUSBH, -
0x00, // bDeviceClass
0x00, // bDeviceSubclass
0x00, // bDeviceProtocol
0x08, // bMaxPacketSize0, EndPointZero packet size = 8
0x94, // idVendorL, LEGO Group
0x06, // idVendorH, -
0x02, // idProductL, LEGO USB IR Tower = 0x01
0x00, // idProductH, -
0x00, // bcdDeviceL, device is version (zero)
0x00, // bcdDeviceH, -
0x00, // iManufacturer, index of string descriptor describing manufacturer
0x00, // iProduct, index of string descriptor describing product
0x01, // iSerialNumber, index of string descriptor describing the device's
// serial no.
0x01 // bNumConfigs, number of possible configurations (only one)
};
/* USB standard request codes */
#define STD_GET_STATUS_ZERO 0x0080
#define STD_GET_STATUS_INTERFACE 0x0081
#define STD_GET_STATUS_ENDPOINT 0x0082
#define STD_CLEAR_FEATURE_ZERO 0x0100
#define STD_CLEAR_FEATURE_INTERFACE 0x0101
#define STD_CLEAR_FEATURE_ENDPOINT 0x0102
#define STD_SET_FEATURE_ZERO 0x0300
#define STD_SET_FEATURE_INTERFACE 0x0301
#define STD_SET_FEATURE_ENDPOINT 0x0302
#define STD_SET_ADDRESS 0x0500
#define STD_GET_DESCRIPTOR 0x0680
#define STD_SET_DESCRIPTOR 0x0700
#define STD_GET_CONFIGURATION 0x0880
#define STD_SET_CONFIGURATION 0x0900
#define STD_GET_INTERFACE 0x0A81
#define STD_SET_INTERFACE 0x0B01
#define STD_SYNCH_FRAME 0x0C82
/* USB constants, masks etc. */
#define END_OF_BUS_RESET ((unsigned int) 0x1 << 12)
#define SUSPEND_INT ((unsigned int) 0x1 << 8)
#define SUSPEND_RESUME ((unsigned int) 0x1 << 9)
#define WAKEUP ((unsigned int) 0x1 << 13)
//USB spec allows 500ms for control transfers
#define USB_MAX_TIMEOUT 500
static UBYTE UsbHandleList[MAX_HANDLES];
static UBYTE UsbHandleCnt;
static UWORD RequestedData;
static UBYTE BrickNameKnown;
enum
{
USB_NOT_CONFIGURED,
USB_CONFIGURED,
USB_CONFIGURED_BUT_SUSPENDED
};
static UBYTE UsbConnectionStates;
const UBYTE ConfigurationDescriptor[] = {
/* ============== CONFIGURATION 1 =========== */
/* Configuration 1 descriptor */
0x09, // bLength, descriptor size in bytes
0x02, // bDescriptorType, The constant Configuration
0x20, // wTotalLengthL for 2 EP + Control
0x00, // wTotalLengthH -
0x01, // bNumInterfaces, Number of interfaces in the configuration
0x01, // bConfigurationValue, Identifier for
// Set_Configuration and Get_Configuration requests
0x00, // iConfiguration, Index of string descriptor for the configuration
0xC0, // bmAttributes, Bit 7 shall always be set. See e.g. page 108 in the book:
// "USB Complete" by Jan Axelson. June 2001
// Self powered only bit 6 = 1 (zero = buspowered USB 1.1 and up)
0x00, // MaxPower, power required (mA./2) We're SELF-POWERED, so ZERO
/* Interface Descriptor */
0x09, // bLength, descriptor size in bytes
0x04, // bDescriptorType, the constant 0x04 = "INTERFACE"
0x00, // bInterfaceNumber, No. identifying this interface
0x00, // bAlternateSetting, value used to get an alternative interface
0x02, // bNumEndpoints, No. of supported endpoints in addition to endpoint 0
0xFF, // bInterfaceClass, Specifies the class code = VENDOR Specific
0xFF, // bInterfaceSubclass, Specifies the subclass code = VENDOR Specific
0xFF, // bInterfaceProtocol, protocol code = VENDOR Specific
0x00, // iInterface, index of string descriptor for the interface
/* Endpoint 1 descriptor */
0x07, // bLength, descriptor length incl. this = 7
0x05, // bDescriptorType
0x01, // bEndpointAddress, Endpoint 01 - OUT
0x02, // bmAttributes BULK
ENDPOINT_OUT_SIZE, // wMaxPacketSize
0x00, // -
0x00, // bInterval
/* Endpoint 2 descriptor */
0x07, // bLength, descriptor length incl. this = 7
0x05, // bDescriptorType
0x82, // bEndpointAddress, Endpoint 02 - IN
0x02, // bmAttributes BULK
ENDPOINT_IN_SIZE, // wMaxPacketSize
0x00, // -
0x00 // bInterval
};
UBYTE SerialNumberDescriptor[] =
{
0x1A, // bLength, descriptor length incl. this = 16 bytes
0x03, // bDescriptorType
0x31, 0x00, // MSD of Lap (Lap[2,3]) in UNICode
0x32, 0x00, // Lap[4,5]
0x33, 0x00, // Lap[6,7]
0x34, 0x00, // Lap[8,9]
0x35, 0x00, // Lap[10,11]
0x36, 0x00, // Lap[12,13]
0x37, 0x00, // Lap[14,15]
0x38, 0x00, // LSD of Lap (Lap[16,17]) in UNICode
0x30, 0x00, // MSD of Nap (Nap[18,19]) in UNICode
0x30, 0x00, // LSD of Nap (Nap[20,21]) in UNICode
0x39, 0x00, // MSD of Uap in UNICode
0x30, 0x00 // LSD of Uap in UNICode
};
const UBYTE LangIdDescriptor[] =
{
0x04, // Length
0x03, // Type, 3 = CONSTANT String
0x09, // English
0x04 // subcode = U.S. English
};
static UCHAR CurrentConfiguration; // Configured or not. We've only 1 conf. so... Boolean
static ULONG CurrentReceiveBank; // Used for keep track of the PING-PONG buffers
ULONG g_UsbTimeoutCounter;
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
void dUsbDisconnect(void)
{
USBDisconnect;
}
void dUsbConnect(void)
{
USBConnect;
}
void dUsbStartTimeoutTimer(void)
{
g_UsbTimeoutCounter = 0;
USBGetActualTime;
}
// A longer version of the USB timer.
// Table 7-14 of the USB 2.0 spec allows up to 500ms for standard request completion.
UBYTE dUsbTimedOut(void)
{
if(USBTimedOut)
{
g_UsbTimeoutCounter++;
USBGetActualTime;
}
return (g_UsbTimeoutCounter >= USB_MAX_TIMEOUT) ? TRUE : FALSE;
}
UBYTE ConvertHighToHex(UBYTE TempChar)
{
TempChar = (TempChar >> 4) & 0x0F;
if (TempChar > 0x09)
TempChar += 0x37;
else
TempChar += 0x30;
return TempChar;
}
UBYTE ConvertLowToHex(UBYTE TempChar)
{
TempChar &= 0x0F;
if (TempChar > 0x09)
TempChar += 0x37;
else
TempChar += 0x30;
return TempChar;
}
void dUsbStoreBtAddress(UBYTE *pBtAddress)
{
UBYTE NoToConvert;
// make the Lap human readable (hmmm Hexadecimal)
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[2] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[4] = ConvertLowToHex(NoToConvert);
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[6] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[8] = ConvertLowToHex(NoToConvert);
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[10] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[12] = ConvertLowToHex(NoToConvert);
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[14] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[16] = ConvertLowToHex(NoToConvert);
// make the Uap human readable (hmmm Hexadecimal)
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[18] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[20] = ConvertLowToHex(NoToConvert);
// make the Nap human readable (hmmm Hexadecimal)
NoToConvert = *pBtAddress++;
SerialNumberDescriptor[22] = ConvertHighToHex(NoToConvert);
SerialNumberDescriptor[24] = ConvertLowToHex(NoToConvert);
USBConnect; // We're ready to participate in the real world
BrickNameKnown = TRUE; // OK for referencing :-)
}
ULONG dUsbRead(UBYTE *pData, ULONG Length)
{
ULONG PacketSize, NumberOfBytesReceived;
NumberOfBytesReceived = 0;
while (Length) // Wished read size from user (Max length)
{
if ( !(BrickNameKnown)) // Right Brick???
break;
if ( !(dUsbIsConfigured()) )
break; // Not configured - no time to waste
if ( (*AT91C_UDP_CSR1) & CurrentReceiveBank ) // Data packet rx'ed in Current bank?
{
PacketSize = MIN((*AT91C_UDP_CSR1) >> 16, Length); // Normalize number of bytes available in FIFO
Length -= PacketSize; // Rest of data to receive
if (PacketSize < ENDPOINT_OUT_SIZE) // If data less, we only have one loop
Length = 0;
while(PacketSize--) // While more data in this very packet...
pData[NumberOfBytesReceived++] = *AT91C_UDP_FDR1; // Fill in buffer
*AT91C_UDP_CSR1 &= ~(CurrentReceiveBank); // Reset current bank pointer
if (CurrentReceiveBank == AT91C_UDP_RX_DATA_BK0) // Current Receive Bank 0?
CurrentReceiveBank = AT91C_UDP_RX_DATA_BK1; // We better use Bank 1
else
CurrentReceiveBank = AT91C_UDP_RX_DATA_BK0; // Okay, go for Bank 0 :-)
}
else Length = 0; // Leave and let's use the CPU cycles in a better way
}
return NumberOfBytesReceived; // Size of actually received stuff
}
ULONG dUsbWrite( const UBYTE *pData, ULONG Length)
{
ULONG CharsEachTx = 0;
// Send the very first (or only) packet
CharsEachTx = MIN(Length, ENDPOINT_IN_SIZE); // First transmission size
Length -= CharsEachTx; // Adjust the rest of transmission size
while (CharsEachTx--) // While more chars in this chunk
*AT91C_UDP_FDR2 = *pData++; // Get rid off it one by one
// Pushing the data into the UDP TX-fifo
*AT91C_UDP_CSR2 |= AT91C_UDP_TXPKTRDY; // Signal "DO THE TX" the stuff is delivered...
while (Length) // While more bytes (I.e. packets) 韓 total transmission
{ // Start filling the second bank
CharsEachTx = MIN(Length, ENDPOINT_IN_SIZE);
Length -= CharsEachTx; // Adjust total length
while (CharsEachTx--) // While more chars in this chunk
*AT91C_UDP_FDR2 = *pData++;
dUsbStartTimeoutTimer();
while ( !((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP) ) // Wait for the the first bank to be sent
if (dUsbTimedOut() || !(dUsbIsConfigured()) ) // Communication down..... Bail out
return Length; // Invalid function - return job length not done
(*AT91C_UDP_CSR2) &= ~(AT91C_UDP_TXCOMP); // Reset transmit interrupt flag
while ((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP); // Wait until flag (H/W) is reset
(*AT91C_UDP_CSR2) |= AT91C_UDP_TXPKTRDY; // We're ready to send next bank
} // Loop while bytes to tx
dUsbStartTimeoutTimer(); // Arm the timeout timing
while ( !((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP) ) // Wait for transmission to complete
if ( !(dUsbIsConfigured()) || dUsbTimedOut()) // Communication down..... Bail out
return Length; // Invalid function - return job length not done
(*AT91C_UDP_CSR2) &= ~(AT91C_UDP_TXCOMP); // Reset Interrupt flag
while ((*AT91C_UDP_CSR2) & AT91C_UDP_TXCOMP); // Wait for H/W to settle.....
return Length; // Return byte count NOT x-ferred
}
static void dUsbSendStall(void)
{
(*AT91C_UDP_CSR0) |= AT91C_UDP_FORCESTALL; // Set STALL condition
while ( !((*AT91C_UDP_CSR0) & AT91C_UDP_ISOERROR) ); // Wait until stall ack'ed
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR); // Reset again
while ((*AT91C_UDP_CSR0) & (AT91C_UDP_FORCESTALL | AT91C_UDP_ISOERROR)); // Wait until H/W really reset
}
static void dUsbSendZeroLengthPackage(void)
{
// Signal that buffer is ready to send
(*AT91C_UDP_CSR0) |= AT91C_UDP_TXPKTRDY;
dUsbStartTimeoutTimer();
// Wait for ACK handshake from host
while ( !((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP) && !dUsbTimedOut());
// Clear handshake flag
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXCOMP);
while ((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP);
}
static void dUsbSendViaControl(const UBYTE *pData, ULONG Length)
{
ULONG BytesToTx = 0;
AT91_REG Temp_Csr;
UBYTE HaveToTxZeroLength = FALSE;
UBYTE ZeroCouldBeNeeded = FALSE;
// If the amount of data requested is more than what can be sent, a 0-length
// packet may be required
if (RequestedData > Length)
{
ZeroCouldBeNeeded = TRUE; // Exact same size would be interpreted as EOP @ host
}
do
{
// The endpoint size is 8 bytes. Limit each data phase to 8 bytes.
BytesToTx = MIN(Length, 8);
Length -= BytesToTx;
// If this is the last data phase containing data, but the host requested
// more, a 0-byte packet will be needed to terminate the data phase.
if(ZeroCouldBeNeeded && (Length == 0) && (BytesToTx == 8))
{
HaveToTxZeroLength = TRUE;
}
// Copy data to endpoint buffer
while (BytesToTx--)
{
(*AT91C_UDP_FDR0) = *pData++;
}
// Signal that buffer is ready to send
(*AT91C_UDP_CSR0) |= AT91C_UDP_TXPKTRDY;
dUsbStartTimeoutTimer();
// Wait for ACK handshake from host
do
{
Temp_Csr = (*AT91C_UDP_CSR0);
// Return if the status phase occurs before the packet is accepted
if (Temp_Csr & AT91C_UDP_RX_DATA_BK0)
{
// Clear the PKTRDY flag
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXPKTRDY);
// Clear the status phase flag
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_RX_DATA_BK0);
return;
}
}
while (!(Temp_Csr & AT91C_UDP_TXCOMP) && !dUsbTimedOut());
// Clear handshake flag
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_TXCOMP);
while ((*AT91C_UDP_CSR0) & AT91C_UDP_TXCOMP);
} while (Length);
if(HaveToTxZeroLength)
{
dUsbSendZeroLengthPackage();
}
dUsbStartTimeoutTimer();
// Wait for Status Phase
while(!((*AT91C_UDP_CSR0) & AT91C_UDP_RX_DATA_BK0) && !dUsbTimedOut());
// Clear flag
(*AT91C_UDP_CSR0) &= ~(AT91C_UDP_RX_DATA_BK0);
}
static void dUsbEnumerate(void)
{
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -