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

#include "usbd.h"
#include <usb.h>
extern "C" {
#include <debug.h>	// ASSERT
}

namespace usb{

bool tDevice::Init() {
  config = 0;
  devstat = 0;

  USBDevMode(BASE);        // Force device mode
  USBIntStatusControl(BASE);
  USBIntStatusEndpoint(BASE);
  USBIntEnableControl(BASE,
		  USB_INTCTRL_RESET
		| USB_INTCTRL_DISCONNECT
		| USB_INTCTRL_RESUME
		| USB_INTCTRL_SUSPEND
		| USB_INTCTRL_SOF);
  USBIntEnableEndpoint(BASE, USB_INTEP_ALL);
  USBDevConnect(BASE);
  return true;
}

void tDevice::Term() {
  USBIntDisableControl(BASE, USB_INTCTRL_ALL);
  USBIntDisableEndpoint(BASE, USB_INTEP_ALL);
  USBDevDisconnect(BASE);
  USBIntStatusControl(BASE);
  USBIntStatusEndpoint(BASE);
}

void tDevice::SendDataEP0(const uint8_t *data, unsigned size) {
  while (size) {
    unsigned sz=size>64?64:size;
    if (USBEndpointDataPut(BASE, 0, const_cast<uint8_t*>(data), sz)) {
      idle();
      if (USBIntStatusControl(BASE)&USB_INTCTRL_RESET) return;
    }
    USBEndpointDataSend(BASE, 0, sz==64 ? USB_TRANS_IN : USB_TRANS_IN_LAST);
    data+=sz;
    size-=sz;
  }
}

void tDevice::SendDataEP0(const uint8_t *data, unsigned size, const tRequest&rq) {
  rq.limitLength(size);
  SendDataEP0(data, size);
}

void tDevice::pollEP0() {
  tRequest rq;
  uint32_t ui32Size = sizeof rq;
    // Get the data from the USB controller end point 0.
  USBEndpointDataGet(BASE, 0, rq,&ui32Size);
    // If there was a null setup packet then just return.
  if (!ui32Size) return;
  USBDevEndpointDataAck(BASE, 0, !(rq.bmRequestType&0x80));
  uint8_t answer[2]={0,0};
  switch (rq.wRequest()) {
    case 0x0080: {	// GetStatus-Device
      answer[0]=devstat;
      SendDataEP0(answer,2);
    }return;
    case 0x0081: {	// GetStatus-Interface(wIndexL)
      SendDataEP0(answer,2);
    }return;
    case 0x0082: {	// GetStatus-Endpoint(wIndexL)
      if (rq.wIndexL != 0x81) goto stall;
      //TODO: Stall-Zustand von EP1IN abfragen
      SendDataEP0(answer,2);
    }return;

    case 0x0100: {	// ClearFeature-Device
      if (rq.wValueL != 1) goto stall;
      devstat &= ~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
      USBDevEndpointStallClear(BASE,1<<4,USB_EP_DEV_IN);
      USBEndpointDataToggleClear(BASE,1<<4,USB_EP_DEV_IN);
    }return;

    case 0x0300: {	// SetFeature-Device
      if (rq.wValueL != 1) goto stall;
      devstat |= 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
      USBDevEndpointStall(BASE,1<<4,USB_EP_DEV_IN);
    }return;

    case 0x0500: {	// SetAddress-Device
      while (!(USBIntStatusEndpoint(BASE)&USB_INTEP_0)) {
	idle();
        if (USBIntStatusControl(BASE)&USB_INTCTRL_RESET) return;
      };
      USBDevAddrSet(BASE,rq.wValueL);
    }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.
      if (cbGetDescriptor(rq)) return;
    }goto stall;

    case 0x0700: goto stall;	// SetDescriptor-Device

    case 0x0880: {	// GetConfig-Device
      answer[0] = config;
      SendDataEP0(answer,1);
    }return;

    case 0x0900: {	// SetConfig-Device
      if (rq.wValueL>1) goto stall;	// nur 0 oder 1 erlaubt
      if ((config = rq.wValueL)) {
      // Konfiguriere EP1IN
        USBDevEndpointConfigSet(BASE,1<<4,64,USB_EP_MODE_INT/*|USB_EP_AUTO_SET*/);
      // EP0 verwendet die ersten 64 Bytes, EP1 kommt danach:
        USBFIFOConfigSet(BASE,1<<4,64,64,USB_EP_DEV_IN);
      }
    }return;

    case 0x0A81: {	// GetInterface-Interface(wIndexL)
      if (rq.wIndexL) goto stall;
      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;
    }return;

    case 0x0C82: goto stall;	// SyncFrame-Endpoint(wIndexL)
	// bmRequestType=0x82, wIndexL=Endpoint, wLength=2: Antwort = FrameNumber
    default: if (cbRequestHandler(rq)) return;
  }
stall:
  USBDevEndpointStall(BASE, 0, USB_EP_DEV_OUT);
}


