diff options
author | Martin Burnicki <martin.burnicki@meinberg.de> | 2023-02-28 23:12:41 +0100 |
---|---|---|
committer | Martin Burnicki <martin.burnicki@meinberg.de> | 2023-02-28 23:12:41 +0100 |
commit | 31dc5b6f6a84517a0e7b7f2fe4c49210771992fa (patch) | |
tree | 64e08b43ac2f72dc78bc674ff25d2d094883b816 | |
parent | b73cfc31e68b854f3f7dbf8837168c1c7b6eb31b (diff) | |
download | mbgreadtimestring-31dc5b6f6a84517a0e7b7f2fe4c49210771992fa.tar.gz mbgreadtimestring-31dc5b6f6a84517a0e7b7f2fe4c49210771992fa.zip |
Update existing mbglib files
-rw-r--r-- | mbglib/common/mbg_tgt.h | 910 | ||||
-rw-r--r-- | mbglib/common/mbg_tmo.h | 181 | ||||
-rw-r--r-- | mbglib/common/mbgserio.c | 1753 | ||||
-rw-r--r-- | mbglib/common/mbgserio.h | 410 | ||||
-rw-r--r-- | mbglib/common/words.h | 647 |
5 files changed, 3228 insertions, 673 deletions
diff --git a/mbglib/common/mbg_tgt.h b/mbglib/common/mbg_tgt.h index d5aba94..07bdcf8 100644 --- a/mbglib/common/mbg_tgt.h +++ b/mbglib/common/mbg_tgt.h @@ -1,7 +1,7 @@ /************************************************************************** * - * $Id: mbg_tgt.h 1.22 2009/10/01 08:20:50 martin REL_M $ + * $Id: mbg_tgt.h 1.54 2022/09/01 15:46:33 martin.burnicki REL_M $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * @@ -11,6 +11,129 @@ * * ----------------------------------------------------------------------- * $Log: mbg_tgt.h $ + * Revision 1.54 2022/09/01 15:46:33 martin.burnicki + * Suppress deprecated function warnings if + * NO_WARN_DEPRECATED is defined. + * Revision 1.53 2022/03/17 16:38:57Z martin.burnicki + * Added definitions MBG_DEV_HANDLE_FMT. + * Revision 1.52 2021/10/05 09:10:44Z martin + * Clean up definitions _MBG_API_ATTR_EXPORT and _MBG_API_ATTR_IMPORT. + * Revision 1.51 2021/09/13 08:59:55 martin + * New definitions _MBG_API_ATTR_EXPORT and _MBG_API_ATTR_IMPORT. + * Required if variables that are exported by a DLL are to be + * imported into a different DLL. + * Revision 1.50 2021/03/18 11:08:51 martin + * Updated some comments. + * Revision 1.49 2021/03/12 11:51:37 martin + * Corrected the wording of some comments. + * Revision 1.48 2021/02/10 13:34:35 martin + * Defined MBG_TGT_MISSING_STRUCT_TIMESPEC for CVI. + * Revision 1.47 2019/11/12 15:33:59 martin + * Added some doxygen comments. + * Revision 1.46 2019/08/28 12:52:02 martin + * Changes for mingw. + * Revision 1.45 2019/02/11 09:53:02Z martin + * Support the mingw build environment. + * Changed inclusion of winsock2.h/windows.h on Windows. + * Removed obsolete RCS log entries. + * Revision 1.44 2018/12/11 15:34:15 martin + * Older Visual Studio versions don't support _strtoi64(). + * Revision 1.43 2018/11/22 11:28:01Z martin + * New preprocessor symbol MBG_TGT_USE_IOCTL. + * Provide 'uintptr_t' for old Linux kernels. + * Removed obsolete Windows driver stuff. + * Revision 1.42 2018/09/20 09:40:21 martin + * Define MBG_TGT_MISSTNG_STRTOLL and + * MBG_TGT_MISSING_LLDDIV_T, if appropriate. + * Revision 1.41 2018/08/23 10:49:56Z martin + * Enhanced handling of Windows DDK builds. + * Revision 1.40 2018/07/02 16:57:49Z martin + * Removed obsolete define MBG_USE_MM_IO_FOR_PCI. + * Revision 1.39 2018/06/25 10:52:00 martin + * Support MBG_TGT_NO_TGT. + * Default settings for KERNEL_HAS_BOOL and KERNEL_HAS_TRUE_FALSE + * for Linux can be overridden by project settings. + * Renamed MBG_PRE64_PREFIX to MBG_PRI_64_PREFIX. + * MBG_PRI_64_PREFIX_L or MBG_PRI_64_PREFIX_LL can be used + * to override the defaults with specific project settings. + * MBG_TGT_HAS_DEV_FN is supported on Windows, but + * MBG_TGT_HAS_DEV_FN_BASE is not supported. + * Enhanced debug support for Windows kernel drivers. + * Include limits.h on Windows. + * Defined MBG_TGT_MISSING_STRUCT_TIMESPEC for Borland C. + * Defined __func__ for BC 5 and for DOS, and provide a + * common default declaration.. + * Updated some comments. + * Revision 1.38 2017/08/08 13:07:31 martin + * Proper fix for PRId64 and others. + * Define _CRT_SECURE_NO_WARNINGS only if not already defined. + * Revision 1.37 2017/07/10 07:08:45 thomas-b + * Define PRId64 if it is not defined in inttypes.h + * Revision 1.36 2017/07/04 12:35:11 martin + * Fixed build for Windows kernel space. + * Don't define ssize_t if HAVE_SSIZE_T is already defined. + * Added definition for _NO_MBG_API. + * Fixed missing 'bool' type for old Linux kernels. + * Fixed missing 'struct timespec' for DOS. + * Evaluate preprocessor symbol KERNEL_HAS_BOOL to avoid + * compiler errors due to duplicate definitions in specific + * Linux kernels patched by the distro maintainers. + * Provide ssize_t for C++Builder 5. + * Improved Visual Studio version checking. + * New define MBG_TGT_HAS_NODE_NAME. + * Revision 1.35 2016/08/05 12:21:34 martin + * Conditionally define a macro _DEPRECATED_BY which can be used to + * tag functions as deprecated, so compilers can emit appropriate warnings. + * New symbol MBG_TGT_HAS_ABS64. + * Moved some compatibility definitions from gpsserio.h here. + * Define ssize_t for Windows, if required. + * Conditionally provided struct timespec for Windows. + * Added compatible 64 bit type print format specifiers. + * Include inttypes.h for all targets providing also stdint.h. + * Added some MSVC version code information. + * Fixes for FreeBSD. + * Fixed some spelling. + * Tmp workaround for 2.6.32-5-sparc64. + * Proper fix required. + * Revision 1.34 2015/03/03 13:32:49 martin + * Provide __func__ for MS Visual Studio. + * Revision 1.33 2015/03/02 11:27:59Z martin + * Windows only: + * Define _CRT_SECURE_NO_WARNINGS to quiet compiler warnings. + * Define WIN32_LEAN_AND_MEAN only if it hasn't been defined before. + * Revision 1.32 2014/06/24 09:21:44 martin + * Update for newer C++Builder versions. + * Revision 1.31 2014/05/27 10:23:33 martin + * Finer control of which types are required for or already + * available on particular target systems. + * First definitions to support SunOS/Solaris. + * Revision 1.30 2014/04/01 12:55:58 martin + * Define MBG_TGT_WIN32 also for MS resource compiler. + * New target MBG_TGT_POSIX. + * Always include winsock2.h and windows.h for MBG_TGT_WIN32. + * Always include unistd.h for MBG_TGT_POSIX. + * Define empty __attribute__ macro for non-gcc environments. + * Revision 1.29 2013/02/01 14:50:46 martin + * Fixed a typo which caused an error with Borland CBuilder 5. + * Revision 1.28 2012/12/12 10:03:16Z martin + * Fix for Borland C 3.1. + * Revision 1.27 2012/11/29 12:03:14Z martin + * Moved definition of _no_macro_fnc() to words.h. + * Revision 1.26 2012/11/02 09:01:47Z martin + * Merged some stuff depending on the build environment here + * and cleaned up. + * Revision 1.25 2012/04/04 07:17:18 martin + * Treat QNX Neutrino as Unix target. + * Revision 1.24 2011/08/23 10:21:23 martin + * New symbol _NO_MBG_API_ATTR which can be used with functions + * which are going to be exported by a DLL, but actually aren't, yet. + * Revision 1.23 2011/08/19 10:47:00 martin + * Don't include stddef.h. + * Distinguish between different gcc target platforms. + * Initial support for IA64 platform. + * Support wchar_t for BSD. + * Defined _NO_USE_PACK_INTF for Sparc and IA64. + * Fixed typo in comment. * Revision 1.22 2009/10/01 08:20:50 martin * Fixed inline code support with different BC versions. * Revision 1.21 2009/09/01 10:34:23Z martin @@ -36,12 +159,12 @@ * Revision 1.13 2008/01/30 15:52:22 martin * Modified checking for availability of wchar_t. * Revision 1.13 2008/01/29 15:18:07Z martin - * Recognize DOS target under Watcom compilers. + * Recognize DOS target with Watcom compilers. * Flag Watcom C always supports wchar_t. * Revision 1.12 2008/01/17 09:38:50Z daniel - * Added macros to determine whether C language extensions + * Added macros to determine whether C language extensions * (e.g. C94, C99) are supported by the target environment. - * Added macro to check whether wchar_t and friends are + * Added macro to check whether wchar_t and friends are * supported, and some compatibility stuff. * Revision 1.11 2007/10/31 16:58:03 martin * Fixed __mbg_inline for Borland C (DOS). @@ -50,7 +173,7 @@ * Added MBG_PORT_HANDLE type for serial ports. * Added macros for unified inline code syntax. * Revision 1.9 2006/12/08 12:45:54Z martin - * Under Windows include ntddk.h rather than windows.h + * On Windows include ntddk.h rather than windows.h * if building kernel driver . * Revision 1.8 2006/10/25 12:20:45Z martin * Initial support for FreeBSD, NetBSD, and OpenBSD. @@ -67,7 +190,7 @@ * Revision 1.3 2003/04/09 13:37:20Z martin * Added definition for _MBG_API. * Revision 1.2 2003/02/24 16:08:45Z martin - * Don't setup for Win32 PNP if explicitely configured non-PNP. + * Don't setup for Win32 PNP if explicitly configured non-PNP. * Revision 1.1 2002/02/19 13:46:20Z MARTIN * Initial revision * @@ -79,7 +202,9 @@ /* Other headers to be included */ -#include <stddef.h> +#ifdef MBG_TGT_NO_TGT + #include <mbg_no_tgt.h> +#else #ifdef _MBG_TGT #define _ext @@ -90,11 +215,35 @@ /* Start of header body */ -#if defined( _CVI ) || defined( _CVI_ ) +#ifdef __cplusplus +extern "C" { +#endif + +// The prefix required in a printf() format string to print an +// int64_t type (i.e. "%li", "%lli", "I64i", etc.) may vary depending +// on the compiler type and version, and the associated run time library. +// For user space applications this is usually defined in the header +// files provided by the build environment, but for kernel code there +// are often no such defines, but there are some commonly used defaults. +// This is a hack that can be used to override these defaults in the +// project settings for kernel code, e.g. in the Makefile. +#if defined( MBG_PRI_64_PREFIX_L ) + #define MBG_PRI_64_PREFIX "l" +#elif defined( MBG_PRI_64_PREFIX_LL ) + #define MBG_PRI_64_PREFIX "ll" +#endif + + +#if defined( _CVI_ ) - #define MBG_TGT_WIN32 #define MBG_TGT_CVI + #if defined( _NI_mswin_ ) + #define MBG_TGT_WIN32 + #else + #error Unsupported CVI target platform. + #endif + #elif defined( _WIN32_WINNT ) // MS platform SDK @@ -104,7 +253,7 @@ #if ( _WIN32_WINNT >= 0x0500 ) // Win2k and above #if !defined( MBG_TGT_WIN32_NON_PNP ) - // only if not explicitely disabled + // only if not explicitly disabled #define MBG_TGT_WIN32_PNP #endif #endif @@ -130,6 +279,11 @@ // MS Visual C++ #define MBG_TGT_WIN32 +#elif defined( RC_INVOKED ) + + // MS resource compiler + #define MBG_TGT_WIN32 + #elif defined( __WINDOWS_386__ ) // Watcom C/C++ for target Win32 @@ -144,6 +298,7 @@ // Watcom C/C++ for target OS/2 #define MBG_TGT_OS2 + #define MBG_TGT_USE_IOCTL 1 #elif defined( __linux ) @@ -151,6 +306,10 @@ #define MBG_TGT_LINUX #define _GNU_SOURCE 1 + #if defined( __KERNEL__ ) + #define MBG_TGT_KERNEL + #endif + #elif defined( __FreeBSD__ ) // GCC for target FreeBSD @@ -163,9 +322,20 @@ #elif defined( __OpenBSD__ ) - // GCC for target FreeBSD + // GCC for target OpenBSD #define MBG_TGT_OPENBSD +#elif defined( __sun ) // Oracle Solaris or other SunOS derived operating system + + // __SUNPRO_C Oracle Solaris Studio C compiler, __SUNPRO_C value is the version number + // __SUNPRO_CC Oracle Solaris Studio C++ compiler, __SUNPRO_CC value is the version number + // __sparc generate code for SPARC (R) architecture (32-bit or 64-bit) + // __sparcv9 generate code for 64-bit SPARC architecture + // __i386 generate code for 32-bit x86 architecture + // __amd64 generate code for 64-bit x64 architecture + + #define MBG_TGT_SUNOS + #elif defined( __QNX__ ) // any compiler for target QNX @@ -189,39 +359,481 @@ #endif -// Some definitions which depend on the type of compiler ... -#if defined( __GNUC__ ) - #define __mbg_inline __inline__ +#if defined( MBG_TGT_FREEBSD ) \ + || defined( MBG_TGT_NETBSD ) \ + || defined( MBG_TGT_OPENBSD ) + #define MBG_TGT_BSD + + #if defined( _KERNEL ) + #define MBG_TGT_KERNEL + #endif - #define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 ) +#endif + +#if defined( MBG_TGT_LINUX ) \ + || defined( MBG_TGT_BSD ) \ + || defined( MBG_TGT_QNX_NTO ) \ + || defined( MBG_TGT_SUNOS ) + + #define MBG_TGT_POSIX + #define MBG_TGT_UNIX + +#endif + +#if defined( MBG_TGT_WIN32 ) + + #if !defined( _CRT_SECURE_NO_WARNINGS ) + #define _CRT_SECURE_NO_WARNINGS 1 + #endif + + #include <limits.h> + + #define MBG_TGT_HAS_DEV_FN 1 + #define MBG_TGT_HAS_DEV_FN_BASE 0 + #define MBG_TGT_USE_IOCTL 1 + +#endif + + +// Some definitions depending on the build environment ... + +#define FUNC_UNKNOWN "func_???" + +#if defined( __GNUC__ ) || defined( __clang__ ) + + #if defined( __clang__ ) + #define _CLANG_VERSION ( __clang_major__ * 10000 \ + + __clang_minor__ * 100 \ + + __clang_patchlevel__ ) + #endif // defined( __clang__ ) + + #define _GCC_VERSION ( __GNUC__ * 10000 \ + + __GNUC_MINOR__ * 100 \ + + __GNUC_PATCHLEVEL__ ) + + #if defined( __MINGW32__ ) + #define MBG_TGT_MINGW + #endif + + #if defined( __MINGW64__ ) + #define MBG_TGT_MINGW + #endif + + #if defined( MBG_TGT_MINGW ) && defined( __MINGW_EXTENSION ) + // MSYS2 / MinGW defines __MINGW_EXTENSION + // and provides _abs64() and some other stuff, + // but the standard MinGW environment does not. + #if !defined( MBG_TGT_HAS_ABS64 ) + #define MBG_TGT_HAS_ABS64 1 + #endif + #endif + + #if defined( __i386__ ) + + #define MBG_ARCH_I386 + #define MBG_ARCH_X86 + + #elif defined( __x86_64__ ) - #if defined( __sparc__ ) + #define MBG_ARCH_X86_64 + #define MBG_ARCH_X86 + + #elif defined( __ia64__ ) + + #define MBG_ARCH_IA64 + + #define _NO_USE_PACK_INTF + + #elif defined( __sparc__ ) #define MBG_ARCH_SPARC - #define _MBG_ARCH_DEFINED + + #define _NO_USE_PACK_INTF #elif defined( __arm__ ) #define MBG_ARCH_ARM - #define _MBG_ARCH_DEFINED #endif + #if defined( MBG_TGT_LINUX ) + + #if defined( MBG_TGT_KERNEL ) + + #include <linux/types.h> + #include <linux/version.h> + + #if ( LINUX_VERSION_CODE <= KERNEL_VERSION( 2, 6, 4 ) ) || \ + ( LINUX_VERSION_CODE >= KERNEL_VERSION( 2, 6, 4 ) ) // must be true for 2.6.32-5-sparc64 + #define _ULONG_DEFINED 1 + #define _USHORT_DEFINED 1 + #define _UINT_DEFINED 1 + #endif + + // The 'bool' type is supported by the vanilla Linux kernel 2.6.19 and later. + #if ( LINUX_VERSION_CODE < KERNEL_VERSION( 2, 6, 19 ) ) + + // However, looks like at least the RedHat folks have backported this + // to 2.6.18, so KERNEL_HAS_BOOL can be defined to avoid a compiler error + // due to a duplicate definition. + #if !defined( KERNEL_HAS_BOOL ) + typedef _Bool bool; + #define bool bool + #endif + + // Similar for 'true' and 'false'. + #if !defined( KERNEL_HAS_TRUE_FALSE ) + enum + { + false, + true + }; + #define falso false + #define true true + #endif + + #endif + + // The 'uintptr_t' type has been introduced + // after v2.6.24-rc1 and before v2.6.24-rc2. + #if ( LINUX_VERSION_CODE < KERNEL_VERSION( 2, 6, 24 ) ) + typedef unsigned long uintptr_t; + #define uintptr_t uintptr_t + #endif + + // String formatter codes like "PRIi64" for int64_t types + // don't seem to be defined in Linux kernel mode, so we + // define the required "ll" or "l" modifier here as a hack. + // Code below uses this to define appropriate "PRIi64"-like + // definitions as "lli" or "li". + #if !defined( MBG_PRI_64_PREFIX ) + // Please note that e.g. Linux 2.6.16 on Itanium (ia64) + // with gcc 4.1.2 expects "%li" to print an int64_t. + // This may be a bug in the specific gcc version, though. + #define MBG_PRI_64_PREFIX "ll" + #endif + + #else + + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + + #if defined( __u_char_defined ) + #define _ULONG_DEFINED 1 + #define _USHORT_DEFINED 1 + #define _UINT_DEFINED 1 + #endif + + #endif + + #define MBG_TGT_HAS_DEV_FN 1 + #define MBG_TGT_HAS_DEV_FN_BASE 1 + #define MBG_TGT_USE_IOCTL 1 + + #elif defined( MBG_TGT_BSD ) + + #if defined( MBG_TGT_KERNEL ) + #include <sys/types.h> + #else + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + #endif + + #define MBG_TGT_HAS_DEV_FN 1 + #define MBG_TGT_HAS_DEV_FN_BASE 1 + #define MBG_TGT_USE_IOCTL 1 + + #if defined( MBG_TGT_FREEBSD ) && defined( MBG_TGT_KERNEL ) + #if !defined( MBG_PRI_64_PREFIX ) + // String formatter codes like "PRIi64" for int64_t types + // don't seem to be defined in FreeBSD kernel mode, so we + // define the required "ll" or "l" modifier here as a hack. + // Code below uses this to define appropriate "PRIi64"-like + // definitions as "lli" or "li". + #define MBG_PRI_64_PREFIX "l" + #endif + #endif + + #elif defined( MBG_TGT_QNX_NTO ) // QNX 6.x (Neutrino) + + #include <unistd.h> + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + + #else + + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + + #endif + + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + + #define MBG_TGT_HAS_WCHAR_T 1 + + #if !defined( NO_WARN_DEPRECATED ) + #if defined( __clang__ ) + #define _DEPRECATED_BY( _s ) __attribute__((deprecated("use \"" _s "\" instead"))) // works with clang 3.4.1 + #elif ( _GCC_VERSION > 40500 ) // gcc 4.5.0 and newer + #define _DEPRECATED_BY( _s ) __attribute__((deprecated("use \"" _s "\" instead"))) + #elif ( _GCC_VERSION > 30100 ) // gcc 3.1 and newer + #define _DEPRECATED_BY( _s ) __attribute__((deprecated)) + #else + // Not supported at all, use empty default definiton below. + #endif + #endif + + #if ( _GCC_VERSION > 30100 ) // gcc 3.1 and newer + #define __mbg_inline __inline__ __attribute__((always_inline)) + #else + // Not supported at all, use empty default definiton below. + #define __mbg_inline __inline__ + #endif + #elif defined( _MSC_VER ) + // Known predifined MS compiler version codes: + // 1910: MSVC++ 15.0 (Visual Studio 2017) + // 1900: MSVC++ 14.0 (Visual Studio 2015) + // 1800: MSVC++ 12.0 (Visual Studio 2013) + // 1700: MSVC++ 11.0 (Visual Studio 2012) + // 1600: MSVC++ 10.0 (Visual Studio 2010) + // 1500: MSVC++ 9.0 (Visual Studio 2008) + // 1400: MSVC++ 8.0 (Visual Studio 2005, Windows Server 2003 SP1 DDK - AMD64) + // 1310: MSVC++ 7.1 (Visual Studio .NET 2003, Windows Server 2003 DDK) + // 1300: MSVC++ 7.0 (Visual Studio .NET 2002, Windows XP SP1 DDK / DDK 2600) + // 1200: MSVC++ 6.0 + // 1100: MSVC++ 5.0 + + // Enable this to get compile-time messages on the compiler version + #if 0 + #if ( _MSC_VER >= 1910 ) + #error >= 1910: MSVC++ 15.0 (Visual Studio 2017), or later + #elif ( _MSC_VER >= 1900 ) + #error 1900: MSVC++ 14.0 (Visual Studio 2015) + #elif ( _MSC_VER >= 1800 ) + #error 1800: MSVC++ 12.0 (Visual Studio 2013) + #elif ( _MSC_VER >= 1700 ) + #error 1700: MSVC++ 11.0 (Visual Studio 2012) + #elif ( _MSC_VER >= 1600 ) + #error 1600: MSVC++ 10.0 (Visual Studio 2010) + #elif ( _MSC_VER >= 1500 ) + #error 1500: MSVC++ 9.0 (Visual Studio 2008) + #elif ( _MSC_VER >= 1400 ) + #error STRINGIFY( _MSC_VER ) MSVC++ 8.0 (Visual Studio 2005, Windows Server 2003 SP1 DDK - AMD64) + #elif ( _MSC_VER >= 1310 ) + #error 1310: MSVC++ 7.1 (Visual Studio .NET 2003, Windows Server 2003 DDK) + #elif ( _MSC_VER >= 1300 ) + #error 1300: MSVC++ 7.0 (Visual Studio .NET 2002, Windows XP SP1 DDK / DDK 2600) + #elif ( _MSC_VER >= 1200 ) + #error 1200: MSVC++ 6.0 + #elif ( _MSC_VER >= 1100 ) + #error 1100: MSVC++ 5.0 + #else + #error <1100: Older than MSVC 4 + #endif + #endif + + // "struct timespec" is supported only since VS2015 + // If it is, also the symbol TIME_UTC should be defined. + // Functions to read the current time as struct timespec + // are timespec_get() and friends, which are also only provided + // by VS2015 and later. + // As of VS2015, only TIME_UTC is supported to read + // the UTC system time, there is no equivalent for + // the POSIX CLOCK_MONOTONIC. However, QPC can be used + // to get monotonic time stamps and intervals. + #if ( _MSC_VER < 1900 ) + #if !defined( HAVE_STRUCT_TIMESPEC ) + #define MBG_TGT_MISSING_STRUCT_TIMESPEC 1 + #endif + #endif + + #if ( _MSC_VER >= 1600 ) // TODO Even 1600 probably doesn't support this. + #include <stdint.h> + #include <inttypes.h> + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + + #if !defined( PRId64 ) + #define MBG_PRI_64_PREFIX "I64" + #endif + #else + #define MBG_TGT_HAS_INT_8_16_32 1 + #define MBG_PRI_64_PREFIX "I64" + #endif + + #if !defined( __cplusplus ) + // no bool support anyway + #define MBG_TGT_MISSING_BOOL_TYPE 1 + #endif + + #define MBG_TGT_HAS_WCHAR_T 1 + #define __mbg_inline __forceinline - #define MBG_TGT_HAS_WCHAR_T 1 + // At least up to VS2008 the C99 builtin symbol __func__ + // is not supported. Some VS versions support __FUNCTION__ + // instead, but at least VC6 doesn't support this, either. + // of the current function instead. + #if ( _MSC_VER >= 1300 ) + #define __func__ __FUNCTION__ + #else + #define __func__ FUNC_UNKNOWN + #endif + + // The "deprecated" attribute should be supported since Visual Studio 2005, + // but doesn't seem to be supported e.g. by the compiler shipped with the + // "Windows Server 2003 SP1 DDK", which is used to build kernel drivers + // and defines the same _MSC_VER number as VS2005. For now we assume + // that this is supported by compilers shipped with newer DDKs. + // The _DDK_BUILD_ symbol has to be explicitly defined in the "sources" + // file of the DDK project. + #if ( ( _MSC_VER >= 1500 ) || \ + ( ( _MSC_VER >= 1400 ) && !defined( _DDK_BUILD_ ) ) ) + #if !defined( NO_WARN_DEPRECATED ) + #define _DEPRECATED_BY( _s ) __declspec(deprecated("deprecated, use \"" _s "\"")) + #endif + #endif + + // availability of _abs64() + #if ( _MSC_VER >= 1310 ) + // This is supported at least since Visual Studio 2008 + // and Windows Server 2003 SP1 DDK. + + #if !defined( MBG_TGT_HAS_ABS64 ) + #define MBG_TGT_HAS_ABS64 1 + #endif + #endif + + #if !defined ( HAVE_SSIZE_T ) + + // ssize_t support + #if ( _MSC_VER >= 1500 ) + // ssize_t may not be defined, but SSIZE_T is + #include <basetsd.h> + typedef SSIZE_T ssize_t; + #else + // At least VC6 hasn't SIZE_T, either, but size_t + // is typedef'ed as unsigned int, so we just typedef + // the signed variant here. + typedef int ssize_t; + #endif -#elif defined( _CVI ) || defined( _CVI_ ) + #define HAVE_SSIZE_T 1 - // Inline code is not supported. + #endif // !defined ( HAVE_SSIZE_T ) - #define MBG_TGT_HAS_WCHAR_T 0 + #if ( _MSC_VER <= 1500 ) + // At least MSVC++ 9.0 / Visual Studio 2008 and older + // don't provide lldiv_t and lldiv(). + #define MBG_TGT_MISSING_LLDIV_T 1 + #endif -#elif defined( __BORLANDC__ ) + #if ( _MSC_VER <= 1400 ) + #define MBG_TGT_MISSING_STRTOLL 1 + #elif ( _MSC_VER <= 1600 ) + // At least MSVC++ 10.0 / Visual Studio 2010 and older + // don't provide strtoll(), but may provide _strtoi64 instead. + #define strtoll _strtoi64 + #endif + +#elif defined( _CVI_ ) + + // 1000 for CVI v10.0 (CVI 2010) + // 911 for CVI v9.1.1 (CVI 2009 SP1) + // 910 for CVI v9.1 (CVI 2009) + // 310 for CVI v3.1 + // 301 for CVI v3.0.1 + // 1 for CVI v3.0 + + #if ( _CVI_ >= 910 ) + // LabWindows/CVI 2009 is the first version providing stdint.h. + #include <stdint.h> + #include <inttypes.h> + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + #else + #define USE_LONG_FOR_INT32 1 + #endif + + // As of LabWindows/CVI 2010, stdbool.h is still missing. + #define MBG_TGT_MISSING_BOOL_TYPE 1 + + #define MBG_TGT_HAS_WCHAR_T 0 + + // Inline code is not supported, though the inline keyword + // is silently accepted since CVI v9.0 + +#elif defined( __BORLANDC__ ) // or __CODEGEARC__ in newer versions + + // 0x0200 Borland C/C++ 2.0 + // 0x0400 Borland C/C++ 3.0 + // 0x0410 Borland C/C++ 3.1 + // 0x0550 Borland C/C++ 5.5 (C++Builder 5.0) + + // Next codes are in addition defined as __CODEGEARC__ + // See http://docwiki.embarcadero.com + + // 0x0570 for Borland Developer Studio 2006 (BDS 2006) + // 0x0590 for C++Builder 2007 + // 0x0591 for update 1 to C++Builder 2007 + // 0x0592 for RAD Studio 2007 + // 0x0593 for the December update to RAD Studio 2007 + // 0x0610 for C++Builder 2009 and for C++Builder 2009 Update 1 + // 0x0620 for C++Builder 2010 and for C++Builder 2010 Update 1 + // 0x0621 for C++Builder 2010 Update 2 + // 0x0630 for C++Builder XE + // 0x0631 for C++Builder XE Update 1 + // 0x0640 for C++Builder XE2 + // 0x0650 for C++Builder XE3 + + #if ( __BORLANDC__ >= 0x630 ) + // C++Builder XE starts to provide stdbool.h + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + #elif ( __BORLANDC__ >= 0x570 ) + // BDS/Borland C++ Builder 2006 starts to provide at least stdint.h + #include <stdint.h> + #include <inttypes.h> + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + #if !defined( __cplusplus ) + #define MBG_TGT_MISSING_BOOL_TYPE 1 + #endif + #elif ( __BORLANDC__ >= 0x0550 ) + #define MBG_TGT_HAS_INT_8_16_32 1 + #define MBG_PRI_64_PREFIX "I64" + #if !defined( __cplusplus ) + #define MBG_TGT_MISSING_BOOL_TYPE 1 + #endif + #define __func__ FUNC_UNKNOWN + #else // e.g. BC 3.1 or earlier + #if ( __BORLANDC__ <= 0x410 ) + #define MBG_TGT_MISSING_64_BIT_TYPES 1 + #define MBG_TGT_MISSING_BOOL_TYPE 1 + #define USE_LONG_FOR_INT32 1 + #define MBG_TGT_MISSING_STRUCT_TIMESPEC 1 + #define __func__ __FILE__ ":" STRINGIFY(__LINE__) + + typedef int ssize_t; + #endif + #endif + + #if ( __BORLANDC__ <= 0x0550 ) + // At least CBuilder 5 and earlier. + #define MBG_TGT_MISSING_STRTOLL 1 + #define MBG_TGT_MISSING_LLDIV_T 1 + #endif + + #define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 ) + + #define MBG_TGT_MISSING_STRUCT_TIMESPEC 1 #if defined( __cplusplus ) #define __mbg_inline inline // standard C++ syntax @@ -231,44 +843,76 @@ #define __mbg_inline // up to BC3.1 not supported for C #endif - #define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 ) + #if !defined ( HAVE_SSIZE_T ) + typedef int ssize_t; // required at least for C++ Builder 5 + #define HAVE_SSIZE_T 1 + #endif #elif defined( __WATCOMC__ ) - #define __mbg_inline _inline + // 1050 v10.5 + // 1100 v11.0 + // 1200 Open Watcom C++ v1.0 + // 1230 Open Watcom C++ v1.3 + // 1270 Open Watcom C++ v1.7 - #define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 ) + #if defined( MBG_TGT_QNX ) // QNX 4.x -#endif + #include <sys/types.h> + + #define MBG_TGT_MISSING_64_BIT_TYPES 1 + #elif ( __WATCOMC__ > 1230 ) // Open Watcom C 1.3 and above + #include <stdint.h> + #include <inttypes.h> -// Currently we support only Sparc and i386/x86_64 architectures, -// so unless we have explicitely found sparc we assume i386. + #elif !defined( __WATCOM_INT64__ ) // Watcom C 11 + + #define MBG_TGT_MISSING_64_BIT_TYPES 1 + + #endif + + #define MBG_TGT_HAS_WCHAR_T defined( MBG_TGT_WIN32 ) + + #define __mbg_inline _inline -#if !defined( _MBG_ARCH_DEFINED ) - #define MBG_ARCH_I386 #endif -#if defined( MBG_TGT_FREEBSD ) \ - || defined( MBG_TGT_NETBSD ) \ - || defined( MBG_TGT_OPENBSD ) - #define MBG_TGT_BSD +#if !defined( MBG_TGT_USE_IOCTL ) + #define MBG_TGT_USE_IOCTL 0 #endif -#if defined( MBG_TGT_LINUX ) \ - || defined( MBG_TGT_BSD ) \ - || defined( MBG_TGT_QNX_NTO ) - #define MBG_TGT_UNIX + +// If the build environment doesn't provide an inttypes.h file +// with print format specifiers for 64 bit fixed size types, +// MBG_PRI_64_PREFIX should be defined, which is used to +// define our own C99 compatible format specifiers. +// Maybe similar definitions are required for 32, 16, +// and 8 bit fixed size types. +#if defined( MBG_PRI_64_PREFIX ) + #define PRIi64 MBG_PRI_64_PREFIX "i" + #define PRId64 MBG_PRI_64_PREFIX "d" + #define PRIo64 MBG_PRI_64_PREFIX "o" + #define PRIu64 MBG_PRI_64_PREFIX "u" + #define PRIx64 MBG_PRI_64_PREFIX "x" + #define PRIX64 MBG_PRI_64_PREFIX "X" +#endif + +#if !defined( __GNUC__ ) && !defined( __attribute__ ) + #define __attribute__( _x ) #endif +#if !defined( _DEPRECATED_BY ) + #define _DEPRECATED_BY( _s ) // empty definition +#endif #if defined( MBG_TGT_WIN32 ) #if defined( _AMD64_ ) - // This is used for AMD64 architecture and for + // This is used for AMD64 architecture and for // Intel XEON CPUs with 64 bit extension. #define MBG_TGT_WIN32_PNP_X64 #define WIN32_FLAVOR "x64" @@ -278,11 +922,29 @@ #endif #if defined( _KDD_ ) + #define MBG_TGT_KERNEL + #include <ntddk.h> + + #if defined( DBG ) && DBG + #define DEBUG 1 + #endif + + #define _MBG_API #else // This must not be used for kernel drivers. + + // Prevent inclusion of obsolete winsock.h in windows.h + #if !defined( WIN32_LEAN_AND_MEAN ) + #define WIN32_LEAN_AND_MEAN 1 + #endif + + #include <winsock2.h> #include <windows.h> + #include <ws2tcpip.h> + typedef HANDLE MBG_HANDLE; + #define MBG_DEV_HANDLE_FMT "%p" #define MBG_INVALID_HANDLE INVALID_HANDLE_VALUE @@ -290,32 +952,61 @@ // CVI uses an own set of functions to support serial ports typedef int MBG_PORT_HANDLE; #define MBG_INVALID_PORT_HANDLE -1 + + // At least CVI 13.02 doesn't provide a declaration of struct timespec. + #define MBG_TGT_MISSING_STRUCT_TIMESPEC 1 #else typedef HANDLE MBG_PORT_HANDLE; #endif // The DWORD_PTR type is not defined in the headers shipping - // with VC6. However, if the SDK is installed then the SDK's + // with VC6. However, if the SDK is installed, the SDK's // headers may declare this type. This is at least the case // in the Oct 2001 SDK which also defines the symbol _W64. #if !defined( _W64 ) typedef DWORD DWORD_PTR; #endif + // socklen_t support + #if ( _MSC_VER < 1500 ) + // At least VS2008 has a socklen_t type + typedef int socklen_t; + #endif + + #define _MBG_API WINAPI + #endif - #define _MBG_API WINAPI + #if !defined( MBG_TGT_MINGW ) // Not required for MinGW + // The definitions below have to be used if a variable + // is exported from one DLL, and imported by a different + // DLL. It has to be used in the header file with the _ext + // symbol, depending on whether the file is included by + // its "parent" .c or .cpp file which implements the + // variable, or by a different file which just references + // the exported variable. + #define _MBG_API_ATTR_EXPORT __declspec( dllexport ) + #define _MBG_API_ATTR_IMPORT __declspec( dllimport ) + #endif + // The old code below works well when exporting functions + // from a DLL, but fails if a variable is exported from + // one DLL, and imported by a different DLL. #if defined( MBG_LIB_EXPORT ) - #define _MBG_API_ATTR __declspec( dllexport ) + #define _MBG_API_ATTR _MBG_API_ATTR_EXPORT #else - #define _MBG_API_ATTR __declspec( dllimport ) + #define _MBG_API_ATTR _MBG_API_ATTR_IMPORT #endif -#elif defined( MBG_TGT_UNIX ) +#elif defined( MBG_TGT_POSIX ) + + #if !defined( MBG_TGT_KERNEL ) + #include <unistd.h> + #endif typedef int MBG_HANDLE; typedef int MBG_PORT_HANDLE; + #define MBG_DEV_HANDLE_FMT "%i" #define MBG_INVALID_HANDLE -1 @@ -323,12 +1014,66 @@ typedef int MBG_HANDLE; typedef int MBG_PORT_HANDLE; + #define MBG_DEV_HANDLE_FMT "%i" #define MBG_INVALID_HANDLE -1 #endif +/** + * @brief A portable socket descriptor type returned by socket(). + * + * @see ::MBG_INVALID_SOCK_FD for the value returned in case of error. + */ +#if defined( MBG_TGT_WIN32 ) // && !defined( MBG_TGT_MINGW ) + #if !defined( MBG_TGT_KERNEL ) // we don't need this in kernel space + /// On Windows usually evaluates to @a UINT_PTR, which in turn + /// evaluates to <em>(unsigned int)</em>, or <em>(unsigned __int64)</em>. + typedef SOCKET MBG_SOCK_FD; + #endif +#elif defined( MBG_TGT_POSIX ) + typedef int MBG_SOCK_FD; //### TODO + //### TODO typedef int SOCKET; +#endif + + + +/** + * @brief The value returned by the socket() function in case of error. + * + * On Windows usually evaluates to (SOCKET)(~0), + * since the SOCKET type is unsigned. + * For POSIX systems the value is usually -1. + * + * If the @a socket() call returns this value, + * call ::mbg_get_last_socket_error to retrieve + * one of the portable @ref MBG_ERROR_CODES. + * + * @see ::MBG_SOCK_FD for a portable socket descriptor type. + */ +#if defined( MBG_TGT_WIN32 ) + #define MBG_INVALID_SOCK_FD INVALID_SOCKET // usually evaluates to (SOCKET)(~0) since SOCKET is unsigned +#elif defined( MBG_TGT_POSIX ) + #define MBG_INVALID_SOCK_FD -1 +#endif + + + +/** + * @brief The return code of networking functions in case of error. + * + * The code is returned by functions like @a select(), + * @a recv(), etc. in case of error. + */ +#if defined( MBG_TGT_WIN32 ) + #define MBG_SOCKET_ERR_RETVAL SOCKET_ERROR // usually evaluates to -1 +#elif defined( MBG_TGT_POSIX ) + #define MBG_SOCKET_ERR_RETVAL -1 +#endif + + + #if !defined( _MBG_API ) #define _MBG_API #endif @@ -337,28 +1082,61 @@ #define _MBG_API_ATTR #endif +#if !defined( _MBG_API_ATTR_EXPORT ) + #define _MBG_API_ATTR_EXPORT +#endif + +#if !defined( _MBG_API_ATTR_IMPORT ) + #define _MBG_API_ATTR_IMPORT +#endif + +#if !defined( _NO_MBG_API ) + #define _NO_MBG_API +#endif + +#if !defined( _NO_MBG_API_ATTR ) + #define _NO_MBG_API_ATTR +#endif + #if !defined( MBG_INVALID_PORT_HANDLE ) #define MBG_INVALID_PORT_HANDLE MBG_INVALID_HANDLE #endif -#if !defined( MBG_USE_MM_IO_FOR_PCI ) - #if ( 0 || defined( MBG_ARCH_SPARC ) ) - #define MBG_USE_MM_IO_FOR_PCI 1 - #else - #define MBG_USE_MM_IO_FOR_PCI 0 - #endif +#if !defined( MBG_TGT_HAS_DEV_FN ) + #define MBG_TGT_HAS_DEV_FN 0 #endif - -#if !defined( _nop_macro_fnc ) - #define _nop_macro_fnc() do {} while (0) +#if !defined( MBG_TGT_HAS_DEV_FN_BASE ) + #define MBG_TGT_HAS_DEV_FN_BASE 0 #endif +#if defined( MBG_TGT_MISSING_STRUCT_TIMESPEC ) + + #include <time.h> + + struct timespec + { + time_t tv_sec; + long tv_nsec; + }; + +#endif // defined( MBG_TGT_MISSING_STRUCT_TIMESPEC ) + // The macros below are defined in order to be able to check if // certain C language extensions are available on the target system: -#define MBG_TGT_C94 ( defined( __STDC_VERSION__ ) && ( __STDC_VERSION__ >= 199409L ) ) -#define MBG_TGT_C99 ( defined( __STDC_VERSION__ ) && ( __STDC_VERSION__ >= 199901L ) ) +#if defined( __STDC_VERSION__ ) && ( __STDC_VERSION__ >= 199409L ) + #define MBG_TGT_C94 1 +#else + #define MBG_TGT_C94 0 +#endif + + +#if defined( __STDC_VERSION__ ) && ( __STDC_VERSION__ >= 199901L ) + #define MBG_TGT_C99 1 +#else + #define MBG_TGT_C99 0 +#endif // Check if wchar_t is supported #if !defined( MBG_TGT_HAS_WCHAR_T ) @@ -371,24 +1149,13 @@ // However, some functions may be missing (e.g. snwprintf()). #if !defined( _WCHAR_T ) /* BC3.1 */ \ && !defined( _WCHAR_T_DEFINED_ ) /* WC11 */ - //##++ #define _WCHAR_T + #define _WCHAR_T #define wchar_t char #endif #endif -/* End of header body */ - -#undef _ext - - -/* function prototypes: */ - -#ifdef __cplusplus -extern "C" { -#endif - /* ----- function prototypes begin ----- */ /* This section was generated automatically */ @@ -402,5 +1169,10 @@ extern "C" { } #endif +/* End of header body */ + +#undef _ext + +#endif // !defined( MBG_TGT_NO_TGT ) -#endif /* _MBG_TGT_H */ +#endif // !defined( _MBG_TGT_H ) diff --git a/mbglib/common/mbg_tmo.h b/mbglib/common/mbg_tmo.h index daf4a25..c2c670e 100644 --- a/mbglib/common/mbg_tmo.h +++ b/mbglib/common/mbg_tmo.h @@ -1,7 +1,7 @@ /************************************************************************** * - * $Id: mbg_tmo.h 1.2 2009/09/01 10:38:21 martin REL_M $ + * $Id: mbg_tmo.h 1.9 2018/09/21 09:01:53 martin REL_M $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * @@ -10,7 +10,28 @@ * * ----------------------------------------------------------------------- * $Log: mbg_tmo.h $ - * Revision 1.2 2009/09/01 10:38:21 martin + * Revision 1.9 2018/09/21 09:01:53 martin + * New function mbg_tmo_add_ms(). + * Renamed mbgserio_msec_to_timeval to mbg_msec_to_timeval. + * Use clock_gettime( CLOCK_MONOTONIC, ... ) for timeouts. + * Include mbgtime.h. + * Windows headers are now included elsewhere. + * Use NSEC_PER_SEC symbol instead of numeric value. + * Check symbol MBG_TGT_POSIX rather than MBG_TGT_POSIX. + * Fixed some compiler warnings. + * Revision 1.8 2013/12/11 12:08:29 martin + * Fixed Windows build. + * Revision 1.7 2012/11/02 09:04:36Z martin + * Fix to have struct timeval defined under Windows. + * Revision 1.6 2012/03/16 11:56:23 martin + * Added mbg_tmo_delta_t(). + * Revision 1.5 2011/11/28 15:26:47 martin + * Enabled mbgserio_msec_to_timeval() for Windows. + * Revision 1.4 2011/01/26 16:55:33Z martin + * Fixed compiler warnings with gcc/Linux. + * Revision 1.3 2010/06/02 12:29:44 daniel + * Excluded mbgserio_msec_to_timeval() from build under WIN32 targets. + * Revision 1.2 2009/09/01 10:38:21Z martin * Cleanup for CVI and other targets which don't support inline code. * Revision 1.1 2009/08/24 13:08:56 martin * Initial revision. @@ -25,6 +46,13 @@ #include <mbg_tgt.h> #include <words.h> +#include <mbgtime.h> + +#include <stdlib.h> + +#if !defined( MBG_TGT_WIN32 ) && !defined( MBG_TGT_DOS ) + #include <sys/time.h> +#endif #ifdef _MBG_TMO #define _ext @@ -36,11 +64,15 @@ /* Start of header body */ -#if defined( MBG_TGT_UNIX ) +#ifdef __cplusplus +extern "C" { +#endif + +#if defined( MBG_TGT_POSIX ) - #include <sys/time.h> + #include <time.h> - typedef struct timeval MBG_TMO_TIME; + typedef struct timespec MBG_TMO_TIME; #elif defined( MBG_TGT_WIN32 ) @@ -61,14 +93,24 @@ +typedef struct +{ + MBG_TMO_TIME t_start; + MBG_TMO_TIME t_tmo; + MBG_TMO_TIME t_now; + +} MBG_MSG_TIMES; + + + #if defined( __mbg_inline ) static __mbg_inline void mbg_tmo_get_time( MBG_TMO_TIME *t ) { - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) - gettimeofday( t, NULL ); + clock_gettime( CLOCK_MONOTONIC, t ); #elif defined( MBG_TGT_WIN32 ) @@ -95,14 +137,15 @@ void mbg_tmo_get_time( MBG_TMO_TIME *t ) #endif + #if defined( __mbg_inline ) static __mbg_inline int mbg_tmo_time_is_set( const MBG_TMO_TIME *t ) { - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) - return ( t->tv_sec != 0 ) || ( t->tv_usec != 0 ); + return ( t->tv_sec != 0 ) || ( t->tv_nsec != 0 ); #elif defined( MBG_TGT_WIN32 ) @@ -132,20 +175,27 @@ int mbg_tmo_time_is_set( const MBG_TMO_TIME *t ) #if defined( __mbg_inline ) static __mbg_inline -void mbg_tmo_set_timeout_ms( MBG_TMO_TIME *t_tmo, ulong msec ) +void mbg_tmo_add_ms( MBG_TMO_TIME *t_tmo, long msec ) { - mbg_tmo_get_time( t_tmo ); + #if defined( MBG_TGT_POSIX ) - #if defined( MBG_TGT_UNIX ) + ldiv_t ldt = ldiv( msec, 1000 ); - t_tmo->tv_usec += msec * 1000; + t_tmo->tv_sec += ldt.quot; + t_tmo->tv_nsec += ldt.rem * 1000000L; - while ( t_tmo->tv_usec > 1000000UL ) + while ( t_tmo->tv_nsec > NSEC_PER_SEC ) { - t_tmo->tv_usec -= 1000000UL; + t_tmo->tv_nsec -= NSEC_PER_SEC; t_tmo->tv_sec++; } + while ( t_tmo->tv_nsec < 0L ) + { + t_tmo->tv_nsec += NSEC_PER_SEC; + t_tmo->tv_sec--; + } + #elif defined( MBG_TGT_WIN32 ) t_tmo->u64 += ( (uint64_t) msec ) * 10000; @@ -156,31 +206,60 @@ void mbg_tmo_set_timeout_ms( MBG_TMO_TIME *t_tmo, ulong msec ) #endif -} // mbg_tmo_set_timeout +} // mbg_tmo_add_ms #elif defined( MBG_TGT_CVI ) - #define mbg_tmo_set_timeout_ms( _t, _msec ) \ - mbg_tmo_get_time( (_t) ); \ + #error FIXME + + #define mbg_tmo_add_ms( _t, _msec ) \ + mbg_tmo_get_time( (_t) ); \ (_t)->u64 += ( (uint64_t) (_msec) ) * 10000 #else // DOS, ... - #define mbg_tmo_set_timeout_ms( _t, _msec ) \ - mbg_tmo_get_time( (_t) ); \ + #error FIXME + + #define mbg_tmo_add_ms( _t, _msec ) \ + mbg_tmo_get_time( (_t) ); \ *(_t) += (clock_t) ( ( (double) (_msec) * CLOCKS_PER_SEC ) / 1000 ); + #endif + +#if defined( __mbg_inline ) + +static __mbg_inline +void mbg_tmo_set_timeout_ms( MBG_TMO_TIME *t_tmo, ulong msec ) +{ + mbg_tmo_get_time( t_tmo ); + mbg_tmo_add_ms( t_tmo, msec ); + +} // mbg_tmo_set_timeout + +#else + + #define mbg_tmo_set_timeout_ms( _t, _msec ) \ + do \ + { \ + mbg_tmo_get_time( (_t) ); \ + mbg_tmo_add_ms( (_t), (_msec) ); \ + } while 0 + +#endif + + + #if defined( __mbg_inline ) static __mbg_inline long mbg_tmo_time_diff_ms( const MBG_TMO_TIME *t, const MBG_TMO_TIME *t0 ) { - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) return ( t->tv_sec - t0->tv_sec ) * 1000 - + ( t->tv_usec - t0->tv_usec ) / 1000; + + ( t->tv_nsec - t0->tv_nsec ) / 1000000L; #elif defined( MBG_TGT_WIN32 ) @@ -207,15 +286,52 @@ long mbg_tmo_time_diff_ms( const MBG_TMO_TIME *t, const MBG_TMO_TIME *t0 ) #endif + +#if defined( __mbg_inline ) + +static __mbg_inline +double mbg_tmo_delta_t( const MBG_TMO_TIME *t, const MBG_TMO_TIME *t0 ) +{ + #if defined( MBG_TGT_POSIX ) + + return (double) ( t->tv_sec - t0->tv_sec ) + + (double) ( t->tv_nsec - t0->tv_nsec ) / 1e9; + + #elif defined( MBG_TGT_WIN32 ) + + return ( (double) (int64_t) ( t->u64 - t0->u64 ) ) / 1e7; + + #else // DOS, ... + + return (double) ( *t - *t0 ) / (double) CLOCKS_PER_SEC; + + #endif + +} // mbg_tmo_delta_t + +#elif defined( MBG_TGT_CVI ) + + #define mbg_tmo_delta_t( _t, _t0 ) \ + ( (double) ( (_t)->u64 - (_t0)->u64 ) / (double) CLOCKS_PER_SEC ) + +#else // DOS, ... + + #define mbg_tmo_delta_t( _t, _t0 ) \ + ( (double) ( *(_t) - *(_t0) ) / (double) CLOCKS_PER_SEC ) + +#endif + + + #if defined( __mbg_inline ) static __mbg_inline int mbg_tmo_time_is_after( const MBG_TMO_TIME *t_now, const MBG_TMO_TIME *tmo ) { - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) return ( ( t_now->tv_sec > tmo->tv_sec ) || - ( ( t_now->tv_sec == tmo->tv_sec ) && ( t_now->tv_usec > tmo->tv_usec ) ) ); + ( ( t_now->tv_sec == tmo->tv_sec ) && ( t_now->tv_nsec > tmo->tv_nsec ) ) ); #elif defined( MBG_TGT_WIN32 ) @@ -270,26 +386,21 @@ int mbg_tmo_curr_time_is_after( const MBG_TMO_TIME *tmo ) #if defined( MBG_TGT_CVI ) // needs to be implemented as non-inline function in mbg_tmo.c - void mbgserio_msec_to_timeval( ulong msec, struct timeval *tv ); + void mbg_msec_to_timeval( ulong msec, struct timeval *tv ); -#elif defined( MBG_TGT_UNIX ) || defined( MBG_TGT_WIN32 ) +#elif defined( MBG_TGT_POSIX ) || defined( MBG_TGT_WIN32 ) static __mbg_inline -void mbgserio_msec_to_timeval( ulong msec, struct timeval *tv ) +void mbg_msec_to_timeval( ulong msec, struct timeval *tv ) { + tv->tv_sec = msec / 1000; tv->tv_usec = ( msec % 1000 ) * 1000; -} // mbgserio_msec_to_timeval - -#endif // defined( MBG_TGT_UNIX ) || defined( MBG_TGT_WIN32 ) +} // mbg_msec_to_timeval +#endif // defined( MBG_TGT_POSIX ) || defined( MBG_TGT_WIN32 ) -/* function prototypes: */ - -#ifdef __cplusplus -extern "C" { -#endif /* ----- function prototypes begin ----- */ diff --git a/mbglib/common/mbgserio.c b/mbglib/common/mbgserio.c index 23005b7..c69a775 100644 --- a/mbglib/common/mbgserio.c +++ b/mbglib/common/mbgserio.c @@ -1,7 +1,7 @@ /************************************************************************** * - * $Id: mbgserio.c 1.3 2009/09/01 10:49:30 martin REL_M $ + * $Id: mbgserio.c 1.11 2021/12/01 10:19:07 martin.burnicki REL_M $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * @@ -10,6 +10,50 @@ * * ----------------------------------------------------------------------- * $Log: mbgserio.c $ + * Revision 1.11 2021/12/01 10:19:07 martin.burnicki + * Make a parameter of mbgserio_get_duration_rcv() const. + * Revision 1.10 2021/05/04 20:31:36 martin + * New functions mbgserio_get_rx_fifo_threshold, + * mbgserio_get_port_basename and mbgserio_free_port_basename, + * mbgserio_get_dev_poll_timeout and mbgserio_set_dev_poll_timeout, + * mbgserio_get_duration_xmt and mbgserio_get_duration_rcv. + * New structure MBGSERIO_SPEED_PARMS which is also + * part of the control structure, and useful to calculate + * the transmission time. + * Renamed some local variables. + * Moved device list functions to the bottom of the file. + * Updated some comments and removed trailing spaces. + * Revision 1.9 2021/04/07 17:20:04 martin + * Updated a bunch of comments, and renamed some local variables. + * Revision 1.8 2019/09/27 15:33:18 martin + * Account for a renamed library function. + * Excluded definition of dev_dir from build since it is + * anyway only used in some conditional code. + * Revision 1.7 2017/07/06 07:21:18 martin + * Allocate a device control structure when opening a port, + * and free the structure when the port is closed. + * Use safe string functions from str_util.c. + * Use common Meinberg error codes from mbgerror.h. + * Check for MBG_TGT_POSIX instead of MBG_TGT_UNIX. + * Account for renamed library symbols. + * Added doxygen comments. + * Revision 1.6 2013/04/18 13:50:58Z udo + * use O_CLOEXEC on open serial port if defined + * Revision 1.5 2013/02/01 16:06:33 martin + * Always use mbgserio_read/write rather than the macros. + * Got rid of _mbg_open/close/read/write() macros. + * Check return code of tcgetattr() when opening port on Linux. + * Set up DCB on Windows when opening the port, + * not when setting parameters. + * Fixed setting up COM port list on Windows. + * Flush output on close. + * Debug code to test flush on Windows (doesn't seem + * to work properly). + * New code trying different ways to detect existing ports + * reliably on Linux. + * Syntax workaround which is required until this module becomes a DLL. + * Revision 1.4 2011/07/29 10:12:15 martin + * Allow baud rates 115200 and 230400 on Linux, if supported by the OS version. * Revision 1.3 2009/09/01 10:49:30 martin * Cleanup for CVI. * Use new portable timeout functions from mbg_tmo.h. @@ -28,21 +72,32 @@ #define _MBGSERIO #include <mbgserio.h> - - //##++ The following lines are required - // until mbgserio becomes a DLL: - #undef _MBG_API_ATTR - #define _MBG_API_ATTR - #undef _MBGSERIO +#include <mbgerror.h> +#include <str_util.h> + #include <stdio.h> #include <ctype.h> #include <time.h> -#if defined( MBG_TGT_UNIX ) - #include <unistd.h> +#if defined( MBG_TGT_POSIX ) #include <fcntl.h> + #include <dirent.h> + #include <errno.h> +#endif + +#if defined( MBG_TGT_WIN32 ) + #define strdup _strdup +#endif + + +// This can be defined != 0 to enabled some +// preliminary, untested code. +#define USE_LINUX_DEV_DIR 0 + +#if USE_LINUX_DEV_DIR + static const char dev_dir[] = "/dev"; #endif @@ -53,9 +108,8 @@ * The definitions in this block and all v24...() functions are part of a * third-party library called V.24 Tools Plus by Langner Expertensysteme. * - * This library may no be distributed freely, so the v24..() functions - * must be replaced by user-written functions or some library available - * to the user of this demo. + * That library may not be freely distributed, so the v24..() functions + * must be replaced by user-written functions or some other library. *-----------------------------------------------------------------------*/ #ifdef __cplusplus @@ -68,358 +122,653 @@ int v24open( char *portname, int mode ); * port could not be opened. */ -#define O_DIRECT 0x0100 /* Schnittstelle fuer direkten Hardware-Zugriff oeffnen */ -#define O_HIGHPRIO 0x2000 /* Schnittstelle mit hoher Prioritaet oeffnen (fastopen) */ +#define O_DIRECT 0x0100 /* open port with direct access to the hardware */ +#define O_HIGHPRIO 0x2000 /* open port with high priority (fastopen) */ +/* This is the open mode we use here. */ #define OPEN_MODE ( O_DIRECT | O_HIGHPRIO ) int v24setparams( int port, long speed, int dbits, int parity, int stopbits ); -/* Set the port's transmission speed, number of data bits, parity and - * number of stop bits. Returns 0 on success. - */ +int v24write(int port,void *s,unsigned len); +int v24read(int port,void *s,unsigned len); +int v24flush(int port); +int v24close( int port ); + +#ifdef __cplusplus +} +#endif + +#endif // defined( _USE_V24TOOLS ) + -int v24qempty( int port, int which ); -/* Returns 1 if the receive buffer is empty, 0 if it is not, or an other - * value on error. - */ -int v24getch( int port ); -/* Return a character from the receive buffer. +static /*HDR*/ +/** + * @brief Deallocate a serial device control structure. + * + * Free the memory allocated for the device control structure + * and set the pointer to @a NULL. + * + * @param[in,out] pp_sdev Address of a pointer to a device control structure. + * + * @see ::alloc_mbgserio_dev */ +void dealloc_mbgserio_dev( MBGSERIO_DEV **pp_sdev ) +{ + MBGSERIO_DEV *sdev = *pp_sdev; + + if ( sdev ) + { + free( sdev ); + *pp_sdev = NULL; + } + +} // dealloc_mbgserio_dev -int v24putc( int port, char c ); -/* Write a character to the port. + + +static /*HDR*/ +/** + * @brief Allocate a serial device control structure. + * + * @param[in,out] pp_sdev Address of a pointer to a device control structure. + * + * @return ::MBG_SUCCESS or one of the @ref MBG_ERROR_CODES. + * + * @see ::dealloc_mbgserio_dev */ +int alloc_mbgserio_dev( MBGSERIO_DEV **pp_sdev ) +{ + int rc; + MBGSERIO_DEV *sdev = (MBGSERIO_DEV *) malloc( sizeof( *sdev ) ); -int v24close( int port ); -/* Close the port + if ( sdev == NULL ) + { + rc = mbg_get_last_error( "malloc failed in alloc_mbgserio_dev" ); + goto out; + } + + memset( sdev, 0, sizeof( *sdev ) ); + sdev->port_handle = MBG_INVALID_PORT_HANDLE; + rc = MBG_SUCCESS; + +out: + *pp_sdev = sdev; + return rc; + +} // alloc_mbgserio_dev + + + +/*HDR*/ +/** + * @brief Return the base name of the device name of a serial port. + * + * In order to leave the original device name string unchanged, + * a buffer is allocated with a duplicate of the original string, + * and a pointer to the basename component in that buffer is returned. + * + * After usage, the allocated buffer whose address is saved in + * @p *pp_dup_port_name should be freed by calling ::mbgserio_free_port_basename. + * + * @param[in] pp_dup_port_name Address of a <em>char *</em> that takes the address + * of the allocated string buffer. + * @param[in] port_name The original device name string. + * + * @return A pointer to the basename which is part of an allocated copy of the original string. + * + * @see ::mbgserio_free_port_basename */ +_NO_MBG_API_ATTR char * _MBG_API mbgserio_get_port_basename( char **pp_dup_port_name, const char *port_name ) +{ + char *cp = NULL; -#ifdef __cplusplus -} -#endif + *pp_dup_port_name = strdup( port_name ); -#endif // defined( _USE_V24TOOLS ) + if ( *pp_dup_port_name ) + { + #if defined( MBG_TGT_POSIX ) + // We remove e.g. the "/dev/" part + // of the device name. + cp = basename( *pp_dup_port_name ); + #else + // Nothing to be removed from a + // device name like "COM1". + cp = *pp_dup_port_name; + #endif + } + + return cp; -/*------------------------------------------------------------------------*/ +} // mbgserio_get_port_basename /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_open( SERIAL_IO_STATUS *pst, const char *dev ) +/** + * @brief Free a buffer that has been allocated by ::mbgserio_get_port_basename. + * + * This function should be called whenever a string buffer allocated + * by an earlier ::mbgserio_get_port_basename call isn't needed anymore. + * + * @param[in] pp_dup_port_name Address of a <em>char *</em> with the address + * of the allocated string buffer. + * + * @see ::mbgserio_get_port_basename + */ +_NO_MBG_API_ATTR void _MBG_API mbgserio_free_port_basename( char **pp_dup_port_name ) { - MBG_PORT_HANDLE port_handle; + if ( *pp_dup_port_name ) + { + free( *pp_dup_port_name ); + *pp_dup_port_name = NULL; + } - #if defined( MBG_TGT_CVI ) +} // mbgserio_free_port_basename + + + +/*HDR*/ +/** + * @brief Determine the threshold of a serial RX FIFO buffer. + * + * This is useful to be able to exactly compensate the delay + * of a received serial data string. + * + * @note This may not be supported on each target system. + * + * @param[in] port_name The device name of the serial port. + * + * @return The number of bytes in the FIFO buffer after which + * a receive event (e.g. IRQ) is generated, + * or 0 if the real threshold can't be determined. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_get_rx_fifo_threshold( const char *port_name ) +{ + int n = 0; + + #if defined( MBG_TGT_LINUX ) { - int data_bits = 8; //##++ - int parity_code = 0; - int baud_rate = 19200; - int stop_bits = 1; - int i; - int len; - int rc; + static const char tty_basename[] = "/sys/class/tty"; + static const char tty_rx_trig_fn[] = "rx_trig_bytes"; - // Under CVI the port handle passed to OpenComConfig and used furtheron - // corresponds to the COM port number, e.g. 1 for COM1, so we extract - // the number from the device name passed as parameter. - port_handle = 0; - len = strlen( dev ); + char *dup_name = NULL; + const char *cp = mbgserio_get_port_basename( &dup_name, port_name ); + char fn[256]; + FILE *fp; - for ( i = 0; i < len; i++ ) + snprintf_safe( fn, sizeof( fn ), "%s/%s/%s", + tty_basename, cp, tty_rx_trig_fn ); + + fp = fopen( fn, "rt" ); + + if ( fp ) { - char c = dev[i]; - if ( c >= '0' && c <= '9' ) - break; + char s[80]; + cp = fgets( s, sizeof( s ), fp ); + + if ( cp ) + n = atoi( cp ); + + fclose( fp ); } - if ( i == len ) - return MBGSERIO_INV_CFG; // no numeric substring found + mbgserio_free_port_basename( &dup_name ); + } + #endif + return n; - port_handle = atoi( &dev[i] ); +} // mbgserio_get_rx_fifo_threshold - rc = OpenComConfig (port_handle, NULL, baud_rate, parity_code, data_bits, stop_bits, 8192, 1024); //##++ - if ( rc < 0 ) - goto fail; - pst->port_handle = port_handle; - SetComTime( port_handle, 1.0 ); //##++ - SetXMode( port_handle, 0 ); - } - #elif defined( MBG_TGT_WIN32 ) - { - static const char *prefix = "\\\\.\\"; +#if defined( MBG_TGT_WIN32 ) - COMMTIMEOUTS commtimeouts; - int len = strlen( prefix ) + strlen( dev ) + 1; - char *tmp_name = (char *) malloc( len ); +static /*HDR*/ +/** + * @brief Win32-specific function which opens a serial port. + * + * @param[in] dev_name Basic device name, e.g. "COM1". + * @param[out] ph Pointer to a @a HANDLE which is set up on success. + * + * @return ::MBG_SUCCESS on success, or one of the @ref MBG_ERROR_CODES. + */ +int win32_open_serial_port( const char *dev_name, HANDLE *ph ) +{ + // A prefix is required for the device name to be able + // to open ports with large numbers, e.g. COM15. + static const char prefix[] = "\\\\.\\"; + + size_t len; + char *tmp_name; + size_t n = 0; + int rc = MBG_ERR_UNSPEC; - if ( tmp_name == NULL ) // unable to allocate memory - goto fail; + if ( dev_name == NULL ) + return MBG_ERR_INV_PARM; + len = strlen( prefix ) + strlen( dev_name ) + 1; + tmp_name = (char *) malloc( len ); - strcpy( tmp_name, prefix ); - strcat( tmp_name, dev ); + if ( tmp_name == NULL ) // Unable to allocate memory. + { + rc = MBG_ERR_NO_MEM; + goto out; + } + + n = sn_cpy_str_safe( tmp_name, len, prefix ); + n += sn_cpy_str_safe( &tmp_name[n], len - n, dev_name ); - port_handle = CreateFile( tmp_name, GENERIC_READ | GENERIC_WRITE, + *ph = CreateFileA( tmp_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, + FILE_ATTRIBUTE_NORMAL, // TODO Also use FILE_FLAG_WRITE_THROUGH ? NULL ); - free( tmp_name ); + if ( *ph == INVALID_HANDLE_VALUE ) + { + rc = mbg_win32_sys_err_to_mbg( GetLastError(), "CreateFile failed in win32_open_serial_port" ); + goto out_free; + } - if ( port_handle == INVALID_HANDLE_VALUE ) - goto fail; + rc = MBG_SUCCESS; +out_free: + free( tmp_name ); - pst->port_handle = port_handle; +out: + return rc; - // save original settings - pst->old_dcb.DCBlength = sizeof( pst->old_dcb ); - GetCommState( port_handle, &pst->old_dcb ); - GetCommTimeouts( port_handle, &pst->old_commtimeouts ); +} // win32_open_serial_port - // configure our settings - memset( &commtimeouts, 0, sizeof( commtimeouts ) ); - SetCommTimeouts( port_handle, &commtimeouts ); +#endif // defined( MBG_TGT_WIN32 ) - #if defined( MBGSERIO_IN_BUFFER_SIZE ) && defined( MBGSERIO_OUT_BUFFER_SIZE ) - SetupComm( port_handle, MBGSERIO_IN_BUFFER_SIZE, MBGSERIO_OUT_BUFFER_SIZE ); - #endif - PurgeComm( port_handle, PURGE_TXABORT|PURGE_TXCLEAR ); - PurgeComm( port_handle, PURGE_RXABORT|PURGE_RXCLEAR ); - //##++ mbgextio_set_console_control_handler(); - } - #elif defined( MBG_TGT_UNIX ) +/*HDR*/ +/** + * @brief Close a serial port specified by a ::MBGSERIO_DEV structure. + * + * After the port has been closed, the buffer that had been allocated + * for the ::MBGSERIO_DEV structure is freed, and the pointer to that + * buffer is set to @a NULL. + * + * @param[in,out] pp_sdev Address of a pointer to the control structure including the port handle. + * + * @return One of the @ref MBG_RETURN_CODES. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_close( MBGSERIO_DEV **pp_sdev ) +{ + if ( *pp_sdev) // Only if a MBGSERIO_DEV structure has been allocated. { - // Open as not controlling TTY to prevent from being - // killed if CTRL-C is received. - // O_NONBLOCK is the same as O_NDELAY. - port_handle = _mbg_open( dev, O_RDWR | O_NOCTTY | O_NONBLOCK ); + MBGSERIO_DEV *sdev = *pp_sdev; + MBG_PORT_HANDLE *p_handle = &sdev->port_handle; - //##++ TODO: Under Unix a serial port can by default be opened - // by several processes. However, we don't want that, so we - // should care about this using a lock file for the device - // and/or setting the TIOCEXCL flag (which unfortunately - // is not an atomic operation with open()). + if ( *p_handle != MBG_INVALID_PORT_HANDLE ) + { + mbgserio_flush_tx( sdev ); - if ( port_handle < 0 ) // check errno for the reason - goto fail; + #if defined( MBG_TGT_CVI ) + CloseCom( *p_handle ); - pst->port_handle = port_handle; + #elif defined( MBG_TGT_WIN32 ) - /* save current device settings */ - tcgetattr( port_handle, &pst->oldtio ); + // Restore original settings that have been saved. - // atexit( port_deinit ); + if ( sdev->org_dcb_has_been_read ) + SetCommState( *p_handle, &sdev->org_dcb ); - fflush( stdout ); //##++ - setvbuf( stdout, NULL, _IONBF, 0 ); - } - #elif defined( MBG_TGT_DOS ) - #if defined( _USE_V24TOOLS ) - { - port_handle = v24open( (char *) dev, OPEN_MODE ); + if ( sdev->org_commtimeouts_have_been_read ) + SetCommTimeouts( *p_handle, &sdev->org_commtimeouts ); - if ( port_handle < 0 ) - goto fail; + CloseHandle( *p_handle ); - pst->port_handle = port_handle; - v24settimeout( port_handle, 1 ); - } - #else + #elif defined( MBG_TGT_POSIX ) - #error Target DOS requires v24tools for serial I/O. + // Restore original settings that have been saved. - #endif + if ( sdev->org_tio_has_been_read ) + tcsetattr( *p_handle, TCSANOW, &sdev->org_tio ); - #else + close( *p_handle ); - #error This target OS is not supported. + #elif defined( MBG_TGT_DOS ) + #if defined( _USE_V24TOOLS ) - #endif + v24close( *p_handle ); - return 0; + #else + #error Target DOS requires v24tools for serial I/O. -fail: - pst->port_handle = MBG_INVALID_PORT_HANDLE; - return MBGSERIO_FAIL; + #endif -} // mbgserio_open + #else + + #error This target OS is not supported. + + #endif + } + + dealloc_mbgserio_dev( pp_sdev ); + } + + return MBG_SUCCESS; + +} // mbgserio_close /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_close( SERIAL_IO_STATUS *pst ) +/** + * @brief Open a serial port and set up a ::MBGSERIO_DEV structure. + * + * @param[in,out] pp_sdev Address of a pointer to a ::MBGSERIO_DEV device control structure + * allocated and set up by this call. Set to @a NULL on error. + * @param[in] dev_name Device name of the port to be opened. + * + * @return One of the @ref MBG_RETURN_CODES. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_open( MBGSERIO_DEV **pp_sdev, const char *dev_name ) { - if ( pst->port_handle != MBG_INVALID_PORT_HANDLE ) - { - MBG_PORT_HANDLE port_handle = pst->port_handle; + MBGSERIO_DEV *sdev; + MBG_PORT_HANDLE *p_handle; - #if defined( MBG_TGT_CVI ) + int rc = alloc_mbgserio_dev( pp_sdev ); - CloseCom( port_handle ); + if ( mbg_rc_is_error( rc ) ) + return rc; - #elif defined( MBG_TGT_WIN32 ) - SetCommState( port_handle, &pst->old_dcb ); - SetCommTimeouts( port_handle, &pst->old_commtimeouts ); - CloseHandle( port_handle ); + sdev = *pp_sdev; + p_handle = &sdev->port_handle; + + #if defined( MBG_TGT_CVI ) + { + MBGSERIO_SPEED_PARMS sp = { 0 }; + int parity_code = 0; + int i; + int len; + int rc; - #elif defined( MBG_TGT_UNIX ) + sp.data_bits = 8; + sp.baud_rate = 19200; + sp.stop_bits = 1; - tcsetattr( port_handle, TCSANOW, &pst->oldtio ); - close( port_handle ); + // On CVI, the port handle passed to OpenComConfig and used furtheron + // corresponds to the COM port number, e.g. 1 for COM1, so we extract + // the number from the device name passed as parameter. + len = strlen( dev_name ); - #elif defined( MBG_TGT_DOS ) - #if defined( _USE_V24TOOLS ) + for ( i = 0; i < len; i++ ) + { + char c = dev_name[i]; - v24close( port_handle ); + if ( c >= '0' && c <= '9' ) + break; + } - #else + if ( i == len ) + { + rc = MBG_ERR_PARM_FMT; // No numeric substring found. + goto out; + } - #error Target DOS requires v24tools for serial I/O. + *p_handle = atoi( &dev_name[i] ); - #endif + rc = OpenComConfig( *p_handle, NULL, sp.baud_rate, parity_code, + sp.data_bits, sp.stop_bits, 8192, 1024 ); // TODO - #else + if ( rc < 0 ) + { + rc = mbg_get_last_error( "OpenComConfig failed in mbgserio_open" ); + *p_handle = MBG_INVALID_PORT_HANDLE; + goto out; + } - #error This target OS is not supported. + rc = SetComTime( *p_handle, 1.0 ); // FIXME TODO WTF? - #endif + if ( rc < 0 ) + { + rc = mbg_get_last_error( "SetComTime failed in mbgserio_open" ); + goto out; + } + + rc = SetXMode( *p_handle, 0 ); + + if ( rc < 0 ) + { + rc = mbg_get_last_error( "SetXMode failed in mbgserio_open" ); + goto out; + } - pst->port_handle = MBG_INVALID_PORT_HANDLE; + sdev->sp = sp; } + #elif defined( MBG_TGT_WIN32 ) + { + COMMTIMEOUTS commtimeouts; + DCB dcb; - return 0; + rc = win32_open_serial_port( dev_name, p_handle ); -} // mbgserio_close + // rc is now one of the @ref MBG_RETURN_CODES, and in case of an error + // the handle has already been set to ::MBG_INVALID_PORT_HANDLE. + if ( mbg_rc_is_error( rc ) ) + goto out; + // Save the settings found at startup. + sdev->org_dcb.DCBlength = sizeof( sdev->org_dcb ); + if ( !GetCommState( *p_handle, &sdev->org_dcb ) ) + { + rc = mbg_get_last_error( "GetCommState failed in mbgserio_open" ); + goto out; + } -/*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_setup_port_str_list( MBG_STR_LIST **list, int max_devs ) -{ - MBG_STR_LIST *list_head; - int n = 0; - int i = 0; + sdev->org_dcb_has_been_read = 1; - (*list) = (MBG_STR_LIST *) malloc( sizeof( **list ) ); - memset( (*list), 0, sizeof( **list ) ); + if ( !GetCommTimeouts( *p_handle, &sdev->org_commtimeouts ) ) + { + rc = mbg_get_last_error( "GetCommTimeouts failed in mbgserio_open" ); + goto out; + } - list_head = (*list); + sdev->org_commtimeouts_have_been_read = 1; - for ( i = 0; i < max_devs; i++ ) - { - SERIAL_IO_STATUS iost; - char dev_name[100] = { 0 }; - int rc; + if ( !GetCommProperties( *p_handle, &sdev->comm_prop ) ) + { + rc = mbg_get_last_error( "GetCommProperties failed in mbgserio_open" ); + goto out; + } - #if defined( MBG_TGT_WIN32 ) - sprintf( dev_name, "COM%i", i + 1 ); - #elif defined( MBG_TGT_LINUX ) - sprintf( dev_name, "/dev/ttyS%i", i ); + sdev->comm_prop_have_been_read = 1; + + #if 0 // fields provided by the Windows DCB structure + DWORD DCBlength; /* sizeof(DCB) */ + DWORD BaudRate; /* Baudrate at which running */ + DWORD fBinary: 1; /* Binary Mode (skip EOF check) */ + DWORD fParity: 1; /* Enable parity checking */ + DWORD fOutxCtsFlow:1; /* CTS handshaking on output */ + DWORD fOutxDsrFlow:1; /* DSR handshaking on output */ + DWORD fDtrControl:2; /* DTR Flow control */ + DWORD fDsrSensitivity:1; /* DSR Sensitivity */ + DWORD fTXContinueOnXoff: 1; /* Continue TX when Xoff sent */ + DWORD fOutX: 1; /* Enable output X-ON/X-OFF */ + DWORD fInX: 1; /* Enable input X-ON/X-OFF */ + DWORD fErrorChar: 1; /* Enable Err Replacement */ + DWORD fNull: 1; /* Enable Null stripping */ + DWORD fRtsControl:2; /* Rts Flow control */ + DWORD fAbortOnError:1; /* Abort all reads and writes on Error */ + DWORD fDummy2:17; /* Reserved */ + WORD wReserved; /* Not currently used */ + WORD XonLim; /* Transmit X-ON threshold */ + WORD XoffLim; /* Transmit X-OFF threshold */ + BYTE ByteSize; /* Number of bits/byte, 4-8 */ + BYTE Parity; /* 0-4=None,Odd,Even,Mark,Space */ + BYTE StopBits; /* 0,1,2 = 1, 1.5, 2 */ + char XonChar; /* Tx and Rx X-ON character */ + char XoffChar; /* Tx and Rx X-OFF character */ + char ErrorChar; /* Error replacement char */ + char EofChar; /* End of Input character */ + char EvtChar; /* Received Event character */ + WORD wReserved1; /* Fill for now. */ #endif - rc = mbgserio_open( &iost, dev_name ); + // Configure our settings. + dcb = sdev->org_dcb; // Current settings as default. + dcb.fOutxCtsFlow = FALSE; // CTS output flow control. + dcb.fOutxDsrFlow = FALSE; // DSR output flow control. + dcb.fDtrControl = DTR_CONTROL_ENABLE; // Enable DTR. + dcb.fDsrSensitivity = FALSE; // Don't require DSR input active. + // TODO more missing here. + dcb.fRtsControl = RTS_CONTROL_ENABLE; // Enable RTS as required for for C28COM. + dcb.fOutX = FALSE; - if ( rc < 0 ) - continue; + if ( !SetCommState( *p_handle, &dcb ) ) + { + rc = mbg_get_last_error( "SetCommState failed in mbgserio_open" ); + goto out; + } - mbgserio_close( &iost ); + memset( &commtimeouts, 0, sizeof( commtimeouts ) ); - (*list)->s = (char *) malloc( strlen( dev_name ) + 1 ); - strcpy( (*list)->s, dev_name ); + if ( !SetCommTimeouts( *p_handle, &commtimeouts ) ) + { + rc = mbg_get_last_error( "SetCommTimeout failed in mbgserio_open" ); + goto out; + } - (*list)->next = (MBG_STR_LIST *) malloc( sizeof( **list ) ); - (*list) = (*list)->next; + #if !defined( MBGSERIO_IN_BUFFER_SIZE ) + #define MBGSERIO_IN_BUFFER_SIZE 2048 + #endif - memset( (*list), 0, sizeof( **list ) ); - n++; + #if !defined( MBGSERIO_OUT_BUFFER_SIZE ) + #define MBGSERIO_OUT_BUFFER_SIZE 2048 + #endif -// if ( ++i >= MBG_MAX_DEVICES ) -// break; - } + if ( !SetupComm( *p_handle, MBGSERIO_IN_BUFFER_SIZE, MBGSERIO_OUT_BUFFER_SIZE ) ) + { + rc = mbg_get_last_error( "SetupComm failed in mbgserio_open" ); + goto out; + } - if ( n == 0 ) - { - free( *list ); - list_head = NULL; + PurgeComm( *p_handle, PURGE_TXABORT | PURGE_TXCLEAR ); + PurgeComm( *p_handle, PURGE_RXABORT | PURGE_RXCLEAR ); + + // TODO Call mbgextio_set_console_control_handler() ? } + #elif defined( MBG_TGT_POSIX ) + { + // Open as not controlling TTY to prevent from being + // killed if CTRL-C is received. + // O_NONBLOCK is the same as O_NDELAY. + int flags = O_RDWR | O_NOCTTY | O_NONBLOCK; - *list = list_head; + #if defined( O_CLOEXEC ) + flags |= O_CLOEXEC; + #endif - return n; + *p_handle = open( dev_name, flags ); // Returns -1 on error. -} // mbgserio_setup_port_str_list + if ( *p_handle < 0 ) + { + rc = mbg_get_last_error( "open failed in mbgserio_open" ); + *p_handle = MBG_INVALID_PORT_HANDLE; + goto out; + } + // TODO On Unix, a serial port can by default be opened + // by several processes. However, we don't want that, so we + // should care about this using a lock file for the device + // and/or setting the TIOCEXCL flag (which unfortunately + // is not an atomic operation with open()). + // Save current device settings. + if ( tcgetattr( *p_handle, &sdev->org_tio ) < 0 ) + { + rc = mbg_get_last_error( "tcgetattr failed in mbgserio_open" ); + goto out; + } -/*HDR*/ -_MBG_API_ATTR void _MBG_API _MBG_API mbgserio_free_str_list( MBG_STR_LIST *list ) -{ - int i = 0; + sdev->org_tio_has_been_read = 1; - while ( i < 1000 ) //##++ - { - if ( list ) + // TODO Set an atexit() function to close the port? + } + #elif defined( MBG_TGT_DOS ) + #if defined( _USE_V24TOOLS ) { - if ( list->s ) - { - free( list->s ); - list->s = NULL; - } + *p_handle = v24open( (char *) dev_name, OPEN_MODE ); - if ( list->next ) - { - MBG_STR_LIST *next = list->next; - free( list ); - list = next; - } - else + if ( *p_handle < 0 ) { - if ( list ) - { - free( list ); - list = NULL; - } - break; + rc = MBG_ERR_UNSPEC; // TODO Translate rc ? + *p_handle = MBG_INVALID_PORT_HANDLE; + goto out; } + + v24settimeout( *p_handle, 1 ); } - else - break; + #else - i++; - } + #error Target DOS requires v24tools for serial I/O. + rc = MBG_ERR_NOT_SUPP_ON_OS; + goto out; -} // mbgserio_free_str_list + #endif + + #else + + #error This target OS is not supported. + rc = MBG_ERR_NOT_SUPP_ON_OS; + goto out; + + #endif + + // Try to get the receive FIFO threshold. + // Will be 0 if this isn't supported. + sdev->rcv_fifo_threshold = mbgserio_get_rx_fifo_threshold( dev_name ); + + rc = MBG_SUCCESS; + +out: + if ( mbg_rc_is_error( rc ) ) + mbgserio_close( pp_sdev ); + + return rc; + +} // mbgserio_open /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, - uint32_t baud_rate, const char *framing ) +/** + * @brief Set the transmission parameters of a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] baud_rate One of the well-known baud rates, e.g. 19200. + * @param[in] framing A short string providing the framing, e.g. "8N1". + * + * @return One of the @ref MBG_RETURN_CODES. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_set_parms( MBGSERIO_DEV *sdev, + uint32_t baud_rate, const char *framing ) { - MBG_PORT_HANDLE port_handle = pst->port_handle; + MBG_PORT_HANDLE port_handle = sdev->port_handle; + MBGSERIO_SPEED_PARMS sp = { 0 }; const char *cp; + int rc = MBG_ERR_UNSPEC; + + sp.baud_rate = baud_rate; #if defined( MBG_TGT_CVI ) { - int data_bits = 8; - int parity_code = 0; - int stop_bits = 1; - int rc; + int parity_code = 0; - // setup framing. + // Setup framing. for ( cp = framing; *cp; cp++ ) { char c = toupper( *cp ); @@ -428,49 +777,66 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, { case '7': case '8': - data_bits = c - '0'; + sp.data_bits = c - '0'; break; case 'N': parity_code = 0; + sp.parity_bits = 0; break; case 'E': parity_code = 2; + sp.parity_bits = 1; break; case 'O': parity_code = 1; + sp.parity_bits = 1; break; case '1': case '2': - stop_bits = c - '0'; + sp.stop_bits = c - '0'; break; default: - return MBGSERIO_INV_CFG; // invalid framing string + return MBG_ERR_PARM_FMT; // Invalid framing string. } } - rc = OpenComConfig( port_handle, NULL, baud_rate, parity_code, - data_bits, stop_bits, 8192, 1024 ); + rc = OpenComConfig( port_handle, NULL, sp.baud_rate, parity_code, + sp.data_bits, sp.stop_bits, 8192, 1024 ); if ( rc < 0 ) - return rc; + return mbg_cvi_rs232_error_to_mbg( rc, "OpenComConfig failed in mbgserio_set_parms" ); - SetComTime( port_handle, 1.0 ); //##++ - SetXMode( port_handle, 0 ); + rc = SetComTime( port_handle, 1.0 ); + + if ( rc < 0 ) + return mbg_cvi_rs232_error_to_mbg( rc, "SetComTime failed in mbgserio_set_parms" ); + + rc = SetXMode( port_handle, 0 ); + + if ( rc < 0 ) + return mbg_cvi_rs232_error_to_mbg( rc, "SetXMode failed in mbgserio_set_parms" ); + + // On success, fall through. } #elif defined( MBG_TGT_WIN32 ) { DCB dcb; - dcb.DCBlength = sizeof( DCB ) ; - GetCommState( port_handle, &dcb ) ; - dcb.BaudRate = baud_rate; + // Get current settings. + dcb.DCBlength = sizeof( DCB ); + if ( GetCommState( port_handle, &dcb ) == FALSE ) + return mbg_get_last_error( "GetCommState failed in mbgserio_set_parms" ); - // setup framing. + // Change settings as requested. + + dcb.BaudRate = baud_rate; + + // Setup framing. for ( cp = framing; *cp; cp++ ) { char c = toupper( *cp ); @@ -480,53 +846,56 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, case '7': case '8': dcb.ByteSize = c - '0'; + sp.data_bits = dcb.ByteSize; break; case 'N': dcb.Parity = NOPARITY; + sp.parity_bits = 0; break; case 'E': dcb.Parity = EVENPARITY; + sp.parity_bits = 1; break; case 'O': dcb.Parity = ODDPARITY; + sp.parity_bits = 1; break; case '1': dcb.StopBits = ONESTOPBIT; + sp.stop_bits = 1; break; case '2': dcb.StopBits = TWOSTOPBITS; + sp.stop_bits = 2; break; default: - return MBGSERIO_INV_CFG; // invalid framing string + return MBG_ERR_PARM_FMT; // Invalid framing string. } } + if ( SetCommState( port_handle, &dcb ) == FALSE ) + return mbg_get_last_error( "SetCommState failed in mbgserio_set_parms" ); - dcb.fOutxCtsFlow = FALSE; // CTS output flow control - dcb.fOutxDsrFlow = FALSE; // DSR output flow control - dcb.fDtrControl = DTR_CONTROL_ENABLE; // enable DTR for C28COM - dcb.fDsrSensitivity = FALSE; // don't require DSR input active - //##++ more missing here - dcb.fRtsControl = RTS_CONTROL_ENABLE; // enable RTS for C28COM - dcb.fOutX = FALSE; - - SetCommState ( port_handle, &dcb ); + // On success, fall through. } - #elif defined( MBG_TGT_UNIX ) + #elif defined( MBG_TGT_POSIX ) { tcflag_t c_cflag = 0; struct termios tio; - tcgetattr( port_handle, &tio ); + rc = tcgetattr( port_handle, &tio ); - // setup transmission speed - switch( baud_rate ) + if ( rc < 0 ) + return mbg_get_last_error( "tcgetattr failed in mbgserio_set_parms" ); + + // Setup transmission speed. + switch ( baud_rate ) { case 300: c_cflag = B300; break; case 600: c_cflag = B600; break; @@ -537,31 +906,100 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, case 19200: c_cflag = B19200; break; case 38400: c_cflag = B38400; break; case 57600: c_cflag = B57600; break; + #if defined( B115200 ) + case 115200: c_cflag = B115200; break; + #endif + #if defined( B230400 ) + case 230400: c_cflag = B230400; break; + #endif + #if defined( B460800 ) + case 460800: c_cflag = B460800; break; + #endif + #if defined( B500000 ) + case 500000: c_cflag = B500000; break; + #endif + #if defined( B576000 ) + case 576000: c_cflag = B576000; break; + #endif + #if defined( B921600 ) + case 921600: c_cflag = B921600; break; + #endif + #if defined( B1000000 ) + case 1000000: c_cflag = B1000000; break; + #endif + #if defined( B1152000 ) + case 1152000: c_cflag = B1152000; break; + #endif + #if defined( B1500000 ) + case 1500000: c_cflag = B1500000; break; + #endif + #if defined( B2000000 ) + case 2000000: c_cflag = B2000000; break; + #endif + #if defined( B2500000 ) + case 2500000: c_cflag = B2500000; break; + #endif + #if defined( B3000000 ) + case 3000000: c_cflag = B3000000; break; + #endif + #if defined( B3500000 ) + case 3500000: c_cflag = B3500000; break; + #endif + #if defined( B4000000 ) + case 4000000: c_cflag = B4000000; break; + #endif - default: return MBGSERIO_INV_CFG; // invalid + default: + return MBG_ERR_INV_PARM; // Invalid baud rate. } - #if 0 //##++ This should be used preferably for portability reasons + #if 0 // TODO This should preferably be used for portability reasons. int cfsetispeed( struct termios *termios_p, speed_t speed ); int cfsetospeed( struct termios *termios_p, speed_t speed ); #endif - // setup framing. + // Setup framing. for ( cp = framing; *cp; cp++ ) { - switch ( _toupper( *cp ) ) + char c = toupper( *cp ); + + switch ( c ) { - case '7': c_cflag |= CS7; break; - case '8': c_cflag |= CS8; break; + case '7': + c_cflag |= CS7; + sp.data_bits = 7; + break; + + case '8': + c_cflag |= CS8; + sp.data_bits = 8; + break; - case 'N': break; - case 'E': c_cflag |= PARENB; break; - case 'O': c_cflag |= PARENB | PARODD; break; + case 'N': + sp.parity_bits = 0; + break; - case '1': break; - case '2': c_cflag |= CSTOPB; break; + case 'E': + c_cflag |= PARENB; + sp.parity_bits = 1; + break; + + case 'O': + c_cflag |= PARENB | PARODD; + sp.parity_bits = 1; + break; - default: return MBGSERIO_INV_CFG; // invalid framing string + case '1': + sp.stop_bits = 1; + break; + + case '2': + c_cflag |= CSTOPB; + sp.stop_bits = 2; + break; + + default: + return MBG_ERR_INV_PARM; // Invalid framing string. } } @@ -572,7 +1010,7 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // (POSIX says that the baud speed is stored in the termios structure // without specifying where precisely, and provides cfgetispeed() and // cfsetispeed() for getting at it. Some systems use bits selected - // by CBAUD in c_cflag, other systems use separate fields, + // by CBAUD in c_cflag, other systems use separate fields, // e.g. sg_ispeed and sg_ospeed.) // CSIZE Character size mask. Values are CS5, CS6, CS7, or CS8. // CSTOPB Set two stop bits, rather than one. @@ -587,25 +1025,26 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // the same as the values for the CBAUD bits, shifted left IBSHIFT bits. // CRTSCTS (not in POSIX) Enable RTS/CTS (hardware) flow control. - // local connection, no modem control (CLOCAL) - // no flow control (no CRTSCTS) - // enable receiving + // We use these flags: + // Local connection, no modem control (CLOCAL). + // No flow control (no CRTSCTS). + // Enable receiving (CREAD). tio.c_cflag = c_cflag | CLOCAL | CREAD; // Setup input flags. The following flags are defined: // IGNBRK Ignore BREAK condition on input - // BRKINT If IGNBRK is set, a BREAK is ignored. If it is not set - // but BRKINT is set, then a BREAK causes the input and output + // BRKINT If IGNBRK is set, a BREAK is ignored. If it is not set + // but BRKINT is set, then a BREAK causes the input and output // queues to be flushed, and if the terminal is the controlling - // terminal of a foreground process group, it will cause a + // terminal of a foreground process group, it will cause a // SIGINT to be sent to this foreground process group. When // neither IGNBRK nor BRKINT are set, a BREAK reads as a NUL // character, except when PARMRK is set, in which case it reads // as the sequence \377 \0 \0. // IGNPAR Ignore framing errors and parity errors. - // PARMRK If IGNPAR is not set, prefix a character with a parity error - // framing error with \377 \0. If neither IGNPAR nor PARMRK or + // PARMRK If IGNPAR is not set, prefix a character with a parity error + // framing error with \377 \0. If neither IGNPAR nor PARMRK or // is set, read a character with a parity error or framing error as \0. // INPCK Enable input parity checking. // ISTRIP Strip off eighth bit. @@ -620,9 +1059,9 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // implement this bit, and acts as if it is always set. tio.c_iflag = 0; - #if 0 //##++ + #if 0 // TODO Do we want or need this? if ( c_cflag & PARENB ) - tio.c_iflag |= IGNPAR; //##++ this also ignores framing errors + tio.c_iflag |= IGNPAR; // This also ignores framing errors. #endif @@ -636,14 +1075,14 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // ONOCR Don't output CR at column 0. // ONLRET Don't output CR. // OFILL Send fill characters for a delay, rather than using a timed delay. - // OFDEL (not in POSIX) Fill character is ASCII DEL (0177). If unset, + // OFDEL (not in POSIX) Fill character is ASCII DEL (0177). If unset, // fill character is ASCII NUL. // NLDLY Newline delay mask. Values are NL0 and NL1. // CRDLY Carriage return delay mask. Values are CR0, CR1, CR2, or CR3. - // TABDLY Horizontal tab delay mask. Values are TAB0, TAB1, TAB2, TAB3 - // (or XTABS). A value of TAB3, that is, XTABS, expands tabs to + // TABDLY Horizontal tab delay mask. Values are TAB0, TAB1, TAB2, TAB3 + // (or XTABS). A value of TAB3, that is, XTABS, expands tabs to // spaces (with tab stops every eight columns). - // BSDLY Backspace delay mask. Values are BS0 or BS1. + // BSDLY Backspace delay mask. Values are BS0 or BS1. // (Has never been implemented.) // VTDLY Vertical tab delay mask. Values are VT0 or VT1. // FFDLY Form feed delay mask. Values are FF0 or FF1. @@ -657,53 +1096,53 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // EOF, EOL, EOL2, ERASE, KILL, LNEXT, REPRINT, STATUS, and // WERASE, and buffers by lines. // XCASE (not in POSIX; not supported under Linux) If ICANON is also - // set, terminal is uppercase only. Input is converted to - // lowercase, except for characters preceded by \. On output, - // uppercase characters are preceded by \ and lowercase + // set, terminal is uppercase only. Input is converted to + // lowercase, except for characters preceded by \. On output, + // uppercase characters are preceded by \ and lowercase // characters are converted to uppercase. // ECHO Echo input characters. - // ECHOE If ICANON is also set, the ERASE character erases the preceding + // ECHOE If ICANON is also set, the ERASE character erases the preceding // input character, and WERASE erases the preceding word. // ECHOK If ICANON is also set, the KILL character erases the current line. // ECHONL If ICANON is also set, echo the NL character even if ECHO is not set. // ECHOCTL (not in POSIX) If ECHO is also set, ASCII control signals // other than TAB, NL, START, and STOP are echoed as ^X, where - // X is the character with ASCII code 0x40 greater than the control + // X is the character with ASCII code 0x40 greater than the control // signal. For example, character 0x08 (BS) is echoed as ^H. - // ECHOPRT (not in POSIX) If ICANON and IECHO are also set, characters are + // ECHOPRT (not in POSIX) If ICANON and IECHO are also set, characters are // printed as they are being erased. - // ECHOKE (not in POSIX) If ICANON is also set, KILL is echoed by erasing + // ECHOKE (not in POSIX) If ICANON is also set, KILL is echoed by erasing // each character on the line, as specified by ECHOE and ECHOPRT. // DEFECHO (not in POSIX) Echo only when a process is reading. // FLUSHO (not in POSIX; not supported under Linux) Output is being flushed. // This flag is toggled by typing the DISCARD character. // NOFLSH Disable flushing the input and output queues when generating the // SIGINT, SIGQUIT and SIGSUSP signals. - // TOSTOP Send the SIGTTOU signal to the process group of a background + // TOSTOP Send the SIGTTOU signal to the process group of a background // process which tries to write to its controlling terminal. // PENDIN (not in POSIX; not supported under Linux) All characters in the - // input queue are reprinted when the next character is read. + // input queue are reprinted when the next character is read. // (bash handles typeahead this way.) // IEXTEN Enable implementation-defined input processing. This flag, as - // well as ICANON must be enabled for the special characters EOL2, + // well as ICANON must be enabled for the special characters EOL2, // LNEXT, REPRINT, WERASE to be interpreted, and for the IUCLC flag // to be effective. tio.c_lflag = 0; // VINTR (003, ETX, Ctrl-C, or also 0177, DEL, rubout) Interrupt character. - // Send a SIGINT signal. Recognized when ISIG is set, and then not + // Send a SIGINT signal. Recognized when ISIG is set, and then not // passed as input. // VQUIT (034, FS, Ctrl-\) Quit character. Send SIGQUIT signal. Recognized // when ISIG is set, and then not passed as input. // VERASE (0177, DEL, rubout, or 010, BS, Ctrl-H, or also #) Erase character. // This erases the previous not-yet-erased character, but does not erase - // past EOF or beginning-of-line. Recognized when ICANON is set, + // past EOF or beginning-of-line. Recognized when ICANON is set, // and then not passed as input. // VKILL (025, NAK, Ctrl-U, or Ctrl-X, or also @) Kill character. This erases // the input since the last EOF or beginning-of-line. Recognized when // ICANON is set, and then not passed as input. - // VEOF (004, EOT, Ctrl-D) End-of-file character. More precisely: this + // VEOF (004, EOT, Ctrl-D) End-of-file character. More precisely: this // character causes the pending tty buffer to be sent to the waiting // user program without waiting for end-of-line. If it is the first // character of the line, the read() in the user program returns 0, @@ -722,9 +1161,9 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // typed. Recognized when IXON is set, and then not passed as input. // VSUSP (032, SUB, Ctrl-Z) Suspend character. Send SIGTSTP signal. Recognized // when ISIG is set, and then not passed as input. - // VDSUSP (not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) Delayed + // VDSUSP (not in POSIX; not supported under Linux; 031, EM, Ctrl-Y) Delayed // suspend character: send SIGTSTP signal when the character is read by - // the user program. Recognized when IEXTEN and ISIG are set, and the + // the user program. Recognized when IEXTEN and ISIG are set, and the // system supports job control, and then not passed as input. // VLNEXT (not in POSIX; 026, SYN, Ctrl-V) Literal next. Quotes the next input // character, depriving it of a possible special meaning. Recognized @@ -733,18 +1172,18 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // and IEXTEN are set, and then not passed as input. // VREPRINT (not in POSIX; 022, DC2, Ctrl-R) Reprint unread characters. Recognized // when ICANON and IEXTEN are set, and then not passed as input. - // VDISCARD (not in POSIX; not supported under Linux; 017, SI, Ctrl-O) Toggle: + // VDISCARD (not in POSIX; not supported under Linux; 017, SI, Ctrl-O) Toggle: // start/stop discarding pending output. Recognized when IEXTEN is set, // and then not passed as input. // VSTATUS (not in POSIX; not supported under Linux; status request: 024, DC4, Ctrl-T). // Setting up c_cc[VMIN] and c_cc[VTIME]: - // If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before + // If MIN > 0 and TIME = 0, MIN sets the number of characters to receive before // the read is satisfied. As TIME is zero, the timer is not used. // If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will be - // satisfied if a single character is read, or TIME is exceeded - // (t = TIME *0.1 s). If TIME is exceeded, no character will be + // satisfied if a single character is read, or TIME is exceeded + // (t = TIME *0.1 s). If TIME is exceeded, no character will be // returned. // If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The // read will be satisfied if MIN characters are received, or the @@ -752,29 +1191,33 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, // every time a character is received and only becomes active after // the first character has been received. // If MIN = 0 and TIME = 0, read will be satisfied immediately. The number of - // characters currently available, or the number of characters - // requested will be returned. You could issue a + // characters currently available, or the number of characters + // requested will be returned. You could issue a // fcntl(fd, F_SETFL, FNDELAY); before reading to get the same result. - // setup control characters for non-blocking read + // Setup control characters for non-blocking read. tio.c_cc[VMIN] = 0; tio.c_cc[VTIME] = 0; - // now clean the modem line and activate the settings for modem - tcflush( port_handle, TCIFLUSH ); - tcsetattr( port_handle, TCSANOW, &tio ); + // Now clean the modem line and activate the settings for modem. + if ( tcflush( port_handle, TCIFLUSH ) < 0 ) + return mbg_get_last_error( "tcflush failed in mbgserio_set_parms" ); + + if ( tcsetattr( port_handle, TCSANOW, &tio ) < 0 ) + return mbg_get_last_error( "tcsetattr failed in mbgserio_set_parms" ); - fflush( stdout ); - setvbuf( stdout, NULL, _IONBF, 0 ); + // On success, fall through. } #elif defined( MBG_TGT_DOS ) #if defined( _USE_V24TOOLS ) { - int datab = 8; char parity = 'N'; - int stopb = 1; - // setup framing. + sp.data_bits = 8; + sp.parity_bits = 0; + sp.stop_bits = 1; + + // Setup framing. for ( cp = framing; *cp; cp++ ) { char c = toupper( *cp ); @@ -783,26 +1226,33 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, { case '7': case '8': - datab = c - '0'; + sp.data_bits = c - '0'; break; case 'N': + parity = c; + sp.parity_bits = 0; + break; + case 'E': case 'O': - parity = *cp; + parity = c; + sp.parity_bits = 1; break; case '1': case '2': - stopb = c - '0'; + sp.stop_bits = c - '0'; break; default: - return MBGSERIO_INV_CFG; // invalid framing string + return MBG_ERR_PARM_FMT; // Invalid framing string. } } - v24setparams( port_handle, baud_rate, datab, parity, stopb ); + v24setparams( port_handle, baud_rate, datab, parity, stopb ); // TODO check rc? + + // On success, fall through. } #else @@ -816,145 +1266,668 @@ _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, #endif - return 0; + // Remember the configured speed parameters. + sdev->sp = sp; -} // mbgserio_set_parms + rc = MBG_SUCCESS; + + return rc; +} // mbgserio_set_parms -#if defined( MBG_TGT_WIN32 ) && !defined( MBG_TGT_CVI ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_read( MBG_PORT_HANDLE h, void *buffer, unsigned int count ) +/** + * @brief Read from a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[out] buffer Buffer to take the bytes read. + * @param[in] count Size of @p buffer. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_read( MBGSERIO_DEV *sdev, void *buffer, unsigned int count ) { - BOOL fReadStat; - COMSTAT ComStat; - DWORD dwErrorFlags; - DWORD dwLength; + #if defined( MBG_TGT_CVI ) - ClearCommError( h, &dwErrorFlags, &ComStat ); + return ComRd( sdev->port_handle, (char *) buffer, count ); // TODO Check or translate return code. - if ( dwErrorFlags ) // transmission error (parity, framing, etc.) - return MBGSERIO_FAIL; + #elif defined( MBG_TGT_WIN32 ) + BOOL fReadStat; + COMSTAT ComStat; + DWORD dwErrorFlags; + DWORD dwLength; - dwLength = min( (DWORD) count, ComStat.cbInQue ); + ClearCommError( sdev->port_handle, &dwErrorFlags, &ComStat ); - if ( dwLength ) - { - fReadStat = ReadFile( h, buffer, dwLength, &dwLength, NULL ); + if ( dwErrorFlags ) // Transmission error (parity, framing, etc.). + return MBG_ERR_IO; // TODO Retrieve and return real error code. - if ( !fReadStat ) - return MBGSERIO_FAIL; - } - return dwLength; + dwLength = min( (DWORD) count, ComStat.cbInQue ); + + if ( dwLength ) + { + fReadStat = ReadFile( sdev->port_handle, buffer, dwLength, &dwLength, NULL ); + + if ( !fReadStat ) + return MBG_ERR_IO; // TODO Retrieve and return real error code. + } + + return dwLength; + + #elif defined( MBG_TGT_POSIX ) + + int rc = read( sdev->port_handle, buffer, count ); + + if ( rc < 0 ) // Usually -1 in case of error. + rc = mbg_get_last_error( "read failed in mbgserio_read" ); + + return rc; + + #elif defined( MBG_TGT_DOS ) && defined( _USE_V24TOOLS ) + + int rc = v24read( sdev->port_handle, buffer, count ); + + if ( rc < 0 ) + rc = MBG_ERR_UNSPEC; + + return rc; + + #else + + #error mbgserio_read() not implemented for this target + + #endif } // mbgserio_read /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_write( MBG_PORT_HANDLE h, const void *buffer, unsigned int count ) +/** + * @brief Write to a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] buffer Buffer with the bytes to write. + * @param[in] count Number of bytes in @p buffer to write. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_write( MBGSERIO_DEV *sdev, const void *buffer, unsigned int count ) { - BOOL fWriteStat; - COMSTAT ComStat; - DWORD dwErrorFlags; - DWORD dwThisBytesWritten; - DWORD dwTotalBytesWritten = 0; + #if defined( MBG_TGT_CVI ) - while ( dwTotalBytesWritten < (DWORD) count ) - { - dwThisBytesWritten = 0; + return ComWrt( sdev->port_handle, (char *) buffer, count ); // TODO Check or translate return code. - fWriteStat = WriteFile( h, ( (char *) buffer ) + dwTotalBytesWritten, - count - dwTotalBytesWritten, - &dwThisBytesWritten, NULL ); - if ( !fWriteStat ) + #elif defined( MBG_TGT_WIN32 ) + + BOOL fWriteStat; + COMSTAT ComStat; + DWORD dwErrorFlags; + DWORD dwThisBytesWritten; + DWORD dwTotalBytesWritten = 0; + MBG_PORT_HANDLE h = sdev->port_handle; + + while ( dwTotalBytesWritten < (DWORD) count ) { - #if defined( _DEBUG ) - DWORD dw = GetLastError(); - #endif - break; //##++ Error: Unable to write + dwThisBytesWritten = 0; + + fWriteStat = WriteFile( h, ( (char *) buffer ) + dwTotalBytesWritten, + count - dwTotalBytesWritten, + &dwThisBytesWritten, NULL ); + if ( !fWriteStat ) + return mbg_get_last_error( "write failed in mbgserio_write" ); + + dwTotalBytesWritten += dwThisBytesWritten; + + ClearCommError( h, &dwErrorFlags, &ComStat ); + + if ( dwErrorFlags ) + { + // Possible errors: + // + // CE_RXOVER 0x0001 // Receive Queue overflow + // CE_OVERRUN 0x0002 // Receive Overrun Error, next character(s) lost + // CE_RXPARITY 0x0004 // Receive Parity Error + // CE_FRAME 0x0008 // Receive Framing error + // CE_BREAK 0x0010 // Break Detected + // CE_TXFULL 0x0100 // TX Queue is full + // CE_PTO 0x0200 // LPTx Timeout + // CE_IOE 0x0400 // LPTx I/O Error + // CE_DNS 0x0800 // LPTx Device not selected + // CE_OOP 0x1000 // LPTx Out-Of-Paper + // CE_MODE 0x8000 // Requested mode unsupported + // + // Except CE_TXFULL these are usually RX error flags, + // so we just ignore them. + } } - dwTotalBytesWritten += dwThisBytesWritten; + return dwTotalBytesWritten; - ClearCommError( h, &dwErrorFlags, &ComStat ); + #elif defined( MBG_TGT_POSIX ) - if ( dwErrorFlags ) - break; //#++ Error: Check flags - } + int rc = write( sdev->port_handle, buffer, count ); // Returns -1 on error, else number of byte written. + + if ( rc == -1 ) + rc = mbg_get_last_error( "write failed in mbgserio_write" ); + + return rc; + + #elif defined( MBG_TGT_DOS ) && defined( _USE_V24TOOLS ) + + int rc = v24write( sdev->port_handle, buffer, count ); // TODO Check or translate return code. - return dwTotalBytesWritten; + if ( rc < 0 ) + rc = MBG_ERR_UNSPEC; + + return rc; + + #else + + #error mbgserio_write() not implemented for this target + + #endif } // mbgserio_write -#endif // defined( MBG_TGT_WIN32 ) && !defined( MBG_TGT_CVI ) + + +/*HDR*/ +/** + * @brief Flush the transmit buffer of a serial port. + * + * @param[in,out] sdev Control structure of the device. + */ +_NO_MBG_API_ATTR void _MBG_API mbgserio_flush_tx( MBGSERIO_DEV *sdev ) +{ + #if defined( MBG_TGT_CVI ) + + FlushOutQ( sdev->port_handle ); + + #elif defined( MBG_TGT_WIN32 ) + + #if defined( DEBUG ) + printf( "Flushing TX buffers ...\n" ); + #endif + + FlushFileBuffers( sdev->port_handle ); + + #if ( 1 && defined( DEBUG ) ) // TODO + { + COMSTAT ComStat; + DWORD dwErrorFlags; + + for (;;) + { + ClearCommError( sdev->port_handle, &dwErrorFlags, &ComStat ); + + printf( "ErrFlags: %04X, out queue: %u\n", dwErrorFlags, ComStat.cbOutQue ); + + if ( ComStat.cbOutQue == 0 ) + break; + + Sleep( 1 ); + } + } + #endif + + #elif defined( MBG_TGT_POSIX ) + + tcdrain( sdev->port_handle ); + + #elif defined( MBG_TGT_DOS ) && defined( _USE_V24TOOLS ) + + v24flush( sdev->port_handle, SND ); + + #else + + #error mbgserio_flush_tx() not implemented for this target + + #endif + +} // mbgserio_flush_tx /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgserio_read_wait( MBG_PORT_HANDLE h, void *buffer, - uint count, ulong char_timeout ) +/** + * @brief Get the current poll timeout count, in milliseconds. + * + * Used for timeout in ::mbgserio_read_wait. + * + * @param[in] sdev Control structure of the device. + * + * @return The current timeout value, in milliseconds. + * + * @see ::mbgserio_set_dev_poll_timeout + * @see ::mbgserio_read_wait + */ +ulong mbgserio_get_dev_poll_timeout( const MBGSERIO_DEV *sdev ) { - int n_bytes; + return sdev->poll_timeout; + +} // mbgserio_get_dev_poll_timeout - #if _USE_SELECT_FOR_SERIAL_IO + + +/*HDR*/ +/** + * @brief Set a new poll timeout, in milliseconds. + * + * Used for timeout in ::mbgserio_read_wait. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] new_timeout New timeout, in milliseconds. + * + * @return The previous timeout value, in milliseconds. + * + * @see ::mbgserio_get_dev_poll_timeout + * @see ::mbgserio_read_wait + */ +ulong mbgserio_set_dev_poll_timeout( MBGSERIO_DEV *sdev, ulong new_timeout ) +{ + ulong prev_timeout = mbgserio_get_dev_poll_timeout( sdev ); + + sdev->poll_timeout = new_timeout; + + return prev_timeout; + +} // mbgserio_set_dev_poll_timeout + + + +/*HDR*/ +/** + * @brief Wait for data and read from a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[out] buffer Buffer to take the bytes read. + * @param[in] count Size of @p buffer. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_read_wait( MBGSERIO_DEV *sdev, void *buffer, + unsigned int count ) +{ + int rc; + + #if _USE_SELECT_FOR_SERIAL_IO // Should be used preferably. struct timeval tv_char_timeout; fd_set fds; - int rc; + int max_fd; - mbgserio_msec_to_timeval( char_timeout, &tv_char_timeout ); + mbg_msec_to_timeval( sdev->poll_timeout, &tv_char_timeout ); FD_ZERO( &fds ); - FD_SET( h, &fds ); + FD_SET( sdev->port_handle, &fds ); + + #if defined( MBG_TGT_WIN32 ) + // On Windows, an fd is a handle which can't simply + // be converted to an int, but the first argument of + // select() is ignored on Windows anyway, so we just + // set max_fd to 0. + max_fd = 0; + #else + max_fd = sdev->port_handle; + #endif - rc = select( h + 1, &fds, NULL, NULL, &tv_char_timeout ); + rc = select( max_fd + 1, &fds, NULL, NULL, &tv_char_timeout ); - if ( rc < 0 ) // error - goto fail; + if ( rc < 0 ) // Error. + return mbg_get_last_error( "select failed in mbgserio_read_wait" ); - if ( rc == 0 ) // timeout - goto timeout; + if ( rc == 0 ) // Timeout. + return MBG_ERR_TIMEOUT; - // data is available - n_bytes = _mbgserio_read( h, buffer, count ); + // Data is available. + rc = mbgserio_read( sdev, buffer, count ); #else + MBG_TMO_TIME tmo; - mbg_tmo_set_timeout_ms( &tmo, char_timeout ); + mbg_tmo_set_timeout_ms( &tmo, sdev->poll_timeout ); for (;;) // wait to read one new char { - n_bytes = _mbgserio_read( h, buffer, count ); + rc = mbgserio_read( sdev, buffer, count ); - if ( n_bytes > 0 ) // new char(s) received + if ( rc != 0 ) // Byte(s) received, or error. break; - if ( n_bytes < 0 ) // error - goto fail; - if ( mbg_tmo_curr_time_is_after( &tmo ) ) - goto timeout; + return MBG_ERR_TIMEOUT; - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) usleep( 10 * 1000 ); #endif } #endif - return n_bytes; + return rc; -timeout: - return MBGSERIO_TIMEOUT; +} // mbgserio_read_wait -fail: - return MBGSERIO_FAIL; -} // mbgserio_read_wait + +/*HDR*/ +/** + * @brief Compute the time required to transmit a number of bytes, in nanoseconds. + * + * Depends on the baud rate and the effective number of + * bits per byte, i.e. data / parity / stop bits. + * Assuming the transmission is without gaps. + * + * If 2 stobits are used, this differs from ::mbgserio_get_duration_rcv. + * See ::mbgserio_get_duration_rcv. + * + * @param[in] p_sp The relevant parameters of the transmission. + * @param[in] n_bytes The number of bytes to transmit. + * + * @return The the computed duration, in nanoseconds. + * + * @see ::mbgserio_get_duration_rcv + */ +_NO_MBG_API_ATTR int32_t _MBG_API mbgserio_get_duration_xmt( MBGSERIO_SPEED_PARMS *p_sp, int n_bytes ) +{ + // Total number of bits, including start bit. + int n_bits = n_bytes * ( 1 + p_sp->data_bits + p_sp->parity_bits + p_sp->stop_bits ); + + return (int32_t) ( ( (int64_t) n_bits * NSEC_PER_SEC ) / p_sp->baud_rate ); + +} // mbgserio_get_duration_xmt +/*HDR*/ +/** + * @brief Compute the receive delay for a number of bytes, in nanoseconds. + * + * Depends on the baud rate and the effective number of + * bits per byte, i.e. data / parity / stop bits. + * Assuming the transmission is without gaps. + * + * Differs from ::mbgserio_get_duration_xmt in case there are + * 2 stop bits. This is because because UARTS usually flag + * reception of a byte after the first stop bit, even if + * 2 stop bits are used for transmission. + * + * @param[in] p_sp The relevant parameters of the transmission. + * @param[in] n_bytes The number of bytes to receive. + * @param[in] n_bits A number of additional bits to account for. + * + * @return The the computed duration, in nanoseconds. + * + * @see ::mbgserio_get_duration_xmt + */ +_NO_MBG_API_ATTR int32_t _MBG_API mbgserio_get_duration_rcv( const MBGSERIO_SPEED_PARMS *p_sp, + int n_bytes, int n_bits ) +{ + // Total number of bits, including start bit. + int n_bits_total = n_bytes * ( 1 + p_sp->data_bits + p_sp->parity_bits + p_sp->stop_bits + n_bits ); + + // If 2 stop bits are used for transmission, we + // subtract 1 stop bit from the total number of bits. + if ( p_sp->stop_bits > 1 ) + n_bits_total--; + + return (int32_t) ( ( (int64_t) n_bits_total * NSEC_PER_SEC ) / p_sp->baud_rate ); + +} // mbgserio_get_duration_rcv + + + +#if USE_LINUX_DEV_DIR + +static /*HDR*/ +int scandir_filter_serial_port( const struct dirent *pde ) +{ + static const char dev_name_1[] = "ttyS"; + static const char dev_name_2[] = "ttyUSB"; + char tmp_str[100]; + MBGSERIO_DEV *sdev; + int rc; + + int l; + + l = strlen( dev_name_1 ); + + if ( strncmp( pde->d_name, dev_name_1, l ) == 0 ) + goto check; + + + l = strlen( dev_name_2 ); + + if ( strncmp( pde->d_name, dev_name_2, l ) == 0 ) + goto check; + + return 0; + +check: + // If the first character after the search string is not a digit, + // the search result is not what we want. + if ( pde->d_name[l] < '0' || pde->d_name[l] > '9' ) + return 0; + + snprintf_safe( tmp_str, sizeof( tmp_str ), "%s/%s", dev_dir, pde->d_name ); + + rc = mbgserio_open( &sdev, tmp_str ); + + if ( rc < 0 ) + { + #if defined( DEBUG ) + fprintf( stderr, "failed to open %s: %i\n", tmp_str, rc ); + #endif + + return 0; + } + + #if defined( DEBUG ) + fprintf( stderr, "port %s opened successfully\n", tmp_str ); + #endif + + mbgserio_close( &sdev ); + + return 1; + +} // scandir_filter_serial_port + +#endif // USE_LINUX_DEV_DIR + + + +/*HDR*/ +/** + * @brief Set up a string list with names of available serial ports. + * + * @param[out] list Address of the header of a list to be set up. + * @param[in] max_devs Maximum number of list entries to create. + * + * @return The number of entries in the list. + * + * @see ::mbgserio_free_str_list + */ +_NO_MBG_API_ATTR int _MBG_API mbgserio_setup_port_str_list( MBG_STR_LIST **list, int max_devs ) +{ + MBG_STR_LIST *list_head; + int n = 0; + int i = 0; + + (*list) = (MBG_STR_LIST *) malloc( sizeof( **list ) ); + memset( (*list), 0, sizeof( **list ) ); + + list_head = (*list); + + +#if 0 && USE_LINUX_DEV_DIR + + struct dirent **namelist; + + n = scandir( dev_dir, &namelist, scandir_filter_serial_port, versionsort ); + + if ( n < 0 ) + perror( "scandir" ); + else + { + for ( i = 0; i < n; i++ ) + { + printf( "%s/%s\n", dev_dir, namelist[i]->d_name ); + free( namelist[i] ); + } + + free( namelist ); + } + +#elif 0 && USE_LINUX_DEV_DIR + + DIR *pd = opendir( dev_dir ); + + if ( pd ) + { + struct dirent *pde; + + while ( ( pde = readdir( pd ) ) != NULL ) + { + if ( strncmp( pde->d_name, "ttyS", 4 ) == 0 ) + goto found; + + if ( strncmp( pde->d_name, "ttyUSB", 6 ) == 0 ) + goto found; + + continue; + +found: + fprintf( stderr, "found /dev/%s\n", pde->d_name ); + } + + closedir( pd ); + } + +#else + + for ( i = 0; i < max_devs; i++ ) + { + char dev_name[100] = { 0 }; + int rc; + + #if defined( MBG_TGT_WIN32 ) + + HANDLE h = INVALID_HANDLE_VALUE; + + snprintf_safe( dev_name, sizeof( dev_name ), "COM%i", i + 1 ); + rc = win32_open_serial_port( dev_name, &h ); + + // If access is denied, the port exists but is in use. + if ( rc != MBG_SUCCESS && rc != MBG_ERR_ACCESS ) + continue; // Memory allocation error. + + if ( h != INVALID_HANDLE_VALUE ) + CloseHandle( h ); + + #elif defined( MBG_TGT_LINUX ) + + MBGSERIO_DEV *sdev; + snprintf_safe( dev_name, sizeof( dev_name ), "/dev/ttyS%i", i ); + rc = mbgserio_open( &sdev, dev_name ); + + if ( rc < 0 ) + { + snprintf_safe( dev_name, sizeof( dev_name ), "/dev/ttyUSB%i", i ); + + rc = mbgserio_open( &sdev, dev_name ); + + if ( rc < 0 ) + continue; + } + + mbgserio_close( &sdev ); + + #endif + + + // Add the device name to the list. + + (*list)->s = (char *) malloc( strlen( dev_name ) + 1 ); + strcpy( (*list)->s, dev_name ); + + (*list)->next = (MBG_STR_LIST *) malloc( sizeof( **list ) ); + (*list) = (*list)->next; + + memset( (*list), 0, sizeof( **list ) ); + n++; + + // TODO Do we want or need this? + // + // if ( ++i >= N_SUPP_DEV_EXT ) + // break; + } + + if ( n == 0 ) + { + free( *list ); + list_head = NULL; + } +#endif + + *list = list_head; + + return n; + +} // mbgserio_setup_port_str_list + + + +/*HDR*/ +/** + * @brief Free a string list that has been allocated before. + * + * @param[out] list Address of the string list to be freed. + * + * @see ::mbgserio_setup_port_str_list + */ +_NO_MBG_API_ATTR void _MBG_API mbgserio_free_str_list( MBG_STR_LIST *list ) +{ + int i = 0; + + while ( i < 1000 ) // FIXME TODO Why 1000 ? + { + if ( list ) + { + if ( list->s ) + { + free( list->s ); + list->s = NULL; + } + + if ( list->next ) + { + MBG_STR_LIST *next = list->next; + free( list ); + list = next; + } + else + { + if ( list ) + { + free( list ); + list = NULL; + } + break; + } + } + else + break; + + i++; + } + +} // mbgserio_free_str_list + diff --git a/mbglib/common/mbgserio.h b/mbglib/common/mbgserio.h index 2367e14..96c3be7 100644 --- a/mbglib/common/mbgserio.h +++ b/mbglib/common/mbgserio.h @@ -1,7 +1,7 @@ /************************************************************************** * - * $Id: mbgserio.h 1.4 2009/09/01 10:54:29 martin REL_M $ + * $Id: mbgserio.h 1.12 2021/12/01 10:19:10 martin.burnicki REL_M $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * @@ -10,6 +10,36 @@ * * ----------------------------------------------------------------------- * $Log: mbgserio.h $ + * Revision 1.12 2021/12/01 10:19:10 martin.burnicki + * Make a parameter of mbgserio_get_duration_rcv() const. + * Revision 1.11 2021/05/04 20:43:25 martin + * New field MBGSERIO_DEV::rcv_fifo_threshold, for the receive FIFO + * threshold, and structure MBGSERIO_SPEED_PARMS. These are useful + * to determine the transmission delay of a received time string. + * Removed some trailing spaces. + * Updated some comments and function prototypes. + * Revision 1.10 2021/04/07 16:32:11 martin + * Updated a bunch of comments, and renamed some local variables. + * Revision 1.9 2018/11/29 16:03:58 martin + * Removed obsolete conditional _USE_MBG_TSTR stuff. + * Revision 1.8 2017/07/06 07:41:00Z martin + * Renamed SERIAL_IO_STATUS to MBGSERIO_DEV, + * and renamed some fields of the structure. + * Defined default device names for serial ports + * depending on the target system. + * Check preprocessor symbol _USE_MBG_TSTR instead + * of _USE_CHK_TSTR. + * Check for MBG_TGT_POSIX instead of MBG_TGT_UNIX. + * Updated function prototypes. + * Revision 1.7 2013/02/01 16:10:45 martin + * Got rid of _mbg_open/close/read/write() macros. + * Functions are now used instead. + * Updated function prototypes. + * Revision 1.6 2011/08/23 10:15:25Z martin + * Updated function prototypes. + * Revision 1.5 2011/08/04 09:48:55 martin + * Support flushing output. + * Re-ordered some definitions. * Revision 1.4 2009/09/01 10:54:29 martin * Include mbg_tmo.h for the new portable timeout functions. * Added symbols for return codes in case of an error. @@ -30,21 +60,19 @@ /* Other headers to be included */ +#include <mbg_tgt.h> #include <mbg_tmo.h> +#include <mbgerror.h> #include <stdlib.h> #include <string.h> -#if defined( MBG_TGT_UNIX ) +#if defined( MBG_TGT_POSIX ) #include <termios.h> #endif -#if _USE_CHK_TSTR - #include <chk_tstr.h> -#endif - #if !defined( _USE_SELECT_FOR_SERIAL_IO ) - #if defined( MBG_TGT_UNIX ) + #if defined( MBG_TGT_POSIX ) #define _USE_SELECT_FOR_SERIAL_IO 1 #else #define _USE_SELECT_FOR_SERIAL_IO 0 @@ -62,16 +90,41 @@ /* Start of header body */ -#define MBGSERIO_FAIL -1 // Generic I/O error -#define MBGSERIO_TIMEOUT -2 // timeout -#define MBGSERIO_INV_CFG -3 // invalid configuration parameters - +#ifdef __cplusplus +extern "C" { +#endif -#if !defined( DEFAULT_DEV_NAME ) +#if !defined( DEFAULT_SERIAL_DEVICE_NAME ) + // For applications that support 2 serial ports, we also + // define default names for the second port to be used. #if defined( MBG_TGT_WIN32 ) || defined( MBG_TGT_DOS ) - #define DEFAULT_DEV_NAME "COM1" + #define DEFAULT_SERIAL_DEVICE_NAME "COM1" + #define DEFAULT_SERIAL_DEVICE_NAME_2 "COM2" #elif defined( MBG_TGT_LINUX ) - #define DEFAULT_DEV_NAME "/dev/ttyS0" + #define DEFAULT_SERIAL_DEVICE_NAME "/dev/ttyS0" + #define DEFAULT_SERIAL_DEVICE_NAME_2 "/dev/ttyS1" + #elif defined( MBG_TGT_FREEBSD ) + // Call-in ports are named /dev/ttyuN where N is the port number, starting from 0. + // Generally, the call-in port is used for terminals. Call-in ports require that + // the serial line assert the "Data Carrier Detect" signal to work correctly. + // Call-out ports are named /dev/cuauN on FreeBSD versions 8.X and later and + // /dev/cuadN on FreeBSD versions 7.X and earlier. Call-out ports are usually not + // used for terminals, but are used for modems. The call-out port can be used if the + // serial cable or the terminal does not support the "Data Carrier Detect" signal. + #if ( __FreeBSD__ > 7 ) + #define DEFAULT_SERIAL_DEVICE_NAME "/dev/cuau0" + #define DEFAULT_SERIAL_DEVICE_NAME_2 "/dev/cuau1" + #else + #define DEFAULT_SERIAL_DEVICE_NAME "/dev/cuad0" + #define DEFAULT_SERIAL_DEVICE_NAME_2 "/dev/cuad1" + #endif + #elif defined( MBG_TGT_NETBSD ) + // The ttyXX devices are traditional dial-in devices; + // the dtyXX devices are used for dial-out. See tty(4). + #define DEFAULT_SERIAL_DEVICE_NAME "/dev/dty00" + #define DEFAULT_SERIAL_DEVICE_NAME_2 "/dev/dty01" + #else + #error DEFAULT_SERIAL_DEVICE_NAME needs to be defined for this target. #endif #endif @@ -94,61 +147,18 @@ #include <rs232.h> - #define _mbg_open _open - #define _mbg_close _close - #define _mbg_read _read - #define _mbg_write _write - - #define _mbgserio_write( _dh, _p, _sz ) \ - ComWrt( _dh, (char *) (_p), _sz ) - - #define _mbgserio_read( _dh, _p, _sz ) \ - ComRd( _dh, (char *) (_p), _sz ) - #elif defined( MBG_TGT_WIN32 ) - #include <windows.h> #include <io.h> - #define _mbg_open _open - #define _mbg_close _close - #define _mbg_read _read - #define _mbg_write _write - - #define _mbgserio_write mbgserio_write - #define _mbgserio_read mbgserio_read - - #elif defined( MBG_TGT_UNIX ) - - #include <unistd.h> - - #define _mbg_open open - #define _mbg_close close - #define _mbg_read read - #define _mbg_write write - #elif defined( MBG_TGT_DOS ) #if defined( _USE_V24TOOLS ) #include <v24tools.h> - - #define _mbgserio_open v24open - #define _mbgserio_read v24read - #define _mbgserio_write v24write #endif #endif - #if !defined( _mbgserio_open ) - #define _mbgserio_open _mbg_open - #endif - #if !defined( _mbgserio_write ) - #define _mbgserio_write _mbg_write - #endif - #if !defined( _mbgserio_read ) - #define _mbgserio_read _mbg_read - #endif - #endif @@ -162,42 +172,286 @@ typedef struct _MBG_STR_LIST +/** + * @brief Serial parameters required to compute the transmission time. + * + * @see ::mbgserio_get_duration_xmt + * @see ::mbgserio_get_duration_rcv + */ typedef struct { - MBG_PORT_HANDLE port_handle; // the handle that will be used for the device + uint32_t baud_rate; ///< Baud rate (0 if unknown). + int data_bits; ///< Number of data bits (7 or 8, 0 if unknown). + int parity_bits; ///< Number of parity bits (0 or 1). + int stop_bits; ///< Number of stop bits (1 or 2, 0 if unknown). + +} MBGSERIO_SPEED_PARMS; + + + +/** + * @brief Control structure for a serial device. + * + * Should be considered opaque. + */ +typedef struct +{ + MBG_PORT_HANDLE port_handle; ///< The handle that will be used for the device. + + MBGSERIO_SPEED_PARMS sp; ///< Can be used to compute the transmission time. + + /// @brief The receive FIFO threshold of the UART. + /// + /// The UART generates a receive event (e.g. an IRQ) after + /// at least this number of bytes has been received. This may + /// affect the timing. 0 if unknown or can't be determined. + int rcv_fifo_threshold; + + ulong poll_timeout; ///< The default timeout when waiting for data, [ms]. #if defined( MBG_TGT_WIN32 ) - DCB old_dcb; - COMMTIMEOUTS old_commtimeouts; - #endif - #if defined( MBG_TGT_UNIX ) - struct termios oldtio; - //##++ struct termios newtio; - #endif + DCB org_dcb; + int org_dcb_has_been_read; + + COMMTIMEOUTS org_commtimeouts; ///< Original timeout configuration when the port was opened. + int org_commtimeouts_have_been_read; ///< Flag indicating that @p org_commtimeouts has been read. -} SERIAL_IO_STATUS; + COMMPROP comm_prop; ///< Configuration settings supported by the driver (r/o). + int comm_prop_have_been_read; ///< Flag indicating that @p comm_prop has been read. + #endif + #if defined( MBG_TGT_POSIX ) + struct termios org_tio; ///< Original port settings when the port was opened. + int org_tio_has_been_read; ///< Flag indicating that @p org_tio has been read. + #endif +} MBGSERIO_DEV; -/* function prototypes: */ -#ifdef __cplusplus -extern "C" { -#endif /* ----- function prototypes begin ----- */ /* This section was generated automatically */ /* by MAKEHDR, do not remove the comments. */ - _MBG_API_ATTR int _MBG_API mbgserio_open( SERIAL_IO_STATUS *pst, const char *dev ) ; - _MBG_API_ATTR int _MBG_API mbgserio_close( SERIAL_IO_STATUS *pst ) ; - _MBG_API_ATTR int _MBG_API mbgserio_setup_port_str_list( MBG_STR_LIST **list, int max_devs ) ; - _MBG_API_ATTR void _MBG_API _MBG_API mbgserio_free_str_list( MBG_STR_LIST *list ) ; - _MBG_API_ATTR int _MBG_API mbgserio_set_parms( SERIAL_IO_STATUS *pst, uint32_t baud_rate, const char *framing ) ; - _MBG_API_ATTR int _MBG_API mbgserio_read( MBG_PORT_HANDLE h, void *buffer, unsigned int count ) ; - _MBG_API_ATTR int _MBG_API mbgserio_write( MBG_PORT_HANDLE h, const void *buffer, unsigned int count ) ; - _MBG_API_ATTR int _MBG_API mbgserio_read_wait( MBG_PORT_HANDLE h, void *buffer, uint count, ulong char_timeout ) ; + /** + * @brief Return the base name of the device name of a serial port. + * + * In order to leave the original device name string unchanged, + * a buffer is allocated with a duplicate of the original string, + * and a pointer to the basename component in that buffer is returned. + * + * After usage, the allocated buffer whose address is saved in + * @p *pp_dup_port_name should be freed by calling ::mbgserio_free_port_basename. + * + * @param[in] pp_dup_port_name Address of a <em>char *</em> that takes the address + * of the allocated string buffer. + * @param[in] port_name The original device name string. + * + * @return A pointer to the basename which is part of an allocated copy of the original string. + * + * @see ::mbgserio_free_port_basename + */ + _NO_MBG_API_ATTR char * _MBG_API mbgserio_get_port_basename( char **pp_dup_port_name, const char *port_name ) ; + + /** + * @brief Free a buffer that has been allocated by ::mbgserio_get_port_basename. + * + * This function should be called whenever a string buffer allocated + * by an earlier ::mbgserio_get_port_basename call isn't needed anymore. + * + * @param[in] pp_dup_port_name Address of a <em>char *</em> with the address + * of the allocated string buffer. + * + * @see ::mbgserio_get_port_basename + */ + _NO_MBG_API_ATTR void _MBG_API mbgserio_free_port_basename( char **pp_dup_port_name ) ; + + /** + * @brief Determine the threshold of a serial RX FIFO buffer. + * + * This is useful to be able to exactly compensate the delay + * of a received serial data string. + * + * @note This may not be supported on each target system. + * + * @param[in] port_name The device name of the serial port. + * + * @return The number of bytes in the FIFO buffer after which + * a receive event (e.g. IRQ) is generated, + * or 0 if the real threshold can't be determined. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_get_rx_fifo_threshold( const char *port_name ) ; + + /** + * @brief Close a serial port specified by a ::MBGSERIO_DEV structure. + * + * After the port has been closed, the buffer that had been allocated + * for the ::MBGSERIO_DEV structure is freed, and the pointer to that + * buffer is set to @a NULL. + * + * @param[in,out] pp_sdev Address of a pointer to the control structure including the port handle. + * + * @return One of the @ref MBG_RETURN_CODES. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_close( MBGSERIO_DEV **pp_sdev ) ; + + /** + * @brief Open a serial port and set up a ::MBGSERIO_DEV structure. + * + * @param[in,out] pp_sdev Address of a pointer to a ::MBGSERIO_DEV device control structure + * allocated and set up by this call. Set to @a NULL on error. + * @param[in] dev_name Device name of the port to be opened. + * + * @return One of the @ref MBG_RETURN_CODES. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_open( MBGSERIO_DEV **pp_sdev, const char *dev_name ) ; + + /** + * @brief Set the transmission parameters of a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] baud_rate One of the well-known baud rates, e.g. 19200. + * @param[in] framing A short string providing the framing, e.g. "8N1". + * + * @return One of the @ref MBG_RETURN_CODES. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_set_parms( MBGSERIO_DEV *sdev, uint32_t baud_rate, const char *framing ) ; + + /** + * @brief Read from a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[out] buffer Buffer to take the bytes read. + * @param[in] count Size of @p buffer. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_read( MBGSERIO_DEV *sdev, void *buffer, unsigned int count ) ; + + /** + * @brief Write to a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] buffer Buffer with the bytes to write. + * @param[in] count Number of bytes in @p buffer to write. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_write( MBGSERIO_DEV *sdev, const void *buffer, unsigned int count ) ; + + /** + * @brief Flush the transmit buffer of a serial port. + * + * @param[in,out] sdev Control structure of the device. + */ + _NO_MBG_API_ATTR void _MBG_API mbgserio_flush_tx( MBGSERIO_DEV *sdev ) ; + + /** + * @brief Get the current poll timeout count, in milliseconds. + * + * Used for timeout in ::mbgserio_read_wait. + * + * @param[in] sdev Control structure of the device. + * + * @return The current timeout value, in milliseconds. + * + * @see ::mbgserio_set_dev_poll_timeout + * @see ::mbgserio_read_wait + */ + ulong mbgserio_get_dev_poll_timeout( const MBGSERIO_DEV *sdev ) ; + + /** + * @brief Set a new poll timeout, in milliseconds. + * + * Used for timeout in ::mbgserio_read_wait. + * + * @param[in,out] sdev Control structure of the device. + * @param[in] new_timeout New timeout, in milliseconds. + * + * @return The previous timeout value, in milliseconds. + * + * @see ::mbgserio_get_dev_poll_timeout + * @see ::mbgserio_read_wait + */ + ulong mbgserio_set_dev_poll_timeout( MBGSERIO_DEV *sdev, ulong new_timeout ) ; + + /** + * @brief Wait for data and read from a serial port. + * + * @param[in,out] sdev Control structure of the device. + * @param[out] buffer Buffer to take the bytes read. + * @param[in] count Size of @p buffer. + * + * @return On Success, the (positive) number of byte read, + * or one of the (negative) @ref MBG_ERROR_CODES on error. + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_read_wait( MBGSERIO_DEV *sdev, void *buffer, unsigned int count ) ; + + /** + * @brief Compute the time required to transmit a number of bytes, in nanoseconds. + * + * Depends on the baud rate and the effective number of + * bits per byte, i.e. data / parity / stop bits. + * Assuming the transmission is without gaps. + * + * If 2 stobits are used, this differs from ::mbgserio_get_duration_rcv. + * See ::mbgserio_get_duration_rcv. + * + * @param[in] p_sp The relevant parameters of the transmission. + * @param[in] n_bytes The number of bytes to transmit. + * + * @return The the computed duration, in nanoseconds. + * + * @see ::mbgserio_get_duration_rcv + */ + _NO_MBG_API_ATTR int32_t _MBG_API mbgserio_get_duration_xmt( MBGSERIO_SPEED_PARMS *p_sp, int n_bytes ) ; + + /** + * @brief Compute the receive delay for a number of bytes, in nanoseconds. + * + * Depends on the baud rate and the effective number of + * bits per byte, i.e. data / parity / stop bits. + * Assuming the transmission is without gaps. + * + * Differs from ::mbgserio_get_duration_xmt in case there are + * 2 stop bits. This is because because UARTS usually flag + * reception of a byte after the first stop bit, even if + * 2 stop bits are used for transmission. + * + * @param[in] p_sp The relevant parameters of the transmission. + * @param[in] n_bytes The number of bytes to receive. + * @param[in] n_bits A number of additional bits to account for. + * + * @return The the computed duration, in nanoseconds. + * + * @see ::mbgserio_get_duration_xmt + */ + _NO_MBG_API_ATTR int32_t _MBG_API mbgserio_get_duration_rcv( const MBGSERIO_SPEED_PARMS *p_sp, int n_bytes, int n_bits ) ; + + /** + * @brief Set up a string list with names of available serial ports. + * + * @param[out] list Address of the header of a list to be set up. + * @param[in] max_devs Maximum number of list entries to create. + * + * @return The number of entries in the list. + * + * @see ::mbgserio_free_str_list + */ + _NO_MBG_API_ATTR int _MBG_API mbgserio_setup_port_str_list( MBG_STR_LIST **list, int max_devs ) ; + + /** + * @brief Free a string list that has been allocated before. + * + * @param[out] list Address of the string list to be freed. + * + * @see ::mbgserio_setup_port_str_list + */ + _NO_MBG_API_ATTR void _MBG_API mbgserio_free_str_list( MBG_STR_LIST *list ) ; + /* ----- function prototypes end ----- */ diff --git a/mbglib/common/words.h b/mbglib/common/words.h index 9f6f1a9..7d6c70e 100644 --- a/mbglib/common/words.h +++ b/mbglib/common/words.h @@ -1,7 +1,7 @@ /************************************************************************** * - * $Id: words.h 1.21 2009/10/01 14:00:17 martin REL_M $ + * $Id: words.h 1.53 2021/11/29 14:22:49 martin.burnicki REL_M $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * @@ -10,6 +10,90 @@ * * ----------------------------------------------------------------------- * $Log: words.h $ + * Revision 1.53 2021/11/29 14:22:49 martin.burnicki + * New macros _nano_time_64_from_timespec() and + * _nano_time_64_from_timeval(). + * Revision 1.52 2021/03/18 11:08:31 martin + * Updated some comments. + * Revision 1.51 2021/03/16 12:20:31 martin + * Updated some comments. + * Revision 1.50 2021/03/12 10:48:34 martin + * Corrected the wording of a comment. + * Revision 1.49 2019/10/23 08:31:48 thomas-b + * Added struct name for NANO_TIME_64 + * Revision 1.48 2019/06/17 08:38:59 thomas-b + * Renamed structs according to Meinberg naming convention + * Revision 1.47 2019/06/06 12:17:14 thomas-b + * Added several struct names to allow forward declaration + * Revision 1.46 2019/02/07 14:38:56 martin + * Removed a duplicate definition. + * Revision 1.45 2018/11/22 16:38:15 martin + * Removed inclusion of mbg_no_tgt.h which is obsolete here. + * Revision 1.44 2018/06/25 09:21:02 martin + * Support MBG_TGT_NO_TGT. + * Improved STRINGIFY() macro. + * Revision 1.43 2018/01/29 10:30:23 martin + * Modified some comments. + * Revision 1.42 2017/07/26 14:28:50Z martin + * Fixed build for NetBSD. + * Revision 1.41 2017/07/05 12:06:35 martin + * Moved macro _int_from_size_t() here. + * Revision 1.40 2017/06/12 11:14:25 martin + * Empty _DEPRECATED_BY definition for firmware targets. + * Revision 1.39 2017/03/15 10:01:09 martin + * Added comments how to represent negative numbers in NANO_TIME + * and NANO_TIME_64 structures. + * Added macros _nano_time_zero() and _nano_time_64_zero(). + * Revision 1.38 2017/02/22 11:56:33 martin + * Made MBG_CODE_NAME_TABLE_ENTRY::code signed to + * avoid signed/unsigned warnings with some code tables. + * Revision 1.37 2017/01/27 12:24:35Z martin + * Moved STRINGIFY() macro here. + * Revision 1.36 2017/01/27 08:59:43 martin + * Fixed macro syntax. + * Revision 1.35 2016/08/05 12:17:21 martin + * Moved definitions for NANO_TIME and NANO_TIME_64 here. + * New macro _nano_time_64_negative(). + * Conditionally define _abs64() macro. + * Include <inttypes.h> for Keil ARMCC target. + * Added some conditional debugging code. + * Fixed some spelling. + * Revision 1.34 2014/10/20 12:31:20 martin + * Moved macro _isdigit() here. + * Revision 1.33 2014/05/27 10:18:35Z martin + * Finer control of which types are required for or already + * available on particular target systems. + * Added macros helpful to simplify declarations of code/name tables. + * Revision 1.32 2014/01/07 15:43:52 martin + * Define __mbg_inline for ARM firmware targets. + * Revision 1.31 2012/11/29 11:54:39Z martin + * Removed #if sizeof() definitions which may cause build errors + * with some older compilers. + * Include stdbool.h for __ARMCC_VERSION targets. + * Moved _nop_macro_fnc() definition here. + * Revision 1.30 2012/11/02 09:12:29Z martin + * Moved most feature detection code to mbg_tgt.h. + * Tried to define missing features most flexibly and portably. + * Revision 1.29 2012/07/11 16:45:45Z martin + * New macros to access individual bytes of long constants. + * Revision 1.28 2012/04/05 14:36:18Z martin + * Support CVI 2010 compiler which provides C99 types. + * Revision 1.27 2011/07/18 10:21:38Z martin + * Added definition for MBG_CODE_NAME_TABLE_ENTRY which can + * be used to define tables assigning strings to numeric codes. + * Revision 1.26 2011/04/06 10:23:03 martin + * Added FBYTE_OF() and FWORD_OF() macros. + * Modifications required for *BSD. + * Revision 1.25 2010/11/17 10:23:09 martin + * Define _BIT_REDEFINED if bit type is redefined. + * Revision 1.24 2010/11/17 08:44:56Z martin + * If supported, use type "bool" to implement "bit". + * Revision 1.23 2010/05/27 08:54:30Z martin + * Support fixed size data types with Keil RealView compiler for ARM. + * Keil RealView ARM targets are always considered as firmware. + * Revision 1.22 2009/10/21 07:53:55 martin + * Undid changes introduced in 1.21 since they were not consistent + * across glibc and/or Linux kernel header versions. * Revision 1.21 2009/10/01 14:00:17 martin * Conditionally define ulong and friends also for Linux/glibc. * Revision 1.20 2009/07/02 15:38:12 martin @@ -69,24 +153,38 @@ #if !defined( _IS_MBG_FIRMWARE ) -#if defined( _C166 ) || \ - defined( _CC51 ) || \ - defined( __ARM ) - #define _IS_MBG_FIRMWARE 1 -#else - #define _IS_MBG_FIRMWARE 0 -#endif - + #if defined( _C166 ) || \ + defined( _CC51 ) || \ + defined( __ARM ) || \ + defined( __ARMCC_VERSION ) + #define _IS_MBG_FIRMWARE 1 + #else + #define _IS_MBG_FIRMWARE 0 + #endif #endif + #if !_IS_MBG_FIRMWARE #include <mbg_tgt.h> +#else + #if defined( __ARMCC_VERSION ) // Keil RealView Compiler for ARM + #define __mbg_inline __inline + #include <stdint.h> + #include <inttypes.h> + #include <stdbool.h> + #define MBG_TGT_HAS_EXACT_SIZE_TYPES 1 + #else + #define MBG_TGT_MISSING_64_BIT_TYPES 1 + #endif - typedef unsigned char bit; + #if !defined( _DEPRECATED_BY ) + #define _DEPRECATED_BY( _s ) // empty definition + #endif #endif + #ifdef _WORDS #define _ext #else @@ -96,164 +194,285 @@ /* Start of header body */ +#if defined( _C166 ) \ + || defined( _CC51 ) + #define _BIT_DEFINED 1 // These compilers natively support the "bit" type. + #define USE_LONG_FOR_INT32 1 +#endif + + + +#if !defined( MBG_TGT_HAS_EXACT_SIZE_TYPES ) -// Check whether the target system supports C99 fixed-size types. + #if defined( MBG_TGT_HAS_INT_8_16_32 ) -#if defined( MBG_TGT_LINUX ) // any Linux target + // Define C99 exact size types using non-standard exact-size types. + typedef __int8 int8_t; + typedef unsigned __int8 uint8_t; + + typedef __int16 int16_t; + typedef unsigned __int16 uint16_t; + + typedef __int32 int32_t; + typedef unsigned __int32 uint32_t; - #if defined( __KERNEL__ ) - #include <linux/types.h> #else - #include <stdint.h> - #include <sys/types.h> - #endif - #define _C99_BIT_TYPES_DEFINED 1 + // Assume a 16 or 32 bit compiler which doesn't + // support exact-size types. -#elif defined( MBG_TGT_BSD ) + typedef char int8_t; + typedef unsigned char uint8_t; - #include <sys/types.h> + typedef short int16_t; + typedef unsigned short uint16_t; - #define _C99_BIT_TYPES_DEFINED 1 + // Using #if sizeof() to determine the size of a type may not + // be supported by all preprocessors, and may even result in + // build errors if used in a conditional preprocessor section, + // so we can't use this here without compatibility problems. -#elif defined( MBG_TGT_QNX ) // QNX 4.x or QNX 6.x + #if defined( USE_LONG_FOR_INT32 ) + typedef long int32_t; + typedef unsigned long uint32_t; + #elif defined( USE_INT_FOR_INT32 ) + typedef int int32_t; + typedef unsigned int uint32_t; + #else + #error Need to define int32_t and uint32_t + #endif - #if defined( MBG_TGT_QNX_NTO ) // QNX 6.x (Neutrino) with gcc - #include <stdint.h> - #else // QNX 4.x with Watcom C 10.6 - #include <sys/types.h> // 64 bit types not supported #endif - #define _C99_BIT_TYPES_DEFINED 1 + #if defined( MBG_TGT_MISSING_64_BIT_TYPES ) + + // The build environment does not support 64 bit types. However, + // 64 bit types need to be defined to avoid build errors e.g. + // if these types are formally used in function prototypes. + // We explicitly use abnormal data types to hopefully + // cause compiler errors in case these types are + // unexpectedly used to generate real code for a target + // platform which does not support 64 bit types. + typedef void *int64_t; + typedef void *uint64_t; + + #else + + // Define C99 types using non-standard exact-size types + // which are usually supported by build environments that + // support 64 bit types but don't support C99 types. + typedef __int64 int64_t; + typedef unsigned __int64 uint64_t; + + #endif #endif -// If it's not yet clear whether fixed-size types are supported, -// check the build environment which may be multi-platform. -#if !defined( _C99_BIT_TYPES_DEFINED ) +#if defined( MBG_TGT_MISSING_64_BIT_TYPES ) - #if defined( __WATCOMC__ ) - #if __WATCOMC__ > 1230 // Open Watcom C 1.3 and above - #include <stdint.h> - #define _C99_BIT_TYPES_DEFINED 1 - #elif defined( __WATCOM_INT64__ ) // Watcom C 11, non-QNX - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; + #define MBG_TGT_HAS_64BIT_TYPES 0 - #define _C99_BIT_TYPES_DEFINED 1 - #endif - #endif +#else - #if defined( __BORLANDC__ ) - #if ( __BORLANDC__ >= 0x570 ) // at least Borland Developer Studio 2006 - #define _C99_BIT_TYPES_DEFINED 1 - #endif - #endif + #define MBG_TGT_HAS_64BIT_TYPES 1 - #if defined( __GNUC__ ) - #include <stdint.h> - #define _C99_BIT_TYPES_DEFINED 1 + #if !defined( MBG_TGT_HAS_ABS64 ) + #define _abs64( _i ) ( (int64_t) ( ( (_i) < 0 ) ? -(_i) : (_i) ) ) #endif #endif -// If neither the target system nor the build environment define C99 fixed-size -// types define those types based on standard types with the proper sizes -// commonly used in 16/32 bit environments. -#if defined( _C99_BIT_TYPES_DEFINED ) +// Some commonly used types - #define MBG_TGT_HAS_64BIT_TYPES 1 +#if !defined( _UCHAR_DEFINED ) + typedef unsigned char uchar; + #define uchar uchar +#endif -#else +#if !defined( _USHORT_DEFINED ) + typedef unsigned short ushort; + #define ushort ushort +#endif - typedef char int8_t; - typedef unsigned char uint8_t; +#if !defined( _UINT_DEFINED ) + typedef unsigned int uint; + #define uint uint +#endif - typedef short int16_t; - typedef unsigned short uint16_t; +#if !defined( _ULONG_DEFINED ) + typedef unsigned long ulong; + #define ulong ulong +#endif - typedef long int32_t; - typedef unsigned long uint32_t; +#if !defined( _UDOUBLE_DEFINED ) + typedef double udouble; + #define udouble udouble +#endif +#if !defined( _BYTE_DEFINED ) + typedef unsigned char byte; + #define byte byte +#endif - #if defined( MBG_TGT_WIN32 ) +#if !defined( _WORD_DEFINED ) + typedef unsigned short word; + #define word word +#endif - typedef __int64 int64_t; - typedef unsigned __int64 uint64_t; +#if !defined( _LONGWORD_DEFINED ) + typedef unsigned long longword; + #define longword longword +#endif - #define MBG_TGT_HAS_64BIT_TYPES 1 +#if !defined( _DWORD_DEFINED ) +// typedef unsigned long dword; +// #define dword dword +#endif - #else - // The types below are required to avoid build errors - // if these types are formally used in function prototypes. - // We explicitely use abnormal data types to hopefully - // cause compiler errors in case these types are - // unexpectedly used to generate real code for a target - // platform which does not support 64 bit types. - typedef void *int64_t; - typedef void *uint64_t; - #endif +#if defined( MBG_TGT_MISSING_BOOL_TYPE ) + //#error MBG_TGT_MISSING_BOOL_TYPE is defined + // BDS/Borland C++ Builder 2006 (non-C++ mode) + // Borland C++ Builder 5 (non-C++ mode) + // BC 3.1 + // VC6 + // DDKbuild + // VS2008 #endif +#if defined( __cplusplus ) + //#error __cplusplus is defined +#endif +#if defined( __bool_true_false_are_defined ) + //#error __bool_true_false_are_defined is defined + // Keil armcc + // gcc / Linux user space + // clang / FreeBSD user space and kernel +#endif -#if !defined( MBG_TGT_HAS_64BIT_TYPES ) - #define MBG_TGT_HAS_64BIT_TYPES 0 +#if defined( MBG_TGT_MISSING_BOOL_TYPE ) /* from mbg_tgt.h */ \ + || ( !defined( __cplusplus ) /* C++ */ \ + && !defined( __bool_true_false_are_defined ) /* C99 */ \ + && !defined( _LINUX_TYPES_H ) ) /* Linux kernel */ \ + && !( defined( MBG_TGT_NETBSD ) && defined( _SYS_TYPES_H_ ) ) /* NetBSD kernel */ -#endif + // There's no native support for a "bool" type, so we + // need a substitute. + #if defined( _BIT_DEFINED ) + // A native "bit" type is supported, so we use it for bool. + //#error substituting bit for bool + // C166 + typedef bit bool; + #else + // Fall back to "int". This is just a hack which + // may yield unexpected results with code like: + // return (bool) ( val & 0x10 ); + // A safe way of coding would be: + // return (bool) ( ( val & 0x10 ) != 0 ); + //#error substituting int for bool + // Borland C++ Builder 5 + // BC 3.1 + // VC6 + // DDKbuild + // VS2008 + typedef int bool; + #endif + // Try to provoke a build error if the build + // environment unexpectedly supports "bool" natively. + #define bool bool + #define true 1 + #define false 0 +#else + //#error native bool type supported + // Borland C++ Builder 5 and newer (C++ mode only) + // Keil armcc + // gcc / Linux user space + // gcc / Linux kernel + // clang / FreeBSD user space and kernel +#endif -// Some commonly used types -typedef unsigned char uchar; +#if !defined( _BIT_DEFINED ) + + // There's no native support for a "bit" type, so we + // need a substitute. The "bool" type would fit best + // and should be fine if it's supported natively. + // + // However, if "bool" has been substituted above + // by "int", this is just a hack which may yield + // unexpected results with code like: + // return (bit) ( val & 0x10 ); + // A safe way of coding would be: + // return (bit) ( ( val & 0x10 ) != 0 ); + + //#error substituting bool for bit + // Keil armcc + // Borland C++ Builder 5 + // BC 3.1 + // VC6 + // DDKbuild + // VS2008 + // gcc / Linux user space + // gcc / Linux kernel + // clang / FreeBSD user space and kernel + typedef bool bit; + + // Try to provoke a build error if the build + // environment unexpectedly supports "bit" natively. + #define bit bit + + #define _BIT_REDEFINED 1 -#if !defined( MBG_TGT_LINUX ) || !defined( __USE_MISC ) - // The glibc headers define the types below if __USE_MISC is - // defined, otherwise we need to define them here. - typedef unsigned short ushort; - typedef unsigned int uint; - typedef unsigned long ulong; +#else + //#error native bit type supported + // C166 #endif -typedef double udouble; -typedef unsigned char byte; -typedef unsigned short word; -typedef unsigned long longword; -typedef unsigned long dword; +#define BYTE_0( _x ) ( (uint8_t ) ( (_x) & 0xFF ) ) +#define BYTE_1( _x ) ( (uint8_t ) ( ( ( (uint16_t) (_x) ) >> 8 ) & 0xFF ) ) +#define BYTE_2( _x ) ( (uint8_t ) ( ( ( (uint32_t) (_x) ) >> 16 ) & 0xFF ) ) +#define BYTE_3( _x ) ( (uint8_t ) ( ( ( (uint32_t) (_x) ) >> 24 ) & 0xFF ) ) + + +#define HI_BYTE( _x ) ( (uint8_t ) ( (_x) >> 8 ) ) +#define LO_BYTE( _x ) ( (uint8_t ) ( (_x) & 0xFF ) ) -#define HI_BYTE( _x ) ( (_x) >> 8 ) -#define LO_BYTE( _x ) ( (_x) & 0xFF ) +#define HI_WORD( _x ) ( (uint16_t ) ( (_x) >> 16 ) ) +#define LO_WORD( _x ) ( (uint16_t ) ( (_x) & 0xFFFF ) ) -#define HI_WORD( _x ) ( (_x) >> 16 ) -#define LO_WORD( _x ) ( (_x) & 0xFFFF ) -// the macros below assume little endianess -// these macros expect the name of a variable +// The macros below assume little endianess. + +// These macros expect the name of a variable. #define BYTE_OF( _v, _n ) *( ( (uint8_t *) &(_v) ) + (_n) ) #define WORD_OF( _v, _n ) *( ( (uint16_t *) &(_v) ) + (_n) ) -// same as above, but taking pointers +#define FBYTE_OF( _v, _n ) *( ( (uint8_t far *) &(_v) ) + (_n) ) +#define FWORD_OF( _v, _n ) *( ( (uint16_t far *) &(_v) ) + (_n) ) + +// Same as above, but taking pointers. #define BYTE_OF_P( _p, _n ) *( ( (uint8_t *) (_p) ) + (_n) ) #define WORD_OF_P( _p, _n ) *( ( (uint16_t *) (_p) ) + (_n) ) -// a macro to swap the byte order of a 16 bit value +// A macro to swap the byte order of a 16 bit value. #define _bswap16( _x ) \ ( \ ( ( ( (uint16_t) (_x) ) & 0x00FF ) << 8 ) | \ ( ( ( (uint16_t) (_x) ) & 0xFF00 ) >> 8 ) \ ) -// a macro to swap the byte order of a 32 bit value +// A macro to swap the byte order of a 32 bit value. #define _bswap32( _x ) \ ( \ ( ( ( (uint32_t) (_x) ) & 0x000000FFUL ) << 24 ) | \ @@ -262,7 +481,7 @@ typedef unsigned long dword; ( ( ( (uint32_t) (_x) ) & 0xFF000000UL ) >> 24 ) \ ) -// a macro to swap the word order of a 32 bit value +// A macro to swap the word order of a 32 bit value. #define _wswap32( _x ) \ ( \ ( ( ( (uint32_t) (_x) ) & 0x0000FFFFUL ) << 16 ) | \ @@ -273,7 +492,7 @@ typedef unsigned long dword; #define _var_bswap32( _v ) (_v) = _bswap32( _v ) -// The C51 compiler is big-endian, that means the most +// The C51 compiler is big-endian, this means the most // significant byte of a 16 or 32 bit value is stored in // the lowest memory location. Most other systems are // little-endian, so we must use macros to adjust the @@ -287,6 +506,232 @@ typedef unsigned long dword; #define _hilo_32( _x ) (_x) #endif + + +#define _set_array_bit( _n, _byte_array, _max_bytes ) \ +do \ +{ \ + int byte_idx = (_n) >> 3; \ + \ + if ( byte_idx < _max_bytes ) \ + _byte_array[byte_idx] |= ( 1 << ( (_n) & 0x07 ) ); \ + \ +} while ( 0 ) + + +#define _clear_array_bit( _n, _byte_array, _max_bytes ) \ +do \ +{ \ + int byte_idx = (_n) >> 3; \ + \ + if ( byte_idx < _max_bytes ) \ + _byte_array[byte_idx] &= ~( 1 << ( (_n) & 0x07 ) ); \ + \ +} while ( 0 ) + + + +#define _isdigit( _c ) ( (_c) >= '0' && (_c) <= '9' ) + + +// A macro function which can safely be used without +// side effects as a macro doing nothing. +// This is useful to define debug macros away in +// release builds, etc. +#if !defined( _nop_macro_fnc ) + #define _nop_macro_fnc() do {} while (0) +#endif + + +/** + * @brief A table entry which can be used to map codes to names. + */ +typedef struct +{ + long code; + const char *name; + +} MBG_CODE_NAME_TABLE_ENTRY; + +/** + * @brief A macro defining a ::MBG_CODE_NAME_TABLE_ENTRY. + * + * The stringified parameter is used for the name. + * + * @param[in] _n The symbolic name of the numeric code. + */ +#define _mbg_cn_table_entry( _n ) { _n, #_n } + +/** + * @brief A macro defining an empty ::MBG_CODE_NAME_TABLE_ENTRY. + * + * This is used to terminate a table. + */ +#define _mbg_cn_table_end() { 0, NULL } + + + +/** + * @brief A timestamp with nanosecond resolution. + * + * @note If the structure is to represent a negative value, both the + * fields @p nano_secs and @p secs have to be set to the negative values. + * Otherwise the sign of the represented number was ambiguous if either + * of the fields was 0 by accident, and only the other field was not 0. + * The macro ::_nano_time_negative should always be used to determine + * if the sign of the represented value is negative, or not. + * + * @note The @p secs field will roll over on 2038-01-19 03:14:07 + * if used for the number of seconds since 1970-01-01, just like + * 32 bit POSIX @a time_t. + * + * @see ::_nano_time_negative + * @see ::_nano_time_zero + * @see ::NANO_TIME_64 + */ +typedef struct nano_time_s +{ + // ATTENTION: + // This structure is and has been used in public API calls for a long time, + // so even though the order of member fields is different than in NANO_TIME_64 + // this must *NOT* be changed, or API compatibility will get lost! + int32_t nano_secs; ///< [nanoseconds]. + int32_t secs; ///< [seconds], usually since 1970-01-01 00:00:00. + +} NANO_TIME; + +#define _mbg_swab_nano_time( _p ) \ +do \ +{ \ + _mbg_swab32( &(_p)->nano_secs ); \ + _mbg_swab32( &(_p)->secs ); \ +} while ( 0 ) + +/** + * @brief Check if the value of the ::NANO_TIME structure _nt is negative. + */ +#define _nano_time_negative( _nt ) \ + ( ( (_nt)->secs < 0 ) || ( (_nt)->nano_secs < 0 ) ) + +/** + * @brief Check if the value of the ::NANO_TIME structure _nt is 0. + */ +#define _nano_time_zero( _nt ) \ + ( ( (_nt)->secs == 0 ) && ( (_nt)->nano_secs == 0 ) ) + + + +/** + * @brief A timestamp with nanosecond resolution, but 64 bit size. + * + * @note If the structure is to represent a negative value, both the + * fields @p nano_secs and @p secs have to be set to the negative values. + * Otherwise the sign of the represented number was ambiguous if either + * of the fields was 0 by accident, and only the other field was not 0. + * The macro ::_nano_time_64_negative should always be used to determine + * if the sign of the represented value is negative, or not. + * + * @see ::_nano_time_64_negative + * @see ::_nano_time_64_zero + * @see ::NANO_TIME + */ +typedef struct nano_time_64_s +{ + // ATTENTION: + // This structure is and has been used in public API calls for a long time, + // so even though the order of member fields is different than in ::NANO_TIME, + // this must *NOT* be changed, or API compatibility will get lost! + int64_t secs; ///< [seconds], usually since 1970-01-01 00:00:00. + int64_t nano_secs; ///< [nanoseconds]. + +} NANO_TIME_64; + +#define _mbg_swab_nano_time_64( _p ) \ +do \ +{ \ + _mbg_swab64( &(_p)->secs ); \ + _mbg_swab64( &(_p)->nano_secs ); \ +} while ( 0 ) + +/** + * @brief Check if the value of the ::NANO_TIME_64 structure _nt is negative. + */ +#define _nano_time_64_negative( _nt ) \ + ( ( (_nt)->secs < 0 ) || ( (_nt)->nano_secs < 0 ) ) + +/** + * @brief Check if the value of the ::NANO_TIME_64 structure _nt is 0. + */ +#define _nano_time_64_zero( _nt ) \ + ( ( (_nt)->secs == 0 ) && ( (_nt)->nano_secs == 0 ) ) + + +/** + * @brief Set up a ::NANO_TIME_64 structure from struct timespec. + */ +#define _nano_time_64_from_timespec( _p, _p_ts ) \ +do \ +{ \ + (_p)->secs = (_p_ts)->tv_sec; \ + (_p)->nano_secs = (_p_ts)->tv_nsec; \ +} while ( 0 ) + +/** + * @brief Set up a ::NANO_TIME_64 structure from struct timeval. + */ +#define _nano_time_64_from_timeval( _p, _p_ts ) \ +do \ +{ \ + (_p)->secs = (_p_ts)->tv_sec; \ + (_p)->nano_secs = (_p_ts)->tv_usec * 1000; \ +} while ( 0 ) + + +// The size_t type may be larger than an int type. +// However, some snprintf-like functions expect a size_t value +// to specify the buffer size, but just return an int value. +// So we take care that at least the return value is limited +// to MAXINT. +#if defined( MBG_TGT_WIN32 ) + #define _int_from_size_t( _n ) \ + ( ( (_n) > INT_MAX ) ? INT_MAX : (int) (_n) ) +#else + #define _int_from_size_t( _n ) (_n) +#endif + + + +/** + * @brief A helper macro to implement ::STRINGIFY correctly. + * + * This just a helper macro which must be defined before + * ::STRINGIFY to work correctly, and should not be used alone. + * + * @see ::STRINGIFY + */ +#define XSTRINGIFY(x) #x + + +/** + * @brief Make a string from a constant definition. + * + * This macro can be used e.g. to define a constant string on the + * compiler's command line, e.g. like -DVERSION_STRING="v1.0 BETA". + * Source code like + * @code{.c} + const char version_string[] = VERSION_STRING; + * @endcode + * + * may not work for every compiler since the double quotes + * in VERSION_STRING may be removed when the definition is evaluated. + * A proper solution is to use the STRINGIFY() macro defined here: + * @code{.c} + const char version_string[] = STRINGIFY( VERSION_STRING ); + * @endcode + */ +#define STRINGIFY(x) XSTRINGIFY(x) + + /* End of header body */ #undef _ext |