test

Dependencies:   mbed

Fork of USBMouse by Simon Ford

USBMouse/usbdc.cpp

Committer:
USER10
Date:
2018-01-19
Revision:
2:554ad697ebbb
Parent:
1:eb68149218fd

File content as of revision 2:554ad697ebbb:

/* usbdc.cpp */
/* USB device controller */
/* Copyright (c) Phil Wright 2008 */

#include "mbed.h"  
#include "usbdc.h"
#include "cmsis.h"

#ifdef  TARGET_LPC2368
#undef  LPC_USB_BASE
#define LPC_USB_BASE (0xFFE0C000) /* TODO */
#endif         

/* Power Control for Peripherals register */
#define PCUSB      ((unsigned long)1<<31)

/* USB Clock Control register */
#define DEV_CLK_EN ((unsigned long)1<<1)
#define AHB_CLK_EN ((unsigned long)1<<4)

/* USB Clock Status register */
#define DEV_CLK_ON ((unsigned long)1<<1)
#define AHB_CLK_ON ((unsigned long)1<<4)

/* USB Device Interupt registers */
#define FRAME      ((unsigned long)1<<0)
#define EP_FAST    ((unsigned long)1<<1)
#define EP_SLOW    ((unsigned long)1<<2)
#define DEV_STAT   ((unsigned long)1<<3)
#define CCEMPTY    ((unsigned long)1<<4)
#define CDFULL     ((unsigned long)1<<5)
#define RxENDPKT   ((unsigned long)1<<6)
#define TxENDPKT   ((unsigned long)1<<7)
#define EP_RLZED   ((unsigned long)1<<8)
#define ERR_INT    ((unsigned long)1<<9)

/* Endpoint Interrupt Registers */
#define EP(endpoint) (1<<endpoint)

/* USB Control register */
#define RD_EN (1<<0)
#define WR_EN (1<<1)
#define LOG_ENDPOINT(endpoint) ((endpoint>>1)<<2)

/* USB Receive Packet Length register */
#define DV      ((unsigned long)1<<10)
#define PKT_RDY ((unsigned long)1<<11)
#define PKT_LNGTH_MASK (0x3ff)

/* Serial Interface Engine (SIE) */
#define SIE_WRITE   (0x01)
#define SIE_READ    (0x02)
#define SIE_COMMAND (0x05)
#define SIE_CMD_CODE(phase, data) ((phase<<8)|(data<<16))

/* SIE Command codes */
#define SIE_CMD_SET_ADDRESS        (0xD0)
#define SIE_CMD_CONFIGURE_DEVICE   (0xD8)
#define SIE_CMD_SET_MODE           (0xF3)
#define SIE_CMD_READ_FRAME_NUMBER  (0xF5)
#define SIE_CMD_READ_TEST_REGISTER (0xFD)
#define SIE_CMD_SET_DEVICE_STATUS  (0xFE)
#define SIE_CMD_GET_DEVICE_STATUS  (0xFE)
#define SIE_CMD_GET_ERROR_CODE     (0xFF)
#define SIE_CMD_READ_ERROR_STATUS  (0xFB)

#define SIE_CMD_SELECT_ENDPOINT(endpoint)                 (0x00+endpoint)
#define SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint) (0x40+endpoint)
#define SIE_CMD_SET_ENDPOINT_STATUS(endpoint)             (0x40+endpoint)

#define SIE_CMD_CLEAR_BUFFER    (0xF2)
#define SIE_CMD_VALIDATE_BUFFER (0xFA)

/* SIE Device Status register */
#define SIE_DS_CON    (1<<0)
#define SIE_DS_CON_CH (1<<1)
#define SIE_DS_SUS    (1<<2)
#define SIE_DS_SUS_CH (1<<3)
#define SIE_DS_RST    (1<<4)

/* SIE Device Set Address register */
#define SIE_DSA_DEV_EN  (1<<7)

/* SIE Configue Device register */
#define SIE_CONF_DEVICE (1<<0)

/* Select Endpoint register */
#define SIE_SE_FE       (1<<0)
#define SIE_SE_ST       (1<<1)
#define SIE_SE_STP      (1<<2)
#define SIE_SE_PO       (1<<3)
#define SIE_SE_EPN      (1<<4)
#define SIE_SE_B_1_FULL (1<<5)
#define SIE_SE_B_2_FULL (1<<6)

