#include "usbstm.h" #include "usb_descriptors.h" //debug use only, should probably move this variable to usb buffer ram if enabling again //since usb drivers don't use any .data nor .bss space //static int log = 0; //pick define based on xtal setup for init_clock and init_usb_clock functions //#define NO_XTAL #define EXTERNAL_XTAL void init_usb_clock() { // stm32f0x2 devices have HSI 48Mhz available to clock usb block, or PLL if it's source accurate enough // stm32f0x0 devices must have ext xtal and use PLL output to drive usb block #ifdef EXTERNAL_XTAL //by default the 072 has HSI 48Mhz selected as USB clock //on the 070 this equates to off, so 070 must set USBSW bit RCC->CFGR3 |= RCC_CFGR3_USBSW_PLLCLK; #endif #ifdef NO_XTAL //Turn on HSI48 supposedly it will turn itself on if USB is enabled with HSI48 selected as clock RCC->CR2 |= RCC_CR2_HSI48ON; while ((RCC->CR2 & RCC_CR2_HSI48RDY) != RCC_CR2_HSI48RDY) /* (10) Wait until the HSI48 is stable */ { /* For robust implementation, add here time-out management */ } //by default the 072 has HSI 48Mhz selected as USB clock RCC->CFGR3 &= ~RCC_CFGR3_USBSW_Msk; //on the 070 this equates to off, so 070 must set USBSW bit //CRS system must be turned on to keep HSI 48Mhz calibrated RCC->APB1ENR |= RCC_APB1ENR_CRSEN; //Default settings are good using SOF packets for calibration #endif //enable USB block by providing clock RCC->APB1ENR |= RCC_APB1ENR_USBEN; } void usb_reset_recovery(){ // USB->CNTR |= USB_CNTR_FRES; // USB->CNTR &= ~USB_CNTR_FRES; //Endpoint-specific registers //The number of these registers varies according to the number of endpoints that the USB peripheral is designed to handle. //The USB peripheral supports up to 8 bidirectional endpoints. Each USB device must support a control endpoint whose //address (EA bits) must be set to 0. The USB peripheral behaves in an undefined way if multiple endpoints //are enabled having the same endpoint number value. For each endpoint, an USB_EPnR register is available to store //the endpoint specific information. //Enable the USB device and set address to zero //USB device address (USB_DADDR) //bit7 must be set to enable USB functionality, bits 6:0 are the address, must be 0 prior to enumeration USB->DADDR = (uint16_t) USB_DADDR_EF; //They are also reset when an USB reset is received from the USB bus or forced through bit FRES in the CTLR register, //except the CTR_RX and CTR_TX bits, which are kept unchanged to avoid missing a correct packet notification //immediately followed by an USB reset event. Each endpoint has its USB_EPnR register where n is the endpoint identifier. //Read-modify-write cycles on these registers should be avoided because between the read and the write operations //some bits could be set by the hardware and the next write would modify them before the CPU has the time to detect the change. //For this purpose, all bits affected by this problem have an "invariant" value that must be used whenever their //modification is not required. It is recommended to modify these registers with a load instruction where all the bits, //which can be modified only by the hardware, are written with their "invariant" value. // //USB endpoint n register (USB_EPnR), n=[0..7] //Address offset: 0x00 to 0x1C //Reset value: 0x0000 // //Bit 15 CTR_RX: Correct Transfer for reception // Hardware sets on successful completetion of OUT/SETUP transaction // a failed transaction won't set this bit // Software can only clear this bit // SETUP bit11 signifies if OUT/SETUP was received // USE: read this bit after interrupt to determine/verify interrupt was due to success // service OUT/SETUP packet then clear the bit so subsequent interrupt can be detected properly // write 0 to clear, write 1 to leave as-is // //Bit 14 DTOG_RX: Data Toggle, for reception transfers // For non-isosync xfrs this bit contains Data0/1 expectation of next data packet // Hardware toggles this bit after ACK'ing the host from successful PID match // For control EPs HW clears this bit after receiving SETUP PID // For double buffered, or isosync EPs this bit has other meanings // Software can toggle this value by writting a 1, writting 0 has no effect. // -This is manditory for non-control EPs! // USE: For control endpoints: Seems this bit can be used to determine if upcoming xfr is DATA0/1 // Shouldn't have to do/care much with this bit for control endpoints // write 1 to toggle, write 0 to leave as-is // // //Bits 13:12 STAT_RX [1:0]: Status bits, for reception transfers // 00: Disabled, all reception requests to this EP are ignored (default post-reset) // 01: Stall, EP is stalled and all reception requests will get STALL handshake // 10: NAK, all reception requests will get NAK handshake // 11: Valid, this endpoint is enabled for reception // Hardware sets these bits to NAK after successful OUT/SETUP xfr // Software can toggle these bits by writting a 1, writting 0 has no effect // USE: to enable the endpoint toggle these bits to set. HW will clear bit12 when xfr is recieved. // That way subsequent xfrs will get NAK'd until software processes EP and ready's it for another. // Once processed toggle back to VALID so next xfr can occur // write 1 to toggle, write 0 to leave as-is // // //Bit 11 SETUP: Setup transaction completed // This bit is read-only and indicates if last completed transaction was a SETUP. // This bit only gets set for control endpoints. It remains set while CTR_RX is set. // USE: For control EPs read this bit to determine if received xfr is SETUP or OUT. // writes have no effect, read only // //Bits 10:9 EP_TYPE[1:0]: Endpoint type // 00: BULK // 01: CONTROL // 10: ISO // 11: INTERRUPT // EP0 must always be CONTROL and always available (set by address==0) // USE: set these bits to desired endpoint type. Hardware uses these bits to dictate it's behavior. // bits are read/write, RMW to leave as is // //Bit 8 EP_KIND: Endpoint kind // BULK: DBL_BUF, this bit is set by software to indicate double buffered EP // CONTROL: STATUS_OUT, this bit is set by software to indicate that STATUS_OUT is expected. // This will cause all OUT transactions with more than zero data bytes to be STALL'd instead of ACK. // When clear, OUT transactions of any data length will be allowed. // ISO/INT: unused // USE: set for BULK EPs if want to double buffer the endpoint // set for CONTROL EPs for robustness, when a STATUS_OUT is expected and want non-zero data to be STALL'd // Think we can get by ignoring this bit for now // bit is read/write, RMW to leave as-is // //Bit 7 CTR_TX: Correct Transfer for transmission // Similar to CTR_RX, but for transmissions. Hardware sets this bit on successful transmission completion. // This won't get set if transfer failed. Software can only write this bit to 0. // USE: read this bit after interrupt to determine if source of interrupt was due to successful transmission. // process what's needed for upcoming transmission if needed, and clear this bit to subsequent interrupts // will be properly detected. // write 0 to clear, write 1 to leave as-is // // //Bit 6 DTOG_TX: Data Toggle, for transmission transfers // Similar to DTOG_RX, but for transmissions. Hardware toggles this bit after an ACK of data transmit. // For control EPs HW sets this bit at reception of SETUP PID // For double buffered, and iso EPs this bit has different functions // Software can toggle this bit by writting a 1, writting 0 is ignored. Required for non-control EPs // USE: should be able to ignore for control EPs. // To leave as-is write a 0. // //Bits 5:4 STAT_TX [1:0]: Status bits, for transmission transfers // 00: Disabled, all transmission requests to this EP are ignored (default post-reset) // 01: Stall, EP is stalled and all transmission requests will get STALL handshake // 10: NAK, all transmission requests will get NAK handshake // 11: Valid, this endpoint is enabled for transmission // Hardware changes these bits from Valid to NAK after successful transmission. // That way subsequent transmit requests will be NAK'd until software can prepare the next one. // These bits will toggle when wrote to with a 1, writting a 0 doesn't affect them. // USE: Toggle the bits to Valid when data has been loaded into buffer and ready for transmission. // write 1 to toggle, write 0 to leave as-is // //Bits 3:0 EA[3:0]: Endpoint address // Software must write the address to these bits to enable the endpoint. // USE: set these bits to the "endpoint number" ie 0 for EP0, 1 for EP1, etc. // These default to 0, so post reset all USB_EPnR are set to be EP0. // But because nothing is initialized they'll all ignore anything going on. // bits are read/write, RMW to leave as is //Reset done, now setup as if USB reset has occured //USB reset (RESET interrupt) //When this event occurs, the USB peripheral is put in the same conditions it is left by the system //reset after the initialization described in the previous paragraph: communication is disabled in all //endpoint registers (the USB peripheral will not respond to any packet). //As a response to the USB reset event, the USB function must be enabled, having as USB address 0, //implementing only the default control endpoint (endpoint address is 0 too). //This is accomplished by setting the Enable Function (EF) bit of the USB_DADDR register and //initializing the EP0R register and its related packet buffers accordingly. // //During USB enumeration process, the host assigns a unique address to this device, //which must be written in the ADD[6:0] bits of the USB_DADDR register, and configures any other necessary endpoint. //When a RESET interrupt is received, the application software is responsible to enable again //the default endpoint of USB function 0 within 10 ms from the end of reset sequence which triggered the interrupt. //clear any pending interrupts USB->ISTR = 0; //initialize EP specific register for EP0 as CONTROL and ready for RX of any OUT //assuming reset condition with all bits clear, except CTR_RX/TX holding prior to reset value USB->EP0R = (uint16_t) (USB_EP_RX_VALID | USB_EP_CONTROL | USB_EP_TX_NAK); //clears CTR_RX/TX bits, ready to recieve, transmit disabled, sets to control type, expect any out, set addr to EP0 // } //Create pointer to 16bit array with 512 entries (1024Bytes) //can only be accessed in byte or half words, not full 32bit words! //uint16_t volatile (* const usb_buff)[512] = (void*)USB_PMAADDR; //this was suggestion by: http://a3f.at/articles/register-syntax-sugar //which errors on compilation due to assigning of type array uint16_t volatile (* const usb_buff) = (void*)USB_PMAADDR; //static uint16_t num_bytes_req; #define num_bytes_req usb_buff[NUM_BYTES_REQ] //place this variable in USB RAM //static uint16_t num_bytes_sending; #define num_bytes_sending usb_buff[NUM_BYTES_SENDING] //place this variable in USB RAM //static uint16_t num_bytes_expecting; //this was never used, so it was cut //#define num_bytes_expecting usb_buff[NUM_BYTES_EXPECTING] //place this variable in USB RAM //static uint16_t num_bytes_xfrd; #define num_bytes_xfrd usb_buff[NUM_BYTES_XFRD] //place this variable in USB RAM //flag and value, since can't change address until after STATUS stage, must use this value //to denote that the USB device address needs updated after the status stage. //static uint8_t new_address = 0; placed in upper byte of newaddr_reqtype //place this variable in the upper byte of reqtype, //but it's prob important to ensure it's cleared & init //these variables are used together, so let's place them in the same usb_buff index //static uint8_t reqtype = 0; //static uint8_t reqdir = 0; //they're actual bit masks of the same bmRequestType byte, so really don't need separate bytes #define newaddr_reqtype usb_buff[NEWADDR_REQTYPE] //place this variable in USB RAM //static uint8_t req_dir; #define req_dir usb_buff[VAR_REQ_DIR] //place this variable in USB RAM //move this into USB buffer ram, definition kept in usbstm.h so application code can import it //usbMsgPtr_t usbMsgPtr; //#define usbMsgPtr_L usb_buff[USBMSGPTR_L] //place this variable in USB RAM //#define usbMsgPtr_H usb_buff[USBMSGPTR_H] //place this variable in USB RAM //#define TSSOP20 //defined when using TSSOP-20 part to get PA11/12 alternate mapping to the pins void init_usb() { //clear variables stored in USB ram since can't rely on .bss clearning them anymore //Don't think most of these actually need to be cleared.. newaddr_reqtype might be only one.. num_bytes_req = 0; num_bytes_sending = 0; //num_bytes_expecting = 0; num_bytes_xfrd = 0; newaddr_reqtype = 0; //two single byte variables stored in single 16bit half word req_dir = 0; //usbMsgPtr_H/L shouldn't need pre-initialized //initialize i/o // TSSOP-20: On STM32F070x6 devices, pin pair PA11/12 can be remapped instead of pin pair PA9/10 using SYSCFG_CFGR1 register. #ifdef TSSOP20 //by default PA9/10 are configured to pins 17/18, have to set bits to map PA11/12 which contain USB PHY SYSCFG->CFGR1 |= PA11_PA12_RMP; #endif // // ensure GPIO bank A is enabled after the discussion below, I'm not even sure this is needed.. // RCC->AHBENR |= (uint32_t) (1<APB1RSTR |= RCC_APB1RSTR_USBRST; //reset // RCC->APB1RSTR &= ~RCC_APB1RSTR_USBRST; //release reset //Sections copied from reference manual //As a first step application software needs to activate register macrocell clock and de-assert //macrocell specific reset signal using related control bits provided by device clock management logic. // //clock was already done in init_usb_clk function. //de-assert translates to set reset high I believe, but I'm still not sure I understand. //The next section talks about removing the reset condition with FRES/CNTR reg, but that can't be done yet. //So I think it's just covering bases to say the RCC_APB1RSTR register can't be set "in reset condition" //Actually I think this section of the datasheet is referring to how the first thing a host does once //a device is detected is send it a reset condition (lack of SOF packets) and as this code is being ran that //has yet to occur. So we need to set things up to expect the first "USB command" to be recieved via the cable //as reset which equates to enabling the reset interrupt and having it call reset recovery routine. //After that, the analog part of the device related to the USB transceiver must be //switched on using the PDWN bit in CNTR register, which requires a special handling. USB->CNTR &= ~USB_CNTR_PDWN; //default is set, clear to power up USB //This bit is intended to switch on the internal voltage references that supply the port transceiver. //This circuit has a defined startup time (tSTARTUP specified in the datasheet) tSTARTUP = 1usec //during which the behavior of the USB transceiver is not defined. int delay = 0; for( delay = 0; delay < 48; delay++ ){ //16Mhz = 62.5nsec clock, 1usec = 1Mhz //need 16 clocks to delay 1usec this loop will take a few instruction cycles at least //Perhaps this code will be ran while the core is running at 48Mhz //So future proof by waiting 3 times as long } //It is thus necessary to wait this time, after setting the PDWN bit in the CNTR register, //before removing the reset condition on the USB part (by clearing the FRES bit in the CNTR register). USB->CNTR &= ~USB_CNTR_FRES; //default set, clear to remove reset //Clearing the ISTR register then removes any spurious pending interrupt before any other macrocell operation is enabled. //Entire register is read, or clear by writing 0. USB->ISTR = 0; //At system reset, the microcontroller must initialize all required registers and the packet buffer description table, //to make the USB peripheral able to properly generate interrupts and data transfers. // //Structure and usage of packet buffers //Each bidirectional endpoint may receive or transmit data from/to the host. The received data //is stored in a dedicated memory buffer reserved for that endpoint, while another memory //buffer contains the data to be transmitted by the endpoint. Access to this memory is //performed by the packet buffer interface block, which delivers a memory access request //and waits for its acknowledgment. Since the packet buffer memory has to be accessed by //the microcontroller also, an arbitration logic takes care of the access conflicts, using half //APB cycle for microcontroller access and the remaining half for the USB peripheral access. //In this way, both the agents can operate as if the packet memory is a dual-port SRAM, //without being aware of any conflict even when the microcontroller is performing back-to- //back accesses. The USB peripheral logic uses a dedicated clock. The frequency of this //dedicated clock is fixed by the requirements of the USB standard at 48 MHz, and this can be //different from the clock used for the interface to the APB bus. Different clock configurations //are possible where the APB clock frequency can be higher or lower than the USB peripheral //one. // //Note: Due to USB data rate and packet memory interface requirements, the APB clock must have a minimum //frequency of 10 MHz to avoid data overrun/underrun problems. // //Each endpoint is associated with two packet buffers (usually one for transmission and the //other one for reception). Buffers can be placed anywhere inside the packet memory //because their location and size is specified in a buffer description table, which is also //located in the packet memory at the address indicated by the USB_BTABLE register. // //Each table entry is associated to an endpoint register and it is composed of four 16-bit half-words //so that table start address must always be aligned to an 8-byte boundary (the lowest three //bits of USB_BTABLE register are always "000"). Buffer descriptor table entries are //described in the Section30.6.2: Buffer descriptor table. If an endpoint is unidirectional and it //is neither an Isochronous nor a double-buffered bulk, only one packet buffer is required //(the one related to the supported transfer direction). Other table locations related to unsupported //transfer directions or unused endpoints, are available to the user. Isochronous and double- //buffered bulk endpoints have special handling of packet buffers (Refer to Section 30.5.4: //Isochronous transfers and Section 30.5.3: Double-buffered endpoints respectively). The //relationship between buffer description table entries and packet buffer areas is depicted in Figure323. // //Each packet buffer is used either during reception or transmission starting from the bottom. //The USB peripheral will never change the contents of memory locations adjacent to the //allocated memory buffers; if a packet bigger than the allocated buffer length is received //(buffer overrun condition) the data will be copied to the memory only up to the last available //location. // //Buffer descriptor table //Although the buffer descriptor table is located inside the packet buffer memory, its entries //can be considered as additional registers used to configure the location and size of the //packet buffers used to exchange data between the USB macro cell and the device. //The first packet memory location is located at 0x40006000. The buffer descriptor table //entry associated with the USB_EPnR registers is described below. The packet memory //should be accessed only by byte (8-bit) or half-word (16-bit) accesses. Word (32-bit) accesses are not allowed. // //Buffer table address (USB_BTABLE) this is the address of the buffer table which is contained in the 1KB of RAM itself //By default this value is set to zero, that's what I would have choosen anyway, so doesn't need initialized //But let's go ahead and do it so it's easy to move later if needed and gives us defines to use for addressing USB->BTABLE = USB_BTABLE_ADDR; //Endpoint initialization //The first step to initialize an endpoint is to write appropriate values to the ADDRn_TX/ADDRn_RX registers //so that the USB peripheral finds the data to be transmitted already available and the data to be received can be buffered. //The EP_TYPE bits in the USB_EPnR register must be set according to the endpoint type, eventually using //the EP_KIND bit to enable any special required feature. //On the transmit side, the endpoint must be enabled using the STAT_TX bits in the USB_EPnR register //and COUNTn_TX must be initialized. //For reception, STAT_RX bits must be set to enable reception and COUNTn_RX must be written with //the allocated buffer size using the BL_SIZE and NUM_BLOCK fields. //only setup endpoint zero buffers for now that's all that's required to satisfy USB and get started usb_buff[USB_ADDR0_TX] = EP0_TX_ADDR; //TX count is set to the number of bytes to xfr in next packet //usb_buff[USB_COUNT0_TX] = EP0_SIZE; usb_buff[USB_ADDR0_RX] = EP0_RX_ADDR; //RX count is made up of 3 parts //the MSB is block size 0->2Bytes, 1->32Bytes //bit 14:10 is number of blocks //end point size = block size * num blocks //bits 9:0 are set by USB block based on actual count received //usb_buff[USB_COUNT0_RX] = (uint16_t) ((BL_SIZE2) | ((EP0_SIZE/2)<CNTR |= (USB_CNTR_CTRM | USB_CNTR_PMAOVRM | USB_CNTR_ERRM | USB_CNTR_WKUPM | // USB_CNTR_SUSPM | USB_CNTR_RESETM | USB_CNTR_SOFM | USB_CNTR_ESOFM | USB_CNTR_L1REQM ); USB->CNTR |= ( USB_CNTR_RESETM ); //USB control register (USB_ISTR) will indicate what endpoint and direction created the interrupt //Apparently you don't even need to set the CTRM interrupt as it's not of much use //The EP0R register creates it's own interrupt which is always enabled upon completion of transfer //The ITSR is still useful for determining which EP and dir of transfer that occured. //Having the reset interrupt is vital as apparently the first thing that occurs over the USB bus is a reset. //Not sure if that's caused by the host intentionally, or just the nature of hot plugging where some //SOF's are missed during the mating of the connectors or what. Banged my head for awhile till actually //serviced the reset interrupt as needed //Battery charging detector (USB_BCDR) //Bit 15 DPPU: DP pull-up control //This bit is set by software to enable the embedded pull-up on the DP line. Clearing it to Î÷Îõ0ÎéÎ÷ //can be used to signalize disconnect to the host when needed by the user software. //all other bits have to do with battery charging which is off by default // //Enable the data plus pull up resistor to signal to host that USB device is connected USB->BCDR = USB_BCDR_DPPU; } //function gets called after reception of setup packet for IN transfer, //and each time an interrupt for successful data tx from control EP //So it's as if this function gets called *AFTER* each transmission //Therefore it's task is to prep the next transmission should there be one needed //If we don't need another transmission, prepare to receive status from host //currently hardcoded for EP0 only //num_bytes_sending, num_bytes_xfrd, and *usbMsgPtr must be initialized prior to entry static void control_xfr_in(){ //determine if have data remaining to be sent // We should never get to this point, if we did there was an error and should prob send STALL // if ( num_bytes_xfrd == num_bytes_sending ) { // //no more data to send // //wrap up transfer and prep endpoint for next setup packet // // // //While enabling the last data stage, the opposite direction should be set to NAK, so that, if // //the host reverses the transfer direction (to perform the status stage) immediately, it is kept // //waiting for the completion of the control operation. If the control operation completes // //successfully, the software will change NAK to VALID, otherwise to STALL. At the same time, // //if the status stage will be an OUT, the STATUS_OUT (EP_KIND in the USB_EPnR register) // //bit should be set, so that an error is generated if a status transaction is performed with not- // //zero data. When the status transaction is serviced, the application clears the STATUS_OUT // //bit and sets STAT_RX to VALID (to accept a new command) and STAT_TX to NAK (to delay // //a possible status stage immediately following the next setup). // // return; // } //need a usbMsgPtr but want it to be a variable from the stack usbMsgPtr_t usbMsgPtr_temp; //copy the actual pointer from usb_buffer ram //the usb_buffer ram can only be accessed in halfwords (16bits) //so this assigment respects this and then casts it to the necessary pointer type usbMsgPtr_temp = (uint16_t *) ((usbMsgPtr_H<<16) | usbMsgPtr_L); //copy over 8bytes from transmit data to EP0 buffer //copy data into EP0 buffer table ram //usb buffer ram is only accessible in halfwords/bytes (16/8bits) usb_buff[EP0_TX_BASE] = usbMsgPtr_temp[num_bytes_xfrd/2]; num_bytes_xfrd += 2; usb_buff[EP0_TX_BASE+1] = usbMsgPtr_temp[num_bytes_xfrd/2]; num_bytes_xfrd += 2; usb_buff[EP0_TX_BASE+2] = usbMsgPtr_temp[num_bytes_xfrd/2]; num_bytes_xfrd += 2; usb_buff[EP0_TX_BASE+3] = usbMsgPtr_temp[num_bytes_xfrd/2]; num_bytes_xfrd += 2; //if there aren't 8bytes of data to send, junk will be copied into end of EP0 TX buffer //to correct for this, simply set tx count to amount of good data if ( num_bytes_xfrd > num_bytes_sending ) { //set tx count to number of bytes we actually want to send usb_buff[USB_COUNT0_TX] = EP0_SIZE - (num_bytes_xfrd - num_bytes_sending); //stop lying about how many bytes will be sent num_bytes_xfrd = num_bytes_sending; } else if ( num_bytes_req < num_bytes_xfrd ) { //set tx count to number bytes requested since host didn't want everything usb_buff[USB_COUNT0_TX] = EP0_SIZE - (num_bytes_xfrd - num_bytes_req); num_bytes_xfrd = num_bytes_req; } else { //update tx count usb_buff[USB_COUNT0_TX] = EP0_SIZE; } // if (num_bytes_xfrd == num_bytes_sending ) { // //expect next token to be OUT STATUS zero data length from host to end transaction // //setting EP_KIND to STATUS_OUT: // //STATUS_OUT: This bit is set by the software to indicate that a status out transaction is // //expected: in this case all OUT transactions containing more than zero data bytes are // //answered "STALL" instead of "ACK". This bit may be used to improve the robustness of the // //application to protocol errors during control transfers and its usage is intended for control // //endpoints only. When STATUS_OUT is reset, OUT transactions can have any number of // //bytes, as required. // // //Don't really feel like doing this on only the last packet, and really should be fine without it. // //The host doesn't always take all the data it asks for in the setup packet, sometimes it simply // //sends an OUT in the midst of large IN xfr asking the device to end current transfer // //This seems like something we should just do during init of SETUP with IN xfr to follow. // } //setup EP0R for transmit USB_EP0R_TX_VALID(); return; } //Need to decode the setup packet to determine what type of standard request was made //Beyondlogic.com's USB in a nutshell is the best resource for this stuff: //http://www.beyondlogic.org/usbnutshell/usb6.shtml // // //STANDARD DEVICE REQUESTS //bmRequestType bRequest (1Byte) wValue (2Bytes) wIndex (2Bytes) wLength (2Bytes) Data(based on SETUP) //1000 0000b GET_STATUS (0x00) Zero Zero Two Device Status #define STD_REQ_GET_STATUS 0x00 //0000 0000b CLEAR_FEATURE (0x01) Feature Selector Zero Zero None #define STD_REQ_CLEAR_FEATURE 0x01 //0000 0000b SET_FEATURE (0x03) Feature Selector Zero Zero None #define STD_REQ_SET_FEATURE 0x03 //0000 0000b SET_ADDRESS (0x05) Device Address Zero Zero None #define STD_REQ_SET_ADDRESS 0x05 //1000 0000b GET_DESCRIPTOR (0x06) Descriptor Type & Index Zero or Language ID Descriptor Length Descriptor #define STD_REQ_GET_DESCRIPTOR 0x06 //0000 0000b SET_DESCRIPTOR (0x07) Descriptor Type & Index Zero or Language ID Descriptor Length Descriptor #define STD_REQ_SET_DESCRIPTOR 0x07 //1000 0000b GET_CONFIGURATION (0x08) Zero Zero 1 Configuration Value #define STD_REQ_GET_CONFIGURATION 0x08 //0000 0000b SET_CONFIGURATION (0x09) Configuration Value Zero Zero None #define STD_REQ_SET_CONFIGURATION 0x09 // // // The Get Status request directed at the device will return two bytes during the data stage with the following format, // D15 - D2 D1 D0 // Reserved Remote Wakeup Self Powered // If D0 is set, then this indicates the device is self powered. If clear, the device is bus powered. If D1 is set, // the device has remote wakeup enabled and can wake the host up during suspend. The remote wakeup bit can be by the // SetFeature and ClearFeature requests with a feature selector of DEVICE_REMOTE_WAKEUP (0x01) // // Clear Feature and Set Feature requests can be used to set boolean features. When the designated recipient is the device, // the only two feature selectors available are DEVICE_REMOTE_WAKEUP and TEST_MODE. Test mode allows the device to exhibit // various conditions. These are further documented in the USB Specification Revision 2.0. // // // Set Descriptor/Get Descriptor is used to return the specified descriptor in wValue. A request for the configuration // descriptor will return the device descriptor and all interface and endpoint descriptors in the one request. // Endpoint Descriptors cannot be accessed directly by a GetDescriptor/SetDescriptor Request. // Interface Descriptors cannot be accessed directly by a GetDescriptor/SetDescriptor Request. // String Descriptors include a Language ID in wIndex to allow for multiple language support. // // Get Configuration/Set Configuration is used to request or set the current device configuration. In the case of a // Get Configuration request, a byte will be returned during the data stage indicating the devices status. A zero value // means the device is not configured and a non-zero value indicates the device is configured. // Set Configuration is used to enable a device. It should contain the value of bConfigurationValue of the desired // configuration descriptor in the lower byte of wValue to select which configuration to enable. // //void control_xfr_out(uint16_t len){ // // usbFunctionWrite( // //} //return number of bytes expected static uint16_t standard_req_out( usbRequest_t *spacket ){ switch ( spacket->bRequest ) { //STANDARD DEVICE REQUESTS //bmRequestType bRequest (1Byte) wValue (2Bytes) wIndex (2Bytes) wLength (2Bytes) Data(based on SETUP) //0000 0000b CLEAR_FEATURE (0x01) Feature Selector Zero Zero None case STD_REQ_CLEAR_FEATURE: break; //0000 0000b SET_FEATURE (0x03) Feature Selector Zero Zero None case STD_REQ_SET_FEATURE: break; //0000 0000b SET_ADDRESS (0x05) Device Address Zero Zero None // Set Address is used during enumeration to assign a unique address to the USB device. The address is specified in wValue // and can only be a maximum of 127. This request is unique in that the device does not set its address until after the // completion of the status stage. (See Control Transfers.) All other requests must complete before the status stage. case STD_REQ_SET_ADDRESS: //new_address = spacket->wValue; //store in upper byte of usb ram variable newaddr_reqtype = (newaddr_reqtype & 0x00FF) | ((spacket->wValue)<<8); //debug logging usb_buff[LOG0] = new_address; return 0; break; //0000 0000b SET_DESCRIPTOR (0x07) Descriptor Type & Index Zero or Language ID Descriptor Length Descriptor case STD_REQ_GET_CONFIGURATION: break; //0000 0000b SET_CONFIGURATION (0x09) Configuration Value Zero Zero None case STD_REQ_SET_CONFIGURATION: break; default: //ERROR send STALL? break; } } static uint16_t standard_req_in( usbRequest_t *spacket ){ switch ( spacket->bRequest ) { //STANDARD DEVICE REQUESTS //bmRequestType bRequest (1Byte) wValue (2Bytes) wIndex (2Bytes) wLength (2Bytes) Data(based on SETUP) //1000 0000b GET_STATUS (0x00) Zero Zero Two Device Status case STD_REQ_GET_STATUS: break; //1000 0000b GET_DESCRIPTOR (0x06) Descriptor Type & Index Zero or Language ID Descriptor Length Descriptor case STD_REQ_GET_DESCRIPTOR: switch ( (spacket->wValue & DESC_TYPE_MASK)>>8) { //must mask out upper byte and shift to get desc type case DESC_TYPE_DEVICE: //usbMsgPtr = (uint16_t *)device_desc; //set the usb_buff[] message ptr instead.. //this works, but requires assingment above //usbMsgPtr_L = (uint32_t)usbMsgPtr; //usbMsgPtr_H = ((uint32_t)usbMsgPtr)>>16; //do the same but without the use of an actual usbMsgPtr //first the const array (which is actually a pointer), is cast to a //16bit pointer. Then that pointer is cast to an int and assinged //to the usb_buff ram/index. //the upper 16bits needs to get shifted prior to assignment usbMsgPtr_L = (uint32_t)(uint16_t *)device_desc; usbMsgPtr_H = ((uint32_t)(uint16_t *)device_desc)>>16; return device_desc[bLength]; case DESC_TYPE_CONFIG: //Must return all config, interface, and endpoint descriptors in one shot //usbMsgPtr = (uint16_t *)config_desc; usbMsgPtr_L = (uint32_t)(uint16_t *)config_desc; usbMsgPtr_H = ((uint32_t)(uint16_t *)config_desc)>>16; return config_desc[wTotalLength]; case DESC_TYPE_STRING: //determine which string index switch ( spacket->wValue & DESC_IDX_MASK ) { //Must mask out index from lower byte case 0: //usbMsgPtr = (uint16_t *)string0_desc; usbMsgPtr_L = (uint32_t)(uint16_t *)string0_desc; usbMsgPtr_H = ((uint32_t)(uint16_t *)string0_desc)>>16; return string0_desc[bLength]; case 1: //usbMsgPtr = (uint16_t *)string1_desc; usbMsgPtr_L = (uint32_t)(uint16_t *)string1_desc; usbMsgPtr_H = ((uint32_t)(uint16_t *)string1_desc)>>16; return string1_desc[bLength]; case 2: //usbMsgPtr = (uint16_t *)string2_desc; usbMsgPtr_L = (uint32_t)(uint16_t *)string2_desc; usbMsgPtr_H = ((uint32_t)(uint16_t *)string2_desc)>>16; return string2_desc[bLength]; default: //error send stall return 0; } // interface and endpoint descriptors can't be accessed directly, get them via config desc // case DESC_TYPE_INTERFACE: // usbMsgPtr = (uint16_t *)interface_desc; // return interface_desc[bLength]; // case DESC_TYPE_ENDPOINT: // usbMsgPtr = (uint16_t *)endpoint_desc; // return endpoint_desc[bLength]; default: //TODO error send stall return 0; } //1000 0000b GET_CONFIGURATION (0x08) Zero Zero 1 Configuration Value case STD_REQ_GET_CONFIGURATION: break; } //just return device descriptor for now } //USB IRQ handler calls this function after recieving a control setup packet //function is responsible for preparing follow on data/status transfers to complete control xfr //must set everything up for control_xfr_in/out functions to be called during data packets static void control_xfr_init( usbRequest_t *spacket ) { //determine number of requested data payload bytes num_bytes_req = spacket->wLength; num_bytes_xfrd = 0; //Vusb calls usbFunctionSetup which sets usbMsgPtr to point to IN data array //and returns length of data to be returned. //Vusb needs nothing else for IN transfers as it already has the data //Vusb calls usbFunctionWrite for OUT xfr on subsequent of data packet arrival (8bytes max) //if data is IN (tx) need to aquire pointer to data that we'd like to transmit //don't think we'll get a zero data length IN as that would mean the host sends all packets //I suppose this is possible for poorly written host application..? //Vusb had option to provide function that would be called for each IN data packet //I found that to be slow and best avoided, so not going to bother supporting that for now.. //Also need to get amount of data that the device would like to return, possible the //device has less data to send back than requested in wLength. /*USB_PUBLIC usbMsgLen_t usbFunctionSetup(uchar data[8]); * If the SETUP indicates a control-in transfer, you should provide the * requested data to the driver. There are two ways to transfer this data: * (1) Set the global pointer 'usbMsgPtr' to the base of the static RAM data * block and return the length of the data in 'usbFunctionSetup()'. The driver * will handle the rest. Or (2) return USB_NO_MSG in 'usbFunctionSetup()'. The * driver will then call 'usbFunctionRead()' when data is needed. See the * documentation for usbFunctionRead() for details. * * If the SETUP indicates a control-out transfer, the only way to receive the * data from the host is through the 'usbFunctionWrite()' call. If you * implement this function, you must return USB_NO_MSG in 'usbFunctionSetup()' * to indicate that 'usbFunctionWrite()' should be used. See the documentation * of this function for more information. If you just want to ignore the data * sent by the host, return 0 in 'usbFunctionSetup()'. */ //set request type so it can be keyed from for subsequent IN/OUT data transfers //reqtype = (spacket->bmRequestType & REQ_TYPE_MASK); //reqdir = (spacket->bmRequestType & REQ_DIR_MASK); //don't need separate bytes to store 2 bits of data coming from the same byte.. //reqtype = spacket->bmRequestType; //store in lower byte newaddr_reqtype = (newaddr_reqtype & 0xFF00) | spacket->bmRequestType; //setup packets not handled by standard requests sent to usbFunctionSetup (just like Vusb) if ((spacket->bmRequestType & REQ_TYPE_MASK) != REQ_TYPE_STD) { //function must set usbMsgPtr to point to return data for IN transfers num_bytes_sending = usbFunctionSetup( (uint8_t*) spacket ); } if ( (spacket->bmRequestType & REQ_DIR_MASK) == REQ_DIR_IN ) { //IN transfer Host wants us to send data (tx) switch ( spacket->bmRequestType & REQ_TYPE_MASK ) { case REQ_TYPE_STD: num_bytes_sending = standard_req_in( spacket ); break; // case REQ_TYPE_CLASS: // num_bytes_sending = 0;//class_req_in( spacket ); // break; // case REQ_TYPE_VEND: // num_bytes_sending = 0;//vendor_req_in( spacket ); // break; // case REQ_TYPE_RES: // default: // num_bytes_sending = 0; // break; } //A USB device can determine the number and //direction of data stages by interpreting the data transferred in the SETUP stage, and is //required to STALL the transaction in the case of errors. To do so, at all data stages before //the last, the unused direction should be set to STALL, so that, if the host reverses the //transfer direction too soon, it gets a STALL as a status stage. // //WRONG!!! This ended up being a shot in the foot. The VERY first request from the host is a //SETUP device descriptor request for 16KBytes worth of data!! //HOWEVER, the host tells us the device to shut it's mouth after the first data stage as if //it doesn't want any more info by sending a STATUS state (OUT xfr expecting zero data) //So we need to be ready for this condition and be prepared to recieve a STATUS OUT zero data "shutup" command //Maybe this is only an issue for standard type requests during enumeration..? // //Need to set RX to stall it should be NAK right now //TODO decide if want to set EP_KIND to STATUS_OUT during this time. I don't think there is much benefit //currently and will cause more hassle than it's worth currently, since have to ensure it's clear for other OUT xfrs USB_EP0R_RX_VALID_STATUS_OUT(); //TODO decide if want to set EP0_RX_COUNT to 0 so we can only receive STATUS OUT with zero data, however that //is effectively what EP_KIND= STATUS_OUT is intended for.. I don't think RX count matters since it can't overflow //Now we've got the pointer to return data initialized //and number of bytes the firmware would like to return control_xfr_in(); } else { //OUT transfer Host is sending us data (rx) //if data is OUT (rx) need a function pointer of what to call when data is received //or if there is no data stage, we just need to send status packet //num_byte_req contains number of bytes expected to be transferred in upcoming OUT xfrs switch ( spacket->bmRequestType & REQ_TYPE_MASK ) { case REQ_TYPE_STD: //num_bytes_expecting was never used, so cut it to save ram //the compiler was cutting it anyway, no need to put in usb_buff[].. //BUT! When I cut it, USB device descriptor fails.. //IDK why, so just let's just keep it anyway.. //num_bytes_expecting = standard_req_out( spacket ); //The reason was because that function actually does something required even //if the return value is ignored you bozo! standard_req_out( spacket ); break; // case REQ_TYPE_CLASS: // //num_bytes_sending = 0;//class_req_in( spacket ); // //break; // case REQ_TYPE_VEND: // //num_bytes_sending = 0;//vendor_req_in( spacket ); // //num_bytes_sending = usbFunctionSetup( spacket ); // break; // case REQ_TYPE_RES: // default: // //num_bytes_sending = 0; // break; } //Clear EP_KIND so OUT transfers of any length are accepted USB_EP0R_EP_KIND_ANY(); //TODO if there is no DATA stage, then we expect the next token to be IN //in which case device need to be prepared to send IN transfer of zero data length //prepare IN STATUS packet of zero length usb_buff[USB_COUNT0_TX] = 0; //enable any packet USB_EP0R_RXTX_VALID(); //control_xfr_out(); } } //This is the ISR that gets called for USB interrupts //At the end of the transaction, an endpoint-specific interrupt is generated, reading status registers //and/or using different interrupt response routines. The microcontroller can determine: // -which endpoint has to be served, // -which type of transaction took place, if errors occurred (bit stuffing, format, CRC, // protocol, missing ACK, over/underrun, etc.). //USB interrupt status register (USB_ISTR) //This register contains the status of all the interrupt sources allowing application //software to determine, which events caused an interrupt request. void USB_IRQHandler(void) { //all interrupts enabled by USB_CNTR, plus any successful rx/tx to an endpoint triggers this ISR //should be our goal to get out of this ISR asap, signal the transaction to the main thread //and let the main thread process the data. //That isn't really how V-usb is setup which we're looking to replace and be compatible with for starters. //So for now this isn't going to be setup ideally, but if USB is our top priority then maybe it's not so bad.. //Plan is to determine source of interrupt //Service what's needed for the interrupt //Clear the pending/active interrupts which have been serviced //If there are other interrupts occuring simulateously we don't have to catch em all // they'll trigger this ISR again //Return from interrupt //First check for successful transfer of USB packets //High level we can determine this by USB_ISTR: // CTR:set if correct transfer occured (read only, cleared via the EP) // DIR: 0-TX, 1-RX // EP_ID[3:0]: endpoint that triggered the interrupt with following priority: // -isosync & double-buffered bulk are considered first getting hi pri // -then EPnR register number ie EP0R gets priority over EP2R // NOTE: you can put whatever endpoint number in whichever register you choose // endpoint0 doesn't have to go in EP0R register, the endpoint number is programmable! // would probably make more sense if they used letters to define EPnR registers.. //Low level we don't have to use EP_ID's priority assignments we could just check EPnR CTR_RX/TX bits //Kinda like EP_ID though as it allows us to focus on a single EP for the given ISR call //CAUTION!!! usb_buff data can only be accessed in half words, or bytes. No 32bit accesses!!! usbRequest_t *setup_packet; uint8_t endpoint_num; //only have EP0 for now //check for OUT/SETUP if ( USB->EP0R & USB_EP_CTR_RX ) { //clear RX interrupt leave everything else unaffected, reference manual says this should be the first thing //we do, but with control endpoints, any subsequent SETUP will stomp over the prior one if CTR_RX is clear //It'll stomp it even if STAT is set to STALL/NAK, setting to DISABLE is only way to prevent it.. //If last setup data is vital to maintain it should be stored more permanently somewhere besides buffer ram. EP0R_CLR_CTR_RX(); //Note: clearing CTR_RX on a control EP, is enough for another setup packet to be accepted //this is true even if STAT_RX is STALL/NAK, so if the data needs retained it must be copied //elsewhere, or set STAT_RX to DISABLED to keep it from being stomped by the next setup if ( USB->EP0R & USB_EP_SETUP ) { //SETUP packet //log ++; //inc log for each setup packet used for debugging purposes to trigger logic analyzer at desired packet #define LOG_COUNT 3 // if ( log >= LOG_COUNT) { DEBUG_HI(); DEBUG_LO(); } //usb_buff[LOG0] = USB->EP0R; //set pointer to usb buffer, type ensures 8/16bit accesses to usb_buff setup_packet = (void*) &usb_buff[EP0_RX_BASE]; req_dir = (setup_packet->bmRequestType & REQ_DIR_MASK); //set to REQ_DIR_IN/OUT for data stage dir control_xfr_init( setup_packet ); } else { //OUT packet //if ((reqtype == REQ_TYPE_VEND) & (reqdir == REQ_DIR_OUT)) { //the two variables (bits) were combined to single byte if (((newaddr_reqtype & REQ_TYPE_MASK) == REQ_TYPE_VEND) && ((newaddr_reqtype & REQ_DIR_MASK) == REQ_DIR_OUT)) { //have to key off of reqdir so OUT status packets don't call usbFunctionWrite // if ( log >= LOG_COUNT) { DEBUG_HI(); DEBUG_LO(); } //number of bytes received is denoted in USB_COUNTn_RX buffer table //control_xfr_out(); usbFunctionWrite((uint8_t*) &usb_buff[EP0_RX_BASE], (usb_buff[USB_COUNT0_RX] & RX_COUNT_MSK)); USB_EP0R_RX_VALID(); } } } else if ( USB->EP0R & USB_EP_CTR_TX ) { //IN packet // if ( log >= LOG_COUNT) { DEBUG_HI(); DEBUG_LO(); } //DEBUG_HI(); DEBUG_LO(); // log ++; //LED_ON(); // if (log >= 2) { // LED_ON(); // } //Servicing of the CTR_TX event starts clearing the interrupt bit; //usb_buff[LOG4] = USB->EP0R; //clear TX interrupt EP0R_CLR_CTR_TX(); //USB->EP0R = (((USB->EP0R | USB_EP_CTR_RX) //set rx field to keep from accidentally clearing // & USB_EPREG_MASK ) //mask out toggle feilds making them zero // & ~USB_EP_CTR_TX ); //clear tx bit removing active interrupt // LED_ON(); //usb_buff[LOG8] = USB->EP0R; //the application software then prepares another buffer full of data to be sent, //updates the COUNTn_TX table location with the number of byte to be transmitted during the next transfer, //and finally sets STAT_TX to 11 (VALID) to re-enable transmissions. //this is only done if the IN transfer had data, if there was no data, it was a status stage //if ( (usb_buff[USB_COUNT0_RX] & RX_COUNT_MSK) != 0 ) { if ( req_dir == REQ_DIR_IN ) { control_xfr_in(); } else { //OUT request, IN denotes STATUS stage //check if USB device address needs set must be after completion of STATUS stage //if (new_address) { //stored in upper byte of newaddr_reqtype if (newaddr_reqtype & 0xFF00) { //USB->DADDR = (new_address | USB_DADDR_EF); //update address and keep USB function enabled USB->DADDR = ((newaddr_reqtype>>8) | USB_DADDR_EF); //update address and keep USB function enabled //new_address = 0; //clear flag value so don't come back here again newaddr_reqtype &= 0x00FF; //LED_ON(); } } //usb_buff[LOGC] = USB->EP0R; //usb_buff[LOG10] = log; //While the STAT_TX bits are equal to 10 (NAK), any IN request addressed to that endpoint is NAKed, //indicating a flow control condition: the USB host will retry the transaction until it succeeds. //It is mandatory to execute the sequence of operations in the above mentioned order to avoid losing the //notification of a second IN transaction addressed to the same endpoint immediately following the one //which triggered the CTR interrupt. //I wasn't doing things in this order thinking that explains why I can't get a second transfer out } if ( USB->ISTR & USB_ISTR_RESET ) { //USB reset event occured //Anytime a reset condition occurs, the EPnR registers are cleared //Must re-enable the USB function and setup EP0 after any reset condition within 10msec usb_reset_recovery(); //handled in reset recovery: USB->ISTR &= ~USB_ISTR_RESET; } }