/* originally based on W.S. VCP implenentation While it should be still compatible with the original one found at http://stefanfrings.de/stm32/STM32F103_usb_test.zip several changes are made to be more flexible. Tested on BluePill boards with STM and WCH controllers (F103XXX) Changes - put all cfg options at the beginning and create a config menue for uVisions Config Wizard - indroduce two header files for usb1.1 spec and cdc1.2 spec - introduce IAD which is supported since XP SP3 - remove func parameter in several Onxxx functions - changes in copy functions to and from usbmem - changes in SetAddress handling - init bulk & interrupt Eps after SetConfiguration(1) - use bidirectional EPs todo: - create a UsbStatus var which can be used to monitor the - status from user mode - prepare for more than one VCP port */ #include "usb_vcp.h" #include "usb11.h" //usb1.1 spec #include "usbcdc12.h" //cdc1.2 spec /* the following comment has to be within the first 100 lines to activate the config wizard in keil */ // <<< Use Configuration Wizard in Context Menu >>> /* Config params to setup by user 1. CORE params UMEMSHIFT <0|1> activate for devices that dont have continious usb memory USB_IRQ_NUMBER <0..31> check the datasheet for the correct value ENABLE_TRACING <0|1> activate to allow trace messages via SWO 2. USB params USB_VID VendorId for this device USB_PID ProductId for this device BCD_DEVICE Firmware Version 0x0100 -> v1.00 USB_SERIAL_NO <0|1> activate to use the 12 byte UID from the chip 3. VCP params VCP_NUM_PORTS <1..4> not yet implemmented -> EP1..EP4 */ // Configuration for STM bare metal VCP // CORE params // UMEMSHIFT <0=> off // <1=> on // Off for devices with 2 x 16 bits / uint16_t access schema // e.g. STM32L0x2, STM32L0x3, STM32F0x2, STM32F303xD and xE // On for devices with 1 x 16 bits / uint16_t access schema // e.g. STM32F103, STM32F302, STM32F303xB and xC // ********************************************************* #define UMEM_SHIFT 0 // ********************************************************* // USB_IRQ_NUMBER <0-31> // Take this number from the reference manual for your controller // ********************************************************* #define USB_IRQ_NUMBER 31 // ********************************************************* // ENABLE_TRACING <0=> false // <0=> true // Allow trace messages for debug via SWO // ********************************************************* #define ENABLE_TRACING 0 // ********************************************************* // // USB params // USB_VID <0-0xFFFF> // USB Vendor ID // ********************************************************* #define USB_VID 0x0416 // ********************************************************* // USB_PID <0-0xFFFF> // USB Prodct ID // ********************************************************* #define USB_PID 0x5011 // ********************************************************* // BCD_DEVICE <0-0xFFFF> // set the reported device revision 0x0100 -> 1.0 // ********************************************************* #define BCD_DEVICE 0x0100 // ********************************************************* // USB_SERIAL_NO <0=> none // <1=> use chip UID // activate if you want a USB serial number // ********************************************************* #define USB_SERIAL_NO 1 // ********************************************************* // // VCP params // VCP_NUM_PORTS <1-4> // ********************************************************* #define VCP_NUM_PORTS 1 //only 1 is supported at this time // ********************************************************* // <<< end of configuration section >>> // set UMEM_FAKEWIDTH acorting to UMEM_SHIFT #if UMEM_SHIFT #define UMEM_FAKEWIDTH uint32_t //read 32 bit but use only low 16bit #else #define UMEM_FAKEWIDTH uint16_t #endif // each port needs 2 interfaces #define MAX_INTERFACES (2*VCP_NUM_PORTS) #define VCP1_BULK 1 #define VCP2_BULK 2 #define VCP3_BULK 3 #define VCP4_BULK 4 #define VCP_STAT_EP 0x83 // for now just use this later set this to 0x85 #define EP0_SIZE 0x40 // can be 8 | 16 | 32 64 #define BULK_SIZE 0x40 // for usb1.1 bulksize always is 64 // The name of the IRQ handler must match startup_stm32.s #define NAME_OF_USB_IRQ_HANDLER USB_IRQHandler #if ENABLE_TRACING #include #define ITM_PORT0_8 (*(volatile char *)(0xE0000000UL)) #define ITM_PORT0_32 (*(volatile unsigned long *)(0xE0000000UL)) #define ITM_TER (*(volatile unsigned long *)(0xE0000E00UL)) #define ITM_TCR (*(volatile unsigned long *)(0xE0000E80UL)) static void trace(char *ptr) { while (*ptr) { if (((ITM_TCR & 1UL) != 0UL) && // ITM enabled ((ITM_TER & 1UL) != 0UL)) // ITM and port 0 enabled { while (ITM_PORT0_32 == 0UL) { asm volatile("NOP"); } ITM_PORT0_8 = *ptr; } ptr++; } } #else #define trace(msg) // nothing #endif #if USB_SERIAL_NO #define U_ID_SIZE 12 // in bytes // todo extend this for other controllers //#define U_ID (*(volatile uint8_t *)( 0x1FFFF7E8UL)) //for F103 only #define U_ID ((volatile uint8_t *)( 0x1FFFF7ACUL)) //for F042 uint8_t SerialString[U_ID_SIZE * 2 * sizeof(uint16_t) +2]; uint8_t toAscii (uint8_t digit) { digit &= 0x0F; return ((digit < 10) ? digit + '0' : digit +('A'- 10)); } void InitSerial(void) { uint8_t *s = &SerialString[0]; uint8_t b; int i; for (i=0; i < sizeof(SerialString); i++) SerialString[i]=0; //memset *s++ = U_ID_SIZE *2 * sizeof(uint16_t) +2; *s++ = USB_STRING_DESCRIPTOR; i = U_ID_SIZE-1; //0..11 while(i > -1) //r�ckw�rts lesen { b = *(U_ID + i); *s = toAscii(b); s += 2; *s = toAscii(b >> 4); s += 2; i--; } } #else #define InitSerial() // nothing #endif // VCP buffers todo still only one VCP Port #define VCPBUF_SIZE 512 #define VCPBUF_MAX (VCPBUF_SIZE-1) volatile char UsbTxBuf1[VCPBUF_SIZE]; volatile char UsbRxBuf1[VCPBUF_SIZE]; volatile int rxr1, rxw1; volatile int txr1, txw1; volatile bool receiving; volatile bool transmitting; /* Cortex-M NVIC Register */ #define NVIC_ISER (*(volatile uint32_t (*) [16])(0xE000E100)) #define NVIC_ICER (*(volatile uint32_t (*) [16])(0xE000E180)) /* All USB register are 16 bit wide but have to be accessed as 32 bit. */ /* USB device (base address 0x4000 5C00) */ #define USB_BASE 0x40005C00 #define USB_EpRegs(x) (*(volatile uint32_t *)(USB_BASE + 4*(x))) #define USB_EP0R (*(volatile uint32_t *)(USB_BASE + 0x00)) #define USB_EP1R (*(volatile uint32_t *)(USB_BASE + 0x04)) #define USB_EP2R (*(volatile uint32_t *)(USB_BASE + 0x08)) #define USB_EP3R (*(volatile uint32_t *)(USB_BASE + 0x0C)) #define USB_EP4R (*(volatile uint32_t *)(USB_BASE + 0x10)) #define USB_EP5R (*(volatile uint32_t *)(USB_BASE + 0x14)) #define USB_EP6R (*(volatile uint32_t *)(USB_BASE + 0x18)) #define USB_EP7R (*(volatile uint32_t *)(USB_BASE + 0x1C)) #define USB_CNTR (*(volatile uint32_t *)(USB_BASE + 0x40)) #define USB_ISTR (*(volatile uint32_t *)(USB_BASE + 0x44)) #define USB_FNR (*(volatile uint32_t *)(USB_BASE + 0x48)) #define USB_DADDR (*(volatile uint32_t *)(USB_BASE + 0x4C)) #define USB_BTABLE (*(volatile uint32_t *)(USB_BASE + 0x50)) //STAT_RX & STAT_TX #define EP_DISABLE 0 #define EP_STALL 1 #define EP_NAK 2 #define EP_ACTIVE 3 #define TYP_BULK 0 #define TYP_CTL 1 #define TYP_ISO 2 #define TYP_IRQ 3 /* Bits in USB_CNTR */ #define FRES_ (1<<0) #define PDWN_ (1<<1) #define LP_MODE_ (1<<2) #define FSUSP_ (1<<3) #define RESUME_ (1<<4) #define ESOFM_ (1<<8) #define SOFM_ (1<<9) #define RESETM_ (1<<10) #define SUSPM_ (1<<11) #define WKUPM_ (1<<12) #define ERRM_ (1<<13) #define PMAOVRM_ (1<<14) #define CTRM_ (1<<15) /* Bits in USB_ISTR */ #define DIR_ (1<<4) #define ESOF_ (1<<8) #define SOF_ (1<<9) #define RESET_ (1<<10) #define SUSP_ (1<<11) #define WKUP_ (1<<12) #define ERR_ (1<<13) #define PMAOVR_ (1<<14) #define CTR_ (1<<15) /* Bits in den USB_EPnR */ #define CTR_RX_ (1<<15) #define DTOG_RX_ (1<<14) #define STAT_RX_ (3<<12) #define SETUP_ (1<<11) #define EP_TYPE_ (3<<9) #define EP_KIND_ (1<<8) #define CTR_TX_ (1<<7) #define DTOG_TX_ (1<<6) #define STAT_TX_ (3<<4) #define MASK_EA_ (15) /* EndPoint Register Mask (No Toggle Fields) */ #define EP_NoToggleBits (CTR_RX_|SETUP_|EP_TYPE_|EP_KIND_|CTR_TX_|MASK_EA_) /******* Zuordnung physischer Endpunkte 0..7 ********************/ //#define logEpCtrl 0 #define logEpBulkIn 1 #define logEpBulkOut 1 #define logEpInt 3 /* f�r Stall, Unstall usw. */ #define physEpCtrlIn 0x80 #define physEpCtrlOut 0x00 #define physEpBulkIn 0x81 #define physEpBulkOut 0x01 #define physEpIntIn 0x83 #define physEpIntOut 0x03 /* Attention: Accessing USB mem differs depending on the exact controllers some old controllers like the F103 implement the USB mem as 16Bit which results in gaps when using the required 32 bit transfers. Example 'Hello-World' 48 65 00 00 6C 6C 00 00 6F 2D 00 00 57 6F 00 00 72 6C 00 00 64 ... H e l l o - W o r l d ... The copy routines have to pay attention of this and therefore no simple memcpy can be used. See also UMEM_SHIFT and UMEM_FAKEWIDTH defines. */ #define USB_RAM 0x40006000 #define EpCtrlLenId ((1<<15)|(1<<10)) #define EpBulkLenId ((1<<15)|(1<<10)) #define EpIntMaxLen 8 #define EpIntLenId (4<<10) /* EP0 = control */ #define Ep0InOffset 0x0000 /* 64 Bytes ab 0 */ #define Ep0OutOffset 0x0040 /* 64 Bytes ab 64 */ #define Ep1InOffset 0x0080 /* 64 Bytes ab 0 */ #define Ep1OutOffset 0x00C0 /* 64 Bytes ab 64 */ #define Ep2InOffset 0x0100 /* 64 Bytes ab 0 */ #define Ep2OutOffset 0x0140 /* 64 Bytes ab 64 */ //old /* EP1 = Bulk-IN */ #define Ep1TxAOffset 0x0080 /* 64 Bytes ab 128 */ #define Ep1TxBOffset 0x00C0 /* 64 Bytes ab 192 */ /* EP2 = Bulk-OUT */ #define Ep2RxAOffset 0x0100 /* 64 Bytes ab 256 */ #define Ep2RxBOffset 0x0140 /* 64 Bytes ab 320 */ /* EP3 = Int (unbenutzt) */ #define Ep3TxOffset 0x0180 /* 8 Bytes ab 384 */ #define Ep3RxOffset 0x0188 /* 8 Bytes ab 392 */ /* EP-Tafel */ #define EpTableOffset 0x0190 /* 64 Bytes ab 400 */ #define EP0InBuffer ((uint8_t*) (USB_RAM + (Ep0InOffset<>8, // usb 1.1 USB_CLASS_UNDEFINED, // no class or subclass USB_SUBCLASS_UNDEFINED, // its done at interface level USB_PROTOCOL_UNDEFINED, EP0_SIZE, // can be 8 | 16 | 32 | 64 USB_VID & 0xFF, USB_VID >> 8, USB_PID & 0xFF, USB_PID >> 8, BCD_DEVICE & 0xFF, BCD_DEVICE >> 8, 0x01, // Vendor string 0x02, // Device string #if USB_SERIAL_NO 0x03, // Serial String #else 0x00, #endif 0x01 // one Configuration }; const uint8_t ConfigDesc[9+ (VCP_NUM_PORTS *66)] = { sizeof(ConfigurationDescriptor), USB_CONFIGURATION_DESCRIPTOR, sizeof(ConfigDesc), // wTotalLength sizeof(ConfigDesc) >> 8, MAX_INTERFACES, 0x01, // bConfigurationValue USB_STRING_UNDEFINED, 0xC0, // selpowered 100/2, // 100mA low power device sizeof(IadDescriptor), USB_IAD_DESCRIPTOR, 0, // first interface = 0 2, // 2 interfaces USB_CLASS_CDC, CDC_SUBCLASS_ABSTRACT, CDC_PROTOCOL_V250, USB_STRING_UNDEFINED, /* INTERFACE descriptor */ sizeof(InterfaceDescriptor), USB_INTERFACE_DESCRIPTOR, 0x00, /* bInterfaceNumber */ 0x00, /* bAlternateSetting */ 0x01, /* bNumEndpoints */ USB_CLASS_CDC, CDC_SUBCLASS_ABSTRACT, CDC_PROTOCOL_V250, USB_STRING_UNDEFINED, /* Communication Class Specified INTERFACE descriptor */ 0x05, CDC_CS_INTERFACE, CDC_SUB_HEADER, BCD_CDC &0xFF,BCD_CDC >>8, /* Communication Class Specified INTERFACE descriptor */ 0x05, CDC_CS_INTERFACE, CDC_SUB_CALL_FUNCTION, 0x00, /* BIT0: Whether device handle call management itself. */ /* BIT1: Whether device can send/receive call */ /* management information over a Data Class Interface 0 */ 0x01, // Interface number of data class interface // Communication Class Specified INTERFACE descriptor 0x04, CDC_CS_INTERFACE, CDC_SUB_ABSTRACT_CTRL, 0x00, // bmCapabilities none // Communication Class Specified INTERFACE descriptor 0x05, CDC_CS_INTERFACE, CDC_SUB_UNION_FUNCTIONAL, 0x00, /* bMasterInterface */ 0x01, /* bSlaveInterface0 */ // ENDPOINT descriptor f�r Interrupt sizeof(EndpointDescriptor), USB_ENDPOINT_DESCRIPTOR, VCP_STAT_EP, EP_INTERRUPT, EpIntMaxLen, 0x00, 0x01, // INTERFACE descriptor for send & recive sizeof(InterfaceDescriptor), USB_INTERFACE_DESCRIPTOR, 0x01, // interface no 1 0x00, // no alternaze settings 0x02, // 2 Enpoints CDC_CLASS_DATA, // data interface CDC_SUBCLASS_RESERVED, // has no subclass or protocol CDC_PROTOCOL_UNDEFINED, CDC_PROTOCOL_UNDEFINED, /* ENDPOINT descriptor f�r Bulk IN */ sizeof(EndpointDescriptor), USB_ENDPOINT_DESCRIPTOR, HOST_ | VCP1_BULK, EP_BULK, BULK_SIZE & 0xFF, BULK_SIZE>>8, 0, /* ENDPOINT descriptor f�r Bulk OUT */ sizeof(EndpointDescriptor), USB_ENDPOINT_DESCRIPTOR, DEVICE_ | VCP1_BULK, EP_BULK, BULK_SIZE & 0xFF, BULK_SIZE>>8, 0 }; const uint8_t LangString[4] = { 4, USB_STRING_DESCRIPTOR, 0x09, 0x04 /* Language ID: USA(0x0409) */ }; const uint8_t VendorString[16] = { sizeof(VendorString), USB_STRING_DESCRIPTOR, 'N', 0, 'u', 0, 'v', 0, 'o', 0, 't', 0, 'o', 0, 'n', 0 }; const uint8_t ProductString[32] = { sizeof(ProductString), USB_STRING_DESCRIPTOR, 'U', 0, 'S', 0, 'B', 0, ' ', 0, 'V', 0, 'i', 0, 'r', 0, 't', 0, 'u', 0, 'a', 0, 'l', 0, ' ', 0, 'C', 0, 'O', 0, 'M', 0 }; /* um Nullbyte oder ein leeres Paket senden zu k�nnen */ const uint8_t always0 = 0; /************ Hilfsroutinen ************************************************/ void EnableUsbIRQ (void) { NVIC_ISER[USB_IRQ_NUMBER/32] = ((uint32_t) 1) << (USB_IRQ_NUMBER % 32); } // Not used void DisableUsbIRQ (void) { NVIC_ICER[USB_IRQ_NUMBER/32] = ((uint32_t) 1) << (USB_IRQ_NUMBER % 32); } void Stall(int physEpNum) { trace("stall\n"); uint32_t D, S, Maske; int logEpNum; logEpNum = physEpNum & 0x0F; if (logEpNum == physEpNum) { Maske = EP_NoToggleBits | STAT_RX_; // ohne STAT_TX und ohne beide DTOG_x S = 1 << 12; } else { Maske = EP_NoToggleBits | STAT_TX_; // ohne STAT_RX und ohne beide DTOG_x S = 1 << 4; } D = USB_EpRegs(logEpNum); USB_EpRegs(logEpNum) = (D ^ S) & Maske; } void UnStall_In(int physEpNum) { uint32_t reg; uint32_t mask; uint32_t status; mask = EP_NoToggleBits; status = (USB_EpRegs(physEpNum & 0x07) >> 4) & 0x03; if (status==EP_STALL) //In stalled? { // toggle bit 5:4 01 -> 10 reg = USB_EpRegs(physEpNum & 0x07) & mask; USB_EpRegs(physEpNum & 0x07) = (reg | (3 << 4)); } } void UnStall_Out(int physEpNum) { uint32_t reg; uint32_t mask; uint32_t status; mask = EP_NoToggleBits; status = (USB_EpRegs(physEpNum & 0x07) >> 12) & 0x03; if (status==EP_STALL) // Out stalled? { // toggle bit 13:12 01 -> 11 reg = USB_EpRegs(physEpNum & 0x07) & mask; USB_EpRegs(physEpNum & 0x07) = (reg | (2 << 12)); } } /** unstall a Endpoint Change only TX (for IN) or RX (for OUT) leave all other fields unchanged if the Endpoint is not stalled change nothing */ void UnStall(int physEpNum) { if (physEpNum & HOST_) // HOST_ bit set means In EPs { UnStall_In(physEpNum); } else // to it for Out EPs { UnStall_Out(physEpNum); } } void ProtocolStall(void) { Stall(0x00); Stall(0x80); } /* Endpoint empfangsbereit machen, also STAT_RX auf 11 setzen per Toggle */ void ClearBuffer(int logEpNum) { #if ENABLE_TRACING char buf[30]; sprintf(buf,"clrBuf logEpNum=%i\n",logEpNum); trace(buf); #endif uint32_t D, Maske; Maske = EP_NoToggleBits | STAT_RX_; /* ohne STAT_TX und ohne beide DTOG_x */ D = USB_EpRegs(logEpNum); USB_EpRegs(logEpNum) = (D ^ STAT_RX_) & Maske; } /* Endpoint sendebereit machen, also STAT_TX auf 11 setzen per Toggle */ void ValidateBuffer(int logEpNum) { #if ENABLE_TRACING char buf[30]; sprintf(buf,"validateBuf logEpNum=%i\n",logEpNum); trace(buf); #endif uint32_t D, Maske; Maske = EP_NoToggleBits | STAT_TX_; /* ohne STAT_RX und ohne beide DTOG_x */ D = USB_EpRegs(logEpNum); USB_EpRegs(logEpNum) = (D ^ STAT_TX_) & Maske; } bool USB_ConfigDevice(bool obConf) { USB_EP1R = (EP_ACTIVE << 12) | (EP_NAK << 4) | (TYP_BULK << 9) | 1;//logEpBulkIn; USB_EP2R = (EP_ACTIVE << 12) | (EP_NAK << 4) | (TYP_BULK << 9) | 2;//logEpBulkOut; USB_EP3R = (EP_ACTIVE << 12) | (EP_NAK << 4) | (TYP_IRQ << 9) | logEpInt; return true; } /* physische Endpunkte aufsetzen (bei Reset-Kommando usw.) */ void InitEndpoints(void) { trace("InitEndpoints\n"); USB_CNTR = 1; /* erstmal Reset und alle Ints aus */ CMD.Configuration = 0; CMD.TransferLen = 0; CMD.PacketLen = 0; CMD.TransferPtr = 0; USB_CNTR = 0; suspended = false; configurationSet = false; transmitting = false; /* EP0 = Control, IN und OUT */ EpTable[0].TxOffset = Ep0InOffset; EpTable[0].TxCount = EpCtrlLenId; EpTable[0].RxOffset = Ep0OutOffset; EpTable[0].RxCount = EpCtrlLenId; /* EP1 = Bulk IN (nur IN) */ EpTable[1].TxOffset = Ep1InOffset; EpTable[1].TxCount = EpBulkLenId; EpTable[1].RxOffset = Ep1OutOffset; EpTable[1].RxCount = EpBulkLenId; /* EP2 = Bulk OUT (nur OUT) */ EpTable[2].TxOffset = Ep1InOffset; EpTable[2].TxCount = EpBulkLenId; EpTable[2].RxOffset = Ep2OutOffset; EpTable[2].RxCount = EpBulkLenId; /* EP3 = Int, IN und OUT */ EpTable[3].TxOffset = Ep3TxOffset; EpTable[3].TxCount = EpIntLenId; EpTable[3].RxOffset = Ep3RxOffset; EpTable[3].RxCount = EpIntLenId; USB_BTABLE = EpTableOffset; USB_EP0R = (EP_ACTIVE << 12) | (EP_NAK << 4) | (TYP_CTL << 9) | 0; USB_ISTR = 0; // clear penting interrupts USB_CNTR = CTRM_ | // Int on each sucessfull transfer RESETM_ | // Int on USB Reset SUSPM_ | WKUPM_ | ESOFM_ | SOFM_; // every ms on SOF USB_DADDR = 0x80; } void Nop(uint32_t count) { while (count) { asm volatile ("NOP"); count--; } } /** read from usbmem -> EP0Buffer to pBuffer */ int ReadControlBlock(uint8_t* pBuffer, int maxlen) { int i; bool odd; UMEM_FAKEWIDTH data16; int count = EpTable[0].RxCount & 0x3FF; UMEM_FAKEWIDTH* P=(UMEM_FAKEWIDTH*) EP0OutBuffer; if (count > maxlen) count = maxlen; i = count; odd = count & 0x01; if (odd) i--; while (i) { data16 = *P++; *pBuffer++ = data16 & 0xFF; *pBuffer++ = (data16>>8) & 0xFF; i-=2; } if (odd) *pBuffer = *P & 0xFF; //one more byte ClearBuffer(0); return count; } int WriteControlBlock(uint8_t* pMem, int count) { int i; bool odd; UMEM_FAKEWIDTH data16; UMEM_FAKEWIDTH *P = (UMEM_FAKEWIDTH*) EP0InBuffer; if (count > EP0_SIZE) count = EP0_SIZE; EpTable[0].TxCount = count; i = count; odd = count & 0x01; if (odd) i--; while (i) { data16 = *pMem++; data16 = ((*pMem++) << 8) | data16; *P++ = data16; i-=2; } if(odd) *P = *pMem & 0xFF; ValidateBuffer(0); return count; } void ACK(void) { WriteControlBlock((uint8_t*) &always0, 0); } /* Request-Typ im Setup-Packet testen (Standard, Class, Vendor) */ bool IsStandardRequest(void) { return (CMD.SetupPacket.bmRequestType & REQUESTMASK_) == 0; } bool IsClassRequest(void) { return (CMD.SetupPacket.bmRequestType & REQUESTMASK_) == CLASS_; } bool IsVendorRequest(void) { return (CMD.SetupPacket.bmRequestType & REQUESTMASK_) == VENDOR_; } /******* anstehende Control-Transfers zum Host blockweise starten *******/ //DataStageIn(void) void DescriptorBlockwiseIn(void) { int i, j; uint8_t* Q; if ((CMD.SetupPacket.bmRequestType & HOST_) == 0) { trace("bmRequestType & 0x80 ==0\n"); return; } i = CMD.TransferLen; if (i > CMD.PacketLen) i = CMD.PacketLen; Q = CMD.TransferPtr; /* Quelle */ //j = UsbMemWrite (/*(uint8_t*)*/ EP0OutBuffer, Q, i); //ValidateBuffer(0); j = WriteControlBlock(Q, i); CMD.TransferPtr = Q + j; /* Zeiger auf evt. Rest merken */ CMD.TransferLen = CMD.TransferLen - j; /* restliche Anzahl Bytes */ if (CMD.TransferLen < 0) CMD.TransferLen = 0; } /**********************************************************************/ /************ Bearbeitung eingegangener Requests **********************/ /**********************************************************************/ /********** USB-Request "SET FEATURE" und "CLEAR FEATURE" behandeln ****/ void DoSetClearFeature(bool value) { int Feature; int FuerWen; int EP; Feature = CMD.SetupPacket.wValue; FuerWen = CMD.SetupPacket.bmRequestType; EP = CMD.SetupPacket.wIndex; #if ENABLE_TRACING char buf[30]; sprintf(buf,"doSetClearFeature for %02x\n",FuerWen); trace(buf); #endif switch (FuerWen) { case DEVICE_: if (Feature == FEATURE_REMOTE_WAKEUP) { CMD.RemoteWakeup = value; ACK(); return; } break; case ENDPOINT_: if (Feature == FEATURE_ENDPOINT_HALT) { switch(EP) { case 0x01: //bulk out case 0x02: //bulk out case 0x81: //bulk in case 0x82: //bulk in case 0x83: //int in if (value) Stall (EP); else UnStall(EP); ACK(); return; } } // break; } trace("unknown feature\n"); ProtocolStall(); } /******** USB-Request "GET STATUS" behandeln ***************************/ void DoGetStatus(void) { uint8_t Buf[4]; int FuerWen; int EP; FuerWen = CMD.SetupPacket.bmRequestType; EP = CMD.SetupPacket.wIndex; Buf[0] = 0; Buf[1] = 0; #if ENABLE_TRACING char buf[30]; sprintf(buf,"doGetStatus for %02x\n",FuerWen); trace(buf); #endif switch (FuerWen) { case HOST_ | DEVICE_: trace("forDevice\n"); if (CMD.RemoteWakeup) Buf[0] |= 2; if (CMD.SelfPowered) Buf[0] |= 1; break; case HOST_ | ENDPOINT_: switch (EP) { trace("forEndpoint\n"); /* nur f�r bulk + int eps notwendig case 0x80: //ctrl in case 0x00: //ctrl out */ case 0x01: if (((USB_EpRegs(1) >> 12) & 0x03) ==1) Buf[0] = 1; break; case 0x02: //bulk out stall condition if (((USB_EpRegs(2) >> 12) & 0x03) ==1) Buf[0] = 1; break; case 0x81: //bulk in stall condition if (((USB_EpRegs(1) >> 4) & 0x03) ==1) Buf[0] = 1; break; case 0x82: if (((USB_EpRegs(2) >> 4) & 0x03) ==1) Buf[0] = 1; break; case 0x83: //interrupt in stall condition if (((USB_EpRegs(3) >> 4) & 0x03) ==1) Buf[0] = 1; break; default: trace("unsuported ep for OnGetStatus\n"); ProtocolStall(); /* kennen wir nicht. Stall. */ return; } break; default: trace("unknown target\n"); ProtocolStall(); return; } CMD.PacketLen = EP0_SIZE; CMD.TransferLen = 2; CMD.TransferPtr = Buf; DescriptorBlockwiseIn(); } /******** Descriptoren zum Host senden *********************************/ /** Send the first part and possibly the only part back to the host */ void DoGetDescriptor(void) { uint16_t Type, Index; int aLen; const uint8_t* P; Type = CMD.SetupPacket.wValue >> 8; Index = CMD.SetupPacket.wValue & 0xFF; aLen = -1; P = 0;//NULL; #if ENABLE_TRACING char buf[30]; sprintf(buf,"doGetDescr type %04x\n",Type); trace(buf); #endif switch (Type) { case USB_DEVICE_DESCRIPTOR: { trace("descDevice\n"); aLen = sizeof(DeviceDescriptor); P = DeviceDesc; } break; case USB_CONFIGURATION_DESCRIPTOR: { trace("descConfig\n"); aLen = ConfigDesc[3]; /* Total-L�nge ist WORD */ aLen = (aLen << 8) | ConfigDesc[2]; P = ConfigDesc; } break; case USB_STRING_DESCRIPTOR: { trace("descString\n"); switch (Index) /* Get String Descriptor */ { case 0: aLen = 4; P = LangString; break; case 1: aLen = VendorString[0]; P = VendorString; break; case 2: aLen = ProductString[0]; P = ProductString; break; #if USB_SERIAL_NO case 3: aLen = SerialString[0]; P = SerialString; break; #endif default: ProtocolStall(); /* kennen wir nicht. Stall. */ aLen = -1; } } break; default: { trace("descElse\n"); ProtocolStall(); /* kennen wir nicht. Stall. */ aLen = -1; } } if (aLen < 0) return; /* nicht mehr senden wollen, als der Host haben will */ if (aLen > CMD.SetupPacket.wLength) aLen = CMD.SetupPacket.wLength; CMD.PacketLen = EP0_SIZE; CMD.TransferLen = aLen; CMD.TransferPtr = (uint8_t*) P; DescriptorBlockwiseIn(); } /*********** USB-Request "SET CONFIGURATION" behandeln *************/ // set bulk eps into working state void DoSetConfiguration(void) { if (CMD.SetupPacket.wValue == 0) { CMD.Configuration = 0; configurationSet = false; } else { USB_ConfigDevice(true); Class_Start(); CMD.Configuration = CMD.SetupPacket.wValue & 0xFF; configurationSet = true; ACK(); } } /*************************** CDC Spezifisches **************************/ /************* "SET LINE CODING" behandeln *****************************/ void VCOM_SetLineCoding(void) { ACK(); /* Vorbereitung auf Empfang von genau 7 Bytes vom Host ???*/ } /* Datenausgabe f�r CDC-spezifischen USB-Request "SET LINE CODING" */ /* hier werden die empfangenen 7 Bytes aus dem USB-Puffer gelesen und im RAM gemerkt */ void SetLineCodingDataOut(void) { ReadControlBlock((uint8_t*) &LineCoding, 7); ACK(); } /* Zustand von DTR und RTS vom Host zum Ger�t merken */ void VCOM_Read_DTR_RTS(void) { Dtr_Rts = CMD.SetupPacket.wValue >> 8; ACK(); } /* CDC-spezifischer USB-Request "GET LINE CODING" behandeln */ void VCOM_GetLineCoding(void) { CMD.PacketLen = EP0_SIZE; CMD.TransferLen = 7; CMD.TransferPtr = (uint8_t*) &LineCoding; DescriptorBlockwiseIn(); /* hier werden die 7 Bytes zum Host geschickt */ } /************************** Setup-Event ***********************************/ /* Merke: 1. Paket abholen und dann ClearBuffer. 2. Bei Setup Paketen, bei denen.. - ..nix hinterher kommt, also wo es keine Datenphase gibt, beantwortet man mit Senden eines leeren Paketes (ist ACK) - ..anschlie�end etwas zum Host gesendet werden mu�, sendet man dies direkt danach. Wenn das zu Sendende gr��er ist als die EpBuffer-L�nge, dann nur ein St�ck senden. Der Host holt es sich ab und der USB-Core gibt dann einen gew�hnlichen Int auf Control-In, wo man dann das n�chste St�ck senden kann. Wiederholt sich, bis man nix mehr zu senden hat. Ob man als Abschlu� ein leeres Paket senden mu� oder nicht, ist ungewi�. Wenn der Host zufrieden ist, sendet er ein leeres Paket als ACK. - ..man anschlie�end noch etwas vom Host bekommt, dann gibt es daf�r ein anschlie�endes Int auf Control-Out. Man liest das Paket und sendet dann als ACK ein leeres Paket. */ void OnSetup(void) { ReadControlBlock(&CMD.SetupPacket.bmRequestType, 8); if (IsStandardRequest()) { trace("isStandardRequest\n"); switch (CMD.SetupPacket.bRequest) { case USB_SET_ADDRESS: trace("SET_ADDRESS\n"); ACK(); return; case USB_CLEAR_FEATURE: trace("CLEAR_FEATURE\n"); DoSetClearFeature(false); return; case USB_SET_FEATURE: trace("SET_FEATURE\n"); DoSetClearFeature(true); return; case USB_GET_CONFIGURATION: trace("GET_CONFIGURATION\n"); CMD.PacketLen = EP0_SIZE; CMD.TransferLen = 1; CMD.TransferPtr = (uint8_t*) &CMD.Configuration; DescriptorBlockwiseIn(); return; case USB_GET_STATUS: trace("GET_STATUS\n"); DoGetStatus(); return; case USB_GET_INTERFACE: trace("GET_INTERFACE\n"); CMD.TransferLen = 1; CMD.TransferPtr = (uint8_t*) &always0; DescriptorBlockwiseIn(); return; case USB_SET_INTERFACE: trace("SET_INTERFACE\n"); //usb spec fordert dass alle DataToggles f�r Bulk und int //auf DATA0 zur�ckgesetzt werden if(USB_EpRegs(1) & DTOG_TX_) USB_EpRegs(1) ^= DTOG_TX_; if(USB_EpRegs(1) & DTOG_RX_) USB_EpRegs(1) ^= DTOG_RX_; if(USB_EpRegs(2) & DTOG_TX_) USB_EpRegs(2) ^= DTOG_TX_; if(USB_EpRegs(2) & DTOG_RX_) USB_EpRegs(2) ^= DTOG_RX_; if(USB_EpRegs(3) & DTOG_TX_) USB_EpRegs(1) ^= DTOG_TX_; Class_Start(); ACK(); return; case USB_GET_DESCRIPTOR: trace("GET_DESCRIPTOR\n"); DoGetDescriptor(); return; case USB_SET_CONFIGURATION: trace("SET_CONFIGURATION\n"); DoSetConfiguration(); return; } } if (IsClassRequest()) { trace("IsClassRequest\n"); switch (CMD.SetupPacket.bRequest) { case SET_LINE_CODE: trace("SET_LINE_CODE\n"); VCOM_SetLineCoding(); return; case GET_LINE_CODE: trace("GET_LINE_CODE\n"); VCOM_GetLineCoding(); return; case SET_CONTROL_LINE_STATE: trace("SET_CONTROL_LINE_STATE\n"); VCOM_Read_DTR_RTS(); return; /* falls es hier noch mehr Class-spezifische Requests geben sollte, dann Behandlung hier hinein. */ } } if (IsVendorRequest()) /* wenn Type = Vendor */ { trace("IsVendorRequest\n"); } ProtocolStall(); } /******* die diversen Endpoint-Interrupts ************************************/ void OnEpCtrlOut(void) /* Control-EP OUT */ { uint8_t tbuf[EP0_SIZE]; if (IsStandardRequest()) /* wenn Type = Standard */ { // eigentlich nur leere Pakete, also ACK vom Host, aber m�glich (nie gesehen) bRequest=7 = SET_DESCRIPTOR //trace("IsStandardRequest\n"); ReadControlBlock(tbuf, EP0_SIZE); return; } if (IsClassRequest()) /* wenn Type = Class */ { trace("IsClassRequest\n"); switch (CMD.SetupPacket.bRequest) { case SET_LINE_CODE: trace("SET_LINE_CODE\n"); SetLineCodingDataOut(); ACK(); return; default: trace("default\n"); ACK(); } return; } /* nach Vendor-Request fragen wir hier garnicht erst */ ACK(); } /** for InTransfers this would be a data stage transfer for OutTransfers this will be a status stage */ void OnEpCtrlIn(void) /* Control-EP IN */ { if (IsStandardRequest()) /* wenn Type = Standard */ { trace("IsStandardRequest\n"); switch (CMD.SetupPacket.bRequest) { case USB_GET_DESCRIPTOR: trace("GET_DESCRIPTOR\n"); if (CMD.TransferLen > 0) DescriptorBlockwiseIn(); return; case USB_SET_ADDRESS: USB_DADDR = 0x80 | CMD.SetupPacket.wValue; return; //todo wrong place gets never executed case GET_LINE_CODE: trace("GET_LINE_CODE\n"); ACK(); break; default: trace("default\n"); ACK(); } return; } } /********* BULK IN und OUT Interrupts **********/ void EpBulkBeginTransmit (void) { int i, n; UMEM_FAKEWIDTH L, A; UMEM_FAKEWIDTH* P; P = (UMEM_FAKEWIDTH*) EP1InBuffer; i = txw1 - txr1; if (i < 0) { i += VCPBUF_SIZE; /* i = Anzahl zu sendender Bytes */ } if (i > BULK_SIZE) { i = BULK_SIZE; } A = 0; n = 0; EpTable[1].TxCount = (i & 0x3FF) | EpBulkLenId; transmitting = true; while (i) { L = UsbTxBuf1[txr1]; txr1 = (txr1 + 1) & VCPBUF_MAX; A = A | (L << n); n += 8; if (n > 8) { *P++ = A; n = 0; A = 0; } --i; } if (n) { *P = A; /* ggf. restliche Bytes ausgeben */ } ValidateBuffer(logEpBulkIn); } void OnEpBulkIn(void) /* EP1 = Bulk-EP IN */ { if (txr1 == txw1) { transmitting = false; } else { EpBulkBeginTransmit (); } } void OnEpBulkOut(void) /* EP2 = Bulk-EP OUT */ { int i, n, hdroom, avail; UMEM_FAKEWIDTH D; char c; UMEM_FAKEWIDTH* P; /* Bulk EP anw�hlen und Anzahl der Bytes ermittlen */ avail = EpTable[1].RxCount & 0x3FF; i = rxw1 - rxr1; if (i < 0) i += VCPBUF_SIZE; hdroom = VCPBUF_SIZE - i; if (hdroom <= avail) { receiving = false; return; } P = (UMEM_FAKEWIDTH*) EP1OutBuffer; n = 2; i = avail; D = *P++; /* 2 Byte laden */ while (i > 0) { c = D & 0xFF; /* LSB zuerst */ UsbRxBuf1[rxw1] = c; rxw1 = (rxw1 + 1) & VCPBUF_MAX; D = D >> 8; --n; if (!n) { D = *P++; n = 2; } --i; } if (hdroom - avail >= BULK_SIZE) { ClearBuffer(1/*logEpBulkOut*/); /* wir haben's gelesen */ } else { receiving = false; } } void OnEpIntIn(void) /* Int-EP IN */ { /* erstmal nix */ } void OnEpIntOut(void) /* Int-EP IN */ { /* erstmal nix */ } /**************** USB-Interrupt-Handler **************************************/ void NAME_OF_USB_IRQ_HANDLER(void) { //trace("irq\n"); uint32_t I; int EpNum; uint16_t EpStatus; I = USB_ISTR; /* Interrupt-Status nach I */ if (I & PMAOVR_) /* interner Timeout... */ { trace("PMAOVR\n"); USB_ISTR = ~PMAOVR_; } if (I & ERR_) /* Datenfehler bei Transaction */ { trace("ERR\n"); USB_ISTR = ~ERR_; } if (I & WKUP_) /* Suspend-->Resume */ { trace("WKUP\n"); USB_CNTR &= ~(FSUSP_ | LP_MODE_); USB_ISTR = ~WKUP_; /* Int l�schen */ suspended = false; } if (I & SUSP_) /* nach 3 ms Pause -->Suspend */ { trace("SUSP\n"); USB_ISTR = ~SUSP_; suspended = true; USB_CNTR |= (FSUSP_ | LP_MODE_); } if (I & RESET_) /* Bus Reset */ { trace("RESET\n"); CMD.Configuration = 0; configurationSet = false; InitEndpoints(); USB_ISTR = ~RESET_; return; } if (I & SOF_) /* Start of Frame, alle 1 ms */ { USB_ISTR = ~SOF_; suspended = false; } if (I & ESOF_) /* Wenn ein SOF Paket fehlt */ { trace("ESOF\n"); USB_ISTR = ~ESOF_; suspended = true; } /* Endpoint Interrupts */ if (I & CTR_) { trace("CTR "); USB_ISTR = ~CTR_; /* Interruptbit l�schen */ EpNum = I & MASK_EA_; /* welcher EP? todo mask 0x07 */ EpStatus = USB_EpRegs(EpNum); /* EP Status lesen */ if (I & DIR_) /* OUT, also Paket wurde empfangen */ { trace("out\n"); USB_EpRegs(EpNum) = EpStatus & ~CTR_RX_ & EP_NoToggleBits; if (EpNum == 0) { trace("logEpCtrl\n"); if (EpStatus & SETUP_) { trace("SETUP\n"); OnSetup(); /* Handle the Setup-Packet */ } else { trace("EpCtrlOut\n"); OnEpCtrlOut(); /* eigentlich nur Class-spezifisches */ } } if (EpNum == 1/*logEpBulkOut*/) { trace("logEpBulkOut\n"); OnEpBulkOut(); } if (EpNum == logEpInt) { trace("logEpInt\n"); OnEpIntOut(); } } else /* IN, also Paket wurde gesendet */ { trace("in\n"); USB_EpRegs(EpNum) = EpStatus & ~CTR_TX_ & EP_NoToggleBits; if (EpNum == 0) { trace("logEpCtrl\n"); OnEpCtrlIn(); } if (EpNum == 1/*logEpBulkIn*/) { trace("logEpBulkIn\n"); OnEpBulkIn(); } if (EpNum == logEpInt) { trace("logEpInt\n"); OnEpIntIn(); } } } } /************ USB-Setup **********************************/ /* Clock muss bereits konfiguriert sein */ /**********************************************************/ uint16_t UsbSetup(void) { trace("setup\n"); uint32_t* P; P = (uint32_t*) USB_RAM; /* RAM abl�schen */ while ((uint32_t) P < (USB_RAM + 1024)) { *P++ = 0; } InitSerial(); CMD.Configuration = 0; configurationSet = false; suspended = false; Class_Start(); /* LineCoding-Block aufsetzen mit unseren Defaultwerten */ USB_CNTR = 3; /* Powerdown+Reset */ Nop(100); /* warten */ USB_CNTR = 1; /* Reset */ USB_ISTR = 0; /* spurious Ints beseitigen */ Nop(1000); /* warten */ EnableUsbIRQ(); InitEndpoints(); return 0; } /********** zeichenweises I/O und Pufferung und Kommunikation ***************/ /* Diese Routinen werden von au�erhalb im Usermode aufgerufen und haben mit dem interrupt-gesteuerten USB-Betrieb nichts zu tun. */ /* liefert true, wenn ein Zeichen abholbereit ist */ bool UsbRxAvail(void) { bool res = rxr1 != rxw1; return res; } /* holt ein Zeichen vom USB ab */ /* Achtung: wenn nix abzuholen ist, wird 0 zur�ckgeliefert */ char UsbGetChar(void) { char c; c = 0; if (!configurationSet || suspended) { return -1; } if (rxr1 != rxw1) { c = UsbRxBuf1[rxr1]; rxr1 = (rxr1 + 1) & VCPBUF_MAX; if (!receiving) { DisableUsbIRQ(); int i, hdroom; i = rxw1 - rxr1; if (i < 0) { i += VCPBUF_SIZE; } hdroom = VCPBUF_SIZE - i; if (hdroom > BULK_SIZE) { receiving = true; ClearBuffer(logEpBulkOut); } EnableUsbIRQ(); } } return c; } /* true, wenn der Host per SET_CONFIGURATION eine Konfiguration aktiviert hat und das Ger�t nicht im Suspend ist. Ansonsten ist keine VCP-Kommunikation m�glich. */ bool UsbActive (void) { bool res = configurationSet && !suspended; return res; } /* liefert true, wenn noch ein Zeichen in den Tx-Buffer passt */ bool UsbTxReady(void) { bool res = configurationSet && !suspended && ((txw1 + 1) & VCPBUF_MAX) != txr1; return res; } /* liefert true, wenn Tx-Buffer leer ist */ bool UsbTxEmpty(void) { bool res = (txw1 == txr1); return res; } /* Anzahl freier Pl�tze im Tx-Buffer liefern */ int UsbTxFree(void) { int i; i = txw1 - txr1; /* i = belegte Pl�tze */ if (i < 0) { i = i + VCPBUF_SIZE; } return VCPBUF_SIZE - i; } void UsbTxFlush (void) { if (!transmitting) { DisableUsbIRQ(); EpBulkBeginTransmit(); EnableUsbIRQ(); } } /* sendet ein Zeichen (d.h. schreibt es in den Tx-Buffer) */ bool UsbCharOut(char c) { while (true) { if (!configurationSet || suspended) { return false; } if (((txw1 + 1) & VCPBUF_MAX) != txr1) break; __asm__ volatile ("wfi"); /* trampeln auf der Stelle!! */ } int i = (txw1 + 1) & VCPBUF_MAX; UsbTxBuf1[txw1] = c; txw1 = i; // Die folgende Bedingung einkommentieren, um nur dann automatisch abzusenden, // wenn Sendepuffer voll. In diesem Fall kann �ber UsbTxFlush abgeschickt werden. // if (((txw1 + 1) & VCPBUF_MAX) == txr1) { if (!transmitting) { DisableUsbIRQ(); EpBulkBeginTransmit(); EnableUsbIRQ(); } // } return true; } /* asciiz zum USB senden */ void UsbStrOut(char* S) { while (*S && UsbCharOut(*S++)); }