diff --git a/Upstream/Src/hal_msp.c b/Upstream/Src/hal_msp.c new file mode 100644 index 0000000..9e8ed65 --- /dev/null +++ b/Upstream/Src/hal_msp.c @@ -0,0 +1,175 @@ +/** + ****************************************************************************** + * File Name : stm32f4xx_hal_msp.c + * Date : 03/02/2015 20:27:00 + * Description : This file provides code for the MSP Initialization + * and de-Initialization codes. + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_hal.h" +#include "interrupts.h" +#include "board_config.h" + + +DMA_HandleTypeDef spiTxDmaHandle; +DMA_HandleTypeDef spiRxDmaHandle; + + +/** + * Initializes the Global MSP. + */ +void HAL_MspInit(void) +{ +// HAL_NVIC_SetPriorityGrouping(NVIC_PRIORITYGROUP_4); +// +// /* System interrupt init*/ +///* SysTick_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(SysTick_IRQn, INT_PRIORITY_SYSTICK, 0); + +} + + +uint32_t HAL_GetHSECrystalFreqMHz(void) +{ + if ((BOARD_REV_ID_PORT->IDR & BOARD_REV_PIN_MASK) < BOARD_REV_1_0_BETA_3) + { + return BOARD_REV_1_0_BETA_FREQ; + } + else + { + return BOARD_REV_1_0_BETA_3_FREQ; + } +} + + +void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) +{ + GPIO_InitTypeDef GPIO_InitStruct; + if(hspi->Instance==SPI1) + { + /* Peripheral clock enable */ + __HAL_RCC_SPI1_CLK_ENABLE(); + __HAL_RCC_DMA2_CLK_ENABLE(); + + /**SPI1 GPIO Configuration + PA4 ------> GPIO manual slave select + PA5 ------> SPI1_SCK + PA6 ------> SPI1_MISO + PA7 ------> SPI1_MOSI + */ + GPIO_InitStruct.Pin = GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_PULLDOWN; + GPIO_InitStruct.Speed = GPIO_SPEED_MEDIUM; + GPIO_InitStruct.Alternate = GPIO_AF5_SPI1; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + SPI1_NSS_DEASSERT; + GPIO_InitStruct.Pin = SPI1_NSS_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(SPI1_NSS_PORT, &GPIO_InitStruct); + + //Configure downstream request pin and interrupt + GPIO_InitStruct.Pin = DOWNSTREAM_TX_OK_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT | GPIO_MODE_IT_FALLING; + GPIO_InitStruct.Pull = GPIO_PULLUP; + HAL_GPIO_Init(DOWNSTREAM_TX_OK_PORT, &GPIO_InitStruct); + HAL_NVIC_SetPriority(EXTI3_IRQn, INT_PRIORITY_EXT3I, 0); + HAL_NVIC_EnableIRQ(EXTI3_IRQn); + + //Prepare Tx DMA stream + hspi->hdmatx = &spiTxDmaHandle; + spiTxDmaHandle.Instance = DMA2_Stream3; + spiTxDmaHandle.Parent = hspi; + spiTxDmaHandle.Init.Channel = DMA_CHANNEL_3; + spiTxDmaHandle.Init.Direction = DMA_MEMORY_TO_PERIPH; + spiTxDmaHandle.Init.PeriphInc = DMA_PINC_DISABLE; + spiTxDmaHandle.Init.MemInc = DMA_MINC_ENABLE; + spiTxDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + spiTxDmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + spiTxDmaHandle.Init.Mode = DMA_NORMAL; + spiTxDmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM; + spiTxDmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + HAL_DMA_Init(&spiTxDmaHandle); + HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, INT_PRIORITY_SPI_DMA, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); + + //Prepare Rx DMA stream + hspi->hdmarx = &spiRxDmaHandle; + spiRxDmaHandle.Instance = DMA2_Stream2; + spiRxDmaHandle.Parent = hspi; + spiRxDmaHandle.Init.Channel = DMA_CHANNEL_3; + spiRxDmaHandle.Init.Direction = DMA_PERIPH_TO_MEMORY; + spiRxDmaHandle.Init.PeriphInc = DMA_PINC_DISABLE; + spiRxDmaHandle.Init.MemInc = DMA_MINC_ENABLE; + spiRxDmaHandle.Init.PeriphDataAlignment = DMA_PDATAALIGN_HALFWORD; + spiRxDmaHandle.Init.MemDataAlignment = DMA_MDATAALIGN_HALFWORD; + spiRxDmaHandle.Init.Mode = DMA_NORMAL; + spiRxDmaHandle.Init.Priority = DMA_PRIORITY_MEDIUM; + spiRxDmaHandle.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + HAL_DMA_Init(&spiRxDmaHandle); + HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, INT_PRIORITY_SPI_DMA, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); + } +} + +void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) +{ + + if(hspi->Instance==SPI1) + { + /* Peripheral clock disable */ + __HAL_RCC_SPI1_CLK_DISABLE(); + __HAL_RCC_DMA2_CLK_DISABLE(); + + /**SPI1 GPIO Configuration + PA4 ------> SPI1_NSS + PA5 ------> SPI1_SCK + PA6 ------> SPI1_MISO + PA7 ------> SPI1_MOSI + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_5|GPIO_PIN_6|GPIO_PIN_7); + HAL_DMA_DeInit(&spiTxDmaHandle); + HAL_DMA_DeInit(&spiRxDmaHandle); + + HAL_NVIC_DisableIRQ(DMA2_Stream3_IRQn); + HAL_NVIC_DisableIRQ(DMA2_Stream2_IRQn); + } + +} + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Upstream/Src/interrupts.c b/Upstream/Src/interrupts.c new file mode 100644 index 0000000..6deaace --- /dev/null +++ b/Upstream/Src/interrupts.c @@ -0,0 +1,129 @@ +/** + ****************************************************************************** + * @file stm32f4xx_it.c + * @date 03/02/2015 20:27:00 + * @brief Interrupt Service Routines. + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + +/* Includes ------------------------------------------------------------------*/ +#include "interrupts.h" +#include "upstream_spi.h" +#include "stm32f4xx_hal.h" +#include "board_config.h" +#include "build_config.h" +#include "led.h" +#include "upstream_hid_botdetect.h" +#include "upstream_statemachine.h" + +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +/* External variables --------------------------------------------------------*/ + +extern PCD_HandleTypeDef hpcd_USB_OTG_FS; +extern DMA_HandleTypeDef spiTxDmaHandle; +extern DMA_HandleTypeDef spiRxDmaHandle; + +uint8_t BusFaultAllowed = 0; + + +/******************************************************************************/ +/* Cortex-M4 Processor Interruption and Exception Handlers */ +/******************************************************************************/ + +void SysTick_Handler(void) +{ + HAL_IncTick(); + LED_Tick(); + Upstream_StateMachine_PollDeviceConnected(); + +#if (defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED)) || \ + (defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED)) + Upstream_HID_BotDetect_Systick(); +#endif +} + +///////////////////////// +//All interrupts in this section must be at the same priority. +//They interact with each other, and calls are not thread-safe +//when different interrupt priorities are used. +///////////////////////// +void OTG_FS_IRQHandler(void) +{ + HAL_PCD_IRQHandler(&hpcd_USB_OTG_FS); +} + +void DMA2_Stream2_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&spiRxDmaHandle); +} + +void DMA2_Stream3_IRQHandler(void) +{ + HAL_DMA_IRQHandler(&spiTxDmaHandle); +} + +void EXTI3_IRQHandler(void) +{ + __HAL_GPIO_EXTI_CLEAR_IT(DOWNSTREAM_TX_OK_PIN); + Upstream_TxOkInterrupt(); +} +///////////////////////// +///////////////////////// + + +//This weird stuff is required when disabling flash writes. +//The deliberate flash lockout will cause a bus fault that we need to process. +void EnableOneBusFault(void) +{ + //It should not be enabled already! + if (BusFaultAllowed) + { + while (1); + } + SCB->SHCSR = SCB_SHCSR_BUSFAULTENA_Msk; + BusFaultAllowed = 1; +} + +void BusFault_Handler(void) +{ + if (BusFaultAllowed) + { + BusFaultAllowed = 0; + return; + } + while (1); +} + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Upstream/Src/led.c b/Upstream/Src/led.c new file mode 100644 index 0000000..a9c873d --- /dev/null +++ b/Upstream/Src/led.c @@ -0,0 +1,143 @@ +/* + * led.c + * + * Created on: 19/08/2015 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +#include "led.h" +#include "board_config.h" +#include "build_config.h" + +uint32_t FaultLedCounter; +uint32_t ReadWriteFlashEndTime; + +LedStatusTypeDef FaultLedState; +uint16_t FaultLedOnMs; +uint16_t FaultLedOffMs; +uint8_t FaultLedBlinkCount; + +uint8_t FaultLedBlinkCountState; +uint8_t FaultLedOutputState; + + +void LED_Init(void) +{ + FAULT_LED_ON; + ReadWriteFlashEndTime = 0; + FaultLedState = LED_STATUS_STARTUP; +} + + +void LED_SetState(LedStatusTypeDef newState) +{ + switch (newState) + { + case LED_STATUS_OFF: + FaultLedCounter = UINT32_MAX; + FaultLedBlinkCountState = 0; + FaultLedOutputState = 0; + FAULT_LED_OFF; + break; + + case LED_STATUS_FLASH_UNSUPPORTED: + FaultLedOnMs = LED_UNSUPPORTED_BLINK_MS; + FaultLedOffMs = LED_UNSUPPORTED_BLINK_MS; + FaultLedBlinkCount = 1; + break; + + case LED_STATUS_FLASH_BOTDETECT: + FaultLedOnMs = LED_BOTDETECT_ON_MS; + FaultLedOffMs = LED_BOTDETECT_OFF_MS; + FaultLedBlinkCount = 2; + break; + + case LED_STATUS_FLASH_READWRITE: +#ifdef CONFIG_WRITE_FLASH_TIME_MS + if (FaultLedState == LED_STATUS_OFF) + { + FaultLedOnMs = LED_READWRITE_ON_MS; + FaultLedOffMs = LED_READWRITE_OFF_MS; + FaultLedBlinkCount = 1; + ReadWriteFlashEndTime = HAL_GetTick() + CONFIG_WRITE_FLASH_TIME_MS; + } + else +#endif + { + newState = FaultLedState; //Don't override other active states + } + break; + + default: + FaultLedOnMs = LED_ERROR_BLINK_MS; //Everything else is LED_STATUS_ERROR + FaultLedOffMs = LED_ERROR_BLINK_MS; + FaultLedBlinkCount = 1; + } + + FaultLedState = newState; +} + + +void LED_Tick(void) +{ + if (FaultLedState == LED_STATUS_OFF) return; + + if (FaultLedState == LED_STATUS_STARTUP) + { + if (HAL_GetTick() >= STARTUP_FLASH_DELAY_MS) + { + LED_SetState(LED_STATUS_OFF); + } + return; + } + +#ifdef CONFIG_WRITE_FLASH_TIME_MS + if (FaultLedState == LED_STATUS_FLASH_READWRITE) + { + if ((int32_t)(HAL_GetTick() - ReadWriteFlashEndTime) > 0) + { + LED_SetState(LED_STATUS_OFF); + return; + } + } +#endif + + if (FaultLedOutputState) + { + if (FaultLedCounter++ >= FaultLedOnMs) //Check to turn LED off + { + FaultLedBlinkCountState++; + FaultLedCounter = 0; + FaultLedOutputState = 0; + FAULT_LED_OFF; + } + } + else + { + if (FaultLedBlinkCountState >= FaultLedBlinkCount) //Checks to turn LED on... + { + if (FaultLedCounter++ >= FaultLedOffMs) //Last flash may have longer off-time + { + FaultLedBlinkCountState = 0; + FaultLedCounter = 0; + FaultLedOutputState = 1; + FAULT_LED_ON; + } + } + else + { + if (FaultLedCounter++ >= FaultLedOnMs) //Flash sequence uses on-time as intermediate off-time + { + FaultLedCounter = 0; + FaultLedOutputState = 1; + FAULT_LED_ON; + } + } + } +} + diff --git a/Upstream/Src/main.c b/Upstream/Src/main.c new file mode 100644 index 0000000..1880946 --- /dev/null +++ b/Upstream/Src/main.c @@ -0,0 +1,258 @@ +/** + ****************************************************************************** + * File Name : main.c + * Date : 03/02/2015 20:27:01 + * Description : Main program body + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx_hal.h" +#include "usb_device.h" +#include "board_config.h" +#include "led.h" +#include "upstream_statemachine.h" +#include "upstream_spi.h" +#include "interrupts.h" + + +/* Private function prototypes -----------------------------------------------*/ +static void SystemClock_Config(void); +static void GPIO_Init(void); +static void DisableFlashWrites(void); +static void CheckFirmwareMatchesHardware(void); + + + +int main(void) +{ + //First things first! + DisableFlashWrites(); + CheckFirmwareMatchesHardware(); + + /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ + HAL_Init(); + + /* Configure the system clock */ + SystemClock_Config(); + + /* Initialize all configured peripherals */ + GPIO_Init(); + LED_Init(); + Upstream_InitStateMachine(); + + HAL_Delay(200); //Delay executing WFI to avoid destroying JTAG access + + while (1) + { + __WFI(); //sleep time! + } +} + + + +void DisableFlashWrites(void) +{ + //Disable flash writes until the next reset + //This will cause a bus fault interrupt, so allow one now. + EnableOneBusFault(); + FLASH->KEYR = 999; + + //Confirm that flash cannot be unlocked + //This unlock attempt will also cause two bus faults. + if ((FLASH->CR & FLASH_CR_LOCK) == 0) while(1); + EnableOneBusFault(); + FLASH->KEYR = FLASH_KEY1; + EnableOneBusFault(); + FLASH->KEYR = FLASH_KEY2; + if ((FLASH->CR & FLASH_CR_LOCK) == 0) while(1); +} + + +void CheckFirmwareMatchesHardware(void) +{ + //Check we are running on the expected hardware: + //STM32F401RC on USG v1.0 beta + + GPIO_InitTypeDef GPIO_InitStruct; + + __HAL_RCC_GPIOB_CLK_ENABLE(); + + if ((*(uint32_t*)DBGMCU_BASE & DBGMCU_IDCODE_DEV_ID) == DBGMCU_IDCODE_DEV_ID_401xB_xC) + { + //Read in board revision and ID on port C + GPIO_InitStruct.Pin = BOARD_REV_PIN_MASK | BOARD_ID_PIN_MASK; + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_LOW; + GPIO_InitStruct.Alternate = 0; + HAL_GPIO_Init(BOARD_REV_ID_PORT, &GPIO_InitStruct); + + //Correct board revision? + if ((BOARD_REV_ID_PORT->IDR & BOARD_REV_PIN_MASK) <= BOARD_REV_1_0_BETA_3) + { + //Correct board ID: upstream? + if ((BOARD_REV_ID_PORT->IDR & BOARD_ID_PIN_MASK)) + { + return; + } + } + } + + //This is not the hardware we expected, so turn on our fault LED(s) and die in a heap. + GPIO_InitStruct.Pin = FAULT_LED_PIN | H405_FAULT_LED_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(FAULT_LED_PORT, &GPIO_InitStruct); + FAULT_LED_ON; + H405_FAULT_LED_ON; + while (1); +} + + + +/** System Clock Configuration +*/ +void SystemClock_Config(void) +{ + + RCC_OscInitTypeDef RCC_OscInitStruct; + RCC_ClkInitTypeDef RCC_ClkInitStruct; + + __PWR_CLK_ENABLE(); + + __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2); + + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.HSEState = RCC_HSE_ON; + RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; + RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; + RCC_OscInitStruct.PLL.PLLM = (HAL_GetHSECrystalFreqMHz() / 2); //PLL input frequency = 2MHz + RCC_OscInitStruct.PLL.PLLN = 168; + RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4; + RCC_OscInitStruct.PLL.PLLQ = 7; + if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) while (1); + + RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_SYSCLK | + RCC_CLOCKTYPE_HCLK | + RCC_CLOCKTYPE_PCLK1 | + RCC_CLOCKTYPE_PCLK2; + RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK; + RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1; + RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV4; + RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV2; + if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK) while(1); + + HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK); +} + + + +/** Configure pins as + * Analog + * Input + * Output + * EVENT_OUT + * EXTI +*/ +void GPIO_Init(void) +{ + GPIO_InitTypeDef GPIO_InitStruct; + + /* GPIO Ports Clock Enable */ + __HAL_RCC_GPIOA_CLK_ENABLE(); + __HAL_RCC_GPIOB_CLK_ENABLE(); + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + //__HAL_RCC_GPIOH_CLK_ENABLE(); //HS oscillator on port H + + //Bulk initialise all ports as inputs with pullups active, + //excluding JTAG pins which must remain as AF0! + GPIO_InitStruct.Pin = (GPIO_PIN_All & ~(PA_JTMS | PA_JTCK | PA_JTDI)); + GPIO_InitStruct.Mode = GPIO_MODE_INPUT; + GPIO_InitStruct.Pull = GPIO_PULLUP; + GPIO_InitStruct.Speed = GPIO_SPEED_LOW; + GPIO_InitStruct.Alternate = 0; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + GPIO_InitStruct.Pin = (GPIO_PIN_All & ~(PB_JTDO | PB_NJTRST)); + HAL_GPIO_Init(GPIOB, &GPIO_InitStruct); + GPIO_InitStruct.Pin = GPIO_PIN_All; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + //Fault LED is output + GPIO_InitStruct.Pin = FAULT_LED_PIN; + GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + HAL_GPIO_Init(FAULT_LED_PORT, &GPIO_InitStruct); + FAULT_LED_OFF; + +// //SPI_INT_ACTIVE indicator +// GPIO_InitStruct.Pin = INT_ACTIVE_PIN; +// //GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP; +// //GPIO_InitStruct.Pull = GPIO_NOPULL; +// HAL_GPIO_Init(INT_ACTIVE_PORT, &GPIO_InitStruct); +// INT_ACTIVE_OFF; +} + +/* USER CODE BEGIN 4 */ + +/* USER CODE END 4 */ + +#ifdef USE_FULL_ASSERT + +/** + * @brief Reports the name of the source file and the source line number + * where the assert_param error has occurred. + * @param file: pointer to the source file name + * @param line: assert_param error line source number + * @retval None + */ +void assert_failed(uint8_t* file, uint32_t line) +{ + /* USER CODE BEGIN 6 */ + /* User can add his own implementation to report the file name and line number, + ex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */ + /* USER CODE END 6 */ + +} + +#endif + +/** + * @} + */ + +/** + * @} +*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Upstream/Src/upstream_hid.c b/Upstream/Src/upstream_hid.c new file mode 100644 index 0000000..1799190 --- /dev/null +++ b/Upstream/Src/upstream_hid.c @@ -0,0 +1,333 @@ +/* + * upstream_hid.c + * + * Created on: Jan 16, 2016 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + + +#include "upstream_hid.h" +#include "upstream_hid_botdetect.h" +#include "upstream_interface_def.h" +#include "build_config.h" + + +#if defined (CONFIG_KEYBOARD_ENABLED) || defined (CONFIG_MOUSE_ENABLED) + + +UpstreamPacketTypeDef* UpstreamHidPacket = NULL; +UpstreamHidGetReportCallback GetReportCallback = NULL; + +#ifdef CONFIG_KEYBOARD_ENABLED +KeyboardOutStateTypeDef KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE; +uint8_t KeyboardOutData[HID_KEYBOARD_OUTPUT_DATA_LEN]; +#endif + +uint8_t GetReportLoopIsRunning = 0; + + +static void Upstream_HID_ReceiveInterruptReport(void); +static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket); +static void Upstream_HID_SendControlReport(void); +static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket); + + + +void Upstream_HID_DeInit(void) +{ + if (UpstreamHidPacket != NULL) + { + Upstream_ReleasePacket(UpstreamHidPacket); + UpstreamHidPacket = NULL; + } + GetReportCallback = NULL; + GetReportLoopIsRunning = 0; + +#ifdef CONFIG_KEYBOARD_ENABLED + KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE; +#endif +} + + + +//Called by usbd_hid to request our next interrupt report +void Upstream_HID_GetInterruptReport(UpstreamHidGetReportCallback callback) +{ + GetReportCallback = callback; + + if (UpstreamHidPacket != NULL) + { + Upstream_ReleasePacket(UpstreamHidPacket); + UpstreamHidPacket = NULL; + } + + //Wakeup interrupts are apparently broken. + //So we check for resume when the host sends us something here. + Upstream_StateMachine_CheckResume(); + + //Start our internal loop? + if (!GetReportLoopIsRunning) + { + GetReportLoopIsRunning = 1; + Upstream_HID_ReceiveInterruptReport(); + } +} + + +//Our internal report request loop +static void Upstream_HID_ReceiveInterruptReport(void) +{ + UpstreamPacketTypeDef* freePacket; + InterfaceCommandClassTypeDef activeClass; + + activeClass = Upstream_StateMachine_CheckActiveClass(); + if ((activeClass != COMMAND_CLASS_HID_MOUSE) && + (activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + freePacket = Upstream_GetFreePacketImmediately(); + if (freePacket == NULL) + { + return; + } + + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = activeClass; + freePacket->Command = COMMAND_HID_GET_REPORT; + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + Upstream_ReceivePacket(Upstream_HID_ReceiveInterruptReportCallback); + } + else + { + Upstream_ReleasePacket(freePacket); + } +} + + + +static void Upstream_HID_ReceiveInterruptReportCallback(UpstreamPacketTypeDef* receivedPacket) +{ + UpstreamHidGetReportCallback tempReportCallback; + InterfaceCommandClassTypeDef activeClass; + uint32_t i; + uint8_t dataLength; + + + activeClass = Upstream_StateMachine_CheckActiveClass(); + if ((activeClass != COMMAND_CLASS_HID_MOUSE) && + (activeClass != COMMAND_CLASS_HID_KEYBOARD)) //add classes here + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + if (receivedPacket == NULL) //Error receiving packet + { + return; //Just give up... + } + + if (receivedPacket->Length16 == UPSTREAM_PACKET_HEADER_LEN_16) + { + //Zero-length reply indicates no data from downstream device + Upstream_ReleasePacket(receivedPacket); + } + else + { +#ifdef CONFIG_MOUSE_ENABLED + if (activeClass == COMMAND_CLASS_HID_MOUSE) + { + if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_MOUSE_INPUT_DATA_LEN + 1) / 2))) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + dataLength = HID_MOUSE_INPUT_DATA_LEN; + if ((receivedPacket->Data[0] & ~((1 << HID_MOUSE_MAX_BUTTONS) - 1)) != 0) //Check number of buttons received + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + //Mouse wakeup is triggered by button clicks only + if (receivedPacket->Data[0] != 0) + { + if (Upstream_StateMachine_GetSuspendState()) + { + Upstream_StateMachine_Wakeup(); //Send wakeup signal to host + } + } + +#ifdef CONFIG_MOUSE_BOT_DETECT_ENABLED + Upstream_HID_BotDetectMouse(receivedPacket->Data); +#endif + } + else +#endif +#ifdef CONFIG_KEYBOARD_ENABLED + if (activeClass == COMMAND_CLASS_HID_KEYBOARD) + { + if (receivedPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_INPUT_DATA_LEN + 1) / 2))) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + dataLength = HID_KEYBOARD_INPUT_DATA_LEN; + + if (receivedPacket->Data[1] != 0) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (receivedPacket->Data[i] > HID_KEYBOARD_MAX_KEY) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + } + + if (Upstream_StateMachine_GetSuspendState()) + { + Upstream_StateMachine_Wakeup(); //Send wakeup signal to host + } + +#ifdef CONFIG_KEYBOARD_BOT_DETECT_ENABLED + Upstream_HID_BotDetectKeyboard(receivedPacket->Data); +#endif + } + + //Other HID classes go here... + else +#endif + + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + if ((GetReportCallback == NULL) || + (UpstreamHidPacket != NULL) || + (Upstream_StateMachine_GetSuspendState())) + { + Upstream_ReleasePacket(receivedPacket); + } + else + { + //Send resulting data to host + UpstreamHidPacket = receivedPacket; //Save packet so we can free it when upstream USB transaction is done + tempReportCallback = GetReportCallback; + GetReportCallback = NULL; + tempReportCallback(receivedPacket->Data, dataLength); + } + } + + if (GetReportLoopIsRunning) + { +#ifdef CONFIG_KEYBOARD_ENABLED + //Check if we need to send OUT data to the keyboard before requesting next Interrupt IN data + if (KeyboardOutDataState == KEYBOARD_OUT_STATE_DATA_READY) + { + Upstream_HID_SendControlReport(); + } + else +#endif + { + Upstream_HID_ReceiveInterruptReport(); //Otherwise poll downstream again + } + } +} + + + +#ifdef CONFIG_KEYBOARD_ENABLED +void Upstream_HID_RequestSendControlReport(UpstreamPacketTypeDef* packetToSend, uint8_t dataLength) +{ + InterfaceCommandClassTypeDef activeClass; + uint32_t i; + + activeClass = Upstream_StateMachine_CheckActiveClass(); + if ((packetToSend == NULL) || + (activeClass != COMMAND_CLASS_HID_KEYBOARD) || + (dataLength != HID_KEYBOARD_OUTPUT_DATA_LEN)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + //Save data until after the next interrupt data is received from Downstream + KeyboardOutDataState = KEYBOARD_OUT_STATE_DATA_READY; + for (i = 0; i < HID_KEYBOARD_OUTPUT_DATA_LEN; i++) + { + KeyboardOutData[i] = packetToSend->Data[i]; + } +} + + + +static void Upstream_HID_SendControlReport(void) +{ + UpstreamPacketTypeDef* freePacket; + uint32_t i; + + KeyboardOutDataState = KEYBOARD_OUT_STATE_BUSY; + + freePacket = Upstream_GetFreePacketImmediately(); + if (freePacket == NULL) return; + + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16 + ((HID_KEYBOARD_OUTPUT_DATA_LEN + 1) / 2); + freePacket->CommandClass = COMMAND_CLASS_HID_KEYBOARD; + freePacket->Command = COMMAND_HID_SET_REPORT; + + for (i = 0; i < HID_KEYBOARD_OUTPUT_DATA_LEN; i++) + { + freePacket->Data[i] = KeyboardOutData[i]; + } + freePacket->Data[0] &= ((1 << HID_KEYBOARD_MAX_LED) - 1); + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + Upstream_ReceivePacket(Upstream_HID_SendControlReportCallback); + } + else + { + Upstream_ReleasePacket(freePacket); + } +} + + + +static void Upstream_HID_SendControlReportCallback(UpstreamPacketTypeDef* receivedPacket) +{ + InterfaceCommandClassTypeDef activeClass; + + activeClass = Upstream_StateMachine_CheckActiveClass(); + if (activeClass != COMMAND_CLASS_HID_KEYBOARD) //add classes here + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + if (receivedPacket == NULL) + { + return; //Just give up... + } + + Upstream_ReleasePacket(receivedPacket); + KeyboardOutDataState = KEYBOARD_OUT_STATE_IDLE; + Upstream_HID_ReceiveInterruptReport(); +} +#endif + +#endif //#if defined (CONFIG_KEYBOARD_ENABLED) || defined (CONFIG_MOUSE_ENABLED) + diff --git a/Upstream/Src/upstream_hid_botdetect.c b/Upstream/Src/upstream_hid_botdetect.c new file mode 100644 index 0000000..cc06c5f --- /dev/null +++ b/Upstream/Src/upstream_hid_botdetect.c @@ -0,0 +1,608 @@ +/* + * upstream_hid_botdetect.c + * + * Created on: Aug 17, 2017 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +#include "upstream_hid_botdetect.h" +#include "upstream_hid.h" +#include "build_config.h" +#include "usbd_hid.h" +#include "math.h" + + + +//Variables common between keyboard and mouse bot detection: +volatile LockoutStateTypeDef LockoutState = LOCKOUT_STATE_INACTIVE; +uint32_t TemporaryLockoutTimeMs; +uint8_t TemporaryLockoutCount = 0; + + +//Variables specific to keyboard bot detection: +#if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + uint32_t LastKeyDownTime = 0; + KeyTimerLogTypeDef KeyTimerLog[KEYBOARD_BOTDETECT_MAX_ACTIVE_KEYS] = {0}; + uint8_t OldKeyboardInData[HID_KEYBOARD_INPUT_DATA_LEN] = {0}; + + uint8_t KeyDelayFastBinDrainDivideCount = 0; + uint8_t KeyDelaySlowBinDrainDivideCount = 0; + uint8_t KeyDowntimeFastBinDrainDivideCount = 0; + uint8_t KeyDowntimeSlowBinDrainDivideCount = 0; + uint8_t KeyDelayFastBinArray[KEYBOARD_BOTDETECT_FAST_BIN_COUNT] = {0}; + uint8_t KeyDelaySlowBinArray[KEYBOARD_BOTDETECT_SLOW_BIN_COUNT] = {0}; + uint8_t KeyDowntimeFastBinArray[KEYBOARD_BOTDETECT_FAST_BIN_COUNT] = {0}; + uint8_t KeyDowntimeSlowBinArray[KEYBOARD_BOTDETECT_SLOW_BIN_COUNT] = {0}; + + //Debug: +// uint8_t KeyDelayFastBinArrayPeak; +// uint8_t KeyDelaySlowBinArrayPeak; +// uint8_t KeyDowntimeFastBinArrayPeak; +// uint8_t KeyDowntimeSlowBinArrayPeak; + + static uint32_t Upstream_HID_BotDetectKeyboard_RolloverCheck(uint8_t* keyboardInData); + static void Upstream_HID_BotDetectKeyboard_DoLockout(void); + static void Upstream_HID_BotDetectKeyboard_KeyDown(uint8_t keyCode); + static void Upstream_HID_BotDetectKeyboard_KeyUp(uint8_t keyCode); +#endif + + + +//Variables specific to mouse bot detection: +#if defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED) + uint32_t LastMouseMoveTime = 0; + + //Jump detection stuff + uint32_t FirstMouseMoveTime = 0; + uint8_t JumpMouseIsMoving = 0; + + //Constant acceleration detection stuff + uint16_t MouseVelocityHistory[MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE] = {0}; + int32_t PreviousSmoothedAcceleration = 0; + int32_t ConstantAccelerationCounter = 0; + + //Jiggle detection stuff + uint8_t MouseStopIntervalBinDrainDivideCount = 0; + uint8_t MouseStopIntervalBinArray[MOUSE_BOTDETECT_JIGGLE_BIN_COUNT] = {0}; + + //Debug: +// int8_t ConstantAccelerationCounterMax = 0; +// int8_t ConstantAccelerationCounterMin = 0; +// uint8_t MouseStopIntervalBinArrayPeak = 0; + + static void Upstream_HID_BotDetectMouse_DoLockout(void); +#endif + + + +//Code specific to keyboard bot detection: +#if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + +//Checks if received keyboard data is from a real human. +//This is not entirely bulletproof as an attacking device may randomize its keypresses. +void Upstream_HID_BotDetectKeyboard(uint8_t* keyboardInData) +{ + uint32_t i; + uint32_t j; + uint8_t tempModifier; + + if (Upstream_HID_BotDetectKeyboard_RolloverCheck(keyboardInData)) return; + + //Process modifier keys in first byte + tempModifier = keyboardInData[0]; + for (i = 0; i < 8; i++) + { + if ((tempModifier & 1) && !(OldKeyboardInData[0] & 1)) + { + Upstream_HID_BotDetectKeyboard_KeyDown(KEY_MODIFIER_BASE + i); + } + if (!(tempModifier & 1) && (OldKeyboardInData[0] & 1)) + { + Upstream_HID_BotDetectKeyboard_KeyUp(KEY_MODIFIER_BASE + i); + } + tempModifier >>= 1; + OldKeyboardInData[0] >>= 1; + } + + + //Process key array: search for keydowns + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (keyboardInData[i] >= KEY_A) + { + for (j = 2; j < HID_KEYBOARD_INPUT_DATA_LEN; j++) + { + if (keyboardInData[i] == OldKeyboardInData[j]) break; + } + if (j >= HID_KEYBOARD_INPUT_DATA_LEN) + { + Upstream_HID_BotDetectKeyboard_KeyDown(keyboardInData[i]); + } + } + } + + //Process key array: search for keyups + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (OldKeyboardInData[i] >= KEY_A) + { + for (j = 2; j < HID_KEYBOARD_INPUT_DATA_LEN; j++) + { + if (OldKeyboardInData[i] == keyboardInData[j]) break; + } + if (j >= HID_KEYBOARD_INPUT_DATA_LEN) + { + Upstream_HID_BotDetectKeyboard_KeyUp(OldKeyboardInData[i]); + } + } + } + + //Check for evidence of bot typing + for (i = 0; i < KEYBOARD_BOTDETECT_FAST_BIN_COUNT; i++) + { + if ((KeyDelayFastBinArray[i] > KEYBOARD_BOTDETECT_LOCKOUT_BIN_THRESHOLD) || + (KeyDowntimeFastBinArray[i] > KEYBOARD_BOTDETECT_LOCKOUT_BIN_THRESHOLD)) + { + Upstream_HID_BotDetectKeyboard_DoLockout(); + break; + } + //Debug: +// if (KeyDelayFastBinArray[i] > KeyDelayFastBinArrayPeak) KeyDelayFastBinArrayPeak = KeyDelayFastBinArray[i]; +// if (KeyDowntimeFastBinArray[i] > KeyDowntimeFastBinArrayPeak) KeyDowntimeFastBinArrayPeak = KeyDowntimeFastBinArray[i]; + } + for (i = 0; i < KEYBOARD_BOTDETECT_SLOW_BIN_COUNT; i++) + { + if ((KeyDelaySlowBinArray[i] > KEYBOARD_BOTDETECT_LOCKOUT_BIN_THRESHOLD) || + (KeyDowntimeSlowBinArray[i] > KEYBOARD_BOTDETECT_LOCKOUT_BIN_THRESHOLD)) + { + Upstream_HID_BotDetectKeyboard_DoLockout(); + break; + } + //Debug: +// if (KeyDelaySlowBinArray[i] > KeyDelaySlowBinArrayPeak) KeyDelaySlowBinArrayPeak = KeyDelaySlowBinArray[i]; +// if (KeyDowntimeSlowBinArray[i] > KeyDowntimeSlowBinArrayPeak) KeyDowntimeSlowBinArrayPeak = KeyDowntimeSlowBinArray[i]; + } + + //Copy new data to old array + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + OldKeyboardInData[i] = keyboardInData[i]; + } + + //Host receives no data if we are locked + if ((LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) || + (LockoutState == LOCKOUT_STATE_PERMANENT_ACTIVE)) + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = 0; + } + } +} + + + +static void Upstream_HID_BotDetectKeyboard_DoLockout(void) +{ + uint32_t i; + + if (LockoutState == LOCKOUT_STATE_PERMANENT_ACTIVE) return; + + //Are we already in warning state? -> activate permanent lockout + if ((LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) || + (LockoutState == LOCKOUT_STATE_TEMPORARY_FLASHING)) + { + LockoutState = LOCKOUT_STATE_PERMANENT_ACTIVE; + return; + } + + //Three (temporary) strikes -> you're out! + if (++TemporaryLockoutCount >= 3) + { + LockoutState = LOCKOUT_STATE_PERMANENT_ACTIVE; + LED_SetState(LED_STATUS_FLASH_BOTDETECT); + return; + } + + //Otherwise, reset counters and give warning + for (i = 0; i < KEYBOARD_BOTDETECT_FAST_BIN_COUNT; i++) + { + KeyDelayFastBinArray[i] = 0; + KeyDowntimeFastBinArray[i] = 0; + } + for (i = 0; i < KEYBOARD_BOTDETECT_SLOW_BIN_COUNT; i++) + { + KeyDelaySlowBinArray[i] = 0; + KeyDowntimeSlowBinArray[i] = 0; + } + + TemporaryLockoutTimeMs = 0; + LockoutState = LOCKOUT_STATE_TEMPORARY_ACTIVE; + LED_SetState(LED_STATUS_FLASH_BOTDETECT); +} + + + +//Keyboard reports a rollover code when there are too many keys to scan/report. +static uint32_t Upstream_HID_BotDetectKeyboard_RolloverCheck(uint8_t* keyboardInData) +{ + uint32_t i; + + for (i = 2; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + if (keyboardInData[i] == KEY_ROLLOVER) break; + } + if (i >= HID_KEYBOARD_INPUT_DATA_LEN) return 0; + + //As I am unclear on the exact usage and interpretation of the rollover code, + //we are going to play it safe by copying the old keyboard data over the new array. + //This ensures the host interprets a rollover event exactly the way we do! + + //Host receives no data if we are locked + if ((LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) || + (LockoutState == LOCKOUT_STATE_PERMANENT_ACTIVE)) + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = 0; + } + } + else + { + for (i = 0; i < HID_KEYBOARD_INPUT_DATA_LEN; i++) + { + keyboardInData[i] = OldKeyboardInData[i]; + } + } + return 1; +} + + + +static void Upstream_HID_BotDetectKeyboard_KeyDown(uint8_t keyCode) +{ + uint32_t i; + uint32_t keyDelay; + uint32_t now = HAL_GetTick(); + + keyDelay = now - LastKeyDownTime; + if (keyDelay < (KEYBOARD_BOTDETECT_FAST_BIN_WIDTH_MS * KEYBOARD_BOTDETECT_FAST_BIN_COUNT)) + { + KeyDelayFastBinArray[(keyDelay / KEYBOARD_BOTDETECT_FAST_BIN_WIDTH_MS)]++; //Add key to fast bin + + //Drain fast bins at specified rate + KeyDelayFastBinDrainDivideCount++; + if (KeyDelayFastBinDrainDivideCount >= KEYBOARD_BOTDETECT_FAST_BIN_DRAIN_DIVIDER) + { + KeyDelayFastBinDrainDivideCount = 0; + for (i = 0; i < KEYBOARD_BOTDETECT_FAST_BIN_COUNT; i++) + { + if (KeyDelayFastBinArray[i] > 0) KeyDelayFastBinArray[i]--; + } + } + } + else + { + keyDelay = keyDelay % (KEYBOARD_BOTDETECT_SLOW_BIN_WIDTH_MS * KEYBOARD_BOTDETECT_SLOW_BIN_COUNT); //Wrap slow key time into the slow array + KeyDelaySlowBinArray[(keyDelay / KEYBOARD_BOTDETECT_SLOW_BIN_WIDTH_MS)]++; //Add key to slow bin + + //Drain slow bins at specified rate + KeyDelaySlowBinDrainDivideCount++; + if (KeyDelaySlowBinDrainDivideCount >= KEYBOARD_BOTDETECT_SLOW_BIN_DRAIN_DIVIDER) + { + KeyDelaySlowBinDrainDivideCount = 0; + for (i = 0; i < KEYBOARD_BOTDETECT_SLOW_BIN_COUNT; i++) + { + if (KeyDelaySlowBinArray[i] > 0) KeyDelaySlowBinArray[i]--; + } + } + } + LastKeyDownTime = now; + + for (i = 0; i < KEYBOARD_BOTDETECT_MAX_ACTIVE_KEYS; i++) + { + if (KeyTimerLog[i].KeyCode == 0) break; + } + if (i >= KEYBOARD_BOTDETECT_MAX_ACTIVE_KEYS) while (1); //Totally should not happen + KeyTimerLog[i].KeyCode = keyCode; + KeyTimerLog[i].KeyDownStart = now; +} + + + +static void Upstream_HID_BotDetectKeyboard_KeyUp(uint8_t keyCode) +{ + uint32_t i; + uint32_t keyDowntime; + + for (i = 0; i < KEYBOARD_BOTDETECT_MAX_ACTIVE_KEYS; i++) + { + if (KeyTimerLog[i].KeyCode == keyCode) break; + } + if (i >= KEYBOARD_BOTDETECT_MAX_ACTIVE_KEYS) while (1); //Totally should not happen + + KeyTimerLog[i].KeyCode = 0; //Clear out the key entry + keyDowntime = HAL_GetTick() - KeyTimerLog[i].KeyDownStart; + if (keyDowntime < (KEYBOARD_BOTDETECT_FAST_BIN_WIDTH_MS * KEYBOARD_BOTDETECT_FAST_BIN_COUNT)) + { + KeyDowntimeFastBinArray[(keyDowntime / KEYBOARD_BOTDETECT_FAST_BIN_WIDTH_MS)]++; //Add key to fast bin + + //Drain fast bins at specified rate + KeyDowntimeFastBinDrainDivideCount++; + if (KeyDowntimeFastBinDrainDivideCount >= KEYBOARD_BOTDETECT_FAST_BIN_DRAIN_DIVIDER) + { + KeyDowntimeFastBinDrainDivideCount = 0; + for (i = 0; i < KEYBOARD_BOTDETECT_FAST_BIN_COUNT; i++) + { + if (KeyDowntimeFastBinArray[i] > 0) KeyDowntimeFastBinArray[i]--; + } + } + } + else + { + keyDowntime = keyDowntime % (KEYBOARD_BOTDETECT_SLOW_BIN_WIDTH_MS * KEYBOARD_BOTDETECT_SLOW_BIN_COUNT); //Wrap slow key time into the slow array + KeyDowntimeSlowBinArray[(keyDowntime / KEYBOARD_BOTDETECT_SLOW_BIN_WIDTH_MS)]++; //Add key to slow bin + + //Drain slow bins at specified rate + KeyDowntimeSlowBinDrainDivideCount++; + if (KeyDowntimeSlowBinDrainDivideCount >= KEYBOARD_BOTDETECT_SLOW_BIN_DRAIN_DIVIDER) + { + KeyDowntimeSlowBinDrainDivideCount = 0; + for (i = 0; i < KEYBOARD_BOTDETECT_SLOW_BIN_COUNT; i++) + { + if (KeyDowntimeSlowBinArray[i] > 0) KeyDowntimeSlowBinArray[i]--; + } + } + } +} + +#endif //if defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED) + + + + + +//Called by Systick_Handler every 1ms, at high interrupt priority. +void Upstream_HID_BotDetect_Systick(void) +{ +#if (defined (CONFIG_KEYBOARD_ENABLED) && defined (CONFIG_KEYBOARD_BOT_DETECT_ENABLED)) || \ + (defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED)) + + //Check if temporary lockout has expired + if (LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) + { + if (TemporaryLockoutTimeMs++ > BOTDETECT_TEMPORARY_LOCKOUT_TIME_MS) + { + LockoutState = LOCKOUT_STATE_TEMPORARY_FLASHING; + } + } + else if (LockoutState == LOCKOUT_STATE_TEMPORARY_FLASHING) + { + if (TemporaryLockoutTimeMs++ > BOTDETECT_TEMPORARY_LOCKOUT_FLASH_TIME_MS) + { + LED_SetState(LED_STATUS_OFF); + LockoutState = LOCKOUT_STATE_INACTIVE; + } + } +#endif +} + + + +//Code specific to mouse bot detection: +#if defined (CONFIG_MOUSE_ENABLED) && defined (CONFIG_MOUSE_BOT_DETECT_ENABLED) + +void Upstream_HID_BotDetectMouse(uint8_t* mouseInData) +{ + uint32_t i; + uint32_t now = HAL_GetTick(); + uint32_t moveDelay; + uint32_t velocity; + int8_t mouseX; + int8_t mouseY; + + //Constant acceleration detection stuff + uint32_t newSmoothedVelocity; + uint32_t oldSmoothedVelocity; + int32_t newSmoothedAcceleration; + int32_t smoothedAccelerationMatchError; + + + mouseX = mouseInData[1]; + mouseY = mouseInData[2]; + velocity = (sqrtf(((int32_t)mouseX * mouseX) + + ((int32_t)mouseY * mouseY))) * MOUSE_BOTDETECT_VELOCITY_MULTIPLIER; //Multiply floating-point sqrt result to avoid integer rounding errors + moveDelay = now - LastMouseMoveTime; + + + //Reset constant acceleration detection state after a few seconds of inactivity + if (moveDelay > MOUSE_BOTDETECT_VELOCITY_RESET_TIMEOUT_MS) + { + //Is this the start of a new movement? + if (velocity != 0) + { + for (i = 0; i < MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE; i++) + { + MouseVelocityHistory[i] = 0; + } + ConstantAccelerationCounter = 0; + } + } + + + //Jiggle detection: did the mouse stop moving? + if (moveDelay > ((MOUSE_BOTDETECT_JIGGLE_STOP_PERIODS * HID_FS_BINTERVAL) - (HID_FS_BINTERVAL / 2))) + { + //Is this the start of a new movement? + if (velocity != 0) + { + //Jiggle detection: add stopped time to jiggle bins + moveDelay = moveDelay % (MOUSE_BOTDETECT_JIGGLE_BIN_WIDTH_MS * MOUSE_BOTDETECT_JIGGLE_BIN_COUNT); //Wrap stopped time into the array + MouseStopIntervalBinArray[(moveDelay / MOUSE_BOTDETECT_JIGGLE_BIN_WIDTH_MS)]++; + if (MouseStopIntervalBinArray[(moveDelay / MOUSE_BOTDETECT_JIGGLE_BIN_WIDTH_MS)] > MOUSE_BOTDETECT_LOCKOUT_JIGGLE_BIN_THRESHOLD) + { + Upstream_HID_BotDetectMouse_DoLockout(); + } + + //Debug: +// if (MouseStopIntervalBinArray[(moveDelay / MOUSE_BOTDETECT_JIGGLE_BIN_WIDTH_MS)] > MouseStopIntervalBinArrayPeak) +// { +// MouseStopIntervalBinArrayPeak = MouseStopIntervalBinArray[(moveDelay / MOUSE_BOTDETECT_JIGGLE_BIN_WIDTH_MS)]; +// } + + //Drain jiggle bins at specified rate + MouseStopIntervalBinDrainDivideCount++; + if (MouseStopIntervalBinDrainDivideCount >= MOUSE_BOTDETECT_JIGGLE_BIN_DIVIDER) + { + MouseStopIntervalBinDrainDivideCount = 0; + for (i = 0; i < MOUSE_BOTDETECT_JIGGLE_BIN_COUNT; i++) + { + if (MouseStopIntervalBinArray[i] > 0) MouseStopIntervalBinArray[i]--; + } + } + } + } + + + //Jump detection: did the mouse stop moving briefly? + if (moveDelay > ((MOUSE_BOTDETECT_JUMP_PERIODS * HID_FS_BINTERVAL) - (HID_FS_BINTERVAL / 2))) + { + FirstMouseMoveTime = 0; + if (JumpMouseIsMoving) //Was a significant movement in progress? + { + JumpMouseIsMoving = 0; + if ((LastMouseMoveTime - FirstMouseMoveTime) < ((MOUSE_BOTDETECT_JUMP_PERIODS * HID_FS_BINTERVAL) - (HID_FS_BINTERVAL / 2))) + { + Upstream_HID_BotDetectMouse_DoLockout(); + } + } + } + + + if (velocity != 0) + { + //Jump detection + LastMouseMoveTime = now; + if (FirstMouseMoveTime == 0) + { + FirstMouseMoveTime = now; + } + if (velocity > (MOUSE_BOTDETECT_JUMP_VELOCITY_THRESHOLD * MOUSE_BOTDETECT_VELOCITY_MULTIPLIER)) + { + JumpMouseIsMoving = 1; + } + + //Constant acceleration detection + for (i = (MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE - 1); i > 0; i--) //Shuffle down history data + { + MouseVelocityHistory[i] = MouseVelocityHistory[i - 1]; + } + MouseVelocityHistory[0] = velocity; //Store latest data at head + + if (MouseVelocityHistory[(MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE - 1)] > 0) + { + velocity = 0; //Calculate new and old average velocities + for (i = 0; i < (MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE / 2); i++) + { + velocity += MouseVelocityHistory[i]; + } + newSmoothedVelocity = (velocity * 8) / (MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE / 2); //Multiply velocity up to avoid rounding errors on divide + + velocity = 0; + for (i = (MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE / 2); i < MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE; i++) + { + velocity += MouseVelocityHistory[i]; + } + oldSmoothedVelocity = (velocity * 8) / (MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE / 2); //Multiply velocity up to avoid rounding errors on divide + + newSmoothedAcceleration = newSmoothedVelocity - oldSmoothedVelocity; + smoothedAccelerationMatchError = (oldSmoothedVelocity * MOUSE_BOTDETECT_VELOCITY_MATCH_ERROR) / MOUSE_BOTDETECT_VELOCITY_MATCH_BASE; + if (((PreviousSmoothedAcceleration + smoothedAccelerationMatchError) >= newSmoothedAcceleration) && + ((PreviousSmoothedAcceleration - smoothedAccelerationMatchError) <= newSmoothedAcceleration)) + { + ConstantAccelerationCounter++; + if (ConstantAccelerationCounter > MOUSE_BOTDETECT_CONSTANT_ACCEL_LOCKOUT) + { + Upstream_HID_BotDetectMouse_DoLockout(); + } + else if (ConstantAccelerationCounter >= MOUSE_BOTDETECT_CONSTANT_ACCEL_STOP) //Stop mouse movement if it looks suspiciously constant + { + mouseInData[1] = 0; + mouseInData[2] = 0; + } + } + else + { + if (ConstantAccelerationCounter > -MOUSE_BOTDETECT_CONSTANT_ACCEL_CREDIT) ConstantAccelerationCounter--; + } + PreviousSmoothedAcceleration = newSmoothedAcceleration; + + //Debug: +// if (ConstantAccelerationCounter > ConstantAccelerationCounterMax) ConstantAccelerationCounterMax = ConstantAccelerationCounter; +// if (ConstantAccelerationCounter < ConstantAccelerationCounterMin) ConstantAccelerationCounterMin = ConstantAccelerationCounter; + } + } + else + { + mouseInData[1] = 0; //If we don't want to process this event, makes sure no movement data gets through + mouseInData[2] = 0; + } + + //Host receives no data if we are locked + if ((LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) || + (LockoutState == LOCKOUT_STATE_PERMANENT_ACTIVE)) + { + for (i = 0; i < HID_MOUSE_INPUT_DATA_LEN; i++) + { + mouseInData[i] = 0; + } + } +} + + + +static void Upstream_HID_BotDetectMouse_DoLockout(void) +{ + uint32_t i; + + if (LockoutState == LOCKOUT_STATE_PERMANENT_ACTIVE) return; + + //Are we already in warning state? -> activate permanent lockout + if ((LockoutState == LOCKOUT_STATE_TEMPORARY_ACTIVE) || + (LockoutState == LOCKOUT_STATE_TEMPORARY_FLASHING)) + { + LockoutState = LOCKOUT_STATE_PERMANENT_ACTIVE; + return; + } + + //Three (temporary) strikes -> you're out! + if (++TemporaryLockoutCount >= 3) + { + LockoutState = LOCKOUT_STATE_PERMANENT_ACTIVE; + LED_SetState(LED_STATUS_FLASH_BOTDETECT); + return; + } + + //Otherwise, reset counters and give warning + for (i = 0; i < MOUSE_BOTDETECT_VELOCITY_HISTORY_SIZE; i++) + { + MouseVelocityHistory[i] = 0; + } + ConstantAccelerationCounter = 0; + + for (i = 0; i < MOUSE_BOTDETECT_JIGGLE_BIN_COUNT; i++) + { + MouseStopIntervalBinArray[i] = 0; + } + + TemporaryLockoutTimeMs = 0; + LockoutState = LOCKOUT_STATE_TEMPORARY_ACTIVE; + LED_SetState(LED_STATUS_FLASH_BOTDETECT); +} + +#endif diff --git a/Upstream/Src/upstream_msc.c b/Upstream/Src/upstream_msc.c new file mode 100644 index 0000000..aac61d2 --- /dev/null +++ b/Upstream/Src/upstream_msc.c @@ -0,0 +1,403 @@ +/* + * upstream_msc.c + * + * Created on: 4/07/2015 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +#include "upstream_interface_def.h" +#include "upstream_msc.h" +#include "upstream_spi.h" +#include "upstream_statemachine.h" +#include "stm32f4xx_hal.h" +#include "build_config.h" + + +#ifdef CONFIG_MASS_STORAGE_ENABLED + + +//Stuff we need to save for our callbacks to use: +UpstreamMSCCallbackTypeDef TestReadyCallback; +UpstreamMSCCallbackTypeDef BeginReadCallback; +UpstreamMSCCallbackTypeDef BeginWriteCallback; +UpstreamMSCCallbackTypeDef DisconnectCallback; +UpstreamMSCCallbackUintPacketTypeDef GetCapacityCallback; +UpstreamMSCCallbackPacketTypeDef GetStreamDataCallback; +uint64_t BlockStart; +uint32_t BlockCount; +uint32_t ByteCount; + +UpstreamPacketTypeDef* ReadStreamPacket; +uint8_t ReadStreamBusy; + + +static void Upstream_MSC_TestReadyFreePacketCallback(UpstreamPacketTypeDef* freePacket); +static void Upstream_MSC_TestReadyReplyCallback(UpstreamPacketTypeDef* replyPacket); +static void Upstream_MSC_GetCapacityFreePacketCallback(UpstreamPacketTypeDef* freePacket); +static void Upstream_MSC_GetCapacityReplyCallback(UpstreamPacketTypeDef* replyPacket); +static void Upstream_MSC_GetStreamDataPacketCallback(UpstreamPacketTypeDef* replyPacket); +static void Upstream_MSC_BeginReadFreePacketCallback(UpstreamPacketTypeDef* freePacket); +static void Upstream_MSC_BeginWriteFreePacketCallback(UpstreamPacketTypeDef* freePacket); +static void Upstream_MSC_RequestDisconnectFreePacketCallback(UpstreamPacketTypeDef* freePacket); +static void Upstream_MSC_RequestDisconnectReplyCallback(UpstreamPacketTypeDef* replyPacket); + + + +HAL_StatusTypeDef Upstream_MSC_TestReady(UpstreamMSCCallbackTypeDef callback) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + //UPSTREAM_STATEMACHINE_FREAKOUT; + return HAL_ERROR; + } + + TestReadyCallback = callback; + return Upstream_GetFreePacket(Upstream_MSC_TestReadyFreePacketCallback); +} + + + +static void Upstream_MSC_TestReadyFreePacketCallback(UpstreamPacketTypeDef* freePacket) +{ + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_TEST_UNIT_READY; + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + //Upstream_PacketManager will free the packet when the transfer is done + if (Upstream_ReceivePacket(Upstream_MSC_TestReadyReplyCallback) != HAL_OK) + { + TestReadyCallback(HAL_ERROR); + } + return; + } + + //else: + Upstream_ReleasePacket(freePacket); + TestReadyCallback(HAL_ERROR); +} + + + +static void Upstream_MSC_TestReadyReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return; + } + + if (replyPacket == NULL) + { + TestReadyCallback(HAL_ERROR); + return; + } + + if ((replyPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + 1)) || + (replyPacket->Data[0] != HAL_OK)) + { + Upstream_ReleasePacket(replyPacket); + TestReadyCallback(HAL_ERROR); + return; + } + + Upstream_ReleasePacket(replyPacket); + TestReadyCallback(HAL_OK); +} + + + +HAL_StatusTypeDef Upstream_MSC_GetCapacity(UpstreamMSCCallbackUintPacketTypeDef callback) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return HAL_ERROR; + } + + GetCapacityCallback = callback; + return Upstream_GetFreePacket(Upstream_MSC_GetCapacityFreePacketCallback); +} + + + +static void Upstream_MSC_GetCapacityFreePacketCallback(UpstreamPacketTypeDef* freePacket) +{ + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_GET_CAPACITY; + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + if (Upstream_ReceivePacket(Upstream_MSC_GetCapacityReplyCallback) != HAL_OK) + { + GetCapacityCallback(NULL, 0, 0); + } + return; + } + + //else: + Upstream_ReleasePacket(freePacket); + GetCapacityCallback(NULL, 0, 0); +} + + +static void Upstream_MSC_GetCapacityReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + uint32_t block_count; + uint32_t block_size; + + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return; + } + + if (replyPacket == NULL) + { + GetCapacityCallback(NULL, 0, 0); + return; + } + + if (replyPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + (8 / 2))) + { + Upstream_ReleasePacket(replyPacket); + GetCapacityCallback(NULL, 0, 0); + return; + } + + block_count = *(uint32_t*)&(replyPacket->Data[0]); + block_size = *(uint32_t*)&(replyPacket->Data[4]); + + if ((block_count < MSC_MINIMUM_BLOCK_COUNT) || + (block_size != MSC_SUPPORTED_BLOCK_SIZE)) + { + Upstream_ReleasePacket(replyPacket); + GetCapacityCallback(NULL, 0, 0); + return; + } + GetCapacityCallback(replyPacket, block_count, block_size); //usb_msc_scsi will use this packet, so don't release now +} + + + +HAL_StatusTypeDef Upstream_MSC_BeginRead(UpstreamMSCCallbackTypeDef callback, + uint64_t readBlockStart, + uint32_t readBlockCount, + uint32_t readByteCount) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return HAL_ERROR; + } + + BlockStart = readBlockStart; + BlockCount = readBlockCount; + ByteCount = readByteCount; + ReadStreamPacket = NULL; //Prepare for GetStreamDataPacket's use + ReadStreamBusy = 0; + + BeginReadCallback = callback; + return Upstream_GetFreePacket(Upstream_MSC_BeginReadFreePacketCallback); +} + + + +static void Upstream_MSC_BeginReadFreePacketCallback(UpstreamPacketTypeDef* freePacket) +{ + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16 + ((4 * 3) / 2); + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_READ; + *(uint64_t*)&(freePacket->Data[0]) = BlockStart; + *(uint32_t*)&(freePacket->Data[8]) = BlockCount; + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + BeginReadCallback(HAL_OK); + return; + } + + //else: + Upstream_ReleasePacket(freePacket); + BeginReadCallback(HAL_ERROR); +} + + + +HAL_StatusTypeDef Upstream_MSC_GetStreamDataPacket(UpstreamMSCCallbackPacketTypeDef callback) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return HAL_ERROR; + } + GetStreamDataCallback = callback; + + if (ReadStreamBusy != 0) + { + return HAL_OK; + } + ReadStreamBusy = 1; + + if (ReadStreamPacket && GetStreamDataCallback) //Do we have a stored packet and an address to send it? + { + Upstream_MSC_GetStreamDataPacketCallback(ReadStreamPacket); //Send it now! + ReadStreamPacket = NULL; + return HAL_OK; //Our callback will call us again, so we don't need to get a packet in this case. + } + return Upstream_ReceivePacket(Upstream_MSC_GetStreamDataPacketCallback); +} + + + +static void Upstream_MSC_GetStreamDataPacketCallback(UpstreamPacketTypeDef* replyPacket) +{ + uint32_t dataLength8; + + ReadStreamBusy = 0; + + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return; + } + + if (GetStreamDataCallback == NULL) + { + ReadStreamPacket = replyPacket; //We used up our callback already, so save this one for later. + return; + } + + if (replyPacket == NULL) + { + GetStreamDataCallback(NULL, 0); + return; + } + + dataLength8 = (replyPacket->Length16 - UPSTREAM_PACKET_HEADER_LEN_16) * 2; + + if (((replyPacket->CommandClass & COMMAND_CLASS_DATA_FLAG) == 0) || //Any 'command' reply (as opposed to 'data' reply) is an automatic fail here + (dataLength8 != MSC_MINIMUM_DATA_UNIT) || //Should only receive integer units of MSC block size + (dataLength8 > ByteCount)) //No more data than expected transfer length + { + GetStreamDataCallback(NULL, 0); + return; + } + + ByteCount -= dataLength8; + GetStreamDataCallback(replyPacket, dataLength8); //usb_msc_scsi will use this packet, so don't release now + if (ByteCount > 0) + { + Upstream_MSC_GetStreamDataPacket(NULL); //Try to get the next packet now, before USB asks for it + } +} + + +#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED +HAL_StatusTypeDef Upstream_MSC_BeginWrite(UpstreamMSCCallbackTypeDef callback, + uint64_t writeBlockStart, + uint32_t writeBlockCount) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return HAL_ERROR; + } + + LED_SetState(LED_STATUS_FLASH_READWRITE); + BlockStart = writeBlockStart; + BlockCount = writeBlockCount; + BeginWriteCallback = callback; + return Upstream_GetFreePacket(Upstream_MSC_BeginWriteFreePacketCallback); +} + + + +static void Upstream_MSC_BeginWriteFreePacketCallback(UpstreamPacketTypeDef* freePacket) +{ + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16 + ((4 * 3) / 2); + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_WRITE; + *(uint64_t*)&(freePacket->Data[0]) = BlockStart; + *(uint32_t*)&(freePacket->Data[8]) = BlockCount; + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + BeginWriteCallback(HAL_OK); + return; + } + + //else: + Upstream_ReleasePacket(freePacket); + BeginWriteCallback(HAL_ERROR); +} + + + +HAL_StatusTypeDef Upstream_MSC_PutStreamDataPacket(UpstreamPacketTypeDef* packetToSend, + uint32_t dataLength8) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) + { + return HAL_ERROR; + } + + if ((dataLength8 % 2) != 0) + { + return HAL_ERROR; + } + + packetToSend->Length16 = (dataLength8 / 2) + UPSTREAM_PACKET_HEADER_LEN_16; + packetToSend->CommandClass = COMMAND_CLASS_MASS_STORAGE | COMMAND_CLASS_DATA_FLAG; + packetToSend->Command = COMMAND_MSC_WRITE; + return Upstream_TransmitPacket(packetToSend); +} +#endif //#ifdef CONFIG_MASS_STORAGE_WRITES_PERMITTED + + + +HAL_StatusTypeDef Upstream_MSC_RequestDisconnect(UpstreamMSCCallbackTypeDef callback) +{ + if (Upstream_StateMachine_CheckActiveClass() != COMMAND_CLASS_MASS_STORAGE) return HAL_ERROR; + + DisconnectCallback = callback; + return Upstream_GetFreePacket(Upstream_MSC_RequestDisconnectFreePacketCallback); +} + + + +static void Upstream_MSC_RequestDisconnectFreePacketCallback(UpstreamPacketTypeDef* freePacket) +{ + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_DISCONNECT; + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + //Upstream_PacketManager will free the packet when the transfer is done + if (Upstream_ReceivePacket(Upstream_MSC_RequestDisconnectReplyCallback) != HAL_OK) + { + DisconnectCallback(HAL_ERROR); + } + return; + } + + //else: + Upstream_ReleasePacket(freePacket); + DisconnectCallback(HAL_ERROR); +} + + + +static void Upstream_MSC_RequestDisconnectReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + //Acknowledge the SCSI Stop command to host now. + //We will disconnect from host when Downstream replies with COMMAND_ERROR_DEVICE_DISCONNECTED. + + Upstream_ReleasePacket(replyPacket); + DisconnectCallback(HAL_OK); +} + + + +#endif //#ifdef CONFIG_MASS_STORAGE_ENABLED + diff --git a/Upstream/Src/upstream_spi.c b/Upstream/Src/upstream_spi.c new file mode 100644 index 0000000..fd79377 --- /dev/null +++ b/Upstream/Src/upstream_spi.c @@ -0,0 +1,545 @@ +/* + * upstream_spi.c + * + * Created on: 21/06/2015 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#include "upstream_interface_def.h" +#include "upstream_spi.h" +#include "stm32f4xx_hal.h" +#include "board_config.h" +#include "interrupts.h" + + + +SPI_HandleTypeDef Hspi1; +UpstreamPacketTypeDef UpstreamPacket0; +UpstreamPacketTypeDef UpstreamPacket1; +UpstreamPacketTypeDef* CurrentWorkingPacket; +UpstreamPacketTypeDef* NextTxPacket = NULL; //Indicates we have a pending TX packet + +InterfaceStateTypeDef UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; +FreePacketCallbackTypeDef PendingFreePacketCallback = NULL; //Indicates someone is waiting for a packet buffer to become available +SpiPacketReceivedCallbackTypeDef ReceivePacketCallback = NULL; //Indicates someone is waiting for a received packet + +uint32_t TemporaryIncomingPacketLength; //We don't actually care about what Downstream sends us when we are transmitting. We just need somewhere to put it so that our own packet length is not overwritten. +uint8_t TxOkInterruptReceived = 0; +uint8_t SentCommandClass; +uint8_t SentCommand; + + +void Upstream_BeginTransmitPacketSize(void); +void Upstream_BeginTransmitPacketBody(void); +HAL_StatusTypeDef Upstream_CheckBeginPacketReception(void); +void Upstream_BeginReceivePacketSize(UpstreamPacketTypeDef* freePacket); +void Upstream_BeginReceivePacketBody(void); + + + +void Upstream_InitSPI(void) +{ + UpstreamPacket0.Busy = NOT_BUSY; + UpstreamPacket1.Busy = NOT_BUSY; + + Hspi1.Instance = SPI1; + Hspi1.State = HAL_SPI_STATE_RESET; + Hspi1.Init.Mode = SPI_MODE_MASTER; + Hspi1.Init.Direction = SPI_DIRECTION_2LINES; + Hspi1.Init.DataSize = SPI_DATASIZE_16BIT; + Hspi1.Init.CLKPolarity = SPI_POLARITY_LOW; + Hspi1.Init.CLKPhase = SPI_PHASE_1EDGE; + Hspi1.Init.NSS = SPI_NSS_SOFT; + Hspi1.Init.BaudRatePrescaler = SPI_BAUDRATEPRESCALER_4; //42MHz APB2 / 4 = 10.5Mbaud + Hspi1.Init.FirstBit = SPI_FIRSTBIT_MSB; + Hspi1.Init.TIMode = SPI_TIMODE_DISABLED; + Hspi1.Init.CRCCalculation = SPI_CRCCALCULATION_ENABLED; + Hspi1.Init.CRCPolynomial = SPI_CRC_DEFAULTPOLYNOMIAL; + HAL_SPI_Init(&Hspi1); + + if (DOWNSTREAM_TX_OK_ACTIVE) + { + TxOkInterruptReceived = 1; + } +} + + + +//Used by USB interface classes, and by our internal RX code. +HAL_StatusTypeDef Upstream_GetFreePacket(FreePacketCallbackTypeDef callback) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return HAL_ERROR; + } + + //Do we already have a queued callback? + if (PendingFreePacketCallback != NULL) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + + //Check if there is a free buffer now + if (UpstreamPacket0.Busy == NOT_BUSY) + { + UpstreamPacket0.Busy = BUSY; + callback(&UpstreamPacket0); + return HAL_OK; + } + if (UpstreamPacket1.Busy == NOT_BUSY) + { + UpstreamPacket1.Busy = BUSY; + callback(&UpstreamPacket1); + return HAL_OK; + } + + //Otherwise save requested address for when a buffer becomes free in the future + PendingFreePacketCallback = callback; + return HAL_OK; +} + + +UpstreamPacketTypeDef* Upstream_GetFreePacketImmediately(void) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return NULL; + } + + //We are expecting a free buffer now + if (UpstreamPacket0.Busy == NOT_BUSY) + { + UpstreamPacket0.Busy = BUSY; + return &UpstreamPacket0; + } + if (UpstreamPacket1.Busy == NOT_BUSY) + { + UpstreamPacket1.Busy = BUSY; + return &UpstreamPacket1; + } + + //Should not happen: + UPSTREAM_SPI_FREAKOUT; + return NULL; +} + + +//Used by USB interface classes, and by our internal RX code. +void Upstream_ReleasePacket(UpstreamPacketTypeDef* packetToRelease) +{ + FreePacketCallbackTypeDef tempCallback; + + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return; + } + + if ((packetToRelease != &UpstreamPacket0) && + (packetToRelease != &UpstreamPacket1)) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + + if (packetToRelease->Busy != BUSY) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + + if (PendingFreePacketCallback != NULL) + { + tempCallback = PendingFreePacketCallback; //In extreme situations, running this callback can trigger another request for a free packet, + PendingFreePacketCallback = NULL; //thereby causing GetFreePacket to freak out. So we need to clear the callback indicator first. + tempCallback(packetToRelease); + } + else + { + packetToRelease->Busy = NOT_BUSY; + } +} + + +//Used by USB interface classes only. +//OK to call when still transmitting another packet. +//Not OK to call when receiving or waiting for downstream reply, +//as we can't let the size/packet sequence get out of sync. +HAL_StatusTypeDef Upstream_TransmitPacket(UpstreamPacketTypeDef* packetToWrite) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return HAL_ERROR; + } + + //Sanity checks + if ((packetToWrite != &UpstreamPacket0) && + (packetToWrite != &UpstreamPacket1)) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + if ((packetToWrite->Busy != BUSY) || + (packetToWrite->Length16 < UPSTREAM_PACKET_LEN_MIN_16) || + (packetToWrite->Length16 > UPSTREAM_PACKET_LEN_16)) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + if (NextTxPacket != NULL) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + + switch (UpstreamInterfaceState) + { + case UPSTREAM_INTERFACE_TX_SIZE_WAIT: + case UPSTREAM_INTERFACE_TX_SIZE: + case UPSTREAM_INTERFACE_TX_PACKET_WAIT: + case UPSTREAM_INTERFACE_TX_PACKET: + NextTxPacket = packetToWrite; + break; + + case UPSTREAM_INTERFACE_IDLE: + UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT; + CurrentWorkingPacket = packetToWrite; + SentCommandClass = CurrentWorkingPacket->CommandClass; + SentCommand = CurrentWorkingPacket->Command; + + //Downstream may have set TxOk pin before we wanted to transmit. + //In this case we can go ahead and transmit now. + if (TxOkInterruptReceived) + { + TxOkInterruptReceived = 0; + Upstream_BeginTransmitPacketSize(); + } + break; + + default: + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + return HAL_OK; +} + + + +//Called at the end of the SPI TxRx transfer, +//at SPI1 interrupt priority. Assume *hspi points to our hspi1. +//We TxRx our outgoing packet because the SPI hardware freaks out if we only Tx it :-/ +void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi) +{ + UNUSED(hspi); + SpiPacketReceivedCallbackTypeDef tempPacketCallback; + UpstreamPacketTypeDef* tempPacketToFree; + + SPI1_NSS_DEASSERT; + + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return; + } + + //Finished transmitting packet size + if (UpstreamInterfaceState == UPSTREAM_INTERFACE_TX_SIZE) + { + UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET_WAIT; + if (TxOkInterruptReceived) + { + TxOkInterruptReceived = 0; + Upstream_BeginTransmitPacketBody(); + } + return; + } + + //Finished transmitting packet body + if (UpstreamInterfaceState == UPSTREAM_INTERFACE_TX_PACKET) + { + if ((PendingFreePacketCallback != NULL) && (NextTxPacket == NULL)) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + + tempPacketToFree = CurrentWorkingPacket; + + if (NextTxPacket != NULL) + { + //NextTxPacket has already passed the checks in Upstream_TransmitPacket. + //So we just need to pass it to HAL_SPI_Transmit_DMA. + UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE_WAIT; + CurrentWorkingPacket = NextTxPacket; + NextTxPacket = NULL; + SentCommandClass = CurrentWorkingPacket->CommandClass; + SentCommand = CurrentWorkingPacket->Command; + if (TxOkInterruptReceived) + { + TxOkInterruptReceived = 0; + Upstream_BeginTransmitPacketSize(); + } + } + else + { + //No packet queued for transmission: + UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; + if (ReceivePacketCallback != NULL) + { + Upstream_CheckBeginPacketReception(); + } + } + + //Release old packet after moving Next to Current + Upstream_ReleasePacket(tempPacketToFree); + return; + } + + + + if (UpstreamInterfaceState == UPSTREAM_INTERFACE_RX_SIZE) + { + if ((CurrentWorkingPacket->Length16 < UPSTREAM_PACKET_LEN_MIN_16) || + (CurrentWorkingPacket->Length16 > UPSTREAM_PACKET_LEN_16)) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET_WAIT; + if (TxOkInterruptReceived) + { + TxOkInterruptReceived = 0; + Upstream_BeginReceivePacketBody(); + } + return; + } + + if (UpstreamInterfaceState == UPSTREAM_INTERFACE_RX_PACKET) + { + UpstreamInterfaceState = UPSTREAM_INTERFACE_IDLE; + if (ReceivePacketCallback == NULL) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + + if ((CurrentWorkingPacket->CommandClass == COMMAND_CLASS_ERROR) && + (CurrentWorkingPacket->Command == COMMAND_ERROR_DEVICE_DISCONNECTED)) + { + Upstream_ReleasePacket(CurrentWorkingPacket); + ReceivePacketCallback = NULL; + Upstream_StateMachine_DeviceDisconnected(); + return; + } + + if (((CurrentWorkingPacket->CommandClass & COMMAND_CLASS_MASK) != (SentCommandClass & COMMAND_CLASS_MASK)) || + (CurrentWorkingPacket->Command != SentCommand)) + { + UPSTREAM_SPI_FREAKOUT; + Upstream_ReleasePacket(CurrentWorkingPacket); + CurrentWorkingPacket = NULL; //Call back with a NULL packet to indicate error + } + + //USB interface may want to receive another packet immediately, + //so clear ReceivePacketCallback before the call. + //It is the callback's responsibility to release the packet buffer we are passing to it! + tempPacketCallback = ReceivePacketCallback; + ReceivePacketCallback = NULL; + tempPacketCallback(CurrentWorkingPacket); + return; + } + + + //case default: + UPSTREAM_SPI_FREAKOUT; +} + + +//Used by USB interface classes. +//Ok to call when idle or transmitting. +//Not OK to call when receiving or waiting for downstream reply. +HAL_StatusTypeDef Upstream_ReceivePacket(SpiPacketReceivedCallbackTypeDef callback) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return HAL_ERROR; + } + + if (ReceivePacketCallback != NULL) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + + ReceivePacketCallback = callback; + return Upstream_CheckBeginPacketReception(); +} + + +//Internal use only. +HAL_StatusTypeDef Upstream_CheckBeginPacketReception(void) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return HAL_ERROR; + } + + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_RX_SIZE_WAIT) + { + UPSTREAM_SPI_FREAKOUT; + return HAL_ERROR; + } + + if (UpstreamInterfaceState == UPSTREAM_INTERFACE_IDLE) + { + UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE_WAIT; + if (TxOkInterruptReceived) + { + TxOkInterruptReceived = 0; + Upstream_GetFreePacket(Upstream_BeginReceivePacketSize); + } + } + return HAL_OK; +} + + +//This is called by EXTI3 falling edge interrupt, +//indicating that downstream is ready for the next transaction. +void Upstream_TxOkInterrupt(void) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return; + } + + switch (UpstreamInterfaceState) + { + case UPSTREAM_INTERFACE_IDLE: + TxOkInterruptReceived = 1; + break; + + case UPSTREAM_INTERFACE_TX_SIZE_WAIT: + Upstream_BeginTransmitPacketSize(); + break; + + case UPSTREAM_INTERFACE_TX_PACKET_WAIT: + Upstream_BeginTransmitPacketBody(); + break; + + case UPSTREAM_INTERFACE_RX_SIZE_WAIT: + Upstream_GetFreePacket(Upstream_BeginReceivePacketSize); + break; + + case UPSTREAM_INTERFACE_RX_PACKET_WAIT: + Upstream_BeginReceivePacketBody(); + break; + + default: + UPSTREAM_SPI_FREAKOUT; + } +} + + +void Upstream_BeginTransmitPacketSize(void) +{ + UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_SIZE; + SPI1_NSS_ASSERT; + if (HAL_SPI_TransmitReceive(&Hspi1, + (uint8_t*)&CurrentWorkingPacket->Length16, + (uint8_t*)&TemporaryIncomingPacketLength, + 2, //We only need to write one word, but the peripheral library freaks out... + SPI_TIMEOUT_VALUE) != HAL_OK) + { + UPSTREAM_SPI_FREAKOUT; + } + + HAL_SPI_TxRxCpltCallback(&Hspi1); +} + + +void Upstream_BeginTransmitPacketBody(void) +{ + UpstreamInterfaceState = UPSTREAM_INTERFACE_TX_PACKET; + SPI1_NSS_ASSERT; + + if (HAL_SPI_TransmitReceive_DMA(&Hspi1, + &CurrentWorkingPacket->CommandClass, + &CurrentWorkingPacket->CommandClass, + ((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK) + { + UPSTREAM_SPI_FREAKOUT; + } +} + + +//Internal use only. +//Called when we want to receive downstream packet, and a packet buffer has become free. +void Upstream_BeginReceivePacketSize(UpstreamPacketTypeDef* freePacket) +{ + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return; + } + + if (UpstreamInterfaceState != UPSTREAM_INTERFACE_RX_SIZE_WAIT) + { + UPSTREAM_SPI_FREAKOUT; + return; + } + UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_SIZE; + CurrentWorkingPacket = freePacket; + CurrentWorkingPacket->Length16 = 0; //Our RX buffer is used by HAL_SPI_TransmitReceive_DMA as dummy TX data, we set Length to 0 so downstream will know this is a dummy packet. + SPI1_NSS_ASSERT; + if (HAL_SPI_TransmitReceive(&Hspi1, + (uint8_t*)&CurrentWorkingPacket->Length16, + (uint8_t*)&CurrentWorkingPacket->Length16, + 2, //We only need to write one word, but the peripheral library freaks out... + SPI_TIMEOUT_VALUE) != HAL_OK) + { + UPSTREAM_SPI_FREAKOUT; + } + + HAL_SPI_TxRxCpltCallback(&Hspi1); +} + + +void Upstream_BeginReceivePacketBody(void) +{ + UpstreamInterfaceState = UPSTREAM_INTERFACE_RX_PACKET; + SPI1_NSS_ASSERT; + if (HAL_SPI_TransmitReceive_DMA(&Hspi1, + &CurrentWorkingPacket->CommandClass, + &CurrentWorkingPacket->CommandClass, + ((CurrentWorkingPacket->Length16 < 2) ? 2 : CurrentWorkingPacket->Length16)) != HAL_OK) + { + UPSTREAM_SPI_FREAKOUT; + } +} + + +//Something bad happened! Possibly CRC error... +void HAL_SPI_ErrorCallback(SPI_HandleTypeDef *hspi) +{ + UNUSED(hspi); + SpiPacketReceivedCallbackTypeDef tempPacketCallback; + + if (UpstreamInterfaceState >= UPSTREAM_INTERFACE_ERROR) + { + return; + } + + UPSTREAM_SPI_FREAKOUT; + + if (ReceivePacketCallback != NULL) + { + tempPacketCallback = ReceivePacketCallback; + ReceivePacketCallback = NULL; + tempPacketCallback(NULL); //Call back with a NULL packet to indicate error + } +} + + + diff --git a/Upstream/Src/upstream_statemachine.c b/Upstream/Src/upstream_statemachine.c new file mode 100644 index 0000000..650fffd --- /dev/null +++ b/Upstream/Src/upstream_statemachine.c @@ -0,0 +1,347 @@ +/* + * upstream_statemachine.c + * + * Created on: 20/08/2015 + * Author: Robert Fisk + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + + +#include "upstream_statemachine.h" +#include "upstream_spi.h" +#include "upstream_hid.h" +#include "usb_device.h" +#include "usbd_core.h" +#include "usbd_msc.h" +#include "usbd_hid.h" +#include "build_config.h" + +#define POLL_DEVICE_CONNECTED_TIMEOUT_MS 1000 + + +UpstreamStateTypeDef UpstreamState = STATE_TEST_INTERFACE; +InterfaceCommandClassTypeDef ConfiguredDeviceClass = COMMAND_CLASS_INTERFACE; +uint32_t PollDeviceConnectedTimer; + + +void Upstream_StateMachine_TestInterfaceReplyCallback(UpstreamPacketTypeDef* replyPacket); +void Upstream_StateMachine_NotifyDevice(UpstreamPacketTypeDef* freePacket); +void Upstream_StateMachine_NotifyDeviceReplyCallback(UpstreamPacketTypeDef* replyPacket); +void Upstream_StateMachine_PollDeviceConnectedReplyCallback(UpstreamPacketTypeDef* replyPacket); + + + +void Upstream_InitStateMachine(void) +{ + UpstreamPacketTypeDef* freePacket; + uint16_t i; + uint8_t testDataValue; + + Upstream_InitSPI(); + + //Prepare SPI test packet + freePacket = Upstream_GetFreePacketImmediately(); + if (freePacket == NULL) + { + UpstreamState = STATE_ERROR; + return; + } + + freePacket->Length16 = UPSTREAM_PACKET_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_INTERFACE; + freePacket->Command = COMMAND_INTERFACE_ECHO; + + //Fill our test packet with some junk + testDataValue = 0xFF; + for (i = 0; i < MSC_MEDIA_PACKET; i++) + { + freePacket->Data[i] = testDataValue; + testDataValue += 39; + } + + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + Upstream_ReceivePacket(Upstream_StateMachine_TestInterfaceReplyCallback); + } +} + + +//Used by upstream_spi freakout macro, indicates we should stop everything. +void Upstream_StateMachine_SetErrorState(void) +{ + UpstreamState = STATE_ERROR; + if ((ConfiguredDeviceClass > COMMAND_CLASS_INTERFACE) && + (ConfiguredDeviceClass < COMMAND_CLASS_ERROR)) + { + USBD_DeInit(&hUsbDeviceFS); + } +} + + +InterfaceCommandClassTypeDef Upstream_StateMachine_CheckActiveClass(void) +{ + if (UpstreamState == STATE_ERROR) + { + return COMMAND_CLASS_ERROR; + } + + if ((UpstreamState == STATE_DEVICE_ACTIVE) || + (UpstreamState == STATE_SUSPENDED)) + { + return ConfiguredDeviceClass; + } + + //else: + UPSTREAM_STATEMACHINE_FREAKOUT; + return COMMAND_CLASS_INTERFACE; +} + + +uint32_t Upstream_StateMachine_GetSuspendState(void) +{ + if (UpstreamState == STATE_SUSPENDED) + { + return 1; + } + return 0; +} + + +void Upstream_StateMachine_TestInterfaceReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + uint16_t i; + uint8_t testDataValue; + + if (UpstreamState >= STATE_ERROR) + { + return; + } + + if ((UpstreamState != STATE_TEST_INTERFACE) || + (replyPacket == NULL)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + if (replyPacket->Length16 != UPSTREAM_PACKET_LEN_16) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + testDataValue = 0xFF; + for (i = 0; i < MSC_MEDIA_PACKET; i++) + { + if (replyPacket->Data[i] != testDataValue) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + testDataValue += 39; + } + + //SPI interface passed checks. Now we wait for a device to be attached to downstream. + Upstream_StateMachine_NotifyDevice(replyPacket); +} + + +void Upstream_StateMachine_NotifyDevice(UpstreamPacketTypeDef* freePacket) +{ + UpstreamState = STATE_WAIT_DEVICE; + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_INTERFACE; + freePacket->Command = COMMAND_INTERFACE_NOTIFY_DEVICE; + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + Upstream_ReceivePacket(Upstream_StateMachine_NotifyDeviceReplyCallback); + } +} + + +void Upstream_StateMachine_NotifyDeviceReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + InterfaceCommandClassTypeDef newActiveClass = COMMAND_CLASS_INTERFACE; + USBD_ClassTypeDef* newClassPointer; + + if (UpstreamState >= STATE_ERROR) + { + return; + } + + if ((UpstreamState != STATE_WAIT_DEVICE) || + (replyPacket == NULL)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + if (replyPacket->Length16 != (UPSTREAM_PACKET_HEADER_LEN_16 + 1)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + switch (replyPacket->Data[0]) + { +#ifdef CONFIG_MASS_STORAGE_ENABLED + case COMMAND_CLASS_MASS_STORAGE: + newActiveClass = COMMAND_CLASS_MASS_STORAGE; + newClassPointer = &USBD_MSC; + break; +#endif +#ifdef CONFIG_MOUSE_ENABLED + case COMMAND_CLASS_HID_MOUSE: + newActiveClass = COMMAND_CLASS_HID_MOUSE; + newClassPointer = &USBD_HID; + USBD_HID_PreinitMouse(); + break; +#endif +#ifdef CONFIG_KEYBOARD_ENABLED + case COMMAND_CLASS_HID_KEYBOARD: + newActiveClass = COMMAND_CLASS_HID_KEYBOARD; + newClassPointer = &USBD_HID; + USBD_HID_PreinitKeyboard(); + break; +#endif + //Add other supported classes here... + } + + Upstream_ReleasePacket(replyPacket); + + if (newActiveClass == COMMAND_CLASS_INTERFACE) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + //Downstream should never change the active device class without rebooting! + if ((ConfiguredDeviceClass != COMMAND_CLASS_INTERFACE) && + (ConfiguredDeviceClass != newActiveClass)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + UpstreamState = STATE_DEVICE_ACTIVE; + ConfiguredDeviceClass = newActiveClass; + USB_Device_Init(); + USBD_RegisterClass(&hUsbDeviceFS, newClassPointer); + USBD_Start(&hUsbDeviceFS); + + //The USB device stack will now receive commands from our host. + //All we need to do is monitor for downstream device disconnection. +} + + +void Upstream_StateMachine_DeviceDisconnected(void) +{ + if ((ConfiguredDeviceClass == COMMAND_CLASS_INTERFACE) || + (ConfiguredDeviceClass >= COMMAND_CLASS_ERROR)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + USBD_DeInit(&hUsbDeviceFS); + Upstream_GetFreePacket(Upstream_StateMachine_NotifyDevice); +} + + +//Suspend event activated by our host +void Upstream_StateMachine_Suspend(void) +{ + if (UpstreamState >= STATE_SUSPENDED) //Ignore when already suspended, or in error state + { + return; + } + if (UpstreamState != STATE_DEVICE_ACTIVE) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + PollDeviceConnectedTimer = 0; + UpstreamState = STATE_SUSPENDED; +} + + +//Resume event activated by our host +void Upstream_StateMachine_CheckResume(void) +{ + if (UpstreamState == STATE_SUSPENDED) + { + UpstreamState = STATE_DEVICE_ACTIVE; + } +} + + +//Activated by downstream report, causing us to wake up the host +void Upstream_StateMachine_Wakeup(void) +{ + USBD_ClassTypeDef* activeClass; + + if (UpstreamState != STATE_SUSPENDED) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + //This is how I'd wakeup the host, IF IT ACTUALLY WORKED! + //USBD_LL_WakeupHost(&hUsbDeviceFS); + + //This is really ugly! But wakeup seems to be broken on the STM32, so we do it the hard way. + activeClass = USBD_DeInit(&hUsbDeviceFS); + USB_Device_Init(); + USBD_RegisterClass(&hUsbDeviceFS, activeClass); + USBD_Start(&hUsbDeviceFS); +} + + +//Called by Systick_Handler every 1ms, at high interrupt priority. +void Upstream_StateMachine_PollDeviceConnected(void) +{ + UpstreamPacketTypeDef* freePacket; + + if ((UpstreamState != STATE_SUSPENDED) || + (ConfiguredDeviceClass != COMMAND_CLASS_MASS_STORAGE)) + { + return; + } + + if (++PollDeviceConnectedTimer >= POLL_DEVICE_CONNECTED_TIMEOUT_MS) + { + PollDeviceConnectedTimer = 0; + freePacket = Upstream_GetFreePacketImmediately(); + if (freePacket == NULL) + { + UpstreamState = STATE_ERROR; + return; + } + + freePacket->Length16 = UPSTREAM_PACKET_HEADER_LEN_16; + freePacket->CommandClass = COMMAND_CLASS_MASS_STORAGE; + freePacket->Command = COMMAND_MSC_POLL_DISCONNECT; + if (Upstream_TransmitPacket(freePacket) == HAL_OK) + { + Upstream_ReceivePacket(Upstream_StateMachine_PollDeviceConnectedReplyCallback); + } + } +} + + +void Upstream_StateMachine_PollDeviceConnectedReplyCallback(UpstreamPacketTypeDef* replyPacket) +{ + if ((UpstreamState != STATE_SUSPENDED) || + (replyPacket == NULL)) + { + UPSTREAM_STATEMACHINE_FREAKOUT; + return; + } + + //Downstream device is still connected, so nothing to do here + Upstream_ReleasePacket(replyPacket); +} diff --git a/Upstream/Src/usb_device.c b/Upstream/Src/usb_device.c new file mode 100644 index 0000000..ce7501c --- /dev/null +++ b/Upstream/Src/usb_device.c @@ -0,0 +1,61 @@ +/** + ****************************************************************************** + * @file : USB_DEVICE + * @date : 03/02/2015 20:26:59 + * @version : v1.0_Cube + * @brief : This file implements the USB Device + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + + +#include "usbd_descriptors.h" +#include "usb_device.h" +#include "usbd_core.h" +#include "usbd_msc.h" + +/* USB Device Core handle declaration */ +USBD_HandleTypeDef hUsbDeviceFS; + + +void USB_Device_Init(void) +{ + USBD_Init(&hUsbDeviceFS, &FS_Desc, DEVICE_FS); + +// USBD_RegisterClass(&hUsbDeviceFS, &USBD_MSC); +// USBD_Start(&hUsbDeviceFS); + +} + + + + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Upstream/Src/usbd_config.c b/Upstream/Src/usbd_config.c new file mode 100644 index 0000000..0c322b6 --- /dev/null +++ b/Upstream/Src/usbd_config.c @@ -0,0 +1,517 @@ +/** + ****************************************************************************** + * @file : usbd_conf.c + * @date : 03/02/2015 20:26:59 + * @version : v1.0_Cube + * @brief : This file implements the board support package for the USB device library + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f4xx.h" +#include "stm32f4xx_hal.h" +#include "usbd_def.h" +#include "usbd_core.h" +#include "interrupts.h" + +PCD_HandleTypeDef hpcd_USB_OTG_FS; + +/* USER CODE BEGIN 0 */ +/* USER CODE END 0 */ + +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ +/* USER CODE BEGIN 1 */ +/* USER CODE END 1 */ + +/******************************************************************************* + LL Driver Callbacks (PCD -> USB Device Library) +*******************************************************************************/ +/* MSP Init */ + +void HAL_PCD_MspInit(PCD_HandleTypeDef* hpcd) +{ + GPIO_InitTypeDef GPIO_InitStruct; + if(hpcd->Instance==USB_OTG_FS) + { + /* USER CODE BEGIN USB_OTG_FS_MspInit 0 */ + + /* USER CODE END USB_OTG_FS_MspInit 0 */ + /* Peripheral clock enable */ + __USB_OTG_FS_CLK_ENABLE(); + + /**USB_OTG_FS GPIO Configuration + PA11 ------> USB_OTG_FS_DM + PA12 ------> USB_OTG_FS_DP + */ + GPIO_InitStruct.Pin = GPIO_PIN_11|GPIO_PIN_12; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_LOW; + GPIO_InitStruct.Alternate = GPIO_AF10_OTG_FS; + HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + + HAL_NVIC_SetPriority(OTG_FS_IRQn, INT_PRIORITY_USB, 0); + HAL_NVIC_EnableIRQ(OTG_FS_IRQn); + /* USER CODE BEGIN USB_OTG_FS_MspInit 1 */ + + /* USER CODE END USB_OTG_FS_MspInit 1 */ + } +} + +void HAL_PCD_MspDeInit(PCD_HandleTypeDef* hpcd) +{ + if(hpcd->Instance==USB_OTG_FS) + { + /* USER CODE BEGIN USB_OTG_FS_MspDeInit 0 */ + + /* USER CODE END USB_OTG_FS_MspDeInit 0 */ + /* Peripheral clock disable */ + __USB_OTG_FS_CLK_DISABLE(); + + /**USB_OTG_FS GPIO Configuration + PA11 ------> USB_OTG_FS_DM + PA12 ------> USB_OTG_FS_DP + */ + HAL_GPIO_DeInit(GPIOA, GPIO_PIN_11|GPIO_PIN_12); + + /* Peripheral interrupt Deinit*/ + HAL_NVIC_DisableIRQ(OTG_FS_IRQn); + + /* USER CODE BEGIN USB_OTG_FS_MspDeInit 1 */ + + /* USER CODE END USB_OTG_FS_MspDeInit 1 */ + } +} + +/** + * @brief Setup stage callback + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_SetupStageCallback(PCD_HandleTypeDef *hpcd) +{ + USBD_SetupStage(hpcd->pData, (uint8_t *)hpcd->Setup); +} + +/** + * @brief Data Out stage callback. + * @param hpcd: PCD handle + * @param epnum: Endpoint Number + * @retval None + */ +void HAL_PCD_DataOutStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +{ + USBD_DataOutStage(hpcd->pData, epnum, hpcd->OUT_ep[epnum].xfer_buff); +} + +/** + * @brief Data In stage callback.. + * @param hpcd: PCD handle + * @param epnum: Endpoint Number + * @retval None + */ +void HAL_PCD_DataInStageCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +{ + USBD_DataInStage(hpcd->pData, epnum, hpcd->IN_ep[epnum].xfer_buff); +} + +/** + * @brief SOF callback. + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_SOFCallback(PCD_HandleTypeDef *hpcd) +{ + USBD_SOF(hpcd->pData); +} + +/** + * @brief Reset callback. + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_ResetCallback(PCD_HandleTypeDef *hpcd) +{ + USBD_SpeedTypeDef speed = USBD_SPEED_FULL; + + /*Set USB Current Speed*/ + switch (hpcd->Init.speed) + { + case PCD_SPEED_HIGH: + speed = USBD_SPEED_HIGH; + break; +// case PCD_SPEED_FULL: +// speed = USBD_SPEED_FULL; +// break; +// +// default: +// speed = USBD_SPEED_FULL; +// break; + } + USBD_SetSpeed(hpcd->pData, speed); + + /*Reset Device*/ + USBD_Reset(hpcd->pData); +} + +/** + * @brief Suspend callback. + * When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it) + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_SuspendCallback(PCD_HandleTypeDef *hpcd) +{ + /* Inform USB library that core enters in suspend Mode */ + USBD_Suspend(hpcd->pData); + __HAL_PCD_GATE_PHYCLOCK(hpcd); + /*Enter in STOP mode */ + /* USER CODE BEGIN 2 */ + if (hpcd->Init.low_power_enable) + { + /* Set SLEEPDEEP bit and SleepOnExit of Cortex System Control Register */ + //SCB->SCR |= (uint32_t)((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk)); + } + /* USER CODE END 2 */ +} + +/** + * @brief Resume callback. + When Low power mode is enabled the debug cannot be used (IAR, Keil doesn't support it) + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_ResumeCallback(PCD_HandleTypeDef *hpcd) +{ + /* USER CODE BEGIN 3 */ + if (hpcd->Init.low_power_enable) + { + /* Reset SLEEPDEEP bit of Cortex System Control Register */ + //SCB->SCR &= (uint32_t)~((uint32_t)(SCB_SCR_SLEEPDEEP_Msk | SCB_SCR_SLEEPONEXIT_Msk)); + } + /* USER CODE END 3 */ + __HAL_PCD_UNGATE_PHYCLOCK(hpcd); + USBD_Resume(hpcd->pData); +} + +/** + * @brief ISOC Out Incomplete callback. + * @param hpcd: PCD handle + * @param epnum: Endpoint Number + * @retval None + */ +void HAL_PCD_ISOOUTIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +{ + USBD_IsoOUTIncomplete(hpcd->pData, epnum); +} + +/** + * @brief ISOC In Incomplete callback. + * @param hpcd: PCD handle + * @param epnum: Endpoint Number + * @retval None + */ +void HAL_PCD_ISOINIncompleteCallback(PCD_HandleTypeDef *hpcd, uint8_t epnum) +{ + USBD_IsoINIncomplete(hpcd->pData, epnum); +} + +/** + * @brief Connect callback. + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_ConnectCallback(PCD_HandleTypeDef *hpcd) +{ + USBD_DevConnected(hpcd->pData); +} + +/** + * @brief Disconnect callback. + * @param hpcd: PCD handle + * @retval None + */ +void HAL_PCD_DisconnectCallback(PCD_HandleTypeDef *hpcd) +{ + USBD_DevDisconnected(hpcd->pData); +} + +void HAL_PCD_BufferFreedCallBack(PCD_HandleTypeDef *hpcd) +{ + USBD_BufferFreed(hpcd->pData); +} + +/******************************************************************************* + LL Driver Interface (USB Device Library --> PCD) +*******************************************************************************/ +/** + * @brief Initializes the Low Level portion of the Device driver. + * @param pdev: Device handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_Init (USBD_HandleTypeDef *pdev) +{ + /* Init USB_IP */ + if (pdev->id == DEVICE_FS) { + /* Link The driver to the stack */ + hpcd_USB_OTG_FS.pData = pdev; + pdev->pData = &hpcd_USB_OTG_FS; + + hpcd_USB_OTG_FS.Instance = USB_OTG_FS; + hpcd_USB_OTG_FS.Init.dev_endpoints = 7; + hpcd_USB_OTG_FS.Init.speed = PCD_SPEED_FULL; + hpcd_USB_OTG_FS.Init.dma_enable = DISABLE; + hpcd_USB_OTG_FS.Init.ep0_mps = DEP0CTL_MPS_64; + hpcd_USB_OTG_FS.Init.phy_itface = PCD_PHY_EMBEDDED; + hpcd_USB_OTG_FS.Init.Sof_enable = ENABLE; + hpcd_USB_OTG_FS.Init.low_power_enable = DISABLE; + hpcd_USB_OTG_FS.Init.vbus_sensing_enable = DISABLE; + hpcd_USB_OTG_FS.Init.use_dedicated_ep1 = DISABLE; + hpcd_USB_OTG_FS.Init.use_external_vbus = DISABLE; + HAL_PCD_Init(&hpcd_USB_OTG_FS); + + HAL_PCD_SetRxFiFo(&hpcd_USB_OTG_FS, 0x80); + HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 0, 0x40); + HAL_PCD_SetTxFiFo(&hpcd_USB_OTG_FS, 1, 0x80); + } + return USBD_OK; +} + +/** + * @brief De-Initializes the Low Level portion of the Device driver. + * @param pdev: Device handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_DeInit (USBD_HandleTypeDef *pdev) +{ + HAL_PCD_DeInit(pdev->pData); + return USBD_OK; +} + +/** + * @brief Starts the Low Level portion of the Device driver. + * @param pdev: Device handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_Start(USBD_HandleTypeDef *pdev) +{ + HAL_PCD_Start(pdev->pData); + return USBD_OK; +} + +/** + * @brief Stops the Low Level portion of the Device driver. + * @param pdev: Device handle + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_Stop (USBD_HandleTypeDef *pdev) +{ + HAL_PCD_Stop(pdev->pData); + return USBD_OK; +} + +/** + * @brief Opens an endpoint of the Low Level Driver. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @param ep_type: Endpoint Type + * @param ep_mps: Endpoint Max Packet Size + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_OpenEP (USBD_HandleTypeDef *pdev, + uint8_t ep_addr, + uint8_t ep_type, + uint16_t ep_mps) +{ + + HAL_PCD_EP_Open(pdev->pData, + ep_addr, + ep_mps, + ep_type); + + return USBD_OK; +} + +/** + * @brief Closes an endpoint of the Low Level Driver. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_CloseEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + + HAL_PCD_EP_Close(pdev->pData, ep_addr); + return USBD_OK; +} + +/** + * @brief Flushes an endpoint of the Low Level Driver. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_FlushEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + + HAL_PCD_EP_Flush(pdev->pData, ep_addr); + return USBD_OK; +} + +/** + * @brief Sets a Stall condition on an endpoint of the Low Level Driver. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_StallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + + HAL_PCD_EP_SetStall(pdev->pData, ep_addr); + return USBD_OK; +} + +/** + * @brief Clears a Stall condition on an endpoint of the Low Level Driver. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_ClearStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + + HAL_PCD_EP_ClrStall(pdev->pData, ep_addr); + return USBD_OK; +} + +/** + * @brief Returns Stall condition. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval Stall (1: Yes, 0: No) + */ +uint8_t USBD_LL_IsStallEP (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + PCD_HandleTypeDef *hpcd = pdev->pData; + + if((ep_addr & 0x80) == 0x80) + { + return hpcd->IN_ep[ep_addr & 0x7F].is_stall; + } + else + { + return hpcd->OUT_ep[ep_addr & 0x7F].is_stall; + } +} +/** + * @brief Assigns a USB address to the device. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_SetUSBAddress (USBD_HandleTypeDef *pdev, uint8_t dev_addr) +{ + + HAL_PCD_SetAddress(pdev->pData, dev_addr); + return USBD_OK; +} + +/** + * @brief Transmits data over an endpoint. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @param pbuf: Pointer to data to be sent + * @param size: Data size + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_Transmit (USBD_HandleTypeDef *pdev, + uint8_t ep_addr, + uint8_t *pbuf, + uint16_t size) +{ + + HAL_PCD_EP_Transmit(pdev->pData, ep_addr, pbuf, size); + return USBD_OK; +} + +/** + * @brief Prepares an endpoint for reception. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @param pbuf: Pointer to data to be received + * @param size: Data size + * @retval USBD Status + */ +USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, + uint8_t ep_addr, + uint8_t *pbuf, + uint16_t size) +{ + + HAL_PCD_EP_Receive(pdev->pData, ep_addr, pbuf, size); + return USBD_OK; +} + +/** + * @brief Returns the last transfered packet size. + * @param pdev: Device handle + * @param ep_addr: Endpoint Number + * @retval Recived Data Size + */ +uint32_t USBD_LL_GetRxDataSize (USBD_HandleTypeDef *pdev, uint8_t ep_addr) +{ + return HAL_PCD_EP_GetRxCount(pdev->pData, ep_addr); +} + +/** + * @brief Delays routine for the USB Device Library. + * @param Delay: Delay in ms + * @retval None + */ +void USBD_LL_Delay (uint32_t Delay) +{ + HAL_Delay(Delay); +} + + +void USBD_LL_WakeupHost(USBD_HandleTypeDef *pdev) +{ + if (pdev->dev_remote_wakeup == 1) + { + HAL_PCD_ActivateRemoteWakeup(pdev->pData); + HAL_Delay(10); + HAL_PCD_DeActivateRemoteWakeup(pdev->pData); + } +} + + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Upstream/Src/usbd_descriptors.c b/Upstream/Src/usbd_descriptors.c new file mode 100644 index 0000000..fd298df --- /dev/null +++ b/Upstream/Src/usbd_descriptors.c @@ -0,0 +1,288 @@ +/** + ****************************************************************************** + * @file : usbd_desc.c + * @date : 03/02/2015 20:26:59 + * @version : v1.0_Cube + * @brief : This file implements the USB Device descriptors + ****************************************************************************** + * + * COPYRIGHT(c) 2015 STMicroelectronics + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of STMicroelectronics nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************** + * + * Modifications by Robert Fisk + */ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_config.h" +#include "usbd_descriptors.h" +#include "usbd_core.h" +#include "usbd_msc.h" +/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY + * @{ + */ + +/** @defgroup USBD_DESC + * @brief USBD descriptors module + * @{ + */ + +/** @defgroup USBD_DESC_Private_TypesDefinitions + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_DESC_Private_Defines + * @{ + */ +#define USBD_VID 0xF000 //TODO: get a real VID :) We also need a separate PID for each supported device class. +#define USBD_LANGID_STRING 1033 +#define USBD_MANUFACTURER_STRING "The USG is Good, not Bad" +#define USBD_PRODUCT_STRING_FS "USG v1.0" +#define USBD_SERIALNUMBER_STRING_FS "00000000001A" +#define USBD_CONFIGURATION_STRING_FS "USG multipurpose configuration" +#define USBD_INTERFACE_STRING_FS "USG multipurpose interface" + + +/** + * @} + */ + +/** @defgroup USBD_DESC_Private_Macros + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_DESC_Private_Variables + * @{ + */ +uint8_t * USBD_FS_DeviceDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_LangIDStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_ManufacturerStrDescriptor ( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_ProductStrDescriptor ( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_SerialStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_ConfigStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); +uint8_t * USBD_FS_InterfaceStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length); + +#ifdef USB_SUPPORT_USER_STRING_DESC +uint8_t * USBD_FS_USRStringDesc (USBD_SpeedTypeDef speed, uint8_t idx , uint16_t *length); +#endif /* USB_SUPPORT_USER_STRING_DESC */ + +USBD_DescriptorsTypeDef FS_Desc = +{ + USBD_FS_DeviceDescriptor, + USBD_FS_LangIDStrDescriptor, + USBD_FS_ManufacturerStrDescriptor, + USBD_FS_ProductStrDescriptor, + USBD_FS_SerialStrDescriptor, + USBD_FS_ConfigStrDescriptor, + USBD_FS_InterfaceStrDescriptor, +}; + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +/* USB Standard Device Descriptor */ +__ALIGN_BEGIN uint8_t USBD_FS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = + { + 0x12, /*bLength */ + USB_DESC_TYPE_DEVICE, /*bDescriptorType*/ + 0x00, /*bcdUSB */ + 0x02, + 0x00, /*bDeviceClass*/ + 0x00, /*bDeviceSubClass*/ + 0x00, /*bDeviceProtocol*/ + USB_MAX_EP0_SIZE, /*bMaxPacketSize*/ + LOBYTE(USBD_VID), /*idVendor*/ + HIBYTE(USBD_VID), /*idVendor*/ + 0x00, //LOBYTE(USBD_PID_FS), /*idVendor*/ + 0x00, //HIBYTE(USBD_PID_FS), /*idVendor*/ + 0x00, /*bcdDevice rel. 2.00*/ + 0x02, + USBD_IDX_MFC_STR, /*Index of manufacturer string*/ + USBD_IDX_PRODUCT_STR, /*Index of product string*/ + USBD_IDX_SERIAL_STR, /*Index of serial number string*/ + USBD_MAX_NUM_CONFIGURATION /*bNumConfigurations*/ + } ; /* USB_DeviceDescriptor */ + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif + +/* USB Standard Device Descriptor */ +__ALIGN_BEGIN uint8_t USBD_LangIDDesc[USB_LEN_LANGID_STR_DESC] __ALIGN_END = +{ + USB_LEN_LANGID_STR_DESC, + USB_DESC_TYPE_STRING, + LOBYTE(USBD_LANGID_STRING), + HIBYTE(USBD_LANGID_STRING), +}; + +#if defined ( __ICCARM__ ) /*!< IAR Compiler */ + #pragma data_alignment=4 +#endif +__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; + + +/* USB Mass storage Standard Inquiry Data */ +const int8_t STORAGE_Inquirydata_FS[] = { //36 + + /* LUN 0 */ + 0x00, + 0x80, + 0x02, + 0x02, + (STANDARD_INQUIRY_DATA_LEN - 5), + 0x00, + 0x00, + 0x00, + 'T', 'h', 'e', ' ', 'U', 'S', 'G', ' ', /* Manufacturer : 8 bytes */ + 'i', 's', ' ', 'G', 'o', 'o', 'd', ',', /* Product : 16 Bytes */ + ' ', 'n', 'o', 't', ' ', 'b', 'a', 'd', + 'v', '1', '.' ,'0', /* Version : 4 Bytes */ +}; + +/** + * @} + */ + +/** @defgroup USBD_DESC_Private_FunctionPrototypes + * @{ + */ +/** + * @} + */ + +/** @defgroup USBD_DESC_Private_Functions + * @{ + */ + +/** +* @brief USBD_FS_DeviceDescriptor +* return the device descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_DeviceDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + *length = sizeof(USBD_FS_DeviceDesc); + return USBD_FS_DeviceDesc; +} + +/** +* @brief USBD_FS_LangIDStrDescriptor +* return the LangID string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_LangIDStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + *length = sizeof(USBD_LangIDDesc); + return USBD_LangIDDesc; +} + +/** +* @brief USBD_FS_ProductStrDescriptor +* return the product string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_ProductStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + USBD_GetString (USBD_PRODUCT_STRING_FS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** +* @brief USBD_FS_ManufacturerStrDescriptor +* return the manufacturer string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_ManufacturerStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + USBD_GetString (USBD_MANUFACTURER_STRING, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** +* @brief USBD_FS_SerialStrDescriptor +* return the serial number string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_SerialStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + USBD_GetString (USBD_SERIALNUMBER_STRING_FS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** +* @brief USBD_FS_ConfigStrDescriptor +* return the configuration string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_ConfigStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + USBD_GetString (USBD_CONFIGURATION_STRING_FS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** +* @brief USBD_HS_InterfaceStrDescriptor +* return the interface string descriptor +* @param speed : current device speed +* @param length : pointer to data length variable +* @retval pointer to descriptor buffer +*/ +uint8_t * USBD_FS_InterfaceStrDescriptor( USBD_SpeedTypeDef speed , uint16_t *length) +{ + USBD_GetString (USBD_INTERFACE_STRING_FS, USBD_StrDesc, length); + return USBD_StrDesc; +} +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/