diff options
Diffstat (limited to 'libmaple/usb/usb.c')
-rw-r--r-- | libmaple/usb/usb.c | 470 |
1 files changed, 470 insertions, 0 deletions
diff --git a/libmaple/usb/usb.c b/libmaple/usb/usb.c new file mode 100644 index 0000000..f926e81 --- /dev/null +++ b/libmaple/usb/usb.c @@ -0,0 +1,470 @@ +/* ***************************************************************************** + * The MIT License + * + * Copyright (c) 2010 LeafLabs LLC. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + * ****************************************************************************/ + +/** + * @file usb.c + * + * @brief usb-specific hardware setup, NVIC, clocks, and usb activities + * in the pre-attached state. includes some of the lower level callbacks + * needed by the usb library, like suspend,resume,init,etc + */ + +#include "usb.h" +#include "libmaple.h" +#include "usb_lib.h" +#include "gpio.h" +#include "usb_hardware.h" + +#include "usb_config.h" +#include "usb_callbacks.h" +#include "usb_lib.h" + +/* persistent usb structs */ + +volatile uint32 bDeviceState = UNCONNECTED; +volatile uint16 wIstr = 0; +volatile bIntPackSOF = 0; + +DEVICE Device_Table = + { + NUM_ENDPTS, + 1 + }; + +DEVICE_PROP Device_Property = + { + usbInit, + usbReset, + usbStatusIn, + usbStatusOut, + usbDataSetup, + usbNoDataSetup, + usbGetInterfaceSetting, + usbGetDeviceDescriptor, + usbGetConfigDescriptor, + usbGetStringDescriptor, + NOP_Process, + 0, + bMaxPacketSize + }; + +USER_STANDARD_REQUESTS User_Standard_Requests = + { + NOP_Process, + usbSetConfiguration, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + usbSetDeviceAddress + }; + +void (*pEpInt_IN[7])(void) = +{ + vcomDataTxCb, + vcomManagementCb, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, +}; + +void (*pEpInt_OUT[7])(void) = +{ + NOP_Process, + NOP_Process, + vcomDataRxCb, + NOP_Process, + NOP_Process, + NOP_Process, + NOP_Process, +}; + +struct +{ + volatile RESUME_STATE eState; + volatile uint8 bESOFcnt; +} ResumeS; + +void setupUSB (void) { + gpio_set_mode(USB_DISC_BANK, + USB_DISC_PIN, + GPIO_MODE_OUTPUT_PP); + + /* setup the apb1 clock for USB */ + pRCC->APB1ENR |= 0x00800000; + + /* initialize the usb application */ + gpio_write_bit(USB_DISC_BANK,USB_DISC_PIN,0); /* present ourselves to the host */ + USB_Init(); /* low level init routine provided by st lib */ + +} + +void usbSuspend(void) { + u16 wCNTR; + wCNTR = _GetCNTR(); + wCNTR |= CNTR_FSUSP | CNTR_LPMODE; + _SetCNTR(wCNTR); + + /* run any power reduction handlers */ + bDeviceState = SUSPENDED; +} + +void usbResumeInit(void) { + u16 wCNTR; + + /* restart any clocks that had been stopped */ + + wCNTR = _GetCNTR(); + wCNTR &= (~CNTR_LPMODE); + _SetCNTR(wCNTR); + + /* undo power reduction handlers here */ + _SetCNTR(ISR_MSK); + +} + +void usbResume(RESUME_STATE eResumeSetVal) { + u16 wCNTR; + + if (eResumeSetVal != RESUME_ESOF) + ResumeS.eState = eResumeSetVal; + + switch (ResumeS.eState) + { + case RESUME_EXTERNAL: + usbResumeInit(); + ResumeS.eState = RESUME_OFF; + break; + case RESUME_INTERNAL: + usbResumeInit(); + ResumeS.eState = RESUME_START; + break; + case RESUME_LATER: + ResumeS.bESOFcnt = 2; + ResumeS.eState = RESUME_WAIT; + break; + case RESUME_WAIT: + ResumeS.bESOFcnt--; + if (ResumeS.bESOFcnt == 0) + ResumeS.eState = RESUME_START; + break; + case RESUME_START: + wCNTR = _GetCNTR(); + wCNTR |= CNTR_RESUME; + _SetCNTR(wCNTR); + ResumeS.eState = RESUME_ON; + ResumeS.bESOFcnt = 10; + break; + case RESUME_ON: + ResumeS.bESOFcnt--; + if (ResumeS.bESOFcnt == 0) + { + wCNTR = _GetCNTR(); + wCNTR &= (~CNTR_RESUME); + _SetCNTR(wCNTR); + ResumeS.eState = RESUME_OFF; + } + break; + case RESUME_OFF: + case RESUME_ESOF: + default: + ResumeS.eState = RESUME_OFF; + break; + } +} + +RESULT usbPowerOn(void) { + u16 wRegVal; + + wRegVal = CNTR_FRES; + _SetCNTR(wRegVal); + + wInterrupt_Mask = 0; + _SetCNTR(wInterrupt_Mask); + _SetISTR(0); + wInterrupt_Mask = CNTR_RESETM | CNTR_SUSPM | CNTR_WKUPM; /* the bare minimum */ + _SetCNTR(wInterrupt_Mask); + + return USB_SUCCESS; +} + +RESULT usbPowerOff(void) { + _SetCNTR(CNTR_FRES); + _SetISTR(0); + _SetCNTR(CNTR_FRES + CNTR_PDWN); + + /* note that all weve done here is powerdown the + usb peripheral. we have no disabled the clocks, + pulled the usb_disc pin back up, or reset the + application state machines */ + + return USB_SUCCESS; +} + +void usbEnbISR(void) { + NVIC_InitTypeDef NVIC_InitStructure; + + + NVIC_InitStructure.NVIC_IRQChannel = USB_LP_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = TRUE; + nvicInit(&NVIC_InitStructure); +} + +void usbDsbISR(void) { + NVIC_InitTypeDef NVIC_InitStructure; + NVIC_InitStructure.NVIC_IRQChannel = USB_LP_IRQ; + NVIC_InitStructure.NVIC_IRQChannelPreemptionPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelSubPriority = 0; + NVIC_InitStructure.NVIC_IRQChannelCmd = FALSE; + nvicInit(&NVIC_InitStructure); +} + +/* overloaded ISR routine, this is the main usb ISR */ +void usb_lpIRQHandler(void) { +wIstr = _GetISTR(); + +/* go nuts with the preproc switches since this is an ISTR and must be FAST */ +#if (ISR_MSK & ISTR_RESET) +if (wIstr & ISTR_RESET & wInterrupt_Mask) + { + _SetISTR((u16)CLR_RESET); + Device_Property.Reset(); + } +#endif + + +#if (ISR_MSK & ISTR_DOVR) +if (wIstr & ISTR_DOVR & wInterrupt_Mask) + { + _SetISTR((u16)CLR_DOVR); + } +#endif + + +#if (ISR_MSK & ISTR_ERR) +if (wIstr & ISTR_ERR & wInterrupt_Mask) + { + _SetISTR((u16)CLR_ERR); + } +#endif + + +#if (ISR_MSK & ISTR_WKUP) +if (wIstr & ISTR_WKUP & wInterrupt_Mask) + { + _SetISTR((u16)CLR_WKUP); + usbResume(RESUME_EXTERNAL); + } +#endif + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ +#if (ISR_MSK & ISTR_SUSP) +if (wIstr & ISTR_SUSP & wInterrupt_Mask) + { + + /* check if SUSPEND is possible */ + if (F_SUSPEND_ENABLED) + { + usbSuspend(); + } + else + { + /* if not possible then resume after xx ms */ + usbResume(RESUME_LATER); + } + /* clear of the ISTR bit must be done after setting of CNTR_FSUSP */ + _SetISTR((u16)CLR_SUSP); + } +#endif + + +#if (ISR_MSK & ISTR_SOF) +if (wIstr & ISTR_SOF & wInterrupt_Mask) + { + _SetISTR((u16)CLR_SOF); + bIntPackSOF++; + } +#endif + + +#if (ISR_MSK & ISTR_ESOF) +if (wIstr & ISTR_ESOF & wInterrupt_Mask) + { + _SetISTR((u16)CLR_ESOF); + /* resume handling timing is made with ESOFs */ + usbResume(RESUME_ESOF); /* request without change of the machine state */ + } +#endif + +/*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*-*/ +#if (ISR_MSK & ISTR_CTR) +if (wIstr & ISTR_CTR & wInterrupt_Mask) + { + /* servicing of the endpoint correct transfer interrupt */ + /* clear of the CTR flag into the sub */ + CTR_LP(); /* low priority ISR defined in the usb core lib */ + } +#endif + +/* if we are about to reset from the DTR signal, then dont return + to user, instead return from the ISR into a wait slide */ + if (reset_state == RESET_NOW) { + reset_state = START; + unsigned int target = (unsigned int)usbWaitReset | 0x1; + + asm volatile("mov r0, %[stack_top] \n\t" // Reset the stack + "mov sp, r0 \n\t" + "mov r0, #1 \n\t" + "mov r1, %[target_addr] \n\t" + "mov r2, %[cpsr] \n\t" + "push {r2} \n\t" // Fake xPSR + "push {r1} \n\t" // Target address for PC + "push {r0} \n\t" // Fake LR + "push {r0} \n\t" // Fake R12 + "push {r0} \n\t" // Fake R3 + "push {r0} \n\t" // Fake R2 + "push {r0} \n\t" // Fake R1 + "push {r0} \n\t" // Fake R0 + "mov lr, %[exc_return] \n\t" + "bx lr" + : + : [stack_top] "r" (STACK_TOP), + [target_addr] "r" (target), + [exc_return] "r" (EXC_RETURN), + [cpsr] "r" (DEFAULT_CPSR) + : "r0", "r1", "r2"); + // Should never get here. + } +} + +void usbWaitReset(void) { + static count=1000000; + while (count-- >0); + systemHardReset(); +} + +/* copies data out of sendBuf into the packet memory for + usb, but waits until any previous usb transmissions have + completed before doing this. It returns without waiting + for its data to be sent. most efficient when 64 bytes are copied + at a time. users responsible for not overflowing sendbuf + with len! if > 64 bytes are being sent, then the function + will block at every 64 byte packet +*/ +int16 usbSendBytes(uint8* sendBuf, uint16 len) { +/* while (countTx != 0) { */ +/* if (reset_state == NDTR_NRTS) { */ +/* return 0; */ +/* } */ +/* }/\* wait for pipe to be clear *\/ */ + + if (reset_state == NDTR_NRTS) { + return -1; /* indicates to caller to stop trying, were not connected */ + } + + /* ideally we should wait here, but it gets stuck + for some reason. countTx wont decrement when + theres no host side port reading the data, this is + known, but even if we add the check for NDTR_NRTS it + still gets stuck...*/ + if (countTx != 0) { + return 0; /* indicated to caller to keep trying, were just busy */ + } + + uint16 sent = len; + + while (len > VCOM_TX_EPSIZE) { + countTx = VCOM_TX_EPSIZE; + + UserToPMABufferCopy(sendBuf,VCOM_TX_ADDR,VCOM_TX_EPSIZE); + _SetEPTxCount(VCOM_TX_ENDP,VCOM_TX_EPSIZE); + _SetEPTxValid(VCOM_TX_ENDP); + + while(countTx != 0); + len -= VCOM_TX_EPSIZE; + sendBuf += VCOM_TX_EPSIZE; + } + + if (len != 0) { + countTx = len; + UserToPMABufferCopy(sendBuf,VCOM_TX_ADDR,len); + _SetEPTxCount(VCOM_TX_ENDP,len); + _SetEPTxValid(VCOM_TX_ENDP); + } + + return sent; +} + +/* returns the number of available bytes are in the recv FIFO */ +uint8 usbBytesAvailable(void) { + return VCOM_RX_EPSIZE - maxNewBytes; +} + +/* copies len bytes from the local recieve FIFO (not + usb packet buffer) into recvBuf and deq's the fifo. + will only copy the minimum of len or the available + bytes. returns the number of bytes copied */ +uint8 usbReceiveBytes(uint8* recvBuf, uint8 len) { + if (len > VCOM_RX_EPSIZE - maxNewBytes) { + len = VCOM_RX_EPSIZE - maxNewBytes; + } + + int i; + for (i=0;i<len;i++) { + recvBuf[i] = (uint8)(vcomBufferRx[recvBufOut]); + recvBufOut = (recvBufOut + 1) % VCOM_RX_EPSIZE; + } + + maxNewBytes += len; + + /* re-enable the rx endpoint which we had set to receive 0 bytes */ + if (maxNewBytes - len == 0) { + SetEPRxCount(VCOM_RX_ENDP,maxNewBytes); + } + + return len; +} + +void usbSendHello(void) { + char* myStr = "hello!"; + + uint8 bufin = 48 + recvBufIn;; + uint8 bufout = 48 + recvBufOut; + uint8 avail = 48 + usbBytesAvailable(); + + char *line = "\r\n"; + while(usbSendBytes(&bufin,1) == 0); + while(usbSendBytes(&bufout,1) == 0); + while(usbSendBytes(&avail,1) == 0); + while(usbSendBytes((uint8*)line,2) == 0); + + uint8 recv[64]; + usbReceiveBytes(&recv[0],1); +} |