Refs #318. Added TCP/IP support to LibOpenBLT and BootCommander.

git-svn-id: https://svn.code.sf.net/p/openblt/code/trunk@388 5dc33758-31d5-4daf-9ae8-b24bf3d40d73
This commit is contained in:
Frank Voorburg 2017-12-11 16:19:05 +00:00
parent 159f00f0b3
commit 2327d92c65
12 changed files with 1224 additions and 8 deletions

Binary file not shown.

View File

@ -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. */

View File

@ -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

View File

@ -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 **********************************/

View File

@ -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);
}

View File

@ -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

View File

@ -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 <assert.h> /* for assertions */
#include <stdint.h> /* for standard integer types */
#include <stddef.h> /* for NULL declaration */
#include <stdbool.h> /* for boolean type */
#include <string.h> /* for string utilities */
#include <unistd.h> /* for close */
#include <sys/socket.h> /* for socket */
#include <arpa/inet.h> /* for inet_addr */
#include <netdb.h> /* 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 ********************************/

View File

@ -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 <assert.h> /* for assertions */
#include <stdint.h> /* for standard integer types */
#include <stddef.h> /* for NULL declaration */
#include <stdbool.h> /* for boolean type */
#include <winsock2.h> /* for WinSock2 definitions */
#include <ws2tcpip.h> /* 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 ********************************/

View File

@ -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 <assert.h> /* for assertions */
#include <stdint.h> /* for standard integer types */
#include <stddef.h> /* for NULL declaration */
#include <stdbool.h> /* for boolean type */
#include <stdlib.h> /* for standard library */
#include <string.h> /* 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; byteIdx<txPacket->len; 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; byteIdx<rxPacket->len; byteIdx++)
{
rxPacket->data[byteIdx] = netBuffer[byteIdx + 4];
}
}
}
/* Give the result back to the caller. */
return result;
} /*** end of XcpTpNetSendPacket ***/
/*********************************** end of xcptpnet.c *********************************/

View File

@ -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 *********************************/

View File

@ -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;

Binary file not shown.