// TITLE: Enumeration code to handle all endpoint zero traffic
#include "usbd.h"
#include <sysctl.h>
#include <usb.h>
namespace usb{
//*****************************************************************************
// This is the currently active class in use by USBLib. There is only one
// of these per USB controller and no device has more than one controller.
//*****************************************************************************
tDevice *g_UsbDevice;
//! Initialize the USB library device control driver for a given hardware
//! controller.
bool tDevice::Init() {
// Save the USB interrupt number.
ui32IntNum = INT_USB;
// Initialize a couple of fields in the device state structure.
ui32Configuration = 0;
// g_psDCDInst.ui32DefaultConfiguration = DEFAULT_CONFIG_ID;
iEP0State = eUSBStateIdle;
// Default to the state where remote wake up is disabled.
ui8Status = 0;
g_UsbDevice = this;
// Initialize the Device Info structure for a USB device instance.
// Default to device mode if no mode was set.
//
// Only do hardware update if the stack is in not in OTG mode.
#ifdef __TMS320C28XX__
SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_USBA);
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_USBA);
#else
SysCtl_resetPeripheral(SYSCTL_PERIPH_RES_USB);
SysCtl_enablePeripheral(SYSCTL_PERIPH_CLK_USB);
#endif
USBDevMode(USB_BASE); // Force device mode
// Initialize the USB DMA interface.
psDMAInstance = USBLibDMAInit(USB_BASE);
// Initialize the USB tick module.
// InternalUSBTickInit();
ui8Status &= ~1; //USB_STATUS_SELF_PWR
USBIntStatusControl(USB_BASE);
USBIntStatusEndpoint(USB_BASE);
// Enable USB Interrupts.
USBIntEnableControl(USB_BASE, USB_INTCTRL_RESET |
USB_INTCTRL_DISCONNECT |
USB_INTCTRL_RESUME |
USB_INTCTRL_SUSPEND |
USB_INTCTRL_SOF);
USBIntEnableEndpoint(USB_BASE, USB_INTEP_ALL);
// Attach the device using the soft connect.
USBDevConnect(USB_BASE);
// Enable the USB interrupt.
Interrupt_enable(ui32IntNum);
return true;
}
//! Free the USB library device control driver for a given hardware controller.
void tDevice::Term() {
// Check the arguments.
// Disable the USB interrupts.
Interrupt_disable(ui32IntNum);
// Reset the tick handlers so that they can be reconfigured when and if
// USBDCDInit() is called.
// InternalUSBTickReset();
// No active device.
g_UsbDevice = 0;
USBIntDisableControl(USB_BASE, USB_INTCTRL_ALL);
USBIntDisableEndpoint(USB_BASE, USB_INTEP_ALL);
// Detach the device using the soft connect.
USBDevDisconnect(USB_BASE);
// Clear any pending interrupts.
USBIntStatusControl(USB_BASE);
USBIntStatusEndpoint(USB_BASE);
// Disable the USB peripheral
#ifdef __TMS320C28XX__
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_USBA);
#else
SysCtl_disablePeripheral(SYSCTL_PERIPH_CLK_USB);
#endif
}
//*****************************************************************************
// This internal function handles sending data on endpoint zero.
//*****************************************************************************
void tDevice::EP0StateTx() {
// In the TX state on endpoint zero.
iEP0State = eUSBStateTx;
// Set the number of bytes to send this iteration.
uint32_t ui32NumBytes = ui32EP0DataRemain;
// Limit individual transfers to 64 bytes.
if (ui32NumBytes > 64) ui32NumBytes = 64;
// Save the pointer so that it can be passed to the USBEndpointDataPut()
// function.
uint8_t *pui8Data = pui8EP0Data;
// Advance the data pointer and counter to the next data to be sent.
ui32EP0DataRemain -= ui32NumBytes;
pui8EP0Data += ui32NumBytes;
// Put the data in the correct FIFO.
USBEndpointDataPut(USB_BASE, 0, pui8Data, ui32NumBytes);
// If this is exactly 64 then don't set the last packet yet.
if (ui32NumBytes == 64) {
// There is more data to send or exactly 64 bytes were sent, this
// means that there is either more data coming or a null packet needs
// to be sent to complete the transaction.
USBEndpointDataSend(USB_BASE, 0, USB_TRANS_IN);
}else{
// Now go to the status state and wait for the transmit to complete.
iEP0State = eUSBStateStatus;
// Send the last bit of data.
USBEndpointDataSend(USB_BASE, 0, USB_TRANS_IN_LAST);
}
}
//*****************************************************************************
//! This function starts the request for data from the host on endpoint zero.
//!
//! \param pui8Data is a pointer to the buffer to fill with data from the USB
//! host.
//! \param ui32Size is the size of the buffer or data to return from the USB
//! host.
//!
//! This function handles retrieving data from the host when a custom command
//! has been issued on endpoint zero. If the application needs notification
//! when the data has been received,
//! <tt>psCallbacks->pfnDataReceived()</tt> in the tDevice structure
//! must contain valid function pointer. In nearly all cases this is necessary
//! because the caller of this function would likely need to know that the data
//! requested was received.
//*****************************************************************************
void tDevice::RequestDataEP0(uint8_t *pui8Data, uint32_t ui32Size) {
// Enter the RX state on end point 0.
iEP0State = eUSBStateRx;
// Save the pointer to the data.
pui8EP0Data = pui8Data;
// Bytes remaining to be received.
ui32EP0DataRemain = ui32Size;
}
//*****************************************************************************
//! This function requests transfer of data to the host on endpoint zero.
//!
//! \param pui8Data is a pointer to the buffer to send via endpoint zero.
//! \param ui32Size is the amount of data to send in bytes.
//!
//! This function handles sending data to the host when a custom command is
//! issued or non-standard descriptor has been requested on endpoint zero. If
//! the application needs notification when this is complete,
//! <tt>psCallbacks->pfnDataSent</tt> in the tDevice structure must
//! contain a valid function pointer. This callback could be used to free up
//! the buffer passed into this function in the \e pui8Data parameter. The
//! contents of the \e pui8Data buffer must remain unchanged until the
//! <tt>pfnDataSent</tt> callback is received.
//*****************************************************************************
void tDevice::SendDataEP0(const uint8_t *pui8Data, unsigned size) {
// Return the externally provided device descriptor.
pui8EP0Data = const_cast<uint8_t*>(pui8Data);
// The size of the device descriptor is in the first byte.
ui32EP0DataRemain = size;
// Now in the transmit data state.
EP0StateTx();
}
void tDevice::SendDataEP0(const uint8_t *data, unsigned size, const tRequest&rq) {
rq.limitLength(size);
SendDataEP0(data, size);
}
//*****************************************************************************
//! This function generates a stall condition on endpoint zero.
//!
//! This function is typically called to signal an error condition to the host
//! when an unsupported request is received by the device. It should be
//! called from within the callback itself (in interrupt context) and not
//! deferred until later since it affects the operation of the endpoint zero
//! state machine in the USB library.
//*****************************************************************************
void tDevice::StallEP0() {
// Stall the endpoint 0
USBDevEndpointStall(USB_BASE, 0, USB_EP_DEV_OUT);
// Enter the stalled state.
iEP0State = eUSBStateStall;
}
void tDevice::EnumResetHandler() {
// Disable remote wake up signaling (as per USB 2.0 spec 9.1.1.6).
ui8Status &= ~2; // USB_STATUS_REMOTE_WAKE
// Call the device dependent code to indicate a bus reset has occurred.
cbResetHandler();
// Reset the default configuration identifier and alternate function selections.
ui32Configuration = 0;
}
//*****************************************************************************
// This internal function reads a request data packet and dispatches it to
// either a standard request handler or the registered device request
// callback depending upon the request type.
//*****************************************************************************
void tDevice::ReadAndDispatchRequest() {
// Cast the buffer to a request structure.
uint8_t DataBufferIn[64];
const tRequest&rq = *(tRequest*)DataBufferIn;
// Set the buffer size.
uint32_t ui32Size = 64;
// Get the data from the USB controller end point 0.
USBEndpointDataGet(USB_BASE, 0, DataBufferIn,&ui32Size);
// If there was a null setup packet then just return.
if (!ui32Size) return;
uint8_t answer[2]={0,0};
switch (rq.wRequest()) {
case 0x0080: { // GetStatus-Device
answer[0]=ui8Status;
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,2);
}return;
case 0x0081: { // GetStatus-Interface(wIndexL)
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,2);
}return;
case 0x0082: { // GetStatus-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, false);
//TODO: Stall-Zustand von EP1IN abfragen
SendDataEP0(answer,2);
}return;
case 0x0100: { // ClearFeature-Device
if (rq.wValueL != 1) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
ui8Status &= ~2; // Clear the remote wake up state.
}return;
case 0x0102: { // ClearFeature-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall; // Nur EP1IN
if (rq.wValueL != 0) goto stall; // nur HALT
USBDevEndpointDataAck(USB_BASE, 0, true);
USBDevEndpointStallClear(USB_BASE,1<<4,USB_EP_DEV_IN);
USBEndpointDataToggleClear(USB_BASE,1<<4,USB_EP_DEV_IN);
}return;
case 0x0300: { // SetFeature-Device
if (rq.wValueL != 1) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
ui8Status |= 2; // Set the remote wake up state
}return;
case 0x0302: { // SetFeature-Endpoint(wIndexL)
if (rq.wIndexL != 0x81) goto stall; // Nur EP1IN
if (rq.wValueL != 0) goto stall; // nur HALT
USBDevEndpointDataAck(USB_BASE, 0, true);
USBDevEndpointStall(USB_BASE,1<<4,USB_EP_DEV_IN);
}return;
case 0x0500: { // SetAddress-Device
USBDevEndpointDataAck(USB_BASE, 0, true);
// Save the device address as we cannot change address until the status phase is complete.
ui32DevAddress = rq.wValueL | 0x80;
// Transition directly to the status state since there is no data phase for this request.
iEP0State = eUSBStateStatus;
}return;
case 0x0680: // GetDescriptor-Device (für HID-Deskriptor aus UsbTreeView)
case 0x0681: { // GetDescriptor-Interface (für HID-Deskriptor aus Windows)
// Need to ACK the data on end point 0 without setting last data as there will be a data phase.
USBDevEndpointDataAck(USB_BASE, 0, false);
switch (rq.wValueH) {
case 1: { // device descriptor
const uint8_t*d = desc.device;
SendDataEP0(d,d[0],rq);
}return;
case 2: { // configuration descriptor
const uint8_t*d = desc.config;
SendDataEP0(d, d[2] | d[3]<<8, rq);
}return;
case 3: switch (rq.wValueL) { // string descriptor
case 0:
case 1:
case 2: {
const uint8_t*d = ppui8StringDescriptors[rq.wValueL];
SendDataEP0(d,d[0],rq);
}return;
}goto stall;
default: if (cbGetDescriptor(rq)) return;
}
}goto stall;
case 0x0700: goto stall; // SetDescriptor-Device
case 0x0880: { // GetConfig-Device
USBDevEndpointDataAck(USB_BASE, 0, false);
answer[0] = ui32Configuration;
SendDataEP0(answer,1);
}return;
case 0x0900: { // SetConfig-Device
if (rq.wValueL>1) goto stall; // nur 0 oder 1 erlaubt
USBDevEndpointDataAck(USB_BASE, 0, true);
if (ui32Configuration = rq.wValueL) {
// Konfiguriere EP1IN
USBDevEndpointConfigSet(USB_BASE,1<<4,64,USB_EP_MODE_INT/*|USB_EP_AUTO_SET*/);
// EP0 verwendet die ersten 64 Bytes, EP1 kommt danach:
USBFIFOConfigSet(USB_BASE,1<<4,64,64,USB_EP_DEV_IN);
}
// If there is a configuration change callback then call it.
cbConfigChange(ui32Configuration);
}return;
case 0x0A81: { // GetInterface-Interface(wIndexL)
if (rq.wIndexL) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, false);
SendDataEP0(answer,1); // Einfaches Beispiel hat nur eine Alternate Setting
}return;
case 0x0B01: { // SetInterface-Interface(wIndexL)
// bmRequestType=0x01, wIndexL=Interface, wValueL=AltSetting, wLength=0
if (rq.wIndexL) goto stall;
if (rq.wValueL) goto stall;
USBDevEndpointDataAck(USB_BASE, 0, true);
}return;
case 0x0C82: goto stall; // SyncFrame-Endpoint(wIndexL)
// bmRequestType=0x82, wIndexL=Endpoint, wLength=2: Antwort = FrameNumber
default: if (cbRequestHandler(rq)) return;
}
stall:
StallEP0();
}
//*****************************************************************************
// This is interrupt handler for endpoint zero.
//
// This function handles all interrupts on endpoint zero in order to maintain
// the state needed for the control endpoint on endpoint zero. In order to
// successfully enumerate and handle all USB standard requests, all requests
// on endpoint zero must pass through this function. The endpoint has the
// following states: \b eUSBStateIdle, \b eUSBStateTx, \b eUSBStateRx,
// \b eUSBStateStall, and \b eUSBStateStatus. In the \b eUSBStateIdle
// state the USB controller has not received the start of a request, and once
// it does receive the data for the request it will either enter the
// \b eUSBStateTx, \b eUSBStateRx, or \b eUSBStateStall depending on the
// command. If the controller enters the \b eUSBStateTx or \b eUSBStateRx
// then once all data has been sent or received, it must pass through the
// \b eUSBStateStatus state to allow the host to acknowledge completion of
// the request. The \b eUSBStateStall is entered from \b eUSBStateIdle in
// the event that the USB request was not valid. Both the \b eUSBStateStall
// and \b eUSBStateStatus are transitional states that return to the
// \b eUSBStateIdle state.
//
// \return None.
//
// eUSBStateIdle -*--> eUSBStateTx -*-> eUSBStateStatus -*->eUSBStateIdle
// | | |
// |--> eUSBStateRx |
// | |
// |--> eUSBStateStall ---------->--------
//
// ----------------------------------------------------------------
// | Current State | State 0 | State 1 |
// | --------------------|-------------------|----------------------
// | eUSBStateIdle | eUSBStateTx/RX | eUSBStateStall |
// | eUSBStateTx | eUSBStateStatus | |
// | eUSBStateRx | eUSBStateStatus | |
// | eUSBStateStatus | eUSBStateIdle | |
// | eUSBStateStall | eUSBStateIdle | |
// ----------------------------------------------------------------
//
//*****************************************************************************
void tDevice::DeviceEnumHandler() {
uint32_t ui32EPStatus, ui32DataSize;
// Get the end point 0 status.
ui32EPStatus = USBEndpointStatus(USB_BASE, USB_EP_0);
switch(iEP0State) {
// Handle the status state, this is a transitory state from
// eUSBStateTx or eUSBStateRx back to eUSBStateIdle.
case eUSBStateStatus: {
// Just go back to the idle state.
iEP0State = eUSBStateIdle;
// If there is a pending address change then set the address.
if (ui32DevAddress & 0x80) {
// Clear the pending address change and set the address.
ui32DevAddress &= ~0x80;
USBDevAddrSet(USB_BASE, ui32DevAddress);
}
// If a new packet is already pending, we need to read it
// and handle whatever request it contains.
if (ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) {
// Process the newly arrived packet.
ReadAndDispatchRequest();
}
}break;
// In the IDLE state the code is waiting to receive data from the host.
case eUSBStateIdle: {
// Is there a packet waiting for us?
if (ui32EPStatus & USB_DEV_EP0_OUT_PKTRDY) {
// Yes - process it.
ReadAndDispatchRequest();
}
}break;
// Data is still being sent to the host so handle this in the
// EP0StateTx() function.
case eUSBStateTx: {
EP0StateTx();
}break;
// We are still in the middle of sending the configuration descriptor
// so handle this in the EP0StateTxConfig() function.
// USBDEP0StateTxConfig();
// }break;
// Handle the receive state for commands that are receiving data on
// endpoint zero.
case eUSBStateRx: {
// Set the number of bytes to get out of this next packet.
ui32DataSize = ui32EP0DataRemain > 64 ? 64 : ui32EP0DataRemain;
// Get the data from the USB controller end point 0.
USBEndpointDataGet(USB_BASE, USB_EP_0, pui8EP0Data, &ui32DataSize);
// If there we not more that EP0_MAX_PACKET_SIZE or more bytes
// remaining then this transfer is complete. If there were exactly
// EP0_MAX_PACKET_SIZE remaining then there still needs to be
// null packet sent before this is complete.
if (ui32EP0DataRemain < 64) {
// Return to the idle state.
iEP0State = eUSBStateStatus;
// Need to ACK the data on end point 0 in this case and set the
// data end as this is the last of the data.
USBDevEndpointDataAck(USB_BASE, USB_EP_0, true);
}else{
// Need to ACK the data on end point 0 in this case
// without setting data end because more data is coming.
USBDevEndpointDataAck(USB_BASE, USB_EP_0, false);
}
// Advance the pointer.
pui8EP0Data += ui32DataSize;
// Decrement the number of bytes that are being waited on.
ui32EP0DataRemain -= ui32DataSize;
}break;
// The device stalled endpoint zero so check if the stall needs to be
// cleared once it has been successfully sent.
case eUSBStateStall: {
// If we sent a stall then acknowledge this interrupt.
if (ui32EPStatus & USB_DEV_EP0_SENT_STALL) {
// Clear the Setup End condition.
USBDevEndpointStatusClear(USB_BASE, USB_EP_0,
USB_DEV_EP0_SENT_STALL);
// Reset the global end point 0 state to IDLE.
iEP0State = eUSBStateIdle;
}
}break;
// Halt on an unknown state, but only in DEBUG mode builds.
default: ASSERT(0);
}
}
//*****************************************************************************
// The internal USB device interrupt handler.
//
// \param ui32Status is the current interrupt status as read via a call to
// USBIntStatus(). This is the value of USBIS.
// \param ui32IntStatusEP is the current interrupt status of the endpoint
// as read from USBIntStatus(). This is the value of RXIS and TXIS.
//
// This function is called from either \e USB0DualModeIntHandler() or
// \e USB0DeviceIntHandler() to process USB interrupts when in device mode.
// This handler will branch the interrupt off to the appropriate application or
// stack handlers depending on the current status of the USB controller.
//
// The two-tiered structure for the interrupt handler ensures that it is
// possible to use the same handler code in both device and OTG modes and
// means that host code can be excluded from applications that only require
// support for USB device mode operation.
//
//*****************************************************************************
void tDevice::IntHandlerInternal(uint32_t ui32Status, uint32_t ui32IntStatusEP) {
// static uint32_t ui32SOFDivide = 0;
uint32_t ui32DMAIntStatus;
// If device initialization has not been performed then just disconnect
// from the USB bus and return from the handler.
if (!this) {
USBDevDisconnect(USB_BASE);
return;
}
// Received a reset from the host.
if (ui32Status & USB_INTCTRL_RESET) {
EnumResetHandler();
}
// Suspend was signaled on the bus.
if (ui32Status & USB_INTCTRL_SUSPEND) {
// Call the SuspendHandler() if it was specified.
// cbSuspendHandler();
}
// Resume was signaled on the bus.
if (ui32Status & USB_INTCTRL_RESUME) {
// Call the ResumeHandler() if it was specified.
// cbResumeHandler();
}
// USB device was disconnected.
if (ui32Status & USB_INTCTRL_DISCONNECT) {
// Call the DisconnectHandler() if it was specified.
cbDisconnectHandler();
}
// Start of Frame was received.
if (ui32Status & USB_INTCTRL_SOF) {
// Increment the global Start of Frame counter.
// g_ui32USBSOFCount++;
// Increment our SOF divider.
// ui32SOFDivide++;
// Handle resume signaling if required.
// ResumeTickHandler();
// Have we counted enough SOFs to allow us to call the tick function?
// if (ui32SOFDivide == USB_SOF_TICK_DIVIDE) {
// Yes - reset the divider and call the SOF tick handler.
// ui32SOFDivide = 0;
// InternalUSBStartOfFrameTick(USB_SOF_TICK_DIVIDE);
// }
}
// Use the controller interrupt status from ui32IntStatusEP.
// This is made up of the values of RXIS and TXIS.
ui32Status = ui32IntStatusEP;
// Handle end point 0 interrupts.
if (ui32Status & USB_INTEP_0) {
DeviceEnumHandler();
ui32Status &= ~USB_INTEP_0;
}
// Check to see if any DMA transfers are pending
ui32DMAIntStatus = psDMAInstance->IntStatus();
if (ui32DMAIntStatus) {
// Handle any DMA interrupt processing.
psDMAInstance->IntHandler(ui32DMAIntStatus);
}
// Because there is no way to detect if a uDMA interrupt has occurred,
// check for an endpoint callback and call it if it is available.
if (ui32Status || ui32DMAIntStatus) {
cbEndpointHandler(ui32Status);
}
}
void tDevice::USB0DeviceIntHandler() {
uint32_t ui32Status;
uint32_t ui32IntStatusEP;
// Get the controller interrupt status.
ui32Status = USBIntStatus(USB_BASE, &ui32IntStatusEP);
// Call the internal handler.
g_UsbDevice->IntHandlerInternal(ui32Status, ui32IntStatusEP);
}
}
Vorgefundene Kodierung: UTF-8 | 0
|