From 3d853fb851742343e7493489e0551c9b510c4c44 Mon Sep 17 00:00:00 2001 From: Frank Voorburg Date: Mon, 16 Oct 2017 12:41:14 +0000 Subject: [PATCH] Refs #317. Added USB support to LibOpenBLT and BootCommander. Use LibOpenBLT shared library in BootCommander instead of the static library. Prepared OpenBLT version number for new patch release. git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@376 5dc33758-31d5-4daf-9ae8-b24bf3d40d73 --- Doc/doxygen/DoxyfileBootCommander | 2 +- Doc/doxygen/DoxyfileLibOpenBLT | 2 +- Doc/doxygen/DoxyfileOpenBLT | 2 +- Host/BootCommander.exe | Bin 164864 -> 54784 bytes Host/Source/BootCommander/CMakeLists.txt | 26 +- Host/Source/BootCommander/main.c | 30 +- Host/Source/LibOpenBLT/CMakeLists.txt | 24 +- Host/Source/LibOpenBLT/lint/gnu/co-gcc.lnt | 1 + Host/Source/LibOpenBLT/openblt.c | 57 ++- Host/Source/LibOpenBLT/openblt.h | 5 + Host/Source/LibOpenBLT/openblt.pas | 1 + Host/Source/LibOpenBLT/port/linux/usbbulk.c | 366 +++++++++++++++++ Host/Source/LibOpenBLT/port/windows/usbbulk.c | 374 ++++++++++++++++++ Host/Source/LibOpenBLT/usbbulk.h | 51 +++ Host/Source/LibOpenBLT/xcptpusb.c | 229 +++++++++++ Host/Source/LibOpenBLT/xcptpusb.h | 50 +++ Host/libopenblt.dll | Bin 142336 -> 149504 bytes Target/Source/boot.h | 2 +- 18 files changed, 1181 insertions(+), 41 deletions(-) create mode 100644 Host/Source/LibOpenBLT/port/linux/usbbulk.c create mode 100644 Host/Source/LibOpenBLT/port/windows/usbbulk.c create mode 100644 Host/Source/LibOpenBLT/usbbulk.h create mode 100644 Host/Source/LibOpenBLT/xcptpusb.c create mode 100644 Host/Source/LibOpenBLT/xcptpusb.h diff --git a/Doc/doxygen/DoxyfileBootCommander b/Doc/doxygen/DoxyfileBootCommander index bc582527..68b703f4 100644 --- a/Doc/doxygen/DoxyfileBootCommander +++ b/Doc/doxygen/DoxyfileBootCommander @@ -38,7 +38,7 @@ PROJECT_NAME = "BootCommander - Reference Manual" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.1.0 +PROJECT_NUMBER = 1.2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/Doc/doxygen/DoxyfileLibOpenBLT b/Doc/doxygen/DoxyfileLibOpenBLT index 2b746ce2..d6567373 100644 --- a/Doc/doxygen/DoxyfileLibOpenBLT +++ b/Doc/doxygen/DoxyfileLibOpenBLT @@ -38,7 +38,7 @@ PROJECT_NAME = "OpenBLT Host Library - Reference Manual" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.1.0 +PROJECT_NUMBER = 1.2.0 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/Doc/doxygen/DoxyfileOpenBLT b/Doc/doxygen/DoxyfileOpenBLT index 7cffbcaa..7791e3d5 100644 --- a/Doc/doxygen/DoxyfileOpenBLT +++ b/Doc/doxygen/DoxyfileOpenBLT @@ -38,7 +38,7 @@ PROJECT_NAME = "OpenBLT Bootloader - Reference Manual" # could be handy for archiving the generated documentation or if some version # control system is used. -PROJECT_NUMBER = 1.4.1 +PROJECT_NUMBER = 1.4.2 # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a diff --git a/Host/BootCommander.exe b/Host/BootCommander.exe index 87ebb17318323d2f6359947111825b508486674b..9b0e9b2df732452d113993673b1535de8a48f643 100644 GIT binary patch delta 16152 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 HSLFW#N-d~r literal 164864 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%0D|313yI5NZ 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 diff --git a/Host/Source/BootCommander/CMakeLists.txt b/Host/Source/BootCommander/CMakeLists.txt index 0e5d6754..e4795205 100644 --- a/Host/Source/BootCommander/CMakeLists.txt +++ b/Host/Source/BootCommander/CMakeLists.txt @@ -69,6 +69,7 @@ endforeach( OUTPUTCONFIG CMAKE_CONFIGURATION_TYPES ) set(LIBOPENBLT_INC ${PROJECT_SOURCE_DIR}/../LibOpenBLT) set(LIBOPENBLT_LIB ${PROJECT_OUTPUT_DIRECTORY}) + #**************************************************************************************** # Compiler flags #**************************************************************************************** @@ -105,11 +106,11 @@ set( ${INCS} ) -# Set library name. When GNU GCC is used, the name to the static library file is used, -# otherwise the shared library is used by default. This works fine too, but the static -# library makes it a bit easier to move the target executable around on the file system. -if(CMAKE_C_COMPILER_ID MATCHES GNU) - set (LIBOPENBLT_LIBNAME libopenblt.a) +# Set library name of the OpenBLT Host Library +if(CMAKE_C_COMPILER_ID MATCHES MSVC) + # When building LibOpenBLT with Microsoft Visual Studio, "lib" was added to the name + # of the DLL. This needs to be done as well when linking the library. + set (LIBOPENBLT_LIBNAME libopenblt) else() set (LIBOPENBLT_LIBNAME openblt) endif() @@ -124,8 +125,19 @@ add_executable( ${PROG_SRCS} ) -# Add libraries -target_link_libraries(BootCommander ${LIBOPENBLT_LIBNAME}) +# Add libraries. Note that when compiling with GNU GCC the path of the executable is +# added to the run-time search path for libraries. This way LibOpenBLT's shared +# object file (.so) does not have to be installed in the Linux's library path as long +# as it is located in the same directory as BootCommander's executable. +if(CMAKE_C_COMPILER_ID MATCHES GNU) + # According to the CMake docs, item names starting with '-', but not '-l' or + # '-framework', are treated as linker flags. This means "-Wl" type linker flags can be + # specified here. + target_link_libraries(BootCommander ${LIBOPENBLT_LIBNAME} "-Wl,-rpath,.") +elseif(CMAKE_C_COMPILER_ID MATCHES MSVC) + target_link_libraries(BootCommander ${LIBOPENBLT_LIBNAME}) +endif() + # Only generate the PC-lint taget if the option is enabled. Use "make BootCommander_LINT" # to lint the project sources diff --git a/Host/Source/BootCommander/main.c b/Host/Source/BootCommander/main.c index f2a8d4e0..8a45ef19 100644 --- a/Host/Source/BootCommander/main.c +++ b/Host/Source/BootCommander/main.c @@ -160,8 +160,13 @@ int main(int argc, char const * const argv[]) appTransportType); /* Extract the firmware filename from the command line. */ appFirmwareFile = ExtractFirmwareFileFromCommandLine(argc, argv); + /* Note that the transport settings are allowed to be NULL in case of + * BLT_TRANSPORT_XCP_V10_USB. + */ + bool appTransportSettingsOkay = (appTransportType == BLT_TRANSPORT_XCP_V10_USB) ? + true : (appTransportSettings != NULL); /* Check the settings that were detected so far. */ - if ( (appSessionSettings == NULL) || (appTransportSettings == NULL) || + if ( (appSessionSettings == NULL) || (!appTransportSettingsOkay) || (appFirmwareFile == NULL) ) { /* Display program info */ @@ -430,7 +435,7 @@ int main(int argc, char const * const argv[]) static void DisplayProgramInfo(void) { printf("--------------------------------------------------------------------------\n"); - printf("BootCommander version 1.01. Performs firmware updates on a microcontroller\n"); + printf("BootCommander version 1.02. Performs firmware updates on a microcontroller\n"); printf("based system that runs the OpenBLT bootloader.\n\n"); printf("Copyright (c) 2017 by Feaser http://www.feaser.com\n"); printf("-------------------------------------------------------------------------\n"); @@ -454,6 +459,7 @@ static void DisplayProgramUsage(void) printf(" -t=[name] Name of the communication transport layer:\n"); printf(" xcp_rs232 (default) -> XCP on RS232.\n"); printf(" xcp_can -> XCP on CAN.\n"); + printf(" xcp_usb -> XCP on USB.\n"); printf("\n"); printf("XCP version 1.0 settings (xcp):\n"); printf(" -t1=[timeout] Command response timeout in milliseconds as a 16-bit\n"); @@ -502,6 +508,9 @@ static void DisplayProgramUsage(void) printf(" as 29-bit CAN identifiers, if this 8-bit value is > 0\n"); printf(" (Default = 0).\n"); printf("\n"); + printf("XCP on USB settings (xcp_usb):\n"); + printf(" No additional settings needed.\n"); + printf("\n"); printf("Program settings:\n"); printf(" -sm Silent mode switch. When specified, only minimal\n"); printf(" information is written to the output (Optional).\n"); @@ -594,6 +603,9 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS case BLT_TRANSPORT_XCP_V10_CAN: printf("XCP on CAN\n"); break; + case BLT_TRANSPORT_XCP_V10_USB: + printf("XCP on USB\n"); + break; default: printf("Unknown\n"); break; @@ -671,6 +683,11 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS } break; } + case BLT_TRANSPORT_XCP_V10_USB: + { + printf(" -> No additional settings required.\n"); + break; + } default: printf(" -> No settings specified\n"); break; @@ -956,7 +973,8 @@ static uint32_t ExtractTransportTypeFromCommandLine(int argc, char const * const } transportMap[] = { { .name = "xcp_rs232", .value = BLT_TRANSPORT_XCP_V10_RS232 }, - { .name = "xcp_can", .value = BLT_TRANSPORT_XCP_V10_CAN } + { .name = "xcp_can", .value = BLT_TRANSPORT_XCP_V10_CAN }, + { .name = "xcp_usb", .value = BLT_TRANSPORT_XCP_V10_USB } }; /* Set the default transport type in case nothing was specified on the command line. */ @@ -1158,6 +1176,12 @@ static void * ExtractTransportSettingsFromCommandLine(int argc, } } break; + /* -------------------------- XCP on USB --------------------------------------- */ + case BLT_TRANSPORT_XCP_V10_USB: + /* No additional command line parameters are neede for the USB transport + * layer. + */ + break; /* -------------------------- Unknown ------------------------------------------ */ default: /* Noting to extract. */ diff --git a/Host/Source/LibOpenBLT/CMakeLists.txt b/Host/Source/LibOpenBLT/CMakeLists.txt index 2fb0a68c..5e8e51d5 100644 --- a/Host/Source/LibOpenBLT/CMakeLists.txt +++ b/Host/Source/LibOpenBLT/CMakeLists.txt @@ -41,16 +41,16 @@ set(CMAKE_BUILD_TYPE "Debug") #**************************************************************************************** # Options #**************************************************************************************** -# Add option with default value to enable the generation and building of the static -# library. It can be overridden on the command line when CMake is called using the -# following parameter: -DBUILD_STATIC=OFF -option(BUILD_STATIC "Configurable to enable/disable building of the static library" ON) - # Add option with default value to enable the generation and building of the shared -# library. It can be overridden on the command line when CMake is called using the -# following parameter: -DBUILD_SHARED=OFF +# library. By default it is turned on. It can be overridden on the command line when +# CMake is called using the following parameter: -DBUILD_SHARED=OFF option(BUILD_SHARED "Configurable to enable/disable building of the shared library" ON) +# Add option with default value to enable the generation and building of the static +# library. By default it is turned off. It can be overridden on the command line when +# CMake is called using the following parameter: -DBUILD_STATIC=ON +option(BUILD_STATIC "Configurable to enable/disable building of the static library" OFF) + # Add option with default value to disable the generation of the PC-lint target. It can # be overridden on the command line when CMake is called using the following parameter: # -DLINT_ENABLED=ON @@ -131,6 +131,9 @@ set( #**************************************************************************************** # Only generate the static library taget if the option is enabled. Use # "make openblt_static" to individually build the static library. +# Note that when you link your own application to the static library of LibOpenBLT under +# Unix, you need to also link the LibUsb library by adding usb-1.0 to the linker library +# dependencies. if(BUILD_STATIC) add_library(openblt_static STATIC ${LIB_SRCS}) SET_TARGET_PROPERTIES(openblt_static PROPERTIES OUTPUT_NAME openblt CLEAN_DIRECT_OUTPUT 1) @@ -140,6 +143,13 @@ endif(BUILD_STATIC) # "make openblt_shared" to build individually shared library. if(BUILD_SHARED) add_library(openblt_shared SHARED ${LIB_SRCS}) + if(UNIX) + # Under Unix the LibUsb library (http://libusb.info/) is needed for the USB support. + # Make sure the libusb-1.0-0 and libusb-1.0-0-dev packages are installed to be able to + # build LibOpenBLT. Example under Debian/Ubuntu: + # sudo apt-get install libusb-1.0-0 libusb-1.0-0-dev + target_link_libraries(openblt_shared usb-1.0) + endif(UNIX) if(CMAKE_C_COMPILER_ID MATCHES MSVC) # Microsoft Visual Studio does not add "lib" to the name of the DLL, whereas GCC # (including MinGW) does. Correct this here. diff --git a/Host/Source/LibOpenBLT/lint/gnu/co-gcc.lnt b/Host/Source/LibOpenBLT/lint/gnu/co-gcc.lnt index 2132dc7b..3bc57762 100644 --- a/Host/Source/LibOpenBLT/lint/gnu/co-gcc.lnt +++ b/Host/Source/LibOpenBLT/lint/gnu/co-gcc.lnt @@ -179,6 +179,7 @@ size-options.lnt // This .lnt file should be generated (preferrably -elib(93) // allow newlines within quoted string arguments to macros -elib(46) // allow bit fields to have integral types other than // '_Bool' and 'int'. +-elibsym(793) // suppress warning about limit of 31 significant characters -elibsym(628) // Suppress 628 for __builtin symbols. -esym(528,__huge_val,__nan,__qnan,__qnanf,__snan,__snanf) diff --git a/Host/Source/LibOpenBLT/openblt.c b/Host/Source/LibOpenBLT/openblt.c index 1b7304df..f771d436 100644 --- a/Host/Source/LibOpenBLT/openblt.c +++ b/Host/Source/LibOpenBLT/openblt.c @@ -40,6 +40,7 @@ #include "xcploader.h" /* XCP loader module */ #include "xcptpuart.h" /* XCP UART transport layer */ #include "xcptpcan.h" /* XCP CAN transport layer */ +#include "xcptpusb.h" /* XCP USB transport layer */ /**************************************************************************************** @@ -49,10 +50,10 @@ * for major-, minor-, and patch-version. Version 1.05.12 would for example be * 10512. */ -#define BLT_VERSION_NUMBER (10100u) +#define BLT_VERSION_NUMBER (10200u) /** \brief The version number of the library as a null-terminated string. */ -#define BLT_VERSION_STRING "1.01.00" +#define BLT_VERSION_STRING "1.02.00" /**************************************************************************************** @@ -117,7 +118,8 @@ LIBOPENBLT_EXPORT void BltSessionInit(uint32_t sessionType, */ assert(sessionType == BLT_SESSION_XCP_V10); assert( (transportType == BLT_TRANSPORT_XCP_V10_RS232) || \ - (transportType == BLT_TRANSPORT_XCP_V10_CAN) ); + (transportType == BLT_TRANSPORT_XCP_V10_CAN) || \ + (transportType == BLT_TRANSPORT_XCP_V10_USB) ); /* Initialize the correct session. */ if (sessionType == BLT_SESSION_XCP_V10) /*lint !e774 */ @@ -169,25 +171,40 @@ LIBOPENBLT_EXPORT void BltSessionInit(uint32_t sessionType, } else if (transportType == BLT_TRANSPORT_XCP_V10_CAN) { - /* Cast transport settings to the correct type. */ - tBltTransportSettingsXcpV10Can * bltTransportSettingsXcpV10CanPtr; - bltTransportSettingsXcpV10CanPtr = - (tBltTransportSettingsXcpV10Can * )transportSettings; - /* Convert transport settings to the format supported by the XCP CAN transport - * layer. It was made static to make sure it doesn't get out of scope when - * used in xcpLoaderSettings. - */ - static tXcpTpCanSettings xcpTpCanSettings; - xcpTpCanSettings.device = bltTransportSettingsXcpV10CanPtr->deviceName; - xcpTpCanSettings.channel = bltTransportSettingsXcpV10CanPtr->deviceChannel; - xcpTpCanSettings.baudrate = bltTransportSettingsXcpV10CanPtr->baudrate; - xcpTpCanSettings.transmitId = bltTransportSettingsXcpV10CanPtr->transmitId; - xcpTpCanSettings.receiveId = bltTransportSettingsXcpV10CanPtr->receiveId; - xcpTpCanSettings.useExtended = (bltTransportSettingsXcpV10CanPtr->useExtended != 0); + /* Verify transportSettings parameters because the XCP CAN transport layer + * requires them. + */ + assert(transportSettings != NULL); + /* Only continue if the transportSettings parameter is valid. */ + if (transportSettings != NULL) /*lint !e774 */ + { + /* Cast transport settings to the correct type. */ + tBltTransportSettingsXcpV10Can * bltTransportSettingsXcpV10CanPtr; + bltTransportSettingsXcpV10CanPtr = + (tBltTransportSettingsXcpV10Can *)transportSettings; + /* Convert transport settings to the format supported by the XCP CAN transport + * layer. It was made static to make sure it doesn't get out of scope when + * used in xcpLoaderSettings. + */ + static tXcpTpCanSettings xcpTpCanSettings; + xcpTpCanSettings.device = bltTransportSettingsXcpV10CanPtr->deviceName; + xcpTpCanSettings.channel = bltTransportSettingsXcpV10CanPtr->deviceChannel; + xcpTpCanSettings.baudrate = bltTransportSettingsXcpV10CanPtr->baudrate; + xcpTpCanSettings.transmitId = bltTransportSettingsXcpV10CanPtr->transmitId; + xcpTpCanSettings.receiveId = bltTransportSettingsXcpV10CanPtr->receiveId; + xcpTpCanSettings.useExtended = (bltTransportSettingsXcpV10CanPtr->useExtended != 0); + /* Store transport layer settings in the XCP loader settings. */ + xcpLoaderSettings.transportSettings = &xcpTpCanSettings; + /* Link the transport layer to the XCP loader settings. */ + xcpLoaderSettings.transport = XcpTpCanGetTransport(); + } + } + else if (transportType == BLT_TRANSPORT_XCP_V10_USB) + { /* Store transport layer settings in the XCP loader settings. */ - xcpLoaderSettings.transportSettings = &xcpTpCanSettings; + xcpLoaderSettings.transportSettings = NULL; /* Link the transport layer to the XCP loader settings. */ - xcpLoaderSettings.transport = XcpTpCanGetTransport(); + xcpLoaderSettings.transport = XcpTpUsbGetTransport(); } /* Perform actual session initialization. */ SessionInit(XcpLoaderGetProtocol(), &xcpLoaderSettings); diff --git a/Host/Source/LibOpenBLT/openblt.h b/Host/Source/LibOpenBLT/openblt.h index f3aacada..e6010c63 100644 --- a/Host/Source/LibOpenBLT/openblt.h +++ b/Host/Source/LibOpenBLT/openblt.h @@ -103,6 +103,11 @@ LIBOPENBLT_EXPORT char const * BltVersionGetString(void); */ #define BLT_TRANSPORT_XCP_V10_CAN ((uint32_t)1u) +/** \brief Transport layer for the XCP v1.0 protocol that uses USB Bulk for data + * exchange. + */ +#define BLT_TRANSPORT_XCP_V10_USB ((uint32_t)2u) + /**************************************************************************************** * Type definitions diff --git a/Host/Source/LibOpenBLT/openblt.pas b/Host/Source/LibOpenBLT/openblt.pas index 32ec8f32..7d7bcd64 100644 --- a/Host/Source/LibOpenBLT/openblt.pas +++ b/Host/Source/LibOpenBLT/openblt.pas @@ -73,6 +73,7 @@ const // Transport layer for the XCP v1.0 protocol that uses Controller Area Network (CAN) // for data exchange. BLT_TRANSPORT_XCP_V10_CAN: LongWord = 1; + BLT_TRANSPORT_XCP_V10_USB: LongWord = 2; type // Structure layout of the XCP version 1.0 session settings. diff --git a/Host/Source/LibOpenBLT/port/linux/usbbulk.c b/Host/Source/LibOpenBLT/port/linux/usbbulk.c new file mode 100644 index 00000000..bb686184 --- /dev/null +++ b/Host/Source/LibOpenBLT/port/linux/usbbulk.c @@ -0,0 +1,366 @@ +/************************************************************************************//** +* \file port/linux/usbbulk.c +* \brief USB bulk driver source file. +* \ingroup XcpTpUsb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT 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. +* +* OpenBLT 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 have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include /* for assertions */ +#include /* for standard integer types */ +#include /* for NULL declaration */ +#include /* for boolean type */ +#include "usbbulk.h" /* USB bulk driver */ +#include /* LibUsb 1.0 driver */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Size of the internal endpoint read buffer. This should be the same as the size + * of the buffer size of the endpoint on the USB device itself. + */ +#define USBBULK_READ_DATA_BUFFER_SIZE (64u) + + +/**************************************************************************************** +* Local constant declarations +****************************************************************************************/ +/** \brief Vendor ID of the OpenBLT bootloader as assigned by the OpenMoko project. */ +static const uint16_t openBltVendorId = 0x1D50; + +/** \brief Product ID of the OpenBLT bootloader as assigned by the OpenMoko project. */ +static const uint16_t openBltProductId = 0x60AC; + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief LibUsb context. */ +static libusb_context * libUsbCtx; + +/** \brief LibUsb device handle. */ +static libusb_device_handle * libUsbDevHandle; + +/** \brief Internal endpoint read buffer. With LibUsb endpoint read operations should + * always be attempted with the size of the endpoint buffer on the USB device + * itself. + */ +static uint8_t readDataBuffer[USBBULK_READ_DATA_BUFFER_SIZE]; + +/** \brief Variable that holds the number of bytes that were read from the endpoint, but + * were not yet retrieved from this module via UsbBulkRead(). + */ +static uint8_t readDataPending; + +/** \brief Index into the endpoint read buffer (readDataBuffer[]) that point to the next + * byte value that should be read. + */ +static uint8_t readDataCurrentReadIdx; + + +/************************************************************************************//** +** \brief Initializes the USB bulk driver. +** +****************************************************************************************/ +void UsbBulkInit(void) +{ + /* Initialize locals. */ + libUsbCtx = NULL; + libUsbDevHandle = NULL; + readDataPending = 0; + readDataCurrentReadIdx = 0; + + /* Initialize LibUsb. */ + if (libusb_init(&libUsbCtx) != (int)LIBUSB_SUCCESS) + { + /* Error occurred while initializing LibUsb. Invalidate the context. */ + libUsbCtx = NULL; + } + /* Only continue initialization with a valid context. */ + if (libUsbCtx != NULL) + { + /* Set the log message verbosity as advised by the documentation. */ + libusb_set_debug(libUsbCtx, (int)LIBUSB_LOG_LEVEL_WARNING); + } +} /*** end of UsbBulkInit ***/ + + +/************************************************************************************//** +** \brief Terminates the USB bulk driver. +** +****************************************************************************************/ +void UsbBulkTerminate(void) +{ + /* Terminate LibUsb. */ + if (libUsbCtx != NULL) + { + libusb_exit(libUsbCtx); + /* Invalidate the context. */ + libUsbCtx = NULL; + } +} /*** end of UsbBulkTerminate ***/ + + +/************************************************************************************//** +** \brief Opens the connection with the USB device. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkOpen(void) +{ + bool result = false; + + /* Reset read buffer management variables. */ + readDataPending = 0; + readDataCurrentReadIdx = 0; + + /* Only continue with a valid context. */ + if (libUsbCtx != NULL) + { + /* Set the result to okay and only update it in case of an error from here on. */ + result = true; + /* Attempt to open the USB device. */ + libUsbDevHandle = libusb_open_device_with_vid_pid(libUsbCtx, openBltVendorId, + openBltProductId); + /* Check if the USB device could be opened. */ + if (libUsbDevHandle == NULL) + { + /* Update result. */ + result = false; + } + /* Before the USB device can be claimed, it cannot be attached to a kernel driver. */ + if (result) + { + if (libusb_kernel_driver_active(libUsbDevHandle, 0) == 1) + { + /* Kernel driver active, so detach it. */ + (void)libusb_detach_kernel_driver(libUsbDevHandle, 0); + } + } + /* Attempt to claim the interface. */ + if (result) + { + if (libusb_claim_interface(libUsbDevHandle, 0) != (int)LIBUSB_SUCCESS) + { + result = false; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkOpen ***/ + + +/************************************************************************************//** +** \brief Closes the connection with the USB device. +** +****************************************************************************************/ +void UsbBulkClose(void) +{ + /* Release the interface and close the connection with the USB device. */ + if (libUsbDevHandle != NULL) + { + (void)libusb_release_interface(libUsbDevHandle, 0); + libusb_close(libUsbDevHandle); + /* Invalidate the handler. */ + libUsbDevHandle = NULL; + } +} /*** end of UsbBulkClose ***/ + + +/************************************************************************************//** +** \brief Writes data to the USB device. +** \param data Pointer to byte array with data to write. +** \param length Number of bytes in the data array. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkWrite(uint8_t const * data, uint16_t length) +{ + bool result = false; + + /* Check parameters. */ + assert(data != NULL); + assert(length > 0); + + /* Only continue with valid parameters. */ + if ((data != NULL) && (length > 0)) /*lint !e774 */ + { + /* Only continue with a valid context and device handle. */ + if ( (libUsbDevHandle != NULL) && (libUsbCtx != NULL) ) + { + /* Attempt to write the data to the endpoint. */ + int bytesWritten = 0; + int transferResult = libusb_bulk_transfer(libUsbDevHandle, 0x01, (uint8_t *)data, + (int)length, &bytesWritten, 0); + /* Check the transfer result. */ + if (transferResult == (int)LIBUSB_SUCCESS) + { + /* Check if all data was written. */ + if (bytesWritten == length) + { + /* Data successfully written the endpoint. */ + result = true; + } + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkWrite ***/ + + +/************************************************************************************//** +** \brief Reads data from the USB device. +** \param data Pointer to byte array where received data should be stored. +** \param length Number of bytes to read from the USB device. +** \param timeout Timeout in milliseconds for the read operation. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkRead(uint8_t * data, uint16_t length, uint32_t timeout) +{ + bool result = false; + + /* Check parameters. */ + assert(data != NULL); + assert(length > 0); + assert(length <= USBBULK_READ_DATA_BUFFER_SIZE); + + /* Only continue with valid parameters. */ + if ((data != NULL) && (length > 0) && + (length <= USBBULK_READ_DATA_BUFFER_SIZE) ) /*lint !e774 */ + { + /* Only continue with a valid context and device handle. */ + if ( (libUsbDevHandle != NULL) && (libUsbCtx != NULL) ) + { + uint16_t totalBytesRead = 0; + uint8_t * currentWriteDataPtr = data; + + /* First empty out whatever is still pending in the read buffer. */ + uint16_t bytesPendingInBuffer = length; + if (readDataPending < length) + { + bytesPendingInBuffer = readDataPending; + } + uint16_t bytesToReadFromDevice = length - bytesPendingInBuffer; + /* Copy data. */ + for (uint16_t idx = 0; idx < bytesPendingInBuffer; idx++) + { + /* Sanity check for the current read indexer/ */ + assert(readDataCurrentReadIdx < USBBULK_READ_DATA_BUFFER_SIZE); + /* Only copy data if current read indexer is not out of bounds. */ + if (readDataCurrentReadIdx < USBBULK_READ_DATA_BUFFER_SIZE) + { + *currentWriteDataPtr = readDataBuffer[readDataCurrentReadIdx]; + } + /* Update total bytes read counter. */ + totalBytesRead++; + /* Increment the write data pointer. */ + currentWriteDataPtr++; + /* Increment current read indexer. */ + readDataCurrentReadIdx++; + /* Sanity check for data pending counter. */ + assert(readDataPending > 0); + /* Decrement data pending counter. */ + if (readDataPending > 0) + { + readDataPending--; + } + } + + /* If more data is still to be read, then perform a read operation of the full + * endpoint size. + */ + if (bytesToReadFromDevice > 0) + { + /* Sanity check. */ + assert(readDataPending == 0); + + /* Attempt to read data from the endpoint. Should be done in steps of the + * endpoint buffer size on the target. This is 64 for OpenBLT so exactly the size + * of our read buffer. + */ + int bytesRead = 0; + int transferResult = libusb_bulk_transfer(libUsbDevHandle, 0x81, readDataBuffer, + USBBULK_READ_DATA_BUFFER_SIZE, + &bytesRead, timeout); + if (transferResult == (int)LIBUSB_SUCCESS) + { + /* Update read buffer management. */ + readDataPending = (uint8_t)bytesRead; + readDataCurrentReadIdx = 0; + } + } + + /* Copy remaining data from the newly read data buffer contents as long as they + * are present. + */ + uint16_t bytesToCopy = bytesToReadFromDevice; + if (readDataPending < bytesToCopy) + { + bytesToCopy = readDataPending; + } + /* Copy data. */ + for (uint16_t idx = 0; idx < bytesToCopy; idx++) + { + /* Sanity check for the current read indexer/ */ + assert(readDataCurrentReadIdx < USBBULK_READ_DATA_BUFFER_SIZE); + /* Only copy data if current read indexer is not out of bounds. */ + if (readDataCurrentReadIdx < USBBULK_READ_DATA_BUFFER_SIZE) + { + *currentWriteDataPtr = readDataBuffer[readDataCurrentReadIdx]; + } + /* Update total bytes read counter. */ + totalBytesRead++; + /* Increment the write data pointer. */ + currentWriteDataPtr++; + /* Increment current read indexer. */ + readDataCurrentReadIdx++; + /* Sanity check for data pending counter. */ + assert(readDataPending > 0); + /* Decrement data pending counter. */ + if (readDataPending > 0) + { + readDataPending--; + } + } + + /* As a final step, check that all requested data bytes were read. */ + if (totalBytesRead == length) + { + result = true; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkRead ***/ + + +/*********************************** end of usbbulk.c **********************************/ + diff --git a/Host/Source/LibOpenBLT/port/windows/usbbulk.c b/Host/Source/LibOpenBLT/port/windows/usbbulk.c new file mode 100644 index 00000000..e48147b5 --- /dev/null +++ b/Host/Source/LibOpenBLT/port/windows/usbbulk.c @@ -0,0 +1,374 @@ +/************************************************************************************//** +* \file port/windows/usbbulk.c +* \brief USB bulk driver source file. +* \ingroup XcpTpUsb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT 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. +* +* OpenBLT 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 have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include /* for assertions */ +#include /* for standard integer types */ +#include /* for NULL declaration */ +#include /* for boolean type */ +#include /* for Windows API */ +#include "usbbulk.h" /* USB bulk driver */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/* USB bulk driver return values. */ +#define UBL_ERROR (0u) +#define UBL_OKAY (1u) + + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/* USB bulk driver interface functions. */ +typedef uint8_t (__stdcall * tUsbBulkLibFuncOpen)(LPGUID guid); +typedef void (__stdcall * tUsbBulkLibFuncClose)(void); +typedef uint8_t (__stdcall * tUsbBulkLibFuncTransmit)(uint8_t * data, uint16_t len); +typedef uint8_t (__stdcall * tUsbBulkLibFuncReceive)(uint8_t * data, uint16_t len, uint32_t timeout); + + +/*************************************************************************************** +* Function prototypes +****************************************************************************************/ +/* USB bulk driver handling. */ +static void UsbBulkLibLoadDll(void); +static void UsbBulkLibUnloadDll(void); +static uint8_t UsbBulkLibFuncOpen(LPGUID guid); +static void UsbBulkLibFuncClose(void); +static uint8_t UsbBulkLibFuncTransmit(uint8_t * data, uint16_t len); +static uint8_t UsbBulkLibFuncReceive(uint8_t * data, uint16_t len, uint32_t timeout); + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief Handle to the USB bulk driver dynamic link library. */ +static HINSTANCE usbBulkLibDllHandle; + +/** \brief Function pointer to the USB bulk driver library Open function. */ +static tUsbBulkLibFuncOpen usbBulkLibFuncOpenPtr; + +/** \brief Function pointer to the USB bulk driver library Close function. */ +static tUsbBulkLibFuncClose usbBulkLibFuncClosePtr; + +/** \brief Function pointer to the USB bulk driver library Transmit function. */ +static tUsbBulkLibFuncTransmit usbBulkLibFuncTransmitPtr; + +/** \brief Function pointer to the USB bulk driver library Receive function. */ +static tUsbBulkLibFuncReceive usbBulkLibFuncReceivePtr; + + +/************************************************************************************//** +** \brief Initializes the USB bulk driver. +** +****************************************************************************************/ +void UsbBulkInit(void) +{ + /* Initialize locals. */ + usbBulkLibDllHandle = NULL; + /* Reset library function pointers. */ + usbBulkLibFuncOpenPtr = NULL; + usbBulkLibFuncClosePtr = NULL; + usbBulkLibFuncTransmitPtr = NULL; + usbBulkLibFuncReceivePtr = NULL; + /* Perform initialization of USB bulk driver API. */ + UsbBulkLibLoadDll(); +} /*** end of UsbBulkInit ***/ + + +/************************************************************************************//** +** \brief Terminates the USB bulk driver. +** +****************************************************************************************/ +void UsbBulkTerminate(void) +{ + /* Perform termination of USB bulk driver API. */ + UsbBulkLibUnloadDll(); +} /*** end of UsbBulkTerminate ***/ + + +/************************************************************************************//** +** \brief Opens the connection with the USB device. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkOpen(void) +{ + /* This must be the same GUID as the one declared in the *.inf driver file. */ + static GUID deviceGuidOpenBLT = + { + 0x807999C3, 0xE4E0, 0x40EA, 0x81, 0x88, 0x48, 0xE8, 0x52, 0xB5, 0x4F, 0x2B + }; + bool result = false; + + /* Open the connection with the USB device. */ + if (UsbBulkLibFuncOpen(&deviceGuidOpenBLT) == UBL_OKAY) + { + result = true; + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkOpen ***/ + + +/************************************************************************************//** +** \brief Closes the connection with the USB device. +** +****************************************************************************************/ +void UsbBulkClose(void) +{ + /* Close the connection with the USB device. */ + UsbBulkLibFuncClose(); +} /*** end of UsbBulkClose ***/ + + +/************************************************************************************//** +** \brief Writes data to the USB device. +** \param data Pointer to byte array with data to write. +** \param length Number of bytes in the data array. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkWrite(uint8_t const * data, uint16_t length) +{ + bool result = false; + + /* Check parameters. */ + assert(data != NULL); + assert(length > 0); + + /* Only continue with valid parameters. */ + if ((data != NULL) && (length > 0)) /*lint !e774 */ + { + /* Write data to the USB device. */ + if (UsbBulkLibFuncTransmit((uint8_t *)data, length) == UBL_OKAY) + { + result = true; + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkWrite ***/ + + +/************************************************************************************//** +** \brief Reads data from the USB device. +** \param data Pointer to byte array where received data should be stored. +** \param length Number of bytes to read from the USB device. +** \param timeout Timeout in milliseconds for the read operation. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool UsbBulkRead(uint8_t * data, uint16_t length, uint32_t timeout) +{ + bool result = false; + + (void)timeout; + + /* Check parameters. */ + assert(data != NULL); + assert(length > 0); + + /* Only continue with valid parameters. */ + if ((data != NULL) && (length > 0)) /*lint !e774 */ + { + /* Read data from the USB device. */ + if (UsbBulkLibFuncReceive(data, length, timeout) == UBL_OKAY) + { + result = true; + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkRead ***/ + + +/************************************************************************************//** +** \brief Loads the USB bulk driver DLL and initializes the API function pointers. +** +****************************************************************************************/ +static void UsbBulkLibLoadDll(void) +{ + /* Start out by resetting the API function pointers. */ + usbBulkLibFuncOpenPtr = NULL; + usbBulkLibFuncClosePtr = NULL; + usbBulkLibFuncTransmitPtr = NULL; + usbBulkLibFuncReceivePtr = NULL; + + /* Attempt to load the library and obtain a handle to it. */ + usbBulkLibDllHandle = LoadLibrary("UsbBulkLib"); + + /* Assert libary handle. */ + assert(usbBulkLibDllHandle != NULL); + + /* Only continue if the library was successfully loaded */ + if (usbBulkLibDllHandle != NULL) /*lint !e774 */ + { + /* Set UblOpen function pointer. */ + usbBulkLibFuncOpenPtr = (tUsbBulkLibFuncOpen)GetProcAddress(usbBulkLibDllHandle, "UblOpen"); + /* Set UblClose function pointer. */ + usbBulkLibFuncClosePtr = (tUsbBulkLibFuncClose)GetProcAddress(usbBulkLibDllHandle, "UblClose"); + /* Set UblTransmit function pointer. */ + usbBulkLibFuncTransmitPtr = (tUsbBulkLibFuncTransmit)GetProcAddress(usbBulkLibDllHandle, "UblTransmit"); + /* Set UblReceive function pointer. */ + usbBulkLibFuncReceivePtr = (tUsbBulkLibFuncReceive)GetProcAddress(usbBulkLibDllHandle, "UblReceive"); + } +} /*** end of UsbBulkLibLoadDll ***/ + + +/************************************************************************************//** +** \brief Unloads the USB bulk driver DLL and resets the API function pointers. +** +****************************************************************************************/ +static void UsbBulkLibUnloadDll(void) +{ + /* Reset the API function pointers. */ + usbBulkLibFuncOpenPtr = NULL; + usbBulkLibFuncClosePtr = NULL; + usbBulkLibFuncTransmitPtr = NULL; + usbBulkLibFuncReceivePtr = NULL; + + /* Unload the library and invalidate its handle. */ + if (usbBulkLibDllHandle != NULL) + { + (void)FreeLibrary(usbBulkLibDllHandle); + usbBulkLibDllHandle = NULL; + } +} /*** end of UsbBulkLibUnloadDll ***/ + + +/************************************************************************************//** +** \brief Opens and configures the connection with the USB bulk device. +** \param guid Pointer to GUID of the USB bulk device as found in the driver's +** INF-file. +** \return UBL_OKAY if successful, UBL_ERROR otherwise. +** +****************************************************************************************/ +static uint8_t UsbBulkLibFuncOpen(LPGUID guid) +{ + uint8_t result = UBL_ERROR; + + /* Check function pointer and library handle. */ + assert(usbBulkLibFuncOpenPtr != NULL); + assert(usbBulkLibDllHandle != NULL); + + /* Only continue with valid function pointer and library handle. */ + if ((usbBulkLibFuncOpenPtr != NULL) && (usbBulkLibDllHandle != NULL)) /*lint !e774 */ + { + /* Call library function. */ + result = usbBulkLibFuncOpenPtr(guid); + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkLibFuncOpen ***/ + + +/************************************************************************************//** +** \brief Closes the connection with the USB bulk device and frees all the related +** handles. +** +****************************************************************************************/ +static void UsbBulkLibFuncClose(void) +{ + /* Check function pointer and library handle. */ + assert(usbBulkLibFuncClosePtr != NULL); + assert(usbBulkLibDllHandle != NULL); + + /* Only continue with valid function pointer and library handle. */ + if ((usbBulkLibFuncClosePtr != NULL) && (usbBulkLibDllHandle != NULL)) /*lint !e774 */ + { + /* Call library function. */ + usbBulkLibFuncClosePtr(); + } +} /*** end of UsbBulkLibFuncClose ***/ + + +/************************************************************************************//** +** \brief Starts transmission of the data on the bulk OUT pipe. Because USB bulk +** transmissions are quick, this function does not use the overlapped +** functionality, which means the caller is blocked until the tranmission +** completed. +** \param data Pointer to byte array with transmit data. +** \param len Number of bytes to transmit. +** \return UBL_OKAY if successful, UBL_ERROR otherwise. +** +****************************************************************************************/ +static uint8_t UsbBulkLibFuncTransmit(uint8_t * data, uint16_t len) +{ + uint8_t result = UBL_ERROR; + + /* Check function pointer and library handle. */ + assert(usbBulkLibFuncTransmitPtr != NULL); + assert(usbBulkLibDllHandle != NULL); + + /* Only continue with valid function pointer and library handle. */ + if ((usbBulkLibFuncTransmitPtr != NULL) && (usbBulkLibDllHandle != NULL)) /*lint !e774 */ + { + /* Call library function. */ + result = usbBulkLibFuncTransmitPtr(data, len); + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkLibFuncTransmit ***/ + + +/************************************************************************************//** +** \brief Starts the asynchronous reception of the data from the bulk IN pipe. This +** function makes use of the overlapped functionality, which means the calling +** thread is placed into sleep mode until the reception is complete. +** \param data Pointer to byte array where the data will be stored. +** \param len Number of bytes to receive. +** \param timeout Maximum time in milliseconds for the read to complete. +** \return UBL_OKAY if successful, UBL_TIMEOUT if failure due to timeout or +** UBL_ERROR otherwise. +** +****************************************************************************************/ +static uint8_t UsbBulkLibFuncReceive(uint8_t * data, uint16_t len, uint32_t timeout) +{ + uint8_t result = UBL_ERROR; + + /* Check function pointer and library handle. */ + assert(usbBulkLibFuncReceivePtr != NULL); + assert(usbBulkLibDllHandle != NULL); + + /* Only continue with valid function pointer and library handle. */ + if ((usbBulkLibFuncReceivePtr != NULL) && (usbBulkLibDllHandle != NULL)) /*lint !e774 */ + { + /* Call library function. */ + result = usbBulkLibFuncReceivePtr(data, len, timeout); + } + /* Give the result back to the caller. */ + return result; +} /*** end of UsbBulkLibFuncReceive ***/ + + +/*********************************** end of usbbulk.c **********************************/ + diff --git a/Host/Source/LibOpenBLT/usbbulk.h b/Host/Source/LibOpenBLT/usbbulk.h new file mode 100644 index 00000000..22915fa3 --- /dev/null +++ b/Host/Source/LibOpenBLT/usbbulk.h @@ -0,0 +1,51 @@ +/************************************************************************************//** +* \file usbbulk.h +* \brief USB bulk driver header file. +* \ingroup XcpTpUsb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT 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. +* +* OpenBLT 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 have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ +#ifndef USBBULK_H +#define USBBULK_H + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +void UsbBulkInit(void); +void UsbBulkTerminate(void); +bool UsbBulkOpen(void); +void UsbBulkClose(void); +bool UsbBulkWrite(uint8_t const * data, uint16_t length); +bool UsbBulkRead(uint8_t * data, uint16_t length, uint32_t timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* USBBULK_H */ +/********************************* end of usbbulk.h ************************************/ + diff --git a/Host/Source/LibOpenBLT/xcptpusb.c b/Host/Source/LibOpenBLT/xcptpusb.c new file mode 100644 index 00000000..69b7931a --- /dev/null +++ b/Host/Source/LibOpenBLT/xcptpusb.c @@ -0,0 +1,229 @@ +/************************************************************************************//** +* \file xcptpusb.c +* \brief XCP USB transport layer source file. +* \ingroup XcpTpUsb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT 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. +* +* OpenBLT 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 have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ + +/**************************************************************************************** +* Include files +****************************************************************************************/ +#include /* for assertions */ +#include /* for standard integer types */ +#include /* for NULL declaration */ +#include /* for boolean type */ +#include /* for standard library */ +#include /* for string library */ +#include "session.h" /* Communication session module */ +#include "xcploader.h" /* XCP loader module */ +#include "xcptpusb.h" /* XCP USB transport layer */ +#include "util.h" /* Utility module */ +#include "usbbulk.h" /* USB bulk driver */ + + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +static void XcpTpUsbInit(void const * settings); +static void XcpTpUsbTerminate(void); +static bool XcpTpUsbConnect(void); +static void XcpTpUsbDisconnect(void); +static bool XcpTpUsbSendPacket(tXcpTransportPacket const * txPacket, + tXcpTransportPacket * rxPacket, uint16_t timeout); + + +/**************************************************************************************** +* Local constant declarations +****************************************************************************************/ +/** \brief XCP transport layer structure filled with USB specifics. */ +static const tXcpTransport usbTransport = +{ + XcpTpUsbInit, + XcpTpUsbTerminate, + XcpTpUsbConnect, + XcpTpUsbDisconnect, + XcpTpUsbSendPacket +}; + + +/***********************************************************************************//** +** \brief Obtains a pointer to the transport layer structure, so that it can be +** linked to the XCP protocol module. +** \return Pointer to transport layer structure. +** +****************************************************************************************/ +tXcpTransport const * XcpTpUsbGetTransport(void) +{ + return &usbTransport; +} /*** end of XcpTpUsbGetTransport ***/ + + +/************************************************************************************//** +** \brief Initializes the transport layer. +** \param settings Pointer to settings structure. +** \return None. +** +****************************************************************************************/ +static void XcpTpUsbInit(void const * settings) +{ + /* No settings needed for this transport layer. */ + (void)settings; + + /* Initialize the USB bulk driver. */ + UsbBulkInit(); +} /*** end of XcpTpUsbInit ***/ + + +/************************************************************************************//** +** \brief Terminates the transport layer. +** +****************************************************************************************/ +static void XcpTpUsbTerminate(void) +{ + /* Terminate the USB bulk driver. */ + UsbBulkTerminate(); +} /*** end of XcpTpUsbTerminate ***/ + + +/************************************************************************************//** +** \brief Connects to the transport layer. +** \return True is connected, false otherwise. +** +****************************************************************************************/ +static bool XcpTpUsbConnect(void) +{ + bool result; + + /* Connect to USB device via USB bulk driver. */ + result = UsbBulkOpen(); + /* Give the result back to the caller. */ + return result; +} /*** end of XcpTpUsbConnect ***/ + + +/************************************************************************************//** +** \brief Disconnects from the transport layer. +** +****************************************************************************************/ +static void XcpTpUsbDisconnect(void) +{ + /* Disconnect from USB device via USB bulk driver. */ + UsbBulkClose(); +} /*** end of XcpTpUsbDisconnect ***/ + + +/************************************************************************************//** +** \brief Transmits an XCP packet on the transport layer and attempts to receive the +** response packet within the specified timeout. +** \param txPacket Pointer to the packet to transmit. +** \param rxPacket Pointer where the received packet info is stored. +** \param timeout Maximum time in milliseconds to wait for the reception of the +** response packet. +** \return True is successful and a response packet was received, false otherwise. +** +****************************************************************************************/ +static bool XcpTpUsbSendPacket(tXcpTransportPacket const * txPacket, + tXcpTransportPacket * rxPacket, uint16_t timeout) +{ + bool result = false; + uint16_t byteIdx; + /* usbBuffer is static to lower the stack load. +1 because the first byte for an XCP + * packet on the USB transport layer contains the packet lenght. + */ + static uint8_t usbBuffer[XCPLOADER_PACKET_SIZE_MAX + 1]; + uint32_t responseTimeoutTime = 0; + + /* Check parameters. */ + assert(txPacket != NULL); + assert(rxPacket != NULL); + + /* Only continue with valid parameters. */ + if ( (txPacket != NULL) && (rxPacket != NULL) ) /*lint !e774 */ + { + /* Set result value to okay and only change it from now on if an error occurred. */ + result = true; + /* Prepare the XCP packet for transmission on UART. This is basically the same as + * the XCP packet data but just the length of the packet is added to the first + * byte. + */ + usbBuffer[0] = txPacket->len; + for (byteIdx=0; byteIdxlen; byteIdx++) + { + usbBuffer[byteIdx + 1] = txPacket->data[byteIdx]; + } + /* Transmit the packet via the USB bulk driver. */ + if (!UsbBulkWrite(usbBuffer, txPacket->len + 1)) + { + result = false; + } + + /* Only continue if the transmission was successful. */ + if (result) + { + /* Determine timeout time for the response packet. */ + responseTimeoutTime = UtilTimeGetSystemTimeMs() + timeout; + /* Initialize packet reception length. */ + rxPacket->len = 0; + /* Receive the first byte. This one contains the packet length and cannot be + * zero. + */ + if (!UsbBulkRead(&rxPacket->len, 1, timeout)) + { + result = false; + } + /* Check if a valid start of packet was received, in which case the first + * byte won't have a zero value. + */ + if (rxPacket->len == 0) + { + /* No valid start of packet received, so a timeout occurred. */ + result = false; + } + } + + /* Only continue with reception if a valid packet length was received. */ + if (result) + { + /* Check if there is still time available before the initial timeout. */ + uint32_t currentTime = UtilTimeGetSystemTimeMs(); + if (currentTime >= responseTimeoutTime) + { + result = false; + } + else + { + /* Receive the actual packet data. */ + if (!UsbBulkRead(&rxPacket->data[0], rxPacket->len, responseTimeoutTime - currentTime)) + { + result = false; + } + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of XcpTpUsbSendPacket ***/ + + +/*********************************** end of xcptpusb.c *********************************/ diff --git a/Host/Source/LibOpenBLT/xcptpusb.h b/Host/Source/LibOpenBLT/xcptpusb.h new file mode 100644 index 00000000..d6ccd8c9 --- /dev/null +++ b/Host/Source/LibOpenBLT/xcptpusb.h @@ -0,0 +1,50 @@ +/************************************************************************************//** +* \file xcptpusb.h +* \brief XCP USB transport layer header file. +* \ingroup XcpTpUsb +* \internal +*---------------------------------------------------------------------------------------- +* C O P Y R I G H T +*---------------------------------------------------------------------------------------- +* Copyright (c) 2017 by Feaser http://www.feaser.com All rights reserved +* +*---------------------------------------------------------------------------------------- +* L I C E N S E +*---------------------------------------------------------------------------------------- +* This file is part of OpenBLT. OpenBLT 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. +* +* OpenBLT 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 have received a copy of the GNU General Public License along with OpenBLT. It +* should be located in ".\Doc\license.html". If not, contact Feaser to obtain a copy. +* +* \endinternal +****************************************************************************************/ +/************************************************************************************//** +* \defgroup XcpTpUsb XCP USB transport layer +* \brief This module implements the XCP transport layer for USB. +* \ingroup XcpLoader +****************************************************************************************/ +#ifndef XCPTPUSB_H +#define XCPTPUSB_H + +#ifdef __cplusplus +extern "C" { +#endif + +/*************************************************************************************** +* Function prototypes +****************************************************************************************/ +tXcpTransport const * XcpTpUsbGetTransport(void); + +#ifdef __cplusplus +} +#endif + +#endif /* XCPTPUSB_H */ +/*********************************** end of xcptpusb.h *********************************/ diff --git a/Host/libopenblt.dll b/Host/libopenblt.dll index 57874642fde7e7cb4113719ad7afebd6bea75999..5505c3a48fc5405a706e1c265947bd33e83a2545 100644 GIT binary patch delta 39514 zcmeFadt8)7`#*l~3W^1?DyyidAfR}{J)aMFz|b@o70)FeyG2sd#Z&5Hph2sK-YQmB zAX?gErfD9Y%rHr;v`EdUtfB9{Mv~8lGY^_N&QCaVAtm-vdfk`gyT{X@K+sI+@)kmevf*G-rw?XHAK zTuWT_!EexC8VIE>oRvF&2FLa4hyqB=#o||S@PyPP*%a2&^{bZSuC4wG(cUhIzBM80 z(H+r4Um`Nl<;^-o8$Lo*Y($hqrBlNZ4c8<3MvZ8}XNb&G5dE5f=;1wx_SPc$jjHV+ z$T_+^O7u>}Av%$T$Qb8BoJ7UZ8bt35LbL{bbWK5dlh-2ZNpiH!M|7D))zjrd1ESC3 z5v5E(G^P`x+a%VWONheK5Op7jXgIq1+Am)qDj`uPQ$bHVqK9>eazW2^t@?E&2X#ht ze+xw0NDD0#h(0eu^dwcT>V~L03D_C>yCz?PNKuDqF40%B# znT#}5SwD=UI)y0z%~z*Hu(tlG@3s!8aqa7-8rCafjS|9_q12DxD@?p=hh(U5>#k9f zdk&7cduX`yyUz}8mR*xby`KuMc6yW@X=g#)&`ymwr9H({+TTJvF-C)U1B+8TQo1qb zg7iSGa8CZ~KNQCau8vf8Cz72HYCDyOxgvB@8ob=;@JLD(tc>9Dy15&2hVmFZGJYu{ zuY7x^R8~+GX>RZ=so@wVx)A2ixkpA67)AUDzKrm(4~OFK0p6V+QDESr3-_Z2KNbK~ zbGIGs*@ZG!K~=<>%DhO=n5$lI1qpL~2tSiB=@IUTOwXgl&j%S6fF$rs36d4zmO4CB z1CYBsGrsXrDX_AJv8K%7i-6|2g`uLo^gc+tM4Ypey8+}zms2fwg!hAjsxY+H4afs9 zngaaZf9hTt@ti*!#c z65bc?@187nM_z)vf!|8nIz#l+7KI9a>AfcUNuF^b^dlJkLqHUzdmm5t?x0@9FRVWd zT%JY&netijFStR(bDxWt^5DnUHDfwGh-uI0!%;+c&D+i{Zn>{%gCe~(XqpK)pDj_d z`rI{g-jb2{A%bMW;WNrMT1<0G69CLd9-6<#{mq=A$sVY&u) zVusA$i@Sjqwc*o3DKdOoXePjPf5)*ds3!q-lK}B~p#qEx5}-$u03DmO5yP~R+MIRQ zbHcO)`B>5!$9S~H6v1bJJYU(2$5&^Y8rcOc8yS)5BR?qCTO%6i31CRghw&@arB@0i z;X{R*9<3r|U{D<%qZ7X9A#eZWmtJpmIAx(h9Ni$E7NLt~7g$9i_q(G&N3)K-zs**L*!ZX4j$>}|*CjUq%gWkTH(Q6-sJyAP^Sv|E<{A}-OmG}7E>n-p|9G=q8 zkf;hPPWO1)2tV|^w*$!V_04Dvs1`=`Dv-W-TDaINu?^Embm2M(O{xt~N$ju!mChgv zR!Z7w!Isk1!qy3zIb%;_h{0GIM|5&evyd*&BZj`%6-YOm#;m*2MupBES(ffkv9VL1SR};NNS|vZNVigLOVH~Xzdr{^9 z1$Sn|XpiG9p|W?6?yUWJF`!sIC=zV3Ox~)X%lTOpmJJ|*1Sx0|&6 zDK>8N``TRj;JXx2u&!QwQ|yc-q=Gr{;P`C>ldTSg6)+fy_X-&#%sG1PZc2$mEV}S% zT)5@b-r}rVXu3H)BCqA?^gkunqnVtJW4dI>CIJq2!1&xyEwXvY>slDy>lEwa+nrUV&r&+4k8{nFCt9VcZSQaex z_2k`fV)=NqfZx`v8NZzn9vjeKI_VAJ@PH}OoD)KfqOZgxq$y%M5Zv)-Xscx>gn5e2 z5e;yUX?+p$le!x2yXYHyr!M#gUt___M$v?_iE%!N@QuwVJ^8xujUq|9==FnXk5)-9Q3Um*tz#Ax?9$9j3uVC)qBJWM-xO*1#|y~9GbKBjewh(B;x zSg9Y7Do2;{5pL*g1)|_yoDG2rR$|@h<{lZDNq(?rlJJSXn^bdH_(R`gu$F3;P|dJ) zp*4SfRjkQo10BKQf#5ecgT4Q%FxAjSdj4Hug`uldc$LjC`wek}$5EBtR3&bGDCMES zl*bdy00h6c8LYcVxy`%66iYYBAx1dc7&kDFYE)7U!-i18TMv=Q82J&N5${kR-okv7 z=^64a8=XE6@!>&A|)%im@S&paf2W*ji!8KPMNa(vhmp)_0iXku&y6!^CAslIE( z#e=@7pxzj#I7S2%sLStcMqn#o%$~A(;Zv<>JqF{6Rmyeor za6t$1Q>>m&q7;kNrrx`?rWv01Q0A2&FfR*L;ZEooCIwGP2dWdt(-!S8Jb z`|*Haw{($KRx=Yic!2dZC=W=bDrcyQMF^!l8?!_+v#vc&y&~73ghV5K8mn0!Cmax> ztf_-15;X@1@m0;JeY9Vs#!P(@!RiP$c5^7x{riPg){e15PP0)z8|F0|!_sF~tM>~p zSqBVWM74QTQCa<_ysiQX@Cw{`5WTx3Ef9ANR)%g{`WkZp@>*AB&E znK&!#e_2>%>mu9p7BenPB*ib&Vj^5KCJugyc;$l)-_VTLP=GMUi7UZAg2}c5R){Fr zD7wEaMBDpHzo}yVb73Fr9~k}M_o+(B8dM3x5*1u!?YP4YsaF5<1zC!?y69nLg03Whl zQNJjvWpK;*<{Tnf4bCS{;~t^IkdzT+)Vi3bLtA$Y2ZhY6;eq+58<+gBl5I45pC9ijIb8af)Rwa}T4VPpH z&kr5!;+_Su6^McjqRk7$lf2QpFss3F-3>oa>;Py@Wpv@=v?4d~dr2S>G&8_!72|bz^~%dc zW0CZFxMwb!<{o+Z^lHLl4l+n(>$9$mUY}m?Ky~4bdt%#Co*Q}Y^!gUqqIlX1o_i89 z1JeD0zQa+E=xN*<3s0j6Iqc*!3AXwvE@;xjF(!C&VOgB49dTuZU?2~vV=() zQ(gN>?0(OM>d^)gq8>ZJbM@?d&Fe)=*GA$3Eu8>#bEQV2MnI`oz?amQ!+C!CH=Bft z(;Es_Hi&bJ2h90=(BP*$Cy5T}zv`XBJ;M?W3E;=}v0MLM`!A{;UxwPZnhBk|Q`j+V zqHBT~%A>{g7^@r+eYj@L`MfYXb&5Wmj&?Z5^MvuFkbzc(IoMSWmxI1AB0HfKWyfGt zMIRpN#nD#^noK493Tijo=`QtIL4&8Ff{b8g6M13z@XiB&J4UN@U339S>&~hh?U{ib zFbmd$krv)cYRPs)!Dei%5I!6}MbE_P*|JHTEH7Fjy*|n>P#o%!fqLal0zJP&;O`wc zk!b-Ywt>NX>x{s3K(KX)f~h^oU@;=K%>7UOE?MLZ5))Q!FGg>b_D@+@eaCcw;t$tQmKPtZIdfcN2j-z+SqMhJt0rTKC%z8%HG*A+MeI6f4*+i-;D0|#^ zUgjtCQXoI}dKw?&r?NGWExX>EBvV(irtPMt#RA1|Mikt%K2Hl*M)yuFL-zWt2Ao(d z2n@38PeIs5sT1c0UhcYVeR>1z8jXm0PB4!dmb#m&jBLOp-&DoftO_WyW^8>{cxB9e zsr&CxrSZv7F8(T=pA}Nac5@xQLzTV%P~|a z%0+nt=4H&U+*0b>SxW~kbIuy$T*#GNcz~0A{{Yus)@mvzOF%Fnj6#?_Rrq|YqGKiG z;w>+PD)ZVlAvsH#6tMW0wz271T+h7nDS?8`+l1v=dgu-yGG10%VN$4?IJ3SkCzNT5F zvP~jwW{KyCHn$hB1&D&1xs1VZq_>nB>s*L)lmRp}yz)i|-5(>zD{GT*Y;wZgf$QFz zgl{G%cg)7!95$@H5_CQ$O%mz*jY7hdUK1~Bq%!qe&<9Sx*y_r}XiHOD8Hpi#DO8Nj z8%bK{pEUUG&!S>0RscV(bqaubq~jBSP3NtMO19Yugg6g zCsNxeXSA$Xz8#4ID_x4K0ajvctU#QPI1tJqpE!s}b|)o@s^aDmBPI3UJDLPkbvPA) z5ZldUASdX~-2K=HHL=678oCFZ_{wJN+6A#o^k~qHZc*yM{my#aKBx|7w0x{SUq|~U zM-%xAM4o#E|Nb#O#^g1=sV`JVxoEr(b;7};%AF-LBNoVnW{(eno zjT5vphD+B!F07tmb?rwTKIPTWibh{Ww%eM?Mi>x$Wiwbif(3Q1#yS$IKL6WKMOhF%>oyE3D*8zHPOFeU``+OzFpOzJ zp#wVlV}B_}XT|q@x-Wp%7anFpVm=3ZKm0Dc!JE$bT@^9J(q5HSccn zlp2ZRf@7i0>$_G|gmCS_UYe~?c+tA>2HXWXoqkh_wMw;q%ZuxXo8bmZ{?fmjzXc_+%qj6-yB5*isUr$l~J^Vx>XeYL%zQh z3JIIj&qZoOCIMFe?nT|jphQ>R=p%9}T>S^jrS*rjO-0y#-5q@-ib~hKO}nx`v6c0$ zO#EU@=e-Jpr}Jy*$<4+NQx^j^!jm+$8&ETm!-DfaE8<5ekn1ykJU@ftv}s6ezyF8T7jqB4wX6 z1>Z@{??gW#!90TbilnvD3>N*^kBEIkhRzIy`UmC0yoYmpl7j!!6wzh{n<5l0Xn5Kq z!5U746$Y&Sc@#BG5eA_B3Z8_t-!2@kJ53QyqAn&)=23eUm1ys7M8Ve45=;U!#A1SR zCjncEC>Zv|^5zP-LJDBc>?cTtF$>FF#FqVyi&9jis-lrM-3^%eNhkklCP-W^Twj!) z9=jSkS+XoxCq~$BKqtphL^`2~ARbu1Mg}46*N8#*GOr8G_iaPwO(x+R&WDr9)BX>m z``A(;D{tsMg^xm-$fd#3ya)RTjDbuP1?Hu70Q<$+hqPauosXh}dK`ScVqLgU@<_PE zA#8m_?b-_=&;~fi`va*%NX3`8Lf%oN9>h6cb@*|lzHCK~WhLP}QAODGK<&)Oz=DFa zlef@cC5VFi>&nCa-XW(9XCv;m#d6~59vD^ujxHIy0Zts<10ySlpF0y<4PxgW7*j#a zP>Y`NB>Ta}6P__KQk)KDv;vH9j)KQLo%0!+(Vjhd;9V6~P$_XYID((1aR7_mEYuVBL+`fK#B@f5YB_^o(iq#>{d$=gfO>5hoO_ikh>ZC|g-S zJ7e7q<&=Utnx2Uo2XfWn3;=rnQ$xrr^m=KVA)7DUUe!a=S4ePmlwX`DN=Uai$ALe} zPtFrYy822lYJ}ykc$)!w=Jz*K_Wh_u4k_!CI5dv0C79z(XrJij3AL_139l5;(+6kP zydAwp2`ufu-EuM@bOcklkeGj82Q|by9bScO&Xb55OxXtEvHWhS$B4j@ELn>;i_jX52ZrJ&uPH#q%Rs3 zFY?07HT_l8`ut>$a~fA$nL6)9y1y@tlR0OCFa0Iba!L!Q*63V}DH{!Ypiw>pE#oVj zHSfmmPLYg6IH_3Bn0bGhGxO+|m=xaxGVF6&Y55`@jN8EA}sW|J#>XkP*2(YB9 zFCi>lJ6cULWO_bX!3-yGM>hs-O94SLR^OJ>^Y4{xptpTc_-pMDTN%j^doEOl!?Vdm zd?Wk^5;YA7{%AAU6SD=^WBsKbtFZ4e!;EW?;QCouMfBk!a(W6*N-}v5GCg}lss4T= zE!#5~MbSq_%37Snoh>{7i9v#@oTo4H&FKw``sGNlr4FY|hsQ1qD;m<_gGa=oK{*mk z3ym%CoU!NXa-pK=UTL>k2kVQnBnC{*{%tl*ZL(#o#Rq0^fm18C0rzX9#d21@aRwfj z>ZbCXSc<(XdnjJCvuxD!E6`t$$aWRU4x9l++Fj2=M84)ds43(9P;FkF9;D6Z2(|zS z-qQ^B27}RYp91rWxa04M6x|5@H-5RfZ_;Red?P8Ns3hKw)8I-Rk2hYAob4Vdy!KdM zR|4|>)&&Ig#G_4^QU2Zq_jwkf=nh>FeI#Axfc8g5u0BZ|Gd-#$L?beY2D--A*=#R9 zjol)pn~ctTJzYT-59Z+(H2QI&c;`jy9@)@$WPK(&dUo_-*45ER&Sd&q$L>|1TkPY4 zB}2UHDjtN>J2^`P`=c+?u50h=$tcmo)WvcV+SBoF%nb|M;1pUMnlC&M7@BmN=J^&yo=2m=-%5Cntpt>E z1bb2sqgHh|WoIGWNx!0che197Ng0$Xurk|n_A9V5({d)_c32eU;m%=`pBBTY`;Cu1 z{PTbix&EF`q~+rDx`LH;5z&ufHG$&lyh-cR%7xtZJIuADhr$b?dbl=)1XzlSqFLVA zkcD9zdPWeaz`8U|ShAsaB3+F3w1hSas^Z`sH9cNX>Fksf=8gjH)P{r@*0kutv6x=y z0fro|90iRV5~cX*`b0tml_y5cOcRDap_j7@W_MU3x%|A2ZrF*i>4`yIsAM(X9r1R@ znGG4y>aXZxG*SEUiJq)HnR0NL$cp_~lq*WQ61Pz$U1R)%GbfCvWKJ8ZpI;)^u>$`i z80?vfelVYLgyC}Vl!#Xzi5qD+n!Ilk$5oJ$3atCngm+6iyV7=X1Oh}RLOKxSD1%VU z`N`6g9eEQ;gUDQwLMoyaRo|8wvs+G=qdm3aWmBIJBQmt^aM>vZL*qn7FHJ<>xx)Qj zhFQ%2B=2UF(1Mcs3@0-oOhw=k9!6M>P=K%w;Q)ev!SStCxYNR7H9X99uZWum4_F|~@j|FRS@@ixv!W!P$^my!2YKP~1Br%Sm zPNO|Vd2X7JE5u0`qzR7;$yUZf!5YT0OIW) zZv@hn6~b*{Ks2%8RwS~A1!%ljHYcT8c7onLjGp+)=hH}2SNdN09D;J%EYkB)j-A4W z%}K5WXcQm!VQ971AX-iTM(ekul2S3C;vaxvNx`LJKIA{2^vmn8y zqnlfL(fE$TeFk*V%LDn)#2*`sggiyb0D=XkovEdeHRAUUSAc=Ye|>LSmH(9-WrXbccQSsO0l>e z$G1SwD*)p!Gi=jqx90%&A)^bo!?Hmy<|90U6GgQ#Da49ocM;hY$U=EP7Xjo1!1Uk) ztRXTCsk}%m&%Oiso>Wds1plSPzZv~jR8FczdC48< zf18Q&&0_iXJJ7GDa_R=u-~D&;yQ!G^2E~Uya{XODMFc1l0mtt^pQrMjV)@y>(;r5~ zyT#&Ks@_cfy{VkKoZN#r7q0yK75_Z{A6-yXh9$#t{@*;tG8L%kBYNA{RqI}6Gp_CUf%B%@& zT78sB3lP8g(@^9vhGaCIUHme%BN-?UGNb^2qmIBs_N_e~kwfc)(7&t`+cYp1+lfUD zsPp-kLs?w7FH|AVFeE_yiqArkdl-_2Am>c<3)2}Msqej8ubj#tR6c-AsJr}c7tFs; zcy4>osWd!A<-A2)j)DQte;&#uv6(!l84@6V<`<#J-$#<^}S^m2u$mw_e4@Zi>MJ zh4iwg_#zb!zD2Pn#G*!suI@nw(^XVOkPRgf%Mx(o)2@WFY|%`(WI`4J#B06@MShI) ztpe+2vV;F19ATid-La?OAQ$C`9FqoIdxBH7kxy?GXU|<`cUMP49RHTK_1G7JWhc8T_wsh;~v}v zz}vmD#k<1T@z?HuJpS@fe=@(|Mj%xmNHqjfje*pyK#CjauNxLfMFdh8fvZ8N4zF!G z{<@3|^2~9t9w2kYmtr>}=$z*z0Qi*OppP0v!SG~Vi9ycg*Rj2HV}id#!vFk}IPU)%i&DLZrgceVyDCGz~KWd9Q z)!~%=0Rps5Q2bSgH&?ccG6UMSvaYAoDc1Ai=@jLKoK79GQ6;}^ki)bCIqVI`%%Jb9 z6j&RGt+P9wKJ#^Nak6sCaCkOHk@oN@9#`1u``LLfoyX*L_E%_|gSYhwI@*&XLUHgR z9@d$_h$(}eX+R_XIyj*5QP+!AAXSu2ki69EAC3%qVPnJZr=FScM`(W^vic3`8>oyJ z2stk;i4_I<<}RP?tHdJ%Pnz)iOL5X0X+p<+$d`yc zPa~h2;NR{8OQ!}g;N>_WY}nWDE>UfL&>o>`pTZUEtKfI*17l((HDLjy=VSj2ZNfKZ ze-p&B0qU!uvjOT+*h|ydz^lMlP;P-}0c^4=Ch~?qr+|K4Gx}?ZzTa5faw5;p1}G6Y z8(>RF<$P=>%$xTH6Qe4b>uDnnqz%gI5_=-?!f&48>gx4ebOrr z6h8z}@Kok83Ox@fB4{`!M%ASW4;;|T*#(;!11F|e(u5Ze3~5JG8BR=Faop_Uku|ur zm$vDZN9Dd;9F(5C7KMT{R3MbT@~exq=^rli&|}l%`0Jon52p{d*Egw$t0th6GLTR9 z3aPox0QzrKmmxiUj&tEO-#a3ARdTX;1RcUKgeeH~5gtX@g5cVX2qX4RQ$)OP?0yfI z90Pq#4ai+aI#Rx$GR zu(BnO_1IWCC7S>kAIGpwXC?gs_aUPT$qqqp1TgL>s@-%}60YN1Tm?x70C$_tN(j(& zR`QJ&d}$daflyvf#TcW$vy#(POlvEOLi;xM?ZBowobsR;t|f8iv_hN1edy5;)8Um;OrFOMv&r+L z!@V4BG*DkwUBR0X#aVUN|LuFV^fXL!vpU!`#~H{-=oyO3$cJg-XwUbBy=s7c=X0`Z z;l3liT+AR?C!VBEEP`(M5r~3^-6D_)?(R1Usqg|>izpa5j3F6Ka^G3god4*)1IOI= z7}WARV{CiTLd|@`_@ic=WJ^LK3hu9E z49RHzgMO$CGC2SY)sI~v`ty!XtEKcx`aeJ3qW}pSfYsreKx#rDH6@VB4x|i$lqHZ# z3#5hwQW=5Nh=DFY6GtF(Y#iPa58>lU-lQ!J9|~WeoGXnVC^+66IH?ltFT$dyThrB-Iy*;zv7Of+{CDqfkH}^o>fQ`0njPC+ud)V;H(py)1BTF24ux(3A{0Jb*}vo3+~<| zadj@Ja#^q_-^L~Ri{g@82Sh!VWb9|T8%WO?ls9|!tcBdJ4>;L=gx3&GBYc2x8R1)m zpAr5-V0%17Mx)@ZGcCK>XM}>ueVa&^WqjjX85MD{#xHVpB`9ky{hy~cguC9_opq76 zWmy;9=@-qI{e{dk$&Rf(AY|5M=4M^KemwBBsoXyyr@{kmBN-dFJAqyoxj>wBhcJG?Vv;8S00{c^Tp=CM?x_3>v zq&@1e=mf$0cJRGx>ScECdcr`#`c9J1(00WObKdD1tb>wwmPi!?h1T!JNul*#@Ai_O z=q@;m5 z+oWKHD*oy;Rdmjp)nZYq~kdDBK{GL=&^P<|Zc4L`hrugN4#tM|=4XT~1Bf5eEfiVIkc?(X zZ{nf6Ism{!#z-^o|#m zN@z0c)?TRpsG|?<>>`%Xd6rm7Ay&HDPMm%|Z%1gCk2bw3GL6sgfi}q!L)%n=3V5@T z0na;Q0`yWudWAlEEASgQlJfa{c(o^p{vtjDr1{!l`l;xf!T-RZpU5D|#~=>B_-gqS z!SQi;>^9N^{=gB=rDu2wdw9RRt?``GG=>xH#`ixa4_cqEI~;9+!cjyZ>0 zza9_mdow*7Lh$a&^`^J$5X#b6u1xO!9e_&;b=CW}?os))I6K)7wFDv>{%e_wd-xQn* ziul$DmEw;^QCu70=gW3mb`mV10a36eoTfWdcqN8?W8za#=Vi%&?L-s|doD_t|5?0@ zsG^V4L^aHh5}y04OTW$Eqlw=~QWJ4>K<_i8fhaW!`&4c_O8t+H?*3l5`q{mc+Z_D3Q1 zi?sHipyX?W8wkH3-2Q<(qHH1ABH8q1YrV+fFKlZ_mA~MZO$oIBqxqP<+c{PQ)%rj+ z_{Fe}r(tc`4wa!b=#sKPYG)v|JCNGzOL?wBYv44Wh@3RGZ(WNKitEhQ^cK$$`qCF) zl7gcedet4S1-^icZ+^4m1^Tp-=xp4L%Hk5m_Nz3XECn27e!{(4;fcgPu3e*`hQ6V|I@-{ZB)xr-B+x@ESe3;8 z)!@lz(%_|@GX=jl=}MHQg{PtwTr;^))3v4Rzw0At#la--?MobBUqKyRU}fFS7T>ER zZ8l(heKTnjNLmr;bb(e%Ke+Z$RMXY8XZWinXp%SpCLBxt5|F@W2AY#4D#l2sFJNpdl*C48w<;yZgjqQx#+OAnOSw zm>oqF+!HOEiNzC=Qvq3mC>TjjDqavLm6JOzisf_1X*A>cGjhiX&;hdT13W~uXIj9w zbW2>`Wb!b)$#ewgO{QZIZ!#UMdXwo58E-OKr#G2~hc`Kn#WZ@o$>e!@lgah-CX;{W zO(wU}o2+3mt@z$#T4%h;wCs74$$#)BlbhpBCilggOm2y{Z8EI_-sB00*`#~+;N>q5 zOXP>BVR_xf<6-inPKO^FD7^5sLYqnOZ7im@th~we4>!EYbi(9KEJNxgT)VkDf&imNM=r%Q=HXNjh*W9vjuja!%IaeUVzq z84=#d$7Bi_-wgV{+rleoA+@WiSs2a#sa-g^D`rh2k@Z*A|93^M`C4dGWcq@xH<>;G z?MQZY zY&?^%fO+uo5GhB>HXdGi3_$QH#m%Gx(*})#xO5m~6vf3+k?BGoPq*j=?a&T;Tg)U? zVK`qB#(VVbYKLItt%@$EUwnG4qIQT-=21x-hX@xvJ(D9)opVO!boabSo+rMZFc{{I zOtN1-CLMR$BL)fGuXWT8L=yj7QRexY(T@;eWXc6AYq`AE>dIq{VDXrwz#7kpCh+m; z!rW_}s!!g7k3_C*yy15m+PQo-B*MpM$S zXLjNrgtltX8#BwP*ilebKW5zca_nDUzTQ(_4y?e(v^g6yh z5c;ffT&56zBM0iEG6*gkji>O~83;v_CCvr8k>nF-{`Z6M z6`GkpScD(GkLa}jA92w?jAgi?O&`*M5aZp}8yexYAG*;RFplTos$!3PGz$-BTyWQ~ z`8BTx9(_kqLF4%7H9w-kkGrF0{R*Z zs($Pzt;-UA{?U?j8NK0-Su+CaE%@E*OdajW^y0sQ96|SB%wbO|&(0J^*QYeSC`Ys1 znnwJ?$nq;1;feae(iNG)#rgr#ZJ9#&PXnbzSwhB7lUwgct6)fAw)dw8=;$Ky=S*nu zfuD8KlCi?ppSwwCW(tRYRwB>y^F2~7OHkhIii=S zf-`iI<4wK=8-Qzq;RM%&#Stuyq*$ITV;3~d!8c(sO#93)8#>iAx5ug)(QXgU|8bPZ6ja61o>6kvSIZfu zuDm`XXCxmpkw7_NIq_b(h5=(}>c#(u37&f8ap`zwq!^QsFt@R9r~1E1dc!~D8#^&U z1GCJPf|XaidF@1x&cYak(WeZ#oCr@b_N*{g|G)A96WN?=QTJUKF1iOpC5BJ5j}_rDp*H7$*2$DP=njmPUwTe%q$*`k;d zcT2qbPM@(o9k%*5)bMR&4Slk7q)|?Kf0*NOAa|Zqs!Ws`$HjdAS z8DTR)O4F`$2z7sU!07(%&osn_Tk*ZNO+xV{oDOCMrpUYuJ`5!AokUD74qA1lI0V zUAV1h)2DZQTp`56hfATIoYoGHT0GBy3P0pmOcGq)C0)>5T((kIPNshG=EZsQV!e5l zq{uC8C9`zDPzUD4sa$eByqt3Qz2Jty7VFruS299cIcduu zk~6fULSC+;^c{)B<-6h?rI}KRg8u0sc26v(2?o0-78_Wc#^NC?&S3Ef7CTr>8yk3o zET(~sJrRpHvzV4p?1@-h#Nt90=d+lG9b8n0f_`X_!bfFsE{k~<&t!2ni>I)7x8sPS zg=BHZBd>IpObQd~q}#)?VfGatNF`y8Xo>8|$<7j)ROS2E-|WAIq{Fb29*}TJL&>}jhGio&m+&VnGjNsV7$+8jrn3@tC5^n_IO?X^I02YDDWTnVM za3JI(lp(PEr;xv$n=Gr@&dC;(<6;LYRe&ho>h}|^I|kuiL)mduM5sZ?#%lnT2m#D{ zxUNAyo`Z?M1i=wX@Z3dxZRvacB&iX!52OuZX`@Ecv$Rit$z2^hnBUHfhVMK^B8wO+ zk)Ur zx$;`!pfx+Lb!~m1;c-c6f=Qwe$IQ(oAN)YtSJDDrC*fO4@A(rl{~riMEw)950;K*= z1V&C;su(PZb(Mu--pAi;{H{j8xcAcaao;ap_Y9ZFj0lu}r}A2qU2j&tKdAneHWJx! zgy8yT#qy{C!CIL_2KA*#TzkcTbkAkv&<0=pm+!cb$4!_c;f^B2BL6ywm0%Y%5WjyQ zgd@C+a13Dv^63GPp$M@^_d|FZ={X3GA*@4CAtWNWQMLkM5z;XT&$Z=da8Ds)JecGl zxDd7=>_@0Xs73ezA#ARM>x3`>!G@5DFb!b=LLtI7gnbBA2wx!lfe@7|;d&yNa*te1 zmweaa@Nh|3htetYB)eNGQxzDirG2JJRvqzVOIAoq!=_6nglBPMxIsszO_wA}N|UlB zyIFK(U5@0a^vL$vlH7WF)F+kZ@03KgSj0^) zb(Kk;4$tE#*}Yscr?l>O$%@j;dnAg|rNbpjrP+HVdrM>Ql~|4-RWeR?>d9sKbyvPi zTV0gl8U5<0z64kv#p-j@xjZhHn_K$hcu8?-vImF*DX<3SYqw!xeJCgaiL@hm1kGBk@2kWG9*lOTalRgMKgb@f+5ONWg zBdkMs9^o*;d4%r}q*+p~GeQc21t9}rEJ8NI0)%{oO$bjREX4mCoQ~i?7=mCxh(nMf z+?XKcE=`d3&!T zFXdzi-4Oa9*bs&zOhCv+$U#_v@HoOYgdGT{5$X_bAlyR80iA4wu?WKv(hzhA{SbN} z#Eyf&ZIEb*a2qXbK==-!4&f5Qdk9qsM-f(yV*>Z;H=0h9#J?Aj&pD)A(wL^ar$DRG zA>EiD|NlGV|Le{;64z4NKO%gZlv^pm!q`J{WI?oaTbQd(&1oVv+1h;VI_)amcHM5> zaQ##IaQh(pV*87B^j$p1?Z?jxipz?&%Av}3>U-2H)Cbg68i`h`ouln(Qkc?A>85*4 z3ru;YM@(Byub7URJ~Ul6HJWal?lMQ4<>q9w+C0uY-Ta{WA@fSJ+x(pQy!lJ>Rr3w= zEtmN&OQL0{CCf6&l5JUFS#Bw`Y_lA)oVR>wxoWv#xn*f-m0L$!r&(uPbFC|^Ypo^L z)7B5IpWxG>^;WO7m%YDzxcw1(zJ0BIyM34a-*!$R3fxA~UeQT0Kw(e}Q#_;CrMRtV zp=_;8RQ6NORL)V(SDsLwQogNpeWUzNS+Crn5>!vB4ywLW6{**&1@%3eF`6Zs6`Dsi zFKQ|^mo)9QvD$80lh&?H*FFSIZPb=(k7zGzztn!KRq5{0jnW;^9o8M!UDAE6>#9%I zFVjDvk216~#2PdPqrq;NVOU_uF%2_6ZLT!GWq#NEf@QDe6-zg3qRZOb%B68!4OASX z9HhKQIa0Y$xkR~AS)tsm+^6iXQmG88NvfHuxvI?&@i|qM>V4Ixs&7?4tNN&w>Rk0Y z^)_{;W}Ie<=A`Dd=B&n}`CfBV^RRZYc7=AE_8D!3HbK`@*Uu+lg1)D|pZTk~ zH~R1Nu6li>p{=2#q1^DiVXxtg;hf=uA=5a{IK^0J{Kk0Q*vb@XYG+z)T5DQo+HHEt zbkOv$d9itgd7JqebA?%H(OE2(36}dUGc51hFWNt`e{a8O=N$N-2k^Bdik^y8#URB( z#d5`A#R*>*q$uYp7b%x1wdu;YjZtIM4AJCh7HC##N;PLRUuZaOBsyf7c9phJyH|TyTd(~^`=|C^okKTH_nPht z-S@g*buztN-(9~-U!X76|HL{Y&M?QY*id5FVtCFVHMTNF8G9RDsYZ>_Vazg4HZC!) zFg|L$%MxvoTLxNkExRmX)(C5)Rc4K`##-a7@zx}33QTdl^?vJE+eF(`TY;^}w!!wk z?V{}mTZFxb-Dr2%AGEKxZ?bQ*pR<2NqbHl=8t^kjai5}6@s1)=*+H41oVpU-7TwdjpD`Fm>9h0?>lf=+=+Egp8sZE+ z3^v1H!!W}`h9?aX#zbR^G1WNSI0{Yp%ot&cF&Ru_OcP8Gn3kJdrZuK0v%x&XyxH=+ zXxaVNnbxJ&t=3)E7p?oPmDcyHUs>DP z5^Ozf{cIN7XxlTkJ+@bDE$wo9XM4O|VK><)+vnS#wO82p*pJ!Ipv@QT*Qoy&aGV1D zze-V{D8|rxQL!Jbe_wG?@d<1oQaJ(|SSEJ6%h&JOs`)A>djDC~Uey&<3-vQ3m+=`QQO z)P1Y_O_!s0=`ZQS3^M5L0mDMWqlU)}>kWGh`wWK+RWS5ggX?p{O~c*BbmJuBpT-oE z26Nde(|Xf(Q@QB{(-G4brkkeG=82fO^2|HU`_0GAx6M(OI7@=1mql-xY?*I4X7N}W zES%K_{bgI7)@4?gwZgjFy3cyTdd_;@T5J6t)0E8ild;et8u%r+IY_Rq49I$+olVqKVc31 zU=Necv&^{|!fVYXX4g|_*FLiW)9+NvEX!)kTFW}iM=%h*wa~iJddB*b^=_NSmS)Sd zxoo>_hiq42>s$$z1@v!~;&X*Z@uz}QwuGqtl{V#I&XT zhStxuox05>tEJZwgEP+E!(!*w$pal_Nnc* zEzEAU55~CPY%jOJWdG3qrTqsQ_hlFXaCD*+{SQhcHK zLGhPM(Ms7?8LLcIrYVPDY8at(D90)%DDnT$l#7-5%EvG}Jco7QRppz?v&u`#&#+uI zDF1>|yQnf$GgQ249_%7U-AA3FeoVbzeO&#$`VV!yCQUO!vk*%d&6v+?UeX-bys7yB z3)yAO4b3l_+nT$zZM6y730l{5?Obh%_JH;iZ6}>xw@!CdSEZ}L0P^S>bevwNPty^a8i|Z7l6Aoh-dADoeU$jb*dtNy~GVmthB|EN@#rwtQu2ZB4h1wNA87wa&M$vKCm2 ztxsBaSYNgNVhyvkwnf{z+fr?6o6$B5v*km!T^KLNY;RzN_}uoj?V9a(o7CRbo@JkD zpM!bRg?T`5*`LJZ`HKA{8O?s2#bH>5D`McebcgR^Qw)apGEp&Iv0CvKrq7RIG(Y;5 z(N4-9%5>!#tRGLpLSFXyGaoCz!l2WthNy6 z-Zy-1_}XyI@H>1KIac|Z7!mW0F5?EHV0_ZJ$M}lzWa-8Psoa%kB0r=NHWq2`fc3c# zRvVi=!#>(R#Xj4_Zw%PaD4`RKpwbx;_{>lD_oxIN*P^suLrz>VE<|yW4 z33^xYA-w(@Sc-nd9DkQGM%hi-OQ}*CG0SgImMhy}ULU8Lf@RyKTH{h}fp@w`^@-{$ zRfIZ59k1@GwqRBotDdA@rCtlK?l83ej{3a1PJLC~U6ZU)VRl-hc~$ee<}=L|sI{Fo zPMe}lg(Amjr^C&5YFBIB+O60;p7DllzjzyWtX zpqr&zs9T|1t6Q&oO1D$@GCcBAa0WiqMd%atDf(3XdHpAF1)~l3VMZ#18*sqq2KHhT z&IsdZ<22)JV=g9}HBj%<#&Y8UqsRC&JcD>FIro{WP2XS&$;RTj6wAo-@CW{Ft}?%8 zt~GyU{@w+%_nN~kF_=KQTLxIHmcdw9#xd78$FkV6(()*lmMvIXc3AdUUbP&zyopKW zL(63>w&X(mX^~)X#8`FKH0y9oG7ngJ>q55US#N#H`j+(~7NIY!KU!~FTVSf`Wb0u| zx2>^lwmoTk4z9%so9mSAZQIATuWUctwDuwPVfIn>srGsHMfPR(VtAxGFnAj5zuUbu z(bREVB>FpA(M^%4=&dlqN1v+LqS%Qc^s?d&#e0eim};&mepcLtDQ2v4qH-!WOskXy z%3@45JCuhp#e}I^tD;ri;jpSzMoc!NUGU%@({Ivm)9=z(WA*-6|AoGlAqI;=cZ0%U zHVia8XmA>qVJRpvJdN#!-1va;8O-K2@J~K9_A;qWM$=@|bdwXCNRR19lf)c>x!Pf# zVa_pcFmEyMHot6s+gxM*+WehaYEfX@kzpANccsKqYT0S|$>RDIo5fgbS8Jkmly#hS zIsPBiPIxcBT5nlnZC!1Nwo$fmm}<6R<-TN##UvANPqL?wHtIR97TP$Y_+F8$902Pc zj7?Y>th>FclS;3$s0OL_VQ+X$^^xi_jC-is0h9h;O`UB>R9PIxRqQ6cSeaX4Y6XK^ zuEjZX?z#7zd(XXBqa-70m3Hb=^rD*>u~>#B04njHF~|?fM99)s*&HoXf!?#VX}gUcm=OQkc9aO zeu|&P?BC$G`8ZEDG|YT9NS7FfQH+V-VYGd=x?bZM{?@`6f%beb=9{+xw+}_1_(u$i z=OQY`#Ds{6B&o?6GD9wqxiTR06E&;kN|^#fFSgg&W%g!!i(L-Vt+P+tgP7^4J!VhX zF*{kM!HVXo92HRCDW+_U_J+^aw_DX>tk0<{zPhPm0AUTvlmTH{h=J=0zs#%hYQ1A#W5T~8-hJ;qb{DU<5@nH&8LuKf zX&_ArBZ-i|$P-NYEAozLbQbmKPU_R6nC}a8oX!bmLrDoHyD)e-*o68#6Kn~#VG1-h zo#n9AY%@E+egSID)QhmOZF;w_KSGWzMhWcV2l?MT%UFh7`T@JN7gloIIEm|l2Z*Dm z#sDq}G}OUDGiYu#TXA0Q0;1lTE5%-MQ_PiFvQGBOR4ZVGtn=0%pqCVz;1tx@*D?Kh z30SSi%wJJA;B$}FQ#Gb0mH%bobK$A^4ndG~JCB_IZ~{U|f=ZO=0k0nGa{~6!fja%< zYyZYi0Dc@ciH~@t=HSA_BW0wT>?dJzjI@yJ&(&DMKy zVv@d3P70q{l9$5#BN&xN@+&z-nz0D&qyrM$Ng|{RD%(r?Kw|?a$00IIGQsa(b9(?~ zQ(zKPH&c;eaabwGliFzqy-GW2gm%%p^d9X2&-c-OIzR{M5FMsbI!e=UuQ8iv0xPnY t^Tj-XIxOIX>);E;3;8O(hOgscv%zcxgSVUa%pP;tjGAd8{thlX^&|0?u2%p6 delta 36576 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 wMQOrUKNk-~?w~lr3gqMWZZNo=r5Vie8 zZRiuB{5$xlF_RoiunZvhLv3IUFADMY9{-d`ND-W^CD+!fo5kR!oGV6JcV1+>NSKGkGZx`Nl43R$g z0UPJ-wzDpR*^hpos>oKN%2GtZeZRLli1J~Al`jM=tj$ETu9{IUt7Vi&SMO%4S55f6 z5}{^vl&iiJb<)a0NgFHC$61)ZXJI-nwo%N9`|*g0Rdm<7ACCl-wmadtvMnU+0y}cC zN_P?5#P$nV>l?PwCYzOUVm0aMZ9;Zhzd<5CCV6f!hrTqIU-|mgGye>ch9%seD--^TzZndkqFLO z9jdNp01nK!us}EvK=4g%VDq0BcBaSoBf;IHuo)GPZ$`J}q<)@R;Fx&3%nd3_5e0Yq z<>!K!9VKR)2^O&?6n6BvAZBcHN-nB^${`AU;yEy@74q*0ml%cFcZ_zO03AMUZ78w( zw-Qfs9Xnx;!8AAz0Ge#QilqTZ&IC|dI`gD_M^V1bhBM&h#sGpd1qu1{&Dney87&WW zcojtq6JeR$f?}HZaPA16Tp9q4a5BP>&PSRY4oc_c2))Kma}|=Ok}6zGY1>}uoYxIHSRpYwzh$Njnm1% zLL)F8JPlY5qF`$K7%W<(wt-*?>j67N)FOjUmCHBnekyCURC?zI;gj)(9{UM2w+*d7 z1tBpzv2$PmeJluB*$J)pF9||n_AqH$p73(^L}h;=Fn%uI$H{*|xQP&!$H`NILL|~L z2z`Y?69)Cj7QiV%Y4mU zO!H0l2Eetb6HKw>X<^GmovSvVgQt!%=E*O?{t26oh@gXVM6GUz$CpL{pEQSVD>yI76a)hZfKGgH-bA+-h_D>^DDQX`u2gsQA?Z; z-0hstm3P0NlMlO}>n^_oiRlQ12^Vv*q~1Ho9=jP-OA!TI-}7Z?mVd_vZQ+EQV6o4H z!WNeaFW;LPYm~?}N60z8QsZ-v>8P`N8S{)1?i*}RK|OwRo67H%2Jv{4cKpcfcLZ!z;|M_LMC%JNVZSIDNo?biHszYaEtJ80a4nx`;*y0x$^K>eKow4R4&sMlF z#kEd|+7CQi%`y|v4KT}msA>w*Go6K;a5XRPZ<@juv~rS>doEPQJzy7TikVI3K=9>l zU~5-WQzSivvZ?Cnw_Xfx$&WAnwI$91wB*&2;FioK3uP_&1^KNlSqiAXC8bDHOLo5% zo+c5>^TS=KlHjLGla7bi#KSc^9)3i;92GCe=%T5aU0sU=J7A!VHvAquYkMB8N!=FO zXjL&anw(MC6TARCajf2ns_t<(z}iKLQL^eixVqfqBFZB7Q_e(rnc@%<#WuPX7rz&X z0}tYSLmLkDPk@7nWRE=}sVbiIFj7+ggPvD`stczg5MrB-&BzIQj5ZbfpjLJ`;Kf>7 z33i(i1zXyTAa?ydT6CjZlsfQmGL^Anw0x|FKSA53>{jx41dm}ypkn&m7UInG$9QCMTZO2AV+-Bvi5RT4Bk*Zg) zt;)>y`c^@@lNpvn2440;s35--1q5;0@Pw`=Ua-#W+c%W*Zh(A-Li@PwMZ#kyZqgn&e|sYHglZxE0-{Gmd#a)Iy|>Mv|^b{ ztjNw2*wI)55d7vguo(mk>g(Ag$qqs$yckN*$Oz)-z2qtBfv19s`Lc;T)GdUdBU0?)lP{T7@W1O@URf8b%McDeB516o%;xbWxOu)4e~NcOi# zqGp24dnr_+qXY|*eK#SC0OFh5AU89DBs&=p=`(5-9s0M*{#-!zH)iV-*?7JziQ0>0 zBtmoM_n%91A$smD*i&TGzVO<3A^gER$DLX#PAIsRdmm$n9M8Sh* zBdReGN?v44Bag%jTOQml{rYhsl^<>iT@-?v*I5dR^+YjaM<~U|9~TuNyuuIE?uEij z*Mv9WiN*2sU!_>8)N40h{nP86xOOFF;$bVZ+X-@d+DcpNUXKgq55)`p=8RO+{3f~5 z!8g(EM;o|@PPALSA(9A`DrnxTr050eRtyA)zH!blDHKvWXQ+$RhD-vi_`{33i$IC) zyixlURJh^>%f;U4CRqvQ&ifBg19k**C*F#S2qgQLrpdv|3=@Scu__@7=%7Xa6S8YD2wiH_BxmQ9}I|MAq z#)Ll#{xN{&>IwfvnNJl$nPW0i7en-mi-Se4M!yF{A5bWY?)H-K89@7GFF@Kad;Vg` zp6UuQ32a#-d^NWT&S1lFZZ1ZyDJlvhI=c|XB|ENwF(3jyLA z38@U)TxkKtNPq+w1yZ@CK5Ff+Xn?q?U8WQ%EAn9@Qx;5)nv%VCEw#?S4vmugPmp#m z5*~WE{Ek^mpsYg+gO#=XF;Z3>1VKMb7KzF#$1OJ(V85=OLfWsZk|oepypaFM|Ip9% z)G+01XxI)!!GrQ4aA%?)cw|8G8)zA1Cd%k;Vm47+MZ{A!Hf9zIVUOPRkT?u5rv+>q z7EgD0UB+j`4UeI5qnyEw3xiPwEa6EMkrDe2XFP!Wo7d^_*5+}-dykI%dkgM>jRNnX zmgVmZZP}FtzLuHh^-F&Hzqjo91wzq0)7Ye?5c}5rV6kh^_W`j-gKUu4Ljm`TeHYUH zUP)WZdL`i>@qe*moWn$ts){d5Ek|5YY-tFnk?dMWZK_0b_}JG&+ay3PEGIH?kL;Uq zjRfBhB(HoU6g~^^;=CXOw-7WNDE>qnsM$x^Cr$B>Qp-Q1)=4mrV7?-0Z8U>LJ$cP% z>Tsdx$WYkDD}>?m=MEqR|FfS$4n`LPU;>B)=3!*EufQ#M@5~G zM)HC6Yvc)}{TeAOf=1Yc-!)|7WFop2{v(+@?f){oJ3b;P7LB>1b_K-AdpKB}co;}v z5LBUvx`<6orvUcL^Apm3c`mJhJpFbrdwET`ki9fqk}6DEs&Soy5NHJa@cKaNEK&(o z9gue(sRwb+R~LQ(sf!)Rvuq%o=gm@Vd!TpbWY9K(Z`_RzU=`a&@@d%edE`&B8*y9AuSVy;I*U3VXPtD8{wHo&cavW< z55@jR^-GbSIsK8lA8 z;PsiLY~M6WueI~L2$r%h6t-%nu(@b(;>;3y1>wZXlTj;Gz|v8ITR{edj$jHG{#|re zOg&J?!!IG5^PDaAdYQ5>3wNxDAATPZ*q1ABcN7moJXyH2q+eL@u&0C(W_8eGnRvjp zT5&aA5LW11{{fxS2>F$Ij{~%>boCf%;f z7TvjXdhl3&gp8~<1xji{KJ_fx!xaK}3HCw5vjk~$tZ>H@!y}kYrMF!xV6GWIV#fCk-cf<3|=o)_B{?Pu3m`2@5#|y7RbT)1<+RGF~shWr&%m0S;e10=( z^K#dLP#P!i4-DtI;>hz{pFvu>&2WAWSl@7FuSPH83qg%5E3kjj2M?s$ptseNi&UIZZ2U zOr3WkJn#j>j$1X$e1mk^9kPSlVL6Flx^%zy$< zK%>#N!aSdvumPn3KY2MD@YC~z_nu6%7lJ+C*hYr-s1g|IKar@hhX5-_6x^n7?h_oP z!=$pl!Xu@|nH?d)l~b7HqxP1P_fvdCGJ%&N(=$et>aP#f@;%=zr9qx0Z+8SwxbW8E z@h7M%=lKZvmh`3tLklF>TZdC-wx?0(Se6#Ez*jV?K!WL^xgA~(_EbDB%q+W8`pCT) zHQAE&?*C!HQl){^nn0>NklGMPr6OfTs0&XEpb+V{_DA60T zqR^fmonbrkyWq4^oFaao6tI1IZNu{eiabw5g1?O#$+4M&QXw@#taaH{-{o}}fn^oHVP z4YH^wG21|K4c?@+Mb8OCpMJq&gbMgeZS-KuB?0nLQ8dfj8?w-G{Qwz(if#D`f?@rj z{&X|Z(;nI=u8qSg+WHu}#@VwV%pD0_@%qGQ*0iXS94vVBpu2#pLc#v^{iV2kwLVcs z<^3Zw6NL7HLBVdAVlahrMGJelVJE^wVN`D_S%Dt}c>7{=Pe!!jTe_J@)ZP>Zu<~Te z!Ko7~_G3}rDD)w2Q5*V1`v+%17*EN9E>u5tgF?>={EzC{v-hDeA3I^V0=!M&RYc%{ z3(l~fpU835q@-fo<^;jLp_i+uiX#vpauG6{Ae9V4G3O^sZvZU3hct-Hr75H$nsE(X znK8Q+bUV>=Uc7DX6JkV$))%fFrC?~B$mq`r=sQ=qzsr0N%jsp$ODLg*?U83V`C|y% z5OyHEg>Vqz7{V!ppXnO(C%&1)q;_Vt@(UCBJw;TT3h=`nLck)(SYc>UR)4cct-YGxwB8dh&w9h*<>@SMR5?sy%8I*okKs+OZ&5j z$~0bDDw2jTtpvS_yNI6nTfQqu6Aj;Q`ASeun~E?{DX$dn+MML7rDd4p>`Xl1*ZkmX;Fhg3yp2pHs#Rq_|rd;0~ByO8Xah0IVXC4bBuP5^2Y-Pu9 zZ4^Yl_^70eC9s0KW$x^-D#~^1h-z8p@#eCe<1O@JKjlIU_B`lXmN4R(L5e`XRnb5! zOTX%l#OQkXnWTQA<*m#TA0lTpmu0OfUbcpFhF?Af5^Or!{meib-xr>-Nzp~Ux3DfU zZ5b#%lqt;EGT7Dh0#rIAGq{WXL;cPk)-6Cj)O$3HE2m|z1=y>;UYb?S$uIa+Jq_u4 zU-}-TYkld7NbmQh$01#bG&L>_>G_3O3m)X=3!R@G;-aob(f`7_FY2Z)-09O&U3g(2 zwKR|_3Z(v*mVFln;s#$8%xfjAHxoqNX2M0S&O>3bjbd>_NZD`N;0o;d- zD%px@9rT>QxFe}HCWTm$>~G^hb{4WwzQ#oW1pzQU_yC_0fENLkD6gVoOm4p7y;M9` zEZ%h+`kSbHo>*RU8}f^&oRkRu=ZSwC`je@gREzS)+t4>q`BJgG$tP&su2>dQX( z3;AX&?bXybD31QX^;i9zCqStPh(rCh>e)}_YsB)Qf2IEn6_<;}MylRM{Y$8vx}4mD zxh~xKAG^|YPxJhLcR~NpnIeW;T{uaLp%G+?fk_v}i|fXHX4Rin`RA zO5_NQGh$q1U81=bkhx|+4SX}A;2C}fz`=(75Fw`=2QsWK6lr8gM$;MVmnX{IzOfDEV|Rqg@C1|g1dvULf~9?iAsJ0)Z@&!P zNCwJ-421x|!415O-O=+Pa%g=J`fQ7J(RdN1*-AW=C&8luQ817GZKQdQA^!>F1w_Hf zKgN*UjHa`{Uz%QUNqxVj^eU(fQq==UQgsfzBYt>{Fs*XHG#a0xJRcC3*bl+w1fpOr z3m6yH!EDhv&XA{otVI-z9Ltc5rqe+32$w386X+f?MEGO~h;TFAr~BCZq=-e#4ni%omL`XrQPHSob*X(!|ffOzNGP~;MZWXq|uuU~aBq&j-*it-^) zPOv=f>EvkiX!b5yW3|FXle}o-(}>G1FyQk)4dwFds8D(G3AqCxKIdF0@+3ntn$8h^ zc?OU?vFOTZbR}N135-ABdXRt{P%ET6i%0q8n?`GlDBnurS`LPM>Sv)`HErZuMaUw6 z_@U=RksTS5(R6A^J|XGF#9nj{G3rOQ5##VkX6>|$i6T@HVf?5SkJ>&DCAe-F86nHJS^j<_fTZ;2Dl4B2gdaz zF8=Dm&jwQG1E~vvR6`(jDUk96Qda}1#z5*2Qu`6=!fRL`F($RhXwOn70>D$g6zy=> zEJ_E_eCZ{qL-}2(I#w8=j<)Ah#E8zPYUoPOr-%uiPZ0}tKII}7K?hX(h`|)l=DF`f z86<*%ct906pSsHmp0_)n`pv-99{6?lyF`@-LEhSis`NtxJD`eYq{IU%lG^j43v}q#Vm&V&Pf=dT@l+V2?zau{n10cd$3#{X^wo@F+XZ6l>`Sk#@(D*dc@<@3dp6MN zS}i`X;tD%HmHl>t4rCVg@>l3ufZt#fbfTv~gyQ5wJgr*|Moby(Oal!4b#OxCqiztZ zK&q*F;mEFme{*IqP=cP3|A3y^k0^M2g@K1?P~Smi#6TGQ*5X)Epzk{QWM3|x8Pq2T z2i}U4o=p%wdn;L*pc8Jr6=Pm{5F65JZSaQl0Tb1uqnj(przZIK`@qt%K{R-k9~AC- zduS(7ZG+Jsq2z6)i}zLV`}Kh_v5cBfPfaNFgf>BkDq<5hfGi$nLYYB_1Jt9im)669 zaNw&cw^+0QHss2P{AN!c=%-!|rGHLCit!nXTS4U6;Q%E9hXd?JN1))t0m_9;%+X$_ z5!US<6dgDmpm(;8r{9oN9!wBU?d}=D6hRHZ@D+aEt#DmEj2GCQ5N0iYd*T@dJA@rw z-UbL?NV)WD>7l;dGRj>_x#^d(JuzbLl|_7!zj`}g)=a*{U$>FEzE*Z6n=kiQ{K}VQ z<2PKM?qmEAP*1INuS%c^$+Db&34gu<7N zqtYXnBT;gKl}z4y-9_s3j~9ABuJy(3Q_!m?dql9ob|Vw2odSd1jC?X!h|Qe^(EmuY z0qGfYob#vqeoZj$5GT(^ScFi9uodA=grf+bBDg$={$`Fy@D1Mov?H!zD{Ge0d9ws> zEkI`Qr5~k%&k^twZ(l+mJsujQCTdX}&%Qw14gQ zofZ5Kio31eTD!*Ae0F~A6WX`6p8+;_1qDp03)>Ut>|w&`1B2-$f*S{xNilmaIml+u zih~2Q?;i^Ft`cMoB{VZ*x$7j?Pl;(SS5c`)SVKZSB_*G9ezge(P!S6&N6emcIhd$i_r@ej_gVV8Ca#k>_s4g%M|(y-rA3Bapig1#5N? zLo%A=%8T-ZoG?%sWa4}>wYu`x1_>kH8{lFimT~EVxq)vWE~P(*a;Y8^+J8}mJOL0t z@0U>I5{6_nZ=)Z|hb%b&h3aSYpn!hZyusudL|iJtfY10fl*_Lvq4Er7NPzf^rcmTb zhGaDVMn6;rnJNJ!nOgO;E=Ba`D|_yxkFdD*{H;G9ngKnnBz}e$nhs0%<(4{L4NUD44eDLsF73i$Z*`@z1Vg__#V zE~Y8=(3w(L09qdl>mg8GIAu!^{+Cs0WBpG~J?nnasi(XUr~dOqA|4lsd}xHZXLX6f zJJ{0$`S?n@sal?+(4^q5e1L@y$SSQO8TF8 zx<*b9K9GvZmw;<-h62|Dh)3x@zIc=#NM$4nuN@!JiMldsUs!H^s&MIeQjQe2ZJyam zK%$x`;F&$u_CJuNA2s9QRTJM%tivAS`kwQC)E9M#SFtTEQOK+77tE)$F44hEF{&gU zg1Z%DJ^dAwB|euf%k@;z-S73C!>Notnl4Mf6XtxYlLGqI?&ikOKN&^UT*jXCo;D{w2QVrxrwA-67^ z5rarRi($@-kE~BMr3}aHiQ!sR6G_Qd9Zq9FR-6Q+TM^m`Ps2gSoI%cy9!MPIpV}3t z4%zbrW;;18q`NRc_?(u|QF5ubVE%BkBb0O|>tFg(>Amdb$w(U27^-#YXCVl8rK3Cd zEbYs3&$`pkS}^+yT~8%vf8Ps2=3ZcK)`csF0`E2MIg7rWgTg6+UvCbnhRj2v~8}RN^1Er&Su?em5R5#Z=w1kiTGqgG3AQ!wrrI{&cgG%i{ z2e)oeKkW%ir`%$3He++j!=x=EDDxNEkbenqACf)2Ch}~9N{PS*mEGu=&Vrs^Q!Z(b zI&4}`;qXVnPp_$$+0*OPMB&eml6;2tTdXkXbe~`yWS?FvjZG9zo{p13>pz?xDD4s} z$p4i*Ve4m5@)JFRm7E5vD6Y)|yMH2IbZ6l(aN7a(KdSx=X?i?OlAT7H9!XRB#Am{) zeraA$I#WspXpYSrNJOMDimSM$> zkc)oE@@ov~oI%B}|AAmLK<-G&!*KEKWIhD0ngm9<%u5>~DsSBgy+-BK43x*Ayo!op z48H1XsF>P@;-OR>KNSZRlrGxmGOC_$&QkR7~0d{Tv_tVDgbvPC6s| z;BbR|l+gdrsePjfh0 zn097>iy0sjA%h4jB_MnrQLqRLP&ZhFv4l*K0(lBiFmfzIGMdf=zjk`ik3rbu8!CV- z{aa;`iKhhJqS^eD@QK33#sL#(Cb8k#fz1v?!EBCo4;5$@AsY#~AuJU63_~)Sw-bm8 zAkYG!d^-rAKr^}r1R~oMM-zKad;%Q!dEudKy0;Oim;~An5TDmR6nVKDiOy&`hxs*> z)cR-;2Tu_beCi^UrFWBOFY5R=IIQnpLLPnaxmnz9YVRzWL%j! zGS$97$aw(q(Xvpai6I$HXNq4wyz1u8mE(C=L^fXSrEe$^0E+9}4WF(L5$#4-Q8YY` z-}!M*7n`>j%lC+72DtH8+pye?D#7OUJ|UGIfjo;SxZledlF`K1D8DAoY4V#DI&eIv<(8iQ@94sq~Q5sR-1%hfdtWC!0!NQ-q1sei*{uc>lShFzhQ8olfO{Wpgc!#$vzG z2~=eflBFvPku0zjaBJ5tL282MYxbxig1K*`fEj!#aC{uT#u41926EHbPPILjPv)t? zwAQXA$`d?yGa@on#}ir1?FH@VWm6uq#(f>pU2MWnGAueIaUzs31Gw-*1r#C120-qccVHfWJdW%7m`p7+pRR*&_M__^+T%hc=N^((3rW zh7vlmhjHG-M$3kt<>;v;MSeNbL?T6hOJsH^5skaa<*#cL>!$y0-66iZ0T*_z2j0~? zI8{|+7f;vAB2tKTPDC}Ce zFydl@oT#FYQ$;l#4i~0f>^=1CWi-(iPEEuELi+y^<+!0n(fRxZl=|Nu^;{NSx_GBO z0mS(|Z8*fBPC$@9NsxMi&4T#`Tkf^6U_q=tLBd2qek4KA)vbcyCLjn(1A_c0y!5S7 zI^~LR{@c{Tb{s==6bO`n<+`g(Vl8yKl&l{cB3N^f!;i^=cJ@g~#U z=uM`{$(u|wh&P!gK5w#?#blG-WYUB;nL5UsOcw4IX@@5v(VUIVNzVYo%rrnP>xfHR#VbFqC zu^kkvwl+G6-5;HSxT|4kYbw?@$5BhH*7@;cHki4UNsd)BzX=_t3{&-4k1pgD&Itvz!yyFwW6^Fb7f^uZ%q_l`elf#68TrYfYA8W>N-%N0es4vT*8cxK1u0Krte#Nlj%Ev-ef0> z>0@Z#jB`C`Tk^NC;hj8&0x}{B3}R*h-yIkmYqOTWG>%D;+nL^?Ldx3#pHO z6D9TL)>n}Np;D1L-pnVO3@(hiK2ps@mia4HQ(+ZFO#7k=paH1h6sh3kK{Vx^>l1n^ zCqrBL=#5!bRGeL0+j#e+$yIQz`u;vZ5fOv=BE5xF%zj0vD7HZp_x|3uM;Lk?znlzx z4K8e?Q2u*?LM(&e@`-pwgt_M^iE38=ZLKNI_k{w>b+M-Ib{Y5g1kgqZBM$zp^dH{xmXpTu);uHe2^rsF8(6L;gv>kIqg zm+L4hZk`;q@){aEX;M&WRLKQo@%sUvB%X1&Pn1ROOcFNU7&s_;42Ud*%_i{)NM`ey zh$rzmhHJ=wmpQ zJ&`WVxM@vl#*lPJuhc^I#eaC6!zX4>@ZuBa`_MfYb2!OWWQ-J!-b`uz83)aFE1Pd( zp5$T#@6FNDStEtCmJ!mTk;1~3k0yKE~PrbA- zO=y2BUYa>l7;sC4yeYTtkb2UDs#|?=1B5-P2 z6&52b@>-=EM7G;Sw&%xeOpy#4u?yDe|6f9wSgQ7f-eVPo_{Gg(i#xi*Rz}>hu}G5S zqB?lkgRiF{Q5Q~C&@Tb!gVA8+R8kjCsNiyXpmZZ_ad&svMyAZHh_VQlntH7G1Z^_B z$t|z}I3*ZPa7tJ#V{rtVjIg@=+AK`@%4AWVm4%8KIID1`ASMiy$U7R6xYV$pHnQ9obuIjhG}Z9 zWEN!cEeuo;R*-z}FsHoj?&CcsN<{t3`&Sew-_fnNO) zX9SwsvOW2XKm@)EK%c4giQJXQ&^L2^_mKKTm}L13GON43Pkqi3V!m^X5H3nUZ29_j=KcY2ek4h{WesRb`yBU6*!f=ql6I*?wQdlk%zlodO|B@@|) zYoI|tNw(Co6%TWuWMTJW8=K%3b`l#h4^-HPv!))6z`u|S{7rhlG#lrh$8BdPO5_Wo z%iZnq^Gy0A-|?^&wCh4@{Z#*9w%!DtMCR6@2;$ z6HPX)KzYiFYaJx_=>lTh?e4P%L?;KPUE=eJyLqK|;m}q(QIjY0m18ShGD*TK~r-db&_{soOfZYcVP`v>59&h2lO9McP_*kYYj_`^Mr$1HQhi#*5lz+ zF1Zq3MLGNi@Wc3vRaV4BNHV26##IzVNKViu3%PTZ73N5Z%Xh~sD~?1;l=Kgfu%BWv z%{$mnvDnDsR2HYPID^HREY4;z?RelOvY19UHcKqtz+zfku~}kqDT_;3T*P7;itt^P z>_P+An@r;h&MS*~7SCdFK8y2MyhFLKvWsL<4}4uCVqeSv$-QA=jTi^j7p0Q0hISJ9 zzQh3%xl}z%Uvm#F1LOmyBShRK;UefGr1I+RtYd=h#1N{rkM3_?Ca z`C9xi5rF{^$;&x;!qXs((6}C0K`hJOguKnT$FG8SJ%{x3NLQjR0;-K>^%M3&hE!ZtNa9Y@K8|sX~p+O$#5BM1Zjs@As;CjkT7$; zbJpCs!)J0_Jv@wEqa^bB(GqzhLQ0xMo|Cq3hDmZXyjubmDJv~h2t^1x@7SkzNKUuw zNTa=IyhJV`t@miBn0R;E-G}qu-gVE>+EMRc*q=9WcXFPz!Zb!=2=9+3^e0$DfLRhL_o{;>Yol_%J?PG(x4s9AO8-`v_+dE+cpmy4)w>5)o7gsR%g;Ji>B> z4G3={{1f3TgzE^M^7h3{l>FFEXp}1VPLhPhRBV_l=^aL6sN&~`CEY9R^CX)p;uc8a z_9-8hER|FwO_R*3nD?mUP{p1{C506`7f9Ar?0;0^+_&@}l1j-wty3~rR*|q+;$)R7 z8X~2;_9+KQN~N1hBs2CE6iLo?0WbN!)OC`^Z`z1RoVkb+id-qGq zC+$0n5BE!5aat)i7(tJaiI9gd7vV94H3%;u>_s?(@FRj$C*^t}q##%kG7xeQ@)71C z6d|lf*n%+MB;{rxWFw>@7!l$SqzG4yQtq5lI+Qzv#7hX}2+I*3LC8bMM6e_B)Q;XFbkg47`8Y6gyRSe z2v-qW5DGviA0Y=}96~CB9$_d#KZIC41nz=FdxTqPVH3iS2n`755Kbf1BJ4+4u4e)d z9IB@2Rs4Gu`J7V9C23mowt!Z%Qu=hF;{WfA|F1h^UtB-wFj@FWDYuMGuKgtY<_(c< z#;=JqyEKP1a-C9V)eYBY>Zj;$=*Jn}vHxNp;JDX;&P(99L%2*(DI4px+UD&$%;?jSO;6R)(q<`>l4axJEwZh&t+tihDs6AtKC^#izhNKZP&)LEOh>Mx!12E0 znB%158;8g7hl5k%Z43yXrOZ{nq1>%Jp!`I6K^d*;sp_kGM72P*MCID3+M=pd+0~=f zW7RX&ThvC4O*2~ayQZCXpmwNMt-V(}PrFKcOnX9mTKl8+n)WwsA82Z%?hf69x;46| zbrrgAb-(Mpy6O7a`nmd5dO`n@{!{%RgTwHi;i%z+;gaF9;hLelF~Qi&{D=8Y%RI|M z%j1^G);#MB>uKvJF6-x3E){b-q^wo`qWoR?r!r2Js2Zpmry8%yRh?CRuKGqLQFm5% zQ=8Q3>Iv#RHLtEzzpVaHeO2A8eoM0#-Iu5x0AVwE8L|y?4gWB>78@kS_QnWfigB1xV|>fF*LcX7YDzcV zX?oB!*EHXB(e%CPs!3*!GRB1zx|f?EXOQSj%Y_O$1sP+G14*H z@rdII$J>sBj_ZzFWIx&Xb^|VU`@C)NEf)lAhK)k@WBRk`Y* zYPZ8VSu*_V`3d>H*ca}7mVWu_Pnq!@UzRHJ5^49g%3hQ&$ z4Yp@&+ib^dCv2x}QTAy2aC?S*hJA^>-2SrtBl{Wqd3&Uzhoi*t6tzBIY(1y!q|8)K zQkE)rDEBC9l_!*^l=aH9%JVMRV;5C-SY$3N@)g}1x;?sj-KV-Qb!z=;{W|?d{agCI z`a}9i!(_B!hGDs(*idG8-SD{~!#LB(qYbN#>(GQgrVLY#sn8^tHk+O|9W)&_ePGHm z7n)1VXD#1b{$pvf+_Z#QW30WciB<=imTkQU2FP1oi>*sB*w&+6+pYVp=d9mZf3!AQ zIa?Q7KifpxblYs(T-#FHdfQjF%eG%^W9+&1d+qu5dG^Qb&)VO#e{KJ_{j&WJyUY>k z=2I#>e@98lix?{*jZy;Ak2Y7cta% z5(eVi>V4|>)aTW2pwCZfK1RR)ppk1e+G_1i?NM!=_LTM$trQcML6@$}(&g(O(iQ1; z>h|kub+>epu=GUzK)pf#s{XJ(#*hX(&oex4cpHPU&hQZi<4=YrLzuCL zdDil#l_ChA2_}sgDDbcETu9JgT;yA@}5$uN>e?nI)S;M zLAAkf((sAlE5i?lpA9!Ltxzj$#&qLd#!1F$#s`g$8;gvqjBgu1Hl8zHFm^CSniQsZ zQ%4M5i#f|Y7Z&lX`9;Nvfw+Kfy`ws!`a>mCN2p;|9;q3l$<*+g zhcyc{J2l^HZfer>_v?B6eEm|)TIKpJ`tABR^!xQk^{4db^bPtS^-cO)dYPe{A zRd=c;sHUqnt6osOrm9!{tm>t{N4;2m3TsE0My_$iX;L&=O{!**rbx3|vqAH`W{2i& zEGfq|^_nj{Dhye>Zk%p{Zjx@QZjNrAZjnp3 zOjpL{?&ox`=yvJe(H+vA(ESTD;02vW*IqBz_tGcnhwF{{QE*Ns>GSmkdZ&K5zErR+=`Lo-u7Ry@^@jnCYbHbJMr* zb9Cl3^H}qE^EC4#<^|>@<}%C?ubaO!H(_=6nxic7mj0GO7L#R^WvXSPWxM4S%oB$# zr{Uy$Zn+ZDVcs*k-`b z`M`G8_Brh1n(daYoxP*Ir@fzDXJ2S9v6tD`*|)(qcH0lwPh#1*XivsmVsbbzpWN@5 z>6qhKf`#~Lnp1XhTr)1wx6z}k6ixxD@+GM9xc)=^$53UnUSjBo!8O1z#Go=vH7qr( zFg%GVw88KrrX7hf+}PQuz>pe*3DkEfK;j;Y0vdFU3vIfd}-twB|ElfhqmQL1W>j>)@>z&r= z7+kBdIPJpFI%mCXje?eX*(TZYY#VLQ+IC>v+_F7k-)LX!5F9T%>KzvxS20GK9L+RN zIIadCaAgZnm{O*UP$nt|VX~U7oTq$DS)zPOxmoF|R=$E6>s{qPmH$$Hs{Bs*tMU&e zr;1=pasq~sMrBkvFo-6qrl_W=9#YL$6{=h+x9TO;E-YEKs?)0Ts&BDy{i@>BMl4*P zs=H$}Woj0}Z7I{N(`?hcq1mlDpgE~Iqxo5*)23<1YR7A*X&+&ur_7~YudRY3-K70P z>(xeKnDo~T(wTIlbh)~Xy6qSfZ|V-~PU}9=eXhH#`$Z?wr|Wa{_rSY(6r-S6U#5RX z|APJezN?EWh)k*_8_au zYPP!Wvfd4AT#42H73-VUFRYzy3g&1lnX5g{mSel$_MmOPZHcW6{?0|)UG}H#o9tWd z2knRLAJ{*&e}nbEm!qE}!;$02hxfM>)6@pX7We_j9hcz)vTms7xbx_SPRee|1mytb zH01-z`IxXiQ2yqEzuO1@lxipxoTuX91G!ZK=Bn4>5gbu{s5+ROX_Rt>*{uzNR2|1p_z%5 z1_Gj%VIPtD5`00CrS78cXI+Y3 zt+!y(S*3qU-@%ZKX{FMz)36^4%lC#x!wonRU9bfAW4^Nk%Ww`Rm1)LDjmwP1SZX#I zpEFh)UxiQo9=yww#!s=6lwzJynBrWfL{o}MYqFR|G7oMMoRHO+c%C=y zFuiR$WI7Iqr8&pp^=t;ELatod{EH|A^RTX4ubT6$XgS=88G zlvv6v>#(4|VcBgtU^!_yWBJyS3`fNT&u_f-e(Ox@9P1J|ecP;`T3wegsIFPP)-Ja0 zn0^M?)V6V$c0?cdP22nMb5CLd`qp;crn9Hn$J)o+r`aE|FMz*WW?v5vkZTY<+&b9z zdChm44%)8To^aFe(B@#3n1KEvj=%0lh@6o@jPce)zj5K6m_AWP68(uY>hR^Q77OJCBZtQOyY#e2rXIyG5HEu8- zH`W_JGhQ;POnQ^mG{rR4G}E-svWI>Hswpo*WvHKt30Os5L?f0 zlpZY5zry4@z~LXFvZ=k)x3bk{SD0?%|T5K7WgxoZ#AwP=&>H!IBh>|3O10r7#^Fn&tnC@fypRV*9V&` zvo1q-k8V2F>y^5-*kok3I`Bn1)^v-wYUt!15mfl!G zGcA*`fX+lOEno))uUU>-8ZE=EI_z8?v#zzCv9_~~u~pk%wRN)R**Dnt*uS@raAd(t zTJNZF+@QH1XR4XZNgbtJpxmpxsQei_EiIHjT9u=kiVeteRjJD5SN$7o3sR#xu9~F2 zPyMiZmHKsn#gn>5&xPsSEyHC&NPrcBuNEb~0`Qgf+!16W{I*cvbwBmaC-8*wc7fswb^rbY(UoAH`|}LZ@0f{-(}xx zw;i${g?oA0e%Ahly#Y$Rij$va`z?Ezqmx7Kh;hVWZclLx$5D!e9ehV%D|QM4k4u$s zg%#nWr5aZe48B~s0S3QSS*hI4wz&J1hm;^Z$SKW#|;dYFNomgb|sP?N4scJAPE@&IHm$V-2`WvxcHfvk7 ooGwf!>!XW+_M>5~8QARQ;88}_}0&nkugKd?jnR{#J2 diff --git a/Target/Source/boot.h b/Target/Source/boot.h index a9573967..273c6ae1 100644 --- a/Target/Source/boot.h +++ b/Target/Source/boot.h @@ -36,7 +36,7 @@ /** \brief Minor version number of the bootloader core. */ #define BOOT_VERSION_CORE_MINOR (4u) /** \brief Patch number of the bootloader core. */ -#define BOOT_VERSION_CORE_BUILD (1u) +#define BOOT_VERSION_CORE_BUILD (2u) /****************************************************************************************