diff --git a/Host/BootCommander.exe b/Host/BootCommander.exe index 4cf80bf0..7c2d139a 100644 Binary files a/Host/BootCommander.exe and b/Host/BootCommander.exe differ diff --git a/Host/Source/BootCommander/main.c b/Host/Source/BootCommander/main.c index 8a45ef19..e181836e 100644 --- a/Host/Source/BootCommander/main.c +++ b/Host/Source/BootCommander/main.c @@ -169,10 +169,6 @@ int main(int argc, char const * const argv[]) if ( (appSessionSettings == NULL) || (!appTransportSettingsOkay) || (appFirmwareFile == NULL) ) { - /* Display program info */ - DisplayProgramInfo(); - /* Display program usage. */ - DisplayProgramUsage(); /* Set error code. */ result = RESULT_ERROR_COMMANDLINE; } @@ -460,6 +456,7 @@ static void DisplayProgramUsage(void) printf(" xcp_rs232 (default) -> XCP on RS232.\n"); printf(" xcp_can -> XCP on CAN.\n"); printf(" xcp_usb -> XCP on USB.\n"); + printf(" xcp_net -> XCP on TCP/IP.\n"); printf("\n"); printf("XCP version 1.0 settings (xcp):\n"); printf(" -t1=[timeout] Command response timeout in milliseconds as a 16-bit\n"); @@ -511,6 +508,12 @@ static void DisplayProgramUsage(void) printf("XCP on USB settings (xcp_usb):\n"); printf(" No additional settings needed.\n"); printf("\n"); + printf("XCP on TCP/IP settings (xcp_net):\n"); + printf(" -a=[value] The IP address or hostname of the target to connect to.\n"); + printf(" For example 192.168.178.23 (Mandatory).\n"); + printf(" -p=[value] The TCP port number to use, as a 16-bit value (Default\n"); + printf(" = 1000).\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"); @@ -606,6 +609,9 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS case BLT_TRANSPORT_XCP_V10_USB: printf("XCP on USB\n"); break; + case BLT_TRANSPORT_XCP_V10_NET: + printf("XCP on TCP/IP\n"); + break; default: printf("Unknown\n"); break; @@ -688,6 +694,34 @@ static void DisplayTransportInfo(uint32_t transportType, void const * transportS printf(" -> No additional settings required.\n"); break; } + case BLT_TRANSPORT_XCP_V10_NET: + { + /* Check settings pointer. */ + assert(transportSettings); + if (transportSettings == NULL) /*lint !e774 */ + { + /* No valid settings present. */ + printf(" -> Invalid settings specified\n"); + } + else + { + tBltTransportSettingsXcpV10Net * xcpNetSettings = + (tBltTransportSettingsXcpV10Net *)transportSettings; + + /* Output the settings to the user. */ + printf(" -> Address: "); + if (xcpNetSettings->address != NULL) + { + printf("%s\n", xcpNetSettings->address); + } + else + { + printf("Unknown\n"); + } + printf(" -> Port: %hu \n", xcpNetSettings->port); + } + break; + } default: printf(" -> No settings specified\n"); break; @@ -974,7 +1008,8 @@ static uint32_t ExtractTransportTypeFromCommandLine(int argc, char const * const { { .name = "xcp_rs232", .value = BLT_TRANSPORT_XCP_V10_RS232 }, { .name = "xcp_can", .value = BLT_TRANSPORT_XCP_V10_CAN }, - { .name = "xcp_usb", .value = BLT_TRANSPORT_XCP_V10_USB } + { .name = "xcp_usb", .value = BLT_TRANSPORT_XCP_V10_USB }, + { .name = "xcp_net", .value = BLT_TRANSPORT_XCP_V10_NET } }; /* Set the default transport type in case nothing was specified on the command line. */ @@ -1182,6 +1217,49 @@ static void * ExtractTransportSettingsFromCommandLine(int argc, * layer. */ break; + /* -------------------------- XCP on TCP/IP ---------------------------------- */ + case BLT_TRANSPORT_XCP_V10_NET: + /* The following transport layer specific command line parameters are supported: + * -a=[value] -> The IP address or hostname of the target to connect to. + * -p=[value] -> The TCP port number to use. + */ + /* Allocate memory for storing the settings and check the result. */ + result = malloc(sizeof(tBltTransportSettingsXcpV10Net)); + assert(result != NULL); + if (result != NULL) /*lint !e774 */ + { + /* Create typed pointer for easy reading. */ + tBltTransportSettingsXcpV10Net * netSettings = + (tBltTransportSettingsXcpV10Net *)result; + /* Set default values. */ + netSettings->address = NULL; + netSettings->port = 1000; + /* Loop through all the command line parameters, just skip the 1st one because + * this is the name of the program, which we are not interested in. + */ + for (paramIdx = 1; paramIdx < argc; paramIdx++) + { + /* Is this the -a=[name] parameter? */ + if ( (strstr(argv[paramIdx], "-a=") != NULL) && + (strlen(argv[paramIdx]) > 3) ) + { + /* Store the pointer to the network address. */ + netSettings->address = &argv[paramIdx][3]; + /* Continue with next loop iteration. */ + continue; + } + /* Is this the -p=[value] parameter? */ + if ( (strstr(argv[paramIdx], "-p=") != NULL) && + (strlen(argv[paramIdx]) > 3) ) + { + /* Extract the port value. */ + sscanf(&argv[paramIdx][3], "%hu", &(netSettings->port)); + /* Continue with next loop iteration. */ + continue; + } + } + } + break; /* -------------------------- Unknown ------------------------------------------ */ default: /* Noting to extract. */ diff --git a/Host/Source/LibOpenBLT/CMakeLists.txt b/Host/Source/LibOpenBLT/CMakeLists.txt index 42f1327b..a56a37a7 100644 --- a/Host/Source/LibOpenBLT/CMakeLists.txt +++ b/Host/Source/LibOpenBLT/CMakeLists.txt @@ -145,7 +145,8 @@ set( # "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 and LibDL libraries by adding usb-1.0 and dl to -# the linker library dependencies. +# the linker library dependencies. Under Windows, you need to also link the Winsock +# library by adding ws2_32 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) @@ -175,6 +176,8 @@ if(BUILD_SHARED) else() SET_TARGET_PROPERTIES(openblt_shared PROPERTIES OUTPUT_NAME openblt CLEAN_DIRECT_OUTPUT 1) endif() + # Link the Winsock library + target_link_libraries(openblt_shared ws2_32) endif(BUILD_SHARED) # Only generate the PC-lint taget if the option is enabled. Use "make openblt_LINT" to diff --git a/Host/Source/LibOpenBLT/netaccess.h b/Host/Source/LibOpenBLT/netaccess.h new file mode 100644 index 00000000..1b30da54 --- /dev/null +++ b/Host/Source/LibOpenBLT/netaccess.h @@ -0,0 +1,56 @@ +/************************************************************************************//** +* \file netaccess.h +* \brief TCP/IP network access header file. +* \ingroup NetAccess +* \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 NetAccess TCP/IP Network Access +* \brief This module implements a generic TCP/IP network access client driver. +* \ingroup Session +****************************************************************************************/ +#ifndef NETACCESS_H +#define NETACCESS_H + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +void NetAccessInit(void); +void NetAccessTerminate(void); +bool NetAccessConnect(char const * address, uint16_t port); +void NetAccessDisconnect(void); +bool NetAccessSend(uint8_t const * data, uint32_t length); +bool NetAccessReceive(uint8_t * data, uint32_t * length, uint32_t timeout); + +#ifdef __cplusplus +} +#endif + +#endif /* NETACCESS_H */ +/********************************* end of netaccess.h **********************************/ + diff --git a/Host/Source/LibOpenBLT/openblt.c b/Host/Source/LibOpenBLT/openblt.c index a0f78042..c8e07251 100644 --- a/Host/Source/LibOpenBLT/openblt.c +++ b/Host/Source/LibOpenBLT/openblt.c @@ -41,6 +41,7 @@ #include "xcptpuart.h" /* XCP UART transport layer */ #include "xcptpcan.h" /* XCP CAN transport layer */ #include "xcptpusb.h" /* XCP USB transport layer */ +#include "xcptpnet.h" /* XCP TCP/IP transport layer */ /**************************************************************************************** @@ -119,7 +120,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_USB) ); + (transportType == BLT_TRANSPORT_XCP_V10_USB) || \ + (transportType == BLT_TRANSPORT_XCP_V10_NET) ); /* Initialize the correct session. */ if (sessionType == BLT_SESSION_XCP_V10) /*lint !e774 */ @@ -207,6 +209,32 @@ LIBOPENBLT_EXPORT void BltSessionInit(uint32_t sessionType, /* Link the transport layer to the XCP loader settings. */ xcpLoaderSettings.transport = XcpTpUsbGetTransport(); } + else if (transportType == BLT_TRANSPORT_XCP_V10_NET) + { + /* Verify transportSettings parameters because the XCP NET 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. */ + tBltTransportSettingsXcpV10Net * bltTransportSettingsXcpV10NetPtr; + bltTransportSettingsXcpV10NetPtr = + (tBltTransportSettingsXcpV10Net * )transportSettings; + /* Convert transport settings to the format supported by the XCP NET transport + * layer. It was made static to make sure it doesn't get out of scope when + * used in xcpLoaderSettings. + */ + static tXcpTpNetSettings xcpTpNetSettings; + xcpTpNetSettings.address = bltTransportSettingsXcpV10NetPtr->address; + xcpTpNetSettings.port = bltTransportSettingsXcpV10NetPtr->port; + /* Store transport layer settings in the XCP loader settings. */ + xcpLoaderSettings.transportSettings = &xcpTpNetSettings; + /* Link the transport layer to the XCP loader settings. */ + xcpLoaderSettings.transport = XcpTpNetGetTransport(); + } + } /* Perform actual session initialization. */ SessionInit(XcpLoaderGetProtocol(), &xcpLoaderSettings); } diff --git a/Host/Source/LibOpenBLT/openblt.h b/Host/Source/LibOpenBLT/openblt.h index e6010c63..8bec7887 100644 --- a/Host/Source/LibOpenBLT/openblt.h +++ b/Host/Source/LibOpenBLT/openblt.h @@ -108,6 +108,11 @@ LIBOPENBLT_EXPORT char const * BltVersionGetString(void); */ #define BLT_TRANSPORT_XCP_V10_USB ((uint32_t)2u) +/** \brief Transport layer for the XCP v1.0 protocol that uses TCP/IP for data + * exchange. + */ +#define BLT_TRANSPORT_XCP_V10_NET ((uint32_t)3u) + /**************************************************************************************** * Type definitions @@ -160,6 +165,17 @@ typedef struct t_blt_transport_settings_xcp_v10_can uint32_t useExtended; /**< Boolean to configure 29-bit CAN identifiers. */ } tBltTransportSettingsXcpV10Can; +/** \brief Structure layout of the XCP version 1.0 RS232 transport layer settings. The + * portName field is platform dependent. On Linux based systems this should be + * the filename of the tty-device, such as "/dev/tty0". On Windows based systems + * it should be the name of the COM-port, such as "COM1". + */ +typedef struct t_blt_transport_settings_xcp_v10_net +{ + char const * address; /**< Target IP-address or hostname on the network. */ + uint16_t port; /**< TCP port to use. */ +} tBltTransportSettingsXcpV10Net; + /**************************************************************************************** * Function prototypes diff --git a/Host/Source/LibOpenBLT/port/linux/netaccess.c b/Host/Source/LibOpenBLT/port/linux/netaccess.c new file mode 100644 index 00000000..449426ce --- /dev/null +++ b/Host/Source/LibOpenBLT/port/linux/netaccess.c @@ -0,0 +1,332 @@ +/************************************************************************************//** +* \file port/linux/netaccess.c +* \brief TCP/IP network access source file. +* \ingroup NetAccess +* \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 string utilities */ +#include /* for close */ +#include /* for socket */ +#include /* for inet_addr */ +#include /* for hostent */ +#include "netaccess.h" /* TCP/IP network access module */ + + +/**************************************************************************************** +* Macro definitions +****************************************************************************************/ +/** \brief Constant value that indicates that the network socket is invalid. */ +#define NETACCESS_INVALID_SOCKET (-1) + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief The socket that is used as an endpoint for the TCP/IP network + * communication. + */ +static int netAccessSocket; + + +/************************************************************************************//** +** \brief Initializes the network access module. +** +****************************************************************************************/ +void NetAccessInit(void) +{ + /* Invalidate the socket. */ + netAccessSocket = NETACCESS_INVALID_SOCKET; +} /*** end of NetAccessInit ***/ + + +/************************************************************************************//** +** \brief Terminates the network access module. +** +****************************************************************************************/ +void NetAccessTerminate(void) +{ + /* Make sure to disconnect form the server. */ + NetAccessDisconnect(); +} /*** end of NetAccessTerminate ***/ + + +/************************************************************************************//** +** \brief Connects to the TCP/IP server at the specified address and the given port. +** \param address The address of the server. This can be a hostname (such as +** mydomain.com) or an IP address (such as 127.0.0.1). +** \param port The port number on the server to connect to. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessConnect(char const * address, uint16_t port) +{ + bool result = false; + struct addrinfo hints = { 0 }; + struct addrinfo * serverinfo = NULL; + struct sockaddr_in serverIPv4 = { 0 }; + struct sockaddr_in6 serverIPv6 = { 0 }; + bool serverIPv4found = false; + bool serverIPv6found = false; + + /* Check parameters. */ + assert(address != NULL); + assert(port > 0); + + /* Start by invalidating the socket. */ + netAccessSocket = NETACCESS_INVALID_SOCKET; + + /* Only continue with valid parameters. */ + if ( (address != NULL) && (port > 0) ) /*lint !e774 */ + { + /* Set result to true and only reset it to false upon detection of a problem. */ + result = true; + + /* Initialize hints structure to aid in hostname resolving. Note that AF_UNSPEC is + * used to support both IPv4 and IPv6. + */ + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = (int)SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + /* Attempt to resolve the hostname. This converts the hostname to an IP address, if + * it wasn't already an IP address. + */ + if (getaddrinfo(address, NULL, &hints, &serverinfo) != 0) + { + /* Could not resolve the hostname. */ + result = false; + } + /* Sanity check on the pointer that should now be initialized and contain data. */ + else + { + if (serverinfo == NULL) + { + result = false; + } + } + /* The serverinfo pointer now points to an array with results of the hostname + * resolving. We only need one, so grab the first valid one. Prefer IPv4 over IPv6. + */ + if (result) + { + /* Point to the first entry. */ + struct addrinfo * entry = serverinfo; + /* Loop over the entries until a valid one was found. */ + while (entry != NULL) + { + /* Does this entry contain an IPv4 address? */ + if (entry->ai_family == AF_INET) + { + /* Copy this one for later usage. */ + memcpy (&serverIPv4, entry->ai_addr, entry->ai_addrlen); + serverIPv4.sin_family = AF_INET; + serverIPv4.sin_port = htons(port); + /* Set flag so we know which socket address variable to use later on. */ + serverIPv4found = true; + /* No need to go over the other entries, since we found a valid one. */ + break; + } + /* Does this entry contain an IPv6 address? */ + if (entry->ai_family == AF_INET6) + { + /* Copy this one for later usage. */ + memcpy (&serverIPv6, entry->ai_addr, entry->ai_addrlen); + serverIPv6.sin6_family = AF_INET6; + serverIPv6.sin6_port = htons(port); + /* Set flag so we know which socket address variable to use later on. */ + serverIPv6found = true; + /* No need to go over the other entries, since we found a valid one. */ + break; + } + /* Move on to the next one. */ + entry = entry->ai_next; + } + } + /* Check that a valid entry was found. */ + if (result) + { + if ( (!serverIPv4found) && (!serverIPv6found) ) + { + result = false; + } + } + /* Create the socket. */ + if (result) + { + /* Create the socket based on the family type. */ + if (serverIPv4found) + { + netAccessSocket = socket(serverIPv4.sin_family, (int)SOCK_STREAM, IPPROTO_TCP); + } + else + { + netAccessSocket = socket(serverIPv6.sin6_family, (int)SOCK_STREAM, IPPROTO_TCP); + } + /* Check the socket. */ + if (netAccessSocket < 0) + { + /* Could not create the socket. */ + netAccessSocket = NETACCESS_INVALID_SOCKET; + result = false; + } + } + /* Connect the socket. */ + if (result) + { + /* Get the socket address pointer based on the family type. */ + struct sockaddr * server_address; + if (serverIPv4found) + { + server_address = (struct sockaddr *)&serverIPv4; /*lint !e740 */ + } + else + { + server_address = (struct sockaddr *)&serverIPv6; /*lint !e740 */ + } + /* Attempt to connect. */ + if (connect(netAccessSocket, server_address, sizeof(struct sockaddr)) < 0) + { + /* Could not connect. Close the socket and negate result value. */ + close(netAccessSocket); + netAccessSocket = NETACCESS_INVALID_SOCKET; + result = false; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessConnect ***/ + + +/************************************************************************************//** +** \brief Disconnects from the TCP/IP server. +** +****************************************************************************************/ +void NetAccessDisconnect(void) +{ + /* Close the socket if it is open. */ + if (netAccessSocket >= 0) + { + close(netAccessSocket); + netAccessSocket = NETACCESS_INVALID_SOCKET; + } +} /*** end of NetAccessDisconnect ***/ + + +/************************************************************************************//** +** \brief Sends data to the TCP/IP server. +** \param data Pointer to byte array with data to send. +** \param length Number of bytes to send. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessSend(uint8_t const * data, uint32_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 socket. */ + if (netAccessSocket >= 0) + { + /* Attempt to send the data. */ + if (send(netAccessSocket, data, length, 0) >= 0) + { + /* Successfully send the data. */ + result = true; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessSend ***/ + + +/************************************************************************************//** +** \brief Receives data from the TCP/IP server in a blocking manner. +** \param data Pointer to byte array to store the received data. +** \param length Holds the max number of bytes that can be stored into the byte +** array. This function also overwrites this value with the number of bytes +** that were actually received. +** \param timeout Timeout in milliseconds for the data reception. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessReceive(uint8_t * data, uint32_t * length, uint32_t timeout) +{ + bool result = false; + struct timeval tv; + ssize_t receivedLen; + + /* Check parameters. */ + assert(data != NULL); + assert(length != NULL); + assert(timeout > 0); + + /* Only continue with valid parameters. */ + if ( (data != NULL) && (length != NULL) && (timeout > 0) ) /*lint !e774 */ + { + /* Only continue with a valid socket. */ + if (netAccessSocket >= 0) + { + /* Configure the timeout for the receive operation. */ + tv.tv_sec = (__time_t)(timeout / 1000u); + tv.tv_usec = (timeout % 1000u) * 1000u; + if (setsockopt(netAccessSocket, SOL_SOCKET, SO_RCVTIMEO, (const char*)&tv, sizeof(struct timeval)) >= 0) + { + /* Attempt to receive data. */ + receivedLen = recv(netAccessSocket, data, *length, 0); + /* Process the result. Everything < 0 indicate that an error occured. A value of + * zero is also treated as an error, since data was expected. + */ + if (receivedLen > 0) + { + /* Store the number of received bytes. */ + *length = (uint32_t)receivedLen; + /* Successfully received data. */ + result = true; + } + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessReceive ***/ + + +/*********************************** end of netaccess.c ********************************/ + diff --git a/Host/Source/LibOpenBLT/port/windows/netaccess.c b/Host/Source/LibOpenBLT/port/windows/netaccess.c new file mode 100644 index 00000000..824f1576 --- /dev/null +++ b/Host/Source/LibOpenBLT/port/windows/netaccess.c @@ -0,0 +1,349 @@ +/************************************************************************************//** +* \file port/windows/netaccess.c +* \brief TCP/IP network access source file. +* \ingroup NetAccess +* \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 WinSock2 definitions */ +#include /* for WinSock2 TCP/IP protocol extensions */ +#include "netaccess.h" /* TCP/IP network access module */ + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief Boolean flag to keep track if the Winsock library is initialized. */ +static bool winsockInitialized; + +/** \brief The socket that is used as an endpoint for the TCP/IP network + * communication. + */ +static SOCKET netAccessSocket; + + +/************************************************************************************//** +** \brief Initializes the network access module. +** +****************************************************************************************/ +void NetAccessInit(void) +{ + WSADATA wsa; + + /* Invalidate the socket. */ + netAccessSocket = INVALID_SOCKET; + + /* Init locals. */ + winsockInitialized = false; + + /* Attempt to initialize the Winsock library. */ + if (WSAStartup(MAKEWORD(2,2),&wsa) == 0) + { + /* Update flag. */ + winsockInitialized = true; + } +} /*** end of NetAccessInit ***/ + + +/************************************************************************************//** +** \brief Terminates the network access module. +** +****************************************************************************************/ +void NetAccessTerminate(void) +{ + /* Make sure to disconnect form the server. */ + NetAccessDisconnect(); + + /* Cleanup the Winsock library, if it was initialized. */ + if (winsockInitialized) + { + /* Update flag. */ + winsockInitialized = false; + /* Free Winsock resources. */ + (void)WSACleanup(); + } +} /*** end of NetAccessTerminate ***/ + + +/************************************************************************************//** +** \brief Connects to the TCP/IP server at the specified address and the given port. +** \param address The address of the server. This can be a hostname (such as +** mydomain.com) or an IP address (such as 127.0.0.1). +** \param port The port number on the server to connect to. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessConnect(char const * address, uint16_t port) +{ + bool result = false; + struct addrinfo hints = { 0 }; + struct addrinfo * serverinfo = NULL; + struct sockaddr_in serverIPv4 = { 0 }; + struct sockaddr_in6 serverIPv6 = { 0 }; + bool serverIPv4found = false; + bool serverIPv6found = false; + + /* Check parameters. */ + assert(address != NULL); + assert(port > 0); + + /* Start by invalidating the socket. */ + netAccessSocket = INVALID_SOCKET; + + /* Only continue with valid parameters and an initialized Winsock. */ + if ( (address != NULL) && (port > 0) && (winsockInitialized) ) /*lint !e774 */ + { + /* Set result to true and only reset it to false upon detection of a problem. */ + result = true; + + /* Initialize hints structure to aid in hostname resolving. Note that AF_UNSPEC is + * used to support both IPv4 and IPv6. + */ + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = (int)SOCK_STREAM; + hints.ai_flags |= AI_CANONNAME; + + /* Attempt to resolve the hostname. This converts the hostname to an IP address, if + * it wasn't already an IP address. + */ + if (getaddrinfo(address, NULL, &hints, &serverinfo) != 0) + { + /* Could not resolve the hostname. */ + result = false; + } + /* Sanity check on the pointer that should now be initialized and contain data. */ + else + { + if (serverinfo == NULL) + { + result = false; + } + } + /* The serverinfo pointer now points to an array with results of the hostname + * resolving. We only need one, so grab the first valid one. Prefer IPv4 over IPv6. + */ + if (result) + { + /* Point to the first entry. */ + struct addrinfo * entry = serverinfo; + /* Loop over the entries until a valid one was found. */ + while (entry != NULL) + { + /* Does this entry contain an IPv4 address? */ + if (entry->ai_family == AF_INET) + { + /* Copy this one for later usage. */ + memcpy (&serverIPv4, entry->ai_addr, entry->ai_addrlen); + serverIPv4.sin_family = AF_INET; + serverIPv4.sin_port = htons(port); + /* Set flag so we know which socket address variable to use later on. */ + serverIPv4found = true; + /* No need to go over the other entries, since we found a valid one. */ + break; + } + /* Does this entry contain an IPv6 address? */ + if (entry->ai_family == AF_INET6) + { + /* Copy this one for later usage. */ + memcpy (&serverIPv6, entry->ai_addr, entry->ai_addrlen); + serverIPv6.sin6_family = AF_INET6; + serverIPv6.sin6_port = htons(port); + /* Set flag so we know which socket address variable to use later on. */ + serverIPv6found = true; + /* No need to go over the other entries, since we found a valid one. */ + break; + } + /* Move on to the next one. */ + entry = entry->ai_next; + } + } + /* Check that a valid entry was found. */ + if (result) + { + if ( (!serverIPv4found) && (!serverIPv6found) ) + { + result = false; + } + } + /* Create the socket. */ + if (result) + { + /* Create the socket based on the family type. */ + if (serverIPv4found) + { + netAccessSocket = socket(serverIPv4.sin_family, (int)SOCK_STREAM, (int)IPPROTO_TCP); + } + else + { + netAccessSocket = socket(serverIPv6.sin6_family, (int)SOCK_STREAM, (int)IPPROTO_TCP); + } + /* Check the socket. */ + if (netAccessSocket == INVALID_SOCKET) + { + /* Could not create the socket. */ + result = false; + } + } + /* Connect the socket. */ + if (result) + { + /* Get the socket address pointer based on the family type. */ + struct sockaddr * server_address; + if (serverIPv4found) + { + server_address = (struct sockaddr *)&serverIPv4; /*lint !e740 */ + } + else + { + server_address = (struct sockaddr *)&serverIPv6; /*lint !e740 */ + } + /* Attempt to connect. */ + if (connect(netAccessSocket, server_address, sizeof(struct sockaddr)) != 0) + { + /* Could not connect. Close the socket and negate result value. */ + (void)closesocket(netAccessSocket); + netAccessSocket = INVALID_SOCKET; + result = false; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessConnect ***/ + + +/************************************************************************************//** +** \brief Disconnects from the TCP/IP server. +** +****************************************************************************************/ +void NetAccessDisconnect(void) +{ + /* Only perform disconnect with an initialized Winsock. */ + if (winsockInitialized) + { + /* Close the socket if it is open. */ + if (netAccessSocket != INVALID_SOCKET) + { + (void)closesocket(netAccessSocket); + netAccessSocket = INVALID_SOCKET; + } + } +} /*** end of NetAccessDisconnect ***/ + + +/************************************************************************************//** +** \brief Sends data to the TCP/IP server. +** \param data Pointer to byte array with data to send. +** \param length Number of bytes to send. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessSend(uint8_t const * data, uint32_t length) +{ + bool result = false; + + /* Check parameters. */ + assert(data != NULL); + assert(length > 0); + + /* Only continue with valid parameters and an initialized Winsock. */ + if ( (data != NULL) && (length > 0) && (winsockInitialized) ) /*lint !e774 */ + { + /* Only continue with a valid socket. */ + if (netAccessSocket != INVALID_SOCKET) + { + /* Attempt to send the data. */ + if (send(netAccessSocket, (char const *)data, (int)length, 0) != SOCKET_ERROR) + { + /* Successfully send the data. */ + result = true; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessSend ***/ + + +/************************************************************************************//** +** \brief Receives data from the TCP/IP server in a blocking manner. +** \param data Pointer to byte array to store the received data. +** \param length Holds the max number of bytes that can be stored into the byte +** array. This function also overwrites this value with the number of bytes +** that were actually received. +** \param timeout Timeout in milliseconds for the data reception. +** \return True if successful, false otherwise. +** +****************************************************************************************/ +bool NetAccessReceive(uint8_t * data, uint32_t * length, uint32_t timeout) +{ + bool result = false; + int tv; + int receivedLen; + + /* Check parameters. */ + assert(data != NULL); + assert(length != NULL); + assert(timeout > 0); + + /* Only continue with valid parameters and an initialized Winsock. */ + if ( (data != NULL) && (length != NULL) && (timeout > 0) && + (winsockInitialized) ) /*lint !e774 */ + { + /* Only continue with a valid socket. */ + if (netAccessSocket != INVALID_SOCKET) + { + /* Configure the timeout for the receive operation. */ + tv = (int)timeout; + if (setsockopt(netAccessSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&tv, sizeof(int)) != SOCKET_ERROR) + { + /* Attempt to receive data. */ + receivedLen = recv(netAccessSocket, (char *)data, (int)*length, 0); + /* Process the result. Everything < 0 indicate that an error occured. A value of + * zero is also treated as an error, since data was expected. + */ + if ((receivedLen != SOCKET_ERROR) && (receivedLen > 0)) + { + /* Store the number of received bytes. */ + *length = (uint32_t)receivedLen; + /* Successfully received data. */ + result = true; + } + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of NetAccessReceive ***/ + + +/*********************************** end of netaccess.c ********************************/ + diff --git a/Host/Source/LibOpenBLT/xcptpnet.c b/Host/Source/LibOpenBLT/xcptpnet.c new file mode 100644 index 00000000..bd7e0f94 --- /dev/null +++ b/Host/Source/LibOpenBLT/xcptpnet.c @@ -0,0 +1,291 @@ +/************************************************************************************//** +* \file xcptpnet.c +* \brief XCP TCP/IP transport layer source file. +* \ingroup XcpTpNet +* \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 "xcptpnet.h" /* XCP TCP/IP transport layer */ +#include "netaccess.h" /* TCP/IP network access module */ + + +/**************************************************************************************** +* Function prototypes +****************************************************************************************/ +static void XcpTpNetInit(void const * settings); +static void XcpTpNetTerminate(void); +static bool XcpTpNetConnect(void); +static void XcpTpNetDisconnect(void); +static bool XcpTpNetSendPacket(tXcpTransportPacket const * txPacket, + tXcpTransportPacket * rxPacket, uint16_t timeout); + + +/**************************************************************************************** +* Local constant declarations +****************************************************************************************/ +/** \brief XCP transport layer structure filled with TCP/IP specifics. */ +static const tXcpTransport netTransport = +{ + XcpTpNetInit, + XcpTpNetTerminate, + XcpTpNetConnect, + XcpTpNetDisconnect, + XcpTpNetSendPacket +}; + + +/**************************************************************************************** +* Local data declarations +****************************************************************************************/ +/** \brief The settings to use in this transport layer. */ +static tXcpTpNetSettings tpNetSettings; + +/** \brief Command receive object (CRO) counter. This counter starts at 1 with each new + * connection and is sent with each command packet. The counter gets incremented + * for each command packet, allowing the server to determine the correct order + * for the received commands. + */ +static uint32_t tpNetCroCounter; + + +/***********************************************************************************//** +** \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 * XcpTpNetGetTransport(void) +{ + return &netTransport; +} /*** end of XcpTpNetGetTransport ***/ + + +/************************************************************************************//** +** \brief Initializes the transport layer. +** \param settings Pointer to settings structure. +** \return None. +** +****************************************************************************************/ +static void XcpTpNetInit(void const * settings) +{ + char * netAddress; + + /* Reset transport layer settings. */ + tpNetSettings.address = NULL; + tpNetSettings.port = 0; + + /* Check parameters. */ + assert(settings != NULL); + + /* Only continue with valid parameters. */ + if (settings != NULL) /*lint !e774 */ + { + /* Shallow copy the transport layer settings for layer usage. */ + tpNetSettings = *((tXcpTpNetSettings *)settings); + /* The address is a pointer and it is not guaranteed that it stays valid so we need + * to deep copy this one. note the +1 for '\0' in malloc. + */ + assert(((tXcpTpNetSettings *)settings)->address != NULL); + if (((tXcpTpNetSettings *)settings)->address != NULL) /*lint !e774 */ + { + netAddress = malloc(strlen(((tXcpTpNetSettings *)settings)->address) + 1); + assert(netAddress != NULL); + if (netAddress != NULL) /*lint !e774 */ + { + strcpy(netAddress, ((tXcpTpNetSettings *)settings)->address); + tpNetSettings.address = netAddress; + } + } + } + /* Initialize the network access module. */ + NetAccessInit(); +} /*** end of XcpTpNetInit ***/ + + +/************************************************************************************//** +** \brief Terminates the transport layer. +** +****************************************************************************************/ +static void XcpTpNetTerminate(void) +{ + /* Terminate the network access module. */ + NetAccessTerminate(); + /* Release memory that was allocated for storing the network address. */ + if (tpNetSettings.address != NULL) + { + free((char *)tpNetSettings.address); + } + /* Reset transport layer settings. */ + tpNetSettings.address = NULL; + tpNetSettings.port = 0; +} /*** end of XcpTpNetTerminate ***/ + + +/************************************************************************************//** +** \brief Connects to the transport layer. +** \return True is connected, false otherwise. +** +****************************************************************************************/ +static bool XcpTpNetConnect(void) +{ + bool result = false; + + /* Check transport layer settings. */ + assert(tpNetSettings.address != NULL); + assert(tpNetSettings.port != 0); + + /* Initialize the CRO counter. */ + tpNetCroCounter = 1; + + /* Only continue if the transport layer settings are valid. */ + if ( (tpNetSettings.address != NULL) && (tpNetSettings.port != 0) ) /*lint !e774 */ + { + /* Connect via the network access module. */ + result = NetAccessConnect(tpNetSettings.address, tpNetSettings.port); + } + /* Give the result back to the caller. */ + return result; +} /*** end of XcpTpNetConnect ***/ + + +/************************************************************************************//** +** \brief Disconnects from the transport layer. +** +****************************************************************************************/ +static void XcpTpNetDisconnect(void) +{ + /* Disconnect via the network access module. */ + NetAccessDisconnect(); +} /*** end of XcpTpNetDisconnect ***/ + + +/************************************************************************************//** +** \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 XcpTpNetSendPacket(tXcpTransportPacket const * txPacket, + tXcpTransportPacket * rxPacket, uint16_t timeout) +{ + bool result = false; + uint16_t byteIdx; + /* netBuffer is static to lower the stack load. +4 because the CRO counter value is + * added at the start of the packet. + */ + static uint8_t netBuffer[XCPLOADER_PACKET_SIZE_MAX + 4]; + + /* 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 via TCP/IP. This is basically the same + * as the XCP packet data but just the CRO counter of the packet is added to the + * first four bytes. + */ + netBuffer[0] = (uint8_t)tpNetCroCounter; + netBuffer[1] = (uint8_t)(tpNetCroCounter >> 8); + netBuffer[2] = (uint8_t)(tpNetCroCounter >> 16); + netBuffer[3] = (uint8_t)(tpNetCroCounter >> 24); + /* Increment the CRO counter for the next packet. */ + tpNetCroCounter++; + /* Copy the actual packet data. */ + for (byteIdx=0; byteIdxlen; byteIdx++) + { + netBuffer[byteIdx + 4] = txPacket->data[byteIdx]; + } + /* Send the packet. */ + if (!NetAccessSend(netBuffer, txPacket->len + 4)) + { + result = false; + } + + /* Only continue if the packet was successfully sent. */ + uint32_t netRxLength = 0; + if (result) + { + /* Reset the length of the received packet data. */ + rxPacket->len = 0; + /* Set the maximum allowed length of the response packet. */ + netRxLength = sizeof(netBuffer)/sizeof(netBuffer[0]); + /* Attempt to receive the response within the specified timeout. */ + if (!NetAccessReceive(netBuffer, &netRxLength, timeout)) + { + result = false; + } + } + /* Only continue if a response packet was received. */ + if (result) + { + /* Validate the response length. It must at least have a DTO counter (32-bits) and + * one byte in the response data. It can also not be longer than the maximum + * allowed size, based on the size of the buffer. + */ + if ( (netRxLength < 5) || + (netRxLength > (sizeof(netBuffer)/sizeof(netBuffer[0]))) ) + { + /* Invalid length. */ + result = false; + } + } + /* Only continue if the response packet has a valid length. */ + if (result) + { + /* The first four bytes contain a DTO counter in which we are not really + * interested. + */ + rxPacket->len = (uint8_t)(netRxLength - 4); + /* Copy the received packet data. */ + for (byteIdx=0; byteIdxlen; byteIdx++) + { + rxPacket->data[byteIdx] = netBuffer[byteIdx + 4]; + } + } + } + /* Give the result back to the caller. */ + return result; +} /*** end of XcpTpNetSendPacket ***/ + + +/*********************************** end of xcptpnet.c *********************************/ diff --git a/Host/Source/LibOpenBLT/xcptpnet.h b/Host/Source/LibOpenBLT/xcptpnet.h new file mode 100644 index 00000000..18060e58 --- /dev/null +++ b/Host/Source/LibOpenBLT/xcptpnet.h @@ -0,0 +1,63 @@ +/************************************************************************************//** +* \file xcptpnet.h +* \brief XCP TCP/IP transport layer header file. +* \ingroup XcpTpNet +* \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 XcpTpNet XCP TCP/IP transport layer +* \brief This module implements the XCP transport layer for TCP/IP. +* \ingroup XcpLoader +****************************************************************************************/ +#ifndef XCPTPNET_H +#define XCPTPNET_H + +#ifdef __cplusplus +extern "C" { +#endif + +/**************************************************************************************** +* Type definitions +****************************************************************************************/ +/** \brief Layout of structure with settings specific to the XCP transport layer module + * for TCP/IP. + */ +typedef struct t_xcp_tp_net_settings +{ + char const * address; /**< Target IP-address or hostname on the network. */ + uint16_t port; /**< TCP port to use. */ +} tXcpTpNetSettings; + + +/*************************************************************************************** +* Function prototypes +****************************************************************************************/ +tXcpTransport const * XcpTpNetGetTransport(void); + +#ifdef __cplusplus +} +#endif + +#endif /* XCPTPNET_H */ +/*********************************** end of xcptpnet.h *********************************/ diff --git a/Host/Source/LibOpenBLT/xcptpuart.c b/Host/Source/LibOpenBLT/xcptpuart.c index ec3634ea..dffe6126 100644 --- a/Host/Source/LibOpenBLT/xcptpuart.c +++ b/Host/Source/LibOpenBLT/xcptpuart.c @@ -227,7 +227,7 @@ static bool XcpTpUartSendPacket(tXcpTransportPacket const * txPacket, bool result = false; uint16_t byteIdx; /* uartBuffer is static to lower the stack load. +1 because the first byte for an XCP - * packet on the UART transport layer contains the packet lenght. + * packet on the UART transport layer contains the packet length. */ static uint8_t uartBuffer[XCPLOADER_PACKET_SIZE_MAX + 1]; uint32_t responseTimeoutTime = 0; diff --git a/Host/libopenblt.dll b/Host/libopenblt.dll index bb13a962..b00053c0 100644 Binary files a/Host/libopenblt.dll and b/Host/libopenblt.dll differ