#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;
}
}
Vorgefundene Kodierung: UTF-8 | 0
|