Monday, August 12, 2013

IE_stm32_usb.c

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





No comments:

Post a Comment