void tDevice::poll() {
  uint32_t IntStatusEP,sta = USBIntStatus(BASE,&IntStatusEP);
  if (sta & USB_INTCTRL_RESET) onReset();
  if (sta & USB_INTCTRL_DISCONNECT) onDisconnect();
  if (sta & USB_INTCTRL_SOF) onSOF();
  if (IntStatusEP & USB_INTEP_0) {
    uint32_t sta = USBEndpointStatus(BASE, 0);
    if (sta & USB_DEV_EP0_OUT_PKTRDY) pollEP0();
//        USBDevEndpointDataAck(BASE, USB_EP_0, true);
    if (sta & USB_DEV_EP0_SENT_STALL)
     USBDevEndpointStatusClear(BASE, USB_EP_0, USB_DEV_EP0_SENT_STALL);
  }
}

// Report-Deskriptor für eine (bootfähige) Maus
static const uint8_t ReportDesc[] = {
  0x05,1,	// UsagePage(GENERIC_DESKTOP)
  0x09,2,	// Usage(MOUSE),
  0xA1,1,	// Collection(APPLICATION)
   0x09,1,	// Usage(POINTER)
   0xA1,0,	// Collection(Physical)
// 3 Knöpfe
    0x05,9,	// UsagePage(BUTTONS)
    0x19,1,	// UsageMinimum(1)
    0x29,3,	// UsageMaximum(3)
    0x15,0,	// LogicalMinimum(0)
    0x25,1,	// LogicalMaximum(1)
    0x75,1,	// ReportSize(1)
    0x95,3,	// ReportCount(3)
    0x81,2,	// Input(VAR,ABS)
    0x75,5,	// ReportSize(5)
    0x95,1,	// ReportCount(1)
    0x81,3, 	// Input(CONST|ARY|ABS)
// X- und Y-Achse
    0x05,1,	// UsagePage(GENERIC_DESKTOP)
    0x09,0x30,	// Usage(X),
    0x09,0x31,	// Usage(Y),
    0x15,(uint8_t)-127,	// LogicalMinimum(-127)
    0x25,127,	// LogicalMaximum(127)
    0x75,8,	// ReportSize(8)
    0x95,2,	// ReportCount(2)
    0x81,6,	// Input(DATA|VAR|REL)
   0xC0,	//  EndCollection
  0xC0,		//  EndCollection
};

// Device-Deskriptor
static const uint8_t DeviceDesc[] = {
  18,		// Size of this structure.
  1,		// Type of this structure = DTYPE_DEVICE
  USB2(0x110),	// USB version 1.1
  0,		// USB Device Class
  0,		// USB Device Sub-class
  0,		// USB Device protocol
  64,		// Maximum packet size for default pipe.
  USB2(0x1CBE),	// Vendor ID = Texas Instruments
  USB2(0),	// Product ID = TI Mouse
  USB2(0x100),	// Device Version BCD.
  1,		// Manufacturer string identifier.
  2,		// Product string identifier.
  0,		// Product serial number.
  1,		// Number of configurations.
};