/* Set Endpoint Status command */
#define SIE_SES_ST      (1<<0)
#define SIE_SES_DA      (1<<5)
#define SIE_SES_RF_MO   (1<<6)
#define SIE_SES_CND_ST  (1<<7)

usbdc *usbdc::instance;

usbdc::usbdc()
{
#ifdef TARGET_LPC1768
    LPC_SC->USBCLKCFG=5; /* TODO */
#endif

    /* Enable power to USB device controller */
    LPC_SC->PCONP |= PCUSB;

    /* Enable USB clocks */
    LPC_USB->USBClkCtrl |= DEV_CLK_EN | AHB_CLK_EN;
    while (LPC_USB->USBClkSt != (DEV_CLK_ON | AHB_CLK_ON));

    /* Configure pins P0.29 and P0.30 to be USB D+ and USB D- */
    LPC_PINCON->PINSEL1 &= 0xc3ffffff;
    LPC_PINCON->PINSEL1 |= 0x14000000;

#ifdef ENABLE_VBUS    
    /* Configure pin P1.30 to be VBUS */
    LPC_PINCON->PINSEL3 &= 0xcfffffff;
    LPC_PINCON->PINSEL3 |= 0x20000000;
    
    /* Configure pin P1.30 to have pull-down */
    LPC_PINCON->PINMODE3 |= 0x30000000;
#endif
        
    /* Configure pin P2.9 to be Connect */
    LPC_PINCON->PINSEL4 &= 0xfffcffff;
    LPC_PINCON->PINSEL4 |= 0x00040000;
    
    /* Connect must be low for at least 2.5uS */
    wait_ms(1);
    
    /* Attach IRQ */
    instance = this;
    NVIC_SetVector(USB_IRQn, (uint32_t)&_usbisr);
    NVIC_EnableIRQ(USB_IRQn); 

    /* Enable device interrupts */
    enableEvents();
}

void usbdc::connect(void)
{
    /* Connect USB device */
    unsigned char status;
    
    status = getDeviceStatus();
    setDeviceStatus(status | SIE_DS_CON);
}

void usbdc::disconnect(void)
{
    /* Disconnect USB device */
    unsigned char status;
    
    status = getDeviceStatus();
    setDeviceStatus(status & ~SIE_DS_CON);
}

void usbdc::SIECommand(unsigned long command)
{
    /* The command phase of a SIE transaction */
    LPC_USB->USBDevIntClr = CCEMPTY;
    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_COMMAND, command);
    while (!(LPC_USB->USBDevIntSt & CCEMPTY)); 
}

void usbdc::SIEWriteData(unsigned char data)
{
    /* The data write phase of a SIE transaction */
    LPC_USB->USBDevIntClr = CCEMPTY;
    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_WRITE, data);
    while (!(LPC_USB->USBDevIntSt & CCEMPTY)); 
}

unsigned char usbdc::SIEReadData(unsigned long command)
{
    /* The data read phase of a SIE transaction */
    LPC_USB->USBDevIntClr = CDFULL;
    LPC_USB->USBCmdCode = SIE_CMD_CODE(SIE_READ, command);
    while (!(LPC_USB->USBDevIntSt & CDFULL));
    return (unsigned char)LPC_USB->USBCmdData;
}

void usbdc::setDeviceStatus(unsigned char status)
{
    /* Write SIE device status register */
    SIECommand(SIE_CMD_SET_DEVICE_STATUS);
    SIEWriteData(status);
}

unsigned char usbdc::getDeviceStatus(void)
{
    /* Read SIE device status register */
    SIECommand(SIE_CMD_GET_DEVICE_STATUS);
    return SIEReadData(SIE_CMD_GET_DEVICE_STATUS);
}

void usbdc::setAddress(unsigned char address)
{
    /* Write SIE device address register */
    SIECommand(SIE_CMD_SET_ADDRESS);
    SIEWriteData((address & 0x7f) | SIE_DSA_DEV_EN);
}

unsigned char usbdc::selectEndpoint(unsigned char endpoint)
{
    /* SIE select endpoint command */
    SIECommand(SIE_CMD_SELECT_ENDPOINT(endpoint));
    return SIEReadData(SIE_CMD_SELECT_ENDPOINT(endpoint));
}

