/* ********************************************************************** *name: IE_stm32_usb.c *author: Samuel Igwe *date: 08/24/2013 *description: Igbo Embedded stm32.c usb source ********************************************************************** */ #include "IE_stm32.h" #include "IE_tclib.h" /* ********************************************************************** *#define functions for USB enumeration ********************************************************************** */ void stm32_usb_isr_control_in(void); void stm32_usb_isr_control_out(void); void stm32_usb_isr_user_in(unsigned int wdEndP); void stm32_usb_isr_user_out(unsigned int wdEndP); void stm32_usb_cancel_queued_IN_transfers(unsigned int wdEndP); void stm32_usb_dequeue_IN_transfers(unsigned int wdEndP); void stm32_usb_queue_IN_transfers(unsigned int wdEndP,\ unsigned char *ptrBuffer,\ unsigned int wdSize); void stm32_usb_set_epr_stat(unsigned int wdEndP,\ unsigned int wdNewStat,\ unsigned int wdOffset); void stm32_usb_clear_epr_ctr(unsigned int wdEndP,\ unsigned int wdMask); void stm32_usb_enable_endp(unsigned int wdEndP,\ unsigned int wdEndPType); void stm32_usb_set_epr_dtog(unsigned int wdEndP,\ int wdNewStat,\ unsigned int wdOffset); /* ********************************************************************** *#define constants for USB enumeration ********************************************************************** */ static volatile unsigned int wdUsbState, wdUsbAddr, wdUsbArgs; struct STR_USB_PKT_XFER{ unsigned char *ptrCtrlBuf; int wdCtrlSize; unsigned char *ptrUserBuf; int wdUserSize; }strUsbPktXfer; #define STMUSB_DEVICE_CONFIGURED (1 << 0) #define STMUSB_DEVICE_IDLE 0x0ff #define USB_NVIC_IRQ_NUM 20 /* ********************************************************************** *description: routine to properly set the EP_TYPE bits in the * Endpoint register. *input: unsigned int wdEndP = endpoint number * unsigned int wdEndPType = the following * STM32_USB_EPR_EP_TYPE_BULK * STM32_USB_EPR_EP_TYPE_CONTROL * STM32_USB_EPR_EP_TYPE_ISO * STM32_USB_EPR_EP_TYPE_INTERRUPT *output: void * *note: the CTR_RX and CTR_TX bits are governed by the rules * 1 = dont change 0 = clear ********************************************************************** */ void stm32_usb_enable_endp(unsigned int wdEndP,\ unsigned int wdEndPType) { unsigned int wdTemp; /*retrieve the current register value*/ wdTemp = *(PTR_STM32_USB_EP0R + wdEndP); /*invariants - values that will not modify these reg bits*/ wdTemp &= ~(STM32_USB_EPR_STAT_TX_MASK | \ STM32_USB_EPR_STAT_RX_MASK | \ (STM32_USB_EPR_DTOG_TX | STM32_USB_EPR_DTOG_RX)); wdTemp |= (STM32_USB_EPR_CTR_TX | STM32_USB_EPR_CTR_RX); /*set EndPType and number*/ wdTemp |= (wdEndPType << STM32_USB_EPR_EP_TYPE_OFFSET); wdTemp |= wdEndP; /*set the current register value*/ *(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP); } /* ********************************************************************** *description: routine to properly set the RX and TX STAT bits in the * Endpoint register. *input: unsigned int wdEndP = endpoint number * unsigned int wdMask = the following * STM32_USB_EPR_CTR_RX * STM32_USB_EPR_CTR_TX *output: void * *note: the CTR_RX and CTR_TX bits are governed by the rules * 1 = dont change 0 = clear ********************************************************************** */ void stm32_usb_clear_epr_ctr(unsigned int wdEndP,\ unsigned int wdMask) { unsigned int wdTemp; /*retrieve the current register value*/ wdTemp = *(PTR_STM32_USB_EP0R + wdEndP); /*invariants - values that will not modify these reg bits*/ wdTemp &= ~(STM32_USB_EPR_STAT_TX_MASK | \ STM32_USB_EPR_STAT_RX_MASK | \ (STM32_USB_EPR_DTOG_TX | STM32_USB_EPR_DTOG_RX)); wdTemp |= (STM32_USB_EPR_CTR_TX | STM32_USB_EPR_CTR_RX); wdTemp &= ~wdMask; /*set the current register value*/ *(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP); } /* ********************************************************************** *description: routine to properly set or toggle the DTOG_RX | DTOG_TX * bits of the Endpoint register. *input: unsigned int wdEndP = endpoint number * unsigned int wdNewStat = the following * 0 = set to 0 * 1 = set to 1 * -1 = toggle * unsigned int wdOffset = DTOG_RX or DTOG_TX STAT * STM32_USB_EPR_DTOG_RX_OFFSET * STM32_USB_EPR_DTOG_TX_OFFSET *output: void * *note: the DTOG_RX and DTOG_TX bits are governed by the rules * 0 = dont change 1 = TOGGLE ********************************************************************** */ void stm32_usb_set_epr_dtog(unsigned int wdEndP,\ int wdNewStat,\ unsigned int wdOffset) { unsigned int wdMask, wdTemp; /*retrieve the current register value*/ wdMask = *(PTR_STM32_USB_EP0R + wdEndP); wdTemp = wdMask; /*wdMask has STAT position extracted*/ if(wdOffset == STM32_USB_EPR_DTOG_RX_OFFSET) wdMask &= STM32_USB_EPR_DTOG_RX; else wdMask &= STM32_USB_EPR_DTOG_TX; /*normalize then compute XOR of value and mask*/ wdMask >>= wdOffset; if(wdNewStat != -1 && wdMask == wdNewStat) return; if(wdNewStat == -1) wdNewStat = 1; wdMask ^= wdNewStat; wdMask <<= wdOffset; /*mask out non important bits*/ if(wdOffset == STM32_USB_EPR_DTOG_RX_OFFSET) wdMask &= STM32_USB_EPR_DTOG_RX; else wdMask &= STM32_USB_EPR_DTOG_TX; /*invariants - values that will not modify these reg bits*/ wdTemp &= ~(STM32_USB_EPR_STAT_TX_MASK | \ STM32_USB_EPR_STAT_RX_MASK | \ (STM32_USB_EPR_DTOG_TX | STM32_USB_EPR_DTOG_RX)); wdTemp |= (STM32_USB_EPR_CTR_TX | STM32_USB_EPR_CTR_RX); /*set user settings*/ wdTemp |= wdMask; /*update register*/ *(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP); } /* ********************************************************************** *description: routine to properly set the RX and TX STAT bits in the * Endpoint register. *input: unsigned int wdEndP = endpoint number * unsigned int wdNewStat = the following * STM32_USB_EPR_STAT_RX_DISABLE * STM32_USB_EPR_STAT_TX_DISABLE * * STM32_USB_EPR_STAT_RX_STALL * STM32_USB_EPR_STAT_TX_STALL * * STM32_USB_EPR_STAT_RX_NAK * STM32_USB_EPR_STAT_TX_NAK * * STM32_USB_EPR_STAT_RX_VALID * STM32_USB_EPR_STAT_TX_VALID * unsigned int wdOffset = RX or TX STAT * STM32_USB_EPR_STAT_RX_OFFSET * STM32_USB_EPR_STAT_TX_OFFSET *output: void * *note: the RX_STAT and TX_STAT bits are governed by the rules * 0 = dont change 1 = TOGGLE * A major error was not taking this into consideration ********************************************************************** */ void stm32_usb_set_epr_stat(unsigned int wdEndP,\ unsigned int wdNewStat,\ unsigned int wdOffset) { unsigned int wdMask, wdTemp; /*retrieve the current register value*/ wdMask = *(PTR_STM32_USB_EP0R + wdEndP); wdTemp = wdMask; /*wdMask has STAT position extracted*/ if(wdOffset == STM32_USB_EPR_STAT_RX_OFFSET) wdMask &= STM32_USB_EPR_STAT_RX_MASK; else wdMask &= STM32_USB_EPR_STAT_TX_MASK; /*normalize then compute XOR of value and mask*/ wdMask >>= wdOffset; wdMask ^= wdNewStat; wdMask <<= wdOffset; /*mask out non important bits*/ if(wdOffset == STM32_USB_EPR_STAT_RX_OFFSET) wdMask &= STM32_USB_EPR_STAT_RX_MASK; else wdMask &= STM32_USB_EPR_STAT_TX_MASK; /*invariants - values that will not modify these reg bits*/ wdTemp &= ~(STM32_USB_EPR_STAT_TX_MASK | \ STM32_USB_EPR_STAT_RX_MASK | \ (STM32_USB_EPR_DTOG_TX | STM32_USB_EPR_DTOG_RX)); wdTemp |= (STM32_USB_EPR_CTR_TX | STM32_USB_EPR_CTR_RX); /*set user settings*/ wdTemp |= wdMask; /*update register*/ *(PTR_STM32_USB_EP0R + wdEndP) = (wdTemp | wdEndP); } /* ********************************************************************** *description: tx transfer cancel routine *input: unsigned int wdEndP = endpoint number *note: called at post_reset and whenever the need arises ********************************************************************** */ void stm32_usb_cancel_queued_IN_transfers(unsigned int wdEndP) { if(wdEndP == STMUSB_ENDP_USER_NUM) strUsbPktXfer.wdUserSize = -1; else strUsbPktXfer.wdCtrlSize = -1; } /* ********************************************************************** *description: tx transfer dequeue routine *input: unsigned int wdEndP = endpoint number *note: be prepared to deal with zero length packets ********************************************************************** */ void stm32_usb_dequeue_IN_transfers(unsigned int wdEndP) { unsigned char *ptrBuf, **ptrPtrBuf; int *ptrCount, wdCount, wdEndPSize; if(wdEndP == STMUSB_ENDP_USER_NUM) { ptrBuf = strUsbPktXfer.ptrUserBuf; ptrPtrBuf = (unsigned char **)&strUsbPktXfer.ptrUserBuf; ptrCount = &strUsbPktXfer.wdUserSize; wdCount = *ptrCount; wdEndPSize= STMUSB_ENDP_USER_SIZE; } else { ptrBuf = (unsigned char *)strUsbPktXfer.ptrCtrlBuf; ptrPtrBuf = (unsigned char **)&strUsbPktXfer.ptrCtrlBuf; ptrCount = &strUsbPktXfer.wdCtrlSize; wdCount = *ptrCount; wdEndPSize= STMUSB_ENDP_CTRL_SIZE; } /*check if xfer is >= 0, return if its -1*/ if(wdCount < 0) /*check xfer == -1 or xfer >=0 */ return; else { if(wdCount == 0) /*should we generate a zero length packet*/ { *ptrCount = -1; /*end future xfers*/ stm32_usb_endp_send_packet(wdEndP,\ ptrBuf,\ wdEndPSize,\ 0); } else { if(wdCount >= wdEndPSize) wdCount = wdEndPSize; stm32_usb_endp_send_packet(wdEndP,\ ptrBuf,\ wdEndPSize,\ wdCount); /*update variables*/ *(ptrPtrBuf) = (unsigned char *)(ptrBuf + wdCount); *(ptrCount) -= wdCount; /* ******************************************************* *prep for a zlp on the next interrupt if *this is the last transfer (wdCount == 0) and *the transfer size matches the endpoint buffer size ******************************************************* */ if(wdCount == wdEndPSize && (*ptrCount) == 0) ; /*do nothing here zlp on next int*/ else { if((*ptrCount) == 0) (*ptrCount) = -1; else ; /*do nothing transfer continues*/ } } } } /* ********************************************************************** *description: tx transfer queue routine *input: unsigned int wdEndP = endpoint number * unsigned char *ptrBuffer = pointer to buffer * unsigned int wdSize = buffer size in bytes *notes: store pointers to the global record. stores count of * records ********************************************************************** */ void stm32_usb_queue_IN_transfers(unsigned int wdEndP,\ unsigned char *ptrBuffer,\ unsigned int wdSize) { if(wdEndP == STMUSB_ENDP_USER_NUM) { strUsbPktXfer.ptrUserBuf = ptrBuffer; strUsbPktXfer.wdUserSize = wdSize; } else { strUsbPktXfer.ptrCtrlBuf = ptrBuffer; strUsbPktXfer.wdCtrlSize = wdSize; } } /* ********************************************************************** *description: setup particular portA pin to an input, so its no * longer driving the pin *input: unsigned int wdPin = PAx pin number to use. it must be * portA block pins ONLY *note: this should be called inside the usb isr on RESET flag *implemented: using three position jumper pin with all three wire * wrapped linked - and a jumper from PAx to one of the * positions ********************************************************************** */ void stm32_usb_connect_pullup(unsigned int wdPin) { volatile unsigned int wdTemp, wdMask; wdTemp = (*PTR_STM32_GPIO_A_CRL); wdTemp &= ~(0x0f << (wdPin << 2)); wdMask = ((STM32_GPIO_CNF_IN_FLOATING << 2) | \ STM32_GPIO_MODE_INPUT_MODE) << (wdPin << 2); (*PTR_STM32_GPIO_A_CRL) = (wdMask | wdTemp); } /* ********************************************************************** *description: I am using PAX (external wire to Dplus pin on J6 facing * the USB connector) to pull up the line to trigger the * USB HOST enumeration. * setup PAX to generic output. pull it high *input: unsigned int wdPin = PAx pin number to use. it must be * portA block pins ONLY ********************************************************************** */ void stm32_usb_disconnect_pullup(unsigned int wdPin) { volatile unsigned int wdTemp, wdMask; (*PTR_STM32_GPIO_A_BSRR) = ((1 << wdPin) << 16); wdTemp = (*PTR_STM32_GPIO_A_CRL); wdTemp &= ~(0x0f << (wdPin << 2)); wdMask = ((STM32_GPIO_CNF_OUT_GEN_PUSH_PULL << 2) | \ STM32_GPIO_MODE_OUTPUT_2MHZ) << (wdPin << 2); (*PTR_STM32_GPIO_A_CRL) = (wdMask | wdTemp); } /* ********************************************************************** *description: usb user out isr handler *input: unsigned int wdEndP * *execute OUT transaction:COMMAND format is as follows: * byte 0 = command * byte 1 = length of data in bytes * byte 2 = 0xed * byte 3 = 0xfe * byte 4 = 32bit address * byte 5 = * byte 6 = * byte 7 = ********************************************************************** */ void stm32_usb_isr_user_out(unsigned int wdEndP) { unsigned char sbBuffer[STMUSB_ENDP_CTRL_MAX_SIZE + 1]; unsigned int wdTemp, wdSize, *ptrEndP, *ptrAddr; unsigned char *ptrTemp; /* ********************************************************************** *handle usb endpoints. clear endpoint interrupts *first grab a snapshot of the interrupt status register ********************************************************************** */ ptrEndP = (unsigned int *)PTR_STM32_USB_EP0R; ptrEndP += wdEndP; wdTemp = (*ptrEndP); if(((wdTemp & STM32_USB_EPR_CTR_RX) != STM32_USB_EPR_CTR_RX) ||\ ((wdTemp & STM32_USB_EPR_EA_MASK)!= wdEndP)) return; else { /* ************************************************************** *receive data if available ************************************************************** */ wdSize = stm32_usb_endp_get_packet(wdEndP,\ sbBuffer,\ STMUSB_ENDP_USER_SIZE); stm32_usb_clear_epr_ctr(wdEndP, STM32_USB_EPR_CTR_RX); } #ifdef DEBUG wdTemp = (STMUSB_ENDP_CTRL_MAX_SIZE - wdSize); ptrTemp = (unsigned char *)(sbBuffer + wdSize); if(((wdSize << 1) + wdSize) < wdTemp) { unsigned int wdCount; for(wdCount=0; wdCount<wdSize; wdCount++) { wdTemp = sbBuffer[wdCount]; tclib_itoa(wdTemp,(unsigned char *) ptrTemp); ptrTemp += 2; (*ptrTemp) =' '; ptrTemp += 1; } *(ptrTemp - 1) = 0; tclib_printf("%s\n", (unsigned int)(sbBuffer + wdSize)); } #endif /* ********************************************************************** *process packet ********************************************************************** */ tclib_memcpy((unsigned char *)&wdTemp, sbBuffer+4, sizeof(int)); ptrAddr = (unsigned char *)wdTemp; if(sbBuffer[2] != 0xed || sbBuffer[3] != 0xfe) { tclib_printf("packet signature missing\n", 0); return; } if(sbBuffer[0] == 0) /*IN*/ { wdTemp = *ptrAddr; /*retrieve value*/ wdTemp = \ stm32_usb_endp_send_packet(wdEndP,\ (unsigned char *)&wdTemp,\ STMUSB_ENDP_USER_SIZE,\ sizeof(int)); if(wdTemp != sizeof(int)) { tclib_printf("isr_user_out: send_packet_error\n", 0); return; } stm32_usb_cancel_queued_IN_transfers(wdEndP); #ifdef IGNORE /*wait until transfer is complete before continuing*/ do { wdTemp = *(PTR_STM32_USB_EP0R + wdEndP); wdTemp &= STM32_USB_EPR_CTR_TX; }while(wdTemp != STM32_USB_EPR_CTR_TX); #endif /*clear TX interrupt*/ stm32_usb_clear_epr_ctr(wdEndP, STM32_USB_EPR_CTR_TX); } else { if(sbBuffer[0] != 1) /*OUT*/ return; wdTemp = sbBuffer[1]; wdTemp -= 8; /*subtract command packet*/ wdSize -= 8; /*ditto*/ if(wdSize != wdTemp) /*i need to get the data*/ { /*wait until data is available before continuing*/ do { wdTemp = *(PTR_STM32_USB_EP0R + wdEndP); wdTemp &= STM32_USB_EPR_CTR_RX; }while(wdTemp != STM32_USB_EPR_CTR_RX); wdSize = stm32_usb_endp_get_packet(wdEndP, \ sbBuffer,\ STMUSB_ENDP_USER_SIZE); /*clear RX interrupt*/ stm32_usb_clear_epr_ctr(wdEndP, STM32_USB_EPR_CTR_RX); if(wdSize >= sizeof(int)) tclib_memcpy((unsigned char *)&wdTemp, sbBuffer, sizeof(int)); else return; } else tclib_memcpy((unsigned char *)&wdTemp, sbBuffer+8, sizeof(int)); /*set value*/ *ptrAddr = wdTemp; } } /* ********************************************************************** *description: usb user in isr handler *input: unsigned int wdEndP ********************************************************************** */ void stm32_usb_isr_user_in(unsigned int wdEndP) { volatile unsigned int *ptrTemp, wdTemp; /* ********************************************************************** *handle usb endpoints. clear endpoint interrupts *first grab a snapshot of the interrupt status register ********************************************************************** */ ptrTemp = PTR_STM32_USB_EP0R; ptrTemp += wdEndP; wdTemp = (*ptrTemp); if(((wdTemp & STM32_USB_EPR_CTR_TX) != STM32_USB_EPR_CTR_TX) ||\ ((wdTemp & STM32_USB_EPR_EA_MASK)!= wdEndP)) return; else { /* ************************************************************** *transmit data if available ************************************************************** */ stm32_usb_dequeue_IN_transfers(wdEndP); stm32_usb_clear_epr_ctr(wdEndP, STM32_USB_EPR_CTR_TX); } } /* ********************************************************************** *description: usb control in isr handler ********************************************************************** */ void stm32_usb_isr_control_in(void) { volatile unsigned int wdTemp; /* ********************************************************************** *handle usb endpoints. clear endpoint interrupts *first grab a snapshot of the interrupt status register ********************************************************************** */ wdTemp = (*PTR_STM32_USB_EP0R); if((wdTemp & STM32_USB_EPR_CTR_TX) != STM32_USB_EPR_CTR_TX) return; else { /* ************************************************************** *transmit data if available *data toggle is set by caller to stm32_queue_IN_transfers to 1 *subsequent toggles are done by the hardware ************************************************************** */ stm32_usb_dequeue_IN_transfers(0); stm32_usb_clear_epr_ctr(0, STM32_USB_EPR_CTR_TX); } } /* ********************************************************************** *description: usb control out isr handler *output: none ********************************************************************** */ void stm32_usb_isr_control_out(void) { unsigned char sbBuffer[STMUSB_ENDP_CTRL_MAX_SIZE + 1]; unsigned char *ptrTemp; volatile unsigned int wdTemp, wdSize, wdIndex, wdType; /* ********************************************************************** *handle usb endpoints. clear endpoint interrupts *first grab a snapshot of the interrupt status register ********************************************************************** */ wdTemp = (*PTR_STM32_USB_EP0R); #ifdef IGNORE if(((wdTemp & STM32_USB_EPR_CTR_RX) != STM32_USB_EPR_CTR_RX) ||\ ((wdTemp & STM32_USB_EPR_SETUP) != STM32_USB_EPR_SETUP)) #endif if((wdTemp & STM32_USB_EPR_CTR_RX) != STM32_USB_EPR_CTR_RX) return; else { /* ************************************************************** *receive data if available ************************************************************** */ wdSize = stm32_usb_endp_get_packet(0, \ sbBuffer,\ STMUSB_ENDP_CTRL_MAX_SIZE); stm32_usb_clear_epr_ctr(0, STM32_USB_EPR_CTR_RX); } #ifdef DEBUG if(wdSize >= 8) { wdTemp = (STMUSB_ENDP_CTRL_MAX_SIZE - wdSize); ptrTemp = (unsigned char *)(sbBuffer + wdSize); if(((wdSize << 1) + wdSize) < wdTemp) { unsigned int wdCount; for(wdCount=0; wdCount<wdSize; wdCount++) { wdTemp = sbBuffer[wdCount]; tclib_itoa(wdTemp, (unsigned char *)ptrTemp); ptrTemp += 2; (*ptrTemp) =' '; ptrTemp += 1; } *(ptrTemp - 1) = 0; tclib_printf("%s\n", (unsigned int)(sbBuffer + wdSize)); } } #endif /* ********************************************************************** *evaluate SETUP command packet ********************************************************************** */ wdTemp = sbBuffer[USBDEV_SETUP_REQTYPE_OFFSET]; if((wdTemp & USBDEV_SETUP_REQTYPE_DIR_MASK) == USBDEV_SETUP_REQTYPE_DIR_H_TO_D) { /*these are SET commands*/ switch(sbBuffer[USBDEV_SETUP_REQUEST_OFFSET]) { case USBDEV_REQ_SET_ADDRESS: { /*return ZLP = zero length packet, before enabling dev*/ stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); wdTemp = \ stm32_usb_endp_send_packet(0,\ (unsigned char *)&wdTemp,\ STMUSB_ENDP_CTRL_SIZE,\ 0); if(wdTemp != 0) { tclib_printf("isr_control_out: send_packet_error\n", 0); return; } /*wait until transfer is complete before setting address*/ do { wdTemp = *(PTR_STM32_USB_EP0R + 0); wdTemp &= STM32_USB_EPR_CTR_TX; }while(wdTemp != STM32_USB_EPR_CTR_TX); /*clear TX interrupt*/ stm32_usb_clear_epr_ctr(0, STM32_USB_EPR_CTR_TX); /*alert user and set the bus address*/ wdTemp = sbBuffer[USBDEV_SETUP_VALUE_OFFSET]; (*PTR_STM32_USB_DADDR) = ((STM32_USB_DADDR_EF) | wdTemp); tclib_printf("usb device address = %x\n", wdTemp); break; } case USBDEV_REQ_SET_CONFIGURATION: { /*return ZLP = zero length packet*/ stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); wdTemp = \ stm32_usb_endp_send_packet(0,\ (unsigned char *)&wdTemp,\ STMUSB_ENDP_CTRL_SIZE,\ 0); if(wdTemp != 0) { tclib_printf("isr_control_out: send_packet_error\n", 0); return; } break; } case USBDEV_REQ_SET_FEATURE: /*not supported*/ case USBDEV_REQ_SET_INTERFACE: /*not supported*/ case USBDEV_REQ_CLEAR_FEATURE: /*not supported*/ case USBDEV_REQ_SET_DESCRIPTOR: /*not supported*/ default: { break; } } } else { /*these are GET* commands*/ switch(sbBuffer[USBDEV_SETUP_REQUEST_OFFSET]) { case USBDEV_REQ_GET_DESCRIPTOR: { wdType = (sbBuffer[(USBDEV_SETUP_VALUE_OFFSET + 1)] & 0x0ff); wdIndex = (sbBuffer[USBDEV_SETUP_VALUE_OFFSET] & 0x0ff); wdSize = (sbBuffer[USBDEV_SETUP_LENGTH_OFFSET] & 0x0ff); /* ********************************************* determine descriptor type ********************************************* */ switch(wdType) { case USBDEV_DESCR_TYPE_DEVICE: { ptrTemp = (unsigned char *)sbDevDescr; break; } case USBDEV_DESCR_TYPE_CONFIGURATION: { ptrTemp = (unsigned char *)sbConfDescr; break; } case USBDEV_DESCR_TYPE_STRING: { wdTemp = swUsbDevDescr[wdIndex]; ptrTemp = (unsigned char *)wdTemp; break; } default: { tclib_printf("unsupported descriptor type = %x\n", wdType); break; } } stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); stm32_usb_queue_IN_transfers(0,\ (unsigned char *)ptrTemp,\ wdSize); stm32_usb_dequeue_IN_transfers(0); break; } case USBDEV_REQ_GET_STATUS: { wdTemp = 0; stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); wdTemp = \ stm32_usb_endp_send_packet(0,\ (unsigned char *)&wdTemp,\ STMUSB_ENDP_CTRL_SIZE,\ 2); if(wdTemp != 2) { tclib_printf("isr_control_out: send_packet_error\n", 0); return; } break; } case USBDEV_REQ_GET_CONFIGURATION: { wdTemp = 0; stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); wdTemp = \ stm32_usb_endp_send_packet(0,\ (unsigned char *)&wdTemp,\ STMUSB_ENDP_CTRL_SIZE,\ 1); if(wdTemp != 1) { tclib_printf("isr_control_out: send_packet_error\n", 0); return; } break; } case USBDEV_REQ_SYNTH_FRAME: /*doesnt apply*/ case USBDEV_REQ_GET_INTERFACE: /*doesnt apply*/ default: { break; } } } } /* ********************************************************************** *description: first stage usb isr handler ********************************************************************** */ void stm32_usb_isr(void) { volatile unsigned int wdIrqStat, wdEndP; /* ********************************************************************** *is this a RESET interrupt? *handle it. re-initialize the usb endpoint sub-system ********************************************************************** */ wdIrqStat = (*PTR_STM32_USB_ISTR); if((wdIrqStat & STM32_USB_ISTR_RESET) == STM32_USB_ISTR_RESET) { wdUsbState = STM32_USB_ISTR_RESET; (*PTR_STM32_USB_ISTR) = ~wdUsbState; /*disable interrupts */ asm("cpsid i"); #ifdef DEBUG (*PTR_STM32_TIM3_CNT) =0; #endif stm32_usb_post_reset_setup(STMUSB_ENDP_USER_NUM,\ STM32_USB_EPR_EP_TYPE_BULK, STMUSB_ENDP_USER_SIZE); #ifdef DEBUG wdUsbState = (*PTR_STM32_TIM3_CNT); #endif /*enable interrupts anew*/ asm("cpsie i"); #ifdef DEBUG tclib_printf("--------RESET----------- %duS\n", wdUsbState); #else tclib_printf("--------RESET----------- \n", 0); #endif wdUsbState = 0; } /* ********************************************************************** *is this a CORRECT TRANSFER interrupt? *handle it. process the data ********************************************************************** */ wdIrqStat = (*PTR_STM32_USB_ISTR); if((wdIrqStat & STM32_USB_ISTR_CTR) == STM32_USB_ISTR_CTR) { wdEndP = (wdIrqStat & STM32_USB_ISTR_EP_ID_MASK); wdEndP >>= STM32_USB_ISTR_EP_ID_OFFSET; /* ************************************************************** *control endpoint can go in either direction = IN or OUT * its complexity requires a function that handles a * a state machine *user endpoint can go in either direction = IN or OUT * this is simple data exchange nothing more ************************************************************** */ switch((wdIrqStat & STM32_USB_ISTR_DIR_MASK)) { case STM32_USB_ISTR_DIR_IN: { if(wdEndP == 0) stm32_usb_isr_control_in(); else stm32_usb_isr_user_in(wdEndP); break; } case STM32_USB_ISTR_DIR_OUT: { stm32_usb_cancel_queued_IN_transfers(0); if(wdEndP == 0) stm32_usb_isr_control_out(); else stm32_usb_isr_user_out(wdEndP); break; } } } } /* ********************************************************************** *description: usb initialization function *note: setup the usb function then setup the endpoint for * control (0) and user endpoint (interrupt for now) ********************************************************************** */ void stm32_usb_init(void) { volatile unsigned int wdTemp; /* ********************************************************************** *clear out record ********************************************************************** */ tclib_memset((unsigned char *)&strUsbPktXfer, -1, sizeof(strUsbPktXfer)); /* ********************************************************************** *now setup the usb interface ********************************************************************** */ wdTemp = STM32_USB_CNTR_CTRM; wdTemp |= STM32_USB_CNTR_RESETM; //wdTemp |= STM32_USB_CNTR_SUSPM; #ifdef ISOCHRONOUS wdTemp |= STM32_USB_CNTR_SOFM; #endif (*PTR_STM32_USB_CNTR) = wdTemp; stm32_usb_pre_reset_setup(STMUSB_ENDP_USER_NUM,\ STM32_USB_EPR_EP_TYPE_BULK, STMUSB_ENDP_USER_SIZE); wdTemp = ~(STM32_USB_ISTR_RESET | STM32_USB_ISTR_SUSP); (*PTR_STM32_USB_ISTR) = wdTemp; stm32_nvic_install_isr(USB_NVIC_IRQ_NUM, (unsigned int)stm32_usb_isr); } /* ********************************************************************** *description: usb routine to be executed after host bus initiated * reset *inputs: unsigned int wdUserEndPNum = user endpoint number * unsigned int wdUserEndPType= endpoint type * STM32_USB_EPR_EP_TYPE_BULK * STM32_USB_EPR_EP_TYPE_CONTROL * STM32_USB_EPR_EP_TYPE_ISO * STM32_USB_EPR_EP_TYPE_INTERRUPT * unsigned int wdUserEndPSize= 8,16,32,64 etc *note: crucial to call stm32_usb_clear_epr_cnt to clear rx and * tx SPURIOUS interrupts!!! ********************************************************************** */ void stm32_usb_post_reset_setup(unsigned int wdUserEndPNum,\ unsigned int wdUserEndPType,\ unsigned int wdUserEndPSize) { stm32_usb_set_epr_dtog(0,\ 0,\ STM32_USB_EPR_DTOG_RX_OFFSET); stm32_usb_set_epr_dtog(0,\ 1,\ STM32_USB_EPR_DTOG_TX_OFFSET); stm32_usb_set_epr_stat(0,\ STM32_USB_EPR_STAT_RX_VALID,\ STM32_USB_EPR_STAT_RX_OFFSET); stm32_usb_set_epr_stat(0,\ STM32_USB_EPR_STAT_TX_NAK,\ STM32_USB_EPR_STAT_TX_OFFSET); stm32_usb_cancel_queued_IN_transfers(0); stm32_usb_clear_epr_ctr(0, STM32_USB_EPR_CTR_RX); stm32_usb_clear_epr_ctr(0, STM32_USB_EPR_CTR_TX); stm32_usb_enable_endp(0, STM32_USB_EPR_EP_TYPE_CONTROL); stm32_usb_set_epr_stat(wdUserEndPNum,\ STM32_USB_EPR_STAT_RX_VALID,\ STM32_USB_EPR_STAT_RX_OFFSET); stm32_usb_set_epr_stat(wdUserEndPNum,\ STM32_USB_EPR_STAT_TX_NAK,\ STM32_USB_EPR_STAT_TX_OFFSET); stm32_usb_cancel_queued_IN_transfers(wdUserEndPNum); stm32_usb_clear_epr_ctr(wdUserEndPNum, STM32_USB_EPR_CTR_RX); stm32_usb_clear_epr_ctr(wdUserEndPNum, STM32_USB_EPR_CTR_TX); stm32_usb_enable_endp(wdUserEndPNum, wdUserEndPType); /*set and enable device addr*/ (*PTR_STM32_USB_DADDR) = ((STM32_USB_DADDR_EF) | 0); } /* ********************************************************************** *description: usb routine to be executed before host bus initiated * reset *inputs: unsigned int wdUserEndPNum = user endpoint number * unsigned int wdUserEndPType= endpoint type * STM32_USB_EPR_EP_TYPE_BULK * STM32_USB_EPR_EP_TYPE_CONTROL * STM32_USB_EPR_EP_TYPE_ISO * STM32_USB_EPR_EP_TYPE_INTERRUPT * unsigned int wdUserEndPSize= 8,16,32,64 etc ********************************************************************** */ void stm32_usb_pre_reset_setup(unsigned int wdUserEndPNum,\ unsigned int wdUserEndPType,\ unsigned int wdUserEndPSize) { volatile unsigned int *ptrTemp, wdLclPktAddr; /*set usb buffer table address*/ (*PTR_STM32_USB_BTABLE) = 0; /*offset ?*/ /*clear out all packet buffer memory*/ tclib_memset((unsigned char *)PTR_STM32_USB_EPRAM, 0, 0x200); /*initialize control endpoint first - calculate pk buffer local addr*/ ptrTemp = (volatile unsigned int *)PTR_STM32_USB_EPRAM; wdLclPktAddr = ((8 * 8) + 0); /*offset ?*/ /*control endp*/ wdLclPktAddr = stm32_usb_build_endp_descriptors(0,\ STM32_USB_EPR_EP_TYPE_CONTROL,\ STMUSB_ENDP_CTRL_SIZE,\ (unsigned int *)ptrTemp,\ wdLclPktAddr); stm32_usb_enable_endp(0, STM32_USB_EPR_EP_TYPE_CONTROL); /*user chosen endp*/ ptrTemp += (4 * STMUSB_ENDP_USER_NUM); wdLclPktAddr = stm32_usb_build_endp_descriptors(wdUserEndPNum,\ wdUserEndPType,\ wdUserEndPSize,\ (unsigned int *)ptrTemp,\ wdLclPktAddr); stm32_usb_enable_endp(wdUserEndPNum, wdUserEndPType); /*finally set and enable device addr*/ (*PTR_STM32_USB_DADDR) = ((STM32_USB_DADDR_EF) | 0); /*cancel all transfers*/ stm32_usb_cancel_queued_IN_transfers(0); stm32_usb_cancel_queued_IN_transfers(wdUserEndPNum); } /* ********************************************************************** *description: usb routine to build a particular endpoints descriptors * *inputs: unsigned int wdEndPNum = endpoint number * unsigned int wdEndPType= endpoint type * STM32_USB_EPR_EP_TYPE_BULK * STM32_USB_EPR_EP_TYPE_CONTROL * STM32_USB_EPR_EP_TYPE_ISO * STM32_USB_EPR_EP_TYPE_INTERRUPT * unsigned int wdEndPSize= 8,16,32,64 etc *output: wdLclPktAddr = usb local packet address *NOTE: packet descriptor size must be pktsize+2 to account for * the two byte crc the host writes ********************************************************************** */ int stm32_usb_build_endp_descriptors(unsigned int wdEndPNum,\ unsigned int wdEndPType,\ unsigned int wdEndPSize,\ unsigned int *ptrDescAddr,\ unsigned int wdLclPktAddr) { volatile unsigned int wdTemp; /*set tx buffer addr*/ (*ptrDescAddr++) = wdLclPktAddr; wdLclPktAddr += wdEndPSize; /*set tx count*/ (*ptrDescAddr++) = wdEndPSize; /*adjust wdEndPSize to account for extra 2 bytes of crc*/ /*set rx buffer addr*/ wdEndPSize += 2; (*ptrDescAddr++) = wdLclPktAddr; wdLclPktAddr += wdEndPSize; if(wdEndPSize >= 64) { wdTemp = STM32_USB_COUNT_RX_BL_SIZE_32BYTE; wdTemp |= ((wdEndPSize >> 6) << STM32_USB_COUNT_RX_NUM_BLOCK_OFFSET); } else { wdTemp = STM32_USB_COUNT_RX_BL_SIZE_2BYTE; wdTemp |= ((wdEndPSize >> 1) << STM32_USB_COUNT_RX_NUM_BLOCK_OFFSET); } /*set rx count*/ (*ptrDescAddr++) = wdTemp; return wdLclPktAddr; } /* ********************************************************************** *description: usb endpoint send packet routine * *inputs: unsigned int wdEndPNum = endpoint number * unsigned int wdEndPSize= 8,16,32,64 etc * char *ptrBuffer= pointer to buffer * unsigned int wdBytelen = byte length *output: number of bytes sent on success -1 on error (timeout) *note: clears CTR_TX flag ********************************************************************** */ int stm32_usb_endp_send_packet(unsigned int wdEndPNum,\ unsigned char *ptrBuffer,\ unsigned int wdEndPSize,\ unsigned int wdBytelen) { volatile unsigned int wdTemp, *ptrTemp, wdCount, *ptrDst, wdSize; unsigned short int *ptrSrc; ptrSrc = (unsigned short int *)ptrBuffer; if(wdBytelen > wdEndPSize) wdCount = wdEndPSize; else wdCount = wdBytelen; wdSize = wdCount; /* ************************************************************** *copy the data into the buffer - retrieve address *update transmission count *now enable transmission control reg - retrieve value *prepare for transmission - set status bits ************************************************************** */ ptrTemp = (volatile unsigned int *)PTR_STM32_USB_EPRAM; ptrTemp += (4 * wdEndPNum); wdTemp = (*ptrTemp); wdTemp = (PTR_STM32_USB_EPRAM + (wdTemp << 1)); ptrTemp += 1; /*tx count descriptor*/ (*ptrTemp) = wdCount; ptrDst = (unsigned int *)wdTemp; while(wdCount > 0) { *(ptrDst++) = (int)(*(ptrSrc++)); if(wdCount >= 2) wdCount -= 2; else wdCount -= 1; } /* ********************************************************************** *prepare for new transmission at a later time, set valid bits ********************************************************************** */ stm32_usb_set_epr_stat(wdEndPNum,\ STM32_USB_EPR_STAT_TX_VALID,\ STM32_USB_EPR_STAT_TX_OFFSET); return wdSize; } /* ********************************************************************** *description: usb endpoint get packet routine * *inputs: unsigned int wdEndPNum = endpoint number * char *ptrBuffer= pointer to buffer * unsigned int wdBytelen = byte length *output: int = size of data posted *note: clears CTR_RX flag ********************************************************************** */ int stm32_usb_endp_get_packet(unsigned int wdEndPNum,\ unsigned char *ptrBuffer,\ unsigned int wdBytelen) { volatile unsigned int wdTemp, *ptrTemp, wdCount, wdSize ,*ptrSrc; unsigned short int *ptrDst; /* ********************************************************************** *get byte size *copy over data ********************************************************************** */ ptrTemp = (volatile unsigned int *)PTR_STM32_USB_EPRAM; ptrTemp += (4 * wdEndPNum); ptrTemp += 2; wdTemp = (*ptrTemp); wdTemp = (PTR_STM32_USB_EPRAM + (wdTemp << 1)); wdCount = *(ptrTemp +1); wdCount &= STM32_USB_COUNT_RX_MASK; wdSize = wdCount; if(wdCount > wdBytelen) wdCount = wdBytelen; ptrSrc = (unsigned int *)wdTemp; ptrDst = (unsigned short int *)ptrBuffer; while(wdCount > 0) { *(ptrDst++) = (short int)(*(ptrSrc++)); if(wdCount >= 2) wdCount -= 2; else wdCount -= 1; } /* ********************************************************************** *prepare for new reception at a later time, set valid bits ********************************************************************** */ stm32_usb_set_epr_stat(wdEndPNum,\ STM32_USB_EPR_STAT_RX_VALID,\ STM32_USB_EPR_STAT_RX_OFFSET); return wdSize; }
Monday, August 12, 2013
IE_stm32_usb.c
Subscribe to:
Post Comments (Atom)
No comments:
Post a Comment