// configuration descriptor.
static const uint8_t ConfigDesc[] = {
  9,		// bLength
  2,		// USB_DTYPE_CONFIGURATION
  USB2(9+9+9+7),	// The total size of this full structure.
  1,		// bNumInterfaces
  1,		// bConfigurationValue
  0,		// iConfiguration
  0xC0,		// Bus Powered, Self Powered, remote wake up.
  50,		// The maximum power in 2mA increments.
// Interface Descriptor.
  9,                          // Size of the interface descriptor.
  4,	//USB_DTYPE_INTERFACE,        // Type of this descriptor.
  0,	// The index for this interface.
  0,	// The alternate setting for this interface.
  1,	// The number of endpoints used by this interface.
  3,	// USB_CLASS_HID,              // interface class
  0,	// interface sub-class.
  0,	// interface protocol for the sub-class specified above.
  0,	// The string index for this interface.
// HID descriptor
  9,		// bLength
  0x21,		// bDescriptorType = DTYPE_HID
  USB2(0x111),	// bcdHID (version 1.11 compliant)
  0,		// bCountryCode (not localized)
  1,		// bNumDescriptors
  0x22,		// Report descriptor = DTYPE_REPORT
  USB2(sizeof ReportDesc),  // Size of report descriptor
// Interrupt IN endpoint descriptor
  7,		// The size of the endpoint descriptor.
  5,		//USB_DTYPE_ENDPOINT,         // Descriptor type is an endpoint.
  0x81,		// EP1IN
  0x03,		// Endpoint is an interrupt endpoint.
  USB2(64),	// The maximum packet size.
  16,		// The polling interval for this endpoint.
};

bool tMyDevice::cbRequestHandler(const tRequest&rq) {
  switch (rq.wRequest()) {
    case 0x01A1: if (	// GET_REPORT, class-specific, target=interface
      rq.wIndexL == 0		// Interface 0
   && rq.wValueH == 0x01	// report_type = INPUT
   && rq.wValueL == 0) {	// report_id
    USBDevEndpointDataAck(BASE, USB_EP_0, true);
    SendDataEP0(inrep,3,rq);
    return true;
    }break;
    case 0x06A1: return cbGetDescriptor(rq);
  }
  return tDevice::cbRequestHandler(rq);	// chain down
}

static void buildStringDesc(uint8_t d[256],const char*s) {
  uint8_t*p=d+1;
  *p++=3;
  wchar_t c;
  while ((c=*s++)) {
    if (c&0x80) {
      if (c&0x20) {	// 3-Byte-UTF-8
        c<<=6;
        c|=*s++&0x3F;
      }else c&=0x3F;	// 2-Byte-UTF-8
      c<<=6;
      c|=*s++&0x3F;
    }
    *p++=c&0xFF;
    *p++=c>>8&0xFF;
  }
  ASSERT(p-d<256);
  *d=p-d;
}

bool tMyDevice::cbGetDescriptor(const tRequest&rq) {
  switch (rq.wValueH) {
    case 1: {	// device descriptor
      SendDataEP0(DeviceDesc, sizeof DeviceDesc, rq);
    }return true;
    case 2: {	// configuration descriptor
      SendDataEP0(ConfigDesc, sizeof ConfigDesc, rq);
    }return true;
    case 3: {
      uint8_t sd[256];
      switch (rq.wValueL) {	// string descriptor
// Bug+Workaround: C++2000 unterstützt weder L"\x0409" noch L"\u0409" richtig, nimmt nur Low-Byte
// Bug+Workaround: C++2000 unterstützt kein L"Ä", speichert UTF-8, daher UTF-8-Strings
        case 0: buildStringDesc(sd,"\xD0\x87"); break;	// 0x0407 deutsch
        case 1: buildStringDesc(sd,"Texas Instruments"); break;
        case 2: buildStringDesc(sd,"Mausbeispiel: ÄÖÜäöüß"); break;
        default: return false;
      }
      SendDataEP0(sd,sd[0],rq);
    }return true;
    case 0x21: {
      SendDataEP0(ConfigDesc+9+9, 9, rq);
    }return true;
    case 0x22: {
      SendDataEP0(ReportDesc, sizeof ReportDesc, rq);
    }return true;
  }
  return tDevice::cbGetDescriptor(rq);
}

bool tMyDevice::StateChange(uint8_t Buttons, int8_t DeltaX, int8_t DeltaY) {
  inrep[0]=Buttons;
  inrep[1]=(uint8_t)DeltaX;
  inrep[2]=(uint8_t)DeltaY;
  return SendReport(inrep,3);
}

bool tMyDevice::SendReport(uint8_t *data, unsigned len) {
  int32_t i32Retcode = USBEndpointDataPut(BASE,USB_EP_1,data,len);
  if (!i32Retcode) {
    i32Retcode = USBEndpointDataSend(BASE,USB_EP_1,USB_TRANS_IN);
  }
  return i32Retcode==0;
}

}
Detected encoding: UTF-80