#if 1
unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
{
    /* SIE select endpoint and clear interrupt command */
    /* Using the Select Endpoint / Clear Interrupt SIE command does not seem   */
    /* to clear the appropriate bit in EP_INT_STAT? - using EP_INT_CLR instead */
    LPC_USB->USBEpIntClr = EP(endpoint);
    while (!(LPC_USB->USBDevIntSt & CDFULL));
    return (unsigned char)LPC_USB->USBCmdData;
}
#else
unsigned char usbdc::selectEndpointClearInterrupt(unsigned char endpoint)
{
    /* SIE select endpoint and clear interrupt command */
    SIECommand(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
    return SIEReadData(SIE_CMD_SELECT_ENDPOINT_CLEAR_INTERRUPT(endpoint));
}
#endif

unsigned char usbdc::clearBuffer(void)
{
    /* SIE clear buffer command */
    SIECommand(SIE_CMD_CLEAR_BUFFER);
    return SIEReadData(SIE_CMD_CLEAR_BUFFER);
}

void usbdc::validateBuffer(void)
{
    /* SIE validate buffer command */
    SIECommand(SIE_CMD_VALIDATE_BUFFER);
}

void usbdc::setEndpointStatus(unsigned char endpoint, unsigned char status)
{
    /* SIE set endpoint status command */
    SIECommand(SIE_CMD_SET_ENDPOINT_STATUS(endpoint));
    SIEWriteData(status);
}

void usbdc::realiseEndpoint(unsigned char endpoint, unsigned long maxPacket)
{
    /* Realise an endpoint */
    LPC_USB->USBDevIntClr = EP_RLZED;
    LPC_USB->USBReEp |= EP(endpoint);
    LPC_USB->USBEpInd = endpoint;
    LPC_USB->USBMaxPSize = maxPacket;
    
    while (!(LPC_USB->USBDevIntSt & EP_RLZED));
    LPC_USB->USBDevIntClr = EP_RLZED;
    
    /* Clear stall state */
    endpointStallState &= ~EP(endpoint);
}

void usbdc::enableEndpointEvent(unsigned char endpoint)
{
    /* Enable an endpoint interrupt */
    LPC_USB->USBEpIntEn |= EP(endpoint);
}

void usbdc::disableEndpointEvent(unsigned char endpoint)
{
    /* Disable an endpoint interrupt */
    LPC_USB->USBEpIntEn &= ~EP(endpoint);
}

void usbdc::stallEndpoint(unsigned char endpoint)
{
    /* Stall an endpoint */
    if ( (endpoint==EP0IN) || (endpoint==EP0OUT) )
    {
        /* Conditionally stall both control endpoints */
        setEndpointStatus(EP0OUT, SIE_SES_CND_ST);
    }
    else
    {
        setEndpointStatus(endpoint, SIE_SES_ST);
        
        /* Update stall state */
        endpointStallState |= EP(endpoint);
    }
}

void usbdc::unstallEndpoint(unsigned char endpoint)
{
    /* Unstall an endpoint. The endpoint will also be reinitialised */
    setEndpointStatus(endpoint, 0);
    
    /* Update stall state */
    endpointStallState &= ~EP(endpoint);
}

bool usbdc::getEndpointStallState(unsigned char endpoint)
{ 
    /* Returns true if endpoint stalled */
    return endpointStallState & EP(endpoint);
}

void usbdc::configureDevice(void)
{
    /* SIE Configure device command */
    SIECommand(SIE_CMD_CONFIGURE_DEVICE);
    SIEWriteData(SIE_CONF_DEVICE);
}

void usbdc::unconfigureDevice(void)
{
    /* SIE Configure device command */
    SIECommand(SIE_CMD_CONFIGURE_DEVICE);
    SIEWriteData(0);
}    

unsigned long usbdc::endpointRead(unsigned char endpoint, unsigned char *buffer)
{
    /* Read from an OUT endpoint */
    unsigned long size;
    unsigned long i;
    unsigned long data;
    unsigned char offset;
    
    LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | RD_EN;
    while (!(LPC_USB->USBRxPLen & PKT_RDY));
        
    size = LPC_USB->USBRxPLen & PKT_LNGTH_MASK;
        
    offset = 0;
    
    for (i=0; i<size; i++)
    {    
        if (offset==0)
        {    
            /* Fetch up to four bytes of data as a word */
            data = LPC_USB->USBRxData;
        }
    
        /* extract a byte */
        *buffer++ = data>>offset;
    
        /* move on to the next byte */
        offset = (offset + 8) % 32;
    }    
    
    /* Clear RD_EN to cover zero length packet case */
    LPC_USB->USBCtrl=0;
    
    selectEndpoint(endpoint);
    clearBuffer();
    
    return size;
}

void usbdc::endpointWrite(unsigned char endpoint, unsigned char *buffer, unsigned long size)
{
    /* Write to an IN endpoint */
    unsigned long temp, data;
    unsigned char offset;
    
    LPC_USB->USBCtrl = LOG_ENDPOINT(endpoint) | WR_EN;
    
    LPC_USB->USBTxPLen = size;    
    offset = 0;
    data = 0;
    
    if (size>0)
    {
        do {
            /* Fetch next data byte into a word-sized temporary variable */
            temp = *buffer++;
        
            /* Add to current data word */
            temp = temp << offset;
            data = data | temp;
        
            /* move on to the next byte */
            offset = (offset + 8) % 32;
            size--;
        
            if ((offset==0) || (size==0))
            {
                /* Write the word to the endpoint */
                LPC_USB->USBTxData = data;
                data = 0;
            }    
        } while (size>0);
    }

    /* Clear WR_EN to cover zero length packet case */
    LPC_USB->USBCtrl=0;
    
    selectEndpoint(endpoint);
    validateBuffer();
}

void usbdc::enableEvents(void)
{
    /* Enable interrupt sources */
    LPC_USB->USBDevIntEn = EP_SLOW | DEV_STAT;
}

void usbdc::disableEvents(void)
{
    /* Disable interrupt sources */
    LPC_USB->USBDevIntClr = EP_SLOW | DEV_STAT;
}

void usbdc::usbisr(void)
{ 
    unsigned char devStat;
    
    if (LPC_USB->USBDevIntSt & FRAME)
    {
        /* Frame event */
        deviceEventFrame();
        /* Clear interrupt status flag */
        LPC_USB->USBDevIntClr = FRAME;
    }
    
    if (LPC_USB->USBDevIntSt & DEV_STAT)
    {
        /* Device Status interrupt */        
        /* Must clear the interrupt status flag before reading the device status from the SIE */
        LPC_USB->USBDevIntClr = DEV_STAT;    
            
        /* Read device status from SIE */
        devStat = getDeviceStatus();
        
        if (devStat & SIE_DS_RST)
        {
            /* Bus reset */
            deviceEventReset();
        }
    }
    
    if (LPC_USB->USBDevIntSt & EP_SLOW)
    {
        /* (Slow) Endpoint Interrupt */
        
        /* Process each endpoint interrupt */
        if (LPC_USB->USBEpIntSt & EP(EP0OUT))
        {
            if (selectEndpointClearInterrupt(EP0OUT) & SIE_SE_STP)
            {
                /* this is a setup packet */
                endpointEventEP0Setup();
            }
            else
            {
                endpointEventEP0Out();
            }
        }

        if (LPC_USB->USBEpIntSt & EP(EP0IN))
        {
            selectEndpointClearInterrupt(EP0IN);
            endpointEventEP0In();
        }
        
        if (LPC_USB->USBEpIntSt & EP(EP1OUT))
        {
            selectEndpointClearInterrupt(EP1OUT);
            endpointEventEP1Out();
        }    
        
        if (LPC_USB->USBEpIntSt & EP(EP1IN))
        {
            selectEndpointClearInterrupt(EP1IN);
            endpointEventEP1In();
        }    
        
        if (LPC_USB->USBEpIntSt & EP(EP2OUT))
        {
            selectEndpointClearInterrupt(EP2OUT);
            endpointEventEP2Out();
        }    
        
        if (LPC_USB->USBEpIntSt & EP(EP2IN))
        {
            selectEndpointClearInterrupt(EP2IN);
            endpointEventEP2In();
        }    
        
        /* Clear interrupt status flag */
        /* EP_SLOW and EP_FAST interrupt bits should be cleared after the corresponding endpoint interrupts are cleared. */
        LPC_USB->USBDevIntClr = EP_SLOW;
    }
}


void usbdc::_usbisr(void)
{
    instance->usbisr();
}

void usbdc::deviceEventReset(void)
{
}

void usbdc::deviceEventFrame(void)
{
}

void usbdc::endpointEventEP0Setup(void)
{
}

void usbdc::endpointEventEP0In(void)
{
}

void usbdc::endpointEventEP0Out(void)
{
}

void usbdc::endpointEventEP1In(void)
{
}

void usbdc::endpointEventEP1Out(void)
{
}

void usbdc::endpointEventEP2In(void)
{
}

void usbdc::endpointEventEP2Out(void)
{
}