STM32H750XB_Hello/Middlewares/Third_Party/SystemView/Sample/embOS/Config/Win32/SEGGER_SYSVIEW_Config_embOS...

1005 lines
37 KiB
C

/*********************************************************************
* SEGGER Microcontroller GmbH *
* The Embedded Experts *
**********************************************************************
* *
* (c) 1995 - 2021 SEGGER Microcontroller GmbH *
* *
* www.segger.com Support: support@segger.com *
* *
**********************************************************************
* *
* SEGGER SystemView * Real-time application analysis *
* *
**********************************************************************
* *
* All rights reserved. *
* *
* SEGGER strongly recommends to not make any changes *
* to or modify the source code of this software in order to stay *
* compatible with the SystemView and RTT protocol, and J-Link. *
* *
* Redistribution and use in source and binary forms, with or *
* without modification, are permitted provided that the following *
* condition is met: *
* *
* o Redistributions of source code must retain the above copyright *
* notice, this condition and the following disclaimer. *
* *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND *
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, *
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE *
* DISCLAIMED. IN NO EVENT SHALL SEGGER Microcontroller BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR *
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT *
* OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; *
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF *
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE *
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
* DAMAGE. *
* *
**********************************************************************
* *
* SystemView version: 3.30 *
* *
**********************************************************************
-------------------------- END-OF-HEADER -----------------------------
File : SEGGER_SYSVIEW_Config_embOS_Win32.c
Purpose : Sample setup configuration of SystemView with embOS.
Revision: $Rev: 15024 $
*/
#include "RTOS.h"
#include "SEGGER_SYSVIEW.h"
#include "SEGGER_SYSVIEW_Conf.h"
#include "SEGGER_SYSVIEW_embOS.h"
#include "SEGGER_SYSVIEW_Win32.h"
#include "SEGGER_RTT.h"
#define WIN32_LEAN_AND_MEAN
#include <stdio.h>
#include <string.h>
#include <Windows.h>
#include <time.h>
#include <winsock2.h>
/*********************************************************************
*
* Defines, configurable
*
**********************************************************************
*/
// The application name to be displayed in SystemViewer
#ifndef SYSVIEW_APP_NAME
#define SYSVIEW_APP_NAME "embOS start project"
#endif
// The target device name
#ifndef SYSVIEW_DEVICE_NAME
#define SYSVIEW_DEVICE_NAME "Simulation"
#endif
// Frequency of the timestamp. Must match SEGGER_SYSVIEW_Conf.h
#ifndef SYSVIEW_TIMESTAMP_FREQ
#define SYSVIEW_TIMESTAMP_FREQ (1000u)
#endif
// System Frequency. SystemcoreClock is used in most CMSIS compatible projects.
#ifndef SYSVIEW_CPU_FREQ
#define SYSVIEW_CPU_FREQ (1000000u)
#endif
// Define as 1 to immediately start recording after initialization to catch system initialization.
#ifndef SYSVIEW_START_ON_INIT
#define SYSVIEW_START_ON_INIT 0
#endif
#ifndef MAX_ISRNAMES_LENGTH
#define MAX_ISRNAMES_LENGTH 400
#endif
/*********************************************************************
*
* Defines, fixed
*
**********************************************************************
*/
#define SYSVIEW_COMM_APP_HELLO_SIZE 32
#define SYSVIEW_COMM_TARGET_HELLO_SIZE 32
#ifdef WIN32
#define _SYS_THREAD_PROC_EX_TYPE U32 __stdcall
#define _SYS_THREAD_PROC_EX_R_TYPE U32
#else
#define _SYS_THREAD_PROC_EX_TYPE void*
#define _SYS_THREAD_PROC_EX_R_TYPE void*
#endif
#define _SYS_THREAD_CREATE_SUSPENDED (1)
#define _SYS_SOCKET_INVALID_HANDLE (-1)
#define _SYS_SOCKET_IP_ADDR_ANY 0
#define _SYS_SOCKET_IP_ADDR_LOCALHOST 0x7F000001 // 127.0.0.1 (localhost)
#define _SYS_SOCKET_PORT_ANY 0
#define _SYS_SOCKET_ERR_UNSPECIFIED -1
#define _SYS_SOCKET_ERR_WOULDBLOCK -2
#define _SYS_SOCKET_ERR_TIMEDOUT -3
#define _SYS_SOCKET_ERR_CONNRESET -4
#define _SYS_SOCKET_SHUT_RD 0
#define _SYS_SOCKET_SHUT_WR 1
#define _SYS_SOCKET_SHUT_RDWR 2
/*********************************************************************
*
* Types, local
*
**********************************************************************
*/
typedef void* _SYS_HANDLE;
typedef int _SYS_SOCKET_HANDLE;
typedef _SYS_THREAD_PROC_EX_TYPE _SYS_THREAD_PROC_EX(void* pPara);
/*********************************************************************
*
* Static data
*
**********************************************************************
*/// "Hello" message expected by SystemView App: SEGGER SystemView VM.mm.rr
static const U8 _abHelloMsg[SYSVIEW_COMM_TARGET_HELLO_SIZE] = { 'S', 'E', 'G', 'G', 'E', 'R', ' ', 'S', 'y', 's', 't', 'e', 'm', 'V', 'i', 'e', 'w', ' ', 'V', '0' + SEGGER_SYSVIEW_MAJOR, '.', '0' + (SEGGER_SYSVIEW_MINOR / 10), '0' + (SEGGER_SYSVIEW_MINOR % 10), '.', '0' + (SEGGER_SYSVIEW_REV / 10), '0' + (SEGGER_SYSVIEW_REV % 10), '\0', 0, 0, 0, 0, 0 };
static volatile int _CloseRequested; // Indicator for threads to terminate themselves
static volatile int _SysViewCommThreadRunning; // Indicator for status of the "SysView communication" thread
static int _int1 = 1;
static U64 _TSFreq;
static U32 _TSDiv;
static CRITICAL_SECTION _csLockString;
static char _sISRNames[MAX_ISRNAMES_LENGTH];
/*********************************************************************
*
* Local functions, SYS
*
**********************************************************************
*/
/*********************************************************************
*
* SYS_Sleep
*/
static void _SYS_Sleep(int ms) {
Sleep(ms);
}
/*********************************************************************
*
* SYS_GetLastError
*/
static U32 _SYS_GetLastError(void) {
return GetLastError();
}
/*********************************************************************
*
* SYS_CreateThreadEx
*/
static _SYS_HANDLE _SYS_CreateThreadEx(_SYS_THREAD_PROC_EX* pfThreadProc, void* pPara, U64* pThreadId, const char* sName, U32 Flags) {
_SYS_HANDLE hThread;
U32 ThreadId;
U32 CreateFlags;
CreateFlags = 0;
if (Flags & _SYS_THREAD_CREATE_SUSPENDED) {
CreateFlags = CREATE_SUSPENDED;
}
hThread = (_SYS_HANDLE)CreateThread(NULL, 0, pfThreadProc, pPara, CreateFlags, &ThreadId);
if (sName != NULL) {
OS_SIM_SetThreadName(ThreadId, sName);
}
if (hThread) {
if (pThreadId) {
*pThreadId = ThreadId;
}
}
return hThread;
}
/*********************************************************************
*
* _WSAStartup
*
* Function description
* Initializes Winsock API. Needs to be called once before using any socket API.
* May be called multiple times.
*/
static void _WSAStartup(void) {
WORD wVersionRequested;
WSADATA wsaData;
//
// Init Winsock API
//
wVersionRequested = MAKEWORD(2, 2);
WSAStartup(wVersionRequested, &wsaData);
}
/*********************************************************************
*
* _WSACleanup
*
* Function description
* Cleans up Winsock API. WSACleanup() needs to be called as many times
* as WSAStartup() if socket API is no longer needed.
*/
static void _WSACleanup(void) {
WSACleanup();
}
/*********************************************************************
*
* _SYS_SOCKET_OpenTCP
*
* Function description
* Creates an IPv4 TCP socket.
*
* Return value
* Handle to socket
*/
static _SYS_SOCKET_HANDLE _SYS_SOCKET_OpenTCP(void) {
SOCKET sock;
//
// Init Winsock API as this is the first socket-related function being called
// WSACleanup is called on SocketClose()
//
_WSAStartup();
//
// Create socket
//
sock = socket(AF_INET, SOCK_STREAM, 0);
if (sock != INVALID_SOCKET) {
//
// Disable Nagle's algorithm to speed things up
// Nagle's algorithm prevents small packets from being transmitted and collects some time until data is actually sent out
//
setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, (char*)&_int1, sizeof(int));
} else {
return _SYS_SOCKET_INVALID_HANDLE;
}
return (_SYS_SOCKET_HANDLE)sock;
}
/*********************************************************************
*
* _SYS_SOCKET_Close
*
* Function description
* Closes a socket. Resources allocated by this socket are freed.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*/
static void _SYS_SOCKET_Close(_SYS_SOCKET_HANDLE hSocket) {
SOCKET Sock;
int OptVal;
int OptLen;
//
// MSDN: A socket that is using the SO_EXCLUSIVEADDRUSE option must be shut down properly prior to closing it. Failure to do so can cause a denial of service attack if the associated service needs to restart.
//
Sock = (SOCKET)hSocket;
OptLen = 4;
OptVal = 0; // Make sure it is zero initialized in case getsockopt does not fill it completely
getsockopt(Sock, SOL_SOCKET, -5, (char*)&OptVal, &OptLen); // SO_EXCLUSIVEADDRUSE (Define not known in the VC6 headers, we use...)
if (OptVal) {
shutdown(Sock, SD_BOTH);
}
//
// Close socket
//
closesocket(Sock);
//
// De-init Winsock API. Needs to be called as often as WSAStartup() has been called
// Has an internal reference counter
// If the counter reaches 0, all sockets opened by the process, are forced closed
//
_WSACleanup();
}
/*********************************************************************
*
* _SYS_SOCKET_ListenAtTCPAddr
*
* Function description
* Puts IPv4 socket into listening state.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
* IPAddr IPv4 address expected in little endian form, meaning 127.0.0.1 is expected as 0x7F000001
* To accept connections from any IP address, pass _SYS_SOCKET_IP_ADDR_ANY
* Port Port to listen at
*
* Return value
* >= 0: O.K.
* < 0: Error
*/
static int _SYS_SOCKET_ListenAtTCPAddr(_SYS_SOCKET_HANDLE hSocket, U32 IPAddr, U32 Port, unsigned NumConnectionsQueued) {
struct sockaddr_in addr;
SOCKET Sock;
int r;
//
// Option SO_REUSEADDR:
//
// <IPAddr>: IP addresses of network adapters
//
// ==================================================
// Original idea from BSD sockets
// ==================================================
// bind() without SO_REUSEADDR set (default):
// bind(SockA, 0.0.0.0:21)
// bind(SockB, 192.168.0.1:21)
// SockB will fail because <IPAddr> of SockA is a wildcard that means "Any local address",
// so it is not possible to bind to any other local address with the same port.
// bind(SockA, 127.0.0.1:21)
// bind(SockB, 192.168.0.1:21)
// Both calls will succeed, as different <IPAddr>:<Port> combinations are used.
//
// bind() with SO_REUSEADDR set:
// bind(SockA, 0.0.0.0:21)
// bind(SockB, 192.168.0.1:21)
// SockA and SockB will succeed.
// The original idea includes that *each* of the sockets must have SO_REUSEADDR set before bind().
// If only the second one calls it, bind() will fail as the first socket did not allow sharing at all.
//
// This was the original idea of SO_REUSEADDR.
// NOTE: Not sure who really ever needed this, but that's the way it is...
//
// ==================================================
// Second effect of SO_REUSEADDR (TIME_WAIT)
// ==================================================
// There is another case where this option has an effect on:
// Calls to send() do not guarantee that data is sent when the function returns. It may be sent delayed.
// Therefore, it is possible that when calling close() to close a socket, send data is still pending.
// What the OS does is: preparing everything for closing the connection and return from close().
// Now the socket changed it's state from ACTIVE to TIME_WAIT but still exists inside the OS (not accessible for the user anymore)
// If now a new socket is opened and a bind() is performed on exactly the <IPAddr>:<Port> combination of the TIME_WAIT socket, the behavior depends on if the original socket had SO_REUSEADDR set.
//
// SO_REUSEADDR not set:
// bind() will fail as TIME_WAIT is handled as if it is ACTIVE
//
// SO_REUSEADDR set:
// bind() will succeed as TIME_WAIT is handled as if socket was not existing anymore.
// NOTE: Under rare circumstances, it now can happen that if there is any receive data arriving late at the system, the new socket that did the bind(), will receive it.
//
// ==================================================
// OS specifics
// ==================================================
//
// Windows:
// When specifying SO_REUSEADDR before bind(), Windows will report SUCCESS on bind(),
// even if there is another ACTIVE socket that is bound to the same <IPAddr>:<Port> combination.
// It does not matter if the process that did the first bind() did specify SO_REUSEADDR for its socket or not
// This allows processes to steal data from other ones...pretty awful bug in Windows... (See MSDN: Using SO_REUSEADDR and SO_EXCLUSIVEADDRUSE)
// Microsoft introduced SO_EXCLUSIVEADDRUSE for this.
// This makes sure hijacking the socket data is not possible.
// But it still allows the special behavior in case a bind() to a closed socket in TIME_WAIT state is possible. (See "Second effect of SO_REUSEADDR (TIME_WAIT)" above)
//
// MSDN: A socket that is using the SO_EXCLUSIVEADDRUSE option must be shut down properly prior to closing it. Failure to do so can cause a denial of service attack if the associated service needs to restart.
//
// Linux:
// Listening socket:
// SO_REUSEADDR does not have any effect for the "original idea" (see above). Linux is more restrictive than BSD sockets here.
// But it has the desired effect on closed sockets in TIME_WAIT state (see above).
//
// Client socket:
// Behaves like the original BSD idea and has the TIME_WAIT effect
//
// Kernel >= 3.9: To have "original idea" effect for listening sockets, since kernel 3.9 SO_REUSEPORT has been introduced.
//
// Normal TCP connection close:
// Client1 (C1), Client2 (C2)
// C1 -> C2 FIN
// C1 <- C2 ACK
// C1 <- C2 FIN
// C1 -> C2 ACK
// Socket of C1 (as the initiator of the close request) now is in TIME_WAIT state and can stay there several seconds/minutes
// so the <IPAddr>:<Port> combination is blocked for some time, after the socket has been closed
// As this is not acceptable for us as the DLL and other J-Link utilities must be able to be started / terminated multiple times in a row,
// we make use of SO_REUSEADDR for all of our listener sockets which need to bind() to a specific port
//
Sock = (SOCKET)hSocket;
r = setsockopt(Sock, SOL_SOCKET, -5, (char*)&_int1, sizeof(int)); // SO_EXCLUSIVEADDRUSE (Define not known in the VC6 headers, we use...)
if (r == 0) {
addr.sin_family = AF_INET;
addr.sin_port = htons((U16)Port);
addr.sin_addr.s_addr = htonl(IPAddr);
r = bind(Sock, (struct sockaddr*)&addr, sizeof(addr));
if (r == 0) {
r = listen(Sock, NumConnectionsQueued);
}
if (r) {
r = -1;
}
} else {
r = -1;
}
return r;
}
/*********************************************************************
*
* _SYS_SOCKET_IsReady
*
* Function description
* Checks if a socket that has been connected with _SYS_SOCKET_Connect() is ready.
* Mainly used on non-blocking sockets to check if they are ready to operate on.
* The procedure (non-blocking connect, then trying FIONREAD) is recommended by MS (MSDN).
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*
* Return value
* == 1 O.K., socket ready
* == 0 O.K., socket not ready yet
* < 0 Error
*/
static unsigned _SYS_SOCKET_IsReady(_SYS_SOCKET_HANDLE hSocket) {
SOCKET Sock;
int IsReady;
unsigned long v;
Sock = (SOCKET)hSocket;
ioctlsocket(Sock, FIONREAD, &v); // Check if socket is ready to read from
IsReady = v ? 1 : 0;
return IsReady;
}
/*********************************************************************
*
* _SYS_SOCKET_IsReadable
*
* Function description
* Checks if a socket that has been connected with _SYS_SOCKET_Connect() is readable.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*
* Return value
* == 1 O.K., socket readable
* == 0 O.K., socket not readable yet
* < 0 Error
*/
static int _SYS_SOCKET_IsReadable(_SYS_SOCKET_HANDLE hSocket, int TimeoutMs) {
SOCKET Sock;
struct timeval tv;
fd_set rfds;
fd_set efds;
int v;
//
//
// Two possible cases when opening a TCP connection:
// 1) On the other side, there is a server listening on the destination port
// 2) On the other side, there is NO server listening on the destination port
//
// Reg 1)
// In such cases, the socket is reported non-writeable for a few [us] up to a few [s] depending on if this is a localhost, LAN or internet connection
// After that period, the socket is reported as writeable
//
// Reg 2)
// In such cases, the following happens (S = Server):
// C -> S: SYN
// S -> C: RST ACK
// This means, the server has rejected the connection and closed it
// In such a case, we need to close the socket and try connecting again
//
// However, Windows is difficult regarding 2)...
// Usually an RST means "it's over, you can close the socket. There is nobody listening"
// Windows keeps the socket in connecting state, so IsReadable() IsWriteable() simply returns 0 instead of 1 (which would allow a following send/receive to return with error)
// In the background, Windows performs the SYN sending another 3 times in intervals of 500ms before giving up and reporting an error state
// Usually, the retransmissions are there in case we do not get an ACK from the other side and therefore must assume that the packet got lost
// Then also the interval time is doubled for each retransmission
// However, Microsoft decided to also retry it in case of RST ACK but without increasing the interval time
// This is explained here: https://support.microsoft.com/en-in/help/175523/info-winsock-tcp-connection-performance-to-unused-ports
//
// Unfortunately, even after this 1.5 seconds, Windows not necessarily reports the socket as readable/writable but instead returns "exceptional state" on select()
// Therefore, we also pass the "exceptional state" structure to select() to catch this case
//
Sock = (SOCKET)hSocket;
FD_ZERO(&rfds); // Zero init file descriptor list
FD_SET(Sock, &rfds); // Add socket to file descriptor list to be monitored by select()
FD_ZERO(&efds);
FD_SET(Sock, &efds);
tv.tv_sec = (long)(TimeoutMs / 1000);
tv.tv_usec = (TimeoutMs % 1000) * 1000;
v = select(0, &rfds, NULL, &efds, &tv); // > 0: in case of success, == 0: Timeout, < 0: Error
return v;
}
/*********************************************************************
*
* _SYS_SOCKET_AcceptEx
*
* Function description
* Waits for a connection (with timeout) on the given socket.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
* TimeoutMs Timeout in ms for waiting
*
* Return value
* >= 0 Handle to socket of new connection that has been established
* < 0 Error (_SYS_SOCKET_INVALID_HANDLE)
* -2 Timeout
*/
static _SYS_SOCKET_HANDLE _SYS_SOCKET_AcceptEx(_SYS_SOCKET_HANDLE hSocket, int TimeoutMs) {
SOCKET SockChild;
int r;
//
// accept() itself does not allow using timeouts
// Therefore we check readability first and then call accept() which should not block then
//
r = _SYS_SOCKET_IsReadable(hSocket, TimeoutMs);
if (r < 0) {
return _SYS_SOCKET_INVALID_HANDLE; // error
} else if (r == 0) {
return -2; // timeout
} else {
SockChild = accept((SOCKET)hSocket, NULL, NULL);
if (SockChild != INVALID_SOCKET) {
//
// If connection is successfully established, handle it as an implicit open(), so also init Winsock API as WSACleanup() is called on SocketClose()
// No matter if the socket has been opened via open() or accept()
//
_WSAStartup();
//
// Disable Nagle's algorithm to speed things up
// Nagle's algorithm prevents small packets from being transmitted and collects some time until data is actually sent out
//
setsockopt(SockChild, IPPROTO_TCP, TCP_NODELAY, (char*)&_int1, sizeof(int));
} else {
return _SYS_SOCKET_INVALID_HANDLE;
}
}
return (_SYS_SOCKET_HANDLE)SockChild;
}
/*********************************************************************
*
* _SYS_SOCKET_Receive
*
* Function description
* Receives data on the given socket.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*
* Return value
* >= 0: O.K., number of bytes received
* < 0: Error, see _SYS_SOCKET_ERR_*
*
* Notes
* (1) Returns as soon as something has been received (may be less than MaxNumBytes) or error happened
*/
static int _SYS_SOCKET_Receive(_SYS_SOCKET_HANDLE hSocket, void* pData, U32 MaxNumBytes) {
int r;
int Err;
SOCKET Sock;
Sock = (SOCKET)hSocket;
r = recv(Sock, (char*)pData, MaxNumBytes, 0);
if (r < 0) {
Err = WSAGetLastError();
switch (Err) {
case WSAEWOULDBLOCK:
r = _SYS_SOCKET_ERR_WOULDBLOCK;
break;
case WSAECONNRESET:
r = _SYS_SOCKET_ERR_CONNRESET;
break;
case WSAETIMEDOUT:
r = _SYS_SOCKET_ERR_TIMEDOUT;
break;
default:
r = _SYS_SOCKET_ERR_UNSPECIFIED;
}
}
return r;
}
/*********************************************************************
*
* _SYS_SOCKET_IsWriteable
*
* Function description
* Checks if a socket that has been connected with _SYS_SOCKET_Connect() is writeable.
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*
* Return value
* == 1 O.K., socket writeable
* == 0 O.K., socket not writeable yet
*/
static int _SYS_SOCKET_IsWriteable(_SYS_SOCKET_HANDLE hSocket, int TimeoutMs) {
SOCKET Sock;
struct timeval tv;
fd_set wfds;
fd_set efds;
int v;
//
//
// Two possible cases when opening a TCP connection:
// 1) On the other side, there is a server listening on the destination port
// 2) On the other side, there is NO server listening on the destination port
//
// Reg 1)
// In such cases, the socket is reported non-writeable for a few [us] up to a few [s] depending on if this is a localhost, LAN or internet connection
// After that period, the socket is reported as writeable
//
// Reg 2)
// In such cases, the following happens (S = Server):
// C -> S: SYN
// S -> C: RST ACK
// This means, the server has rejected the connection and closed it
// In such a case, we need to close the socket and try connecting again
//
// However, Windows is difficult regarding 2)...
// Usually an RST means "it's over, you can close the socket. There is nobody listening"
// Windows keeps the socket in connecting state, so IsReadable() IsWriteable() simply returns 0 instead of 1 (which would allow a following send/receive to return with error)
// In the background, Windows performs the SYN sending another 3 times in intervals of 500ms before giving up and reporting an error state
// Usually, the retransmissions are there in case we do not get an ACK from the other side and therefore must assume that the packet got lost
// Then also the interval time is doubled for each retransmission
// However, Microsoft decided to also retry it in case of RST ACK but without increasing the interval time
// This is explained here: https://support.microsoft.com/en-in/help/175523/info-winsock-tcp-connection-performance-to-unused-ports
//
// Unfortunately, even after this 1.5 seconds, Windows not necessarily reports the socket as readable/writable but instead returns "exceptional state" on select()
// Therefore, we also pass the "exceptional state" structure to select() to catch this case
//
Sock = (SOCKET)hSocket;
FD_ZERO(&wfds); // Zero init file descriptor list
FD_SET(Sock, &wfds); // Add socket to file descriptor list to be monitored by select()
FD_ZERO(&efds);
FD_SET(Sock, &efds);
tv.tv_sec = (long)(TimeoutMs / 1000);
tv.tv_usec = (TimeoutMs % 1000) * 1000;
v = select(0, NULL, &wfds, &efds, &tv); // > 0: in case of success, == 0: Timeout, < 0: Error
return v;
}
/*********************************************************************
*
* _SYS_SOCKET_Send
*
* Function description
* Sends data on the specified socket
*
* Parameters
* hSocket Handle to socket that has been returned by _SYS_SOCKET_OpenTCP() / _SYS_SOCKET_OpenUDP()
*
* Return value
* >= 0: O.K., number of bytes sent
* < 0: Error, see _SYS_SOCKET_ERR_*
*/
static int _SYS_SOCKET_Send(_SYS_SOCKET_HANDLE hSocket, const void* pData, U32 NumBytes) {
int r;
int Err;
SOCKET Sock;
Sock = (SOCKET)hSocket;
r = send(Sock, pData, NumBytes, 0);
if (r == SOCKET_ERROR) {
Err = WSAGetLastError();
r = (Err == WSAEWOULDBLOCK) ? _SYS_SOCKET_ERR_WOULDBLOCK : _SYS_SOCKET_ERR_UNSPECIFIED;
}
return r;
}
/*********************************************************************
*
* Local functions, SystemView
*
**********************************************************************
*/
/*********************************************************************
*
* _cbSendSystemDesc()
*
* Function description
* Sends SystemView description strings.
*/
static void _cbSendSystemDesc(void) {
SEGGER_SYSVIEW_SendSysDesc("N=" SYSVIEW_APP_NAME ",O=embOS,D=" SYSVIEW_DEVICE_NAME );
if (strlen(_sISRNames) > 0) {
SEGGER_SYSVIEW_SendSysDesc(_sISRNames);
}
}
/*********************************************************************
*
* Local functions, SystemView Communication Channel
*
**********************************************************************
*/
/*********************************************************************
*
* _SysViewCommThread
*
* Function description
* Function that handles TCP/IP connection and communication with SysView.
*
* Parameters
* pPara Expected to be the ID of the RTT <Up> channel used by SysView
*/
_SYS_THREAD_PROC_EX_TYPE _SysViewCommThread(void* pPara) {
_SYS_SOCKET_HANDLE hSockListen;
_SYS_SOCKET_HANDLE hSockSV;
int Result;
int ChannelID;
int v;
int r;
int NumBytes;
char acBuf[2048];
_SysViewCommThreadRunning = 1;
Result = 0;
ChannelID = (int)pPara;
hSockSV = _SYS_SOCKET_INVALID_HANDLE;
//
// Try and connect to SystemView instance
//
hSockListen = _SYS_SOCKET_OpenTCP();
if (hSockListen == _SYS_SOCKET_INVALID_HANDLE) { // Failed to open socket? => Done
Result = -1;
goto Done;
}
r = _SYS_SOCKET_ListenAtTCPAddr(hSockListen, _SYS_SOCKET_IP_ADDR_ANY, 19111, 1);
if (r < 0) { // Failed to set socket to listening? => Done
Result = -1;
goto Done;
}
//
// After a succesful connection, poll RTT buffer for data
//
do {
if (_CloseRequested) { // Close requested? => Stop systemview session
if (r == 1) { // Systemview session running?
r = _SYS_SOCKET_IsWriteable(hSockSV, 10);
if (r == 1 && v == 1) { // TCP/IP connection still established? => Stop systemview session
r = SEGGER_RTT_ReadUpBufferNoLock(ChannelID, acBuf, sizeof(acBuf));
if (r > 0) { // Read RTT data? => Send it via TCP/IP
NumBytes = _SYS_SOCKET_Send(hSockSV, acBuf, r); // We do not care if the send succeded or not as we are closing anyway.
_SYS_Sleep(10); // Give system view some time to receive <Stop> info before closing socket.
}
}
}
goto Done;
}
if (hSockSV > _SYS_SOCKET_INVALID_HANDLE) {
r = _SYS_SOCKET_IsWriteable(hSockSV, 10);
if (r == 0) { // Timeout
continue;
} else if (r < 0) { // Error
_SYS_SOCKET_Close(hSockSV);
hSockSV = _SYS_SOCKET_INVALID_HANDLE;
continue;
}
} else {
hSockSV = _SYS_SOCKET_AcceptEx(hSockListen, 100);
if (hSockSV < 0) {
continue;
}
r = _SYS_SOCKET_IsReady(hSockSV);
if (r != 1) { // Failed to connect? => Try again later
continue;
}
//
// Successful connection? => Start systemview session
// First, receive <Hello> message from SysView and send back own <Hello> message
//
r = _SYS_SOCKET_Receive(hSockSV, acBuf, SYSVIEW_COMM_APP_HELLO_SIZE);
if (r != SYSVIEW_COMM_APP_HELLO_SIZE) {
printf(" --- Failed to receive \"Hello\" message from SysView...\n");
}
r = _SYS_SOCKET_Send(hSockSV, _abHelloMsg, SYSVIEW_COMM_TARGET_HELLO_SIZE);
if (r != SYSVIEW_COMM_TARGET_HELLO_SIZE) {
printf(" --- Failed to send \"Hello\" message to SysView...\n");
}
}
//
// Connection established? => Handle communication
// Check for data sent by SysView
//
r = _SYS_SOCKET_IsReadable(hSockSV, 0);
if (r == 1) { // Data to read from SysView available?
r = _SYS_SOCKET_Receive(hSockSV, acBuf, 1); // Receive <NumBytes> to read
if (r != 1) { // Failed to receive data? => Connection lost
_SYS_SOCKET_Close(hSockSV);
hSockSV = _SYS_SOCKET_INVALID_HANDLE;
continue;
}
v = acBuf[0];
r = _SYS_SOCKET_Receive(hSockSV, acBuf, v); // Receive all data
if (r != v) { // Failed to receive data? => Connection lost
_SYS_SOCKET_Close(hSockSV);
hSockSV = _SYS_SOCKET_INVALID_HANDLE;
continue;
}
NumBytes = SEGGER_RTT_WriteDownBufferNoLock(ChannelID, &acBuf[0], r); // Write data into corresponding RTT buffer for application to read and handle accordingly
}
//
// Check for data to send to SysView
//
NumBytes = SEGGER_RTT_ReadUpBufferNoLock(ChannelID, &acBuf[0], sizeof(acBuf));
if (NumBytes > 0) { // Data to send available?
r = _SYS_SOCKET_Send(hSockSV, acBuf, NumBytes); // Send data to SysView
if (NumBytes != r) { // Failed to send data? => Connection lost
v = _SYS_GetLastError();
_SYS_SOCKET_Close(hSockSV);
hSockSV = _SYS_SOCKET_INVALID_HANDLE;
}
}
_SYS_Sleep(1); // Sleep for some time before polling again
} while (1);
Done:
//
// Clean up
//
if (hSockSV >= 0) {
_SYS_SOCKET_Close(hSockSV);
}
if (hSockListen >= 0) {
_SYS_SOCKET_Close(hSockListen);
}
_SysViewCommThreadRunning = 0;
return Result;
}
/*********************************************************************
*
* _SetupComm()
*
* Function description
* Setup communication channel.
*/
static void _SetupComm(void) {
int r;
U64 ThreadID;
//
// Initialize SysView communication
//
r = SEGGER_SYSVIEW_GetChannelID(); // Retrieve the ID of the RTT <Up> / <Down> channel used by SysView
_SYS_CreateThreadEx(_SysViewCommThread, (void*)r, &ThreadID, "SysView Communication Thread", 0); // Start thread handling TCP/IP connection and communication with SysView instance
}
/*********************************************************************
*
* Global functions
*
**********************************************************************
*/
/*********************************************************************
*
* SEGGER_SYSVIEW_Conf()
*
* Function description
* Configure SystemView and embOS for use of SystemView.
*/
void SEGGER_SYSVIEW_Conf(void) {
LARGE_INTEGER TSFreq;
//
// Get the performace counter frequency and scale it down to be < 1 GHz.
// (We can only handle cycles >= 1ns)
//
QueryPerformanceFrequency(&TSFreq);
_TSDiv = 1;
if (TSFreq.QuadPart > 1000000000LL) {
_TSDiv = (U32)(TSFreq.QuadPart / 1000000000LL);
if (TSFreq.QuadPart % 1000000000LL) {
_TSDiv++;
}
_TSFreq = TSFreq.QuadPart;
TSFreq.QuadPart /= _TSDiv;
}
SEGGER_SYSVIEW_Init(TSFreq.LowPart, TSFreq.LowPart,
&SYSVIEW_X_OS_TraceAPI, _cbSendSystemDesc);
OS_TRACE_SetAPI(&embOS_TraceAPI_SYSVIEW); // Configure embOS to use SYSVIEW.
#if SYSVIEW_START_ON_INIT
SEGGER_SYSVIEW_Start(); // Start recording to catch system initialization.
#endif
_SetupComm();
}
/*********************************************************************
*
* SEGGER_SYSVIEW_X_GetTimestamp()
*
* Function description
* Get the timestamp for SystemView.
* On Windows use the performance counter.
*/
U32 SEGGER_SYSVIEW_X_GetTimestamp(void) {
LARGE_INTEGER TS;
QueryPerformanceCounter(&TS);
if (_TSDiv > 1) {
TS.QuadPart /= _TSDiv;
}
return TS.LowPart;
}
/*********************************************************************
*
* SEGGER_SYSVIEW_X_GetInterruptId()
*
* Function description
* Get the "dummy" interrupt ID.
*/
U32 SEGGER_SYSVIEW_X_GetInterruptId(void) {
return GetCurrentThreadId();
}
/*********************************************************************
*
* SEGGER_SYSVIEW_X_SetISRName()
*
* Function description
* Informs SystemView about an ISR name.
*
* Parameters
* sName: ISR Name
*
* Additional information
* Must be called from an ISR after SEGGER_SYSVIEW_Conf() only.
* It uses the thread ID as an unique ID for SystemView.
*/
void SEGGER_SYSVIEW_X_SetISRName(const char* sName) {
static int CriticalSectionInitialized = 0;
char s[100];
//
// Make sure the used critical section is initialized.
//
if (CriticalSectionInitialized == 0) {
InitializeCriticalSection(&_csLockString);
CriticalSectionInitialized = 1;
}
//
// Check whether the string fits in the string buffer.
//
if (strlen(sName) < (sizeof(s) - 10)) {
//
// If this is the first entry we don't need the comma.
//
EnterCriticalSection(&_csLockString);
if (strlen(_sISRNames) == 0) {
sprintf(s, "I#%u=%s", (unsigned int)GetCurrentThreadId(), sName);
} else {
sprintf(s, ",I#%u=%s", (unsigned int)GetCurrentThreadId(), sName);
}
//
// Add new ISR name to the ISR name string and inform SystemView (if enough space is left in the string buffer).
//
if ((strlen(_sISRNames) + strlen(s) + 1) < MAX_ISRNAMES_LENGTH) {
strcat(_sISRNames, s);
//
// Send new description if SystemView is started.
//
if (SEGGER_SYSVIEW_IsStarted() > 0) {
SEGGER_SYSVIEW_SendSysDesc(_sISRNames);
}
}
LeaveCriticalSection(&_csLockString);
}
}
/*************************** End of file ****************************/