/*
**********************************************************************
*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