From 694f7e7feb0cde20c683045898f599c1b0c43b7b Mon Sep 17 00:00:00 2001 From: corvus Date: Sun, 27 Jun 2010 14:14:07 +0000 Subject: [PATCH] OP-85 x86 port of PiOS - including pios_com - UDP support git-svn-id: svn://svn.openpilot.org/OpenPilot/trunk@905 ebee16cc-31ac-478f-84a7-5cbb03baadba --- flight/PiOS.x86/inc/FreeRTOSConfig.h | 73 + flight/PiOS.x86/inc/pios_com.h | 55 + flight/PiOS.x86/inc/pios_com_priv.h | 41 + flight/PiOS.x86/inc/pios_delay.h | 37 + flight/PiOS.x86/inc/pios_led.h | 43 + flight/PiOS.x86/inc/pios_sdcard.h | 113 + flight/PiOS.x86/inc/pios_sys.h | 35 + flight/PiOS.x86/inc/pios_udp.h | 51 + flight/PiOS.x86/inc/pios_udp_priv.h | 66 + flight/PiOS.x86/inc/pios_x86.h | 45 + flight/PiOS.x86/pios.h | 70 + .../x86/Libraries/FreeRTOS/Source/croutine.c | 371 +++ .../FreeRTOS/Source/include/FreeRTOS.h | 420 +++ .../FreeRTOS/Source/include/StackMacros.h | 173 ++ .../FreeRTOS/Source/include/croutine.h | 749 ++++++ .../Libraries/FreeRTOS/Source/include/list.h | 305 +++ .../FreeRTOS/Source/include/mpu_wrappers.h | 135 + .../FreeRTOS/Source/include/portable.h | 391 +++ .../FreeRTOS/Source/include/projdefs.h | 77 + .../Libraries/FreeRTOS/Source/include/queue.h | 1261 +++++++++ .../FreeRTOS/Source/include/semphr.h | 711 +++++ .../Libraries/FreeRTOS/Source/include/task.h | 1263 +++++++++ .../x86/Libraries/FreeRTOS/Source/list.c | 191 ++ .../FreeRTOS/Source/portable/GCC/Posix/port.c | 774 ++++++ .../Source/portable/GCC/Posix/portmacro.h | 168 ++ .../FreeRTOS/Source/portable/MemMang/heap_3.c | 117 + .../FreeRTOS/Source/portable/readme.txt | 19 + .../x86/Libraries/FreeRTOS/Source/queue.c | 1465 +++++++++++ .../x86/Libraries/FreeRTOS/Source/readme.txt | 17 + .../x86/Libraries/FreeRTOS/Source/tasks.c | 2315 +++++++++++++++++ flight/PiOS.x86/x86/Libraries/minIni/LICENSE | 176 ++ flight/PiOS.x86/x86/Libraries/minIni/NOTICE | 12 + .../x86/Libraries/minIni/UPDATING.txt | 5 + .../PiOS.x86/x86/Libraries/minIni/VERSION.txt | 1 + .../x86/Libraries/minIni/minGlue-FatFs.h | 34 + .../x86/Libraries/minIni/minGlue-efsl.h | 67 + .../x86/Libraries/minIni/minGlue-stdio.h | 30 + .../PiOS.x86/x86/Libraries/minIni/minGlue.c | 95 + .../PiOS.x86/x86/Libraries/minIni/minGlue.h | 42 + flight/PiOS.x86/x86/Libraries/minIni/minIni.c | 649 +++++ flight/PiOS.x86/x86/Libraries/minIni/minIni.h | 84 + .../PiOS.x86/x86/Libraries/minIni/minIni.pdf | Bin 0 -> 165009 bytes flight/PiOS.x86/x86/Libraries/minIni/test.c | 75 + flight/PiOS.x86/x86/Libraries/minIni/test.ini | 8 + .../x86/Libraries/minIni/test_settings.ini | 20 + .../PiOS.x86/x86/Libraries/minIni/wxMinIni.h | 73 + flight/PiOS.x86/x86/pios_com.c | 285 ++ flight/PiOS.x86/x86/pios_com.c.old | 332 +++ flight/PiOS.x86/x86/pios_delay.c | 106 + flight/PiOS.x86/x86/pios_led.c | 102 + flight/PiOS.x86/x86/pios_sdcard.c | 300 +++ flight/PiOS.x86/x86/pios_sys.c | 186 ++ flight/PiOS.x86/x86/pios_udp.c | 460 ++++ 53 files changed, 14693 insertions(+) create mode 100644 flight/PiOS.x86/inc/FreeRTOSConfig.h create mode 100644 flight/PiOS.x86/inc/pios_com.h create mode 100644 flight/PiOS.x86/inc/pios_com_priv.h create mode 100644 flight/PiOS.x86/inc/pios_delay.h create mode 100644 flight/PiOS.x86/inc/pios_led.h create mode 100644 flight/PiOS.x86/inc/pios_sdcard.h create mode 100644 flight/PiOS.x86/inc/pios_sys.h create mode 100644 flight/PiOS.x86/inc/pios_udp.h create mode 100644 flight/PiOS.x86/inc/pios_udp_priv.h create mode 100644 flight/PiOS.x86/inc/pios_x86.h create mode 100644 flight/PiOS.x86/pios.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/croutine.c create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/FreeRTOS.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/StackMacros.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/croutine.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/list.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/mpu_wrappers.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/portable.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/projdefs.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/queue.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/semphr.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/task.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/list.c create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/port.c create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/portmacro.h create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/MemMang/heap_3.c create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/readme.txt create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/queue.c create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/readme.txt create mode 100644 flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/tasks.c create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/LICENSE create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/NOTICE create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/UPDATING.txt create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/VERSION.txt create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minGlue-FatFs.h create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minGlue-efsl.h create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minGlue-stdio.h create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minGlue.c create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minGlue.h create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minIni.c create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minIni.h create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/minIni.pdf create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/test.c create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/test.ini create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/test_settings.ini create mode 100644 flight/PiOS.x86/x86/Libraries/minIni/wxMinIni.h create mode 100644 flight/PiOS.x86/x86/pios_com.c create mode 100644 flight/PiOS.x86/x86/pios_com.c.old create mode 100644 flight/PiOS.x86/x86/pios_delay.c create mode 100644 flight/PiOS.x86/x86/pios_led.c create mode 100644 flight/PiOS.x86/x86/pios_sdcard.c create mode 100644 flight/PiOS.x86/x86/pios_sys.c create mode 100644 flight/PiOS.x86/x86/pios_udp.c diff --git a/flight/PiOS.x86/inc/FreeRTOSConfig.h b/flight/PiOS.x86/inc/FreeRTOSConfig.h new file mode 100644 index 000000000..79e10e6a7 --- /dev/null +++ b/flight/PiOS.x86/inc/FreeRTOSConfig.h @@ -0,0 +1,73 @@ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html. + *----------------------------------------------------------*/ + +/* Notes: We use 5 task priorities */ + +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 0 +#define configCPU_CLOCK_HZ ( ( unsigned long ) 72000000 ) +#define configTICK_RATE_HZ ( ( portTickType ) 1000 ) +#define configMAX_PRIORITIES ( ( unsigned portBASE_TYPE ) 5 ) +#define configMINIMAL_STACK_SIZE ( ( unsigned short ) 256 ) +#define configTOTAL_HEAP_SIZE ( ( size_t ) ( 45 * 1024 ) ) +#define configMAX_TASK_NAME_LEN ( 16 ) +#define configUSE_TRACE_FACILITY 0 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 0 +#define configUSE_MUTEXES 1 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_COUNTING_SEMAPHORES 0 +#define configUSE_ALTERNATIVE_API 0 +#define configCHECK_FOR_STACK_OVERFLOW 2 +#define configQUEUE_REGISTRY_SIZE 10 + + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES ( 2 ) + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ + +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 0 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTaskGetSchedulerState 1 +#define INCLUDE_xTaskGetCurrentTaskHandle 1 +#define INCLUDE_uxTaskGetStackHighWaterMark 0 + + + + +/* This is the raw value as per the Cortex-M3 NVIC. Values can be 255 +(lowest) to 1 (highest maskable) to 0 (highest non-maskable). */ +#define configKERNEL_INTERRUPT_PRIORITY 15 << 4 /* equivalent to NVIC priority 15 */ +#define configMAX_SYSCALL_INTERRUPT_PRIORITY 3 << 4 /* equivalent to NVIC priority 3 */ + + +/* This is the value being used as per the ST library which permits 16 +priority values, 0 to 15. This must correspond to the +configKERNEL_INTERRUPT_PRIORITY setting. Here 15 corresponds to the lowest +NVIC value of 255. */ +#define configLIBRARY_KERNEL_INTERRUPT_PRIORITY 15 + +#endif /* FREERTOS_CONFIG_H */ + diff --git a/flight/PiOS.x86/inc/pios_com.h b/flight/PiOS.x86/inc/pios_com.h new file mode 100644 index 000000000..361e820d5 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_com.h @@ -0,0 +1,55 @@ +/** + ****************************************************************************** + * + * @file pios_com.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief COM layer functions header + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_COM_H +#define PIOS_COM_H + +/* Public Functions */ +extern int32_t PIOS_COM_Init(void); +extern int32_t PIOS_COM_ChangeBaud(uint8_t port, uint32_t baud); +extern int32_t PIOS_COM_SendCharNonBlocking(uint8_t port, char c); +extern int32_t PIOS_COM_SendChar(uint8_t port, char c); +extern int32_t PIOS_COM_SendBufferNonBlocking(uint8_t port, uint8_t *buffer, uint16_t len); +extern int32_t PIOS_COM_SendBuffer(uint8_t port, uint8_t *buffer, uint16_t len); +extern int32_t PIOS_COM_SendStringNonBlocking(uint8_t port, char *str); +extern int32_t PIOS_COM_SendString(uint8_t port, char *str); +extern int32_t PIOS_COM_SendFormattedStringNonBlocking(uint8_t port, char *format, ...); +extern int32_t PIOS_COM_SendFormattedString(uint8_t port, char *format, ...); +extern uint8_t PIOS_COM_ReceiveBuffer(uint8_t port); +extern int32_t PIOS_COM_ReceiveBufferUsed(uint8_t port); + +extern int32_t PIOS_COM_ReceiveHandler(void); + +struct pios_com_driver { + void (*init)(uint8_t id); + void (*set_baud)(uint8_t id, uint32_t baud); + int32_t (*tx_nb)(uint8_t id, uint8_t *buffer, uint16_t len); + int32_t (*tx)(uint8_t id, uint8_t *buffer, uint16_t len); + int32_t (*rx)(uint8_t id); + int32_t (*rx_avail)(uint8_t id); +}; + +#endif /* PIOS_COM_H */ diff --git a/flight/PiOS.x86/inc/pios_com_priv.h b/flight/PiOS.x86/inc/pios_com_priv.h new file mode 100644 index 000000000..5b285edac --- /dev/null +++ b/flight/PiOS.x86/inc/pios_com_priv.h @@ -0,0 +1,41 @@ +/** + ****************************************************************************** + * + * @file pios_com_priv.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief COM private definitions. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_COM_PRIV_H +#define PIOS_COM_PRIV_H + +#include + +struct pios_com_dev { + uint8_t id; + const struct pios_com_driver * const driver; +}; + +extern struct pios_com_dev pios_com_devs[]; +extern const uint8_t pios_com_num_devices; + +#endif /* PIOS_COM_PRIV_H */ + diff --git a/flight/PiOS.x86/inc/pios_delay.h b/flight/PiOS.x86/inc/pios_delay.h new file mode 100644 index 000000000..dcd9bb68d --- /dev/null +++ b/flight/PiOS.x86/inc/pios_delay.h @@ -0,0 +1,37 @@ +/** + ****************************************************************************** + * + * @file pios_settings.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief Settings functions header + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_DELAY_H +#define PIOS_DELAY_H + + +/* Public Functions */ +extern int32_t PIOS_DELAY_Init(void); +extern int32_t PIOS_DELAY_WaituS(uint16_t uS); +extern int32_t PIOS_DELAY_WaitmS(uint16_t mS); + + +#endif /* PIOS_DELAY_H */ diff --git a/flight/PiOS.x86/inc/pios_led.h b/flight/PiOS.x86/inc/pios_led.h new file mode 100644 index 000000000..42b0d2443 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_led.h @@ -0,0 +1,43 @@ +/** + ****************************************************************************** + * + * @file pios_led.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief LED functions header. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_LED_H +#define PIOS_LED_H + +/* Type Definitions */ + +#if (PIOS_LED_NUM == 1) +typedef enum {LED1 = 0} LedTypeDef; +#elif (PIOS_LED_NUM == 2) +typedef enum {LED1 = 0, LED2 = 1} LedTypeDef; +#endif + +/* Public Functions */ +extern void PIOS_LED_Init(void); +extern void PIOS_LED_On(LedTypeDef LED); +extern void PIOS_LED_Off(LedTypeDef LED); +extern void PIOS_LED_Toggle(LedTypeDef LED); + +#endif /* PIOS_LED_H */ diff --git a/flight/PiOS.x86/inc/pios_sdcard.h b/flight/PiOS.x86/inc/pios_sdcard.h new file mode 100644 index 000000000..6a821de0d --- /dev/null +++ b/flight/PiOS.x86/inc/pios_sdcard.h @@ -0,0 +1,113 @@ +/** + ****************************************************************************** + * + * @file pios_sdcard.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief System and hardware Init functions header. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_SDCARD_H +#define PIOS_SDCARD_H + +#if defined(PIOS_INCLUDE_SDCARD) + +/* Public Functions */ +typedef struct { + uint8_t CSDStruct; /* CSD structure */ + uint8_t SysSpecVersion; /* System specification version */ + uint8_t Reserved1; /* Reserved */ + uint8_t TAAC; /* Data read access-time 1 */ + uint8_t NSAC; /* Data read access-time 2 in CLK cycles */ + uint8_t MaxBusClkFrec; /* Max. bus clock frequency */ + uint16_t CardComdClasses; /* Card command classes */ + uint8_t RdBlockLen; /* Max. read data block length */ + uint8_t PartBlockRead; /* Partial blocks for read allowed */ + uint8_t WrBlockMisalign; /* Write block misalignment */ + uint8_t RdBlockMisalign; /* Read block misalignment */ + uint8_t DSRImpl; /* DSR implemented */ + uint8_t Reserved2; /* Reserved */ + uint16_t DeviceSize; /* Device Size */ + uint8_t MaxRdCurrentVDDMin; /* Max. read current @ VDD min */ + uint8_t MaxRdCurrentVDDMax; /* Max. read current @ VDD max */ + uint8_t MaxWrCurrentVDDMin; /* Max. write current @ VDD min */ + uint8_t MaxWrCurrentVDDMax; /* Max. write current @ VDD max */ + uint8_t DeviceSizeMul; /* Device size multiplier */ + uint8_t EraseGrSize; /* Erase group size */ + uint8_t EraseGrMul; /* Erase group size multiplier */ + uint8_t WrProtectGrSize; /* Write protect group size */ + uint8_t WrProtectGrEnable; /* Write protect group enable */ + uint8_t ManDeflECC; /* Manufacturer default ECC */ + uint8_t WrSpeedFact; /* Write speed factor */ + uint8_t MaxWrBlockLen; /* Max. write data block length */ + uint8_t WriteBlockPaPartial; /* Partial blocks for write allowed */ + uint8_t Reserved3; /* Reserved */ + uint8_t ContentProtectAppli; /* Content protection application */ + uint8_t FileFormatGrouop; /* File format group */ + uint8_t CopyFlag; /* Copy flag (OTP) */ + uint8_t PermWrProtect; /* Permanent write protection */ + uint8_t TempWrProtect; /* Temporary write protection */ + uint8_t FileFormat; /* File Format */ + uint8_t ECC; /* ECC code */ + uint8_t msd_CRC; /* CRC */ + uint8_t Reserved4; /* always 1*/ +} SDCARDCsdTypeDef; + +/* Structure taken from Mass Storage Driver example provided by STM */ +typedef struct { + uint8_t ManufacturerID; /* ManufacturerID */ + uint16_t OEM_AppliID; /* OEM/Application ID */ + char ProdName[6]; /* Product Name */ + uint8_t ProdRev; /* Product Revision */ + u32 ProdSN; /* Product Serial Number */ + uint8_t Reserved1; /* Reserved1 */ + uint16_t ManufactDate; /* Manufacturing Date */ + uint8_t msd_CRC; /* CRC */ + uint8_t Reserved2; /* always 1*/ +} SDCARDCidTypeDef; + +/* Global Variables */ +//extern VOLINFO PIOS_SDCARD_VolInfo; +//extern uint8_t PIOS_SDCARD_Sector[SECTOR_SIZE]; + +/* Prototypes */ +extern int32_t PIOS_SDCARD_Init(void); +extern int32_t PIOS_SDCARD_PowerOn(void); +extern int32_t PIOS_SDCARD_PowerOff(void); +extern int32_t PIOS_SDCARD_CheckAvailable(uint8_t was_available); +extern int32_t PIOS_SDCARD_SendSDCCmd(uint8_t cmd, uint32_t addr, uint8_t crc); +extern int32_t PIOS_SDCARD_SectorRead(uint32_t sector, uint8_t *buffer); +extern int32_t PIOS_SDCARD_SectorWrite(uint32_t sector, uint8_t *buffer); +extern int32_t PIOS_SDCARD_CIDRead(SDCARDCidTypeDef *cid); +extern int32_t PIOS_SDCARD_CSDRead(SDCARDCsdTypeDef *csd); + +extern int32_t PIOS_SDCARD_StartupLog(void); +extern int32_t POIS_SDCARD_IsMounted(); +extern int32_t PIOS_SDCARD_MountFS(uint32_t StartupLog); +extern int32_t PIOS_SDCARD_GetFree(void); + +//extern int32_t PIOS_SDCARD_ReadBuffer(PFILEINFO fileinfo, uint8_t *buffer, uint32_t len); +//extern int32_t PIOS_SDCARD_ReadLine(PFILEINFO fileinfo, uint8_t *buffer, uint32_t max_len); +extern int32_t PIOS_SDCARD_FileCopy(char *Source, char *Destination); +extern int32_t PIOS_SDCARD_FileDelete(char *Filename); + +#endif + +#endif /* PIOS_SDCARD_H */ diff --git a/flight/PiOS.x86/inc/pios_sys.h b/flight/PiOS.x86/inc/pios_sys.h new file mode 100644 index 000000000..4e71b54a0 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_sys.h @@ -0,0 +1,35 @@ +/** + ****************************************************************************** + * + * @file pios_sys.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief System and hardware Init functions header. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_SYS_H +#define PIOS_SYS_H + +/* Public Functions */ +extern void PIOS_SYS_Init(void); +extern int32_t PIOS_SYS_Reset(void); +extern int32_t PIOS_SYS_SerialNumberGet(char *str); + +#endif /* PIOS_SYS_H */ diff --git a/flight/PiOS.x86/inc/pios_udp.h b/flight/PiOS.x86/inc/pios_udp.h new file mode 100644 index 000000000..7376ee6a1 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_udp.h @@ -0,0 +1,51 @@ +/** + ****************************************************************************** + * + * @file pios_usart.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief UDP functions header. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_UDP_H +#define PIOS_UDP_H + + +/* Global Types */ + +/* Public Functions */ +//extern void PIOS_UDP_Init(void); +void PIOS_UDP_Init(void); +extern void PIOS_UDP_ChangeBaud(uint8_t usart, uint32_t baud); + +extern int32_t PIOS_UDP_RxBufferFree(uint8_t usart); +extern int32_t PIOS_UDP_RxBufferUsed(uint8_t usart); +extern int32_t PIOS_UDP_RxBufferGet(uint8_t usart); +extern int32_t PIOS_UDP_RxBufferPeek(uint8_t usart); +extern int32_t PIOS_UDP_RxBufferPut(uint8_t usart, uint8_t b); + +extern int32_t PIOS_UDP_TxBufferFree(uint8_t usart); +extern int32_t PIOS_UDP_TxBufferGet(uint8_t usart); +extern int32_t PIOS_UDP_TxBufferPutMoreNonBlocking(uint8_t usart, uint8_t *buffer, uint16_t len); +extern int32_t PIOS_UDP_TxBufferPutMore(uint8_t usart, uint8_t *buffer, uint16_t len); +extern int32_t PIOS_UDP_TxBufferPutNonBlocking(uint8_t usart, uint8_t b); +extern int32_t PIOS_UDP_TxBufferPut(uint8_t usart, uint8_t b); + +#endif /* PIOS_UDP_H */ diff --git a/flight/PiOS.x86/inc/pios_udp_priv.h b/flight/PiOS.x86/inc/pios_udp_priv.h new file mode 100644 index 000000000..8f714cc56 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_udp_priv.h @@ -0,0 +1,66 @@ +/** + ****************************************************************************** + * + * @file pios_udp_priv.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief UDP private definitions. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_UDP_PRIV_H +#define PIOS_UDP_PRIV_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +struct pios_udp_cfg { + const char * ip; + uint16_t port; +}; + +struct pios_udp_buffer { + uint8_t buf[PIOS_UDP_RX_BUFFER_SIZE]; + uint16_t head; + uint16_t tail; + uint16_t size; +}; + +struct pios_udp_dev { + const struct pios_udp_cfg * const cfg; + struct pios_udp_buffer rx; + int socket; + struct sockaddr_in server; + struct sockaddr_in client; + uint32_t clientLength; +}; + +extern struct pios_udp_dev pios_udp_devs[]; +extern uint8_t pios_udp_num_devices; + +#endif /* PIOS_UDP_PRIV_H */ diff --git a/flight/PiOS.x86/inc/pios_x86.h b/flight/PiOS.x86/inc/pios_x86.h new file mode 100644 index 000000000..ef281e5e1 --- /dev/null +++ b/flight/PiOS.x86/inc/pios_x86.h @@ -0,0 +1,45 @@ +/** + ****************************************************************************** + * + * @file x86.h + * @author Corvus Corax Copyright (C) 2010. + * @brief Definitions to run PiOS on x86 + * @see The GNU Public License (GPL) Version 2 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef PIOS_X86_H +#define PIOS_X86_H + +#include + +typedef __uint16_t uint16_t; +typedef __uint32_t uint32_t; +typedef __uint32_t u32; +typedef __uint8_t uint8_t; + +typedef __int16_t int16_t; +typedef __int32_t int32_t; +typedef __int8_t int8_t; + +#define FILEINFO FILE* + + + +#endif + diff --git a/flight/PiOS.x86/pios.h b/flight/PiOS.x86/pios.h new file mode 100644 index 000000000..47e16ce02 --- /dev/null +++ b/flight/PiOS.x86/pios.h @@ -0,0 +1,70 @@ +/** + ****************************************************************************** + * + * @file pios.h + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief Main PiOS header. + * - Central header for the project. + * @see The GNU Public License (GPL) Version 3 + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#ifndef PIOS_H +#define PIOS_H + +/* PIOS Feature Selection */ +#include "pios_config.h" +#include + +#if defined(PIOS_INCLUDE_FREERTOS) +/* FreeRTOS Includes */ +#include "FreeRTOS.h" +#include "task.h" +#include "queue.h" +#include "semphr.h" +#endif + +/* C Lib Includes */ +#include +#include +#include +#include +#include + +#if defined(PIOS_INCLUDE_SDCARD) + +/* minIni Includes */ +#include + +#endif + +/* PIOS Board Specific Device Configuration */ +#include "pios_board_x86.h" + +/* PIOS Hardware Includes (x86) */ +#include +#include +#include +#include +#include +#include + +#define NELEMENTS(x) (sizeof(x) / sizeof(*(x))) + +#endif /* PIOS_H */ diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/croutine.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/croutine.c new file mode 100644 index 000000000..d6f116262 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/croutine.c @@ -0,0 +1,371 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" + +/* + * Some kernel aware debuggers require data to be viewed to be global, rather + * than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + + +/* Lists for ready and blocked co-routines. --------------------*/ +static xList pxReadyCoRoutineLists[ configMAX_CO_ROUTINE_PRIORITIES ]; /*< Prioritised ready co-routines. */ +static xList xDelayedCoRoutineList1; /*< Delayed co-routines. */ +static xList xDelayedCoRoutineList2; /*< Delayed co-routines (two lists are used - one for delays that have overflowed the current tick count. */ +static xList * pxDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used. */ +static xList * pxOverflowDelayedCoRoutineList; /*< Points to the delayed co-routine list currently being used to hold co-routines that have overflowed the current tick count. */ +static xList xPendingReadyCoRoutineList; /*< Holds co-routines that have been readied by an external event. They cannot be added directly to the ready lists as the ready lists cannot be accessed by interrupts. */ + +/* Other file private variables. --------------------------------*/ +corCRCB * pxCurrentCoRoutine = NULL; +static unsigned portBASE_TYPE uxTopCoRoutineReadyPriority = 0; +static portTickType xCoRoutineTickCount = 0, xLastTickCount = 0, xPassedTicks = 0; + +/* The initial state of the co-routine when it is created. */ +#define corINITIAL_STATE ( 0 ) + +/* + * Place the co-routine represented by pxCRCB into the appropriate ready queue + * for the priority. It is inserted at the end of the list. + * + * This macro accesses the co-routine ready lists and therefore must not be + * used from within an ISR. + */ +#define prvAddCoRoutineToReadyQueue( pxCRCB ) \ +{ \ + if( pxCRCB->uxPriority > uxTopCoRoutineReadyPriority ) \ + { \ + uxTopCoRoutineReadyPriority = pxCRCB->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyCoRoutineLists[ pxCRCB->uxPriority ] ), &( pxCRCB->xGenericListItem ) ); \ +} + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first co-routine. + */ +static void prvInitialiseCoRoutineLists( void ); + +/* + * Co-routines that are readied by an interrupt cannot be placed directly into + * the ready lists (there is no mutual exclusion). Instead they are placed in + * in the pending ready list in order that they can later be moved to the ready + * list by the co-routine scheduler. + */ +static void prvCheckPendingReadyList( void ); + +/* + * Macro that looks at the list of co-routines that are currently delayed to + * see if any require waking. + * + * Co-routines are stored in the queue in the order of their wake time - + * meaning once one co-routine has been found whose timer has not expired + * we need not look any further down the list. + */ +static void prvCheckDelayedList( void ); + +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ) +{ +signed portBASE_TYPE xReturn; +corCRCB *pxCoRoutine; + + /* Allocate the memory that will store the co-routine control block. */ + pxCoRoutine = ( corCRCB * ) pvPortMalloc( sizeof( corCRCB ) ); + if( pxCoRoutine ) + { + /* If pxCurrentCoRoutine is NULL then this is the first co-routine to + be created and the co-routine data structures need initialising. */ + if( pxCurrentCoRoutine == NULL ) + { + pxCurrentCoRoutine = pxCoRoutine; + prvInitialiseCoRoutineLists(); + } + + /* Check the priority is within limits. */ + if( uxPriority >= configMAX_CO_ROUTINE_PRIORITIES ) + { + uxPriority = configMAX_CO_ROUTINE_PRIORITIES - 1; + } + + /* Fill out the co-routine control block from the function parameters. */ + pxCoRoutine->uxState = corINITIAL_STATE; + pxCoRoutine->uxPriority = uxPriority; + pxCoRoutine->uxIndex = uxIndex; + pxCoRoutine->pxCoRoutineFunction = pxCoRoutineCode; + + /* Initialise all the other co-routine control block parameters. */ + vListInitialiseItem( &( pxCoRoutine->xGenericListItem ) ); + vListInitialiseItem( &( pxCoRoutine->xEventListItem ) ); + + /* Set the co-routine control block as a link back from the xListItem. + This is so we can get back to the containing CRCB from a generic item + in a list. */ + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xGenericListItem ), pxCoRoutine ); + listSET_LIST_ITEM_OWNER( &( pxCoRoutine->xEventListItem ), pxCoRoutine ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxCoRoutine->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + + /* Now the co-routine has been initialised it can be added to the ready + list at the correct priority. */ + prvAddCoRoutineToReadyQueue( pxCoRoutine ); + + xReturn = pdPASS; + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ) +{ +portTickType xTimeToWake; + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xCoRoutineTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentCoRoutine->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xCoRoutineTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedCoRoutineList, ( xListItem * ) &( pxCurrentCoRoutine->xGenericListItem ) ); + } + + if( pxEventList ) + { + /* Also add the co-routine to an event list. If this is done then the + function must be called with interrupts disabled. */ + vListInsert( pxEventList, &( pxCurrentCoRoutine->xEventListItem ) ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckPendingReadyList( void ) +{ + /* Are there any co-routines waiting to get moved to the ready list? These + are co-routines that have been readied by an ISR. The ISR cannot access + the ready lists itself. */ + while( !listLIST_IS_EMPTY( &xPendingReadyCoRoutineList ) ) + { + corCRCB *pxUnblockedCRCB; + + /* The pending ready list can be accessed by an ISR. */ + portDISABLE_INTERRUPTS(); + { + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( (&xPendingReadyCoRoutineList) ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + } + portENABLE_INTERRUPTS(); + + vListRemove( &( pxUnblockedCRCB->xGenericListItem ) ); + prvAddCoRoutineToReadyQueue( pxUnblockedCRCB ); + } +} +/*-----------------------------------------------------------*/ + +static void prvCheckDelayedList( void ) +{ +corCRCB *pxCRCB; + + xPassedTicks = xTaskGetTickCount() - xLastTickCount; + while( xPassedTicks ) + { + xCoRoutineTickCount++; + xPassedTicks--; + + /* If the tick count has overflowed we need to swap the ready lists. */ + if( xCoRoutineTickCount == 0 ) + { + xList * pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. If there are + any items in pxDelayedCoRoutineList here then there is an error! */ + pxTemp = pxDelayedCoRoutineList; + pxDelayedCoRoutineList = pxOverflowDelayedCoRoutineList; + pxOverflowDelayedCoRoutineList = pxTemp; + } + + /* See if this tick has made a timeout expire. */ + while( ( pxCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedCoRoutineList ) ) != NULL ) + { + if( xCoRoutineTickCount < listGET_LIST_ITEM_VALUE( &( pxCRCB->xGenericListItem ) ) ) + { + /* Timeout not yet expired. */ + break; + } + + portDISABLE_INTERRUPTS(); + { + /* The event could have occurred just before this critical + section. If this is the case then the generic list item will + have been moved to the pending ready list and the following + line is still valid. Also the pvContainer parameter will have + been set to NULL so the following lines are also valid. */ + vListRemove( &( pxCRCB->xGenericListItem ) ); + + /* Is the co-routine waiting on an event also? */ + if( pxCRCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxCRCB->xEventListItem ) ); + } + } + portENABLE_INTERRUPTS(); + + prvAddCoRoutineToReadyQueue( pxCRCB ); + } + } + + xLastTickCount = xCoRoutineTickCount; +} +/*-----------------------------------------------------------*/ + +void vCoRoutineSchedule( void ) +{ + /* See if any co-routines readied by events need moving to the ready lists. */ + prvCheckPendingReadyList(); + + /* See if any delayed co-routines have timed out. */ + prvCheckDelayedList(); + + /* Find the highest priority queue that contains ready co-routines. */ + while( listLIST_IS_EMPTY( &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ) ) + { + if( uxTopCoRoutineReadyPriority == 0 ) + { + /* No more co-routines to check. */ + return; + } + --uxTopCoRoutineReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the co-routines + of the same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentCoRoutine, &( pxReadyCoRoutineLists[ uxTopCoRoutineReadyPriority ] ) ); + + /* Call the co-routine. */ + ( pxCurrentCoRoutine->pxCoRoutineFunction )( pxCurrentCoRoutine, pxCurrentCoRoutine->uxIndex ); + + return; +} +/*-----------------------------------------------------------*/ + +static void prvInitialiseCoRoutineLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_CO_ROUTINE_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyCoRoutineLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedCoRoutineList1 ); + vListInitialise( ( xList * ) &xDelayedCoRoutineList2 ); + vListInitialise( ( xList * ) &xPendingReadyCoRoutineList ); + + /* Start with pxDelayedCoRoutineList using list1 and the + pxOverflowDelayedCoRoutineList using list2. */ + pxDelayedCoRoutineList = &xDelayedCoRoutineList1; + pxOverflowDelayedCoRoutineList = &xDelayedCoRoutineList2; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ) +{ +corCRCB *pxUnblockedCRCB; +signed portBASE_TYPE xReturn; + + /* This function is called from within an interrupt. It can only access + event lists and the pending ready list. */ + pxUnblockedCRCB = ( corCRCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + vListRemove( &( pxUnblockedCRCB->xEventListItem ) ); + vListInsertEnd( ( xList * ) &( xPendingReadyCoRoutineList ), &( pxUnblockedCRCB->xEventListItem ) ); + + if( pxUnblockedCRCB->uxPriority >= pxCurrentCoRoutine->uxPriority ) + { + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/FreeRTOS.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/FreeRTOS.h new file mode 100644 index 000000000..ee02592ae --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/FreeRTOS.h @@ -0,0 +1,420 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef INC_FREERTOS_H +#define INC_FREERTOS_H + + +/* + * Include the generic headers required for the FreeRTOS port being used. + */ +#include + +/* Basic FreeRTOS definitions. */ +#include "projdefs.h" + +/* Application specific configuration options. */ +#include "FreeRTOSConfig.h" + +/* Definitions specific to the port being used. */ +#include "portable.h" + + +/* Defines the prototype to which the application task hook function must +conform. */ +typedef portBASE_TYPE (*pdTASK_HOOK_CODE)( void * ); + + + + + +/* + * Check all the required application specific macros have been defined. + * These macros are application specific and (as downloaded) are defined + * within FreeRTOSConfig.h. + */ + +#ifndef configUSE_PREEMPTION + #error Missing definition: configUSE_PREEMPTION should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_IDLE_HOOK + #error Missing definition: configUSE_IDLE_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_TICK_HOOK + #error Missing definition: configUSE_TICK_HOOK should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_CO_ROUTINES + #error Missing definition: configUSE_CO_ROUTINES should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskPrioritySet + #error Missing definition: INCLUDE_vTaskPrioritySet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_uxTaskPriorityGet + #error Missing definition: INCLUDE_uxTaskPriorityGet should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelete + #error Missing definition: INCLUDE_vTaskDelete should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskCleanUpResources + #error Missing definition: INCLUDE_vTaskCleanUpResources should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskSuspend + #error Missing definition: INCLUDE_vTaskSuspend should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelayUntil + #error Missing definition: INCLUDE_vTaskDelayUntil should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef INCLUDE_vTaskDelay + #error Missing definition: INCLUDE_vTaskDelay should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_16_BIT_TICKS + #error Missing definition: configUSE_16_BIT_TICKS should be defined in FreeRTOSConfig.h as either 1 or 0. See the Configuration section of the FreeRTOS API documentation for details. +#endif + +#ifndef configUSE_APPLICATION_TASK_TAG + #define configUSE_APPLICATION_TASK_TAG 0 +#endif + +#ifndef INCLUDE_uxTaskGetStackHighWaterMark + #define INCLUDE_uxTaskGetStackHighWaterMark 0 +#endif + +#ifndef configUSE_RECURSIVE_MUTEXES + #define configUSE_RECURSIVE_MUTEXES 0 +#endif + +#ifndef configUSE_MUTEXES + #define configUSE_MUTEXES 0 +#endif + +#ifndef configUSE_COUNTING_SEMAPHORES + #define configUSE_COUNTING_SEMAPHORES 0 +#endif + +#ifndef configUSE_ALTERNATIVE_API + #define configUSE_ALTERNATIVE_API 0 +#endif + +#ifndef portCRITICAL_NESTING_IN_TCB + #define portCRITICAL_NESTING_IN_TCB 0 +#endif + +#ifndef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 16 +#endif + +#ifndef configIDLE_SHOULD_YIELD + #define configIDLE_SHOULD_YIELD 1 +#endif + +#if configMAX_TASK_NAME_LEN < 1 + #undef configMAX_TASK_NAME_LEN + #define configMAX_TASK_NAME_LEN 1 +#endif + +#ifndef INCLUDE_xTaskResumeFromISR + #define INCLUDE_xTaskResumeFromISR 1 +#endif + +#ifndef INCLUDE_xTaskGetSchedulerState + #define INCLUDE_xTaskGetSchedulerState 0 +#endif + +#if ( configUSE_MUTEXES == 1 ) + /* xTaskGetCurrentTaskHandle is used by the priority inheritance mechanism + within the mutex implementation so must be available if mutexes are used. */ + #undef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 1 +#else + #ifndef INCLUDE_xTaskGetCurrentTaskHandle + #define INCLUDE_xTaskGetCurrentTaskHandle 0 + #endif +#endif + + +#ifndef portSET_INTERRUPT_MASK_FROM_ISR + #define portSET_INTERRUPT_MASK_FROM_ISR() 0 +#endif + +#ifndef portCLEAR_INTERRUPT_MASK_FROM_ISR + #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedStatusValue ) ( void ) uxSavedStatusValue +#endif + + +#ifndef configQUEUE_REGISTRY_SIZE + #define configQUEUE_REGISTRY_SIZE 0 +#endif + +#if configQUEUE_REGISTRY_SIZE < 1 + #define configQUEUE_REGISTRY_SIZE 0 + #define vQueueAddToRegistry( xQueue, pcName ) + #define vQueueUnregisterQueue( xQueue ) +#endif + + +/* Remove any unused trace macros. */ +#ifndef traceSTART + /* Used to perform any necessary initialisation - for example, open a file + into which trace is to be written. */ + #define traceSTART() +#endif + +#ifndef traceEND + /* Use to close a trace, for example close a file into which trace has been + written. */ + #define traceEND() +#endif + +#ifndef traceTASK_SWITCHED_IN + /* Called after a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the selected task. */ + #define traceTASK_SWITCHED_IN() +#endif + +#ifndef traceTASK_SWITCHED_OUT + /* Called before a task has been selected to run. pxCurrentTCB holds a pointer + to the task control block of the task being switched out. */ + #define traceTASK_SWITCHED_OUT() +#endif + +#ifndef traceBLOCKING_ON_QUEUE_RECEIVE + /* Task is about to block because it cannot read from a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the read was attempted. pxCurrentTCB points to the TCB of the + task that attempted the read. */ + #define traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceBLOCKING_ON_QUEUE_SEND + /* Task is about to block because it cannot write to a + queue/mutex/semaphore. pxQueue is a pointer to the queue/mutex/semaphore + upon which the write was attempted. pxCurrentTCB points to the TCB of the + task that attempted the write. */ + #define traceBLOCKING_ON_QUEUE_SEND( pxQueue ) +#endif + +#ifndef configCHECK_FOR_STACK_OVERFLOW + #define configCHECK_FOR_STACK_OVERFLOW 0 +#endif + +/* The following event macros are embedded in the kernel API calls. */ + +#ifndef traceQUEUE_CREATE + #define traceQUEUE_CREATE( pxNewQueue ) +#endif + +#ifndef traceQUEUE_CREATE_FAILED + #define traceQUEUE_CREATE_FAILED() +#endif + +#ifndef traceCREATE_MUTEX + #define traceCREATE_MUTEX( pxNewQueue ) +#endif + +#ifndef traceCREATE_MUTEX_FAILED + #define traceCREATE_MUTEX_FAILED() +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE + #define traceGIVE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceGIVE_MUTEX_RECURSIVE_FAILED + #define traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ) +#endif + +#ifndef traceTAKE_MUTEX_RECURSIVE + #define traceTAKE_MUTEX_RECURSIVE( pxMutex ) +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE + #define traceCREATE_COUNTING_SEMAPHORE() +#endif + +#ifndef traceCREATE_COUNTING_SEMAPHORE_FAILED + #define traceCREATE_COUNTING_SEMAPHORE_FAILED() +#endif + +#ifndef traceQUEUE_SEND + #define traceQUEUE_SEND( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FAILED + #define traceQUEUE_SEND_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE + #define traceQUEUE_RECEIVE( pxQueue ) +#endif + +#ifndef traceQUEUE_PEEK + #define traceQUEUE_PEEK( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FAILED + #define traceQUEUE_RECEIVE_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR + #define traceQUEUE_SEND_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_SEND_FROM_ISR_FAILED + #define traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR + #define traceQUEUE_RECEIVE_FROM_ISR( pxQueue ) +#endif + +#ifndef traceQUEUE_RECEIVE_FROM_ISR_FAILED + #define traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ) +#endif + +#ifndef traceQUEUE_DELETE + #define traceQUEUE_DELETE( pxQueue ) +#endif + +#ifndef traceTASK_CREATE + #define traceTASK_CREATE( pxNewTCB ) +#endif + +#ifndef traceTASK_CREATE_FAILED + #define traceTASK_CREATE_FAILED( pxNewTCB ) +#endif + +#ifndef traceTASK_DELETE + #define traceTASK_DELETE( pxTaskToDelete ) +#endif + +#ifndef traceTASK_DELAY_UNTIL + #define traceTASK_DELAY_UNTIL() +#endif + +#ifndef traceTASK_DELAY + #define traceTASK_DELAY() +#endif + +#ifndef traceTASK_PRIORITY_SET + #define traceTASK_PRIORITY_SET( pxTask, uxNewPriority ) +#endif + +#ifndef traceTASK_SUSPEND + #define traceTASK_SUSPEND( pxTaskToSuspend ) +#endif + +#ifndef traceTASK_RESUME + #define traceTASK_RESUME( pxTaskToResume ) +#endif + +#ifndef traceTASK_RESUME_FROM_ISR + #define traceTASK_RESUME_FROM_ISR( pxTaskToResume ) +#endif + +#ifndef traceTASK_INCREMENT_TICK + #define traceTASK_INCREMENT_TICK( xTickCount ) +#endif + +#ifndef configGENERATE_RUN_TIME_STATS + #define configGENERATE_RUN_TIME_STATS 0 +#endif + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + #ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #error If configGENERATE_RUN_TIME_STATS is defined then portCONFIGURE_TIMER_FOR_RUN_TIME_STATS must also be defined. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS should call a port layer function to setup a peripheral timer/counter that can then be used as the run time counter time base. + #endif /* portCONFIGURE_TIMER_FOR_RUN_TIME_STATS */ + + #ifndef portGET_RUN_TIME_COUNTER_VALUE + #error If configGENERATE_RUN_TIME_STATS is defined then portGET_RUN_TIME_COUNTER_VALUE must also be defined. portGET_RUN_TIME_COUNTER_VALUE should evaluate to the counter value of the timer/counter peripheral used as the run time counter time base. + #endif /* portGET_RUN_TIME_COUNTER_VALUE */ + +#endif /* configGENERATE_RUN_TIME_STATS */ + +#ifndef portCONFIGURE_TIMER_FOR_RUN_TIME_STATS + #define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#endif + +#ifndef configUSE_MALLOC_FAILED_HOOK + #define configUSE_MALLOC_FAILED_HOOK 0 +#endif + +#ifndef portPRIVILEGE_BIT + #define portPRIVILEGE_BIT ( ( unsigned portBASE_TYPE ) 0x00 ) +#endif + +#ifndef portYIELD_WITHIN_API + #define portYIELD_WITHIN_API portYIELD +#endif + +#ifndef pvPortMallocAligned + #define pvPortMallocAligned( x, puxStackBuffer ) ( ( puxStackBuffer == NULL ) ? ( pvPortMalloc( x ) ) : ( puxStackBuffer ) ) +#endif + +#ifndef vPortFreeAligned + #define vPortFreeAligned( pvBlockToFree ) vPortFree( pvBlockToFree ) +#endif + +#endif /* INC_FREERTOS_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/StackMacros.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/StackMacros.h new file mode 100644 index 000000000..a7514d790 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/StackMacros.h @@ -0,0 +1,173 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef STACK_MACROS_H +#define STACK_MACROS_H + +/* + * Call the stack overflow hook function if the stack of the task being swapped + * out is currently overflowed, or looks like it might have overflowed in the + * past. + * + * Setting configCHECK_FOR_STACK_OVERFLOW to 1 will cause the macro to check + * the current stack state only - comparing the current top of stack value to + * the stack limit. Setting configCHECK_FOR_STACK_OVERFLOW to greater than 1 + * will also cause the last few stack bytes to be checked to ensure the value + * to which the bytes were set when the task was created have not been + * overwritten. Note this second test does not guarantee that an overflowed + * stack will always be recognised. + */ + +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 0 ) + + /* FreeRTOSConfig.h is not set to check for stack overflows. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 0 */ +/*-----------------------------------------------------------*/ + +#if( configCHECK_FOR_STACK_OVERFLOW == 1 ) + + /* FreeRTOSConfig.h is only set to use the first method of + overflow checking. */ + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() + +#endif +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH < 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + extern void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName ); \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack <= pxCurrentTCB->pxStack ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW > 0 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 0 ) && ( portSTACK_GROWTH > 0 ) ) + + /* Only the current stack state is to be checked. */ + #define taskFIRST_CHECK_FOR_STACK_OVERFLOW() \ + { \ + extern void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName ); \ + \ + /* Is the currently saved stack pointer within the stack limit? */ \ + if( pxCurrentTCB->pxTopOfStack >= pxCurrentTCB->pxEndOfStack ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* configCHECK_FOR_STACK_OVERFLOW == 1 */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH < 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + extern void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName ); \ + static const unsigned char ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pxCurrentTCB->pxStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#if( ( configCHECK_FOR_STACK_OVERFLOW > 1 ) && ( portSTACK_GROWTH > 0 ) ) + + #define taskSECOND_CHECK_FOR_STACK_OVERFLOW() \ + { \ + extern void vApplicationStackOverflowHook( xTaskHandle *pxTask, signed char *pcTaskName ); \ + char *pcEndOfStack = ( char * ) pxCurrentTCB->pxEndOfStack; \ + static const unsigned char ucExpectedStackBytes[] = { tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, \ + tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE, tskSTACK_FILL_BYTE }; \ + \ + \ + pcEndOfStack -= sizeof( ucExpectedStackBytes ); \ + \ + /* Has the extremity of the task stack ever been written over? */ \ + if( memcmp( ( void * ) pcEndOfStack, ( void * ) ucExpectedStackBytes, sizeof( ucExpectedStackBytes ) ) != 0 ) \ + { \ + vApplicationStackOverflowHook( ( xTaskHandle ) pxCurrentTCB, pxCurrentTCB->pcTaskName ); \ + } \ + } + +#endif /* #if( configCHECK_FOR_STACK_OVERFLOW > 1 ) */ +/*-----------------------------------------------------------*/ + +#endif /* STACK_MACROS_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/croutine.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/croutine.h new file mode 100644 index 000000000..c189a1e87 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/croutine.h @@ -0,0 +1,749 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include croutine.h" +#endif + + + + +#ifndef CO_ROUTINE_H +#define CO_ROUTINE_H + +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/* Used to hide the implementation of the co-routine control block. The +control block structure however has to be included in the header due to +the macro implementation of the co-routine functionality. */ +typedef void * xCoRoutineHandle; + +/* Defines the prototype to which co-routine functions must conform. */ +typedef void (*crCOROUTINE_CODE)( xCoRoutineHandle, unsigned portBASE_TYPE ); + +typedef struct corCoRoutineControlBlock +{ + crCOROUTINE_CODE pxCoRoutineFunction; + xListItem xGenericListItem; /*< List item used to place the CRCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the CRCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the co-routine in relation to other co-routines. */ + unsigned portBASE_TYPE uxIndex; /*< Used to distinguish between co-routines when multiple co-routines use the same co-routine function. */ + unsigned short uxState; /*< Used internally by the co-routine implementation. */ +} corCRCB; /* Co-routine control block. Note must be identical in size down to uxPriority with tskTCB. */ + +/** + * croutine. h + *
+ portBASE_TYPE xCoRoutineCreate(
+                                 crCOROUTINE_CODE pxCoRoutineCode,
+                                 unsigned portBASE_TYPE uxPriority,
+                                 unsigned portBASE_TYPE uxIndex
+                               );
+ * + * Create a new co-routine and add it to the list of co-routines that are + * ready to run. + * + * @param pxCoRoutineCode Pointer to the co-routine function. Co-routine + * functions require special syntax - see the co-routine section of the WEB + * documentation for more information. + * + * @param uxPriority The priority with respect to other co-routines at which + * the co-routine will run. + * + * @param uxIndex Used to distinguish between different co-routines that + * execute the same function. See the example below and the co-routine section + * of the WEB documentation for further information. + * + * @return pdPASS if the co-routine was successfully created and added to a ready + * list, otherwise an error code defined with ProjDefs.h. + * + * Example usage: +
+ // Co-routine to be created.
+ void vFlashCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ static const char cLedToFlash[ 2 ] = { 5, 6 };
+ static const portTickType xTimeToDelay[ 2 ] = { 200, 400 };
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // This co-routine just delays for a fixed period, then toggles
+         // an LED.  Two co-routines are created using this function, so
+         // the uxIndex parameter is used to tell the co-routine which
+         // LED to flash and how long to delay.  This assumes xQueue has
+         // already been created.
+         vParTestToggleLED( cLedToFlash[ uxIndex ] );
+         crDELAY( xHandle, uxFlashRates[ uxIndex ] );
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+
+ // Function that creates two co-routines.
+ void vOtherFunction( void )
+ {
+ unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+		
+     // Create two co-routines at priority 0.  The first is given index 0
+     // so (from the code above) toggles LED 5 every 200 ticks.  The second
+     // is given index 1 so toggles LED 6 every 400 ticks.
+     for( uxIndex = 0; uxIndex < 2; uxIndex++ )
+     {
+         xCoRoutineCreate( vFlashCoRoutine, 0, uxIndex );
+     }
+ }
+   
+ * \defgroup xCoRoutineCreate xCoRoutineCreate + * \ingroup Tasks + */ +signed portBASE_TYPE xCoRoutineCreate( crCOROUTINE_CODE pxCoRoutineCode, unsigned portBASE_TYPE uxPriority, unsigned portBASE_TYPE uxIndex ); + + +/** + * croutine. h + *
+ void vCoRoutineSchedule( void );
+ * + * Run a co-routine. + * + * vCoRoutineSchedule() executes the highest priority co-routine that is able + * to run. The co-routine will execute until it either blocks, yields or is + * preempted by a task. Co-routines execute cooperatively so one + * co-routine cannot be preempted by another, but can be preempted by a task. + * + * If an application comprises of both tasks and co-routines then + * vCoRoutineSchedule should be called from the idle task (in an idle task + * hook). + * + * Example usage: +
+ // This idle task hook will schedule a co-routine each time it is called.
+ // The rest of the idle task will execute between co-routine calls.
+ void vApplicationIdleHook( void )
+ {
+	vCoRoutineSchedule();
+ }
+
+ // Alternatively, if you do not require any other part of the idle task to
+ // execute, the idle task hook can call vCoRoutineScheduler() within an
+ // infinite loop.
+ void vApplicationIdleHook( void )
+ {
+    for( ;; )
+    {
+        vCoRoutineSchedule();
+    }
+ }
+ 
+ * \defgroup vCoRoutineSchedule vCoRoutineSchedule + * \ingroup Tasks + */ +void vCoRoutineSchedule( void ); + +/** + * croutine. h + *
+ crSTART( xCoRoutineHandle xHandle );
+ * + * This macro MUST always be called at the start of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static long ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crSTART( pxCRCB ) switch( ( ( corCRCB * )pxCRCB )->uxState ) { case 0: + +/** + * croutine. h + *
+ crEND();
+ * + * This macro MUST always be called at the end of a co-routine function. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static long ulAVariable;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+          // Co-routine functionality goes here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crSTART crSTART + * \ingroup Tasks + */ +#define crEND() } + +/* + * These macros are intended for internal use by the co-routine implementation + * only. The macros should not be used directly by application writers. + */ +#define crSET_STATE0( xHandle ) ( ( corCRCB * )xHandle)->uxState = (__LINE__ * 2); return; case (__LINE__ * 2): +#define crSET_STATE1( xHandle ) ( ( corCRCB * )xHandle)->uxState = ((__LINE__ * 2)+1); return; case ((__LINE__ * 2)+1): + +/** + * croutine. h + *
+ crDELAY( xCoRoutineHandle xHandle, portTickType xTicksToDelay );
+ * + * Delay a co-routine for a fixed period of time. + * + * crDELAY can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * @param xHandle The handle of the co-routine to delay. This is the xHandle + * parameter of the co-routine function. + * + * @param xTickToDelay The number of ticks that the co-routine should delay + * for. The actual amount of time this equates to is defined by + * configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant portTICK_RATE_MS + * can be used to convert ticks to milliseconds. + * + * Example usage: +
+ // Co-routine to be created.
+ void vACoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ // This may not be necessary for const variables.
+ // We are to delay for 200ms.
+ static const xTickType xDelayTime = 200 / portTICK_RATE_MS;
+
+     // Must start every co-routine with a call to crSTART();
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+        // Delay for 200ms.
+        crDELAY( xHandle, xDelayTime );
+
+        // Do something here.
+     }
+
+     // Must end every co-routine with a call to crEND();
+     crEND();
+ }
+ * \defgroup crDELAY crDELAY + * \ingroup Tasks + */ +#define crDELAY( xHandle, xTicksToDelay ) \ + if( xTicksToDelay > 0 ) \ + { \ + vCoRoutineAddToDelayedList( xTicksToDelay, NULL ); \ + } \ + crSET_STATE0( xHandle ); + +/** + *
+ crQUEUE_SEND(
+                  xCoRoutineHandle xHandle,
+                  xQueueHandle pxQueue,
+                  void *pvItemToQueue,
+                  portTickType xTicksToWait,
+                  portBASE_TYPE *pxResult
+             )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_SEND can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue on which the data will be posted. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvItemToQueue A pointer to the data being posted onto the queue. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied from pvItemToQueue into the queue + * itself. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for space to become available on the queue, should space not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see example + * below). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully posted onto the queue, otherwise it will be set to an + * error defined within ProjDefs.h. + * + * Example usage: +
+ // Co-routine function that blocks for a fixed period then posts a number onto
+ // a queue.
+ static void prvCoRoutineFlashTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xNumberToPost = 0;
+ static portBASE_TYPE xResult;
+
+    // Co-routines must begin with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // This assumes the queue has already been created.
+        crQUEUE_SEND( xHandle, xCoRoutineQueue, &xNumberToPost, NO_DELAY, &xResult );
+
+        if( xResult != pdPASS )
+        {
+            // The message was not posted!
+        }
+
+        // Increment the number to be posted onto the queue.
+        xNumberToPost++;
+
+        // Delay for 100 ticks.
+        crDELAY( xHandle, 100 );
+    }
+
+    // Co-routines must end with a call to crEND().
+    crEND();
+ }
+ * \defgroup crQUEUE_SEND crQUEUE_SEND + * \ingroup Tasks + */ +#define crQUEUE_SEND( xHandle, pxQueue, pvItemToQueue, xTicksToWait, pxResult ) \ +{ \ + *pxResult = xQueueCRSend( pxQueue, pvItemToQueue, xTicksToWait ); \ + if( *pxResult == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( xHandle ); \ + *pxResult = xQueueCRSend( pxQueue, pvItemToQueue, 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( xHandle ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_RECEIVE(
+                     xCoRoutineHandle xHandle,
+                     xQueueHandle pxQueue,
+                     void *pvBuffer,
+                     portTickType xTicksToWait,
+                     portBASE_TYPE *pxResult
+                 )
+ * + * The macro's crQUEUE_SEND() and crQUEUE_RECEIVE() are the co-routine + * equivalent to the xQueueSend() and xQueueReceive() functions used by tasks. + * + * crQUEUE_SEND and crQUEUE_RECEIVE can only be used from a co-routine whereas + * xQueueSend() and xQueueReceive() can only be used from tasks. + * + * crQUEUE_RECEIVE can only be called from the co-routine function itself - not + * from within a function called by the co-routine function. This is because + * co-routines do not maintain their own stack. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xHandle The handle of the calling co-routine. This is the xHandle + * parameter of the co-routine function. + * + * @param pxQueue The handle of the queue from which the data will be received. + * The handle is obtained as the return value when the queue is created using + * the xQueueCreate() API function. + * + * @param pvBuffer The buffer into which the received item is to be copied. + * The number of bytes of each queued item is specified when the queue is + * created. This number of bytes is copied into pvBuffer. + * + * @param xTickToDelay The number of ticks that the co-routine should block + * to wait for data to become available from the queue, should data not be + * available immediately. The actual amount of time this equates to is defined + * by configTICK_RATE_HZ (set in FreeRTOSConfig.h). The constant + * portTICK_RATE_MS can be used to convert ticks to milliseconds (see the + * crQUEUE_SEND example). + * + * @param pxResult The variable pointed to by pxResult will be set to pdPASS if + * data was successfully retrieved from the queue, otherwise it will be set to + * an error code as defined within ProjDefs.h. + * + * Example usage: +
+ // A co-routine receives the number of an LED to flash from a queue.  It
+ // blocks on the queue until the number is received.
+ static void prvCoRoutineFlashWorkTask( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // Variables in co-routines must be declared static if they must maintain value across a blocking call.
+ static portBASE_TYPE xResult;
+ static unsigned portBASE_TYPE uxLEDToFlash;
+
+    // All co-routines must start with a call to crSTART().
+    crSTART( xHandle );
+
+    for( ;; )
+    {
+        // Wait for data to become available on the queue.
+        crQUEUE_RECEIVE( xHandle, xCoRoutineQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+        if( xResult == pdPASS )
+        {
+            // We received the LED to flash - flash it!
+            vParTestToggleLED( uxLEDToFlash );
+        }
+    }
+
+    crEND();
+ }
+ * \defgroup crQUEUE_RECEIVE crQUEUE_RECEIVE + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE( xHandle, pxQueue, pvBuffer, xTicksToWait, pxResult ) \ +{ \ + *pxResult = xQueueCRReceive( pxQueue, pvBuffer, xTicksToWait ); \ + if( *pxResult == errQUEUE_BLOCKED ) \ + { \ + crSET_STATE0( xHandle ); \ + *pxResult = xQueueCRReceive( pxQueue, pvBuffer, 0 ); \ + } \ + if( *pxResult == errQUEUE_YIELD ) \ + { \ + crSET_STATE1( xHandle ); \ + *pxResult = pdPASS; \ + } \ +} + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvItemToQueue,
+                            portBASE_TYPE xCoRoutinePreviouslyWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_SEND_FROM_ISR can only be called from an ISR to send data to a queue + * that is being used from within a co-routine. + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xCoRoutinePreviouslyWoken This is included so an ISR can post onto + * the same queue multiple times from a single interrupt. The first call + * should always pass in pdFALSE. Subsequent calls should pass in + * the value returned from the previous call. + * + * @return pdTRUE if a co-routine was woken by posting onto the queue. This is + * used by the ISR to determine if a context switch may be required following + * the ISR. + * + * Example usage: +
+ // A co-routine that blocks on a queue waiting for characters to be received.
+ static void vReceivingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ char cRxedChar;
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Wait for data to become available on the queue.  This assumes the
+         // queue xCommsRxQueue has already been created!
+         crQUEUE_RECEIVE( xHandle, xCommsRxQueue, &uxLEDToFlash, portMAX_DELAY, &xResult );
+
+         // Was a character received?
+         if( xResult == pdPASS )
+         {
+             // Process the character here.
+         }
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to send characters received on a serial port to
+ // a co-routine.
+ void vUART_ISR( void )
+ {
+ char cRxedChar;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     // We loop around reading characters until there are none left in the UART.
+     while( UART_RX_REG_NOT_EMPTY() )
+     {
+         // Obtain the character from the UART.
+         cRxedChar = UART_RX_REG;
+
+         // Post the character onto a queue.  xCRWokenByPost will be pdFALSE
+         // the first time around the loop.  If the post causes a co-routine
+         // to be woken (unblocked) then xCRWokenByPost will be set to pdTRUE.
+         // In this manner we can ensure that if more than one co-routine is
+         // blocked on the queue only one is woken by this ISR no matter how
+         // many characters are posted to the queue.
+         xCRWokenByPost = crQUEUE_SEND_FROM_ISR( xCommsRxQueue, &cRxedChar, xCRWokenByPost );
+     }
+ }
+ * \defgroup crQUEUE_SEND_FROM_ISR crQUEUE_SEND_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_SEND_FROM_ISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) xQueueCRSendFromISR( pxQueue, pvItemToQueue, xCoRoutinePreviouslyWoken ) + + +/** + * croutine. h + *
+  crQUEUE_SEND_FROM_ISR(
+                            xQueueHandle pxQueue,
+                            void *pvBuffer,
+                            portBASE_TYPE * pxCoRoutineWoken
+                       )
+ * + * The macro's crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() are the + * co-routine equivalent to the xQueueSendFromISR() and xQueueReceiveFromISR() + * functions used by tasks. + * + * crQUEUE_SEND_FROM_ISR() and crQUEUE_RECEIVE_FROM_ISR() can only be used to + * pass data between a co-routine and and ISR, whereas xQueueSendFromISR() and + * xQueueReceiveFromISR() can only be used to pass data between a task and and + * ISR. + * + * crQUEUE_RECEIVE_FROM_ISR can only be called from an ISR to receive data + * from a queue that is being used from within a co-routine (a co-routine + * posted to the queue). + * + * See the co-routine section of the WEB documentation for information on + * passing data between tasks and co-routines and between ISR's and + * co-routines. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvBuffer A pointer to a buffer into which the received item will be + * placed. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from the queue into + * pvBuffer. + * + * @param pxCoRoutineWoken A co-routine may be blocked waiting for space to become + * available on the queue. If crQUEUE_RECEIVE_FROM_ISR causes such a + * co-routine to unblock *pxCoRoutineWoken will get set to pdTRUE, otherwise + * *pxCoRoutineWoken will remain unchanged. + * + * @return pdTRUE an item was successfully received from the queue, otherwise + * pdFALSE. + * + * Example usage: +
+ // A co-routine that posts a character to a queue then blocks for a fixed
+ // period.  The character is incremented each time.
+ static void vSendingCoRoutine( xCoRoutineHandle xHandle, unsigned portBASE_TYPE uxIndex )
+ {
+ // cChar holds its value while this co-routine is blocked and must therefore
+ // be declared static.
+ static char cCharToTx = 'a';
+ portBASE_TYPE xResult;
+
+     // All co-routines must start with a call to crSTART().
+     crSTART( xHandle );
+
+     for( ;; )
+     {
+         // Send the next character to the queue.
+         crQUEUE_SEND( xHandle, xCoRoutineQueue, &cCharToTx, NO_DELAY, &xResult );
+
+         if( xResult == pdPASS )
+         {
+             // The character was successfully posted to the queue.
+         }
+		 else
+		 {
+			// Could not post the character to the queue.
+		 }
+
+         // Enable the UART Tx interrupt to cause an interrupt in this
+		 // hypothetical UART.  The interrupt will obtain the character
+		 // from the queue and send it.
+		 ENABLE_RX_INTERRUPT();
+
+		 // Increment to the next character then block for a fixed period.
+		 // cCharToTx will maintain its value across the delay as it is
+		 // declared static.
+		 cCharToTx++;
+		 if( cCharToTx > 'x' )
+		 {
+			cCharToTx = 'a';
+		 }
+		 crDELAY( 100 );
+     }
+
+     // All co-routines must end with a call to crEND().
+     crEND();
+ }
+
+ // An ISR that uses a queue to receive characters to send on a UART.
+ void vUART_ISR( void )
+ {
+ char cCharToTx;
+ portBASE_TYPE xCRWokenByPost = pdFALSE;
+
+     while( UART_TX_REG_EMPTY() )
+     {
+         // Are there any characters in the queue waiting to be sent?
+		 // xCRWokenByPost will automatically be set to pdTRUE if a co-routine
+		 // is woken by the post - ensuring that only a single co-routine is
+		 // woken no matter how many times we go around this loop.
+         if( crQUEUE_RECEIVE_FROM_ISR( pxQueue, &cCharToTx, &xCRWokenByPost ) )
+		 {
+			 SEND_CHARACTER( cCharToTx );
+		 }
+     }
+ }
+ * \defgroup crQUEUE_RECEIVE_FROM_ISR crQUEUE_RECEIVE_FROM_ISR + * \ingroup Tasks + */ +#define crQUEUE_RECEIVE_FROM_ISR( pxQueue, pvBuffer, pxCoRoutineWoken ) xQueueCRReceiveFromISR( pxQueue, pvBuffer, pxCoRoutineWoken ) + +/* + * This function is intended for internal use by the co-routine macros only. + * The macro nature of the co-routine implementation requires that the + * prototype appears here. The function should not be used by application + * writers. + * + * Removes the current co-routine from its ready list and places it in the + * appropriate delayed list. + */ +void vCoRoutineAddToDelayedList( portTickType xTicksToDelay, xList *pxEventList ); + +/* + * This function is intended for internal use by the queue implementation only. + * The function should not be used by application writers. + * + * Removes the highest priority co-routine from the event list and places it in + * the pending ready list. + */ +signed portBASE_TYPE xCoRoutineRemoveFromEventList( const xList *pxEventList ); + +#ifdef __cplusplus +} +#endif + +#endif /* CO_ROUTINE_H */ diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/list.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/list.h new file mode 100644 index 000000000..65712836e --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/list.h @@ -0,0 +1,305 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/* + * This is the list implementation used by the scheduler. While it is tailored + * heavily for the schedulers needs, it is also available for use by + * application code. + * + * xLists can only store pointers to xListItems. Each xListItem contains a + * numeric value (xItemValue). Most of the time the lists are sorted in + * descending item value order. + * + * Lists are created already containing one list item. The value of this + * item is the maximum possible that can be stored, it is therefore always at + * the end of the list and acts as a marker. The list member pxHead always + * points to this marker - even though it is at the tail of the list. This + * is because the tail contains a wrap back pointer to the true head of + * the list. + * + * In addition to it's value, each list item contains a pointer to the next + * item in the list (pxNext), a pointer to the list it is in (pxContainer) + * and a pointer to back to the object that contains it. These later two + * pointers are included for efficiency of list manipulation. There is + * effectively a two way link between the object containing the list item and + * the list item itself. + * + * + * \page ListIntroduction List Implementation + * \ingroup FreeRTOSIntro + */ + +/* + Changes from V4.3.1 + + + Included local const within listGET_OWNER_OF_NEXT_ENTRY() to assist + compiler with optimisation. Thanks B.R. +*/ + +#ifndef LIST_H +#define LIST_H + +#ifdef __cplusplus +extern "C" { +#endif +/* + * Definition of the only type of object that a list can contain. + */ +struct xLIST_ITEM +{ + portTickType xItemValue; /*< The value being listed. In most cases this is used to sort the list in descending order. */ + volatile struct xLIST_ITEM * pxNext; /*< Pointer to the next xListItem in the list. */ + volatile struct xLIST_ITEM * pxPrevious;/*< Pointer to the previous xListItem in the list. */ + void * pvOwner; /*< Pointer to the object (normally a TCB) that contains the list item. There is therefore a two way link between the object containing the list item and the list item itself. */ + void * pvContainer; /*< Pointer to the list in which this list item is placed (if any). */ +}; +typedef struct xLIST_ITEM xListItem; /* For some reason lint wants this as two separate definitions. */ + +struct xMINI_LIST_ITEM +{ + portTickType xItemValue; + volatile struct xLIST_ITEM *pxNext; + volatile struct xLIST_ITEM *pxPrevious; +}; +typedef struct xMINI_LIST_ITEM xMiniListItem; + +/* + * Definition of the type of queue used by the scheduler. + */ +typedef struct xLIST +{ + volatile unsigned portBASE_TYPE uxNumberOfItems; + volatile xListItem * pxIndex; /*< Used to walk through the list. Points to the last item returned by a call to pvListGetOwnerOfNextEntry (). */ + volatile xMiniListItem xListEnd; /*< List item that contains the maximum possible item value meaning it is always at the end of the list and is therefore used as a marker. */ +} xList; + +/* + * Access macro to set the owner of a list item. The owner of a list item + * is the object (usually a TCB) that contains the list item. + * + * \page listSET_LIST_ITEM_OWNER listSET_LIST_ITEM_OWNER + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_OWNER( pxListItem, pxOwner ) ( pxListItem )->pvOwner = ( void * ) pxOwner + +/* + * Access macro to set the value of the list item. In most cases the value is + * used to sort the list in descending order. + * + * \page listSET_LIST_ITEM_VALUE listSET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listSET_LIST_ITEM_VALUE( pxListItem, xValue ) ( pxListItem )->xItemValue = xValue + +/* + * Access macro the retrieve the value of the list item. The value can + * represent anything - for example a the priority of a task, or the time at + * which a task should be unblocked. + * + * \page listGET_LIST_ITEM_VALUE listGET_LIST_ITEM_VALUE + * \ingroup LinkedList + */ +#define listGET_LIST_ITEM_VALUE( pxListItem ) ( ( pxListItem )->xItemValue ) + +/* + * Access macro to determine if a list contains any items. The macro will + * only have the value true if the list is empty. + * + * \page listLIST_IS_EMPTY listLIST_IS_EMPTY + * \ingroup LinkedList + */ +#define listLIST_IS_EMPTY( pxList ) ( ( pxList )->uxNumberOfItems == ( unsigned portBASE_TYPE ) 0 ) + +/* + * Access macro to return the number of items in the list. + */ +#define listCURRENT_LIST_LENGTH( pxList ) ( ( pxList )->uxNumberOfItems ) + +/* + * Access function to obtain the owner of the next entry in a list. + * + * The list member pxIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pxIndex to the next item in the list + * and returns that entries pxOwner parameter. Using multiple calls to this + * function it is therefore possible to move through every item contained in + * a list. + * + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the next item owner is to be returned. + * + * \page listGET_OWNER_OF_NEXT_ENTRY listGET_OWNER_OF_NEXT_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_NEXT_ENTRY( pxTCB, pxList ) \ +{ \ +xList * const pxConstList = pxList; \ + /* Increment the index to the next item and return the item, ensuring */ \ + /* we don't return the marker used at the end of the list. */ \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + if( ( pxConstList )->pxIndex == ( xListItem * ) &( ( pxConstList )->xListEnd ) ) \ + { \ + ( pxConstList )->pxIndex = ( pxConstList )->pxIndex->pxNext; \ + } \ + pxTCB = ( pxConstList )->pxIndex->pvOwner; \ +} + + +/* + * Access function to obtain the owner of the first entry in a list. Lists + * are normally sorted in ascending item value order. + * + * This function returns the pxOwner member of the first item in the list. + * The pxOwner parameter of a list item is a pointer to the object that owns + * the list item. In the scheduler this is normally a task control block. + * The pxOwner parameter effectively creates a two way link between the list + * item and its owner. + * + * @param pxList The list from which the owner of the head item is to be + * returned. + * + * \page listGET_OWNER_OF_HEAD_ENTRY listGET_OWNER_OF_HEAD_ENTRY + * \ingroup LinkedList + */ +#define listGET_OWNER_OF_HEAD_ENTRY( pxList ) ( ( pxList->uxNumberOfItems != ( unsigned portBASE_TYPE ) 0 ) ? ( (&( pxList->xListEnd ))->pxNext->pvOwner ) : ( NULL ) ) + +/* + * Check to see if a list item is within a list. The list item maintains a + * "container" pointer that points to the list it is in. All this macro does + * is check to see if the container and the list match. + * + * @param pxList The list we want to know if the list item is within. + * @param pxListItem The list item we want to know if is in the list. + * @return pdTRUE is the list item is in the list, otherwise pdFALSE. + * pointer against + */ +#define listIS_CONTAINED_WITHIN( pxList, pxListItem ) ( ( pxListItem )->pvContainer == ( void * ) pxList ) + +/* + * Must be called before a list is used! This initialises all the members + * of the list structure and inserts the xListEnd item into the list as a + * marker to the back of the list. + * + * @param pxList Pointer to the list being initialised. + * + * \page vListInitialise vListInitialise + * \ingroup LinkedList + */ +void vListInitialise( xList *pxList ); + +/* + * Must be called before a list item is used. This sets the list container to + * null so the item does not think that it is already contained in a list. + * + * @param pxItem Pointer to the list item being initialised. + * + * \page vListInitialiseItem vListInitialiseItem + * \ingroup LinkedList + */ +void vListInitialiseItem( xListItem *pxItem ); + +/* + * Insert a list item into a list. The item will be inserted into the list in + * a position determined by its item value (descending item value order). + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The item to that is to be placed in the list. + * + * \page vListInsert vListInsert + * \ingroup LinkedList + */ +void vListInsert( xList *pxList, xListItem *pxNewListItem ); + +/* + * Insert a list item into a list. The item will be inserted in a position + * such that it will be the last item within the list returned by multiple + * calls to listGET_OWNER_OF_NEXT_ENTRY. + * + * The list member pvIndex is used to walk through a list. Calling + * listGET_OWNER_OF_NEXT_ENTRY increments pvIndex to the next item in the list. + * Placing an item in a list using vListInsertEnd effectively places the item + * in the list position pointed to by pvIndex. This means that every other + * item within the list will be returned by listGET_OWNER_OF_NEXT_ENTRY before + * the pvIndex parameter again points to the item being inserted. + * + * @param pxList The list into which the item is to be inserted. + * + * @param pxNewListItem The list item to be inserted into the list. + * + * \page vListInsertEnd vListInsertEnd + * \ingroup LinkedList + */ +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ); + +/* + * Remove an item from a list. The list item has a pointer to the list that + * it is in, so only the list item need be passed into the function. + * + * @param vListRemove The item to be removed. The item will remove itself from + * the list pointed to by it's pxContainer parameter. + * + * \page vListRemove vListRemove + * \ingroup LinkedList + */ +void vListRemove( xListItem *pxItemToRemove ); + +#ifdef __cplusplus +} +#endif + +#endif + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/mpu_wrappers.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/mpu_wrappers.h new file mode 100644 index 000000000..e197d5085 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/mpu_wrappers.h @@ -0,0 +1,135 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef MPU_WRAPPERS_H +#define MPU_WRAPPERS_H + +/* This file redefines API functions to be called through a wrapper macro, but +only for ports that are using the MPU. */ +#ifdef portUSING_MPU_WRAPPERS + + /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE will be defined when this file is + included from queue.c or task.c to prevent it from having an effect within + those files. */ + #ifndef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + + #define xTaskGenericCreate MPU_xTaskGenericCreate + #define vTaskAllocateMPURegions MPU_vTaskAllocateMPURegions + #define vTaskDelete MPU_vTaskDelete + #define vTaskDelayUntil MPU_vTaskDelayUntil + #define vTaskDelay MPU_vTaskDelay + #define uxTaskPriorityGet MPU_uxTaskPriorityGet + #define vTaskPrioritySet MPU_vTaskPrioritySet + #define vTaskSuspend MPU_vTaskSuspend + #define xTaskIsTaskSuspended MPU_xTaskIsTaskSuspended + #define vTaskResume MPU_vTaskResume + #define vTaskSuspendAll MPU_vTaskSuspendAll + #define xTaskResumeAll MPU_xTaskResumeAll + #define xTaskGetTickCount MPU_xTaskGetTickCount + #define uxTaskGetNumberOfTasks MPU_uxTaskGetNumberOfTasks + #define vTaskList MPU_vTaskList + #define vTaskGetRunTimeStats MPU_vTaskGetRunTimeStats + #define vTaskStartTrace MPU_vTaskStartTrace + #define ulTaskEndTrace MPU_ulTaskEndTrace + #define vTaskSetApplicationTaskTag MPU_vTaskSetApplicationTaskTag + #define xTaskGetApplicationTaskTag MPU_xTaskGetApplicationTaskTag + #define xTaskCallApplicationTaskHook MPU_xTaskCallApplicationTaskHook + #define uxTaskGetStackHighWaterMark MPU_uxTaskGetStackHighWaterMark + #define xTaskGetCurrentTaskHandle MPU_xTaskGetCurrentTaskHandle + #define xTaskGetSchedulerState MPU_xTaskGetSchedulerState + + #define xQueueCreate MPU_xQueueCreate + #define xQueueCreateMutex MPU_xQueueCreateMutex + #define xQueueGiveMutexRecursive MPU_xQueueGiveMutexRecursive + #define xQueueTakeMutexRecursive MPU_xQueueTakeMutexRecursive + #define xQueueCreateCountingSemaphore MPU_xQueueCreateCountingSemaphore + #define xQueueGenericSend MPU_xQueueGenericSend + #define xQueueAltGenericSend MPU_xQueueAltGenericSend + #define xQueueAltGenericReceive MPU_xQueueAltGenericReceive + #define xQueueGenericReceive MPU_xQueueGenericReceive + #define uxQueueMessagesWaiting MPU_uxQueueMessagesWaiting + #define vQueueDelete MPU_vQueueDelete + + #define pvPortMalloc MPU_pvPortMalloc + #define vPortFree MPU_vPortFree + #define xPortGetFreeHeapSize MPU_xPortGetFreeHeapSize + #define vPortInitialiseBlocks MPU_vPortInitialiseBlocks + + #if configQUEUE_REGISTRY_SIZE > 0 + #define vQueueAddToRegistry MPU_vQueueAddToRegistry + #define vQueueUnregisterQueue MPU_vQueueUnregisterQueue + #endif + + /* Remove the privileged function macro. */ + #define PRIVILEGED_FUNCTION + + #else /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + + /* Ensure API functions go in the privileged execution section. */ + #define PRIVILEGED_FUNCTION __attribute__((section("privileged_functions"))) + #define PRIVILEGED_DATA __attribute__((section("privileged_data"))) + //#define PRIVILEGED_DATA + + #endif /* MPU_WRAPPERS_INCLUDED_FROM_API_FILE */ + +#else /* portUSING_MPU_WRAPPERS */ + + #define PRIVILEGED_FUNCTION + #define PRIVILEGED_DATA + #define portUSING_MPU_WRAPPERS 0 + +#endif /* portUSING_MPU_WRAPPERS */ + + +#endif /* MPU_WRAPPERS_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/portable.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/portable.h new file mode 100644 index 000000000..b72a3f038 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/portable.h @@ -0,0 +1,391 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/*----------------------------------------------------------- + * Portable layer API. Each function must be defined for each port. + *----------------------------------------------------------*/ + +#ifndef PORTABLE_H +#define PORTABLE_H + +/* Include the macro file relevant to the port being used. */ + +#ifdef OPEN_WATCOM_INDUSTRIAL_PC_PORT + #include "..\..\Source\portable\owatcom\16bitdos\pc\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef OPEN_WATCOM_FLASH_LITE_186_PORT + #include "..\..\Source\portable\owatcom\16bitdos\flsh186\portmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef GCC_MEGA_AVR + #include "../portable/GCC/ATMega323/portmacro.h" +#endif + +#ifdef IAR_MEGA_AVR + #include "../portable/IAR/ATMega323/portmacro.h" +#endif + +#ifdef MPLAB_PIC24_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_DSPIC_PORT + #include "..\..\Source\portable\MPLAB\PIC24_dsPIC\portmacro.h" +#endif + +#ifdef MPLAB_PIC18F_PORT + #include "..\..\Source\portable\MPLAB\PIC18F\portmacro.h" +#endif + +#ifdef MPLAB_PIC32MX_PORT + #include "..\..\Source\portable\MPLAB\PIC32MX\portmacro.h" +#endif + +#ifdef _FEDPICC + #include "libFreeRTOS/Include/portmacro.h" +#endif + +#ifdef SDCC_CYGNAL + #include "../../Source/portable/SDCC/Cygnal/portmacro.h" +#endif + +#ifdef GCC_ARM7 + #include "../../Source/portable/GCC/ARM7_LPC2000/portmacro.h" +#endif + +#ifdef GCC_ARM7_ECLIPSE + #include "portmacro.h" +#endif + +#ifdef ROWLEY_LPC23xx + #include "../../Source/portable/GCC/ARM7_LPC23xx/portmacro.h" +#endif + +#ifdef IAR_MSP430 + #include "..\..\Source\portable\IAR\MSP430\portmacro.h" +#endif + +#ifdef GCC_MSP430 + #include "../../Source/portable/GCC/MSP430F449/portmacro.h" +#endif + +#ifdef ROWLEY_MSP430 + #include "../../Source/portable/Rowley/MSP430F449/portmacro.h" +#endif + +#ifdef ARM7_LPC21xx_KEIL_RVDS + #include "..\..\Source\portable\RVDS\ARM7_LPC21xx\portmacro.h" +#endif + +#ifdef SAM7_GCC + #include "../../Source/portable/GCC/ARM7_AT91SAM7S/portmacro.h" +#endif + +#ifdef SAM7_IAR + #include "..\..\Source\portable\IAR\AtmelSAM7S64\portmacro.h" +#endif + +#ifdef SAM9XE_IAR + #include "..\..\Source\portable\IAR\AtmelSAM9XE\portmacro.h" +#endif + +#ifdef LPC2000_IAR + #include "..\..\Source\portable\IAR\LPC2000\portmacro.h" +#endif + +#ifdef STR71X_IAR + #include "..\..\Source\portable\IAR\STR71x\portmacro.h" +#endif + +#ifdef STR75X_IAR + #include "..\..\Source\portable\IAR\STR75x\portmacro.h" +#endif + +#ifdef STR75X_GCC + #include "..\..\Source\portable\GCC\STR75x\portmacro.h" +#endif + +#ifdef STR91X_IAR + #include "..\..\Source\portable\IAR\STR91x\portmacro.h" +#endif + +#ifdef GCC_H8S + #include "../../Source/portable/GCC/H8S2329/portmacro.h" +#endif + +#ifdef GCC_AT91FR40008 + #include "../../Source/portable/GCC/ARM7_AT91FR40008/portmacro.h" +#endif + +#ifdef RVDS_ARMCM3_LM3S102 + #include "../../Source/portable/RVDS/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3_LM3S102 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef GCC_ARMCM3 + #include "../../Source/portable/GCC/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARM_CM3 + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef IAR_ARMCM3_LM + #include "../../Source/portable/IAR/ARM_CM3/portmacro.h" +#endif + +#ifdef HCS12_CODE_WARRIOR + #include "../../Source/portable/CodeWarrior/HCS12/portmacro.h" +#endif + +#ifdef MICROBLAZE_GCC + #include "../../Source/portable/GCC/MicroBlaze/portmacro.h" +#endif + +#ifdef TERN_EE + #include "..\..\Source\portable\Paradigm\Tern_EE\small\portmacro.h" +#endif + +#ifdef GCC_HCS12 + #include "../../Source/portable/GCC/HCS12/portmacro.h" +#endif + +#ifdef GCC_MCF5235 + #include "../../Source/portable/GCC/MCF5235/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_GCC + #include "../../../Source/portable/GCC/ColdFire_V2/portmacro.h" +#endif + +#ifdef COLDFIRE_V2_CODEWARRIOR + #include "../../Source/portable/CodeWarrior/ColdFire_V2/portmacro.h" +#endif + +#ifdef GCC_PPC405 + #include "../../Source/portable/GCC/PPC405_Xilinx/portmacro.h" +#endif + +#ifdef GCC_PPC440 + #include "../../Source/portable/GCC/PPC440_Xilinx/portmacro.h" +#endif + +#ifdef _16FX_SOFTUNE + #include "..\..\Source\portable\Softune\MB96340\portmacro.h" +#endif + +#ifdef BCC_INDUSTRIAL_PC_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\PC\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef BCC_FLASH_LITE_186_PORT + /* A short file name has to be used in place of the normal + FreeRTOSConfig.h when using the Borland compiler. */ + #include "frconfig.h" + #include "..\portable\BCC\16BitDOS\flsh186\prtmacro.h" + typedef void ( __interrupt __far *pxISR )(); +#endif + +#ifdef __GNUC__ + #ifdef __AVR32_AVR32A__ + #include "portmacro.h" + #endif +#endif + +#ifdef __ICCAVR32__ + #ifdef __CORE__ + #if __CORE__ == __AVR32A__ + #include "portmacro.h" + #endif + #endif +#endif + +#ifdef __91467D + #include "portmacro.h" +#endif + +#ifdef __96340 + #include "portmacro.h" +#endif + + +#ifdef __IAR_V850ES_Fx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx3_L__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Jx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_V850ES_Hx2__ + #include "../../Source/portable/IAR/V850ES/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +#ifdef __IAR_78K0R_Kx3L__ + #include "../../Source/portable/IAR/78K0R/portmacro.h" +#endif + +/* Catch all to ensure portmacro.h is included in the build. Newer demos +have the path as part of the project options, rather than as relative from +the project location. If portENTER_CRITICAL() has not been defined then +portmacro.h has not yet been included - as every portmacro.h provides a +portENTER_CRITICAL() definition. Check the demo application for your demo +to find the path to the correct portmacro.h file. */ +#ifndef portENTER_CRITICAL + #include "../../Source/portable/GCC/Posix/portmacro.h" + //#include "portmacro.h" +#endif + +#if portBYTE_ALIGNMENT == 8 + #define portBYTE_ALIGNMENT_MASK ( 0x0007 ) +#endif + +#if portBYTE_ALIGNMENT == 4 + #define portBYTE_ALIGNMENT_MASK ( 0x0003 ) +#endif + +#if portBYTE_ALIGNMENT == 2 + #define portBYTE_ALIGNMENT_MASK ( 0x0001 ) +#endif + +#if portBYTE_ALIGNMENT == 1 + #define portBYTE_ALIGNMENT_MASK ( 0x0000 ) +#endif + +#ifndef portBYTE_ALIGNMENT_MASK + #error "Invalid portBYTE_ALIGNMENT definition" +#endif + +#ifndef portNUM_CONFIGURABLE_REGIONS + #define portNUM_CONFIGURABLE_REGIONS 1 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +#include "mpu_wrappers.h" + +/* + * Setup the stack of a new task so it is ready to be placed under the + * scheduler control. The registers have to be placed on the stack in + * the order that the port expects to find them. + * + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters, portBASE_TYPE xRunPrivileged ) PRIVILEGED_FUNCTION; +#else + portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ); +#endif + +/* + * Map to the memory management routines required for the port. + */ +void *pvPortMalloc( size_t xSize ) PRIVILEGED_FUNCTION; +void vPortFree( void *pv ) PRIVILEGED_FUNCTION; +void vPortInitialiseBlocks( void ) PRIVILEGED_FUNCTION; +size_t xPortGetFreeHeapSize( void ) PRIVILEGED_FUNCTION; + +/* + * Setup the hardware ready for the scheduler to take control. This generally + * sets up a tick interrupt and sets timers for the correct tick frequency. + */ +portBASE_TYPE xPortStartScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * Undo any hardware/ISR setup that was performed by xPortStartScheduler() so + * the hardware is left in its original condition after the scheduler stops + * executing. + */ +void vPortEndScheduler( void ) PRIVILEGED_FUNCTION; + +/* + * The structures and methods of manipulating the MPU are contained within the + * port layer. + * + * Fills the xMPUSettings structure with the memory region information + * contained in xRegions. + */ +#if( portUSING_MPU_WRAPPERS == 1 ) + struct xMEMORY_REGION; + void vPortStoreTaskMPUSettings( xMPU_SETTINGS *xMPUSettings, const struct xMEMORY_REGION * const xRegions, portSTACK_TYPE *pxBottomOfStack, unsigned short usStackDepth ) PRIVILEGED_FUNCTION; +#endif + +#ifdef __cplusplus +} +#endif + +#endif /* PORTABLE_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/projdefs.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/projdefs.h new file mode 100644 index 000000000..25e9560ea --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/projdefs.h @@ -0,0 +1,77 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef PROJDEFS_H +#define PROJDEFS_H + +/* Defines the prototype to which task functions must conform. */ +typedef void (*pdTASK_CODE)( void * ); + +#define pdTRUE ( 1 ) +#define pdFALSE ( 0 ) + +#define pdPASS ( 1 ) +#define pdFAIL ( 0 ) +#define errQUEUE_EMPTY ( 0 ) +#define errQUEUE_FULL ( 0 ) + +/* Error definitions. */ +#define errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY ( -1 ) +#define errNO_TASK_TO_RUN ( -2 ) +#define errQUEUE_BLOCKED ( -4 ) +#define errQUEUE_YIELD ( -5 ) + +#endif /* PROJDEFS_H */ + + + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/queue.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/queue.h new file mode 100644 index 000000000..c26ccc7fe --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/queue.h @@ -0,0 +1,1261 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include queue.h" +#endif + + + + +#ifndef QUEUE_H +#define QUEUE_H + +#ifdef __cplusplus +extern "C" { +#endif + + +#include "mpu_wrappers.h" + + +typedef void * xQueueHandle; + + +/* For internal use only. */ +#define queueSEND_TO_BACK ( 0 ) +#define queueSEND_TO_FRONT ( 1 ) + + +/** + * queue. h + *
+ xQueueHandle xQueueCreate(
+							  unsigned portBASE_TYPE uxQueueLength,
+							  unsigned portBASE_TYPE uxItemSize
+						  );
+ * 
+ * + * Creates a new queue instance. This allocates the storage required by the + * new queue and returns a handle for the queue. + * + * @param uxQueueLength The maximum number of items that the queue can contain. + * + * @param uxItemSize The number of bytes each item in the queue will require. + * Items are queued by copy, not by reference, so this is the number of bytes + * that will be copied for each posted item. Each item on the queue must be + * the same size. + * + * @return If the queue is successfully create then a handle to the newly + * created queue is returned. If the queue cannot be created then 0 is + * returned. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ };
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+	if( xQueue1 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue2 == 0 )
+	{
+		// Queue was not created and must not be used.
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueCreate xQueueCreate + * \ingroup QueueManagement + */ +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToToFront(
+								   xQueueHandle	xQueue,
+								   const	void	*	pvItemToQueue,
+								   portTickType	xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the front of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToFront( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToFront( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, queueSEND_TO_FRONT ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToBack(
+								   xQueueHandle	xQueue,
+								   const	void	*	pvItemToQueue,
+								   portTickType	xTicksToWait
+							   );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). + * + * Post an item to the back of a queue. The item is queued by copy, not by + * reference. This function must not be called from an interrupt service + * routine. See xQueueSendFromISR () for an alternative which may be used + * in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the queue + * is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSendToBack( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSendToBack( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSend(
+							  xQueueHandle xQueue,
+							  const void * pvItemToQueue,
+							  portTickType xTicksToWait
+						 );
+ * 
+ * + * This is a macro that calls xQueueGenericSend(). It is included for + * backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToFront() and xQueueSendToBack() macros. It is + * equivalent to xQueueSendToBack(). + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10 ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0 );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +#define xQueueSend( xQueue, pvItemToQueue, xTicksToWait ) xQueueGenericSend( xQueue, pvItemToQueue, xTicksToWait, queueSEND_TO_BACK ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericSend(
+									xQueueHandle xQueue,
+									const void * pvItemToQueue,
+									portTickType xTicksToWait
+									portBASE_TYPE xCopyPosition
+								);
+ * 
+ * + * It is preferred that the macros xQueueSend(), xQueueSendToFront() and + * xQueueSendToBack() are used in place of calling this function directly. + * + * Post an item on a queue. The item is queued by copy, not by reference. + * This function must not be called from an interrupt service routine. + * See xQueueSendFromISR () for an alternative which may be used in an ISR. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for space to become available on the queue, should it already + * be full. The call will return immediately if this is set to 0 and the + * queue is full. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the item was successfully posted, otherwise errQUEUE_FULL. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ unsigned long ulVar = 10UL;
+
+ void vATask( void *pvParameters )
+ {
+ xQueueHandle xQueue1, xQueue2;
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 unsigned long values.
+	xQueue1 = xQueueCreate( 10, sizeof( unsigned long ) );
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue2 = xQueueCreate( 10, sizeof( struct AMessage * ) );
+
+	// ...
+
+	if( xQueue1 != 0 )
+	{
+		// Send an unsigned long.  Wait for 10 ticks for space to become
+		// available if necessary.
+		if( xQueueGenericSend( xQueue1, ( void * ) &ulVar, ( portTickType ) 10, queueSEND_TO_BACK ) != pdPASS )
+		{
+			// Failed to post the message, even after 10 ticks.
+		}
+	}
+
+	if( xQueue2 != 0 )
+	{
+		// Send a pointer to a struct AMessage object.  Don't block if the
+		// queue is already full.
+		pxMessage = & xMessage;
+		xQueueGenericSend( xQueue2, ( void * ) &pxMessage, ( portTickType ) 0, queueSEND_TO_BACK );
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueSend xQueueSend + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericSend( xQueueHandle xQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ); + +/** + * queue. h + *
+ portBASE_TYPE xQueuePeek(
+							 xQueueHandle xQueue,
+							 void *pvBuffer,
+							 portTickType xTicksToWait
+						 );
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue without removing the item from the queue. + * The item is received by copy so a buffer of adequate size must be + * provided. The number of bytes copied into the buffer was defined when + * the queue was created. + * + * Successfully received items remain on the queue so will be returned again + * by the next call, or a call to xQueueReceive(). + * + * This macro must not be used in an interrupt service routine. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * xQueuePeek() will return immediately if xTicksToWait is 0 and the queue + * is empty. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to peek the data from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Peek a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueuePeek( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask, but the item still remains on the queue.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueuePeek( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdTRUE ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceive(
+								 xQueueHandle xQueue,
+								 void *pvBuffer,
+								 portTickType xTicksToWait
+							);
+ * + * This is a macro that calls the xQueueGenericReceive() function. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * Successfully received items are removed from the queue. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. xQueueReceive() will return immediately if xTicksToWait + * is zero and the queue is empty. The time is defined in tick periods so the + * constant portTICK_RATE_MS should be used to convert to real time if this is + * required. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +#define xQueueReceive( xQueue, pvBuffer, xTicksToWait ) xQueueGenericReceive( xQueue, pvBuffer, xTicksToWait, pdFALSE ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericReceive(
+									   xQueueHandle	xQueue,
+									   void	*pvBuffer,
+									   portTickType	xTicksToWait
+									   portBASE_TYPE	xJustPeek
+									);
+ * + * It is preferred that the macro xQueueReceive() be used rather than calling + * this function directly. + * + * Receive an item from a queue. The item is received by copy so a buffer of + * adequate size must be provided. The number of bytes copied into the buffer + * was defined when the queue was created. + * + * This function must not be used in an interrupt service routine. See + * xQueueReceiveFromISR for an alternative that can. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param xTicksToWait The maximum amount of time the task should block + * waiting for an item to receive should the queue be empty at the time + * of the call. The time is defined in tick periods so the constant + * portTICK_RATE_MS should be used to convert to real time if this is required. + * xQueueGenericReceive() will return immediately if the queue is empty and + * xTicksToWait is 0. + * + * @param xJustPeek When set to true, the item received from the queue is not + * actually removed from the queue - meaning a subsequent call to + * xQueueReceive() will return the same item. When set to false, the item + * being received from the queue is also removed from the queue. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+ struct AMessage
+ {
+	char ucMessageID;
+	char ucData[ 20 ];
+ } xMessage;
+
+ xQueueHandle xQueue;
+
+ // Task to create a queue and post a value.
+ void vATask( void *pvParameters )
+ {
+ struct AMessage *pxMessage;
+
+	// Create a queue capable of containing 10 pointers to AMessage structures.
+	// These should be passed by pointer as they contain a lot of data.
+	xQueue = xQueueCreate( 10, sizeof( struct AMessage * ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Send a pointer to a struct AMessage object.  Don't block if the
+	// queue is already full.
+	pxMessage = & xMessage;
+	xQueueSend( xQueue, ( void * ) &pxMessage, ( portTickType ) 0 );
+
+	// ... Rest of task code.
+ }
+
+ // Task to receive from the queue.
+ void vADifferentTask( void *pvParameters )
+ {
+ struct AMessage *pxRxedMessage;
+
+	if( xQueue != 0 )
+	{
+		// Receive a message on the created queue.  Block for 10 ticks if a
+		// message is not immediately available.
+		if( xQueueGenericReceive( xQueue, &( pxRxedMessage ), ( portTickType ) 10 ) )
+		{
+			// pcRxedMessage now points to the struct AMessage variable posted
+			// by vATask.
+		}
+	}
+
+	// ... Rest of task code.
+ }
+ 
+ * \defgroup xQueueReceive xQueueReceive + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle xQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeek ); + +/** + * queue. h + *
unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle xQueue );
+ * + * Return the number of messages stored in a queue. + * + * @param xQueue A handle to the queue being queried. + * + * @return The number of messages available in the queue. + * + * \page uxQueueMessagesWaiting uxQueueMessagesWaiting + * \ingroup QueueManagement + */ +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle xQueue ); + +/** + * queue. h + *
void vQueueDelete( xQueueHandle xQueue );
+ * + * Delete a queue - freeing all the memory allocated for storing of items + * placed on the queue. + * + * @param xQueue A handle to the queue to be deleted. + * + * \page vQueueDelete vQueueDelete + * \ingroup QueueManagement + */ +void vQueueDelete( xQueueHandle xQueue ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToFrontFromISR(
+										 xQueueHandle pxQueue,
+										 const void *pvItemToQueue,
+										 portBASE_TYPE *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the front of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToFrontFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToFromFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPrioritTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToFrontFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToFrontFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken, queueSEND_TO_FRONT ) + + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendToBackFromISR(
+										 xQueueHandle pxQueue,
+										 const void *pvItemToQueue,
+										 portBASE_TYPE *pxHigherPriorityTaskWoken
+									  );
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). + * + * Post an item to the back of a queue. It is safe to use this macro from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendToBackFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendToBackFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendToBackFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendToBackFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken, queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueSendFromISR(
+									 xQueueHandle pxQueue,
+									 const void *pvItemToQueue,
+									 portBASE_TYPE *pxHigherPriorityTaskWoken
+								);
+ 
+ * + * This is a macro that calls xQueueGenericSendFromISR(). It is included + * for backward compatibility with versions of FreeRTOS.org that did not + * include the xQueueSendToBackFromISR() and xQueueSendToFrontFromISR() + * macros. + * + * Post an item to the back of a queue. It is safe to use this function from + * within an interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWoken;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWoken = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post the byte.
+		xQueueSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWoken );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.
+	if( xHigherPriorityTaskWoken )
+	{
+		// Actual macro used here is port specific.
+		taskYIELD_FROM_ISR ();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +#define xQueueSendFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( pxQueue, pvItemToQueue, pxHigherPriorityTaskWoken, queueSEND_TO_BACK ) + +/** + * queue. h + *
+ portBASE_TYPE xQueueGenericSendFromISR(
+										   xQueueHandle	pxQueue,
+										   const	void	*pvItemToQueue,
+										   portBASE_TYPE	*pxHigherPriorityTaskWoken,
+										   portBASE_TYPE	xCopyPosition
+									   );
+ 
+ * + * It is preferred that the macros xQueueSendFromISR(), + * xQueueSendToFrontFromISR() and xQueueSendToBackFromISR() be used in place + * of calling this function directly. + * + * Post an item on a queue. It is safe to use this function from within an + * interrupt service routine. + * + * Items are queued by copy not reference so it is preferable to only + * queue small items, especially when called from an ISR. In most cases + * it would be preferable to store a pointer to the item being queued. + * + * @param xQueue The handle to the queue on which the item is to be posted. + * + * @param pvItemToQueue A pointer to the item that is to be placed on the + * queue. The size of the items the queue will hold was defined when the + * queue was created, so this many bytes will be copied from pvItemToQueue + * into the queue storage area. + * + * @param pxHigherPriorityTaskWoken xQueueGenericSendFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if sending to the queue caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xQueueGenericSendFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @param xCopyPosition Can take the value queueSEND_TO_BACK to place the + * item at the back of the queue, or queueSEND_TO_FRONT to place the item + * at the front of the queue (for high priority messages). + * + * @return pdTRUE if the data was successfully sent to the queue, otherwise + * errQUEUE_FULL. + * + * Example usage for buffered IO (where the ISR can obtain more than one value + * per call): +
+ void vBufferISR( void )
+ {
+ char cIn;
+ portBASE_TYPE xHigherPriorityTaskWokenByPost;
+
+	// We have not woken a task at the start of the ISR.
+	xHigherPriorityTaskWokenByPost = pdFALSE;
+
+	// Loop until the buffer is empty.
+	do
+	{
+		// Obtain a byte from the buffer.
+		cIn = portINPUT_BYTE( RX_REGISTER_ADDRESS );
+
+		// Post each byte.
+		xQueueGenericSendFromISR( xRxQueue, &cIn, &xHigherPriorityTaskWokenByPost, queueSEND_TO_BACK );
+
+	} while( portINPUT_BYTE( BUFFER_COUNT ) );
+
+	// Now the buffer is empty we can switch context if necessary.  Note that the
+	// name of the yield function required is port specific.
+	if( xHigherPriorityTaskWokenByPost )
+	{
+		taskYIELD_YIELD_FROM_ISR();
+	}
+ }
+ 
+ * + * \defgroup xQueueSendFromISR xQueueSendFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ); + +/** + * queue. h + *
+ portBASE_TYPE xQueueReceiveFromISR(
+									   xQueueHandle	pxQueue,
+									   void	*pvBuffer,
+									   portBASE_TYPE	*pxTaskWoken
+								   );
+ * 
+ * + * Receive an item from a queue. It is safe to use this function from within an + * interrupt service routine. + * + * @param pxQueue The handle to the queue from which the item is to be + * received. + * + * @param pvBuffer Pointer to the buffer into which the received item will + * be copied. + * + * @param pxTaskWoken A task may be blocked waiting for space to become + * available on the queue. If xQueueReceiveFromISR causes such a task to + * unblock *pxTaskWoken will get set to pdTRUE, otherwise *pxTaskWoken will + * remain unchanged. + * + * @return pdTRUE if an item was successfully received from the queue, + * otherwise pdFALSE. + * + * Example usage: +
+
+ xQueueHandle xQueue;
+
+ // Function to create a queue and post some values.
+ void vAFunction( void *pvParameters )
+ {
+ char cValueToPost;
+ const portTickType xBlockTime = ( portTickType )0xff;
+
+	// Create a queue capable of containing 10 characters.
+	xQueue = xQueueCreate( 10, sizeof( char ) );
+	if( xQueue == 0 )
+	{
+		// Failed to create the queue.
+	}
+
+	// ...
+
+	// Post some characters that will be used within an ISR.  If the queue
+	// is full then this task will block for xBlockTime ticks.
+	cValueToPost = 'a';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+	cValueToPost = 'b';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+
+	// ... keep posting characters ... this task may block when the queue
+	// becomes full.
+
+	cValueToPost = 'c';
+	xQueueSend( xQueue, ( void * ) &cValueToPost, xBlockTime );
+ }
+
+ // ISR that outputs all the characters received on the queue.
+ void vISR_Routine( void )
+ {
+ portBASE_TYPE xTaskWokenByReceive = pdFALSE;
+ char cRxedChar;
+
+	while( xQueueReceiveFromISR( xQueue, ( void * ) &cRxedChar, &xTaskWokenByReceive) )
+	{
+		// A character was received.  Output the character now.
+		vOutputCharacter( cRxedChar );
+
+		// If removing the character from the queue woke the task that was
+		// posting onto the queue cTaskWokenByReceive will have been set to
+		// pdTRUE.  No matter how many times this loop iterates only one
+		// task will be woken.
+	}
+
+	if( cTaskWokenByPost != ( char ) pdFALSE;
+	{
+		taskYIELD ();
+	}
+ }
+ 
+ * \defgroup xQueueReceiveFromISR xQueueReceiveFromISR + * \ingroup QueueManagement + */ +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ); + +/* + * Utilities to query queue that are safe to use from an ISR. These utilities + * should be used only from witin an ISR, or within a critical section. + */ +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ); +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ); +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ); + + +/* + * xQueueAltGenericSend() is an alternative version of xQueueGenericSend(). + * Likewise xQueueAltGenericReceive() is an alternative version of + * xQueueGenericReceive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ); +signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ); +#define xQueueAltSendToFront( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( xQueue, pvItemToQueue, xTicksToWait, queueSEND_TO_FRONT ) +#define xQueueAltSendToBack( xQueue, pvItemToQueue, xTicksToWait ) xQueueAltGenericSend( xQueue, pvItemToQueue, xTicksToWait, queueSEND_TO_BACK ) +#define xQueueAltReceive( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( xQueue, pvBuffer, xTicksToWait, pdFALSE ) +#define xQueueAltPeek( xQueue, pvBuffer, xTicksToWait ) xQueueAltGenericReceive( xQueue, pvBuffer, xTicksToWait, pdTRUE ) + +/* + * The functions defined above are for passing data to and from tasks. The + * functions below are the equivalents for passing data to and from + * co-routines. + * + * These functions are called from the co-routine macro implementation and + * should not be called directly from application code. Instead use the macro + * wrappers defined within croutine.h. + */ +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ); +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ); +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ); +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ); + +/* + * For internal use only. Use xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting() instead of calling these functions directly. + */ +xQueueHandle xQueueCreateMutex( void ); +xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ); + +/* + * For internal use only. Use xSemaphoreTakeMutexRecursive() or + * xSemaphoreGiveMutexRecursive() instead of calling these functions directly. + */ +portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle xMutex, portTickType xBlockTime ); +portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle xMutex ); + +/* + * The registry is provided as a means for kernel aware debuggers to + * locate queues, semaphores and mutexes. Call vQueueAddToRegistry() add + * a queue, semaphore or mutex handle to the registry if you want the handle + * to be available to a kernel aware debugger. If you are not using a kernel + * aware debugger then this function can be ignored. + * + * configQUEUE_REGISTRY_SIZE defines the maximum number of handles the + * registry can hold. configQUEUE_REGISTRY_SIZE must be greater than 0 + * within FreeRTOSConfig.h for the registry to be available. Its value + * does not effect the number of queues, semaphores and mutexes that can be + * created - just the number that the registry can hold. + * + * @param xQueue The handle of the queue being added to the registry. This + * is the handle returned by a call to xQueueCreate(). Semaphore and mutex + * handles can also be passed in here. + * + * @param pcName The name to be associated with the handle. This is the + * name that the kernel aware debugger will display. + */ +#if configQUEUE_REGISTRY_SIZE > 0 + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcName ); +#endif + + + + +#ifdef __cplusplus +} +#endif + +#endif /* QUEUE_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/semphr.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/semphr.h new file mode 100644 index 000000000..3984e4bb9 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/semphr.h @@ -0,0 +1,711 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include semphr.h" +#endif + +#ifndef SEMAPHORE_H +#define SEMAPHORE_H + +#include "queue.h" + +typedef xQueueHandle xSemaphoreHandle; + +#define semBINARY_SEMAPHORE_QUEUE_LENGTH ( ( unsigned char ) 1 ) +#define semSEMAPHORE_QUEUE_ITEM_LENGTH ( ( unsigned char ) 0 ) +#define semGIVE_BLOCK_TIME ( ( portTickType ) 0 ) + + +/** + * semphr. h + *
vSemaphoreCreateBinary( xSemaphoreHandle xSemaphore )
+ * + * Macro that implements a semaphore by using the existing queue mechanism. + * The queue length is 1 as this is a binary semaphore. The data size is 0 + * as we don't want to actually store any data - we just want to know if the + * queue is empty or full. + * + * This type of semaphore can be used for pure synchronisation between tasks or + * between an interrupt and a task. The semaphore need not be given back once + * obtained, so one task/interrupt can continuously 'give' the semaphore while + * another continuously 'takes' the semaphore. For this reason this type of + * semaphore does not use a priority inheritance mechanism. For an alternative + * that does use priority inheritance see xSemaphoreCreateMutex(). + * + * @param xSemaphore Handle to the created semaphore. Should be of type xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to vSemaphoreCreateBinary ().
+    // This is a macro so pass the variable in directly.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateBinary vSemaphoreCreateBinary + * \ingroup Semaphores + */ +#define vSemaphoreCreateBinary( xSemaphore ) { \ + xSemaphore = xQueueCreate( ( unsigned portBASE_TYPE ) 1, semSEMAPHORE_QUEUE_ITEM_LENGTH ); \ + if( xSemaphore != NULL ) \ + { \ + xSemaphoreGive( xSemaphore ); \ + } \ + } + +/** + * semphr. h + *
xSemaphoreTake( 
+ *                   xSemaphoreHandle xSemaphore, 
+ *                   portTickType xBlockTime 
+ *               )
+ * + * Macro to obtain a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). + * + * @param xSemaphore A handle to the semaphore being taken - obtained when + * the semaphore was created. + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_RATE_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. A block + * time of portMAX_DELAY can be used to block indefinitely (provided + * INCLUDE_vTaskSuspend is set to 1 in FreeRTOSConfig.h). + * + * @return pdTRUE if the semaphore was obtained. pdFALSE + * if xBlockTime expired without the semaphore becoming available. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // A task that creates a semaphore.
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+ }
+
+ // A task that uses the semaphore.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xSemaphore != NULL )
+    {
+        // See if we can obtain the semaphore.  If the semaphore is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the semaphore and can now access the
+            // shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource.  Release the 
+            // semaphore.
+            xSemaphoreGive( xSemaphore );
+        }
+        else
+        {
+            // We could not obtain the semaphore and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTake xSemaphoreTake + * \ingroup Semaphores + */ +#define xSemaphoreTake( xSemaphore, xBlockTime ) xQueueGenericReceive( ( xQueueHandle ) xSemaphore, NULL, xBlockTime, pdFALSE ) + +/** + * semphr. h + * xSemaphoreTakeRecursive( + * xSemaphoreHandle xMutex, + * portTickType xBlockTime + * ) + * + * Macro to recursively obtain, or 'take', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being obtained. This is the + * handle returned by xSemaphoreCreateRecursiveMutex(); + * + * @param xBlockTime The time in ticks to wait for the semaphore to become + * available. The macro portTICK_RATE_MS can be used to convert this to a + * real time. A block time of zero can be used to poll the semaphore. If + * the task already owns the semaphore then xSemaphoreTakeRecursive() will + * return immediately no matter what the value of xBlockTime. + * + * @return pdTRUE if the semaphore was obtained. pdFALSE if xBlockTime + * expired without the semaphore becoming available. + * + * Example usage: +
+ xSemaphoreHandle xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTakeRecursive( xSemaphore, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to 
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be 
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, but instead buried in a more complex
+			// call structure.  This is just for illustrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreTakeRecursive xSemaphoreTakeRecursive + * \ingroup Semaphores + */ +#define xSemaphoreTakeRecursive( xMutex, xBlockTime ) xQueueTakeMutexRecursive( xMutex, xBlockTime ) + + +/* + * xSemaphoreAltTake() is an alternative version of xSemaphoreTake(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltTake( xSemaphore, xBlockTime ) xQueueAltGenericReceive( ( xQueueHandle ) xSemaphore, NULL, xBlockTime, pdFALSE ) + +/** + * semphr. h + *
xSemaphoreGive( xSemaphoreHandle xSemaphore )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary(), xSemaphoreCreateMutex() or + * xSemaphoreCreateCounting(). and obtained using sSemaphoreTake(). + * + * This macro must not be used from an ISR. See xSemaphoreGiveFromISR () for + * an alternative which can be used from an ISR. + * + * This macro must also not be used on semaphores created using + * xSemaphoreCreateRecursiveMutex(). + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @return pdTRUE if the semaphore was released. pdFALSE if an error occurred. + * Semaphores are implemented using queues. An error can occur if there is + * no space on the queue to post a message - indicating that the + * semaphore was not first obtained correctly. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore = NULL;
+
+ void vATask( void * pvParameters )
+ {
+    // Create the semaphore to guard a shared resource.
+    vSemaphoreCreateBinary( xSemaphore );
+
+    if( xSemaphore != NULL )
+    {
+        if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+        {
+            // We would expect this call to fail because we cannot give
+            // a semaphore without first "taking" it!
+        }
+
+        // Obtain the semaphore - don't block if the semaphore is not
+        // immediately available.
+        if( xSemaphoreTake( xSemaphore, ( portTickType ) 0 ) )
+        {
+            // We now have the semaphore and can access the shared resource.
+
+            // ...
+
+            // We have finished accessing the shared resource so can free the
+            // semaphore.
+            if( xSemaphoreGive( xSemaphore ) != pdTRUE )
+            {
+                // We would not expect this call to fail because we must have
+                // obtained the semaphore to get here.
+            }
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGive xSemaphoreGive + * \ingroup Semaphores + */ +#define xSemaphoreGive( xSemaphore ) xQueueGenericSend( ( xQueueHandle ) xSemaphore, NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
xSemaphoreGiveRecursive( xSemaphoreHandle xMutex )
+ * + * Macro to recursively release, or 'give', a mutex type semaphore. + * The mutex must have previously been created using a call to + * xSemaphoreCreateRecursiveMutex(); + * + * configUSE_RECURSIVE_MUTEXES must be set to 1 in FreeRTOSConfig.h for this + * macro to be available. + * + * This macro must not be used on mutexes created using xSemaphoreCreateMutex(). + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * @param xMutex A handle to the mutex being released, or 'given'. This is the + * handle returned by xSemaphoreCreateMutex(); + * + * @return pdTRUE if the semaphore was given. + * + * Example usage: +
+ xSemaphoreHandle xMutex = NULL;
+
+ // A task that creates a mutex.
+ void vATask( void * pvParameters )
+ {
+    // Create the mutex to guard a shared resource.
+    xMutex = xSemaphoreCreateRecursiveMutex();
+ }
+
+ // A task that uses the mutex.
+ void vAnotherTask( void * pvParameters )
+ {
+    // ... Do other things.
+
+    if( xMutex != NULL )
+    {
+        // See if we can obtain the mutex.  If the mutex is not available
+        // wait 10 ticks to see if it becomes free.	
+        if( xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 ) == pdTRUE )
+        {
+            // We were able to obtain the mutex and can now access the
+            // shared resource.
+
+            // ...
+            // For some reason due to the nature of the code further calls to 
+			// xSemaphoreTakeRecursive() are made on the same mutex.  In real
+			// code these would not be just sequential calls as this would make
+			// no sense.  Instead the calls are likely to be buried inside
+			// a more complex call structure.
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+            xSemaphoreTakeRecursive( xMutex, ( portTickType ) 10 );
+
+            // The mutex has now been 'taken' three times, so will not be 
+			// available to another task until it has also been given back
+			// three times.  Again it is unlikely that real code would have
+			// these calls sequentially, it would be more likely that the calls
+			// to xSemaphoreGiveRecursive() would be called as a call stack
+			// unwound.  This is just for demonstrative purposes.
+            xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+			xSemaphoreGiveRecursive( xMutex );
+
+			// Now the mutex can be taken by other tasks.
+        }
+        else
+        {
+            // We could not obtain the mutex and can therefore not access
+            // the shared resource safely.
+        }
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveRecursive xSemaphoreGiveRecursive + * \ingroup Semaphores + */ +#define xSemaphoreGiveRecursive( xMutex ) xQueueGiveMutexRecursive( xMutex ) + +/* + * xSemaphoreAltGive() is an alternative version of xSemaphoreGive(). + * + * The source code that implements the alternative (Alt) API is much + * simpler because it executes everything from within a critical section. + * This is the approach taken by many other RTOSes, but FreeRTOS.org has the + * preferred fully featured API too. The fully featured API has more + * complex code that takes longer to execute, but makes much less use of + * critical sections. Therefore the alternative API sacrifices interrupt + * responsiveness to gain execution speed, whereas the fully featured API + * sacrifices execution speed to ensure better interrupt responsiveness. + */ +#define xSemaphoreAltGive( xSemaphore ) xQueueAltGenericSend( ( xQueueHandle ) xSemaphore, NULL, semGIVE_BLOCK_TIME, queueSEND_TO_BACK ) + +/** + * semphr. h + *
+ xSemaphoreGiveFromISR( 
+                          xSemaphoreHandle xSemaphore, 
+                          signed portBASE_TYPE *pxHigherPriorityTaskWoken
+                      )
+ * + * Macro to release a semaphore. The semaphore must have previously been + * created with a call to vSemaphoreCreateBinary() or xSemaphoreCreateCounting(). + * + * Mutex type semaphores (those created using a call to xSemaphoreCreateMutex()) + * must not be used with this macro. + * + * This macro can be used from an ISR. + * + * @param xSemaphore A handle to the semaphore being released. This is the + * handle returned when the semaphore was created. + * + * @param pxHigherPriorityTaskWoken xSemaphoreGiveFromISR() will set + * *pxHigherPriorityTaskWoken to pdTRUE if giving the semaphore caused a task + * to unblock, and the unblocked task has a priority higher than the currently + * running task. If xSemaphoreGiveFromISR() sets this value to pdTRUE then + * a context switch should be requested before the interrupt is exited. + * + * @return pdTRUE if the semaphore was successfully given, otherwise errQUEUE_FULL. + * + * Example usage: +
+ \#define LONG_TIME 0xffff
+ \#define TICKS_TO_WAIT	10
+ xSemaphoreHandle xSemaphore = NULL;
+
+ // Repetitive task.
+ void vATask( void * pvParameters )
+ {
+    for( ;; )
+    {
+        // We want this task to run every 10 ticks of a timer.  The semaphore 
+        // was created before this task was started.
+
+        // Block waiting for the semaphore to become available.
+        if( xSemaphoreTake( xSemaphore, LONG_TIME ) == pdTRUE )
+        {
+            // It is time to execute.
+
+            // ...
+
+            // We have finished our task.  Return to the top of the loop where
+            // we will block on the semaphore until it is time to execute 
+            // again.  Note when using the semaphore for synchronisation with an
+			// ISR in this manner there is no need to 'give' the semaphore back.
+        }
+    }
+ }
+
+ // Timer ISR
+ void vTimerISR( void * pvParameters )
+ {
+ static unsigned char ucLocalTickCount = 0;
+ static signed portBASE_TYPE xHigherPriorityTaskWoken;
+
+    // A timer tick has occurred.
+
+    // ... Do other time functions.
+
+    // Is it time for vATask () to run?
+	xHigherPriorityTaskWoken = pdFALSE;
+    ucLocalTickCount++;
+    if( ucLocalTickCount >= TICKS_TO_WAIT )
+    {
+        // Unblock the task by releasing the semaphore.
+        xSemaphoreGiveFromISR( xSemaphore, &xHigherPriorityTaskWoken );
+
+        // Reset the count so we release the semaphore again in 10 ticks time.
+        ucLocalTickCount = 0;
+    }
+
+    if( xHigherPriorityTaskWoken != pdFALSE )
+    {
+        // We can force a context switch here.  Context switching from an
+        // ISR uses port specific syntax.  Check the demo task for your port
+        // to find the syntax required.
+    }
+ }
+ 
+ * \defgroup xSemaphoreGiveFromISR xSemaphoreGiveFromISR + * \ingroup Semaphores + */ +#define xSemaphoreGiveFromISR( xSemaphore, pxHigherPriorityTaskWoken ) xQueueGenericSendFromISR( ( xQueueHandle ) xSemaphore, NULL, pxHigherPriorityTaskWoken, queueSEND_TO_BACK ) + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateMutex( void )
+ * + * Macro that implements a mutex semaphore by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the xSemaphoreTake() + * and xSemaphoreGive() macros. The xSemaphoreTakeRecursive() and + * xSemaphoreGiveRecursive() macros should not be used. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateMutex() xQueueCreateMutex() + + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateRecursiveMutex( void )
+ * + * Macro that implements a recursive mutex by using the existing queue + * mechanism. + * + * Mutexes created using this macro can be accessed using the + * xSemaphoreTakeRecursive() and xSemaphoreGiveRecursive() macros. The + * xSemaphoreTake() and xSemaphoreGive() macros should not be used. + * + * A mutex used recursively can be 'taken' repeatedly by the owner. The mutex + * doesn't become available again until the owner has called + * xSemaphoreGiveRecursive() for each successful 'take' request. For example, + * if a task successfully 'takes' the same mutex 5 times then the mutex will + * not be available to any other task until it has also 'given' the mutex back + * exactly five times. + * + * This type of semaphore uses a priority inheritance mechanism so a task + * 'taking' a semaphore MUST ALWAYS 'give' the semaphore back once the + * semaphore it is no longer required. + * + * Mutex type semaphores cannot be used from within interrupt service routines. + * + * See vSemaphoreCreateBinary() for an alternative implementation that can be + * used for pure synchronisation (where one task or interrupt always 'gives' the + * semaphore and another always 'takes' the semaphore) and from within interrupt + * service routines. + * + * @return xSemaphore Handle to the created mutex semaphore. Should be of type + * xSemaphoreHandle. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+    // Semaphore cannot be used before a call to xSemaphoreCreateMutex().
+    // This is a macro so pass the variable in directly.
+    xSemaphore = xSemaphoreCreateRecursiveMutex();
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup vSemaphoreCreateMutex vSemaphoreCreateMutex + * \ingroup Semaphores + */ +#define xSemaphoreCreateRecursiveMutex() xQueueCreateMutex() + +/** + * semphr. h + *
xSemaphoreHandle xSemaphoreCreateCounting( unsigned portBASE_TYPE uxMaxCount, unsigned portBASE_TYPE uxInitialCount )
+ * + * Macro that creates a counting semaphore by using the existing + * queue mechanism. + * + * Counting semaphores are typically used for two things: + * + * 1) Counting events. + * + * In this usage scenario an event handler will 'give' a semaphore each time + * an event occurs (incrementing the semaphore count value), and a handler + * task will 'take' a semaphore each time it processes an event + * (decrementing the semaphore count value). The count value is therefore + * the difference between the number of events that have occurred and the + * number that have been processed. In this case it is desirable for the + * initial count value to be zero. + * + * 2) Resource management. + * + * In this usage scenario the count value indicates the number of resources + * available. To obtain control of a resource a task must first obtain a + * semaphore - decrementing the semaphore count value. When the count value + * reaches zero there are no free resources. When a task finishes with the + * resource it 'gives' the semaphore back - incrementing the semaphore count + * value. In this case it is desirable for the initial count value to be + * equal to the maximum count value, indicating that all resources are free. + * + * @param uxMaxCount The maximum count value that can be reached. When the + * semaphore reaches this value it can no longer be 'given'. + * + * @param uxInitialCount The count value assigned to the semaphore when it is + * created. + * + * @return Handle to the created semaphore. Null if the semaphore could not be + * created. + * + * Example usage: +
+ xSemaphoreHandle xSemaphore;
+
+ void vATask( void * pvParameters )
+ {
+ xSemaphoreHandle xSemaphore = NULL;
+
+    // Semaphore cannot be used before a call to xSemaphoreCreateCounting().
+    // The max value to which the semaphore can count should be 10, and the
+    // initial value assigned to the count should be 0.
+    xSemaphore = xSemaphoreCreateCounting( 10, 0 );
+
+    if( xSemaphore != NULL )
+    {
+        // The semaphore was created successfully.
+        // The semaphore can now be used.  
+    }
+ }
+ 
+ * \defgroup xSemaphoreCreateCounting xSemaphoreCreateCounting + * \ingroup Semaphores + */ +#define xSemaphoreCreateCounting( uxMaxCount, uxInitialCount ) xQueueCreateCountingSemaphore( uxMaxCount, uxInitialCount ) + + +#endif /* SEMAPHORE_H */ + + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/task.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/task.h new file mode 100644 index 000000000..df059e001 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/include/task.h @@ -0,0 +1,1263 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef INC_FREERTOS_H + #error "#include FreeRTOS.h" must appear in source files before "#include task.h" +#endif + + + +#ifndef TASK_H +#define TASK_H + +#include "portable.h" +#include "list.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * MACROS AND DEFINITIONS + *----------------------------------------------------------*/ + +#define tskKERNEL_VERSION_NUMBER "V6.0.4" + +/** + * task. h + * + * Type by which tasks are referenced. For example, a call to xTaskCreate + * returns (via a pointer parameter) an xTaskHandle variable that can then + * be used as a parameter to vTaskDelete to delete the task. + * + * \page xTaskHandle xTaskHandle + * \ingroup Tasks + */ +typedef void * xTaskHandle; + +/* + * Used internally only. + */ +typedef struct xTIME_OUT +{ + portBASE_TYPE xOverflowCount; + portTickType xTimeOnEntering; +} xTimeOutType; + +/* + * Defines the memory ranges allocated to the task when an MPU is used. + */ +typedef struct xMEMORY_REGION +{ + void *pvBaseAddress; + unsigned long ulLengthInBytes; + unsigned long ulParameters; +} xMemoryRegion; + +/* + * Parameters required to create an MPU protected task. + */ +typedef struct xTASK_PARAMTERS +{ + pdTASK_CODE pvTaskCode; + const signed char * const pcName; + unsigned short usStackDepth; + void *pvParameters; + unsigned portBASE_TYPE uxPriority; + portSTACK_TYPE *puxStackBuffer; + xMemoryRegion xRegions[ portNUM_CONFIGURABLE_REGIONS ]; +} xTaskParameters; + +/* + * Defines the priority used by the idle task. This must not be modified. + * + * \ingroup TaskUtils + */ +#define tskIDLE_PRIORITY ( ( unsigned portBASE_TYPE ) 0 ) + +/** + * task. h + * + * Macro for forcing a context switch. + * + * \page taskYIELD taskYIELD + * \ingroup SchedulerControl + */ +#define taskYIELD() portYIELD() + +/** + * task. h + * + * Macro to mark the start of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskENTER_CRITICAL taskENTER_CRITICAL + * \ingroup SchedulerControl + */ +#define taskENTER_CRITICAL() portENTER_CRITICAL() + +/** + * task. h + * + * Macro to mark the end of a critical code region. Preemptive context + * switches cannot occur when in a critical region. + * + * NOTE: This may alter the stack (depending on the portable implementation) + * so must be used with care! + * + * \page taskEXIT_CRITICAL taskEXIT_CRITICAL + * \ingroup SchedulerControl + */ +#define taskEXIT_CRITICAL() portEXIT_CRITICAL() + +/** + * task. h + * + * Macro to disable all maskable interrupts. + * + * \page taskDISABLE_INTERRUPTS taskDISABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskDISABLE_INTERRUPTS() portDISABLE_INTERRUPTS() + +/** + * task. h + * + * Macro to enable microcontroller interrupts. + * + * \page taskENABLE_INTERRUPTS taskENABLE_INTERRUPTS + * \ingroup SchedulerControl + */ +#define taskENABLE_INTERRUPTS() portENABLE_INTERRUPTS() + +/* Definitions returned by xTaskGetSchedulerState(). */ +#define taskSCHEDULER_NOT_STARTED 0 +#define taskSCHEDULER_RUNNING 1 +#define taskSCHEDULER_SUSPENDED 2 + +/*----------------------------------------------------------- + * TASK CREATION API + *----------------------------------------------------------*/ + +/** + * task. h + *
+ portBASE_TYPE xTaskCreate(
+							  pdTASK_CODE pvTaskCode,
+							  const char * const pcName,
+							  unsigned short usStackDepth,
+							  void *pvParameters,
+							  unsigned portBASE_TYPE uxPriority,
+							  xTaskHandle *pvCreatedTask
+						  );
+ * + * Create a new task and add it to the list of tasks that are ready to run. + * + * xTaskCreate() can only be used to create a task that has unrestricted + * access to the entire microcontroller memory map. Systems that include MPU + * support can alternatively create an MPU constrained task using + * xTaskCreateRestricted(). + * + * @param pvTaskCode Pointer to the task entry function. Tasks + * must be implemented to never return (i.e. continuous loop). + * + * @param pcName A descriptive name for the task. This is mainly used to + * facilitate debugging. Max length defined by tskMAX_TASK_NAME_LEN - default + * is 16. + * + * @param usStackDepth The size of the task stack specified as the number of + * variables the stack can hold - not the number of bytes. For example, if + * the stack is 16 bits wide and usStackDepth is defined as 100, 200 bytes + * will be allocated for stack storage. + * + * @param pvParameters Pointer that will be used as the parameter for the task + * being created. + * + * @param uxPriority The priority at which the task should run. Systems that + * include MPU support can optionally create tasks in a privileged (system) + * mode by setting bit portPRIVILEGE_BIT of the priority parameter. For + * example, to create a privileged task at priority 2 the uxPriority parameter + * should be set to ( 2 | portPRIVILEGE_BIT ). + * + * @param pvCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file errors. h + * + * Example usage: +
+ // Task to be created.
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+	 }
+ }
+
+ // Function that creates a task.
+ void vOtherFunction( void )
+ {
+ static unsigned char ucParameterToPass;
+ xTaskHandle xHandle;
+
+	 // Create the task, storing the handle.  Note that the passed parameter ucParameterToPass
+	 // must exist for the lifetime of the task, so in this case is declared static.  If it was just an
+	 // an automatic stack variable it might no longer exist, or at least have been corrupted, by the time
+	 // the new task attempts to access it.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, &ucParameterToPass, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup xTaskCreate xTaskCreate + * \ingroup Tasks + */ +#define xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask ) xTaskGenericCreate( ( pvTaskCode ), ( pcName ), ( usStackDepth ), ( pvParameters ), ( uxPriority ), ( pxCreatedTask ), ( NULL ), ( NULL ) ) + +/** + * task. h + *
+ portBASE_TYPE xTaskCreateRestricted( xTaskParameters *pxTaskDefinition, xTaskHandle *pxCreatedTask );
+ * + * xTaskCreateRestricted() should only be used in systems that include an MPU + * implementation. + * + * Create a new task and add it to the list of tasks that are ready to run. + * The function parameters define the memory regions and associated access + * permissions allocated to the task. + * + * @param pxTaskDefinition Pointer to a structure that contains a member + * for each of the normal xTaskCreate() parameters (see the xTaskCreate() API + * documentation) plus an optional stack buffer and the memory region + * definitions. + * + * @param pxCreatedTask Used to pass back a handle by which the created task + * can be referenced. + * + * @return pdPASS if the task was successfully created and added to a ready + * list, otherwise an error code defined in the file errors. h + * + * Example usage: +
+// Create an xTaskParameters structure that defines the task to be created.
+static const xTaskParameters xCheckTaskParameters =
+{
+	vATask,		// pvTaskCode - the function that implements the task.
+	"ATask",	// pcName - just a text name for the task to assist debugging.
+	100,		// usStackDepth	- the stack size DEFINED IN WORDS.
+	NULL,		// pvParameters - passed into the task function as the function parameters.
+	( 1UL | portPRIVILEGE_BIT ),// uxPriority - task priority, set the portPRIVILEGE_BIT if the task should run in a privileged state.
+	cStackBuffer,// puxStackBuffer - the buffer to be used as the task stack.
+
+	// xRegions - Allocate up to three separate memory regions for access by
+	// the task, with appropriate access permissions.  Different processors have
+	// different memory alignment requirements - refer to the FreeRTOS documentation
+	// for full information.
+	{											
+		// Base address					Length	Parameters
+        { cReadWriteArray,				32,		portMPU_REGION_READ_WRITE },
+        { cReadOnlyArray,				32,		portMPU_REGION_READ_ONLY },
+        { cPrivilegedOnlyAccessArray,	128,	portMPU_REGION_PRIVILEGED_READ_WRITE }
+	}
+};
+
+int main( void )
+{
+xTaskHandle xHandle;
+
+	// Create a task from the const structure defined above.  The task handle
+	// is requested (the second parameter is not NULL) but in this case just for
+	// demonstration purposes as its not actually used.
+	xTaskCreateRestricted( &xRegTest1Parameters, &xHandle );
+
+	// Start the scheduler.
+	vTaskStartScheduler();
+
+	// Will only get here if there was insufficient memory to create the idle
+	// task.
+	for( ;; );
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +#define xTaskCreateRestricted( x, pxCreatedTask ) xTaskGenericCreate( ((x)->pvTaskCode), ((x)->pcName), ((x)->usStackDepth), ((x)->pvParameters), ((x)->uxPriority), (pxCreatedTask), ((x)->puxStackBuffer), ((x)->xRegions) ) + +/** + * task. h + *
+ void vTaskAllocateMPURegions( xTaskHandle xTask, const xMemoryRegion * const pxRegions );
+ * + * Memory regions are assigned to a restricted task when the task is created by + * a call to xTaskCreateRestricted(). These regions can be redefined using + * vTaskAllocateMPURegions(). + * + * @param xTask The handle of the task being updated. + * + * @param xRegions A pointer to an xMemoryRegion structure that contains the + * new memory region definitions. + * + * Example usage: +
+// Define an array of xMemoryRegion structures that configures an MPU region
+// allowing read/write access for 1024 bytes starting at the beginning of the
+// ucOneKByte array.  The other two of the maximum 3 definable regions are
+// unused so set to zero.
+static const xMemoryRegion xAltRegions[ portNUM_CONFIGURABLE_REGIONS ] =
+{											
+	// Base address		Length		Parameters
+	{ ucOneKByte,		1024,		portMPU_REGION_READ_WRITE },
+	{ 0,				0,			0 },
+	{ 0,				0,			0 }
+};
+
+void vATask( void *pvParameters )
+{
+	// This task was created such that it has access to certain regions of
+	// memory as defined by the MPU configuration.  At some point it is 
+	// desired that these MPU regions are replaced with that defined in the
+	// xAltRegions const struct above.  Use a call to vTaskAllocateMPURegions()
+	// for this purpose.  NULL is used as the task handle to indicate that this
+	// function should modify the MPU regions of the calling task.
+	vTaskAllocateMPURegions( NULL, xAltRegions );
+	
+	// Now the task can continue its function, but from this point on can only
+	// access its stack and the ucOneKByte array (unless any other statically
+	// defined or shared regions have been declared elsewhere).
+}
+   
+ * \defgroup xTaskCreateRestricted xTaskCreateRestricted + * \ingroup Tasks + */ +void vTaskAllocateMPURegions( xTaskHandle xTask, const xMemoryRegion * const pxRegions ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelete( xTaskHandle pxTask );
+ * + * INCLUDE_vTaskDelete must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Remove a task from the RTOS real time kernels management. The task being + * deleted will be removed from all ready, blocked, suspended and event lists. + * + * NOTE: The idle task is responsible for freeing the kernel allocated + * memory from tasks that have been deleted. It is therefore important that + * the idle task is not starved of microcontroller processing time if your + * application makes any calls to vTaskDelete (). Memory allocated by the + * task code is not automatically freed, and should be freed before the task + * is deleted. + * + * See the demo application file death.c for sample code that utilises + * vTaskDelete (). + * + * @param pxTask The handle of the task to be deleted. Passing NULL will + * cause the calling task to be deleted. + * + * Example usage: +
+ void vOtherFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create the task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // Use the handle to delete the task.
+	 vTaskDelete( xHandle );
+ }
+   
+ * \defgroup vTaskDelete vTaskDelete + * \ingroup Tasks + */ +void vTaskDelete( xTaskHandle pxTask ) PRIVILEGED_FUNCTION; + + +/*----------------------------------------------------------- + * TASK CONTROL API + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskDelay( portTickType xTicksToDelay );
+ * + * Delay a task for a given number of ticks. The actual time that the + * task remains blocked depends on the tick rate. The constant + * portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * INCLUDE_vTaskDelay must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * + * vTaskDelay() specifies a time at which the task wishes to unblock relative to + * the time at which vTaskDelay() is called. For example, specifying a block + * period of 100 ticks will cause the task to unblock 100 ticks after + * vTaskDelay() is called. vTaskDelay() does not therefore provide a good method + * of controlling the frequency of a cyclical task as the path taken through the + * code, as well as other task and interrupt activity, will effect the frequency + * at which vTaskDelay() gets called and therefore the time at which the task + * next executes. See vTaskDelayUntil() for an alternative API function designed + * to facilitate fixed frequency execution. It does this by specifying an + * absolute time (rather than a relative time) at which the calling task should + * unblock. + * + * @param xTicksToDelay The amount of time, in tick periods, that + * the calling task should block. + * + * Example usage: + + void vTaskFunction( void * pvParameters ) + { + void vTaskFunction( void * pvParameters ) + { + // Block for 500ms. + const portTickType xDelay = 500 / portTICK_RATE_MS; + + for( ;; ) + { + // Simply toggle the LED every 500ms, blocking between each toggle. + vToggleLED(); + vTaskDelay( xDelay ); + } + } + + * \defgroup vTaskDelay vTaskDelay + * \ingroup TaskCtrl + */ +void vTaskDelay( portTickType xTicksToDelay ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskDelayUntil( portTickType *pxPreviousWakeTime, portTickType xTimeIncrement );
+ * + * INCLUDE_vTaskDelayUntil must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Delay a task until a specified time. This function can be used by cyclical + * tasks to ensure a constant execution frequency. + * + * This function differs from vTaskDelay () in one important aspect: vTaskDelay () will + * cause a task to block for the specified number of ticks from the time vTaskDelay () is + * called. It is therefore difficult to use vTaskDelay () by itself to generate a fixed + * execution frequency as the time between a task starting to execute and that task + * calling vTaskDelay () may not be fixed [the task may take a different path though the + * code between calls, or may get interrupted or preempted a different number of times + * each time it executes]. + * + * Whereas vTaskDelay () specifies a wake time relative to the time at which the function + * is called, vTaskDelayUntil () specifies the absolute (exact) time at which it wishes to + * unblock. + * + * The constant portTICK_RATE_MS can be used to calculate real time from the tick + * rate - with the resolution of one tick period. + * + * @param pxPreviousWakeTime Pointer to a variable that holds the time at which the + * task was last unblocked. The variable must be initialised with the current time + * prior to its first use (see the example below). Following this the variable is + * automatically updated within vTaskDelayUntil (). + * + * @param xTimeIncrement The cycle time period. The task will be unblocked at + * time *pxPreviousWakeTime + xTimeIncrement. Calling vTaskDelayUntil with the + * same xTimeIncrement parameter value will cause the task to execute with + * a fixed interface period. + * + * Example usage: +
+ // Perform an action every 10 ticks.
+ void vTaskFunction( void * pvParameters )
+ {
+ portTickType xLastWakeTime;
+ const portTickType xFrequency = 10;
+
+	 // Initialise the xLastWakeTime variable with the current time.
+	 xLastWakeTime = xTaskGetTickCount ();
+	 for( ;; )
+	 {
+		 // Wait for the next cycle.
+		 vTaskDelayUntil( &xLastWakeTime, xFrequency );
+
+		 // Perform action here.
+	 }
+ }
+   
+ * \defgroup vTaskDelayUntil vTaskDelayUntil + * \ingroup TaskCtrl + */ +void vTaskDelayUntil( portTickType * const pxPreviousWakeTime, portTickType xTimeIncrement ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask );
+ * + * INCLUDE_xTaskPriorityGet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Obtain the priority of any task. + * + * @param pxTask Handle of the task to be queried. Passing a NULL + * handle results in the priority of the calling task being returned. + * + * @return The priority of pxTask. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to obtain the priority of the created task.
+	 // It was created with tskIDLE_PRIORITY, but may have changed
+	 // it itself.
+	 if( uxTaskPriorityGet( xHandle ) != tskIDLE_PRIORITY )
+	 {
+		 // The task has changed it's priority.
+	 }
+
+	 // ...
+
+	 // Is our priority higher than the created task?
+	 if( uxTaskPriorityGet( xHandle ) < uxTaskPriorityGet( NULL ) )
+	 {
+		 // Our priority (obtained using NULL handle) is higher.
+	 }
+ }
+   
+ * \defgroup uxTaskPriorityGet uxTaskPriorityGet + * \ingroup TaskCtrl + */ +unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority );
+ * + * INCLUDE_vTaskPrioritySet must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Set the priority of any task. + * + * A context switch will occur before the function returns if the priority + * being set is higher than the currently executing task. + * + * @param pxTask Handle to the task for which the priority is being set. + * Passing a NULL handle results in the priority of the calling task being set. + * + * @param uxNewPriority The priority to which the task will be set. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to raise the priority of the created task.
+	 vTaskPrioritySet( xHandle, tskIDLE_PRIORITY + 1 );
+
+	 // ...
+
+	 // Use a NULL handle to raise our priority to the same value.
+	 vTaskPrioritySet( NULL, tskIDLE_PRIORITY + 1 );
+ }
+   
+ * \defgroup vTaskPrioritySet vTaskPrioritySet + * \ingroup TaskCtrl + */ +void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspend( xTaskHandle pxTaskToSuspend );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Suspend any task. When suspended a task will never get any microcontroller + * processing time, no matter what its priority. + * + * Calls to vTaskSuspend are not accumulative - + * i.e. calling vTaskSuspend () twice on the same task still only requires one + * call to vTaskResume () to ready the suspended task. + * + * @param pxTaskToSuspend Handle to the task being suspended. Passing a NULL + * handle will cause the calling task to be suspended. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Suspend ourselves.
+	 vTaskSuspend( NULL );
+
+	 // We cannot get here unless another task calls vTaskResume
+	 // with our handle as the parameter.
+ }
+   
+ * \defgroup vTaskSuspend vTaskSuspend + * \ingroup TaskCtrl + */ +void vTaskSuspend( xTaskHandle pxTaskToSuspend ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskResume( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_vTaskSuspend must be defined as 1 for this function to be available. + * See the configuration section for more information. + * + * Resumes a suspended task. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * vTaskResume (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * Example usage: +
+ void vAFunction( void )
+ {
+ xTaskHandle xHandle;
+
+	 // Create a task, storing the handle.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
+
+	 // ...
+
+	 // Use the handle to suspend the created task.
+	 vTaskSuspend( xHandle );
+
+	 // ...
+
+	 // The created task will not run during this period, unless
+	 // another task calls vTaskResume( xHandle ).
+
+	 //...
+
+
+	 // Resume the suspended task ourselves.
+	 vTaskResume( xHandle );
+
+	 // The created task will once again get microcontroller processing
+	 // time in accordance with it priority within the system.
+ }
+   
+ * \defgroup vTaskResume vTaskResume + * \ingroup TaskCtrl + */ +void vTaskResume( xTaskHandle pxTaskToResume ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void xTaskResumeFromISR( xTaskHandle pxTaskToResume );
+ * + * INCLUDE_xTaskResumeFromISR must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * An implementation of vTaskResume() that can be called from within an ISR. + * + * A task that has been suspended by one of more calls to vTaskSuspend () + * will be made available for running again by a single call to + * xTaskResumeFromISR (). + * + * @param pxTaskToResume Handle to the task being readied. + * + * \defgroup vTaskResumeFromISR vTaskResumeFromISR + * \ingroup TaskCtrl + */ +portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * SCHEDULER CONTROL + *----------------------------------------------------------*/ + +/** + * task. h + *
void vTaskStartScheduler( void );
+ * + * Starts the real time kernel tick processing. After calling the kernel + * has control over which tasks are executed and when. This function + * does not return until an executing task calls vTaskEndScheduler (). + * + * At least one task should be created via a call to xTaskCreate () + * before calling vTaskStartScheduler (). The idle task is created + * automatically when the first application task is created. + * + * See the demo application file main.c for an example of creating + * tasks and starting the kernel. + * + * Example usage: +
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will not get here unless a task calls vTaskEndScheduler ()
+ }
+   
+ * + * \defgroup vTaskStartScheduler vTaskStartScheduler + * \ingroup SchedulerControl + */ +void vTaskStartScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskEndScheduler( void );
+ * + * Stops the real time kernel tick. All created tasks will be automatically + * deleted and multitasking (either preemptive or cooperative) will + * stop. Execution then resumes from the point where vTaskStartScheduler () + * was called, as if vTaskStartScheduler () had just returned. + * + * See the demo application file main. c in the demo/PC directory for an + * example that uses vTaskEndScheduler (). + * + * vTaskEndScheduler () requires an exit function to be defined within the + * portable layer (see vPortEndScheduler () in port. c for the PC port). This + * performs hardware specific operations such as stopping the kernel tick. + * + * vTaskEndScheduler () will cause all of the resources allocated by the + * kernel to be freed - but will not free resources allocated by application + * tasks. + * + * Example usage: +
+ void vTaskCode( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // At some point we want to end the real time kernel processing
+		 // so call ...
+		 vTaskEndScheduler ();
+	 }
+ }
+
+ void vAFunction( void )
+ {
+	 // Create at least one task before starting the kernel.
+	 xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, NULL );
+
+	 // Start the real time kernel with preemption.
+	 vTaskStartScheduler ();
+
+	 // Will only get here when the vTaskCode () task has called
+	 // vTaskEndScheduler ().  When we get here we are back to single task
+	 // execution.
+ }
+   
+ * + * \defgroup vTaskEndScheduler vTaskEndScheduler + * \ingroup SchedulerControl + */ +void vTaskEndScheduler( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskSuspendAll( void );
+ * + * Suspends all real time kernel activity while keeping interrupts (including the + * kernel tick) enabled. + * + * After calling vTaskSuspendAll () the calling task will continue to execute + * without risk of being swapped out until a call to xTaskResumeAll () has been + * made. + * + * API functions that have the potential to cause a context switch (for example, + * vTaskDelayUntil(), xQueueSend(), etc.) must not be called while the scheduler + * is suspended. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the kernel
+		 // tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.
+		 xTaskResumeAll ();
+	 }
+ }
+   
+ * \defgroup vTaskSuspendAll vTaskSuspendAll + * \ingroup SchedulerControl + */ +void vTaskSuspendAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
char xTaskResumeAll( void );
+ * + * Resumes real time kernel activity following a call to vTaskSuspendAll (). + * After a call to vTaskSuspendAll () the kernel will take control of which + * task is executing at any time. + * + * @return If resuming the scheduler caused a context switch then pdTRUE is + * returned, otherwise pdFALSE is returned. + * + * Example usage: +
+ void vTask1( void * pvParameters )
+ {
+	 for( ;; )
+	 {
+		 // Task code goes here.
+
+		 // ...
+
+		 // At some point the task wants to perform a long operation during
+		 // which it does not want to get swapped out.  It cannot use
+		 // taskENTER_CRITICAL ()/taskEXIT_CRITICAL () as the length of the
+		 // operation may cause interrupts to be missed - including the
+		 // ticks.
+
+		 // Prevent the real time kernel swapping out the task.
+		 vTaskSuspendAll ();
+
+		 // Perform the operation here.  There is no need to use critical
+		 // sections as we have all the microcontroller processing time.
+		 // During this time interrupts will still operate and the real
+		 // time kernel tick count will be maintained.
+
+		 // ...
+
+		 // The operation is complete.  Restart the kernel.  We want to force
+		 // a context switch - but there is no point if resuming the scheduler
+		 // caused a context switch already.
+		 if( !xTaskResumeAll () )
+		 {
+			  taskYIELD ();
+		 }
+	 }
+ }
+   
+ * \defgroup xTaskResumeAll xTaskResumeAll + * \ingroup SchedulerControl + */ +signed portBASE_TYPE xTaskResumeAll( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask );
+ * + * Utility task that simply returns pdTRUE if the task referenced by xTask is + * currently in the Suspended state, or pdFALSE if the task referenced by xTask + * is in any other state. + * + */ +signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + +/*----------------------------------------------------------- + * TASK UTILITIES + *----------------------------------------------------------*/ + +/** + * task. h + *
volatile portTickType xTaskGetTickCount( void );
+ * + * @return The count of ticks since vTaskStartScheduler was called. + * + * \page xTaskGetTickCount xTaskGetTickCount + * \ingroup TaskUtils + */ +portTickType xTaskGetTickCount( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned short uxTaskGetNumberOfTasks( void );
+ * + * @return The number of tasks that the real time kernel is currently managing. + * This includes all ready, blocked and suspended tasks. A task that + * has been deleted but not yet freed by the idle task will also be + * included in the count. + * + * \page uxTaskGetNumberOfTasks uxTaskGetNumberOfTasks + * \ingroup TaskUtils + */ +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskList( char *pcWriteBuffer );
+ * + * configUSE_TRACE_FACILITY must be defined as 1 for this function to be + * available. See the configuration section for more information. + * + * NOTE: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Lists all the current tasks, along with their current state and stack + * usage high water mark. + * + * Tasks are reported as blocked ('B'), ready ('R'), deleted ('D') or + * suspended ('S'). + * + * @param pcWriteBuffer A buffer into which the above mentioned details + * will be written, in ascii form. This buffer is assumed to be large + * enough to contain the generated report. Approximately 40 bytes per + * task should be sufficient. + * + * \page vTaskList vTaskList + * \ingroup TaskUtils + */ +void vTaskList( signed char *pcWriteBuffer ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskGetRunTimeStats( char *pcWriteBuffer );
+ * + * configGENERATE_RUN_TIME_STATS must be defined as 1 for this function + * to be available. The application must also then provide definitions + * for portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() and + * portGET_RUN_TIME_COUNTER_VALUE to configure a peripheral timer/counter + * and return the timers current count value respectively. The counter + * should be at least 10 times the frequency of the tick count. + * + * NOTE: This function will disable interrupts for its duration. It is + * not intended for normal application runtime use but as a debug aid. + * + * Setting configGENERATE_RUN_TIME_STATS to 1 will result in a total + * accumulated execution time being stored for each task. The resolution + * of the accumulated time value depends on the frequency of the timer + * configured by the portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() macro. + * Calling vTaskGetRunTimeStats() writes the total execution time of each + * task into a buffer, both as an absolute count value and as a percentage + * of the total system execution time. + * + * @param pcWriteBuffer A buffer into which the execution times will be + * written, in ascii form. This buffer is assumed to be large enough to + * contain the generated report. Approximately 40 bytes per task should + * be sufficient. + * + * \page vTaskGetRunTimeStats vTaskGetRunTimeStats + * \ingroup TaskUtils + */ +void vTaskGetRunTimeStats( signed char *pcWriteBuffer ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
void vTaskStartTrace( char * pcBuffer, unsigned portBASE_TYPE uxBufferSize );
+ * + * Starts a real time kernel activity trace. The trace logs the identity of + * which task is running when. + * + * The trace file is stored in binary format. A separate DOS utility called + * convtrce.exe is used to convert this into a tab delimited text file which + * can be viewed and plotted in a spread sheet. + * + * @param pcBuffer The buffer into which the trace will be written. + * + * @param ulBufferSize The size of pcBuffer in bytes. The trace will continue + * until either the buffer in full, or ulTaskEndTrace () is called. + * + * \page vTaskStartTrace vTaskStartTrace + * \ingroup TaskUtils + */ +void vTaskStartTrace( signed char * pcBuffer, unsigned long ulBufferSize ) PRIVILEGED_FUNCTION; + +/** + * task. h + *
unsigned long ulTaskEndTrace( void );
+ * + * Stops a kernel activity trace. See vTaskStartTrace (). + * + * @return The number of bytes that have been written into the trace buffer. + * + * \page usTaskEndTrace usTaskEndTrace + * \ingroup TaskUtils + */ +unsigned long ulTaskEndTrace( void ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask );
+ * + * INCLUDE_uxTaskGetStackHighWaterMark must be set to 1 in FreeRTOSConfig.h for + * this function to be available. + * + * Returns the high water mark of the stack associated with xTask. That is, + * the minimum free stack space there has been (in bytes) since the task + * started. The smaller the returned number the closer the task has come + * to overflowing its stack. + * + * @param xTask Handle of the task associated with the stack to be checked. + * Set xTask to NULL to check the stack of the calling task. + * + * @return The smallest amount of free stack space there has been (in bytes) + * since the task referenced by xTask was created. + */ +unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction );
+ * + * Sets pxHookFunction to be the task hook function used by the task xTask. + * Passing xTask as NULL has the effect of setting the calling tasks hook + * function. + */ +void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
void xTaskGetApplicationTaskTag( xTaskHandle xTask );
+ * + * Returns the pxHookFunction value assigned to the task xTask. + */ +pdTASK_HOOK_CODE xTaskGetApplicationTaskTag( xTaskHandle xTask ) PRIVILEGED_FUNCTION; + +/** + * task.h + *
portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, pdTASK_HOOK_CODE pxHookFunction );
+ * + * Calls the hook function associated with xTask. Passing xTask as NULL has + * the effect of calling the Running tasks (the calling task) hook function. + * + * pvParameter is passed to the hook function for the task to interpret as it + * wants. + */ +portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParameter ) PRIVILEGED_FUNCTION; + + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + *----------------------------------------------------------*/ + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Called from the real time kernel tick (either preemptive or cooperative), + * this increments the tick count and checks if any tasks that are blocked + * for a finite period required removing from a blocked list and placing on + * a ready list. + */ +void vTaskIncrementTick( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes the calling task from the ready list and places it both + * on the list of tasks waiting for a particular event, and the + * list of delayed tasks. The task will be removed from both lists + * and replaced on the ready list should either the event occur (and + * there be no higher priority tasks waiting on the same event) or + * the delay period expires. + * + * @param pxEventList The list containing tasks that are blocked waiting + * for the event to occur. + * + * @param xTicksToWait The maximum amount of time that the task should wait + * for the event to occur. This is specified in kernel ticks,the constant + * portTICK_RATE_MS can be used to convert kernel ticks into a real time + * period. + */ +void vTaskPlaceOnEventList( const xList * const pxEventList, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED. + * + * Removes a task from both the specified event list and the list of blocked + * tasks, and places it on a ready queue. + * + * xTaskRemoveFromEventList () will be called if either an event occurs to + * unblock a task, or the block timeout period expires. + * + * @return pdTRUE if the task being removed has a higher priority than the task + * making the call, otherwise pdFALSE. + */ +signed portBASE_TYPE xTaskRemoveFromEventList( const xList * const pxEventList ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS AN + * INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * INCLUDE_vTaskCleanUpResources and INCLUDE_vTaskSuspend must be defined as 1 + * for this function to be available. + * See the configuration section for more information. + * + * Empties the ready and delayed queues of task control blocks, freeing the + * memory allocated for the task control block and task stacks as it goes. + */ +void vTaskCleanUpResources( void ) PRIVILEGED_FUNCTION; + +/* + * THIS FUNCTION MUST NOT BE USED FROM APPLICATION CODE. IT IS ONLY + * INTENDED FOR USE WHEN IMPLEMENTING A PORT OF THE SCHEDULER AND IS + * AN INTERFACE WHICH IS FOR THE EXCLUSIVE USE OF THE SCHEDULER. + * + * Sets the pointer to the current TCB to the TCB of the highest priority task + * that is ready to run. + */ +void vTaskSwitchContext( void ) PRIVILEGED_FUNCTION; + +/* + * Return the handle of the calling task. + */ +xTaskHandle xTaskGetCurrentTaskHandle( void ) PRIVILEGED_FUNCTION; + +/* + * Capture the current time status for future reference. + */ +void vTaskSetTimeOutState( xTimeOutType * const pxTimeOut ) PRIVILEGED_FUNCTION; + +/* + * Compare the time status now with that previously captured to see if the + * timeout has expired. + */ +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType * const pxTimeOut, portTickType * const pxTicksToWait ) PRIVILEGED_FUNCTION; + +/* + * Shortcut used by the queue implementation to prevent unnecessary call to + * taskYIELD(); + */ +void vTaskMissedYield( void ) PRIVILEGED_FUNCTION; + +/* + * Returns the scheduler state as taskSCHEDULER_RUNNING, + * taskSCHEDULER_NOT_STARTED or taskSCHEDULER_SUSPENDED. + */ +portBASE_TYPE xTaskGetSchedulerState( void ) PRIVILEGED_FUNCTION; + +/* + * Raises the priority of the mutex holder to that of the calling task should + * the mutex holder have a priority less than the calling task. + */ +void vTaskPriorityInherit( xTaskHandle * const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Set the priority of a task back to its proper priority in the case that it + * inherited a higher priority while it was holding a semaphore. + */ +void vTaskPriorityDisinherit( xTaskHandle * const pxMutexHolder ) PRIVILEGED_FUNCTION; + +/* + * Generic version of the task creation function which is in turn called by the + * xTaskCreate() and xTaskCreateRestricted() macros. + */ +signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pvTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions ) PRIVILEGED_FUNCTION; + +#ifdef __cplusplus +} +#endif +#endif /* TASK_H */ + + + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/list.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/list.c new file mode 100644 index 000000000..fe970ee1a --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/list.c @@ -0,0 +1,191 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#include +#include "FreeRTOS.h" +#include "list.h" + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +void vListInitialise( xList *pxList ) +{ + /* The list structure contains a list item which is used to mark the + end of the list. To initialise the list the list end is inserted + as the only list entry. */ + pxList->pxIndex = ( xListItem * ) &( pxList->xListEnd ); + + /* The list end value is the highest possible value in the list to + ensure it remains at the end of the list. */ + pxList->xListEnd.xItemValue = portMAX_DELAY; + + /* The list end next and previous pointers point to itself so we know + when the list is empty. */ + pxList->xListEnd.pxNext = ( xListItem * ) &( pxList->xListEnd ); + pxList->xListEnd.pxPrevious = ( xListItem * ) &( pxList->xListEnd ); + + pxList->uxNumberOfItems = 0; +} +/*-----------------------------------------------------------*/ + +void vListInitialiseItem( xListItem *pxItem ) +{ + /* Make sure the list item is not recorded as being on a list. */ + pxItem->pvContainer = NULL; +} +/*-----------------------------------------------------------*/ + +void vListInsertEnd( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem * pxIndex; + + /* Insert a new list item into pxList, but rather than sort the list, + makes the new list item the last item to be removed by a call to + pvListGetOwnerOfNextEntry. This means it has to be the item pointed to by + the pxIndex member. */ + pxIndex = pxList->pxIndex; + + pxNewListItem->pxNext = pxIndex->pxNext; + pxNewListItem->pxPrevious = pxList->pxIndex; + pxIndex->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxIndex->pxNext = ( volatile xListItem * ) pxNewListItem; + pxList->pxIndex = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListInsert( xList *pxList, xListItem *pxNewListItem ) +{ +volatile xListItem *pxIterator; +portTickType xValueOfInsertion; + + /* Insert the new list item into the list, sorted in ulListItem order. */ + xValueOfInsertion = pxNewListItem->xItemValue; + + /* If the list already contains a list item with the same item value then + the new list item should be placed after it. This ensures that TCB's which + are stored in ready lists (all of which have the same ulListItem value) + get an equal share of the CPU. However, if the xItemValue is the same as + the back marker the iteration loop below will not end. This means we need + to guard against this by checking the value first and modifying the + algorithm slightly if necessary. */ + if( xValueOfInsertion == portMAX_DELAY ) + { + pxIterator = pxList->xListEnd.pxPrevious; + } + else + { + /* *** NOTE *********************************************************** + If you find your application is crashing here then likely causes are: + 1) Stack overflow - + see http://www.freertos.org/Stacks-and-stack-overflow-checking.html + 2) Incorrect interrupt priority assignment, especially on Cortex M3 + parts where numerically high priority values denote low actual + interrupt priories, which can seem counter intuitive. See + configMAX_SYSCALL_INTERRUPT_PRIORITY on http://www.freertos.org/a00110.html + 3) Calling an API function from within a critical section or when + the scheduler is suspended. + 4) Using a queue or semaphore before it has been initialised or + before the scheduler has been started (are interrupts firing + before vTaskStartScheduler() has been called?). + See http://www.freertos.org/FAQHelp.html for more tips. + **********************************************************************/ + + for( pxIterator = ( xListItem * ) &( pxList->xListEnd ); pxIterator->pxNext->xItemValue <= xValueOfInsertion; pxIterator = pxIterator->pxNext ) + { + /* There is nothing to do here, we are just iterating to the + wanted insertion position. */ + } + } + + pxNewListItem->pxNext = pxIterator->pxNext; + pxNewListItem->pxNext->pxPrevious = ( volatile xListItem * ) pxNewListItem; + pxNewListItem->pxPrevious = pxIterator; + pxIterator->pxNext = ( volatile xListItem * ) pxNewListItem; + + /* Remember which list the item is in. This allows fast removal of the + item later. */ + pxNewListItem->pvContainer = ( void * ) pxList; + + ( pxList->uxNumberOfItems )++; +} +/*-----------------------------------------------------------*/ + +void vListRemove( xListItem *pxItemToRemove ) +{ +xList * pxList; + + pxItemToRemove->pxNext->pxPrevious = pxItemToRemove->pxPrevious; + pxItemToRemove->pxPrevious->pxNext = pxItemToRemove->pxNext; + + /* The list item knows which list it is in. Obtain the list from the list + item. */ + pxList = ( xList * ) pxItemToRemove->pvContainer; + + /* Make sure the index is left pointing to a valid item. */ + if( pxList->pxIndex == pxItemToRemove ) + { + pxList->pxIndex = pxItemToRemove->pxPrevious; + } + + pxItemToRemove->pvContainer = NULL; + ( pxList->uxNumberOfItems )--; +} +/*-----------------------------------------------------------*/ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/port.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/port.c new file mode 100644 index 000000000..639dd1d33 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/port.c @@ -0,0 +1,774 @@ +/* + Copyright (C) 2009 William Davy - william.davy@wittenstein.co.uk + Contributed to FreeRTOS.org V5.3.0. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License (version 2) as published + by the Free Software Foundation and modified by the FreeRTOS exception. + + FreeRTOS.org is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with FreeRTOS.org; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + A special exception to the GPL is included to allow you to distribute a + combined work that includes FreeRTOS.org without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details. + + + *************************************************************************** + * * + * Get the FreeRTOS eBook! See http://www.FreeRTOS.org/Documentation * + * * + * This is a concise, step by step, 'hands on' guide that describes both * + * general multitasking concepts and FreeRTOS specifics. It presents and * + * explains numerous examples that are written using the FreeRTOS API. * + * Full source code for all the examples is provided in an accompanying * + * .zip file. * + * * + *************************************************************************** + + 1 tab == 4 spaces! + + Please ensure to read the configuration and relevant port sections of the + online documentation. + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +/*----------------------------------------------------------- + * Implementation of functions defined in portable.h for the Posix port. + *----------------------------------------------------------*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* Scheduler includes. */ +#include "FreeRTOS.h" +#include "task.h" +/*-----------------------------------------------------------*/ + +#define MAX_NUMBER_OF_TASKS ( _POSIX_THREAD_THREADS_MAX ) +/*-----------------------------------------------------------*/ + +/* Parameters to pass to the newly created pthread. */ +typedef struct XPARAMS +{ + pdTASK_CODE pxCode; + void *pvParams; +} xParams; + +/* Each task maintains its own interrupt status in the critical nesting variable. */ +typedef struct THREAD_SUSPENSIONS +{ + pthread_t hThread; + xTaskHandle hTask; + unsigned portBASE_TYPE uxCriticalNesting; +} xThreadState; +/*-----------------------------------------------------------*/ + +static xThreadState *pxThreads; +static pthread_once_t hSigSetupThread = PTHREAD_ONCE_INIT; +static pthread_attr_t xThreadAttributes; +static pthread_mutex_t xSuspendResumeThreadMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_mutex_t xSingleThreadMutex = PTHREAD_MUTEX_INITIALIZER; +static pthread_t hMainThread = ( pthread_t )NULL; +/*-----------------------------------------------------------*/ + +static volatile portBASE_TYPE xSentinel = 0; +static volatile portBASE_TYPE xSchedulerEnd = pdFALSE; +static volatile portBASE_TYPE xInterruptsEnabled = pdTRUE; +static volatile portBASE_TYPE xServicingTick = pdFALSE; +static volatile portBASE_TYPE xPendYield = pdFALSE; +static volatile portLONG lIndexOfLastAddedTask = 0; +static volatile unsigned portBASE_TYPE uxCriticalNesting; +/*-----------------------------------------------------------*/ + +/* + * Setup the timer to generate the tick interrupts. + */ +static void prvSetupTimerInterrupt( void ); +static void *prvWaitForStart( void * pvParams ); +static void prvSuspendSignalHandler(int sig); +static void prvResumeSignalHandler(int sig); +static void prvSetupSignalsAndSchedulerPolicy( void ); +static void prvSuspendThread( pthread_t xThreadId ); +static void prvResumeThread( pthread_t xThreadId ); +static pthread_t prvGetThreadHandle( xTaskHandle hTask ); +static portLONG prvGetFreeThreadState( void ); +static void prvSetTaskCriticalNesting( pthread_t xThreadId, unsigned portBASE_TYPE uxNesting ); +static unsigned portBASE_TYPE prvGetTaskCriticalNesting( pthread_t xThreadId ); +static void prvDeleteThread( void *xThreadId ); +/*-----------------------------------------------------------*/ + +/* + * Exception handlers. + */ +void vPortYield( void ); +void vPortSystemTickHandler( int sig ); + +/* + * Start first task is a separate function so it can be tested in isolation. + */ +void vPortStartFirstTask( void ); +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portSTACK_TYPE *pxPortInitialiseStack( portSTACK_TYPE *pxTopOfStack, pdTASK_CODE pxCode, void *pvParameters ) +{ +/* Should actually keep this struct on the stack. */ +xParams *pxThisThreadParams = pvPortMalloc( sizeof( xParams ) ); + + (void)pthread_once( &hSigSetupThread, prvSetupSignalsAndSchedulerPolicy ); + + if ( (pthread_t)NULL == hMainThread ) + { + hMainThread = pthread_self(); + } + + /* No need to join the threads. */ + pthread_attr_init( &xThreadAttributes ); + pthread_attr_setdetachstate( &xThreadAttributes, PTHREAD_CREATE_DETACHED ); + + /* Add the task parameters. */ + pxThisThreadParams->pxCode = pxCode; + pxThisThreadParams->pvParams = pvParameters; + + vPortEnterCritical(); + + lIndexOfLastAddedTask = prvGetFreeThreadState(); + + /* Create the new pThread. */ + if ( 0 == pthread_mutex_lock( &xSingleThreadMutex ) ) + { + xSentinel = 0; + if ( 0 != pthread_create( &( pxThreads[ lIndexOfLastAddedTask ].hThread ), &xThreadAttributes, prvWaitForStart, (void *)pxThisThreadParams ) ) + { + /* Thread create failed, signal the failure */ + pxTopOfStack = 0; + } + + /* Wait until the task suspends. */ + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + while ( xSentinel == 0 ); + vPortExitCritical(); + } + + return pxTopOfStack; +} +/*-----------------------------------------------------------*/ + +void vPortStartFirstTask( void ) +{ + /* Initialise the critical nesting count ready for the first task. */ + uxCriticalNesting = 0; + + /* Start the first task. */ + vPortEnableInterrupts(); + + /* Start the first task. */ + prvResumeThread( prvGetThreadHandle( xTaskGetCurrentTaskHandle() ) ); +} +/*-----------------------------------------------------------*/ + +/* + * See header file for description. + */ +portBASE_TYPE xPortStartScheduler( void ) +{ +portBASE_TYPE xResult; +int iSignal; +sigset_t xSignals; +sigset_t xSignalToBlock; +sigset_t xSignalsBlocked; +portLONG lIndex; + + /* Establish the signals to block before they are needed. */ + sigfillset( &xSignalToBlock ); + + /* Block until the end */ + (void)pthread_sigmask( SIG_SETMASK, &xSignalToBlock, &xSignalsBlocked ); + + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + pxThreads[ lIndex ].uxCriticalNesting = 0; + } + + /* Start the timer that generates the tick ISR. Interrupts are disabled + here already. */ + prvSetupTimerInterrupt(); + + /* Start the first task. Will not return unless all threads are killed. */ + vPortStartFirstTask(); + + /* This is the end signal we are looking for. */ + sigemptyset( &xSignals ); + sigaddset( &xSignals, SIG_RESUME ); + + while ( pdTRUE != xSchedulerEnd ) + { + if ( 0 != sigwait( &xSignals, &iSignal ) ) + { + printf( "Main thread spurious signal: %d\n", iSignal ); + } + } + + printf( "Cleaning Up, Exiting.\n" ); + /* Cleanup the mutexes */ + xResult = pthread_mutex_destroy( &xSuspendResumeThreadMutex ); + xResult = pthread_mutex_destroy( &xSingleThreadMutex ); + vPortFree( (void *)pxThreads ); + + /* Should not get here! */ + return 0; +} +/*-----------------------------------------------------------*/ + +void vPortEndScheduler( void ) +{ +portBASE_TYPE xNumberOfThreads; +portBASE_TYPE xResult; + for ( xNumberOfThreads = 0; xNumberOfThreads < MAX_NUMBER_OF_TASKS; xNumberOfThreads++ ) + { + if ( ( pthread_t )NULL != pxThreads[ xNumberOfThreads ].hThread ) + { + /* Kill all of the threads, they are in the detached state. */ + xResult = pthread_cancel( pxThreads[ xNumberOfThreads ].hThread ); + } + } + + /* Signal the scheduler to exit its loop. */ + xSchedulerEnd = pdTRUE; + (void)pthread_kill( hMainThread, SIG_RESUME ); +} +/*-----------------------------------------------------------*/ + +void vPortYieldFromISR( void ) +{ + /* Calling Yield from a Interrupt/Signal handler often doesn't work because the + * xSingleThreadMutex is already owned by an original call to Yield. Therefore, + * simply indicate that a yield is required soon. + */ + xPendYield = pdTRUE; +} +/*-----------------------------------------------------------*/ + +void vPortEnterCritical( void ) +{ + vPortDisableInterrupts(); + uxCriticalNesting++; +} +/*-----------------------------------------------------------*/ + +void vPortExitCritical( void ) +{ + /* Check for unmatched exits. */ + if ( uxCriticalNesting > 0 ) + { + uxCriticalNesting--; + } + + /* If we have reached 0 then re-enable the interrupts. */ + if( uxCriticalNesting == 0 ) + { + /* Have we missed ticks? This is the equivalent of pending an interrupt. */ + if ( pdTRUE == xPendYield ) + { + xPendYield = pdFALSE; + vPortYield(); + } + vPortEnableInterrupts(); + } +} +/*-----------------------------------------------------------*/ + +void vPortYield( void ) +{ +pthread_t xTaskToSuspend; +pthread_t xTaskToResume; + + if ( 0 == pthread_mutex_lock( &xSingleThreadMutex ) ) + { + xTaskToSuspend = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + + vTaskSwitchContext(); + + xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + if ( xTaskToSuspend != xTaskToResume ) + { + /* Remember and switch the critical nesting. */ + prvSetTaskCriticalNesting( xTaskToSuspend, uxCriticalNesting ); + uxCriticalNesting = prvGetTaskCriticalNesting( xTaskToResume ); + /* Switch tasks. */ + prvResumeThread( xTaskToResume ); + prvSuspendThread( xTaskToSuspend ); + } + else + { + /* Yielding to self */ + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + } + } +} +/*-----------------------------------------------------------*/ + +void vPortDisableInterrupts( void ) +{ + xInterruptsEnabled = pdFALSE; +} +/*-----------------------------------------------------------*/ + +void vPortEnableInterrupts( void ) +{ + xInterruptsEnabled = pdTRUE; +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xPortSetInterruptMask( void ) +{ +portBASE_TYPE xReturn = xInterruptsEnabled; + xInterruptsEnabled = pdFALSE; + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vPortClearInterruptMask( portBASE_TYPE xMask ) +{ + xInterruptsEnabled = xMask; +} +/*-----------------------------------------------------------*/ + +/* + * Setup the systick timer to generate the tick interrupts at the required + * frequency. + */ +void prvSetupTimerInterrupt( void ) +{ +struct itimerval itimer, oitimer; +portTickType xMicroSeconds = portTICK_RATE_MICROSECONDS; + + /* Initialise the structure with the current timer information. */ + if ( 0 == getitimer( TIMER_TYPE, &itimer ) ) + { + /* Set the interval between timer events. */ + itimer.it_interval.tv_sec = 0; + itimer.it_interval.tv_usec = xMicroSeconds; + + /* Set the current count-down. */ + itimer.it_value.tv_sec = 0; + itimer.it_value.tv_usec = xMicroSeconds; + + /* Set-up the timer interrupt. */ + if ( 0 != setitimer( TIMER_TYPE, &itimer, &oitimer ) ) + { + printf( "Set Timer problem.\n" ); + } + } + else + { + printf( "Get Timer problem.\n" ); + } +} +/*-----------------------------------------------------------*/ + +void vPortSystemTickHandler( int sig ) +{ +pthread_t xTaskToSuspend; +pthread_t xTaskToResume; + + if ( ( pdTRUE == xInterruptsEnabled ) && ( pdTRUE != xServicingTick ) ) + { + if ( 0 == pthread_mutex_trylock( &xSingleThreadMutex ) ) + { + xServicingTick = pdTRUE; + + xTaskToSuspend = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + /* Tick Increment. */ + vTaskIncrementTick(); + + /* Select Next Task. */ +#if ( configUSE_PREEMPTION == 1 ) + vTaskSwitchContext(); +#endif + xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + + /* The only thread that can process this tick is the running thread. */ + if ( xTaskToSuspend != xTaskToResume ) + { + /* Remember and switch the critical nesting. */ + prvSetTaskCriticalNesting( xTaskToSuspend, uxCriticalNesting ); + uxCriticalNesting = prvGetTaskCriticalNesting( xTaskToResume ); + /* Resume next task. */ + prvResumeThread( xTaskToResume ); + /* Suspend the current task. */ + prvSuspendThread( xTaskToSuspend ); + } + else + { + /* Release the lock as we are Resuming. */ + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + } + xServicingTick = pdFALSE; + } + else + { + xPendYield = pdTRUE; + } + } + else + { + xPendYield = pdTRUE; + } +} +/*-----------------------------------------------------------*/ + +void vPortForciblyEndThread( void *pxTaskToDelete ) +{ +xTaskHandle hTaskToDelete = ( xTaskHandle )pxTaskToDelete; +pthread_t xTaskToDelete; +pthread_t xTaskToResume; +portBASE_TYPE xResult; + + if ( 0 == pthread_mutex_lock( &xSingleThreadMutex ) ) + { + xTaskToDelete = prvGetThreadHandle( hTaskToDelete ); + xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + + if ( xTaskToResume == xTaskToDelete ) + { + /* This is a suicidal thread, need to select a different task to run. */ + vTaskSwitchContext(); + xTaskToResume = prvGetThreadHandle( xTaskGetCurrentTaskHandle() ); + } + + if ( pthread_self() != xTaskToDelete ) + { + /* Cancelling a thread that is not me. */ + if ( xTaskToDelete != ( pthread_t )NULL ) + { + /* Send a signal to wake the task so that it definitely cancels. */ + pthread_testcancel(); + xResult = pthread_cancel( xTaskToDelete ); + /* Pthread Clean-up function will note the cancellation. */ + } + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + } + else + { + /* Resume the other thread. */ + prvResumeThread( xTaskToResume ); + /* Pthread Clean-up function will note the cancellation. */ + /* Release the execution. */ + uxCriticalNesting = 0; + vPortEnableInterrupts(); + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + /* Commit suicide */ + pthread_exit( (void *)1 ); + } + } +} +/*-----------------------------------------------------------*/ + +void *prvWaitForStart( void * pvParams ) +{ +xParams * pxParams = ( xParams * )pvParams; +pdTASK_CODE pvCode = pxParams->pxCode; +void * pParams = pxParams->pvParams; + vPortFree( pvParams ); + + pthread_cleanup_push( prvDeleteThread, (void *)pthread_self() ); + + if ( 0 == pthread_mutex_lock( &xSingleThreadMutex ) ) + { + prvSuspendThread( pthread_self() ); + } + + pvCode( pParams ); + + pthread_cleanup_pop( 1 ); + return (void *)NULL; +} +/*-----------------------------------------------------------*/ + +void prvSuspendSignalHandler(int sig) +{ +sigset_t xSignals; + + /* Only interested in the resume signal. */ + sigemptyset( &xSignals ); + sigaddset( &xSignals, SIG_RESUME ); + xSentinel = 1; + + /* Unlock the Single thread mutex to allow the resumed task to continue. */ + if ( 0 != pthread_mutex_unlock( &xSingleThreadMutex ) ) + { + printf( "Releasing someone else's lock.\n" ); + } + + /* Wait on the resume signal. */ + if ( 0 != sigwait( &xSignals, &sig ) ) + { + printf( "SSH: Sw %d\n", sig ); + } + + /* Will resume here when the SIG_RESUME signal is received. */ + /* Need to set the interrupts based on the task's critical nesting. */ + if ( uxCriticalNesting == 0 ) + { + vPortEnableInterrupts(); + } + else + { + vPortDisableInterrupts(); + } +} +/*-----------------------------------------------------------*/ + +void prvSuspendThread( pthread_t xThreadId ) +{ +portBASE_TYPE xResult = pthread_mutex_lock( &xSuspendResumeThreadMutex ); + if ( 0 == xResult ) + { + /* Set-up for the Suspend Signal handler? */ + xSentinel = 0; + xResult = pthread_mutex_unlock( &xSuspendResumeThreadMutex ); + xResult = pthread_kill( xThreadId, SIG_SUSPEND ); + while ( ( xSentinel == 0 ) && ( pdTRUE != xServicingTick ) ) + { + sched_yield(); + } + } +} +/*-----------------------------------------------------------*/ + +void prvResumeSignalHandler(int sig) +{ + /* Yield the Scheduler to ensure that the yielding thread completes. */ + if ( 0 == pthread_mutex_lock( &xSingleThreadMutex ) ) + { + (void)pthread_mutex_unlock( &xSingleThreadMutex ); + } +} +/*-----------------------------------------------------------*/ + +void prvResumeThread( pthread_t xThreadId ) +{ +portBASE_TYPE xResult; + if ( 0 == pthread_mutex_lock( &xSuspendResumeThreadMutex ) ) + { + if ( pthread_self() != xThreadId ) + { + xResult = pthread_kill( xThreadId, SIG_RESUME ); + } + xResult = pthread_mutex_unlock( &xSuspendResumeThreadMutex ); + } +} +/*-----------------------------------------------------------*/ + +void prvSetupSignalsAndSchedulerPolicy( void ) +{ +/* The following code would allow for configuring the scheduling of this task as a Real-time task. + * The process would then need to be run with higher privileges for it to take affect. +int iPolicy; +int iResult; +int iSchedulerPriority; + iResult = pthread_getschedparam( pthread_self(), &iPolicy, &iSchedulerPriority ); + iResult = pthread_attr_setschedpolicy( &xThreadAttributes, SCHED_FIFO ); + iPolicy = SCHED_FIFO; + iResult = pthread_setschedparam( pthread_self(), iPolicy, &iSchedulerPriority ); */ + +struct sigaction sigsuspendself, sigresume, sigtick; +portLONG lIndex; + + pxThreads = ( xThreadState *)pvPortMalloc( sizeof( xThreadState ) * MAX_NUMBER_OF_TASKS ); + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + pxThreads[ lIndex ].hThread = ( pthread_t )NULL; + pxThreads[ lIndex ].hTask = ( xTaskHandle )NULL; + pxThreads[ lIndex ].uxCriticalNesting = 0; + } + + sigsuspendself.sa_flags = 0; + sigsuspendself.sa_handler = prvSuspendSignalHandler; + sigfillset( &sigsuspendself.sa_mask ); + + sigresume.sa_flags = 0; + sigresume.sa_handler = prvResumeSignalHandler; + sigfillset( &sigresume.sa_mask ); + + sigtick.sa_flags = 0; + sigtick.sa_handler = vPortSystemTickHandler; + sigfillset( &sigtick.sa_mask ); + + if ( 0 != sigaction( SIG_SUSPEND, &sigsuspendself, NULL ) ) + { + printf( "Problem installing SIG_SUSPEND_SELF\n" ); + } + if ( 0 != sigaction( SIG_RESUME, &sigresume, NULL ) ) + { + printf( "Problem installing SIG_RESUME\n" ); + } + if ( 0 != sigaction( SIG_TICK, &sigtick, NULL ) ) + { + printf( "Problem installing SIG_TICK\n" ); + } + printf( "Running as PID: %d\n", getpid() ); +} +/*-----------------------------------------------------------*/ + +pthread_t prvGetThreadHandle( xTaskHandle hTask ) +{ +pthread_t hThread = ( pthread_t )NULL; +portLONG lIndex; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hTask == hTask ) + { + hThread = pxThreads[ lIndex ].hThread; + break; + } + } + return hThread; +} +/*-----------------------------------------------------------*/ + +portLONG prvGetFreeThreadState( void ) +{ +portLONG lIndex; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hThread == ( pthread_t )NULL ) + { + break; + } + } + + if ( MAX_NUMBER_OF_TASKS == lIndex ) + { + printf( "No more free threads, please increase the maximum.\n" ); + lIndex = 0; + vPortEndScheduler(); + } + + return lIndex; +} +/*-----------------------------------------------------------*/ + +void prvSetTaskCriticalNesting( pthread_t xThreadId, unsigned portBASE_TYPE uxNesting ) +{ +portLONG lIndex; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hThread == xThreadId ) + { + pxThreads[ lIndex ].uxCriticalNesting = uxNesting; + break; + } + } +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE prvGetTaskCriticalNesting( pthread_t xThreadId ) +{ +unsigned portBASE_TYPE uxNesting = 0; +portLONG lIndex; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hThread == xThreadId ) + { + uxNesting = pxThreads[ lIndex ].uxCriticalNesting; + break; + } + } + return uxNesting; +} +/*-----------------------------------------------------------*/ + +void prvDeleteThread( void *xThreadId ) +{ +portLONG lIndex; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hThread == ( pthread_t )xThreadId ) + { + pxThreads[ lIndex ].hThread = (pthread_t)NULL; + pxThreads[ lIndex ].hTask = (xTaskHandle)NULL; + if ( pxThreads[ lIndex ].uxCriticalNesting > 0 ) + { + uxCriticalNesting = 0; + vPortEnableInterrupts(); + } + pxThreads[ lIndex ].uxCriticalNesting = 0; + break; + } + } +} +/*-----------------------------------------------------------*/ + +void vPortAddTaskHandle( void *pxTaskHandle ) +{ +portLONG lIndex; + + pxThreads[ lIndexOfLastAddedTask ].hTask = ( xTaskHandle )pxTaskHandle; + for ( lIndex = 0; lIndex < MAX_NUMBER_OF_TASKS; lIndex++ ) + { + if ( pxThreads[ lIndex ].hThread == pxThreads[ lIndexOfLastAddedTask ].hThread ) + { + if ( pxThreads[ lIndex ].hTask != pxThreads[ lIndexOfLastAddedTask ].hTask ) + { + pxThreads[ lIndex ].hThread = ( pthread_t )NULL; + pxThreads[ lIndex ].hTask = NULL; + pxThreads[ lIndex ].uxCriticalNesting = 0; + } + } + } +} +/*-----------------------------------------------------------*/ + +void vPortFindTicksPerSecond( void ) +{ + /* Needs to be reasonably high for accuracy. */ + unsigned long ulTicksPerSecond = sysconf(_SC_CLK_TCK); + printf( "Timer Resolution for Run TimeStats is %ld ticks per second.\n", ulTicksPerSecond ); +} +/*-----------------------------------------------------------*/ + +unsigned long ulPortGetTimerValue( void ) +{ +struct tms xTimes; + unsigned long ulTotalTime = times( &xTimes ); + /* Return the application code times. + * The timer only increases when the application code is actually running + * which means that the total execution times should add up to 100%. + */ + return ( unsigned long ) xTimes.tms_utime; + + /* Should check ulTotalTime for being clock_t max minus 1. */ + (void)ulTotalTime; +} +/*-----------------------------------------------------------*/ diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/portmacro.h b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/portmacro.h new file mode 100644 index 000000000..8f4b9146e --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/GCC/Posix/portmacro.h @@ -0,0 +1,168 @@ +/* + FreeRTOS.org V5.2.0 - Copyright (C) 2003-2009 Richard Barry. + + This file is part of the FreeRTOS.org distribution. + + FreeRTOS.org is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License (version 2) as published + by the Free Software Foundation and modified by the FreeRTOS exception. + + FreeRTOS.org is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. + + You should have received a copy of the GNU General Public License along + with FreeRTOS.org; if not, write to the Free Software Foundation, Inc., 59 + Temple Place, Suite 330, Boston, MA 02111-1307 USA. + + A special exception to the GPL is included to allow you to distribute a + combined work that includes FreeRTOS.org without being obliged to provide + the source code for any proprietary components. See the licensing section + of http://www.FreeRTOS.org for full details. + + + *************************************************************************** + * * + * Get the FreeRTOS eBook! See http://www.FreeRTOS.org/Documentation * + * * + * This is a concise, step by step, 'hands on' guide that describes both * + * general multitasking concepts and FreeRTOS specifics. It presents and * + * explains numerous examples that are written using the FreeRTOS API. * + * Full source code for all the examples is provided in an accompanying * + * .zip file. * + * * + *************************************************************************** + + 1 tab == 4 spaces! + + Please ensure to read the configuration and relevant port sections of the + online documentation. + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#ifndef PORTMACRO_H +#define PORTMACRO_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*----------------------------------------------------------- + * Port specific definitions. + * + * The settings in this file configure FreeRTOS correctly for the + * given hardware and compiler. + * + * These settings should not be altered. + *----------------------------------------------------------- + */ + +/* Type definitions. */ +#define portCHAR char +#define portFLOAT float +#define portDOUBLE double +#define portLONG int +#define portSHORT short +#define portSTACK_TYPE unsigned long +#define portBASE_TYPE long + +#if( configUSE_16_BIT_TICKS == 1 ) + typedef unsigned portSHORT portTickType; + #define portMAX_DELAY ( portTickType ) 0xffff +#else + typedef unsigned portLONG portTickType; + #define portMAX_DELAY ( portTickType ) 0xffffffff +#endif +/*-----------------------------------------------------------*/ + +/* Architecture specifics. */ +#define portSTACK_GROWTH ( -1 ) +#define portTICK_RATE_MS ( ( portTickType ) 1000 / configTICK_RATE_HZ ) +#define portTICK_RATE_MICROSECONDS ( ( portTickType ) 1000000 / configTICK_RATE_HZ ) +#define portBYTE_ALIGNMENT 4 +#define portREMOVE_STATIC_QUALIFIER +/*-----------------------------------------------------------*/ + + +/* Scheduler utilities. */ +extern void vPortYieldFromISR( void ); +extern void vPortYield( void ); + +#define portYIELD() vPortYield() + +#define portEND_SWITCHING_ISR( xSwitchRequired ) if( xSwitchRequired ) vPortYieldFromISR() +/*-----------------------------------------------------------*/ + + +/* Critical section management. */ +extern void vPortDisableInterrupts( void ); +extern void vPortEnableInterrupts( void ); +#define portSET_INTERRUPT_MASK() ( vPortDisableInterrupts() ) +#define portCLEAR_INTERRUPT_MASK() ( vPortEnableInterrupts() ) + +extern portBASE_TYPE xPortSetInterruptMask( void ); +extern void vPortClearInterruptMask( portBASE_TYPE xMask ); + +#define portSET_INTERRUPT_MASK_FROM_ISR() xPortSetInterruptMask() +#define portCLEAR_INTERRUPT_MASK_FROM_ISR(x) vPortClearInterruptMask(x) + + +extern void vPortEnterCritical( void ); +extern void vPortExitCritical( void ); + +#define portDISABLE_INTERRUPTS() portSET_INTERRUPT_MASK() +#define portENABLE_INTERRUPTS() portCLEAR_INTERRUPT_MASK() +#define portENTER_CRITICAL() vPortEnterCritical() +#define portEXIT_CRITICAL() vPortExitCritical() +/*-----------------------------------------------------------*/ + +/* Task function macros as described on the FreeRTOS.org WEB site. */ +#define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters ) +#define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters ) + +#define portNOP() + +#define portOUTPUT_BYTE( a, b ) + +extern void vPortForciblyEndThread( void *pxTaskToDelete ); +#define traceTASK_DELETE( pxTaskToDelete ) vPortForciblyEndThread( pxTaskToDelete ) + +extern void vPortAddTaskHandle( void *pxTaskHandle ); +#define traceTASK_CREATE( pxNewTCB ) vPortAddTaskHandle( pxNewTCB ) + +/* Posix Signal definitions that can be changed or read as appropriate. */ +#define SIG_SUSPEND SIGUSR1 +#define SIG_RESUME SIGUSR2 + +/* Enable the following hash defines to make use of the real-time tick where time progresses at real-time. */ +#define SIG_TICK SIGALRM +#define TIMER_TYPE ITIMER_REAL +/* Enable the following hash defines to make use of the process tick where time progresses only when the process is executing. +#define SIG_TICK SIGVTALRM +#define TIMER_TYPE ITIMER_VIRTUAL */ +/* Enable the following hash defines to make use of the profile tick where time progresses when the process or system calls are executing. +#define SIG_TICK SIGPROF +#define TIMER_TYPE ITIMER_PROF */ + +/* Make use of times(man 2) to gather run-time statistics on the tasks. */ +extern void vPortFindTicksPerSecond( void ); +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() vPortFindTicksPerSecond() /* Nothing to do because the timer is already present. */ +extern unsigned long ulPortGetTimerValue( void ); +#define portGET_RUN_TIME_COUNTER_VALUE() ulPortGetTimerValue() /* Query the System time stats for this process. */ + +#ifdef __cplusplus +} +#endif + +#endif /* PORTMACRO_H */ + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/MemMang/heap_3.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/MemMang/heap_3.c new file mode 100644 index 000000000..e4fd22f8a --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/MemMang/heap_3.c @@ -0,0 +1,117 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +/* + * Implementation of pvPortMalloc() and vPortFree() that relies on the + * compilers own malloc() and free() implementations. + * + * This file can only be used if the linker is configured to to generate + * a heap memory area. + * + * See heap_2.c and heap_1.c for alternative implementations, and the memory + * management pages of http://www.FreeRTOS.org for more information. + */ + +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/*-----------------------------------------------------------*/ + +void *pvPortMalloc( size_t xWantedSize ) +{ +void *pvReturn; + + vTaskSuspendAll(); + { + pvReturn = malloc( xWantedSize ); + } + xTaskResumeAll(); + + #if( configUSE_MALLOC_FAILED_HOOK == 1 ) + { + if( pvReturn == NULL ) + { + extern void vApplicationMallocFailedHook( void ); + vApplicationMallocFailedHook(); + } + } + #endif + + return pvReturn; +} +/*-----------------------------------------------------------*/ + +void vPortFree( void *pv ) +{ + if( pv ) + { + vTaskSuspendAll(); + { + free( pv ); + } + xTaskResumeAll(); + } +} + + + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/readme.txt b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/readme.txt new file mode 100644 index 000000000..a20d687e0 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/portable/readme.txt @@ -0,0 +1,19 @@ +Each real time kernel port consists of three files that contain the core kernel +components and are common to every port, and one or more files that are +specific to a particular microcontroller and/or compiler. + + ++ The FreeRTOS/Source/Portable/MemMang directory contains the three sample +memory allocators as described on the http://www.FreeRTOS.org WEB site. + ++ The other directories each contain files specific to a particular +microcontroller or compiler. + + + +For example, if you are interested in the GCC port for the ATMega323 +microcontroller then the port specific files are contained in +FreeRTOS/Source/Portable/GCC/ATMega323 directory. If this is the only +port you are interested in then all the other directories can be +ignored. + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/queue.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/queue.c new file mode 100644 index 000000000..7258f7a3e --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/queue.c @@ -0,0 +1,1465 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "croutine.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/*----------------------------------------------------------- + * PUBLIC LIST API documented in list.h + *----------------------------------------------------------*/ + +/* Constants used with the cRxLock and cTxLock structure members. */ +#define queueUNLOCKED ( ( signed portBASE_TYPE ) -1 ) +#define queueLOCKED_UNMODIFIED ( ( signed portBASE_TYPE ) 0 ) + +#define queueERRONEOUS_UNBLOCK ( -1 ) + +/* For internal use only. */ +#define queueSEND_TO_BACK ( 0 ) +#define queueSEND_TO_FRONT ( 1 ) + +/* Effectively make a union out of the xQUEUE structure. */ +#define pxMutexHolder pcTail +#define uxQueueType pcHead +#define uxRecursiveCallCount pcReadFrom +#define queueQUEUE_IS_MUTEX NULL + +/* Semaphores do not actually store or copy data, so have an items size of +zero. */ +#define queueSEMAPHORE_QUEUE_ITEM_LENGTH ( 0 ) +#define queueDONT_BLOCK ( ( portTickType ) 0 ) +#define queueMUTEX_GIVE_BLOCK_TIME ( ( portTickType ) 0 ) + +/* + * Definition of the queue used by the scheduler. + * Items are queued by copy, not reference. + */ +typedef struct QueueDefinition +{ + signed char *pcHead; /*< Points to the beginning of the queue storage area. */ + signed char *pcTail; /*< Points to the byte at the end of the queue storage area. Once more byte is allocated than necessary to store the queue items, this is used as a marker. */ + + signed char *pcWriteTo; /*< Points to the free next place in the storage area. */ + signed char *pcReadFrom; /*< Points to the last place that a queued item was read from. */ + + xList xTasksWaitingToSend; /*< List of tasks that are blocked waiting to post onto this queue. Stored in priority order. */ + xList xTasksWaitingToReceive; /*< List of tasks that are blocked waiting to read from this queue. Stored in priority order. */ + + volatile unsigned portBASE_TYPE uxMessagesWaiting;/*< The number of items currently in the queue. */ + unsigned portBASE_TYPE uxLength; /*< The length of the queue defined as the number of items it will hold, not the number of bytes. */ + unsigned portBASE_TYPE uxItemSize; /*< The size of each items that the queue will hold. */ + + signed portBASE_TYPE xRxLock; /*< Stores the number of items received from the queue (removed from the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + signed portBASE_TYPE xTxLock; /*< Stores the number of items transmitted to the queue (added to the queue) while the queue was locked. Set to queueUNLOCKED when the queue is not locked. */ + +} xQUEUE; +/*-----------------------------------------------------------*/ + +/* + * Inside this file xQueueHandle is a pointer to a xQUEUE structure. + * To keep the definition private the API header file defines it as a + * pointer to void. + */ +typedef xQUEUE * xQueueHandle; + +/* + * Prototypes for public functions are included here so we don't have to + * include the API header file (as it defines xQueueHandle differently). These + * functions are documented in the API header file. + */ +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericSend( xQueueHandle xQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +void vQueueDelete( xQueueHandle xQueue ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ) PRIVILEGED_FUNCTION; +xQueueHandle xQueueCreateMutex( void ) PRIVILEGED_FUNCTION; +xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ) PRIVILEGED_FUNCTION; +portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle xMutex, portTickType xBlockTime ) PRIVILEGED_FUNCTION; +portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle xMutex ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Co-routine queue functions differ from task queue functions. Co-routines are + * an optional component. + */ +#if configUSE_CO_ROUTINES == 1 + signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxTaskWoken ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; + signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) PRIVILEGED_FUNCTION; +#endif + +/* + * The queue registry is just a means for kernel aware debuggers to locate + * queue structures. It has no other purpose so is an optional component. + */ +#if configQUEUE_REGISTRY_SIZE > 0 + + /* The type stored within the queue registry array. This allows a name + to be assigned to each queue making kernel aware debugging a little + more user friendly. */ + typedef struct QUEUE_REGISTRY_ITEM + { + signed char *pcQueueName; + xQueueHandle xHandle; + } xQueueRegistryItem; + + /* The queue registry is simply an array of xQueueRegistryItem structures. + The pcQueueName member of a structure being NULL is indicative of the + array position being vacant. */ + xQueueRegistryItem xQueueRegistry[ configQUEUE_REGISTRY_SIZE ]; + + /* Removes a queue from the registry by simply setting the pcQueueName + member to NULL. */ + static void vQueueUnregisterQueue( xQueueHandle xQueue ) PRIVILEGED_FUNCTION; + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcQueueName ) PRIVILEGED_FUNCTION; +#endif + +/* + * Unlocks a queue locked by a call to prvLockQueue. Locking a queue does not + * prevent an ISR from adding or removing items to the queue, but does prevent + * an ISR from removing tasks from the queue event lists. If an ISR finds a + * queue is locked it will instead increment the appropriate queue lock count + * to indicate that a task may require unblocking. When the queue in unlocked + * these lock counts are inspected, and the appropriate action taken. + */ +static void prvUnlockQueue( xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any data in a queue. + * + * @return pdTRUE if the queue contains no items, otherwise pdFALSE. + */ +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Uses a critical section to determine if there is any space in a queue. + * + * @return pdTRUE if there is no space, otherwise pdFALSE; + */ +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ) PRIVILEGED_FUNCTION; + +/* + * Copies an item into the queue, either at the front of the queue or the + * back of the queue. + */ +static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition ) PRIVILEGED_FUNCTION; + +/* + * Copies an item out of a queue. + */ +static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer ) PRIVILEGED_FUNCTION; +/*-----------------------------------------------------------*/ + +/* + * Macro to mark a queue as locked. Locking a queue prevents an ISR from + * accessing the queue event lists. + */ +#define prvLockQueue( pxQueue ) \ +{ \ + taskENTER_CRITICAL(); \ + { \ + if( pxQueue->xRxLock == queueUNLOCKED ) \ + { \ + pxQueue->xRxLock = queueLOCKED_UNMODIFIED; \ + } \ + if( pxQueue->xTxLock == queueUNLOCKED ) \ + { \ + pxQueue->xTxLock = queueLOCKED_UNMODIFIED; \ + } \ + } \ + taskEXIT_CRITICAL(); \ +} +/*-----------------------------------------------------------*/ + + +/*----------------------------------------------------------- + * PUBLIC QUEUE MANAGEMENT API documented in queue.h + *----------------------------------------------------------*/ + +xQueueHandle xQueueCreate( unsigned portBASE_TYPE uxQueueLength, unsigned portBASE_TYPE uxItemSize ) +{ +xQUEUE *pxNewQueue; +size_t xQueueSizeInBytes; + + /* Allocate the new queue structure. */ + if( uxQueueLength > ( unsigned portBASE_TYPE ) 0 ) + { + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Create the list of pointers to queue items. The queue is one byte + longer than asked for to make wrap checking easier/faster. */ + xQueueSizeInBytes = ( size_t ) ( uxQueueLength * uxItemSize ) + ( size_t ) 1; + + pxNewQueue->pcHead = ( signed char * ) pvPortMalloc( xQueueSizeInBytes ); + if( pxNewQueue->pcHead != NULL ) + { + /* Initialise the queue members as described above where the + queue type is defined. */ + pxNewQueue->pcTail = pxNewQueue->pcHead + ( uxQueueLength * uxItemSize ); + pxNewQueue->uxMessagesWaiting = 0; + pxNewQueue->pcWriteTo = pxNewQueue->pcHead; + pxNewQueue->pcReadFrom = pxNewQueue->pcHead + ( ( uxQueueLength - 1 ) * uxItemSize ); + pxNewQueue->uxLength = uxQueueLength; + pxNewQueue->uxItemSize = uxItemSize; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Likewise ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + traceQUEUE_CREATE( pxNewQueue ); + return pxNewQueue; + } + else + { + traceQUEUE_CREATE_FAILED(); + vPortFree( pxNewQueue ); + } + } + } + + /* Will only reach here if we could not allocate enough memory or no memory + was required. */ + return NULL; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + xQueueHandle xQueueCreateMutex( void ) + { + xQUEUE *pxNewQueue; + + /* Allocate the new queue structure. */ + pxNewQueue = ( xQUEUE * ) pvPortMalloc( sizeof( xQUEUE ) ); + if( pxNewQueue != NULL ) + { + /* Information required for priority inheritance. */ + pxNewQueue->pxMutexHolder = NULL; + pxNewQueue->uxQueueType = queueQUEUE_IS_MUTEX; + + /* Queues used as a mutex no data is actually copied into or out + of the queue. */ + pxNewQueue->pcWriteTo = NULL; + pxNewQueue->pcReadFrom = NULL; + + /* Each mutex has a length of 1 (like a binary semaphore) and + an item size of 0 as nothing is actually copied into or out + of the mutex. */ + pxNewQueue->uxMessagesWaiting = 0; + pxNewQueue->uxLength = 1; + pxNewQueue->uxItemSize = 0; + pxNewQueue->xRxLock = queueUNLOCKED; + pxNewQueue->xTxLock = queueUNLOCKED; + + /* Ensure the event queues start with the correct state. */ + vListInitialise( &( pxNewQueue->xTasksWaitingToSend ) ); + vListInitialise( &( pxNewQueue->xTasksWaitingToReceive ) ); + + /* Start with the semaphore in the expected state. */ + xQueueGenericSend( pxNewQueue, NULL, 0, queueSEND_TO_BACK ); + + traceCREATE_MUTEX( pxNewQueue ); + } + else + { + traceCREATE_MUTEX_FAILED(); + } + + return pxNewQueue; + } + +#endif /* configUSE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_RECURSIVE_MUTEXES == 1 + + portBASE_TYPE xQueueGiveMutexRecursive( xQueueHandle pxMutex ) + { + portBASE_TYPE xReturn; + + /* If this is the task that holds the mutex then pxMutexHolder will not + change outside of this task. If this task does not hold the mutex then + pxMutexHolder can never coincidentally equal the tasks handle, and as + this is the only condition we are interested in it does not matter if + pxMutexHolder is accessed simultaneously by another task. Therefore no + mutual exclusion is required to test the pxMutexHolder variable. */ + if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() ) + { + traceGIVE_MUTEX_RECURSIVE( pxMutex ); + + /* uxRecursiveCallCount cannot be zero if pxMutexHolder is equal to + the task handle, therefore no underflow check is required. Also, + uxRecursiveCallCount is only modified by the mutex holder, and as + there can only be one, no mutual exclusion is required to modify the + uxRecursiveCallCount member. */ + ( pxMutex->uxRecursiveCallCount )--; + + /* Have we unwound the call count? */ + if( pxMutex->uxRecursiveCallCount == 0 ) + { + /* Return the mutex. This will automatically unblock any other + task that might be waiting to access the mutex. */ + xQueueGenericSend( pxMutex, NULL, queueMUTEX_GIVE_BLOCK_TIME, queueSEND_TO_BACK ); + } + + xReturn = pdPASS; + } + else + { + /* We cannot give the mutex because we are not the holder. */ + xReturn = pdFAIL; + + traceGIVE_MUTEX_RECURSIVE_FAILED( pxMutex ); + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_RECURSIVE_MUTEXES == 1 + + portBASE_TYPE xQueueTakeMutexRecursive( xQueueHandle pxMutex, portTickType xBlockTime ) + { + portBASE_TYPE xReturn; + + /* Comments regarding mutual exclusion as per those within + xQueueGiveMutexRecursive(). */ + + traceTAKE_MUTEX_RECURSIVE( pxMutex ); + + if( pxMutex->pxMutexHolder == xTaskGetCurrentTaskHandle() ) + { + ( pxMutex->uxRecursiveCallCount )++; + xReturn = pdPASS; + } + else + { + xReturn = xQueueGenericReceive( pxMutex, NULL, xBlockTime, pdFALSE ); + + /* pdPASS will only be returned if we successfully obtained the mutex, + we may have blocked to reach here. */ + if( xReturn == pdPASS ) + { + ( pxMutex->uxRecursiveCallCount )++; + } + } + + return xReturn; + } + +#endif /* configUSE_RECURSIVE_MUTEXES */ +/*-----------------------------------------------------------*/ + +#if configUSE_COUNTING_SEMAPHORES == 1 + + xQueueHandle xQueueCreateCountingSemaphore( unsigned portBASE_TYPE uxCountValue, unsigned portBASE_TYPE uxInitialCount ) + { + xQueueHandle pxHandle; + + pxHandle = xQueueCreate( ( unsigned portBASE_TYPE ) uxCountValue, queueSEMAPHORE_QUEUE_ITEM_LENGTH ); + + if( pxHandle != NULL ) + { + pxHandle->uxMessagesWaiting = uxInitialCount; + + traceCREATE_COUNTING_SEMAPHORE(); + } + else + { + traceCREATE_COUNTING_SEMAPHORE_FAILED(); + } + + return pxHandle; + } + +#endif /* configUSE_COUNTING_SEMAPHORES */ +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) +{ +signed portBASE_TYPE xEntryTimeSet = pdFALSE; +xTimeOutType xTimeOut; + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND( pxQueue ); + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. Yes it is ok to do + this from within the critical section - the kernel + takes care of that. */ + portYIELD_WITHIN_API(); + } + } + + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting the + function. */ + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + /* The queue was full and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + + /* Return to the original privilege level before exiting + the function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was full and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + + /* Unlocking the queue means queue events can effect the + event list. It is possible that interrupts occurring now + remove this task from the event list again - but as the + scheduler is suspended the task will go onto the pending + ready last instead of the actual ready list. */ + prvUnlockQueue( pxQueue ); + + /* Resuming the scheduler will move tasks from the pending + ready list into the ready list - so it is feasible that this + task is already in a ready list before it yields - in which + case the yield will not cause a context switch unless there + is also a higher priority task in the pending ready list. */ + if( !xTaskResumeAll() ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + /* The timeout has expired. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + + /* Return to the original privilege level before exiting the + function. */ + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } +} +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + + signed portBASE_TYPE xQueueAltGenericSend( xQueueHandle pxQueue, const void * const pvItemToQueue, portTickType xTicksToWait, portBASE_TYPE xCopyPosition ) + { + signed portBASE_TYPE xEntryTimeSet = pdFALSE; + xTimeOutType xTimeOut; + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there room on the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND( pxQueue ); + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If there was a task waiting for data to arrive on the + queue then unblock it now. */ + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) == pdTRUE ) + { + /* The unblocked task has a priority higher than + our own so yield immediately. */ + portYIELD_WITHIN_API(); + } + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + taskEXIT_CRITICAL(); + return errQUEUE_FULL; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueFull( pxQueue ) ) + { + traceBLOCKING_ON_QUEUE_SEND( pxQueue ); + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToSend ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_SEND_FAILED( pxQueue ); + return errQUEUE_FULL; + } + } + taskEXIT_CRITICAL(); + } + } + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +#if configUSE_ALTERNATIVE_API == 1 + + signed portBASE_TYPE xQueueAltGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) + { + signed portBASE_TYPE xEntryTimeSet = pdFALSE; + xTimeOutType xTimeOut; + signed char *pcOriginalReadPosition; + + for( ;; ) + { + taskENTER_CRITICAL(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Remember our read position in case we are just peeking. */ + pcOriginalReadPosition = pxQueue->pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* We are actually removing data. */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle(); + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* We are not removing the data, so reset our read + pointer. */ + pxQueue->pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + portYIELD_WITHIN_API(); + } + } + + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + taskENTER_CRITICAL(); + { + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + portENTER_CRITICAL(); + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + portEXIT_CRITICAL(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + portYIELD_WITHIN_API(); + } + } + else + { + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } + taskEXIT_CRITICAL(); + } + } + + +#endif /* configUSE_ALTERNATIVE_API */ +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericSendFromISR( xQueueHandle pxQueue, const void * const pvItemToQueue, signed portBASE_TYPE *pxHigherPriorityTaskWoken, portBASE_TYPE xCopyPosition ) +{ +signed portBASE_TYPE xReturn; +unsigned portBASE_TYPE uxSavedInterruptStatus; + + /* Similar to xQueueGenericSend, except we don't block if there is no room + in the queue. Also we don't directly wake a task that was blocked on a + queue read, instead we return a flag to say whether a context switch is + required or not (i.e. has a task with a higher priority than us been woken + by this post). */ + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + traceQUEUE_SEND_FROM_ISR( pxQueue ); + + prvCopyDataToQueue( pxQueue, pvItemToQueue, xCopyPosition ); + + /* If the queue is locked we do not alter the event list. This will + be done when the queue is unlocked later. */ + if( pxQueue->xTxLock == queueUNLOCKED ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + *pxHigherPriorityTaskWoken = pdTRUE; + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was posted while it was locked. */ + ++( pxQueue->xTxLock ); + } + + xReturn = pdPASS; + } + else + { + traceQUEUE_SEND_FROM_ISR_FAILED( pxQueue ); + xReturn = errQUEUE_FULL; + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueGenericReceive( xQueueHandle pxQueue, void * const pvBuffer, portTickType xTicksToWait, portBASE_TYPE xJustPeeking ) +{ +signed portBASE_TYPE xEntryTimeSet = pdFALSE; +xTimeOutType xTimeOut; +signed char *pcOriginalReadPosition; + + /* This function relaxes the coding standard somewhat to allow return + statements within the function itself. This is done in the interest + of execution time efficiency. */ + + for( ;; ) + { + taskENTER_CRITICAL(); + { + /* Is there data in the queue now? To be running we must be + the highest priority task wanting to access the queue. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Remember our read position in case we are just peeking. */ + pcOriginalReadPosition = pxQueue->pcReadFrom; + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + + if( xJustPeeking == pdFALSE ) + { + traceQUEUE_RECEIVE( pxQueue ); + + /* We are actually removing data. */ + --( pxQueue->uxMessagesWaiting ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* Record the information required to implement + priority inheritance should it become necessary. */ + pxQueue->pxMutexHolder = xTaskGetCurrentTaskHandle(); + } + } + #endif + + if( listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) == pdFALSE ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + else + { + traceQUEUE_PEEK( pxQueue ); + + /* We are not removing the data, so reset our read + pointer. */ + pxQueue->pcReadFrom = pcOriginalReadPosition; + + /* The data is being left in the queue, so see if there are + any other tasks waiting for the data. */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than this task. */ + portYIELD_WITHIN_API(); + } + } + + } + + taskEXIT_CRITICAL(); + return pdPASS; + } + else + { + if( xTicksToWait == ( portTickType ) 0 ) + { + /* The queue was empty and no block time is specified (or + the block time has expired) so leave now. */ + taskEXIT_CRITICAL(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + else if( xEntryTimeSet == pdFALSE ) + { + /* The queue was empty and a block time was specified so + configure the timeout structure. */ + vTaskSetTimeOutState( &xTimeOut ); + xEntryTimeSet = pdTRUE; + } + } + } + taskEXIT_CRITICAL(); + + /* Interrupts and other tasks can send to and receive from the queue + now the critical section has been exited. */ + + vTaskSuspendAll(); + prvLockQueue( pxQueue ); + + /* Update the timeout state to see if it has expired yet. */ + if( xTaskCheckForTimeOut( &xTimeOut, &xTicksToWait ) == pdFALSE ) + { + if( prvIsQueueEmpty( pxQueue ) ) + { + traceBLOCKING_ON_QUEUE_RECEIVE( pxQueue ); + + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + portENTER_CRITICAL(); + { + vTaskPriorityInherit( ( void * ) pxQueue->pxMutexHolder ); + } + portEXIT_CRITICAL(); + } + } + #endif + + vTaskPlaceOnEventList( &( pxQueue->xTasksWaitingToReceive ), xTicksToWait ); + prvUnlockQueue( pxQueue ); + if( !xTaskResumeAll() ) + { + portYIELD_WITHIN_API(); + } + } + else + { + /* Try again. */ + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + } + } + else + { + prvUnlockQueue( pxQueue ); + ( void ) xTaskResumeAll(); + traceQUEUE_RECEIVE_FAILED( pxQueue ); + return errQUEUE_EMPTY; + } + } +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueReceiveFromISR( xQueueHandle pxQueue, void * const pvBuffer, signed portBASE_TYPE *pxTaskWoken ) +{ +signed portBASE_TYPE xReturn; +unsigned portBASE_TYPE uxSavedInterruptStatus; + + uxSavedInterruptStatus = portSET_INTERRUPT_MASK_FROM_ISR(); + { + /* We cannot block from an ISR, so check there is data available. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + traceQUEUE_RECEIVE_FROM_ISR( pxQueue ); + + prvCopyDataFromQueue( pxQueue, pvBuffer ); + --( pxQueue->uxMessagesWaiting ); + + /* If the queue is locked we will not modify the event list. Instead + we update the lock count so the task that unlocks the queue will know + that an ISR has removed data while the queue was locked. */ + if( pxQueue->xRxLock == queueUNLOCKED ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + /* The task waiting has a higher priority than us so + force a context switch. */ + *pxTaskWoken = pdTRUE; + } + } + } + else + { + /* Increment the lock count so the task that unlocks the queue + knows that data was removed while it was locked. */ + ++( pxQueue->xRxLock ); + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + traceQUEUE_RECEIVE_FROM_ISR_FAILED( pxQueue ); + } + } + portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxQueueMessagesWaiting( const xQueueHandle pxQueue ) +{ +unsigned portBASE_TYPE uxReturn; + + taskENTER_CRITICAL(); + uxReturn = pxQueue->uxMessagesWaiting; + taskEXIT_CRITICAL(); + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxQueueMessagesWaitingFromISR( const xQueueHandle pxQueue ) +{ +unsigned portBASE_TYPE uxReturn; + + uxReturn = pxQueue->uxMessagesWaiting; + + return uxReturn; +} +/*-----------------------------------------------------------*/ + +void vQueueDelete( xQueueHandle pxQueue ) +{ + traceQUEUE_DELETE( pxQueue ); + vQueueUnregisterQueue( pxQueue ); + vPortFree( pxQueue->pcHead ); + vPortFree( pxQueue ); +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataToQueue( xQUEUE *pxQueue, const void *pvItemToQueue, portBASE_TYPE xPosition ) +{ + if( pxQueue->uxItemSize == ( unsigned portBASE_TYPE ) 0 ) + { + #if ( configUSE_MUTEXES == 1 ) + { + if( pxQueue->uxQueueType == queueQUEUE_IS_MUTEX ) + { + /* The mutex is no longer being held. */ + vTaskPriorityDisinherit( ( void * ) pxQueue->pxMutexHolder ); + pxQueue->pxMutexHolder = NULL; + } + } + #endif + } + else if( xPosition == queueSEND_TO_BACK ) + { + memcpy( ( void * ) pxQueue->pcWriteTo, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); + pxQueue->pcWriteTo += pxQueue->uxItemSize; + if( pxQueue->pcWriteTo >= pxQueue->pcTail ) + { + pxQueue->pcWriteTo = pxQueue->pcHead; + } + } + else + { + memcpy( ( void * ) pxQueue->pcReadFrom, pvItemToQueue, ( unsigned ) pxQueue->uxItemSize ); + pxQueue->pcReadFrom -= pxQueue->uxItemSize; + if( pxQueue->pcReadFrom < pxQueue->pcHead ) + { + pxQueue->pcReadFrom = ( pxQueue->pcTail - pxQueue->uxItemSize ); + } + } + + ++( pxQueue->uxMessagesWaiting ); +} +/*-----------------------------------------------------------*/ + +static void prvCopyDataFromQueue( xQUEUE * const pxQueue, const void *pvBuffer ) +{ + if( pxQueue->uxQueueType != queueQUEUE_IS_MUTEX ) + { + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + } +} +/*-----------------------------------------------------------*/ + +static void prvUnlockQueue( xQueueHandle pxQueue ) +{ + /* THIS FUNCTION MUST BE CALLED WITH THE SCHEDULER SUSPENDED. */ + + /* The lock counts contains the number of extra data items placed or + removed from the queue while the queue was locked. When a queue is + locked items can be added or removed, but the event lists cannot be + updated. */ + taskENTER_CRITICAL(); + { + /* See if data was added to the queue while it was locked. */ + while( pxQueue->xTxLock > queueLOCKED_UNMODIFIED ) + { + /* Data was posted while the queue was locked. Are any tasks + blocked waiting for data to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* Tasks that are removed from the event list will get added to + the pending ready list as the scheduler is still suspended. */ + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The task waiting has a higher priority so record that a + context switch is required. */ + vTaskMissedYield(); + } + + --( pxQueue->xTxLock ); + } + else + { + break; + } + } + + pxQueue->xTxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); + + /* Do the same for the Rx lock. */ + taskENTER_CRITICAL(); + { + while( pxQueue->xRxLock > queueLOCKED_UNMODIFIED ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xTaskRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + vTaskMissedYield(); + } + + --( pxQueue->xRxLock ); + } + else + { + break; + } + } + + pxQueue->xRxLock = queueUNLOCKED; + } + taskEXIT_CRITICAL(); +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueEmpty( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueIsQueueEmptyFromISR( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + xReturn = ( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +static signed portBASE_TYPE prvIsQueueFull( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + taskENTER_CRITICAL(); + xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength ); + taskEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xQueueIsQueueFullFromISR( const xQueueHandle pxQueue ) +{ +signed portBASE_TYPE xReturn; + + xReturn = ( pxQueue->uxMessagesWaiting == pxQueue->uxLength ); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSend( xQueueHandle pxQueue, const void *pvItemToQueue, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already full we may have to block. A critical section + is required to prevent an interrupt removing something from the queue + between the check to see if the queue is full and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( prvIsQueueFull( pxQueue ) ) + { + /* The queue is full - do we want to block or just leave without + posting? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is called from a coroutine we cannot block directly, but + return indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToSend ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + /* There is room in the queue, copy the data into the queue. */ + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + xReturn = pdPASS; + + /* Were any co-routines waiting for data to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + /* The co-routine waiting has a higher priority so record + that a yield might be appropriate. */ + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = errQUEUE_FULL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceive( xQueueHandle pxQueue, void *pvBuffer, portTickType xTicksToWait ) +{ +signed portBASE_TYPE xReturn; + + /* If the queue is already empty we may have to block. A critical section + is required to prevent an interrupt adding something to the queue + between the check to see if the queue is empty and blocking on the queue. */ + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting == ( unsigned portBASE_TYPE ) 0 ) + { + /* There are no messages in the queue, do we want to block or just + leave with nothing? */ + if( xTicksToWait > ( portTickType ) 0 ) + { + /* As this is a co-routine we cannot block directly, but return + indicating that we need to block. */ + vCoRoutineAddToDelayedList( xTicksToWait, &( pxQueue->xTasksWaitingToReceive ) ); + portENABLE_INTERRUPTS(); + return errQUEUE_BLOCKED; + } + else + { + portENABLE_INTERRUPTS(); + return errQUEUE_FULL; + } + } + } + portENABLE_INTERRUPTS(); + + portNOP(); + + portDISABLE_INTERRUPTS(); + { + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Data is available from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + xReturn = pdPASS; + + /* Were any co-routines waiting for space to become available? */ + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + /* In this instance the co-routine could be placed directly + into the ready list as we are within a critical section. + Instead the same pending ready list mechanism is used as if + the event were caused from within an interrupt. */ + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + xReturn = errQUEUE_YIELD; + } + } + } + else + { + xReturn = pdFAIL; + } + } + portENABLE_INTERRUPTS(); + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + + + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRSendFromISR( xQueueHandle pxQueue, const void *pvItemToQueue, signed portBASE_TYPE xCoRoutinePreviouslyWoken ) +{ + /* Cannot block within an ISR so if there is no space on the queue then + exit without doing anything. */ + if( pxQueue->uxMessagesWaiting < pxQueue->uxLength ) + { + prvCopyDataToQueue( pxQueue, pvItemToQueue, queueSEND_TO_BACK ); + + /* We only want to wake one co-routine per ISR, so check that a + co-routine has not already been woken. */ + if( !xCoRoutinePreviouslyWoken ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToReceive ) ) ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToReceive ) ) != pdFALSE ) + { + return pdTRUE; + } + } + } + } + + return xCoRoutinePreviouslyWoken; +} +#endif +/*-----------------------------------------------------------*/ + +#if configUSE_CO_ROUTINES == 1 +signed portBASE_TYPE xQueueCRReceiveFromISR( xQueueHandle pxQueue, void *pvBuffer, signed portBASE_TYPE *pxCoRoutineWoken ) +{ +signed portBASE_TYPE xReturn; + + /* We cannot block from an ISR, so check there is data available. If + not then just leave without doing anything. */ + if( pxQueue->uxMessagesWaiting > ( unsigned portBASE_TYPE ) 0 ) + { + /* Copy the data from the queue. */ + pxQueue->pcReadFrom += pxQueue->uxItemSize; + if( pxQueue->pcReadFrom >= pxQueue->pcTail ) + { + pxQueue->pcReadFrom = pxQueue->pcHead; + } + --( pxQueue->uxMessagesWaiting ); + memcpy( ( void * ) pvBuffer, ( void * ) pxQueue->pcReadFrom, ( unsigned ) pxQueue->uxItemSize ); + + if( !( *pxCoRoutineWoken ) ) + { + if( !listLIST_IS_EMPTY( &( pxQueue->xTasksWaitingToSend ) ) ) + { + if( xCoRoutineRemoveFromEventList( &( pxQueue->xTasksWaitingToSend ) ) != pdFALSE ) + { + *pxCoRoutineWoken = pdTRUE; + } + } + } + + xReturn = pdPASS; + } + else + { + xReturn = pdFAIL; + } + + return xReturn; +} +#endif +/*-----------------------------------------------------------*/ + +#if configQUEUE_REGISTRY_SIZE > 0 + + void vQueueAddToRegistry( xQueueHandle xQueue, signed char *pcQueueName ) + { + unsigned portBASE_TYPE ux; + + /* See if there is an empty space in the registry. A NULL name denotes + a free slot. */ + for( ux = 0; ux < configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].pcQueueName == NULL ) + { + /* Store the information on this queue. */ + xQueueRegistry[ ux ].pcQueueName = pcQueueName; + xQueueRegistry[ ux ].xHandle = xQueue; + break; + } + } + } + +#endif + /*-----------------------------------------------------------*/ + +#if configQUEUE_REGISTRY_SIZE > 0 + + static void vQueueUnregisterQueue( xQueueHandle xQueue ) + { + unsigned portBASE_TYPE ux; + + /* See if the handle of the queue being unregistered in actually in the + registry. */ + for( ux = 0; ux < configQUEUE_REGISTRY_SIZE; ux++ ) + { + if( xQueueRegistry[ ux ].xHandle == xQueue ) + { + /* Set the name to NULL to show that this slot if free again. */ + xQueueRegistry[ ux ].pcQueueName = NULL; + break; + } + } + + } + +#endif + diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/readme.txt b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/readme.txt new file mode 100644 index 000000000..81518ecb4 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/readme.txt @@ -0,0 +1,17 @@ +Each real time kernel port consists of three files that contain the core kernel +components and are common to every port, and one or more files that are +specific to a particular microcontroller and or compiler. + ++ The FreeRTOS/Source directory contains the three files that are common to +every port - list.c, queue.c and tasks.c. The kernel is contained within these +three files. croutine.c implements the optional co-routine functionality - which +is normally only used on very memory limited systems. + ++ The FreeRTOS/Source/Portable directory contains the files that are specific to +a particular microcontroller and or compiler. + ++ The FreeRTOS/Source/include directory contains the real time kernel header +files. + +See the readme file in the FreeRTOS/Source/Portable directory for more +information. \ No newline at end of file diff --git a/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/tasks.c b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/tasks.c new file mode 100644 index 000000000..bdc1088f9 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/FreeRTOS/Source/tasks.c @@ -0,0 +1,2315 @@ +/* + FreeRTOS V6.0.4 - Copyright (C) 2010 Real Time Engineers Ltd. + + *************************************************************************** + * * + * If you are: * + * * + * + New to FreeRTOS, * + * + Wanting to learn FreeRTOS or multitasking in general quickly * + * + Looking for basic training, * + * + Wanting to improve your FreeRTOS skills and productivity * + * * + * then take a look at the FreeRTOS eBook * + * * + * "Using the FreeRTOS Real Time Kernel - a Practical Guide" * + * http://www.FreeRTOS.org/Documentation * + * * + * A pdf reference manual is also available. Both are usually delivered * + * to your inbox within 20 minutes to two hours when purchased between 8am * + * and 8pm GMT (although please allow up to 24 hours in case of * + * exceptional circumstances). Thank you for your support! * + * * + *************************************************************************** + + This file is part of the FreeRTOS distribution. + + FreeRTOS is free software; you can redistribute it and/or modify it under + the terms of the GNU General Public License (version 2) as published by the + Free Software Foundation AND MODIFIED BY the FreeRTOS exception. + ***NOTE*** The exception to the GPL is included to allow you to distribute + a combined work that includes FreeRTOS without being obliged to provide the + source code for proprietary components outside of the FreeRTOS kernel. + FreeRTOS is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + more details. You should have received a copy of the GNU General Public + License and the FreeRTOS license exception along with FreeRTOS; if not it + can be viewed here: http://www.freertos.org/a00114.html and also obtained + by writing to Richard Barry, contact details for whom are available on the + FreeRTOS WEB site. + + 1 tab == 4 spaces! + + http://www.FreeRTOS.org - Documentation, latest information, license and + contact details. + + http://www.SafeRTOS.com - A version that is certified for use in safety + critical systems. + + http://www.OpenRTOS.com - Commercial support, development, porting, + licensing and training services. +*/ + + +#include +#include +#include + +/* Defining MPU_WRAPPERS_INCLUDED_FROM_API_FILE prevents task.h from redefining +all the API functions to use the MPU wrappers. That should only be done when +task.h is included from an application file. */ +#define MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +#include "FreeRTOS.h" +#include "task.h" +#include "StackMacros.h" + +#undef MPU_WRAPPERS_INCLUDED_FROM_API_FILE + +/* + * Macro to define the amount of stack available to the idle task. + */ +#define tskIDLE_STACK_SIZE configMINIMAL_STACK_SIZE + +/* + * Task control block. A task control block (TCB) is allocated to each task, + * and stores the context of the task. + */ +typedef struct tskTaskControlBlock +{ + volatile portSTACK_TYPE *pxTopOfStack; /*< Points to the location of the last item placed on the tasks stack. THIS MUST BE THE FIRST MEMBER OF THE STRUCT. */ + + #if ( portUSING_MPU_WRAPPERS == 1 ) + xMPU_SETTINGS xMPUSettings; /*< The MPU settings are defined as part of the port layer. THIS MUST BE THE SECOND MEMBER OF THE STRUCT. */ + #endif + + xListItem xGenericListItem; /*< List item used to place the TCB in ready and blocked queues. */ + xListItem xEventListItem; /*< List item used to place the TCB in event lists. */ + unsigned portBASE_TYPE uxPriority; /*< The priority of the task where 0 is the lowest priority. */ + portSTACK_TYPE *pxStack; /*< Points to the start of the stack. */ + signed char pcTaskName[ configMAX_TASK_NAME_LEN ];/*< Descriptive name given to the task when created. Facilitates debugging only. */ + + #if ( portSTACK_GROWTH > 0 ) + portSTACK_TYPE *pxEndOfStack; /*< Used for stack overflow checking on architectures where the stack grows up from low memory. */ + #endif + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + unsigned portBASE_TYPE uxCriticalNesting; + #endif + + #if ( configUSE_TRACE_FACILITY == 1 ) + unsigned portBASE_TYPE uxTCBNumber; /*< This is used for tracing the scheduler and making debugging easier only. */ + #endif + + #if ( configUSE_MUTEXES == 1 ) + unsigned portBASE_TYPE uxBasePriority; /*< The priority last assigned to the task - used by the priority inheritance mechanism. */ + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + pdTASK_HOOK_CODE pxTaskTag; + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + unsigned long ulRunTimeCounter; /*< Used for calculating how much CPU time each task is utilising. */ + #endif + +} tskTCB; + + +/* + * Some kernel aware debuggers require data to be viewed to be global, rather + * than file scope. + */ +#ifdef portREMOVE_STATIC_QUALIFIER + #define static +#endif + +/*lint -e956 */ +PRIVILEGED_DATA tskTCB * volatile pxCurrentTCB = NULL; + +/* Lists for ready and blocked tasks. --------------------*/ + +PRIVILEGED_DATA static xList pxReadyTasksLists[ configMAX_PRIORITIES ]; /*< Prioritised ready tasks. */ +PRIVILEGED_DATA static xList xDelayedTaskList1; /*< Delayed tasks. */ +PRIVILEGED_DATA static xList xDelayedTaskList2; /*< Delayed tasks (two lists are used - one for delays that have overflowed the current tick count. */ +PRIVILEGED_DATA static xList * volatile pxDelayedTaskList ; /*< Points to the delayed task list currently being used. */ +PRIVILEGED_DATA static xList * volatile pxOverflowDelayedTaskList; /*< Points to the delayed task list currently being used to hold tasks that have overflowed the current tick count. */ +PRIVILEGED_DATA static xList xPendingReadyList; /*< Tasks that have been readied while the scheduler was suspended. They will be moved to the ready queue when the scheduler is resumed. */ + +#if ( INCLUDE_vTaskDelete == 1 ) + + PRIVILEGED_DATA static volatile xList xTasksWaitingTermination; /*< Tasks that have been deleted - but the their memory not yet freed. */ + PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxTasksDeleted = ( unsigned portBASE_TYPE ) 0; + +#endif + +#if ( INCLUDE_vTaskSuspend == 1 ) + + PRIVILEGED_DATA static xList xSuspendedTaskList; /*< Tasks that are currently suspended. */ + +#endif + +/* File private variables. --------------------------------*/ +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxCurrentNumberOfTasks = ( unsigned portBASE_TYPE ) 0; +PRIVILEGED_DATA static volatile portTickType xTickCount = ( portTickType ) 0; +PRIVILEGED_DATA static unsigned portBASE_TYPE uxTopUsedPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxTopReadyPriority = tskIDLE_PRIORITY; +PRIVILEGED_DATA static volatile signed portBASE_TYPE xSchedulerRunning = pdFALSE; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxSchedulerSuspended = ( unsigned portBASE_TYPE ) pdFALSE; +PRIVILEGED_DATA static volatile unsigned portBASE_TYPE uxMissedTicks = ( unsigned portBASE_TYPE ) 0; +PRIVILEGED_DATA static volatile portBASE_TYPE xMissedYield = ( portBASE_TYPE ) pdFALSE; +PRIVILEGED_DATA static volatile portBASE_TYPE xNumOfOverflows = ( portBASE_TYPE ) 0; +PRIVILEGED_DATA static unsigned portBASE_TYPE uxTaskNumber = ( unsigned portBASE_TYPE ) 0; + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + PRIVILEGED_DATA static char pcStatsString[ 50 ] ; + PRIVILEGED_DATA static unsigned long ulTaskSwitchedInTime = 0UL; /*< Holds the value of a timer/counter the last time a task was switched in. */ + static void prvGenerateRunTimeStatsForTasksInList( const signed char *pcWriteBuffer, xList *pxList, unsigned long ulTotalRunTime ) PRIVILEGED_FUNCTION; + +#endif + +/* Debugging and trace facilities private variables and macros. ------------*/ + +/* + * The value used to fill the stack of a task when the task is created. This + * is used purely for checking the high water mark for tasks. + */ +#define tskSTACK_FILL_BYTE ( 0xa5 ) + +/* + * Macros used by vListTask to indicate which state a task is in. + */ +#define tskBLOCKED_CHAR ( ( signed char ) 'B' ) +#define tskREADY_CHAR ( ( signed char ) 'R' ) +#define tskDELETED_CHAR ( ( signed char ) 'D' ) +#define tskSUSPENDED_CHAR ( ( signed char ) 'S' ) + +/* + * Macros and private variables used by the trace facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define tskSIZE_OF_EACH_TRACE_LINE ( ( unsigned long ) ( sizeof( unsigned long ) + sizeof( unsigned long ) ) ) + PRIVILEGED_DATA static volatile signed char * volatile pcTraceBuffer; + PRIVILEGED_DATA static signed char *pcTraceBufferStart; + PRIVILEGED_DATA static signed char *pcTraceBufferEnd; + PRIVILEGED_DATA static signed portBASE_TYPE xTracing = pdFALSE; + static unsigned portBASE_TYPE uxPreviousTask = 255; + PRIVILEGED_DATA static char pcStatusString[ 50 ]; + +#endif + +/*-----------------------------------------------------------*/ + +/* + * Macro that writes a trace of scheduler activity to a buffer. This trace + * shows which task is running when and is very useful as a debugging tool. + * As this macro is called each context switch it is a good idea to undefine + * it if not using the facility. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + #define vWriteTraceToBuffer() \ + { \ + if( xTracing ) \ + { \ + if( uxPreviousTask != pxCurrentTCB->uxTCBNumber ) \ + { \ + if( ( pcTraceBuffer + tskSIZE_OF_EACH_TRACE_LINE ) < pcTraceBufferEnd ) \ + { \ + uxPreviousTask = pxCurrentTCB->uxTCBNumber; \ + *( unsigned long * ) pcTraceBuffer = ( unsigned long ) xTickCount; \ + pcTraceBuffer += sizeof( unsigned long ); \ + *( unsigned long * ) pcTraceBuffer = ( unsigned long ) uxPreviousTask; \ + pcTraceBuffer += sizeof( unsigned long ); \ + } \ + else \ + { \ + xTracing = pdFALSE; \ + } \ + } \ + } \ + } + +#else + + #define vWriteTraceToBuffer() + +#endif +/*-----------------------------------------------------------*/ + +/* + * Place the task represented by pxTCB into the appropriate ready queue for + * the task. It is inserted at the end of the list. One quirk of this is + * that if the task being inserted is at the same priority as the currently + * executing task, then it will only be rescheduled after the currently + * executing task has been rescheduled. + */ +#define prvAddTaskToReadyQueue( pxTCB ) \ +{ \ + if( pxTCB->uxPriority > uxTopReadyPriority ) \ + { \ + uxTopReadyPriority = pxTCB->uxPriority; \ + } \ + vListInsertEnd( ( xList * ) &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ); \ +} +/*-----------------------------------------------------------*/ + +/* + * Macro that looks at the list of tasks that are currently delayed to see if + * any require waking. + * + * Tasks are stored in the queue in the order of their wake time - meaning + * once one tasks has been found whose timer has not expired we need not look + * any further down the list. + */ +#define prvCheckDelayedTasks() \ +{ \ +register tskTCB *pxTCB; \ + \ + while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxDelayedTaskList ) ) != NULL ) \ + { \ + if( xTickCount < listGET_LIST_ITEM_VALUE( &( pxTCB->xGenericListItem ) ) ) \ + { \ + break; \ + } \ + vListRemove( &( pxTCB->xGenericListItem ) ); \ + /* Is the task waiting on an event also? */ \ + if( pxTCB->xEventListItem.pvContainer ) \ + { \ + vListRemove( &( pxTCB->xEventListItem ) ); \ + } \ + prvAddTaskToReadyQueue( pxTCB ); \ + } \ +} +/*-----------------------------------------------------------*/ + +/* + * Several functions take an xTaskHandle parameter that can optionally be NULL, + * where NULL is used to indicate that the handle of the currently executing + * task should be used in place of the parameter. This macro simply checks to + * see if the parameter is NULL and returns a pointer to the appropriate TCB. + */ +#define prvGetTCBFromHandle( pxHandle ) ( ( pxHandle == NULL ) ? ( tskTCB * ) pxCurrentTCB : ( tskTCB * ) pxHandle ) + + +/* File private functions. --------------------------------*/ + +/* + * Utility to ready a TCB for a given task. Mainly just copies the parameters + * into the TCB structure. + */ +static void prvInitialiseTCBVariables( tskTCB *pxTCB, const signed char * const pcName, unsigned portBASE_TYPE uxPriority, const xMemoryRegion * const xRegions, unsigned short usStackDepth ) PRIVILEGED_FUNCTION; + +/* + * Utility to ready all the lists used by the scheduler. This is called + * automatically upon the creation of the first task. + */ +static void prvInitialiseTaskLists( void ) PRIVILEGED_FUNCTION; + +/* + * The idle task, which as all tasks is implemented as a never ending loop. + * The idle task is automatically created and added to the ready lists upon + * creation of the first user task. + * + * The portTASK_FUNCTION_PROTO() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION_PROTO( prvIdleTask, pvParameters ); + +/* + * Utility to free all memory allocated by the scheduler to hold a TCB, + * including the stack pointed to by the TCB. + * + * This does not free memory allocated by the task itself (i.e. memory + * allocated by calls to pvPortMalloc from within the tasks application code). + */ +#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) ) + + static void prvDeleteTCB( tskTCB *pxTCB ) PRIVILEGED_FUNCTION; + +#endif + +/* + * Used only by the idle task. This checks to see if anything has been placed + * in the list of tasks waiting to be deleted. If so the task is cleaned up + * and its TCB deleted. + */ +static void prvCheckTasksWaitingTermination( void ) PRIVILEGED_FUNCTION; + +/* + * Allocates memory from the heap for a TCB and associated stack. Checks the + * allocation was successful. + */ +static tskTCB *prvAllocateTCBAndStack( unsigned short usStackDepth, portSTACK_TYPE *puxStackBuffer ) PRIVILEGED_FUNCTION; + +/* + * Called from vTaskList. vListTasks details all the tasks currently under + * control of the scheduler. The tasks may be in one of a number of lists. + * prvListTaskWithinSingleList accepts a list and details the tasks from + * within just that list. + * + * THIS FUNCTION IS INTENDED FOR DEBUGGING ONLY, AND SHOULD NOT BE CALLED FROM + * NORMAL APPLICATION CODE. + */ +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( const signed char *pcWriteBuffer, xList *pxList, signed char cStatus ) PRIVILEGED_FUNCTION; + +#endif + +/* + * When a task is created, the stack of the task is filled with a known value. + * This function determines the 'high water mark' of the task stack by + * determining how much of the stack remains at the original preset value. + */ +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static unsigned short usTaskCheckFreeStackSpace( const unsigned char * pucStackByte ) PRIVILEGED_FUNCTION; + +#endif + + +/*lint +e956 */ + + + +/*----------------------------------------------------------- + * TASK CREATION API documented in task.h + *----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskGenericCreate( pdTASK_CODE pxTaskCode, const signed char * const pcName, unsigned short usStackDepth, void *pvParameters, unsigned portBASE_TYPE uxPriority, xTaskHandle *pxCreatedTask, portSTACK_TYPE *puxStackBuffer, const xMemoryRegion * const xRegions ) +{ +signed portBASE_TYPE xReturn; +tskTCB * pxNewTCB; + + /* Allocate the memory required by the TCB and stack for the new task, + checking that the allocation was successful. */ + pxNewTCB = prvAllocateTCBAndStack( usStackDepth, puxStackBuffer ); + + if( pxNewTCB != NULL ) + { + portSTACK_TYPE *pxTopOfStack; + + #if( portUSING_MPU_WRAPPERS == 1 ) + /* Should the task be created in privileged mode? */ + portBASE_TYPE xRunPrivileged; + if( ( uxPriority & portPRIVILEGE_BIT ) != 0x00 ) + { + xRunPrivileged = pdTRUE; + } + else + { + xRunPrivileged = pdFALSE; + } + uxPriority &= ~portPRIVILEGE_BIT; + #endif /* portUSING_MPU_WRAPPERS == 1 */ + + /* Calculate the top of stack address. This depends on whether the + stack grows from high memory to low (as per the 80x86) or visa versa. + portSTACK_GROWTH is used to make the result positive or negative as + required by the port. */ + #if( portSTACK_GROWTH < 0 ) + { + pxTopOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + pxTopOfStack = ( portSTACK_TYPE * ) ( ( ( unsigned long ) pxTopOfStack ) & ( ( unsigned long ) ~portBYTE_ALIGNMENT_MASK ) ); + } + #else + { + pxTopOfStack = pxNewTCB->pxStack; + + /* If we want to use stack checking on architectures that use + a positive stack growth direction then we also need to store the + other extreme of the stack space. */ + pxNewTCB->pxEndOfStack = pxNewTCB->pxStack + ( usStackDepth - 1 ); + } + #endif + + /* Setup the newly allocated TCB with the initial state of the task. */ + prvInitialiseTCBVariables( pxNewTCB, pcName, uxPriority, xRegions, usStackDepth ); + + /* Initialize the TCB stack to look as if the task was already running, + but had been interrupted by the scheduler. The return address is set + to the start of the task function. Once the stack has been initialised + the top of stack variable is updated. */ + #if( portUSING_MPU_WRAPPERS == 1 ) + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters, xRunPrivileged ); + } + #else + { + pxNewTCB->pxTopOfStack = pxPortInitialiseStack( pxTopOfStack, pxTaskCode, pvParameters ); + } + #endif + + /* We are going to manipulate the task queues to add this task to a + ready list, so must make sure no interrupts occur. */ + portENTER_CRITICAL(); + { + uxCurrentNumberOfTasks++; + if( uxCurrentNumberOfTasks == ( unsigned portBASE_TYPE ) 1 ) + { + /* As this is the first task it must also be the current task. */ + pxCurrentTCB = pxNewTCB; + + /* This is the first task to be created so do the preliminary + initialisation required. We will not recover if this call + fails, but we will report the failure. */ + prvInitialiseTaskLists(); + } + else + { + /* If the scheduler is not already running, make this task the + current task if it is the highest priority task to be created + so far. */ + if( xSchedulerRunning == pdFALSE ) + { + if( pxCurrentTCB->uxPriority <= uxPriority ) + { + pxCurrentTCB = pxNewTCB; + } + } + } + + /* Remember the top priority to make context switching faster. Use + the priority in pxNewTCB as this has been capped to a valid value. */ + if( pxNewTCB->uxPriority > uxTopUsedPriority ) + { + uxTopUsedPriority = pxNewTCB->uxPriority; + } + + #if ( configUSE_TRACE_FACILITY == 1 ) + { + /* Add a counter into the TCB for tracing only. */ + pxNewTCB->uxTCBNumber = uxTaskNumber; + } + #endif + uxTaskNumber++; + + prvAddTaskToReadyQueue( pxNewTCB ); + + xReturn = pdPASS; + traceTASK_CREATE( pxNewTCB ); + } + portEXIT_CRITICAL(); + } + else + { + xReturn = errCOULD_NOT_ALLOCATE_REQUIRED_MEMORY; + traceTASK_CREATE_FAILED( pxNewTCB ); + } + + if( xReturn == pdPASS ) + { + if( ( void * ) pxCreatedTask != NULL ) + { + /* Pass the TCB out - in an anonymous way. The calling function/ + task can use this as a handle to delete the task later if + required.*/ + *pxCreatedTask = ( xTaskHandle ) pxNewTCB; + } + + if( xSchedulerRunning != pdFALSE ) + { + /* If the created task is of a higher priority than the current task + then it should run now. */ + if( pxCurrentTCB->uxPriority < uxPriority ) + { + portYIELD_WITHIN_API(); + } + } + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelete == 1 ) + + void vTaskDelete( xTaskHandle pxTaskToDelete ) + { + tskTCB *pxTCB; + + portENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + deleted. */ + if( pxTaskToDelete == pxCurrentTCB ) + { + pxTaskToDelete = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToDelete ); + + /* Remove task from the ready list and place in the termination list. + This will stop the task from be scheduled. The idle task will check + the termination list and free up any memory allocated by the + scheduler for the TCB and stack. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xTasksWaitingTermination, &( pxTCB->xGenericListItem ) ); + + /* Increment the ucTasksDeleted variable so the idle task knows + there is a task that has been deleted and that it should therefore + check the xTasksWaitingTermination list. */ + ++uxTasksDeleted; + + /* Increment the uxTaskNumberVariable also so kernel aware debuggers + can detect that the task lists need re-generating. */ + uxTaskNumber++; + + traceTASK_DELETE( pxTCB ); + } + portEXIT_CRITICAL(); + + /* Force a reschedule if we have just deleted the current task. */ + if( xSchedulerRunning != pdFALSE ) + { + if( ( void * ) pxTaskToDelete == NULL ) + { + portYIELD_WITHIN_API(); + } + } + } + +#endif + + + + + + +/*----------------------------------------------------------- + * TASK CONTROL API documented in task.h + *----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelayUntil == 1 ) + + void vTaskDelayUntil( portTickType * const pxPreviousWakeTime, portTickType xTimeIncrement ) + { + portTickType xTimeToWake; + portBASE_TYPE xAlreadyYielded, xShouldDelay = pdFALSE; + + vTaskSuspendAll(); + { + /* Generate the tick time at which the task wants to wake. */ + xTimeToWake = *pxPreviousWakeTime + xTimeIncrement; + + if( xTickCount < *pxPreviousWakeTime ) + { + /* The tick count has overflowed since this function was + lasted called. In this case the only time we should ever + actually delay is if the wake time has also overflowed, + and the wake time is greater than the tick time. When this + is the case it is as if neither time had overflowed. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) && ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + else + { + /* The tick time has not overflowed. In this case we will + delay if either the wake time has overflowed, and/or the + tick time is less than the wake time. */ + if( ( xTimeToWake < *pxPreviousWakeTime ) || ( xTimeToWake > xTickCount ) ) + { + xShouldDelay = pdTRUE; + } + } + + /* Update the wake time ready for the next call. */ + *pxPreviousWakeTime = xTimeToWake; + + if( xShouldDelay ) + { + traceTASK_DELAY_UNTIL(); + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + } + xAlreadyYielded = xTaskResumeAll(); + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( !xAlreadyYielded ) + { + portYIELD_WITHIN_API(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskDelay == 1 ) + + void vTaskDelay( portTickType xTicksToDelay ) + { + portTickType xTimeToWake; + signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* A delay time of zero just forces a reschedule. */ + if( xTicksToDelay > ( portTickType ) 0 ) + { + vTaskSuspendAll(); + { + traceTASK_DELAY(); + + /* A task that is removed from the event list while the + scheduler is suspended will not get placed in the ready + list or removed from the blocked list until the scheduler + is resumed. + + This task cannot be in an event list as it is the currently + executing task. */ + + /* Calculate the time to wake - this may overflow but this is + not a problem. */ + xTimeToWake = xTickCount + xTicksToDelay; + + /* We must remove ourselves from the ready list before adding + ourselves to the blocked list as the same list item is used for + both lists. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + /* The list item will be inserted in wake time order. */ + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the + overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the + current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + xAlreadyYielded = xTaskResumeAll(); + } + + /* Force a reschedule if xTaskResumeAll has not already done so, we may + have put ourselves to sleep. */ + if( !xAlreadyYielded ) + { + portYIELD_WITHIN_API(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskPriorityGet == 1 ) + + unsigned portBASE_TYPE uxTaskPriorityGet( xTaskHandle pxTask ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxReturn; + + portENTER_CRITICAL(); + { + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + uxReturn = pxTCB->uxPriority; + } + portEXIT_CRITICAL(); + + return uxReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskPrioritySet == 1 ) + + void vTaskPrioritySet( xTaskHandle pxTask, unsigned portBASE_TYPE uxNewPriority ) + { + tskTCB *pxTCB; + unsigned portBASE_TYPE uxCurrentPriority, xYieldRequired = pdFALSE; + + /* Ensure the new priority is valid. */ + if( uxNewPriority >= configMAX_PRIORITIES ) + { + uxNewPriority = configMAX_PRIORITIES - 1; + } + + portENTER_CRITICAL(); + { + if( pxTask == pxCurrentTCB ) + { + pxTask = NULL; + } + + /* If null is passed in here then we are changing the + priority of the calling function. */ + pxTCB = prvGetTCBFromHandle( pxTask ); + + traceTASK_PRIORITY_SET( pxTask, uxNewPriority ); + + #if ( configUSE_MUTEXES == 1 ) + { + uxCurrentPriority = pxTCB->uxBasePriority; + } + #else + { + uxCurrentPriority = pxTCB->uxPriority; + } + #endif + + if( uxCurrentPriority != uxNewPriority ) + { + /* The priority change may have readied a task of higher + priority than the calling task. */ + if( uxNewPriority > uxCurrentPriority ) + { + if( pxTask != NULL ) + { + /* The priority of another task is being raised. If we + were raising the priority of the currently running task + there would be no need to switch as it must have already + been the highest priority task. */ + xYieldRequired = pdTRUE; + } + } + else if( pxTask == NULL ) + { + /* Setting our own priority down means there may now be another + task of higher priority that is ready to execute. */ + xYieldRequired = pdTRUE; + } + + + + #if ( configUSE_MUTEXES == 1 ) + { + /* Only change the priority being used if the task is not + currently using an inherited priority. */ + if( pxTCB->uxBasePriority == pxTCB->uxPriority ) + { + pxTCB->uxPriority = uxNewPriority; + } + + /* The base priority gets set whatever. */ + pxTCB->uxBasePriority = uxNewPriority; + } + #else + { + pxTCB->uxPriority = uxNewPriority; + } + #endif + + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), ( configMAX_PRIORITIES - ( portTickType ) uxNewPriority ) ); + + /* If the task is in the blocked or suspended list we need do + nothing more than change it's priority variable. However, if + the task is in a ready list it needs to be removed and placed + in the queue appropriate to its new priority. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ uxCurrentPriority ] ), &( pxTCB->xGenericListItem ) ) ) + { + /* The task is currently in its ready list - remove before adding + it to it's new ready list. As we are in a critical section we + can do this even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + + if( xYieldRequired == pdTRUE ) + { + portYIELD_WITHIN_API(); + } + } + } + portEXIT_CRITICAL(); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskSuspend( xTaskHandle pxTaskToSuspend ) + { + tskTCB *pxTCB; + + portENTER_CRITICAL(); + { + /* Ensure a yield is performed if the current task is being + suspended. */ + if( pxTaskToSuspend == pxCurrentTCB ) + { + pxTaskToSuspend = NULL; + } + + /* If null is passed in here then we are suspending ourselves. */ + pxTCB = prvGetTCBFromHandle( pxTaskToSuspend ); + + traceTASK_SUSPEND( pxTCB ); + + /* Remove task from the ready/delayed list and place in the suspended list. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Is the task waiting on an event also? */ + if( pxTCB->xEventListItem.pvContainer ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + } + + vListInsertEnd( ( xList * ) &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ); + } + portEXIT_CRITICAL(); + + /* We may have just suspended the current task. */ + if( ( void * ) pxTaskToSuspend == NULL ) + { + portYIELD_WITHIN_API(); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + signed portBASE_TYPE xTaskIsTaskSuspended( xTaskHandle xTask ) + { + portBASE_TYPE xReturn = pdFALSE; + const tskTCB * const pxTCB = ( tskTCB * ) xTask; + + /* Is the task we are attempting to resume actually in the + suspended list? */ + if( listIS_CONTAINED_WITHIN( &xSuspendedTaskList, &( pxTCB->xGenericListItem ) ) != pdFALSE ) + { + /* Has the task already been resumed from within an ISR? */ + if( listIS_CONTAINED_WITHIN( &xPendingReadyList, &( pxTCB->xEventListItem ) ) != pdTRUE ) + { + /* Is it in the suspended list because it is in the + Suspended state? It is possible to be in the suspended + list because it is blocked on a task with no timeout + specified. */ + if( listIS_CONTAINED_WITHIN( NULL, &( pxTCB->xEventListItem ) ) == pdTRUE ) + { + xReturn = pdTRUE; + } + } + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_vTaskSuspend == 1 ) + + void vTaskResume( xTaskHandle pxTaskToResume ) + { + tskTCB *pxTCB; + + /* Remove the task from whichever list it is currently in, and place + it in the ready list. */ + pxTCB = ( tskTCB * ) pxTaskToResume; + + /* The parameter cannot be NULL as it is impossible to resume the + currently executing task. */ + if( ( pxTCB != NULL ) && ( pxTCB != pxCurrentTCB ) ) + { + portENTER_CRITICAL(); + { + if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME( pxTCB ); + + /* As we are in a critical section we can access the ready + lists even if the scheduler is suspended. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* We may have just resumed a higher priority task. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* This yield may not cause the task just resumed to run, but + will leave the lists in the correct state for the next yield. */ + portYIELD_WITHIN_API(); + } + } + } + portEXIT_CRITICAL(); + } + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_xTaskResumeFromISR == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + portBASE_TYPE xTaskResumeFromISR( xTaskHandle pxTaskToResume ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + tskTCB *pxTCB; + + pxTCB = ( tskTCB * ) pxTaskToResume; + + if( xTaskIsTaskSuspended( pxTCB ) == pdTRUE ) + { + traceTASK_RESUME_FROM_ISR( pxTCB ); + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xYieldRequired = ( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed, at which point a + yield will be performed if necessary. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxTCB->xEventListItem ) ); + } + } + + return xYieldRequired; + } + +#endif + + + + +/*----------------------------------------------------------- + * PUBLIC SCHEDULER CONTROL documented in task.h + *----------------------------------------------------------*/ + + +void vTaskStartScheduler( void ) +{ +portBASE_TYPE xReturn; + + /* Add the idle task at the lowest priority. */ + xReturn = xTaskCreate( prvIdleTask, ( signed char * ) "IDLE", tskIDLE_STACK_SIZE, ( void * ) NULL, ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), ( xTaskHandle * ) NULL ); + + if( xReturn == pdPASS ) + { + /* Interrupts are turned off here, to ensure a tick does not occur + before or during the call to xPortStartScheduler(). The stacks of + the created tasks contain a status word with interrupts switched on + so interrupts will automatically get re-enabled when the first task + starts to run. + + STEPPING THROUGH HERE USING A DEBUGGER CAN CAUSE BIG PROBLEMS IF THE + DEBUGGER ALLOWS INTERRUPTS TO BE PROCESSED. */ + portDISABLE_INTERRUPTS(); + + xSchedulerRunning = pdTRUE; + xTickCount = ( portTickType ) 0; + + /* If configGENERATE_RUN_TIME_STATS is defined then the following + macro must be defined to configure the timer/counter used to generate + the run time counter time base. */ + portCONFIGURE_TIMER_FOR_RUN_TIME_STATS(); + + /* Setting up the timer tick is hardware specific and thus in the + portable interface. */ + if( xPortStartScheduler() ) + { + /* Should not reach here as if the scheduler is running the + function will not return. */ + } + else + { + /* Should only reach here if a task calls xTaskEndScheduler(). */ + } + } +} +/*-----------------------------------------------------------*/ + +void vTaskEndScheduler( void ) +{ + /* Stop the scheduler interrupts and call the portable scheduler end + routine so the original ISRs can be restored if necessary. The port + layer must ensure interrupts enable bit is left in the correct state. */ + portDISABLE_INTERRUPTS(); + xSchedulerRunning = pdFALSE; + vPortEndScheduler(); +} +/*----------------------------------------------------------*/ + +void vTaskSuspendAll( void ) +{ + /* A critical section is not required as the variable is of type + portBASE_TYPE. */ + ++uxSchedulerSuspended; +} +/*----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskResumeAll( void ) +{ +register tskTCB *pxTCB; +signed portBASE_TYPE xAlreadyYielded = pdFALSE; + + /* It is possible that an ISR caused a task to be removed from an event + list while the scheduler was suspended. If this was the case then the + removed task will have been added to the xPendingReadyList. Once the + scheduler has been resumed it is safe to move all the pending ready + tasks from this list into their appropriate ready list. */ + portENTER_CRITICAL(); + { + --uxSchedulerSuspended; + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + if( uxCurrentNumberOfTasks > ( unsigned portBASE_TYPE ) 0 ) + { + portBASE_TYPE xYieldRequired = pdFALSE; + + /* Move any readied tasks from the pending list into the + appropriate ready list. */ + while( ( pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xPendingReadyList ) ) ) != NULL ) + { + vListRemove( &( pxTCB->xEventListItem ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxTCB ); + + /* If we have moved a task that has a priority higher than + the current task then we should yield. */ + if( pxTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + xYieldRequired = pdTRUE; + } + } + + /* If any ticks occurred while the scheduler was suspended then + they should be processed now. This ensures the tick count does not + slip, and that any delayed tasks are resumed at the correct time. */ + if( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 ) + { + while( uxMissedTicks > ( unsigned portBASE_TYPE ) 0 ) + { + vTaskIncrementTick(); + --uxMissedTicks; + } + + /* As we have processed some ticks it is appropriate to yield + to ensure the highest priority task that is ready to run is + the task actually running. */ + #if configUSE_PREEMPTION == 1 + { + xYieldRequired = pdTRUE; + } + #endif + } + + if( ( xYieldRequired == pdTRUE ) || ( xMissedYield == pdTRUE ) ) + { + xAlreadyYielded = pdTRUE; + xMissedYield = pdFALSE; + portYIELD_WITHIN_API(); + } + } + } + } + portEXIT_CRITICAL(); + + return xAlreadyYielded; +} + + + + + + +/*----------------------------------------------------------- + * PUBLIC TASK UTILITIES documented in task.h + *----------------------------------------------------------*/ + + + +portTickType xTaskGetTickCount( void ) +{ +portTickType xTicks; + + /* Critical section required if running on a 16 bit processor. */ + portENTER_CRITICAL(); + { + xTicks = xTickCount; + } + portEXIT_CRITICAL(); + + return xTicks; +} +/*-----------------------------------------------------------*/ + +unsigned portBASE_TYPE uxTaskGetNumberOfTasks( void ) +{ + /* A critical section is not required because the variables are of type + portBASE_TYPE. */ + return uxCurrentNumberOfTasks; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskList( signed char *pcWriteBuffer ) + { + unsigned portBASE_TYPE uxQueue; + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + /* Run through all the lists that could potentially contain a TCB and + report the task name, state and stack high water mark. */ + + pcWriteBuffer[ 0 ] = ( signed char ) 0x00; + strcat( ( char * ) pcWriteBuffer, ( const char * ) "\r\n" ); + + uxQueue = uxTopUsedPriority + 1; + + do + { + uxQueue--; + + if( !listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), tskREADY_CHAR ); + } + }while( uxQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + if( !listLIST_IS_EMPTY( pxDelayedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxDelayedTaskList, tskBLOCKED_CHAR ); + } + + if( !listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) pxOverflowDelayedTaskList, tskBLOCKED_CHAR ); + } + + #if( INCLUDE_vTaskDelete == 1 ) + { + if( !listLIST_IS_EMPTY( &xTasksWaitingTermination ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &xTasksWaitingTermination, tskDELETED_CHAR ); + } + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( !listLIST_IS_EMPTY( &xSuspendedTaskList ) ) + { + prvListTaskWithinSingleList( pcWriteBuffer, ( xList * ) &xSuspendedTaskList, tskSUSPENDED_CHAR ); + } + } + #endif + } + xTaskResumeAll(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + void vTaskGetRunTimeStats( signed char *pcWriteBuffer ) + { + unsigned portBASE_TYPE uxQueue; + unsigned long ulTotalRunTime = portGET_RUN_TIME_COUNTER_VALUE(); + + /* This is a VERY costly function that should be used for debug only. + It leaves interrupts disabled for a LONG time. */ + + vTaskSuspendAll(); + { + /* Run through all the lists that could potentially contain a TCB, + generating a table of run timer percentages in the provided + buffer. */ + + pcWriteBuffer[ 0 ] = ( signed char ) 0x00; + strcat( ( char * ) pcWriteBuffer, ( const char * ) "\r\n" ); + + uxQueue = uxTopUsedPriority + 1; + + do + { + uxQueue--; + + if( !listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxQueue ] ) ) ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) &( pxReadyTasksLists[ uxQueue ] ), ulTotalRunTime ); + } + }while( uxQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + if( !listLIST_IS_EMPTY( pxDelayedTaskList ) ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) pxDelayedTaskList, ulTotalRunTime ); + } + + if( !listLIST_IS_EMPTY( pxOverflowDelayedTaskList ) ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) pxOverflowDelayedTaskList, ulTotalRunTime ); + } + + #if ( INCLUDE_vTaskDelete == 1 ) + { + if( !listLIST_IS_EMPTY( &xTasksWaitingTermination ) ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) &xTasksWaitingTermination, ulTotalRunTime ); + } + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( !listLIST_IS_EMPTY( &xSuspendedTaskList ) ) + { + prvGenerateRunTimeStatsForTasksInList( pcWriteBuffer, ( xList * ) &xSuspendedTaskList, ulTotalRunTime ); + } + } + #endif + } + xTaskResumeAll(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + void vTaskStartTrace( signed char * pcBuffer, unsigned long ulBufferSize ) + { + portENTER_CRITICAL(); + { + pcTraceBuffer = ( signed char * )pcBuffer; + pcTraceBufferStart = pcBuffer; + pcTraceBufferEnd = pcBuffer + ( ulBufferSize - tskSIZE_OF_EACH_TRACE_LINE ); + xTracing = pdTRUE; + } + portEXIT_CRITICAL(); + } + +#endif +/*----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + unsigned long ulTaskEndTrace( void ) + { + unsigned long ulBufferLength; + + portENTER_CRITICAL(); + xTracing = pdFALSE; + portEXIT_CRITICAL(); + + ulBufferLength = ( unsigned long ) ( pcTraceBuffer - pcTraceBufferStart ); + + return ulBufferLength; + } + +#endif + + + +/*----------------------------------------------------------- + * SCHEDULER INTERNALS AVAILABLE FOR PORTING PURPOSES + * documented in task.h + *----------------------------------------------------------*/ + + +void vTaskIncrementTick( void ) +{ + /* Called by the portable layer each time a tick interrupt occurs. + Increments the tick then checks to see if the new tick value will cause any + tasks to be unblocked. */ + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + ++xTickCount; + if( xTickCount == ( portTickType ) 0 ) + { + xList *pxTemp; + + /* Tick count has overflowed so we need to swap the delay lists. + If there are any items in pxDelayedTaskList here then there is + an error! */ + pxTemp = pxDelayedTaskList; + pxDelayedTaskList = pxOverflowDelayedTaskList; + pxOverflowDelayedTaskList = pxTemp; + xNumOfOverflows++; + } + + /* See if this tick has made a timeout expire. */ + prvCheckDelayedTasks(); + } + else + { + ++uxMissedTicks; + + /* The tick hook gets called at regular intervals, even if the + scheduler is locked. */ + #if ( configUSE_TICK_HOOK == 1 ) + { + extern void vApplicationTickHook( void ); + + vApplicationTickHook(); + } + #endif + } + + #if ( configUSE_TICK_HOOK == 1 ) + { + extern void vApplicationTickHook( void ); + + /* Guard against the tick hook being called when the missed tick + count is being unwound (when the scheduler is being unlocked. */ + if( uxMissedTicks == 0 ) + { + vApplicationTickHook(); + } + } + #endif + + traceTASK_INCREMENT_TICK( xTickCount ); +} +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_vTaskCleanUpResources == 1 ) && ( INCLUDE_vTaskSuspend == 1 ) ) + + void vTaskCleanUpResources( void ) + { + unsigned short usQueue; + volatile tskTCB *pxTCB; + + usQueue = ( unsigned short ) uxTopUsedPriority + ( unsigned short ) 1; + + /* Remove any TCB's from the ready queues. */ + do + { + usQueue--; + + while( !listLIST_IS_EMPTY( &( pxReadyTasksLists[ usQueue ] ) ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &( pxReadyTasksLists[ usQueue ] ) ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + }while( usQueue > ( unsigned short ) tskIDLE_PRIORITY ); + + /* Remove any TCB's from the delayed queue. */ + while( !listLIST_IS_EMPTY( &xDelayedTaskList1 ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xDelayedTaskList1 ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + + /* Remove any TCB's from the overflow delayed queue. */ + while( !listLIST_IS_EMPTY( &xDelayedTaskList2 ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xDelayedTaskList2 ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + + while( !listLIST_IS_EMPTY( &xSuspendedTaskList ) ) + { + listGET_OWNER_OF_NEXT_ENTRY( pxTCB, &xSuspendedTaskList ); + vListRemove( ( xListItem * ) &( pxTCB->xGenericListItem ) ); + + prvDeleteTCB( ( tskTCB * ) pxTCB ); + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + void vTaskSetApplicationTaskTag( xTaskHandle xTask, pdTASK_HOOK_CODE pxTagValue ) + { + tskTCB *xTCB; + + /* If xTask is NULL then we are setting our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + portENTER_CRITICAL(); + xTCB->pxTaskTag = pxTagValue; + portEXIT_CRITICAL(); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + pdTASK_HOOK_CODE xTaskGetApplicationTaskTag( xTaskHandle xTask ) + { + tskTCB *xTCB; + pdTASK_HOOK_CODE xReturn; + + /* If xTask is NULL then we are setting our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + /* Save the hook function in the TCB. A critical section is required as + the value can be accessed from an interrupt. */ + portENTER_CRITICAL(); + xReturn = xTCB->pxTaskTag; + portEXIT_CRITICAL(); + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_APPLICATION_TASK_TAG == 1 ) + + portBASE_TYPE xTaskCallApplicationTaskHook( xTaskHandle xTask, void *pvParameter ) + { + tskTCB *xTCB; + portBASE_TYPE xReturn; + + /* If xTask is NULL then we are calling our own task hook. */ + if( xTask == NULL ) + { + xTCB = ( tskTCB * ) pxCurrentTCB; + } + else + { + xTCB = ( tskTCB * ) xTask; + } + + if( xTCB->pxTaskTag != NULL ) + { + xReturn = xTCB->pxTaskTag( pvParameter ); + } + else + { + xReturn = pdFAIL; + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +void vTaskSwitchContext( void ) +{ + if( uxSchedulerSuspended != ( unsigned portBASE_TYPE ) pdFALSE ) + { + /* The scheduler is currently suspended - do not allow a context + switch. */ + xMissedYield = pdTRUE; + return; + } + + traceTASK_SWITCHED_OUT(); + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + unsigned long ulTempCounter = portGET_RUN_TIME_COUNTER_VALUE(); + + /* Add the amount of time the task has been running to the accumulated + time so far. The time the task started running was stored in + ulTaskSwitchedInTime. Note that there is no overflow protection here + so count values are only valid until the timer overflows. Generally + this will be about 1 hour assuming a 1uS timer increment. */ + pxCurrentTCB->ulRunTimeCounter += ( ulTempCounter - ulTaskSwitchedInTime ); + ulTaskSwitchedInTime = ulTempCounter; + } + #endif + + taskFIRST_CHECK_FOR_STACK_OVERFLOW(); + taskSECOND_CHECK_FOR_STACK_OVERFLOW(); + + /* Find the highest priority queue that contains ready tasks. */ + while( listLIST_IS_EMPTY( &( pxReadyTasksLists[ uxTopReadyPriority ] ) ) ) + { + --uxTopReadyPriority; + } + + /* listGET_OWNER_OF_NEXT_ENTRY walks through the list, so the tasks of the + same priority get an equal share of the processor time. */ + listGET_OWNER_OF_NEXT_ENTRY( pxCurrentTCB, &( pxReadyTasksLists[ uxTopReadyPriority ] ) ); + + traceTASK_SWITCHED_IN(); + vWriteTraceToBuffer(); +} +/*-----------------------------------------------------------*/ + +void vTaskPlaceOnEventList( const xList * const pxEventList, portTickType xTicksToWait ) +{ +portTickType xTimeToWake; + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. */ + + /* Place the event list item of the TCB in the appropriate event list. + This is placed in the list in priority order so the highest priority task + is the first to be woken by the event. */ + vListInsert( ( xList * ) pxEventList, ( xListItem * ) &( pxCurrentTCB->xEventListItem ) ); + + /* We must remove ourselves from the ready list before adding ourselves + to the blocked list as the same list item is used for both lists. We have + exclusive access to the ready lists as the scheduler is locked. */ + vListRemove( ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + if( xTicksToWait == portMAX_DELAY ) + { + /* Add ourselves to the suspended task list instead of a delayed task + list to ensure we are not woken by a timing event. We will block + indefinitely. */ + vListInsertEnd( ( xList * ) &xSuspendedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + } + #else + { + /* Calculate the time at which the task should be woken if the event does + not occur. This may overflow but this doesn't matter. */ + xTimeToWake = xTickCount + xTicksToWait; + + listSET_LIST_ITEM_VALUE( &( pxCurrentTCB->xGenericListItem ), xTimeToWake ); + + if( xTimeToWake < xTickCount ) + { + /* Wake time has overflowed. Place this item in the overflow list. */ + vListInsert( ( xList * ) pxOverflowDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + else + { + /* The wake time has not overflowed, so we can use the current block list. */ + vListInsert( ( xList * ) pxDelayedTaskList, ( xListItem * ) &( pxCurrentTCB->xGenericListItem ) ); + } + } + #endif +} +/*-----------------------------------------------------------*/ + +signed portBASE_TYPE xTaskRemoveFromEventList( const xList * const pxEventList ) +{ +tskTCB *pxUnblockedTCB; +portBASE_TYPE xReturn; + + /* THIS FUNCTION MUST BE CALLED WITH INTERRUPTS DISABLED OR THE + SCHEDULER SUSPENDED. It can also be called from within an ISR. */ + + /* The event list is sorted in priority order, so we can remove the + first in the list, remove the TCB from the delayed list, and add + it to the ready list. + + If an event is for a queue that is locked then this function will never + get called - the lock count on the queue will get modified instead. This + means we can always expect exclusive access to the event list here. */ + pxUnblockedTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( pxEventList ); + vListRemove( &( pxUnblockedTCB->xEventListItem ) ); + + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + vListRemove( &( pxUnblockedTCB->xGenericListItem ) ); + prvAddTaskToReadyQueue( pxUnblockedTCB ); + } + else + { + /* We cannot access the delayed or ready lists, so will hold this + task pending until the scheduler is resumed. */ + vListInsertEnd( ( xList * ) &( xPendingReadyList ), &( pxUnblockedTCB->xEventListItem ) ); + } + + if( pxUnblockedTCB->uxPriority >= pxCurrentTCB->uxPriority ) + { + /* Return true if the task removed from the event list has + a higher priority than the calling task. This allows + the calling task to know if it should force a context + switch now. */ + xReturn = pdTRUE; + } + else + { + xReturn = pdFALSE; + } + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskSetTimeOutState( xTimeOutType * const pxTimeOut ) +{ + pxTimeOut->xOverflowCount = xNumOfOverflows; + pxTimeOut->xTimeOnEntering = xTickCount; +} +/*-----------------------------------------------------------*/ + +portBASE_TYPE xTaskCheckForTimeOut( xTimeOutType * const pxTimeOut, portTickType * const pxTicksToWait ) +{ +portBASE_TYPE xReturn; + + portENTER_CRITICAL(); + { + #if ( INCLUDE_vTaskSuspend == 1 ) + /* If INCLUDE_vTaskSuspend is set to 1 and the block time specified is + the maximum block time then the task should block indefinitely, and + therefore never time out. */ + if( *pxTicksToWait == portMAX_DELAY ) + { + xReturn = pdFALSE; + } + else /* We are not blocking indefinitely, perform the checks below. */ + #endif + + if( ( xNumOfOverflows != pxTimeOut->xOverflowCount ) && ( ( portTickType ) xTickCount >= ( portTickType ) pxTimeOut->xTimeOnEntering ) ) + { + /* The tick count is greater than the time at which vTaskSetTimeout() + was called, but has also overflowed since vTaskSetTimeOut() was called. + It must have wrapped all the way around and gone past us again. This + passed since vTaskSetTimeout() was called. */ + xReturn = pdTRUE; + } + else if( ( ( portTickType ) ( ( portTickType ) xTickCount - ( portTickType ) pxTimeOut->xTimeOnEntering ) ) < ( portTickType ) *pxTicksToWait ) + { + /* Not a genuine timeout. Adjust parameters for time remaining. */ + *pxTicksToWait -= ( ( portTickType ) xTickCount - ( portTickType ) pxTimeOut->xTimeOnEntering ); + vTaskSetTimeOutState( pxTimeOut ); + xReturn = pdFALSE; + } + else + { + xReturn = pdTRUE; + } + } + portEXIT_CRITICAL(); + + return xReturn; +} +/*-----------------------------------------------------------*/ + +void vTaskMissedYield( void ) +{ + xMissedYield = pdTRUE; +} + +/* + * ----------------------------------------------------------- + * The Idle task. + * ---------------------------------------------------------- + * + * The portTASK_FUNCTION() macro is used to allow port/compiler specific + * language extensions. The equivalent prototype for this function is: + * + * void prvIdleTask( void *pvParameters ); + * + */ +static portTASK_FUNCTION( prvIdleTask, pvParameters ) +{ + /* Stop warnings. */ + ( void ) pvParameters; + + for( ;; ) + { + /* See if any tasks have been deleted. */ + prvCheckTasksWaitingTermination(); + + #if ( configUSE_PREEMPTION == 0 ) + { + /* If we are not using preemption we keep forcing a task switch to + see if any other task has become available. If we are using + preemption we don't need to do this as any task becoming available + will automatically get the processor anyway. */ + taskYIELD(); + } + #endif + + #if ( ( configUSE_PREEMPTION == 1 ) && ( configIDLE_SHOULD_YIELD == 1 ) ) + { + /* When using preemption tasks of equal priority will be + timesliced. If a task that is sharing the idle priority is ready + to run then the idle task should yield before the end of the + timeslice. + + A critical region is not required here as we are just reading from + the list, and an occasional incorrect value will not matter. If + the ready list at the idle priority contains more than one task + then a task other than the idle task is ready to execute. */ + if( listCURRENT_LIST_LENGTH( &( pxReadyTasksLists[ tskIDLE_PRIORITY ] ) ) > ( unsigned portBASE_TYPE ) 1 ) + { + taskYIELD(); + } + } + #endif + + #if ( configUSE_IDLE_HOOK == 1 ) + { + extern void vApplicationIdleHook( void ); + + /* Call the user defined function from within the idle task. This + allows the application designer to add background functionality + without the overhead of a separate task. + NOTE: vApplicationIdleHook() MUST NOT, UNDER ANY CIRCUMSTANCES, + CALL A FUNCTION THAT MIGHT BLOCK. */ + vApplicationIdleHook(); + } + #endif + } +} /*lint !e715 pvParameters is not accessed but all task functions require the same prototype. */ + + + + + + + +/*----------------------------------------------------------- + * File private functions documented at the top of the file. + *----------------------------------------------------------*/ + + + +static void prvInitialiseTCBVariables( tskTCB *pxTCB, const signed char * const pcName, unsigned portBASE_TYPE uxPriority, const xMemoryRegion * const xRegions, unsigned short usStackDepth ) +{ + /* Store the function name in the TCB. */ + #if configMAX_TASK_NAME_LEN > 1 + { + /* Don't bring strncpy into the build unnecessarily. */ + strncpy( ( char * ) pxTCB->pcTaskName, ( const char * ) pcName, ( unsigned short ) configMAX_TASK_NAME_LEN ); + } + #endif + pxTCB->pcTaskName[ ( unsigned short ) configMAX_TASK_NAME_LEN - ( unsigned short ) 1 ] = '\0'; + + /* This is used as an array index so must ensure it's not too large. First + remove the privilege bit if one is present. */ + if( uxPriority >= configMAX_PRIORITIES ) + { + uxPriority = configMAX_PRIORITIES - 1; + } + + pxTCB->uxPriority = uxPriority; + #if ( configUSE_MUTEXES == 1 ) + { + pxTCB->uxBasePriority = uxPriority; + } + #endif + + vListInitialiseItem( &( pxTCB->xGenericListItem ) ); + vListInitialiseItem( &( pxTCB->xEventListItem ) ); + + /* Set the pxTCB as a link back from the xListItem. This is so we can get + back to the containing TCB from a generic item in a list. */ + listSET_LIST_ITEM_OWNER( &( pxTCB->xGenericListItem ), pxTCB ); + + /* Event lists are always in priority order. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) uxPriority ); + listSET_LIST_ITEM_OWNER( &( pxTCB->xEventListItem ), pxTCB ); + + #if ( portCRITICAL_NESTING_IN_TCB == 1 ) + { + pxTCB->uxCriticalNesting = ( unsigned portBASE_TYPE ) 0; + } + #endif + + #if ( configUSE_APPLICATION_TASK_TAG == 1 ) + { + pxTCB->pxTaskTag = NULL; + } + #endif + + #if ( configGENERATE_RUN_TIME_STATS == 1 ) + { + pxTCB->ulRunTimeCounter = 0UL; + } + #endif + + #if ( portUSING_MPU_WRAPPERS == 1 ) + { + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, pxTCB->pxStack, usStackDepth ); + } + #else + { + ( void ) xRegions; + ( void ) usStackDepth; + } + #endif +} +/*-----------------------------------------------------------*/ + +#if ( portUSING_MPU_WRAPPERS == 1 ) + + void vTaskAllocateMPURegions( xTaskHandle xTaskToModify, const xMemoryRegion * const xRegions ) + { + tskTCB *pxTCB; + + if( xTaskToModify == pxCurrentTCB ) + { + xTaskToModify = NULL; + } + + /* If null is passed in here then we are deleting ourselves. */ + pxTCB = prvGetTCBFromHandle( xTaskToModify ); + + vPortStoreTaskMPUSettings( &( pxTCB->xMPUSettings ), xRegions, NULL, 0 ); + } + /*-----------------------------------------------------------*/ +#endif + +static void prvInitialiseTaskLists( void ) +{ +unsigned portBASE_TYPE uxPriority; + + for( uxPriority = 0; uxPriority < configMAX_PRIORITIES; uxPriority++ ) + { + vListInitialise( ( xList * ) &( pxReadyTasksLists[ uxPriority ] ) ); + } + + vListInitialise( ( xList * ) &xDelayedTaskList1 ); + vListInitialise( ( xList * ) &xDelayedTaskList2 ); + vListInitialise( ( xList * ) &xPendingReadyList ); + + #if ( INCLUDE_vTaskDelete == 1 ) + { + vListInitialise( ( xList * ) &xTasksWaitingTermination ); + } + #endif + + #if ( INCLUDE_vTaskSuspend == 1 ) + { + vListInitialise( ( xList * ) &xSuspendedTaskList ); + } + #endif + + /* Start with pxDelayedTaskList using list1 and the pxOverflowDelayedTaskList + using list2. */ + pxDelayedTaskList = &xDelayedTaskList1; + pxOverflowDelayedTaskList = &xDelayedTaskList2; +} +/*-----------------------------------------------------------*/ + +static void prvCheckTasksWaitingTermination( void ) +{ + #if ( INCLUDE_vTaskDelete == 1 ) + { + portBASE_TYPE xListIsEmpty; + + /* ucTasksDeleted is used to prevent vTaskSuspendAll() being called + too often in the idle task. */ + if( uxTasksDeleted > ( unsigned portBASE_TYPE ) 0 ) + { + vTaskSuspendAll(); + xListIsEmpty = listLIST_IS_EMPTY( &xTasksWaitingTermination ); + xTaskResumeAll(); + + if( !xListIsEmpty ) + { + tskTCB *pxTCB; + + portENTER_CRITICAL(); + { + pxTCB = ( tskTCB * ) listGET_OWNER_OF_HEAD_ENTRY( ( ( xList * ) &xTasksWaitingTermination ) ); + vListRemove( &( pxTCB->xGenericListItem ) ); + --uxCurrentNumberOfTasks; + --uxTasksDeleted; + } + portEXIT_CRITICAL(); + + prvDeleteTCB( pxTCB ); + } + } + } + #endif +} +/*-----------------------------------------------------------*/ + +static tskTCB *prvAllocateTCBAndStack( unsigned short usStackDepth, portSTACK_TYPE *puxStackBuffer ) +{ +tskTCB *pxNewTCB; + + /* Allocate space for the TCB. Where the memory comes from depends on + the implementation of the port malloc function. */ + pxNewTCB = ( tskTCB * ) pvPortMalloc( sizeof( tskTCB ) ); + + if( pxNewTCB != NULL ) + { + /* Allocate space for the stack used by the task being created. + The base of the stack memory stored in the TCB so the task can + be deleted later if required. */ + pxNewTCB->pxStack = ( portSTACK_TYPE * ) pvPortMallocAligned( ( ( ( size_t )usStackDepth ) * sizeof( portSTACK_TYPE ) ), puxStackBuffer ); + + if( pxNewTCB->pxStack == NULL ) + { + /* Could not allocate the stack. Delete the allocated TCB. */ + vPortFree( pxNewTCB ); + pxNewTCB = NULL; + } + else + { + /* Just to help debugging. */ + memset( pxNewTCB->pxStack, tskSTACK_FILL_BYTE, usStackDepth * sizeof( portSTACK_TYPE ) ); + } + } + + return pxNewTCB; +} +/*-----------------------------------------------------------*/ + +#if ( configUSE_TRACE_FACILITY == 1 ) + + static void prvListTaskWithinSingleList( const signed char *pcWriteBuffer, xList *pxList, signed char cStatus ) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + unsigned short usStackRemaining; + + /* Write the details of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + #if ( portSTACK_GROWTH > 0 ) + { + usStackRemaining = usTaskCheckFreeStackSpace( ( unsigned char * ) pxNextTCB->pxEndOfStack ); + } + #else + { + usStackRemaining = usTaskCheckFreeStackSpace( ( unsigned char * ) pxNextTCB->pxStack ); + } + #endif + + sprintf( pcStatusString, ( char * ) "%s\t\t%c\t%u\t%u\t%u\r\n", pxNextTCB->pcTaskName, cStatus, ( unsigned int ) pxNextTCB->uxPriority, usStackRemaining, ( unsigned int ) pxNextTCB->uxTCBNumber ); + strcat( ( char * ) pcWriteBuffer, ( char * ) pcStatusString ); + + } while( pxNextTCB != pxFirstTCB ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configGENERATE_RUN_TIME_STATS == 1 ) + + static void prvGenerateRunTimeStatsForTasksInList( const signed char *pcWriteBuffer, xList *pxList, unsigned long ulTotalRunTime ) + { + volatile tskTCB *pxNextTCB, *pxFirstTCB; + unsigned long ulStatsAsPercentage; + + /* Write the run time stats of all the TCB's in pxList into the buffer. */ + listGET_OWNER_OF_NEXT_ENTRY( pxFirstTCB, pxList ); + do + { + /* Get next TCB in from the list. */ + listGET_OWNER_OF_NEXT_ENTRY( pxNextTCB, pxList ); + + /* Divide by zero check. */ + if( ulTotalRunTime > 0UL ) + { + /* Has the task run at all? */ + if( pxNextTCB->ulRunTimeCounter == 0 ) + { + /* The task has used no CPU time at all. */ + sprintf( pcStatsString, ( char * ) "%s\t\t0\t\t0%%\r\n", pxNextTCB->pcTaskName ); + } + else + { + /* What percentage of the total run time as the task used? + This will always be rounded down to the nearest integer. */ + ulStatsAsPercentage = ( 100UL * pxNextTCB->ulRunTimeCounter ) / ulTotalRunTime; + + if( ulStatsAsPercentage > 0UL ) + { + sprintf( pcStatsString, ( char * ) "%s\t\t%u\t\t%u%%\r\n", pxNextTCB->pcTaskName, ( unsigned int ) pxNextTCB->ulRunTimeCounter, ( unsigned int ) ulStatsAsPercentage ); + } + else + { + /* If the percentage is zero here then the task has + consumed less than 1% of the total run time. */ + sprintf( pcStatsString, ( char * ) "%s\t\t%u\t\t<1%%\r\n", pxNextTCB->pcTaskName, ( unsigned int ) pxNextTCB->ulRunTimeCounter ); + } + } + + strcat( ( char * ) pcWriteBuffer, ( char * ) pcStatsString ); + } + + } while( pxNextTCB != pxFirstTCB ); + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( ( configUSE_TRACE_FACILITY == 1 ) || ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) ) + + static unsigned short usTaskCheckFreeStackSpace( const unsigned char * pucStackByte ) + { + register unsigned short usCount = 0; + + while( *pucStackByte == tskSTACK_FILL_BYTE ) + { + pucStackByte -= portSTACK_GROWTH; + usCount++; + } + + usCount /= sizeof( portSTACK_TYPE ); + + return usCount; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_uxTaskGetStackHighWaterMark == 1 ) + + unsigned portBASE_TYPE uxTaskGetStackHighWaterMark( xTaskHandle xTask ) + { + tskTCB *pxTCB; + unsigned char *pcEndOfStack; + unsigned portBASE_TYPE uxReturn; + + pxTCB = prvGetTCBFromHandle( xTask ); + + #if portSTACK_GROWTH < 0 + { + pcEndOfStack = ( unsigned char * ) pxTCB->pxStack; + } + #else + { + pcEndOfStack = ( unsigned char * ) pxTCB->pxEndOfStack; + } + #endif + + uxReturn = ( unsigned portBASE_TYPE ) usTaskCheckFreeStackSpace( pcEndOfStack ); + + return uxReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( ( INCLUDE_vTaskDelete == 1 ) || ( INCLUDE_vTaskCleanUpResources == 1 ) ) + + static void prvDeleteTCB( tskTCB *pxTCB ) + { + /* Free up the memory allocated by the scheduler for the task. It is up to + the task to free any memory allocated at the application level. */ + vPortFreeAligned( pxTCB->pxStack ); + vPortFree( pxTCB ); + } + +#endif + + +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetCurrentTaskHandle == 1 ) + + xTaskHandle xTaskGetCurrentTaskHandle( void ) + { + xTaskHandle xReturn; + + /* A critical section is not required as this is not called from + an interrupt and the current TCB will always be the same for any + individual execution thread. */ + xReturn = pxCurrentTCB; + + return xReturn; + } + +#endif + +/*-----------------------------------------------------------*/ + +#if ( INCLUDE_xTaskGetSchedulerState == 1 ) + + portBASE_TYPE xTaskGetSchedulerState( void ) + { + portBASE_TYPE xReturn; + + if( xSchedulerRunning == pdFALSE ) + { + xReturn = taskSCHEDULER_NOT_STARTED; + } + else + { + if( uxSchedulerSuspended == ( unsigned portBASE_TYPE ) pdFALSE ) + { + xReturn = taskSCHEDULER_RUNNING; + } + else + { + xReturn = taskSCHEDULER_SUSPENDED; + } + } + + return xReturn; + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityInherit( xTaskHandle * const pxMutexHolder ) + { + tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder; + + if( pxTCB->uxPriority < pxCurrentTCB->uxPriority ) + { + /* Adjust the mutex holder state to account for its new priority. */ + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) pxCurrentTCB->uxPriority ); + + /* If the task being modified is in the ready state it will need to + be moved in to a new list. */ + if( listIS_CONTAINED_WITHIN( &( pxReadyTasksLists[ pxTCB->uxPriority ] ), &( pxTCB->xGenericListItem ) ) ) + { + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Inherit the priority before being moved into the new list. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + prvAddTaskToReadyQueue( pxTCB ); + } + else + { + /* Just inherit the priority. */ + pxTCB->uxPriority = pxCurrentTCB->uxPriority; + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( configUSE_MUTEXES == 1 ) + + void vTaskPriorityDisinherit( xTaskHandle * const pxMutexHolder ) + { + tskTCB * const pxTCB = ( tskTCB * ) pxMutexHolder; + + if( pxMutexHolder != NULL ) + { + if( pxTCB->uxPriority != pxTCB->uxBasePriority ) + { + /* We must be the running task to be able to give the mutex back. + Remove ourselves from the ready list we currently appear in. */ + vListRemove( &( pxTCB->xGenericListItem ) ); + + /* Disinherit the priority before adding ourselves into the new + ready list. */ + pxTCB->uxPriority = pxTCB->uxBasePriority; + listSET_LIST_ITEM_VALUE( &( pxTCB->xEventListItem ), configMAX_PRIORITIES - ( portTickType ) pxTCB->uxPriority ); + prvAddTaskToReadyQueue( pxTCB ); + } + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + + void vTaskEnterCritical( void ) + { + portDISABLE_INTERRUPTS(); + + if( xSchedulerRunning != pdFALSE ) + { + pxCurrentTCB->uxCriticalNesting++; + } + } + +#endif +/*-----------------------------------------------------------*/ + +#if ( portCRITICAL_NESTING_IN_TCB == 1 ) + +void vTaskExitCritical( void ) +{ + if( xSchedulerRunning != pdFALSE ) + { + if( pxCurrentTCB->uxCriticalNesting > 0 ) + { + pxCurrentTCB->uxCriticalNesting--; + + if( pxCurrentTCB->uxCriticalNesting == 0 ) + { + portENABLE_INTERRUPTS(); + } + } + } +} + +#endif +/*-----------------------------------------------------------*/ + + + + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/LICENSE b/flight/PiOS.x86/x86/Libraries/minIni/LICENSE new file mode 100644 index 000000000..68c771a09 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/LICENSE @@ -0,0 +1,176 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/NOTICE b/flight/PiOS.x86/x86/Libraries/minIni/NOTICE new file mode 100644 index 000000000..6ce0ef3bd --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/NOTICE @@ -0,0 +1,12 @@ +minIni is a programmer's library to read and write "INI" files in embedded +systems. The library takes little resources and can be configured for various +kinds of file I/O libraries. + +The method for portable INI file management in minIni is, in part based, on the +article "Multiplatform .INI Files" by Joseph J. Graf in the March 1994 issue of +Dr. Dobb's Journal. + +The C++ class in minIni.h was contributed by Steven Van Ingelgem. + +The option to compile minIni as a read-only library was contributed by Luca +Bassanello. diff --git a/flight/PiOS.x86/x86/Libraries/minIni/UPDATING.txt b/flight/PiOS.x86/x86/Libraries/minIni/UPDATING.txt new file mode 100644 index 000000000..577ade917 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/UPDATING.txt @@ -0,0 +1,5 @@ +Notes when upgrading MinINI to newer versions +Gussy - 11/09 + +-> Do not overwrite the following files, these files are platofrm depantant +----> minGlue.h \ No newline at end of file diff --git a/flight/PiOS.x86/x86/Libraries/minIni/VERSION.txt b/flight/PiOS.x86/x86/Libraries/minIni/VERSION.txt new file mode 100644 index 000000000..61a0f4963 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/VERSION.txt @@ -0,0 +1 @@ +Current version is 0.8 \ No newline at end of file diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minGlue-FatFs.h b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-FatFs.h new file mode 100644 index 000000000..3c14e4e83 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-FatFs.h @@ -0,0 +1,34 @@ +/* Glue functions for the minIni library, based on the FatFs and Tiny-FatFs + * libraries, see http://elm-chan.org/fsw/ff/00index_e.html + * + * Copyright (c) ITB CompuPhase, 2008-2009 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +/* You must set _USE_STRFUNC to 1 or 2 in the include file ff.h (or tff.h) + * to enable the "string functions" fgets() and fputs(). + */ +#include "ff.h" /* include tff.h for Tiny-FatFs */ +#define INI_FILETYPE FIL + +#define ini_openread(filename,file) (f_open((file),(filename),FA_READ+FA_OPEN_EXISTING) == 0) +#define ini_openwrite(filename,file) (f_open((file),(filename),FA_WRITE+FA_CREATE_ALWAYS) == 0) +#define ini_close(file) f_close(file) +#define ini_read(buffer,size,file) fgets((buffer),(size),(file)) +#define ini_write(buffer,file) fputs((buffer),(file)) +#define ini_rename(source,dest) f_rename((source),(dest)) +#define ini_remove(filename) f_unlink(filename) +#define ini_rewind(file) f_lseek((file),0) diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minGlue-efsl.h b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-efsl.h new file mode 100644 index 000000000..ebb587a39 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-efsl.h @@ -0,0 +1,67 @@ +/* Glue functions for the minIni library, based on the EFS Library, see + * http://www.efsl.be/ + * + * Copyright (c) ITB CompuPhase, 2008-2009 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +#define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#include "efs.h" +#define INI_FILETYPE EmbeddedFile +#define INI_LINETERM "\r\n" /* set line termination explicitly */ + + +extern EmbeddedFileSystem g_efs; + +#define ini_openread(filename,file) (file_fopen((file),&g_efs.myFs,(char*)(filename),'r') == 0) +#define ini_openwrite(filename,file) (file_fopen((file),&g_efs.myFs,(char*)(filename),'w') == 0) +#define ini_close(file) file_fclose(file) +#define ini_read(buffer,size,file) (file_read((file),(size),(buffer)) > 0) +#define ini_write(buffer,file) file_write((file),strlen(buffer),(char*)(buffer)) +#define ini_remove(filename) rmfile(&g_efs.myFs,(char*)(filename)) +#define ini_rewind(file) file_setpos(*(file),0) + +/* EFSL lacks a rename function, so instead we copy the file to the new name + * and delete the old file + */ +static int ini_rename(char *source, const char *dest) +{ + EmbeddedFile fr, fw; + int n; + + if (file_fopen(&fr, &g_efs.myFs, source, 'r') != 0) + return 0; + if (rmfile(&g_efs.myFs, (char*)dest) != 0) + return 0; + if (file_fopen(&fw, &g_efs.myFs, (char*)dest, 'w') != 0) + return 0; + + /* With some "insider knowledge", we can save some memory: the "source" + * parameter holds a filename that was built from the "dest" parameter. It + * was built in buffer and this buffer has the size INI_BUFFERSIZE. We can + * reuse this buffer for copying the file. + */ + while (n=file_read(&fr, INI_BUFFERSIZE, source)) + file_write(&fw, n, source); + + file_fclose(&fr); + file_fclose(&fw); + + /* Now we need to delete the source file. However, we have garbled the buffer + * that held the filename of the source. So we need to build it again. + */ + ini_tempname(source, dest, INI_BUFFERSIZE); + return rmfile(&g_efs.myFs, source) == 0; +} diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minGlue-stdio.h b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-stdio.h new file mode 100644 index 000000000..8388564fc --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minGlue-stdio.h @@ -0,0 +1,30 @@ +/* Glue functions for the minIni library, based on the C/C++ stdio library + * + * Or better said: this file contains macros that maps the function interface + * used by minIni to the standard C/C++ file I/O functions. + * + * Copyright (c) ITB CompuPhase, 2008-2009 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + */ + +/* map required file I/O to the standard C library */ +#include +#define ini_openread(filename,file) ((*(file) = fopen((filename),"rt")) != NULL) +#define ini_openwrite(filename,file) ((*(file) = fopen((filename),"wt")) != NULL) +#define ini_close(file) fclose(*(file)) +#define ini_read(buffer,size,file) fgets((buffer),(size),*(file)) +#define ini_write(buffer,file) fputs((buffer),*(file)) +#define ini_rename(source,dest) rename((source),(dest)) +#define ini_remove(filename) remove(filename) +#define ini_rewind(file) rewind(*(file)) diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minGlue.c b/flight/PiOS.x86/x86/Libraries/minIni/minGlue.c new file mode 100644 index 000000000..10f20168f --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minGlue.c @@ -0,0 +1,95 @@ +#include "dosfs.h" +#include "pios.h" +#include "minGlue.h" + +/* Global Variables */ +extern uint8_t PIOS_SDCARD_Sector[SECTOR_SIZE]; +extern VOLINFO PIOS_SDCARD_VolInfo; + +/* Local Variables */ +static uint32_t SuccessCount; + +int dosfs_ini_openread(const char *filename, PFILEINFO file) +{ + if(DFS_OpenFile(&PIOS_SDCARD_VolInfo, (uint8_t *)filename, DFS_READ, PIOS_SDCARD_Sector, file)) { + /* Error opening file */ + return 0; + } + + /* No errors */ + return 1; +} + +int dosfs_ini_openwrite(const char *filename, PFILEINFO file) +{ + /* TODO: Check this works */ + if(DFS_OpenFile(&PIOS_SDCARD_VolInfo, (uint8_t *)filename, DFS_WRITE, PIOS_SDCARD_Sector, file)) { + /* Error opening file */ + return 0; + } + + /* No errors */ + return 1; +} + +int dosfs_ini_close(PFILEINFO file) +{ + /* This doesn't actually do anything */ + DFS_Close(file); + + /* No errors */ + return 1; +} + +int dosfs_ini_read(char *buffer, int size, PFILEINFO file) +{ + if(PIOS_SDCARD_ReadLine(file, (uint8_t *)buffer, size) < 0) + { + /* Error reading line */ + return 0; + } + + /* No errors */ + return 1; +} + +int dosfs_ini_write(const char *buffer, PFILEINFO file) +{ + /* TODO: Check this works */ + DFS_WriteFile(file, PIOS_SDCARD_Sector, (uint8_t *)buffer, &SuccessCount, sizeof(buffer)); + + /* No errors */ + return 1; +} + +int dosfs_ini_rename(const char *source, const char *dest) +{ + if(PIOS_SDCARD_FileCopy((char *)source, (char *)dest)) { + /* Error renaming file */ + return 0; + } + + /* No errors */ + return 1; +} + +int dosfs_ini_remove(const char *filename) +{ + /* Remove the file */ + if(PIOS_SDCARD_FileDelete((char *)filename)) { + /* Error deleting file */ + return 0; + } + + /* No errors */ + return 1; +} + +int dosfs_ini_rewind(PFILEINFO file) +{ + /* TODO: Check this works */ + DFS_Seek(file, 0, PIOS_SDCARD_Sector); + /* No errors */ + return 1; +} + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minGlue.h b/flight/PiOS.x86/x86/Libraries/minIni/minGlue.h new file mode 100644 index 000000000..d71d7be0d --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minGlue.h @@ -0,0 +1,42 @@ +/* Glue functions for the minIni library */ + +#define INI_LINETERM "\r\n" +#define NDEBUG +#define INI_BUFFERSIZE 80 +// #define INI_BUFFERSIZE 256 /* maximum line length, maximum path length */ + +#include +#include +#define INI_FILETYPE FILE* + +#define ini_openread(filename,file) ((*file=fopen((char*)filename,"r"))!=NULL) +#define ini_openwrite(filename,file) ((*file=fopen((char*)filename,"2"))!=NULL) +#define ini_close(file) fclose(*file) +#define ini_read(buffer,size,file) fread(buffer,1,size,*file) +#define ini_write(buffer,file) fprintf(*file,"%s",buffer) +#define ini_rename(source,dest) rename(source,dest) +#define ini_remove(filename) unlink(filename) +#define ini_rewind(file) rewind(*file) + +//#include "dosfs.h" +//#define INI_FILETYPE FILEINFO + + +//#define ini_openread(filename,file) dosfs_ini_openread(filename,file) +//#define ini_openwrite(filename,file) dosfs_ini_openwrite(filename,file) +//#define ini_close(file) dosfs_ini_close(file) +//#define ini_read(buffer,size,file) dosfs_ini_read(buffer,size,file) +//#define ini_write(buffer,file) dosfs_ini_write(buffer,file) +//#define ini_rename(source,dest) dosfs_ini_rename(source,dest) +//#define ini_remove(filename) dosfs_ini_remove(filename) +//#define ini_rewind(file) dosfs_ini_rewind(file) + +//extern int dosfs_ini_openread(const char *filename, PFILEINFO file); +//extern int dosfs_ini_openwrite(const char *filename, PFILEINFO file); +//extern int dosfs_ini_close(PFILEINFO file); +//extern int dosfs_ini_read(char *buffer, int size, PFILEINFO file); +//extern int dosfs_ini_write(const char *buffer, PFILEINFO file); +//extern int dosfs_ini_rename(const char *source, const char *dest); +//extern int dosfs_ini_remove(const char *filename); +//extern int dosfs_ini_rewind(PFILEINFO file); + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minIni.c b/flight/PiOS.x86/x86/Libraries/minIni/minIni.c new file mode 100644 index 000000000..87400029e --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minIni.c @@ -0,0 +1,649 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * These routines are in part based on the article "Multiplatform .INI Files" + * by Joseph J. Graf in the March 1994 issue of Dr. Dobb's Journal. + * + * Copyright (c) ITB CompuPhase, 2008-2009 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.c 24 2009-05-06 08:01:53Z thiadmer.riemersma $ + */ + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined MININI_ANSI +# if !defined UNICODE /* for Windows */ +# define UNICODE +# endif +# if !defined _UNICODE /* for C library */ +# define _UNICODE +# endif +#endif + +#include "minIni.h" +#if defined NDEBUG + #define assert(e) +#else + #include +#endif + +#if !defined __T + #include + #include + /* definition of TCHAR already in minIni.h */ + #define __T(s) s + #define _tcscat strcat + #define _tcschr strchr + #define _tcscmp strcmp + #define _tcscpy strcpy + #define _tcsicmp stricmp + #define _tcslen strlen + #define _tcsncpy strncpy + #define _tcsnicmp strnicmp + #define _tcsrchr strrchr + #define _tcstol strtol + #define _tfgets fgets + #define _tfputs fputs + #define _tfopen fopen + #define _tremove remove + #define _trename rename +#endif + +#if defined __linux || defined __linux__ + #define __LINUX__ +#endif +#if defined FREEBSD && !defined __FreeBSD__ + #define __FreeBSD__ +#endif +#if !defined strnicmp + #if defined __LINUX__ || defined __FreeBSD__ || defined __OpenBSD__ + #define strnicmp strncasecmp + #endif +#endif + +#if !defined INI_LINETERM + #define INI_LINETERM __T("\n") +#endif +#if !defined INI_FILETYPE + #define INI_FILETYPE FILE* +#endif + +#if !defined sizearray + #define sizearray(a) (sizeof(a) / sizeof((a)[0])) +#endif + +enum quote_option { + QUOTE_NONE, + QUOTE_ENQUOTE, + QUOTE_DEQUOTE, +}; + +static TCHAR *skipleading(const TCHAR *str) +{ + assert(str != NULL); + while (*str != '\0' && *str <= ' ') + str++; + return (TCHAR *)str; +} + +static TCHAR *skiptrailing(const TCHAR *str, const TCHAR *base) +{ + assert(str != NULL); + assert(base != NULL); + while (str > base && *(str-1) <= ' ') + str--; + return (TCHAR *)str; +} + +static TCHAR *striptrailing(TCHAR *str) +{ + TCHAR *ptr = skiptrailing(_tcschr(str, '\0'), str); + assert(ptr != NULL); + *ptr='\0'; + return str; +} + +static TCHAR *save_strncpy(TCHAR *dest, const TCHAR *source, size_t maxlen, enum quote_option option) +{ + int d, s; + + assert(maxlen>0); + if (option == QUOTE_ENQUOTE && maxlen < 3) + option = QUOTE_NONE; /* cannot store two quotes and a terminating zero in less than 3 characters */ + + switch (option) { + case QUOTE_NONE: + _tcsncpy(dest,source,maxlen); + dest[maxlen-1]='\0'; + break; + case QUOTE_ENQUOTE: + d = 0; + dest[d++] = '"'; + for (s = 0; source[s] != '\0' && d < maxlen - 2; s++, d++) { + if (source[s] == '"') { + if (d >= maxlen - 3) + break; /* no space to store the escape character plus the one that follows it */ + dest[d++] = '\\'; + } /* if */ + dest[d] = source[s]; + } /* for */ + dest[d++] = '"'; + dest[d] = '\0'; + break; + case QUOTE_DEQUOTE: + for (d = s = 0; source[s] != '\0' && d < maxlen - 1; s++, d++) { + if ((source[s] == '"' || source[s] == '\\') && source[s + 1] == '"') + s++; + dest[d] = source[s]; + } /* for */ + dest[d] = '\0'; + break; + default: + assert(0); + } /* switch */ + + return dest; +} + +static int getkeystring(INI_FILETYPE *fp, const TCHAR *Section, const TCHAR *Key, + int idxSection, int idxKey, TCHAR *Buffer, int BufferSize) +{ + TCHAR *sp, *ep; + int len, idx, isstring; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + + assert(fp != NULL); + /* Move through file 1 line at a time until a section is matched or EOF. If + * parameter Section is NULL, only look at keys above the first section. If + * idxSection is postive, copy the relevant section name. + */ + len = (Section != NULL) ? _tcslen(Section) : 0; + if (len > 0 || idxSection >= 0) { + idx = -1; + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, fp)) + return 0; + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, ']'); + } while (*sp != '[' || ep == NULL || (((int)(ep-sp-1) != len || _tcsnicmp(sp+1,Section,len) != 0) && ++idx != idxSection)); + if (idxSection >= 0) { + if (idx == idxSection) { + assert(ep != NULL); + assert(*ep == ']'); + *ep = '\0'; + save_strncpy(Buffer, sp + 1, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more section found */ + } /* if */ + } /* if */ + + /* Now that the section has been found, find the entry. + * Stop searching upon leaving the section's area. + */ + assert(Key != NULL || idxKey >= 0); + len = (Key != NULL) ? (int)_tcslen(Key) : 0; + idx = -1; + do { + if (!ini_read(LocalBuffer,INI_BUFFERSIZE,fp) || *(sp = skipleading(LocalBuffer)) == '[') + return 0; + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + } while (*sp == ';' || *sp == '#' || ep == NULL || (((int)(skiptrailing(ep,sp)-sp) != len || _tcsnicmp(sp,Key,len) != 0) && ++idx != idxKey)); + if (idxKey >= 0) { + if (idx == idxKey) { + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + *ep = '\0'; + striptrailing(sp); + save_strncpy(Buffer, sp, BufferSize, QUOTE_NONE); + return 1; + } /* if */ + return 0; /* no more key found (in this section) */ + } /* if */ + + /* Copy up to BufferSize chars to buffer */ + assert(ep != NULL); + assert(*ep == '=' || *ep == ':'); + sp = skipleading(ep + 1); + /* Remove a trailing comment */ + isstring = 0; + for (ep = sp; *ep != '\0' && ((*ep != ';' && *ep != '#') || isstring); ep++) { + if (*ep == '"') { + if (*(ep + 1) == '"') + ep++; /* skip "" (both quotes) */ + else + isstring = !isstring; /* single quote, toggle isstring */ + } else if (*ep == '\\' && *(ep + 1) == '"') { + ep++; /* skip \" (both quotes */ + } /* if */ + } /* for */ + assert(ep != NULL && (*ep == '\0' || *ep == ';' || *ep == '#')); + *ep = '\0'; /* terminate at a comment */ + striptrailing(sp); + /* Remove double quotes surrounding a value */ + isstring = QUOTE_NONE; + if (*sp == '"' && (ep = _tcschr(sp, '\0')) != NULL && *(ep - 1) == '"') { + sp++; + *--ep = '\0'; + isstring = QUOTE_DEQUOTE; /* this is a string, so remove escaped characters */ + } /* if */ + save_strncpy(Buffer, sp, BufferSize, isstring); + return 1; +} + +/** ini_gets() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue default string in the event of a failed read + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, + TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || Key == NULL) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, Key, -1, -1, Buffer, BufferSize); + ini_close(&fp); + } /* if */ + if (!ok) + save_strncpy(Buffer, DefValue, BufferSize, QUOTE_NONE); + return _tcslen(Buffer); +} + +/** ini_getl() + * \param Section the name of the section to search for + * \param Key the name of the entry to find the value of + * \param DefValue the default value in the event of a failed read + * \param Filename the name of the .ini file to read from + * + * \return the value located at Key + */ +long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename) +{ + TCHAR buff[64]; + int len = ini_gets(Section, Key, __T(""), buff, sizearray(buff), Filename); + return (len == 0) ? DefValue : _tcstol(buff,NULL,10); +} + +/** ini_getsection() + * \param idx the zero-based sequence number of the section to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, NULL, NULL, idx, -1, Buffer, BufferSize); + ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return _tcslen(Buffer); +} + +/** ini_getkey() + * \param Section the name of the section to browse through, or NULL to + * browse through the keys outside any section + * \param idx the zero-based sequence number of the key to return + * \param Buffer a pointer to the buffer to copy into + * \param BufferSize the maximum number of characters to copy + * \param Filename the name and full path of the .ini file to read from + * + * \return the number of characters copied into the supplied buffer + */ +int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename) +{ + INI_FILETYPE fp; + int ok = 0; + + if (Buffer == NULL || BufferSize <= 0 || idx < 0) + return 0; + if (ini_openread(Filename, &fp)) { + ok = getkeystring(&fp, Section, NULL, -1, idx, Buffer, BufferSize); + ini_close(&fp); + } /* if */ + if (!ok) + *Buffer = '\0'; + return _tcslen(Buffer); +} + + +#if ! defined INI_READONLY +static void ini_tempname(TCHAR *dest, const TCHAR *source, int maxlength) +{ + TCHAR *p; + + save_strncpy(dest, source, maxlength, QUOTE_NONE); + p = _tcsrchr(dest, '\0'); + assert(p != NULL); + *(p - 1) = '~'; +} + +static enum quote_option check_enquote(const TCHAR *Value) +{ + const TCHAR *p; + + /* run through the value, if it has trailing spaces, or '"', ';' or '#' + * characters, enquote it + */ + assert(Value != NULL); + for (p = Value; *p != '\0' && *p != '"' && *p != ';' && *p != '#'; p++) + /* nothing */; + return (*p != '\0' || (p > Value && *(p - 1) == ' ')) ? QUOTE_ENQUOTE : QUOTE_NONE; +} + +static void writesection(TCHAR *LocalBuffer, const TCHAR *Section, INI_FILETYPE *fp) +{ + TCHAR *p; + + if (Section != NULL && _tcslen(Section) > 0) { + LocalBuffer[0] = '['; + save_strncpy(LocalBuffer + 1, Section, INI_BUFFERSIZE - 4, QUOTE_NONE); /* -1 for '[', -1 for ']', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = ']'; + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + ini_write(LocalBuffer, fp); + } /* if */ +} + +static void writekey(TCHAR *LocalBuffer, const TCHAR *Key, const TCHAR *Value, INI_FILETYPE *fp) +{ + TCHAR *p; + enum quote_option option = check_enquote(Value); + save_strncpy(LocalBuffer, Key, INI_BUFFERSIZE - 3, QUOTE_NONE); /* -1 for '=', -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + *p++ = '='; + save_strncpy(p, Value, INI_BUFFERSIZE - (p - LocalBuffer) - 2, option); /* -2 for '\r\n' */ + p = _tcsrchr(LocalBuffer, '\0'); + assert(p != NULL); + _tcscpy(p, INI_LINETERM); /* copy line terminator (typically "\n") */ + ini_write(LocalBuffer, fp); +} + +static void write_quoted(const TCHAR *Value, INI_FILETYPE *fp) +{ + TCHAR s[3]; + int idx; + if (check_enquote(Value) == QUOTE_NONE) { + ini_write(Value, fp); + } else { + ini_write("\"", fp); + for (idx = 0; Value[idx] != '\0'; idx++) { + if (Value[idx] == '"') { + s[0] = '\\'; + s[1] = Value[idx]; + s[2] = '\0'; + } else { + s[0] = Value[idx]; + s[1] = '\0'; + } /* if */ + ini_write(s, fp); + } /* for */ + ini_write("\"", fp); + } /* if */ +} + +/** ini_puts() + * \param Section the name of the section to write the string in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value a pointer to the buffer the string, or NULL to erase the key + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename) +{ + INI_FILETYPE rfp; + INI_FILETYPE wfp; + TCHAR *sp, *ep; + TCHAR LocalBuffer[INI_BUFFERSIZE]; + int len, match, count; + + assert(Filename!=NULL); + if (!ini_openread(Filename, &rfp)) { + /* If the .ini file doesn't exist, make a new file */ + if (Key!=NULL && Value!=NULL) { + if (!ini_openwrite(Filename, &wfp)) + return 0; + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + ini_close(&wfp); + } /* if */ + return 1; + } /* if */ + + /* If parameters Key and Value are valid (so this is not an "erase" request) + * and the setting already exists and it already has the correct value, do + * nothing. This early bail-out avoids rewriting the INI file for no reason. + */ + if (Key!=NULL && Value!=NULL) { + match = getkeystring(&rfp, Section, Key, -1, -1, LocalBuffer, sizearray(LocalBuffer)); + if (match && _tcscmp(LocalBuffer,Value)==0) { + ini_close(&rfp); + return 1; + } /* if */ + /* key not found, or different value -> proceed (but rewind the input file first) */ + ini_rewind(&rfp); + } /* if */ + + /* Get a temporary file name to copy to. Use the existing name, but with + * the last character set to a '~'. + */ + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + if (!ini_openwrite(LocalBuffer, &wfp)) { + ini_close(&rfp); + return 0; + } /* if */ + + /* Move through the file one line at a time until a section is + * matched or until EOF. Copy to temp file as it is read. + */ + count = 0; + len = (Section != NULL) ? _tcslen(Section) : 0; + if (len > 0) { + do { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* Failed to find section, so add one to the end */ + if (Key!=NULL && Value!=NULL) { + ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ + writesection(LocalBuffer, Section, &wfp); + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + /* Clean up and rename */ + ini_close(&rfp); + ini_close(&wfp); + ini_remove(Filename); + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + ini_rename(LocalBuffer, Filename); + return 1; + } /* if */ + /* Copy the line from source to dest, but not if this is the section that + * we are looking for and this section must be removed + */ + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, ']'); + match = (*sp == '[' && ep != NULL && (int)(ep-sp-1) == len && _tcsnicmp(sp + 1,Section,len) == 0); + if (!match || Key!=NULL) { + /* Remove blank lines, but insert a blank line (possibly one that was + * removed on the previous iteration) before a new section. This creates + * "neat" INI files. + */ + if (_tcslen(sp) > 0) { + if (*sp == '[' && count > 0) + ini_write(INI_LINETERM, &wfp); + ini_write(sp, &wfp); + count++; + } /* if */ + } /* if */ + } while (!match); + } /* if */ + + /* Now that the section has been found, find the entry. Stop searching + * upon leaving the section's area. Copy the file as it is read + * and create an entry if one is not found. + */ + len = (Key!=NULL) ? _tcslen(Key) : 0; + for( ;; ) { + if (!ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + /* EOF without an entry so make one */ + if (Key!=NULL && Value!=NULL) { + ini_write(INI_LINETERM, &wfp); /* force a new line (there may not have been one) behind the last line of the INI file */ + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + /* Clean up and rename */ + ini_close(&rfp); + ini_close(&wfp); + ini_remove(Filename); + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + ini_rename(LocalBuffer, Filename); + return 1; + } /* if */ + sp = skipleading(LocalBuffer); + ep = _tcschr(sp, '='); /* Parse out the equal sign */ + if (ep == NULL) + ep = _tcschr(sp, ':'); + match = (ep != NULL && (int)(skiptrailing(ep,sp)-sp) == len && _tcsnicmp(sp,Key,len) == 0); + if ((Key!=NULL && match) || *sp == '[') + break; /* found the key, or found a new section */ + /* in the section that we re-write, do not copy empty lines */ + if (Key!=NULL && _tcslen(sp) > 0) + ini_write(sp, &wfp); + } /* for */ + if (*sp == '[') { + /* found start of new section, the key was not in the specified + * section, so we add it just before the new section + */ + if (Key!=NULL && Value!=NULL) { + /* We cannot use "writekey()" here, because we need to preserve the + * contents of LocalBuffer. + */ + ini_write(Key, &wfp); + ini_write("=", &wfp); + write_quoted(Value, &wfp); + ini_write(INI_LINETERM INI_LINETERM, &wfp); /* put a blank line between the current and the next section */ + } /* if */ + /* write the new section header that we read previously */ + ini_write(sp, &wfp); + } else { + /* We found the key; ignore the line just read (with the key and + * the current value) and write the key with the new value. + */ + if (Key!=NULL && Value!=NULL) + writekey(LocalBuffer, Key, Value, &wfp); + } /* if */ + /* Copy the rest of the INI file (removing empty lines, except before a section) */ + while (ini_read(LocalBuffer, INI_BUFFERSIZE, &rfp)) { + sp = skipleading(LocalBuffer); + if (_tcslen(sp) > 0) { + if (*sp == '[') + ini_write(INI_LINETERM, &wfp); + ini_write(sp, &wfp); + } /* if */ + } /* while */ + /* Clean up and rename */ + ini_close(&rfp); + ini_close(&wfp); + ini_remove(Filename); + ini_tempname(LocalBuffer, Filename, INI_BUFFERSIZE); + ini_rename(LocalBuffer, Filename); + return 1; +} + +/* Ansi C "itoa" based on Kernighan & Ritchie's "Ansi C" book. */ +#define ABS(v) ((v) < 0 ? -(v) : (v)) + +static void strreverse(TCHAR *str) +{ + TCHAR t; + int i, j; + + for (i = 0, j = _tcslen(str) - 1; i < j; i++, j--) { + t = str[i]; + str[i] = str[j]; + str[j] = t; + } /* for */ +} + +static void long2str(long value, TCHAR *str) +{ + int i = 0; + long sign = value; + int n; + + /* generate digits in reverse order */ + do { + n = (int)(value % 10); /* get next lowest digit */ + str[i++] = (TCHAR)(ABS(n) + '0'); /* handle case of negative digit */ + } while (value /= 10); /* delete the lowest digit */ + if (sign < 0) + str[i++] = '-'; + str[i] = '\0'; + + strreverse(str); +} + +/** ini_putl() + * \param Section the name of the section to write the value in + * \param Key the name of the entry to write, or NULL to erase all keys in the section + * \param Value the value to write + * \param Filename the name and full path of the .ini file to write to + * + * \return 1 if successful, otherwise 0 + */ +int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename) +{ + TCHAR str[32]; + long2str(Value, str); + return ini_puts(Section, Key, str, Filename); +} +#endif /* !INI_READONLY */ + + +#if defined PORTABLE_STRNICMP +int strnicmp(const TCHAR *s1, const TCHAR *s2, size_t n) +{ + register unsigned TCHAR c1, c2; + + while (n-- != 0 && (*s1 || *s2)) { + c1 = *(const unsigned TCHAR *)s1++; + if ('a' <= c1 && c1 <= 'z') + c1 += ('A' - 'a'); + c2 = *(const unsigned TCHAR *)s2++; + if ('a' <= c2 && c2 <= 'z') + c2 += ('A' - 'a'); + if (c1 != c2) + return c1 - c2; + } /* while */ + return 0; +} +#endif /* PORTABLE_STRNICMP */ diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minIni.h b/flight/PiOS.x86/x86/Libraries/minIni/minIni.h new file mode 100644 index 000000000..096fa51bc --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/minIni.h @@ -0,0 +1,84 @@ +/* minIni - Multi-Platform INI file parser, suitable for embedded systems + * + * Copyright (c) ITB CompuPhase, 2008-2009 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not + * use this file except in compliance with the License. You may obtain a copy + * of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + * License for the specific language governing permissions and limitations + * under the License. + * + * Version: $Id: minIni.h 24 2009-05-06 08:01:53Z thiadmer.riemersma $ + */ +#ifndef MININI_H +#define MININI_H + +#include "minGlue.h" + +#if (defined _UNICODE || defined __UNICODE__ || defined UNICODE) && !defined INI_ANSIONLY + #include +#elif !defined __T + typedef char TCHAR; +#endif + +#if !defined INI_BUFFERSIZE + #define INI_BUFFERSIZE 512 +#endif + +#if defined __cplusplus + extern "C" { +#endif + +long ini_getl(const TCHAR *Section, const TCHAR *Key, long DefValue, const TCHAR *Filename); +int ini_gets(const TCHAR *Section, const TCHAR *Key, const TCHAR *DefValue, TCHAR *Buffer, int BufferSize, const TCHAR *Filename); +int ini_putl(const TCHAR *Section, const TCHAR *Key, long Value, const TCHAR *Filename); +int ini_puts(const TCHAR *Section, const TCHAR *Key, const TCHAR *Value, const TCHAR *Filename); +int ini_getsection(int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename); +int ini_getkey(const TCHAR *Section, int idx, TCHAR *Buffer, int BufferSize, const TCHAR *Filename); + +#if defined __cplusplus + } +#endif + +#if defined __cplusplus +#include + +/* The C++ class in minIni.h was contributed by Steven Van Ingelgem. */ +class minIni +{ +public: + minIni(const std::string& filename) : iniFilename(filename) + { } + + long getl(const std::string& Section, const std::string& Key, long DefValue=0) const + { return ini_getl(Section.c_str(), Key.c_str(), DefValue, iniFilename.c_str()); } + + long geti(const std::string& Section, const std::string& Key, int DefValue=0) const + { return reinterpret_cast( this->getl(Section, Key, DefValue) ); } + + std::string gets(const std::string& Section, const std::string& Key, const std::string& DefValue="") const + { + char buffer[INI_BUFFERSIZE]; + ini_gets(Section.c_str(), Key.c_str(), DefValue.c_str(), buffer, INI_BUFFERSIZE, iniFilename.c_str()); + return buffer; + } + + bool put(const std::string& Section, const std::string& Key, long Value) const + { return ini_putl(Section.c_str(), Key.c_str(), Value, iniFilename.c_str()); } + + bool put(const std::string& Section, const std::string& Key, const std::string& Value) const + { return ini_puts(Section.c_str(), Key.c_str(), Value.c_str(), iniFilename.c_str()); } + +private: + std::string iniFilename; +}; + +#endif /* __cplusplus */ + +#endif /* MININI_H */ diff --git a/flight/PiOS.x86/x86/Libraries/minIni/minIni.pdf b/flight/PiOS.x86/x86/Libraries/minIni/minIni.pdf new file mode 100644 index 0000000000000000000000000000000000000000..88f487b45030c09c8e705318719c5e71d499652a GIT binary patch literal 165009 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%0A(EoE~DLBLTLACOie;zcG|^gMMjwJQzMo0h2js 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%0A2!yC+k_9k!0Yn8iBnklO*{w>zuXZjn3g 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

0&IuZ(~I+`(Pu!nea;Vcix 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 literal 0 HcmV?d00001 diff --git a/flight/PiOS.x86/x86/Libraries/minIni/test.c b/flight/PiOS.x86/x86/Libraries/minIni/test.c new file mode 100644 index 000000000..cd4cd8183 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/test.c @@ -0,0 +1,75 @@ +/* Simple test program + * + * wcl386 -wx -d2 -q test.c minini.c + */ +#include +#include +#include +#include "minIni.h" + +#define sizearray(a) (sizeof(a) / sizeof((a)[0])) + +const char inifile[] = "test.ini"; + +int main(void) +{ + char str[100]; + long n; + int s, k; + char section[50]; + + /* string reading */ + n = ini_gets("first", "string", "aap", str, sizearray(str), inifile); + assert(n==4 && strcmp(str,"noot")==0); + n = ini_gets("second", "string", "aap", str, sizearray(str), inifile); + assert(n==4 && strcmp(str,"mies")==0); + n = ini_gets("first", "dummy", "aap", str, sizearray(str), inifile); + assert(n==3 && strcmp(str,"aap")==0); + printf("1. String reading tests passed\n"); + + /* value reading */ + n = ini_getl("first", "val", -1, inifile); + assert(n==1); + n = ini_getl("second", "val", -1, inifile); + assert(n==2); + n = ini_getl("first", "dummy", -1, inifile); + assert(n==-1); + printf("2. Value reading tests passed\n"); + + /* string writing */ + n = ini_puts("first", "alt", "flagged as \"correct\"", inifile); + assert(n==1); + n = ini_gets("first", "alt", "aap", str, sizearray(str), inifile); + assert(n==20 && strcmp(str,"flagged as \"correct\"")==0); + /* ----- */ + n = ini_puts("second", "alt", "correct", inifile); + assert(n==1); + n = ini_gets("second", "alt", "aap", str, sizearray(str), inifile); + assert(n==7 && strcmp(str,"correct")==0); + /* ----- */ + n = ini_puts("third", "alt", "correct", inifile); + assert(n==1); + n = ini_gets("third", "alt", "aap", str, sizearray(str), inifile); + assert(n==7 && strcmp(str,"correct")==0); + /* ----- */ + printf("3. String writing tests passed\n"); + + /* section/key enumeration */ + for (s = 0; ini_getsection(s, section, sizearray(section), inifile) > 0; s++) { + printf("[%s]\n", section); + for (k = 0; ini_getkey(section, k, str, sizearray(str), inifile) > 0; k++) { + printf("\t%s\n", str); + } /* for */ + } /* for */ + + /* string deletion */ + n = ini_puts("first", "alt", NULL, inifile); + assert(n==1); + n = ini_puts("second", "alt", NULL, inifile); + assert(n==1); + n = ini_puts("third", NULL, NULL, inifile); + assert(n==1); + + return 0; +} + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/test.ini b/flight/PiOS.x86/x86/Libraries/minIni/test.ini new file mode 100644 index 000000000..131fbdde8 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/test.ini @@ -0,0 +1,8 @@ +[First] +String=noot # trailing commment +Val=1 + +[Second] +Val = 2 +String = mies +#comment=3 diff --git a/flight/PiOS.x86/x86/Libraries/minIni/test_settings.ini b/flight/PiOS.x86/x86/Libraries/minIni/test_settings.ini new file mode 100644 index 000000000..2f10defba --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/test_settings.ini @@ -0,0 +1,20 @@ +# +# Test ini-File for minIni with FatFs on STM32 +# + +[Meta] +Version=0.0.1 +Author=Erwin Lindemann + +[Motor] +RPM_max=4000 +IGN_time1=123 +IGN_time2=543 + +[Regulator1] +Kp=10 +Ki=11 +Kd=12 +Limit_max=200 +Limit_min=100 + diff --git a/flight/PiOS.x86/x86/Libraries/minIni/wxMinIni.h b/flight/PiOS.x86/x86/Libraries/minIni/wxMinIni.h new file mode 100644 index 000000000..51609f959 --- /dev/null +++ b/flight/PiOS.x86/x86/Libraries/minIni/wxMinIni.h @@ -0,0 +1,73 @@ +#ifndef WXMININI_H +#define WXMININI_H + +#include +#include "minini.h" + +#if defined __linux || defined __linux__ || defined __LINUX__ \ + || defined FREEBSD || defined __FreeBSD__ || defined __OpenBSD__ + #define DIRSEP_CHAR '/' + #define DIRSEP_STR "/" +#else + #define DIRSEP_CHAR '\\' + #define DIRSEP_STR "\\" +#endif + +class minIni { +public: + minIni(const wxString& name, const wxString& path=wxT("")) + { + if (path.Len() > 0) + iniFilename = path; + else + iniFilename = wxGetCwd(); + int len = iniFilename.Len(); + if (len > 0 && iniFilename[len] != DIRSEP_CHAR) + iniFilename += wxT(DIRSEP_STR); + iniFilename += name; + } + + long getl(const wxString& Section, const wxString& Key, long DefValue=0, const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + return ini_getl(Section.utf8_str(), Key.utf8_str(), DefValue, name.utf8_str()); + } + + int geti(const wxString& Section, const wxString& Key, int DefValue=0, const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + return (int)ini_getl(Section.utf8_str(), Key.utf8_str(), DefValue, name.utf8_str()); + } + + wxString gets(const wxString& Section, const wxString& Key, const wxString& DefValue=wxT(""), const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + char buffer[INI_BUFFERSIZE]; + ini_gets(Section.utf8_str(), Key.utf8_str(), DefValue.utf8_str(), buffer, INI_BUFFERSIZE, name.utf8_str()); + wxString result = wxString::FromUTF8(buffer); + return result; + } + + bool put(const wxString& Section, const wxString& Key, long Value, const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + return ini_putl(Section.utf8_str(), Key.utf8_str(), Value, name.utf8_str()); + } + + bool put(const wxString& Section, const wxString& Key, int Value, const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + return ini_putl(Section.utf8_str(), Key.utf8_str(), Value, name.utf8_str()); + } + + bool put(const wxString& Section, const wxString& Key, const wxString& Value, const wxString& Filename=wxT("")) + { + wxString name = Filename.Len() > 0 ? Filename : iniFilename; + return ini_puts(Section.utf8_str(), Key.utf8_str(), Value.utf8_str(), name.utf8_str()); + } + +private: + wxString iniFilename; +}; + +#endif /* WXMININI_H */ diff --git a/flight/PiOS.x86/x86/pios_com.c b/flight/PiOS.x86/x86/pios_com.c new file mode 100644 index 000000000..a70715f24 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_com.c @@ -0,0 +1,285 @@ +/** + ****************************************************************************** + * + * @file pios_com.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief COM layer functions + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_COM COM layer functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_COM) + +#include + +static struct pios_com_dev * find_com_dev_by_id (uint8_t port) +{ + if (port >= pios_com_num_devices) { + /* Undefined COM port for this board (see pios_board.c) */ + return NULL; + } + + /* Get a handle for the device configuration */ + return &(pios_com_devs[port]); +} + +/** +* Initialises COM layer +* \param[in] mode currently only mode 0 supported +* \return < 0 if initialisation failed +*/ +int32_t PIOS_COM_Init(void) +{ + int32_t ret = 0; + + /* If any COM assignment: */ +#if defined(PIOS_INCLUDE_SERIAL) + PIOS_SERIAL_Init(); +#endif + +#if defined(PIOS_INCLUDE_UDP) + PIOS_UDP_Init(); +#endif + + return ret; +} + +/** +* Change the port speed without re-initializing +* \param[in] port COM port +* \param[in] baud Requested baud rate +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_ChangeBaud(uint8_t port, uint32_t baud) +{ + struct pios_com_dev * com_dev; + + com_dev = find_com_dev_by_id (port); + + if (!com_dev) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Invoke the driver function if it exists */ + if (com_dev->driver->set_baud) { + com_dev->driver->set_baud(com_dev->id, baud); + } + + return 0; +} + +/** +* Sends a package over given port +* \param[in] port COM port +* \param[in] buffer character buffer +* \param[in] len buffer length +* \return -1 if port not available +* \return -2 if non-blocking mode activated: buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendBufferNonBlocking(uint8_t port, uint8_t *buffer, uint16_t len) +{ + struct pios_com_dev * com_dev; + + com_dev = find_com_dev_by_id (port); + + if (!com_dev) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Invoke the driver function if it exists */ + if (com_dev->driver->tx_nb) { + return com_dev->driver->tx_nb(com_dev->id, buffer, len); + } + + return 0; +} + +/** +* Sends a package over given port +* (blocking function) +* \param[in] port COM port +* \param[in] buffer character buffer +* \param[in] len buffer length +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendBuffer(uint8_t port, uint8_t *buffer, uint16_t len) +{ + struct pios_com_dev * com_dev; + + com_dev = find_com_dev_by_id (port); + + if (!com_dev) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Invoke the driver function if it exists */ + if (com_dev->driver->tx) { + return com_dev->driver->tx(com_dev->id, buffer, len); + } + + return 0; +} + +/** +* Sends a single character over given port +* \param[in] port COM port +* \param[in] c character +* \return -1 if port not available +* \return -2 buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendCharNonBlocking(uint8_t port, char c) +{ + return PIOS_COM_SendBufferNonBlocking(port, (uint8_t *)&c, 1); +} + +/** +* Sends a single character over given port +* (blocking function) +* \param[in] port COM port +* \param[in] c character +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendChar(uint8_t port, char c) +{ + return PIOS_COM_SendBuffer(port, (uint8_t *)&c, 1); +} + +/** +* Sends a string over given port +* \param[in] port COM port +* \param[in] str zero-terminated string +* \return -1 if port not available +* \return -2 buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendStringNonBlocking(uint8_t port, char *str) +{ + return PIOS_COM_SendBufferNonBlocking(port, (uint8_t *)str, (uint16_t)strlen(str)); +} + +/** +* Sends a string over given port +* (blocking function) +* \param[in] port COM port +* \param[in] str zero-terminated string +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendString(uint8_t port, char *str) +{ + return PIOS_COM_SendBuffer(port, (uint8_t *)str, strlen(str)); +} + +/** +* Sends a formatted string (-> printf) over given port +* \param[in] port COM port +* \param[in] *format zero-terminated format string - 128 characters supported maximum! +* \param[in] ... optional arguments, +* 128 characters supported maximum! +* \return -2 if non-blocking mode activated: buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendFormattedStringNonBlocking(uint8_t port, char *format, ...) +{ + uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later! + + va_list args; + + va_start(args, format); + vsprintf((char *)buffer, format, args); + return PIOS_COM_SendBufferNonBlocking(port, buffer, (uint16_t)strlen((char *)buffer)); +} + +/** +* Sends a formatted string (-> printf) over given port +* (blocking function) +* \param[in] port COM port +* \param[in] *format zero-terminated format string - 128 characters supported maximum! +* \param[in] ... optional arguments, +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendFormattedString(uint8_t port, char *format, ...) +{ + uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later! + va_list args; + + va_start(args, format); + vsprintf((char *)buffer, format, args); + return PIOS_COM_SendBuffer(port, buffer, (uint16_t)strlen((char *)buffer)); +} + +/** +* Transfer bytes from port buffers into another buffer +* \param[in] port COM port +* \returns Byte from buffer +*/ +uint8_t PIOS_COM_ReceiveBuffer(uint8_t port) +{ + struct pios_com_dev * com_dev; + + com_dev = find_com_dev_by_id (port); + //PIOS_DEBUG_Assert(com_dev); + //PIOS_DEBUG_Assert(com_dev->driver->rx); + + return com_dev->driver->rx(com_dev->id); +} + +/** +* Get the number of bytes waiting in the buffer +* \param[in] port COM port +* \return Number of bytes used in buffer +*/ +int32_t PIOS_COM_ReceiveBufferUsed(uint8_t port) +{ + struct pios_com_dev * com_dev; + + com_dev = find_com_dev_by_id (port); + + if (!com_dev) { + /* Undefined COM port for this board (see pios_board.c) */ + return 0; + } + + if (!com_dev->driver->rx_avail) { + return 0; + } + + return com_dev->driver->rx_avail(com_dev->id); +} + +#endif diff --git a/flight/PiOS.x86/x86/pios_com.c.old b/flight/PiOS.x86/x86/pios_com.c.old new file mode 100644 index 000000000..a00de373f --- /dev/null +++ b/flight/PiOS.x86/x86/pios_com.c.old @@ -0,0 +1,332 @@ +/** + ****************************************************************************** + * + * @file pios_com.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief COM layer functions + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_COM COM layer functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_COM) + +#include +#include +#include +#include +#include + + + +//#include +#define NUM_FIFOS 16 +#define BUFFER_SIZE 1024 + +int fifos_in[NUM_FIFOS]; +int fifos_out[NUM_FIFOS]; + +int buffer_fill[NUM_FIFOS]; +char buffer[NUM_FIFOS][BUFFER_SIZE]; + + +static int find_com_dev_by_id (uint8_t port) +{ + if (port >= NUM_FIFOS) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Get a handle for the device configuration */ + return port; +} + +/** +* Initialises COM layer +* \param[in] mode currently only mode 0 supported +* \return < 0 if initialisation failed +*/ +int32_t PIOS_COM_Init(void) +{ + int32_t ret = 0; + + char filename[]="/tmp/OpenPlotCOMPort66666666.out"; + int32_t t; + for (t=0;tdriver->set_baud) { +// com_dev->driver->set_baud(com_dev->id, baud); +// } + + return 0; +} + +/** +* Sends a package over given port +* \param[in] port COM port +* \param[in] buffer character buffer +* \param[in] len buffer length +* \return -1 if port not available +* \return -2 if non-blocking mode activated: buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendBufferNonBlocking(uint8_t port, uint8_t *buffer, uint16_t len) +{ + int com_dev; + int opts; + + com_dev = find_com_dev_by_id (port); + + if (com_dev==-1) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Invoke the driver function if it exists */ + //if (com_dev->driver->tx_nb) { +// return com_dev->driver->tx_nb(com_dev->id, buffer, len); +// } + opts = fcntl(fifos_out[com_dev],F_GETFL); + opts = opts | O_NONBLOCK; + fcntl(fifos_out[com_dev],F_SETFL, opts); + write(fifos_out[com_dev],buffer,len); + + return 0; +} + +/** +* Sends a package over given port +* (blocking function) +* \param[in] port COM port +* \param[in] buffer character buffer +* \param[in] len buffer length +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendBuffer(uint8_t port, uint8_t *buffer, uint16_t len) +{ + int com_dev; + int opts; + + com_dev = find_com_dev_by_id (port); + + if (com_dev==-1) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* Invoke the driver function if it exists */ + //if (com_dev->driver->tx_nb) { +// return com_dev->driver->tx_nb(com_dev->id, buffer, len); +// } + opts = fcntl(fifos_out[com_dev],F_GETFL); + opts = opts & ~O_NONBLOCK; + fcntl(fifos_out[com_dev],F_SETFL, opts); + write(fifos_out[com_dev],buffer,len); + + return 0; +} + +/** +* Sends a single character over given port +* \param[in] port COM port +* \param[in] c character +* \return -1 if port not available +* \return -2 buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendCharNonBlocking(uint8_t port, char c) +{ + return PIOS_COM_SendBufferNonBlocking(port, (uint8_t *)&c, 1); +} + +/** +* Sends a single character over given port +* (blocking function) +* \param[in] port COM port +* \param[in] c character +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendChar(uint8_t port, char c) +{ + return PIOS_COM_SendBuffer(port, (uint8_t *)&c, 1); +} + +/** +* Sends a string over given port +* \param[in] port COM port +* \param[in] str zero-terminated string +* \return -1 if port not available +* \return -2 buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendStringNonBlocking(uint8_t port, char *str) +{ + return PIOS_COM_SendBufferNonBlocking(port, (uint8_t *)str, (uint16_t)strlen(str)); +} + +/** +* Sends a string over given port +* (blocking function) +* \param[in] port COM port +* \param[in] str zero-terminated string +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendString(uint8_t port, char *str) +{ + return PIOS_COM_SendBuffer(port, (uint8_t *)str, strlen(str)); +} + +/** +* Sends a formatted string (-> printf) over given port +* \param[in] port COM port +* \param[in] *format zero-terminated format string - 128 characters supported maximum! +* \param[in] ... optional arguments, +* 128 characters supported maximum! +* \return -2 if non-blocking mode activated: buffer is full +* caller should retry until buffer is free again +* \return 0 on success +*/ +int32_t PIOS_COM_SendFormattedStringNonBlocking(uint8_t port, char *format, ...) +{ + uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later! + + va_list args; + + va_start(args, format); + vsprintf((char *)buffer, format, args); + return PIOS_COM_SendBufferNonBlocking(port, buffer, (uint16_t)strlen((char *)buffer)); +} + +/** +* Sends a formatted string (-> printf) over given port +* (blocking function) +* \param[in] port COM port +* \param[in] *format zero-terminated format string - 128 characters supported maximum! +* \param[in] ... optional arguments, +* \return -1 if port not available +* \return 0 on success +*/ +int32_t PIOS_COM_SendFormattedString(uint8_t port, char *format, ...) +{ + uint8_t buffer[128]; // TODO: tmp!!! Provide a streamed COM method later! + va_list args; + + va_start(args, format); + vsprintf((char *)buffer, format, args); + return PIOS_COM_SendBuffer(port, buffer, (uint16_t)strlen((char *)buffer)); +} + +/** +* Transfer bytes from port buffers into another buffer +* \param[in] port COM port +* \returns Byte from buffer +*/ +uint8_t PIOS_COM_ReceiveBuffer(uint8_t port) +{ + int com_dev; + uint8_t b; + + com_dev = find_com_dev_by_id (port); + + if (com_dev==-1) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* read one byte from buffer */ + b=buffer[com_dev][0]; + memmove(&buffer[com_dev][0],&buffer[com_dev][1],BUFFER_SIZE-1); + + return b; +} + +/** +* Get the number of bytes waiting in the buffer +* \param[in] port COM port +* \return Number of bytes used in buffer +*/ +int32_t PIOS_COM_ReceiveBufferUsed(uint8_t port) +{ + int com_dev,opts; + ssize_t read_bytes; + + com_dev = find_com_dev_by_id (port); + + if (com_dev==-1) { + /* Undefined COM port for this board (see pios_board.c) */ + return -1; + } + + /* fill buffer */ + opts = fcntl(fifos_out[com_dev],F_GETFL); + opts = opts | O_NONBLOCK; + fcntl(fifos_out[com_dev],F_SETFL, opts); + read_bytes=read(fifos_in[com_dev],buffer[com_dev]+buffer_fill[com_dev],BUFFER_SIZE-buffer_fill[com_dev]); + if (read_bytes<0) { + read_bytes=0; + } + buffer_fill[com_dev]+=read_bytes; + return buffer_fill[com_dev]; + +} + +#endif diff --git a/flight/PiOS.x86/x86/pios_delay.c b/flight/PiOS.x86/x86/pios_delay.c new file mode 100644 index 000000000..c295b4ac9 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_delay.c @@ -0,0 +1,106 @@ +/** + ****************************************************************************** + * + * @file pios_delay.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) + * @brief Delay Functions + * - Provides a micro-second granular delay using a TIM + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_DELAY Delay Functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_DELAY) + +/** +* Initialises the Timer used by PIOS_DELAY functions
+* This is called from pios.c as part of the main() function +* at system start up. +* \return < 0 if initialisation failed +*/ +#include + +int32_t PIOS_DELAY_Init(void) +{ + // stub + + /* No error */ + return 0; +} + +/** +* Waits for a specific number of uS
+* Example:
+* \code +* // Wait for 500 uS +* PIOS_DELAY_Wait_uS(500); +* \endcode +* \param[in] uS delay (1..65535 microseconds) +* \return < 0 on errors +*/ +int32_t PIOS_DELAY_WaituS(uint16_t uS) +{ + static struct timespec wait,rest; + wait.tv_sec=0; + wait.tv_nsec=1000*uS; + while (!nanosleep(&wait,&rest)) { + wait=rest; + } + //uint16_t start = PIOS_DELAY_TIMER->CNT; + + /* Note that this event works on 16bit counter wrap-arounds */ + //while((uint16_t)(PIOS_DELAY_TIMER->CNT - start) <= uS); + + /* No error */ + return 0; +} + + +/** +* Waits for a specific number of mS
+* Example:
+* \code +* // Wait for 500 mS +* PIOS_DELAY_Wait_mS(500); +* \endcode +* \param[in] mS delay (1..65535 milliseconds) +* \return < 0 on errors +*/ +int32_t PIOS_DELAY_WaitmS(uint16_t mS) +{ + //for(int i = 0; i < mS; i++) { + // PIOS_DELAY_WaituS(1000); + static struct timespec wait,rest; + wait.tv_sec=mS/1000; + wait.tv_nsec=(mS%1000)*1000000; + while (!nanosleep(&wait,&rest)) { + wait=rest; + } + //} + + /* No error */ + return 0; +} + +#endif diff --git a/flight/PiOS.x86/x86/pios_led.c b/flight/PiOS.x86/x86/pios_led.c new file mode 100644 index 000000000..b35477d45 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_led.c @@ -0,0 +1,102 @@ +/** + ****************************************************************************** + * + * @file pios_led.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * @brief LED functions, init, toggle, on & off. + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_LED LED Functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_LED) + +/* Private Function Prototypes */ + + +/* Local Variables */ +//static GPIO_TypeDef* LED_GPIO_PORT[PIOS_LED_NUM] = PIOS_LED_PORTS; +//static const uint32_t LED_GPIO_PIN[PIOS_LED_NUM] = PIOS_LED_PINS; +//static const uint32_t LED_GPIO_CLK[PIOS_LED_NUM] = PIOS_LED_CLKS; +static uint8_t LED_GPIO[PIOS_LED_NUM]; + + +static inline void PIOS_SetLED(LedTypeDef LED,uint8_t stat) { + printf("PIOS: LED %i status %i\n",LED,stat); + LED_GPIO[LED]=stat; +} + +/** +* Initialises all the LED's +*/ +void PIOS_LED_Init(void) +{ + //GPIO_InitTypeDef GPIO_InitStructure; + //GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP; + //GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz; + + for(int LEDNum = 0; LEDNum < PIOS_LED_NUM; LEDNum++) { + //RCC_APB2PeriphClockCmd(LED_GPIO_CLK[LEDNum], ENABLE); + //GPIO_InitStructure.GPIO_Pin = LED_GPIO_PIN[LEDNum]; + //GPIO_Init(LED_GPIO_PORT[LEDNum], &GPIO_InitStructure); + + /* LED's Off */ + //LED_GPIO_PORT[LEDNum]->BSRR = LED_GPIO_PIN[LEDNum]; + LED_GPIO[LEDNum]=0; + } +} + + +/** +* Turn on LED +* \param[in] LED LED Name (LED1, LED2) +*/ +void PIOS_LED_On(LedTypeDef LED) +{ + //LED_GPIO_PORT[LED]->BRR = LED_GPIO_PIN[LED]; + PIOS_SetLED(LED,1); +} + + +/** +* Turn off LED +* \param[in] LED LED Name (LED1, LED2) +*/ +void PIOS_LED_Off(LedTypeDef LED) +{ + //LED_GPIO_PORT[LED]->BSRR = LED_GPIO_PIN[LED]; + PIOS_SetLED(LED,0); +} + + +/** +* Toggle LED on/off +* \param[in] LED LED Name (LED1, LED2) +*/ +void PIOS_LED_Toggle(LedTypeDef LED) +{ + //LED_GPIO_PORT[LED]->ODR ^= LED_GPIO_PIN[LED]; + PIOS_SetLED(LED,LED_GPIO[LED]?0:1); +} + +#endif diff --git a/flight/PiOS.x86/x86/pios_sdcard.c b/flight/PiOS.x86/x86/pios_sdcard.c new file mode 100644 index 000000000..f588399f0 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_sdcard.c @@ -0,0 +1,300 @@ +/** + ****************************************************************************** + * + * @file pios_sdcard.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) + * @brief Sets up basic system hardware, functions are called from Main. + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_SDCARD SDCard Functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_SDCARD) + + +/** +* Initialises SPI pins and peripheral to access MMC/SD Card +* \param[in] mode currently only mode 0 supported +* \return < 0 if initialisation failed +*/ +int32_t PIOS_SDCARD_Init(void) +{ + /* No error */ + return 0; +} + + +/** +* Connects to SD Card +* \return < 0 if initialisation sequence failed +*/ +int32_t PIOS_SDCARD_PowerOn(void) +{ + return 0; /* Status should be 0 if nothing went wrong! */ +} + + +/** +* Disconnects from SD Card +* \return < 0 on errors +*/ +int32_t PIOS_SDCARD_PowerOff(void) +{ + return 0; // no error +} + + +/** +* If SD card was previously available: Checks if the SD Card is still +* available by sending the STATUS command.
+* This takes ca. 10 uS +* +* If SD card was previously not available: Checks if the SD Card is +* available by sending the IDLE command at low speed.
+* This takes ca. 500 uS!
+* Once we got a positive response, SDCARD_PowerOn() will be +* called by this function to initialize the card completely. +* +* Example for Connection/Disconnection detection: +* \code +* // this function is called each second from a low-priority task +* // If multiple tasks are accessing the SD card, add a semaphore/mutex +* // to avoid IO access collisions with other tasks! +* u8 sdcard_available; +* int32_t CheckSDCard(void) +* { +* // check if SD card is available +* // High speed access if the card was previously available +* u8 prev_sdcard_available = sdcard_available; +* sdcard_available = PIOS_SDCARD_CheckAvailable(prev_sdcard_available); +* +* if(sdcard_available && !prev_sdcard_available) { +* // SD Card has been connected +* +* // now it's possible to read/write sectors +* +* } else if( !sdcard_available && prev_sdcard_available ) { +* // SD Card has been disconnected +* +* // here you can notify your application about this state +* } +* +* return 0; // no error +* } +* \endcode +* \param[in] was_available should only be set if the SD card was previously available +* \return 0 if no response from SD Card +* \return 1 if SD card is accessible +*/ +int32_t PIOS_SDCARD_CheckAvailable(uint8_t was_available) +{ + return 1; /* 1 = available, 0 = not available. */ +} + + +/** +* Sends command to SD card +* \param[in] cmd SD card command +* \param[in] addr 32bit address +* \param[in] crc precalculated CRC +* \return >= 0x00 if command has been sent successfully (contains received byte) +* \return -1 if no response from SD Card (timeout) +*/ +int32_t PIOS_SDCARD_SendSDCCmd(uint8_t cmd, uint32_t addr, uint8_t crc) +{ + return -1; +} + + +/** +* Reads 512 bytes from selected sector +* \param[in] sector 32bit sector +* \param[in] *buffer pointer to 512 byte buffer +* \return 0 if whole sector has been successfully read +* \return -error if error occured during read operation:
+*

+* \return -256 if timeout during command has been sent +* \return -257 if timeout while waiting for start token +*/ +int32_t PIOS_SDCARD_SectorRead(uint32_t sector, uint8_t *buffer) +{ + return -256; +} + + +/** +* Writes 512 bytes into selected sector +* \param[in] sector 32bit sector +* \param[in] *buffer pointer to 512 byte buffer +* \return 0 if whole sector has been successfully read +* \return -error if error occured during read operation:
+*
    +*
  • Bit 0 - In idle state if 1 +*
  • Bit 1 - Erase Reset if 1 +*
  • Bit 2 - Illgal Command if 1 +*
  • Bit 3 - Com CRC Error if 1 +*
  • Bit 4 - Erase Sequence Error if 1 +*
  • Bit 5 - Address Error if 1 +*
  • Bit 6 - Parameter Error if 1 +*
  • Bit 7 - Not used, always '0' +*
+* \return -256 if timeout during command has been sent +* \return -257 if write operation not accepted +* \return -258 if timeout during write operation +*/ +int32_t PIOS_SDCARD_SectorWrite(uint32_t sector, uint8_t *buffer) +{ + return -256; +} + + +/** +* Reads the CID informations from SD Card +* \param[in] *cid pointer to buffer which holds the CID informations +* \return 0 if the informations haven been successfully read +* \return -error if error occured during read operation +* \return -256 if timeout during command has been sent +* \return -257 if timeout while waiting for start token +*/ +int32_t PIOS_SDCARD_CIDRead(SDCARDCidTypeDef *cid) +{ + return -256; +} + +/** +* Reads the CSD informations from SD Card +* \param[in] *csd pointer to buffer which holds the CSD informations +* \return 0 if the informations haven been successfully read +* \return -error if error occured during read operation +* \return -256 if timeout during command has been sent +* \return -257 if timeout while waiting for start token +*/ +int32_t PIOS_SDCARD_CSDRead(SDCARDCsdTypeDef *csd) +{ + return -256; +} + +/** +* Attempts to write a startup log to the SDCard +* return 0 No errors +* return -1 Error deleting file +* return -2 Error opening file +* return -3 Error writing file +*/ +int32_t PIOS_SDCARD_StartupLog(void) +{ + return -3; +} + +/** + * Check if the SD card has been mounted + * @return 0 if no + * @return 1 if yes + */ +int32_t POIS_SDCARD_IsMounted() +{ + return 1; +} + +/** +* Mounts the file system +* param[in] CreateStartupLog 1 = True, 0 = False +* return 0 No errors +* return -1 SDCard not available +* return -2 Cannot find first partition +* return -3 No volume information +* return -4 Error writing startup log file +*/ +int32_t PIOS_SDCARD_MountFS(uint32_t CreateStartupLog) +{ + return 0; +} + +/** +* Mounts the file system +* return Amount of free bytes +*/ +int32_t PIOS_SDCARD_GetFree(void) +{ + return 10240; +} + +/** +* Read from file +* return 0 No error +* return -1 DFS_ReadFile failed +* return -2 Less bytes read than expected +*/ +//int32_t PIOS_SDCARD_ReadBuffer(PFILEINFO fileinfo, uint8_t *buffer, uint32_t len) +//{ + /* No error */ +// return -1; +//} + +/** +* Read a line from file +* returns Number of bytes read +*/ +//int32_t PIOS_SDCARD_ReadLine(PFILEINFO fileinfo, uint8_t *buffer, uint32_t max_len) +//{ +// return -1; +//} + +/** +* Copy a file +* WARNING: This will overwrite the destination file even if it exists +* param[in] *Source Path to file to copy +* param[in] *Destination Path to destination file +* return 0 No errors +* return -1 Source file doesn't exist +* return -2 Failed to create destination file +* return -3 DFS_ReadFile failed +* return -4 DFS_WriteFile failed +*/ +int32_t PIOS_SDCARD_FileCopy(char *Source, char *Destination) +{ + return -2; +} + +/** +* Delete a file +* param[in] *Filename File to delete +* return 0 No errors +* return -1 Error deleting file +*/ +int32_t PIOS_SDCARD_FileDelete(char *Filename) +{ + return -1; +} + +#endif diff --git a/flight/PiOS.x86/x86/pios_sys.c b/flight/PiOS.x86/x86/pios_sys.c new file mode 100644 index 000000000..85cdc9727 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_sys.c @@ -0,0 +1,186 @@ +/** + ****************************************************************************** + * + * @file pios_sys.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) + * @brief Sets up basic system hardware, functions are called from Main. + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_SYS System Functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_SYS) + + +/* Private Function Prototypes */ +void NVIC_Configuration(void); +void SysTick_Handler(void); + +/* Local Macros */ +#define MEM8(addr) (*((volatile uint8_t *)(addr))) + +/** +* Initialises all system peripherals +*/ +void PIOS_SYS_Init(void) +{ + + /** + * stub + */ + printf("PIOS_SYS_Init\n"); + + /* Initialise Basic NVIC */ + NVIC_Configuration(); + +#if defined(PIOS_INCLUDE_LED) + /* Initialise LEDs */ + PIOS_LED_Init(); +#endif +} + +/** +* Shutdown PIOS and reset the microcontroller:
+*
    +*
  • Disable all RTOS tasks +*
  • Disable all interrupts +*
  • Turn off all board LEDs +*
  • Reset STM32 +*
+* \return < 0 if reset failed +*/ +int32_t PIOS_SYS_Reset(void) +{ + /** + * stub + */ + printf("PIOS_SYS_Reset\n"); + /* Disable all RTOS tasks */ +#if defined(PIOS_INCLUDE_FREERTOS) + /* port specific FreeRTOS function to disable tasks (nested) */ + portENTER_CRITICAL(); +#endif + + // disable all interrupts + //PIOS_IRQ_Disable(); + + // turn off all board LEDs + #if (PIOS_LED_NUM == 1) + PIOS_LED_Off(LED1); + #elif (PIOS_LED_NUM == 2) + PIOS_LED_Off(LED1); + PIOS_LED_Off(LED2); + #endif + + + + /* Reset STM32 */ + //RCC_APB2PeriphResetCmd(0xfffffff8, ENABLE); /* MBHP_CORE_STM32: don't reset GPIOA/AF due to USB pins */ + //RCC_APB1PeriphResetCmd(0xff7fffff, ENABLE); /* don't reset USB, so that the connection can survive! */ + + //RCC_APB2PeriphResetCmd(0xffffffff, DISABLE); + //RCC_APB1PeriphResetCmd(0xffffffff, DISABLE); + //SCB->AIRCR = NVIC_AIRCR_VECTKEY | (1 << NVIC_VECTRESET); + exit(1); + + while(1); + + /* We will never reach this point */ + return -1; +} + +/** +* Returns the serial number as a string +* param[out] str pointer to a string which can store at least 32 digits + zero terminator! +* (24 digits returned for STM32) +* return < 0 if feature not supported +*/ +int32_t PIOS_SYS_SerialNumberGet(char *str) +{ + int i; + + /* Stored in the so called "electronic signature" */ + for(i=0; i<24; ++i) { + //uint8_t b = MEM8(0x1ffff7e8 + (i/2)); + //if( !(i & 1) ) + //b >>= 4; + //b &= 0x0f; + + //str[i] = ((b > 9) ? ('A'-10) : '0') + b; + str[i]='6'; + } + str[i] = 0; + + /* No error */ + return 0; +} + +/** +* Configures Vector Table base location and SysTick +*/ +void NVIC_Configuration(void) +{ + /** + * stub + */ + printf("NVIC_Configuration\n"); + /* Set the Vector Table base address as specified in .ld file */ + //NVIC_SetVectorTable(PIOS_NVIC_VECTTAB_FLASH, 0x0); + + /* 4 bits for Interrupt priorities so no sub priorities */ + //NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); + + /* Configure HCLK clock as SysTick clock source. */ + //SysTick_CLKSourceConfig(SysTick_CLKSource_HCLK); +} + +#ifdef USE_FULL_ASSERT +/** +* Reports the name of the source file and the source line number +* where the assert_param error has occurred. +* \param[in] file pointer to the source file name +* \param[in] line assert_param error line source number +* \retval None +*/ +void assert_failed(uint8_t* file, uint32_t line) +{ + /* When serial debugging is implemented, use something like this. */ + /* printf("Wrong parameters value: file %s on line %d\r\n", file, line); */ + printf("Wrong parameters value: file %s on line %d\r\n", file, line); + + /* Setup the LEDs to Alternate */ + PIOS_LED_On(LED1); + PIOS_LED_Off(LED2); + + /* Infinite loop */ + while (1) + { + PIOS_LED_Toggle(LED1); + PIOS_LED_Toggle(LED2); + for(int i = 0; i < 1000000; i++); + } +} +#endif + +#endif diff --git a/flight/PiOS.x86/x86/pios_udp.c b/flight/PiOS.x86/x86/pios_udp.c new file mode 100644 index 000000000..f6167d0f7 --- /dev/null +++ b/flight/PiOS.x86/x86/pios_udp.c @@ -0,0 +1,460 @@ +/** + ****************************************************************************** + * + * @file pios_udp.c + * @author The OpenPilot Team, http://www.openpilot.org Copyright (C) 2010. + * Parts by Thorsten Klose (tk@midibox.org) (tk@midibox.org) + * @brief UDP commands. Inits UDPs, controls UDPs & Interupt handlers. + * @see The GNU Public License (GPL) Version 3 + * @defgroup PIOS_UDP UDP Functions + * @{ + * + *****************************************************************************/ +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* Project Includes */ +#include "pios.h" + +#if defined(PIOS_INCLUDE_UDP) + +#include + +/* Provide a COM driver */ +const struct pios_com_driver pios_udp_com_driver = { + .set_baud = PIOS_UDP_ChangeBaud, + .tx_nb = PIOS_UDP_TxBufferPutMoreNonBlocking, + .tx = PIOS_UDP_TxBufferPutMore, + .rx = PIOS_UDP_RxBufferGet, + .rx_avail = PIOS_UDP_RxBufferUsed, +}; + +static struct pios_udp_dev * find_udp_dev_by_id (uint8_t udp) +{ + if (udp >= pios_udp_num_devices) { + /* Undefined UDP port for this board (see pios_board.c) */ + return NULL; + } + + /* Get a handle for the device configuration */ + return &(pios_udp_devs[udp]); +} + +/** +* Open some UDP sockets +*/ +void PIOS_UDP_Init(void) +{ + struct pios_udp_dev * udp_dev; + uint8_t i; + + for (i = 0; i < pios_udp_num_devices; i++) { + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(i); + //PIOS_DEBUG_Assert(udp_dev); + + /* Clear buffer counters */ + udp_dev->rx.head = udp_dev->rx.tail = udp_dev->rx.size = 0; + + /* assign socket */ + udp_dev->socket = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP); + memset(&udp_dev->server,0,sizeof(udp_dev->server)); + memset(&udp_dev->client,0,sizeof(udp_dev->client)); + udp_dev->server.sin_family = AF_INET; + udp_dev->server.sin_addr.s_addr = inet_addr(udp_dev->cfg->ip); + udp_dev->server.sin_port = htons(udp_dev->cfg->port); + int res= bind(udp_dev->socket, (struct sockaddr *)&udp_dev->server,sizeof(udp_dev->server)); + /* use nonblocking IO */ + int flags = fcntl(udp_dev->socket, F_GETFL, 0); + fcntl(udp_dev->socket, F_SETFL, flags | O_NONBLOCK); + printf("udp dev %i - socket %i opened - result %i\n",i,udp_dev->socket,res); + + /* TODO do some error handling - wait no, we can't - we are void anyway ;) */ + } +} + + +/** +* Changes the baud rate of the UDP peripheral without re-initialising. +* \param[in] udp UDP name (GPS, TELEM, AUX) +* \param[in] baud Requested baud rate +*/ +void PIOS_UDP_ChangeBaud(uint8_t udp, uint32_t baud) +{ +} + + +/** +* puts a byte onto the receive buffer +* \param[in] UDP UDP name +* \param[in] b byte which should be put into Rx buffer +* \return 0 if no error +* \return -1 if UDP not available +* \return -2 if buffer full (retry) +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_RxBufferPut(uint8_t udp, uint8_t b) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -1; + } + + if (udp_dev->rx.size >= sizeof(udp_dev->rx.buf)) { + /* Buffer full (retry) */ + return -2; + } + + /* Copy received byte into receive buffer */ + udp_dev->rx.buf[udp_dev->rx.head++] = b; + if (udp_dev->rx.head >= sizeof(udp_dev->rx.buf)) { + udp_dev->rx.head = 0; + } + udp_dev->rx.size++; + + /* No error */ + return 0; +} + +/** + * attempt to receive + */ +void PIOS_UDP_RECV(uint8_t udp) { + + struct pios_udp_dev * udp_dev; + unsigned char localbuffer[PIOS_UDP_RX_BUFFER_SIZE]; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return; + } + + /* use nonblocking IO */ + int flags = fcntl(udp_dev->socket, F_GETFL, 0); + fcntl(udp_dev->socket, F_SETFL, flags | O_NONBLOCK); + + /* receive data */ + int received; + udp_dev->clientLength=sizeof(udp_dev->client); + if ((received = recvfrom(udp_dev->socket, + localbuffer, + (PIOS_UDP_RX_BUFFER_SIZE - udp_dev->rx.size), + 0, + (struct sockaddr *) &udp_dev->client, + &udp_dev->clientLength)) < 0) { + + return; + } + /* copy received data to buffer */ + int t; + for (t=0;trx.buf) - udp_dev->rx.size); +} + +/** +* Returns number of used bytes in receive buffer +* \param[in] UDP UDP name +* \return > 0: number of used bytes +* \return 0 if UDP not available +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_RxBufferUsed(uint8_t udp) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -2; + } + + /* fill buffer */ + PIOS_UDP_RECV(udp); + + return (udp_dev->rx.size); +} + +/** +* Gets a byte from the receive buffer +* \param[in] UDP UDP name +* \return -1 if UDP not available +* \return -2 if no new byte available +* \return >= 0: number of received bytes +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_RxBufferGet(uint8_t udp) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -2; + } + + /* fill buffer */ + PIOS_UDP_RECV(udp); + + if (!udp_dev->rx.size) { + /* Nothing new in the buffer */ + return -1; + } + + /* get byte */ + uint8_t b = udp_dev->rx.buf[udp_dev->rx.tail++]; + if (udp_dev->rx.tail >= sizeof(udp_dev->rx.buf)) { + udp_dev->rx.tail = 0; + } + udp_dev->rx.size--; + + /* Return received byte */ + return b; +} + +/** +* Returns the next byte of the receive buffer without taking it +* \param[in] UDP UDP name +* \return -1 if UDP not available +* \return -2 if no new byte available +* \return >= 0: number of received bytes +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_RxBufferPeek(uint8_t udp) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -2; + } + + /* fill buffer */ + PIOS_UDP_RECV(udp); + + if (!udp_dev->rx.size) { + /* Nothing new in the buffer */ + return -1; + } + + /* get byte */ + uint8_t b = udp_dev->rx.buf[udp_dev->rx.tail]; + + /* Return received byte */ + return b; +} + +/** +* returns number of free bytes in transmit buffer +* \param[in] UDP UDP name +* \return number of free bytes +* \return 0 if UDP not available +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferFree(uint8_t udp) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return 0; + } + + return PIOS_UDP_RX_BUFFER_SIZE; +} + +/** +* returns number of used bytes in transmit buffer +* \param[in] UDP UDP name +* \return number of used bytes +* \return 0 if UDP not available +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferUsed(uint8_t udp) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return 0; + } + + return 0; +} + + +/** +* puts more than one byte onto the transmit buffer (used for atomic sends) +* \param[in] UDP UDP name +* \param[in] *buffer pointer to buffer to be sent +* \param[in] len number of bytes to be sent +* \return 0 if no error +* \return -1 if UDP not available +* \return -2 if buffer full or cannot get all requested bytes (retry) +* \return -3 if UDP not supported by UDPTxBufferPut Routine +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferPutMoreNonBlocking(uint8_t udp, uint8_t *buffer, uint16_t len) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -1; + } + + if (len >= PIOS_UDP_RX_BUFFER_SIZE) { + /* Buffer cannot accept all requested bytes (retry) */ + return -2; + } + /* send data to client - non blocking*/ + + /* use nonblocking IO */ + int flags = fcntl(udp_dev->socket, F_GETFL, 0); + fcntl(udp_dev->socket, F_SETFL, flags | O_NONBLOCK); + sendto(udp_dev->socket, buffer, len, 0, + (struct sockaddr *) &udp_dev->client, + sizeof(udp_dev->client)); + + + /* No error */ + return 0; +} + +/** +* puts more than one byte onto the transmit buffer (used for atomic sends)
+* (blocking function) +* \param[in] UDP UDP name +* \param[in] *buffer pointer to buffer to be sent +* \param[in] len number of bytes to be sent +* \return 0 if no error +* \return -1 if UDP not available +* \return -3 if UDP not supported by UDPTxBufferPut Routine +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferPutMore(uint8_t udp, uint8_t *buffer, uint16_t len) +{ + struct pios_udp_dev * udp_dev; + + /* Get a handle for the device configuration */ + udp_dev = find_udp_dev_by_id(udp); + + if (!udp_dev) { + /* Undefined UDP port for this board (see pios_board.c) */ + return -1; + } + + if (len >= PIOS_UDP_RX_BUFFER_SIZE) { + /* Buffer cannot accept all requested bytes (retry) */ + return -2; + } + + /* send data to client - blocking*/ + /* use blocking IO */ + int flags = fcntl(udp_dev->socket, F_GETFL, 0); + fcntl(udp_dev->socket, F_SETFL, flags & ~O_NONBLOCK); + sendto(udp_dev->socket, buffer, len, 0, + (struct sockaddr *) &udp_dev->client, + sizeof(udp_dev->client)); + + /* No error */ + return 0; +} + +/** +* puts a byte onto the transmit buffer +* \param[in] UDP UDP name +* \param[in] b byte which should be put into Tx buffer +* \return 0 if no error +* \return -1 if UDP not available +* \return -2 if buffer full (retry) +* \return -3 if UDP not supported by UDPTxBufferPut Routine +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferPut_NonBlocking(uint8_t udp, uint8_t b) +{ + return PIOS_UDP_TxBufferPutMoreNonBlocking(udp, &b, 1); +} + +/** +* puts a byte onto the transmit buffer
+* (blocking function) +* \param[in] UDP UDP name +* \param[in] b byte which should be put into Tx buffer +* \return 0 if no error +* \return -1 if UDP not available +* \return -3 if UDP not supported by UDPTxBufferPut Routine +* \note Applications shouldn't call these functions directly, instead please use \ref PIOS_COM layer functions +*/ +int32_t PIOS_UDP_TxBufferPut(uint8_t udp, uint8_t b) +{ + return PIOS_UDP_TxBufferPutMore(udp, &b, 1); +} + + +#endif