/* ***************************************************************************** * 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 "dfu.h" void setupUSB (void) { /* enable USB DISC Pin */ pRCC->APB2ENR |= RCC_APB2ENR_USB; /* Setup USB DISC pin as output open drain */ SET_REG(USB_DISC_CR, (GET_REG(USB_DISC_CR) & USB_DISC_CR_MASK) | USB_DISC_CR_OUTPUT_OD); setPin (USB_DISC_BANK,USB_DISC); /* turn on the USB clock */ pRCC->APB1ENR |= RCC_APB1ENR_USB_CLK; /* initialize the usb application */ resetPin (USB_DISC_BANK,USB_DISC); /* present ourselves to the host */ usbAppInit(); } void usbDsbBus(void) { setPin(USB_DISC_BANK,USB_DISC); } vu32 bDeviceState = UNCONNECTED; /* tracks sequential behavior of the ISTR */ vu16 wIstr; vu8 bIntPackSOF = 0; DEVICE Device_Table = { NUM_ENDPTS, 1 }; DEVICE_PROP Device_Property = { usbInit, usbReset, usbStatusIn, usbStatusOut, usbDataSetup, usbNoDataSetup, usbGetInterfaceSetting, usbGetDeviceDescriptor, usbGetConfigDescriptor, usbGetStringDescriptor, usbGetFunctionalDescriptor, 0, bMaxPacketSize }; USER_STANDARD_REQUESTS User_Standard_Requests = { usbGetConfiguration, usbSetConfiguration, usbGetInterface, usbSetInterface, usbGetStatus, usbClearFeature, usbSetEndpointFeature, usbSetDeviceFeature, usbSetDeviceAddress }; void (*pEpInt_IN[7])(void) = { nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, }; void (*pEpInt_OUT[7])(void) = { nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, nothingProc, }; struct { volatile RESUME_STATE eState; volatile u8 bESOFcnt; } ResumeS; /* dummy proc */ void nothingProc(void) { } /* Function Definitions */ void usbAppInit(void) { /* hook in to usb_core, depends on all those damn non encapsulated externs! */ USB_Init(); } 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 usbInit(void) { dfuInit(); pInformation->Current_Configuration = 0; usbPowerOn(); _SetISTR(0); wInterrupt_Mask = ISR_MSK; _SetCNTR(wInterrupt_Mask); usbEnbISR(); /* configure the cortex M3 private peripheral NVIC */ bDeviceState = UNCONNECTED; } void usbReset(void) { dfuUpdateByReset(); pInformation->Current_Configuration = 0; pInformation->Current_Feature = usbConfigDescriptorDFU.Descriptor[7]; _SetBTABLE(BTABLE_ADDRESS); /* setup the ctrl endpoint */ _SetEPType(ENDP0, EP_CONTROL); _SetEPTxStatus(ENDP0, EP_TX_STALL); _SetEPRxAddr(ENDP0, ENDP0_RXADDR); _SetEPTxAddr(ENDP0, ENDP0_TXADDR); Clear_Status_Out(ENDP0); SetEPRxCount(ENDP0, pProperty->MaxPacketSize); // SetEPTxCount(ENDP0, pProperty->MaxPacketSize); SetEPRxValid(ENDP0); bDeviceState = ATTACHED; SetDeviceAddress(0); /* different than usbSetDeviceAddr! comes from usb_core */ } void usbStatusIn(void) { } void usbStatusOut(void) { } RESULT usbDataSetup(u8 request) { u8 *(*CopyRoutine)(u16); CopyRoutine = NULL; /* handle dfu class requests */ if ((pInformation->USBbmRequestType & (REQUEST_TYPE | RECIPIENT)) == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { if (dfuUpdateByRequest()) { /* successfull state transition, handle the request */ switch (request) { case(DFU_GETSTATUS): CopyRoutine = dfuCopyStatus; break; case(DFU_GETSTATE): CopyRoutine = dfuCopyState; break; case(DFU_DNLOAD): CopyRoutine = dfuCopyDNLOAD; break; case(DFU_UPLOAD): CopyRoutine = dfuCopyUPLOAD; break; default: /* leave copy routine null */ break; } } } if (CopyRoutine != NULL) { pInformation->Ctrl_Info.CopyData = CopyRoutine; pInformation->Ctrl_Info.Usb_wOffset = 0; (*CopyRoutine)(0); return USB_SUCCESS; } return USB_UNSUPPORT; } RESULT usbNoDataSetup(u8 request) { if ((pInformation->USBbmRequestType & (REQUEST_TYPE | RECIPIENT)) == (CLASS_REQUEST | INTERFACE_RECIPIENT)) { /* todo, keep track of the destination interface, often stored in wIndex */ if (dfuUpdateByRequest()) { return USB_SUCCESS; } } return USB_UNSUPPORT; } RESULT usbGetInterfaceSetting(u8 interface, u8 altSetting) { /* alt setting 0 -> program RAM, alt setting 1 -> FLASH */ if (interface > NUM_ALT_SETTINGS) { return USB_UNSUPPORT; } else { return USB_SUCCESS; } } u8 *usbGetDeviceDescriptor(u16 len) { return Standard_GetDescriptorData(len, &usbDeviceDescriptorDFU); } u8 *usbGetConfigDescriptor(u16 len) { return Standard_GetDescriptorData(len, &usbConfigDescriptorDFU); } u8 *usbGetStringDescriptor(u16 len) { u8 strIndex = pInformation->USBwValue0; if (strIndex > STR_DESC_LEN) { return NULL; } else { return Standard_GetDescriptorData(len, &usbStringDescriptor[strIndex]); } } u8 *usbGetFunctionalDescriptor(u16 len) { return Standard_GetDescriptorData(len, &usbFunctionalDescriptor); } /***** start of USER STANDARD REQUESTS ****** * * These are the USER STANDARD REQUESTS, they are handled * in the core but we are given these callbacks at the * application level *******************************************/ void usbGetConfiguration(void) { /* nothing process */ } void usbSetConfiguration(void) { if (pInformation->Current_Configuration != 0) { bDeviceState = CONFIGURED; } } void usbGetInterface(void) { /* nothing process */ } void usbSetInterface(void) { /* nothing process */ } void usbGetStatus(void) { /* nothing process */ } void usbClearFeature(void) { /* nothing process */ } void usbSetEndpointFeature(void) { /* nothing process */ } void usbSetDeviceFeature(void) { /* nothing process */ } void usbSetDeviceAddress(void) { bDeviceState = ADDRESSED; } /***** end of USER STANDARD REQUESTS *****/ 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); } void USB_LP_CAN1_RX0_IRQHandler(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 }