diff --git a/.gitignore b/.gitignore index f5e4d11..3ab6672 100644 --- a/.gitignore +++ b/.gitignore @@ -7,11 +7,15 @@ __pycache__ .idea/ venv +.venv Drivers Middlewares +!Core/Inc/Middlewares/** +!Core/Src/Middlewares/** USB_DEVICE startup -STM32F767ZITx_FLASH.ld +*.ld +compile_commands.json python_utils/rem python_utils/*.csv diff --git a/CompilerFlags.py b/CompilerFlags.py index 8bf31cb..c008645 100644 --- a/CompilerFlags.py +++ b/CompilerFlags.py @@ -1,10 +1,31 @@ # This file sets additional flags that cannot be set from platformio.ini itself # these can be for the compiler or for the linker import configparser +import subprocess +import re # load build environment +print("\n==================== CompilerFlags.py ====================") +l = lambda *args, **kwargs : print("[CompilerFlags.py]", *args, **kwargs) + Import("env") +# read settings from platform.ini to also include +conf = configparser.ConfigParser() +conf.read("platformio.ini") +opt = conf.get("env:nucleo_f767zi", "optimization") + +git_branch_name = subprocess.check_output(['git', 'rev-parse', '--abbrev-ref', 'HEAD']).decode('ascii').strip() +git_commit_date = subprocess.check_output(['git', 'show', '-s', '--date=format:%d/%m/%Y', '--format=%cd', 'HEAD']).decode('ascii').strip() +git_commit_hash = subprocess.check_output(['git', 'show', '-s', '--format=%h', 'HEAD']).decode('ascii').strip() +git_commit_msg = subprocess.check_output(['git', 'show', '-s', '--format=%s', 'HEAD']).decode('ascii').strip() + +git_string = f"{git_branch_name} {git_commit_hash} | {git_commit_date} | {git_commit_msg}" +git_string = re.sub('-', '_', git_string) +git_string = re.sub(r'[^a-zA-Z0-9 /_]', '', git_string) +l(git_string) + + # read settings from platform.ini to also include conf = configparser.ConfigParser() conf.read("platformio.ini") @@ -13,6 +34,8 @@ # compiler settings env.Append( CCFLAGS=[ + f"-D__GIT_STRING__={git_string}", + f"-D__GIT_DEVELOPMENT__=\"{[0, 1][git_branch_name == 'development']}\"", opt, # "-std=c++11", "-g3", @@ -47,4 +70,6 @@ "-mfpu=fpv5-sp-d16", "-Wl,-u,_printf_float,-u,_scanf_float" ] -) \ No newline at end of file +) + +print("\n") \ No newline at end of file diff --git a/Core/Inc/FATFS/App/fatfs.h b/Core/Inc/FATFS/App/fatfs.h new file mode 100644 index 0000000..4deef31 --- /dev/null +++ b/Core/Inc/FATFS/App/fatfs.h @@ -0,0 +1,47 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fatfs.h + * @brief Header for fatfs applications + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __fatfs_H +#define __fatfs_H +#ifdef __cplusplus + extern "C" { +#endif + +#include "ff.h" +#include "ff_gen_drv.h" +#include "sd_diskio.h" /* defines SD_Driver as external */ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern uint8_t retSD; /* Return value for SD */ +extern char SDPath[4]; /* SD logical drive path */ +extern FATFS SDFatFS; /* File system object for SD logical drive */ +extern FIL SDFile; /* File object for SD */ + +void MX_FATFS_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ +#ifdef __cplusplus +} +#endif +#endif /*__fatfs_H */ diff --git a/Core/Inc/FATFS/Target/bsp_driver_sd.h b/Core/Inc/FATFS/Target/bsp_driver_sd.h new file mode 100644 index 0000000..79d0353 --- /dev/null +++ b/Core/Inc/FATFS/Target/bsp_driver_sd.h @@ -0,0 +1,89 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file bsp_driver_sd.h (based on stm32756g_eval_sd.h) + * @brief This file contains the common defines and functions prototypes for + * the bsp_driver_sd.c driver. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F7_SD_H +#define __STM32F7_SD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx_hal.h" +#include "fatfs_platform.h" + +/* Exported types --------------------------------------------------------*/ +/** + * @brief SD Card information structure + */ +#define BSP_SD_CardInfo HAL_SD_CardInfoTypeDef + +/* Exported constants --------------------------------------------------------*/ +/** + * @brief SD status structure definition + */ +#define MSD_OK ((uint8_t)0x00) +#define MSD_ERROR ((uint8_t)0x01) +#define MSD_ERROR_SD_NOT_PRESENT ((uint8_t)0x02) + +/** + * @brief SD transfer state definition + */ +#define SD_TRANSFER_OK ((uint8_t)0x00) +#define SD_TRANSFER_BUSY ((uint8_t)0x01) + +#define SD_PRESENT ((uint8_t)0x01) +#define SD_NOT_PRESENT ((uint8_t)0x00) +#define SD_DATATIMEOUT ((uint32_t)100000000) + +#ifdef OLD_API +/* kept to avoid issue when migrating old projects. */ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +#else +/* USER CODE BEGIN BSP_H_CODE */ + +/* Exported functions --------------------------------------------------------*/ +uint8_t BSP_SD_Init(void); +uint8_t BSP_SD_ITConfig(void); +uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); +uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); +uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks); +uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks); +uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); +uint8_t BSP_SD_GetCardState(void); +void BSP_SD_GetCardInfo(BSP_SD_CardInfo *CardInfo); +uint8_t BSP_SD_IsDetected(void); + +/* These functions can be modified in case the current settings (e.g. DMA stream) + need to be changed for specific application needs */ +void BSP_SD_AbortCallback(void); +void BSP_SD_WriteCpltCallback(void); +void BSP_SD_ReadCpltCallback(void); +/* USER CODE END BSP_H_CODE */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F7_SD_H */ diff --git a/Core/Inc/FATFS/Target/fatfs_platform.h b/Core/Inc/FATFS/Target/fatfs_platform.h new file mode 100644 index 0000000..17f9817 --- /dev/null +++ b/Core/Inc/FATFS/Target/fatfs_platform.h @@ -0,0 +1,27 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : fatfs_platform.h + * @brief : fatfs_platform header file + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** +*/ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx_hal.h" +/* Defines ------------------------------------------------------------------*/ +#define SD_PRESENT ((uint8_t)0x01) /* also in bsp_driver_sd.h */ +#define SD_NOT_PRESENT ((uint8_t)0x00) /* also in bsp_driver_sd.h */ +#define SD_DETECT_PIN GPIO_PIN_0 +#define SD_DETECT_GPIO_PORT GPIOE +/* Prototypes ---------------------------------------------------------------*/ +uint8_t BSP_PlatformIsDetected(void); diff --git a/Core/Inc/FATFS/Target/ffconf.h b/Core/Inc/FATFS/Target/ffconf.h new file mode 100644 index 0000000..40c0df4 --- /dev/null +++ b/Core/Inc/FATFS/Target/ffconf.h @@ -0,0 +1,269 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * FatFs - Generic FAT file system module R0.12c (C)ChaN, 2017 + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifndef _FFCONF +#define _FFCONF 68300 /* Revision ID */ + +/*-----------------------------------------------------------------------------/ +/ Additional user header to be used +/-----------------------------------------------------------------------------*/ + +#include "main.h" +#include "stm32f7xx_hal.h" +#include "bsp_driver_sd.h" + +/*-----------------------------------------------------------------------------/ +/ Function Configurations +/-----------------------------------------------------------------------------*/ + +#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + +#define _FS_MINIMIZE 0 /* 0 to 3 */ +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: All basic functions are enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + +#define _USE_STRFUNC 2 /* 0:Disable or 1-2:Enable */ +/* This option switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + +#define _USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + +#define _USE_MKFS 1 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + +#define _USE_FASTSEEK 1 +/* This option switches fast seek feature. (0:Disable or 1:Enable) */ + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ + +#define _USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + +#define _USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + +/*-----------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/-----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 850 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 1 - ASCII (No extended character. Non-LFN cfg. only) +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +*/ + +#define _USE_LFN 0 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN switches the support of long file name (LFN). +/ +/ 0: Disable support of LFN. _MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree(), must be added to the project. */ + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ + +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to +/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). +/ +/ 0: ANSI/OEM +/ 1: UTF-16LE +/ 2: UTF-16BE +/ 3: UTF-8 +/ +/ This option has no effect when _LFN_UNICODE == 0. */ + +#define _FS_RPATH 0 /* 0 to 2 */ +/* This option configures support of relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + +/* USER CODE BEGIN Volumes */ +#define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */ +#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. +/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. _VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ +/* USER CODE END Volumes */ + +#define _MULTI_PARTITION 0 /* 0:Single partition, 1:Multiple partition */ +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ function will be available. */ +#define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* These options configure the range of sector size to be supported. (512, 1024, +/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured +/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the +/ disk_ioctl() function. */ + +#define _USE_TRIM 0 +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + +#define _FS_NOFSINFO 0 /* 0,1,2 or 3 */ +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) +/ Note that enabling exFAT discards C89 compatibility. */ + +#define _FS_NORTC 0 +#define _NORTC_MON 6 +#define _NORTC_MDAY 4 +#define _NORTC_YEAR 2015 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, +/ _NORTC_MDAY and _NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ + +#define _FS_LOCK 2 /* 0:Disable or >=1:Enable */ +/* The option _FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when _FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t NULL +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The _FS_TIMEOUT defines timeout period in unit of time tick. +/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + +/* define the ff_malloc ff_free macros as standard malloc free */ +#if !defined(ff_malloc) && !defined(ff_free) +#include +#define ff_malloc malloc +#define ff_free free +#endif + +#endif /* _FFCONF */ diff --git a/Core/Inc/FATFS/Target/sd_diskio.h b/Core/Inc/FATFS/Target/sd_diskio.h new file mode 100644 index 0000000..3376701 --- /dev/null +++ b/Core/Inc/FATFS/Target/sd_diskio.h @@ -0,0 +1,41 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file sd_diskio.h + * @brief Header for sd_diskio.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Note: code generation based on sd_diskio_dma_template.h */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __SD_DISKIO_H +#define __SD_DISKIO_H + +/* USER CODE BEGIN firstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END firstSection */ + +/* Includes ------------------------------------------------------------------*/ +#include "bsp_driver_sd.h" +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +extern const Diskio_drvTypeDef SD_Driver; + +/* USER CODE BEGIN lastSection */ +/* can be used to modify / undefine previous code or add new definitions */ +/* USER CODE END lastSection */ + +#endif /* __SD_DISKIO_H */ diff --git a/Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h b/Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h deleted file mode 100644 index 5ed432d..0000000 --- a/Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc/usbd_cdc.h +++ /dev/null @@ -1,174 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_cdc.h - * @author MCD Application Team - * @brief header file for the usbd_cdc.c file. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2015 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_CDC_H -#define __USB_CDC_H - -#ifdef __cplusplus -extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_ioreq.h" - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup usbd_cdc - * @brief This file is the Header file for usbd_cdc.c - * @{ - */ - - -/** @defgroup usbd_cdc_Exported_Defines - * @{ - */ -#define CDC_IN_EP 0x81U /* EP1 for data IN */ -#define CDC_OUT_EP 0x01U /* EP1 for data OUT */ -#define CDC_CMD_EP 0x82U /* EP2 for CDC commands */ - -#ifndef CDC_HS_BINTERVAL -#define CDC_HS_BINTERVAL 0x10U -#endif /* CDC_HS_BINTERVAL */ - -#ifndef CDC_FS_BINTERVAL -#define CDC_FS_BINTERVAL 0x10U -#endif /* CDC_FS_BINTERVAL */ - -/* CDC Endpoints parameters: you can fine tune these values depending on the needed baudrates and performance. */ -#define CDC_DATA_HS_MAX_PACKET_SIZE 512U /* Endpoint IN & OUT Packet size */ -#define CDC_DATA_FS_MAX_PACKET_SIZE 64U /* Endpoint IN & OUT Packet size */ -#define CDC_CMD_PACKET_SIZE 8U /* Control Endpoint Packet size */ - -#define USB_CDC_CONFIG_DESC_SIZ 67U -#define CDC_DATA_HS_IN_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE -#define CDC_DATA_HS_OUT_PACKET_SIZE CDC_DATA_HS_MAX_PACKET_SIZE - -#define CDC_DATA_FS_IN_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE -#define CDC_DATA_FS_OUT_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE - -/*---------------------------------------------------------------------*/ -/* CDC definitions */ -/*---------------------------------------------------------------------*/ -#define CDC_SEND_ENCAPSULATED_COMMAND 0x00U -#define CDC_GET_ENCAPSULATED_RESPONSE 0x01U -#define CDC_SET_COMM_FEATURE 0x02U -#define CDC_GET_COMM_FEATURE 0x03U -#define CDC_CLEAR_COMM_FEATURE 0x04U -#define CDC_SET_LINE_CODING 0x20U -#define CDC_GET_LINE_CODING 0x21U -#define CDC_SET_CONTROL_LINE_STATE 0x22U -#define CDC_SEND_BREAK 0x23U - -/** - * @} - */ - - -/** @defgroup USBD_CORE_Exported_TypesDefinitions - * @{ - */ - -/** - * @} - */ -typedef struct -{ - uint32_t bitrate; - uint8_t format; - uint8_t paritytype; - uint8_t datatype; -} USBD_CDC_LineCodingTypeDef; - -typedef struct _USBD_CDC_Itf -{ - int8_t (* Init)(void); - int8_t (* DeInit)(void); - int8_t (* Control)(uint8_t cmd, uint8_t *pbuf, uint16_t length); - int8_t (* Receive)(uint8_t *Buf, uint32_t *Len); - int8_t (* TransmitCplt)(uint8_t *Buf, uint32_t *Len, uint8_t epnum); -} USBD_CDC_ItfTypeDef; - - -typedef struct -{ - uint32_t data[CDC_DATA_HS_MAX_PACKET_SIZE / 4U]; /* Force 32bits alignment */ - uint8_t CmdOpCode; - uint8_t CmdLength; - uint8_t *RxBuffer; - uint8_t *TxBuffer; - uint32_t RxLength; - uint32_t TxLength; - - __IO uint32_t TxState; - __IO uint32_t RxState; -} USBD_CDC_HandleTypeDef; - - - -/** @defgroup USBD_CORE_Exported_Macros - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CORE_Exported_Variables - * @{ - */ - -extern USBD_ClassTypeDef USBD_CDC; -#define USBD_CDC_CLASS &USBD_CDC -/** - * @} - */ - -/** @defgroup USB_CORE_Exported_Functions - * @{ - */ -uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops); - -uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff, - uint32_t length); - -uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff); -uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev); -uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev); -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_CDC_H */ -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/Middlewares/Third_Party/FatFs/src/diskio.h b/Core/Inc/Middlewares/Third_Party/FatFs/src/diskio.h new file mode 100644 index 0000000..5b61e57 --- /dev/null +++ b/Core/Inc/Middlewares/Third_Party/FatFs/src/diskio.h @@ -0,0 +1,80 @@ +/*-----------------------------------------------------------------------/ +/ Low level disk interface modlue include file (C)ChaN, 2014 / +/-----------------------------------------------------------------------*/ + +#ifndef _DISKIO_DEFINED +#define _DISKIO_DEFINED + +#ifdef __cplusplus +extern "C" { +#endif + +#define _USE_WRITE 1 /* 1: Enable disk_write function */ +#define _USE_IOCTL 1 /* 1: Enable disk_ioctl function */ + +#include "integer.h" + + +/* Status of Disk Functions */ +typedef BYTE DSTATUS; + +/* Results of Disk Functions */ +typedef enum { + RES_OK = 0, /* 0: Successful */ + RES_ERROR, /* 1: R/W Error */ + RES_WRPRT, /* 2: Write Protected */ + RES_NOTRDY, /* 3: Not Ready */ + RES_PARERR /* 4: Invalid Parameter */ +} DRESULT; + + +/*---------------------------------------*/ +/* Prototypes for disk control functions */ + + +DSTATUS disk_initialize (BYTE pdrv); +DSTATUS disk_status (BYTE pdrv); +DRESULT disk_read (BYTE pdrv, BYTE* buff, DWORD sector, UINT count); +DRESULT disk_write (BYTE pdrv, const BYTE* buff, DWORD sector, UINT count); +DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); +DWORD get_fattime (void); + +/* Disk Status Bits (DSTATUS) */ + +#define STA_NOINIT 0x01 /* Drive not initialized */ +#define STA_NODISK 0x02 /* No medium in the drive */ +#define STA_PROTECT 0x04 /* Write protected */ + + +/* Command code for disk_ioctrl fucntion */ + +/* Generic command (Used by FatFs) */ +#define CTRL_SYNC 0 /* Complete pending write process (needed at _FS_READONLY == 0) */ +#define GET_SECTOR_COUNT 1 /* Get media size (needed at _USE_MKFS == 1) */ +#define GET_SECTOR_SIZE 2 /* Get sector size (needed at _MAX_SS != _MIN_SS) */ +#define GET_BLOCK_SIZE 3 /* Get erase block size (needed at _USE_MKFS == 1) */ +#define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at _USE_TRIM == 1) */ + +/* Generic command (Not used by FatFs) */ +#define CTRL_POWER 5 /* Get/Set power status */ +#define CTRL_LOCK 6 /* Lock/Unlock media removal */ +#define CTRL_EJECT 7 /* Eject media */ +#define CTRL_FORMAT 8 /* Create physical format on the media */ + +/* MMC/SDC specific ioctl command */ +#define MMC_GET_TYPE 10 /* Get card type */ +#define MMC_GET_CSD 11 /* Get CSD */ +#define MMC_GET_CID 12 /* Get CID */ +#define MMC_GET_OCR 13 /* Get OCR */ +#define MMC_GET_SDSTAT 14 /* Get SD status */ + +/* ATA/CF specific ioctl command */ +#define ATA_GET_REV 20 /* Get F/W revision */ +#define ATA_GET_MODEL 21 /* Get model name */ +#define ATA_GET_SN 22 /* Get serial number */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/Core/Inc/Middlewares/Third_Party/FatFs/src/ff.h b/Core/Inc/Middlewares/Third_Party/FatFs/src/ff.h new file mode 100644 index 0000000..b14c3ce --- /dev/null +++ b/Core/Inc/Middlewares/Third_Party/FatFs/src/ff.h @@ -0,0 +1,361 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12c / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: + +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/----------------------------------------------------------------------------*/ + + +#ifndef _FATFS +#define _FATFS 68300 /* Revision ID */ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "integer.h" /* Basic integer types */ +#include "ffconf.h" /* FatFs configuration options */ + +#if _FATFS != _FFCONF +#error Wrong configuration file (ffconf.h). +#endif + + + +/* Definitions of volume management */ + +#if _MULTI_PARTITION /* Multiple partition configuration */ +typedef struct { + BYTE pd; /* Physical drive number */ + BYTE pt; /* Partition: 0:Auto detect, 1-4:Forced partition) */ +} PARTITION; +extern PARTITION VolToPart[]; /* Volume - Partition resolution table */ +#endif + + + +/* Type of path name strings on FatFs API */ + +#if _LFN_UNICODE /* Unicode (UTF-16) string */ +#if _USE_LFN == 0 +#error _LFN_UNICODE must be 0 at non-LFN cfg. +#endif +#ifndef _INC_TCHAR +typedef WCHAR TCHAR; +#define _T(x) L ## x +#define _TEXT(x) L ## x +#endif +#else /* ANSI/OEM string */ +#ifndef _INC_TCHAR +typedef char TCHAR; +#define _T(x) x +#define _TEXT(x) x +#endif +#endif + + + +/* Type of file size variables */ + +#if _FS_EXFAT +#if _USE_LFN == 0 +#error LFN must be enabled when enable exFAT +#endif +typedef QWORD FSIZE_t; +#else +typedef DWORD FSIZE_t; +#endif + + + +/* File system object structure (FATFS) */ + +typedef struct { + BYTE fs_type; /* File system type (0:N/A) */ + BYTE drv; /* Physical drive number */ + BYTE n_fats; /* Number of FATs (1 or 2) */ + BYTE wflag; /* win[] flag (b0:dirty) */ + BYTE fsi_flag; /* FSINFO flags (b7:disabled, b0:dirty) */ + WORD id; /* File system mount ID */ + WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ + WORD csize; /* Cluster size [sectors] */ +#if _MAX_SS != _MIN_SS + WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ +#endif +#if _USE_LFN != 0 + WCHAR* lfnbuf; /* LFN working buffer */ +#endif +#if _FS_EXFAT + BYTE* dirbuf; /* Directory entry block scratchpad buffer */ +#endif +#if _FS_REENTRANT + _SYNC_t sobj; /* Identifier of sync object */ +#endif +#if !_FS_READONLY + DWORD last_clst; /* Last allocated cluster */ + DWORD free_clst; /* Number of free clusters */ +#endif +#if _FS_RPATH != 0 + DWORD cdir; /* Current directory start cluster (0:root) */ +#if _FS_EXFAT + DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ + DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ + DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ +#endif +#endif + DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ + DWORD fsize; /* Size of an FAT [sectors] */ + DWORD volbase; /* Volume base sector */ + DWORD fatbase; /* FAT base sector */ + DWORD dirbase; /* Root directory base sector/cluster */ + DWORD database; /* Data base sector */ + DWORD winsect; /* Current sector appearing in the win[] */ + BYTE win[_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ +} FATFS; + + + +/* Object ID and allocation information (_FDID) */ + +typedef struct { + FATFS* fs; /* Pointer to the owner file system object */ + WORD id; /* Owner file system mount ID */ + BYTE attr; /* Object attribute */ + BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous (no data on FAT), =3:flagmented in this session, b2:sub-directory stretched) */ + DWORD sclust; /* Object start cluster (0:no cluster or root directory) */ + FSIZE_t objsize; /* Object size (valid when sclust != 0) */ +#if _FS_EXFAT + DWORD n_cont; /* Size of first fragment, clusters - 1 (valid when stat == 3) */ + DWORD n_frag; /* Size of last fragment needs to be written (valid when not zero) */ + DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ + DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ + DWORD c_ofs; /* Offset in the containing directory (valid when sclust != 0 and non-directory object) */ +#endif +#if _FS_LOCK != 0 + UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ +#endif +} _FDID; + + + +/* File object structure (FIL) */ + +typedef struct { + _FDID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ + BYTE flag; /* File status flags */ + BYTE err; /* Abort flag (error code) */ + FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ + DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ + DWORD sect; /* Sector number appearing in buf[] (0:invalid) */ +#if !_FS_READONLY + DWORD dir_sect; /* Sector number containing the directory entry */ + BYTE* dir_ptr; /* Pointer to the directory entry in the win[] */ +#endif +#if _USE_FASTSEEK + DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ +#endif +#if !_FS_TINY + BYTE buf[_MAX_SS]; /* File private data read/write window */ +#endif +} FIL; + + + +/* Directory object structure (DIR) */ + +typedef struct { + _FDID obj; /* Object identifier */ + DWORD dptr; /* Current read/write offset */ + DWORD clust; /* Current cluster */ + DWORD sect; /* Current sector (0:Read operation has terminated) */ + BYTE* dir; /* Pointer to the directory item in the win[] */ + BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ +#if _USE_LFN != 0 + DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ +#endif +#if _USE_FIND + const TCHAR* pat; /* Pointer to the name matching pattern */ +#endif +} DIR; + + + +/* File information structure (FILINFO) */ + +typedef struct { + FSIZE_t fsize; /* File size */ + WORD fdate; /* Modified date */ + WORD ftime; /* Modified time */ + BYTE fattrib; /* File attribute */ +#if _USE_LFN != 0 + TCHAR altname[13]; /* Alternative file name */ + TCHAR fname[_MAX_LFN + 1]; /* Primary file name */ +#else + TCHAR fname[13]; /* File name */ +#endif +} FILINFO; + + + +/* File function return code (FRESULT) */ + +typedef enum { + FR_OK = 0, /* (0) Succeeded */ + FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ + FR_INT_ERR, /* (2) Assertion failed */ + FR_NOT_READY, /* (3) The physical drive cannot work */ + FR_NO_FILE, /* (4) Could not find the file */ + FR_NO_PATH, /* (5) Could not find the path */ + FR_INVALID_NAME, /* (6) The path name format is invalid */ + FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ + FR_EXIST, /* (8) Access denied due to prohibited access */ + FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ + FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ + FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ + FR_NOT_ENABLED, /* (12) The volume has no work area */ + FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ + FR_MKFS_ABORTED, /* (14) The f_mkfs() aborted due to any problem */ + FR_TIMEOUT, /* (15) Could not get a grant to access the volume within defined period */ + FR_LOCKED, /* (16) The operation is rejected according to the file sharing policy */ + FR_NOT_ENOUGH_CORE, /* (17) LFN working buffer could not be allocated */ + FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > _FS_LOCK */ + FR_INVALID_PARAMETER /* (19) Given parameter is invalid */ +} FRESULT; + + + +/*--------------------------------------------------------------*/ +/* FatFs module application interface */ + +FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ +FRESULT f_close (FIL* fp); /* Close an open file object */ +FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ +FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ +FRESULT f_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ +FRESULT f_truncate (FIL* fp); /* Truncate the file */ +FRESULT f_sync (FIL* fp); /* Flush cached data of the writing file */ +FRESULT f_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ +FRESULT f_closedir (DIR* dp); /* Close an open directory */ +FRESULT f_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ +FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ +FRESULT f_findnext (DIR* dp, FILINFO* fno); /* Find next file */ +FRESULT f_mkdir (const TCHAR* path); /* Create a sub directory */ +FRESULT f_unlink (const TCHAR* path); /* Delete an existing file or directory */ +FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ +FRESULT f_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ +FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ +FRESULT f_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ +FRESULT f_chdir (const TCHAR* path); /* Change current directory */ +FRESULT f_chdrive (const TCHAR* path); /* Change current drive */ +FRESULT f_getcwd (TCHAR* buff, UINT len); /* Get current directory */ +FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ +FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ +FRESULT f_setlabel (const TCHAR* label); /* Set volume label */ +FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ +FRESULT f_expand (FIL* fp, FSIZE_t szf, BYTE opt); /* Allocate a contiguous block to the file */ +FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ +FRESULT f_mkfs (const TCHAR* path, BYTE opt, DWORD au, void* work, UINT len); /* Create a FAT volume */ +FRESULT f_fdisk (BYTE pdrv, const DWORD* szt, void* work); /* Divide a physical drive into some partitions */ +int f_putc (TCHAR c, FIL* fp); /* Put a character to the file */ +int f_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ +int f_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ +TCHAR* f_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ + +#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) +#define f_error(fp) ((fp)->err) +#define f_tell(fp) ((fp)->fptr) +#define f_size(fp) ((fp)->obj.objsize) +#define f_rewind(fp) f_lseek((fp), 0) +#define f_rewinddir(dp) f_readdir((dp), 0) +#define f_rmdir(path) f_unlink(path) + +#ifndef EOF +#define EOF (-1) +#endif + + + + +/*--------------------------------------------------------------*/ +/* Additional user defined functions */ + +/* RTC function */ +#if !_FS_READONLY && !_FS_NORTC +DWORD get_fattime (void); +#endif + +/* Unicode support functions */ +#if _USE_LFN != 0 /* Unicode - OEM code conversion */ +WCHAR ff_convert (WCHAR chr, UINT dir); /* OEM-Unicode bidirectional conversion */ +WCHAR ff_wtoupper (WCHAR chr); /* Unicode upper-case conversion */ +#if _USE_LFN == 3 /* Memory functions */ +void* ff_memalloc (UINT msize); /* Allocate memory block */ +void ff_memfree (void* mblock); /* Free memory block */ +#endif +#endif + +/* Sync functions */ +#if _FS_REENTRANT +int ff_cre_syncobj (BYTE vol, _SYNC_t* sobj); /* Create a sync object */ +int ff_req_grant (_SYNC_t sobj); /* Lock sync object */ +void ff_rel_grant (_SYNC_t sobj); /* Unlock sync object */ +int ff_del_syncobj (_SYNC_t sobj); /* Delete a sync object */ +#endif + + + + +/*--------------------------------------------------------------*/ +/* Flags and offset address */ + + +/* File access mode and open method flags (3rd argument of f_open) */ +#define FA_READ 0x01 +#define FA_WRITE 0x02 +#define FA_OPEN_EXISTING 0x00 +#define FA_CREATE_NEW 0x04 +#define FA_CREATE_ALWAYS 0x08 +#define FA_OPEN_ALWAYS 0x10 +#define FA_OPEN_APPEND 0x30 + +/* Fast seek controls (2nd argument of f_lseek) */ +#define CREATE_LINKMAP ((FSIZE_t)0 - 1) + +/* Format options (2nd argument of f_mkfs) */ +#define FM_FAT 0x01 +#define FM_FAT32 0x02 +#define FM_EXFAT 0x04 +#define FM_ANY 0x07 +#define FM_SFD 0x08 + +/* Filesystem type (FATFS.fs_type) */ +#define FS_FAT12 1 +#define FS_FAT16 2 +#define FS_FAT32 3 +#define FS_EXFAT 4 + +/* File attribute bits for directory entry (FILINFO.fattrib) */ +#define AM_RDO 0x01 /* Read only */ +#define AM_HID 0x02 /* Hidden */ +#define AM_SYS 0x04 /* System */ +#define AM_DIR 0x10 /* Directory */ +#define AM_ARC 0x20 /* Archive */ + + +#ifdef __cplusplus +} +#endif + +#endif /* _FATFS */ diff --git a/Core/Inc/Middlewares/Third_Party/FatFs/src/ff_gen_drv.h b/Core/Inc/Middlewares/Third_Party/FatFs/src/ff_gen_drv.h new file mode 100644 index 0000000..5172e0d --- /dev/null +++ b/Core/Inc/Middlewares/Third_Party/FatFs/src/ff_gen_drv.h @@ -0,0 +1,80 @@ +/** + ****************************************************************************** + * @file ff_gen_drv.h + * @author MCD Application Team + * @brief Header for ff_gen_drv.c module. + ***************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. All rights reserved. + * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +**/ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __FF_GEN_DRV_H +#define __FF_GEN_DRV_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "diskio.h" +#include "ff.h" +#include "stdint.h" + + +/* Exported types ------------------------------------------------------------*/ + +/** + * @brief Disk IO Driver structure definition + */ +typedef struct +{ + DSTATUS (*disk_initialize) (BYTE); /*!< Initialize Disk Drive */ + DSTATUS (*disk_status) (BYTE); /*!< Get Disk Status */ + DRESULT (*disk_read) (BYTE, BYTE*, DWORD, UINT); /*!< Read Sector(s) */ +#if _USE_WRITE == 1 + DRESULT (*disk_write) (BYTE, const BYTE*, DWORD, UINT); /*!< Write Sector(s) when _USE_WRITE = 0 */ +#endif /* _USE_WRITE == 1 */ +#if _USE_IOCTL == 1 + DRESULT (*disk_ioctl) (BYTE, BYTE, void*); /*!< I/O control operation when _USE_IOCTL = 1 */ +#endif /* _USE_IOCTL == 1 */ + +}Diskio_drvTypeDef; + +/** + * @brief Global Disk IO Drivers structure definition + */ +typedef struct +{ + uint8_t is_initialized[_VOLUMES]; + const Diskio_drvTypeDef *drv[_VOLUMES]; + uint8_t lun[_VOLUMES]; + volatile uint8_t nbr; + +}Disk_drvTypeDef; + +/* Exported constants --------------------------------------------------------*/ +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +uint8_t FATFS_LinkDriver(const Diskio_drvTypeDef *drv, char *path); +uint8_t FATFS_UnLinkDriver(char *path); +uint8_t FATFS_LinkDriverEx(const Diskio_drvTypeDef *drv, char *path, BYTE lun); +uint8_t FATFS_UnLinkDriverEx(char *path, BYTE lun); +uint8_t FATFS_GetAttachedDriversNbr(void); + +#ifdef __cplusplus +} +#endif + +#endif /* __FF_GEN_DRV_H */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/Core/Inc/Middlewares/Third_Party/FatFs/src/integer.h b/Core/Inc/Middlewares/Third_Party/FatFs/src/integer.h new file mode 100644 index 0000000..9ce7865 --- /dev/null +++ b/Core/Inc/Middlewares/Third_Party/FatFs/src/integer.h @@ -0,0 +1,38 @@ +/*-------------------------------------------*/ +/* Integer type definitions for FatFs module */ +/*-------------------------------------------*/ + +#ifndef _FF_INTEGER +#define _FF_INTEGER + +#ifdef _WIN32 /* FatFs development platform */ + +#include +#include +typedef unsigned __int64 QWORD; + + +#else /* Embedded platform */ + +/* These types MUST be 16-bit or 32-bit */ +typedef int INT; +typedef unsigned int UINT; + +/* This type MUST be 8-bit */ +typedef unsigned char BYTE; + +/* These types MUST be 16-bit */ +typedef short SHORT; +typedef unsigned short WORD; +typedef unsigned short WCHAR; + +/* These types MUST be 32-bit */ +typedef long LONG; +typedef unsigned long DWORD; + +/* This type MUST be 64-bit (Remove this for ANSI C (C89) compatibility) */ +typedef unsigned long long QWORD; + +#endif + +#endif diff --git a/Core/Inc/USB/usb_device.h b/Core/Inc/USB/usb_device.h new file mode 100644 index 0000000..955433a --- /dev/null +++ b/Core/Inc/USB/usb_device.h @@ -0,0 +1,15 @@ +#ifndef __USB_DEVICE__H__ +#define __USB_DEVICE__H__ + +#include "stm32f7xx.h" +#include "stm32f7xx_hal.h" +#include "usbd_def.h" +#include "usbd_RTT_class.h" + + +// USB function called by main.c to initialise the USB device +void MX_USB_DEVICE_Init(void); +// USB function called in basestation.c to register callback functions and starts the USB device after +void USB_Start_Class(USBD_RTT_Callbacks* callbacks); + +#endif //__USB_DEVICE__H__ \ No newline at end of file diff --git a/Core/Inc/USB/usbd_RTT_class.h b/Core/Inc/USB/usbd_RTT_class.h new file mode 100644 index 0000000..950f32c --- /dev/null +++ b/Core/Inc/USB/usbd_RTT_class.h @@ -0,0 +1,102 @@ +#ifndef __USB_RTT_CORE_H +#define __USB_RTT_CORE_H + + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_ioreq.h" + +#include "usbd_def.h" // USB constants and definitions +#include "usbd_desc.h" // USB descriptors + +// RX Buffer Sizes +#define USB_HIGH_PRIO_RX_BUF_SIZE 1024 +#define USB_LOW_PRIO_RX_BUF_SIZE 1024 + +// Endpoint addresses +// IN (0x80) means host <-- device, OUT (0x00) means host --> device +// EP 0x00U and 0x80U are reserved for command packets +#define RTT_HIGH_PRIO_IN_EP 0x81U +#define RTT_LOW_PRIO_IN_EP 0x82U +#define RTT_HIGH_PRIO_OUT_EP 0x03U +#define RTT_LOW_PRIO_OUT_EP 0x04U + +// Packet Sizes +#define USB_BULK_HS_MAX_PACKET_SIZE USB_HS_MAX_PACKET_SIZE // Max Bulk packet size in bytes for HS +#define USB_BULK_FS_MAX_PACKET_SIZE USB_FS_MAX_PACKET_SIZE // Max Bulk packet size in bytes for FS + +// The USB host can only allocate for a certain bandwidth on the bus, BULK are not allocated and lowest priority. +// The user program may opt for a certain interface but the USB host alone controls the interface selection. +// The host will try to use the interface if possible, but no interface is guaranteed. +// INTR are calculated by max_packet_size*(1000/bInterval). So small max_packet_size and bInterval is prefered for fast updates. + +#define USB_INTR_HS_MAX_PACKET_SIZE 1024 // MAX Interupt packet size in HS +#define USB_INTR_FS_MAX_PACKET_SIZE 64 // MAX Interupt packet size in FS + +// Endpoints and interfaces +#define USB_RTT_HIGH_PRIO_INTERFACE 0x00 +#define USB_RTT_EP1_TYPE USBD_EP_TYPE_BULK +#define USB_RTT_EP1_MAX_PACKET_SIZE USB_BULK_HS_MAX_PACKET_SIZE + +#define USB_RTT_EP1_ALT_TYPE USBD_EP_TYPE_INTR +#define USB_RTT_EP1_ALT_MAX_PACKET_SIZE 64 +#define USB_RTT_EP1_ALT_BINTERVAL 1 + +#define USB_RTT_LOW_PRIO_INTERFACE 0x01 +#define USB_RTT_EP2_TYPE USBD_EP_TYPE_BULK +#define USB_RTT_EP2_MAX_PACKET_SIZE USB_BULK_HS_MAX_PACKET_SIZE + +// reference to the RTT USB class struct created in usbd_RTT_class.c, which contains all callback functions needed for usb +extern USBD_ClassTypeDef USBD_RTT_ClassDriver; +extern USBD_HandleTypeDef hUsbDeviceHS; // in usb_device.c + +// Interface setting container +typedef struct{ + uint8_t intID; // interface number + uint8_t InAddress; // IN ENDPOINT address + uint8_t OutAddress; // OUT ENDPOINT address + uint8_t Type; // Endpoint type + uint16_t MaxPacketSize; // Max packet size that can be sent over this connection + uint8_t* Rxbuffer; // pointer to where packets can be stored +} InterfaceConfig; + + +// Class specific data +typedef struct{ + // Control transfer data + uint8_t CmdOpCode; + uint8_t CmdLength; + uint32_t setup_data[USB_HS_MAX_PACKET_SIZE / 4U]; + + // Interface variables + uint8_t INT0AltSetting; // which alternative interface is selected, 0 is nomal, 1 is alt + InterfaceConfig* INT0active; // pointer to active interface + uint8_t INT1AltSetting; + InterfaceConfig* INT1active; // pointer to active interface + __IO uint32_t INT0TxState; + __IO uint32_t INT1TxState; + __IO uint32_t RxState; +} USBD_RTT_HandleTypeDef; + +// User callable functions to transmit data +USBD_StatusTypeDef USB_TransmitLowPriority(uint8_t* buf, uint32_t len); +USBD_StatusTypeDef USB_TransmitHighPriority(uint8_t* buf, uint32_t len); + +// Callback prototypes +typedef USBD_StatusTypeDef USB_Class_Setup_Requests(uint8_t cmd, uint8_t* pbuf, uint16_t length); // Make class implementation instead of user? +typedef void USB_HighPriority_TX_cplt(void); +typedef void USB_HighPriority_RX_cplt(uint8_t* buf, uint32_t len); +typedef void USB_LowPriority_TX_cplt(void); +typedef void USB_LowPriority_RX_cplt(uint8_t* buf, uint32_t len); + + +// Callback functions needed to be implemented in basestation.c +// NOTE: These functions are called from an interupt, so the implementation of these functions should be the bare minimum needed. +typedef struct USBD_RTT_Callbacks{ + USB_Class_Setup_Requests* usbControl; + USB_HighPriority_TX_cplt* highprioTXcplt; + USB_HighPriority_RX_cplt* highprioRXcplt; + USB_LowPriority_TX_cplt* lowprioTXcplt; + USB_LowPriority_RX_cplt* lowprioRXcplt; +} USBD_RTT_Callbacks; + +#endif /* __USB_RTT_CORE_H */ diff --git a/Core/Inc/USB/usbd_conf.h b/Core/Inc/USB/usbd_conf.h new file mode 100644 index 0000000..8702528 --- /dev/null +++ b/Core/Inc/USB/usbd_conf.h @@ -0,0 +1,58 @@ +#ifndef __USBD_CONF_RTT_H +#define __USBD_CONF_RTT_H + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx.h" +#include +#include +#include + +#define USBD_MAX_NUM_INTERFACES 2U +#define USBD_MAX_NUM_CONFIGURATION 1U +#define USBD_MAX_STR_DESC_SIZ 0x100U +#define USBD_SELF_POWERED 1U +#define USBD_DEBUG_LEVEL 0U + +/* #define for FS and HS identification */ +#define DEVICE_FS 0 +#define DEVICE_HS 1 + +/* Memory management macros */ +#define USBD_malloc malloc +#define USBD_free free +#define USBD_memset memset +#define USBD_memcpy memcpy +#define USBD_Delay HAL_Delay + +/* DEBUG macros */ +#if (USBD_DEBUG_LEVEL > 0U) +#define USBD_UsrLog(...) do { \ + printf(__VA_ARGS__); \ + printf("\n"); \ +} while (0) +#else +#define USBD_UsrLog(...) do {} while (0) +#endif + +#if (USBD_DEBUG_LEVEL > 1U) + +#define USBD_ErrLog(...) do { \ + printf("ERROR: ") ; \ + printf(__VA_ARGS__); \ + printf("\n"); \ +} while (0) +#else +#define USBD_ErrLog(...) do {} while (0) +#endif + +#if (USBD_DEBUG_LEVEL > 2U) +#define USBD_DbgLog(...) do { \ + printf("DEBUG : ") ; \ + printf(__VA_ARGS__); \ + printf("\n"); \ +} while (0) +#else +#define USBD_DbgLog(...) do {} while (0) +#endif + +#endif /* __USBD_CONF_RTT_H */ diff --git a/Core/Inc/USB/usbd_desc.h b/Core/Inc/USB/usbd_desc.h new file mode 100644 index 0000000..d9424c7 --- /dev/null +++ b/Core/Inc/USB/usbd_desc.h @@ -0,0 +1,26 @@ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __USBD_DESC_TEMPLATE_H +#define __USBD_DESC_TEMPLATE_H + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_def.h" + +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +#define DEVICE_ID1 (UID_BASE) +#define DEVICE_ID2 (UID_BASE + 0x4U) +#define DEVICE_ID3 (UID_BASE + 0x8U) + +#define USB_SIZ_STRING_SERIAL 0x1AU + +/* Exported macro ------------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +extern USBD_DescriptorsTypeDef HS_Desc; + +extern uint8_t *USBD_RTT_DeviceQualifierDescriptor(uint16_t *length); +extern uint8_t *USBD_RTT_GetDeviceQualifierDesc(uint16_t *length); + +#endif /* __USBD_DESC_TEMPLATE_H*/ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/USB_DEVICE/App/usb_device.h b/Core/Inc/USB_DEVICE/App/usb_device.h deleted file mode 100644 index a8613b2..0000000 --- a/Core/Inc/USB_DEVICE/App/usb_device.h +++ /dev/null @@ -1,105 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usb_device.h - * @version : v1.0_Cube - * @brief : Header for usb_device.c file. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USB_DEVICE__H__ -#define __USB_DEVICE__H__ - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "stm32f7xx.h" -#include "stm32f7xx_hal.h" -#include "usbd_def.h" - -/* USER CODE BEGIN INCLUDE */ - -/* USER CODE END INCLUDE */ - -/** @addtogroup USBD_OTG_DRIVER - * @{ - */ - -/** @defgroup USBD_DEVICE USBD_DEVICE - * @brief Device file for Usb otg low level driver. - * @{ - */ - -/** @defgroup USBD_DEVICE_Exported_Variables USBD_DEVICE_Exported_Variables - * @brief Public variables. - * @{ - */ - -/* Private variables ---------------------------------------------------------*/ -/* USER CODE BEGIN PV */ - -/* USER CODE END PV */ - -/* Private function prototypes -----------------------------------------------*/ -/* USER CODE BEGIN PFP */ - -/* USER CODE END PFP */ - -/* - * -- Insert your variables declaration here -- - */ -/* USER CODE BEGIN VARIABLES */ - -/* USER CODE END VARIABLES */ -/** - * @} - */ - -/** @defgroup USBD_DEVICE_Exported_FunctionsPrototype USBD_DEVICE_Exported_FunctionsPrototype - * @brief Declaration of public functions for Usb device. - * @{ - */ - -/** USB Device initialization function. */ -void MX_USB_DEVICE_Init(void); - -/* - * -- Insert functions declaration here -- - */ -/* USER CODE BEGIN FD */ - -/* USER CODE END FD */ -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USB_DEVICE__H__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/USB_DEVICE/App/usbd_cdc_if.h b/Core/Inc/USB_DEVICE/App/usbd_cdc_if.h deleted file mode 100644 index 68a92c0..0000000 --- a/Core/Inc/USB_DEVICE/App/usbd_cdc_if.h +++ /dev/null @@ -1,134 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usbd_cdc_if.h - * @version : v1.0_Cube - * @brief : Header for usbd_cdc_if.c file. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_CDC_IF_H__ -#define __USBD_CDC_IF_H__ - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_cdc.h" - -/* USER CODE BEGIN INCLUDE */ - -/* USER CODE END INCLUDE */ - -/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY - * @brief For Usb device. - * @{ - */ - -/** @defgroup USBD_CDC_IF USBD_CDC_IF - * @brief Usb VCP device module - * @{ - */ - -/** @defgroup USBD_CDC_IF_Exported_Defines USBD_CDC_IF_Exported_Defines - * @brief Defines. - * @{ - */ -/* USER CODE BEGIN EXPORTED_DEFINES */ -/* Define size for the receive and transmit buffer over CDC */ -/* It's up to user to redefine and/or remove those define */ -#define APP_RX_DATA_SIZE 2048 -#define APP_TX_DATA_SIZE 2048 - -/* USER CODE END EXPORTED_DEFINES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Exported_Types USBD_CDC_IF_Exported_Types - * @brief Types. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_TYPES */ - -/* USER CODE END EXPORTED_TYPES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Exported_Macros USBD_CDC_IF_Exported_Macros - * @brief Aliases. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_MACRO */ - -/* USER CODE END EXPORTED_MACRO */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables - * @brief Public variables. - * @{ - */ - -/** CDC Interface callback. */ -extern USBD_CDC_ItfTypeDef USBD_Interface_fops_HS; - -/* USER CODE BEGIN EXPORTED_VARIABLES */ - -/* USER CODE END EXPORTED_VARIABLES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Exported_FunctionsPrototype USBD_CDC_IF_Exported_FunctionsPrototype - * @brief Public functions declaration. - * @{ - */ - -uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len); - -/* USER CODE BEGIN EXPORTED_FUNCTIONS */ - -/* USER CODE END EXPORTED_FUNCTIONS */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_CDC_IF_H__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/USB_DEVICE/App/usbd_desc.h b/Core/Inc/USB_DEVICE/App/usbd_desc.h deleted file mode 100644 index 4033860..0000000 --- a/Core/Inc/USB_DEVICE/App/usbd_desc.h +++ /dev/null @@ -1,145 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usbd_desc.c - * @version : v1.0_Cube - * @brief : Header for usbd_conf.c file. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_DESC__C__ -#define __USBD_DESC__C__ - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_def.h" - -/* USER CODE BEGIN INCLUDE */ - -/* USER CODE END INCLUDE */ - -/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY - * @{ - */ - -/** @defgroup USBD_DESC USBD_DESC - * @brief Usb device descriptors module. - * @{ - */ - -/** @defgroup USBD_DESC_Exported_Constants USBD_DESC_Exported_Constants - * @brief Constants. - * @{ - */ -#define DEVICE_ID1 (UID_BASE) -#define DEVICE_ID2 (UID_BASE + 0x4) -#define DEVICE_ID3 (UID_BASE + 0x8) - -#define USB_SIZ_STRING_SERIAL 0x1A - -/* USER CODE BEGIN EXPORTED_CONSTANTS */ - -/* USER CODE END EXPORTED_CONSTANTS */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Exported_Defines USBD_DESC_Exported_Defines - * @brief Defines. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_DEFINES */ - -/* USER CODE END EXPORTED_DEFINES */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Exported_TypesDefinitions USBD_DESC_Exported_TypesDefinitions - * @brief Types. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_TYPES */ - -/* USER CODE END EXPORTED_TYPES */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Exported_Macros USBD_DESC_Exported_Macros - * @brief Aliases. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_MACRO */ - -/* USER CODE END EXPORTED_MACRO */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Exported_Variables USBD_DESC_Exported_Variables - * @brief Public variables. - * @{ - */ - -/** Descriptor for the Usb device. */ -extern USBD_DescriptorsTypeDef HS_Desc; - -/* USER CODE BEGIN EXPORTED_VARIABLES */ - -/* USER CODE END EXPORTED_VARIABLES */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Exported_FunctionsPrototype USBD_DESC_Exported_FunctionsPrototype - * @brief Public functions declaration. - * @{ - */ - -/* USER CODE BEGIN EXPORTED_FUNCTIONS */ - -/* USER CODE END EXPORTED_FUNCTIONS */ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_DESC__C__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/USB_DEVICE/Target/usbd_conf.h b/Core/Inc/USB_DEVICE/Target/usbd_conf.h deleted file mode 100644 index 67a043a..0000000 --- a/Core/Inc/USB_DEVICE/Target/usbd_conf.h +++ /dev/null @@ -1,173 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usbd_conf.h - * @version : v1.0_Cube - * @brief : Header for usbd_conf.c file. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Define to prevent recursive inclusion -------------------------------------*/ -#ifndef __USBD_CONF__H__ -#define __USBD_CONF__H__ - -#ifdef __cplusplus - extern "C" { -#endif - -/* Includes ------------------------------------------------------------------*/ -#include -#include -#include -#include "main.h" -#include "stm32f7xx.h" -#include "stm32f7xx_hal.h" - -/* USER CODE BEGIN INCLUDE */ - -/* USER CODE END INCLUDE */ - -/** @addtogroup USBD_OTG_DRIVER - * @{ - */ - -/** @defgroup USBD_CONF USBD_CONF - * @brief Configuration file for Usb otg low level driver. - * @{ - */ - -/** @defgroup USBD_CONF_Exported_Variables USBD_CONF_Exported_Variables - * @brief Public variables. - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CONF_Exported_Defines USBD_CONF_Exported_Defines - * @brief Defines for configuration of the Usb device. - * @{ - */ - -/*---------- -----------*/ -#define USBD_MAX_NUM_INTERFACES 1U -/*---------- -----------*/ -#define USBD_MAX_NUM_CONFIGURATION 1U -/*---------- -----------*/ -#define USBD_MAX_STR_DESC_SIZ 512U -/*---------- -----------*/ -#define USBD_DEBUG_LEVEL 0U -/*---------- -----------*/ -#define USBD_LPM_ENABLED 1U -/*---------- -----------*/ -#define USBD_SELF_POWERED 1U - -/****************************************/ -/* #define for FS and HS identification */ -#define DEVICE_FS 0 -#define DEVICE_HS 1 - -/** - * @} - */ - -/** @defgroup USBD_CONF_Exported_Macros USBD_CONF_Exported_Macros - * @brief Aliases. - * @{ - */ - -/* Memory management macros */ - -/** Alias for memory allocation. */ -#define USBD_malloc malloc - -/** Alias for memory release. */ -#define USBD_free free - -/** Alias for memory set. */ -#define USBD_memset memset - -/** Alias for memory copy. */ -#define USBD_memcpy memcpy - -/** Alias for delay. */ -#define USBD_Delay HAL_Delay - -/* DEBUG macros */ - -#if (USBD_DEBUG_LEVEL > 0) -#define USBD_UsrLog(...) printf(__VA_ARGS__);\ - printf("\n"); -#else -#define USBD_UsrLog(...) -#endif - -#if (USBD_DEBUG_LEVEL > 1) - -#define USBD_ErrLog(...) printf("ERROR: ") ;\ - printf(__VA_ARGS__);\ - printf("\n"); -#else -#define USBD_ErrLog(...) -#endif - -#if (USBD_DEBUG_LEVEL > 2) -#define USBD_DbgLog(...) printf("DEBUG : ") ;\ - printf(__VA_ARGS__);\ - printf("\n"); -#else -#define USBD_DbgLog(...) -#endif - -/** - * @} - */ - -/** @defgroup USBD_CONF_Exported_Types USBD_CONF_Exported_Types - * @brief Types. - * @{ - */ - -/** - * @} - */ - -/** @defgroup USBD_CONF_Exported_FunctionsPrototype USBD_CONF_Exported_FunctionsPrototype - * @brief Declaration of public functions for Usb device. - * @{ - */ - -/* Exported functions -------------------------------------------------------*/ - -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -#ifdef __cplusplus -} -#endif - -#endif /* __USBD_CONF__H__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/Utilities/CircularBuffer.h b/Core/Inc/Utilities/CircularBuffer.h index ea2695e..59ed81d 100644 --- a/Core/Inc/Utilities/CircularBuffer.h +++ b/Core/Inc/Utilities/CircularBuffer.h @@ -47,6 +47,14 @@ uint32_t CircularBuffer_spaceFilled(CircularBuffer* circBuf); */ bool CircularBuffer_canWrite(CircularBuffer* circBuf, uint32_t length); +/** + * Check if a hypotethical message of 'length' bytes could be read from the buffer. + * @param circBuf Pointer to the circular buffer object + * @param length Length of the hypothetical message + * @return True if it can be read from buffer, False if can not be read from the buffer + */ +bool CircularBuffer_canRead(CircularBuffer* circBuf, uint32_t length); + /** * Writes 'length' bytes to the buffer, even if this overflows. * @param circBuf Pointer to the circular buffer object diff --git a/Core/Inc/Utilities/packet_buffers.h b/Core/Inc/Utilities/packet_buffers.h index d559fd7..c813862 100644 --- a/Core/Inc/Utilities/packet_buffers.h +++ b/Core/Inc/Utilities/packet_buffers.h @@ -4,73 +4,47 @@ #include #include "basestation.h" -#include "REM_SX1280Filler.h" #include "REM_BaseTypes.h" #include "REM_RobotCommand.h" #include "REM_RobotFeedback.h" -#include "REM_RobotBuzzer.h" -#include "REM_RobotStateInfo.h" -#include "REM_RobotGetPIDGains.h" -#include "REM_RobotSetPIDGains.h" -#include "REM_RobotPIDGains.h" -#include "REM_RobotMusicCommand.h" +#include "REM_SX1280Filler.h" REM_SX1280FillerPayload SX1280_filler_payload; -struct _buffer_RobotCommand { - REM_RobotCommandPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotCommand buffer_RobotCommand[MAX_NUMBER_OF_ROBOTS]; +#include "CircularBuffer.h" -struct _buffer_RobotSetPIDGains { - REM_RobotSetPIDGainsPayload packet; +typedef struct _wrapper_REM_RobotCommand { + REM_RobotCommandPayload packet; bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotSetPIDGains buffer_RobotSetPIDGains[MAX_NUMBER_OF_ROBOTS]; +} wrapper_REM_RobotCommand; -struct _buffer_RobotFeedback { +typedef struct _wrapper_REM_RobotFeedback { REM_RobotFeedbackPayload packet; bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotFeedback buffer_RobotFeedback[MAX_NUMBER_OF_ROBOTS]; +} wrapper_REM_RobotFeedback; -struct _buffer_RobotStateInfo { - REM_RobotStateInfoPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotStateInfo buffer_RobotStateInfo[MAX_NUMBER_OF_ROBOTS]; -struct _buffer_RobotBuzzer { - REM_RobotBuzzerPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotBuzzer buffer_RobotBuzzer[MAX_NUMBER_OF_ROBOTS]; -struct _buffer_RobotGetPIDGains { - REM_RobotGetPIDGainsPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotGetPIDGains buffer_RobotGetPIDGains[MAX_NUMBER_OF_ROBOTS]; +wrapper_REM_RobotCommand buffer_REM_RobotCommand[MAX_NUMBER_OF_ROBOTS]; +wrapper_REM_RobotFeedback buffer_REM_RobotFeedback[MAX_NUMBER_OF_ROBOTS]; -struct _buffer_RobotPIDGains { - REM_RobotPIDGainsPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotPIDGains buffer_RobotPIDGains[MAX_NUMBER_OF_ROBOTS]; +CircularBuffer* nonpriority_queue_robots_index[MAX_NUMBER_OF_ROBOTS]; +CircularBuffer* nonpriority_queue_pc_index; +CircularBuffer* nonpriority_queue_bs_index; + +typedef struct _Wrapper_REM_Packet { + uint8_t data[REM_MAX_TOTAL_PACKET_SIZE_SX1280]; +} Wrapper_REM_Packet; + +Wrapper_REM_Packet nonpriority_queue_robots[MAX_NUMBER_OF_ROBOTS][40]; +Wrapper_REM_Packet nonpriority_queue_pc[40]; +Wrapper_REM_Packet nonpriority_queue_bs[40]; + + + +// uint8_t nonpriority_queue_robots[MAX_NUMBER_OF_ROBOTS][40][REM_MAX_TOTAL_PACKET_SIZE_SX1280]; +// uint8_t nonpriority_queue_pc[40][REM_MAX_TOTAL_PACKET_SIZE_SX1280]; +// uint8_t nonpriority_queue_bs[40][REM_MAX_TOTAL_PACKET_SIZE_SX1280]; -struct _buffer_RobotMusicCommand { - REM_RobotMusicCommandPayload packet; - bool isNewPacket; - uint32_t counter; -}; -struct _buffer_RobotMusicCommand buffer_RobotMusicCommand[MAX_NUMBER_OF_ROBOTS]; #endif // __MSG_BUFF_STATUS_H diff --git a/Core/Inc/Wireless/Wireless.h b/Core/Inc/Wireless/Wireless.h index feed47c..5c8e896 100644 --- a/Core/Inc/Wireless/Wireless.h +++ b/Core/Inc/Wireless/Wireless.h @@ -25,10 +25,10 @@ */ // These frequencies are not between the 2.4 and 2.5, but far below the 2.4 wifi to prevent interference -#define WIRELESS_CHANNEL_YELLOW_ROBOT_TO_BASESTATION -10 -#define WIRELESS_CHANNEL_BLUE_ROBOT_TO_BASESTATION -11.5 -#define WIRELESS_CHANNEL_YELLOW_BASESTATION_TO_ROBOT -13 -#define WIRELESS_CHANNEL_BLUE_BASESTATION_TO_ROBOT -14.5 +#define WIRELESS_CHANNEL_YELLOW_ROBOT_TO_BASESTATION 86 +#define WIRELESS_CHANNEL_BLUE_ROBOT_TO_BASESTATION 87 +#define WIRELESS_CHANNEL_YELLOW_BASESTATION_TO_ROBOT 88 +#define WIRELESS_CHANNEL_BLUE_BASESTATION_TO_ROBOT 89 #define WIRELESS_CHANNEL_DEFAULT_ROBOT_TO_BASESTATION WIRELESS_CHANNEL_YELLOW_ROBOT_TO_BASESTATION #define WIRELESS_CHANNEL_DEFAULT_BASESTATION_TO_ROBOT WIRELESS_CHANNEL_YELLOW_BASESTATION_TO_ROBOT diff --git a/Core/Inc/basestation.h b/Core/Inc/basestation.h index 105bd65..9a57ed9 100644 --- a/Core/Inc/basestation.h +++ b/Core/Inc/basestation.h @@ -4,6 +4,8 @@ #include #include #include "FT812Q.h" +#include "REM_BaseTypes.h" +#include "REM_BasestationConfiguration.h" #define MAX_NUMBER_OF_ROBOTS 16 #define MAX_ROBOT_ID (MAX_NUMBER_OF_ROBOTS-1) @@ -13,7 +15,9 @@ void init(); void loop(); +bool handleREM_BasestationGetConfiguration(); +bool handleREM_BasestationConfiguration(REM_BasestationConfigurationPayload* payload); void updateTouchState(TouchState* touchState); -bool handlePacket(uint8_t* packet, uint32_t packet_length); +bool handlePackets(uint8_t* packets_buffer, uint32_t packets_buffer_length); #endif /* __BASESTATION_H */ \ No newline at end of file diff --git a/Core/Inc/logging.h b/Core/Inc/logging.h index 2d80eee..e128c09 100644 --- a/Core/Inc/logging.h +++ b/Core/Inc/logging.h @@ -74,6 +74,11 @@ void LOG_sendBlocking(uint8_t* data, uint8_t length); */ bool LOG_canAddLog(); +/** + * @brief Send a buffer over USB, possibly blocking + */ +bool LOG_sendBuffer(uint8_t* data, uint32_t length, bool blocking); + /** * @brief Send all messages in the buffer using a blocking while-loop */ diff --git a/Core/Inc/main.h b/Core/Inc/main.h index 8dbaf77..46e04b2 100644 --- a/Core/Inc/main.h +++ b/Core/Inc/main.h @@ -114,5 +114,3 @@ void Error_Handler(void); #endif #endif /* __MAIN_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/roboteam_embedded_messages b/Core/Inc/roboteam_embedded_messages index 59f9450..27e6961 160000 --- a/Core/Inc/roboteam_embedded_messages +++ b/Core/Inc/roboteam_embedded_messages @@ -1 +1 @@ -Subproject commit 59f945049c9b2eadd2011227533aab5fdae56831 +Subproject commit 27e696188df37742fb24f938496d09348fa77423 diff --git a/Core/Inc/stm32f7xx_hal_conf.h b/Core/Inc/stm32f7xx_hal_conf.h index 8e88f0e..d0d63e6 100644 --- a/Core/Inc/stm32f7xx_hal_conf.h +++ b/Core/Inc/stm32f7xx_hal_conf.h @@ -1,3 +1,4 @@ +/* USER CODE BEGIN Header */ /** ****************************************************************************** * @file stm32f7xx_hal_conf_template.h @@ -8,16 +9,16 @@ ****************************************************************************** * @attention * - *

© Copyright (c) 2017 STMicroelectronics. - * All rights reserved.

+ * Copyright (c) 2017 STMicroelectronics. + * All rights reserved. * - * This software component is licensed by ST under BSD 3-Clause license, - * the "License"; You may not use this file except in compliance with the - * License. You may obtain a copy of the License at: - * opensource.org/licenses/BSD-3-Clause + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ +/* USER CODE END Header */ /* Define to prevent recursive inclusion -------------------------------------*/ #ifndef __STM32F7xx_HAL_CONF_H @@ -52,14 +53,14 @@ /* #define HAL_SDRAM_MODULE_ENABLED */ /* #define HAL_HASH_MODULE_ENABLED */ /* #define HAL_I2S_MODULE_ENABLED */ -/* #define HAL_IWDG_MODULE_ENABLED */ +#define HAL_IWDG_MODULE_ENABLED /* #define HAL_LPTIM_MODULE_ENABLED */ /* #define HAL_LTDC_MODULE_ENABLED */ #define HAL_QSPI_MODULE_ENABLED /* #define HAL_RNG_MODULE_ENABLED */ /* #define HAL_RTC_MODULE_ENABLED */ /* #define HAL_SAI_MODULE_ENABLED */ -/* #define HAL_SD_MODULE_ENABLED */ +#define HAL_SD_MODULE_ENABLED /* #define HAL_MMC_MODULE_ENABLED */ /* #define HAL_SPDIFRX_MODULE_ENABLED */ #define HAL_SPI_MODULE_ENABLED @@ -144,7 +145,7 @@ /** * @brief This is the HAL system configuration section */ -#define VDD_VALUE ((uint32_t)3300U) /*!< Value of VDD in mv */ +#define VDD_VALUE 3300U /*!< Value of VDD in mv */ #define TICK_INT_PRIORITY ((uint32_t)0U) /*!< tick interrupt priority */ #define USE_RTOS 0U #define PREFETCH_ENABLE 1U @@ -477,4 +478,3 @@ #endif /* __STM32F7xx_HAL_CONF_H */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Inc/stm32f7xx_it.h b/Core/Inc/stm32f7xx_it.h index 0c80fd3..852ccdc 100644 --- a/Core/Inc/stm32f7xx_it.h +++ b/Core/Inc/stm32f7xx_it.h @@ -58,10 +58,17 @@ void PendSV_Handler(void); void SysTick_Handler(void); void EXTI3_IRQHandler(void); void EXTI4_IRQHandler(void); +void DMA1_Stream1_IRQHandler(void); +void DMA1_Stream4_IRQHandler(void); +void DMA1_Stream6_IRQHandler(void); void TIM1_UP_TIM10_IRQHandler(void); void SPI2_IRQHandler(void); void EXTI15_10_IRQHandler(void); +void SDMMC1_IRQHandler(void); +void DMA2_Stream0_IRQHandler(void); +void DMA2_Stream1_IRQHandler(void); void DMA2_Stream2_IRQHandler(void); +void DMA2_Stream3_IRQHandler(void); void OTG_HS_IRQHandler(void); void SPI4_IRQHandler(void); void QUADSPI_IRQHandler(void); @@ -74,5 +81,3 @@ void QUADSPI_IRQHandler(void); #endif #endif /* __STM32F7xx_IT_H */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/FATFS/App/fatfs.c b/Core/Src/FATFS/App/fatfs.c new file mode 100644 index 0000000..04eb636 --- /dev/null +++ b/Core/Src/FATFS/App/fatfs.c @@ -0,0 +1,54 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fatfs.c + * @brief Code for fatfs applications + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +#include "fatfs.h" + +uint8_t retSD; /* Return value for SD */ +char SDPath[4]; /* SD logical drive path */ +FATFS SDFatFS; /* File system object for SD logical drive */ +FIL SDFile; /* File object for SD */ + +/* USER CODE BEGIN Variables */ + +/* USER CODE END Variables */ + +void MX_FATFS_Init(void) +{ + /*## FatFS: Link the SD driver ###########################*/ + retSD = FATFS_LinkDriver(&SD_Driver, SDPath); + + /* USER CODE BEGIN Init */ + /* additional user code for init */ + /* USER CODE END Init */ +} + +/** + * @brief Gets Time from RTC + * @param None + * @retval Time in DWORD + */ +DWORD get_fattime(void) +{ + /* USER CODE BEGIN get_fattime */ + return 0; + /* USER CODE END get_fattime */ +} + +/* USER CODE BEGIN Application */ + +/* USER CODE END Application */ diff --git a/Core/Src/FATFS/Target/bsp_driver_sd.c b/Core/Src/FATFS/Target/bsp_driver_sd.c new file mode 100644 index 0000000..7c1189e --- /dev/null +++ b/Core/Src/FATFS/Target/bsp_driver_sd.c @@ -0,0 +1,314 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file bsp_driver_sd.c for F7 (based on stm32756g_eval_sd.c) + * @brief This file includes a generic uSD card driver. + * To be completed by the user according to the board used for the project. + * @note Some functions generated as weak: they can be overridden by + * - code in user files + * - or BSP code from the FW pack files + * if such files are added to the generated project (by the user). + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifdef OLD_API +/* kept to avoid issue when migrating old projects. */ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +#else +/* USER CODE BEGIN FirstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END FirstSection */ +/* Includes ------------------------------------------------------------------*/ +#include "bsp_driver_sd.h" + +/* Extern variables ---------------------------------------------------------*/ + +extern SD_HandleTypeDef hsd1; + +/* USER CODE BEGIN BeforeInitSection */ +/* can be used to modify / undefine following code or add code */ +/* USER CODE END BeforeInitSection */ +/** + * @brief Initializes the SD card device. + * @retval SD status + */ +__weak uint8_t BSP_SD_Init(void) +{ + uint8_t sd_state = MSD_OK; + /* Check if the SD card is plugged in the slot */ + if (BSP_SD_IsDetected() != SD_PRESENT) + { + return MSD_ERROR_SD_NOT_PRESENT; + } + /* HAL SD initialization */ + sd_state = HAL_SD_Init(&hsd1); + /* Configure SD Bus width (4 bits mode selected) */ + if (sd_state == MSD_OK) + { + /* Enable wide operation */ + if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK) + { + sd_state = MSD_ERROR; + } + } + + return sd_state; +} +/* USER CODE BEGIN AfterInitSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END AfterInitSection */ + +/* USER CODE BEGIN InterruptMode */ +/** + * @brief Configures Interrupt mode for SD detection pin. + * @retval Returns 0 + */ +__weak uint8_t BSP_SD_ITConfig(void) +{ + /* Code to be updated by the user or replaced by one from the FW pack (in a stmxxxx_sd.c file) */ + + return (uint8_t)0; +} + +/* USER CODE END InterruptMode */ + +/* USER CODE BEGIN BeforeReadBlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeReadBlocksSection */ +/** + * @brief Reads block(s) from a specified address in an SD card, in polling mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param ReadAddr: Address from where data is to be read + * @param NumOfBlocks: Number of SD blocks to read + * @param Timeout: Timeout for read operation + * @retval SD status + */ +__weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_ReadBlocks(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeWriteBlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeWriteBlocksSection */ +/** + * @brief Writes block(s) to a specified address in an SD card, in polling mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param WriteAddr: Address from where data is to be written + * @param NumOfBlocks: Number of SD blocks to write + * @param Timeout: Timeout for write operation + * @retval SD status + */ +__weak uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_WriteBlocks(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeReadDMABlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeReadDMABlocksSection */ +/** + * @brief Reads block(s) from a specified address in an SD card, in DMA mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param ReadAddr: Address from where data is to be read + * @param NumOfBlocks: Number of SD blocks to read + * @retval SD status + */ +__weak uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks) +{ + uint8_t sd_state = MSD_OK; + + /* Read block(s) in DMA transfer mode */ + if (HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeWriteDMABlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeWriteDMABlocksSection */ +/** + * @brief Writes block(s) to a specified address in an SD card, in DMA mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param WriteAddr: Address from where data is to be written + * @param NumOfBlocks: Number of SD blocks to write + * @retval SD status + */ +__weak uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks) +{ + uint8_t sd_state = MSD_OK; + + /* Write block(s) in DMA transfer mode */ + if (HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeEraseSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeEraseSection */ +/** + * @brief Erases the specified memory area of the given SD card. + * @param StartAddr: Start byte address + * @param EndAddr: End byte address + * @retval SD status + */ +__weak uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_Erase(&hsd1, StartAddr, EndAddr) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeGetCardStateSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeGetCardStateSection */ + +/** + * @brief Gets the current SD card data status. + * @param None + * @retval Data transfer state. + * This value can be one of the following values: + * @arg SD_TRANSFER_OK: No data transfer is acting + * @arg SD_TRANSFER_BUSY: Data transfer is acting + */ +__weak uint8_t BSP_SD_GetCardState(void) +{ + return ((HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER ) ? SD_TRANSFER_OK : SD_TRANSFER_BUSY); +} + +/** + * @brief Get SD information about specific SD card. + * @param CardInfo: Pointer to HAL_SD_CardInfoTypedef structure + * @retval None + */ +__weak void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo) +{ + /* Get SD card Information */ + HAL_SD_GetCardInfo(&hsd1, CardInfo); +} + +/* USER CODE BEGIN BeforeCallBacksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeCallBacksSection */ +/** + * @brief SD Abort callbacks + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_AbortCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_AbortCallback(); +} + +/** + * @brief Tx Transfer completed callback + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_WriteCpltCallback(); +} + +/** + * @brief Rx Transfer completed callback + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_ReadCpltCallback(); +} + +/* USER CODE BEGIN CallBacksSection_C */ +/** + * @brief BSP SD Abort callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_AbortCallback(void) +{ + +} + +/** + * @brief BSP Tx Transfer completed callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_WriteCpltCallback(void) +{ + +} + +/** + * @brief BSP Rx Transfer completed callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_ReadCpltCallback(void) +{ + +} +/* USER CODE END CallBacksSection_C */ +#endif + +/** + * @brief Detects if SD card is correctly plugged in the memory slot or not. + * @param None + * @retval Returns if SD is detected or not + */ +__weak uint8_t BSP_SD_IsDetected(void) +{ + __IO uint8_t status = SD_PRESENT; + + if (BSP_PlatformIsDetected() == 0x0) + { + status = SD_NOT_PRESENT; + } + + return status; +} + +/* USER CODE BEGIN AdditionalCode */ +/* user code can be inserted here */ +/* USER CODE END AdditionalCode */ diff --git a/Core/Src/FATFS/Target/fatfs_platform.c b/Core/Src/FATFS/Target/fatfs_platform.c new file mode 100644 index 0000000..dded63d --- /dev/null +++ b/Core/Src/FATFS/Target/fatfs_platform.c @@ -0,0 +1,32 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : fatfs_platform.c + * @brief : fatfs_platform source file + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** +*/ +/* USER CODE END Header */ +#include "fatfs_platform.h" + +uint8_t BSP_PlatformIsDetected(void) { + uint8_t status = SD_PRESENT; + /* Check SD card detect pin */ + if(HAL_GPIO_ReadPin(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != GPIO_PIN_RESET) + { + status = SD_NOT_PRESENT; + } + /* USER CODE BEGIN 1 */ + /* user code can be inserted here */ + /* USER CODE END 1 */ + return status; +} diff --git a/Core/Src/FATFS/Target/sd_diskio.c b/Core/Src/FATFS/Target/sd_diskio.c new file mode 100644 index 0000000..e72005f --- /dev/null +++ b/Core/Src/FATFS/Target/sd_diskio.c @@ -0,0 +1,519 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file sd_diskio.c + * @brief SD Disk I/O driver + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Note: code generation based on sd_diskio_dma_template_bspv1.c v2.1.4 + as "Use dma template" is enabled. */ + +/* USER CODE BEGIN firstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END firstSection*/ + +/* Includes ------------------------------------------------------------------*/ +#include "ff_gen_drv.h" +#include "sd_diskio.h" + +#include + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + + /* + * the following Timeout is useful to give the control back to the applications + * in case of errors in either BSP_SD_ReadCpltCallback() or BSP_SD_WriteCpltCallback() + * the value by default is as defined in the BSP platform driver otherwise 30 secs + */ +#define SD_TIMEOUT 30 * 1000 + +#define SD_DEFAULT_BLOCK_SIZE 512 + +/* + * Depending on the use case, the SD card initialization could be done at the + * application level: if it is the case define the flag below to disable + * the BSP_SD_Init() call in the SD_Initialize() and add a call to + * BSP_SD_Init() elsewhere in the application. + */ +/* USER CODE BEGIN disableSDInit */ +/* #define DISABLE_SD_INIT */ +/* USER CODE END disableSDInit */ + +/* + * when using cacheable memory region, it may be needed to maintain the cache + * validity. Enable the define below to activate a cache maintenance at each + * read and write operation. + * Notice: This is applicable only for cortex M7 based platform. + */ +/* USER CODE BEGIN enableSDDmaCacheMaintenance */ +/* #define ENABLE_SD_DMA_CACHE_MAINTENANCE 1 */ +/* USER CODE END enableSDDmaCacheMaintenance */ + +/* +* Some DMA requires 4-Byte aligned address buffer to correctly read/write data, +* in FatFs some accesses aren't thus we need a 4-byte aligned scratch buffer to correctly +* transfer data +*/ +/* USER CODE BEGIN enableScratchBuffer */ +/* #define ENABLE_SCRATCH_BUFFER */ +/* USER CODE END enableScratchBuffer */ + +/* Private variables ---------------------------------------------------------*/ +#if defined(ENABLE_SCRATCH_BUFFER) +#if defined (ENABLE_SD_DMA_CACHE_MAINTENANCE) +ALIGN_32BYTES(static uint8_t scratch[BLOCKSIZE]); // 32-Byte aligned for cache maintenance +#else +__ALIGN_BEGIN static uint8_t scratch[BLOCKSIZE] __ALIGN_END; +#endif +#endif +/* Disk status */ +static volatile DSTATUS Stat = STA_NOINIT; + +static volatile UINT WriteStatus = 0, ReadStatus = 0; +/* Private function prototypes -----------------------------------------------*/ +static DSTATUS SD_CheckStatus(BYTE lun); +DSTATUS SD_initialize (BYTE); +DSTATUS SD_status (BYTE); +DRESULT SD_read (BYTE, BYTE*, DWORD, UINT); +#if _USE_WRITE == 1 +DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT); +#endif /* _USE_WRITE == 1 */ +#if _USE_IOCTL == 1 +DRESULT SD_ioctl (BYTE, BYTE, void*); +#endif /* _USE_IOCTL == 1 */ + +const Diskio_drvTypeDef SD_Driver = +{ + SD_initialize, + SD_status, + SD_read, +#if _USE_WRITE == 1 + SD_write, +#endif /* _USE_WRITE == 1 */ + +#if _USE_IOCTL == 1 + SD_ioctl, +#endif /* _USE_IOCTL == 1 */ +}; + +/* USER CODE BEGIN beforeFunctionSection */ +/* can be used to modify / undefine following code or add new code */ +/* USER CODE END beforeFunctionSection */ + +/* Private functions ---------------------------------------------------------*/ + +static int SD_CheckStatusWithTimeout(uint32_t timeout) +{ + uint32_t timer = HAL_GetTick(); + /* block until SDIO IP is ready again or a timeout occur */ + while(HAL_GetTick() - timer < timeout) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + return 0; + } + } + + return -1; +} + +static DSTATUS SD_CheckStatus(BYTE lun) +{ + Stat = STA_NOINIT; + + if(BSP_SD_GetCardState() == MSD_OK) + { + Stat &= ~STA_NOINIT; + } + + return Stat; +} + +/** + * @brief Initializes a Drive + * @param lun : not used + * @retval DSTATUS: Operation status + */ +DSTATUS SD_initialize(BYTE lun) +{ + +#if !defined(DISABLE_SD_INIT) + + if(BSP_SD_Init() == MSD_OK) + { + Stat = SD_CheckStatus(lun); + } + +#else + Stat = SD_CheckStatus(lun); +#endif + + return Stat; +} + +/** + * @brief Gets Disk Status + * @param lun : not used + * @retval DSTATUS: Operation status + */ +DSTATUS SD_status(BYTE lun) +{ + return SD_CheckStatus(lun); +} + +/* USER CODE BEGIN beforeReadSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeReadSection */ +/** + * @brief Reads Sector(s) + * @param lun : not used + * @param *buff: Data buffer to store read data + * @param sector: Sector address (LBA) + * @param count: Number of sectors to read (1..128) + * @retval DRESULT: Operation result + */ + +DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count) +{ + DRESULT res = RES_ERROR; + uint32_t timeout; +#if defined(ENABLE_SCRATCH_BUFFER) + uint8_t ret; +#endif +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + uint32_t alignedAddr; +#endif + + /* + * ensure the SDCard is ready for a new operation + */ + + if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) + { + return res; + } + +#if defined(ENABLE_SCRATCH_BUFFER) + if (!((uint32_t)buff & 0x3)) + { +#endif + if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff, + (uint32_t) (sector), + count) == MSD_OK) + { + ReadStatus = 0; + /* Wait that the reading process is completed or a timeout occurs */ + timeout = HAL_GetTick(); + while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + /* in case of a timeout return error */ + if (ReadStatus == 0) + { + res = RES_ERROR; + } + else + { + ReadStatus = 0; + timeout = HAL_GetTick(); + + while((HAL_GetTick() - timeout) < SD_TIMEOUT) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + res = RES_OK; +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address, + adjust the address and the D-Cache size to invalidate accordingly. + */ + alignedAddr = (uint32_t)buff & ~0x1F; + SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr)); +#endif + break; + } + } + } + } +#if defined(ENABLE_SCRATCH_BUFFER) + } + else + { + /* Slow path, fetch each sector a part and memcpy to destination buffer */ + int i; + + for (i = 0; i < count; i++) { + ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1); + if (ret == MSD_OK) { + /* wait until the read is successful or a timeout occurs */ + + timeout = HAL_GetTick(); + while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + if (ReadStatus == 0) + { + res = RES_ERROR; + break; + } + ReadStatus = 0; + +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + * + * invalidate the scratch buffer before the next read to get the actual data instead of the cached one + */ + SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE); +#endif + memcpy(buff, scratch, BLOCKSIZE); + buff += BLOCKSIZE; + } + else + { + break; + } + } + + if ((i == count) && (ret == MSD_OK)) + res = RES_OK; + } +#endif + + return res; +} + +/* USER CODE BEGIN beforeWriteSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeWriteSection */ +/** + * @brief Writes Sector(s) + * @param lun : not used + * @param *buff: Data to be written + * @param sector: Sector address (LBA) + * @param count: Number of sectors to write (1..128) + * @retval DRESULT: Operation result + */ +#if _USE_WRITE == 1 + +DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count) +{ + DRESULT res = RES_ERROR; + uint32_t timeout; +#if defined(ENABLE_SCRATCH_BUFFER) + uint8_t ret; + int i; +#endif + + WriteStatus = 0; +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + uint32_t alignedAddr; +#endif + + if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) + { + return res; + } + +#if defined(ENABLE_SCRATCH_BUFFER) + if (!((uint32_t)buff & 0x3)) + { +#endif +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + + /* + the SCB_CleanDCache_by_Addr() requires a 32-Byte aligned address + adjust the address and the D-Cache size to clean accordingly. + */ + alignedAddr = (uint32_t)buff & ~0x1F; + SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr)); +#endif + + if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff, + (uint32_t)(sector), + count) == MSD_OK) + { + /* Wait that writing process is completed or a timeout occurs */ + + timeout = HAL_GetTick(); + while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + /* in case of a timeout return error */ + if (WriteStatus == 0) + { + res = RES_ERROR; + } + else + { + WriteStatus = 0; + timeout = HAL_GetTick(); + + while((HAL_GetTick() - timeout) < SD_TIMEOUT) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + res = RES_OK; + break; + } + } + } + } +#if defined(ENABLE_SCRATCH_BUFFER) + } + else + { + /* Slow path, fetch each sector a part and memcpy to destination buffer */ +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + * invalidate the scratch buffer before the next write to get the actual data instead of the cached one + */ + SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE); +#endif + + for (i = 0; i < count; i++) + { + WriteStatus = 0; + + memcpy((void *)scratch, (void *)buff, BLOCKSIZE); + buff += BLOCKSIZE; + + ret = BSP_SD_WriteBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1); + if (ret == MSD_OK) { + /* wait for a message from the queue or a timeout */ + timeout = HAL_GetTick(); + while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + if (WriteStatus == 0) + { + break; + } + + } + else + { + break; + } + } + if ((i == count) && (ret == MSD_OK)) + res = RES_OK; + } +#endif + return res; +} +#endif /* _USE_WRITE == 1 */ + +/* USER CODE BEGIN beforeIoctlSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeIoctlSection */ +/** + * @brief I/O control operation + * @param lun : not used + * @param cmd: Control code + * @param *buff: Buffer to send/receive control data + * @retval DRESULT: Operation result + */ +#if _USE_IOCTL == 1 +DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff) +{ + DRESULT res = RES_ERROR; + BSP_SD_CardInfo CardInfo; + + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (cmd) + { + /* Make sure that no pending write process */ + case CTRL_SYNC : + res = RES_OK; + break; + + /* Get number of sectors on the disk (DWORD) */ + case GET_SECTOR_COUNT : + BSP_SD_GetCardInfo(&CardInfo); + *(DWORD*)buff = CardInfo.LogBlockNbr; + res = RES_OK; + break; + + /* Get R/W sector size (WORD) */ + case GET_SECTOR_SIZE : + BSP_SD_GetCardInfo(&CardInfo); + *(WORD*)buff = CardInfo.LogBlockSize; + res = RES_OK; + break; + + /* Get erase block size in unit of sector (DWORD) */ + case GET_BLOCK_SIZE : + BSP_SD_GetCardInfo(&CardInfo); + *(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + return res; +} +#endif /* _USE_IOCTL == 1 */ + +/* USER CODE BEGIN afterIoctlSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END afterIoctlSection */ + +/* USER CODE BEGIN callbackSection */ +/* can be used to modify / following code or add new code */ +/* USER CODE END callbackSection */ +/** + * @brief Tx Transfer completed callbacks + * @param hsd: SD handle + * @retval None + */ +void BSP_SD_WriteCpltCallback(void) +{ + + WriteStatus = 1; +} + +/** + * @brief Rx Transfer completed callbacks + * @param hsd: SD handle + * @retval None + */ +void BSP_SD_ReadCpltCallback(void) +{ + ReadStatus = 1; +} + +/* USER CODE BEGIN ErrorAbortCallbacks */ +/* +============================================================================================== + depending on the SD_HAL_Driver version, either the HAL_SD_ErrorCallback() or HAL_SD_AbortCallback() + or both could be defined, activate the callbacks below when suitable and needed +============================================================================================== +void BSP_SD_AbortCallback(void) +{ +} + +void BSP_SD_ErrorCallback(void) +{ +} +*/ +/* USER CODE END ErrorAbortCallbacks */ + +/* USER CODE BEGIN lastSection */ +/* can be used to modify / undefine previous code or add new code */ +/* USER CODE END lastSection */ diff --git a/Core/Src/FT812Q/FT812Q_Drawing.c b/Core/Src/FT812Q/FT812Q_Drawing.c index ce120ae..c50baae 100644 --- a/Core/Src/FT812Q/FT812Q_Drawing.c +++ b/Core/Src/FT812Q/FT812Q_Drawing.c @@ -93,7 +93,8 @@ void drawBasestation(bool USBstatus){ } uint32_t drawRobotInfo(uint8_t id, bool USBstatus){ - REM_RobotFeedbackPayload *rfp = &buffer_RobotFeedback[id].packet; + return 0; + REM_RobotFeedbackPayload *rfp = NULL;//&buffer_RobotFeedback[id].packet; // /* CALCULATE DATA */ float angle = REM_RobotFeedback_get_angle(rfp); diff --git a/Core/Src/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c b/Core/Src/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c deleted file mode 100644 index f89a34d..0000000 --- a/Core/Src/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Src/usbd_cdc.c +++ /dev/null @@ -1,935 +0,0 @@ -/** - ****************************************************************************** - * @file usbd_cdc.c - * @author MCD Application Team - * @brief This file provides the high layer firmware functions to manage the - * following functionalities of the USB CDC Class: - * - Initialization and Configuration of high and low layer - * - Enumeration as CDC Device (and enumeration for each implemented memory interface) - * - OUT/IN data transfer - * - Command IN transfer (class requests management) - * - Error management - * - * @verbatim - * - * =================================================================== - * CDC Class Driver Description - * =================================================================== - * This driver manages the "Universal Serial Bus Class Definitions for Communications Devices - * Revision 1.2 November 16, 2007" and the sub-protocol specification of "Universal Serial Bus - * Communications Class Subclass Specification for PSTN Devices Revision 1.2 February 9, 2007" - * This driver implements the following aspects of the specification: - * - Device descriptor management - * - Configuration descriptor management - * - Enumeration as CDC device with 2 data endpoints (IN and OUT) and 1 command endpoint (IN) - * - Requests management (as described in section 6.2 in specification) - * - Abstract Control Model compliant - * - Union Functional collection (using 1 IN endpoint for control) - * - Data interface class - * - * These aspects may be enriched or modified for a specific user application. - * - * This driver doesn't implement the following aspects of the specification - * (but it is possible to manage these features with some modifications on this driver): - * - Any class-specific aspect relative to communication classes should be managed by user application. - * - All communication classes other than PSTN are not managed - * - * @endverbatim - * - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2015 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ - -/* BSPDependencies -- "stm32xxxxx_{eval}{discovery}{nucleo_144}.c" -- "stm32xxxxx_{eval}{discovery}_io.c" -EndBSPDependencies */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_cdc.h" -#include "usbd_ctlreq.h" - - -/** @addtogroup STM32_USB_DEVICE_LIBRARY - * @{ - */ - - -/** @defgroup USBD_CDC - * @brief usbd core module - * @{ - */ - -/** @defgroup USBD_CDC_Private_TypesDefinitions - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_CDC_Private_Defines - * @{ - */ -/** - * @} - */ - - -/** @defgroup USBD_CDC_Private_Macros - * @{ - */ - -/** - * @} - */ - - -/** @defgroup USBD_CDC_Private_FunctionPrototypes - * @{ - */ - -static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); -static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); -static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); -static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); -static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); -static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev); - -static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length); -static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length); -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length); -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length); - -/* USB Standard Device Descriptor */ -__ALIGN_BEGIN static uint8_t USBD_CDC_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = -{ - USB_LEN_DEV_QUALIFIER_DESC, - USB_DESC_TYPE_DEVICE_QUALIFIER, - 0x00, - 0x02, - 0x00, - 0x00, - 0x00, - 0x40, - 0x01, - 0x00, -}; - -/** - * @} - */ - -/** @defgroup USBD_CDC_Private_Variables - * @{ - */ - - -/* CDC interface class callbacks structure */ -USBD_ClassTypeDef USBD_CDC = -{ - USBD_CDC_Init, - USBD_CDC_DeInit, - USBD_CDC_Setup, - NULL, /* EP0_TxSent, */ - USBD_CDC_EP0_RxReady, - USBD_CDC_DataIn, - USBD_CDC_DataOut, - NULL, - NULL, - NULL, - USBD_CDC_GetHSCfgDesc, - USBD_CDC_GetFSCfgDesc, - USBD_CDC_GetOtherSpeedCfgDesc, - USBD_CDC_GetDeviceQualifierDescriptor, -}; - -/* USB CDC device Configuration Descriptor */ -__ALIGN_BEGIN static uint8_t USBD_CDC_CfgHSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = -{ - /* Configuration Descriptor */ - 0x09, /* bLength: Configuration Descriptor size */ - USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ - USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */ - 0x00, - 0x02, /* bNumInterfaces: 2 interface */ - 0x01, /* bConfigurationValue: Configuration value */ - 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ - 0xC0, /* bmAttributes: self powered */ - 0x32, /* MaxPower 0 mA */ - - /*---------------------------------------------------------------------------*/ - - /* Interface Descriptor */ - 0x09, /* bLength: Interface Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ - /* Interface descriptor type */ - 0x00, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x01, /* bNumEndpoints: One endpoints used */ - 0x02, /* bInterfaceClass: Communication Interface Class */ - 0x02, /* bInterfaceSubClass: Abstract Control Model */ - 0x01, /* bInterfaceProtocol: Common AT commands */ - 0x00, /* iInterface: */ - - /* Header Functional Descriptor */ - 0x05, /* bLength: Endpoint Descriptor size */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x00, /* bDescriptorSubtype: Header Func Desc */ - 0x10, /* bcdCDC: spec release number */ - 0x01, - - /* Call Management Functional Descriptor */ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x01, /* bDescriptorSubtype: Call Management Func Desc */ - 0x00, /* bmCapabilities: D0+D1 */ - 0x01, /* bDataInterface: 1 */ - - /* ACM Functional Descriptor */ - 0x04, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ - 0x02, /* bmCapabilities */ - - /* Union Functional Descriptor */ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x06, /* bDescriptorSubtype: Union func desc */ - 0x00, /* bMasterInterface: Communication class interface */ - 0x01, /* bSlaveInterface0: Data Class Interface */ - - /* Endpoint 2 Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_CMD_EP, /* bEndpointAddress */ - 0x03, /* bmAttributes: Interrupt */ - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_CMD_PACKET_SIZE), - CDC_HS_BINTERVAL, /* bInterval: */ - /*---------------------------------------------------------------------------*/ - - /* Data class interface descriptor */ - 0x09, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ - 0x01, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x02, /* bNumEndpoints: Two endpoints used */ - 0x0A, /* bInterfaceClass: CDC */ - 0x00, /* bInterfaceSubClass: */ - 0x00, /* bInterfaceProtocol: */ - 0x00, /* iInterface: */ - - /* Endpoint OUT Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_OUT_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), - 0x00, /* bInterval: ignore for Bulk transfer */ - - /* Endpoint IN Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_IN_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - LOBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_DATA_HS_MAX_PACKET_SIZE), - 0x00 /* bInterval: ignore for Bulk transfer */ -}; - - -/* USB CDC device Configuration Descriptor */ -__ALIGN_BEGIN static uint8_t USBD_CDC_CfgFSDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = -{ - /* Configuration Descriptor */ - 0x09, /* bLength: Configuration Descriptor size */ - USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ - USB_CDC_CONFIG_DESC_SIZ, /* wTotalLength:no of returned bytes */ - 0x00, - 0x02, /* bNumInterfaces: 2 interface */ - 0x01, /* bConfigurationValue: Configuration value */ - 0x00, /* iConfiguration: Index of string descriptor describing the configuration */ - 0xC0, /* bmAttributes: self powered */ - 0x32, /* MaxPower 0 mA */ - - /*---------------------------------------------------------------------------*/ - - /* Interface Descriptor */ - 0x09, /* bLength: Interface Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ - /* Interface descriptor type */ - 0x00, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x01, /* bNumEndpoints: One endpoints used */ - 0x02, /* bInterfaceClass: Communication Interface Class */ - 0x02, /* bInterfaceSubClass: Abstract Control Model */ - 0x01, /* bInterfaceProtocol: Common AT commands */ - 0x00, /* iInterface: */ - - /* Header Functional Descriptor */ - 0x05, /* bLength: Endpoint Descriptor size */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x00, /* bDescriptorSubtype: Header Func Desc */ - 0x10, /* bcdCDC: spec release number */ - 0x01, - - /* Call Management Functional Descriptor */ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x01, /* bDescriptorSubtype: Call Management Func Desc */ - 0x00, /* bmCapabilities: D0+D1 */ - 0x01, /* bDataInterface: 1 */ - - /* ACM Functional Descriptor */ - 0x04, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ - 0x02, /* bmCapabilities */ - - /* Union Functional Descriptor */ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x06, /* bDescriptorSubtype: Union func desc */ - 0x00, /* bMasterInterface: Communication class interface */ - 0x01, /* bSlaveInterface0: Data Class Interface */ - - /* Endpoint 2 Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_CMD_EP, /* bEndpointAddress */ - 0x03, /* bmAttributes: Interrupt */ - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_CMD_PACKET_SIZE), - CDC_FS_BINTERVAL, /* bInterval: */ - /*---------------------------------------------------------------------------*/ - - /* Data class interface descriptor */ - 0x09, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ - 0x01, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x02, /* bNumEndpoints: Two endpoints used */ - 0x0A, /* bInterfaceClass: CDC */ - 0x00, /* bInterfaceSubClass: */ - 0x00, /* bInterfaceProtocol: */ - 0x00, /* iInterface: */ - - /* Endpoint OUT Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_OUT_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), - 0x00, /* bInterval: ignore for Bulk transfer */ - - /* Endpoint IN Descriptor */ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_IN_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - LOBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_DATA_FS_MAX_PACKET_SIZE), - 0x00 /* bInterval: ignore for Bulk transfer */ -}; - -__ALIGN_BEGIN static uint8_t USBD_CDC_OtherSpeedCfgDesc[USB_CDC_CONFIG_DESC_SIZ] __ALIGN_END = -{ - 0x09, /* bLength: Configuation Descriptor size */ - USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION, - USB_CDC_CONFIG_DESC_SIZ, - 0x00, - 0x02, /* bNumInterfaces: 2 interfaces */ - 0x01, /* bConfigurationValue: */ - 0x04, /* iConfiguration: */ - 0xC0, /* bmAttributes: */ - 0x32, /* MaxPower 100 mA */ - - /*Interface Descriptor */ - 0x09, /* bLength: Interface Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: Interface */ - /* Interface descriptor type */ - 0x00, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x01, /* bNumEndpoints: One endpoints used */ - 0x02, /* bInterfaceClass: Communication Interface Class */ - 0x02, /* bInterfaceSubClass: Abstract Control Model */ - 0x01, /* bInterfaceProtocol: Common AT commands */ - 0x00, /* iInterface: */ - - /* Header Functional Descriptor */ - 0x05, /* bLength: Endpoint Descriptor size */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x00, /* bDescriptorSubtype: Header Func Desc */ - 0x10, /* bcdCDC: spec release number */ - 0x01, - - /*Call Management Functional Descriptor*/ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x01, /* bDescriptorSubtype: Call Management Func Desc */ - 0x00, /* bmCapabilities: D0+D1 */ - 0x01, /* bDataInterface: 1 */ - - /*ACM Functional Descriptor*/ - 0x04, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x02, /* bDescriptorSubtype: Abstract Control Management desc */ - 0x02, /* bmCapabilities */ - - /*Union Functional Descriptor*/ - 0x05, /* bFunctionLength */ - 0x24, /* bDescriptorType: CS_INTERFACE */ - 0x06, /* bDescriptorSubtype: Union func desc */ - 0x00, /* bMasterInterface: Communication class interface */ - 0x01, /* bSlaveInterface0: Data Class Interface */ - - /*Endpoint 2 Descriptor*/ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_CMD_EP, /* bEndpointAddress */ - 0x03, /* bmAttributes: Interrupt */ - LOBYTE(CDC_CMD_PACKET_SIZE), /* wMaxPacketSize: */ - HIBYTE(CDC_CMD_PACKET_SIZE), - CDC_FS_BINTERVAL, /* bInterval: */ - - /*---------------------------------------------------------------------------*/ - - /*Data class interface descriptor*/ - 0x09, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_INTERFACE, /* bDescriptorType: */ - 0x01, /* bInterfaceNumber: Number of Interface */ - 0x00, /* bAlternateSetting: Alternate setting */ - 0x02, /* bNumEndpoints: Two endpoints used */ - 0x0A, /* bInterfaceClass: CDC */ - 0x00, /* bInterfaceSubClass: */ - 0x00, /* bInterfaceProtocol: */ - 0x00, /* iInterface: */ - - /*Endpoint OUT Descriptor*/ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_OUT_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - 0x40, /* wMaxPacketSize: */ - 0x00, - 0x00, /* bInterval: ignore for Bulk transfer */ - - /*Endpoint IN Descriptor*/ - 0x07, /* bLength: Endpoint Descriptor size */ - USB_DESC_TYPE_ENDPOINT, /* bDescriptorType: Endpoint */ - CDC_IN_EP, /* bEndpointAddress */ - 0x02, /* bmAttributes: Bulk */ - 0x40, /* wMaxPacketSize: */ - 0x00, - 0x00 /* bInterval */ -}; - -/** - * @} - */ - -/** @defgroup USBD_CDC_Private_Functions - * @{ - */ - -/** - * @brief USBD_CDC_Init - * Initialize the CDC interface - * @param pdev: device instance - * @param cfgidx: Configuration index - * @retval status - */ -static uint8_t USBD_CDC_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - UNUSED(cfgidx); - USBD_CDC_HandleTypeDef *hcdc; - - hcdc = USBD_malloc(sizeof(USBD_CDC_HandleTypeDef)); - - if (hcdc == NULL) - { - pdev->pClassData = NULL; - return (uint8_t)USBD_EMEM; - } - - pdev->pClassData = (void *)hcdc; - - if (pdev->dev_speed == USBD_SPEED_HIGH) - { - /* Open EP IN */ - (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, - CDC_DATA_HS_IN_PACKET_SIZE); - - pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; - - /* Open EP OUT */ - (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, - CDC_DATA_HS_OUT_PACKET_SIZE); - - pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; - - /* Set bInterval for CDC CMD Endpoint */ - pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_HS_BINTERVAL; - } - else - { - /* Open EP IN */ - (void)USBD_LL_OpenEP(pdev, CDC_IN_EP, USBD_EP_TYPE_BULK, - CDC_DATA_FS_IN_PACKET_SIZE); - - pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 1U; - - /* Open EP OUT */ - (void)USBD_LL_OpenEP(pdev, CDC_OUT_EP, USBD_EP_TYPE_BULK, - CDC_DATA_FS_OUT_PACKET_SIZE); - - pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 1U; - - /* Set bInterval for CMD Endpoint */ - pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = CDC_FS_BINTERVAL; - } - - /* Open Command IN EP */ - (void)USBD_LL_OpenEP(pdev, CDC_CMD_EP, USBD_EP_TYPE_INTR, CDC_CMD_PACKET_SIZE); - pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 1U; - - /* Init physical Interface components */ - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); - - /* Init Xfer states */ - hcdc->TxState = 0U; - hcdc->RxState = 0U; - - if (pdev->dev_speed == USBD_SPEED_HIGH) - { - /* Prepare Out endpoint to receive next packet */ - (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, - CDC_DATA_HS_OUT_PACKET_SIZE); - } - else - { - /* Prepare Out endpoint to receive next packet */ - (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, - CDC_DATA_FS_OUT_PACKET_SIZE); - } - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_Init - * DeInitialize the CDC layer - * @param pdev: device instance - * @param cfgidx: Configuration index - * @retval status - */ -static uint8_t USBD_CDC_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx) -{ - UNUSED(cfgidx); - uint8_t ret = 0U; - - /* Close EP IN */ - (void)USBD_LL_CloseEP(pdev, CDC_IN_EP); - pdev->ep_in[CDC_IN_EP & 0xFU].is_used = 0U; - - /* Close EP OUT */ - (void)USBD_LL_CloseEP(pdev, CDC_OUT_EP); - pdev->ep_out[CDC_OUT_EP & 0xFU].is_used = 0U; - - /* Close Command IN EP */ - (void)USBD_LL_CloseEP(pdev, CDC_CMD_EP); - pdev->ep_in[CDC_CMD_EP & 0xFU].is_used = 0U; - pdev->ep_in[CDC_CMD_EP & 0xFU].bInterval = 0U; - - /* DeInit physical Interface components */ - if (pdev->pClassData != NULL) - { - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->DeInit(); - (void)USBD_free(pdev->pClassData); - pdev->pClassData = NULL; - } - - return ret; -} - -/** - * @brief USBD_CDC_Setup - * Handle the CDC specific requests - * @param pdev: instance - * @param req: usb requests - * @retval status - */ -static uint8_t USBD_CDC_Setup(USBD_HandleTypeDef *pdev, - USBD_SetupReqTypedef *req) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - uint8_t ifalt = 0U; - uint16_t status_info = 0U; - USBD_StatusTypeDef ret = USBD_OK; - - switch (req->bmRequest & USB_REQ_TYPE_MASK) - { - case USB_REQ_TYPE_CLASS: - if (req->wLength != 0U) - { - if ((req->bmRequest & 0x80U) != 0U) - { - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, - (uint8_t *)hcdc->data, - req->wLength); - - (void)USBD_CtlSendData(pdev, (uint8_t *)hcdc->data, req->wLength); - } - else - { - hcdc->CmdOpCode = req->bRequest; - hcdc->CmdLength = (uint8_t)req->wLength; - - (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hcdc->data, req->wLength); - } - } - else - { - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(req->bRequest, - (uint8_t *)req, 0U); - } - break; - - case USB_REQ_TYPE_STANDARD: - switch (req->bRequest) - { - case USB_REQ_GET_STATUS: - if (pdev->dev_state == USBD_STATE_CONFIGURED) - { - (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); - } - else - { - USBD_CtlError(pdev, req); - ret = USBD_FAIL; - } - break; - - case USB_REQ_GET_INTERFACE: - if (pdev->dev_state == USBD_STATE_CONFIGURED) - { - (void)USBD_CtlSendData(pdev, &ifalt, 1U); - } - else - { - USBD_CtlError(pdev, req); - ret = USBD_FAIL; - } - break; - - case USB_REQ_SET_INTERFACE: - if (pdev->dev_state != USBD_STATE_CONFIGURED) - { - USBD_CtlError(pdev, req); - ret = USBD_FAIL; - } - break; - - case USB_REQ_CLEAR_FEATURE: - break; - - default: - USBD_CtlError(pdev, req); - ret = USBD_FAIL; - break; - } - break; - - default: - USBD_CtlError(pdev, req); - ret = USBD_FAIL; - break; - } - - return (uint8_t)ret; -} - -/** - * @brief USBD_CDC_DataIn - * Data sent on non-control IN endpoint - * @param pdev: device instance - * @param epnum: endpoint number - * @retval status - */ -static uint8_t USBD_CDC_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - USBD_CDC_HandleTypeDef *hcdc; - PCD_HandleTypeDef *hpcd = pdev->pData; - - if (pdev->pClassData == NULL) - { - return (uint8_t)USBD_FAIL; - } - - hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - if ((pdev->ep_in[epnum].total_length > 0U) && - ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U)) - { - /* Update the packet total length */ - pdev->ep_in[epnum].total_length = 0U; - - /* Send ZLP */ - (void)USBD_LL_Transmit(pdev, epnum, NULL, 0U); - } - else - { - hcdc->TxState = 0U; - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->TransmitCplt(hcdc->TxBuffer, &hcdc->TxLength, epnum); - } - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_DataOut - * Data received on non-control Out endpoint - * @param pdev: device instance - * @param epnum: endpoint number - * @retval status - */ -static uint8_t USBD_CDC_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - if (pdev->pClassData == NULL) - { - return (uint8_t)USBD_FAIL; - } - - /* Get the received data length */ - hcdc->RxLength = USBD_LL_GetRxDataSize(pdev, epnum); - - /* USB data will be immediately processed, this allow next USB traffic being - NAKed till the end of the application Xfer */ - - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Receive(hcdc->RxBuffer, &hcdc->RxLength); - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_EP0_RxReady - * Handle EP0 Rx Ready event - * @param pdev: device instance - * @retval status - */ -static uint8_t USBD_CDC_EP0_RxReady(USBD_HandleTypeDef *pdev) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - if ((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFFU)) - { - ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Control(hcdc->CmdOpCode, - (uint8_t *)hcdc->data, - (uint16_t)hcdc->CmdLength); - hcdc->CmdOpCode = 0xFFU; - - } - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_GetFSCfgDesc - * Return configuration descriptor - * @param speed : current device speed - * @param length : pointer data length - * @retval pointer to descriptor buffer - */ -static uint8_t *USBD_CDC_GetFSCfgDesc(uint16_t *length) -{ - *length = (uint16_t)sizeof(USBD_CDC_CfgFSDesc); - - return USBD_CDC_CfgFSDesc; -} - -/** - * @brief USBD_CDC_GetHSCfgDesc - * Return configuration descriptor - * @param speed : current device speed - * @param length : pointer data length - * @retval pointer to descriptor buffer - */ -static uint8_t *USBD_CDC_GetHSCfgDesc(uint16_t *length) -{ - *length = (uint16_t)sizeof(USBD_CDC_CfgHSDesc); - - return USBD_CDC_CfgHSDesc; -} - -/** - * @brief USBD_CDC_GetCfgDesc - * Return configuration descriptor - * @param speed : current device speed - * @param length : pointer data length - * @retval pointer to descriptor buffer - */ -static uint8_t *USBD_CDC_GetOtherSpeedCfgDesc(uint16_t *length) -{ - *length = (uint16_t)sizeof(USBD_CDC_OtherSpeedCfgDesc); - - return USBD_CDC_OtherSpeedCfgDesc; -} - -/** -* @brief DeviceQualifierDescriptor -* return Device Qualifier descriptor -* @param length : pointer data length -* @retval pointer to descriptor buffer -*/ -uint8_t *USBD_CDC_GetDeviceQualifierDescriptor(uint16_t *length) -{ - *length = (uint16_t)sizeof(USBD_CDC_DeviceQualifierDesc); - - return USBD_CDC_DeviceQualifierDesc; -} - -/** -* @brief USBD_CDC_RegisterInterface - * @param pdev: device instance - * @param fops: CD Interface callback - * @retval status - */ -uint8_t USBD_CDC_RegisterInterface(USBD_HandleTypeDef *pdev, - USBD_CDC_ItfTypeDef *fops) -{ - if (fops == NULL) - { - return (uint8_t)USBD_FAIL; - } - - pdev->pUserData = fops; - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_SetTxBuffer - * @param pdev: device instance - * @param pbuff: Tx Buffer - * @retval status - */ -uint8_t USBD_CDC_SetTxBuffer(USBD_HandleTypeDef *pdev, - uint8_t *pbuff, uint32_t length) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - hcdc->TxBuffer = pbuff; - hcdc->TxLength = length; - - return (uint8_t)USBD_OK; -} - - -/** - * @brief USBD_CDC_SetRxBuffer - * @param pdev: device instance - * @param pbuff: Rx Buffer - * @retval status - */ -uint8_t USBD_CDC_SetRxBuffer(USBD_HandleTypeDef *pdev, uint8_t *pbuff) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - hcdc->RxBuffer = pbuff; - - return (uint8_t)USBD_OK; -} - -/** - * @brief USBD_CDC_TransmitPacket - * Transmit packet on IN endpoint - * @param pdev: device instance - * @retval status - */ -uint8_t USBD_CDC_TransmitPacket(USBD_HandleTypeDef *pdev) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - USBD_StatusTypeDef ret = USBD_BUSY; - - if (pdev->pClassData == NULL) - { - return (uint8_t)USBD_FAIL; - } - - if (hcdc->TxState == 0U) - { - /* Tx Transfer in progress */ - hcdc->TxState = 1U; - - /* Update the packet total length */ - pdev->ep_in[CDC_IN_EP & 0xFU].total_length = hcdc->TxLength; - - /* Transmit next packet */ - (void)USBD_LL_Transmit(pdev, CDC_IN_EP, hcdc->TxBuffer, hcdc->TxLength); - - ret = USBD_OK; - } - - return (uint8_t)ret; -} - - -/** - * @brief USBD_CDC_ReceivePacket - * prepare OUT Endpoint for reception - * @param pdev: device instance - * @retval status - */ -uint8_t USBD_CDC_ReceivePacket(USBD_HandleTypeDef *pdev) -{ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef *)pdev->pClassData; - - if (pdev->pClassData == NULL) - { - return (uint8_t)USBD_FAIL; - } - - if (pdev->dev_speed == USBD_SPEED_HIGH) - { - /* Prepare Out endpoint to receive next packet */ - (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, - CDC_DATA_HS_OUT_PACKET_SIZE); - } - else - { - /* Prepare Out endpoint to receive next packet */ - (void)USBD_LL_PrepareReceive(pdev, CDC_OUT_EP, hcdc->RxBuffer, - CDC_DATA_FS_OUT_PACKET_SIZE); - } - - return (uint8_t)USBD_OK; -} -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/Middlewares/Third_Party/FatFs/src/diskio.c b/Core/Src/Middlewares/Third_Party/FatFs/src/diskio.c new file mode 100644 index 0000000..5cf8edf --- /dev/null +++ b/Core/Src/Middlewares/Third_Party/FatFs/src/diskio.c @@ -0,0 +1,141 @@ +/*-----------------------------------------------------------------------*/ +/* Low level disk I/O module skeleton for FatFs (C)ChaN, 2017 */ +/* */ +/* Portions COPYRIGHT 2017 STMicroelectronics */ +/* Portions Copyright (C) 2017, ChaN, all right reserved */ +/*-----------------------------------------------------------------------*/ +/* If a working storage control module is available, it should be */ +/* attached to the FatFs via a glue function rather than modifying it. */ +/* This is an example of glue functions to attach various existing */ +/* storage control modules to the FatFs module with a defined API. */ +/*-----------------------------------------------------------------------*/ + +/* Includes ------------------------------------------------------------------*/ +#include "diskio.h" +#include "ff_gen_drv.h" + +#if defined ( __GNUC__ ) +#ifndef __weak +#define __weak __attribute__((weak)) +#endif +#endif + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +extern Disk_drvTypeDef disk; + +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/** + * @brief Gets Disk Status + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +DSTATUS disk_status ( + BYTE pdrv /* Physical drive number to identify the drive */ +) +{ + DSTATUS stat; + + stat = disk.drv[pdrv]->disk_status(disk.lun[pdrv]); + return stat; +} + +/** + * @brief Initializes a Drive + * @param pdrv: Physical drive number (0..) + * @retval DSTATUS: Operation status + */ +DSTATUS disk_initialize ( + BYTE pdrv /* Physical drive nmuber to identify the drive */ +) +{ + DSTATUS stat = RES_OK; + + if(disk.is_initialized[pdrv] == 0) + { + disk.is_initialized[pdrv] = 1; + stat = disk.drv[pdrv]->disk_initialize(disk.lun[pdrv]); + } + return stat; +} + +/** + * @brief Reads Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data buffer to store read data + * @param sector: Sector address (LBA) + * @param count: Number of sectors to read (1..128) + * @retval DRESULT: Operation result + */ +DRESULT disk_read ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + BYTE *buff, /* Data buffer to store read data */ + DWORD sector, /* Sector address in LBA */ + UINT count /* Number of sectors to read */ +) +{ + DRESULT res; + + res = disk.drv[pdrv]->disk_read(disk.lun[pdrv], buff, sector, count); + return res; +} + +/** + * @brief Writes Sector(s) + * @param pdrv: Physical drive number (0..) + * @param *buff: Data to be written + * @param sector: Sector address (LBA) + * @param count: Number of sectors to write (1..128) + * @retval DRESULT: Operation result + */ +#if _USE_WRITE == 1 +DRESULT disk_write ( + BYTE pdrv, /* Physical drive nmuber to identify the drive */ + const BYTE *buff, /* Data to be written */ + DWORD sector, /* Sector address in LBA */ + UINT count /* Number of sectors to write */ +) +{ + DRESULT res; + + res = disk.drv[pdrv]->disk_write(disk.lun[pdrv], buff, sector, count); + return res; +} +#endif /* _USE_WRITE == 1 */ + +/** + * @brief I/O control operation + * @param pdrv: Physical drive number (0..) + * @param cmd: Control code + * @param *buff: Buffer to send/receive control data + * @retval DRESULT: Operation result + */ +#if _USE_IOCTL == 1 +DRESULT disk_ioctl ( + BYTE pdrv, /* Physical drive nmuber (0..) */ + BYTE cmd, /* Control code */ + void *buff /* Buffer to send/receive control data */ +) +{ + DRESULT res; + + res = disk.drv[pdrv]->disk_ioctl(disk.lun[pdrv], cmd, buff); + return res; +} +#endif /* _USE_IOCTL == 1 */ + +/** + * @brief Gets Time from RTC + * @param None + * @retval Time in DWORD + */ +__weak DWORD get_fattime (void) +{ + return 0; +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/Core/Src/Middlewares/Third_Party/FatFs/src/ff.c b/Core/Src/Middlewares/Third_Party/FatFs/src/ff.c new file mode 100644 index 0000000..b0bd436 --- /dev/null +++ b/Core/Src/Middlewares/Third_Party/FatFs/src/ff.c @@ -0,0 +1,6140 @@ +/*----------------------------------------------------------------------------/ +/ FatFs - Generic FAT file system module R0.12c / +/-----------------------------------------------------------------------------/ +/ +/ Copyright (C) 2017, ChaN, all right reserved. +/ +/ FatFs module is an open source software. Redistribution and use of FatFs in +/ source and binary forms, with or without modification, are permitted provided +/ that the following condition is met: +/ +/ 1. Redistributions of source code must retain the above copyright notice, +/ this condition and the following disclaimer. +/ +/ This software is provided by the copyright holder and contributors "AS IS" +/ and any warranties related to this software are DISCLAIMED. +/ The copyright owner or contributors be NOT LIABLE for any damages caused +/ by use of this software. +/----------------------------------------------------------------------------*/ + + +#include "ff.h" /* Declarations of FatFs API */ +#include "diskio.h" /* Declarations of device I/O functions */ + + +/*-------------------------------------------------------------------------- + + Module Private Definitions + +---------------------------------------------------------------------------*/ + +#if _FATFS != 68300 /* Revision ID */ +#error Wrong include file (ff.h). +#endif + + +/* DBCS code ranges and SBCS upper conversion tables */ + +#if _CODE_PAGE == 932 /* Japanese Shift-JIS */ +#define _DF1S 0x81 /* DBC 1st byte range 1 start */ +#define _DF1E 0x9F /* DBC 1st byte range 1 end */ +#define _DF2S 0xE0 /* DBC 1st byte range 2 start */ +#define _DF2E 0xFC /* DBC 1st byte range 2 end */ +#define _DS1S 0x40 /* DBC 2nd byte range 1 start */ +#define _DS1E 0x7E /* DBC 2nd byte range 1 end */ +#define _DS2S 0x80 /* DBC 2nd byte range 2 start */ +#define _DS2E 0xFC /* DBC 2nd byte range 2 end */ + +#elif _CODE_PAGE == 936 /* Simplified Chinese GBK */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0x80 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 949 /* Korean */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x41 +#define _DS1E 0x5A +#define _DS2S 0x61 +#define _DS2E 0x7A +#define _DS3S 0x81 +#define _DS3E 0xFE + +#elif _CODE_PAGE == 950 /* Traditional Chinese Big5 */ +#define _DF1S 0x81 +#define _DF1E 0xFE +#define _DS1S 0x40 +#define _DS1E 0x7E +#define _DS2S 0xA1 +#define _DS2E 0xFE + +#elif _CODE_PAGE == 437 /* U.S. */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 720 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 737 /* Greek */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \ + 0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 771 /* KBL */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 775 /* Baltic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 850 /* Latin 1 */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \ + 0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \ + 0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 852 /* Latin 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \ + 0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF} + +#elif _CODE_PAGE == 855 /* Cyrillic */ +#define _DF1S 0 +#define _EXCVT {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \ + 0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \ + 0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \ + 0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \ + 0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 857 /* Turkish */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \ + 0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 860 /* Portuguese */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \ + 0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 861 /* Icelandic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \ + 0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 862 /* Hebrew */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 863 /* Canadian-French */ +#define _DF1S 0 +#define _EXCVT {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \ + 0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \ + 0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 864 /* Arabic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 865 /* Nordic */ +#define _DF1S 0 +#define _EXCVT {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \ + 0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \ + 0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 866 /* Russian */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \ + 0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF} + +#elif _CODE_PAGE == 869 /* Greek 2 */ +#define _DF1S 0 +#define _EXCVT {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \ + 0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \ + 0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \ + 0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \ + 0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \ + 0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \ + 0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \ + 0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF} + +#elif _CODE_PAGE == 1 /* ASCII (for only non-LFN cfg) */ +#if _USE_LFN != 0 +#error Cannot enable LFN without valid code page. +#endif +#define _DF1S 0 + +#else +#error Unknown code page + +#endif + + +/* Character code support macros */ +#define IsUpper(c) (((c)>='A')&&((c)<='Z')) +#define IsLower(c) (((c)>='a')&&((c)<='z')) +#define IsDigit(c) (((c)>='0')&&((c)<='9')) + +#if _DF1S != 0 /* Code page is DBCS */ + +#ifdef _DF2S /* Two 1st byte areas */ +#define IsDBCS1(c) (((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) || ((BYTE)(c) >= _DF2S && (BYTE)(c) <= _DF2E)) +#else /* One 1st byte area */ +#define IsDBCS1(c) ((BYTE)(c) >= _DF1S && (BYTE)(c) <= _DF1E) +#endif + +#ifdef _DS3S /* Three 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E) || ((BYTE)(c) >= _DS3S && (BYTE)(c) <= _DS3E)) +#else /* Two 2nd byte areas */ +#define IsDBCS2(c) (((BYTE)(c) >= _DS1S && (BYTE)(c) <= _DS1E) || ((BYTE)(c) >= _DS2S && (BYTE)(c) <= _DS2E)) +#endif + +#else /* Code page is SBCS */ + +#define IsDBCS1(c) 0 +#define IsDBCS2(c) 0 + +#endif /* _DF1S */ + + +/* Additional file attribute bits for internal use */ +#define AM_VOL 0x08 /* Volume label */ +#define AM_LFN 0x0F /* LFN entry */ +#define AM_MASK 0x3F /* Mask of defined bits */ + + +/* Additional file access control and file status flags for internal use */ +#define FA_SEEKEND 0x20 /* Seek to end of the file on file open */ +#define FA_MODIFIED 0x40 /* File has been modified */ +#define FA_DIRTY 0x80 /* FIL.buf[] needs to be written-back */ + + +/* Name status flags in fn[] */ +#define NSFLAG 11 /* Index of the name status byte */ +#define NS_LOSS 0x01 /* Out of 8.3 format */ +#define NS_LFN 0x02 /* Force to create LFN entry */ +#define NS_LAST 0x04 /* Last segment */ +#define NS_BODY 0x08 /* Lower case flag (body) */ +#define NS_EXT 0x10 /* Lower case flag (ext) */ +#define NS_DOT 0x20 /* Dot entry */ +#define NS_NOLFN 0x40 /* Do not find LFN */ +#define NS_NONAME 0x80 /* Not followed */ + + +/* Limits and boundaries */ +#define MAX_DIR 0x200000 /* Max size of FAT directory */ +#define MAX_DIR_EX 0x10000000 /* Max size of exFAT directory */ +#define MAX_FAT12 0xFF5 /* Max FAT12 clusters (differs from specs, but correct for real DOS/Windows behavior) */ +#define MAX_FAT16 0xFFF5 /* Max FAT16 clusters (differs from specs, but correct for real DOS/Windows behavior) */ +#define MAX_FAT32 0x0FFFFFF5 /* Max FAT32 clusters (not specified, practical limit) */ +#define MAX_EXFAT 0x7FFFFFFD /* Max exFAT clusters (differs from specs, implementation limit) */ + + +/* FatFs refers the FAT structure as simple byte array instead of structure member +/ because the C structure is not binary compatible between different platforms */ + +#define BS_JmpBoot 0 /* x86 jump instruction (3-byte) */ +#define BS_OEMName 3 /* OEM name (8-byte) */ +#define BPB_BytsPerSec 11 /* Sector size [byte] (WORD) */ +#define BPB_SecPerClus 13 /* Cluster size [sector] (BYTE) */ +#define BPB_RsvdSecCnt 14 /* Size of reserved area [sector] (WORD) */ +#define BPB_NumFATs 16 /* Number of FATs (BYTE) */ +#define BPB_RootEntCnt 17 /* Size of root directory area for FAT12/16 [entry] (WORD) */ +#define BPB_TotSec16 19 /* Volume size (16-bit) [sector] (WORD) */ +#define BPB_Media 21 /* Media descriptor byte (BYTE) */ +#define BPB_FATSz16 22 /* FAT size (16-bit) [sector] (WORD) */ +#define BPB_SecPerTrk 24 /* Track size for int13h [sector] (WORD) */ +#define BPB_NumHeads 26 /* Number of heads for int13h (WORD) */ +#define BPB_HiddSec 28 /* Volume offset from top of the drive (DWORD) */ +#define BPB_TotSec32 32 /* Volume size (32-bit) [sector] (DWORD) */ +#define BS_DrvNum 36 /* Physical drive number for int13h (BYTE) */ +#define BS_NTres 37 /* Error flag (BYTE) */ +#define BS_BootSig 38 /* Extended boot signature (BYTE) */ +#define BS_VolID 39 /* Volume serial number (DWORD) */ +#define BS_VolLab 43 /* Volume label string (8-byte) */ +#define BS_FilSysType 54 /* File system type string (8-byte) */ +#define BS_BootCode 62 /* Boot code (448-byte) */ +#define BS_55AA 510 /* Signature word (WORD) */ + +#define BPB_FATSz32 36 /* FAT32: FAT size [sector] (DWORD) */ +#define BPB_ExtFlags32 40 /* FAT32: Extended flags (WORD) */ +#define BPB_FSVer32 42 /* FAT32: File system version (WORD) */ +#define BPB_RootClus32 44 /* FAT32: Root directory cluster (DWORD) */ +#define BPB_FSInfo32 48 /* FAT32: Offset of FSINFO sector (WORD) */ +#define BPB_BkBootSec32 50 /* FAT32: Offset of backup boot sector (WORD) */ +#define BS_DrvNum32 64 /* FAT32: Physical drive number for int13h (BYTE) */ +#define BS_NTres32 65 /* FAT32: Error flag (BYTE) */ +#define BS_BootSig32 66 /* FAT32: Extended boot signature (BYTE) */ +#define BS_VolID32 67 /* FAT32: Volume serial number (DWORD) */ +#define BS_VolLab32 71 /* FAT32: Volume label string (8-byte) */ +#define BS_FilSysType32 82 /* FAT32: File system type string (8-byte) */ +#define BS_BootCode32 90 /* FAT32: Boot code (420-byte) */ + +#define BPB_ZeroedEx 11 /* exFAT: MBZ field (53-byte) */ +#define BPB_VolOfsEx 64 /* exFAT: Volume offset from top of the drive [sector] (QWORD) */ +#define BPB_TotSecEx 72 /* exFAT: Volume size [sector] (QWORD) */ +#define BPB_FatOfsEx 80 /* exFAT: FAT offset from top of the volume [sector] (DWORD) */ +#define BPB_FatSzEx 84 /* exFAT: FAT size [sector] (DWORD) */ +#define BPB_DataOfsEx 88 /* exFAT: Data offset from top of the volume [sector] (DWORD) */ +#define BPB_NumClusEx 92 /* exFAT: Number of clusters (DWORD) */ +#define BPB_RootClusEx 96 /* exFAT: Root directory start cluster (DWORD) */ +#define BPB_VolIDEx 100 /* exFAT: Volume serial number (DWORD) */ +#define BPB_FSVerEx 104 /* exFAT: File system version (WORD) */ +#define BPB_VolFlagEx 106 /* exFAT: Volume flags (BYTE) */ +#define BPB_ActFatEx 107 /* exFAT: Active FAT flags (BYTE) */ +#define BPB_BytsPerSecEx 108 /* exFAT: Log2 of sector size in unit of byte (BYTE) */ +#define BPB_SecPerClusEx 109 /* exFAT: Log2 of cluster size in unit of sector (BYTE) */ +#define BPB_NumFATsEx 110 /* exFAT: Number of FATs (BYTE) */ +#define BPB_DrvNumEx 111 /* exFAT: Physical drive number for int13h (BYTE) */ +#define BPB_PercInUseEx 112 /* exFAT: Percent in use (BYTE) */ +#define BPB_RsvdEx 113 /* exFAT: Reserved (7-byte) */ +#define BS_BootCodeEx 120 /* exFAT: Boot code (390-byte) */ + +#define DIR_Name 0 /* Short file name (11-byte) */ +#define DIR_Attr 11 /* Attribute (BYTE) */ +#define DIR_NTres 12 /* Lower case flag (BYTE) */ +#define DIR_CrtTime10 13 /* Created time sub-second (BYTE) */ +#define DIR_CrtTime 14 /* Created time (DWORD) */ +#define DIR_LstAccDate 18 /* Last accessed date (WORD) */ +#define DIR_FstClusHI 20 /* Higher 16-bit of first cluster (WORD) */ +#define DIR_ModTime 22 /* Modified time (DWORD) */ +#define DIR_FstClusLO 26 /* Lower 16-bit of first cluster (WORD) */ +#define DIR_FileSize 28 /* File size (DWORD) */ +#define LDIR_Ord 0 /* LFN: LFN order and LLE flag (BYTE) */ +#define LDIR_Attr 11 /* LFN: LFN attribute (BYTE) */ +#define LDIR_Type 12 /* LFN: Entry type (BYTE) */ +#define LDIR_Chksum 13 /* LFN: Checksum of the SFN (BYTE) */ +#define LDIR_FstClusLO 26 /* LFN: MBZ field (WORD) */ +#define XDIR_Type 0 /* exFAT: Type of exFAT directory entry (BYTE) */ +#define XDIR_NumLabel 1 /* exFAT: Number of volume label characters (BYTE) */ +#define XDIR_Label 2 /* exFAT: Volume label (11-WORD) */ +#define XDIR_CaseSum 4 /* exFAT: Sum of case conversion table (DWORD) */ +#define XDIR_NumSec 1 /* exFAT: Number of secondary entries (BYTE) */ +#define XDIR_SetSum 2 /* exFAT: Sum of the set of directory entries (WORD) */ +#define XDIR_Attr 4 /* exFAT: File attribute (WORD) */ +#define XDIR_CrtTime 8 /* exFAT: Created time (DWORD) */ +#define XDIR_ModTime 12 /* exFAT: Modified time (DWORD) */ +#define XDIR_AccTime 16 /* exFAT: Last accessed time (DWORD) */ +#define XDIR_CrtTime10 20 /* exFAT: Created time subsecond (BYTE) */ +#define XDIR_ModTime10 21 /* exFAT: Modified time subsecond (BYTE) */ +#define XDIR_CrtTZ 22 /* exFAT: Created timezone (BYTE) */ +#define XDIR_ModTZ 23 /* exFAT: Modified timezone (BYTE) */ +#define XDIR_AccTZ 24 /* exFAT: Last accessed timezone (BYTE) */ +#define XDIR_GenFlags 33 /* exFAT: General secondary flags (WORD) */ +#define XDIR_NumName 35 /* exFAT: Number of file name characters (BYTE) */ +#define XDIR_NameHash 36 /* exFAT: Hash of file name (WORD) */ +#define XDIR_ValidFileSize 40 /* exFAT: Valid file size (QWORD) */ +#define XDIR_FstClus 52 /* exFAT: First cluster of the file data (DWORD) */ +#define XDIR_FileSize 56 /* exFAT: File/Directory size (QWORD) */ + +#define SZDIRE 32 /* Size of a directory entry */ +#define DDEM 0xE5 /* Deleted directory entry mark set to DIR_Name[0] */ +#define RDDEM 0x05 /* Replacement of the character collides with DDEM */ +#define LLEF 0x40 /* Last long entry flag in LDIR_Ord */ + +#define FSI_LeadSig 0 /* FAT32 FSI: Leading signature (DWORD) */ +#define FSI_StrucSig 484 /* FAT32 FSI: Structure signature (DWORD) */ +#define FSI_Free_Count 488 /* FAT32 FSI: Number of free clusters (DWORD) */ +#define FSI_Nxt_Free 492 /* FAT32 FSI: Last allocated cluster (DWORD) */ + +#define MBR_Table 446 /* MBR: Offset of partition table in the MBR */ +#define SZ_PTE 16 /* MBR: Size of a partition table entry */ +#define PTE_Boot 0 /* MBR PTE: Boot indicator */ +#define PTE_StHead 1 /* MBR PTE: Start head */ +#define PTE_StSec 2 /* MBR PTE: Start sector */ +#define PTE_StCyl 3 /* MBR PTE: Start cylinder */ +#define PTE_System 4 /* MBR PTE: System ID */ +#define PTE_EdHead 5 /* MBR PTE: End head */ +#define PTE_EdSec 6 /* MBR PTE: End sector */ +#define PTE_EdCyl 7 /* MBR PTE: End cylinder */ +#define PTE_StLba 8 /* MBR PTE: Start in LBA */ +#define PTE_SizLba 12 /* MBR PTE: Size in LBA */ + + +/* Post process after fatal error on file operation */ +#define ABORT(fs, res) { fp->err = (BYTE)(res); LEAVE_FF(fs, res); } + + +/* Reentrancy related */ +#if _FS_REENTRANT +#if _USE_LFN == 1 +#error Static LFN work area cannot be used at thread-safe configuration +#endif +#define ENTER_FF(fs) { if (!lock_fs(fs)) return FR_TIMEOUT; } +#define LEAVE_FF(fs, res) { unlock_fs(fs, res); return res; } +#else +#define ENTER_FF(fs) +#define LEAVE_FF(fs, res) return res +#endif + + +/* Definitions of volume - partition conversion */ +#if _MULTI_PARTITION +#define LD2PD(vol) VolToPart[vol].pd /* Get physical drive number */ +#define LD2PT(vol) VolToPart[vol].pt /* Get partition index */ +#else +#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is bound to the same physical drive number */ +#define LD2PT(vol) 0 /* Find first valid partition or in SFD */ +#endif + + +/* Definitions of sector size */ +#if (_MAX_SS < _MIN_SS) || (_MAX_SS != 512 && _MAX_SS != 1024 && _MAX_SS != 2048 && _MAX_SS != 4096) || (_MIN_SS != 512 && _MIN_SS != 1024 && _MIN_SS != 2048 && _MIN_SS != 4096) +#error Wrong sector size configuration +#endif +#if _MAX_SS == _MIN_SS +#define SS(fs) ((UINT)_MAX_SS) /* Fixed sector size */ +#else +#define SS(fs) ((fs)->ssize) /* Variable sector size */ +#endif + + +/* Timestamp */ +#if _FS_NORTC == 1 +#if _NORTC_YEAR < 1980 || _NORTC_YEAR > 2107 || _NORTC_MON < 1 || _NORTC_MON > 12 || _NORTC_MDAY < 1 || _NORTC_MDAY > 31 +#error Invalid _FS_NORTC settings +#endif +#define GET_FATTIME() ((DWORD)(_NORTC_YEAR - 1980) << 25 | (DWORD)_NORTC_MON << 21 | (DWORD)_NORTC_MDAY << 16) +#else +#define GET_FATTIME() get_fattime() +#endif + + +/* File lock controls */ +#if _FS_LOCK != 0 +#if _FS_READONLY +#error _FS_LOCK must be 0 at read-only configuration +#endif +typedef struct { + FATFS *fs; /* Object ID 1, volume (NULL:blank entry) */ + DWORD clu; /* Object ID 2, containing directory (0:root) */ + DWORD ofs; /* Object ID 3, offset in the directory */ + WORD ctr; /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */ +} FILESEM; +#endif + + + + + +/*-------------------------------------------------------------------------- + + Module Private Work Area + +---------------------------------------------------------------------------*/ + +/* Remark: Variables defined here without initial value shall be guaranteed +/ zero/null at start-up. If not, the linker option or start-up routine is +/ not compliance with C standard. */ + +#if _VOLUMES < 1 || _VOLUMES > 10 +#error Wrong _VOLUMES setting +#endif +static FATFS *FatFs[_VOLUMES]; /* Pointer to the file system objects (logical drives) */ +static WORD Fsid; /* File system mount ID */ + +#if _FS_RPATH != 0 && _VOLUMES >= 2 +static BYTE CurrVol; /* Current drive */ +#endif + +#if _FS_LOCK != 0 +static FILESEM Files[_FS_LOCK]; /* Open object lock semaphores */ +#endif + +#if _USE_LFN == 0 /* Non-LFN configuration */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#else /* LFN configuration */ +#if _MAX_LFN < 12 || _MAX_LFN > 255 +#error Wrong _MAX_LFN value +#endif +#define MAXDIRB(nc) ((nc + 44U) / 15 * SZDIRE) + +#if _USE_LFN == 1 /* LFN enabled with static working buffer */ +#if _FS_EXFAT +static BYTE DirBuf[MAXDIRB(_MAX_LFN)]; /* Directory entry block scratchpad buffer */ +#endif +static WCHAR LfnBuf[_MAX_LFN + 1]; /* LFN enabled with static working buffer */ +#define DEF_NAMBUF +#define INIT_NAMBUF(fs) +#define FREE_NAMBUF() + +#elif _USE_LFN == 2 /* LFN enabled with dynamic working buffer on the stack */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; BYTE dbuf[MAXDIRB(_MAX_LFN)]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; } +#define FREE_NAMBUF() +#else +#define DEF_NAMBUF WCHAR lbuf[_MAX_LFN+1]; +#define INIT_NAMBUF(fs) { (fs)->lfnbuf = lbuf; } +#define FREE_NAMBUF() +#endif + +#elif _USE_LFN == 3 /* LFN enabled with dynamic working buffer on the heap */ +#if _FS_EXFAT +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2 + MAXDIRB(_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+_MAX_LFN+1); } +#define FREE_NAMBUF() ff_memfree(lfn) +#else +#define DEF_NAMBUF WCHAR *lfn; +#define INIT_NAMBUF(fs) { lfn = ff_memalloc((_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; } +#define FREE_NAMBUF() ff_memfree(lfn) +#endif + +#else +#error Wrong _USE_LFN setting + +#endif +#endif /* else _USE_LFN == 0 */ + +#ifdef _EXCVT +static const BYTE ExCvt[] = _EXCVT; /* Upper conversion table for SBCS extended characters */ +#endif + + + + + + +/*-------------------------------------------------------------------------- + + Module Private Functions + +---------------------------------------------------------------------------*/ + + +/*-----------------------------------------------------------------------*/ +/* Load/Store multi-byte word in the FAT structure */ +/*-----------------------------------------------------------------------*/ + +static +WORD ld_word (const BYTE* ptr) /* Load a 2-byte little-endian word */ +{ + WORD rv; + + rv = ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +static +DWORD ld_dword (const BYTE* ptr) /* Load a 4-byte little-endian word */ +{ + DWORD rv; + + rv = ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} + +#if _FS_EXFAT +static +QWORD ld_qword (const BYTE* ptr) /* Load an 8-byte little-endian word */ +{ + QWORD rv; + + rv = ptr[7]; + rv = rv << 8 | ptr[6]; + rv = rv << 8 | ptr[5]; + rv = rv << 8 | ptr[4]; + rv = rv << 8 | ptr[3]; + rv = rv << 8 | ptr[2]; + rv = rv << 8 | ptr[1]; + rv = rv << 8 | ptr[0]; + return rv; +} +#endif + +#if !_FS_READONLY +static +void st_word (BYTE* ptr, WORD val) /* Store a 2-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +static +void st_dword (BYTE* ptr, DWORD val) /* Store a 4-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} + +#if _FS_EXFAT +static +void st_qword (BYTE* ptr, QWORD val) /* Store an 8-byte word in little-endian */ +{ + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; val >>= 8; + *ptr++ = (BYTE)val; +} +#endif +#endif /* !_FS_READONLY */ + + + +/*-----------------------------------------------------------------------*/ +/* String functions */ +/*-----------------------------------------------------------------------*/ + +/* Copy memory to memory */ +static +void mem_cpy (void* dst, const void* src, UINT cnt) { + BYTE *d = (BYTE*)dst; + const BYTE *s = (const BYTE*)src; + + if (cnt) { + do { + *d++ = *s++; + } while (--cnt); + } +} + +/* Fill memory block */ +static +void mem_set (void* dst, int val, UINT cnt) { + BYTE *d = (BYTE*)dst; + + do { + *d++ = (BYTE)val; + } while (--cnt); +} + +/* Compare memory block */ +static +int mem_cmp (const void* dst, const void* src, UINT cnt) { /* ZR:same, NZ:different */ + const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src; + int r = 0; + + do { + r = *d++ - *s++; + } while (--cnt && r == 0); + + return r; +} + +/* Check if chr is contained in the string */ +static +int chk_chr (const char* str, int chr) { /* NZ:contained, ZR:not contained */ + while (*str && *str != chr) str++; + return *str; +} + + + + +#if _FS_REENTRANT +/*-----------------------------------------------------------------------*/ +/* Request/Release grant to access the volume */ +/*-----------------------------------------------------------------------*/ +static +int lock_fs ( + FATFS* fs /* File system object */ +) +{ + return (fs && ff_req_grant(fs->sobj)) ? 1 : 0; +} + + +static +void unlock_fs ( + FATFS* fs, /* File system object */ + FRESULT res /* Result code to be returned */ +) +{ + if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) { + ff_rel_grant(fs->sobj); + } +} + +#endif + + + +#if _FS_LOCK != 0 +/*-----------------------------------------------------------------------*/ +/* File lock control functions */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT chk_lock ( /* Check if the file can be accessed */ + DIR* dp, /* Directory object pointing the file to be checked */ + int acc /* Desired access type (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i, be; + + /* Search file semaphore table */ + for (i = be = 0; i < _FS_LOCK; i++) { + if (Files[i].fs) { /* Existing entry */ + if (Files[i].fs == dp->obj.fs && /* Check if the object matched with an open object */ + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } else { /* Blank entry */ + be = 1; + } + } + if (i == _FS_LOCK) { /* The object is not opened */ + return (be || acc == 2) ? FR_OK : FR_TOO_MANY_OPEN_FILES; /* Is there a blank entry for new object? */ + } + + /* The object has been opened. Reject any open against writing file and all write mode open */ + return (acc || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK; +} + + +static +int enq_lock (void) /* Check if an entry is available for a new object */ +{ + UINT i; + + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + return (i == _FS_LOCK) ? 0 : 1; +} + + +static +UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */ + DIR* dp, /* Directory object pointing the file to register or increment */ + int acc /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */ +) +{ + UINT i; + + + for (i = 0; i < _FS_LOCK; i++) { /* Find the object */ + if (Files[i].fs == dp->obj.fs && + Files[i].clu == dp->obj.sclust && + Files[i].ofs == dp->dptr) break; + } + + if (i == _FS_LOCK) { /* Not opened. Register it as new. */ + for (i = 0; i < _FS_LOCK && Files[i].fs; i++) ; + if (i == _FS_LOCK) return 0; /* No free entry to register (int err) */ + Files[i].fs = dp->obj.fs; + Files[i].clu = dp->obj.sclust; + Files[i].ofs = dp->dptr; + Files[i].ctr = 0; + } + + if (acc && Files[i].ctr) return 0; /* Access violation (int err) */ + + Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1; /* Set semaphore value */ + + return i + 1; +} + + +static +FRESULT dec_lock ( /* Decrement object open counter */ + UINT i /* Semaphore index (1..) */ +) +{ + WORD n; + FRESULT res; + + + if (--i < _FS_LOCK) { /* Shift index number origin from 0 */ + n = Files[i].ctr; + if (n == 0x100) n = 0; /* If write mode open, delete the entry */ + if (n > 0) n--; /* Decrement read mode open count */ + Files[i].ctr = n; + if (n == 0) Files[i].fs = 0; /* Delete the entry if open count gets zero */ + res = FR_OK; + } else { + res = FR_INT_ERR; /* Invalid index nunber */ + } + return res; +} + + +static +void clear_lock ( /* Clear lock entries of the volume */ + FATFS *fs +) +{ + UINT i; + + for (i = 0; i < _FS_LOCK; i++) { + if (Files[i].fs == fs) Files[i].fs = 0; + } +} + +#endif /* _FS_LOCK != 0 */ + + + +/*-----------------------------------------------------------------------*/ +/* Move/Flush disk access window in the file system object */ +/*-----------------------------------------------------------------------*/ +#if !_FS_READONLY +static +FRESULT sync_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs /* File system object */ +) +{ + DWORD wsect; + UINT nf; + FRESULT res = FR_OK; + + + if (fs->wflag) { /* Write back the sector if it is dirty */ + wsect = fs->winsect; /* Current sector number */ + if (disk_write(fs->drv, fs->win, wsect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fs->wflag = 0; + if (wsect - fs->fatbase < fs->fsize) { /* Is it in the FAT area? */ + for (nf = fs->n_fats; nf >= 2; nf--) { /* Reflect the change to all FAT copies */ + wsect += fs->fsize; + disk_write(fs->drv, fs->win, wsect, 1); + } + } + } + } + return res; +} +#endif + + +static +FRESULT move_window ( /* Returns FR_OK or FR_DISK_ERROR */ + FATFS* fs, /* File system object */ + DWORD sector /* Sector number to make appearance in the fs->win[] */ +) +{ + FRESULT res = FR_OK; + + + if (sector != fs->winsect) { /* Window offset changed? */ +#if !_FS_READONLY + res = sync_window(fs); /* Write-back changes */ +#endif + if (res == FR_OK) { /* Fill sector window with new data */ + if (disk_read(fs->drv, fs->win, sector, 1) != RES_OK) { + sector = 0xFFFFFFFF; /* Invalidate window if data is not reliable */ + res = FR_DISK_ERR; + } + fs->winsect = sector; + } + } + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Synchronize file system and strage device */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT sync_fs ( /* FR_OK:succeeded, !=0:error */ + FATFS* fs /* File system object */ +) +{ + FRESULT res; + + + res = sync_window(fs); + if (res == FR_OK) { + /* Update FSInfo sector if needed */ + if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) { + /* Create FSInfo structure */ + mem_set(fs->win, 0, SS(fs)); + st_word(fs->win + BS_55AA, 0xAA55); + st_dword(fs->win + FSI_LeadSig, 0x41615252); + st_dword(fs->win + FSI_StrucSig, 0x61417272); + st_dword(fs->win + FSI_Free_Count, fs->free_clst); + st_dword(fs->win + FSI_Nxt_Free, fs->last_clst); + /* Write it into the FSInfo sector */ + fs->winsect = fs->volbase + 1; + disk_write(fs->drv, fs->win, fs->winsect, 1); + fs->fsi_flag = 0; + } + /* Make sure that no pending write process in the physical drive */ + if (disk_ioctl(fs->drv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR; + } + + return res; +} + +#endif + + + +/*-----------------------------------------------------------------------*/ +/* Get sector# from cluster# */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clust2sect ( /* !=0:Sector number, 0:Failed (invalid cluster#) */ + FATFS* fs, /* File system object */ + DWORD clst /* Cluster# to be converted */ +) +{ + clst -= 2; + if (clst >= fs->n_fatent - 2) return 0; /* Invalid cluster# */ + return clst * fs->csize + fs->database; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT access - Read value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +DWORD get_fat ( /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster number to get the value */ +) +{ + UINT wc, bc; + DWORD val; + FATFS *fs = obj->fs; + + + if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */ + val = 1; /* Internal error */ + + } else { + val = 0xFFFFFFFF; /* Default value falls on disk error */ + + switch (fs->fs_type) { + case FS_FAT12 : + bc = (UINT)clst; bc += bc / 2; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc = fs->win[bc++ % SS(fs)]; + if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break; + wc |= fs->win[bc % SS(fs)] << 8; + val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF); + break; + + case FS_FAT16 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break; + val = ld_word(fs->win + clst * 2 % SS(fs)); + break; + + case FS_FAT32 : + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF; + break; +#if _FS_EXFAT + case FS_EXFAT : + if (obj->objsize) { + DWORD cofs = clst - obj->sclust; /* Offset from start cluster */ + DWORD clen = (DWORD)((obj->objsize - 1) / SS(fs)) / fs->csize; /* Number of clusters - 1 */ + + if (obj->stat == 2) { /* Is there no valid chain on the FAT? */ + if (cofs <= clen) { + val = (cofs == clen) ? 0x7FFFFFFF : clst + 1; /* Generate the value */ + break; + } + } + if (obj->stat == 3 && cofs < obj->n_cont) { /* Is it in the 1st fragment? */ + val = clst + 1; /* Generate the value */ + break; + } + if (obj->stat != 2) { /* Get value from FAT if FAT chain is valid */ + if (obj->n_frag != 0) { /* Is it on the growing edge? */ + val = 0x7FFFFFFF; /* Generate EOC */ + } else { + if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break; + val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF; + } + break; + } + } + /* go to default */ +#endif + default: + val = 1; /* Internal error */ + } + } + + return val; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT access - Change value of a FAT entry */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT put_fat ( /* FR_OK(0):succeeded, !=0:error */ + FATFS* fs, /* Corresponding file system object */ + DWORD clst, /* FAT index number (cluster number) to be changed */ + DWORD val /* New value to be set to the entry */ +) +{ + UINT bc; + BYTE *p; + FRESULT res = FR_INT_ERR; + + if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */ + switch (fs->fs_type) { + case FS_FAT12 : /* Bitfield items */ + bc = (UINT)clst; bc += bc / 2; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc++ % SS(fs); + *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val; + fs->wflag = 1; + res = move_window(fs, fs->fatbase + (bc / SS(fs))); + if (res != FR_OK) break; + p = fs->win + bc % SS(fs); + *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); + fs->wflag = 1; + break; + + case FS_FAT16 : /* WORD aligned items */ + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))); + if (res != FR_OK) break; + st_word(fs->win + clst * 2 % SS(fs), (WORD)val); + fs->wflag = 1; + break; + + case FS_FAT32 : /* DWORD aligned items */ +#if _FS_EXFAT + case FS_EXFAT : +#endif + res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))); + if (res != FR_OK) break; + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000); + } + st_dword(fs->win + clst * 4 % SS(fs), val); + fs->wflag = 1; + break; + } + } + return res; +} + +#endif /* !_FS_READONLY */ + + + + +#if _FS_EXFAT && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* exFAT: Accessing FAT and Allocation Bitmap */ +/*-----------------------------------------------------------------------*/ + +/*--------------------------------------*/ +/* Find a contiguous free cluster block */ +/*--------------------------------------*/ + +static +DWORD find_bitmap ( /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */ + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to scan from */ + DWORD ncl /* Number of contiguous clusters to find (1..) */ +) +{ + BYTE bm, bv; + UINT i; + DWORD val, scl, ctr; + + + clst -= 2; /* The first bit in the bitmap corresponds to cluster #2 */ + if (clst >= fs->n_fatent - 2) clst = 0; + scl = val = clst; ctr = 0; + for (;;) { + if (move_window(fs, fs->database + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF; /* (assuming bitmap is located top of the cluster heap) */ + i = val / 8 % SS(fs); bm = 1 << (val % 8); + do { + do { + bv = fs->win[i] & bm; bm <<= 1; /* Get bit value */ + if (++val >= fs->n_fatent - 2) { /* Next cluster (with wrap-around) */ + val = 0; bm = 0; i = SS(fs); + } + if (!bv) { /* Is it a free cluster? */ + if (++ctr == ncl) return scl + 2; /* Check if run length is sufficient for required */ + } else { + scl = val; ctr = 0; /* Encountered a cluster in-use, restart to scan */ + } + if (val == clst) return 0; /* All cluster scanned? */ + } while (bm); + bm = 1; + } while (++i < SS(fs)); + } +} + + +/*----------------------------------------*/ +/* Set/Clear a block of allocation bitmap */ +/*----------------------------------------*/ + +static +FRESULT change_bitmap ( + FATFS* fs, /* File system object */ + DWORD clst, /* Cluster number to change from */ + DWORD ncl, /* Number of clusters to be changed */ + int bv /* bit value to be set (0 or 1) */ +) +{ + BYTE bm; + UINT i; + DWORD sect; + + clst -= 2; /* The first bit corresponds to cluster #2 */ + sect = fs->database + clst / 8 / SS(fs); /* Sector address (assuming bitmap is located top of the cluster heap) */ + i = clst / 8 % SS(fs); /* Byte offset in the sector */ + bm = 1 << (clst % 8); /* Bit mask in the byte */ + for (;;) { + if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR; + do { + do { + if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR; /* Is the bit expected value? */ + fs->win[i] ^= bm; /* Flip the bit */ + fs->wflag = 1; + if (--ncl == 0) return FR_OK; /* All bits processed? */ + } while (bm <<= 1); /* Next bit */ + bm = 1; + } while (++i < SS(fs)); /* Next byte */ + i = 0; + } +} + + +/*---------------------------------------------*/ +/* Fill the first fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_first_frag ( + _FDID* obj /* Pointer to the corresponding object */ +) +{ + FRESULT res; + DWORD cl, n; + + if (obj->stat == 3) { /* Has the object been changed 'fragmented'? */ + for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */ + res = put_fat(obj->fs, cl, cl + 1); + if (res != FR_OK) return res; + } + obj->stat = 0; /* Change status 'FAT chain is valid' */ + } + return FR_OK; +} + + +/*---------------------------------------------*/ +/* Fill the last fragment of the FAT chain */ +/*---------------------------------------------*/ + +static +FRESULT fill_last_frag ( + _FDID* obj, /* Pointer to the corresponding object */ + DWORD lcl, /* Last cluster of the fragment */ + DWORD term /* Value to set the last FAT entry */ +) +{ + FRESULT res; + + while (obj->n_frag > 0) { /* Create the last chain on the FAT */ + res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term); + if (res != FR_OK) return res; + obj->n_frag--; + } + return FR_OK; +} + +#endif /* _FS_EXFAT && !_FS_READONLY */ + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT handling - Remove a cluster chain */ +/*-----------------------------------------------------------------------*/ +static +FRESULT remove_chain ( /* FR_OK(0):succeeded, !=0:error */ + _FDID* obj, /* Corresponding object */ + DWORD clst, /* Cluster to remove a chain from */ + DWORD pclst /* Previous cluster of clst (0:an entire chain) */ +) +{ + FRESULT res = FR_OK; + DWORD nxt; + FATFS *fs = obj->fs; +#if _FS_EXFAT || _USE_TRIM + DWORD scl = clst, ecl = clst; +#endif +#if _USE_TRIM + DWORD rt[2]; +#endif + + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Check if in valid range */ + + /* Mark the previous cluster 'EOC' on the FAT if it exists */ + if (pclst && (!_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) { + res = put_fat(fs, pclst, 0xFFFFFFFF); + if (res != FR_OK) return res; + } + + /* Remove the chain */ + do { + nxt = get_fat(obj, clst); /* Get cluster status */ + if (nxt == 0) break; /* Empty cluster? */ + if (nxt == 1) return FR_INT_ERR; /* Internal error? */ + if (nxt == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error? */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + res = put_fat(fs, clst, 0); /* Mark the cluster 'free' on the FAT */ + if (res != FR_OK) return res; + } + if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst++; + fs->fsi_flag |= 1; + } +#if _FS_EXFAT || _USE_TRIM + if (ecl + 1 == nxt) { /* Is next cluster contiguous? */ + ecl = nxt; + } else { /* End of contiguous cluster block */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */ + if (res != FR_OK) return res; + } +#endif +#if _USE_TRIM + rt[0] = clust2sect(fs, scl); /* Start sector */ + rt[1] = clust2sect(fs, ecl) + fs->csize - 1; /* End sector */ + disk_ioctl(fs->drv, CTRL_TRIM, rt); /* Inform device the block can be erased */ +#endif + scl = ecl = nxt; + } +#endif + clst = nxt; /* Next cluster */ + } while (clst < fs->n_fatent); /* Repeat while not the last link */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + if (pclst == 0) { /* Does the object have no chain? */ + obj->stat = 0; /* Change the object status 'initial' */ + } else { + if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) { /* Did the chain get contiguous? */ + obj->stat = 2; /* Change the object status 'contiguous' */ + } + } + } +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* FAT handling - Stretch a chain or Create a new chain */ +/*-----------------------------------------------------------------------*/ +static +DWORD create_chain ( /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */ + _FDID* obj, /* Corresponding object */ + DWORD clst /* Cluster# to stretch, 0:Create a new chain */ +) +{ + DWORD cs, ncl, scl; + FRESULT res; + FATFS *fs = obj->fs; + + + if (clst == 0) { /* Create a new chain */ + scl = fs->last_clst; /* Get suggested cluster to start from */ + if (scl == 0 || scl >= fs->n_fatent) scl = 1; + } + else { /* Stretch current chain */ + cs = get_fat(obj, clst); /* Check the cluster status */ + if (cs < 2) return 1; /* Invalid FAT value */ + if (cs == 0xFFFFFFFF) return cs; /* A disk error occurred */ + if (cs < fs->n_fatent) return cs; /* It is already followed by next cluster */ + scl = clst; + } + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + ncl = find_bitmap(fs, scl, 1); /* Find a free cluster */ + if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl; /* No free cluster or hard error? */ + res = change_bitmap(fs, ncl, 1, 1); /* Mark the cluster 'in use' */ + if (res == FR_INT_ERR) return 1; + if (res == FR_DISK_ERR) return 0xFFFFFFFF; + if (clst == 0) { /* Is it a new chain? */ + obj->stat = 2; /* Set status 'contiguous' */ + } else { /* It is a stretched chain */ + if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */ + obj->n_cont = scl - obj->sclust; /* Set size of the contiguous part */ + obj->stat = 3; /* Change status 'just fragmented' */ + } + } + if (obj->stat != 2) { /* Is the file non-contiguous? */ + if (ncl == clst + 1) { /* Is the cluster next to previous one? */ + obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2; /* Increment size of last framgent */ + } else { /* New fragment */ + if (obj->n_frag == 0) obj->n_frag = 1; + res = fill_last_frag(obj, clst, ncl); /* Fill last fragment on the FAT and link it to new one */ + if (res == FR_OK) obj->n_frag = 1; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + ncl = scl; /* Start cluster */ + for (;;) { + ncl++; /* Next cluster */ + if (ncl >= fs->n_fatent) { /* Check wrap-around */ + ncl = 2; + if (ncl > scl) return 0; /* No free cluster */ + } + cs = get_fat(obj, ncl); /* Get the cluster status */ + if (cs == 0) break; /* Found a free cluster */ + if (cs == 1 || cs == 0xFFFFFFFF) return cs; /* An error occurred */ + if (ncl == scl) return 0; /* No free cluster */ + } + res = put_fat(fs, ncl, 0xFFFFFFFF); /* Mark the new cluster 'EOC' */ + if (res == FR_OK && clst != 0) { + res = put_fat(fs, clst, ncl); /* Link it from the previous one if needed */ + } + } + + if (res == FR_OK) { /* Update FSINFO if function succeeded. */ + fs->last_clst = ncl; + if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--; + fs->fsi_flag |= 1; + } else { + ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1; /* Failed. Generate error status */ + } + + return ncl; /* Return new cluster number or error status */ +} + +#endif /* !_FS_READONLY */ + + + + +#if _USE_FASTSEEK +/*-----------------------------------------------------------------------*/ +/* FAT handling - Convert offset into cluster with link map table */ +/*-----------------------------------------------------------------------*/ + +static +DWORD clmt_clust ( /* <2:Error, >=2:Cluster number */ + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File offset to be converted to cluster# */ +) +{ + DWORD cl, ncl, *tbl; + FATFS *fs = fp->obj.fs; + + + tbl = fp->cltbl + 1; /* Top of CLMT */ + cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */ + for (;;) { + ncl = *tbl++; /* Number of cluters in the fragment */ + if (ncl == 0) return 0; /* End of table? (error) */ + if (cl < ncl) break; /* In this fragment? */ + cl -= ncl; tbl++; /* Next fragment */ + } + return cl + *tbl; /* Return the cluster number */ +} + +#endif /* _USE_FASTSEEK */ + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Set directory index */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_sdi ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to directory object */ + DWORD ofs /* Offset of directory table */ +) +{ + DWORD csz, clst; + FATFS *fs = dp->obj.fs; + + + if (ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) { /* Check range of offset and alignment */ + return FR_INT_ERR; + } + dp->dptr = ofs; /* Set current offset */ + clst = dp->obj.sclust; /* Table start cluster (0:root) */ + if (clst == 0 && fs->fs_type >= FS_FAT32) { /* Replace cluster# 0 with root cluster# */ + clst = fs->dirbase; + if (_FS_EXFAT) dp->obj.stat = 0; /* exFAT: Root dir has an FAT chain */ + } + + if (clst == 0) { /* Static table (root-directory in FAT12/16) */ + if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR; /* Is index out of range? */ + dp->sect = fs->dirbase; + + } else { /* Dynamic table (sub-directory or root-directory in FAT32+) */ + csz = (DWORD)fs->csize * SS(fs); /* Bytes per cluster */ + while (ofs >= csz) { /* Follow cluster chain */ + clst = get_fat(&dp->obj, clst); /* Get next cluster */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR; /* Reached to end of table or internal error */ + ofs -= csz; + } + dp->sect = clust2sect(fs, clst); + } + dp->clust = clst; /* Current cluster# */ + if (!dp->sect) return FR_INT_ERR; + dp->sect += ofs / SS(fs); /* Sector# of the directory entry */ + dp->dir = fs->win + (ofs % SS(fs)); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Move directory table index next */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_next ( /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */ + DIR* dp, /* Pointer to the directory object */ + int stretch /* 0: Do not stretch table, 1: Stretch table if needed */ +) +{ + DWORD ofs, clst; + FATFS *fs = dp->obj.fs; +#if !_FS_READONLY + UINT n; +#endif + + ofs = dp->dptr + SZDIRE; /* Next entry */ + if (!dp->sect || ofs >= (DWORD)((_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) return FR_NO_FILE; /* Report EOT when offset has reached max value */ + + if (ofs % SS(fs) == 0) { /* Sector changed? */ + dp->sect++; /* Next sector */ + + if (!dp->clust) { /* Static table */ + if (ofs / SZDIRE >= fs->n_rootdir) { /* Report EOT if it reached end of static table */ + dp->sect = 0; return FR_NO_FILE; + } + } + else { /* Dynamic table */ + if ((ofs / SS(fs) & (fs->csize - 1)) == 0) { /* Cluster changed? */ + clst = get_fat(&dp->obj, dp->clust); /* Get next cluster */ + if (clst <= 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + if (clst >= fs->n_fatent) { /* Reached end of dynamic table */ +#if !_FS_READONLY + if (!stretch) { /* If no stretch, report EOT */ + dp->sect = 0; return FR_NO_FILE; + } + clst = create_chain(&dp->obj, dp->clust); /* Allocate a cluster */ + if (clst == 0) return FR_DENIED; /* No free cluster */ + if (clst == 1) return FR_INT_ERR; /* Internal error */ + if (clst == 0xFFFFFFFF) return FR_DISK_ERR; /* Disk error */ + /* Clean-up the stretched table */ + if (_FS_EXFAT) dp->obj.stat |= 4; /* The directory needs to be updated */ + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; /* Flush disk access window */ + mem_set(fs->win, 0, SS(fs)); /* Clear window buffer */ + for (n = 0, fs->winsect = clust2sect(fs, clst); n < fs->csize; n++, fs->winsect++) { /* Fill the new cluster with 0 */ + fs->wflag = 1; + if (sync_window(fs) != FR_OK) return FR_DISK_ERR; + } + fs->winsect -= n; /* Restore window offset */ +#else + if (!stretch) dp->sect = 0; /* (this line is to suppress compiler warning) */ + dp->sect = 0; return FR_NO_FILE; /* Report EOT */ +#endif + } + dp->clust = clst; /* Initialize data for new cluster */ + dp->sect = clust2sect(fs, clst); + } + } + } + dp->dptr = ofs; /* Current entry */ + dp->dir = fs->win + ofs % SS(fs); /* Pointer to the entry in the win[] */ + + return FR_OK; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Directory handling - Reserve a block of directory entries */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_alloc ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp, /* Pointer to the directory object */ + UINT nent /* Number of contiguous entries to allocate */ +) +{ + FRESULT res; + UINT n; + FATFS *fs = dp->obj.fs; + + + res = dir_sdi(dp, 0); + if (res == FR_OK) { + n = 0; + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; +#if _FS_EXFAT + if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) { +#else + if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) { +#endif + if (++n == nent) break; /* A block of contiguous free entries is found */ + } else { + n = 0; /* Not a blank entry. Restart to search */ + } + res = dir_next(dp, 1); + } while (res == FR_OK); /* Next entry with table stretch enabled */ + } + + if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */ + return res; +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* FAT: Directory handling - Load/Store start cluster number */ +/*-----------------------------------------------------------------------*/ + +static +DWORD ld_clust ( /* Returns the top cluster value of the SFN entry */ + FATFS* fs, /* Pointer to the fs object */ + const BYTE* dir /* Pointer to the key entry */ +) +{ + DWORD cl; + + cl = ld_word(dir + DIR_FstClusLO); + if (fs->fs_type == FS_FAT32) { + cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16; + } + + return cl; +} + + +#if !_FS_READONLY +static +void st_clust ( + FATFS* fs, /* Pointer to the fs object */ + BYTE* dir, /* Pointer to the key entry */ + DWORD cl /* Value to be set */ +) +{ + st_word(dir + DIR_FstClusLO, (WORD)cl); + if (fs->fs_type == FS_FAT32) { + st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16)); + } +} +#endif + + + +#if _USE_LFN != 0 +/*------------------------------------------------------------------------*/ +/* FAT-LFN: LFN handling */ +/*------------------------------------------------------------------------*/ +static +const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30}; /* Offset of LFN characters in the directory entry */ + + +/*--------------------------------------------------------*/ +/* FAT-LFN: Compare a part of file name with an LFN entry */ +/*--------------------------------------------------------*/ +static +int cmp_lfn ( /* 1:matched, 0:not matched */ + const WCHAR* lfnbuf, /* Pointer to the LFN working buffer to be compared */ + BYTE* dir /* Pointer to the directory entry containing the part of LFN */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO */ + + i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) { /* Compare it */ + return 0; /* Not matched */ + } + wc = uc; + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0; /* Last segment matched but different length */ + + return 1; /* The part of LFN matched */ +} + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------*/ +/* FAT-LFN: Pick a part of file name from an LFN entry */ +/*-----------------------------------------------------*/ +static +int pick_lfn ( /* 1:succeeded, 0:buffer overflow or invalid LFN entry */ + WCHAR* lfnbuf, /* Pointer to the LFN working buffer */ + BYTE* dir /* Pointer to the LFN entry */ +) +{ + UINT i, s; + WCHAR wc, uc; + + + if (ld_word(dir + LDIR_FstClusLO) != 0) return 0; /* Check LDIR_FstClusLO is 0 */ + + i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */ + + for (wc = 1, s = 0; s < 13; s++) { /* Process all characters in the entry */ + uc = ld_word(dir + LfnOfs[s]); /* Pick an LFN character */ + if (wc) { + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i++] = wc = uc; /* Store it */ + } else { + if (uc != 0xFFFF) return 0; /* Check filler */ + } + } + + if (dir[LDIR_Ord] & LLEF) { /* Put terminator if it is the last LFN part */ + if (i >= _MAX_LFN) return 0; /* Buffer overflow? */ + lfnbuf[i] = 0; + } + + return 1; /* The part of LFN is valid */ +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------*/ +/* FAT-LFN: Create an entry of LFN entries */ +/*-----------------------------------------*/ +static +void put_lfn ( + const WCHAR* lfn, /* Pointer to the LFN */ + BYTE* dir, /* Pointer to the LFN entry to be created */ + BYTE ord, /* LFN order (1-20) */ + BYTE sum /* Checksum of the corresponding SFN */ +) +{ + UINT i, s; + WCHAR wc; + + + dir[LDIR_Chksum] = sum; /* Set checksum */ + dir[LDIR_Attr] = AM_LFN; /* Set attribute. LFN entry */ + dir[LDIR_Type] = 0; + st_word(dir + LDIR_FstClusLO, 0); + + i = (ord - 1) * 13; /* Get offset in the LFN working buffer */ + s = wc = 0; + do { + if (wc != 0xFFFF) wc = lfn[i++]; /* Get an effective character */ + st_word(dir + LfnOfs[s], wc); /* Put it */ + if (wc == 0) wc = 0xFFFF; /* Padding characters for left locations */ + } while (++s < 13); + if (wc == 0xFFFF || !lfn[i]) ord |= LLEF; /* Last LFN part is the start of LFN sequence */ + dir[LDIR_Ord] = ord; /* Set the LFN order */ +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LFN != 0 */ + + + +#if _USE_LFN != 0 && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Create a Numbered SFN */ +/*-----------------------------------------------------------------------*/ + +static +void gen_numname ( + BYTE* dst, /* Pointer to the buffer to store numbered SFN */ + const BYTE* src, /* Pointer to SFN */ + const WCHAR* lfn, /* Pointer to LFN */ + UINT seq /* Sequence number */ +) +{ + BYTE ns[8], c; + UINT i, j; + WCHAR wc; + DWORD sr; + + + mem_cpy(dst, src, 11); + + if (seq > 5) { /* In case of many collisions, generate a hash number instead of sequential number */ + sr = seq; + while (*lfn) { /* Create a CRC */ + wc = *lfn++; + for (i = 0; i < 16; i++) { + sr = (sr << 1) + (wc & 1); + wc >>= 1; + if (sr & 0x10000) sr ^= 0x11021; + } + } + seq = (UINT)sr; + } + + /* itoa (hexdecimal) */ + i = 7; + do { + c = (BYTE)((seq % 16) + '0'); + if (c > '9') c += 7; + ns[i--] = c; + seq /= 16; + } while (seq); + ns[i] = '~'; + + /* Append the number */ + for (j = 0; j < i && dst[j] != ' '; j++) { + if (IsDBCS1(dst[j])) { + if (j == i - 1) break; + j++; + } + } + do { + dst[j++] = (i < 8) ? ns[i++] : ' '; + } while (j < 8); +} +#endif /* _USE_LFN != 0 && !_FS_READONLY */ + + + +#if _USE_LFN != 0 +/*-----------------------------------------------------------------------*/ +/* FAT-LFN: Calculate checksum of an SFN entry */ +/*-----------------------------------------------------------------------*/ + +static +BYTE sum_sfn ( + const BYTE* dir /* Pointer to the SFN entry */ +) +{ + BYTE sum = 0; + UINT n = 11; + + do { + sum = (sum >> 1) + (sum << 7) + *dir++; + } while (--n); + return sum; +} + +#endif /* _USE_LFN != 0 */ + + + +#if _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* exFAT: Checksum */ +/*-----------------------------------------------------------------------*/ + +static +WORD xdir_sum ( /* Get checksum of the directoly block */ + const BYTE* dir /* Directory entry block to be calculated */ +) +{ + UINT i, szblk; + WORD sum; + + + szblk = (dir[XDIR_NumSec] + 1) * SZDIRE; + for (i = sum = 0; i < szblk; i++) { + if (i == XDIR_SetSum) { /* Skip sum field */ + i++; + } else { + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i]; + } + } + return sum; +} + + + +static +WORD xname_sum ( /* Get check sum (to be used as hash) of the name */ + const WCHAR* name /* File name to be calculated */ +) +{ + WCHAR chr; + WORD sum = 0; + + + while ((chr = *name++) != 0) { + chr = ff_wtoupper(chr); /* File name needs to be ignored case */ + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF); + sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8); + } + return sum; +} + + +#if !_FS_READONLY && _USE_MKFS +static +DWORD xsum32 ( + BYTE dat, /* Data to be sumed */ + DWORD sum /* Previous value */ +) +{ + sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat; + return sum; +} +#endif + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*------------------------------------------------------*/ +/* exFAT: Get object information from a directory block */ +/*------------------------------------------------------*/ + +static +void get_xdir_info ( + BYTE* dirb, /* Pointer to the direcotry entry block 85+C0+C1s */ + FILINFO* fno /* Buffer to store the extracted file information */ +) +{ + UINT di, si; + WCHAR w; +#if !_LFN_UNICODE + UINT nc; +#endif + + /* Get file name */ + di = 0; +#if _LFN_UNICODE + for (si = SZDIRE * 2; di < dirb[XDIR_NumName]; si += 2, di++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ld_word(dirb + si); /* Get a character */ + if (di >= _MAX_LFN) { di = 0; break; } /* Buffer overflow --> inaccessible object name */ + fno->fname[di] = w; /* Store it */ + } +#else + for (si = SZDIRE * 2, nc = 0; nc < dirb[XDIR_NumName]; si += 2, nc++) { + if ((si % SZDIRE) == 0) si += 2; /* Skip entry type field */ + w = ff_convert(ld_word(dirb + si), 0); /* Get a character and Unicode -> OEM */ + if (_DF1S && w >= 0x100) { /* Is it a double byte char? (always false at SBCS cfg) */ + fno->fname[di++] = (char)(w >> 8); /* Put 1st byte of the DBC */ + } + if (w == 0 || di >= _MAX_LFN) { di = 0; break; } /* Invalid char or buffer overflow --> inaccessible object name */ + fno->fname[di++] = (char)w; + } +#endif + if (di == 0) fno->fname[di++] = '?'; /* Inaccessible object name? */ + fno->fname[di] = 0; /* Terminate file name */ + + fno->altname[0] = 0; /* No SFN */ + fno->fattrib = dirb[XDIR_Attr]; /* Attribute */ + fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize); /* Size */ + fno->ftime = ld_word(dirb + XDIR_ModTime + 0); /* Time */ + fno->fdate = ld_word(dirb + XDIR_ModTime + 2); /* Date */ +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + +/*-----------------------------------*/ +/* exFAT: Get a directry entry block */ +/*-----------------------------------*/ + +static +FRESULT load_xdir ( /* FR_INT_ERR: invalid entry block */ + DIR* dp /* Pointer to the reading direcotry object pointing the 85 entry */ +) +{ + FRESULT res; + UINT i, sz_ent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the on-memory direcotry entry block 85+C0+C1s */ + + + /* Load 85 entry */ + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0x85) return FR_INT_ERR; + mem_cpy(dirb + 0, dp->dir, SZDIRE); + sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE; + if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR; + + /* Load C0 entry */ + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC0) return FR_INT_ERR; + mem_cpy(dirb + SZDIRE, dp->dir, SZDIRE); + if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR; + + /* Load C1 entries */ + i = SZDIRE * 2; /* C1 offset */ + do { + res = dir_next(dp, 0); + if (res != FR_OK) return res; + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) return res; + if (dp->dir[XDIR_Type] != 0xC1) return FR_INT_ERR; + if (i < MAXDIRB(_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE); + } while ((i += SZDIRE) < sz_ent); + + /* Sanity check (do it when accessible object name) */ + if (i <= MAXDIRB(_MAX_LFN)) { + if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR; + } + return FR_OK; +} + + +#if !_FS_READONLY || _FS_RPATH != 0 +/*------------------------------------------------*/ +/* exFAT: Load the object's directory entry block */ +/*------------------------------------------------*/ +static +FRESULT load_obj_dir ( + DIR* dp, /* Blank directory object to be used to access containing direcotry */ + const _FDID* obj /* Object with its containing directory information */ +) +{ + FRESULT res; + + /* Open object containing directory */ + dp->obj.fs = obj->fs; + dp->obj.sclust = obj->c_scl; + dp->obj.stat = (BYTE)obj->c_size; + dp->obj.objsize = obj->c_size & 0xFFFFFF00; + dp->blk_ofs = obj->c_ofs; + + res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */ + if (res == FR_OK) { + res = load_xdir(dp); /* Load the object's entry block */ + } + return res; +} +#endif + + +#if !_FS_READONLY +/*-----------------------------------------------*/ +/* exFAT: Store the directory block to the media */ +/*-----------------------------------------------*/ +static +FRESULT store_xdir ( + DIR* dp /* Pointer to the direcotry object */ +) +{ + FRESULT res; + UINT nent; + BYTE* dirb = dp->obj.fs->dirbuf; /* Pointer to the direcotry entry block 85+C0+C1s */ + + /* Create set sum */ + st_word(dirb + XDIR_SetSum, xdir_sum(dirb)); + nent = dirb[XDIR_NumSec] + 1; + + /* Store the set of directory to the volume */ + res = dir_sdi(dp, dp->blk_ofs); + while (res == FR_OK) { + res = move_window(dp->obj.fs, dp->sect); + if (res != FR_OK) break; + mem_cpy(dp->dir, dirb, SZDIRE); + dp->obj.fs->wflag = 1; + if (--nent == 0) break; + dirb += SZDIRE; + res = dir_next(dp, 0); + } + return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR; +} + + + +/*-------------------------------------------*/ +/* exFAT: Create a new directory enrty block */ +/*-------------------------------------------*/ + +static +void create_xdir ( + BYTE* dirb, /* Pointer to the direcotry entry block buffer */ + const WCHAR* lfn /* Pointer to the nul terminated file name */ +) +{ + UINT i; + BYTE nb, nc; + WCHAR chr; + + + /* Create 85+C0 entry */ + mem_set(dirb, 0, 2 * SZDIRE); + dirb[XDIR_Type] = 0x85; + dirb[XDIR_Type + SZDIRE] = 0xC0; + + /* Create C1 entries */ + nc = 0; nb = 1; chr = 1; i = SZDIRE * 2; + do { + dirb[i++] = 0xC1; dirb[i++] = 0; /* Entry type C1 */ + do { /* Fill name field */ + if (chr && (chr = lfn[nc]) != 0) nc++; /* Get a character if exist */ + st_word(dirb + i, chr); /* Store it */ + } while ((i += 2) % SZDIRE != 0); + nb++; + } while (lfn[nc]); /* Fill next entry if any char follows */ + + dirb[XDIR_NumName] = nc; /* Set name length */ + dirb[XDIR_NumSec] = nb; /* Set block length */ + st_word(dirb + XDIR_NameHash, xname_sum(lfn)); /* Set name hash */ +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_EXFAT */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 || _USE_LABEL || _FS_EXFAT +/*-----------------------------------------------------------------------*/ +/* Read an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_read ( + DIR* dp, /* Pointer to the directory object */ + int vol /* Filtered by 0:file/directory or 1:volume label */ +) +{ + FRESULT res = FR_NO_FILE; + FATFS *fs = dp->obj.fs; + BYTE a, c; +#if _USE_LFN != 0 + BYTE ord = 0xFF, sum = 0xFF; +#endif + + while (dp->sect) { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; /* Test for the entry type */ + if (c == 0) { + res = FR_NO_FILE; break; /* Reached to end of the directory */ + } +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + if (_USE_LABEL && vol) { + if (c == 0x83) break; /* Volume label entry? */ + } else { + if (c == 0x85) { /* Start of the file entry block? */ + dp->blk_ofs = dp->dptr; /* Get location of the block */ + res = load_xdir(dp); /* Load the entry block */ + if (res == FR_OK) { + dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */ + } + break; + } + } + } else +#endif + { /* On the FAT12/16/32 volume */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; /* Get attribute */ +#if _USE_LFN != 0 /* LFN configuration */ + if (c == DDEM || c == '.' || (int)((a & ~AM_ARC) == AM_VOL) != vol) { /* An entry without valid data */ + ord = 0xFF; + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (c & LLEF) { /* Is it start of an LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; + dp->blk_ofs = dp->dptr; + } + /* Check LFN validity and capture it */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } else { /* An SFN entry is found */ + if (ord || sum != sum_sfn(dp->dir)) { /* Is there a valid LFN? */ + dp->blk_ofs = 0xFFFFFFFF; /* It has no LFN. */ + } + break; + } + } +#else /* Non LFN configuration */ + if (c != DDEM && c != '.' && a != AM_LFN && (int)((a & ~AM_ARC) == AM_VOL) == vol) { /* Is it a valid entry? */ + break; + } +#endif + } + res = dir_next(dp, 0); /* Next entry */ + if (res != FR_OK) break; + } + + if (res != FR_OK) dp->sect = 0; /* Terminate the read operation on error or EOT */ + return res; +} + +#endif /* _FS_MINIMIZE <= 1 || _USE_LABEL || _FS_RPATH >= 2 */ + + + +/*-----------------------------------------------------------------------*/ +/* Directory handling - Find an object in the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_find ( /* FR_OK(0):succeeded, !=0:error */ + DIR* dp /* Pointer to the directory object with the file name */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; + BYTE c; +#if _USE_LFN != 0 + BYTE a, ord, sum; +#endif + + res = dir_sdi(dp, 0); /* Rewind directory object */ + if (res != FR_OK) return res; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + BYTE nc; + UINT di, ni; + WORD hash = xname_sum(fs->lfnbuf); /* Hash value of the name to find */ + + while ((res = dir_read(dp, 0)) == FR_OK) { /* Read an item */ +#if _MAX_LFN < 255 + if (fs->dirbuf[XDIR_NumName] > _MAX_LFN) continue; /* Skip comparison if inaccessible object name */ +#endif + if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue; /* Skip comparison if hash mismatched */ + for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */ + if ((di % SZDIRE) == 0) di += 2; + if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break; + } + if (nc == 0 && !fs->lfnbuf[ni]) break; /* Name matched? */ + } + return res; + } +#endif + /* On the FAT12/16/32 volume */ +#if _USE_LFN != 0 + ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ +#endif + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + c = dp->dir[DIR_Name]; + if (c == 0) { res = FR_NO_FILE; break; } /* Reached to end of table */ +#if _USE_LFN != 0 /* LFN configuration */ + dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK; + if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) { /* An entry without valid data */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } else { + if (a == AM_LFN) { /* An LFN entry is found */ + if (!(dp->fn[NSFLAG] & NS_NOLFN)) { + if (c & LLEF) { /* Is it start of LFN sequence? */ + sum = dp->dir[LDIR_Chksum]; + c &= (BYTE)~LLEF; ord = c; /* LFN start order */ + dp->blk_ofs = dp->dptr; /* Start offset of LFN */ + } + /* Check validity of the LFN entry and compare it with given name */ + ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF; + } + } else { /* An SFN entry is found */ + if (!ord && sum == sum_sfn(dp->dir)) break; /* LFN matched? */ + if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* SFN matched? */ + ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF; /* Reset LFN sequence */ + } + } +#else /* Non LFN configuration */ + dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK; + if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break; /* Is it a valid entry? */ +#endif + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + + return res; +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Register an object to the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_register ( /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */ + DIR* dp /* Target directory with object name to be created */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + UINT n, nlen, nent; + BYTE sn[12], sum; + + + if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME; /* Check name validity */ + for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ; /* Get lfn length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + DIR dj; + + nent = (nlen + 14) / 15 + 2; /* Number of entries to allocate (85+C0+C1s) */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res != FR_OK) return res; + dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1); /* Set the allocated entry block offset */ + + if (dp->obj.sclust != 0 && (dp->obj.stat & 4)) { /* Has the sub-directory been stretched? */ + dp->obj.objsize += (DWORD)fs->csize * SS(fs); /* Increase the directory size by cluster size */ + res = fill_first_frag(&dp->obj); /* Fill first fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + if (res != FR_OK) return res; + res = load_obj_dir(&dj, &dp->obj); /* Load the object status */ + if (res != FR_OK) return res; + st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize); /* Update the allocation status */ + st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize); + fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1; + res = store_xdir(&dj); /* Store the object status */ + if (res != FR_OK) return res; + } + + create_xdir(fs->dirbuf, fs->lfnbuf); /* Create on-memory directory block to be written later */ + return FR_OK; + } +#endif + /* On the FAT12/16/32 volume */ + mem_cpy(sn, dp->fn, 12); + if (sn[NSFLAG] & NS_LOSS) { /* When LFN is out of 8.3 format, generate a numbered name */ + dp->fn[NSFLAG] = NS_NOLFN; /* Find only SFN */ + for (n = 1; n < 100; n++) { + gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */ + res = dir_find(dp); /* Check if the name collides with existing SFN */ + if (res != FR_OK) break; + } + if (n == 100) return FR_DENIED; /* Abort if too many collisions */ + if (res != FR_NO_FILE) return res; /* Abort if the result is other than 'not collided' */ + dp->fn[NSFLAG] = sn[NSFLAG]; + } + + /* Create an SFN with/without LFNs. */ + nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1; /* Number of entries to allocate */ + res = dir_alloc(dp, nent); /* Allocate entries */ + if (res == FR_OK && --nent) { /* Set LFN entry if needed */ + res = dir_sdi(dp, dp->dptr - nent * SZDIRE); + if (res == FR_OK) { + sum = sum_sfn(dp->fn); /* Checksum value of the SFN tied to the LFN */ + do { /* Store LFN entries in bottom first */ + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum); + fs->wflag = 1; + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK && --nent); + } + } + +#else /* Non LFN configuration */ + res = dir_alloc(dp, 1); /* Allocate an entry for SFN */ + +#endif + + /* Set SFN entry */ + if (res == FR_OK) { + res = move_window(fs, dp->sect); + if (res == FR_OK) { + mem_set(dp->dir, 0, SZDIRE); /* Clean the entry */ + mem_cpy(dp->dir + DIR_Name, dp->fn, 11); /* Put SFN */ +#if _USE_LFN != 0 + dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT); /* Put NT flag */ +#endif + fs->wflag = 1; + } + } + + return res; +} + +#endif /* !_FS_READONLY */ + + + +#if !_FS_READONLY && _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Remove an object from the directory */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT dir_remove ( /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */ + DIR* dp /* Directory object pointing the entry to be removed */ +) +{ + FRESULT res; + FATFS *fs = dp->obj.fs; +#if _USE_LFN != 0 /* LFN configuration */ + DWORD last = dp->dptr; + + res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs); /* Goto top of the entry block if LFN is exist */ + if (res == FR_OK) { + do { + res = move_window(fs, dp->sect); + if (res != FR_OK) break; + /* Mark an entry 'deleted' */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + dp->dir[XDIR_Type] &= 0x7F; + } else { /* On the FAT12/16/32 volume */ + dp->dir[DIR_Name] = DDEM; + } + fs->wflag = 1; + if (dp->dptr >= last) break; /* If reached last entry then all entries of the object has been deleted. */ + res = dir_next(dp, 0); /* Next entry */ + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR; + } +#else /* Non LFN configuration */ + + res = move_window(fs, dp->sect); + if (res == FR_OK) { + dp->dir[DIR_Name] = DDEM; + fs->wflag = 1; + } +#endif + + return res; +} + +#endif /* !_FS_READONLY && _FS_MINIMIZE == 0 */ + + + +#if _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 +/*-----------------------------------------------------------------------*/ +/* Get file information from directory entry */ +/*-----------------------------------------------------------------------*/ + +static +void get_fileinfo ( /* No return code */ + DIR* dp, /* Pointer to the directory object */ + FILINFO* fno /* Pointer to the file information to be filled */ +) +{ + UINT i, j; + TCHAR c; + DWORD tm; +#if _USE_LFN != 0 + WCHAR w, lfv; + FATFS *fs = dp->obj.fs; +#endif + + + fno->fname[0] = 0; /* Invaidate file info */ + if (!dp->sect) return; /* Exit if read pointer has reached end of directory */ + +#if _USE_LFN != 0 /* LFN configuration */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + get_xdir_info(fs->dirbuf, fno); + return; + } else +#endif + { /* On the FAT12/16/32 volume */ + if (dp->blk_ofs != 0xFFFFFFFF) { /* Get LFN if available */ + i = j = 0; + while ((w = fs->lfnbuf[j++]) != 0) { /* Get an LFN character */ +#if !_LFN_UNICODE + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) { i = 0; break; } /* No LFN if it could not be converted */ + if (_DF1S && w >= 0x100) { /* Put 1st byte if it is a DBC (always false at SBCS cfg) */ + fno->fname[i++] = (char)(w >> 8); + } +#endif + if (i >= _MAX_LFN) { i = 0; break; } /* No LFN if buffer overflow */ + fno->fname[i++] = (TCHAR)w; + } + fno->fname[i] = 0; /* Terminate the LFN */ + } + } + + i = j = 0; + lfv = fno->fname[i]; /* LFN is exist if non-zero */ + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) { /* Insert a . if extension is exist */ + if (!lfv) fno->fname[j] = '.'; + fno->altname[j++] = '.'; + } +#if _LFN_UNICODE + if (IsDBCS1(c) && i != 8 && i != 11 && IsDBCS2(dp->dir[i])) { + c = c << 8 | dp->dir[i++]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif + fno->altname[j] = c; + if (!lfv) { + if (IsUpper(c) && (dp->dir[DIR_NTres] & ((i >= 9) ? NS_EXT : NS_BODY))) { + c += 0x20; /* To lower */ + } + fno->fname[j] = c; + } + j++; + } + if (!lfv) { + fno->fname[j] = 0; + if (!dp->dir[DIR_NTres]) j = 0; /* Altname is no longer needed if neither LFN nor case info is exist. */ + } + fno->altname[j] = 0; /* Terminate the SFN */ + +#else /* Non-LFN configuration */ + i = j = 0; + while (i < 11) { /* Copy name body and extension */ + c = (TCHAR)dp->dir[i++]; + if (c == ' ') continue; /* Skip padding spaces */ + if (c == RDDEM) c = (TCHAR)DDEM; /* Restore replaced DDEM character */ + if (i == 9) fno->fname[j++] = '.'; /* Insert a . if extension is exist */ + fno->fname[j++] = c; + } + fno->fname[j] = 0; +#endif + + fno->fattrib = dp->dir[DIR_Attr]; /* Attribute */ + fno->fsize = ld_dword(dp->dir + DIR_FileSize); /* Size */ + tm = ld_dword(dp->dir + DIR_ModTime); /* Timestamp */ + fno->ftime = (WORD)tm; fno->fdate = (WORD)(tm >> 16); +} + +#endif /* _FS_MINIMIZE <= 1 || _FS_RPATH >= 2 */ + + + +#if _USE_FIND && _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Pattern matching */ +/*-----------------------------------------------------------------------*/ + +static +WCHAR get_achar ( /* Get a character and advances ptr 1 or 2 */ + const TCHAR** ptr /* Pointer to pointer to the SBCS/DBCS/Unicode string */ +) +{ +#if !_LFN_UNICODE + WCHAR chr; + + chr = (BYTE)*(*ptr)++; /* Get a byte */ + if (IsLower(chr)) chr -= 0x20; /* To upper ASCII char */ +#ifdef _EXCVT + if (chr >= 0x80) chr = ExCvt[chr - 0x80]; /* To upper SBCS extended char */ +#else + if (IsDBCS1(chr) && IsDBCS2(**ptr)) { /* Get DBC 2nd byte if needed */ + chr = chr << 8 | (BYTE)*(*ptr)++; + } +#endif + return chr; +#else + return ff_wtoupper(*(*ptr)++); /* Get a word and to upper */ +#endif +} + + +static +int pattern_matching ( /* 0:not matched, 1:matched */ + const TCHAR* pat, /* Matching pattern */ + const TCHAR* nam, /* String to be tested */ + int skip, /* Number of pre-skip chars (number of ?s) */ + int inf /* Infinite search (* specified) */ +) +{ + const TCHAR *pp, *np; + WCHAR pc, nc; + int nm, nx; + + + while (skip--) { /* Pre-skip name chars */ + if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */ + } + if (!*pat && inf) return 1; /* (short circuit) */ + + do { + pp = pat; np = nam; /* Top of pattern and name to match */ + for (;;) { + if (*pp == '?' || *pp == '*') { /* Wildcard? */ + nm = nx = 0; + do { /* Analyze the wildcard chars */ + if (*pp++ == '?') nm++; else nx = 1; + } while (*pp == '?' || *pp == '*'); + if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */ + nc = *np; break; /* Branch mismatched */ + } + pc = get_achar(&pp); /* Get a pattern char */ + nc = get_achar(&np); /* Get a name char */ + if (pc != nc) break; /* Branch mismatched? */ + if (pc == 0) return 1; /* Branch matched? (matched at end of both strings) */ + } + get_achar(&nam); /* nam++ */ + } while (inf && nc); /* Retry until end of name if infinite search is specified */ + + return 0; +} + +#endif /* _USE_FIND && _FS_MINIMIZE <= 1 */ + + + +/*-----------------------------------------------------------------------*/ +/* Pick a top segment and create the object name in directory form */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT create_name ( /* FR_OK: successful, FR_INVALID_NAME: could not create */ + DIR* dp, /* Pointer to the directory object */ + const TCHAR** path /* Pointer to pointer to the segment in the path string */ +) +{ +#if _USE_LFN != 0 /* LFN configuration */ + BYTE b, cf; + WCHAR w, *lfn; + UINT i, ni, si, di; + const TCHAR *p; + + /* Create LFN in Unicode */ + p = *path; lfn = dp->obj.fs->lfnbuf; si = di = 0; + for (;;) { + w = p[si++]; /* Get a character */ + if (w < ' ') break; /* Break if end of the path name */ + if (w == '/' || w == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (di >= _MAX_LFN) return FR_INVALID_NAME; /* Reject too long name */ +#if !_LFN_UNICODE + w &= 0xFF; + if (IsDBCS1(w)) { /* Check if it is a DBC 1st byte (always false on SBCS cfg) */ + b = (BYTE)p[si++]; /* Get 2nd byte */ + w = (w << 8) + b; /* Create a DBC */ + if (!IsDBCS2(b)) return FR_INVALID_NAME; /* Reject invalid sequence */ + } + w = ff_convert(w, 1); /* Convert ANSI/OEM to Unicode */ + if (!w) return FR_INVALID_NAME; /* Reject invalid code */ +#endif + if (w < 0x80 && chk_chr("\"*:<>\?|\x7F", w)) return FR_INVALID_NAME; /* Reject illegal characters for LFN */ + lfn[di++] = w; /* Store the Unicode character */ + } + *path = &p[si]; /* Return pointer to the next segment */ + cf = (w < ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ +#if _FS_RPATH != 0 + if ((di == 1 && lfn[di - 1] == '.') || + (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) { /* Is this segment a dot name? */ + lfn[di] = 0; + for (i = 0; i < 11; i++) /* Create dot name for SFN entry */ + dp->fn[i] = (i < di) ? '.' : ' '; + dp->fn[i] = cf | NS_DOT; /* This is a dot entry */ + return FR_OK; + } +#endif + while (di) { /* Snip off trailing spaces and dots if exist */ + w = lfn[di - 1]; + if (w != ' ' && w != '.') break; + di--; + } + lfn[di] = 0; /* LFN is created */ + if (di == 0) return FR_INVALID_NAME; /* Reject nul name */ + + /* Create SFN in directory form */ + mem_set(dp->fn, ' ', 11); + for (si = 0; lfn[si] == ' ' || lfn[si] == '.'; si++) ; /* Strip leading spaces and dots */ + if (si) cf |= NS_LOSS | NS_LFN; + while (di && lfn[di - 1] != '.') di--; /* Find extension (di<=si: no extension) */ + + i = b = 0; ni = 8; + for (;;) { + w = lfn[si++]; /* Get an LFN character */ + if (!w) break; /* Break on end of the LFN */ + if (w == ' ' || (w == '.' && si != di)) { /* Remove spaces and dots */ + cf |= NS_LOSS | NS_LFN; continue; + } + + if (i >= ni || si == di) { /* Extension or end of SFN */ + if (ni == 11) { /* Long extension */ + cf |= NS_LOSS | NS_LFN; break; + } + if (si != di) cf |= NS_LOSS | NS_LFN; /* Out of 8.3 format */ + if (si > di) break; /* No extension */ + si = di; i = 8; ni = 11; /* Enter extension section */ + b <<= 2; continue; + } + + if (w >= 0x80) { /* Non ASCII character */ +#ifdef _EXCVT + w = ff_convert(w, 0); /* Unicode -> OEM code */ + if (w) w = ExCvt[w - 0x80]; /* Convert extended character to upper (SBCS) */ +#else + w = ff_convert(ff_wtoupper(w), 0); /* Upper converted Unicode -> OEM code */ +#endif + cf |= NS_LFN; /* Force create LFN entry */ + } + + if (_DF1S && w >= 0x100) { /* Is this DBC? (always false at SBCS cfg) */ + if (i >= ni - 1) { + cf |= NS_LOSS | NS_LFN; i = ni; continue; + } + dp->fn[i++] = (BYTE)(w >> 8); + } else { /* SBC */ + if (!w || chk_chr("+,;=[]", w)) { /* Replace illegal characters for SFN */ + w = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */ + } else { + if (IsUpper(w)) { /* ASCII large capital */ + b |= 2; + } else { + if (IsLower(w)) { /* ASCII small capital */ + b |= 1; w -= 0x20; + } + } + } + } + dp->fn[i++] = (BYTE)w; + } + + if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + + if (ni == 8) b <<= 2; + if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN; /* Create LFN entry when there are composite capitals */ + if (!(cf & NS_LFN)) { /* When LFN is in 8.3 format without extended character, NT flags are created */ + if ((b & 0x03) == 0x01) cf |= NS_EXT; /* NT flag (Extension has only small capital) */ + if ((b & 0x0C) == 0x04) cf |= NS_BODY; /* NT flag (Filename has only small capital) */ + } + + dp->fn[NSFLAG] = cf; /* SFN is created */ + + return FR_OK; + + +#else /* _USE_LFN != 0 : Non-LFN configuration */ + BYTE c, d, *sfn; + UINT ni, si, i; + const char *p; + + /* Create file name in directory form */ + p = *path; sfn = dp->fn; + mem_set(sfn, ' ', 11); + si = i = 0; ni = 8; +#if _FS_RPATH != 0 + if (p[si] == '.') { /* Is this a dot entry? */ + for (;;) { + c = (BYTE)p[si++]; + if (c != '.' || si >= 3) break; + sfn[i++] = c; + } + if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME; + *path = p + si; /* Return pointer to the next segment */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT; /* Set last segment flag if end of the path */ + return FR_OK; + } +#endif + for (;;) { + c = (BYTE)p[si++]; + if (c <= ' ') break; /* Break if end of the path name */ + if (c == '/' || c == '\\') { /* Break if a separator is found */ + while (p[si] == '/' || p[si] == '\\') si++; /* Skip duplicated separator if exist */ + break; + } + if (c == '.' || i >= ni) { /* End of body or over size? */ + if (ni == 11 || c != '.') return FR_INVALID_NAME; /* Over size or invalid dot */ + i = 8; ni = 11; /* Goto extension */ + continue; + } + if (c >= 0x80) { /* Extended character? */ +#ifdef _EXCVT + c = ExCvt[c - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else +#if !_DF1S + return FR_INVALID_NAME; /* Reject extended characters (ASCII only cfg) */ +#endif +#endif + } + if (IsDBCS1(c)) { /* Check if it is a DBC 1st byte (always false at SBCS cfg.) */ + d = (BYTE)p[si++]; /* Get 2nd byte */ + if (!IsDBCS2(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */ + sfn[i++] = c; + sfn[i++] = d; + } else { /* SBC */ + if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME; /* Reject illegal chrs for SFN */ + if (IsLower(c)) c -= 0x20; /* To upper */ + sfn[i++] = c; + } + } + *path = p + si; /* Return pointer to the next segment */ + if (i == 0) return FR_INVALID_NAME; /* Reject nul string */ + + if (sfn[0] == DDEM) sfn[0] = RDDEM; /* If the first character collides with DDEM, replace it with RDDEM */ + sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0; /* Set last segment flag if end of the path */ + + return FR_OK; +#endif /* _USE_LFN != 0 */ +} + + + + +/*-----------------------------------------------------------------------*/ +/* Follow a file path */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT follow_path ( /* FR_OK(0): successful, !=0: error code */ + DIR* dp, /* Directory object to return last directory and found object */ + const TCHAR* path /* Full-path string to find a file or directory */ +) +{ + FRESULT res; + BYTE ns; + _FDID *obj = &dp->obj; + FATFS *fs = obj->fs; + + +#if _FS_RPATH != 0 + if (*path != '/' && *path != '\\') { /* Without heading separator */ + obj->sclust = fs->cdir; /* Start from current directory */ + } else +#endif + { /* With heading separator */ + while (*path == '/' || *path == '\\') path++; /* Strip heading separator */ + obj->sclust = 0; /* Start from root directory */ + } +#if _FS_EXFAT + obj->n_frag = 0; /* Invalidate last fragment counter of the object */ +#if _FS_RPATH != 0 + if (fs->fs_type == FS_EXFAT && obj->sclust) { /* Retrieve the sub-directory status if needed */ + DIR dj; + + obj->c_scl = fs->cdc_scl; + obj->c_size = fs->cdc_size; + obj->c_ofs = fs->cdc_ofs; + res = load_obj_dir(&dj, obj); + if (res != FR_OK) return res; + obj->objsize = ld_dword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } +#endif +#endif + + if ((UINT)*path < ' ') { /* Null path name is the origin directory itself */ + dp->fn[NSFLAG] = NS_NONAME; + res = dir_sdi(dp, 0); + + } else { /* Follow path */ + for (;;) { + res = create_name(dp, &path); /* Get a segment name of the path */ + if (res != FR_OK) break; + res = dir_find(dp); /* Find an object with the segment name */ + ns = dp->fn[NSFLAG]; + if (res != FR_OK) { /* Failed to find the object */ + if (res == FR_NO_FILE) { /* Object is not found */ + if (_FS_RPATH && (ns & NS_DOT)) { /* If dot entry is not exist, stay there */ + if (!(ns & NS_LAST)) continue; /* Continue to follow if not last segment */ + dp->fn[NSFLAG] = NS_NONAME; + res = FR_OK; + } else { /* Could not find the object */ + if (!(ns & NS_LAST)) res = FR_NO_PATH; /* Adjust error code if not last segment */ + } + } + break; + } + if (ns & NS_LAST) break; /* Last segment matched. Function completed. */ + /* Get into the sub-directory */ + if (!(obj->attr & AM_DIR)) { /* It is not a sub-directory and cannot follow */ + res = FR_NO_PATH; break; + } +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Save containing directory information for next dir */ + obj->c_scl = obj->sclust; + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Open next directory */ + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + } else +#endif + { + obj->sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs)); /* Open next directory */ + } + } + } + + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Get logical drive number from path name */ +/*-----------------------------------------------------------------------*/ + +static +int get_ldnumber ( /* Returns logical drive number (-1:invalid drive) */ + const TCHAR** path /* Pointer to pointer to the path name */ +) +{ + const TCHAR *tp, *tt; + UINT i; + int vol = -1; +#if _STR_VOLUME_ID /* Find string drive id */ + static const char* const volid[] = {_VOLUME_STRS}; + const char *sp; + char c; + TCHAR tc; +#endif + + + if (*path) { /* If the pointer is not a null */ + for (tt = *path; (UINT)*tt >= (_USE_LFN ? ' ' : '!') && *tt != ':'; tt++) ; /* Find ':' in the path */ + if (*tt == ':') { /* If a ':' is exist in the path name */ + tp = *path; + i = *tp++ - '0'; + if (i < 10 && tp == tt) { /* Is there a numeric drive id? */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = ++tt; + } + } +#if _STR_VOLUME_ID + else { /* No numeric drive number, find string drive id */ + i = 0; tt++; + do { + sp = volid[i]; tp = *path; + do { /* Compare a string drive id with path name */ + c = *sp++; tc = *tp++; + if (IsLower(tc)) tc -= 0x20; + } while (c && (TCHAR)c == tc); + } while ((c || tp != tt) && ++i < _VOLUMES); /* Repeat for each id until pattern match */ + if (i < _VOLUMES) { /* If a drive id is found, get the value and strip it */ + vol = (int)i; + *path = tt; + } + } +#endif + return vol; + } +#if _FS_RPATH != 0 && _VOLUMES >= 2 + vol = CurrVol; /* Current drive */ +#else + vol = 0; /* Drive 0 */ +#endif + } + return vol; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Load a sector and check if it is an FAT boot sector */ +/*-----------------------------------------------------------------------*/ + +static +BYTE check_fs ( /* 0:FAT, 1:exFAT, 2:Valid BS but not FAT, 3:Not a BS, 4:Disk error */ + FATFS* fs, /* File system object */ + DWORD sect /* Sector# (lba) to load and check if it is an FAT-VBR or not */ +) +{ + fs->wflag = 0; fs->winsect = 0xFFFFFFFF; /* Invaidate window */ + if (move_window(fs, sect) != FR_OK) return 4; /* Load boot record */ + + if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3; /* Check boot record signature (always placed here even if the sector size is >512) */ + + if (fs->win[BS_JmpBoot] == 0xE9 || (fs->win[BS_JmpBoot] == 0xEB && fs->win[BS_JmpBoot + 2] == 0x90)) { + if ((ld_dword(fs->win + BS_FilSysType) & 0xFFFFFF) == 0x544146) return 0; /* Check "FAT" string */ + if (ld_dword(fs->win + BS_FilSysType32) == 0x33544146) return 0; /* Check "FAT3" string */ + } +#if _FS_EXFAT + if (!mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11)) return 1; +#endif + return 2; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Find logical drive and check if the volume is mounted */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT find_volume ( /* FR_OK(0): successful, !=0: any error occurred */ + const TCHAR** path, /* Pointer to pointer to the path name (drive number) */ + FATFS** rfs, /* Pointer to pointer to the found file system object */ + BYTE mode /* !=0: Check write protection for write access */ +) +{ + BYTE fmt, *pt; + int vol; + DSTATUS stat; + DWORD bsect, fasize, tsect, sysect, nclst, szbfat, br[4]; + WORD nrsv; + FATFS *fs; + UINT i; + + + /* Get logical drive number */ + *rfs = 0; + vol = get_ldnumber(path); + if (vol < 0) return FR_INVALID_DRIVE; + + /* Check if the file system object is valid or not */ + fs = FatFs[vol]; /* Get pointer to the file system object */ + if (!fs) return FR_NOT_ENABLED; /* Is the file system object available? */ + + ENTER_FF(fs); /* Lock the volume */ + *rfs = fs; /* Return pointer to the file system object */ + + mode &= (BYTE)~FA_READ; /* Desired access mode, write access or not */ + if (fs->fs_type) { /* If the volume has been mounted */ + stat = disk_status(fs->drv); + if (!(stat & STA_NOINIT)) { /* and the physical drive is kept initialized */ + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check write protection if needed */ + return FR_WRITE_PROTECTED; + } + return FR_OK; /* The file system object is valid */ + } + } + + /* The file system object is not valid. */ + /* Following code attempts to mount the volume. (analyze BPB and initialize the fs object) */ + + fs->fs_type = 0; /* Clear the file system object */ + fs->drv = LD2PD(vol); /* Bind the logical drive and a physical drive */ + stat = disk_initialize(fs->drv); /* Initialize the physical drive */ + if (stat & STA_NOINIT) { /* Check if the initialization succeeded */ + return FR_NOT_READY; /* Failed to initialize due to no medium or hard error */ + } + if (!_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */ + return FR_WRITE_PROTECTED; + } +#if _MAX_SS != _MIN_SS /* Get sector size (multiple sector size cfg only) */ + if (disk_ioctl(fs->drv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR; + if (SS(fs) > _MAX_SS || SS(fs) < _MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR; +#endif + + /* Find an FAT partition on the drive. Supports only generic partitioning rules, FDISK and SFD. */ + bsect = 0; + fmt = check_fs(fs, bsect); /* Load sector 0 and check if it is an FAT-VBR as SFD */ + if (fmt == 2 || (fmt < 2 && LD2PT(vol) != 0)) { /* Not an FAT-VBR or forced partition number */ + for (i = 0; i < 4; i++) { /* Get partition offset */ + pt = fs->win + (MBR_Table + i * SZ_PTE); + br[i] = pt[PTE_System] ? ld_dword(pt + PTE_StLba) : 0; + } + i = LD2PT(vol); /* Partition number: 0:auto, 1-4:forced */ + if (i) i--; + do { /* Find an FAT volume */ + bsect = br[i]; + fmt = bsect ? check_fs(fs, bsect) : 3; /* Check the partition */ + } while (LD2PT(vol) == 0 && fmt >= 2 && ++i < 4); + } + if (fmt == 4) return FR_DISK_ERR; /* An error occured in the disk I/O layer */ + if (fmt >= 2) return FR_NO_FILESYSTEM; /* No FAT volume is found */ + + /* An FAT volume is found (bsect). Following code initializes the file system object */ + +#if _FS_EXFAT + if (fmt == 1) { + QWORD maxlba; + + for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */ + if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM; + + if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM; /* Check exFAT revision (Must be 1.0) */ + + if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */ + return FR_NO_FILESYSTEM; + } + + maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect; /* Last LBA + 1 of the volume */ + if (maxlba >= 0x100000000) return FR_NO_FILESYSTEM; /* (It cannot be handled in 32-bit LBA) */ + + fs->fsize = ld_dword(fs->win + BPB_FatSzEx); /* Number of sectors per FAT */ + + fs->n_fats = fs->win[BPB_NumFATsEx]; /* Number of FATs */ + if (fs->n_fats != 1) return FR_NO_FILESYSTEM; /* (Supports only 1 FAT) */ + + fs->csize = 1 << fs->win[BPB_SecPerClusEx]; /* Cluster size */ + if (fs->csize == 0) return FR_NO_FILESYSTEM; /* (Must be 1..32768) */ + + nclst = ld_dword(fs->win + BPB_NumClusEx); /* Number of clusters */ + if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */ + fs->n_fatent = nclst + 2; + + /* Boundaries and Limits */ + fs->volbase = bsect; + fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx); + fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx); + if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM; /* (Volume size must not be smaller than the size requiered) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClusEx); + + /* Check if bitmap location is in assumption (at the first cluster) */ + if (move_window(fs, clust2sect(fs, fs->dirbase)) != FR_OK) return FR_DISK_ERR; + for (i = 0; i < SS(fs); i += SZDIRE) { + if (fs->win[i] == 0x81 && ld_dword(fs->win + i + 20) == 2) break; /* 81 entry with cluster #2? */ + } + if (i == SS(fs)) return FR_NO_FILESYSTEM; +#if !_FS_READONLY + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ +#endif + fmt = FS_EXFAT; /* FAT sub-type */ + } else +#endif /* _FS_EXFAT */ + { + if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_BytsPerSec must be equal to the physical sector size) */ + + fasize = ld_word(fs->win + BPB_FATSz16); /* Number of sectors per FAT */ + if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32); + fs->fsize = fasize; + + fs->n_fats = fs->win[BPB_NumFATs]; /* Number of FATs */ + if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM; /* (Must be 1 or 2) */ + fasize *= fs->n_fats; /* Number of sectors for FAT area */ + + fs->csize = fs->win[BPB_SecPerClus]; /* Cluster size */ + if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM; /* (Must be power of 2) */ + + fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt); /* Number of root directory entries */ + if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */ + + tsect = ld_word(fs->win + BPB_TotSec16); /* Number of sectors on the volume */ + if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32); + + nrsv = ld_word(fs->win + BPB_RsvdSecCnt); /* Number of reserved sectors */ + if (nrsv == 0) return FR_NO_FILESYSTEM; /* (Must not be 0) */ + + /* Determine the FAT sub type */ + sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE); /* RSV + FAT + DIR */ + if (tsect < sysect) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + nclst = (tsect - sysect) / fs->csize; /* Number of clusters */ + if (nclst == 0) return FR_NO_FILESYSTEM; /* (Invalid volume size) */ + fmt = FS_FAT32; + if (nclst <= MAX_FAT16) fmt = FS_FAT16; + if (nclst <= MAX_FAT12) fmt = FS_FAT12; + + /* Boundaries and Limits */ + fs->n_fatent = nclst + 2; /* Number of FAT entries */ + fs->volbase = bsect; /* Volume start sector */ + fs->fatbase = bsect + nrsv; /* FAT start sector */ + fs->database = bsect + sysect; /* Data start sector */ + if (fmt == FS_FAT32) { + if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM; /* (Must be FAT32 revision 0.0) */ + if (fs->n_rootdir) return FR_NO_FILESYSTEM; /* (BPB_RootEntCnt must be 0) */ + fs->dirbase = ld_dword(fs->win + BPB_RootClus32); /* Root directory start cluster */ + szbfat = fs->n_fatent * 4; /* (Needed FAT size) */ + } else { + if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;/* (BPB_RootEntCnt must not be 0) */ + fs->dirbase = fs->fatbase + fasize; /* Root directory start sector */ + szbfat = (fmt == FS_FAT16) ? /* (Needed FAT size) */ + fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1); + } + if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM; /* (BPB_FATSz must not be less than the size needed) */ + +#if !_FS_READONLY + /* Get FSINFO if available */ + fs->last_clst = fs->free_clst = 0xFFFFFFFF; /* Initialize cluster allocation information */ + fs->fsi_flag = 0x80; +#if (_FS_NOFSINFO & 3) != 3 + if (fmt == FS_FAT32 /* Enable FSINFO only if FAT32 and BPB_FSInfo32 == 1 */ + && ld_word(fs->win + BPB_FSInfo32) == 1 + && move_window(fs, bsect + 1) == FR_OK) + { + fs->fsi_flag = 0; + if (ld_word(fs->win + BS_55AA) == 0xAA55 /* Load FSINFO data if available */ + && ld_dword(fs->win + FSI_LeadSig) == 0x41615252 + && ld_dword(fs->win + FSI_StrucSig) == 0x61417272) + { +#if (_FS_NOFSINFO & 1) == 0 + fs->free_clst = ld_dword(fs->win + FSI_Free_Count); +#endif +#if (_FS_NOFSINFO & 2) == 0 + fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free); +#endif + } + } +#endif /* (_FS_NOFSINFO & 3) != 3 */ +#endif /* !_FS_READONLY */ + } + + fs->fs_type = fmt; /* FAT sub-type */ + fs->id = ++Fsid; /* File system mount ID */ +#if _USE_LFN == 1 + fs->lfnbuf = LfnBuf; /* Static LFN working buffer */ +#if _FS_EXFAT + fs->dirbuf = DirBuf; /* Static directory block scratchpad buuffer */ +#endif +#endif +#if _FS_RPATH != 0 + fs->cdir = 0; /* Initialize current directory */ +#endif +#if _FS_LOCK != 0 /* Clear file lock semaphores */ + clear_lock(fs); +#endif + return FR_OK; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Check if the file/directory object is valid or not */ +/*-----------------------------------------------------------------------*/ + +static +FRESULT validate ( /* Returns FR_OK or FR_INVALID_OBJECT */ + _FDID* obj, /* Pointer to the _OBJ, the 1st member in the FIL/DIR object, to check validity */ + FATFS** fs /* Pointer to pointer to the owner file system object to return */ +) +{ + FRESULT res = FR_INVALID_OBJECT; + + + if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) { /* Test if the object is valid */ +#if _FS_REENTRANT + if (lock_fs(obj->fs)) { /* Obtain the filesystem object */ + if (!(disk_status(obj->fs->drv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } else { + unlock_fs(obj->fs, FR_OK); + } + } else { + res = FR_TIMEOUT; + } +#else + if (!(disk_status(obj->fs->drv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */ + res = FR_OK; + } +#endif + } + *fs = (res == FR_OK) ? obj->fs : 0; /* Corresponding filesystem object */ + return res; +} + + + + +/*--------------------------------------------------------------------------- + + Public Functions (FatFs API) + +----------------------------------------------------------------------------*/ + + + +/*-----------------------------------------------------------------------*/ +/* Mount/Unmount a Logical Drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mount ( + FATFS* fs, /* Pointer to the file system object (NULL:unmount)*/ + const TCHAR* path, /* Logical drive number to be mounted/unmounted */ + BYTE opt /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */ +) +{ + FATFS *cfs; + int vol; + FRESULT res; + const TCHAR *rp = path; + + + /* Get logical drive number */ + vol = get_ldnumber(&rp); + if (vol < 0) return FR_INVALID_DRIVE; + cfs = FatFs[vol]; /* Pointer to fs object */ + + if (cfs) { +#if _FS_LOCK != 0 + clear_lock(cfs); +#endif +#if _FS_REENTRANT /* Discard sync object of the current volume */ + if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR; +#endif + cfs->fs_type = 0; /* Clear old fs object */ + } + + if (fs) { + fs->fs_type = 0; /* Clear new fs object */ +#if _FS_REENTRANT /* Create sync object for the new volume */ + if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR; +#endif + } + FatFs[vol] = fs; /* Register new fs object */ + + if (!fs || opt != 1) return FR_OK; /* Do not mount now, it will be mounted later */ + + res = find_volume(&path, &fs, 0); /* Force mounted the volume */ + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Open or Create a File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_open ( + FIL* fp, /* Pointer to the blank file object */ + const TCHAR* path, /* Pointer to the file name */ + BYTE mode /* Access mode and file open mode flags */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; +#if !_FS_READONLY + DWORD dw, cl, bcs, clst, sc; + FSIZE_t ofs; +#endif + DEF_NAMBUF + + + if (!fp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + mode &= _FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND | FA_SEEKEND; + res = find_volume(&path, &fs, mode); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ +#if !_FS_READONLY /* R/W configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } +#if _FS_LOCK != 0 + else { + res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + } +#endif + } + /* Create or Open a file */ + if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) { + if (res != FR_OK) { /* No file, create new */ + if (res == FR_NO_FILE) { /* There is no file to open, create a new entry */ +#if _FS_LOCK != 0 + res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES; +#else + res = dir_register(&dj); +#endif + } + mode |= FA_CREATE_ALWAYS; /* File is created */ + } + else { /* Any object is already existing */ + if (dj.obj.attr & (AM_RDO | AM_DIR)) { /* Cannot overwrite it (R/O or DIR) */ + res = FR_DENIED; + } else { + if (mode & FA_CREATE_NEW) res = FR_EXIST; /* Cannot create as new file */ + } + } + if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) { /* Truncate it if overwrite mode */ + dw = GET_FATTIME(); +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + /* Get current allocation info */ + fp->obj.fs = fs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + fp->obj.n_frag = 0; + /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_CrtTime, dw); /* Set created time */ + fs->dirbuf[XDIR_CrtTime10] = 0; + st_dword(fs->dirbuf + XDIR_ModTime, dw); /* Set modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + fs->dirbuf[XDIR_Attr] = AM_ARC; /* Reset attribute */ + st_dword(fs->dirbuf + XDIR_FstClus, 0); /* Reset file allocation info */ + st_qword(fs->dirbuf + XDIR_FileSize, 0); + st_qword(fs->dirbuf + XDIR_ValidFileSize, 0); + fs->dirbuf[XDIR_GenFlags] = 1; + res = store_xdir(&dj); + if (res == FR_OK && fp->obj.sclust) { /* Remove the cluster chain if exist */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fs->last_clst = fp->obj.sclust - 1; /* Reuse the cluster hole */ + } + } else +#endif + { + /* Clean directory info */ + st_dword(dj.dir + DIR_CrtTime, dw); /* Set created time */ + st_dword(dj.dir + DIR_ModTime, dw); /* Set modified time */ + dj.dir[DIR_Attr] = AM_ARC; /* Reset attribute */ + cl = ld_clust(fs, dj.dir); /* Get cluster chain */ + st_clust(fs, dj.dir, 0); /* Reset file allocation info */ + st_dword(dj.dir + DIR_FileSize, 0); + fs->wflag = 1; + + if (cl) { /* Remove the cluster chain if exist */ + dw = fs->winsect; + res = remove_chain(&dj.obj, cl, 0); + if (res == FR_OK) { + res = move_window(fs, dw); + fs->last_clst = cl - 1; /* Reuse the cluster hole */ + } + } + } + } + } + else { /* Open an existing file */ + if (res == FR_OK) { /* Following succeeded */ + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } else { + if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* R/O violation */ + res = FR_DENIED; + } + } + } + } + if (res == FR_OK) { + if (mode & FA_CREATE_ALWAYS) /* Set file change flag if created or overwritten */ + mode |= FA_MODIFIED; + fp->dir_sect = fs->winsect; /* Pointer to the directory entry */ + fp->dir_ptr = dj.dir; +#if _FS_LOCK != 0 + fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0); + if (!fp->obj.lockid) res = FR_INT_ERR; +#endif + } +#else /* R/O configuration */ + if (res == FR_OK) { + if (dj.fn[NSFLAG] & NS_NONAME) { /* Origin directory itself? */ + res = FR_INVALID_NAME; + } else { + if (dj.obj.attr & AM_DIR) { /* It is a directory */ + res = FR_NO_FILE; + } + } + } +#endif + + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fp->obj.c_scl = dj.obj.sclust; /* Get containing directory info */ + fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fp->obj.c_ofs = dj.blk_ofs; + fp->obj.sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ + fp->obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + fp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + fp->obj.sclust = ld_clust(fs, dj.dir); /* Get object allocation info */ + fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize); + } +#if _USE_FASTSEEK + fp->cltbl = 0; /* Disable fast seek mode */ +#endif + fp->obj.fs = fs; /* Validate the file object */ + fp->obj.id = fs->id; + fp->flag = mode; /* Set file access mode */ + fp->err = 0; /* Clear error flag */ + fp->sect = 0; /* Invalidate current data sector */ + fp->fptr = 0; /* Set file pointer top of the file */ +#if !_FS_READONLY +#if !_FS_TINY + mem_set(fp->buf, 0, _MAX_SS); /* Clear sector buffer */ +#endif + if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) { /* Seek to end of file if FA_OPEN_APPEND is specified */ + fp->fptr = fp->obj.objsize; /* Offset to seek */ + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size in byte */ + clst = fp->obj.sclust; /* Follow the cluster chain */ + for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) { + clst = get_fat(&fp->obj, clst); + if (clst <= 1) res = FR_INT_ERR; + if (clst == 0xFFFFFFFF) res = FR_DISK_ERR; + } + fp->clust = clst; + if (res == FR_OK && ofs % SS(fs)) { /* Fill sector buffer if not on the sector boundary */ + if ((sc = clust2sect(fs, clst)) == 0) { + res = FR_INT_ERR; + } else { + fp->sect = sc + (DWORD)(ofs / SS(fs)); +#if !_FS_TINY + if (disk_read(fs->drv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR; +#endif + } + } + } +#endif + } + + FREE_NAMBUF(); + } + + if (res != FR_OK) fp->obj.fs = 0; /* Invalidate file object on error */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_read ( + FIL* fp, /* Pointer to the file object */ + void* buff, /* Pointer to data buffer */ + UINT btr, /* Number of bytes to read */ + UINT* br /* Pointer to number of bytes read */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, cc, csect; + BYTE *rbuff = (BYTE*)buff; + + + *br = 0; /* Clear read byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + remain = fp->obj.objsize - fp->fptr; + if (btr > remain) btr = (UINT)remain; /* Truncate btr by remaining bytes */ + + for ( ; btr; /* Repeat until all data read */ + rbuff += rcnt, fp->fptr += rcnt, *br += rcnt, btr -= rcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow cluster chain from the origin */ + } else { /* Middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = get_fat(&fp->obj, fp->clust); /* Follow cluster chain on the FAT */ + } + } + if (clst < 2) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btr / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Read maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_read(fs->drv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if !_FS_READONLY && _FS_MINIMIZE <= 2 /* Replace one of the read sectors with cached data if it contains a dirty sector */ +#if _FS_TINY + if (fs->wflag && fs->winsect - sect < cc) { + mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs)); + } +#else + if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) { + mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs)); + } +#endif +#endif + rcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if !_FS_TINY + if (fp->sect != sect) { /* Load data sector if not in cache */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ + } +#endif + fp->sect = sect; + } + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btr) rcnt = btr; /* Clip it by btr if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#else + mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt); /* Extract partial sector */ +#endif + } + + LEAVE_FF(fs, FR_OK); +} + + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Write File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_write ( + FIL* fp, /* Pointer to the file object */ + const void* buff, /* Pointer to the data to be written */ + UINT btw, /* Number of bytes to write */ + UINT* bw /* Pointer to number of bytes written */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + UINT wcnt, cc, csect; + const BYTE *wbuff = (const BYTE*)buff; + + + *bw = 0; /* Clear write byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); /* Check validity */ + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + /* Check fptr wrap-around (file size cannot reach 4GiB on FATxx) */ + if ((!_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) { + btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr); + } + + for ( ; btw; /* Repeat until all data written */ + wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize, *bw += wcnt, btw -= wcnt) { + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1); /* Sector offset in the cluster */ + if (csect == 0) { /* On the cluster boundary? */ + if (fp->fptr == 0) { /* On the top of the file? */ + clst = fp->obj.sclust; /* Follow from the origin */ + if (clst == 0) { /* If no cluster is allocated, */ + clst = create_chain(&fp->obj, 0); /* create a new cluster chain */ + } + } else { /* On the middle or end of the file */ +#if _USE_FASTSEEK + if (fp->cltbl) { + clst = clmt_clust(fp, fp->fptr); /* Get cluster# from the CLMT */ + } else +#endif + { + clst = create_chain(&fp->obj, fp->clust); /* Follow or stretch cluster chain on the FAT */ + } + } + if (clst == 0) break; /* Could not allocate a new cluster (disk full) */ + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */ + } +#if _FS_TINY + if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Write-back sector cache */ +#else + if (fp->flag & FA_DIRTY) { /* Write-back sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + sect = clust2sect(fs, fp->clust); /* Get current sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; + cc = btw / SS(fs); /* When remaining bytes >= sector size, */ + if (cc) { /* Write maximum contiguous sectors directly */ + if (csect + cc > fs->csize) { /* Clip at cluster boundary */ + cc = fs->csize - csect; + } + if (disk_write(fs->drv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR); +#if _FS_MINIMIZE <= 2 +#if _FS_TINY + if (fs->winsect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs)); + fs->wflag = 0; + } +#else + if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */ + mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs)); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif +#endif + wcnt = SS(fs) * cc; /* Number of bytes transferred */ + continue; + } +#if _FS_TINY + if (fp->fptr >= fp->obj.objsize) { /* Avoid silly cache filling on the growing edge */ + if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR); + fs->winsect = sect; + } +#else + if (fp->sect != sect && /* Fill sector cache with file data */ + fp->fptr < fp->obj.objsize && + disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) { + ABORT(fs, FR_DISK_ERR); + } +#endif + fp->sect = sect; + } + wcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (wcnt > btw) wcnt = btw; /* Clip it by btw if needed */ +#if _FS_TINY + if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */ + mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fs->wflag = 1; +#else + mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt); /* Fit data to the sector */ + fp->flag |= FA_DIRTY; +#endif + } + + fp->flag |= FA_MODIFIED; /* Set file change flag */ + + LEAVE_FF(fs, FR_OK); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Synchronize the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_sync ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD tm; + BYTE *dir; +#if _FS_EXFAT + DIR dj; + DEF_NAMBUF +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { + if (fp->flag & FA_MODIFIED) { /* Is there any change to the file? */ +#if !_FS_TINY + if (fp->flag & FA_DIRTY) { /* Write-back cached data if needed */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + /* Update the directory entry */ + tm = GET_FATTIME(); /* Modified time */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + res = fill_first_frag(&fp->obj); /* Fill first fragment on the FAT if needed */ + if (res == FR_OK) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = load_obj_dir(&dj, &fp->obj); /* Load directory entry block */ + if (res == FR_OK) { + fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive bit */ + fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1; /* Update file allocation info */ + st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust); + st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize); + st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize); + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Update modified time */ + fs->dirbuf[XDIR_ModTime10] = 0; + st_dword(fs->dirbuf + XDIR_AccTime, 0); + res = store_xdir(&dj); /* Restore it to the directory */ + if (res == FR_OK) { + res = sync_fs(fs); + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + FREE_NAMBUF(); + } + } else +#endif + { + res = move_window(fs, fp->dir_sect); + if (res == FR_OK) { + dir = fp->dir_ptr; + dir[DIR_Attr] |= AM_ARC; /* Set archive bit */ + st_clust(fp->obj.fs, dir, fp->obj.sclust); /* Update file allocation info */ + st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize); /* Update file size */ + st_dword(dir + DIR_ModTime, tm); /* Update modified time */ + st_word(dir + DIR_LstAccDate, 0); + fs->wflag = 1; + res = sync_fs(fs); /* Restore it to the directory */ + fp->flag &= (BYTE)~FA_MODIFIED; + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ + + + + +/*-----------------------------------------------------------------------*/ +/* Close File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_close ( + FIL* fp /* Pointer to the file object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + +#if !_FS_READONLY + res = f_sync(fp); /* Flush cached data */ + if (res == FR_OK) +#endif + { + res = validate(&fp->obj, &fs); /* Lock volume */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + res = dec_lock(fp->obj.lockid); /* Decrement file open counter */ + if (res == FR_OK) +#endif + { + fp->obj.fs = 0; /* Invalidate file object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + } + return res; +} + + + + +#if _FS_RPATH >= 1 +/*-----------------------------------------------------------------------*/ +/* Change Current Directory or Current Drive, Get Current Directory */ +/*-----------------------------------------------------------------------*/ + +#if _VOLUMES >= 2 +FRESULT f_chdrive ( + const TCHAR* path /* Drive number */ +) +{ + int vol; + + + /* Get logical drive number */ + vol = get_ldnumber(&path); + if (vol < 0) return FR_INVALID_DRIVE; + + CurrVol = (BYTE)vol; /* Set it as current volume */ + + return FR_OK; +} +#endif + + +FRESULT f_chdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { + fs->cdir = dj.obj.sclust; /* It is the start directory itself */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdc_scl = dj.obj.c_scl; + fs->cdc_size = dj.obj.c_size; + fs->cdc_ofs = dj.obj.c_ofs; + } +#endif + } else { + if (dj.obj.attr & AM_DIR) { /* It is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus); /* Sub-directory cluster */ + fs->cdc_scl = dj.obj.sclust; /* Save containing directory information */ + fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat; + fs->cdc_ofs = dj.blk_ofs; + } else +#endif + { + fs->cdir = ld_clust(fs, dj.dir); /* Sub-directory cluster */ + } + } else { + res = FR_NO_PATH; /* Reached but a file */ + } + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + + LEAVE_FF(fs, res); +} + + +#if _FS_RPATH >= 2 +FRESULT f_getcwd ( + TCHAR* buff, /* Pointer to the directory path */ + UINT len /* Size of path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT i, n; + DWORD ccl; + TCHAR *tp; + FILINFO fno; + DEF_NAMBUF + + + *buff = 0; + /* Get logical drive */ + res = find_volume((const TCHAR**)&buff, &fs, 0); /* Get current volume */ + if (res == FR_OK) { + dj.obj.fs = fs; + INIT_NAMBUF(fs); + i = len; /* Bottom of buffer (directory stack base) */ + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { /* (Cannot do getcwd on exFAT and returns root path) */ + dj.obj.sclust = fs->cdir; /* Start to follow upper directory from current directory */ + while ((ccl = dj.obj.sclust) != 0) { /* Repeat while current directory is a sub-directory */ + res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */ + if (res != FR_OK) break; + res = move_window(fs, dj.sect); + if (res != FR_OK) break; + dj.obj.sclust = ld_clust(fs, dj.dir); /* Goto parent directory */ + res = dir_sdi(&dj, 0); + if (res != FR_OK) break; + do { /* Find the entry links to the child directory */ + res = dir_read(&dj, 0); + if (res != FR_OK) break; + if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */ + res = dir_next(&dj, 0); + } while (res == FR_OK); + if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */ + if (res != FR_OK) break; + get_fileinfo(&dj, &fno); /* Get the directory name and push it to the buffer */ + for (n = 0; fno.fname[n]; n++) ; + if (i < n + 3) { + res = FR_NOT_ENOUGH_CORE; break; + } + while (n) buff[--i] = fno.fname[--n]; + buff[--i] = '/'; + } + } + tp = buff; + if (res == FR_OK) { +#if _VOLUMES >= 2 + *tp++ = '0' + CurrVol; /* Put drive number */ + *tp++ = ':'; +#endif + if (i == len) { /* Root-directory */ + *tp++ = '/'; + } else { /* Sub-directroy */ + do /* Add stacked path str */ + *tp++ = buff[i++]; + while (i < len); + } + } + *tp = 0; + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _FS_RPATH >= 2 */ +#endif /* _FS_RPATH >= 1 */ + + + +#if _FS_MINIMIZE <= 2 +/*-----------------------------------------------------------------------*/ +/* Seek File R/W Pointer */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_lseek ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t ofs /* File pointer from top of file */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, bcs, nsect; + FSIZE_t ifptr; +#if _USE_FASTSEEK + DWORD cl, pcl, ncl, tcl, dsc, tlen, ulen, *tbl; +#endif + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) res = (FRESULT)fp->err; +#if _FS_EXFAT && !_FS_READONLY + if (res == FR_OK && fs->fs_type == FS_EXFAT) { + res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF); /* Fill last fragment on the FAT if needed */ + } +#endif + if (res != FR_OK) LEAVE_FF(fs, res); + +#if _USE_FASTSEEK + if (fp->cltbl) { /* Fast seek */ + if (ofs == CREATE_LINKMAP) { /* Create CLMT */ + tbl = fp->cltbl; + tlen = *tbl++; ulen = 2; /* Given table size and required table size */ + cl = fp->obj.sclust; /* Origin of the chain */ + if (cl) { + do { + /* Get a fragment */ + tcl = cl; ncl = 0; ulen += 2; /* Top, length and used items */ + do { + pcl = cl; ncl++; + cl = get_fat(&fp->obj, cl); + if (cl <= 1) ABORT(fs, FR_INT_ERR); + if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + } while (cl == pcl + 1); + if (ulen <= tlen) { /* Store the length and top of the fragment */ + *tbl++ = ncl; *tbl++ = tcl; + } + } while (cl < fs->n_fatent); /* Repeat until end of chain */ + } + *fp->cltbl = ulen; /* Number of items used */ + if (ulen <= tlen) { + *tbl = 0; /* Terminate table */ + } else { + res = FR_NOT_ENOUGH_CORE; /* Given table size is smaller than required */ + } + } else { /* Fast seek */ + if (ofs > fp->obj.objsize) ofs = fp->obj.objsize; /* Clip offset at the file size */ + fp->fptr = ofs; /* Set file pointer */ + if (ofs) { + fp->clust = clmt_clust(fp, ofs - 1); + dsc = clust2sect(fs, fp->clust); + if (!dsc) ABORT(fs, FR_INT_ERR); + dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1); + if (fp->fptr % SS(fs) && dsc != fp->sect) { /* Refill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Load current sector */ +#endif + fp->sect = dsc; + } + } + } + } else +#endif + + /* Normal Seek */ + { +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF; /* Clip at 4GiB-1 if at FATxx */ +#endif + if (ofs > fp->obj.objsize && (_FS_READONLY || !(fp->flag & FA_WRITE))) { /* In read-only mode, clip offset with the file size */ + ofs = fp->obj.objsize; + } + ifptr = fp->fptr; + fp->fptr = nsect = 0; + if (ofs) { + bcs = (DWORD)fs->csize * SS(fs); /* Cluster size (byte) */ + if (ifptr > 0 && + (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */ + fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1); /* start from the current cluster */ + ofs -= fp->fptr; + clst = fp->clust; + } else { /* When seek to back cluster, */ + clst = fp->obj.sclust; /* start from the first cluster */ +#if !_FS_READONLY + if (clst == 0) { /* If no cluster chain, create a new chain */ + clst = create_chain(&fp->obj, 0); + if (clst == 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->obj.sclust = clst; + } +#endif + fp->clust = clst; + } + if (clst != 0) { + while (ofs > bcs) { /* Cluster following loop */ + ofs -= bcs; fp->fptr += bcs; +#if !_FS_READONLY + if (fp->flag & FA_WRITE) { /* Check if in write mode or not */ + if (_FS_EXFAT && fp->fptr > fp->obj.objsize) { /* No FAT chain object needs correct objsize to generate FAT value */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + clst = create_chain(&fp->obj, clst); /* Follow chain with forceed stretch */ + if (clst == 0) { /* Clip file size in case of disk full */ + ofs = 0; break; + } + } else +#endif + { + clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */ + } + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR); + fp->clust = clst; + } + fp->fptr += ofs; + if (ofs % SS(fs)) { + nsect = clust2sect(fs, clst); /* Current sector */ + if (!nsect) ABORT(fs, FR_INT_ERR); + nsect += (DWORD)(ofs / SS(fs)); + } + } + } + if (!_FS_READONLY && fp->fptr > fp->obj.objsize) { /* Set file change flag if the file size is extended */ + fp->obj.objsize = fp->fptr; + fp->flag |= FA_MODIFIED; + } + if (fp->fptr % SS(fs) && nsect != fp->sect) { /* Fill sector cache if needed */ +#if !_FS_TINY +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); /* Fill sector cache */ +#endif + fp->sect = nsect; + } + } + + LEAVE_FF(fs, res); +} + + + +#if _FS_MINIMIZE <= 1 +/*-----------------------------------------------------------------------*/ +/* Create a Directory Object */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_opendir ( + DIR* dp, /* Pointer to directory object to create */ + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + FATFS *fs; + _FDID *obj; + DEF_NAMBUF + + + if (!dp) return FR_INVALID_OBJECT; + + /* Get logical drive */ + obj = &dp->obj; + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + obj->fs = fs; + INIT_NAMBUF(fs); + res = follow_path(dp, path); /* Follow the path to the directory */ + if (res == FR_OK) { /* Follow completed */ + if (!(dp->fn[NSFLAG] & NS_NONAME)) { /* It is not the origin directory itself */ + if (obj->attr & AM_DIR) { /* This object is a sub-directory */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + obj->c_scl = obj->sclust; /* Get containing directory inforamation */ + obj->c_size = ((DWORD)obj->objsize & 0xFFFFFF00) | obj->stat; + obj->c_ofs = dp->blk_ofs; + obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus); /* Get object allocation info */ + obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj->stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + obj->sclust = ld_clust(fs, dp->dir); /* Get object allocation info */ + } + } else { /* This object is a file */ + res = FR_NO_PATH; + } + } + if (res == FR_OK) { + obj->id = fs->id; + res = dir_sdi(dp, 0); /* Rewind directory */ +#if _FS_LOCK != 0 + if (res == FR_OK) { + if (obj->sclust) { + obj->lockid = inc_lock(dp, 0); /* Lock the sub directory */ + if (!obj->lockid) res = FR_TOO_MANY_OPEN_FILES; + } else { + obj->lockid = 0; /* Root directory need not to be locked */ + } + } +#endif + } + } + FREE_NAMBUF(); + if (res == FR_NO_FILE) res = FR_NO_PATH; + } + if (res != FR_OK) obj->fs = 0; /* Invalidate the directory object if function faild */ + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Close Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_closedir ( + DIR *dp /* Pointer to the directory object to be closed */ +) +{ + FRESULT res; + FATFS *fs; + + + res = validate(&dp->obj, &fs); /* Check validity of the file object */ + if (res == FR_OK) { +#if _FS_LOCK != 0 + if (dp->obj.lockid) { /* Decrement sub-directory open counter */ + res = dec_lock(dp->obj.lockid); + } + if (res == FR_OK) +#endif + { + dp->obj.fs = 0; /* Invalidate directory object */ + } +#if _FS_REENTRANT + unlock_fs(fs, FR_OK); /* Unlock volume */ +#endif + } + return res; +} + + + + +/*-----------------------------------------------------------------------*/ +/* Read Directory Entries in Sequence */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_readdir ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + FATFS *fs; + DEF_NAMBUF + + + res = validate(&dp->obj, &fs); /* Check validity of the directory object */ + if (res == FR_OK) { + if (!fno) { + res = dir_sdi(dp, 0); /* Rewind the directory object */ + } else { + INIT_NAMBUF(fs); + res = dir_read(dp, 0); /* Read an item */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory */ + if (res == FR_OK) { /* A valid entry is found */ + get_fileinfo(dp, fno); /* Get the object information */ + res = dir_next(dp, 0); /* Increment index for next */ + if (res == FR_NO_FILE) res = FR_OK; /* Ignore end of directory now */ + } + FREE_NAMBUF(); + } + } + LEAVE_FF(fs, res); +} + + + +#if _USE_FIND +/*-----------------------------------------------------------------------*/ +/* Find Next File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findnext ( + DIR* dp, /* Pointer to the open directory object */ + FILINFO* fno /* Pointer to the file information structure */ +) +{ + FRESULT res; + + + for (;;) { + res = f_readdir(dp, fno); /* Get a directory item */ + if (res != FR_OK || !fno || !fno->fname[0]) break; /* Terminate if any error or end of directory */ + if (pattern_matching(dp->pat, fno->fname, 0, 0)) break; /* Test for the file name */ +#if _USE_LFN != 0 && _USE_FIND == 2 + if (pattern_matching(dp->pat, fno->altname, 0, 0)) break; /* Test for alternative name if exist */ +#endif + } + return res; +} + + + +/*-----------------------------------------------------------------------*/ +/* Find First File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_findfirst ( + DIR* dp, /* Pointer to the blank directory object */ + FILINFO* fno, /* Pointer to the file information structure */ + const TCHAR* path, /* Pointer to the directory to open */ + const TCHAR* pattern /* Pointer to the matching pattern */ +) +{ + FRESULT res; + + + dp->pat = pattern; /* Save pointer to pattern string */ + res = f_opendir(dp, path); /* Open the target directory */ + if (res == FR_OK) { + res = f_findnext(dp, fno); /* Find the first item */ + } + return res; +} + +#endif /* _USE_FIND */ + + + +#if _FS_MINIMIZE == 0 +/*-----------------------------------------------------------------------*/ +/* Get File Status */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_stat ( + const TCHAR* path, /* Pointer to the file path */ + FILINFO* fno /* Pointer to file information to return */ +) +{ + FRESULT res; + DIR dj; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &dj.obj.fs, 0); + if (res == FR_OK) { + INIT_NAMBUF(dj.obj.fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) { /* Follow completed */ + if (dj.fn[NSFLAG] & NS_NONAME) { /* It is origin directory */ + res = FR_INVALID_NAME; + } else { /* Found an object */ + if (fno) get_fileinfo(&dj, fno); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(dj.obj.fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Get Number of Free Clusters */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getfree ( + const TCHAR* path, /* Path name of the logical drive number */ + DWORD* nclst, /* Pointer to a variable to return number of free clusters */ + FATFS** fatfs /* Pointer to return pointer to corresponding file system object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD nfree, clst, sect, stat; + UINT i; + BYTE *p; + _FDID obj; + + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + if (res == FR_OK) { + *fatfs = fs; /* Return ptr to the fs object */ + /* If free_clst is valid, return it without full cluster scan */ + if (fs->free_clst <= fs->n_fatent - 2) { + *nclst = fs->free_clst; + } else { + /* Get number of free clusters */ + nfree = 0; + if (fs->fs_type == FS_FAT12) { /* FAT12: Sector unalighed FAT entries */ + clst = 2; obj.fs = fs; + do { + stat = get_fat(&obj, clst); + if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (stat == 1) { res = FR_INT_ERR; break; } + if (stat == 0) nfree++; + } while (++clst < fs->n_fatent); + } else { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* exFAT: Scan bitmap table */ + BYTE bm; + UINT b; + + clst = fs->n_fatent - 2; + sect = fs->database; + i = 0; + do { + if (i == 0 && (res = move_window(fs, sect++)) != FR_OK) break; + for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) { + if (!(bm & 1)) nfree++; + bm >>= 1; + } + i = (i + 1) % SS(fs); + } while (clst); + } else +#endif + { /* FAT16/32: Sector alighed FAT entries */ + clst = fs->n_fatent; sect = fs->fatbase; + i = 0; p = 0; + do { + if (i == 0) { + res = move_window(fs, sect++); + if (res != FR_OK) break; + p = fs->win; + i = SS(fs); + } + if (fs->fs_type == FS_FAT16) { + if (ld_word(p) == 0) nfree++; + p += 2; i -= 2; + } else { + if ((ld_dword(p) & 0x0FFFFFFF) == 0) nfree++; + p += 4; i -= 4; + } + } while (--clst); + } + } + *nclst = nfree; /* Return the free clusters */ + fs->free_clst = nfree; /* Now free_clst is valid */ + fs->fsi_flag |= 1; /* FSInfo is to be updated */ + } + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Truncate File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_truncate ( + FIL* fp /* Pointer to the file object */ +) +{ + FRESULT res; + FATFS *fs; + DWORD ncl; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + if (fp->fptr < fp->obj.objsize) { /* Process when fptr is not on the eof */ + if (fp->fptr == 0) { /* When set file size to zero, remove entire cluster chain */ + res = remove_chain(&fp->obj, fp->obj.sclust, 0); + fp->obj.sclust = 0; + } else { /* When truncate a part of the file, remove remaining clusters */ + ncl = get_fat(&fp->obj, fp->clust); + res = FR_OK; + if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (ncl == 1) res = FR_INT_ERR; + if (res == FR_OK && ncl < fs->n_fatent) { + res = remove_chain(&fp->obj, ncl, fp->clust); + } + } + fp->obj.objsize = fp->fptr; /* Set file size to current R/W point */ + fp->flag |= FA_MODIFIED; +#if !_FS_TINY + if (res == FR_OK && (fp->flag & FA_DIRTY)) { + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) { + res = FR_DISK_ERR; + } else { + fp->flag &= (BYTE)~FA_DIRTY; + } + } +#endif + if (res != FR_OK) ABORT(fs, res); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Delete a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_unlink ( + const TCHAR* path /* Pointer to the file or directory path */ +) +{ + FRESULT res; + DIR dj, sdj; + DWORD dclst = 0; + FATFS *fs; +#if _FS_EXFAT + _FDID obj; +#endif + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; /* Cannot remove dot entry */ + } +#if _FS_LOCK != 0 + if (res == FR_OK) res = chk_lock(&dj, 2); /* Check if it is an open object */ +#endif + if (res == FR_OK) { /* The object is accessible */ + if (dj.fn[NSFLAG] & NS_NONAME) { + res = FR_INVALID_NAME; /* Cannot remove the origin directory */ + } else { + if (dj.obj.attr & AM_RDO) { + res = FR_DENIED; /* Cannot remove R/O object */ + } + } + if (res == FR_OK) { +#if _FS_EXFAT + obj.fs = fs; + if (fs->fs_type == FS_EXFAT) { + obj.sclust = dclst = ld_dword(fs->dirbuf + XDIR_FstClus); + obj.objsize = ld_qword(fs->dirbuf + XDIR_FileSize); + obj.stat = fs->dirbuf[XDIR_GenFlags] & 2; + } else +#endif + { + dclst = ld_clust(fs, dj.dir); + } + if (dj.obj.attr & AM_DIR) { /* Is it a sub-directory? */ +#if _FS_RPATH != 0 + if (dclst == fs->cdir) { /* Is it the current directory? */ + res = FR_DENIED; + } else +#endif + { + sdj.obj.fs = fs; /* Open the sub-directory */ + sdj.obj.sclust = dclst; +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + sdj.obj.objsize = obj.objsize; + sdj.obj.stat = obj.stat; + } +#endif + res = dir_sdi(&sdj, 0); + if (res == FR_OK) { + res = dir_read(&sdj, 0); /* Read an item */ + if (res == FR_OK) res = FR_DENIED; /* Not empty? */ + if (res == FR_NO_FILE) res = FR_OK; /* Empty? */ + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&dj); /* Remove the directory entry */ + if (res == FR_OK && dclst) { /* Remove the cluster chain if exist */ +#if _FS_EXFAT + res = remove_chain(&obj, dclst, 0); +#else + res = remove_chain(&dj.obj, dclst, 0); +#endif + } + if (res == FR_OK) res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Create a Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkdir ( + const TCHAR* path /* Pointer to the directory path */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE *dir; + UINT n; + DWORD dsc, dcl, pcl, tm; + DEF_NAMBUF + + + /* Get logical drive */ + res = find_volume(&path, &fs, FA_WRITE); + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK) res = FR_EXIST; /* Any object with same name is already existing */ + if (_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) { + res = FR_INVALID_NAME; + } + if (res == FR_NO_FILE) { /* Can create a new directory */ + dcl = create_chain(&dj.obj, 0); /* Allocate a cluster for the new directory table */ + dj.obj.objsize = (DWORD)fs->csize * SS(fs); + res = FR_OK; + if (dcl == 0) res = FR_DENIED; /* No space to allocate a new cluster */ + if (dcl == 1) res = FR_INT_ERR; + if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) res = sync_window(fs); /* Flush FAT */ + tm = GET_FATTIME(); + if (res == FR_OK) { /* Initialize the new directory table */ + dsc = clust2sect(fs, dcl); + dir = fs->win; + mem_set(dir, 0, SS(fs)); + if (!_FS_EXFAT || fs->fs_type != FS_EXFAT) { + mem_set(dir + DIR_Name, ' ', 11); /* Create "." entry */ + dir[DIR_Name] = '.'; + dir[DIR_Attr] = AM_DIR; + st_dword(dir + DIR_ModTime, tm); + st_clust(fs, dir, dcl); + mem_cpy(dir + SZDIRE, dir, SZDIRE); /* Create ".." entry */ + dir[SZDIRE + 1] = '.'; pcl = dj.obj.sclust; + if (fs->fs_type == FS_FAT32 && pcl == fs->dirbase) pcl = 0; + st_clust(fs, dir + SZDIRE, pcl); + } + for (n = fs->csize; n; n--) { /* Write dot entries and clear following sectors */ + fs->winsect = dsc++; + fs->wflag = 1; + res = sync_window(fs); + if (res != FR_OK) break; + mem_set(dir, 0, SS(fs)); + } + } + if (res == FR_OK) { + res = dir_register(&dj); /* Register the object to the directoy */ + } + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* Initialize directory entry block */ + st_dword(fs->dirbuf + XDIR_ModTime, tm); /* Created time */ + st_dword(fs->dirbuf + XDIR_FstClus, dcl); /* Table start cluster */ + st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)dj.obj.objsize); /* File size needs to be valid */ + st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)dj.obj.objsize); + fs->dirbuf[XDIR_GenFlags] = 3; /* Initialize the object flag (contiguous) */ + fs->dirbuf[XDIR_Attr] = AM_DIR; /* Attribute */ + res = store_xdir(&dj); + } else +#endif + { + dir = dj.dir; + st_dword(dir + DIR_ModTime, tm); /* Created time */ + st_clust(fs, dir, dcl); /* Table start cluster */ + dir[DIR_Attr] = AM_DIR; /* Attribute */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } else { + remove_chain(&dj.obj, dcl, 0); /* Could not register, remove cluster chain */ + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Rename a File/Directory */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_rename ( + const TCHAR* path_old, /* Pointer to the object name to be renamed */ + const TCHAR* path_new /* Pointer to the new name */ +) +{ + FRESULT res; + DIR djo, djn; + FATFS *fs; + BYTE buf[_FS_EXFAT ? SZDIRE * 2 : 24], *dir; + DWORD dw; + DEF_NAMBUF + + + get_ldnumber(&path_new); /* Snip drive number of new name off */ + res = find_volume(&path_old, &fs, FA_WRITE); /* Get logical drive of the old object */ + if (res == FR_OK) { + djo.obj.fs = fs; + INIT_NAMBUF(fs); + res = follow_path(&djo, path_old); /* Check old object */ + if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check validity of name */ +#if _FS_LOCK != 0 + if (res == FR_OK) { + res = chk_lock(&djo, 2); + } +#endif + if (res == FR_OK) { /* Object to be renamed is found */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* At exFAT */ + BYTE nf, nn; + WORD nh; + + mem_cpy(buf, fs->dirbuf, SZDIRE * 2); /* Save 85+C0 entry of old object */ + mem_cpy(&djn, &djo, sizeof djo); + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName]; + nh = ld_word(fs->dirbuf + XDIR_NameHash); + mem_cpy(fs->dirbuf, buf, SZDIRE * 2); + fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn; + st_word(fs->dirbuf + XDIR_NameHash, nh); +/* Start of critical section where an interruption can cause a cross-link */ + res = store_xdir(&djn); + } + } + } else +#endif + { /* At FAT12/FAT16/FAT32 */ + mem_cpy(buf, djo.dir + DIR_Attr, 21); /* Save information about the object except name */ + mem_cpy(&djn, &djo, sizeof (DIR)); /* Duplicate the directory object */ + res = follow_path(&djn, path_new); /* Make sure if new object name is not in use */ + if (res == FR_OK) { /* Is new name already in use by any other object? */ + res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST; + } + if (res == FR_NO_FILE) { /* It is a valid path and no name collision */ + res = dir_register(&djn); /* Register the new entry */ + if (res == FR_OK) { + dir = djn.dir; /* Copy information about object except name */ + mem_cpy(dir + 13, buf + 2, 19); + dir[DIR_Attr] = buf[0] | AM_ARC; + fs->wflag = 1; + if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) { /* Update .. entry in the sub-directory if needed */ + dw = clust2sect(fs, ld_clust(fs, dir)); + if (!dw) { + res = FR_INT_ERR; + } else { +/* Start of critical section where an interruption can cause a cross-link */ + res = move_window(fs, dw); + dir = fs->win + SZDIRE * 1; /* Ptr to .. entry */ + if (res == FR_OK && dir[1] == '.') { + st_clust(fs, dir, djn.obj.sclust); + fs->wflag = 1; + } + } + } + } + } + } + if (res == FR_OK) { + res = dir_remove(&djo); /* Remove old entry */ + if (res == FR_OK) { + res = sync_fs(fs); + } + } +/* End of the critical section */ + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _FS_MINIMIZE == 0 */ +#endif /* _FS_MINIMIZE <= 1 */ +#endif /* _FS_MINIMIZE <= 2 */ + + + +#if _USE_CHMOD && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Change Attribute */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_chmod ( + const TCHAR* path, /* Pointer to the file path */ + BYTE attr, /* Attribute bits */ + BYTE mask /* Attribute mask to change */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { + mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC; /* Valid attribute mask */ +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + res = store_xdir(&dj); + } else +#endif + { + dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask); /* Apply attribute change */ + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Change Timestamp */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_utime ( + const TCHAR* path, /* Pointer to the file/directory name */ + const FILINFO* fno /* Pointer to the time stamp to be set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + DEF_NAMBUF + + + res = find_volume(&path, &fs, FA_WRITE); /* Get logical drive */ + dj.obj.fs = fs; + if (res == FR_OK) { + INIT_NAMBUF(fs); + res = follow_path(&dj, path); /* Follow the file path */ + if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME; /* Check object validity */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + res = store_xdir(&dj); + } else +#endif + { + st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime); + fs->wflag = 1; + } + if (res == FR_OK) { + res = sync_fs(fs); + } + } + FREE_NAMBUF(); + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_CHMOD && !_FS_READONLY */ + + + +#if _USE_LABEL +/*-----------------------------------------------------------------------*/ +/* Get Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_getlabel ( + const TCHAR* path, /* Path name of the logical drive number */ + TCHAR* label, /* Pointer to a buffer to return the volume label */ + DWORD* vsn /* Pointer to a variable to return the volume serial number */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + UINT si, di; +#if _LFN_UNICODE || _FS_EXFAT + WCHAR w; +#endif + + /* Get logical drive */ + res = find_volume(&path, &fs, 0); + + /* Get volume label */ + if (res == FR_OK && label) { + dj.obj.fs = fs; dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Find a volume label entry */ + if (res == FR_OK) { +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + for (si = di = 0; si < dj.dir[XDIR_NumLabel]; si++) { /* Extract volume label from 83 entry */ + w = ld_word(dj.dir + XDIR_Label + si * 2); +#if _LFN_UNICODE + label[di++] = w; +#else + w = ff_convert(w, 0); /* Unicode -> OEM */ + if (w == 0) w = '?'; /* Replace wrong character */ + if (_DF1S && w >= 0x100) label[di++] = (char)(w >> 8); + label[di++] = (char)w; +#endif + } + label[di] = 0; + } else +#endif + { + si = di = 0; /* Extract volume label from AM_VOL entry with code comversion */ + do { +#if _LFN_UNICODE + w = (si < 11) ? dj.dir[si++] : ' '; + if (IsDBCS1(w) && si < 11 && IsDBCS2(dj.dir[si])) { + w = w << 8 | dj.dir[si++]; + } + label[di++] = ff_convert(w, 1); /* OEM -> Unicode */ +#else + label[di++] = dj.dir[si++]; +#endif + } while (di < 11); + do { /* Truncate trailing spaces */ + label[di] = 0; + if (di == 0) break; + } while (label[--di] == ' '); + } + } + } + if (res == FR_NO_FILE) { /* No label entry and return nul string */ + label[0] = 0; + res = FR_OK; + } + } + + /* Get volume serial number */ + if (res == FR_OK && vsn) { + res = move_window(fs, fs->volbase); + if (res == FR_OK) { + switch (fs->fs_type) { + case FS_EXFAT: + di = BPB_VolIDEx; break; + + case FS_FAT32: + di = BS_VolID32; break; + + default: + di = BS_VolID; + } + *vsn = ld_dword(fs->win + di); + } + } + + LEAVE_FF(fs, res); +} + + + +#if !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Set Volume Label */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_setlabel ( + const TCHAR* label /* Pointer to the volume label to set */ +) +{ + FRESULT res; + DIR dj; + FATFS *fs; + BYTE dirvn[22]; + UINT i, j, slen; + WCHAR w; + static const char badchr[] = "\"*+,.:;<=>\?[]|\x7F"; + + + /* Get logical drive */ + res = find_volume(&label, &fs, FA_WRITE); + if (res != FR_OK) LEAVE_FF(fs, res); + dj.obj.fs = fs; + + /* Get length of given volume label */ + for (slen = 0; (UINT)label[slen] >= ' '; slen++) ; /* Get name length */ + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { /* On the exFAT volume */ + for (i = j = 0; i < slen; ) { /* Create volume label in directory form */ + w = label[i++]; +#if !_LFN_UNICODE + if (IsDBCS1(w)) { + w = (i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } + w = ff_convert(w, 1); +#endif + if (w == 0 || chk_chr(badchr, w) || j == 22) { /* Check validity check validity of the volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + st_word(dirvn + j, w); j += 2; + } + slen = j; + } else +#endif + { /* On the FAT12/16/32 volume */ + for ( ; slen && label[slen - 1] == ' '; slen--) ; /* Remove trailing spaces */ + if (slen) { /* Is there a volume label to be set? */ + dirvn[0] = 0; i = j = 0; /* Create volume label in directory form */ + do { +#if _LFN_UNICODE + w = ff_convert(ff_wtoupper(label[i++]), 0); +#else + w = (BYTE)label[i++]; + if (IsDBCS1(w)) { + w = (j < 10 && i < slen && IsDBCS2(label[i])) ? w << 8 | (BYTE)label[i++] : 0; + } +#if _USE_LFN != 0 + w = ff_convert(ff_wtoupper(ff_convert(w, 1)), 0); +#else + if (IsLower(w)) w -= 0x20; /* To upper ASCII characters */ +#ifdef _EXCVT + if (w >= 0x80) w = ExCvt[w - 0x80]; /* To upper extended characters (SBCS cfg) */ +#else + if (!_DF1S && w >= 0x80) w = 0; /* Reject extended characters (ASCII cfg) */ +#endif +#endif +#endif + if (w == 0 || chk_chr(badchr, w) || j >= (UINT)((w >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */ + LEAVE_FF(fs, FR_INVALID_NAME); + } + if (w >= 0x100) dirvn[j++] = (BYTE)(w >> 8); + dirvn[j++] = (BYTE)w; + } while (i < slen); + while (j < 11) dirvn[j++] = ' '; /* Fill remaining name field */ + if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME); /* Reject illegal name (heading DDEM) */ + } + } + + /* Set volume label */ + dj.obj.sclust = 0; /* Open root directory */ + res = dir_sdi(&dj, 0); + if (res == FR_OK) { + res = dir_read(&dj, 1); /* Get volume label entry */ + if (res == FR_OK) { + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); /* Change the volume label */ + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + if (slen) { + mem_cpy(dj.dir, dirvn, 11); /* Change the volume label */ + } else { + dj.dir[DIR_Name] = DDEM; /* Remove the volume label */ + } + } + fs->wflag = 1; + res = sync_fs(fs); + } else { /* No volume label entry is found or error */ + if (res == FR_NO_FILE) { + res = FR_OK; + if (slen) { /* Create a volume label entry */ + res = dir_alloc(&dj, 1); /* Allocate an entry */ + if (res == FR_OK) { + mem_set(dj.dir, 0, SZDIRE); /* Clear the entry */ + if (_FS_EXFAT && fs->fs_type == FS_EXFAT) { + dj.dir[XDIR_Type] = 0x83; /* Create 83 entry */ + dj.dir[XDIR_NumLabel] = (BYTE)(slen / 2); + mem_cpy(dj.dir + XDIR_Label, dirvn, slen); + } else { + dj.dir[DIR_Attr] = AM_VOL; /* Create volume label entry */ + mem_cpy(dj.dir, dirvn, 11); + } + fs->wflag = 1; + res = sync_fs(fs); + } + } + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_LABEL */ + + + +#if _USE_EXPAND && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Allocate a Contiguous Blocks to the File */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_expand ( + FIL* fp, /* Pointer to the file object */ + FSIZE_t fsz, /* File size to be expanded to */ + BYTE opt /* Operation mode 0:Find and prepare or 1:Find and allocate */ +) +{ + FRESULT res; + FATFS *fs; + DWORD n, clst, stcl, scl, ncl, tcl, lclst; + + + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED); +#if _FS_EXFAT + if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED); /* Check if in size limit */ +#endif + n = (DWORD)fs->csize * SS(fs); /* Cluster size */ + tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0); /* Number of clusters required */ + stcl = fs->last_clst; lclst = 0; + if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2; + +#if _FS_EXFAT + if (fs->fs_type == FS_EXFAT) { + scl = find_bitmap(fs, stcl, tcl); /* Find a contiguous cluster block */ + if (scl == 0) res = FR_DENIED; /* No contiguous cluster block was found */ + if (scl == 0xFFFFFFFF) res = FR_DISK_ERR; + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + res = change_bitmap(fs, scl, tcl, 1); /* Mark the cluster block 'in use' */ + lclst = scl + tcl - 1; + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } else +#endif + { + scl = clst = stcl; ncl = 0; + for (;;) { /* Find a contiguous cluster block */ + n = get_fat(&fp->obj, clst); + if (++clst >= fs->n_fatent) clst = 2; + if (n == 1) { res = FR_INT_ERR; break; } + if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; } + if (n == 0) { /* Is it a free cluster? */ + if (++ncl == tcl) break; /* Break if a contiguous cluster block is found */ + } else { + scl = clst; ncl = 0; /* Not a free cluster */ + } + if (clst == stcl) { res = FR_DENIED; break; } /* No contiguous cluster? */ + } + if (res == FR_OK) { /* A contiguous free area is found */ + if (opt) { /* Allocate it now */ + for (clst = scl, n = tcl; n; clst++, n--) { /* Create a cluster chain on the FAT */ + res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1); + if (res != FR_OK) break; + lclst = clst; + } + } else { /* Set it as suggested point for next allocation */ + lclst = scl - 1; + } + } + } + + if (res == FR_OK) { + fs->last_clst = lclst; /* Set suggested start cluster to start next */ + if (opt) { /* Is it allocated now? */ + fp->obj.sclust = scl; /* Update object allocation information */ + fp->obj.objsize = fsz; + if (_FS_EXFAT) fp->obj.stat = 2; /* Set status 'contiguous chain' */ + fp->flag |= FA_MODIFIED; + if (fs->free_clst <= fs->n_fatent - 2) { /* Update FSINFO */ + fs->free_clst -= tcl; + fs->fsi_flag |= 1; + } + } + } + + LEAVE_FF(fs, res); +} + +#endif /* _USE_EXPAND && !_FS_READONLY */ + + + +#if _USE_FORWARD +/*-----------------------------------------------------------------------*/ +/* Forward data to the stream directly */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_forward ( + FIL* fp, /* Pointer to the file object */ + UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */ + UINT btf, /* Number of bytes to forward */ + UINT* bf /* Pointer to number of bytes forwarded */ +) +{ + FRESULT res; + FATFS *fs; + DWORD clst, sect; + FSIZE_t remain; + UINT rcnt, csect; + BYTE *dbuf; + + + *bf = 0; /* Clear transfer byte counter */ + res = validate(&fp->obj, &fs); /* Check validity of the file object */ + if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res); + if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */ + + remain = fp->obj.objsize - fp->fptr; + if (btf > remain) btf = (UINT)remain; /* Truncate btf by remaining bytes */ + + for ( ; btf && (*func)(0, 0); /* Repeat until all data transferred or stream goes busy */ + fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) { + csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1)); /* Sector offset in the cluster */ + if (fp->fptr % SS(fs) == 0) { /* On the sector boundary? */ + if (csect == 0) { /* On the cluster boundary? */ + clst = (fp->fptr == 0) ? /* On the top of the file? */ + fp->obj.sclust : get_fat(&fp->obj, fp->clust); + if (clst <= 1) ABORT(fs, FR_INT_ERR); + if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR); + fp->clust = clst; /* Update current cluster */ + } + } + sect = clust2sect(fs, fp->clust); /* Get current data sector */ + if (!sect) ABORT(fs, FR_INT_ERR); + sect += csect; +#if _FS_TINY + if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window to the file data */ + dbuf = fs->win; +#else + if (fp->sect != sect) { /* Fill sector cache with file data */ +#if !_FS_READONLY + if (fp->flag & FA_DIRTY) { /* Write-back dirty sector cache */ + if (disk_write(fs->drv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + fp->flag &= (BYTE)~FA_DIRTY; + } +#endif + if (disk_read(fs->drv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR); + } + dbuf = fp->buf; +#endif + fp->sect = sect; + rcnt = SS(fs) - (UINT)fp->fptr % SS(fs); /* Number of bytes left in the sector */ + if (rcnt > btf) rcnt = btf; /* Clip it by btr if needed */ + rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */ + if (!rcnt) ABORT(fs, FR_INT_ERR); + } + + LEAVE_FF(fs, FR_OK); +} +#endif /* _USE_FORWARD */ + + + +#if _USE_MKFS && !_FS_READONLY +/*-----------------------------------------------------------------------*/ +/* Create an FAT/exFAT volume */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_mkfs ( + const TCHAR* path, /* Logical drive number */ + BYTE opt, /* Format option */ + DWORD au, /* Size of allocation unit (cluster) [byte] */ + void* work, /* Pointer to working buffer */ + UINT len /* Size of working buffer */ +) +{ + const UINT n_fats = 1; /* Number of FATs for FAT12/16/32 volume (1 or 2) */ + const UINT n_rootdir = 512; /* Number of root directory entries for FAT12/16 volume */ + static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0}; /* Cluster size boundary for FAT12/16 volume (4Ks unit) */ + static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0}; /* Cluster size boundary for FAT32 volume (128Ks unit) */ + BYTE fmt, sys, *buf, *pte, pdrv, part; + WORD ss; + DWORD szb_buf, sz_buf, sz_blk, n_clst, pau, sect, nsect, n; + DWORD b_vol, b_fat, b_data; /* Base LBA for volume, fat, data */ + DWORD sz_vol, sz_rsv, sz_fat, sz_dir; /* Size for volume, fat, dir, data */ + UINT i; + int vol; + DSTATUS stat; +#if _USE_TRIM || _FS_EXFAT + DWORD tbl[3]; +#endif + + + /* Check mounted drive and clear work area */ + vol = get_ldnumber(&path); /* Get target logical drive */ + if (vol < 0) return FR_INVALID_DRIVE; + if (FatFs[vol]) FatFs[vol]->fs_type = 0; /* Clear the volume */ + pdrv = LD2PD(vol); /* Physical drive */ + part = LD2PT(vol); /* Partition (0:create as new, 1-4:get from partition table) */ + + /* Check physical drive status */ + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK || !sz_blk || sz_blk > 32768 || (sz_blk & (sz_blk - 1))) sz_blk = 1; /* Erase block to align data area */ +#if _MAX_SS != _MIN_SS /* Get sector size of the medium if variable sector size cfg. */ + if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR; + if (ss > _MAX_SS || ss < _MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR; +#else + ss = _MAX_SS; +#endif + if ((au != 0 && au < ss) || au > 0x1000000 || (au & (au - 1))) return FR_INVALID_PARAMETER; /* Check if au is valid */ + au /= ss; /* Cluster size in unit of sector */ + + /* Get working buffer */ + buf = (BYTE*)work; /* Working buffer */ + sz_buf = len / ss; /* Size of working buffer (sector) */ + szb_buf = sz_buf * ss; /* Size of working buffer (byte) */ + if (!szb_buf) return FR_MKFS_ABORTED; + + /* Determine where the volume to be located (b_vol, sz_vol) */ + if (_MULTI_PARTITION && part != 0) { + /* Get partition information from partition table in the MBR */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Load MBR */ + if (ld_word(buf + BS_55AA) != 0xAA55) return FR_MKFS_ABORTED; /* Check if MBR is valid */ + pte = buf + (MBR_Table + (part - 1) * SZ_PTE); + if (!pte[PTE_System]) return FR_MKFS_ABORTED; /* No partition? */ + b_vol = ld_dword(pte + PTE_StLba); /* Get volume start sector */ + sz_vol = ld_dword(pte + PTE_SizLba); /* Get volume size */ + } else { + /* Create a single-partition in this function */ + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) return FR_DISK_ERR; + b_vol = (opt & FM_SFD) ? 0 : 63; /* Volume start sector */ + if (sz_vol < b_vol) return FR_MKFS_ABORTED; + sz_vol -= b_vol; /* Volume size */ + } + if (sz_vol < 128) return FR_MKFS_ABORTED; /* Check if volume size is >=128s */ + + /* Pre-determine the FAT type */ + do { + if (_FS_EXFAT && (opt & FM_EXFAT)) { /* exFAT possible? */ + if ((opt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || au > 128) { /* exFAT only, vol >= 64Ms or au > 128s ? */ + fmt = FS_EXFAT; break; + } + } + if (au > 128) return FR_INVALID_PARAMETER; /* Too large au for FAT/FAT32 */ + if (opt & FM_FAT32) { /* FAT32 possible? */ + if ((opt & FM_ANY) == FM_FAT32 || !(opt & FM_FAT)) { /* FAT32 only or no-FAT? */ + fmt = FS_FAT32; break; + } + } + if (!(opt & FM_FAT)) return FR_INVALID_PARAMETER; /* no-FAT? */ + fmt = FS_FAT16; + } while (0); + +#if _FS_EXFAT + if (fmt == FS_EXFAT) { /* Create an exFAT volume */ + DWORD szb_bit, szb_case, sum, nb, cl; + WCHAR ch, si; + UINT j, st; + BYTE b; + + if (sz_vol < 0x1000) return FR_MKFS_ABORTED; /* Too small volume? */ +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area may be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Determine FAT location, data location and number of clusters */ + if (!au) { /* au auto-selection */ + au = 8; + if (sz_vol >= 0x80000) au = 64; /* >= 512Ks */ + if (sz_vol >= 0x4000000) au = 256; /* >= 64Ms */ + } + b_fat = b_vol + 32; /* FAT start at offset 32 */ + sz_fat = ((sz_vol / au + 2) * 4 + ss - 1) / ss; /* Number of FAT sectors */ + b_data = (b_fat + sz_fat + sz_blk - 1) & ~(sz_blk - 1); /* Align data area to the erase block boundary */ + if (b_data >= sz_vol / 2) return FR_MKFS_ABORTED; /* Too small volume? */ + n_clst = (sz_vol - (b_data - b_vol)) / au; /* Number of clusters */ + if (n_clst <16) return FR_MKFS_ABORTED; /* Too few clusters? */ + if (n_clst > MAX_EXFAT) return FR_MKFS_ABORTED; /* Too many clusters? */ + + szb_bit = (n_clst + 7) / 8; /* Size of allocation bitmap */ + tbl[0] = (szb_bit + au * ss - 1) / (au * ss); /* Number of allocation bitmap clusters */ + + /* Create a compressed up-case table */ + sect = b_data + au * tbl[0]; /* Table start sector */ + sum = 0; /* Table checksum to be stored in the 82 entry */ + st = si = i = j = szb_case = 0; + do { + switch (st) { + case 0: + ch = ff_wtoupper(si); /* Get an up-case char */ + if (ch != si) { + si++; break; /* Store the up-case char if exist */ + } + for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ; /* Get run length of no-case block */ + if (j >= 128) { + ch = 0xFFFF; st = 2; break; /* Compress the no-case block if run is >= 128 */ + } + st = 1; /* Do not compress short run */ + /* go to next case */ + case 1: + ch = si++; /* Fill the short run */ + if (--j == 0) st = 0; + break; + + default: + ch = (WCHAR)j; si += j; /* Number of chars to skip */ + st = 0; + } + sum = xsum32(buf[i + 0] = (BYTE)ch, sum); /* Put it into the write buffer */ + sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum); + i += 2; szb_case += 2; + if (!si || i == szb_buf) { /* Write buffered data when buffer full or end of process */ + n = (i + ss - 1) / ss; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; i = 0; + } + } while (si); + tbl[1] = (szb_case + au * ss - 1) / (au * ss); /* Number of up-case table clusters */ + tbl[2] = 1; /* Number of root dir clusters */ + + /* Initialize the allocation bitmap */ + sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */ + nb = tbl[0] + tbl[1] + tbl[2]; /* Number of clusters in-use by system */ + do { + mem_set(buf, 0, szb_buf); + for (i = 0; nb >= 8 && i < szb_buf; buf[i++] = 0xFF, nb -= 8) ; + for (b = 1; nb && i < szb_buf; buf[i] |= b, b <<= 1, nb--) ; + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the FAT */ + sect = b_fat; nsect = sz_fat; /* Start of FAT and number of FAT sectors */ + j = nb = cl = 0; + do { + mem_set(buf, 0, szb_buf); i = 0; /* Clear work area and reset write index */ + if (cl == 0) { /* Set entry 0 and 1 */ + st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++; + st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++; + } + do { /* Create chains of bitmap, up-case and root dir */ + while (nb && i < szb_buf) { /* Create a chain */ + st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF); + i += 4; cl++; nb--; + } + if (!nb && j < 3) nb = tbl[j++]; /* Next chain */ + } while (nb && i < szb_buf); + n = (nsect > sz_buf) ? sz_buf : nsect; /* Write the buffered data */ + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + + /* Initialize the root directory */ + mem_set(buf, 0, szb_buf); + buf[SZDIRE * 0 + 0] = 0x83; /* 83 entry (volume label) */ + buf[SZDIRE * 1 + 0] = 0x81; /* 81 entry (allocation bitmap) */ + st_dword(buf + SZDIRE * 1 + 20, 2); + st_dword(buf + SZDIRE * 1 + 24, szb_bit); + buf[SZDIRE * 2 + 0] = 0x82; /* 82 entry (up-case table) */ + st_dword(buf + SZDIRE * 2 + 4, sum); + st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]); + st_dword(buf + SZDIRE * 2 + 24, szb_case); + sect = b_data + au * (tbl[0] + tbl[1]); nsect = au; /* Start of the root directory and number of sectors */ + do { /* Fill root directory sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + + /* Create two set of the exFAT VBR blocks */ + sect = b_vol; + for (n = 0; n < 2; n++) { + /* Main record (+0) */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT ", 11); /* Boot jump code (x86), OEM name */ + st_dword(buf + BPB_VolOfsEx, b_vol); /* Volume offset in the physical drive [sector] */ + st_dword(buf + BPB_TotSecEx, sz_vol); /* Volume size [sector] */ + st_dword(buf + BPB_FatOfsEx, b_fat - b_vol); /* FAT offset [sector] */ + st_dword(buf + BPB_FatSzEx, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_DataOfsEx, b_data - b_vol); /* Data offset [sector] */ + st_dword(buf + BPB_NumClusEx, n_clst); /* Number of clusters */ + st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]); /* Root dir cluster # */ + st_dword(buf + BPB_VolIDEx, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FSVerEx, 0x100); /* File system version (1.00) */ + for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ; /* Log2 of sector size [byte] */ + for (buf[BPB_SecPerClusEx] = 0, i = au; i >>= 1; buf[BPB_SecPerClusEx]++) ; /* Log2 of cluster size [sector] */ + buf[BPB_NumFATsEx] = 1; /* Number of FATs */ + buf[BPB_DrvNumEx] = 0x80; /* Drive number (for int13) */ + st_word(buf + BS_BootCodeEx, 0xFEEB); /* Boot code (x86) */ + st_word(buf + BS_55AA, 0xAA55); /* Signature (placed here regardless of sector size) */ + for (i = sum = 0; i < ss; i++) { /* VBR checksum */ + if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum); + } + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + /* Extended bootstrap record (+1..+8) */ + mem_set(buf, 0, ss); + st_word(buf + ss - 2, 0xAA55); /* Signature (placed at end of sector) */ + for (j = 1; j < 9; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* OEM/Reserved record (+9..+10) */ + mem_set(buf, 0, ss); + for ( ; j < 11; j++) { + for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ; /* VBR checksum */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + /* Sum record (+11) */ + for (i = 0; i < ss; i += 4) st_dword(buf + i, sum); /* Fill with checksum value */ + if (disk_write(pdrv, buf, sect++, 1) != RES_OK) return FR_DISK_ERR; + } + + } else +#endif /* _FS_EXFAT */ + { /* Create an FAT12/16/32 volume */ + do { + pau = au; + /* Pre-determine number of clusters and FAT sub-type */ + if (fmt == FS_FAT32) { /* FAT32 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x20000; /* Volume size in unit of 128KS */ + for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; /* Number of clusters */ + sz_fat = (n_clst * 4 + 8 + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 32; /* Number of reserved sectors */ + sz_dir = 0; /* No static directory */ + if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) return FR_MKFS_ABORTED; + } else { /* FAT12/16 volume */ + if (!pau) { /* au auto-selection */ + n = sz_vol / 0x1000; /* Volume size in unit of 4KS */ + for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ; /* Get from table */ + } + n_clst = sz_vol / pau; + if (n_clst > MAX_FAT12) { + n = n_clst * 2 + 4; /* FAT size [byte] */ + } else { + fmt = FS_FAT12; + n = (n_clst * 3 + 1) / 2 + 3; /* FAT size [byte] */ + } + sz_fat = (n + ss - 1) / ss; /* FAT size [sector] */ + sz_rsv = 1; /* Number of reserved sectors */ + sz_dir = (DWORD)n_rootdir * SZDIRE / ss; /* Rootdir size [sector] */ + } + b_fat = b_vol + sz_rsv; /* FAT base */ + b_data = b_fat + sz_fat * n_fats + sz_dir; /* Data base */ + + /* Align data base to erase block boundary (for flash memory media) */ + n = ((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data; /* Next nearest erase block from current data base */ + if (fmt == FS_FAT32) { /* FAT32: Move FAT base */ + sz_rsv += n; b_fat += n; + } else { /* FAT12/16: Expand FAT size */ + sz_fat += n / n_fats; + } + + /* Determine number of clusters and final check of validity of the FAT sub-type */ + if (sz_vol < b_data + pau * 16 - b_vol) return FR_MKFS_ABORTED; /* Too small volume */ + n_clst = (sz_vol - sz_rsv - sz_fat * n_fats - sz_dir) / pau; + if (fmt == FS_FAT32) { + if (n_clst <= MAX_FAT16) { /* Too few clusters for FAT32 */ + if (!au && (au = pau / 2) != 0) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT16) { + if (n_clst > MAX_FAT16) { /* Too many clusters for FAT16 */ + if (!au && (pau * 2) <= 64) { + au = pau * 2; continue; /* Adjust cluster size and retry */ + } + if ((opt & FM_FAT32)) { + fmt = FS_FAT32; continue; /* Switch type to FAT32 and retry */ + } + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + if (n_clst <= MAX_FAT12) { /* Too few clusters for FAT16 */ + if (!au && (au = pau * 2) <= 128) continue; /* Adjust cluster size and retry */ + return FR_MKFS_ABORTED; + } + } + if (fmt == FS_FAT12 && n_clst > MAX_FAT12) return FR_MKFS_ABORTED; /* Too many clusters for FAT12 */ + + /* Ok, it is the valid cluster configuration */ + break; + } while (1); + +#if _USE_TRIM + tbl[0] = b_vol; tbl[1] = b_vol + sz_vol - 1; /* Inform the device the volume area can be erased */ + disk_ioctl(pdrv, CTRL_TRIM, tbl); +#endif + /* Create FAT VBR */ + mem_set(buf, 0, ss); + mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */ + st_word(buf + BPB_BytsPerSec, ss); /* Sector size [byte] */ + buf[BPB_SecPerClus] = (BYTE)pau; /* Cluster size [sector] */ + st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv); /* Size of reserved area */ + buf[BPB_NumFATs] = (BYTE)n_fats; /* Number of FATs */ + st_word(buf + BPB_RootEntCnt, (WORD)((fmt == FS_FAT32) ? 0 : n_rootdir)); /* Number of root directory entries */ + if (sz_vol < 0x10000) { + st_word(buf + BPB_TotSec16, (WORD)sz_vol); /* Volume size in 16-bit LBA */ + } else { + st_dword(buf + BPB_TotSec32, sz_vol); /* Volume size in 32-bit LBA */ + } + buf[BPB_Media] = 0xF8; /* Media descriptor byte */ + st_word(buf + BPB_SecPerTrk, 63); /* Number of sectors per track (for int13) */ + st_word(buf + BPB_NumHeads, 255); /* Number of heads (for int13) */ + st_dword(buf + BPB_HiddSec, b_vol); /* Volume offset in the physical drive [sector] */ + if (fmt == FS_FAT32) { + st_dword(buf + BS_VolID32, GET_FATTIME()); /* VSN */ + st_dword(buf + BPB_FATSz32, sz_fat); /* FAT size [sector] */ + st_dword(buf + BPB_RootClus32, 2); /* Root directory cluster # (2) */ + st_word(buf + BPB_FSInfo32, 1); /* Offset of FSINFO sector (VBR + 1) */ + st_word(buf + BPB_BkBootSec32, 6); /* Offset of backup VBR (VBR + 6) */ + buf[BS_DrvNum32] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig32] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab32, "NO NAME " "FAT32 ", 19); /* Volume label, FAT signature */ + } else { + st_dword(buf + BS_VolID, GET_FATTIME()); /* VSN */ + st_word(buf + BPB_FATSz16, (WORD)sz_fat); /* FAT size [sector] */ + buf[BS_DrvNum] = 0x80; /* Drive number (for int13) */ + buf[BS_BootSig] = 0x29; /* Extended boot signature */ + mem_cpy(buf + BS_VolLab, "NO NAME " "FAT ", 19); /* Volume label, FAT signature */ + } + st_word(buf + BS_55AA, 0xAA55); /* Signature (offset is fixed here regardless of sector size) */ + if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the VBR sector */ + + /* Create FSINFO record if needed */ + if (fmt == FS_FAT32) { + disk_write(pdrv, buf, b_vol + 6, 1); /* Write backup VBR (VBR + 6) */ + mem_set(buf, 0, ss); + st_dword(buf + FSI_LeadSig, 0x41615252); + st_dword(buf + FSI_StrucSig, 0x61417272); + st_dword(buf + FSI_Free_Count, n_clst - 1); /* Number of free clusters */ + st_dword(buf + FSI_Nxt_Free, 2); /* Last allocated cluster# */ + st_word(buf + BS_55AA, 0xAA55); + disk_write(pdrv, buf, b_vol + 7, 1); /* Write backup FSINFO (VBR + 7) */ + disk_write(pdrv, buf, b_vol + 1, 1); /* Write original FSINFO (VBR + 1) */ + } + + /* Initialize FAT area */ + mem_set(buf, 0, (UINT)szb_buf); + sect = b_fat; /* FAT start sector */ + for (i = 0; i < n_fats; i++) { /* Initialize FATs each */ + if (fmt == FS_FAT32) { + st_dword(buf + 0, 0xFFFFFFF8); /* Entry 0 */ + st_dword(buf + 4, 0xFFFFFFFF); /* Entry 1 */ + st_dword(buf + 8, 0x0FFFFFFF); /* Entry 2 (root directory) */ + } else { + st_dword(buf + 0, (fmt == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8); /* Entry 0 and 1 */ + } + nsect = sz_fat; /* Number of FAT sectors */ + do { /* Fill FAT sectors */ + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + mem_set(buf, 0, ss); + sect += n; nsect -= n; + } while (nsect); + } + + /* Initialize root directory (fill with zero) */ + nsect = (fmt == FS_FAT32) ? pau : sz_dir; /* Number of root directory sectors */ + do { + n = (nsect > sz_buf) ? sz_buf : nsect; + if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) return FR_DISK_ERR; + sect += n; nsect -= n; + } while (nsect); + } + + /* Determine system ID in the partition table */ + if (_FS_EXFAT && fmt == FS_EXFAT) { + sys = 0x07; /* HPFS/NTFS/exFAT */ + } else { + if (fmt == FS_FAT32) { + sys = 0x0C; /* FAT32X */ + } else { + if (sz_vol >= 0x10000) { + sys = 0x06; /* FAT12/16 (>=64KS) */ + } else { + sys = (fmt == FS_FAT16) ? 0x04 : 0x01; /* FAT16 (<64KS) : FAT12 (<64KS) */ + } + } + } + + /* Update partition information */ + if (_MULTI_PARTITION && part != 0) { /* Created in the existing partition */ + /* Update system ID in the partition table */ + if (disk_read(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Read the MBR */ + buf[MBR_Table + (part - 1) * SZ_PTE + PTE_System] = sys; /* Set system ID */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it back to the MBR */ + } else { /* Created as a new single partition */ + if (!(opt & FM_SFD)) { /* Create partition table if in FDISK format */ + mem_set(buf, 0, ss); + st_word(buf + BS_55AA, 0xAA55); /* MBR signature */ + pte = buf + MBR_Table; /* Create partition table for single partition in the drive */ + pte[PTE_Boot] = 0; /* Boot indicator */ + pte[PTE_StHead] = 1; /* Start head */ + pte[PTE_StSec] = 1; /* Start sector */ + pte[PTE_StCyl] = 0; /* Start cylinder */ + pte[PTE_System] = sys; /* System type */ + n = (b_vol + sz_vol) / (63 * 255); /* (End CHS may be invalid) */ + pte[PTE_EdHead] = 254; /* End head */ + pte[PTE_EdSec] = (BYTE)(n >> 2 | 63); /* End sector */ + pte[PTE_EdCyl] = (BYTE)n; /* End cylinder */ + st_dword(pte + PTE_StLba, b_vol); /* Start offset in LBA */ + st_dword(pte + PTE_SizLba, sz_vol); /* Size in sectors */ + if (disk_write(pdrv, buf, 0, 1) != RES_OK) return FR_DISK_ERR; /* Write it to the MBR */ + } + } + + if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) return FR_DISK_ERR; + + return FR_OK; +} + + + +#if _MULTI_PARTITION +/*-----------------------------------------------------------------------*/ +/* Create partition table on the physical drive */ +/*-----------------------------------------------------------------------*/ + +FRESULT f_fdisk ( + BYTE pdrv, /* Physical drive number */ + const DWORD* szt, /* Pointer to the size table for each partitions */ + void* work /* Pointer to the working buffer */ +) +{ + UINT i, n, sz_cyl, tot_cyl, b_cyl, e_cyl, p_cyl; + BYTE s_hd, e_hd, *p, *buf = (BYTE*)work; + DSTATUS stat; + DWORD sz_disk, sz_part, s_part; + + + stat = disk_initialize(pdrv); + if (stat & STA_NOINIT) return FR_NOT_READY; + if (stat & STA_PROTECT) return FR_WRITE_PROTECTED; + if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_disk)) return FR_DISK_ERR; + + /* Determine the CHS without any consideration of the drive geometry */ + for (n = 16; n < 256 && sz_disk / n / 63 > 1024; n *= 2) ; + if (n == 256) n--; + e_hd = n - 1; + sz_cyl = 63 * n; + tot_cyl = sz_disk / sz_cyl; + + /* Create partition table */ + mem_set(buf, 0, _MAX_SS); + p = buf + MBR_Table; b_cyl = 0; + for (i = 0; i < 4; i++, p += SZ_PTE) { + p_cyl = (szt[i] <= 100U) ? (DWORD)tot_cyl * szt[i] / 100 : szt[i] / sz_cyl; /* Number of cylinders */ + if (!p_cyl) continue; + s_part = (DWORD)sz_cyl * b_cyl; + sz_part = (DWORD)sz_cyl * p_cyl; + if (i == 0) { /* Exclude first track of cylinder 0 */ + s_hd = 1; + s_part += 63; sz_part -= 63; + } else { + s_hd = 0; + } + e_cyl = b_cyl + p_cyl - 1; /* End cylinder */ + if (e_cyl >= tot_cyl) return FR_INVALID_PARAMETER; + + /* Set partition table */ + p[1] = s_hd; /* Start head */ + p[2] = (BYTE)((b_cyl >> 2) + 1); /* Start sector */ + p[3] = (BYTE)b_cyl; /* Start cylinder */ + p[4] = 0x07; /* System type (temporary setting) */ + p[5] = e_hd; /* End head */ + p[6] = (BYTE)((e_cyl >> 2) + 63); /* End sector */ + p[7] = (BYTE)e_cyl; /* End cylinder */ + st_dword(p + 8, s_part); /* Start sector in LBA */ + st_dword(p + 12, sz_part); /* Number of sectors */ + + /* Next partition */ + b_cyl += p_cyl; + } + st_word(p, 0xAA55); + + /* Write it to the MBR */ + return (disk_write(pdrv, buf, 0, 1) != RES_OK || disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) ? FR_DISK_ERR : FR_OK; +} + +#endif /* _MULTI_PARTITION */ +#endif /* _USE_MKFS && !_FS_READONLY */ + + + + +#if _USE_STRFUNC +/*-----------------------------------------------------------------------*/ +/* Get a string from the file */ +/*-----------------------------------------------------------------------*/ + +TCHAR* f_gets ( + TCHAR* buff, /* Pointer to the string buffer to read */ + int len, /* Size of string buffer (characters) */ + FIL* fp /* Pointer to the file object */ +) +{ + int n = 0; + TCHAR c, *p = buff; + BYTE s[2]; + UINT rc; + + + while (n < len - 1) { /* Read characters until buffer gets filled */ +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Read a character in UTF-8 */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (c >= 0x80) { + if (c < 0xC0) continue; /* Skip stray trailer */ + if (c < 0xE0) { /* Two-byte sequence (0x80-0x7FF) */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c & 0x1F) << 6 | (s[0] & 0x3F); + if (c < 0x80) c = '?'; /* Reject invalid code range */ + } else { + if (c < 0xF0) { /* Three-byte sequence (0x800-0xFFFF) */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = c << 12 | (s[0] & 0x3F) << 6 | (s[1] & 0x3F); + if (c < 0x800) c = '?'; /* Reject invalid code range */ + } else { /* Reject four-byte sequence */ + c = '?'; + } + } + } +#elif _STRF_ENCODE == 2 /* Read a character in UTF-16BE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[1] + (s[0] << 8); +#elif _STRF_ENCODE == 1 /* Read a character in UTF-16LE */ + f_read(fp, s, 2, &rc); + if (rc != 2) break; + c = s[0] + (s[1] << 8); +#else /* Read a character in ANSI/OEM */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; + if (IsDBCS1(c)) { + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = (c << 8) + s[0]; + } + c = ff_convert(c, 1); /* OEM -> Unicode */ + if (!c) c = '?'; +#endif +#else /* Read a character without conversion */ + f_read(fp, s, 1, &rc); + if (rc != 1) break; + c = s[0]; +#endif + if (_USE_STRFUNC == 2 && c == '\r') continue; /* Strip '\r' */ + *p++ = c; + n++; + if (c == '\n') break; /* Break on EOL */ + } + *p = 0; + return n ? buff : 0; /* When no data read (eof or error), return with error. */ +} + + + + +#if !_FS_READONLY +#include +/*-----------------------------------------------------------------------*/ +/* Put a character to the file */ +/*-----------------------------------------------------------------------*/ + +typedef struct { + FIL *fp; /* Ptr to the writing file */ + int idx, nchr; /* Write index of buf[] (-1:error), number of chars written */ + BYTE buf[64]; /* Write buffer */ +} putbuff; + + +static +void putc_bfd ( /* Buffered write with code conversion */ + putbuff* pb, + TCHAR c +) +{ + UINT bw; + int i; + + + if (_USE_STRFUNC == 2 && c == '\n') { /* LF -> CRLF conversion */ + putc_bfd(pb, '\r'); + } + + i = pb->idx; /* Write index of pb->buf[] */ + if (i < 0) return; + +#if _LFN_UNICODE +#if _STRF_ENCODE == 3 /* Write a character in UTF-8 */ + if (c < 0x80) { /* 7-bit */ + pb->buf[i++] = (BYTE)c; + } else { + if (c < 0x800) { /* 11-bit */ + pb->buf[i++] = (BYTE)(0xC0 | c >> 6); + } else { /* 16-bit */ + pb->buf[i++] = (BYTE)(0xE0 | c >> 12); + pb->buf[i++] = (BYTE)(0x80 | (c >> 6 & 0x3F)); + } + pb->buf[i++] = (BYTE)(0x80 | (c & 0x3F)); + } +#elif _STRF_ENCODE == 2 /* Write a character in UTF-16BE */ + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#elif _STRF_ENCODE == 1 /* Write a character in UTF-16LE */ + pb->buf[i++] = (BYTE)c; + pb->buf[i++] = (BYTE)(c >> 8); +#else /* Write a character in ANSI/OEM */ + c = ff_convert(c, 0); /* Unicode -> OEM */ + if (!c) c = '?'; + if (c >= 0x100) + pb->buf[i++] = (BYTE)(c >> 8); + pb->buf[i++] = (BYTE)c; +#endif +#else /* Write a character without conversion */ + pb->buf[i++] = (BYTE)c; +#endif + + if (i >= (int)(sizeof pb->buf) - 3) { /* Write buffered characters to the file */ + f_write(pb->fp, pb->buf, (UINT)i, &bw); + i = (bw == (UINT)i) ? 0 : -1; + } + pb->idx = i; + pb->nchr++; +} + + +static +int putc_flush ( /* Flush left characters in the buffer */ + putbuff* pb +) +{ + UINT nw; + + if ( pb->idx >= 0 /* Flush buffered characters to the file */ + && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK + && (UINT)pb->idx == nw) return pb->nchr; + return EOF; +} + + +static +void putc_init ( /* Initialize write buffer */ + putbuff* pb, + FIL* fp +) +{ + pb->fp = fp; + pb->nchr = pb->idx = 0; +} + + + +int f_putc ( + TCHAR c, /* A character to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + putc_bfd(&pb, c); /* Put the character */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_puts ( + const TCHAR* str, /* Pointer to the string to be output */ + FIL* fp /* Pointer to the file object */ +) +{ + putbuff pb; + + + putc_init(&pb, fp); + while (*str) putc_bfd(&pb, *str++); /* Put the string */ + return putc_flush(&pb); +} + + + + +/*-----------------------------------------------------------------------*/ +/* Put a formatted string to the file */ +/*-----------------------------------------------------------------------*/ + +int f_printf ( + FIL* fp, /* Pointer to the file object */ + const TCHAR* fmt, /* Pointer to the format string */ + ... /* Optional arguments... */ +) +{ + va_list arp; + putbuff pb; + BYTE f, r; + UINT i, j, w; + DWORD v; + TCHAR c, d, str[32], *p; + + + putc_init(&pb, fp); + + va_start(arp, fmt); + + for (;;) { + c = *fmt++; + if (c == 0) break; /* End of string */ + if (c != '%') { /* Non escape character */ + putc_bfd(&pb, c); + continue; + } + w = f = 0; + c = *fmt++; + if (c == '0') { /* Flag: '0' padding */ + f = 1; c = *fmt++; + } else { + if (c == '-') { /* Flag: left justified */ + f = 2; c = *fmt++; + } + } + while (IsDigit(c)) { /* Precision */ + w = w * 10 + c - '0'; + c = *fmt++; + } + if (c == 'l' || c == 'L') { /* Prefix: Size is long int */ + f |= 4; c = *fmt++; + } + if (!c) break; + d = c; + if (IsLower(d)) d -= 0x20; + switch (d) { /* Type is... */ + case 'S' : /* String */ + p = va_arg(arp, TCHAR*); + for (j = 0; p[j]; j++) ; + if (!(f & 2)) { + while (j++ < w) putc_bfd(&pb, ' '); + } + while (*p) putc_bfd(&pb, *p++); + while (j++ < w) putc_bfd(&pb, ' '); + continue; + + case 'C' : /* Character */ + putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue; + + case 'B' : /* Binary */ + r = 2; break; + + case 'O' : /* Octal */ + r = 8; break; + + case 'D' : /* Signed decimal */ + case 'U' : /* Unsigned decimal */ + r = 10; break; + + case 'X' : /* Hexdecimal */ + r = 16; break; + + default: /* Unknown type (pass-through) */ + putc_bfd(&pb, c); continue; + } + + /* Get an argument and put it in numeral */ + v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int)); + if (d == 'D' && (v & 0x80000000)) { + v = 0 - v; + f |= 8; + } + i = 0; + do { + d = (TCHAR)(v % r); v /= r; + if (d > 9) d += (c == 'x') ? 0x27 : 0x07; + str[i++] = d + '0'; + } while (v && i < sizeof str / sizeof str[0]); + if (f & 8) str[i++] = '-'; + j = i; d = (f & 1) ? '0' : ' '; + while (!(f & 2) && j++ < w) putc_bfd(&pb, d); + do { + putc_bfd(&pb, str[--i]); + } while (i); + while (j++ < w) putc_bfd(&pb, d); + } + + va_end(arp); + + return putc_flush(&pb); +} + +#endif /* !_FS_READONLY */ +#endif /* _USE_STRFUNC */ diff --git a/Core/Src/Middlewares/Third_Party/FatFs/src/ff_gen_drv.c b/Core/Src/Middlewares/Third_Party/FatFs/src/ff_gen_drv.c new file mode 100644 index 0000000..ccd595b --- /dev/null +++ b/Core/Src/Middlewares/Third_Party/FatFs/src/ff_gen_drv.c @@ -0,0 +1,122 @@ +/** + ****************************************************************************** + * @file ff_gen_drv.c + * @author MCD Application Team + * @brief FatFs generic low level driver. + ***************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. All rights reserved. + * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +**/ +/* Includes ------------------------------------------------------------------*/ +#include "ff_gen_drv.h" + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ +/* Private variables ---------------------------------------------------------*/ +Disk_drvTypeDef disk = {{0},{0},{0},0}; + +/* Private function prototypes -----------------------------------------------*/ +/* Private functions ---------------------------------------------------------*/ + +/** + * @brief Links a compatible diskio driver/lun id and increments the number of active + * linked drivers. + * @note The number of linked drivers (volumes) is up to 10 due to FatFs limits. + * @param drv: pointer to the disk IO Driver structure + * @param path: pointer to the logical drive path + * @param lun : only used for USB Key Disk to add multi-lun management + else the parameter must be equal to 0 + * @retval Returns 0 in case of success, otherwise 1. + */ +uint8_t FATFS_LinkDriverEx(const Diskio_drvTypeDef *drv, char *path, uint8_t lun) +{ + uint8_t ret = 1; + uint8_t DiskNum = 0; + + if(disk.nbr < _VOLUMES) + { + disk.is_initialized[disk.nbr] = 0; + disk.drv[disk.nbr] = drv; + disk.lun[disk.nbr] = lun; + DiskNum = disk.nbr++; + path[0] = DiskNum + '0'; + path[1] = ':'; + path[2] = '/'; + path[3] = 0; + ret = 0; + } + + return ret; +} + +/** + * @brief Links a compatible diskio driver and increments the number of active + * linked drivers. + * @note The number of linked drivers (volumes) is up to 10 due to FatFs limits + * @param drv: pointer to the disk IO Driver structure + * @param path: pointer to the logical drive path + * @retval Returns 0 in case of success, otherwise 1. + */ +uint8_t FATFS_LinkDriver(const Diskio_drvTypeDef *drv, char *path) +{ + return FATFS_LinkDriverEx(drv, path, 0); +} + +/** + * @brief Unlinks a diskio driver and decrements the number of active linked + * drivers. + * @param path: pointer to the logical drive path + * @param lun : not used + * @retval Returns 0 in case of success, otherwise 1. + */ +uint8_t FATFS_UnLinkDriverEx(char *path, uint8_t lun) +{ + uint8_t DiskNum = 0; + uint8_t ret = 1; + + if(disk.nbr >= 1) + { + DiskNum = path[0] - '0'; + if(disk.drv[DiskNum] != 0) + { + disk.drv[DiskNum] = 0; + disk.lun[DiskNum] = 0; + disk.nbr--; + ret = 0; + } + } + + return ret; +} + +/** + * @brief Unlinks a diskio driver and decrements the number of active linked + * drivers. + * @param path: pointer to the logical drive path + * @retval Returns 0 in case of success, otherwise 1. + */ +uint8_t FATFS_UnLinkDriver(char *path) +{ + return FATFS_UnLinkDriverEx(path, 0); +} + +/** + * @brief Gets number of linked drivers to the FatFs module. + * @param None + * @retval Number of attached drivers. + */ +uint8_t FATFS_GetAttachedDriversNbr(void) +{ + return disk.nbr; +} + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ + diff --git a/Core/Src/Middlewares/Third_Party/FatFs/src/option/syscall.c b/Core/Src/Middlewares/Third_Party/FatFs/src/option/syscall.c new file mode 100644 index 0000000..cd6370d --- /dev/null +++ b/Core/Src/Middlewares/Third_Party/FatFs/src/option/syscall.c @@ -0,0 +1,177 @@ +/*------------------------------------------------------------------------*/ +/* Sample code of OS dependent controls for FatFs */ +/* (C)ChaN, 2014 */ +/* Portions COPYRIGHT 2017 STMicroelectronics */ +/* Portions Copyright (C) 2014, ChaN, all right reserved */ +/*------------------------------------------------------------------------*/ + +/** + ****************************************************************************** + * @attention + * + * Copyright (c) 2017 STMicroelectronics. All rights reserved. + * + * This software component is licensed by ST under BSD 3-Clause license, + * the "License"; You may not use this file except in compliance with the + * License. You may obtain a copy of the License at: + * opensource.org/licenses/BSD-3-Clause + * + ****************************************************************************** +**/ + + + +#include "../ff.h" + + +#if _FS_REENTRANT +/*------------------------------------------------------------------------*/ +/* Create a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to create a new +/ synchronization object, such as semaphore and mutex. When a 0 is returned, +/ the f_mount() function fails with FR_INT_ERR. +*/ + +int ff_cre_syncobj ( /* 1:Function succeeded, 0:Could not create the sync object */ + BYTE vol, /* Corresponding volume (logical drive number) */ + _SYNC_t *sobj /* Pointer to return the created sync object */ +) +{ + + int ret; +#if _USE_MUTEX + +#if (osCMSIS < 0x20000U) + osMutexDef(MTX); + *sobj = osMutexCreate(osMutex(MTX)); +#else + *sobj = osMutexNew(NULL); +#endif + +#else + +#if (osCMSIS < 0x20000U) + osSemaphoreDef(SEM); + *sobj = osSemaphoreCreate(osSemaphore(SEM), 1); +#else + *sobj = osSemaphoreNew(1, 1, NULL); +#endif + +#endif + ret = (*sobj != NULL); + + return ret; +} + + + +/*------------------------------------------------------------------------*/ +/* Delete a Synchronization Object */ +/*------------------------------------------------------------------------*/ +/* This function is called in f_mount() function to delete a synchronization +/ object that created with ff_cre_syncobj() function. When a 0 is returned, +/ the f_mount() function fails with FR_INT_ERR. +*/ + +int ff_del_syncobj ( /* 1:Function succeeded, 0:Could not delete due to any error */ + _SYNC_t sobj /* Sync object tied to the logical drive to be deleted */ +) +{ +#if _USE_MUTEX + osMutexDelete (sobj); +#else + osSemaphoreDelete (sobj); +#endif + return 1; +} + + + +/*------------------------------------------------------------------------*/ +/* Request Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on entering file functions to lock the volume. +/ When a 0 is returned, the file function fails with FR_TIMEOUT. +*/ + +int ff_req_grant ( /* 1:Got a grant to access the volume, 0:Could not get a grant */ + _SYNC_t sobj /* Sync object to wait */ +) +{ + int ret = 0; +#if (osCMSIS < 0x20000U) + +#if _USE_MUTEX + if(osMutexWait(sobj, _FS_TIMEOUT) == osOK) +#else + if(osSemaphoreWait(sobj, _FS_TIMEOUT) == osOK) +#endif + +#else + +#if _USE_MUTEX + if(osMutexAcquire(sobj, _FS_TIMEOUT) == osOK) +#else + if(osSemaphoreAcquire(sobj, _FS_TIMEOUT) == osOK) +#endif + +#endif + { + ret = 1; + } + + return ret; +} + + + +/*------------------------------------------------------------------------*/ +/* Release Grant to Access the Volume */ +/*------------------------------------------------------------------------*/ +/* This function is called on leaving file functions to unlock the volume. +*/ + +void ff_rel_grant ( + _SYNC_t sobj /* Sync object to be signaled */ +) +{ +#if _USE_MUTEX + osMutexRelease(sobj); +#else + osSemaphoreRelease(sobj); +#endif +} + +#endif + + + + +#if _USE_LFN == 3 /* LFN with a working buffer on the heap */ +/*------------------------------------------------------------------------*/ +/* Allocate a memory block */ +/*------------------------------------------------------------------------*/ +/* If a NULL is returned, the file function fails with FR_NOT_ENOUGH_CORE. +*/ + +void* ff_memalloc ( /* Returns pointer to the allocated memory block */ + UINT msize /* Number of bytes to allocate */ +) +{ + return ff_malloc(msize); /* Allocate a new memory block with POSIX API */ +} + + +/*------------------------------------------------------------------------*/ +/* Free a memory block */ +/*------------------------------------------------------------------------*/ + +void ff_memfree ( + void* mblock /* Pointer to the memory block to free */ +) +{ + ff_free(mblock); /* Discard the memory block with POSIX API */ +} + +#endif diff --git a/Core/Src/USB_DEVICE/App/usb_device.c b/Core/Src/USB/usb_device.c similarity index 50% rename from Core/Src/USB_DEVICE/App/usb_device.c rename to Core/Src/USB/usb_device.c index 7bbe2f0..93ea9f0 100644 --- a/Core/Src/USB_DEVICE/App/usb_device.c +++ b/Core/Src/USB/usb_device.c @@ -1,102 +1,63 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usb_device.c - * @version : v1.0_Cube - * @brief : This file implements the USB Device - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Includes ------------------------------------------------------------------*/ - -#include "usb_device.h" -#include "usbd_core.h" -#include "usbd_desc.h" -#include "usbd_cdc.h" -#include "usbd_cdc_if.h" - -/* USER CODE BEGIN Includes */ - -/* USER CODE END Includes */ - -/* USER CODE BEGIN PV */ -/* Private variables ---------------------------------------------------------*/ - -/* USER CODE END PV */ - -/* USER CODE BEGIN PFP */ -/* Private function prototypes -----------------------------------------------*/ - -/* USER CODE END PFP */ - -/* USB Device Core handle declaration. */ -USBD_HandleTypeDef hUsbDeviceHS; - -/* - * -- Insert your variables declaration here -- - */ -/* USER CODE BEGIN 0 */ - -/* USER CODE END 0 */ - -/* - * -- Insert your external function declaration here -- - */ -/* USER CODE BEGIN 1 */ - -/* USER CODE END 1 */ - -/** - * Init USB device Library, add supported class and start the library - * @retval None - */ -void MX_USB_DEVICE_Init(void) -{ - /* USER CODE BEGIN USB_DEVICE_Init_PreTreatment */ - - /* USER CODE END USB_DEVICE_Init_PreTreatment */ - - /* Init Device Library, add supported class and start the library. */ - if (USBD_Init(&hUsbDeviceHS, &HS_Desc, DEVICE_HS) != USBD_OK) - { - Error_Handler(); - } - if (USBD_RegisterClass(&hUsbDeviceHS, &USBD_CDC) != USBD_OK) - { - Error_Handler(); - } - if (USBD_CDC_RegisterInterface(&hUsbDeviceHS, &USBD_Interface_fops_HS) != USBD_OK) - { - Error_Handler(); - } - if (USBD_Start(&hUsbDeviceHS) != USBD_OK) - { - Error_Handler(); - } - - /* USER CODE BEGIN USB_DEVICE_Init_PostTreatment */ - - /* USER CODE END USB_DEVICE_Init_PostTreatment */ -} - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : usb_device.c + * @version : v1.0_Cube + * @brief : This file implements the USB Device + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2021 STMicroelectronics. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Includes ------------------------------------------------------------------*/ +#include "main.h" // for Error_Handler +#include "usb_device.h" +#include "usbd_core.h" +#include "usbd_desc.h" +#include "usbd_RTT_class.h" + +/* USB Device Core handle declaration. */ +USBD_HandleTypeDef hUsbDeviceHS; + +/** + * Init USB device Library, add supported class + * @retval None + */ +void MX_USB_DEVICE_Init(void) +{ + + /* Init Device Library, add supported class. */ + // WARNING: This function also indirectly sets the RX and TX fifos (the actual call is USBD_LL_Init in usbd_conf.c). The default values generated by CUBEMX are not alway right. + // The stm32F7 has 4kB of shared RAM for these fifos, so the sum of all the Fifo buffers cannot exceed this value. The sizes passed are in words (32 bits), so multiply with 4 for the actual size + if (USBD_Init(&hUsbDeviceHS, &HS_Desc, DEVICE_HS) != USBD_OK) + { + Error_Handler(); + } + if (USBD_RegisterClass(&hUsbDeviceHS, &USBD_RTT_ClassDriver) != USBD_OK) + { + Error_Handler(); + } +} + +void USB_Start_Class(USBD_RTT_Callbacks* callbacks){ + if(callbacks == NULL){ + Error_Handler(); + } + // assign user callback functions + hUsbDeviceHS.pUserData = callbacks; + // start USB device + if (USBD_Start(&hUsbDeviceHS) != USBD_OK) + { + Error_Handler(); + } +} diff --git a/Core/Src/USB/usbd_RTT_class.c b/Core/Src/USB/usbd_RTT_class.c new file mode 100644 index 0000000..50219ff --- /dev/null +++ b/Core/Src/USB/usbd_RTT_class.c @@ -0,0 +1,596 @@ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_RTT_class.h" +#include "usbd_ctlreq.h" + +// standard functions used by RTT class needed to implement for USBD library +static uint8_t USBD_RTT_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_RTT_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx); +static uint8_t USBD_RTT_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req); +static uint8_t USBD_RTT_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_RTT_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_RTT_EP0_RxReady(USBD_HandleTypeDef *pdev); +static uint8_t USBD_RTT_EP0_TxReady(USBD_HandleTypeDef *pdev); +static uint8_t USBD_RTT_SOF(USBD_HandleTypeDef *pdev); +static uint8_t USBD_RTT_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); +static uint8_t USBD_RTT_IsoOutIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum); + +static uint8_t *USBD_RTT_GetCfgDesc(uint16_t *length); +uint8_t *USBD_RTT_GetDeviceQualifierDesc(uint16_t *length); + + +// Helper functions +static void USBD_RTT_OpenInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface); +static void USBD_RTT_CloseInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface); +static void USBD_RTT_PrepareReceiveInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface); + +// USBD functions for device driver +USBD_ClassTypeDef USBD_RTT_ClassDriver = +{ + USBD_RTT_Init, + USBD_RTT_DeInit, + USBD_RTT_Setup, + USBD_RTT_EP0_TxReady, //not used currently + USBD_RTT_EP0_RxReady, + USBD_RTT_DataIn, + USBD_RTT_DataOut, + USBD_RTT_SOF, //not used currently + USBD_RTT_IsoINIncomplete, //not used currently + USBD_RTT_IsoOutIncomplete,//not used currently + USBD_RTT_GetCfgDesc, + USBD_RTT_GetCfgDesc, + USBD_RTT_GetCfgDesc, + USBD_RTT_GetDeviceQualifierDesc, +}; + +// RX buffers +static uint8_t HighPriorityRxBuffer[USB_HIGH_PRIO_RX_BUF_SIZE] __attribute__ ((aligned (4))); +static uint8_t LowPriorityRxBuffer[USB_LOW_PRIO_RX_BUF_SIZE] __attribute__ ((aligned (4))); + +// Interface configs +static InterfaceConfig int0 = {.InAddress = RTT_HIGH_PRIO_IN_EP, .OutAddress = RTT_HIGH_PRIO_OUT_EP, .MaxPacketSize = USB_RTT_EP1_MAX_PACKET_SIZE, .Type = USB_RTT_EP1_TYPE, .Rxbuffer = HighPriorityRxBuffer}; +static InterfaceConfig int0ALT = {.InAddress = RTT_HIGH_PRIO_IN_EP, .OutAddress = RTT_HIGH_PRIO_OUT_EP, .MaxPacketSize = USB_RTT_EP1_ALT_MAX_PACKET_SIZE, .Type = USB_RTT_EP1_ALT_TYPE, .Rxbuffer = HighPriorityRxBuffer}; +static InterfaceConfig int1 = {.InAddress = RTT_LOW_PRIO_IN_EP, .OutAddress = RTT_LOW_PRIO_OUT_EP, .MaxPacketSize = USB_RTT_EP2_MAX_PACKET_SIZE, .Type = USB_RTT_EP2_TYPE, .Rxbuffer = LowPriorityRxBuffer}; + +// RTT USB class state data +USBD_RTT_HandleTypeDef rtt_handle = {0}; + +/* USB RTT device Configuration Descriptor */ +// Contents are described in https://www.keil.com/pack/doc/mw/USB/html/_u_s_b__configuration__descriptor.html +// A layout example is described in https://learn.microsoft.com/en-us/windows-hardware/drivers/usbcon/usb-device-layout +#define USB_RTT_CONFIG_DESC_SIZ 78U // config size in bytes, needs manual update if config is changed +__ALIGN_BEGIN static uint8_t USBD_RTT_CfgDesc[] __ALIGN_END = +{ + 0x09, /* bLength: Configuation Descriptor size */ + USB_DESC_TYPE_CONFIGURATION, /* bDescriptorType: Configuration */ + LOBYTE(USB_RTT_CONFIG_DESC_SIZ),/* wTotalLength: Bytes returned */ + HIBYTE(USB_RTT_CONFIG_DESC_SIZ), + 0x02, /*bNumInterfaces: 2 interface*/ + 0x01, /*bConfigurationValue: Configuration value*/ + 0x02, /*iConfiguration: Index of human readable string descriptor describing the configuration*/ + 0b11000000, /*bmAttributes: bus powered and Supports Remote Wakeup */ + 0x32, /*MaxPower 100 mA: this current is used for detecting Vbus*/ + /* 09 */ + + /********** Descriptor of RTT interface 0 Alternate setting 0 (HIGH PRIO channel BULK)**************/ + 0x09, // blength + USB_DESC_TYPE_INTERFACE, //bDescriptorType: Interface + USB_RTT_HIGH_PRIO_INTERFACE, //bInterfaceNumber + 0x00, //bAlternateSetting + 0x02, //bNumEndpoints + 0xFF, //bInterfaceClass - Custom class + 0x00, //bInterfaceSubClass + 0x00, //bInterfaceProtocol + 0x00, //Index to a human readable string describing this interface (0x00 - not available) + + /********************* Endpoints for interface 0, ALT 0*/ + // Endpoint HIGH PRIO IN + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_HIGH_PRIO_IN_EP, //bEndpointAddress + USB_RTT_EP1_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP1_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP1_MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + + // Endpoint HIGH PRIO OUT + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_HIGH_PRIO_OUT_EP, //bEndpointAddress + USB_RTT_EP1_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP1_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP1_MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + + /********** Descriptor of RTT interface 0 Alternate setting 1 (HIGH PRIO channel Interupt)**************/ + 0x09, // blength + USB_DESC_TYPE_INTERFACE, //bDescriptorType: Interface + USB_RTT_HIGH_PRIO_INTERFACE, //bInterfaceNumber + 0x01, //bAlternateSetting + 0x02, //bNumEndpoints + 0xFF, //bInterfaceClass - Custom class + 0x00, //bInterfaceSubClass + 0x00, //bInterfaceProtocol + 0x00, //Index to a human readable string describing this interface (0x00 - not available) + + /********************* Endpoints for interface 0, ALT 1*/ + // Endpoint HIGH PRIO IN + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_HIGH_PRIO_IN_EP, //bEndpointAddress + USB_RTT_EP1_ALT_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP1_ALT_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP1_ALT_MAX_PACKET_SIZE), // wMaxPacketSize + USB_RTT_EP1_ALT_BINTERVAL, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + + // Endpoint HIGH PRIO OUT + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_HIGH_PRIO_OUT_EP, //bEndpointAddress + USB_RTT_EP1_ALT_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP1_ALT_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP1_ALT_MAX_PACKET_SIZE), // wMaxPacketSize + USB_RTT_EP1_ALT_BINTERVAL, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + + /********** Descriptor of RTT interface 1 Alternate setting 0 (LOW PRIO channel)**************/ + 0x09, // blength + USB_DESC_TYPE_INTERFACE, //bDescriptorType: Interface + USB_RTT_LOW_PRIO_INTERFACE, //bInterfaceNumber + 0x00, //bAlternateSetting + 0x02, //bNumEndpoints + 0xFF, //bInterfaceClass - Custom class + 0x00, //bInterfaceSubClass + 0x00, //bInterfaceProtocol + 0x00, //Index to a human readable string describing this interface (0x00 - not available) + + /********************* Endpoints for interface 1, ALT 0*/ + // Endpoint LOW PRIO IN + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_LOW_PRIO_IN_EP, //bEndpointAddress + USB_RTT_EP2_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP2_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP2_MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + + // Endpoint LOW PRIO OUT + 0x07, //bLength + USB_DESC_TYPE_ENDPOINT, //bDescriptorType: Endpoint + RTT_LOW_PRIO_OUT_EP, //bEndpointAddress + USB_RTT_EP2_TYPE, //bmAttributes, (ISO has more settings) + LOBYTE(USB_RTT_EP2_MAX_PACKET_SIZE), // wMaxPacketSize + HIBYTE(USB_RTT_EP2_MAX_PACKET_SIZE), // wMaxPacketSize + 0x00, //bInterval interval for polling (frame counts). Ignored for Bulk and Control + }; + + +/** + * @brief USBD_RTT_GetCfgDesc + * return configuration descriptor + * @param length : pointer data length + * @retval pointer to descriptor buffer + */ +static uint8_t *USBD_RTT_GetCfgDesc(uint16_t *length) +{ + *length = (uint16_t)sizeof(USBD_RTT_CfgDesc); + return USBD_RTT_CfgDesc; +} + +/** + * @brief USBD_RTT_Init + * Initialize the RTT interface + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_RTT_Init(USBD_HandleTypeDef *pdev, uint8_t cfgidx) +{ + UNUSED(cfgidx); + + // alloc RTTclass specific data struct + USBD_RTT_HandleTypeDef *hcdc; + hcdc = &rtt_handle; + + if (hcdc == NULL){ + pdev->pClassData = NULL; + return (uint8_t)USBD_EMEM; + } + + // link RTT class to device + pdev->pClassData = (void *)hcdc; + + if (pdev->dev_speed != USBD_SPEED_HIGH){ + // Only HS allowed + return (uint8_t)USBD_EMEM; + } + + // Link active interfaces + InterfaceConfig* i0 = &int0; + InterfaceConfig* i1 = &int1; + hcdc->INT0active = &int0; + hcdc->INT1active = &int1; + + /* Open all interfaces IN */ + USBD_RTT_OpenInterface(pdev, i0); + USBD_RTT_OpenInterface(pdev, i1); + + /* Init physical Interface components */ + // TODO: Init user? + // ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->Init(); + + /* Init Xfer states */ + // TODO: Make enum for readability + hcdc->INT0TxState = 0U; + hcdc->INT1TxState = 0U; + + /* Prepare Out endpoints to receive next packet */ + USBD_RTT_PrepareReceiveInterface(pdev,i0); + USBD_RTT_PrepareReceiveInterface(pdev,i1); + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_RTT_Init + * DeInitialize the RTT layer + * @param pdev: device instance + * @param cfgidx: Configuration index + * @retval status + */ +static uint8_t USBD_RTT_DeInit(USBD_HandleTypeDef *pdev, uint8_t cfgidx){ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)pdev->pClassData; + + // Close all endpoints + USBD_RTT_CloseInterface(pdev, hcdc->INT0active); + USBD_RTT_CloseInterface(pdev, hcdc->INT1active); + + // remove RTT class + if (pdev->pClassData != NULL) + { + // TODO: DeInit user? + // ((USBD_CDC_ItfTypeDef *)pdev->pUserData)->DeInit(); + (void)USBD_free(pdev->pClassData); + pdev->pClassData = NULL; + } + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_RTT_Setup + * Handle the RTT specific setup requests + * @param pdev: instance + * @param req: usb requests + * @retval status + */ +static uint8_t USBD_RTT_Setup(USBD_HandleTypeDef *pdev, USBD_SetupReqTypedef *req){ + // TODO: figure out setup + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)pdev->pClassData; + USBD_RTT_Callbacks * callbacks = ((USBD_RTT_Callbacks *)pdev->pUserData); + USBD_StatusTypeDef ret = USBD_OK; + + switch (req->bmRequest & USB_REQ_TYPE_MASK) + { + // RTT Class specific requests + case USB_REQ_TYPE_CLASS: + // If meant to receive data, prepare to receive + if ((req->bmRequest & 0x80U) != 0U){ + hcdc->CmdOpCode = req->bRequest; + hcdc->CmdLength = (uint8_t)req->wLength; + (void)USBD_CtlPrepareRx(pdev, (uint8_t *)hcdc->setup_data, req->wLength); + }else if(req->wLength != 0U){ + // Have to send data + if(callbacks->usbControl){callbacks->usbControl(req->bRequest, (uint8_t *)hcdc->setup_data, req->wLength);} + (void)USBD_CtlSendData(pdev, (uint8_t *)hcdc->setup_data, req->wLength); + + }else{ + // just process request data + if(callbacks->usbControl){callbacks->usbControl(req->bRequest, (uint8_t *)hcdc->setup_data, 0);} + } + break; + + // Standard requests. Should already be implemented by the library + case USB_REQ_TYPE_STANDARD: + switch (req->bRequest){ + case USB_REQ_GET_STATUS: + if (pdev->dev_state == USBD_STATE_CONFIGURED){ + uint16_t status_info = 0U; + (void)USBD_CtlSendData(pdev, (uint8_t *)&status_info, 2U); + }else{ + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + case USB_REQ_GET_INTERFACE: + if (pdev->dev_state == USBD_STATE_CONFIGURED){ + // device has to be configured + if(req->wIndex == USB_RTT_HIGH_PRIO_INTERFACE){ + // send active alt interface on interface 0 + (void)USBD_CtlSendData(pdev, &hcdc->INT0AltSetting, 1U); + }else if(req->wIndex == USB_RTT_LOW_PRIO_INTERFACE){ + // send active alt interface on interface 1 + (void)USBD_CtlSendData(pdev, &hcdc->INT1AltSetting, 1U); + }else{ + // interface not known + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + }else{ + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + break; + case USB_REQ_SET_INTERFACE: + if (pdev->dev_state != USBD_STATE_CONFIGURED){ + // device has to be configured + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + }else{ + // Switch alternate interface (only interface 0 is implemented) + if(req->wIndex == USB_RTT_HIGH_PRIO_INTERFACE){ + hcdc->INT0AltSetting = req->wValue; + if(req->wValue == 0){ + USBD_RTT_CloseInterface(pdev,&int0ALT); + USBD_RTT_OpenInterface(pdev,&int0); + hcdc->INT0active = &int0; + hcdc->INT0TxState = 0U; + }else if(req->wValue == 1){ + USBD_RTT_CloseInterface(pdev,&int0); + USBD_RTT_OpenInterface(pdev,&int0ALT); + hcdc->INT0active = &int0ALT; + hcdc->INT0TxState = 0U; + } + }else{ + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + } + } + break; + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + break; + // Unknown request + default: + USBD_CtlError(pdev, req); + ret = USBD_FAIL; + break; + } + + return (uint8_t)ret; +} + +/** + * @brief USBD_RTT_DataIn + * handle data IN Stage (host <-- device), This is called when a message has been transmitted to the host + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_RTT_DataIn(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)pdev->pClassData; + USBD_RTT_Callbacks * callbacks = ((USBD_RTT_Callbacks *)pdev->pUserData); + PCD_HandleTypeDef *hpcd = pdev->pData; + + if (hcdc == NULL){ + return (uint8_t)USBD_FAIL; + } + + // When the complete message length fits perfectly in n packets, send a zero length packet (ZLP) to let the host know there is no more data + if ((pdev->ep_in[epnum].total_length > 0U) && ((pdev->ep_in[epnum].total_length % hpcd->IN_ep[epnum].maxpacket) == 0U)) { + /* Update the packet total length */ + pdev->ep_in[epnum].total_length = 0U; + /* Send ZLP */ + (void)USBD_LL_Transmit(pdev, epnum, NULL, 0U); + } + else + { + // TX done + // Remove the 0x80 (IN) from the endpoint to just get the index (ep & 0x7F) + if(epnum == (hcdc->INT0active->InAddress &0x7F)){ + hcdc->INT0TxState = 0U; + if(callbacks->highprioTXcplt){ + callbacks->highprioTXcplt(); + } + }else if(epnum == (hcdc->INT1active->InAddress &0x7F)){ + hcdc->INT1TxState = 0U; + if(callbacks->lowprioTXcplt){ + callbacks->lowprioTXcplt(); + } + } + } + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_RTT_DataOut + * handle data OUT Stage (host --> device), At this point the data is already in the buffer + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_RTT_DataOut(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)pdev->pClassData; + USBD_RTT_Callbacks * callbacks = ((USBD_RTT_Callbacks *)pdev->pUserData); + + if (pdev->pClassData == NULL){ + return (uint8_t)USBD_FAIL; + } + + /* Get the received data length */ + uint32_t received_length = USBD_LL_GetRxDataSize(pdev, epnum); + + /* USB data will be immediately processed, this allow next USB traffic being + NAKed till the end of the application Xfer */ + if(epnum == hcdc->INT0active->OutAddress){ + // Let the user process data in the buffer + if(callbacks->highprioRXcplt){ + callbacks->highprioRXcplt(hcdc->INT0active->Rxbuffer, received_length); + } + // Listen for new packets + USBD_RTT_PrepareReceiveInterface(pdev, hcdc->INT0active); + }else if( epnum == hcdc->INT1active->OutAddress){ + // Let the user process data in the buffer + if(callbacks->lowprioRXcplt){ + callbacks->lowprioRXcplt(hcdc->INT1active->Rxbuffer, received_length); + } + // Listen for new packets + USBD_RTT_PrepareReceiveInterface(pdev, hcdc->INT1active); + } + + return (uint8_t)USBD_OK; +} + +/** + * @brief USBD_RTT_EP0_RxReady + * handle EP0 Rx Ready event. This is called when data that was requested to be received on Ep0 is complete. + * Now process request. (USBD_RTT_Setup was called before this) + * @param pdev: device instance + * @retval status + */ +static uint8_t USBD_RTT_EP0_RxReady(USBD_HandleTypeDef *pdev) +{ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)pdev->pClassData; + if ((pdev->pUserData != NULL) && (hcdc->CmdOpCode != 0xFFU)){ + ((USBD_RTT_Callbacks *)pdev->pUserData)->usbControl(hcdc->CmdOpCode, (uint8_t *)hcdc->setup_data, (uint16_t)hcdc->CmdLength); + hcdc->CmdOpCode = 0xFFU; + } + return (uint8_t)USBD_OK; +} +/** + * @brief USBD_RTT_EP0_TxReady + * handle EP0 TRx Ready event + * @param pdev: device instance + * @retval status + */ +static uint8_t USBD_RTT_EP0_TxReady(USBD_HandleTypeDef *pdev) +{ + // Not needed + return (uint8_t)USBD_OK; +} +/** + * @brief USBD_RTT_SOF + * handle SOF event + * @param pdev: device instance + * @retval status + */ +static uint8_t USBD_RTT_SOF(USBD_HandleTypeDef *pdev) +{ + //not needed + return (uint8_t)USBD_OK; +} +/** + * @brief USBD_RTT_IsoINIncomplete + * handle data ISO IN Incomplete event + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_RTT_IsoINIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + // not used + return (uint8_t)USBD_OK; +} +/** + * @brief USBD_RTT_IsoOutIncomplete + * handle data ISO OUT Incomplete event + * @param pdev: device instance + * @param epnum: endpoint index + * @retval status + */ +static uint8_t USBD_RTT_IsoOutIncomplete(USBD_HandleTypeDef *pdev, uint8_t epnum) +{ + // Not used + return (uint8_t)USBD_OK; +} + +/** + * @brief USB_TransmitLowPriority + * Transmits data on RTT_LOW_PRIO_IN_EP + * @param buf: buffer with data to send + * @param len: length of the data + * @retval USB status + */ +USBD_StatusTypeDef USB_TransmitLowPriority(uint8_t* buf, uint32_t len){ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)hUsbDeviceHS.pClassData; + + // Device needs to be configured before data can be sent + if (hcdc == NULL || hUsbDeviceHS.dev_state != USBD_STATE_CONFIGURED){ + return (uint8_t)USBD_FAIL; + } + // can't send when still busy sending + if(hcdc->INT1TxState == 1){ + return USBD_BUSY; + } + /* Tx Transfer in progress */ + hcdc->INT1TxState = 1U; + /* Update the packet total length */ + hUsbDeviceHS.ep_in[hcdc->INT1active->InAddress & 0xFU].total_length = len; + /* Transmit next packet */ + (void)USBD_LL_Transmit(&hUsbDeviceHS, hcdc->INT1active->InAddress, buf, len); + + return USBD_OK; +} + +/** + * @brief USB_TransmitHighPriority + * Transmits data on RTT_HIGH_PRIO_IN_EP + * @param buf: buffer with data to send + * @param len: length of the data + * @retval USB status + */ +USBD_StatusTypeDef USB_TransmitHighPriority(uint8_t* buf, uint32_t len){ + USBD_RTT_HandleTypeDef *hcdc = (USBD_RTT_HandleTypeDef *)hUsbDeviceHS.pClassData; + + // Device needs to be configured before data can be sent + if (hcdc == NULL || hUsbDeviceHS.dev_state != USBD_STATE_CONFIGURED){ + return (uint8_t)USBD_FAIL; + } + // can't send new packets when already sending + // if(hcdc->INT0TxState == 1){ + // return USBD_BUSY; + // } + /* Tx Transfer in progress */ + hcdc->INT0TxState = 1U; + /* Update the packet total length */ + hUsbDeviceHS.ep_in[RTT_HIGH_PRIO_IN_EP & 0xFU].total_length = len; + /* Transmit next packet */ + return USBD_LL_Transmit(&hUsbDeviceHS, RTT_HIGH_PRIO_IN_EP, buf, len); +} + + + +// Helper functions +static void USBD_RTT_OpenInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface){ + // Open IN Endpoint if defined + if(interface->InAddress){ + (void)USBD_LL_OpenEP(pdev, interface->InAddress, interface->Type, interface->MaxPacketSize); + pdev->ep_in[interface->InAddress & 0xFU].is_used = 1U; + } + // Open OUT Endpoint if defined + if(interface->OutAddress){ + (void)USBD_LL_OpenEP(pdev, interface->OutAddress, interface->Type, interface->MaxPacketSize); + pdev->ep_in[interface->OutAddress & 0xFU].is_used = 1U; + (void)USBD_LL_PrepareReceive(pdev, interface->OutAddress, interface->Rxbuffer, interface->MaxPacketSize); + } +} + +static void USBD_RTT_CloseInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface){ + // Close IN Endpoint if defined + if(interface->InAddress){ + (void)USBD_LL_CloseEP(pdev, interface->InAddress); + pdev->ep_in[interface->InAddress & 0xFU].is_used = 0U; + } + // Close OUT Endpoint if defined + if(interface->OutAddress){ + (void)USBD_LL_CloseEP(pdev, interface->OutAddress); + pdev->ep_in[interface->OutAddress & 0xFU].is_used = 0U; + } +} + +static void USBD_RTT_PrepareReceiveInterface(USBD_HandleTypeDef *pdev, InterfaceConfig* interface){ + (void)USBD_LL_PrepareReceive(pdev, interface->OutAddress, interface->Rxbuffer, interface->MaxPacketSize); +} \ No newline at end of file diff --git a/Core/Src/USB/usbd_desc.c b/Core/Src/USB/usbd_desc.c new file mode 100644 index 0000000..7fc25f1 --- /dev/null +++ b/Core/Src/USB/usbd_desc.c @@ -0,0 +1,276 @@ +/* + This file contains the device descriptor for the usb device and device qualifier +*/ + +/* Includes ------------------------------------------------------------------*/ +#include "usbd_core.h" +#include "usbd_desc.h" +#include "usbd_conf.h" + + +#define USBD_VID 1155 +#define USBD_LANGID_STRING 1033 +#define USBD_MANUFACTURER_STRING "RTT" +#define USBD_PID_HS 25432 +#define USBD_PRODUCT_STRING_HS "BaseStation" +#define USBD_CONFIGURATION_STRING_HS "RTT default" +#define USBD_INTERFACE_STRING_HS "default" + +#define USB_SIZ_BOS_DESC 0x0C + +static void Get_SerialNum(void); +static void IntToUnicode(uint32_t value, uint8_t * pbuf, uint8_t len); + +uint8_t * USBD_HS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); +uint8_t * USBD_HS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); + + +USBD_DescriptorsTypeDef HS_Desc = +{ + USBD_HS_DeviceDescriptor +, USBD_HS_LangIDStrDescriptor +, USBD_HS_ManufacturerStrDescriptor +, USBD_HS_ProductStrDescriptor +, USBD_HS_SerialStrDescriptor +, USBD_HS_ConfigStrDescriptor +, USBD_HS_InterfaceStrDescriptor +}; + + +// ---------------------------------------- DESCRIPTORS +/** USB Device Qualifier Descriptor + * Describes how the device should be set up for every speed (FS or HS). If not acknowledged it will go to FS. Should be the same as USBD_HS_DeviceDesc + */ +// Device Qualifier Descriptors contents are described in https://www.keil.com/pack/doc/mw/USB/html/_u_s_b__device__qualifier__descriptor.html +__ALIGN_BEGIN static uint8_t USBD_RTT_DeviceQualifierDesc[USB_LEN_DEV_QUALIFIER_DESC] __ALIGN_END = +{ + USB_LEN_DEV_QUALIFIER_DESC, //bLength + USB_DESC_TYPE_DEVICE_QUALIFIER, //bdescriptorType + 0x00, //bcsUSB (LSB) + 0x02, //bcsUSB (MSB) + 0x00, //bDeviceClass (0x00 - derive from interface) + 0x00, //bDeviceSubClass + 0x00, //bDeviceProtocol + USB_MAX_EP0_SIZE, //bMaxPacketSize EP0 + 0x01, //bNumConfigurations + 0x00, //RESERVED +}; + +/** USB device descriptor. */ +// Device Descriptor contents are described in https://www.keil.com/pack/doc/mw/USB/html/_u_s_b__device__descriptor.html +// Or https://www.beyondlogic.org/usbnutshell/usb5.shtml#ConfigurationDescriptors +__ALIGN_BEGIN uint8_t USBD_HS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = +{ + 0x12, //bLength + USB_DESC_TYPE_DEVICE, //bDescriptorType + 0x00, //bcdUSB (LSB) + 0x02, //bcdUSB (MSB) + 0x00, //bDeviceClass (0x00 - derive from interface) + 0x00, //bDeviceSubClass + 0x00, //bDeviceProtocol + USB_MAX_EP0_SIZE, //bMaxPacketSize EP0 + LOBYTE(USBD_VID), //idVendor + HIBYTE(USBD_VID), //idVendor + LOBYTE(USBD_PID_HS), //idProduct + HIBYTE(USBD_PID_HS), //idProduct + 0x00, //device release number (LSB) TODO: match with REM version? + 0x02, //device release number (MSB) + 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 +}; + +#if defined ( __ICCARM__ ) /* IAR Compiler */ + #pragma data_alignment=4 +#endif /* defined ( __ICCARM__ ) */ + +/** USB lang indentifier 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) +}; + +/* Internal string descriptor. */ +__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; + +__ALIGN_BEGIN uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END = { + USB_SIZ_STRING_SERIAL, + USB_DESC_TYPE_STRING, +}; + +/** + * @brief Return the device descriptor + * @param speed : Current device speed + * @param length : Pointer to data length variable + * @retval Pointer to descriptor buffer + */ +uint8_t * USBD_HS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_HS_DeviceDesc); + return USBD_HS_DeviceDesc; +} + +/** + * @brief 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_HS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = sizeof(USBD_LangIDDesc); + return USBD_LangIDDesc; +} + +/** + * @brief 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_HS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + USBD_GetString((uint8_t *)USBD_PRODUCT_STRING_HS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** + * @brief 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_HS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** + * @brief 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_HS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + UNUSED(speed); + *length = USB_SIZ_STRING_SERIAL; + + /* Update the serial number string descriptor with the data from the unique + * ID */ + Get_SerialNum(); + + return (uint8_t *) USBD_StringSerial; +} + +/** + * @brief 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_HS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_HS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** + * @brief 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_HS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) +{ + USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_HS, USBD_StrDesc, length); + return USBD_StrDesc; +} + +/** + * @brief Create the serial number string descriptor + * @param None + * @retval None + */ +static void Get_SerialNum(void) +{ + uint32_t deviceserial0, deviceserial1, deviceserial2; + + deviceserial0 = *(uint32_t *) DEVICE_ID1; + deviceserial1 = *(uint32_t *) DEVICE_ID2; + deviceserial2 = *(uint32_t *) DEVICE_ID3; + + deviceserial0 += deviceserial2; + + if (deviceserial0 != 0) + { + IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8); + IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4); + } +} + +/** +* @brief DeviceQualifierDescriptor +* return Device Qualifier descriptor +* @param length : pointer data length +* @retval pointer to descriptor buffer +*/ +uint8_t *USBD_RTT_GetDeviceQualifierDesc(uint16_t *length) +{ + *length = (uint16_t)sizeof(USBD_RTT_DeviceQualifierDesc); + + return USBD_RTT_DeviceQualifierDesc; +} + +/** +* @brief DeviceQualifierDescriptor +* return Device Qualifier descriptor +* @param length : pointer data length +* @retval pointer to descriptor buffer +*/ +uint8_t *USBD_RTT_DeviceQualifierDescriptor(uint16_t *length) +{ + *length = (uint16_t)sizeof(USBD_RTT_DeviceQualifierDesc); + return USBD_RTT_DeviceQualifierDesc; +} + +/** + * @brief Convert Hex 32Bits value into char + * @param value: value to convert + * @param pbuf: pointer to the buffer + * @param len: buffer length + * @retval None + */ +static void IntToUnicode(uint32_t value, uint8_t * pbuf, uint8_t len) +{ + uint8_t idx = 0; + + for (idx = 0; idx < len; idx++) + { + if (((value >> 28)) < 0xA) + { + pbuf[2 * idx] = (value >> 28) + '0'; + } + else + { + pbuf[2 * idx] = (value >> 28) + 'A' - 10; + } + + value = value << 4; + + pbuf[2 * idx + 1] = 0; + } +} diff --git a/Core/Src/USB_DEVICE/App/usbd_cdc_if.c b/Core/Src/USB_DEVICE/App/usbd_cdc_if.c deleted file mode 100644 index ce7f214..0000000 --- a/Core/Src/USB_DEVICE/App/usbd_cdc_if.c +++ /dev/null @@ -1,343 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : usbd_cdc_if.c - * @version : v1.0_Cube - * @brief : Usb device for Virtual Com Port. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_cdc_if.h" - -/* USER CODE BEGIN INCLUDE */ -#include "basestation.h" -#include "packet_buffers.h" -#include "gpio_util.h" -/* USER CODE END INCLUDE */ - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private macro -------------------------------------------------------------*/ - -/* USER CODE BEGIN PV */ -/* Private variables ---------------------------------------------------------*/ -uint8_t tempbuf[7]; -/* USER CODE END PV */ - -/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY - * @brief Usb device library. - * @{ - */ - -/** @addtogroup USBD_CDC_IF - * @{ - */ - -/** @defgroup USBD_CDC_IF_Private_TypesDefinitions USBD_CDC_IF_Private_TypesDefinitions - * @brief Private types. - * @{ - */ - -/* USER CODE BEGIN PRIVATE_TYPES */ - -/* USER CODE END PRIVATE_TYPES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Private_Defines USBD_CDC_IF_Private_Defines - * @brief Private defines. - * @{ - */ - -/* USER CODE BEGIN PRIVATE_DEFINES */ -/* USER CODE END PRIVATE_DEFINES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Private_Macros USBD_CDC_IF_Private_Macros - * @brief Private macros. - * @{ - */ - -/* USER CODE BEGIN PRIVATE_MACRO */ - -/* USER CODE END PRIVATE_MACRO */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Private_Variables USBD_CDC_IF_Private_Variables - * @brief Private variables. - * @{ - */ - -/* Create buffer for reception and transmission */ -/* It's up to user to redefine and/or remove those define */ -/** Received data over USB are stored in this buffer */ -uint8_t UserRxBufferHS[APP_RX_DATA_SIZE]; - -/** Data to send over USB CDC are stored in this buffer */ -uint8_t UserTxBufferHS[APP_TX_DATA_SIZE]; - -/* USER CODE BEGIN PRIVATE_VARIABLES */ - -/* USER CODE END PRIVATE_VARIABLES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Exported_Variables USBD_CDC_IF_Exported_Variables - * @brief Public variables. - * @{ - */ - -extern USBD_HandleTypeDef hUsbDeviceHS; - -/* USER CODE BEGIN EXPORTED_VARIABLES */ - -/* USER CODE END EXPORTED_VARIABLES */ - -/** - * @} - */ - -/** @defgroup USBD_CDC_IF_Private_FunctionPrototypes USBD_CDC_IF_Private_FunctionPrototypes - * @brief Private functions declaration. - * @{ - */ - -static int8_t CDC_Init_HS(void); -static int8_t CDC_DeInit_HS(void); -static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length); -static int8_t CDC_Receive_HS(uint8_t* pbuf, uint32_t *Len); -static int8_t CDC_TransmitCplt_HS(uint8_t *pbuf, uint32_t *Len, uint8_t epnum); - -/* USER CODE BEGIN PRIVATE_FUNCTIONS_DECLARATION */ - -/* USER CODE END PRIVATE_FUNCTIONS_DECLARATION */ - -/** - * @} - */ - -USBD_CDC_ItfTypeDef USBD_Interface_fops_HS = -{ - CDC_Init_HS, - CDC_DeInit_HS, - CDC_Control_HS, - CDC_Receive_HS, - CDC_TransmitCplt_HS -}; - -/* Private functions ---------------------------------------------------------*/ - -/** - * @brief Initializes the CDC media low layer over the USB HS IP - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ -static int8_t CDC_Init_HS(void) -{ - /* USER CODE BEGIN 8 */ - /* Set Application Buffers */ - USBD_CDC_SetTxBuffer(&hUsbDeviceHS, UserTxBufferHS, 0); - USBD_CDC_SetRxBuffer(&hUsbDeviceHS, UserRxBufferHS); - return (USBD_OK); - /* USER CODE END 8 */ -} - -/** - * @brief DeInitializes the CDC media low layer - * @param None - * @retval USBD_OK if all operations are OK else USBD_FAIL - */ -static int8_t CDC_DeInit_HS(void) -{ - /* USER CODE BEGIN 9 */ - return (USBD_OK); - /* USER CODE END 9 */ -} - -/** - * @brief Manage the CDC class requests - * @param cmd: Command code - * @param pbuf: Buffer containing command data (request parameters) - * @param length: Number of data to be sent (in bytes) - * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL - */ -static int8_t CDC_Control_HS(uint8_t cmd, uint8_t* pbuf, uint16_t length) -{ - /* USER CODE BEGIN 10 */ - switch(cmd) - { - case CDC_SEND_ENCAPSULATED_COMMAND: - - break; - - case CDC_GET_ENCAPSULATED_RESPONSE: - - break; - - case CDC_SET_COMM_FEATURE: - - break; - - case CDC_GET_COMM_FEATURE: - - break; - - case CDC_CLEAR_COMM_FEATURE: - - break; - - /*******************************************************************************/ - /* Line Coding Structure */ - /*-----------------------------------------------------------------------------*/ - /* Offset | Field | Size | Value | Description */ - /* 0 | dwDTERate | 4 | Number |Data terminal rate, in bits per second*/ - /* 4 | bCharFormat | 1 | Number | Stop bits */ - /* 0 - 1 Stop bit */ - /* 1 - 1.5 Stop bits */ - /* 2 - 2 Stop bits */ - /* 5 | bParityType | 1 | Number | Parity */ - /* 0 - None */ - /* 1 - Odd */ - /* 2 - Even */ - /* 3 - Mark */ - /* 4 - Space */ - /* 6 | bDataBits | 1 | Number Data bits (5, 6, 7, 8 or 16). */ - /*******************************************************************************/ - case CDC_SET_LINE_CODING: - /* Needed for Windows to detect device */ - for (int i=0; i<7; i++) { - tempbuf[i] = pbuf[i]; - } - break; - - case CDC_GET_LINE_CODING: - /* Needed for Windows to detect device */ - for (int i=0; i<7; i++) { - pbuf[i] = tempbuf[i]; - } - break; - - case CDC_SET_CONTROL_LINE_STATE: - - break; - - case CDC_SEND_BREAK: - - break; - - default: - break; - } - - return (USBD_OK); - /* USER CODE END 10 */ -} - -/** - * @brief Data received over USB OUT endpoint are sent over CDC interface - * through this function. - * - * @note - * This function will issue a NAK packet on any OUT packet received on - * USB endpoint until exiting this function. If you exit this function - * before transfer is complete on CDC interface (ie. using DMA controller) - * it will result in receiving more data while previous ones are still - * not sent. - * - * @param Buf: Buffer of data to be received - * @param Len: Number of data received (in bytes) - * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL - */ -static int8_t CDC_Receive_HS(uint8_t* Buf, uint32_t *Len) -{ - /* USER CODE BEGIN 11 */ - USBD_CDC_SetRxBuffer(&hUsbDeviceHS, &Buf[0]); - USBD_CDC_ReceivePacket(&hUsbDeviceHS); - - bool succes = handlePacket(Buf, *Len); - if(succes) - return USBD_OK; - - return USBD_FAIL; - /* USER CODE END 11 */ -} - -/** - * @brief Data to send over USB IN endpoint are sent over CDC interface - * through this function. - * @param Buf: Buffer of data to be sent - * @param Len: Number of data to be sent (in bytes) - * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL or USBD_BUSY - */ -uint8_t CDC_Transmit_HS(uint8_t* Buf, uint16_t Len) -{ - uint8_t result = USBD_OK; - /* USER CODE BEGIN 12 */ - USBD_CDC_HandleTypeDef *hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceHS.pClassData; - if (hcdc->TxState != 0){ - return USBD_BUSY; - } - USBD_CDC_SetTxBuffer(&hUsbDeviceHS, Buf, Len); - result = USBD_CDC_TransmitPacket(&hUsbDeviceHS); - /* USER CODE END 12 */ - return result; -} - -/** - * @brief CDC_TransmitCplt_HS - * Data transmited callback - * - * @note - * This function is IN transfer complete callback used to inform user that - * the submitted Data is successfully sent over USB. - * - * @param Buf: Buffer of data to be received - * @param Len: Number of data received (in bytes) - * @retval Result of the operation: USBD_OK if all operations are OK else USBD_FAIL - */ -static int8_t CDC_TransmitCplt_HS(uint8_t *Buf, uint32_t *Len, uint8_t epnum) -{ - uint8_t result = USBD_OK; - /* USER CODE BEGIN 14 */ - UNUSED(Buf); - UNUSED(Len); - UNUSED(epnum); - /* USER CODE END 14 */ - return result; -} - -/* USER CODE BEGIN PRIVATE_FUNCTIONS_IMPLEMENTATION */ - -/* USER CODE END PRIVATE_FUNCTIONS_IMPLEMENTATION */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/USB_DEVICE/App/usbd_desc.c b/Core/Src/USB_DEVICE/App/usbd_desc.c deleted file mode 100644 index 46d6e0c..0000000 --- a/Core/Src/USB_DEVICE/App/usbd_desc.c +++ /dev/null @@ -1,447 +0,0 @@ -/* USER CODE BEGIN Header */ -/** - ****************************************************************************** - * @file : App/usbd_desc.c - * @version : v1.0_Cube - * @brief : This file implements the USB device descriptors. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ -/* USER CODE END Header */ - -/* Includes ------------------------------------------------------------------*/ -#include "usbd_core.h" -#include "usbd_desc.h" -#include "usbd_conf.h" - -/* USER CODE BEGIN INCLUDE */ - -/* USER CODE END INCLUDE */ - -/* Private typedef -----------------------------------------------------------*/ -/* Private define ------------------------------------------------------------*/ -/* Private macro -------------------------------------------------------------*/ - -/* USER CODE BEGIN PV */ -/* Private variables ---------------------------------------------------------*/ - -/* USER CODE END PV */ - -/** @addtogroup STM32_USB_OTG_DEVICE_LIBRARY - * @{ - */ - -/** @addtogroup USBD_DESC - * @{ - */ - -/** @defgroup USBD_DESC_Private_TypesDefinitions USBD_DESC_Private_TypesDefinitions - * @brief Private types. - * @{ - */ - -/* USER CODE BEGIN PRIVATE_TYPES */ - -/* USER CODE END PRIVATE_TYPES */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_Defines USBD_DESC_Private_Defines - * @brief Private defines. - * @{ - */ - -#define USBD_VID 1155 -#define USBD_LANGID_STRING 1033 -#define USBD_MANUFACTURER_STRING "RTT" -#define USBD_PID_HS 22336 -#define USBD_PRODUCT_STRING_HS "BaseStation" -#define USBD_CONFIGURATION_STRING_HS "CDC Config" -#define USBD_INTERFACE_STRING_HS "CDC Interface" - -#define USB_SIZ_BOS_DESC 0x0C - -/* USER CODE BEGIN PRIVATE_DEFINES */ - -/* USER CODE END PRIVATE_DEFINES */ - -/** - * @} - */ - -/* USER CODE BEGIN 0 */ - -/* USER CODE END 0 */ - -/** @defgroup USBD_DESC_Private_Macros USBD_DESC_Private_Macros - * @brief Private macros. - * @{ - */ - -/* USER CODE BEGIN PRIVATE_MACRO */ - -/* USER CODE END PRIVATE_MACRO */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_FunctionPrototypes USBD_DESC_Private_FunctionPrototypes - * @brief Private functions declaration. - * @{ - */ - -static void Get_SerialNum(void); -static void IntToUnicode(uint32_t value, uint8_t * pbuf, uint8_t len); - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_FunctionPrototypes USBD_DESC_Private_FunctionPrototypes - * @brief Private functions declaration for HS. - * @{ - */ - -uint8_t * USBD_HS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -uint8_t * USBD_HS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); - -#if (USBD_LPM_ENABLED == 1) -uint8_t * USBD_HS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length); -#endif /* (USBD_LPM_ENABLED == 1) */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_Variables USBD_DESC_Private_Variables - * @brief Private variables. - * @{ - */ - -USBD_DescriptorsTypeDef HS_Desc = -{ - USBD_HS_DeviceDescriptor -, USBD_HS_LangIDStrDescriptor -, USBD_HS_ManufacturerStrDescriptor -, USBD_HS_ProductStrDescriptor -, USBD_HS_SerialStrDescriptor -, USBD_HS_ConfigStrDescriptor -, USBD_HS_InterfaceStrDescriptor -#if (USBD_LPM_ENABLED == 1) -, USBD_HS_USR_BOSDescriptor -#endif /* (USBD_LPM_ENABLED == 1) */ -}; - -#if defined ( __ICCARM__ ) /* IAR Compiler */ - #pragma data_alignment=4 -#endif /* defined ( __ICCARM__ ) */ -/** USB standard device descriptor. */ -__ALIGN_BEGIN uint8_t USBD_HS_DeviceDesc[USB_LEN_DEV_DESC] __ALIGN_END = -{ - 0x12, /*bLength */ - USB_DESC_TYPE_DEVICE, /*bDescriptorType*/ -#if (USBD_LPM_ENABLED == 1) - 0x01, /*bcdUSB */ /* changed to USB version 2.01 - in order to support LPM L1 suspend - resume test of USBCV3.0*/ -#else - 0x00, /*bcdUSB */ -#endif /* (USBD_LPM_ENABLED == 1) */ - - 0x02, - 0x02, /*bDeviceClass*/ - 0x02, /*bDeviceSubClass*/ - 0x00, /*bDeviceProtocol*/ - USB_MAX_EP0_SIZE, /*bMaxPacketSize*/ - LOBYTE(USBD_VID), /*idVendor*/ - HIBYTE(USBD_VID), /*idVendor*/ - LOBYTE(USBD_PID_HS), /*idProduct*/ - HIBYTE(USBD_PID_HS), /*idProduct*/ - 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*/ -}; - -/** BOS descriptor. */ -#if (USBD_LPM_ENABLED == 1) -#if defined ( __ICCARM__ ) /* IAR Compiler */ - #pragma data_alignment=4 -#endif /* defined ( __ICCARM__ ) */ -__ALIGN_BEGIN uint8_t USBD_HS_BOSDesc[USB_SIZ_BOS_DESC] __ALIGN_END = -{ - 0x5, - USB_DESC_TYPE_BOS, - 0xC, - 0x0, - 0x1, /* 1 device capability */ - /* device capability */ - 0x7, - USB_DEVICE_CAPABITY_TYPE, - 0x2, - 0x2, /*LPM capability bit set */ - 0x0, - 0x0, - 0x0 -}; -#endif /* (USBD_LPM_ENABLED == 1) */ - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_Variables USBD_DESC_Private_Variables - * @brief Private variables. - * @{ - */ - -#if defined ( __ICCARM__ ) /* IAR Compiler */ - #pragma data_alignment=4 -#endif /* defined ( __ICCARM__ ) */ - -/** USB lang indentifier 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 /* defined ( __ICCARM__ ) */ -/* Internal string descriptor. */ -__ALIGN_BEGIN uint8_t USBD_StrDesc[USBD_MAX_STR_DESC_SIZ] __ALIGN_END; - -#if defined ( __ICCARM__ ) /*!< IAR Compiler */ - #pragma data_alignment=4 -#endif -__ALIGN_BEGIN uint8_t USBD_StringSerial[USB_SIZ_STRING_SERIAL] __ALIGN_END = { - USB_SIZ_STRING_SERIAL, - USB_DESC_TYPE_STRING, -}; - -/** - * @} - */ - -/** @defgroup USBD_DESC_Private_Functions USBD_DESC_Private_Functions - * @brief Private functions. - * @{ - */ - -/** - * @brief Return the device descriptor - * @param speed : Current device speed - * @param length : Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -uint8_t * USBD_HS_DeviceDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_HS_DeviceDesc); - return USBD_HS_DeviceDesc; -} - -/** - * @brief 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_HS_LangIDStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_LangIDDesc); - return USBD_LangIDDesc; -} - -/** - * @brief 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_HS_ProductStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - if(speed == 0) - { - USBD_GetString((uint8_t *)USBD_PRODUCT_STRING_HS, USBD_StrDesc, length); - } - else - { - USBD_GetString((uint8_t *)USBD_PRODUCT_STRING_HS, USBD_StrDesc, length); - } - return USBD_StrDesc; -} - -/** - * @brief 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_HS_ManufacturerStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - USBD_GetString((uint8_t *)USBD_MANUFACTURER_STRING, USBD_StrDesc, length); - return USBD_StrDesc; -} - -/** - * @brief 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_HS_SerialStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = USB_SIZ_STRING_SERIAL; - - /* Update the serial number string descriptor with the data from the unique - * ID */ - Get_SerialNum(); - /* USER CODE BEGIN USBD_HS_SerialStrDescriptor */ - - /* USER CODE END USBD_HS_SerialStrDescriptor */ - - return (uint8_t *) USBD_StringSerial; -} - -/** - * @brief 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_HS_ConfigStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - if(speed == USBD_SPEED_HIGH) - { - USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_HS, USBD_StrDesc, length); - } - else - { - USBD_GetString((uint8_t *)USBD_CONFIGURATION_STRING_HS, USBD_StrDesc, length); - } - return USBD_StrDesc; -} - -/** - * @brief 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_HS_InterfaceStrDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - if(speed == 0) - { - USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_HS, USBD_StrDesc, length); - } - else - { - USBD_GetString((uint8_t *)USBD_INTERFACE_STRING_HS, USBD_StrDesc, length); - } - return USBD_StrDesc; -} - -#if (USBD_LPM_ENABLED == 1) -/** - * @brief Return the BOS descriptor - * @param speed : Current device speed - * @param length : Pointer to data length variable - * @retval Pointer to descriptor buffer - */ -uint8_t * USBD_HS_USR_BOSDescriptor(USBD_SpeedTypeDef speed, uint16_t *length) -{ - UNUSED(speed); - *length = sizeof(USBD_HS_BOSDesc); - return (uint8_t*)USBD_HS_BOSDesc; -} -#endif /* (USBD_LPM_ENABLED == 1) */ - -/** - * @brief Create the serial number string descriptor - * @param None - * @retval None - */ -static void Get_SerialNum(void) -{ - uint32_t deviceserial0, deviceserial1, deviceserial2; - - deviceserial0 = *(uint32_t *) DEVICE_ID1; - deviceserial1 = *(uint32_t *) DEVICE_ID2; - deviceserial2 = *(uint32_t *) DEVICE_ID3; - - deviceserial0 += deviceserial2; - - if (deviceserial0 != 0) - { - IntToUnicode(deviceserial0, &USBD_StringSerial[2], 8); - IntToUnicode(deviceserial1, &USBD_StringSerial[18], 4); - } -} - -/** - * @brief Convert Hex 32Bits value into char - * @param value: value to convert - * @param pbuf: pointer to the buffer - * @param len: buffer length - * @retval None - */ -static void IntToUnicode(uint32_t value, uint8_t * pbuf, uint8_t len) -{ - uint8_t idx = 0; - - for (idx = 0; idx < len; idx++) - { - if (((value >> 28)) < 0xA) - { - pbuf[2 * idx] = (value >> 28) + '0'; - } - else - { - pbuf[2 * idx] = (value >> 28) + 'A' - 10; - } - - value = value << 4; - - pbuf[2 * idx + 1] = 0; - } -} -/** - * @} - */ - -/** - * @} - */ - -/** - * @} - */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/USB_DEVICE/Target/usbd_conf.c b/Core/Src/USB_DEVICE/Target/usbd_conf.c index c6f6423..4994c44 100644 --- a/Core/Src/USB_DEVICE/Target/usbd_conf.c +++ b/Core/Src/USB_DEVICE/Target/usbd_conf.c @@ -7,13 +7,12 @@ ****************************************************************************** * @attention * - *

© Copyright (c) 2021 STMicroelectronics. - * All rights reserved.

+ * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. * ****************************************************************************** */ @@ -406,9 +405,10 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev) HAL_PCD_RegisterIsoOutIncpltCallback(&hpcd_USB_OTG_HS, PCD_ISOOUTIncompleteCallback); HAL_PCD_RegisterIsoInIncpltCallback(&hpcd_USB_OTG_HS, PCD_ISOINIncompleteCallback); #endif /* USE_HAL_PCD_REGISTER_CALLBACKS */ - HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x200); + HAL_PCDEx_SetRxFiFo(&hpcd_USB_OTG_HS, 0x160); HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 0, 0x80); - HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x174); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 1, 0x80); + HAL_PCDEx_SetTxFiFo(&hpcd_USB_OTG_HS, 2, 0x80); } return USBD_OK; } @@ -635,10 +635,10 @@ USBD_StatusTypeDef USBD_LL_PrepareReceive(USBD_HandleTypeDef *pdev, uint8_t ep_a } /** - * @brief Returns the last transfered packet size. + * @brief Returns the last transferred packet size. * @param pdev: Device handle * @param ep_addr: Endpoint number - * @retval Recived Data Size + * @retval Received Data Size */ uint32_t USBD_LL_GetRxDataSize(USBD_HandleTypeDef *pdev, uint8_t ep_addr) { @@ -703,7 +703,7 @@ static void SystemClockConfig_Resume(void) } /* USER CODE END 5 */ /** - * @brief Retuns the USB status depending on the HAL status: + * @brief Returns the USB status depending on the HAL status: * @param hal_status: HAL status * @retval USB status */ @@ -732,4 +732,3 @@ USBD_StatusTypeDef USBD_Get_USB_Status(HAL_StatusTypeDef hal_status) return usb_status; } -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/basestation.c b/Core/Src/basestation.c index f5bf3a1..81460c4 100644 --- a/Core/Src/basestation.c +++ b/Core/Src/basestation.c @@ -6,6 +6,9 @@ #include "FT812Q_Drawing.h" #include "iwdg.h" +#include "usb_device.h" // USB start function +#include "usbd_RTT_class.h" // USB class definition + #include "REM_BaseTypes.h" #include "REM_BasestationConfiguration.h" #include "REM_RobotCommand.h" @@ -13,8 +16,17 @@ #include "REM_RobotStateInfo.h" #include "REM_RobotSetPIDGains.h" #include "REM_RobotMusicCommand.h" +#include "REM_Packet.h" #include "REM_SX1280Filler.h" +#include "CircularBuffer.h" + + +/* Counters, tracking the number of packets handled */ +volatile uint32_t packet_counter_in[REM_TOTAL_NUMBER_OF_PACKETS]; +volatile uint32_t packet_counter_out[REM_TOTAL_NUMBER_OF_PACKETS]; + + /* Counters, tracking the number of packets handled */ volatile int handled_RobotCommand = 0; volatile int handled_RobotFeedback = 0; @@ -25,14 +37,13 @@ volatile int handled_RobotSetPIDGains = 0; volatile int handled_RobotPIDGains = 0; volatile int handled_RobotMusicCommand = 0; - /* Import hardware handles from main.c */ extern SPI_HandleTypeDef hspi4; extern SPI_HandleTypeDef hspi2; extern TIM_HandleTypeDef htim1; /* Watchdog timer handler */ -IWDG_Handle* iwdg; +IWDG_Handle iwdg; /* Screen variables */ DISPLAY_STATES displayState = DISPLAY_STATE_DEINITIALIZED; @@ -43,7 +54,6 @@ TouchState touchState; // TODO check default initialization. What is touchState- // Currently, we're splitting the SX1280 256 byte buffer in half. 128 for sending, 128 for receiving // Set to 127, because that's the max value as defined in the datasheet // Table 14-38: Payload Length Definition in FLRC Packet, page 124 -#define MAX_PACKET_SIZE 127 /* SX data */ // TODO: Maybe move all configs to its own file? (basestation_config.c/h???) @@ -61,14 +71,14 @@ static Wireless_Packet txPacket; static Wireless_Packet rxPacket; // The pins cannot be set at this point as they are not "const" enough for the compiler, so set them in the init -SX1280_Interface SX_TX_Interface = {.SPI= &hspi2, .TXbuf= SXTX_TX_buffer, .RXbuf= SXTX_RX_buffer, .logger=LOG_printf,}; -SX1280_Interface SX_RX_Interface = {.SPI= &hspi4, .TXbuf= SXRX_TX_buffer, .RXbuf= SXRX_RX_buffer, .logger=LOG_printf,}; +SX1280_Interface SX_TX_Interface = {.SPI= &hspi2, .TXbuf= SXTX_TX_buffer, .RXbuf= SXTX_RX_buffer, /*.logger=LOG_printf*/}; +SX1280_Interface SX_RX_Interface = {.SPI= &hspi4, .TXbuf= SXRX_TX_buffer, .RXbuf= SXRX_RX_buffer, /*.logger=LOG_printf*/}; void Wireless_Writepacket_Cplt(void){ TransmitPacket(SX_TX); } void Wireless_Readpacket_Cplt(void){ - handlePacket(rxPacket.message, rxPacket.payloadLength); + handlePackets(rxPacket.message, rxPacket.payloadLength); }; void Wireless_TXDone(SX1280_Packet_Status *status){ @@ -76,16 +86,15 @@ void Wireless_TXDone(SX1280_Packet_Status *status){ } void Wireless_RXDone(SX1280_Packet_Status *status){ - toggle_pin(LD_RX); - toggle_pin(LD_LED2); /* It is possible that random noise can trigger the syncword. - * Syncword is 32 bits. Noise comes in at 2.4GHz. Syncword resets when wrong bit is received. + * Syncword is 32 bits. Information comes in at 1.2MHz (someone confirm). Syncword resets when wrong bit is received. * Expected length of wrong syncword is 1*0.5 + 2*0.25 + 3*0.125 + ... = 2 - * 2^32 combinations / (2400000000 / 2) syncwords = correct syncword every 3.57 seconds purely from noise + * 2^32 combinations / (1200000 / 2) syncwords = correct syncword every 7158 seconds (2 hours) purely from noise */ // Correct syncword from noise have a very weak signal // Threshold is at -160/2 = -80 dBm if (status->RSSISync < 160) { + toggle_pin(LD_RX); ReadPacket_DMA(SX_RX, &rxPacket, &Wireless_Readpacket_Cplt); // not necessary to force WaitForPacket() here when configured in Rx Continuous mode } @@ -99,7 +108,37 @@ void Wireless_RXTXTimeout(void){ Wireless_IRQcallbacks SXTX_IRQcallbacks = {.txdone= &Wireless_TXDone, .rxdone= NULL, .rxtxtimeout= &Wireless_RXTXTimeout}; Wireless_IRQcallbacks SXRX_IRQcallbacks = {.txdone= NULL, .rxdone= &Wireless_RXDone, .rxtxtimeout= &Wireless_RXTXTimeout}; +// USB functions +USBD_StatusTypeDef USB_Control(uint8_t cmd, uint8_t* pbuf, uint16_t length){ + // currently no RTT class specific stuff + return USBD_OK; +} + +void USB_HighPrioTXCplt(void){ + toggle_pin(LD_LED1); + return; +} + +void USB_HighPrioRXCplt(uint8_t* buf, uint32_t len){ + handlePackets(buf,len); +} + +void USB_LowPrioRXCplt(uint8_t* buf, uint32_t len){ + handlePackets(buf,len); +} +void USB_LowPrioTXCplt(void){ + toggle_pin(LD_LED1); + return; +} + +USBD_RTT_Callbacks USB_callbacks = { + .usbControl = &USB_Control, + .highprioRXcplt = &USB_HighPrioRXCplt, + .highprioTXcplt = &USB_HighPrioTXCplt, + .lowprioRXcplt = &USB_LowPrioRXCplt, + .lowprioTXcplt = &USB_LowPrioTXCplt, +}; /* Flags */ volatile bool flagHandleConfiguration = false; @@ -107,128 +146,253 @@ volatile bool flagHandleConfiguration = false; // screenCounter acts as a timer for updating the screen uint32_t screenCounter = 0; -/* Tracks time since last heartbeat. Runs at 1Hz */ +uint32_t timestamp_initialized = 0; +uint32_t heartbeat_17ms = 0; +uint32_t heartbeat_100ms = 0; uint32_t heartbeat_1000ms = 0; - +// For manually writing to the programmer UART, without the logging library +// uint8_t stringbuffer[1024]; +// extern UART_HandleTypeDef huart4; + +void sendFakeRobotCommand(uint8_t id){ + static REM_RobotCommand fake_cmd = {0}; + fake_cmd.header = REM_PACKET_TYPE_REM_ROBOT_COMMAND; + fake_cmd.toRobotId = id; + fake_cmd.fromBS = true; + fake_cmd.remVersion = REM_LOCAL_VERSION; + fake_cmd.payloadSize = REM_PACKET_SIZE_REM_ROBOT_COMMAND; + fake_cmd.rho = 0.01; + encodeREM_RobotCommand(&buffer_REM_RobotCommand[id].packet.payload, &fake_cmd); + buffer_REM_RobotCommand[id].isNewPacket = true; +} void init(){ - HAL_Delay(1000); // TODO Why do we have this again? To allow for USB to start up iirc? - LOG_init(); - - // Init SX_TX - LOG("[init] Initializing SX_TX\n"); - Wireless_Error err; - SX_TX_Interface.BusyPin = SX_TX_BUSY; - SX_TX_Interface.CS= SX_TX_CS; - SX_TX_Interface.Reset= SX_TX_RST; - err = Wireless_setPrint_Callback(SX_TX, NULL); - err = Wireless_Init(SX_TX, SX1280_DEFAULT_SETTINGS, &SX_TX_Interface); - err = Wireless_setIRQ_Callbacks(SX_TX,&SXTX_IRQcallbacks); - if(err != WIRELESS_OK){ - //TODO: What do? - while(1); + // Initialize Watchdog timer. Already started in main.c:MX_IWDG_Init(), but now, we can call IWDG_Refresh. + IWDG_Init(&iwdg); + + USB_Start_Class(&USB_callbacks); + HAL_Delay(100); // TODO Why do we have this again? To allow for USB to start up iirc? + + LOG_init(); + + LOG("[init:"STRINGIZE(__LINE__)"] Last programmed on " __DATE__ "\n"); + LOG("[init:"STRINGIZE(__LINE__)"] GIT: " STRINGIZE(__GIT_STRING__) "\n"); + LOG_printf("[init:"STRINGIZE(__LINE__)"] REM_LOCAL_VERSION: %d\n", REM_LOCAL_VERSION); + LOG_sendAll(); + // static char charbuffer[100]; + // sprintf(charbuffer, "Yellow World!\n"); + + // while(1){ + // LOG("Hello world\n"); + // LOG_sendBlocking(charbuffer, strlen(charbuffer)); + // // CDC_Transmit_FS(charbuffer, strlen(charbuffer)); + // // toggle_pin(LD_ACTIVE); + // // HAL_UART_Transmit(&huart3, charbuffer, strlen(charbuffer), 10); + // HAL_GPIO_TogglePin(LD_LED2_GPIO_Port, LD_LED2_Pin); + // HAL_Delay(1000); + // } + + // Initialize the circular buffers for the nonprioriry queue + // STM32F767 has 512kb of RAM. Give each robot a 10kb buffer for a total of 160kb RAM. + // That should be enough for around 200 messages per robot at least + + for(uint8_t robot_id = 0; robot_id < MAX_NUMBER_OF_ROBOTS; robot_id++){ + nonpriority_queue_robots_index[robot_id] = CircularBuffer_init(true, 40); + } + nonpriority_queue_pc_index = CircularBuffer_init(true, 40); + nonpriority_queue_bs_index = CircularBuffer_init(true, 40); + + + + // Init SX_TX + LOG("[init:"STRINGIZE(__LINE__)"] Initializing SX_TX\n"); + bool SX_TX_init_err = false; + SX_TX_Interface.BusyPin = SX_TX_BUSY; + SX_TX_Interface.CS= SX_TX_CS; + SX_TX_Interface.Reset= SX_TX_RST; + // Set the print function. NULL to supress printing, LOG_printf to enable printing + // SX_TX_init_err |= WIRELESS_OK != Wireless_setPrint_Callback(SX_TX, LOG_printf); + // Wake up the TX SX1280 and send it all the default settings + SX_TX_init_err |= WIRELESS_OK != Wireless_Init(SX_TX, SX1280_DEFAULT_SETTINGS, &SX_TX_Interface); + // Set the functions that have to be called on stuff like "a packet has been received" or "a packet has been sent" or "a timeout has occured". See Wireless_IRQcallbacks in Wireless.h + SX_TX_init_err |= WIRELESS_OK != Wireless_setIRQ_Callbacks(SX_TX,&SXTX_IRQcallbacks); + // Set the channel (radio frequency) to the YELLOW_CHANNEL. Can be changed by sending a REM_BasestationConfiguration message + SX_TX_init_err |= WIRELESS_OK != Wireless_setChannel(SX_TX, YELLOW_CHANNEL); + + if(SX_TX_init_err){ + while(true){ + LOG_printf("[init:"STRINGIZE(__LINE__)"]["STRINGIZE(__LINE__)"] Error! Could not initialize SX_TX! Please reboot the basestation\n"); + LOG_sendAll(); + HAL_Delay(1000); } - Wireless_setChannel(SX_TX, YELLOW_CHANNEL); - - // Init SX_RX - LOG("[init] Initializing SX_RX\n"); - SX_RX_Interface.BusyPin= SX_RX_BUSY; - SX_RX_Interface.CS= SX_RX_CS; - SX_RX_Interface.Reset= SX_RX_RST; - err = Wireless_setPrint_Callback(SX_TX, NULL); - err = Wireless_Init(SX_RX, SX1280_DEFAULT_SETTINGS, &SX_RX_Interface); - err = Wireless_setIRQ_Callbacks(SX_RX,&SXRX_IRQcallbacks); - if(err != WIRELESS_OK){ - //TODO: What do? - while(1); + } + + + + // Init SX_RX + LOG("[init:"STRINGIZE(__LINE__)"] Initializing SX_RX\n"); + bool SX_RX_init_err = false; + SX_RX_Interface.BusyPin= SX_RX_BUSY; + SX_RX_Interface.CS= SX_RX_CS; + SX_RX_Interface.Reset= SX_RX_RST; + // Set the print function. NULL to supress printing, LOG_printf to enable printing + SX_RX_init_err |= WIRELESS_OK != Wireless_setPrint_Callback(SX_TX, NULL); + // Wake up the RX SX1280 and send it all the default settings + SX_RX_init_err |= WIRELESS_OK != Wireless_Init(SX_RX, SX1280_DEFAULT_SETTINGS, &SX_RX_Interface); + // Set the functions that have to be called on stuff like "a packet has been received" or "a packet has been sent" or "a timeout has occured". See Wireless_IRQcallbacks in Wireless.h + SX_RX_init_err |= WIRELESS_OK != Wireless_setIRQ_Callbacks(SX_RX, &SXRX_IRQcallbacks); + // Set the channel (radio frequency) to the YELLOW_CHANNEL. Can be changed by sending a REM_BasestationConfiguration message + SX_RX_init_err |= WIRELESS_OK != Wireless_setChannel(SX_RX, YELLOW_CHANNEL); + // Set SX_RX syncword to basestation syncword. Meaning, let the receiving SX only receive packets meant for the basestation + uint32_t syncwords[2] = {robot_syncWord[16],0}; + SX_RX_init_err |= WIRELESS_OK != Wireless_setRXSyncwords(SX_RX, syncwords); + // Start listening on the SX_RX for packets from the robots + SX_RX_init_err |= WIRELESS_OK != WaitForPacketContinuous(SX_RX); + + if(SX_RX_init_err){ + while(true){ + LOG_printf("[init:"STRINGIZE(__LINE__)"]["STRINGIZE(__LINE__)"] Error! Could not initialize SX_RX! Please reboot the basestation\n"); + LOG_sendAll(); + HAL_Delay(1000); } - // TODO: Use proper defines - Wireless_setChannel(SX_RX, YELLOW_CHANNEL); - // Set SX_RX syncword to basestation syncword - uint32_t syncwords[2] = {robot_syncWord[16],0}; - Wireless_setRXSyncwords(SX_RX, syncwords); + } + - // Start listening on the SX_RX for packets from the robots - WaitForPacketContinuous(SX_RX); - // Start the timer that is responsible for sending packets to the robots - // With 16 robots at 60Hz each, this timer runs at approximately 960Hz - LOG("[init] Initializing Timer\n"); - HAL_TIM_Base_Start_IT(&htim1); + // Start the timer that is responsible for sending packets to the robots + // With 16 robots at 60Hz each, this timer runs at approximately 960Hz + /// It's required that all buffers are initialized before starting the timer! + LOG("[init:"STRINGIZE(__LINE__)"] Initializing Timer\n"); + HAL_TIM_Base_Start_IT(&htim1); - // display_Init(); - // displayState = DISPLAY_STATE_INITIALIZED; - // drawBasestation(true); + // display_Init(); + // displayState = DISPLAY_STATE_INITIALIZED; + // drawBasestation(true); - IWDG_Init(iwdg); + // Initialize the REM_SX1280FillerPayload packet + REM_SX1280Filler filler = {0}; + filler.header = REM_PACKET_TYPE_REM_SX1280FILLER; + filler.remVersion = REM_LOCAL_VERSION; + encodeREM_SX1280Filler(&SX1280_filler_payload, &filler); - // Initialize the REM_SX1280FillerPayload packet - REM_SX1280Filler filler = {0}; - filler.header = REM_PACKET_TYPE_REM_SX1280FILLER; - filler.remVersion = REM_LOCAL_VERSION; - encodeREM_SX1280Filler(&SX1280_filler_payload, &filler); + LOG("[init:"STRINGIZE(__LINE__)"] Initializion complete\n"); + LOG_sendAll(); + + timestamp_initialized = HAL_GetTick(); + + /* Set the heartbeat timers */ + heartbeat_17ms = timestamp_initialized + 17; + heartbeat_100ms = timestamp_initialized + 100; + heartbeat_1000ms = timestamp_initialized + 1000; - LOG("[init] Initializion complete\n"); } void loop(){ - IWDG_Refresh(iwdg); + IWDG_Refresh(&iwdg); LOG_send(); - handled_RobotStateInfo++; + uint32_t current_time = HAL_GetTick(); + + // Heartbeat every 17ms + if(heartbeat_17ms < current_time){ + while (heartbeat_17ms < current_time) heartbeat_17ms += 17; + } + + // Heartbeat every 100ms + if(heartbeat_100ms < current_time){ + while (heartbeat_100ms < current_time) heartbeat_100ms += 100; + } + /* Heartbeat every second */ - if(heartbeat_1000ms + 1000 < HAL_GetTick()){ - heartbeat_1000ms += 1000; - // HexOut("Tick!\n", 6); - LOG_printf("Tick | RC %d RF %d RB %d RSI %d GPID %d PID %d\n", - handled_RobotCommand, handled_RobotFeedback, handled_RobotBuzzer, handled_RobotStateInfo, handled_RobotGetPIDGains, handled_RobotPIDGains); + if(heartbeat_1000ms < current_time){ + while (heartbeat_1000ms < current_time) heartbeat_1000ms += 1000; + + LOG_printf("Tick : Type in out | RC %d %d | RF %d %d | RB %d %d | RSI %d %d | toPC %d | toBS %d\n", + packet_counter_in[REM_PACKET_INDEX_REM_ROBOT_COMMAND], packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_COMMAND], + packet_counter_in[REM_PACKET_INDEX_REM_ROBOT_FEEDBACK], packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_FEEDBACK], + packet_counter_in[REM_PACKET_INDEX_REM_ROBOT_BUZZER], packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_BUZZER], + packet_counter_in[REM_PACKET_INDEX_REM_ROBOT_STATE_INFO], packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_STATE_INFO], + CircularBuffer_spaceFilled(nonpriority_queue_pc_index), CircularBuffer_spaceFilled(nonpriority_queue_bs_index) + ); + + + // for(int robot_id = 0; robot_id <= MAX_ROBOT_ID; robot_id++){ + // CircularBuffer* queue_index = nonpriority_queue_robots_index[robot_id]; + // if(queue_index != NULL){ + // LOG_printf("Robot %d: %d %p\n", robot_id, CircularBuffer_spaceFilled(queue_index), (void*) queue_index); + // } + // } + toggle_pin(LD_ACTIVE); } - // RC %d RF %d RB %d handled_RobotCommand, handled_RobotFeedback, handled_RobotBuzzer, - return; // TODO put multiple of these messages into a single USB packet, instead of sending every packet separately /* Send any new RobotFeedback packets */ for(int id = 0; id <= MAX_ROBOT_ID; id++){ - if(buffer_RobotFeedback[id].isNewPacket){ - LOG_sendBlocking(buffer_RobotFeedback[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_FEEDBACK); - buffer_RobotFeedback[id].isNewPacket = false; + if(buffer_REM_RobotFeedback[id].isNewPacket){ + // LOG_sendBlocking(buffer_REM_RobotFeedback[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_FEEDBACK); + USBD_StatusTypeDef ret = USB_TransmitHighPriority(buffer_REM_RobotFeedback[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_FEEDBACK); + if(ret==USBD_FAIL){ + toggle_pin(LD_LED3); + } + buffer_REM_RobotFeedback[id].isNewPacket = false; + packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_FEEDBACK]++; } } - /* Send any new RobotStateInfo packets */ - for(int id = 0; id <= MAX_ROBOT_ID; id++){ - if(buffer_RobotStateInfo[id].isNewPacket){ - LOG_sendBlocking(buffer_RobotStateInfo[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_STATE_INFO); - buffer_RobotStateInfo[id].isNewPacket = false; - } + /* Send any packets that are in the queue and meant for the PC */ + if(CircularBuffer_canRead(nonpriority_queue_pc_index, 1)){ + // LOG_printf("Reading from index %d\n", nonpriority_queue_pc_index->indexRead); + uint8_t* data = nonpriority_queue_pc[ nonpriority_queue_pc_index->indexRead ].data; + REM_PacketPayload* packet = (REM_PacketPayload*) nonpriority_queue_pc[ nonpriority_queue_pc_index->indexRead ].data; + uint8_t packet_type = REM_Packet_get_header(packet); + uint32_t packet_size = REM_Packet_get_payloadSize(packet); + bool packet_sent = LOG_sendBuffer((uint8_t*)packet, packet_size, true); + if(packet_sent) { + uint8_t packet_type = REM_Packet_get_header(packet); + packet_counter_out[REM_PACKET_TYPE_TO_INDEX(packet_type)]++; + // LOG_printf("Packet sent! type=%d (%d) size=%d (%d) p=%p\n", packet_type, data[0], packet_size, data[4], nonpriority_queue_pc[ nonpriority_queue_pc_index->indexRead ].data); + CircularBuffer_read(nonpriority_queue_pc_index, NULL, 1); + }else{ + // LOG("Couldn't send packet..\n"); + } } - /* Send any new RobotPIDGains packets */ - for(int id = 0; id <= MAX_ROBOT_ID; id++){ - if(buffer_RobotPIDGains[id].isNewPacket){ - LOG_sendBlocking(buffer_RobotPIDGains[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_PIDGAINS); - buffer_RobotPIDGains[id].isNewPacket = false; - } - } + /* Deal with any packets that are in the queue and meant for the Basestation, one at a time */ + if(CircularBuffer_canRead(nonpriority_queue_bs_index, 1)){ + uint8_t* data = nonpriority_queue_bs[ nonpriority_queue_bs_index->indexRead ].data; + REM_PacketPayload* packet = (REM_PacketPayload*) nonpriority_queue_bs[ nonpriority_queue_bs_index->indexRead ].data; - if (flagHandleConfiguration) { - // TODO: Make a nice function for this - REM_BasestationConfiguration configuration; - configuration.header = REM_PACKET_TYPE_REM_BASESTATION_CONFIGURATION; - configuration.remVersion = REM_LOCAL_VERSION; - configuration.channel = Wireless_getChannel(SX_TX); + LOG_printf("[loop]["STRINGIZE(__LINE__)"] Packet ready for Basestation with type %d\n", REM_Packet_get_header(packet)); + + if(REM_Packet_get_header(packet) == REM_PACKET_TYPE_REM_BASESTATION_GET_CONFIGURATION) + if( handleREM_BasestationGetConfiguration() ) + CircularBuffer_read(nonpriority_queue_bs_index, NULL, 1); + + if(REM_Packet_get_header(packet) == REM_PACKET_TYPE_REM_BASESTATION_CONFIGURATION) + if( handleREM_BasestationConfiguration( (REM_BasestationConfigurationPayload*) packet) ) + CircularBuffer_read(nonpriority_queue_bs_index, NULL, 1); + } - REM_BasestationConfigurationPayload payload; - encodeREM_BasestationConfiguration(&payload, &configuration); + // /* Send any new RobotStateInfo packets */ + // for(int id = 0; id <= MAX_ROBOT_ID; id++){ + // if(buffer_RobotStateInfo[id].isNewPacket){ + // //LOG_sendBlocking(buffer_RobotStateInfo[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_STATE_INFO); + // buffer_RobotStateInfo[id].isNewPacket = false; + // } + // } - LOG_sendBlocking(payload.payload, REM_PACKET_SIZE_REM_BASESTATION_CONFIGURATION); - flagHandleConfiguration = false; - } + // /* Send any new RobotPIDGains packets */ + // for(int id = 0; id <= MAX_ROBOT_ID; id++){ + // if(buffer_RobotPIDGains[id].isNewPacket){ + // //LOG_sendBlocking(buffer_RobotPIDGains[id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_PIDGAINS); + // buffer_RobotPIDGains[id].isNewPacket = false; + // } + // } /* Skip all screen stuff */ @@ -272,6 +436,31 @@ void loop(){ } +bool handleREM_BasestationGetConfiguration(){ + /* Create REM_BasestationConfiguration packet */ + REM_BasestationConfiguration configuration = {0}; + configuration.header = REM_PACKET_TYPE_REM_BASESTATION_CONFIGURATION; + configuration.toPC = true; + configuration.fromBS = true; + configuration.remVersion = REM_LOCAL_VERSION; + configuration.payloadSize = REM_PACKET_SIZE_REM_BASESTATION_CONFIGURATION; + configuration.timestamp = HAL_GetTick(); + configuration.channel = Wireless_getChannel(SX_TX); + /* Encode packet */ + REM_BasestationConfigurationPayload payload = {0}; + encodeREM_BasestationConfiguration(&payload, &configuration); + /* Send packet blocking */ + bool sent = LOG_sendBuffer((uint8_t*)payload.payload, REM_PACKET_SIZE_REM_BASESTATION_CONFIGURATION, true); + /* Let caller know whether the request been handled succesfully */ + return sent; +} + +bool handleREM_BasestationConfiguration(REM_BasestationConfigurationPayload* payload){ + WIRELESS_CHANNEL new_channel = REM_BasestationConfiguration_get_channel(payload); + Wireless_setChannel(SX_TX, new_channel); + Wireless_setChannel(SX_RX, new_channel); + return true; +} /** * @brief Updates the state of the touch. This helps identifying @@ -305,176 +494,6 @@ void updateTouchState(TouchState* touchState){ } } - - -/** - * @brief Receives a buffer which is assumed to be holding a RobotCommand packet. - * If so, it moves the packet to the RobotCommand buffer, and sets a flag to send the packet to - * the corresponding robot. - * - * @param packet_buffer Pointer to the buffer that holds the packet - */ -void handleRobotCommand(uint8_t* packet_buffer){ - handled_RobotCommand++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotCommand_get_remVersion((REM_RobotCommandPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotCommand] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotCommand buffer. Set flag indicating packet needs to be sent to the robot - uint8_t robot_id = REM_RobotCommand_get_toRobotId((REM_RobotCommandPayload*) packet_buffer); - memcpy(buffer_RobotCommand[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_COMMAND); - buffer_RobotCommand[robot_id].isNewPacket = true; - buffer_RobotCommand[robot_id].counter++; -} - -void handleRobotSetPIDGains(uint8_t* packet_buffer){ - handled_RobotSetPIDGains++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotSetPIDGains_get_remVersion((REM_RobotSetPIDGainsPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotSetPIDGains] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotSetPIDGains buffer. Set flag indicating packet needs to be sent to the robot - uint8_t robot_id = REM_RobotSetPIDGains_get_toRobotId((REM_RobotSetPIDGainsPayload*) packet_buffer); - memcpy(buffer_RobotSetPIDGains[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_SET_PIDGAINS); - buffer_RobotSetPIDGains[robot_id].isNewPacket = true; - buffer_RobotSetPIDGains[robot_id].counter++; -} - - -/** - * @brief Receives a buffer which is assumed to be holding a RobotFeedback packet. - * If so, it moves the packet to the RobotFeedback buffer, and sets a flag to send the packet to - * the computer. - * - * @param packet_buffer Pointer to the buffer that holds the packet - */ -void handleRobotFeedback(uint8_t* packet_buffer){ - handled_RobotFeedback++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotFeedback_get_remVersion((REM_RobotFeedbackPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotFeedback] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotFeedback buffer. Set flag indicating packet needs to be sent to the robot - uint8_t robot_id = REM_RobotFeedback_get_toRobotId((REM_RobotFeedbackPayload*) packet_buffer); - memcpy(buffer_RobotFeedback[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_FEEDBACK); - buffer_RobotFeedback[robot_id].isNewPacket = true; - buffer_RobotFeedback[robot_id].counter++; -} - - -/** - * @brief Receives a buffer which is assumed to be holding a RobotStateInfo packet. - * If so, it moves the packet to the RobotStateInfo buffer, and sets a flag to send the packet to - * the computer. - * - * @param packet_buffer Pointer to the buffer that holds the packet - */ -void handleRobotStateInfo(uint8_t* packet_buffer){ - handled_RobotStateInfo++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotStateInfo_get_remVersion((REM_RobotStateInfoPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotStateInfo] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotStateInfo buffer. Set flag to be sent to the robot - uint8_t robot_id = REM_RobotStateInfo_get_toRobotId((REM_RobotStateInfoPayload*) packet_buffer); - memcpy(buffer_RobotStateInfo[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_STATE_INFO); - buffer_RobotStateInfo[robot_id].isNewPacket = true; - buffer_RobotStateInfo[robot_id].counter++; -} - - -/** - * @brief Receives a buffer which is assumed to be holding a RobotBuzzer packet. - * If so, it moves the packet to the RobotBuzzer buffer, and sets a flag to send the packet to - * the corresponding robot. - * - * @param packet_buffer Pointer to the buffer that holds the packet - */ -void handleRobotBuzzer(uint8_t* packet_buffer){ - handled_RobotBuzzer++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotBuzzer_get_remVersion((REM_RobotBuzzerPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotBuzzer] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotBuzzer buffer. Set flag to be sent to the robot - uint8_t robot_id = REM_RobotBuzzer_get_toRobotId((REM_RobotBuzzerPayload*) packet_buffer); - memcpy(buffer_RobotBuzzer[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_BUZZER); - buffer_RobotBuzzer[robot_id].isNewPacket = true; - buffer_RobotBuzzer[robot_id].counter++; -} - -void handleRobotGetPIDGains(uint8_t* packet_buffer){ - handled_RobotGetPIDGains++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotGetPIDGains_get_remVersion((REM_RobotGetPIDGainsPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotGetPIDGains] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotGetPIDGains buffer. Set flag to be sent to the robot - uint8_t robot_id = REM_RobotGetPIDGains_get_toRobotId((REM_RobotGetPIDGainsPayload*) packet_buffer); - memcpy(buffer_RobotGetPIDGains[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_GET_PIDGAINS); - buffer_RobotGetPIDGains[robot_id].isNewPacket = true; - buffer_RobotGetPIDGains[robot_id].counter++; -} - -void handleRobotPIDGains(uint8_t* packet_buffer){ - handled_RobotPIDGains++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotPIDGains_get_remVersion((REM_RobotPIDGainsPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotPIDGains] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotGetPIDGains buffer. Set flag to be sent to the robot - uint8_t robot_id = REM_RobotPIDGains_get_toRobotId((REM_RobotPIDGainsPayload*) packet_buffer); - memcpy(buffer_RobotPIDGains[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_PIDGAINS); - buffer_RobotPIDGains[robot_id].isNewPacket = true; - buffer_RobotPIDGains[robot_id].counter++; -} - -void handleRobotMusicCommand(uint8_t* packet_buffer){ - handled_RobotMusicCommand++; - - // Check if the packet REM version corresponds to the local REM version. If the REM versions do not correspond, drop the packet. - uint8_t packet_rem_version = REM_RobotMusicCommand_get_remVersion((REM_RobotMusicCommandPayload*) packet_buffer); - if(packet_rem_version != REM_LOCAL_VERSION){ - LOG_printf("[handleRobotMusicCommand] Error! packet_rem_version %u != %u REM_LOCAL_VERSION.", packet_rem_version, REM_LOCAL_VERSION); - return; - } - - // Store the message in the RobotMusicCommand buffer. Set flag to be sent to the robot - uint8_t robot_id = REM_RobotMusicCommand_get_toRobotId((REM_RobotMusicCommandPayload*) packet_buffer); - memcpy(buffer_RobotMusicCommand[robot_id].packet.payload, packet_buffer, REM_PACKET_SIZE_REM_ROBOT_MUSIC_COMMAND); - buffer_RobotMusicCommand[robot_id].isNewPacket = true; - buffer_RobotMusicCommand[robot_id].counter++; -} - - /** * @brief routes any incoming packet to the correct function. Hub for all incoming packets. * TODO actually make it route all incoming packets, and not just USB packets @@ -484,70 +503,108 @@ void handleRobotMusicCommand(uint8_t* packet_buffer){ * will arrive as two packets of 64 and 36 bytes. This is because the wMaxPacketSize for * full speed USB is 64 bytes. * - * @param packet_buffer Pointer to the buffer the packet from the USB is stored in - * @param packet_length Length of the packet currently in the buffer + * @param packets_buffer Pointer to the buffer the packet from the USB is stored in + * @param packets_buffer_length Length of the packets currently in the buffer * @return true if the packet(s) have been handled succesfully * @return false if the packet(s) have been handled unsuccessfully, e.g. due to corruption */ -bool handlePacket(uint8_t* packet_buffer, uint32_t packet_length){ - uint8_t packet_type; +bool handlePackets(uint8_t* packets_buffer, uint32_t packets_buffer_length){ uint32_t bytes_processed = 0; + while(bytes_processed < packets_buffer_length){ + + // Get the packet and its type + REM_PacketPayload* packet = (REM_PacketPayload*) (packets_buffer + bytes_processed); + int8_t packet_type = REM_Packet_get_header(packet); + + // Skip filler packets. We need to skip these before we do anything else, since this packet does + // not have all the normal functions such as REM_Packet_get_payloadSize. + if(packet_type == REM_PACKET_TYPE_REM_SX1280FILLER){ + bytes_processed += REM_PACKET_SIZE_REM_SX1280FILLER; + packet_counter_in[REM_PACKET_INDEX_REM_SX1280FILLER]++; + continue; + } + + // Get some information about the packet + bool packet_valid = REM_PACKET_TYPE_TO_VALID(packet_type); + uint32_t packet_size = REM_Packet_get_payloadSize(packet); // Actual packet size + uint32_t packet_size_rem = REM_PACKET_TYPE_TO_SIZE(packet_type); // Expected packet size according to REM + uint32_t packet_rem_version = REM_Packet_get_remVersion(packet); + uint32_t packet_index = REM_PACKET_TYPE_TO_INDEX(packet_type); + // Now that we know the index, update the counter + packet_counter_in[packet_index]++; + + // Check if the packet type is valid + if(!packet_valid){ + LOG_printf("[handlePackets]["STRINGIZE(__LINE__)"] Error! Invalid packet type %d\n", packet_type); + return true; + } + + // Check if the packet REM_version corresponds to the local REM version. If the REM versions do not correspond, drop everything + if(packet_rem_version != REM_LOCAL_VERSION){ + // If the REM_VERSION is wrong, we can't be sure that functions like REM_Packet_get_payloadSize + // still work correctly. We can't even be sure about the entire buffer anymore. If we read this + // packet wrong, everything behind this packet will be read wrong as well. + LOG_printf("[handlePackets]["STRINGIZE(__LINE__)"] Error! Packet type %u : packet_rem_version %u != %u REM_LOCAL_VERSION\n", packet_type, packet_rem_version, REM_LOCAL_VERSION); + return true; + } - while(bytes_processed < packet_length){ - - packet_type = packet_buffer[bytes_processed]; - - switch (packet_type){ - - case REM_PACKET_TYPE_REM_ROBOT_COMMAND: - handleRobotCommand(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_COMMAND; - break; - - case REM_PACKET_TYPE_REM_ROBOT_SET_PIDGAINS: - handleRobotSetPIDGains(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_SET_PIDGAINS; - break; - - case REM_PACKET_TYPE_REM_ROBOT_FEEDBACK: - handleRobotFeedback(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_FEEDBACK; - break; - - case REM_PACKET_TYPE_REM_ROBOT_BUZZER: - handleRobotBuzzer(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_BUZZER; - break; - - case REM_PACKET_TYPE_REM_ROBOT_STATE_INFO: - handleRobotStateInfo(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_STATE_INFO; - break; - - case REM_PACKET_TYPE_REM_ROBOT_GET_PIDGAINS: - handleRobotGetPIDGains(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_TYPE_REM_ROBOT_GET_PIDGAINS; - break; - - case REM_PACKET_TYPE_REM_ROBOT_PIDGAINS: - handleRobotPIDGains(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_TYPE_REM_ROBOT_PIDGAINS; - break; - - case REM_PACKET_TYPE_REM_BASESTATION_GET_CONFIGURATION: - bytes_processed += REM_PACKET_SIZE_REM_BASESTATION_GET_CONFIGURATION; - flagHandleConfiguration = true; - break; - - case REM_PACKET_TYPE_REM_ROBOT_MUSIC_COMMAND: - handleRobotMusicCommand(packet_buffer + bytes_processed); - bytes_processed += REM_PACKET_SIZE_REM_ROBOT_MUSIC_COMMAND; - break; - - default: - LOG_printf("[handlePacket] Error! At %ld of %ld bytes. [@] = %d\n", bytes_processed, packet_length, packet_buffer[bytes_processed]); - return false; + // Check size of static packets. Should not be needed but can't hurt. Skip the LOG packet, since it has a dynamic length + if(packet_type != REM_PACKET_TYPE_REM_LOG){ + if(packet_size != packet_size_rem){ + // Somewhere, someone did not correctly set the payload size. This is not an error per se since + // REM knows all payload sizes (except for REM_Log), but it is a problem e.g. for logging. If we + // ever want to read old logs, we can rely only on the payload size. It should always be correct. + LOG_printf("[handlePackets]["STRINGIZE(__LINE__)"] Error! Packet type %u : packet_size %u != %u packet_size_rem\n", packet_type, packet_size, packet_size_rem); + return true; + } + } + + // Figure out where the packet is headed to + // If neither toPC or toBS is true, then that means that the packet is meant for a robot + bool to_PC = REM_Packet_get_toPC(packet); // Packet is destined for the PC + bool to_BS = REM_Packet_get_toBS(packet); // Packet is destined for the BaseStation + bool to_robot = !(to_PC || to_BS); // Packet is destined for a robot + uint8_t robot_id = REM_Packet_get_toRobotId(packet); + + // LOG_printf("[handlePackets]["STRINGIZE(__LINE__)"] Packet type %u; to_PC %d; to_BS %d; to_robot %d; robot_id %d;\n", packet_type, to_PC, to_BS, to_robot, robot_id); + + // High priority : Deal with RobotCommand packets that are destined for a robot + if(packet_type == REM_PACKET_TYPE_REM_ROBOT_COMMAND && to_robot){ + // Store the message in the RobotCommand buffer. Set flag indicating packet needs to be sent to the robot + memcpy(buffer_REM_RobotCommand[robot_id].packet.payload, packet, packet_size); + buffer_REM_RobotCommand[robot_id].isNewPacket = true; + handled_RobotCommand++; + }else + + // High priority : Deal with RobotFeedback packets that are destined for the PC + if(packet_type == REM_PACKET_TYPE_REM_ROBOT_FEEDBACK && to_PC){ + // Store the message in the RobotFeedback buffer. Set flag indicating packet needs to be sent to the PC + memcpy(buffer_REM_RobotFeedback[robot_id].packet.payload, packet, packet_size); + buffer_REM_RobotFeedback[robot_id].isNewPacket = true; + handled_RobotFeedback++; + }else + + // Low priority : Deal with any other packet + { + // Assume the packet is meant for a robot + CircularBuffer* index = nonpriority_queue_robots_index[robot_id]; + Wrapper_REM_Packet* queue = nonpriority_queue_robots[robot_id]; + // Check if the packet is meant for the PC or the BaseStation + if(to_PC || to_BS){ + index = to_PC ? nonpriority_queue_pc_index : nonpriority_queue_bs_index; + queue = to_PC ? nonpriority_queue_pc : nonpriority_queue_bs; + } + // Write the packet to the correct queue, and move up the queue index by one + if(CircularBuffer_canWrite(index, 1)){ + memcpy(queue[index->indexWrite].data, (uint8_t*) packet, packet_size); + CircularBuffer_write(index, NULL, 1); + }else{ + LOG_printf("[handlePackets]["STRINGIZE(__LINE__)"] Error! Packet type %u : queue is full\n", packet_type); + } } + + // Update the total number of bytes processed, to keep track of where we are in the packets_buffer + bytes_processed += packet_size; } return true; @@ -556,10 +613,12 @@ bool handlePacket(uint8_t* packet_buffer, uint32_t packet_length){ /* Triggers when a call to HAL_SPI_TransmitReceive_DMA or HAL_SPI_TransmitReceive_IT (both non-blocking) completes */ +/* ELI5: triggers when something has been sent to or received over a SPI interface. For the Basestation, this means either +* of the two SX1280 chips */ void HAL_SPI_TxRxCpltCallback(SPI_HandleTypeDef *hspi){ if(hspi->Instance == SX_TX->Interface->SPI->Instance){ Wireless_DMA_Handler(SX_TX); - // SX_TX should never receive a packet so that's why we don't call handlePacket here. + // SX_TX should never receive a packet so that's why we don't call handlePackets here. } if(hspi->Instance == SX_RX->Interface->SPI->Instance) { Wireless_DMA_Handler(SX_RX); @@ -576,14 +635,13 @@ void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { // SX that receives packets wants to tell us something if (GPIO_Pin == SX_RX_IRQ.PIN) { Wireless_IRQ_Handler(SX_RX); - toggle_pin(LD_LED1); + toggle_pin(LD_LED2); } } /* Responsible for sending RobotCommand packets to the corresponding robots */ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ - // TDMA Timer callback, runs at approximately 960Hz /* Every millisecond, a transmission can be made to a single robot. As per the TDMA protocol, the robot has * to respond with any of its own packets within this millisecond. The code loops through all possible robot @@ -594,53 +652,56 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ if(htim->Instance == htim1.Instance){ // Counter that tracks the current robot id that the basestation sends a packet to - static uint8_t idCounter = 0; + static uint8_t robot_id = 0; // Keeps track of the total length of the packet that goes to the robot. - // Cannot exceed MAX_PACKET_SIZE, or it will overflow the internal buffer of the SX1280 + // Cannot exceed REM_MAX_TOTAL_PACKET_SIZE_SX1280, or it will overflow the internal buffer of the SX1280 uint8_t total_packet_length = 0; /* Add RobotCommand to the transmission */ - if(buffer_RobotCommand[idCounter].isNewPacket - && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_COMMAND < MAX_PACKET_SIZE){ - buffer_RobotCommand[idCounter].isNewPacket = false; - memcpy(txPacket.message + total_packet_length, buffer_RobotCommand[idCounter].packet.payload, REM_PACKET_SIZE_REM_ROBOT_COMMAND); + if(buffer_REM_RobotCommand[robot_id].isNewPacket + && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_COMMAND < REM_MAX_TOTAL_PACKET_SIZE_SX1280){ + buffer_REM_RobotCommand[robot_id].isNewPacket = false; + memcpy(txPacket.message + total_packet_length, buffer_REM_RobotCommand[robot_id].packet.payload, REM_PACKET_SIZE_REM_ROBOT_COMMAND); total_packet_length += REM_PACKET_SIZE_REM_ROBOT_COMMAND; + packet_counter_out[REM_PACKET_INDEX_REM_ROBOT_COMMAND]++; } - /* Add RobotSetPIDGains to the transmission */ - if(buffer_RobotSetPIDGains[idCounter].isNewPacket - && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_SET_PIDGAINS < MAX_PACKET_SIZE){ - buffer_RobotSetPIDGains[idCounter].isNewPacket = false; - memcpy(txPacket.message + total_packet_length, buffer_RobotSetPIDGains[idCounter].packet.payload, REM_PACKET_SIZE_REM_ROBOT_SET_PIDGAINS); - total_packet_length += REM_PACKET_SIZE_REM_ROBOT_SET_PIDGAINS; - } + /* Add any other packet from the queue to the transmission */ + CircularBuffer* index = nonpriority_queue_robots_index[robot_id]; + Wrapper_REM_Packet* queue = nonpriority_queue_robots[robot_id]; + + while(true){ + // Check if there is a packet in the queue. If not, break + if(!CircularBuffer_canRead(index, 1)) break; + + // Get packet + REM_PacketPayload* packet = (REM_PacketPayload*) &queue[index->indexRead].data; + // Get type and size of packet + uint8_t packet_type = REM_Packet_get_header(packet); + uint8_t packet_size = REM_Packet_get_payloadSize(packet); + // Check if the packet fits in the transmission. If not, break + if(REM_MAX_TOTAL_PACKET_SIZE_SX1280 < total_packet_length + packet_size) break; + + // Check if the packet is destined for the robot. Should always be the case, but again, just to be sure + if(REM_Packet_get_toBS(packet) || REM_Packet_get_toPC(packet) || REM_Packet_get_toRobotId(packet) != robot_id){ + LOG_printf("[htim1]["STRINGIZE(__LINE__)"] Warning! Packet with type %u is not destined for robot %u", packet_type, robot_id); + } - /* Add RobotBuzzer to the transmission */ - if(buffer_RobotBuzzer[idCounter].isNewPacket - && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_BUZZER < MAX_PACKET_SIZE){ - buffer_RobotBuzzer[idCounter].isNewPacket = false; - memcpy(txPacket.message + total_packet_length, buffer_RobotBuzzer[idCounter].packet.payload, REM_PACKET_SIZE_REM_ROBOT_BUZZER); - total_packet_length += REM_PACKET_SIZE_REM_ROBOT_BUZZER; + // Copy packet to the transmission + CircularBuffer_read(index, NULL, 1); + memcpy(txPacket.message + total_packet_length, (uint8_t*)packet, packet_size); + // Update total packet length + total_packet_length += packet_size; + // Increment packet counter + packet_counter_out[REM_PACKET_TYPE_TO_INDEX(packet_type)]++; } - /* Add RobotGetPIDGains to the transmission */ - if(buffer_RobotGetPIDGains[idCounter].isNewPacket && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_GET_PIDGAINS < MAX_PACKET_SIZE){ - buffer_RobotGetPIDGains[idCounter].isNewPacket = false; - memcpy(txPacket.message + total_packet_length, buffer_RobotGetPIDGains[idCounter].packet.payload, REM_PACKET_SIZE_REM_ROBOT_GET_PIDGAINS); - total_packet_length += REM_PACKET_SIZE_REM_ROBOT_GET_PIDGAINS; - } - - /* Add RobotMusicCommand to the transmission */ - if(buffer_RobotMusicCommand[idCounter].isNewPacket && total_packet_length + REM_PACKET_SIZE_REM_ROBOT_MUSIC_COMMAND < MAX_PACKET_SIZE){ - buffer_RobotMusicCommand[idCounter].isNewPacket = false; - memcpy(txPacket.message + total_packet_length, buffer_RobotMusicCommand[idCounter].packet.payload, REM_PACKET_SIZE_REM_ROBOT_MUSIC_COMMAND); - total_packet_length += REM_PACKET_SIZE_REM_ROBOT_MUSIC_COMMAND; - } /* Send new command if available for this robot ID */ if(0 < total_packet_length){ if(SX_TX->state == WIRELESS_READY){ + /* Add a filler packet to the buffer if there are currently less than 6 bytes in the buffer * The minimum payload size for the SX1280 in FLRC mode is 6 bytes. * See documentation page 124 - Table 14-36: Sync Word Combination in FLRC Packet */ @@ -650,16 +711,17 @@ void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim){ } txPacket.payloadLength = total_packet_length; - Wireless_setTXSyncword(SX_TX,robot_syncWord[idCounter]); + Wireless_setTXSyncword(SX_TX,robot_syncWord[robot_id]); WritePacket_DMA(SX_TX, &txPacket, &Wireless_Writepacket_Cplt); + } } // Schedule next ID to be sent - idCounter++; + robot_id++; // Wrap around if the last ID has been dealt with - if(MAX_ROBOT_ID < idCounter){ - idCounter = 0; + if(MAX_ROBOT_ID < robot_id){ + robot_id = 0; } } } \ No newline at end of file diff --git a/Core/Src/logging.c b/Core/Src/logging.c index 9a2d542..aaa4c2e 100644 --- a/Core/Src/logging.c +++ b/Core/Src/logging.c @@ -1,17 +1,15 @@ #include "logging.h" #include "REM_BaseTypes.h" -#include "REM_BasestationLog.h" +#include "REM_Log.h" #include "CircularBuffer.h" #include #include #include +#include -#include "usbd_cdc.h" -#include "usb_device.h" -#include "usbd_cdc_if.h" -extern USBD_HandleTypeDef hUsbDeviceFS; +#include "usbd_RTT_class.h" // Buffer used by vsprintf. Static to make it private to this file static char printf_buffer[1024]; @@ -27,15 +25,14 @@ static bool log_initialized = false; void LOG_init(){ // UPDATE TO CORRECT USB DEVICES - /* // Can't initialize twice if(log_initialized) return; // Create buffer indexer buffer_indexer = CircularBuffer_init(true, LOG_MAX_MESSAGES); // Wait for USB to be ready for communication - while(!(hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceFS.dev_state == USBD_STATE_SUSPENDED)); + while(!(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceHS.dev_state == USBD_STATE_SUSPENDED)); log_initialized = true; - */ + } void LOG_printf(char *format, ...){ @@ -54,7 +51,7 @@ void LOG_printf(char *format, ...){ /** * TODO Ensure that messages are not "too" long, whatever that may be. * - * @brief Sends a log message over USB using the PACKET_TYPE_BASESTATION_LOG header. + * @brief Sends a log message over USB using the REM_PACKET_TYPE_LOG header. * The log must always end with \n, which this function enforces. * * @param message The message to send over the USB @@ -66,8 +63,8 @@ void LOG(char *message){ // Get message length uint32_t message_length = strlen(message); - // Clip the message length to 127 - PACKET_SIZE_REM_BASESTATION_LOG, as to not overflow the MessageContainer buffer - if(127 - REM_PACKET_SIZE_REM_BASESTATION_LOG < message_length) message_length = 127 - REM_PACKET_SIZE_REM_BASESTATION_LOG; + // Clip the message length to 127 - REM_PACKET_SIZE_REM_LOG, as to not overflow the MessageContainer buffer + if(127 - REM_PACKET_SIZE_REM_LOG < message_length) message_length = 127 - REM_PACKET_SIZE_REM_LOG; // Ensure newline at the end of the message (Can be removed if all software everywhere properly used the BasestationLog_messageLength field) message[message_length-1] = '\n'; @@ -79,52 +76,45 @@ void LOG(char *message){ MessageContainer* message_container = &message_buffer[index_write]; uint8_t* payload = message_container->payload; - REM_BasestationLog_set_header((REM_BasestationLogPayload*) payload, REM_PACKET_TYPE_REM_BASESTATION_LOG); // 8 bits - REM_BasestationLog_set_remVersion((REM_BasestationLogPayload*) payload, REM_LOCAL_VERSION); // 4 bits - // REM_BasestationLog_set_payloadSize((REM_BasestationLogPayload*) payload, message_length); // 8 bits - - // Copy the message into the message container, next to the BasestationLog header - memcpy(payload + REM_PACKET_SIZE_REM_BASESTATION_LOG, message, message_length); - message_container->length = REM_PACKET_SIZE_REM_BASESTATION_LOG + message_length; - + REM_Log_set_header ((REM_LogPayload*) payload, REM_PACKET_TYPE_REM_LOG); + REM_Log_set_remVersion ((REM_LogPayload*) payload, REM_LOCAL_VERSION); + REM_Log_set_payloadSize((REM_LogPayload*) payload, REM_PACKET_SIZE_REM_LOG + message_length); + REM_Log_set_fromBS ((REM_LogPayload*) payload, 1); + REM_Log_set_toPC ((REM_LogPayload*) payload, 1); + // TODO implement REM_Log_set_fromChannel + REM_Log_set_timestamp ((REM_LogPayload*) payload, HAL_GetTick()); + + // Copy the message into the message container, next to the REM_Log header + memcpy(payload + REM_PACKET_SIZE_REM_LOG, message, message_length); + message_container->length = REM_PACKET_SIZE_REM_LOG + message_length; } -static uint8_t buffer[100]; - void LOG_send(){ // UPDATE TO CORRECT USB DEVICES - /* + // Check if the USB is ready for transmitting - if(!(hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceFS.dev_state == USBD_STATE_SUSPENDED)) return; - USBD_CDC_HandleTypeDef* hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData; - if (hcdc->TxState != 0) return; + if(!(hUsbDeviceHS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceHS.dev_state == USBD_STATE_SUSPENDED)) return; + USBD_RTT_HandleTypeDef* hcdc = (USBD_RTT_HandleTypeDef*)hUsbDeviceHS.pClassData; + if (hcdc->INT1TxState != 0) return; // Check if there is something in the buffer if(CircularBuffer_spaceFilled(buffer_indexer) == 0) return; // Write the message over USB - MessageContainer* message_container = &message_buffer[buffer_indexer->indexRead]; - CDC_Transmit_FS(message_container->payload, message_container->length); - - // REM_BasestationLog_set_header((REM_BasestationLogPayload*) buffer, PACKET_TYPE_REM_BASESTATION_LOG); // 8 bits - // REM_BasestationLog_set_remVersion((REM_BasestationLogPayload*) buffer, LOCAL_REM_VERSION); // 4 bits - // REM_BasestationLog_set_messageLength((REM_BasestationLogPayload*) buffer, 5); // 8 bits - // sprintf(buffer+3, "01234\n"); - // CDC_Transmit_FS(buffer, 8); - + // MessageContainer* message_container = &message_buffer[buffer_indexer->indexRead]; + // USB_TransmitHighPriority(message_container->payload, message_container->length); // Move up the circular buffer CircularBuffer_read(buffer_indexer, NULL, 1); - */ + } void LOG_sendBlocking(uint8_t* data, uint8_t length){ - USBD_CDC_HandleTypeDef* hcdc = (USBD_CDC_HandleTypeDef*)hUsbDeviceFS.pClassData; - while(!(hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceFS.dev_state == USBD_STATE_SUSPENDED)); - // if (hUsbDeviceFS.dev_state == USBD_STATE_CONFIGURED || hUsbDeviceFS.dev_state == USBD_STATE_SUSPENDED) { - while(hcdc->TxState != 0); - // TODO unneccesary memcpy? How about CDC_Transmit_FS(data, length);? - // if CDC_Transmit_FS is blocking, no fear to have 'data' go out of scope. - // memcpy(TxBuffer, data, length); - CDC_Transmit_FS(data, length); - // } + // TODO: USB_TransmitLowPriority can return busy or fail, deal with that + // USB_TransmitLowPriority(data, length); +} + +bool LOG_sendBuffer(uint8_t* data, uint32_t length, bool blocking){ + if(!log_initialized) return false; + // return USB_TransmitHighPriority(data, length) == USBD_OK; + return true; } void LOG_sendAll(){ diff --git a/Core/Src/main.c b/Core/Src/main.c index f547cbf..23f6dd4 100644 --- a/Core/Src/main.c +++ b/Core/Src/main.c @@ -19,6 +19,7 @@ /* USER CODE END Header */ /* Includes ------------------------------------------------------------------*/ #include "main.h" +#include "fatfs.h" #include "usb_device.h" /* Private includes ----------------------------------------------------------*/ @@ -43,15 +44,25 @@ /* Private variables ---------------------------------------------------------*/ +IWDG_HandleTypeDef hiwdg; + QSPI_HandleTypeDef hqspi; DMA_HandleTypeDef hdma_quadspi; +SD_HandleTypeDef hsd1; +DMA_HandleTypeDef hdma_sdmmc1_tx; + SPI_HandleTypeDef hspi2; SPI_HandleTypeDef hspi4; +DMA_HandleTypeDef hdma_spi2_tx; +DMA_HandleTypeDef hdma_spi2_rx; +DMA_HandleTypeDef hdma_spi4_rx; +DMA_HandleTypeDef hdma_spi4_tx; TIM_HandleTypeDef htim1; UART_HandleTypeDef huart4; +DMA_HandleTypeDef hdma_uart4_tx; /* USER CODE BEGIN PV */ @@ -61,10 +72,12 @@ UART_HandleTypeDef huart4; void SystemClock_Config(void); static void MX_GPIO_Init(void); static void MX_DMA_Init(void); +static void MX_IWDG_Init(void); static void MX_QUADSPI_Init(void); static void MX_SPI2_Init(void); static void MX_SPI4_Init(void); static void MX_UART4_Init(void); +static void MX_SDMMC1_SD_Init(void); static void MX_TIM1_Init(void); /* USER CODE BEGIN PFP */ @@ -88,9 +101,6 @@ int main(void) /* Enable I-Cache---------------------------------------------------------*/ SCB_EnableICache(); - /* Enable D-Cache---------------------------------------------------------*/ - SCB_EnableDCache(); - /* MCU Configuration--------------------------------------------------------*/ /* Reset of all peripherals, Initializes the Flash interface and the Systick. */ @@ -110,11 +120,14 @@ int main(void) /* Initialize all configured peripherals */ MX_GPIO_Init(); MX_DMA_Init(); + MX_IWDG_Init(); MX_QUADSPI_Init(); MX_USB_DEVICE_Init(); MX_SPI2_Init(); MX_SPI4_Init(); MX_UART4_Init(); + MX_SDMMC1_SD_Init(); + MX_FATFS_Init(); MX_TIM1_Init(); /* USER CODE BEGIN 2 */ init(); @@ -141,33 +154,37 @@ void SystemClock_Config(void) { RCC_OscInitTypeDef RCC_OscInitStruct = {0}; RCC_ClkInitTypeDef RCC_ClkInitStruct = {0}; - RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; /** Configure the main internal regulator output voltage */ __HAL_RCC_PWR_CLK_ENABLE(); __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE1); + /** Initializes the RCC Oscillators according to the specified parameters * in the RCC_OscInitTypeDef structure. */ - RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE; + RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI|RCC_OSCILLATORTYPE_HSE; RCC_OscInitStruct.HSEState = RCC_HSE_BYPASS; + RCC_OscInitStruct.LSIState = RCC_LSI_ON; RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON; RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE; RCC_OscInitStruct.PLL.PLLM = 12; RCC_OscInitStruct.PLL.PLLN = 216; RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV2; RCC_OscInitStruct.PLL.PLLQ = 9; + RCC_OscInitStruct.PLL.PLLR = 2; if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK) { Error_Handler(); } + /** Activate the Over-Drive mode */ if (HAL_PWREx_EnableOverDrive() != HAL_OK) { Error_Handler(); } + /** Initializes the CPU, AHB and APB buses clocks */ RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK @@ -181,12 +198,35 @@ void SystemClock_Config(void) { Error_Handler(); } - PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART4; - PeriphClkInitStruct.Uart4ClockSelection = RCC_UART4CLKSOURCE_PCLK1; - if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) +} + +/** + * @brief IWDG Initialization Function + * @param None + * @retval None + */ +static void MX_IWDG_Init(void) +{ + + /* USER CODE BEGIN IWDG_Init 0 */ + + /* USER CODE END IWDG_Init 0 */ + + /* USER CODE BEGIN IWDG_Init 1 */ + + /* USER CODE END IWDG_Init 1 */ + hiwdg.Instance = IWDG; + hiwdg.Init.Prescaler = IWDG_PRESCALER_4; + hiwdg.Init.Window = 4095; + hiwdg.Init.Reload = 4095; + if (HAL_IWDG_Init(&hiwdg) != HAL_OK) { Error_Handler(); } + /* USER CODE BEGIN IWDG_Init 2 */ + + /* USER CODE END IWDG_Init 2 */ + } /** @@ -224,6 +264,34 @@ static void MX_QUADSPI_Init(void) } +/** + * @brief SDMMC1 Initialization Function + * @param None + * @retval None + */ +static void MX_SDMMC1_SD_Init(void) +{ + + /* USER CODE BEGIN SDMMC1_Init 0 */ + + /* USER CODE END SDMMC1_Init 0 */ + + /* USER CODE BEGIN SDMMC1_Init 1 */ + + /* USER CODE END SDMMC1_Init 1 */ + hsd1.Instance = SDMMC1; + hsd1.Init.ClockEdge = SDMMC_CLOCK_EDGE_RISING; + hsd1.Init.ClockBypass = SDMMC_CLOCK_BYPASS_DISABLE; + hsd1.Init.ClockPowerSave = SDMMC_CLOCK_POWER_SAVE_DISABLE; + hsd1.Init.BusWide = SDMMC_BUS_WIDE_1B; + hsd1.Init.HardwareFlowControl = SDMMC_HARDWARE_FLOW_CONTROL_DISABLE; + hsd1.Init.ClockDiv = 0; + /* USER CODE BEGIN SDMMC1_Init 2 */ + + /* USER CODE END SDMMC1_Init 2 */ + +} + /** * @brief SPI2 Initialization Function * @param None @@ -394,11 +462,30 @@ static void MX_DMA_Init(void) /* DMA controller clock enable */ __HAL_RCC_DMA2_CLK_ENABLE(); + __HAL_RCC_DMA1_CLK_ENABLE(); /* DMA interrupt init */ + /* DMA1_Stream1_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream1_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream1_IRQn); + /* DMA1_Stream4_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream4_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream4_IRQn); + /* DMA1_Stream6_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA1_Stream6_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA1_Stream6_IRQn); + /* DMA2_Stream0_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream0_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream0_IRQn); + /* DMA2_Stream1_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream1_IRQn, 0, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream1_IRQn); /* DMA2_Stream2_IRQn interrupt configuration */ HAL_NVIC_SetPriority(DMA2_Stream2_IRQn, 2, 0); HAL_NVIC_EnableIRQ(DMA2_Stream2_IRQn); + /* DMA2_Stream3_IRQn interrupt configuration */ + HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 2, 0); + HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn); } @@ -550,5 +637,3 @@ void assert_failed(uint8_t *file, uint32_t line) /* USER CODE END 6 */ } #endif /* USE_FULL_ASSERT */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/stm32f7xx_hal_msp.c b/Core/Src/stm32f7xx_hal_msp.c index 3644862..5db83cd 100644 --- a/Core/Src/stm32f7xx_hal_msp.c +++ b/Core/Src/stm32f7xx_hal_msp.c @@ -26,6 +26,18 @@ /* USER CODE END Includes */ extern DMA_HandleTypeDef hdma_quadspi; +extern DMA_HandleTypeDef hdma_sdmmc1_tx; + +extern DMA_HandleTypeDef hdma_spi2_tx; + +extern DMA_HandleTypeDef hdma_spi2_rx; + +extern DMA_HandleTypeDef hdma_spi4_rx; + +extern DMA_HandleTypeDef hdma_spi4_tx; + +extern DMA_HandleTypeDef hdma_uart4_tx; + /* Private typedef -----------------------------------------------------------*/ /* USER CODE BEGIN TD */ @@ -199,6 +211,133 @@ void HAL_QSPI_MspDeInit(QSPI_HandleTypeDef* hqspi) } +/** +* @brief SD MSP Initialization +* This function configures the hardware resources used in this example +* @param hsd: SD handle pointer +* @retval None +*/ +void HAL_SD_MspInit(SD_HandleTypeDef* hsd) +{ + GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; + if(hsd->Instance==SDMMC1) + { + /* USER CODE BEGIN SDMMC1_MspInit 0 */ + + /* USER CODE END SDMMC1_MspInit 0 */ + + /** Initializes the peripherals clock + */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_SDMMC1|RCC_PERIPHCLK_CLK48; + PeriphClkInitStruct.Clk48ClockSelection = RCC_CLK48SOURCE_PLL; + PeriphClkInitStruct.Sdmmc1ClockSelection = RCC_SDMMC1CLKSOURCE_CLK48; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } + + /* Peripheral clock enable */ + __HAL_RCC_SDMMC1_CLK_ENABLE(); + + __HAL_RCC_GPIOC_CLK_ENABLE(); + __HAL_RCC_GPIOD_CLK_ENABLE(); + /**SDMMC1 GPIO Configuration + PC8 ------> SDMMC1_D0 + PC9 ------> SDMMC1_D1 + PC10 ------> SDMMC1_D2 + PC11 ------> SDMMC1_D3 + PC12 ------> SDMMC1_CK + PD2 ------> SDMMC1_CMD + */ + GPIO_InitStruct.Pin = GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 + |GPIO_PIN_12; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1; + HAL_GPIO_Init(GPIOC, &GPIO_InitStruct); + + GPIO_InitStruct.Pin = GPIO_PIN_2; + GPIO_InitStruct.Mode = GPIO_MODE_AF_PP; + GPIO_InitStruct.Pull = GPIO_NOPULL; + GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_VERY_HIGH; + GPIO_InitStruct.Alternate = GPIO_AF12_SDMMC1; + HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + + /* SDMMC1 DMA Init */ + /* SDMMC1_TX Init */ + hdma_sdmmc1_tx.Instance = DMA2_Stream3; + hdma_sdmmc1_tx.Init.Channel = DMA_CHANNEL_4; + hdma_sdmmc1_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_sdmmc1_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_sdmmc1_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_sdmmc1_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_WORD; + hdma_sdmmc1_tx.Init.MemDataAlignment = DMA_MDATAALIGN_WORD; + hdma_sdmmc1_tx.Init.Mode = DMA_PFCTRL; + hdma_sdmmc1_tx.Init.Priority = DMA_PRIORITY_LOW; + hdma_sdmmc1_tx.Init.FIFOMode = DMA_FIFOMODE_ENABLE; + hdma_sdmmc1_tx.Init.FIFOThreshold = DMA_FIFO_THRESHOLD_FULL; + hdma_sdmmc1_tx.Init.MemBurst = DMA_MBURST_INC4; + hdma_sdmmc1_tx.Init.PeriphBurst = DMA_PBURST_INC4; + if (HAL_DMA_Init(&hdma_sdmmc1_tx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hsd,hdmatx,hdma_sdmmc1_tx); + + /* SDMMC1 interrupt Init */ + HAL_NVIC_SetPriority(SDMMC1_IRQn, 3, 0); + HAL_NVIC_EnableIRQ(SDMMC1_IRQn); + /* USER CODE BEGIN SDMMC1_MspInit 1 */ + + /* USER CODE END SDMMC1_MspInit 1 */ + } + +} + +/** +* @brief SD MSP De-Initialization +* This function freeze the hardware resources used in this example +* @param hsd: SD handle pointer +* @retval None +*/ +void HAL_SD_MspDeInit(SD_HandleTypeDef* hsd) +{ + if(hsd->Instance==SDMMC1) + { + /* USER CODE BEGIN SDMMC1_MspDeInit 0 */ + + /* USER CODE END SDMMC1_MspDeInit 0 */ + /* Peripheral clock disable */ + __HAL_RCC_SDMMC1_CLK_DISABLE(); + + /**SDMMC1 GPIO Configuration + PC8 ------> SDMMC1_D0 + PC9 ------> SDMMC1_D1 + PC10 ------> SDMMC1_D2 + PC11 ------> SDMMC1_D3 + PC12 ------> SDMMC1_CK + PD2 ------> SDMMC1_CMD + */ + HAL_GPIO_DeInit(GPIOC, GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11 + |GPIO_PIN_12); + + HAL_GPIO_DeInit(GPIOD, GPIO_PIN_2); + + /* SDMMC1 DMA DeInit */ + HAL_DMA_DeInit(hsd->hdmatx); + + /* SDMMC1 interrupt DeInit */ + HAL_NVIC_DisableIRQ(SDMMC1_IRQn); + /* USER CODE BEGIN SDMMC1_MspDeInit 1 */ + + /* USER CODE END SDMMC1_MspDeInit 1 */ + } + +} + /** * @brief SPI MSP Initialization * This function configures the hardware resources used in this example @@ -237,6 +376,43 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) GPIO_InitStruct.Alternate = GPIO_AF5_SPI2; HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); + /* SPI2 DMA Init */ + /* SPI2_TX Init */ + hdma_spi2_tx.Instance = DMA1_Stream6; + hdma_spi2_tx.Init.Channel = DMA_CHANNEL_9; + hdma_spi2_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_spi2_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_spi2_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_spi2_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_spi2_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_spi2_tx.Init.Mode = DMA_NORMAL; + hdma_spi2_tx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_spi2_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_spi2_tx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hspi,hdmatx,hdma_spi2_tx); + + /* SPI2_RX Init */ + hdma_spi2_rx.Instance = DMA1_Stream1; + hdma_spi2_rx.Init.Channel = DMA_CHANNEL_9; + hdma_spi2_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_spi2_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_spi2_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_spi2_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_spi2_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_spi2_rx.Init.Mode = DMA_NORMAL; + hdma_spi2_rx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_spi2_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_spi2_rx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hspi,hdmarx,hdma_spi2_rx); + /* SPI2 interrupt Init */ HAL_NVIC_SetPriority(SPI2_IRQn, 1, 0); HAL_NVIC_EnableIRQ(SPI2_IRQn); @@ -265,6 +441,43 @@ void HAL_SPI_MspInit(SPI_HandleTypeDef* hspi) GPIO_InitStruct.Alternate = GPIO_AF5_SPI4; HAL_GPIO_Init(GPIOE, &GPIO_InitStruct); + /* SPI4 DMA Init */ + /* SPI4_RX Init */ + hdma_spi4_rx.Instance = DMA2_Stream0; + hdma_spi4_rx.Init.Channel = DMA_CHANNEL_4; + hdma_spi4_rx.Init.Direction = DMA_PERIPH_TO_MEMORY; + hdma_spi4_rx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_spi4_rx.Init.MemInc = DMA_MINC_ENABLE; + hdma_spi4_rx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_spi4_rx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_spi4_rx.Init.Mode = DMA_NORMAL; + hdma_spi4_rx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_spi4_rx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_spi4_rx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hspi,hdmarx,hdma_spi4_rx); + + /* SPI4_TX Init */ + hdma_spi4_tx.Instance = DMA2_Stream1; + hdma_spi4_tx.Init.Channel = DMA_CHANNEL_4; + hdma_spi4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_spi4_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_spi4_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_spi4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_spi4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_spi4_tx.Init.Mode = DMA_NORMAL; + hdma_spi4_tx.Init.Priority = DMA_PRIORITY_HIGH; + hdma_spi4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_spi4_tx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(hspi,hdmatx,hdma_spi4_tx); + /* SPI4 interrupt Init */ HAL_NVIC_SetPriority(SPI4_IRQn, 1, 0); HAL_NVIC_EnableIRQ(SPI4_IRQn); @@ -300,6 +513,10 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) HAL_GPIO_DeInit(GPIOA, GPIO_PIN_9); + /* SPI2 DMA DeInit */ + HAL_DMA_DeInit(hspi->hdmatx); + HAL_DMA_DeInit(hspi->hdmarx); + /* SPI2 interrupt DeInit */ HAL_NVIC_DisableIRQ(SPI2_IRQn); /* USER CODE BEGIN SPI2_MspDeInit 1 */ @@ -321,6 +538,10 @@ void HAL_SPI_MspDeInit(SPI_HandleTypeDef* hspi) */ HAL_GPIO_DeInit(GPIOE, GPIO_PIN_2|GPIO_PIN_5|GPIO_PIN_6); + /* SPI4 DMA DeInit */ + HAL_DMA_DeInit(hspi->hdmarx); + HAL_DMA_DeInit(hspi->hdmatx); + /* SPI4 interrupt DeInit */ HAL_NVIC_DisableIRQ(SPI4_IRQn); /* USER CODE BEGIN SPI4_MspDeInit 1 */ @@ -389,11 +610,22 @@ void HAL_TIM_Base_MspDeInit(TIM_HandleTypeDef* htim_base) void HAL_UART_MspInit(UART_HandleTypeDef* huart) { GPIO_InitTypeDef GPIO_InitStruct = {0}; + RCC_PeriphCLKInitTypeDef PeriphClkInitStruct = {0}; if(huart->Instance==UART4) { /* USER CODE BEGIN UART4_MspInit 0 */ /* USER CODE END UART4_MspInit 0 */ + + /** Initializes the peripherals clock + */ + PeriphClkInitStruct.PeriphClockSelection = RCC_PERIPHCLK_UART4; + PeriphClkInitStruct.Uart4ClockSelection = RCC_UART4CLKSOURCE_PCLK1; + if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInitStruct) != HAL_OK) + { + Error_Handler(); + } + /* Peripheral clock enable */ __HAL_RCC_UART4_CLK_ENABLE(); @@ -409,6 +641,25 @@ void HAL_UART_MspInit(UART_HandleTypeDef* huart) GPIO_InitStruct.Alternate = GPIO_AF8_UART4; HAL_GPIO_Init(GPIOD, &GPIO_InitStruct); + /* UART4 DMA Init */ + /* UART4_TX Init */ + hdma_uart4_tx.Instance = DMA1_Stream4; + hdma_uart4_tx.Init.Channel = DMA_CHANNEL_4; + hdma_uart4_tx.Init.Direction = DMA_MEMORY_TO_PERIPH; + hdma_uart4_tx.Init.PeriphInc = DMA_PINC_DISABLE; + hdma_uart4_tx.Init.MemInc = DMA_MINC_ENABLE; + hdma_uart4_tx.Init.PeriphDataAlignment = DMA_PDATAALIGN_BYTE; + hdma_uart4_tx.Init.MemDataAlignment = DMA_MDATAALIGN_BYTE; + hdma_uart4_tx.Init.Mode = DMA_NORMAL; + hdma_uart4_tx.Init.Priority = DMA_PRIORITY_LOW; + hdma_uart4_tx.Init.FIFOMode = DMA_FIFOMODE_DISABLE; + if (HAL_DMA_Init(&hdma_uart4_tx) != HAL_OK) + { + Error_Handler(); + } + + __HAL_LINKDMA(huart,hdmatx,hdma_uart4_tx); + /* USER CODE BEGIN UART4_MspInit 1 */ /* USER CODE END UART4_MspInit 1 */ @@ -438,6 +689,8 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* huart) */ HAL_GPIO_DeInit(GPIOD, GPIO_PIN_0|GPIO_PIN_1); + /* UART4 DMA DeInit */ + HAL_DMA_DeInit(huart->hdmatx); /* USER CODE BEGIN UART4_MspDeInit 1 */ /* USER CODE END UART4_MspDeInit 1 */ @@ -448,5 +701,3 @@ void HAL_UART_MspDeInit(UART_HandleTypeDef* huart) /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/stm32f7xx_it.c b/Core/Src/stm32f7xx_it.c index e989749..7ff8813 100644 --- a/Core/Src/stm32f7xx_it.c +++ b/Core/Src/stm32f7xx_it.c @@ -59,9 +59,16 @@ extern PCD_HandleTypeDef hpcd_USB_OTG_HS; extern DMA_HandleTypeDef hdma_quadspi; extern QSPI_HandleTypeDef hqspi; +extern DMA_HandleTypeDef hdma_sdmmc1_tx; +extern SD_HandleTypeDef hsd1; +extern DMA_HandleTypeDef hdma_spi2_tx; +extern DMA_HandleTypeDef hdma_spi2_rx; +extern DMA_HandleTypeDef hdma_spi4_rx; +extern DMA_HandleTypeDef hdma_spi4_tx; extern SPI_HandleTypeDef hspi2; extern SPI_HandleTypeDef hspi4; extern TIM_HandleTypeDef htim1; +extern DMA_HandleTypeDef hdma_uart4_tx; /* USER CODE BEGIN EV */ /* USER CODE END EV */ @@ -210,7 +217,7 @@ void EXTI3_IRQHandler(void) /* USER CODE BEGIN EXTI3_IRQn 0 */ /* USER CODE END EXTI3_IRQn 0 */ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_3); + HAL_GPIO_EXTI_IRQHandler(SX_RX_IRQ_Pin); /* USER CODE BEGIN EXTI3_IRQn 1 */ /* USER CODE END EXTI3_IRQn 1 */ @@ -224,12 +231,54 @@ void EXTI4_IRQHandler(void) /* USER CODE BEGIN EXTI4_IRQn 0 */ /* USER CODE END EXTI4_IRQn 0 */ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_4); + HAL_GPIO_EXTI_IRQHandler(QUADSPI_IRQ_Pin); /* USER CODE BEGIN EXTI4_IRQn 1 */ /* USER CODE END EXTI4_IRQn 1 */ } +/** + * @brief This function handles DMA1 stream1 global interrupt. + */ +void DMA1_Stream1_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream1_IRQn 0 */ + + /* USER CODE END DMA1_Stream1_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi2_rx); + /* USER CODE BEGIN DMA1_Stream1_IRQn 1 */ + + /* USER CODE END DMA1_Stream1_IRQn 1 */ +} + +/** + * @brief This function handles DMA1 stream4 global interrupt. + */ +void DMA1_Stream4_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream4_IRQn 0 */ + + /* USER CODE END DMA1_Stream4_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_uart4_tx); + /* USER CODE BEGIN DMA1_Stream4_IRQn 1 */ + + /* USER CODE END DMA1_Stream4_IRQn 1 */ +} + +/** + * @brief This function handles DMA1 stream6 global interrupt. + */ +void DMA1_Stream6_IRQHandler(void) +{ + /* USER CODE BEGIN DMA1_Stream6_IRQn 0 */ + + /* USER CODE END DMA1_Stream6_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi2_tx); + /* USER CODE BEGIN DMA1_Stream6_IRQn 1 */ + + /* USER CODE END DMA1_Stream6_IRQn 1 */ +} + /** * @brief This function handles TIM1 update interrupt and TIM10 global interrupt. */ @@ -266,12 +315,54 @@ void EXTI15_10_IRQHandler(void) /* USER CODE BEGIN EXTI15_10_IRQn 0 */ /* USER CODE END EXTI15_10_IRQn 0 */ - HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_12); + HAL_GPIO_EXTI_IRQHandler(SX_TX_IRQ_Pin); /* USER CODE BEGIN EXTI15_10_IRQn 1 */ /* USER CODE END EXTI15_10_IRQn 1 */ } +/** + * @brief This function handles SDMMC1 global interrupt. + */ +void SDMMC1_IRQHandler(void) +{ + /* USER CODE BEGIN SDMMC1_IRQn 0 */ + + /* USER CODE END SDMMC1_IRQn 0 */ + HAL_SD_IRQHandler(&hsd1); + /* USER CODE BEGIN SDMMC1_IRQn 1 */ + + /* USER CODE END SDMMC1_IRQn 1 */ +} + +/** + * @brief This function handles DMA2 stream0 global interrupt. + */ +void DMA2_Stream0_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Stream0_IRQn 0 */ + + /* USER CODE END DMA2_Stream0_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi4_rx); + /* USER CODE BEGIN DMA2_Stream0_IRQn 1 */ + + /* USER CODE END DMA2_Stream0_IRQn 1 */ +} + +/** + * @brief This function handles DMA2 stream1 global interrupt. + */ +void DMA2_Stream1_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Stream1_IRQn 0 */ + + /* USER CODE END DMA2_Stream1_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_spi4_tx); + /* USER CODE BEGIN DMA2_Stream1_IRQn 1 */ + + /* USER CODE END DMA2_Stream1_IRQn 1 */ +} + /** * @brief This function handles DMA2 stream2 global interrupt. */ @@ -286,6 +377,20 @@ void DMA2_Stream2_IRQHandler(void) /* USER CODE END DMA2_Stream2_IRQn 1 */ } +/** + * @brief This function handles DMA2 stream3 global interrupt. + */ +void DMA2_Stream3_IRQHandler(void) +{ + /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */ + + /* USER CODE END DMA2_Stream3_IRQn 0 */ + HAL_DMA_IRQHandler(&hdma_sdmmc1_tx); + /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */ + + /* USER CODE END DMA2_Stream3_IRQn 1 */ +} + /** * @brief This function handles USB On The Go HS global interrupt. */ @@ -331,4 +436,3 @@ void QUADSPI_IRQHandler(void) /* USER CODE BEGIN 1 */ /* USER CODE END 1 */ -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ diff --git a/Core/Src/syscalls.c b/Core/Src/syscalls.c index 914f768..fadb992 100644 --- a/Core/Src/syscalls.c +++ b/Core/Src/syscalls.c @@ -1,207 +1,155 @@ -/** -***************************************************************************** -** -** File : syscalls.c -** -** Author : Auto-generated by System workbench for STM32 -** -** Abstract : System Workbench Minimal System calls file -** -** For more information about which c-functions -** need which of these lowlevel functions -** please consult the Newlib libc-manual -** -** Target : STMicroelectronics STM32 -** -** Distribution: The file is distributed “as is,” without any warranty -** of any kind. -** -***************************************************************************** -** @attention -** -**

© COPYRIGHT(c) 2019 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. -** -***************************************************************************** -*/ - -/* Includes */ -#include -#include -#include -#include -#include -#include -#include -#include - - -/* Variables */ -//#undef errno -extern int errno; -extern int __io_putchar(int ch) __attribute__((weak)); -extern int __io_getchar(void) __attribute__((weak)); - -register char * stack_ptr asm("sp"); - -char *__env[1] = { 0 }; -char **environ = __env; - - -/* Functions */ -void initialise_monitor_handles() -{ -} - -int _getpid(void) -{ - return 1; -} - -int _kill(int pid, int sig) -{ - errno = EINVAL; - return -1; -} - -void _exit (int status) -{ - _kill(status, -1); - while (1) {} /* Make sure we hang here */ -} - -__attribute__((weak)) int _read(int file, char *ptr, int len) -{ - int DataIdx; - - for (DataIdx = 0; DataIdx < len; DataIdx++) - { - *ptr++ = __io_getchar(); - } - -return len; -} - -__attribute__((weak)) int _write(int file, char *ptr, int len) -{ - int DataIdx; - - for (DataIdx = 0; DataIdx < len; DataIdx++) - { - __io_putchar(*ptr++); - } - return len; -} - -caddr_t _sbrk(int incr) -{ - extern char end asm("end"); - static char *heap_end; - char *prev_heap_end; - - if (heap_end == 0) - heap_end = &end; - - prev_heap_end = heap_end; - if (heap_end + incr > stack_ptr) - { -// write(1, "Heap and stack collision\n", 25); -// abort(); - errno = ENOMEM; - return (caddr_t) -1; - } - - heap_end += incr; - - return (caddr_t) prev_heap_end; -} - -int _close(int file) -{ - return -1; -} - - -int _fstat(int file, struct stat *st) -{ - st->st_mode = S_IFCHR; - return 0; -} - -int _isatty(int file) -{ - return 1; -} - -int _lseek(int file, int ptr, int dir) -{ - return 0; -} - -int _open(char *path, int flags, ...) -{ - /* Pretend like we always fail */ - return -1; -} - -int _wait(int *status) -{ - errno = ECHILD; - return -1; -} - -int _unlink(char *name) -{ - errno = ENOENT; - return -1; -} - -int _times(struct tms *buf) -{ - return -1; -} - -int _stat(char *file, struct stat *st) -{ - st->st_mode = S_IFCHR; - return 0; -} - -int _link(char *old, char *new) -{ - errno = EMLINK; - return -1; -} - -int _fork(void) -{ - errno = EAGAIN; - return -1; -} - -int _execve(char *name, char **argv, char **env) -{ - errno = ENOMEM; - return -1; -} +/** + ****************************************************************************** + * @file syscalls.c + * @author Auto-generated by STM32CubeIDE + * @brief STM32CubeIDE Minimal System calls file + * + * For more information about which c-functions + * need which of these lowlevel functions + * please consult the Newlib libc-manual + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes */ +#include +#include +#include +#include +#include +#include +#include +#include + + +/* Variables */ +extern int __io_putchar(int ch) __attribute__((weak)); +extern int __io_getchar(void) __attribute__((weak)); + + +char *__env[1] = { 0 }; +char **environ = __env; + + +/* Functions */ +void initialise_monitor_handles() +{ +} + +int _getpid(void) +{ + return 1; +} + +int _kill(int pid, int sig) +{ + errno = EINVAL; + return -1; +} + +void _exit (int status) +{ + _kill(status, -1); + while (1) {} /* Make sure we hang here */ +} + +__attribute__((weak)) int _read(int file, char *ptr, int len) +{ + int DataIdx; + + for (DataIdx = 0; DataIdx < len; DataIdx++) + { + *ptr++ = __io_getchar(); + } + +return len; +} + +__attribute__((weak)) int _write(int file, char *ptr, int len) +{ + int DataIdx; + + for (DataIdx = 0; DataIdx < len; DataIdx++) + { + __io_putchar(*ptr++); + } + return len; +} + +int _close(int file) +{ + return -1; +} + + +int _fstat(int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) +{ + return 1; +} + +int _lseek(int file, int ptr, int dir) +{ + return 0; +} + +int _open(char *path, int flags, ...) +{ + /* Pretend like we always fail */ + return -1; +} + +int _wait(int *status) +{ + errno = ECHILD; + return -1; +} + +int _unlink(char *name) +{ + errno = ENOENT; + return -1; +} + +int _times(struct tms *buf) +{ + return -1; +} + +int _stat(char *file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; +} + +int _link(char *old, char *new) +{ + errno = EMLINK; + return -1; +} + +int _fork(void) +{ + errno = EAGAIN; + return -1; +} + +int _execve(char *name, char **argv, char **env) +{ + errno = ENOMEM; + return -1; +} diff --git a/Core/Src/sysmem.c b/Core/Src/sysmem.c new file mode 100644 index 0000000..54081ac --- /dev/null +++ b/Core/Src/sysmem.c @@ -0,0 +1,79 @@ +/** + ****************************************************************************** + * @file sysmem.c + * @author Generated by STM32CubeIDE + * @brief STM32CubeIDE System Memory calls file + * + * For more information about which C functions + * need which of these lowlevel functions + * please consult the newlib libc manual + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/* Includes */ +#include +#include + +/** + * Pointer to the current high watermark of the heap usage + */ +static uint8_t *__sbrk_heap_end = NULL; + +/** + * @brief _sbrk() allocates memory to the newlib heap and is used by malloc + * and others from the C library + * + * @verbatim + * ############################################################################ + * # .data # .bss # newlib heap # MSP stack # + * # # # # Reserved by _Min_Stack_Size # + * ############################################################################ + * ^-- RAM start ^-- _end _estack, RAM end --^ + * @endverbatim + * + * This implementation starts allocating at the '_end' linker symbol + * The '_Min_Stack_Size' linker symbol reserves a memory for the MSP stack + * The implementation considers '_estack' linker symbol to be RAM end + * NOTE: If the MSP stack, at any point during execution, grows larger than the + * reserved size, please increase the '_Min_Stack_Size'. + * + * @param incr Memory size + * @return Pointer to allocated memory + */ +void *_sbrk(ptrdiff_t incr) +{ + extern uint8_t _end; /* Symbol defined in the linker script */ + extern uint8_t _estack; /* Symbol defined in the linker script */ + extern uint32_t _Min_Stack_Size; /* Symbol defined in the linker script */ + const uint32_t stack_limit = (uint32_t)&_estack - (uint32_t)&_Min_Stack_Size; + const uint8_t *max_heap = (uint8_t *)stack_limit; + uint8_t *prev_heap_end; + + /* Initialize heap end at first call */ + if (NULL == __sbrk_heap_end) + { + __sbrk_heap_end = &_end; + } + + /* Protect heap from growing into the reserved MSP stack */ + if (__sbrk_heap_end + incr > max_heap) + { + errno = ENOMEM; + return (void *)-1; + } + + prev_heap_end = __sbrk_heap_end; + __sbrk_heap_end += incr; + + return (void *)prev_heap_end; +} diff --git a/Core/Startup/startup_stm32f767zitx.s b/Core/Startup/startup_stm32f767zitx.s new file mode 100644 index 0000000..cd7170c --- /dev/null +++ b/Core/Startup/startup_stm32f767zitx.s @@ -0,0 +1,620 @@ +/** + ****************************************************************************** + * @file startup_stm32f767xx.s + * @author MCD Application Team + * @brief STM32F767xx Devices vector table for GCC based toolchain. + * This module performs: + * - Set the initial SP + * - Set the initial PC == Reset_Handler, + * - Set the vector table entries with the exceptions ISR address + * - Branches to main in the C library (which eventually + * calls main()). + * After Reset the Cortex-M7 processor is in Thread mode, + * priority is Privileged, and the Stack is set to Main. + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + + .syntax unified + .cpu cortex-m7 + .fpu softvfp + .thumb + +.global g_pfnVectors +.global Default_Handler + +/* start address for the initialization values of the .data section. +defined in linker script */ +.word _sidata +/* start address for the .data section. defined in linker script */ +.word _sdata +/* end address for the .data section. defined in linker script */ +.word _edata +/* start address for the .bss section. defined in linker script */ +.word _sbss +/* end address for the .bss section. defined in linker script */ +.word _ebss +/* stack used for SystemInit_ExtMemCtl; always internal RAM used */ + +/** + * @brief This is the code that gets called when the processor first + * starts execution following a reset event. Only the absolutely + * necessary set is performed, after which the application + * supplied main() routine is called. + * @param None + * @retval : None +*/ + + .section .text.Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + ldr sp, =_estack /* set stack pointer */ + +/* Copy the data segment initializers from flash to SRAM */ + ldr r0, =_sdata + ldr r1, =_edata + ldr r2, =_sidata + movs r3, #0 + b LoopCopyDataInit + +CopyDataInit: + ldr r4, [r2, r3] + str r4, [r0, r3] + adds r3, r3, #4 + +LoopCopyDataInit: + adds r4, r0, r3 + cmp r4, r1 + bcc CopyDataInit + +/* Zero fill the bss segment. */ + ldr r2, =_sbss + ldr r4, =_ebss + movs r3, #0 + b LoopFillZerobss + +FillZerobss: + str r3, [r2] + adds r2, r2, #4 + +LoopFillZerobss: + cmp r2, r4 + bcc FillZerobss + +/* Call the clock system initialization function.*/ + bl SystemInit +/* Call static constructors */ + bl __libc_init_array +/* Call the application's entry point.*/ + bl main + bx lr +.size Reset_Handler, .-Reset_Handler + +/** + * @brief This is the code that gets called when the processor receives an + * unexpected interrupt. This simply enters an infinite loop, preserving + * the system state for examination by a debugger. + * @param None + * @retval None +*/ + .section .text.Default_Handler,"ax",%progbits +Default_Handler: +Infinite_Loop: + b Infinite_Loop + .size Default_Handler, .-Default_Handler +/****************************************************************************** +* +* The minimal vector table for a Cortex M7. Note that the proper constructs +* must be placed on this to ensure that it ends up at physical address +* 0x0000.0000. +* +*******************************************************************************/ + .section .isr_vector,"a",%progbits + .type g_pfnVectors, %object + .size g_pfnVectors, .-g_pfnVectors + + +g_pfnVectors: + .word _estack + .word Reset_Handler + + .word NMI_Handler + .word HardFault_Handler + .word MemManage_Handler + .word BusFault_Handler + .word UsageFault_Handler + .word 0 + .word 0 + .word 0 + .word 0 + .word SVC_Handler + .word DebugMon_Handler + .word 0 + .word PendSV_Handler + .word SysTick_Handler + + /* External Interrupts */ + .word WWDG_IRQHandler /* Window WatchDog */ + .word PVD_IRQHandler /* PVD through EXTI Line detection */ + .word TAMP_STAMP_IRQHandler /* Tamper and TimeStamps through the EXTI line */ + .word RTC_WKUP_IRQHandler /* RTC Wakeup through the EXTI line */ + .word FLASH_IRQHandler /* FLASH */ + .word RCC_IRQHandler /* RCC */ + .word EXTI0_IRQHandler /* EXTI Line0 */ + .word EXTI1_IRQHandler /* EXTI Line1 */ + .word EXTI2_IRQHandler /* EXTI Line2 */ + .word EXTI3_IRQHandler /* EXTI Line3 */ + .word EXTI4_IRQHandler /* EXTI Line4 */ + .word DMA1_Stream0_IRQHandler /* DMA1 Stream 0 */ + .word DMA1_Stream1_IRQHandler /* DMA1 Stream 1 */ + .word DMA1_Stream2_IRQHandler /* DMA1 Stream 2 */ + .word DMA1_Stream3_IRQHandler /* DMA1 Stream 3 */ + .word DMA1_Stream4_IRQHandler /* DMA1 Stream 4 */ + .word DMA1_Stream5_IRQHandler /* DMA1 Stream 5 */ + .word DMA1_Stream6_IRQHandler /* DMA1 Stream 6 */ + .word ADC_IRQHandler /* ADC1, ADC2 and ADC3s */ + .word CAN1_TX_IRQHandler /* CAN1 TX */ + .word CAN1_RX0_IRQHandler /* CAN1 RX0 */ + .word CAN1_RX1_IRQHandler /* CAN1 RX1 */ + .word CAN1_SCE_IRQHandler /* CAN1 SCE */ + .word EXTI9_5_IRQHandler /* External Line[9:5]s */ + .word TIM1_BRK_TIM9_IRQHandler /* TIM1 Break and TIM9 */ + .word TIM1_UP_TIM10_IRQHandler /* TIM1 Update and TIM10 */ + .word TIM1_TRG_COM_TIM11_IRQHandler /* TIM1 Trigger and Commutation and TIM11 */ + .word TIM1_CC_IRQHandler /* TIM1 Capture Compare */ + .word TIM2_IRQHandler /* TIM2 */ + .word TIM3_IRQHandler /* TIM3 */ + .word TIM4_IRQHandler /* TIM4 */ + .word I2C1_EV_IRQHandler /* I2C1 Event */ + .word I2C1_ER_IRQHandler /* I2C1 Error */ + .word I2C2_EV_IRQHandler /* I2C2 Event */ + .word I2C2_ER_IRQHandler /* I2C2 Error */ + .word SPI1_IRQHandler /* SPI1 */ + .word SPI2_IRQHandler /* SPI2 */ + .word USART1_IRQHandler /* USART1 */ + .word USART2_IRQHandler /* USART2 */ + .word USART3_IRQHandler /* USART3 */ + .word EXTI15_10_IRQHandler /* External Line[15:10]s */ + .word RTC_Alarm_IRQHandler /* RTC Alarm (A and B) through EXTI Line */ + .word OTG_FS_WKUP_IRQHandler /* USB OTG FS Wakeup through EXTI line */ + .word TIM8_BRK_TIM12_IRQHandler /* TIM8 Break and TIM12 */ + .word TIM8_UP_TIM13_IRQHandler /* TIM8 Update and TIM13 */ + .word TIM8_TRG_COM_TIM14_IRQHandler /* TIM8 Trigger and Commutation and TIM14 */ + .word TIM8_CC_IRQHandler /* TIM8 Capture Compare */ + .word DMA1_Stream7_IRQHandler /* DMA1 Stream7 */ + .word FMC_IRQHandler /* FMC */ + .word SDMMC1_IRQHandler /* SDMMC1 */ + .word TIM5_IRQHandler /* TIM5 */ + .word SPI3_IRQHandler /* SPI3 */ + .word UART4_IRQHandler /* UART4 */ + .word UART5_IRQHandler /* UART5 */ + .word TIM6_DAC_IRQHandler /* TIM6 and DAC1&2 underrun errors */ + .word TIM7_IRQHandler /* TIM7 */ + .word DMA2_Stream0_IRQHandler /* DMA2 Stream 0 */ + .word DMA2_Stream1_IRQHandler /* DMA2 Stream 1 */ + .word DMA2_Stream2_IRQHandler /* DMA2 Stream 2 */ + .word DMA2_Stream3_IRQHandler /* DMA2 Stream 3 */ + .word DMA2_Stream4_IRQHandler /* DMA2 Stream 4 */ + .word ETH_IRQHandler /* Ethernet */ + .word ETH_WKUP_IRQHandler /* Ethernet Wakeup through EXTI line */ + .word CAN2_TX_IRQHandler /* CAN2 TX */ + .word CAN2_RX0_IRQHandler /* CAN2 RX0 */ + .word CAN2_RX1_IRQHandler /* CAN2 RX1 */ + .word CAN2_SCE_IRQHandler /* CAN2 SCE */ + .word OTG_FS_IRQHandler /* USB OTG FS */ + .word DMA2_Stream5_IRQHandler /* DMA2 Stream 5 */ + .word DMA2_Stream6_IRQHandler /* DMA2 Stream 6 */ + .word DMA2_Stream7_IRQHandler /* DMA2 Stream 7 */ + .word USART6_IRQHandler /* USART6 */ + .word I2C3_EV_IRQHandler /* I2C3 event */ + .word I2C3_ER_IRQHandler /* I2C3 error */ + .word OTG_HS_EP1_OUT_IRQHandler /* USB OTG HS End Point 1 Out */ + .word OTG_HS_EP1_IN_IRQHandler /* USB OTG HS End Point 1 In */ + .word OTG_HS_WKUP_IRQHandler /* USB OTG HS Wakeup through EXTI */ + .word OTG_HS_IRQHandler /* USB OTG HS */ + .word DCMI_IRQHandler /* DCMI */ + .word 0 /* Reserved */ + .word RNG_IRQHandler /* RNG */ + .word FPU_IRQHandler /* FPU */ + .word UART7_IRQHandler /* UART7 */ + .word UART8_IRQHandler /* UART8 */ + .word SPI4_IRQHandler /* SPI4 */ + .word SPI5_IRQHandler /* SPI5 */ + .word SPI6_IRQHandler /* SPI6 */ + .word SAI1_IRQHandler /* SAI1 */ + .word LTDC_IRQHandler /* LTDC */ + .word LTDC_ER_IRQHandler /* LTDC error */ + .word DMA2D_IRQHandler /* DMA2D */ + .word SAI2_IRQHandler /* SAI2 */ + .word QUADSPI_IRQHandler /* QUADSPI */ + .word LPTIM1_IRQHandler /* LPTIM1 */ + .word CEC_IRQHandler /* HDMI_CEC */ + .word I2C4_EV_IRQHandler /* I2C4 Event */ + .word I2C4_ER_IRQHandler /* I2C4 Error */ + .word SPDIF_RX_IRQHandler /* SPDIF_RX */ + .word 0 /* Reserved */ + .word DFSDM1_FLT0_IRQHandler /* DFSDM1 Filter 0 global Interrupt */ + .word DFSDM1_FLT1_IRQHandler /* DFSDM1 Filter 1 global Interrupt */ + .word DFSDM1_FLT2_IRQHandler /* DFSDM1 Filter 2 global Interrupt */ + .word DFSDM1_FLT3_IRQHandler /* DFSDM1 Filter 3 global Interrupt */ + .word SDMMC2_IRQHandler /* SDMMC2 */ + .word CAN3_TX_IRQHandler /* CAN3 TX */ + .word CAN3_RX0_IRQHandler /* CAN3 RX0 */ + .word CAN3_RX1_IRQHandler /* CAN3 RX1 */ + .word CAN3_SCE_IRQHandler /* CAN3 SCE */ + .word JPEG_IRQHandler /* JPEG */ + .word MDIOS_IRQHandler /* MDIOS */ + +/******************************************************************************* +* +* Provide weak aliases for each Exception handler to the Default_Handler. +* As they are weak aliases, any function with the same name will override +* this definition. +* +*******************************************************************************/ + .weak NMI_Handler + .thumb_set NMI_Handler,Default_Handler + + .weak HardFault_Handler + .thumb_set HardFault_Handler,Default_Handler + + .weak MemManage_Handler + .thumb_set MemManage_Handler,Default_Handler + + .weak BusFault_Handler + .thumb_set BusFault_Handler,Default_Handler + + .weak UsageFault_Handler + .thumb_set UsageFault_Handler,Default_Handler + + .weak SVC_Handler + .thumb_set SVC_Handler,Default_Handler + + .weak DebugMon_Handler + .thumb_set DebugMon_Handler,Default_Handler + + .weak PendSV_Handler + .thumb_set PendSV_Handler,Default_Handler + + .weak SysTick_Handler + .thumb_set SysTick_Handler,Default_Handler + + .weak WWDG_IRQHandler + .thumb_set WWDG_IRQHandler,Default_Handler + + .weak PVD_IRQHandler + .thumb_set PVD_IRQHandler,Default_Handler + + .weak TAMP_STAMP_IRQHandler + .thumb_set TAMP_STAMP_IRQHandler,Default_Handler + + .weak RTC_WKUP_IRQHandler + .thumb_set RTC_WKUP_IRQHandler,Default_Handler + + .weak FLASH_IRQHandler + .thumb_set FLASH_IRQHandler,Default_Handler + + .weak RCC_IRQHandler + .thumb_set RCC_IRQHandler,Default_Handler + + .weak EXTI0_IRQHandler + .thumb_set EXTI0_IRQHandler,Default_Handler + + .weak EXTI1_IRQHandler + .thumb_set EXTI1_IRQHandler,Default_Handler + + .weak EXTI2_IRQHandler + .thumb_set EXTI2_IRQHandler,Default_Handler + + .weak EXTI3_IRQHandler + .thumb_set EXTI3_IRQHandler,Default_Handler + + .weak EXTI4_IRQHandler + .thumb_set EXTI4_IRQHandler,Default_Handler + + .weak DMA1_Stream0_IRQHandler + .thumb_set DMA1_Stream0_IRQHandler,Default_Handler + + .weak DMA1_Stream1_IRQHandler + .thumb_set DMA1_Stream1_IRQHandler,Default_Handler + + .weak DMA1_Stream2_IRQHandler + .thumb_set DMA1_Stream2_IRQHandler,Default_Handler + + .weak DMA1_Stream3_IRQHandler + .thumb_set DMA1_Stream3_IRQHandler,Default_Handler + + .weak DMA1_Stream4_IRQHandler + .thumb_set DMA1_Stream4_IRQHandler,Default_Handler + + .weak DMA1_Stream5_IRQHandler + .thumb_set DMA1_Stream5_IRQHandler,Default_Handler + + .weak DMA1_Stream6_IRQHandler + .thumb_set DMA1_Stream6_IRQHandler,Default_Handler + + .weak ADC_IRQHandler + .thumb_set ADC_IRQHandler,Default_Handler + + .weak CAN1_TX_IRQHandler + .thumb_set CAN1_TX_IRQHandler,Default_Handler + + .weak CAN1_RX0_IRQHandler + .thumb_set CAN1_RX0_IRQHandler,Default_Handler + + .weak CAN1_RX1_IRQHandler + .thumb_set CAN1_RX1_IRQHandler,Default_Handler + + .weak CAN1_SCE_IRQHandler + .thumb_set CAN1_SCE_IRQHandler,Default_Handler + + .weak EXTI9_5_IRQHandler + .thumb_set EXTI9_5_IRQHandler,Default_Handler + + .weak TIM1_BRK_TIM9_IRQHandler + .thumb_set TIM1_BRK_TIM9_IRQHandler,Default_Handler + + .weak TIM1_UP_TIM10_IRQHandler + .thumb_set TIM1_UP_TIM10_IRQHandler,Default_Handler + + .weak TIM1_TRG_COM_TIM11_IRQHandler + .thumb_set TIM1_TRG_COM_TIM11_IRQHandler,Default_Handler + + .weak TIM1_CC_IRQHandler + .thumb_set TIM1_CC_IRQHandler,Default_Handler + + .weak TIM2_IRQHandler + .thumb_set TIM2_IRQHandler,Default_Handler + + .weak TIM3_IRQHandler + .thumb_set TIM3_IRQHandler,Default_Handler + + .weak TIM4_IRQHandler + .thumb_set TIM4_IRQHandler,Default_Handler + + .weak I2C1_EV_IRQHandler + .thumb_set I2C1_EV_IRQHandler,Default_Handler + + .weak I2C1_ER_IRQHandler + .thumb_set I2C1_ER_IRQHandler,Default_Handler + + .weak I2C2_EV_IRQHandler + .thumb_set I2C2_EV_IRQHandler,Default_Handler + + .weak I2C2_ER_IRQHandler + .thumb_set I2C2_ER_IRQHandler,Default_Handler + + .weak SPI1_IRQHandler + .thumb_set SPI1_IRQHandler,Default_Handler + + .weak SPI2_IRQHandler + .thumb_set SPI2_IRQHandler,Default_Handler + + .weak USART1_IRQHandler + .thumb_set USART1_IRQHandler,Default_Handler + + .weak USART2_IRQHandler + .thumb_set USART2_IRQHandler,Default_Handler + + .weak USART3_IRQHandler + .thumb_set USART3_IRQHandler,Default_Handler + + .weak EXTI15_10_IRQHandler + .thumb_set EXTI15_10_IRQHandler,Default_Handler + + .weak RTC_Alarm_IRQHandler + .thumb_set RTC_Alarm_IRQHandler,Default_Handler + + .weak OTG_FS_WKUP_IRQHandler + .thumb_set OTG_FS_WKUP_IRQHandler,Default_Handler + + .weak TIM8_BRK_TIM12_IRQHandler + .thumb_set TIM8_BRK_TIM12_IRQHandler,Default_Handler + + .weak TIM8_UP_TIM13_IRQHandler + .thumb_set TIM8_UP_TIM13_IRQHandler,Default_Handler + + .weak TIM8_TRG_COM_TIM14_IRQHandler + .thumb_set TIM8_TRG_COM_TIM14_IRQHandler,Default_Handler + + .weak TIM8_CC_IRQHandler + .thumb_set TIM8_CC_IRQHandler,Default_Handler + + .weak DMA1_Stream7_IRQHandler + .thumb_set DMA1_Stream7_IRQHandler,Default_Handler + + .weak FMC_IRQHandler + .thumb_set FMC_IRQHandler,Default_Handler + + .weak SDMMC1_IRQHandler + .thumb_set SDMMC1_IRQHandler,Default_Handler + + .weak TIM5_IRQHandler + .thumb_set TIM5_IRQHandler,Default_Handler + + .weak SPI3_IRQHandler + .thumb_set SPI3_IRQHandler,Default_Handler + + .weak UART4_IRQHandler + .thumb_set UART4_IRQHandler,Default_Handler + + .weak UART5_IRQHandler + .thumb_set UART5_IRQHandler,Default_Handler + + .weak TIM6_DAC_IRQHandler + .thumb_set TIM6_DAC_IRQHandler,Default_Handler + + .weak TIM7_IRQHandler + .thumb_set TIM7_IRQHandler,Default_Handler + + .weak DMA2_Stream0_IRQHandler + .thumb_set DMA2_Stream0_IRQHandler,Default_Handler + + .weak DMA2_Stream1_IRQHandler + .thumb_set DMA2_Stream1_IRQHandler,Default_Handler + + .weak DMA2_Stream2_IRQHandler + .thumb_set DMA2_Stream2_IRQHandler,Default_Handler + + .weak DMA2_Stream3_IRQHandler + .thumb_set DMA2_Stream3_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak DMA2_Stream4_IRQHandler + .thumb_set DMA2_Stream4_IRQHandler,Default_Handler + + .weak ETH_IRQHandler + .thumb_set ETH_IRQHandler,Default_Handler + + .weak ETH_WKUP_IRQHandler + .thumb_set ETH_WKUP_IRQHandler,Default_Handler + + .weak CAN2_TX_IRQHandler + .thumb_set CAN2_TX_IRQHandler,Default_Handler + + .weak CAN2_RX0_IRQHandler + .thumb_set CAN2_RX0_IRQHandler,Default_Handler + + .weak CAN2_RX1_IRQHandler + .thumb_set CAN2_RX1_IRQHandler,Default_Handler + + .weak CAN2_SCE_IRQHandler + .thumb_set CAN2_SCE_IRQHandler,Default_Handler + + .weak OTG_FS_IRQHandler + .thumb_set OTG_FS_IRQHandler,Default_Handler + + .weak DMA2_Stream5_IRQHandler + .thumb_set DMA2_Stream5_IRQHandler,Default_Handler + + .weak DMA2_Stream6_IRQHandler + .thumb_set DMA2_Stream6_IRQHandler,Default_Handler + + .weak DMA2_Stream7_IRQHandler + .thumb_set DMA2_Stream7_IRQHandler,Default_Handler + + .weak USART6_IRQHandler + .thumb_set USART6_IRQHandler,Default_Handler + + .weak I2C3_EV_IRQHandler + .thumb_set I2C3_EV_IRQHandler,Default_Handler + + .weak I2C3_ER_IRQHandler + .thumb_set I2C3_ER_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_OUT_IRQHandler + .thumb_set OTG_HS_EP1_OUT_IRQHandler,Default_Handler + + .weak OTG_HS_EP1_IN_IRQHandler + .thumb_set OTG_HS_EP1_IN_IRQHandler,Default_Handler + + .weak OTG_HS_WKUP_IRQHandler + .thumb_set OTG_HS_WKUP_IRQHandler,Default_Handler + + .weak OTG_HS_IRQHandler + .thumb_set OTG_HS_IRQHandler,Default_Handler + + .weak DCMI_IRQHandler + .thumb_set DCMI_IRQHandler,Default_Handler + + .weak RNG_IRQHandler + .thumb_set RNG_IRQHandler,Default_Handler + + .weak FPU_IRQHandler + .thumb_set FPU_IRQHandler,Default_Handler + + .weak UART7_IRQHandler + .thumb_set UART7_IRQHandler,Default_Handler + + .weak UART8_IRQHandler + .thumb_set UART8_IRQHandler,Default_Handler + + .weak SPI4_IRQHandler + .thumb_set SPI4_IRQHandler,Default_Handler + + .weak SPI5_IRQHandler + .thumb_set SPI5_IRQHandler,Default_Handler + + .weak SPI6_IRQHandler + .thumb_set SPI6_IRQHandler,Default_Handler + + .weak SAI1_IRQHandler + .thumb_set SAI1_IRQHandler,Default_Handler + + .weak LTDC_IRQHandler + .thumb_set LTDC_IRQHandler,Default_Handler + + .weak LTDC_ER_IRQHandler + .thumb_set LTDC_ER_IRQHandler,Default_Handler + + .weak DMA2D_IRQHandler + .thumb_set DMA2D_IRQHandler,Default_Handler + + .weak SAI2_IRQHandler + .thumb_set SAI2_IRQHandler,Default_Handler + + .weak QUADSPI_IRQHandler + .thumb_set QUADSPI_IRQHandler,Default_Handler + + .weak LPTIM1_IRQHandler + .thumb_set LPTIM1_IRQHandler,Default_Handler + + .weak CEC_IRQHandler + .thumb_set CEC_IRQHandler,Default_Handler + + .weak I2C4_EV_IRQHandler + .thumb_set I2C4_EV_IRQHandler,Default_Handler + + .weak I2C4_ER_IRQHandler + .thumb_set I2C4_ER_IRQHandler,Default_Handler + + .weak SPDIF_RX_IRQHandler + .thumb_set SPDIF_RX_IRQHandler,Default_Handler + + .weak DFSDM1_FLT0_IRQHandler + .thumb_set DFSDM1_FLT0_IRQHandler,Default_Handler + + .weak DFSDM1_FLT1_IRQHandler + .thumb_set DFSDM1_FLT1_IRQHandler,Default_Handler + + .weak DFSDM1_FLT2_IRQHandler + .thumb_set DFSDM1_FLT2_IRQHandler,Default_Handler + + .weak DFSDM1_FLT3_IRQHandler + .thumb_set DFSDM1_FLT3_IRQHandler,Default_Handler + + .weak SDMMC2_IRQHandler + .thumb_set SDMMC2_IRQHandler,Default_Handler + + .weak CAN3_TX_IRQHandler + .thumb_set CAN3_TX_IRQHandler,Default_Handler + + .weak CAN3_RX0_IRQHandler + .thumb_set CAN3_RX0_IRQHandler,Default_Handler + + .weak CAN3_RX1_IRQHandler + .thumb_set CAN3_RX1_IRQHandler,Default_Handler + + .weak CAN3_SCE_IRQHandler + .thumb_set CAN3_SCE_IRQHandler,Default_Handler + + .weak JPEG_IRQHandler + .thumb_set JPEG_IRQHandler,Default_Handler + + .weak MDIOS_IRQHandler + .thumb_set MDIOS_IRQHandler,Default_Handler + + + diff --git a/FATFS/App/fatfs.c b/FATFS/App/fatfs.c new file mode 100644 index 0000000..4ea8493 --- /dev/null +++ b/FATFS/App/fatfs.c @@ -0,0 +1,54 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fatfs.c + * @brief Code for fatfs applications + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +#include "fatfs.h" + +uint8_t retSD; /* Return value for SD */ +char SDPath[4]; /* SD logical drive path */ +FATFS SDFatFS; /* File system object for SD logical drive */ +FIL SDFile; /* File object for SD */ + +/* USER CODE BEGIN Variables */ + +/* USER CODE END Variables */ + +void MX_FATFS_Init(void) +{ + /*## FatFS: Link the SD driver ###########################*/ + retSD = FATFS_LinkDriver(&SD_Driver, SDPath); + + /* USER CODE BEGIN Init */ + /* additional user code for init */ + /* USER CODE END Init */ +} + +/** + * @brief Gets Time from RTC + * @param None + * @retval Time in DWORD + */ +DWORD get_fattime(void) +{ + /* USER CODE BEGIN get_fattime */ + return 0; + /* USER CODE END get_fattime */ +} + +/* USER CODE BEGIN Application */ + +/* USER CODE END Application */ diff --git a/FATFS/App/fatfs.h b/FATFS/App/fatfs.h new file mode 100644 index 0000000..d9b069d --- /dev/null +++ b/FATFS/App/fatfs.h @@ -0,0 +1,47 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file fatfs.h + * @brief Header for fatfs applications + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __fatfs_H +#define __fatfs_H +#ifdef __cplusplus + extern "C" { +#endif + +#include "ff.h" +#include "ff_gen_drv.h" +#include "sd_diskio.h" /* defines SD_Driver as external */ + +/* USER CODE BEGIN Includes */ + +/* USER CODE END Includes */ + +extern uint8_t retSD; /* Return value for SD */ +extern char SDPath[4]; /* SD logical drive path */ +extern FATFS SDFatFS; /* File system object for SD logical drive */ +extern FIL SDFile; /* File object for SD */ + +void MX_FATFS_Init(void); + +/* USER CODE BEGIN Prototypes */ + +/* USER CODE END Prototypes */ +#ifdef __cplusplus +} +#endif +#endif /*__fatfs_H */ diff --git a/FATFS/Target/bsp_driver_sd.c b/FATFS/Target/bsp_driver_sd.c new file mode 100644 index 0000000..b3f028e --- /dev/null +++ b/FATFS/Target/bsp_driver_sd.c @@ -0,0 +1,314 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file bsp_driver_sd.c for F7 (based on stm32756g_eval_sd.c) + * @brief This file includes a generic uSD card driver. + * To be completed by the user according to the board used for the project. + * @note Some functions generated as weak: they can be overridden by + * - code in user files + * - or BSP code from the FW pack files + * if such files are added to the generated project (by the user). + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifdef OLD_API +/* kept to avoid issue when migrating old projects. */ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +#else +/* USER CODE BEGIN FirstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END FirstSection */ +/* Includes ------------------------------------------------------------------*/ +#include "bsp_driver_sd.h" + +/* Extern variables ---------------------------------------------------------*/ + +extern SD_HandleTypeDef hsd1; + +/* USER CODE BEGIN BeforeInitSection */ +/* can be used to modify / undefine following code or add code */ +/* USER CODE END BeforeInitSection */ +/** + * @brief Initializes the SD card device. + * @retval SD status + */ +__weak uint8_t BSP_SD_Init(void) +{ + uint8_t sd_state = MSD_OK; + /* Check if the SD card is plugged in the slot */ + if (BSP_SD_IsDetected() != SD_PRESENT) + { + return MSD_ERROR_SD_NOT_PRESENT; + } + /* HAL SD initialization */ + sd_state = HAL_SD_Init(&hsd1); + /* Configure SD Bus width (4 bits mode selected) */ + if (sd_state == MSD_OK) + { + /* Enable wide operation */ + if (HAL_SD_ConfigWideBusOperation(&hsd1, SDMMC_BUS_WIDE_4B) != HAL_OK) + { + sd_state = MSD_ERROR; + } + } + + return sd_state; +} +/* USER CODE BEGIN AfterInitSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END AfterInitSection */ + +/* USER CODE BEGIN InterruptMode */ +/** + * @brief Configures Interrupt mode for SD detection pin. + * @retval Returns 0 + */ +__weak uint8_t BSP_SD_ITConfig(void) +{ + /* Code to be updated by the user or replaced by one from the FW pack (in a stmxxxx_sd.c file) */ + + return (uint8_t)0; +} + +/* USER CODE END InterruptMode */ + +/* USER CODE BEGIN BeforeReadBlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeReadBlocksSection */ +/** + * @brief Reads block(s) from a specified address in an SD card, in polling mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param ReadAddr: Address from where data is to be read + * @param NumOfBlocks: Number of SD blocks to read + * @param Timeout: Timeout for read operation + * @retval SD status + */ +__weak uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_ReadBlocks(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks, Timeout) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeWriteBlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeWriteBlocksSection */ +/** + * @brief Writes block(s) to a specified address in an SD card, in polling mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param WriteAddr: Address from where data is to be written + * @param NumOfBlocks: Number of SD blocks to write + * @param Timeout: Timeout for write operation + * @retval SD status + */ +__weak uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_WriteBlocks(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks, Timeout) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeReadDMABlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeReadDMABlocksSection */ +/** + * @brief Reads block(s) from a specified address in an SD card, in DMA mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param ReadAddr: Address from where data is to be read + * @param NumOfBlocks: Number of SD blocks to read + * @retval SD status + */ +__weak uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks) +{ + uint8_t sd_state = MSD_OK; + + /* Read block(s) in DMA transfer mode */ + if (HAL_SD_ReadBlocks_DMA(&hsd1, (uint8_t *)pData, ReadAddr, NumOfBlocks) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeWriteDMABlocksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeWriteDMABlocksSection */ +/** + * @brief Writes block(s) to a specified address in an SD card, in DMA mode. + * @param pData: Pointer to the buffer that will contain the data to transmit + * @param WriteAddr: Address from where data is to be written + * @param NumOfBlocks: Number of SD blocks to write + * @retval SD status + */ +__weak uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks) +{ + uint8_t sd_state = MSD_OK; + + /* Write block(s) in DMA transfer mode */ + if (HAL_SD_WriteBlocks_DMA(&hsd1, (uint8_t *)pData, WriteAddr, NumOfBlocks) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeEraseSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeEraseSection */ +/** + * @brief Erases the specified memory area of the given SD card. + * @param StartAddr: Start byte address + * @param EndAddr: End byte address + * @retval SD status + */ +__weak uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr) +{ + uint8_t sd_state = MSD_OK; + + if (HAL_SD_Erase(&hsd1, StartAddr, EndAddr) != HAL_OK) + { + sd_state = MSD_ERROR; + } + + return sd_state; +} + +/* USER CODE BEGIN BeforeGetCardStateSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeGetCardStateSection */ + +/** + * @brief Gets the current SD card data status. + * @param None + * @retval Data transfer state. + * This value can be one of the following values: + * @arg SD_TRANSFER_OK: No data transfer is acting + * @arg SD_TRANSFER_BUSY: Data transfer is acting + */ +__weak uint8_t BSP_SD_GetCardState(void) +{ + return ((HAL_SD_GetCardState(&hsd1) == HAL_SD_CARD_TRANSFER ) ? SD_TRANSFER_OK : SD_TRANSFER_BUSY); +} + +/** + * @brief Get SD information about specific SD card. + * @param CardInfo: Pointer to HAL_SD_CardInfoTypedef structure + * @retval None + */ +__weak void BSP_SD_GetCardInfo(HAL_SD_CardInfoTypeDef *CardInfo) +{ + /* Get SD card Information */ + HAL_SD_GetCardInfo(&hsd1, CardInfo); +} + +/* USER CODE BEGIN BeforeCallBacksSection */ +/* can be used to modify previous code / undefine following code / add code */ +/* USER CODE END BeforeCallBacksSection */ +/** + * @brief SD Abort callbacks + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_AbortCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_AbortCallback(); +} + +/** + * @brief Tx Transfer completed callback + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_WriteCpltCallback(); +} + +/** + * @brief Rx Transfer completed callback + * @param hsd: SD handle + * @retval None + */ +void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd) +{ + BSP_SD_ReadCpltCallback(); +} + +/* USER CODE BEGIN CallBacksSection_C */ +/** + * @brief BSP SD Abort callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_AbortCallback(void) +{ + +} + +/** + * @brief BSP Tx Transfer completed callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_WriteCpltCallback(void) +{ + +} + +/** + * @brief BSP Rx Transfer completed callback + * @retval None + * @note empty (up to the user to fill it in or to remove it if useless) + */ +__weak void BSP_SD_ReadCpltCallback(void) +{ + +} +/* USER CODE END CallBacksSection_C */ +#endif + +/** + * @brief Detects if SD card is correctly plugged in the memory slot or not. + * @param None + * @retval Returns if SD is detected or not + */ +__weak uint8_t BSP_SD_IsDetected(void) +{ + __IO uint8_t status = SD_PRESENT; + + if (BSP_PlatformIsDetected() == 0x0) + { + status = SD_NOT_PRESENT; + } + + return status; +} + +/* USER CODE BEGIN AdditionalCode */ +/* user code can be inserted here */ +/* USER CODE END AdditionalCode */ diff --git a/FATFS/Target/bsp_driver_sd.h b/FATFS/Target/bsp_driver_sd.h new file mode 100644 index 0000000..4af3b78 --- /dev/null +++ b/FATFS/Target/bsp_driver_sd.h @@ -0,0 +1,89 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file bsp_driver_sd.h (based on stm32756g_eval_sd.h) + * @brief This file contains the common defines and functions prototypes for + * the bsp_driver_sd.c driver. + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __STM32F7_SD_H +#define __STM32F7_SD_H + +#ifdef __cplusplus + extern "C" { +#endif + +/* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx_hal.h" +#include "fatfs_platform.h" + +/* Exported types --------------------------------------------------------*/ +/** + * @brief SD Card information structure + */ +#define BSP_SD_CardInfo HAL_SD_CardInfoTypeDef + +/* Exported constants --------------------------------------------------------*/ +/** + * @brief SD status structure definition + */ +#define MSD_OK ((uint8_t)0x00) +#define MSD_ERROR ((uint8_t)0x01) +#define MSD_ERROR_SD_NOT_PRESENT ((uint8_t)0x02) + +/** + * @brief SD transfer state definition + */ +#define SD_TRANSFER_OK ((uint8_t)0x00) +#define SD_TRANSFER_BUSY ((uint8_t)0x01) + +#define SD_PRESENT ((uint8_t)0x01) +#define SD_NOT_PRESENT ((uint8_t)0x00) +#define SD_DATATIMEOUT ((uint32_t)100000000) + +#ifdef OLD_API +/* kept to avoid issue when migrating old projects. */ +/* USER CODE BEGIN 0 */ + +/* USER CODE END 0 */ +#else +/* USER CODE BEGIN BSP_H_CODE */ + +/* Exported functions --------------------------------------------------------*/ +uint8_t BSP_SD_Init(void); +uint8_t BSP_SD_ITConfig(void); +uint8_t BSP_SD_ReadBlocks(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks, uint32_t Timeout); +uint8_t BSP_SD_WriteBlocks(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks, uint32_t Timeout); +uint8_t BSP_SD_ReadBlocks_DMA(uint32_t *pData, uint32_t ReadAddr, uint32_t NumOfBlocks); +uint8_t BSP_SD_WriteBlocks_DMA(uint32_t *pData, uint32_t WriteAddr, uint32_t NumOfBlocks); +uint8_t BSP_SD_Erase(uint32_t StartAddr, uint32_t EndAddr); +uint8_t BSP_SD_GetCardState(void); +void BSP_SD_GetCardInfo(BSP_SD_CardInfo *CardInfo); +uint8_t BSP_SD_IsDetected(void); + +/* These functions can be modified in case the current settings (e.g. DMA stream) + need to be changed for specific application needs */ +void BSP_SD_AbortCallback(void); +void BSP_SD_WriteCpltCallback(void); +void BSP_SD_ReadCpltCallback(void); +/* USER CODE END BSP_H_CODE */ +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* __STM32F7_SD_H */ diff --git a/FATFS/Target/fatfs_platform.c b/FATFS/Target/fatfs_platform.c new file mode 100644 index 0000000..26e4df7 --- /dev/null +++ b/FATFS/Target/fatfs_platform.c @@ -0,0 +1,32 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : fatfs_platform.c + * @brief : fatfs_platform source file + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** +*/ +/* USER CODE END Header */ +#include "fatfs_platform.h" + +uint8_t BSP_PlatformIsDetected(void) { + uint8_t status = SD_PRESENT; + /* Check SD card detect pin */ + if(HAL_GPIO_ReadPin(SD_DETECT_GPIO_PORT, SD_DETECT_PIN) != GPIO_PIN_RESET) + { + status = SD_NOT_PRESENT; + } + /* USER CODE BEGIN 1 */ + /* user code can be inserted here */ + /* USER CODE END 1 */ + return status; +} diff --git a/FATFS/Target/fatfs_platform.h b/FATFS/Target/fatfs_platform.h new file mode 100644 index 0000000..293fb4b --- /dev/null +++ b/FATFS/Target/fatfs_platform.h @@ -0,0 +1,27 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file : fatfs_platform.h + * @brief : fatfs_platform header file + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** +*/ +/* USER CODE END Header */ +/* Includes ------------------------------------------------------------------*/ +#include "stm32f7xx_hal.h" +/* Defines ------------------------------------------------------------------*/ +#define SD_PRESENT ((uint8_t)0x01) /* also in bsp_driver_sd.h */ +#define SD_NOT_PRESENT ((uint8_t)0x00) /* also in bsp_driver_sd.h */ +#define SD_DETECT_PIN GPIO_PIN_0 +#define SD_DETECT_GPIO_PORT GPIOE +/* Prototypes ---------------------------------------------------------------*/ +uint8_t BSP_PlatformIsDetected(void); diff --git a/FATFS/Target/ffconf.h b/FATFS/Target/ffconf.h new file mode 100644 index 0000000..5cb7f30 --- /dev/null +++ b/FATFS/Target/ffconf.h @@ -0,0 +1,269 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * FatFs - Generic FAT file system module R0.12c (C)ChaN, 2017 + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +#ifndef _FFCONF +#define _FFCONF 68300 /* Revision ID */ + +/*-----------------------------------------------------------------------------/ +/ Additional user header to be used +/-----------------------------------------------------------------------------*/ + +#include "main.h" +#include "stm32f7xx_hal.h" +#include "bsp_driver_sd.h" + +/*-----------------------------------------------------------------------------/ +/ Function Configurations +/-----------------------------------------------------------------------------*/ + +#define _FS_READONLY 0 /* 0:Read/Write or 1:Read only */ +/* This option switches read-only configuration. (0:Read/Write or 1:Read-only) +/ Read-only configuration removes writing API functions, f_write(), f_sync(), +/ f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree() +/ and optional writing functions as well. */ + +#define _FS_MINIMIZE 0 /* 0 to 3 */ +/* This option defines minimization level to remove some basic API functions. +/ +/ 0: All basic functions are enabled. +/ 1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename() +/ are removed. +/ 2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1. +/ 3: f_lseek() function is removed in addition to 2. */ + +#define _USE_STRFUNC 2 /* 0:Disable or 1-2:Enable */ +/* This option switches string functions, f_gets(), f_putc(), f_puts() and +/ f_printf(). +/ +/ 0: Disable string functions. +/ 1: Enable without LF-CRLF conversion. +/ 2: Enable with LF-CRLF conversion. */ + +#define _USE_FIND 0 +/* This option switches filtered directory read functions, f_findfirst() and +/ f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ + +#define _USE_MKFS 1 +/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */ + +#define _USE_FASTSEEK 1 +/* This option switches fast seek feature. (0:Disable or 1:Enable) */ + +#define _USE_EXPAND 0 +/* This option switches f_expand function. (0:Disable or 1:Enable) */ + +#define _USE_CHMOD 0 +/* This option switches attribute manipulation functions, f_chmod() and f_utime(). +/ (0:Disable or 1:Enable) Also _FS_READONLY needs to be 0 to enable this option. */ + +#define _USE_LABEL 0 +/* This option switches volume label functions, f_getlabel() and f_setlabel(). +/ (0:Disable or 1:Enable) */ + +#define _USE_FORWARD 0 +/* This option switches f_forward() function. (0:Disable or 1:Enable) */ + +/*-----------------------------------------------------------------------------/ +/ Locale and Namespace Configurations +/-----------------------------------------------------------------------------*/ + +#define _CODE_PAGE 850 +/* This option specifies the OEM code page to be used on the target system. +/ Incorrect setting of the code page can cause a file open failure. +/ +/ 1 - ASCII (No extended character. Non-LFN cfg. only) +/ 437 - U.S. +/ 720 - Arabic +/ 737 - Greek +/ 771 - KBL +/ 775 - Baltic +/ 850 - Latin 1 +/ 852 - Latin 2 +/ 855 - Cyrillic +/ 857 - Turkish +/ 860 - Portuguese +/ 861 - Icelandic +/ 862 - Hebrew +/ 863 - Canadian French +/ 864 - Arabic +/ 865 - Nordic +/ 866 - Russian +/ 869 - Greek 2 +/ 932 - Japanese (DBCS) +/ 936 - Simplified Chinese (DBCS) +/ 949 - Korean (DBCS) +/ 950 - Traditional Chinese (DBCS) +*/ + +#define _USE_LFN 0 /* 0 to 3 */ +#define _MAX_LFN 255 /* Maximum LFN length to handle (12 to 255) */ +/* The _USE_LFN switches the support of long file name (LFN). +/ +/ 0: Disable support of LFN. _MAX_LFN has no effect. +/ 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. +/ 2: Enable LFN with dynamic working buffer on the STACK. +/ 3: Enable LFN with dynamic working buffer on the HEAP. +/ +/ To enable the LFN, Unicode handling functions (option/unicode.c) must be added +/ to the project. The working buffer occupies (_MAX_LFN + 1) * 2 bytes and +/ additional 608 bytes at exFAT enabled. _MAX_LFN can be in range from 12 to 255. +/ It should be set 255 to support full featured LFN operations. +/ When use stack for the working buffer, take care on stack overflow. When use heap +/ memory for the working buffer, memory management functions, ff_memalloc() and +/ ff_memfree(), must be added to the project. */ + +#define _LFN_UNICODE 0 /* 0:ANSI/OEM or 1:Unicode */ +/* This option switches character encoding on the API. (0:ANSI/OEM or 1:UTF-16) +/ To use Unicode string for the path name, enable LFN and set _LFN_UNICODE = 1. +/ This option also affects behavior of string I/O functions. */ + +#define _STRF_ENCODE 3 +/* When _LFN_UNICODE == 1, this option selects the character encoding ON THE FILE to +/ be read/written via string I/O functions, f_gets(), f_putc(), f_puts and f_printf(). +/ +/ 0: ANSI/OEM +/ 1: UTF-16LE +/ 2: UTF-16BE +/ 3: UTF-8 +/ +/ This option has no effect when _LFN_UNICODE == 0. */ + +#define _FS_RPATH 0 /* 0 to 2 */ +/* This option configures support of relative path. +/ +/ 0: Disable relative path and remove related functions. +/ 1: Enable relative path. f_chdir() and f_chdrive() are available. +/ 2: f_getcwd() function is available in addition to 1. +*/ + +/*---------------------------------------------------------------------------/ +/ Drive/Volume Configurations +/----------------------------------------------------------------------------*/ + +#define _VOLUMES 1 +/* Number of volumes (logical drives) to be used. */ + +/* USER CODE BEGIN Volumes */ +#define _STR_VOLUME_ID 0 /* 0:Use only 0-9 for drive ID, 1:Use strings for drive ID */ +#define _VOLUME_STRS "RAM","NAND","CF","SD1","SD2","USB1","USB2","USB3" +/* _STR_VOLUME_ID switches string support of volume ID. +/ When _STR_VOLUME_ID is set to 1, also pre-defined strings can be used as drive +/ number in the path name. _VOLUME_STRS defines the drive ID strings for each +/ logical drives. Number of items must be equal to _VOLUMES. Valid characters for +/ the drive ID strings are: A-Z and 0-9. */ +/* USER CODE END Volumes */ + +#define _MULTI_PARTITION 0 /* 0:Single partition, 1:Multiple partition */ +/* This option switches support of multi-partition on a physical drive. +/ By default (0), each logical drive number is bound to the same physical drive +/ number and only an FAT volume found on the physical drive will be mounted. +/ When multi-partition is enabled (1), each logical drive number can be bound to +/ arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk() +/ function will be available. */ +#define _MIN_SS 512 /* 512, 1024, 2048 or 4096 */ +#define _MAX_SS 512 /* 512, 1024, 2048 or 4096 */ +/* These options configure the range of sector size to be supported. (512, 1024, +/ 2048 or 4096) Always set both 512 for most systems, all type of memory cards and +/ harddisk. But a larger value may be required for on-board flash memory and some +/ type of optical media. When _MAX_SS is larger than _MIN_SS, FatFs is configured +/ to variable sector size and GET_SECTOR_SIZE command must be implemented to the +/ disk_ioctl() function. */ + +#define _USE_TRIM 0 +/* This option switches support of ATA-TRIM. (0:Disable or 1:Enable) +/ To enable Trim function, also CTRL_TRIM command should be implemented to the +/ disk_ioctl() function. */ + +#define _FS_NOFSINFO 0 /* 0,1,2 or 3 */ +/* If you need to know correct free space on the FAT32 volume, set bit 0 of this +/ option, and f_getfree() function at first time after volume mount will force +/ a full FAT scan. Bit 1 controls the use of last allocated cluster number. +/ +/ bit0=0: Use free cluster count in the FSINFO if available. +/ bit0=1: Do not trust free cluster count in the FSINFO. +/ bit1=0: Use last allocated cluster number in the FSINFO if available. +/ bit1=1: Do not trust last allocated cluster number in the FSINFO. +*/ + +/*---------------------------------------------------------------------------/ +/ System Configurations +/----------------------------------------------------------------------------*/ + +#define _FS_TINY 0 /* 0:Normal or 1:Tiny */ +/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) +/ At the tiny configuration, size of file object (FIL) is reduced _MAX_SS bytes. +/ Instead of private sector buffer eliminated from the file object, common sector +/ buffer in the file system object (FATFS) is used for the file data transfer. */ + +#define _FS_EXFAT 0 +/* This option switches support of exFAT file system. (0:Disable or 1:Enable) +/ When enable exFAT, also LFN needs to be enabled. (_USE_LFN >= 1) +/ Note that enabling exFAT discards C89 compatibility. */ + +#define _FS_NORTC 0 +#define _NORTC_MON 6 +#define _NORTC_MDAY 4 +#define _NORTC_YEAR 2015 +/* The option _FS_NORTC switches timestamp functiton. If the system does not have +/ any RTC function or valid timestamp is not needed, set _FS_NORTC = 1 to disable +/ the timestamp function. All objects modified by FatFs will have a fixed timestamp +/ defined by _NORTC_MON, _NORTC_MDAY and _NORTC_YEAR in local time. +/ To enable timestamp function (_FS_NORTC = 0), get_fattime() function need to be +/ added to the project to get current time form real-time clock. _NORTC_MON, +/ _NORTC_MDAY and _NORTC_YEAR have no effect. +/ These options have no effect at read-only configuration (_FS_READONLY = 1). */ + +#define _FS_LOCK 2 /* 0:Disable or >=1:Enable */ +/* The option _FS_LOCK switches file lock function to control duplicated file open +/ and illegal operation to open objects. This option must be 0 when _FS_READONLY +/ is 1. +/ +/ 0: Disable file lock function. To avoid volume corruption, application program +/ should avoid illegal open, remove and rename to the open objects. +/ >0: Enable file lock function. The value defines how many files/sub-directories +/ can be opened simultaneously under file lock control. Note that the file +/ lock control is independent of re-entrancy. */ + +#define _FS_REENTRANT 0 /* 0:Disable or 1:Enable */ +#define _FS_TIMEOUT 1000 /* Timeout period in unit of time ticks */ +#define _SYNC_t NULL +/* The option _FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs +/ module itself. Note that regardless of this option, file access to different +/ volume is always re-entrant and volume control functions, f_mount(), f_mkfs() +/ and f_fdisk() function, are always not re-entrant. Only file/directory access +/ to the same volume is under control of this function. +/ +/ 0: Disable re-entrancy. _FS_TIMEOUT and _SYNC_t have no effect. +/ 1: Enable re-entrancy. Also user provided synchronization handlers, +/ ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj() +/ function, must be added to the project. Samples are available in +/ option/syscall.c. +/ +/ The _FS_TIMEOUT defines timeout period in unit of time tick. +/ The _SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*, +/ SemaphoreHandle_t and etc.. A header file for O/S definitions needs to be +/ included somewhere in the scope of ff.h. */ + +/* define the ff_malloc ff_free macros as standard malloc free */ +#if !defined(ff_malloc) && !defined(ff_free) +#include +#define ff_malloc malloc +#define ff_free free +#endif + +#endif /* _FFCONF */ diff --git a/FATFS/Target/sd_diskio.c b/FATFS/Target/sd_diskio.c new file mode 100644 index 0000000..4f96b40 --- /dev/null +++ b/FATFS/Target/sd_diskio.c @@ -0,0 +1,519 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file sd_diskio.c + * @brief SD Disk I/O driver + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Note: code generation based on sd_diskio_dma_template_bspv1.c v2.1.4 + as "Use dma template" is enabled. */ + +/* USER CODE BEGIN firstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END firstSection*/ + +/* Includes ------------------------------------------------------------------*/ +#include "ff_gen_drv.h" +#include "sd_diskio.h" + +#include + +/* Private typedef -----------------------------------------------------------*/ +/* Private define ------------------------------------------------------------*/ + + /* + * the following Timeout is useful to give the control back to the applications + * in case of errors in either BSP_SD_ReadCpltCallback() or BSP_SD_WriteCpltCallback() + * the value by default is as defined in the BSP platform driver otherwise 30 secs + */ +#define SD_TIMEOUT 30 * 1000 + +#define SD_DEFAULT_BLOCK_SIZE 512 + +/* + * Depending on the use case, the SD card initialization could be done at the + * application level: if it is the case define the flag below to disable + * the BSP_SD_Init() call in the SD_Initialize() and add a call to + * BSP_SD_Init() elsewhere in the application. + */ +/* USER CODE BEGIN disableSDInit */ +/* #define DISABLE_SD_INIT */ +/* USER CODE END disableSDInit */ + +/* + * when using cacheable memory region, it may be needed to maintain the cache + * validity. Enable the define below to activate a cache maintenance at each + * read and write operation. + * Notice: This is applicable only for cortex M7 based platform. + */ +/* USER CODE BEGIN enableSDDmaCacheMaintenance */ +/* #define ENABLE_SD_DMA_CACHE_MAINTENANCE 1 */ +/* USER CODE END enableSDDmaCacheMaintenance */ + +/* +* Some DMA requires 4-Byte aligned address buffer to correctly read/write data, +* in FatFs some accesses aren't thus we need a 4-byte aligned scratch buffer to correctly +* transfer data +*/ +/* USER CODE BEGIN enableScratchBuffer */ +/* #define ENABLE_SCRATCH_BUFFER */ +/* USER CODE END enableScratchBuffer */ + +/* Private variables ---------------------------------------------------------*/ +#if defined(ENABLE_SCRATCH_BUFFER) +#if defined (ENABLE_SD_DMA_CACHE_MAINTENANCE) +ALIGN_32BYTES(static uint8_t scratch[BLOCKSIZE]); // 32-Byte aligned for cache maintenance +#else +__ALIGN_BEGIN static uint8_t scratch[BLOCKSIZE] __ALIGN_END; +#endif +#endif +/* Disk status */ +static volatile DSTATUS Stat = STA_NOINIT; + +static volatile UINT WriteStatus = 0, ReadStatus = 0; +/* Private function prototypes -----------------------------------------------*/ +static DSTATUS SD_CheckStatus(BYTE lun); +DSTATUS SD_initialize (BYTE); +DSTATUS SD_status (BYTE); +DRESULT SD_read (BYTE, BYTE*, DWORD, UINT); +#if _USE_WRITE == 1 +DRESULT SD_write (BYTE, const BYTE*, DWORD, UINT); +#endif /* _USE_WRITE == 1 */ +#if _USE_IOCTL == 1 +DRESULT SD_ioctl (BYTE, BYTE, void*); +#endif /* _USE_IOCTL == 1 */ + +const Diskio_drvTypeDef SD_Driver = +{ + SD_initialize, + SD_status, + SD_read, +#if _USE_WRITE == 1 + SD_write, +#endif /* _USE_WRITE == 1 */ + +#if _USE_IOCTL == 1 + SD_ioctl, +#endif /* _USE_IOCTL == 1 */ +}; + +/* USER CODE BEGIN beforeFunctionSection */ +/* can be used to modify / undefine following code or add new code */ +/* USER CODE END beforeFunctionSection */ + +/* Private functions ---------------------------------------------------------*/ + +static int SD_CheckStatusWithTimeout(uint32_t timeout) +{ + uint32_t timer = HAL_GetTick(); + /* block until SDIO IP is ready again or a timeout occur */ + while(HAL_GetTick() - timer < timeout) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + return 0; + } + } + + return -1; +} + +static DSTATUS SD_CheckStatus(BYTE lun) +{ + Stat = STA_NOINIT; + + if(BSP_SD_GetCardState() == MSD_OK) + { + Stat &= ~STA_NOINIT; + } + + return Stat; +} + +/** + * @brief Initializes a Drive + * @param lun : not used + * @retval DSTATUS: Operation status + */ +DSTATUS SD_initialize(BYTE lun) +{ + +#if !defined(DISABLE_SD_INIT) + + if(BSP_SD_Init() == MSD_OK) + { + Stat = SD_CheckStatus(lun); + } + +#else + Stat = SD_CheckStatus(lun); +#endif + + return Stat; +} + +/** + * @brief Gets Disk Status + * @param lun : not used + * @retval DSTATUS: Operation status + */ +DSTATUS SD_status(BYTE lun) +{ + return SD_CheckStatus(lun); +} + +/* USER CODE BEGIN beforeReadSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeReadSection */ +/** + * @brief Reads Sector(s) + * @param lun : not used + * @param *buff: Data buffer to store read data + * @param sector: Sector address (LBA) + * @param count: Number of sectors to read (1..128) + * @retval DRESULT: Operation result + */ + +DRESULT SD_read(BYTE lun, BYTE *buff, DWORD sector, UINT count) +{ + DRESULT res = RES_ERROR; + uint32_t timeout; +#if defined(ENABLE_SCRATCH_BUFFER) + uint8_t ret; +#endif +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + uint32_t alignedAddr; +#endif + + /* + * ensure the SDCard is ready for a new operation + */ + + if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) + { + return res; + } + +#if defined(ENABLE_SCRATCH_BUFFER) + if (!((uint32_t)buff & 0x3)) + { +#endif + if(BSP_SD_ReadBlocks_DMA((uint32_t*)buff, + (uint32_t) (sector), + count) == MSD_OK) + { + ReadStatus = 0; + /* Wait that the reading process is completed or a timeout occurs */ + timeout = HAL_GetTick(); + while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + /* in case of a timeout return error */ + if (ReadStatus == 0) + { + res = RES_ERROR; + } + else + { + ReadStatus = 0; + timeout = HAL_GetTick(); + + while((HAL_GetTick() - timeout) < SD_TIMEOUT) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + res = RES_OK; +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + the SCB_InvalidateDCache_by_Addr() requires a 32-Byte aligned address, + adjust the address and the D-Cache size to invalidate accordingly. + */ + alignedAddr = (uint32_t)buff & ~0x1F; + SCB_InvalidateDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr)); +#endif + break; + } + } + } + } +#if defined(ENABLE_SCRATCH_BUFFER) + } + else + { + /* Slow path, fetch each sector a part and memcpy to destination buffer */ + int i; + + for (i = 0; i < count; i++) { + ret = BSP_SD_ReadBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1); + if (ret == MSD_OK) { + /* wait until the read is successful or a timeout occurs */ + + timeout = HAL_GetTick(); + while((ReadStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + if (ReadStatus == 0) + { + res = RES_ERROR; + break; + } + ReadStatus = 0; + +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + * + * invalidate the scratch buffer before the next read to get the actual data instead of the cached one + */ + SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE); +#endif + memcpy(buff, scratch, BLOCKSIZE); + buff += BLOCKSIZE; + } + else + { + break; + } + } + + if ((i == count) && (ret == MSD_OK)) + res = RES_OK; + } +#endif + + return res; +} + +/* USER CODE BEGIN beforeWriteSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeWriteSection */ +/** + * @brief Writes Sector(s) + * @param lun : not used + * @param *buff: Data to be written + * @param sector: Sector address (LBA) + * @param count: Number of sectors to write (1..128) + * @retval DRESULT: Operation result + */ +#if _USE_WRITE == 1 + +DRESULT SD_write(BYTE lun, const BYTE *buff, DWORD sector, UINT count) +{ + DRESULT res = RES_ERROR; + uint32_t timeout; +#if defined(ENABLE_SCRATCH_BUFFER) + uint8_t ret; + int i; +#endif + + WriteStatus = 0; +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + uint32_t alignedAddr; +#endif + + if (SD_CheckStatusWithTimeout(SD_TIMEOUT) < 0) + { + return res; + } + +#if defined(ENABLE_SCRATCH_BUFFER) + if (!((uint32_t)buff & 0x3)) + { +#endif +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + + /* + the SCB_CleanDCache_by_Addr() requires a 32-Byte aligned address + adjust the address and the D-Cache size to clean accordingly. + */ + alignedAddr = (uint32_t)buff & ~0x1F; + SCB_CleanDCache_by_Addr((uint32_t*)alignedAddr, count*BLOCKSIZE + ((uint32_t)buff - alignedAddr)); +#endif + + if(BSP_SD_WriteBlocks_DMA((uint32_t*)buff, + (uint32_t)(sector), + count) == MSD_OK) + { + /* Wait that writing process is completed or a timeout occurs */ + + timeout = HAL_GetTick(); + while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + /* in case of a timeout return error */ + if (WriteStatus == 0) + { + res = RES_ERROR; + } + else + { + WriteStatus = 0; + timeout = HAL_GetTick(); + + while((HAL_GetTick() - timeout) < SD_TIMEOUT) + { + if (BSP_SD_GetCardState() == SD_TRANSFER_OK) + { + res = RES_OK; + break; + } + } + } + } +#if defined(ENABLE_SCRATCH_BUFFER) + } + else + { + /* Slow path, fetch each sector a part and memcpy to destination buffer */ +#if (ENABLE_SD_DMA_CACHE_MAINTENANCE == 1) + /* + * invalidate the scratch buffer before the next write to get the actual data instead of the cached one + */ + SCB_InvalidateDCache_by_Addr((uint32_t*)scratch, BLOCKSIZE); +#endif + + for (i = 0; i < count; i++) + { + WriteStatus = 0; + + memcpy((void *)scratch, (void *)buff, BLOCKSIZE); + buff += BLOCKSIZE; + + ret = BSP_SD_WriteBlocks_DMA((uint32_t*)scratch, (uint32_t)sector++, 1); + if (ret == MSD_OK) { + /* wait for a message from the queue or a timeout */ + timeout = HAL_GetTick(); + while((WriteStatus == 0) && ((HAL_GetTick() - timeout) < SD_TIMEOUT)) + { + } + if (WriteStatus == 0) + { + break; + } + + } + else + { + break; + } + } + if ((i == count) && (ret == MSD_OK)) + res = RES_OK; + } +#endif + return res; +} +#endif /* _USE_WRITE == 1 */ + +/* USER CODE BEGIN beforeIoctlSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END beforeIoctlSection */ +/** + * @brief I/O control operation + * @param lun : not used + * @param cmd: Control code + * @param *buff: Buffer to send/receive control data + * @retval DRESULT: Operation result + */ +#if _USE_IOCTL == 1 +DRESULT SD_ioctl(BYTE lun, BYTE cmd, void *buff) +{ + DRESULT res = RES_ERROR; + BSP_SD_CardInfo CardInfo; + + if (Stat & STA_NOINIT) return RES_NOTRDY; + + switch (cmd) + { + /* Make sure that no pending write process */ + case CTRL_SYNC : + res = RES_OK; + break; + + /* Get number of sectors on the disk (DWORD) */ + case GET_SECTOR_COUNT : + BSP_SD_GetCardInfo(&CardInfo); + *(DWORD*)buff = CardInfo.LogBlockNbr; + res = RES_OK; + break; + + /* Get R/W sector size (WORD) */ + case GET_SECTOR_SIZE : + BSP_SD_GetCardInfo(&CardInfo); + *(WORD*)buff = CardInfo.LogBlockSize; + res = RES_OK; + break; + + /* Get erase block size in unit of sector (DWORD) */ + case GET_BLOCK_SIZE : + BSP_SD_GetCardInfo(&CardInfo); + *(DWORD*)buff = CardInfo.LogBlockSize / SD_DEFAULT_BLOCK_SIZE; + res = RES_OK; + break; + + default: + res = RES_PARERR; + } + + return res; +} +#endif /* _USE_IOCTL == 1 */ + +/* USER CODE BEGIN afterIoctlSection */ +/* can be used to modify previous code / undefine following code / add new code */ +/* USER CODE END afterIoctlSection */ + +/* USER CODE BEGIN callbackSection */ +/* can be used to modify / following code or add new code */ +/* USER CODE END callbackSection */ +/** + * @brief Tx Transfer completed callbacks + * @param hsd: SD handle + * @retval None + */ +void BSP_SD_WriteCpltCallback(void) +{ + + WriteStatus = 1; +} + +/** + * @brief Rx Transfer completed callbacks + * @param hsd: SD handle + * @retval None + */ +void BSP_SD_ReadCpltCallback(void) +{ + ReadStatus = 1; +} + +/* USER CODE BEGIN ErrorAbortCallbacks */ +/* +============================================================================================== + depending on the SD_HAL_Driver version, either the HAL_SD_ErrorCallback() or HAL_SD_AbortCallback() + or both could be defined, activate the callbacks below when suitable and needed +============================================================================================== +void BSP_SD_AbortCallback(void) +{ +} + +void BSP_SD_ErrorCallback(void) +{ +} +*/ +/* USER CODE END ErrorAbortCallbacks */ + +/* USER CODE BEGIN lastSection */ +/* can be used to modify / undefine previous code or add new code */ +/* USER CODE END lastSection */ diff --git a/FATFS/Target/sd_diskio.h b/FATFS/Target/sd_diskio.h new file mode 100644 index 0000000..ae772cc --- /dev/null +++ b/FATFS/Target/sd_diskio.h @@ -0,0 +1,41 @@ +/* USER CODE BEGIN Header */ +/** + ****************************************************************************** + * @file sd_diskio.h + * @brief Header for sd_diskio.c module + ****************************************************************************** + * @attention + * + * Copyright (c) 2022 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ +/* USER CODE END Header */ + +/* Note: code generation based on sd_diskio_dma_template.h */ + +/* Define to prevent recursive inclusion -------------------------------------*/ +#ifndef __SD_DISKIO_H +#define __SD_DISKIO_H + +/* USER CODE BEGIN firstSection */ +/* can be used to modify / undefine following code or add new definitions */ +/* USER CODE END firstSection */ + +/* Includes ------------------------------------------------------------------*/ +#include "bsp_driver_sd.h" +/* Exported types ------------------------------------------------------------*/ +/* Exported constants --------------------------------------------------------*/ +/* Exported functions ------------------------------------------------------- */ +extern const Diskio_drvTypeDef SD_Driver; + +/* USER CODE BEGIN lastSection */ +/* can be used to modify / undefine previous code or add new definitions */ +/* USER CODE END lastSection */ + +#endif /* __SD_DISKIO_H */ diff --git a/basestation.ioc b/basestation.ioc index 534c433..a23b6a2 100644 --- a/basestation.ioc +++ b/basestation.ioc @@ -1,5 +1,5 @@ #MicroXplorer Configuration settings - do not modify -CORTEX_M7.CPU_DCache=Enabled +CORTEX_M7.CPU_DCache=Disabled CORTEX_M7.CPU_ICache=Enabled CORTEX_M7.IPParameters=PREFETCH_ENABLE,CPU_ICache,CPU_DCache CORTEX_M7.PREFETCH_ENABLE=1 @@ -18,7 +18,12 @@ Dma.QUADSPI.0.Priority=DMA_PRIORITY_LOW Dma.QUADSPI.0.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,FIFOThreshold,MemBurst,PeriphBurst Dma.Request0=QUADSPI Dma.Request1=SDMMC1_TX -Dma.RequestsNb=2 +Dma.Request2=SPI2_TX +Dma.Request3=SPI4_RX +Dma.Request4=UART4_TX +Dma.Request5=SPI2_RX +Dma.Request6=SPI4_TX +Dma.RequestsNb=7 Dma.SDMMC1_TX.1.Direction=DMA_MEMORY_TO_PERIPH Dma.SDMMC1_TX.1.FIFOMode=DMA_FIFOMODE_ENABLE Dma.SDMMC1_TX.1.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL @@ -32,6 +37,56 @@ Dma.SDMMC1_TX.1.PeriphDataAlignment=DMA_PDATAALIGN_WORD Dma.SDMMC1_TX.1.PeriphInc=DMA_PINC_DISABLE Dma.SDMMC1_TX.1.Priority=DMA_PRIORITY_LOW Dma.SDMMC1_TX.1.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode,FIFOThreshold,MemBurst,PeriphBurst +Dma.SPI2_RX.5.Direction=DMA_PERIPH_TO_MEMORY +Dma.SPI2_RX.5.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.SPI2_RX.5.Instance=DMA1_Stream1 +Dma.SPI2_RX.5.MemDataAlignment=DMA_MDATAALIGN_BYTE +Dma.SPI2_RX.5.MemInc=DMA_MINC_ENABLE +Dma.SPI2_RX.5.Mode=DMA_NORMAL +Dma.SPI2_RX.5.PeriphDataAlignment=DMA_PDATAALIGN_BYTE +Dma.SPI2_RX.5.PeriphInc=DMA_PINC_DISABLE +Dma.SPI2_RX.5.Priority=DMA_PRIORITY_HIGH +Dma.SPI2_RX.5.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode +Dma.SPI2_TX.2.Direction=DMA_MEMORY_TO_PERIPH +Dma.SPI2_TX.2.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.SPI2_TX.2.Instance=DMA1_Stream6 +Dma.SPI2_TX.2.MemDataAlignment=DMA_MDATAALIGN_BYTE +Dma.SPI2_TX.2.MemInc=DMA_MINC_ENABLE +Dma.SPI2_TX.2.Mode=DMA_NORMAL +Dma.SPI2_TX.2.PeriphDataAlignment=DMA_PDATAALIGN_BYTE +Dma.SPI2_TX.2.PeriphInc=DMA_PINC_DISABLE +Dma.SPI2_TX.2.Priority=DMA_PRIORITY_HIGH +Dma.SPI2_TX.2.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode +Dma.SPI4_RX.3.Direction=DMA_PERIPH_TO_MEMORY +Dma.SPI4_RX.3.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.SPI4_RX.3.Instance=DMA2_Stream0 +Dma.SPI4_RX.3.MemDataAlignment=DMA_MDATAALIGN_BYTE +Dma.SPI4_RX.3.MemInc=DMA_MINC_ENABLE +Dma.SPI4_RX.3.Mode=DMA_NORMAL +Dma.SPI4_RX.3.PeriphDataAlignment=DMA_PDATAALIGN_BYTE +Dma.SPI4_RX.3.PeriphInc=DMA_PINC_DISABLE +Dma.SPI4_RX.3.Priority=DMA_PRIORITY_HIGH +Dma.SPI4_RX.3.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode +Dma.SPI4_TX.6.Direction=DMA_MEMORY_TO_PERIPH +Dma.SPI4_TX.6.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.SPI4_TX.6.Instance=DMA2_Stream1 +Dma.SPI4_TX.6.MemDataAlignment=DMA_MDATAALIGN_BYTE +Dma.SPI4_TX.6.MemInc=DMA_MINC_ENABLE +Dma.SPI4_TX.6.Mode=DMA_NORMAL +Dma.SPI4_TX.6.PeriphDataAlignment=DMA_PDATAALIGN_BYTE +Dma.SPI4_TX.6.PeriphInc=DMA_PINC_DISABLE +Dma.SPI4_TX.6.Priority=DMA_PRIORITY_HIGH +Dma.SPI4_TX.6.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode +Dma.UART4_TX.4.Direction=DMA_MEMORY_TO_PERIPH +Dma.UART4_TX.4.FIFOMode=DMA_FIFOMODE_DISABLE +Dma.UART4_TX.4.Instance=DMA1_Stream4 +Dma.UART4_TX.4.MemDataAlignment=DMA_MDATAALIGN_BYTE +Dma.UART4_TX.4.MemInc=DMA_MINC_ENABLE +Dma.UART4_TX.4.Mode=DMA_NORMAL +Dma.UART4_TX.4.PeriphDataAlignment=DMA_PDATAALIGN_BYTE +Dma.UART4_TX.4.PeriphInc=DMA_PINC_DISABLE +Dma.UART4_TX.4.Priority=DMA_PRIORITY_LOW +Dma.UART4_TX.4.RequestParameters=Instance,Direction,PeriphInc,MemInc,PeriphDataAlignment,MemDataAlignment,Mode,Priority,FIFOMode FATFS.BSP.number=1 FATFS.IPParameters=USE_DMA_CODE_SD FATFS.USE_DMA_CODE_SD=1 @@ -39,8 +94,6 @@ FATFS0.BSP.STBoard=false FATFS0.BSP.api=Unknown FATFS0.BSP.component= FATFS0.BSP.condition= -FATFS0.BSP.i2caddr=0 -FATFS0.BSP.i2creg= FATFS0.BSP.instance=PE0 FATFS0.BSP.ip=GPIO FATFS0.BSP.mode=Input @@ -139,28 +192,33 @@ Mcu.UserConstants=APB1,108;APB2,216 Mcu.UserName=STM32F767ZITx MxCube.Version=6.5.0 MxDb.Version=DB.6.0.50 -NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true +NVIC.BusFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.DMA1_Stream1_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.DMA1_Stream4_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.DMA1_Stream6_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.DMA2_Stream0_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.DMA2_Stream1_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true NVIC.DMA2_Stream2_IRQn=true\:2\:0\:true\:false\:true\:false\:true\:true NVIC.DMA2_Stream3_IRQn=true\:2\:0\:true\:false\:true\:false\:true\:true -NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true +NVIC.DebugMonitor_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.EXTI15_10_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true NVIC.EXTI3_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true NVIC.EXTI4_IRQn=true\:5\:0\:true\:false\:true\:true\:true\:true NVIC.ForceEnableDMAVector=true -NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true -NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true -NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true +NVIC.HardFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.MemoryManagement_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.NonMaskableInt_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.OTG_HS_IRQn=true\:1\:0\:true\:false\:true\:false\:true\:true -NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true +NVIC.PendSV_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false NVIC.PriorityGroup=NVIC_PRIORITYGROUP_4 NVIC.QUADSPI_IRQn=true\:4\:0\:true\:false\:true\:true\:true\:true NVIC.SDMMC1_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true NVIC.SPI2_IRQn=true\:1\:0\:true\:false\:true\:true\:true\:true NVIC.SPI4_IRQn=true\:1\:0\:true\:false\:true\:true\:true\:true -NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true -NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:true +NVIC.SVCall_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false +NVIC.SysTick_IRQn=true\:0\:0\:false\:false\:true\:false\:true\:false NVIC.TIM1_UP_TIM10_IRQn=true\:3\:0\:true\:false\:true\:true\:true\:true -NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:true +NVIC.UsageFault_IRQn=true\:0\:0\:false\:false\:true\:false\:false\:false PA10.GPIOParameters=GPIO_Label PA10.GPIO_Label=SX_TX_RST PA10.Locked=true diff --git a/include/README b/include/README new file mode 100644 index 0000000..194dcd4 --- /dev/null +++ b/include/README @@ -0,0 +1,39 @@ + +This directory is intended for project header files. + +A header file is a file containing C declarations and macro definitions +to be shared between several project source files. You request the use of a +header file in your project source file (C, C++, etc) located in `src` folder +by including it, with the C preprocessing directive `#include'. + +```src/main.c + +#include "header.h" + +int main (void) +{ + ... +} +``` + +Including a header file produces the same results as copying the header file +into each source file that needs it. Such copying would be time-consuming +and error-prone. With a header file, the related declarations appear +in only one place. If they need to be changed, they can be changed in one +place, and programs that include the header file will automatically use the +new version when next recompiled. The header file eliminates the labor of +finding and changing all the copies as well as the risk that a failure to +find one copy will result in inconsistencies within a program. + +In C, the usual convention is to give header files names that end with `.h'. +It is most portable to use only letters, digits, dashes, and underscores in +header file names, and at most one dot. + +Read more about using header files in official GCC documentation: + +* Include Syntax +* Include Operation +* Once-Only Headers +* Computed Includes + +https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html diff --git a/platformio.ini b/platformio.ini index cc49b81..20fec84 100644 --- a/platformio.ini +++ b/platformio.ini @@ -16,21 +16,23 @@ extra_scripts = optimization = -O3 +monitor_speed = 115200 + build_flags = -I ./Core/Inc -I ./Core/Inc/FATFS/App -I ./Core/Inc/FATFS/Target -I ./Core/Inc/FT812Q - -I ./Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc -I ./Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Core/Inc + -I ./Core/Inc/USB -I ./Core/Inc/Middlewares/Third_Party/FatFs/src + -I ./Core/Inc/Middlewares/Third_Party/FatFs/src/option -I ./Core/Inc/roboteam_embedded_messages/include -I ./Core/Inc/TextOut - -I ./Core/Inc/USB_DEVICE/App - -I ./Core/Inc/USB_DEVICE/Target -I ./Core/Inc/Utilities -I ./Core/Inc/Wireless -I ./Core/Inc/Wireless/SX1280 - -I ./Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Core/Inc - -I ./Core/Inc/Middlewares/ST/STM32_USB_Device_Library/Class/CDC/Inc -I ./Core/Inc/FT812Q + -Wno-unused-variable + -Wno-unused-function + -Wno-discarded-qualifiers \ No newline at end of file diff --git a/python_utils/.gitignore b/python_utils/.gitignore new file mode 100644 index 0000000..6f7ada3 --- /dev/null +++ b/python_utils/.gitignore @@ -0,0 +1 @@ +*.rembin \ No newline at end of file diff --git a/python_utils/BasicListener.py b/python_utils/BasicListener.py new file mode 100644 index 0000000..613853e --- /dev/null +++ b/python_utils/BasicListener.py @@ -0,0 +1,10 @@ +import utils +import serial + +print("Opening basestation..") +basestation = utils.openContinuous(timeout=0.01) +print("Opened", basestation.port) + +# while True: +# line = basestation.readline().decode() +# if 0 < len(line): print(line) \ No newline at end of file diff --git a/python_utils/REMParser.py b/python_utils/REMParser.py index 3f221f6..b6b50bf 100644 --- a/python_utils/REMParser.py +++ b/python_utils/REMParser.py @@ -1,14 +1,26 @@ import numpy as np from collections import deque +import argparse +import utils +import json +import copy +import time +from datetime import datetime, timedelta +import os + import roboteam_embedded_messages.python.REM_BaseTypes as BaseTypes -from roboteam_embedded_messages.python.REM_RobotLog import REM_RobotLog -from roboteam_embedded_messages.python.REM_BasestationLog import REM_BasestationLog +from roboteam_embedded_messages.python.REM_Packet import REM_Packet +from roboteam_embedded_messages.python.REM_RobotFeedback import REM_RobotFeedback +from roboteam_embedded_messages.python.REM_RobotStateInfo import REM_RobotStateInfo +from roboteam_embedded_messages.python.REM_Log import REM_Log + +DEBUG = False class REMParser(): def __init__(self, device, output_file=None): print(f"[REMParser] New REMParser") - if device: print(f"[REMParser] Device {device.port}") + if device and hasattr(device, 'port'): print(f"[REMParser] Device {device.port}") self.device = device self.byte_buffer = bytes() @@ -18,74 +30,113 @@ def __init__(self, device, output_file=None): if type(output_file) == str: print(f"[REMParser] Creating output file {output_file}") self.output_file = open(output_file, "wb") - - + try: + # Create symlink + os.remove("latest.rembin") + except Exception as e: + print("\n") + print(e) + os.symlink(output_file, "latest.rembin") def read(self): + bytes_read = self.device.read() + if bytes_read > 0: + # print(f"\tREMParser received {bytes_read} bytes from basestation") + self.byte_buffer += self.device.byte_buffer if type(self.device.byte_buffer) == bytes else self.device.byte_buffer[0] + self.device.byte_buffer[1] + self.device.byte_buffer = bytes() + # self.byte_buffer += self.device.get_byte_buffer(erase=True, high_priority=False) - bytes_in_waiting = self.device.inWaiting() - if bytes_in_waiting == 0: return - # print(f"[read] {bytes_in_waiting} bytes in waiting") - self.byte_buffer += self.device.read(bytes_in_waiting) - # print(f"Read {bytes_in_waiting} bytes") + # bytes_in_waiting = self.device.inWaiting() + # if bytes_in_waiting == 0: return + # if DEBUG: print(f"[read] {bytes_in_waiting} bytes in waiting") + # self.byte_buffer += self.device.read(bytes_in_waiting) + # if DEBUG: print(f"[read] Read {bytes_in_waiting} bytes") - def process(self): + def process(self, parse_file=False): # No bytes in the buffer, so nothing to process if len(self.byte_buffer) == 0: return - + + if DEBUG: print("--process()-----------") + # Process as many bytes / packets as possible while True: # Stop when there are no more bytes to process if len(self.byte_buffer) == 0: break - # Get the packet type of the packet at the front of the buffer + + timestamp_parser_ms = None + if parse_file: + timestamp_ms_bytes = self.byte_buffer[:8] + timestamp_parser_ms = int.from_bytes(timestamp_ms_bytes, 'little') + self.byte_buffer = self.byte_buffer[8:] + + if DEBUG: print(f"- while True | {len(self.byte_buffer)} bytes in buffer") + + # Check if the packet type is valid according to REM packet_type = self.byte_buffer[0] + packet_valid = BaseTypes.REM_PACKET_TYPE_TO_VALID(packet_type) + # If the packet type is not valid / unknown + if not packet_valid: + print(self.byte_buffer) + self.byte_buffer = bytes() + raise Exception(f"[REMParser][process] Error! Received invalid packet type {packet_type}!") + + # Make sure that at least the entire default REM_Packet header is in the buffer + # This is need to call functions such as get_remVersion()and get_payloadSize() + if len(self.byte_buffer) < BaseTypes.REM_PACKET_SIZE_REM_PACKET: + if DEBUG: print(f"- Complete REM_Packet not yet in buffer. {len(self.byte_buffer)}/{BaseTypes.REM_PACKET_SIZE_REM_PACKET} bytes") + break - # BasestationLog or RobotLog. Assumption that these are the only two packets with dynamic size - if packet_type in [BaseTypes.PACKET_TYPE_REM_BASESTATION_LOG, BaseTypes.PACKET_TYPE_REM_ROBOT_LOG]: - idx = 0 if packet_type == BaseTypes.PACKET_TYPE_REM_BASESTATION_LOG else 1 - packet_size = [BaseTypes.PACKET_SIZE_REM_BASESTATION_LOG, BaseTypes.PACKET_SIZE_REM_ROBOT_LOG][idx] - # Check if the entire packet is in the buffer - if len(self.byte_buffer) < packet_size: break - - packet_bytes = self.byte_buffer[:packet_size] - # Get required bytes from buffer and process - packet = [REM_BasestationLog, REM_RobotLog][idx]() - packet.decode(packet_bytes) - # Check if the entire message is in the buffer - if len(self.byte_buffer) < packet_size + packet.messageLength: break - # Get the message from the buffer - message = self.byte_buffer[packet_size : packet_size + packet.messageLength] + # At least the REM_Packet headers are in the buffer. Decode it + packet = REM_Packet() + packet.decode(self.byte_buffer[:BaseTypes.REM_PACKET_SIZE_REM_PACKET]) + + # Get the expected packet size as expected by REM + rem_packet_size = BaseTypes.REM_PACKET_TYPE_TO_SIZE(packet.header) + + if DEBUG: print(f"- type={packet.header} ({BaseTypes.REM_PACKET_TYPE_TO_OBJ(packet.header).__name__}), size={packet.payloadSize}, REM_size={rem_packet_size}") + + # Ensure that the entire payload is in the byte buffer + if len(self.byte_buffer) < packet.payloadSize: + if DEBUG: print(f"- Complete packet not yet in buffer. {len(self.byte_buffer)}/{rem_packet_size} bytes") + break + + # if not REM_log, packet->payloadSize should be equal to expected REM_PACKET_SIZE + if packet.header != BaseTypes.REM_PACKET_TYPE_REM_LOG: + if packet.payloadSize != rem_packet_size: + self.byte_buffer = bytes() + raise Exception(f"[REMParser][process] Error! REM_Packet->payloadSize={packet.payloadSize} does not equal expected REM_PACKET_SIZE_*={rem_packet_size}! ") + + # Retrieve the bytes of the entire packet from the byte buffer + packet_bytes = self.byte_buffer[:packet.payloadSize] + # Create packet instance + packet = BaseTypes.REM_PACKET_TYPE_TO_OBJ(packet_type)() + # Decode the packet + packet.decode(packet_bytes) + + if packet.header == BaseTypes.REM_PACKET_TYPE_REM_LOG: + # Get the message from the buffer. The message is everything after the REM_Packet header + message = packet_bytes[BaseTypes.REM_PACKET_SIZE_REM_LOG:] + # Convert bytes into string, and store in REM_Log object packet.message = message.decode() - # Add packet to buffer - self.addPacket(packet) - # Write bytes to output file - self.writeBytes(packet_bytes + message) - # Remove processed bytes from buffer - self.byte_buffer = self.byte_buffer[packet_size + packet.messageLength:] - - # Basestation get configuration - else: - packet_size = BaseTypes.PACKET_TYPE_TO_SIZE(packet_type) - - if len(self.byte_buffer) < packet_size: break - # Create packet instance - packet_obj = BaseTypes.PACKET_TYPE_TO_OBJ(packet_type)() - # print(f"[process] Packet of size {packet_size} : {packet_type} : {type(packet_obj).__name__}") - packet_bytes = self.byte_buffer[:packet_size] - # Get required bytes from buffer and process - packet_obj.decode(packet_bytes) - # Add packet to buffer - self.addPacket(packet_obj) - # Write bytes to output file - self.writeBytes(packet_bytes) - # Remove processed bytes from buffer - self.byte_buffer = self.byte_buffer[packet_size:] + + if timestamp_parser_ms is not None: + packet.timestamp_parser_ms = timestamp_parser_ms + + # Add packet to buffer + self.addPacket(packet) + if DEBUG: print(f"- Added packet type={type(packet)}") + # Write bytes to output file + self.writeBytes(packet_bytes) + # Remove processed bytes from buffer + self.byte_buffer = self.byte_buffer[packet.payloadSize:] def addPacket(self, packet): self.packet_buffer.append(packet) def writeBytes(self, _bytes): - if self.output_file is not None: + if self.output_file is not None: + time_ms_bytes = int(time.time()*1000).to_bytes(8, 'little') + self.output_file.write(time_ms_bytes) self.output_file.write(_bytes) def hasPackets(self): @@ -94,18 +145,91 @@ def hasPackets(self): def getNextPacket(self): if self.hasPackets(): return self.packet_buffer.popleft() - def parseFile(self, filepath): + def parseFile(self, filepath, print_statistics=True): print(f"[REMParser] Parsing file {filepath}") with open(filepath, "rb") as file: self.byte_buffer = file.read() - self.process() + self.process(parse_file=True) + + if not print_statistics: return + + # Print file statistics packet_counts = {} + packet_timestamps = {} + + print(" ", "PACKET TYPE".ljust(20), "COUNT", " ", "START DATE".ljust(23), " ", "STOP DATE".ljust(23), " ", "DURATION H:M:S:MS") for packet in self.packet_buffer: packet_type = type(packet) if packet_type not in packet_counts: packet_counts[packet_type] = 0 + packet_timestamps[packet_type] = {'start' : packet.timestamp_parser_ms / 1000} packet_counts[packet_type] += 1 + packet_timestamps[packet_type]['stop'] = packet.timestamp_parser_ms / 1000 for packet_type in packet_counts: - print(packet_type.__name__.ljust(30), packet_counts[packet_type]) \ No newline at end of file + start_sec, stop_sec = packet_timestamps[packet_type].values() + start_msec, stop_msec = start_sec % 1, stop_sec % 1 + datetime_str_start = datetime.fromtimestamp(np.floor(start_sec)).strftime("%Y-%m-%d %H:%M:%S") + (f".{start_msec:.3f}"[2:]) + datetime_str_stop = datetime.fromtimestamp(np.floor(stop_sec )).strftime("%Y-%m-%d %H:%M:%S") + (f".{stop_msec :.3f}"[2:]) + duration_sec = stop_sec - start_sec + duration_str = str(timedelta(seconds=duration_sec))[:-3] + + print(" ", packet_type.__name__.ljust(20), str(packet_counts[packet_type]).rjust(5), " ", datetime_str_start, " ", datetime_str_stop, " ", duration_str) + +if __name__ == "__main__": + print("Running REMParser directly") + + argparser = argparse.ArgumentParser() + argparser.add_argument('input_file', help='File to parse') + args = argparser.parse_args() + + print("Parsing file", args.input_file) + + parser = REMParser(device=None) + parser.parseFile(args.input_file) + + packet_dicts = [] + for packet in parser.packet_buffer: + if type(packet) in [REM_RobotFeedback]: + packet_dict = utils.packetToDict(packet) + packet_dicts.append(packet_dict) + + # Split up packets into types + packets_by_type = {} + for packet in parser.packet_buffer: + type_str = type(packet).__name__ + if type_str not in packets_by_type: + packets_by_type[type_str] = [] + packets_by_type[type_str].append(utils.packetToDict(packet)) + + output_file_no_ext = os.path.splitext(args.input_file)[0] + + for type_str in packets_by_type: + packets = packets_by_type[type_str] + + # json + output_file_json = f"{output_file_no_ext}_{type_str}.json" + with open(output_file_json, 'w') as file: + file.write(json.dumps(packets)) + + # CSV + output_file_csv = f"{output_file_no_ext}_{type_str}.csv" + with open(output_file_csv, 'w') as file: + header = ",".join(list(packets[0].keys())) + file.write(header + "\n") + for packet in packets: + values = list(packet.values()) + string = ",".join([str(v) for v in values]) + file.write(string + "\n") + + print("Done!") + + + + + + + + + diff --git a/python_utils/SerialSimulator.py b/python_utils/SerialSimulator.py new file mode 100644 index 0000000..b71ec9f --- /dev/null +++ b/python_utils/SerialSimulator.py @@ -0,0 +1,100 @@ +""" +This file fakes a basestation / robot serial connection. It creates a pty (pseudo-terminal), +that can act as a serial device. This pty can be opened with the Pyserial library, exactly the same as a +basestation / programmer is opened. Useful to test scripts without actually having a basestation or programmer. +Doesn't support Windows. + +Fake serial device : https://stackoverflow.com/questions/2291772/virtual-serial-device-in-python +""" + +import os +import pty +import threading +import time +import serial +import numpy as np + +import roboteam_embedded_messages.python.REM_BaseTypes as REM_BaseTypes +from roboteam_embedded_messages.python.REM_RobotCommand import REM_RobotCommand +from roboteam_embedded_messages.python.REM_RobotFeedback import REM_RobotFeedback +from roboteam_embedded_messages.python.REM_Log import REM_Log + +def getValue(a, b, p): + return (b-a) * p + a + + + +class SerialSimulator: + def __init__(self): + self.master, self.slave = pty.openpty() + self.serial_name = os.ttyname(self.slave) + self.running = False + + def getSerialName(self): + return self.serial_name + + def run(self): + self.thread = threading.Thread(target=self.fakeREM_RobotCommand) + self.running = True + self.thread.start() + + def stop(self): + print("[SerialSimulator][stop] Stopping and joining thread..") + self.running = False + self.thread.join() + + def fakeREM_RobotCommand(self): + time.sleep(0.5) + start_time = time.time() + while self.running: + current_time = time.time() - start_time + + # Default stuff + cmd = REM_RobotFeedback() + cmd.header = REM_BaseTypes.REM_PACKET_TYPE_REM_ROBOT_FEEDBACK + cmd.remVersion = REM_BaseTypes.REM_LOCAL_VERSION + cmd.payloadSize = REM_BaseTypes.REM_PACKET_SIZE_REM_ROBOT_FEEDBACK + cmd.fromRobot = 1 + cmd.toPC = True + # The actual feedback + cmd.rho = getValue(REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_RHO_MIN, REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_RHO_MAX, current_time%1) + cmd.theta = getValue(REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_THETA_MIN, REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_THETA_MAX, 0.2*current_time%1) + cmd.angle = getValue(REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_ANGLE_MIN, REM_BaseTypes.REM_PACKET_RANGE_REM_ROBOT_FEEDBACK_ANGLE_MAX, current_time%1) + + os.write(self.master, cmd.encode()) + time.sleep(0.02) # 50Hz + + if np.random.rand() < 0.02: + from_bot = 0.25 < np.random.rand() + message = ["Basestation log!\n", "Robot log!\n"][from_bot].encode() + rem_log = REM_Log() + rem_log.header = REM_BaseTypes.REM_PACKET_TYPE_REM_LOG + rem_log.toPC = True + rem_log.fromRobotId = from_bot * np.random.randint(0, 16) + rem_log.fromBS = not from_bot + rem_log.remVersion = REM_BaseTypes.REM_LOCAL_VERSION + rem_log.payloadSize = REM_BaseTypes.REM_PACKET_SIZE_REM_LOG + len(message) + + os.write(self.master, rem_log.encode().tobytes() + message) + +if __name__ == "__main__": + print("SerialSimulator.py") + + ss = SerialSimulator() + ss.run() + + import REMParser + parser = REMParser.REMParser(device=serial.Serial(ss.getSerialName())) + + # Atm, this doesn't seem to work. No packets arrive + try: + while(True): + time.sleep(0.01) + parser.read() + parser.process() + while parser.hasPackets(): + packet = parser.getNextPacket() + print("\n\nReceived", type(packet).__name__) + except KeyboardInterrupt as ki: + ss.stop() + diff --git a/python_utils/joystick.py b/python_utils/joystick.py index d1ea402..4aa8b9b 100644 --- a/python_utils/joystick.py +++ b/python_utils/joystick.py @@ -4,7 +4,6 @@ import time import signal from xbox360controller import Xbox360Controller -import utils import serial from datetime import datetime import numpy as np @@ -12,9 +11,11 @@ from glob import glob import roboteam_embedded_messages.python.REM_BaseTypes as BaseTypes -from roboteam_embedded_messages.python.REM_RobotCommand import REM_RobotCommand as RobotCommand -from roboteam_embedded_messages.python.REM_RobotBuzzer import REM_RobotBuzzer as RobotBuzzer - +from roboteam_embedded_messages.python.REM_RobotCommand import REM_RobotCommand +from roboteam_embedded_messages.python.REM_RobotBuzzer import REM_RobotBuzzer +from roboteam_embedded_messages.python.REM_Log import REM_Log +from REMParser import REMParser +import utils basestation_handler = None joystick_handler = None @@ -44,13 +45,15 @@ def loop(self): try: while self.running: # Clear terminal - os.system('cls' if os.name == 'nt' else 'clear') + # os.system('cls' if os.name == 'nt' else 'clear') + string = "\r" for id in self.joystick_handler.controllers: controller = self.joystick_handler.controllers[id] id = int(id) + 1 - print(f"Controller: {id} | Robot: {controller.robot_id} (Dribbler: {controller.dribbler})") - print() + string += f" Joystick {id} -> Robot {controller.robot_id} | " + # print(f"\rController: {id} | Robot: {controller.robot_id} (Dribbler: {controller.dribbler})", end=) + print(string, end="") for event in self.events: print(event) @@ -96,12 +99,13 @@ def loop(self): self.event_handler.record_event(id, "New controller discovered") except Exception as e: print(e) + sleep(1) pass time.sleep(0.1) except Exception as e: self.event_handler.record_event(-1, e) - print(e) + print(f"\n{e}") self.shutdown() class Joystick: @@ -117,10 +121,16 @@ def __init__(self, joystick_handler, controller, robot_id=0): self.B = False self.X = False self.Y = False + self.TRIGGER_R = False + self.TRIGGER_L = False self.HAT_X = 0 self.HAT_Y = 0 - self.command = RobotCommand() - + self.command = REM_RobotCommand() + self.command.header = BaseTypes.REM_PACKET_TYPE_REM_ROBOT_COMMAND + self.command.fromPC = True + self.command.remVersion = BaseTypes.REM_LOCAL_VERSION + self.command.payloadSize = BaseTypes.REM_PACKET_SIZE_REM_ROBOT_COMMAND + self.assign_open_robot(1) def assign_open_robot(self, addition=0): @@ -139,10 +149,15 @@ def get_payload(self): self.robot_id = (self.robot_id + self.controller.hat.x) % 16 self.assign_open_robot(addition=self.controller.hat.x) - # Toggle dribbler + # Toggle dribbler with Y if self.controller.button_y._value and not self.Y: self.dribbler = not self.dribbler self.Y = self.controller.button_y._value + # Toggle dribbler with left trigger + if self.controller.button_trigger_l._value and not self.Y: + self.dribbler = not self.dribbler + self.TRIGGER_L = self.controller.button_trigger_l._value + self.command.dribbler = self.dribbler # Kick or chip @@ -154,11 +169,19 @@ def get_payload(self): self.command.doForce = True self.A = self.controller.button_a._value + # Kick with B if self.controller.button_b._value and not self.B: self.command.kickChipPower = self.kick_speed self.command.doKick = True self.command.doForce = True self.B = self.controller.button_b._value + # Kick with right trigger + if self.controller.button_trigger_r._value and not self.TRIGGER_R: + self.command.kickChipPower = self.kick_speed + self.command.doKick = True + self.command.doForce = True + self.TRIGGER_R = self.controller.button_trigger_r._value + # Calculate angle if 0.3 < abs(self.controller.axis_r.x): self.absolute_angle -= self.controller.axis_r.x * 0.1 @@ -179,24 +202,22 @@ def get_payload(self): rho = math.sqrt(velocity_x * velocity_x + velocity_y * velocity_y); theta = math.atan2(-velocity_x, -velocity_y); - self.command.header = BaseTypes.PACKET_TYPE_REM_ROBOT_COMMAND - self.command.remVersion = BaseTypes.LOCAL_REM_VERSION - self.command.id = self.robot_id + self.command.toRobotId = self.robot_id self.command.rho = rho self.command.theta = theta + self.absolute_angle self.command.angle = self.absolute_angle self.command.useAbsoluteAngle = 1 - buzzer_value = self.controller.trigger_l._value - if 0.3 < buzzer_value: - buzzer_command = RobotBuzzer() - buzzer_command.header = BaseTypes.PACKET_TYPE_REM_ROBOT_BUZZER - buzzer_command.remVersion = BaseTypes.LOCAL_REM_VERSION - buzzer_command.id = self.robot_id - buzzer_command.period = int(buzzer_value * 1000) - buzzer_command.duration = 0.1 - return buzzer_command.encode() + # buzzer_value = self.controller.trigger_l._value + # if 0.3 < buzzer_value: + # buzzer_command = REM_RobotBuzzer() + # buzzer_command.header = BaseTypes.REM_PACKET_TYPE_REM_ROBOT_BUZZER + # buzzer_command.remVersion = BaseTypes.REM_LOCAL_VERISON + # buzzer_command.id = self.robot_id + # buzzer_command.period = int(buzzer_value * 1000) + # buzzer_command.duration = 0.1 + # return buzzer_command.encode() return self.command.encode() @@ -205,7 +226,7 @@ def __init__(self, event_handler, joystick_handler, shutdown): self.shutdown = shutdown self.packet_Hz = 60 self.running = True - self.basestation = utils.openContinuous(timeout=0.001) + self.basestation = utils.Basestation() #utils.openContinuous(timeout=0.001) self.event_handler = event_handler self.joystick_handler = joystick_handler @@ -215,13 +236,42 @@ def __init__(self, event_handler, joystick_handler, shutdown): def loop(self): try: + os.makedirs("logs/joystick", exist_ok=True) + filename = datetime.now().strftime("%Y-%m-%d_%H:%M:%S") + ".rembin" + logger = REMParser(self.basestation, f"logs/joystick/{filename}") + last_written = time.time() while self.running: if 1./self.packet_Hz <= time.time() - last_written: last_written += 1./self.packet_Hz for i in joystick_handler.controllers: - self.basestation.write(joystick_handler.controllers[i].get_payload()) + payload = joystick_handler.controllers[i].get_payload() + logger.writeBytes(payload) + self.basestation.write(payload) + + logger.read() # Read all available bytes + logger.process() # Convert all read bytes into packets + + def handleREM_LOG(rem_log): + # Prepend where the packet originates from + log_from = "[?] " + if rem_log.fromBS: log_from = "[BS] " + if not rem_log.fromPC and not rem_log.fromBS: + log_from = f"[{str(rem_log.fromRobotId).rjust(2)}] " + + # Get message. Strip possible newline + message = rem_log.message.strip() + message = log_from + message + + # Print message on new line + nwhitespace = os.get_terminal_size().columns - len(message) - 2 + print(f"\r{message}{' ' * nwhitespace}") + + while logger.hasPackets(): + packet = logger.getNextPacket() + # RobotLog gets special treatment since we're interested in ALL logs, not just the last one + if type(packet) == REM_Log: handleREM_LOG(packet) time.sleep(0.005) except Exception as e: diff --git a/python_utils/listener.py b/python_utils/listener.py index 148ef11..b76937e 100644 --- a/python_utils/listener.py +++ b/python_utils/listener.py @@ -9,23 +9,31 @@ import sys import shutil import multiprocessing +import traceback import roboteam_embedded_messages.python.REM_BaseTypes as REM_BaseTypes from roboteam_embedded_messages.python.REM_RobotCommand import REM_RobotCommand from roboteam_embedded_messages.python.REM_RobotFeedback import REM_RobotFeedback from roboteam_embedded_messages.python.REM_RobotStateInfo import REM_RobotStateInfo +from roboteam_embedded_messages.python.REM_Log import REM_Log +from REMParser import REMParser -def printPacket(rc): - maxLength = max([len(k) for k, v in getmembers(rc)]) - title = re.findall(r"_(\w+) ", str(rc))[0] - - lines = [("┌─ %s "%title) + ("─"*100)[:maxLength*2+2-len(title)] + "┐" ] - lines += [ "│ %s : %s │" % ( k.rjust(maxLength) , str(v).ljust(maxLength) ) for k, v in getmembers(rc) ] - lines += [ "└" + ("─"*(maxLength*2+5)) + "┘"] - print("\n".join(lines)) +parser = argparse.ArgumentParser() +parser.add_argument('--robotcommand', '-r', help='Print REM_RobotCommand', action='store_true') +parser.add_argument('--robotfeedback', '-f', help='Print REM_RobotFeedback', action='store_true') +parser.add_argument('--robotstateinfo', '-s', help='Print REM_RobotStateInfo', action='store_true') +parser.add_argument('--log', '-l', help='Print REM_Log', action='store_true') -serial_connection = None +parser.add_argument('--verbose', '-v', help='Print entire packet', action='store_true') +parser.add_argument('--all', help='Print all packets', action='store_true') +parser.print_help() + +args = parser.parse_args() +print() + +basestation = None +parser = None robotCommand = REM_RobotCommand() robotFeedback = REM_RobotFeedback() @@ -34,65 +42,67 @@ def printPacket(rc): feedbackTimestamp = 0 stateInfoTimestamp = 0 -# stlink_port = "/dev/serial/by-id/usb-STMicroelectronics_STM32_STLink_0674FF525750877267181714-if02" -# stlink_port = "/dev/serial/by-id/usb-STMicroelectronics_STM32_STLink_066FFF544852707267223637-if02" +packet_types_selected = [] +if args.all or args.robotcommand: packet_types_selected.append(REM_RobotCommand) +if args.all or args.robotfeedback: packet_types_selected.append(REM_RobotFeedback) +if args.all or args.robotstateinfo: packet_types_selected.append(REM_RobotStateInfo) +if args.all or args.log: packet_types_selected.append(REM_Log) +print("Listening for the following packet types:", ", ".join([ o.__name__ for o in packet_types_selected])) +print() + +if len(packet_types_selected) == 0: + print("No packet types were selected to listen to.") + print("Run 'listener.py --all' to listen to all packets") + exit() + while True: # Open serial_connection with the serial_connection - if serial_connection is None or not serial_connection.isOpen(): - serial_connection = utils.openContinuous(timeout=0.1) - # serial_connection = utils.openContinuous(timeout=0.001) + if basestation is None: + basestation = utils.Basestation() + if parser is not None: parser.device = basestation + + if parser is None and basestation is not None: + parser = REMParser(basestation) try: # Continuously read and print messages from the serial_connection while True: - msg = serial_connection.readline() - if len(msg) == 0: - continue - print(msg.decode()) - continue - - ### Read any packets coming from the serial_connection - # Read packet type - packet_type = serial_connection.read(1) - if len(packet_type) == 0: - continue - - packetType = packet_type[0] + time.sleep(0.005) - # Parse packet based on packet type - if packetType == REM_BaseTypes.PACKET_TYPE_REM_ROBOT_FEEDBACK: - feedbackTimestamp = time.time() - packet = packet_type + serial_connection.read(REM_BaseTypes.PACKET_SIZE_REM_ROBOT_FEEDBACK - 1) - # robotFeedback.decode(packet) - print("[PACKET_TYPE_REM_ROBOT_FEEDBACK]") + # ========== READING ========== # + parser.read() # Read all available bytes + parser.process() # Convert all read bytes into packets - elif packetType == REM_BaseTypes.PACKET_TYPE_REM_ROBOT_STATE_INFO: - stateInfoTimestamp = time.time() - packet = packet_type + serial_connection.read(REM_BaseTypes.PACKET_SIZE_REM_ROBOT_STATE_INFO - 1) - # robotStateInfo.decode(packet) - print("[ROBOT_STATE_INFO]") + while parser.hasPackets(): + packet = parser.getNextPacket() + if type(packet) not in packet_types_selected: continue - elif packetType == REM_BaseTypes.PACKET_TYPE_REM_BASESTATION_LOG: - logmessage = serial_connection.readline().decode() - print("[BASESTATION]", logmessage) + if args.verbose: + utils.printCompletePacket(packet) + else: + timestamp = str(packet.timestamp).rjust(5) + sender = str(packet.fromRobotId).rjust(2) + if packet.fromBS: sender = "BS" + if packet.fromPC: senders = "PC" - elif packetType == REM_BaseTypes.PACKET_TYPE_REM_ROBOT_LOG: - logmessage = serial_connection.readline().decode() - print("[BOT]", logmessage) + message = "" + if type(packet) == REM_Log: + message = packet.message.strip() - else: - print(f"Error : Unhandled packet with type {packetType}") - print(serial_connection.readline().decode()) + print(f"[{timestamp}][{type(packet).__name__}][{sender}] {message}") except serial.SerialException as se: print("SerialException", se) - serial_connection = None + basestation = None except serial.SerialTimeoutException as ste: print("SerialTimeoutException", ste) except KeyError: print("[Error] KeyError", e, "{0:b}".format(int(str(e)))) except Exception as e: - print("[Error]", e) + print("\n[Exception]", e) + basestation = None + # raise e + # print(traceback.format_exc()) diff --git a/python_utils/monitor.py b/python_utils/monitor.py deleted file mode 100644 index 0e988f6..0000000 --- a/python_utils/monitor.py +++ /dev/null @@ -1,93 +0,0 @@ -import time -import math -from inspect import getmembers -import utils -import serial -import numpy as np -import re -import argparse -import sys -import shutil - -try: - from rem import rem -except ImportError: - print("[Error] Could not import rem, the roboteam_embedded_messages python bindings") - print("[Error] Generate the bindings by going to ./roboteam_embedded_messages/python_bindings, and execute:") - print("[Error] $ python generate.py --includes ../include/* --name rem --output ../../rem") - exit() - -def printPacket(rc): - maxLength = max([len(k) for k, v in getmembers(rc)]) - title = re.findall(r"_(\w+) ", str(rc))[0] - - lines = [("┌─ %s "%title) + ("─"*100)[:maxLength*2+2-len(title)] + "┐" ] - lines += [ "│ %s : %s │" % ( k.rjust(maxLength) , str(v)[:6].ljust(maxLength) ) for k, v in getmembers(rc) ] - lines += [ "└" + ("─"*(maxLength*2+5)) + "┘"] - print("\n"*20 + "\n".join(lines)) - -def drawProgressBar(progress): - cols = min(40, shutil.get_terminal_size((80, 20)).columns) - filled = int(cols*progress) - string = "[" - string += "*" * filled - string += " " * (cols - filled) - string += "]" - return string - -lastWritten = time.time() -tickCounter = 0 -periodLength = 300 - -packetsReceived = 0 -robotConnected = True - -connection = None - -# connection = serial.Serial(port="/dev/ttyACM0", timeout=0.001, baudrate=115200) -total_bytes_received = 0 -while True: - # Open basestation with the basestation - - if connection is None or not connection.isOpen(): - connection = utils.openContinuous(timeout = 0.1) - - try: - bytes_received = 0 - # Continuously read and print messages from the basestation - while True: - - # Read feedback packets coming from the robot - packet_type = connection.read(1) - if len(packet_type) == 0: - continue - - packetType = packet_type[0] - - if packetType == rem.lib.PACKET_TYPE_REM_ROBOT_COMMAND: - packet = packet_type + connection.read(rem.lib.PACKET_SIZE_REM_ROBOT_COMMAND - 1) - payload = rem.ffi.new("RobotCommandPayload*") - payload.payload = packet - - cmd = rem.ffi.new("RobotCommand*") - rem.lib.decodeRobotCommand(cmd, payload) - printPacket(cmd) - - if packetType == rem.lib.PACKET_TYPE_REM_BASESTATION_LOG: - line = connection.readline().decode() - bytes_received += len(line) + 1 - total_bytes_received += len(line) + 1 - print(f"{total_bytes_received} {bytes_received} [LOG] {line}", end="") - continue - - except serial.SerialException as se: - print("SerialException", se) - connection = None - except serial.SerialTimeoutException as ste: - print("SerialTimeoutException", ste) - except KeyError: - print("[Error] KeyError", e, "{0:b}".format(int(str(e)))) - except Exception as e: - print("[Error]", e) - # Reset the connection to the basestation - # ser = None \ No newline at end of file diff --git a/python_utils/musicPlayer.py b/python_utils/musicPlayer.py index 53376ac..050be22 100644 --- a/python_utils/musicPlayer.py +++ b/python_utils/musicPlayer.py @@ -3,7 +3,7 @@ import roboteam_embedded_messages.python.REM_BaseTypes as BaseTypes from roboteam_embedded_messages.python.REM_RobotMusicCommand import REM_RobotMusicCommand -connection = utils.openContinuous(timeout=0.01) +connection = utils.Basestation() def cleanRMC(): cmd = REM_RobotMusicCommand() diff --git a/python_utils/playback.py b/python_utils/playback.py new file mode 100644 index 0000000..d0a9025 --- /dev/null +++ b/python_utils/playback.py @@ -0,0 +1,39 @@ +import argparse +import time + +import roboteam_embedded_messages.python.REM_BaseTypes as BaseTypes +import utils +from REMParser import REMParser + +if __name__ == "__main__": + print("Running REMParser directly") + + argparser = argparse.ArgumentParser() + argparser.add_argument('input_file', help='File to parse') + args = argparser.parse_args() + + print("Parsing file", args.input_file) + + parser = REMParser(device=None) + parser.parseFile(args.input_file) + + # Get all RobotCommands + rcs = [ packet for packet in parser.packet_buffer if type(packet) == BaseTypes.REM_RobotCommand ] + t_start, t_stop = rcs[0].timestamp_parser_ms, rcs[-1].timestamp_parser_ms + print(t_start, t_stop) + + t_now = time.time() + 1 + + for command in rcs: + command.timestamp_parser_ms = (command.timestamp_parser_ms - t_start)/1000 + t_now + + basestation = utils.Basestation() + + rc_index = 0 + while True: + time.sleep(0.001) + t_now = time.time() + if rcs[rc_index].timestamp_parser_ms < t_now: + # print(rc_index) + rc_index += 1 + basestation.write(rcs[rc_index].encode()) \ No newline at end of file diff --git a/python_utils/roboteam_embedded_messages b/python_utils/roboteam_embedded_messages index 4f30272..27e6961 160000 --- a/python_utils/roboteam_embedded_messages +++ b/python_utils/roboteam_embedded_messages @@ -1 +1 @@ -Subproject commit 4f30272c269a168d6c51db46f10b90a9fcaf9961 +Subproject commit 27e696188df37742fb24f938496d09348fa77423 diff --git a/python_utils/testBuzzer.py b/python_utils/testBuzzer.py index 0de2e00..0566f9d 100644 --- a/python_utils/testBuzzer.py +++ b/python_utils/testBuzzer.py @@ -83,8 +83,8 @@ def read_input(note): while True: # Open basestation with the basestation - if basestation is None or not basestation.isOpen(): - basestation = utils.openContinuous(timeout=0.001) + if basestation is None: + basestation = utils.Basestation() try: # Continuously read and print messages from the basestation @@ -101,14 +101,15 @@ def read_input(note): note.value = 0 # Read feedback packets coming from the robot - packet_type = basestation.read(1) + packet = basestation.read() + packet_type = packet[0] + msg = packet[1:].decode() if len(packet_type) == 0: continue packetType = packet_type[0] if packetType == BaseTypes.PACKET_TYPE_REM_BASESTATION_LOG: - msg = basestation.readline().decode() print("[LOG]", msg, end="") except serial.SerialException as se: diff --git a/python_utils/testRobot.py b/python_utils/testRobot.py index d0de139..18b2e45 100644 --- a/python_utils/testRobot.py +++ b/python_utils/testRobot.py @@ -1,7 +1,6 @@ import time from datetime import datetime import math -from inspect import getmembers import serial import numpy as np import re @@ -9,9 +8,10 @@ import sys import shutil import multiprocessing - +import os import utils from REMParser import REMParser +from SerialSimulator import SerialSimulator import roboteam_embedded_messages.python.REM_BaseTypes as BaseTypes from roboteam_embedded_messages.python.REM_RobotCommand import REM_RobotCommand @@ -20,16 +20,10 @@ from roboteam_embedded_messages.python.REM_RobotGetPIDGains import REM_RobotGetPIDGains from roboteam_embedded_messages.python.REM_RobotSetPIDGains import REM_RobotSetPIDGains from roboteam_embedded_messages.python.REM_RobotPIDGains import REM_RobotPIDGains -from roboteam_embedded_messages.python.REM_RobotLog import REM_RobotLog -from roboteam_embedded_messages.python.REM_BasestationLog import REM_BasestationLog +from roboteam_embedded_messages.python.REM_Log import REM_Log from roboteam_embedded_messages.python.REM_BasestationGetConfiguration import REM_BasestationGetConfiguration -from roboteam_embedded_messages.python.REM_BasestationSetConfiguration import REM_BasestationSetConfiguration from roboteam_embedded_messages.python.REM_BasestationConfiguration import REM_BasestationConfiguration -robotStateInfoFile = open(f"logs/robotStateInfo_{int(time.time())}.csv", "w+") -robotCommandFile = open(f"logs/robotCommand_{int(time.time())}.csv", "w+") -robotFeedbackFile = open(f"logs/robotFeedback_{int(time.time())}.csv", "w+") - robotStateInfo = REM_RobotStateInfo() robotFeedback = REM_RobotFeedback() @@ -40,15 +34,6 @@ print("Warning! Could not import cv2. Can't visualize.") cv2_available = False -def printPacket(rc): - maxLength = max([len(k) for k, v in getmembers(rc)]) - title = re.findall(r"_(\w+) ", str(rc))[0] - - lines = [("┌─ %s "%title) + ("─"*100)[:maxLength*2+2-len(title)] + "┐" ] - lines += [ "│ %s : %s │" % ( k.rjust(maxLength) , str(v).ljust(maxLength) ) for k, v in getmembers(rc) ] - lines += [ "└" + ("─"*(maxLength*2+5)) + "┘"] - print("\n".join(lines)) - def drawProgressBar(progress): cols = min(40, shutil.get_terminal_size((80, 20)).columns) filled = int(cols*progress) @@ -78,28 +63,31 @@ def normalize_angle(angle): testsAvailable = ["nothing", "full", "kicker-reflect", "kicker", "chipper", "dribbler", "rotate", "forward", "sideways", "rotate-discrete", "forward-rotate", "getpid", "angular-velocity", "circle", "raised-cosine"] +parser = argparse.ArgumentParser() +parser.add_argument('robot_id', help='Robot ID to send commands to', type=int) +parser.add_argument('test', help='Test to execute', type=str) +parser.add_argument('--simulate', '-s', action='store_true', help='Create a fake basestation that sends REM_RobotFeedback packets') +parser.add_argument('--no-visualization', '--nv', action='store_true', help='Disable robot feedback visualization') +parser.add_argument('--output-dir', '-d', help="REMParser output directory. Logs will be placed under 'logs/OUTPUT_DIR'") + +args = parser.parse_args() +print(args) +# exit() + # Parse input arguments -try: - if len(sys.argv) != 3: - raise Exception("Error : Invalid number of arguments. Expected id and test") - - robot_id = int(sys.argv[1]) - if robot_id < 0 or 15 < robot_id: - raise Exception("Error : Invalid robot id %d. Robot id should be between 0 and 15" % robot_id) +robot_id = args.robot_id +if robot_id < 0 or 15 < robot_id: + raise Exception("Error : Invalid robot id %d. Robot id should be between 0 and 15" % robot_id) - test = sys.argv[2] - if test not in testsAvailable: - raise Exception("Error : Unknown test %s. Choose a test : %s" % (test, ", ".join(testsAvailable))) -except Exception as e: - print(e) - print("Error : Run script with \"python testRobot.py id test\"") - exit() - +test = args.test +if test not in testsAvailable: + raise Exception("Error : Unknown test %s. Choose a test : %s" % (test, ", ".join(testsAvailable))) basestation = None +simulated_basestation = None tick_counter = 0 -periodLength = 300 +periodLength = 150 packetHz = 60 robotConnected = True @@ -113,8 +101,8 @@ def normalize_angle(angle): def createSetPIDCommand(robot_id, PbodyX = 0.2, IbodyX = 0.0, DbodyX = 0.0, PbodyY = 0.3, IbodyY = 0.0, DbodyY = 0.0, PbodyW = 0.25, IbodyW = 5.0, DbodyW = 0.0, PbodyYaw = 20.0, IbodyYaw = 5.0, DbodyYaw = 0.0, Pwheels = 2.0, Iwheels = 0.0, Dwheels = 0.0): # Change the default values if the robot PIDs change # Create new empty setPID command setPID = REM_RobotSetPIDGains() - setPID.header = BaseTypes.PACKET_TYPE_REM_ROBOT_SET_PIDGAINS - setPID.remVersion = BaseTypes.LOCAL_REM_VERSION + setPID.header = BaseTypes.REM_PACKET_TYPE_REM_ROBOT_SET_PIDGAINS + setPID.remVersion = BaseTypes.REM_LOCAL_VERSION setPID.id = robot_id # Set the PID gains @@ -140,8 +128,6 @@ def createSetPIDCommand(robot_id, PbodyX = 0.2, IbodyX = 0.0, DbodyX = 0.0, Pbod return setPID - - def createRobotCommand(robot_id, test, tick_counter, period_fraction): log = "" @@ -149,18 +135,20 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): if test == "getpid": if period_fraction == 0: robotGetPIDGains = REM_RobotGetPIDGains() - robotGetPIDGains.header = BaseTypes.PACKET_TYPE_REM_ROBOT_GET_PIDGAINS - robotGetPIDGains.remVersion = BaseTypes.LOCAL_REM_VERSION + robotGetPIDGains.header = BaseTypes.REM_PACKET_TYPE_REM_ROBOT_GET_PIDGAINS + robotGetPIDGains.remVersion = BaseTypes.REM_LOCAL_VERSION robotGetPIDGains.id = robot_id return robotGetPIDGains, log - # Create new empty robot command + # Create new empty robot command. Fill required fields cmd = REM_RobotCommand() - cmd.header = BaseTypes.PACKET_TYPE_REM_ROBOT_COMMAND - cmd.remVersion = BaseTypes.LOCAL_REM_VERSION - cmd.id = robot_id + cmd.header = BaseTypes.REM_PACKET_TYPE_REM_ROBOT_COMMAND + cmd.toRobotId = robot_id + cmd.fromPC = True + cmd.remVersion = BaseTypes.REM_LOCAL_VERSION cmd.messageId = tick_counter - + cmd.payloadSize = BaseTypes.REM_PACKET_SIZE_REM_ROBOT_COMMAND + counter = 0 beta = 0.5 T = 1 @@ -230,16 +218,11 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): return cmd, log -# parser = REMParser(basestation) -# parser.parseFile("out.bin") -# print(len(parser.packet_buffer)) -# while parser.hasPackets(): -# packet = parser.getNextPacket() -# exit() - - while True: try: + + + # ========== INIT ========== # # Loop control last_tick_time = time.time() @@ -249,33 +232,51 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): last_robotstateinfo_time = 0 last_basestation_log = "" parser = None + # Visualisation image_vis = np.zeros((500, 500, 3), dtype=float) wheel_speeds_avg = np.zeros(4) rate_of_turn_avg = 0 + # Create simulated basestation if needed + if args.simulate: + simulated_basestation = SerialSimulator() + simulated_basestation.run() + # Open basestation if basestation is None or not basestation.isOpen(): - basestation = utils.openContinuous(timeout=0.01) + port = None if not args.simulate else simulated_basestation.getSerialName() + basestation = utils.Basestation() + # basestation = utils.openContinuous(timeout=0.01, port=port) print("Basestation opened") + if parser is not None: + parser.device = basestation # Open writer / parser if parser is None and basestation is not None: datetime_str = datetime.now().strftime("%Y%m%d_%H%M%S") - parser = REMParser(basestation, output_file=f"log_{datetime_str}.bin") + output_file = None + if args.output_dir is not None: + os.makedirs(f"logs/{args.output_dir}", exist_ok=True) + output_file = f"logs/{args.output_dir}/log_{datetime_str}.bin" + + parser = REMParser(basestation, output_file=output_file) - - # Create and send new PID gains - # Comment these lines out if you're not tuning PIDs - #setPID = createSetPIDCommand(robot_id, PbodyW = 0.25, IbodyW = 5.0) #put PID gains as arguments to change them (unchanged keep default value) - #setPID_encoded = setPID.encode() - #basestation.write(setPID_encoded) - #parser.writeBytes(setPID_encoded) + # ========== LOOP ========== # # Continuously write -> read -> visualise while True: + # Timing stuff. Get current time, seconds to next tick, and check if a new tick is required + current_time = time.time() + s_until_next_tick = last_tick_time + 1./packetHz - current_time + tick_required = s_until_next_tick < 0 + + # If tick is not required yet, sleep for 10% of the time between ticks, to prevent 100% CPU usage + # It 'should' also still give the script enough time between ticks to handle all reading and rendering + if not tick_required and 0.1 / packetHz < s_until_next_tick: + time.sleep(0.1 / packetHz) + - tick_required = 1./packetHz <= time.time() - last_tick_time # ========== WRITING ========== # if tick_required: @@ -298,24 +299,18 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): # Create and send new robot command cmd, cmd_log = createRobotCommand(robot_id, test, tick_counter, period_fraction) cmd_encoded = cmd.encode() - basestation.write(cmd_encoded) + + # Send command only if an actual basestation is connected, not a simulated one + if not args.simulate: + basestation.write(cmd_encoded) parser.writeBytes(cmd_encoded) last_robotcommand_time = time.time() - # Write packet info to files (used in plotPID.py) - - - # if period == 0: - # cmd = REM_BasestationGetConfiguration() - # cmd.header = BaseTypes.PACKET_TYPE_REM_BASESTATION_GET_CONFIGURATION - # cmd.remVersion = BaseTypes.LOCAL_REM_VERSION - # basestation.write(cmd.encode()) - # Logging bar = drawProgressBar(period_fraction) if not robotConnected: print(" Receiving no feedback!", end="") - print(f" {robot_id} - {test} {bar} {cmd_log} | {last_basestation_log} ", end=" "*23 + "\r") + print(f" {robot_id} - {test} {bar} {cmd_log} ", end=" "*23 + "\r") @@ -323,23 +318,35 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): parser.read() # Read all available bytes parser.process() # Convert all read bytes into packets - # Handle and store all new packets - while parser.hasPackets(): - packet = parser.getNextPacket() - latest_packets[type(packet)] = packet + def handleREM_LOG(rem_log): + # Prepend where the packet originates from + log_from = "[?] " + if rem_log.fromBS: log_from = "[BS] " + if not rem_log.fromPC and not rem_log.fromBS: + log_from = f"[{str(rem_log.fromRobotId).rjust(2)}] " + # Get message. Strip possible newline + message = rem_log.message.strip() + message = log_from + message - if REM_BasestationLog in latest_packets and latest_packets[REM_BasestationLog] is not None: - last_basestation_log = latest_packets[REM_BasestationLog].message - latest_packets[REM_BasestationLog] = None - if last_basestation_log[-1] == '\n': last_basestation_log = last_basestation_log[:-1] + # Print message on new line + nwhitespace = os.get_terminal_size().columns - len(message) - 2 + print(f"\r{message}{' ' * nwhitespace}") + # Handle and store all new packets + while parser.hasPackets(): + packet = parser.getNextPacket() + # RobotLog gets special treatment since we're interested in ALL logs, not just the last one + if type(packet) == REM_Log: + handleREM_LOG(packet) + else: + latest_packets[type(packet)] = packet # ========== VISUALISING ========== # # Break if cv2 is not imported - if not cv2_available: continue + if not cv2_available or args.no_visualization : continue # Draw robot on the image s = 101.2 @@ -350,7 +357,7 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): if REM_RobotFeedback in latest_packets and latest_packets[REM_RobotFeedback] is not None: robotFeedback = latest_packets[REM_RobotFeedback] latest_packets[REM_RobotFeedback] = None - + last_robotfeedback_time = time.time() # Ballsensor if robotFeedback.ballSensorWorking: @@ -372,15 +379,8 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): if REM_RobotStateInfo in latest_packets and latest_packets[REM_RobotStateInfo] is not None: robotStateInfo = latest_packets[REM_RobotStateInfo] latest_packets[REM_RobotStateInfo] = None - last_robotstateinfo_time = time.time() - robotStateInfoFile.write(f"{last_robotstateinfo_time} {robotStateInfo.xsensAcc1} {robotStateInfo.xsensAcc2} {robotStateInfo.xsensYaw} {robotStateInfo.rateOfTurn} {robotStateInfo.wheelSpeed1} {robotStateInfo.wheelSpeed2} {robotStateInfo.wheelSpeed3} {robotStateInfo.wheelSpeed4} {robotStateInfo.dribbleSpeed} {robotStateInfo.filteredDribbleSpeed} {robotStateInfo.dribblespeedBeforeGotBall } {robotStateInfo.bodyXIntegral} {robotStateInfo.bodyYIntegral} {robotStateInfo.bodyWIntegral} {robotStateInfo.bodyYawIntegral} {robotStateInfo.wheel1Integral} {robotStateInfo.wheel2Integral} {robotStateInfo.wheel3Integral} {robotStateInfo.wheel4Integral} \n") - robotStateInfoFile.flush() last_robotfeedback_time = time.time() - robotFeedbackFile.write(f"{last_robotfeedback_time} {robotFeedback.batteryLevel} {robotFeedback.XsensCalibrated} {robotFeedback.ballSensorWorking} {robotFeedback.ballSensorSeesBall} {robotFeedback.dribblerSeesBall} {robotFeedback.capacitorCharged} {robotFeedback.ballPos} {robotFeedback.rho} {robotFeedback.theta} {robotFeedback.angle} {robotFeedback.wheelLocked} {robotFeedback.wheelBraking} {robotFeedback.rssi} \n") - robotFeedbackFile.flush() - robotCommandFile.write(f"{last_robotcommand_time} {cmd.doKick} {cmd.doChip} {cmd.kickAtAngle} {cmd.doForce} {cmd.useCameraAngle} {cmd.rho} {cmd.theta} {cmd.angle} {cmd.angularVelocity} {cmd.cameraAngle} {cmd.dribbler} {cmd.kickChipPower} {cmd.useAbsoluteAngle} \n") - robotCommandFile.flush() - + # XSens yaw px, py = rotate((250, 250), (250, 150), -robotStateInfo.xsensYaw) cv2.line(image_vis, (250, 250), (int(px), int(py)), (1, 1, 1), 1) @@ -432,7 +432,10 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): if tick_required: cv2.imshow("Press esc to quit", image_vis) - if cv2.waitKey(1) == 27: exit() + if cv2.waitKey(1) == 27: + if simulated_basestation is not None: simulated_basestation.stop() + if(args.output_dir): print(f"Logs are written to {parser.output_file}") + exit() image_vis *= 0.7 # for packet_type in latest_packets.keys(): @@ -453,4 +456,5 @@ def createRobotCommand(robot_id, test, tick_counter, period_fraction): print("[Error] KeyError", e, "{0:b}".format(int(str(e)))) except Exception as e: print("[Error]", e) - raise e + basestation = None + raise e \ No newline at end of file diff --git a/python_utils/utils.py b/python_utils/utils.py index 0503c98..629d6ac 100644 --- a/python_utils/utils.py +++ b/python_utils/utils.py @@ -1,68 +1,281 @@ +import enum import os +import re import time +from inspect import getmembers + import serial -from bitarray import bitarray -from bitarray.util import int2ba, ba2int - -def openPort(port=None, suppressError = True, timeout=None): - ser = None - if port is None: - return None - - try: - ser = serial.Serial( - port=port, - baudrate=115200, - parity=serial.PARITY_NONE, - stopbits=serial.STOPBITS_ONE, - bytesize=serial.EIGHTBITS, - timeout=timeout - ) - except serial.serialutil.SerialException as e: - print("[open]", e) - # if not suppressError: - - return ser +import usb.core +import usb.util + + +class Basestation(object): + class BasestationV1(object): + """ + The basestation with hardware version 1. + + This basestation uses a serial connection in order to communicate with the computer. + """ + + def __init__(self): + self.byte_buffer = bytes() + self.connection = openContinuous(timeout=0.01) + + def write(self, data) -> int: + return self.connection.write(data) + + def read(self) -> int: + bytes_waiting = self.connection.inWaiting() + if bytes_waiting > 0: + self.byte_buffer += self.connection.read(bytes_waiting) + return bytes_waiting + + class BasestationV2(object): + """ + The basestation with hardware version 2. + + This basestation uses a high-speed usb connection with multiple interfaces (low and high priority). + """ + + def __init__(self): + """ + Initializes the byte buffers for both the high and low priority interfaces. And sets up the read and write + endpoints for each interface. + """ + self.byte_buffer_high_priority = bytes() + self.byte_buffer_low_priority = bytes() + + # Find the USB device + device = None + i = 0 + while device is None: + device = usb.core.find(idVendor=1155, idProduct=25432) + if device is None: + print(f"\r", end='') + print(f"\r[BasestationV2] Basestation could not be found {chr(33 + i)}", end='') + i = (i + 1) % 93 + time.sleep(0.1) + print(f"\r[BasestationV2] Connected to basestation") + + # Configure the basestation for high and low priority interfaces + device.set_configuration() + device.set_interface_altsetting(interface=0, alternate_setting=1) + configuration = device.get_active_configuration() + + # Find the high and low priority interfaces + high_prio_interface = configuration[(0, 1)] + low_prio_interface = configuration[(1, 0)] + + # Connect to the endpoints (IN, OUT) for each interface (high, low) + self._high_prio_out = usb.util.find_descriptor( + high_prio_interface, + # match the first OUT endpoint + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT + ) + self._high_prio_in = usb.util.find_descriptor( + high_prio_interface, + # match the first IN endpoint, + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN + ) + self._low_prio_out = usb.util.find_descriptor( + low_prio_interface, + # match the first OUT endpoint + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_OUT + ) + self._low_prio_in = usb.util.find_descriptor( + low_prio_interface, + # match the first IN endpoint, + custom_match=lambda e: usb.util.endpoint_direction(e.bEndpointAddress) == usb.util.ENDPOINT_IN + ) + + @property + def byte_buffer(self): + """ + TODO: Still have to figure out whether it is desirable to suddenly return a tuple. + """ + return self.byte_buffer_high_priority, self.byte_buffer_low_priority + + @byte_buffer.setter + def byte_buffer(self, value): + """ + FIXME: This is strange behaviour to have, but I don't really know a good way to use this. + For now it is only used to reset the byte buffers. + """ + self.byte_buffer_low_priority = value + self.byte_buffer_high_priority = value + + def write(self, data, high_priority=False) -> int: + if high_priority: + return self._high_prio_out.write(data) + else: + return self._low_prio_out.write(data) + + def read(self) -> int: + byte_count = 0 + + # Try to read both the low and high priority interfaces + try: + bytes_read = self._high_prio_in.read(self._high_prio_in.wMaxPacketSize, 1) + self.byte_buffer_high_priority += bytes_read + byte_count += len(bytes_read) + except usb.core.USBTimeoutError: + pass + + try: + bytes_read = self._low_prio_in.read(self._low_prio_in.wMaxPacketSize, 1) + self.byte_buffer_low_priority += bytes_read + byte_count += len(bytes_read) + print(f'Received {len(bytes_read)} on the low priority connection') + except usb.core.USBTimeoutError: + pass + + return byte_count + + def __init__(self, version=None): + """ + Initializes and connects with the basestation. + + The constructor automatically detects and connects with the first basestation that it finds, + if you want to override this behaviour, specify the exact basestation version that you want + to connect to. + + :param version If defined only connect to this specific hardware version. + """ + i = 0 + while version is None: + i = (i + 1) % 93 + # If we find a serial connection, we use the basestation v1 connection protocol. + # If we find an usb connection, we use the basestation v2 connection protocol + if getBasestationPath() or getSTLinkPath(): + version = ConnectionVersion.BASESTATION_V1 + print("[Basestation] Detected a basestation with hardware version 1") + elif usb.core.find(idVendor=1155, idProduct=25432): + version = ConnectionVersion.BASESTATION_V2 + print("[Basestation] Detected a basestation with hardware version 2") + else: + print(f"\r[Basestation] Could detect any basestation {chr(33 + i)}", end="") + time.sleep(0.1) + + self.version = version + if version == ConnectionVersion.BASESTATION_V1 or version == ConnectionVersion.ST_LINK: + self.bs = self.BasestationV1() + elif version == ConnectionVersion.BASESTATION_V2: + self.bs = self.BasestationV2() + + @property + def byte_buffer(self): + return self.bs.byte_buffer + + @byte_buffer.setter + def byte_buffer(self, value): + self.bs.byte_buffer = value + + def write(self, data, high_priority=False) -> int: + """ + Writes data to the basestation. + + :returns The total amount of bytes written + """ + if self.version == ConnectionVersion.BASESTATION_V2: + return self.bs.write(data, high_priority) + else: + return self.bs.write(data) + + def read(self) -> int: + """ + Reads data from the basestation and writes it into a byte buffer. + + :returns The total amount of bytes read + """ + return self.bs.read() + + +class ConnectionVersion(enum.Enum): + BASESTATION_V1 = 0 + ST_LINK = 1 + BASESTATION_V2 = 2 + + +def openPort(port=None, suppressError=True, timeout=None): + ser = None + if port is None: + return None + + try: + ser = serial.Serial( + port=port, + baudrate=115200, + parity=serial.PARITY_NONE, + stopbits=serial.STOPBITS_ONE, + bytesize=serial.EIGHTBITS, + timeout=timeout + ) + except serial.serialutil.SerialException as e: + print("[open][SerialException] Could not open port", port) + + return ser + def openContinuous(*args, **kwargs): - ser = None - i = -1 - - while ser is None: - i = (i + 1) % 93 - - if "port" not in kwargs or kwargs["port"] is None: - kwargs["port"] = getBasestationPath() - - if "port" not in kwargs or kwargs["port"] is None: - kwargs["port"] = getSTLinkPath() - - if kwargs["port"] is None: - print("\r[openContinuous] Basestation path not found %s " % chr(33 + i), end="") - else: - ser = openPort(**kwargs, suppressError = True) - print("\r[openContinuous] %s " % chr(33 + i), end="") - - time.sleep(0.1) - print(f"\r[openContinuous] Basestation opened at {ser.port}") - return ser + ser = None + i = -1 + + while ser is None: + i = (i + 1) % 93 + + if "port" not in kwargs or kwargs["port"] is None: + kwargs["port"] = getBasestationPath() + + if "port" not in kwargs or kwargs["port"] is None: + kwargs["port"] = getSTLinkPath() + + if kwargs["port"] is None: + print("\r[openContinuous] Basestation path not found %s " % chr(33 + i), end="") + else: + ser = openPort(**kwargs, suppressError=True) + print("\r[openContinuous] %s " % chr(33 + i), end="") + + time.sleep(0.1) + print(f"\r[openContinuous] Basestation opened at {ser.port}") + return ser + def getBasestationPath(): - try: - usb_devices = os.listdir("/dev/serial/by-id") - basestations = [device for device in usb_devices if device.startswith("usb-RTT_BaseStation")] - if 0 < len(basestations): - return os.path.join("/dev/serial/by-id", basestations[0]) - return None - except Exception: - return None + try: + usb_devices = os.listdir("/dev/serial/by-id") + basestations = [device for device in usb_devices if device.startswith("usb-RTT_BaseStation")] + if 0 < len(basestations): + return os.path.join("/dev/serial/by-id", basestations[0]) + return None + except Exception: + return None + def getSTLinkPath(): - try: - usb_devices = os.listdir("/dev/serial/by-id") - stlinks = [device for device in usb_devices if device.startswith("usb-STMicroelectronics_STM32_STLink")] - if 0 < len(stlinks): - return os.path.join("/dev/serial/by-id", stlinks[0]) - return None - except Exception: - return None + try: + usb_devices = os.listdir("/dev/serial/by-id") + stlinks = [device for device in usb_devices if device.startswith("usb-STMicroelectronics_STM32_STLink")] + if 0 < len(stlinks): + return os.path.join("/dev/serial/by-id", stlinks[0]) + return None + except Exception: + return None + + +def printCompletePacket(rc): + types_allowed = [int, str, bool, float] + maxLength = max([len(k) for k, v in getmembers(rc)]) + title = re.findall(r"_(\w+) ", str(rc))[0] + + lines = [("┌─ %s " % title) + ("─" * 100)[:maxLength * 2 + 2 - len(title)] + "┐"] + members = [[k, v] for k, v in getmembers(rc) if type(v) in types_allowed and not k.startswith("__")] + + lines += ["│ %s : %s │" % (k.rjust(maxLength), str(v).strip().ljust(maxLength)) for k, v in members] + lines += ["└" + ("─" * (maxLength * 2 + 5)) + "┘"] + print("\n".join(lines)) + + +def packetToDict(rc): + types_allowed = [int, str, bool, float] + members = {k: v for k, v in getmembers(rc) if type(v) in types_allowed and not k.startswith("__")} + return members diff --git a/test/README b/test/README new file mode 100644 index 0000000..9b1e87b --- /dev/null +++ b/test/README @@ -0,0 +1,11 @@ + +This directory is intended for PlatformIO Test Runner and project tests. + +Unit Testing is a software testing method by which individual units of +source code, sets of one or more MCU program modules together with associated +control data, usage procedures, and operating procedures, are tested to +determine whether they are fit for use. Unit testing finds problems early +in the development cycle. + +More information about PlatformIO Unit Testing: +- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html