Source file: /~heha/ewa/Motor/Maxmaus6.zip/usblib/usbd.cpp

// 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);
}
}
Detected encoding: UTF-80