diff options
Diffstat (limited to 'mbglib/common/mbgextio.c')
-rw-r--r-- | mbglib/common/mbgextio.c | 5219 |
1 files changed, 4651 insertions, 568 deletions
diff --git a/mbglib/common/mbgextio.c b/mbglib/common/mbgextio.c index 53b2486..5bb57ff 100644 --- a/mbglib/common/mbgextio.c +++ b/mbglib/common/mbgextio.c @@ -1,16 +1,110 @@ /************************************************************************** * - * $Id: mbgextio.c 1.11 2011/04/15 13:17:14 martin REL_M $ + * $Id: mbgextio.c 1.20.1.19 2015/07/22 16:02:07 martin TEST $ * * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany * * Description: * Meinberg extended I/O functions for the binary data protocol - * via serial communication and network socket I/O. + * via serial communication, network socket I/O, or direct USB I/O. + * + * These functions can *not* be used to access LANTIME NTP servers, + * or the modules assembled within a LANTIME since LANTIMEs are using + * this kind of communication only internally. + * + * Also, standalone USB devices are usually handled by the driver + * software package for the given operating system and thus can be + * accessed in the same way as PCI cards, using the API functions + * provided by the mbgdevio library. + * + * These functions can be used, however, with standalone devices + * which are accessible either directly via a serial port, or via a + * special serial-to-LAN converter like the meinberg LAN_XPT module. * * ----------------------------------------------------------------------- * $Log: mbgextio.c $ + * Revision 1.20.1.19 2015/07/22 16:02:07 martin + * Started to support variable USB endpoint numbers. + * Revision 1.20.1.18 2015/07/14 15:08:43 martin + * Unified parameter naming and updated doxygen comments. + * Revision 1.20.1.17 2015/07/14 14:05:55 martin + * Support XBP addressing. + * Added functions to read/send UTC parameters. + * Support for USB_DIRECT_IO + * Fixed dealloc_msg_ctl() where not all memory was freed. + * Reworked mbgextio_force_conn_serial_ftdi(). + * Revision 1.20.1.16 2014/10/30 16:03:05 martin + * Doxygen fixes. + * Revision 1.20.1.15 2014/10/30 14:45:55 martin + * Generally return Meinberg error codes only. + * Do not use GPS_REQACK for secu_settings. + * Added FTDI support functions. + * Reworked "force connection" functions, also supporting high speed now. + * Updated libusb support. + * Many new API functions. + * Revision 1.20.1.14 2013/11/28 15:51:45Z marvin + * Added mbgextio_get_ntp_peer_state_idx. + * Revision 1.20.1.13 2013/11/26 16:06:43Z marvin + * Added mbgextio_get_ntp_sys_state. + * Revision 1.20.1.12 2013/11/21 07:46:44Z marvin + * Added mbgextio_xmt_secu_settings. + * Cleanup for socket port. + * Revision 1.20.1.11 2013/11/15 12:17:30Z marvin + * Added error return for TGT_LINUX if socket_opt_error detected. + * Revision 1.20.1.10 2013/11/15 11:49:12Z marvin + * Fixed: return 0 if non_blocking_socket is OK. + * check if received GPS_NACK for encryption mode (socket connection). + * Revision 1.20.1.9 2013/11/13 16:30:10Z martin + * Revision 1.20.1.8 2013/11/13 16:13:26 martin + * Revision 1.20.1.7 2013/11/13 16:05:52 marvin + * Revision 1.20.1.6 2013/11/13 15:14:17Z martin + * Cleaned up socket connection code. + * Revision 1.20.1.5 2013/11/12 12:12:02 marvin + * Changed calls for NTP info and settings. + * Revision 1.20.1.4 2013/11/11 11:30:27Z marvin + * Changed socket connection. + * Revision 1.20.1.3 2013/11/05 15:53:11Z marvin + * Changed connecting via socket. + * Revision 1.20.1.2 2013/10/14 08:54:19Z marvin + * Added mbgextio_set_ntp_clnt_mode_cfg. + * Revision 1.20.1.1 2013/09/25 11:09:53Z marvin + * Added NTP support and new support for PTP. + * Revision 1.20 2013/09/10 08:56:35Z marvin + * Changed Doxygen comments. + * Revision 1.19 2013/09/05 15:10:39Z marvin + * Added support for LAN interface setup + * Added support for PTP state + * Added support for XMR. + * Revision 1.18 2013/09/02 15:16:48Z marvin + * Added support for XMR (get multi ref info, set multi ref settings) + * Revision 1.17 2013/08/28 11:01:41Z marvin + * Added read and set tr_distance and gnss_mode. + * Revision 1.16.1.1 2013/06/05 09:17:54Z marvin + * Changed close_connection for socket. + * Revision 1.16 2013/04/11 14:18:33Z Gregoire + * new calls for sending havequick rx and tx settings to clock + * Revision 1.15 2013/02/14 14:42:13Z martin + * Fixed syntax err due to unintentionally pasted word. + * Revision 1.14 2013/02/06 15:43:54 martin + * Updated doxygen comments. + * Revision 1.13 2013/02/01 15:58:43 martin + * In mbgextio_force_connection() for Windows wait until all chars + * must have been sent since flushing output buffer doesn't work reliably. + * Added a number of new functions. + * Added doxygen comments. + * Revision 1.12 2012/10/30 16:17:30 martin + * Started to migrate to opaque stuctures. + * Conditionally let xmt routines request for ACK packet, + * though this is by default disabled. + * Support receiving NACK status. + * Support big endian target platforms. + * Merged and adapted Daniel's USB support functions. + * Support event log entries. + * Syntax workaround which is required until this module becomes a DLL. + * Added some new functions. + * Updated doxygen comments. + * Huge cleanup. * Revision 1.11 2011/04/15 13:17:14 martin * Use common mutex support macros from mbgmutex.h. * Revision 1.10 2011/04/08 11:28:24 martin @@ -36,7 +130,7 @@ * Moved generic serial I/O stuff to mbgserio.c and mbgserio.h. * Restart reception if received msg does not match expected cmd code. * Fixed timeout value for Windows. - * New symbol _MBGEXTIO_DIRECT_RC controls whether the return code of the + * New symbol _MBGEXTIO_DIRECT_RC controls whether the return code of the * mbgextio_set_...() functions is evaluated or returned as-is. * New functions mbgextio_set_time(), mbgextio_set_tzdl(). * Added mbgextio_get_ucap(). This may not work with older firmware, @@ -60,30 +154,47 @@ #define _MBGEXTIO #include <mbgextio.h> - - //##++ The following lines are required - // until mbgextio becomes a DLL: - #undef _MBG_API_ATTR - #define _MBG_API_ATTR #undef _MBGEXTIO #include <mbgserio.h> +#include <mbg_arch.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <gpsutils.h> +#include <mbgerror.h> -#if defined( MBG_TGT_UNIX ) - #include <unistd.h> +#if defined( MBG_TGT_POSIX ) #include <fcntl.h> + #include <errno.h> #else typedef int ssize_t; #endif -#if !defined( MBGEXTIO_DIRECT_RC ) - #define _MBGEXTIO_DIRECT_RC 0 //##++ 1 +#if _USE_USB_IO + #include <mbgusbio.h> +#endif + +#if _USE_USB_DIRECT_IO + #include <poll.h> +#endif + +#if !defined( _MBGEXTIO_REQ_ACK ) + // If _MBGEXTIO_REQ_ACK is != 0 then mbgextio_...() functions writing + // configuration parameters send the cmd code with the ACK request bit set + // which lets mbgextio_xmt_msg() try to receive a response from the device + // after the parameters have been sent. + #define _MBGEXTIO_REQ_ACK 0 +#endif + +#if _MBGEXTIO_REQ_ACK + #define OPT_GPS_ACK_CODE GPS_REQACK +#else + #define OPT_GPS_ACK_CODE 0 // dummy #endif +#define DEBUG_SOCK_IO ( 1 && defined( DEBUG ) ) + // default serial message timeout #if !defined ( MBGEXTIO_MSG_RCV_TIMEOUT_SERIAL ) @@ -96,6 +207,134 @@ #endif + +static const char force_conn_cmd_str[] = MBG_FORCE_CONN_CMD_STR; +static const char force_conn_hs_cmd_str[] = MBG_FORCE_CONN_HS_CMD_STR; + + +typedef struct +{ + int model_code; + long feature_mask; + +} BUILTIN_FEATURE_TABLE_ENTRY; + +static BUILTIN_FEATURE_TABLE_ENTRY builtin_feature_table[] = GPS_MODEL_BUILTIN_FEATURES; + + + +static /*HDR*/ +/** + * @brief Deallocate a message control structure + * + * Free the memory allocated for a message control structure + * and set the pointer to NULL. + * + * @param[in,out] ppmctl Address of a pointer to a message control structure + * + * @see ::alloc_msg_ctl + */ +void dealloc_msg_ctl( MBG_MSG_CTL **ppmctl ) +{ + MBG_MSG_CTL *pmctl = *ppmctl; + + if ( pmctl ) + { + if ( pmctl->rcv.pmb ) + { + free( pmctl->rcv.pmb ); + pmctl->rcv.pmb = NULL; + } + + pmctl->rcv.buf_size = 0; + + if ( pmctl->xmt.pmb ) + { + free( pmctl->xmt.pmb ); + pmctl->xmt.pmb = NULL; + } + + pmctl->xmt.buf_size = 0; + + free( pmctl ); + *ppmctl = NULL; + } + +} // dealloc_msg_ctl + + + +static /*HDR*/ +/** + * @brief Allocate a message control structure + * + * Allocate memory for a message control structure and associated + * message receive and transmit buffers. + * + * @param[out] ppmctl Address of a pointer to a message control structure to be allocated, set to NULL on error + * + * @return ::MBG_SUCCESS or one of the @ref MBG_ERROR_CODES + * + * @see ::dealloc_msg_ctl + */ +int alloc_msg_ctl( MBG_MSG_CTL **ppmctl ) +{ + int rc = MBG_ERR_UNSPEC; + + MBG_MSG_CTL *pmctl = malloc( sizeof( *pmctl ) ); + + if ( pmctl == NULL ) + { + #if defined( DEBUG ) + fprintf( stderr, "failed to malloc control block in %s\n", __func__ ); + #endif + goto fail; + } + + memset( pmctl, 0, sizeof( *pmctl ) ); + + // allocate receive buffer + pmctl->rcv.buf_size = sizeof( *(pmctl->rcv.pmb) ); + pmctl->rcv.pmb = malloc( pmctl->rcv.buf_size ); + + if ( pmctl->rcv.pmb == NULL ) + { + #if defined( DEBUG ) + fprintf( stderr, "failed to malloc rcv buffer in %s\n", __func__ ); + #endif + goto fail; + } + + // allocate transmit buffer + pmctl->xmt.buf_size = sizeof( *(pmctl->xmt.pmb) ); + pmctl->xmt.pmb = malloc( pmctl->xmt.buf_size ); + + if ( pmctl->xmt.pmb == NULL ) + { + #if defined( DEBUG ) + fprintf( stderr, "failed to malloc xmt buffer in %s\n", __func__ ); + #endif + goto fail; + } + + + // buffers allocated successfully + rc = MBG_SUCCESS; + goto out; + + +fail: // if not all memory blocks could be allocated, clean up + dealloc_msg_ctl( &pmctl ); // also sets pmctl to NULL + rc = MBG_ERR_NO_MEM; + +out: + *ppmctl = pmctl; + return rc; + +} // alloc_msg_ctl + + + #if defined( MBG_TGT_WIN32 ) && _USE_SOCKET_IO static /*HDR*/ @@ -134,25 +373,299 @@ void mbgextio_set_console_control_handler( void ) - #if _USE_SOCKET_IO static /*HDR*/ +/** + * @brief Set the blocking mode of a network socket + * + * @param[in] sock_fd The socket descriptor + * @param[in] blocking Blocking mode, 0: non-blocking, else blocking + * + * @return One of the @ref MBG_RETURN_CODES + */ +int set_socket_blocking_mode( MBG_SOCK_FD sock_fd, int blocking ) +{ + int rc = MBG_ERR_UNSPEC; + + if ( sock_fd == MBG_INVALID_SOCK_FD ) + { + rc = MBG_ERR_INV_SOCK_FD; + goto out; + } + + #if defined( MBG_TGT_WIN32 ) + { + u_long mode = blocking ? 0 : 1; // ioctlsocket expects an (u_long *) + + rc = ioctlsocket( sock_fd, FIONBIO, &mode ); // returns 0 on success + + if ( rc != 0 ) // error + { + rc = mbg_get_last_socket_error( "ioctlsocket(FIONBIO) failed in set_socket_blocking_mode" ); + goto out; + } + } + #elif defined( MBG_TGT_POSIX ) + { + int val = fcntl( sock_fd, F_GETFL, 0 ); // returns -1 on error + + if ( val == -1 ) + { + rc = mbg_get_last_socket_error( "fcntl(F_GETFL) failed in set_socket_blocking_mode" ); + goto out; + } + + if ( blocking ) + val &= ~O_NONBLOCK; + else + val |= O_NONBLOCK; + + rc = fcntl( sock_fd, F_SETFL, val ); // returns -1 on error + + if ( rc == -1 ) + { + rc = mbg_get_last_socket_error( "fcntl(F_SETFL) failed in set_socket_blocking_mode" ); + goto out; + } + } + #else + { + rc = MBG_ERR_NOT_SUPP_ON_OS; + goto out; + } + #endif + + rc = MBG_SUCCESS; + +out: + return rc; + +} // set_socket_blocking_mode + + + +static /*HDR*/ +/** + * @brief Get last socket error code converted to @ref MBG_RETURN_CODES + * + * @param[in] sock_fd The socket descriptor + * + * @return One of the @ref MBG_RETURN_CODES + */ +int get_sock_error( MBG_SOCK_FD sock_fd ) +{ + int so_err = 0; + socklen_t so_err_sz = sizeof( so_err ); + int rc = getsockopt( sock_fd, SOL_SOCKET, SO_ERROR, (void *) &so_err, &so_err_sz ); // returns 0 on success + + if ( rc != 0 ) // error + { + rc = mbg_get_last_socket_error( "getsockopt SO_ERROR failed in get_sock_error" ); + goto out; + } + + if ( so_err_sz != sizeof( so_err ) ) // might have been modified by getsockopt() + { + fprintf( stderr, "Warning: getsockopt option size changed in get_sock_error: %li -> %li", + (long) sizeof( so_err ), (long) so_err_sz ); + rc = MBG_ERR_UNSPEC; + goto out; + } + + if ( so_err != 0 ) // error + { + #if defined( MBG_TGT_WIN32 ) + rc = mbg_win32_wsa_err_to_mbg( so_err, "wait failed in wait_nonblocking_socket_ready" ); + #elif defined( MBG_TGT_POSIX ) + rc = mbg_posix_errno_to_mbg( so_err, "wait failed in wait_nonblocking_socket_ready" ); + #else + #error This function is not supported for this target. + #endif + goto out; + } + + rc = MBG_SUCCESS; + +out: + return rc; + +} // get_sock_error + + + +static /*HDR*/ +/** + * @brief Wait for a non-blocking socket to get ready after a connect() call + * + * A connect() call to a socket which is not available can fail + * after a pretty long timeout which usually can't be changed. + * + * A workaround is to switch the socket to non-blocking mode, + * then call connect(), then do a select() with specified timeout, + * and finally switch the socket back to blocking mode. + * + * @param[in] sock_fd The socket descriptor + * + * @return One of the @ref MBG_RETURN_CODES + */ +int wait_nonblocking_socket_ready( MBG_SOCK_FD sock_fd, long timeout_msec ) +{ + // At least under Windows a select call can set except_fds if + // the non-blocking attempt to connect fails. + // See: http://msdn.microsoft.com/en-us/library/windows/desktop/ms740141(v=vs.85).aspx + // Linux and FreeBSD suggest to use write_fds only. + #define USE_EXCEPT_FDS_WITH_CONNECT ( 1 || defined( MBG_TGT_WIN32 ) ) //##+++++++++++++++++++++++++++ + + fd_set write_fds; + #if USE_EXCEPT_FDS_WITH_CONNECT + fd_set except_fds; + #endif + int max_fd; + struct timeval tv_timeout; + int rc = MBG_ERR_UNSPEC; + + mbg_msec_to_timeval( timeout_msec, &tv_timeout ); + + FD_ZERO( &write_fds ); + FD_SET( sock_fd, &write_fds ); + + #if defined( MBG_TGT_WIN32 ) + // Under Windows an fd is a handle which can't simply + // be converted to an int, but the first argument of + // select() is ignored under Windows anyway, so we just + // set max_fd to 0. + max_fd = 0; + #else + max_fd = sock_fd; + #endif + + #if USE_EXCEPT_FDS_WITH_CONNECT + FD_ZERO( &except_fds ); + FD_SET( sock_fd, &except_fds ); + rc = select( max_fd + 1, NULL, &write_fds, &except_fds, &tv_timeout ); + #else + rc = select( max_fd + 1, NULL, &write_fds, NULL, &tv_timeout ); + #endif + + if ( rc == MBG_SOCKET_ERR_RETVAL ) // < 0, error + { + rc = mbg_get_last_socket_error( "select failed in wait_nonblocking_socket_ready" ); + goto out; + } + + if ( rc == 0 ) // timeout + { + #if DEBUG_SOCK_IO + fprintf( stderr, "select timed out in wait_nonblocking_socket_ready" ); + #endif + rc = MBG_ERR_NBLOCK_WAIT_SLCT; + goto out; + } + + #if USE_EXCEPT_FDS_WITH_CONNECT + // The except_fds might be set if no connection could be + // established if e.g. the target socket is already busy. + if ( FD_ISSET( sock_fd, &except_fds ) ) + { + rc = get_sock_error( sock_fd ); + goto out; + } + #endif + + // Usually the write_fds are checked to see if the + // non-blocking connect() call completed successfully, + // in which case the associated descriptor is set. + if ( !FD_ISSET( sock_fd, &write_fds ) ) + { + #if DEBUG_SOCK_IO + fprintf( stderr, "write_fd is not set after select in wait_nonblocking_socket_ready" ); + #endif + rc = MBG_ERR_NBLOCK_WAIT_WR_FD; + goto out; + } + + #if defined( MBG_TGT_LINUX ) + // From the connect(2) man page: + // After select(2) indicates writability, use getsockopt(2) to read + // the SO_ERROR option at level SOL_SOCKET to determine whether connect() + // completed successfully (SO_ERROR is zero) or unsuccessfully (SO_ERROR + // is one of the usual error codes, explaining the reason for the failure). + rc = get_sock_error( sock_fd ); + goto out; + #endif + + rc = MBG_SUCCESS; + +out: + return rc; + +} // wait_nonblocking_socket_ready + + + +static /*HDR*/ +/** + * @brief Close a socket and make the socket descriptor invalid + * + * @param[in,out] pmctl Pointer to a valid message control structure containing the socket descriptor + * + * @return One of the @ref MBG_RETURN_CODES + */ +int socket_close( MBG_MSG_CTL *pmctl ) +{ + int rc; + + #if defined( MBG_TGT_CVI ) || defined( MBG_TGT_WIN32 ) + rc = closesocket( pmctl->st.sockio.sockfd ); // returns 0 on success + #elif defined( MBG_TGT_POSIX ) + rc = close( pmctl->st.sockio.sockfd ); // returns 0 on success + #else + #error close socket needs to be implemented for this target + #endif + + pmctl->st.sockio.sockfd = MBG_INVALID_SOCK_FD; + + if ( rc != 0 ) + { + rc = mbg_get_last_socket_error( "failed to close socket socket_close" ); + goto out; + } + + rc = MBG_SUCCESS; + +out: + return rc; + +} // socket_close + + + +static /*HDR*/ +/** + * @brief Initialize a socket connection to a specified host + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] host Host name or IP address of the target system + * + * @return One of the @ref MBG_RETURN_CODES + */ int socket_init( MBG_MSG_CTL *pmctl, const char *host ) { struct hostent *hp; struct sockaddr_in *paddr; const struct sockaddr *p; int sz; - int rc; + int rc = MBG_ERR_UNSPEC; + //##++++++++ should use getaddrinfo() preferably hp = gethostbyname( host ); #if defined( MBG_TGT_WIN32 ) + // Under Windows the winsock2.dll may not yet have been initialized, + // so initialize now and try once more. if ( hp == NULL ) { - // The winsock2.dll may not yet have been initialized, - // so try to initialize now. WORD wVersionRequested; WSADATA wsaData; @@ -160,22 +673,37 @@ int socket_init( MBG_MSG_CTL *pmctl, const char *host ) rc = WSAStartup( wVersionRequested, &wsaData ); - // If initialization has succeeded, try again. + // If initialization has succeeded, try once more. if ( rc == 0 ) hp = gethostbyname( host ); } #endif // defined( MBG_TGT_WIN32 ) - if ( hp == NULL ) - return TR_OPEN_ERR; - + if ( hp == NULL ) // error + { + rc = mbg_get_gethostbyname_error( "gethostbyname failed in socket_init" ); + goto out; + } - // create socket on which to send. + // Create socket on which to send. pmctl->st.sockio.sockfd = socket( PF_INET, SOCK_STREAM, 0 ); - if ( pmctl->st.sockio.sockfd == INVALID_SOCKET ) - return TR_OPEN_ERR; + if ( pmctl->st.sockio.sockfd == MBG_INVALID_SOCK_FD ) + { + rc = mbg_get_last_socket_error( "failed to create socket in socket_init" ); + goto out; + } + + // Set the socket to nonblocking mode to be able to + // reduce the timeout when connecting to a socket + // which is offline. + rc = set_socket_blocking_mode( pmctl->st.sockio.sockfd, 0 ); + + if ( rc != MBG_SUCCESS ) + goto out_close; + + // Now try to connect to the socket. paddr = &pmctl->st.sockio.addr; memset( paddr, 0, sizeof( *paddr ) ); @@ -186,65 +714,174 @@ int socket_init( MBG_MSG_CTL *pmctl, const char *host ) p = (const struct sockaddr *) paddr; sz = sizeof( *paddr ); - rc = connect( pmctl->st.sockio.sockfd, p, sz ); + rc = connect( pmctl->st.sockio.sockfd, p, sz ); // returns 0 on success on Windows and Linux - if ( rc < 0 ) - return TR_OPEN_ERR; + // In nonblocking mode connect() might complete successfully + // if the destination socket is on the same machine (localhost) + // However, for remote connections the call usually returns + // immediately with WSAEWOULDBLOCK on Windows, or EINPROGRESS or + // EALREADY on POSIX systems. In the latter cases we need to wait + // and see if the connection can be established successfully. + + if ( rc != 0 ) // eventually not (yet) completed in non-blocking mode + { + #if defined( MBG_TGT_WIN32 ) + DWORD wsa_err = WSAGetLastError(); - return 0; + // WSAEWOULDBLOCK is actually not an error in non-blocking mode + if ( wsa_err != WSAEWOULDBLOCK ) + { + rc = mbg_win32_wsa_err_to_mbg( wsa_err, "connect failed in socket_init" ); + goto out_close; + } + #else // POSIX ... + int posix_errno = errno; + + // EINPROGRESS and EALREADY are actually not errors in non-blocking mode + if ( ( posix_errno != EINPROGRESS ) && ( posix_errno != EALREADY ) ) + { + rc = mbg_posix_errno_to_mbg( posix_errno, "connect failed in socket_init" ); + goto out_close; + } + #endif + + rc = wait_nonblocking_socket_ready( pmctl->st.sockio.sockfd, 1500 ); //##++++++++++++ + + if ( rc != MBG_SUCCESS ) + goto out_close; + } + + // Set the socket back to blocking mode. + rc = set_socket_blocking_mode( pmctl->st.sockio.sockfd, 1 ); + + if ( rc == MBG_SUCCESS ) + goto out; + + // error, fall through and close socket + +out_close: + socket_close( pmctl ); + +out: + return rc; } // socket_init -static /*HDR*/ -int comm_init( MBG_MSG_CTL *pmctl, const char *passwd ) +/*HDR*/ +/** + * @brief Send security settings for the socket connection + * + * If new_passwd is not a NULL pointer, the old_passwd + * will be overwritten by the new_passwd. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] old_passwd Pointer to the current LAN port password of the device + * @param[in] new_passwd Pointer to the new LAN port password for the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_socket + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_xmt_secu_settings( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, + const char *old_passwd, const char *new_passwd ) { - SECU_SETTINGS *pss = &pmctl->secu_settings; MBG_MSG_BUFF *pmb; - int rc; + SECU_SETTINGS *pss = &pmctl->secu_settings; + GPS_CMD cmd = GPS_SECU_SETTINGS; // GPS_REQACK is not supported by LAN_XPT, so omit it + int rc = MBG_ERR_UNSPEC; memset( pss, 0, sizeof *pss ); - strncpy( pss->password, passwd, sizeof( pss->password ) ); + strncpy( pss->password, old_passwd, sizeof( pss->password ) ); - pmctl->conn_type = MBG_CONN_TYPE_SOCKET; - pmctl->msg_rcv_timeout = MBGEXTIO_RCV_TIMEOUT_SOCKET; - // pmctl->char_rcv_timeout is not used with sockets + set_encryption_mode( pmctl, MBG_XFER_MODE_ENCRYPTED, pss->password ); - set_encryption_mode( pmctl, MBG_XFER_MODE_ENCRYTED, pss->password ); + if ( new_passwd ) + { + if ( strlen( new_passwd ) > 0 ) + strncpy( pss->new_password, new_passwd, sizeof( pss->new_password ) ); + + pss->flags |= MSK_FLAG_CHANGE_PASSWORD; + } pmb = pmctl->xmt.pmb; pmb->u.msg_data.secu_settings = *pss; - pmb->hdr.cmd = GPS_SECU_SETTINGS; + pmb->hdr.cmd = cmd; pmb->hdr.len = sizeof( pmb->u.msg_data.secu_settings ); - xmt_tbuff( pmctl ); - rc = mbgextio_rcv_msg( pmctl, GPS_SECU_SETTINGS ); + rc = xmt_tbuff( pmctl, NULL ); //##++++++++++++++ TODO Use mbgextio_req_data instead? Is NULL OK? - if ( rc != TR_COMPLETE ) - return -1; /* connection refused */ + if ( rc != MBG_SUCCESS ) // error + goto out; - pmb = pmctl->rcv.pmb; + rc = mbgextio_rcv_msg( pmctl, NULL, cmd ); //##++++++++++++++ TODO Is NULL OK? - if ( !( pmb->hdr.cmd & GPS_ACK ) ) - return -2; /* authentication failed */ + // With some devices a timeout may also occur + // if a wrong password has been used. + if ( rc != MBG_SUCCESS ) // error + goto out; - return 0; + cmd = pmctl->rcv.pmb->hdr.cmd; -} // comm_init + // If we either received a reply with GPS_NACK, or + // without GPS_ACK, the password was definitely wrong. + if ( ( cmd & GPS_NACK ) || !( cmd & GPS_ACK ) ) + { + rc = MBG_ERR_AUTH; + goto out; + } + + rc = MBG_SUCCESS; + +out: + return rc; + +} // mbgextio_xmt_secu_settings /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_open_socket( MBG_MSG_CTL *pmctl, - const char *host, const char *passwd ) +/** + * @brief Open a binary communication channel via a LAN/socket connection + * + * @param[in] host DNS name or IP address of the target device + * @param[out] ppmctl Address of a pointer to a ::MBG_MSG_CTL control structure + * allocated and set up by this call. + * Pointer is set to NULL on error. + * @param[in] passwd Password string for the encrypted communication + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_serial + * @see ::mbgextio_open_serial_ftdi + * @see ::mbgextio_open_usb + * @see ::mbgextio_open_usb_direct_io + * @see ::mbgextio_close_connection + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_open_socket( const char *host, MBG_MSG_CTL **ppmctl, const char *passwd ) { - int rc = socket_init( pmctl, host ); + MBG_MSG_CTL *pmctl; + int rc = alloc_msg_ctl( &pmctl ); - if ( rc < 0 ) - return rc; + if ( rc != MBG_SUCCESS ) + goto out; + + + pmctl->conn_type = MBG_CONN_TYPE_SOCKET; + pmctl->msg_rcv_timeout = MBGEXTIO_RCV_TIMEOUT_SOCKET; + // pmctl->char_rcv_timeout is not used with sockets - comm_init( pmctl, passwd ); + rc = socket_init( pmctl, host ); + + if ( rc != MBG_SUCCESS ) + goto fail_free; + + rc = mbgextio_xmt_secu_settings( pmctl, NULL, passwd, NULL ); + + if ( rc != MBG_SUCCESS ) + goto fail_close; #if defined( MBG_TGT_WIN32 ) mbgextio_set_console_control_handler(); @@ -254,6 +891,18 @@ _MBG_API_ATTR int _MBG_API mbgextio_open_socket( MBG_MSG_CTL *pmctl, _mbg_mutex_init( &pmctl->xmt.xmt_mutex ); #endif + rc = MBG_SUCCESS; + goto out; + +fail_close: + socket_close( pmctl ); + +fail_free: + dealloc_msg_ctl( &pmctl ); + +out: + *ppmctl = pmctl; + return rc; } // mbgextio_open_socket @@ -265,10 +914,39 @@ _MBG_API_ATTR int _MBG_API mbgextio_open_socket( MBG_MSG_CTL *pmctl, #if _USE_SERIAL_IO /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_open_serial( MBG_MSG_CTL *pmctl, const char *dev, - uint32_t baud_rate, const char *framing ) +/** + * @brief Open a binary communication channel using direct serial I/O + * + * Commonly used serial parameters are 19200/8N1, see + * ::MBG_DEFAULT_BAUDRATE and ::MBG_DEFAULT_FRAMING. + * Some newer devices may also support ::MBG_DEFAULT_BAUDRATE_HS. + * + * @param[in] dev Name of the serial port to which the device is connected, + * depending on the naming conventions of the host system. + * @param[out] ppmctl Address of a pointer to a ::MBG_MSG_CTL control structure + * allocated and set up by this call. Set to NULL on error. + * @param[in] baud_rate Baud rate used for serial communication + * @param[in] framing Framing used for serial communication + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_force_conn_serial + * @see ::mbgextio_open_serial_ftdi + * @see ::mbgextio_force_conn_serial_ftdi + * @see ::mbgextio_open_socket + * @see ::mbgextio_open_usb + * @see ::mbgextio_open_usb_direct_io + * @see ::mbgextio_close_connection + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_open_serial( const char *dev, MBG_MSG_CTL **ppmctl, + uint32_t baud_rate, const char *framing ) { - int rc; + MBG_MSG_CTL *pmctl; + int rc = alloc_msg_ctl( &pmctl ); + + if ( rc != MBG_SUCCESS ) + goto out; + pmctl->conn_type = MBG_CONN_TYPE_SERIAL; pmctl->msg_rcv_timeout = MBGEXTIO_MSG_RCV_TIMEOUT_SERIAL; @@ -276,16 +954,28 @@ _MBG_API_ATTR int _MBG_API mbgextio_open_serial( MBG_MSG_CTL *pmctl, const char rc = mbgserio_open( &pmctl->st.serio, dev ); - if ( rc < 0 ) //##++ - return rc; + if ( rc != MBG_SUCCESS ) + goto fail_free; - mbgserio_set_parms( &pmctl->st.serio, baud_rate, framing ); + rc = mbgserio_set_parms( &pmctl->st.serio, baud_rate, framing ); + + if ( rc != MBG_SUCCESS ) + goto fail_free; #if _USE_MUTEX _mbg_mutex_init( &pmctl->xmt.xmt_mutex ); #endif - return 0; + rc = MBG_SUCCESS; + goto out; + +fail_free: + dealloc_msg_ctl( &pmctl ); + +out: + *ppmctl = pmctl; + + return rc; } // mbgextio_open_serial @@ -293,56 +983,490 @@ _MBG_API_ATTR int _MBG_API mbgextio_open_serial( MBG_MSG_CTL *pmctl, const char +#if _USE_SERIAL_IO_FTDI + /*HDR*/ -_MBG_API_ATTR void _MBG_API mbgextio_close_connection( MBG_MSG_CTL *pmctl ) +/** + * @brief Get the port handle of a serial FTDI device + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return the port handle retrieved from the message control structure + */ +_NO_MBG_API_ATTR FT_HANDLE _MBG_API mbgextio_get_serial_ftdi_port_handle( MBG_MSG_CTL *pmctl ) { + return pmctl->st.ftdiio.port_handle; + +} // mbgextio_get_serial_ftdi_port_handle + + + +/*HDR*/ +/** + * @brief Open a binary communication channel using serial FTDI D2xx port + * + * Commonly used serial parameters are 19200/8N1, see + * ::MBG_DEFAULT_BAUDRATE and ::MBG_DEFAULT_FRAMING. + * Some newer devices may also support ::MBG_DEFAULT_BAUDRATE_HS. + * + * @param[in] device_num device number from a list set up by FT_ListDevices() + * @param[out] ppmctl Address of a pointer to a ::MBG_MSG_CTL control structure + * allocated and set up by this call. Set to NULL on error. + * @param[in] baud_rate Baud rate used for serial communication + * @param[in] framing Framing used for serial communication + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_force_conn_serial_ftdi + * @see ::mbgextio_open_serial + * @see ::mbgextio_force_conn_serial + * @see ::mbgextio_open_socket + * @see ::mbgextio_open_usb + * @see ::mbgextio_open_usb_direct_io + * @see ::mbgextio_close_connection + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_open_serial_ftdi( int device_num, + MBG_MSG_CTL **ppmctl, uint32_t baud_rate, const char *framing ) +{ + FT_STATUS status; + ULONG ft_baud; + UCHAR ft_data_bits; + UCHAR ft_stopbits; + UCHAR ft_parity; + const char *cp; + MBG_MSG_CTL *pmctl; + int rc = alloc_msg_ctl( &pmctl ); + + if ( rc != MBG_SUCCESS ) + goto out; + + + pmctl->conn_type = MBG_CONN_TYPE_SERIAL_FTDI; + pmctl->msg_rcv_timeout = MBGEXTIO_MSG_RCV_TIMEOUT_SERIAL; + pmctl->char_rcv_timeout = MBGEXTIO_CHAR_RCV_TIMEOUT_SERIAL; + + status = FT_Open( device_num, &pmctl->st.ftdiio.port_handle ); + + if ( status != FT_OK ) + goto ft_fail_free; + + + // setup transmission speed + switch( baud_rate ) + { + case 300: ft_baud = FT_BAUD_300; break; + case 600: ft_baud = FT_BAUD_600; break; + case 1200: ft_baud = FT_BAUD_1200; break; + case 2400: ft_baud = FT_BAUD_2400; break; + case 4800: ft_baud = FT_BAUD_4800; break; + case 9600: ft_baud = FT_BAUD_9600; break; + case 14400: ft_baud = FT_BAUD_14400; break; + case 19200: ft_baud = FT_BAUD_19200; break; + case 38400: ft_baud = FT_BAUD_38400; break; + case 57600: ft_baud = FT_BAUD_57600; break; + case 115200: ft_baud = FT_BAUD_115200; break; + case 230400: ft_baud = FT_BAUD_230400; break; + case 460800: ft_baud = FT_BAUD_460800; break; + case 921600: ft_baud = FT_BAUD_921600; break; + + default: + rc = MBG_ERR_INV_PARM; + goto fail_free; + } + + + // setup framing + for ( cp = framing; *cp; cp++ ) + { + char c = toupper( *cp ); + + switch ( c ) + { + case '7': ft_data_bits = FT_BITS_7; break; + case '8': ft_data_bits = FT_BITS_8; break; + + case 'N': ft_parity = FT_PARITY_NONE; break; + case 'E': ft_parity = FT_PARITY_EVEN; break; + case 'O': ft_parity = FT_PARITY_ODD; break; + // FT_PARITY_MARK and FT_PARITY_SPACE not supp. by Meinberg API + + case '1': ft_stopbits = FT_STOP_BITS_1; break; + case '2': ft_stopbits = FT_STOP_BITS_2; break; + + default: + rc = MBG_ERR_INV_PARM; + goto fail_free; + } + } + + + status = FT_SetBaudRate( pmctl->st.ftdiio.port_handle, ft_baud ); + + if ( status != FT_OK ) + goto ft_fail_free; + + status = FT_SetDataCharacteristics( pmctl->st.ftdiio.port_handle, + ft_data_bits, ft_stopbits, ft_parity ); + + if ( status != FT_OK ) + goto ft_fail_free; + + + #if _USE_MUTEX + _mbg_mutex_init( &pmctl->xmt.xmt_mutex ); + #endif + + rc = MBG_SUCCESS; + goto out; + + +ft_fail_free: + rc = mbg_ftdi_ft_status_to_mbg( status ); + +fail_free: + dealloc_msg_ctl( &pmctl ); + +out: + *ppmctl = pmctl; + + return rc; + +} // mbgextio_open_serial_ftdi + +#endif + + + +#if _USE_USB_IO + +/*HDR*/ +/** + * @brief Open a binary communication channel using direct USB I/O + * + * @param[in] usbdev The USB device to communicate with. + * @param[out] ppmctl Address of a pointer to a ::MBG_MSG_CTL control structure + * allocated and set up by this call. Set to NULL on error. + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_socket + * @see ::mbgextio_open_serial + * @see ::mbgextio_open_serial_ftdi + * @see ::mbgextio_close_connection + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_open_usb( const MBG_USB_DEVICE *usbdev, MBG_MSG_CTL **ppmctl ) +{ + MBG_MSG_CTL *pmctl; + int rc = alloc_msg_ctl( &pmctl ); + + if ( rc != MBG_SUCCESS ) + goto out; + + + pmctl->conn_type = MBG_CONN_TYPE_USB; + pmctl->msg_rcv_timeout = MBGEXTIO_MSG_RCV_TIMEOUT_SERIAL; + pmctl->char_rcv_timeout = MBGEXTIO_CHAR_RCV_TIMEOUT_SERIAL; + + pmctl->st.usbio = usbdev->iost; + + rc = mbgusbio_open( &pmctl->st.usbio ); + + if ( rc != MBG_SUCCESS ) + goto fail_free; + + #if _USE_MUTEX + _mbg_mutex_init( &pmctl->xmt.xmt_mutex ); + #endif + + rc = MBG_SUCCESS; + goto out; + + +fail_free: + dealloc_msg_ctl( &pmctl ); + +out: + *ppmctl = pmctl; + + return rc; + +} // mbgextio_open_usb + +#endif + + + +#if _USE_USB_DIRECT_IO + +/*HDR*/ +/** + * @brief Open a binary communication channel using direct USB I/O + * + * Currently it is only supported by Linux systems since a file like + * USB device (e.g. "/dev/mbgims") is required and needs to support + * basic I/O operations like write, read, etc... + * + * @param[in] dev Path to file like USB device + * @param[in] flags Bitwise OR flags of access modes like W,RW,RO,etc. + * @param[out] ppmctl Address of a pointer to a ::MBG_MSG_CTL control structure + * allocated and set up by this call. Set to NULL on error. + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_socket + * @see ::mbgextio_open_serial + * @see ::mbgextio_open_serial_ftdi + * @see ::mbgextio_open_usb + * @see ::mbgextio_open_usb_direct_io + * @see ::mbgextio_close_connection + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_open_usb_direct_io( const char *dev, int flags, MBG_MSG_CTL **ppmctl ) +{ + MBG_MSG_CTL *pmctl; + int rc; + + if ( dev == NULL ) + return MBG_ERR_NO_DEV; + + rc = alloc_msg_ctl( &pmctl ); + if ( rc != MBG_SUCCESS ) + goto out; + + pmctl->conn_type = MBG_CONN_TYPE_USB_DIRECT_IO; + pmctl->msg_rcv_timeout = MBGEXTIO_RCV_TIMEOUT_USB_DIRECT_IO; + + pmctl->st.usbdio.usbdiofd = open(dev, flags); + + if ( pmctl->st.usbdio.usbdiofd == MBG_USB_DIRECT_IO_INVALID_FD ) + { + rc = mbg_get_last_error( "failed to open direct USB I/O device" ); + goto fail_free; + } + + rc = MBG_SUCCESS; + goto out; + +fail_free: + dealloc_msg_ctl( &pmctl ); + +out: + *ppmctl = pmctl; + + return rc; + +} // mbgextio_open_usb_direct_io + +#endif + + + +/*HDR*/ +/** + * @brief Close a binary communication channel and release resources + * + * Closes a binary communication channel which has been opened by one + * of the mbgextio_open_...() functions and releases the buffers which + * have been allocated when the channel was opened. + * + * The pointer to the message control structure passed by address is set + * to NULL after the channel has been closed and the resources have + * been released. + * + * @param[in,out] ppmctl Address of a pointer to a message control structure + * created when the communication channel was opened + * by one of the mbgextio_open_...() calls. + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_socket + * @see ::mbgextio_open_serial + * @see ::mbgextio_open_serial_ftdi + * @see ::mbgextio_open_usb + * @see ::mbgextio_open_usb_direct_io + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_close_connection( MBG_MSG_CTL **ppmctl ) +{ + MBG_MSG_CTL *pmctl = *ppmctl; + int rc = MBG_ERR_UNSPEC; + switch ( pmctl->conn_type ) { #if _USE_SERIAL_IO case MBG_CONN_TYPE_SERIAL: - { - #if defined( MBG_TGT_UNIX ) - tcsetattr( pmctl->st.serio.port_handle, TCSANOW, &pmctl->st.serio.oldtio ); - #endif + rc = mbgserio_close( &pmctl->st.serio ); + break; + #endif // _USE_SERIAL_IO - mbgserio_close( &pmctl->st.serio ); + #if _USE_SERIAL_IO_FTDI + case MBG_CONN_TYPE_SERIAL_FTDI: + { + FT_STATUS status = FT_Close( pmctl->st.ftdiio.port_handle ); + pmctl->st.ftdiio.port_handle = MBG_INVALID_PORT_HANDLE; + rc = mbg_ftdi_ft_status_to_mbg( status ); } break; - #endif // _USE_SERIAL_IO + #endif // _USE_SERIAL_IO_FTDI #if _USE_SOCKET_IO case MBG_CONN_TYPE_SOCKET: - { - _mbg_close( pmctl->st.sockio.sockfd ); - pmctl->st.sockio.sockfd = 0; - } break; + rc = socket_close( pmctl ); + break; #endif // _USE_SOCKET_IO - }; // switch + #if _USE_USB_IO + case MBG_CONN_TYPE_USB: + rc = mbgusbio_close( &pmctl->st.usbio ); + break; + #endif // _USE_USB_IO + + #if _USE_USB_DIRECT_IO + case MBG_CONN_TYPE_USB_DIRECT_IO: + if ( pmctl->st.usbdio.usbdiofd == MBG_USB_DIRECT_IO_INVALID_FD ) + { + rc = MBG_SUCCESS; + break; + } + + rc = close( pmctl->st.usbdio.usbdiofd ); + + if ( rc < 0 ) + rc = mbg_get_last_error( "failed to close direct USB I/O device" ); + else + rc = MBG_SUCCESS; + #endif // _USE_USB_DIRECT_IO + + default: + rc = MBG_ERR_CONN_TYPE; + + } // switch #if _USE_MUTEX _mbg_mutex_destroy( &pmctl->xmt.xmt_mutex ); #endif + dealloc_msg_ctl( ppmctl ); + + return rc; + } // mbgextio_close_connection #if _USE_SERIAL_IO -/*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_force_connection( const char *dev ) +static /*HDR*/ +int do_force_conn_serial( const char *dev, const char *cmd_str, BAUD_RATE expected_baudrate ) { - static const char *cmd_str = "\nDFC\n"; //##++ + int i; + int j; + MBG_MSG_CTL *pmctl; + int len = (int) strlen( cmd_str ); + int rc = MBG_ERR_UNSPEC; - MBG_MSG_CTL mctl = + for ( i = 0; i < N_MBG_BAUD_RATES; i++ ) { - { NULL, 0 }, - { NULL, 0 } - }; + for ( j = 0; j < N_MBG_FRAMINGS; j++ ) + { + uint32_t baud_rate = mbg_baud_rates[i]; + const char *framing = mbg_framing_strs[j]; + + rc = mbgextio_open_serial( dev, &pmctl, baud_rate, framing ); + + if ( rc != MBG_SUCCESS ) // failed to open port + goto out; + + mbgserio_write( pmctl->st.serio.port_handle, cmd_str, len ); + // should we check rc here or just continue? + + #ifdef MBG_TGT_WIN32 + // Flushing the output when the serial port is closed doesn't + // always work correctly under Windows, so we insert a delay + // here to make sure the string has been set. + // The required delay depends on the number of characters to + // send, and on the transmission speed (baud rate). + Sleep( ( ( 10 * 1000 * len ) / baud_rate ) + 1 ); + #endif + + mbgextio_close_connection( &pmctl ); + } + } + + rc = mbgextio_open_serial( dev, &pmctl, expected_baudrate, MBG_DEFAULT_FRAMING ); + + if ( rc != MBG_SUCCESS ) // failed to open port + goto out; + + rc = mbgextio_get_receiver_info( pmctl, NULL, NULL ); + + mbgextio_close_connection( &pmctl); + +out: + return rc; + +} // do_force_conn_serial + + + +/*HDR*/ +/** + * @brief Try to force a serial connection to a device + * + * A device's serial port may have been configured to work by default + * in a way which is not appropriate for binary communication, e.g. using + * a low communication speed, or some framing like "7E2" which messes up + * binary data since the parity bit overrides a data bit. + * + * This function sends a special ASCII string to a device which lets the device + * temporarily switch to a specific baud rate and 8N1 framing which is usually + * used for binary communication, see ::MBG_DEFAULT_BAUDRATE, + * ::MBG_DEFAULT_BAUDRATE_HS, and ::MBG_DEFAULT_FRAMING. + * + * Since the current settings of the device's serial port are unknown this + * this command is sent in all common combinations of baud rates and + * framings. + * + * If the connected device supports this it should be possible to open + * the serial communication channel using the default parameters after + * this function has finished. + * + * @param[in] dev Name of the serial port to which the device is connected, + * depending on the naming conventions of the target system + * + * @return One of the negative @ref MBG_ERROR_CODES on error, + * else the determined baud rate. + * + * @see ::mbgextio_open_serial + * @see ::mbgextio_open_serial_ftdi + */ +_NO_MBG_API_ATTR long _MBG_API mbgextio_force_conn_serial( const char *dev ) +{ + long rc = do_force_conn_serial( dev, force_conn_hs_cmd_str, MBG_DEFAULT_BAUDRATE_HS ); + + if ( rc == MBG_SUCCESS ) + return MBG_DEFAULT_BAUDRATE_HS; + rc = do_force_conn_serial( dev, force_conn_cmd_str, MBG_DEFAULT_BAUDRATE ); + + if ( rc == MBG_SUCCESS ) + return MBG_DEFAULT_BAUDRATE; + + return rc; + +} // mbgextio_force_conn_serial + +#endif // _USE_SERIAL_IO + + + +#if _USE_SERIAL_IO_FTDI + +static /*HDR*/ +int do_force_conn_serial_ftdi( int device_num, const char *cmd_str, BAUD_RATE expected_baudrate ) +{ int i; int j; - int len = strlen( cmd_str ); + MBG_MSG_CTL *pmctl; + int len = (int) strlen( force_conn_cmd_str ); + long rc = MBG_ERR_UNSPEC; for ( i = 0; i < N_MBG_BAUD_RATES; i++ ) { @@ -350,126 +1474,315 @@ _MBG_API_ATTR int _MBG_API mbgextio_force_connection( const char *dev ) { uint32_t baud_rate = mbg_baud_rates[i]; const char *framing = mbg_framing_strs[j]; - int rc = mbgextio_open_serial( &mctl, dev, baud_rate, framing ); + DWORD bytes_written; + FT_HANDLE port_handle; + FT_STATUS status; + + rc = mbgextio_open_serial_ftdi( device_num, &pmctl, baud_rate, framing ); + + if ( rc != MBG_SUCCESS ) // failed to open port + goto out; + + port_handle = pmctl->st.ftdiio.port_handle; - if ( rc != 0 ) // failed to open port - return -1; + status = FT_Write( port_handle, (LPVOID) force_conn_cmd_str, len, &bytes_written ); - _mbgserio_write( mctl.st.serio.port_handle, cmd_str, len ); + #if 0 //##+++++++++++ do we need some error checking here? + if ( status != FT_OK ) + { + rc = mbg_ftdi_ft_status_to_mbg( status ); + goto out; + } - #if defined( MBG_TGT_UNIX ) - tcdrain( mctl.st.serio.port_handle ); - #elif defined( MBG_TGT_CVI ) - //##++++ - #elif defined( MBG_TGT_WIN32 ) - FlushFileBuffers( mctl.st.serio.port_handle ); + if ( bytes_written != sizeof( len ) ) + goto out_write_failed; + #endif + + #ifdef MBG_TGT_WIN32 + #if 1 + // TODO Check if this works as expected + status = FT_Purge( port_handle, FT_PURGE_TX ); + #else + + // Flushing the output when the serial port is closed doesn't + // always work correctly under Windows, so we insert a delay + // here to make sure the string has been set. + // The required delay depends on the number of characters to + // send, and on the transmission speed (baud rate). + Sleep( ( ( 10 * 1000 * len ) / baud_rate ) + 1 ); + #endif #endif - mbgextio_close_connection( &mctl ); + mbgextio_close_connection( &pmctl ); } } - return 0; + rc = mbgextio_open_serial_ftdi( device_num, &pmctl, expected_baudrate, MBG_DEFAULT_FRAMING ); -} // mbgextio_force_connection + if ( rc != MBG_SUCCESS ) // failed to open port + goto out; -#endif // _USE_SERIAL_IO + rc = mbgextio_get_receiver_info( pmctl, NULL, NULL ); + + mbgextio_close_connection( &pmctl ); + +out: + return rc; + +} // do_force_conn_serial_ftdi /*HDR*/ -_MBG_API_ATTR void _MBG_API mbgextio_set_char_rcv_timeout( MBG_MSG_CTL *pmctl, ulong new_timeout ) +/** + * @brief Try to force a serial connection to a device via the FTDI API + * + * A device's serial port may have been configured to work by default + * in a way which is not appropriate for binary communication, e.g. using + * a low communication speed, or some framing like "7E2" which messes up + * binary data since the parity bit overrides a data bit. + * + * This function sends a special ASCII string to a device which lets the device + * temporarily switch to a specific baud rate and 8N1 framing which is usually + * used for binary communication, see ::MBG_DEFAULT_BAUDRATE, + * ::MBG_DEFAULT_BAUDRATE_HS, and ::MBG_DEFAULT_FRAMING. + * + * Since the current settings of the device's serial port are unknown this + * this command is sent in all common combinations of baud rates and + * framings. + * + * If the connected device supports this it should be possible to open + * the serial communication channel using the default parameters after + * this function has finished. + * + * @param[in] device_num device number from a list set up by FT_ListDevices() + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_open_serial_ftdi + */ +_NO_MBG_API_ATTR long _MBG_API mbgextio_force_conn_serial_ftdi( int device_num ) { - pmctl->char_rcv_timeout = new_timeout; + long rc = do_force_conn_serial_ftdi( device_num, force_conn_hs_cmd_str, MBG_DEFAULT_BAUDRATE_HS ); -} // mbgextio_set_char_rcv_timeout + if ( rc == MBG_SUCCESS ) + return MBG_DEFAULT_BAUDRATE_HS; + + rc = do_force_conn_serial_ftdi( device_num, force_conn_cmd_str, MBG_DEFAULT_BAUDRATE ); + + if ( rc == MBG_SUCCESS ) + return MBG_DEFAULT_BAUDRATE; + + return rc; + +} // mbgextio_force_conn_serial_ftdi + +#endif // _USE_SERIAL_IO_FTDI /*HDR*/ -_MBG_API_ATTR ulong _MBG_API mbgextio_get_char_rcv_timeout( const MBG_MSG_CTL *pmctl ) +/** + * @brief Retrieve address of the allocated receive buffer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Address of the allocated receive buffer + * + * @see ::mbgextio_get_rcv_buffer_size + * @see ::mbgextio_get_xmt_buffer_addr + * @see ::mbgextio_get_xmt_buffer_size + */ +_NO_MBG_API_ATTR MBG_MSG_BUFF * _MBG_API mbgextio_get_rcv_buffer_addr( MBG_MSG_CTL *pmctl ) { - return pmctl->char_rcv_timeout; + if ( pmctl == NULL ) + return NULL; -} // mbgextio_get_char_rcv_timeout + return pmctl->rcv.pmb; + +} // mbgextio_get_rcv_buffer_addr /*HDR*/ -_MBG_API_ATTR void _MBG_API mbgextio_set_msg_rcv_timeout( MBG_MSG_CTL *pmctl, ulong new_timeout ) +/** + * @brief Retrieve size of the allocated receive buffer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Size of the allocated receive buffer + * + * @see ::mbgextio_get_rcv_buffer_addr + * @see ::mbgextio_get_xmt_buffer_addr + * @see ::mbgextio_get_xmt_buffer_size + */ +_NO_MBG_API_ATTR size_t _MBG_API mbgextio_get_rcv_buffer_size( MBG_MSG_CTL *pmctl ) { - pmctl->msg_rcv_timeout = new_timeout; + if ( pmctl == NULL ) + return 0; -} // mbgextio_set_msg_rcv_timeout + return sizeof( *pmctl->rcv.pmb ); + +} // mbgextio_get_rcv_buffer_size /*HDR*/ -_MBG_API_ATTR ulong _MBG_API mbgextio_get_msg_rcv_timeout( const MBG_MSG_CTL *pmctl ) +/** + * @brief Retrieve address of the allocated transmit buffer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Address of the allocated transmit buffer + * + * @see ::mbgextio_get_rcv_buffer_addr + * @see ::mbgextio_get_rcv_buffer_size + * @see ::mbgextio_get_xmt_buffer_size + */ +_NO_MBG_API_ATTR MBG_MSG_BUFF * _MBG_API mbgextio_get_xmt_buffer_addr( MBG_MSG_CTL *pmctl ) { - return pmctl->msg_rcv_timeout; + if ( pmctl == NULL ) + return NULL; -} // mbgextio_get_msg_rcv_timeout + return pmctl->xmt.pmb; + +} // mbgextio_get_xmt_buffer_addr /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_xmt_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd, - const void *p, int n_bytes ) +/** + * @brief Retrieve size of the allocated transmit buffer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Size of the allocated transmit buffer + * + * @see ::mbgextio_get_rcv_buffer_addr + * @see ::mbgextio_get_rcv_buffer_size + * @see ::mbgextio_get_xmt_buffer_addr + */ +_NO_MBG_API_ATTR size_t _MBG_API mbgextio_get_xmt_buffer_size( MBG_MSG_CTL *pmctl ) { - MBG_MSG_BUFF *pmb; + if ( pmctl == NULL ) + return 0; - if ( n_bytes > sizeof( pmb->u.msg_data ) ) - return -1; // bytes to send exceed buffer size + return sizeof( *pmctl->xmt.pmb ); +} // mbgextio_get_xmt_buffer_size - #if _USE_MUTEX - _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); - #endif - pmb = pmctl->xmt.pmb; - if ( p && n_bytes ) - memcpy( pmb->u.bytes, p, n_bytes ); +/*HDR*/ +/** + * @brief Set character receive timeout value + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] new_timeout New timeout value [ms] + * + * @see ::mbgextio_get_char_rcv_timeout + * @see ::mbgextio_set_msg_rcv_timeout + * @see ::mbgextio_get_msg_rcv_timeout + */ +_NO_MBG_API_ATTR void _MBG_API mbgextio_set_char_rcv_timeout( MBG_MSG_CTL *pmctl, ulong new_timeout ) +{ + pmctl->char_rcv_timeout = new_timeout; - pmb->hdr.len = n_bytes; - pmb->hdr.cmd = cmd; - xmt_tbuff( pmctl ); +} // mbgextio_set_char_rcv_timeout - #if _USE_MUTEX - _mbg_mutex_release( &pmctl->xmt.xmt_mutex ); - #endif - if ( cmd & GPS_REQACK ) - { - int rc = mbgextio_rcv_msg( pmctl, (GPS_CMD) ( cmd & ~GPS_CTRL_MSK ) ); - if ( rc != TR_COMPLETE ) - return -2; +/*HDR*/ +/** + * @brief Get character receive timeout value + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Current timeout value [ms] + * + * @see ::mbgextio_set_char_rcv_timeout + * @see ::mbgextio_set_msg_rcv_timeout + * @see ::mbgextio_get_msg_rcv_timeout + */ +_NO_MBG_API_ATTR ulong _MBG_API mbgextio_get_char_rcv_timeout( const MBG_MSG_CTL *pmctl ) +{ + return pmctl->char_rcv_timeout; - if ( pmctl->rcv.pmb->hdr.cmd & GPS_NACK ) - return -3; +} // mbgextio_get_char_rcv_timeout - if ( !(pmctl->rcv.pmb->hdr.cmd & GPS_ACK) ) - return -4; - } - return 0; +/*HDR*/ +/** + * @brief Set message receive timeout value + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] new_timeout New timeout value [ms] + * + * @see ::mbgextio_set_char_rcv_timeout + * @see ::mbgextio_get_char_rcv_timeout + * @see ::mbgextio_get_msg_rcv_timeout + */ +_NO_MBG_API_ATTR void _MBG_API mbgextio_set_msg_rcv_timeout( MBG_MSG_CTL *pmctl, ulong new_timeout ) +{ + pmctl->msg_rcv_timeout = new_timeout; -} // mbgextio_xmt_msg +} // mbgextio_set_msg_rcv_timeout /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) +/** + * @brief Get message receive timeout value + * + * @param[in,out] pmctl Pointer to a valid message control structure + * + * @return Current timeout value [ms] + * + * @see ::mbgextio_set_char_rcv_timeout + * @see ::mbgextio_get_char_rcv_timeout + * @see ::mbgextio_set_msg_rcv_timeout + */ +_NO_MBG_API_ATTR ulong _MBG_API mbgextio_get_msg_rcv_timeout( const MBG_MSG_CTL *pmctl ) +{ + return pmctl->msg_rcv_timeout; + +} // mbgextio_get_msg_rcv_timeout + + + +/*HDR*/ +/** + * @brief Generic reception of a binary message + * + * @note A certain message type to be waited for can be specified by + * passing one of the ::GPS_CMD_CODES. + * If the special cmd code ::GPS_WILDCARD is specified the function returns + * successfully after *any* type of binary message has been received. + * + * //##++ TODO: callback function to handle asynchronous spontaneous messages + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES, or ::GPS_WILDCARD + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_rcv_msg + * @see ::mbgextio_xmt_msg //##++++ + * @see ::mbgextio_xmt_cmd + * @see ::mbgextio_req_data + * @see ::mbgextio_xmt_cmd_us + * @see ::mbgextio_req_data_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, GPS_CMD cmd ) { MBG_MSG_RCV_CTL *prctl; MBG_MSG_BUFF *pmb; MBG_TMO_TIME msg_timeout; char buff[MBGEXTIO_READ_BUFFER_SIZE]; - ssize_t n_bytes; - int rc; + ssize_t n_bytes = 0; + int rc = MBG_ERR_UNSPEC; int i; mbg_tmo_set_timeout_ms( &msg_timeout, pmctl->msg_rcv_timeout ); @@ -479,61 +1792,164 @@ _MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) n_bytes = 0; if ( mbg_tmo_curr_time_is_after( &msg_timeout ) ) - return TR_TIMEOUT; + { + rc = MBG_ERR_TIMEOUT; + goto out; + } - #if _USE_SOCKET_IO - if ( pmctl->conn_type == MBG_CONN_TYPE_SOCKET ) - { - struct timeval tv_timeout; - fd_set fds; + switch ( pmctl->conn_type ) + { + #if _USE_SOCKET_IO + case MBG_CONN_TYPE_SOCKET: + { + struct timeval tv_timeout; + fd_set fds; + int max_fd; + + mbg_msec_to_timeval( pmctl->msg_rcv_timeout, &tv_timeout ); + + FD_ZERO( &fds ); + FD_SET( pmctl->st.sockio.sockfd, &fds ); + + #if defined( MBG_TGT_WIN32 ) + // Under Windows an fd is a handle which can't simply + // be converted to an int, but the first argument of + // select() is ignored under Windows anyway, so we just + // set max_fd to 0. + max_fd = 0; + #else + max_fd = pmctl->st.sockio.sockfd; + #endif + + rc = select( max_fd + 1, &fds, NULL, NULL, &tv_timeout ); - if ( pmctl->io_error ) - return TR_IO_ERR; + if ( rc == MBG_SOCKET_ERR_RETVAL ) // < 0, error + { + rc = mbg_get_last_socket_error( "select failed in mbgextio_rcv_msg" ); + goto out; + } + + if ( rc == 0 ) // timeout + { + rc = MBG_ERR_TIMEOUT; + goto out; + } - mbgserio_msec_to_timeval( pmctl->msg_rcv_timeout, &tv_timeout ); + // data is available - FD_ZERO( &fds ); - FD_SET( pmctl->st.sockio.sockfd, &fds ); + n_bytes = recv( pmctl->st.sockio.sockfd, buff, sizeof( buff ), 0 ); - rc = select( pmctl->st.sockio.sockfd + 1, &fds, NULL, NULL, &tv_timeout ); + if ( n_bytes < 0 ) + { + rc = mbg_get_last_socket_error( "recv failed in mbgextio_rcv_msg" ); + goto out; + } - if ( rc == 0 ) // timeout - return TR_TIMEOUT; + } break; + #endif // _USE_SOCKET_IO - if ( rc < 0 ) // error + #if _USE_SERIAL_IO + case MBG_CONN_TYPE_SERIAL: { - pmctl->io_error = 1; - return TR_IO_ERR; - } + n_bytes = mbgserio_read_wait( pmctl->st.serio.port_handle, &buff[0], + sizeof( buff[0] ), pmctl->char_rcv_timeout ); - // data is available + if ( n_bytes < 0 ) + { + rc = n_bytes; + goto out; + } - n_bytes = recv( pmctl->st.sockio.sockfd, buff, sizeof( buff ), 0 ); + } break; + #endif // _USE_SERIAL_IO - if ( n_bytes < 0 ) + #if _USE_SERIAL_IO_FTDI + case MBG_CONN_TYPE_SERIAL_FTDI: { - pmctl->io_error = 1; - return TR_IO_ERR; - } - } - #endif // _USE_SOCKET_IO + DWORD bytes_read; + FT_STATUS status; - #if _USE_SERIAL_IO - if ( pmctl->conn_type == MBG_CONN_TYPE_SERIAL ) - { - n_bytes = mbgserio_read_wait( pmctl->st.serio.port_handle, &buff[0], - sizeof( buff[0] ), pmctl->char_rcv_timeout ); + //##++++++ pmctl->char_rcv_timeout, + status = FT_Read( pmctl->st.ftdiio.port_handle, &buff[0], + sizeof( buff[0] ), &bytes_read ); - if ( n_bytes < 0 ) + if ( status != FT_OK ) + { + rc = mbg_ftdi_ft_status_to_mbg( status ); + goto out; + } + + n_bytes = bytes_read; + + } break; + #endif // _USE_SERIAL_IO_FTDI + + #if _USE_USB_IO + case MBG_CONN_TYPE_USB: { - if ( n_bytes == MBGSERIO_TIMEOUT ) - return TR_TIMEOUT; + rc = mbgusbio_read( &pmctl->st.usbio, (uint8_t *) buff, sizeof( buff ), 1000 ); - pmctl->io_error = 1; - return TR_IO_ERR; - } - } - #endif // _USE_SERIAL_IO + if ( rc < 0 ) + goto out; + + // data is available + + n_bytes = rc; + + } break; + #endif // _USE_USB_IO + + #if _USE_USB_DIRECT_IO + case MBG_CONN_TYPE_USB_DIRECT_IO: + { + struct pollfd pfd; + + pfd.fd = pmctl->st.usbdio.usbdiofd; + pfd.events = POLLIN | POLLRDNORM | POLLRDBAND; + pfd.revents = 0; + + rc = poll( &pfd, 1, pmctl->msg_rcv_timeout); + + if ( rc == 0 ) // timeout + { + rc = MBG_ERR_TIMEOUT; + goto out; + } + + if ( rc < 0 ) // error + { + rc = mbg_get_last_error( "failed to poll direct USB I/O" ); + goto out; + } + + // Other stuff? + if ( pfd.revents & ( POLLERR | POLLHUP | POLLNVAL ) ) + { + rc = MBG_ERR_UNSPEC; + goto out; + } + + // Read data? + if ( pfd.revents & ( POLLIN | POLLRDNORM | POLLRDBAND ) ) + { + rc = read( pmctl->st.usbdio.usbdiofd, (void*)buff, sizeof(buff) ); + + if ( rc < 0 ) + { + rc = mbg_get_last_error( "failed to read direct USB I/O" ); + goto out; + } + + n_bytes = rc; + } + } break; + #endif + + default: + rc = MBG_ERR_CONN_TYPE; + goto out; + + } // switch prctl = &pmctl->rcv; pmb = prctl->pmb; @@ -550,7 +1966,16 @@ _MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) case TR_WAITING: /* no data transfer sequence in progress */ #if _USE_CHK_TSTR if ( prctl->chk_tstr_fnc ) /* optionally handle normal, non-protocol data */ - prctl->chk_tstr_fnc( c, prctl->chk_tstr_arg ); + { + int tstr_rc = prctl->chk_tstr_fnc( c, prctl->chk_tstr_arg ); + + if ( tstr_rc > 0 ) // a valid time string has been received + { + // return only if the caller does not wait for a specific packet type + if ( cmd == GPS_WILDCARD ) + return TR_COMPLETE_TSTR; //##++++++++++++++++++++ TODO better way to detect string + } + } #endif // intentional fall-through @@ -559,10 +1984,27 @@ _MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) case TR_COMPLETE: { - uint16_t rcvd_cmd = pmb->hdr.cmd & ~GPS_CTRL_MSK; + GPS_CMD rcvd_cmd; + + #if _USE_CHK_TSTR + //##+++++++++++++++++ + // If a valid binary packet has been received then discard + // a partial time string possibly received before. + #endif + + rcvd_cmd = pmb->hdr.cmd & ~GPS_CTRL_MSK; if ( rcvd_cmd == cmd ) /* the received packet is what we've been waiting for */ - return TR_COMPLETE; + { + if ( pmb->hdr.cmd & GPS_NACK ) + { + rc = MBG_ERR_RCVD_NACK; + goto out; + } + + rc = MBG_SUCCESS; + goto out; + } #if _USE_ENCRYPTION /* if an encrypted packet has been received then decrypt it */ @@ -571,37 +2013,162 @@ _MBG_API_ATTR int _MBG_API mbgextio_rcv_msg( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) rc = decrypt_message( pmctl ); if ( rc < 0 ) /* decryption error */ - return rc; + goto out; rcvd_cmd = pmb->hdr.cmd & ~GPS_CTRL_MSK; + if ( pmb->hdr.cmd & GPS_NACK ) + { + rc = MBG_ERR_RCVD_NACK; + goto out; + } + if ( rcvd_cmd == cmd ) /* the received packet is what we've been waiting for */ - return TR_COMPLETE; + { + rc = MBG_SUCCESS; + goto out; + } } #endif /* not waiting for a specific packet, so return if any packet is complete */ - if ( cmd == (uint16_t) -1 ) - return TR_COMPLETE; + if ( cmd == GPS_WILDCARD ) + { + rc = MBG_SUCCESS; + goto out; + } //##++ received a msg which does not match the expected code prctl->cnt = 0; /* restart receiving */ } break; + case TR_CSUM_HDR: + rc = MBG_ERR_HDR_CSUM; + goto out; + + case TR_CSUM_DATA: + rc = MBG_ERR_DATA_CSUM; + goto out; + default: /* any error condition */ - return rc; + rc = MBG_ERR_UNSPEC; //##+++++++++++++++++++++ use detailed rc + goto out; } /* switch */ } } +out: + return rc; + } // mbgextio_rcv_msg /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) +/** + * @brief Set up and send a generic binary message + * + * @note This function should preferably be used only as low level function + * called from within more specific functions used to send specific data. + * If this function is called directly with a buffer to be sent then the + * transmit mutex is acquired by this function. However, other (higher level) + * API functions which set up the transmit buffer directly have to acquire + * the transmit mutex by themselves before they set up the transmit buffer, + * and pass a NULL pointer to indicate the transmit buffer has already been + * set up. The correct number of bytes to send (n_bytes) has to be specified + * anyway, though. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES + * @param[in] p Address of a data structure according to the specified cmd parameter, or NULL + * @param[in] n_bytes Size of the data structure addressed by parameter p + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_xmt_msg( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, + GPS_CMD cmd, const void *p, uint16_t n_bytes ) +{ + MBG_MSG_BUFF *pmb; + int rc = MBG_ERR_UNSPEC; + + if ( n_bytes > sizeof( pmb->u.msg_data ) ) + { + rc = MBG_ERR_OVERFLOW; // bytes to send exceed transmit buffer size + goto out; + } + + + pmb = pmctl->xmt.pmb; + + if ( p ) + { + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + memcpy( pmb->u.bytes, p, n_bytes ); + } + + pmb->hdr.len = n_bytes; + pmb->hdr.cmd = cmd; + rc = xmt_tbuff( pmctl, p_addr ); + + #if _USE_MUTEX + _mbg_mutex_release( &pmctl->xmt.xmt_mutex ); + #endif + + if ( rc != MBG_SUCCESS ) // error + goto out; + + + // If an acknowledge has been requested + // wait for a reply and check it. + if ( cmd & GPS_REQACK ) + { + rc = mbgextio_rcv_msg( pmctl, p_addr, (GPS_CMD) ( cmd & ~GPS_CTRL_MSK ) ); + + if ( rc != MBG_SUCCESS ) + { + //##++++++++++ rc = ... eventually return a different error code + goto out; + } + + if ( pmctl->rcv.pmb->hdr.cmd & GPS_NACK ) + { + rc = MBG_ERR_RCVD_NACK; + goto out; + } + + if ( !(pmctl->rcv.pmb->hdr.cmd & GPS_ACK) ) + rc = MBG_ERR_RCVD_NO_ACK; + } + +out: + return rc; + +} // mbgextio_xmt_msg + + + +/*HDR*/ +/** + * @brief Transmit a command-only message without additional data. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_xmt_msg + * @see ::mbgextio_rcv_msg + * @see ::mbgextio_req_data + * @see ::mbgextio_xmt_cmd_us + * @see ::mbgextio_req_data_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, GPS_CMD cmd ) { int rc; @@ -609,7 +2176,7 @@ _MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); #endif - rc = xmt_cmd( pmctl, cmd ); + rc = xmt_cmd( pmctl, p_addr, cmd ); #if _USE_MUTEX _mbg_mutex_release( &pmctl->xmt.xmt_mutex ); @@ -622,7 +2189,26 @@ _MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd_us( MBG_MSG_CTL *pmctl, GPS_CMD cmd, uint16_t us ) +/** + * @brief Transmit a message without a single ushort (uint16_t) parameter + * + * The ushort parameter is often used to send an index value when requesting + * a certain element of an array of same data structures. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES + * @param[in] us The ushort parameter for the command code + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_xmt_msg + * @see ::mbgextio_rcv_msg + * @see ::mbgextio_xmt_cmd + * @see ::mbgextio_req_data + * @see ::mbgextio_req_data_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd_us( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, GPS_CMD cmd, uint16_t us ) { int rc; @@ -630,7 +2216,7 @@ _MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd_us( MBG_MSG_CTL *pmctl, GPS_CMD cmd, _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); #endif - rc = xmt_cmd_us( pmctl, cmd, us ); + rc = xmt_cmd_us( pmctl, p_addr, cmd, us ); #if _USE_MUTEX _mbg_mutex_release( &pmctl->xmt.xmt_mutex ); @@ -643,34 +2229,110 @@ _MBG_API_ATTR int _MBG_API mbgextio_xmt_cmd_us( MBG_MSG_CTL *pmctl, GPS_CMD cmd, /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_req_data( MBG_MSG_CTL *pmctl, GPS_CMD cmd ) +/** + * @brief Transmit a message without a single ushort (16 bit) parameter + * + * The ushort parameter is often used to send an index value when requesting + * a specific element of an array of data structures. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_xmt_msg + * @see ::mbgextio_rcv_msg + * @see ::mbgextio_xmt_cmd + * @see ::mbgextio_xmt_cmd_us + * @see ::mbgextio_req_data_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_req_data( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, GPS_CMD cmd ) { - xmt_cmd( pmctl, cmd ); /* request a set of data */ + int rc = xmt_cmd( pmctl, p_addr, cmd ); /* request a set of data */ + + if ( rc != MBG_SUCCESS ) // error + goto out; - return mbgextio_rcv_msg( pmctl, cmd ); + rc = mbgextio_rcv_msg( pmctl, p_addr, cmd ); + +out: + return rc; } // mbgextio_req_data /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_req_data_idx( MBG_MSG_CTL *pmctl, GPS_CMD cmd, uint16_t idx ) +/** + * @brief Read a specific element of an array of data structures + * + * The type of data is implicitely associated with the cmd parameter. + * Usually the number of supported array elements has to be determined + * by some other means, e.g. from a field in the ::RECEIVER_INFO structure. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] cmd One of the command codes enumerated in ::GPS_CMD_CODES + * @param[in] idx The index of the array element to read + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_xmt_msg + * @see ::mbgextio_rcv_msg + * @see ::mbgextio_xmt_cmd + * @see ::mbgextio_req_data + * @see ::mbgextio_xmt_cmd_us + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_req_data_idx( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, + GPS_CMD cmd, uint16_t idx ) { - xmt_cmd_us( pmctl, cmd, idx ); /* request a set of data */ + int rc = xmt_cmd_us( pmctl, p_addr, cmd, idx ); // send a request for a set of data - return mbgextio_rcv_msg( pmctl, cmd ); + if ( rc < 0 ) // error + goto out; + + rc = mbgextio_rcv_msg( pmctl, p_addr, cmd ); // wait for the reply + +out: + return rc; } // mbgextio_req_data_idx + /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_receiver_info( MBG_MSG_CTL *pmctl, RECEIVER_INFO *p ) +/** + * @brief Read a receiver info structure + * + * The ::RECEIVER_INFO should be read at first to identify the connected + * device and determine the basic features supported by the device. + * + * @note Some very old devices may not provide a ::RECEIVER_INFO, + * so ::mbgextio_setup_receiver_info should be called preferably + * to read the receiver info using this function, if supported, + * or set up a default structure for devices which don't provide + * a receiver info. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_setup_receiver_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_receiver_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, RECEIVER_INFO *p ) { - int rc = mbgextio_req_data( pmctl, GPS_RECEIVER_INFO ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_RECEIVER_INFO ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.receiver_info; + _mbg_swab_receiver_info( p ); + } return rc; @@ -679,12 +2341,67 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_receiver_info( MBG_MSG_CTL *pmctl, RECEI /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_sw_rev( MBG_MSG_CTL *pmctl, SW_REV *p ) +/** + * @brief Setup a receiver info structure + * + * The ::RECEIVER_INFO should be read at first to identify the connected + * device and determine the basic features supported by the device. + * + * @note Some very old devices may not provide a ::RECEIVER_INFO. + * This function tries to read the ::RECEIVER_INFO from the device, + * and sets up a default structure if the device doesn't support this. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_receiver_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_setup_receiver_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, RECEIVER_INFO *p ) +{ + int rc = mbgextio_get_receiver_info( pmctl, p_addr, p ); + + if ( rc == MBG_SUCCESS ) //##++++++++++++++ or in all cases except of MBG_ERR_TIMEOUT ? + goto out; + + //##+++++++++++++ TODO try to read SW_REV, etc., evt. depending on p_addr == NULL or not + +out: + return rc; + +} // mbgextio_setup_receiver_info + + + +/*HDR*/ +/** + * @brief Read the software revision + * + * @deprecated This function is deprecated since the ::SW_REV structure + * is also a member of the ::RECEIVER_INFO. This call may be required, + * though, for very old devices which don't support the ::RECEIVER_INFO. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_setup_receiver_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_sw_rev( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, SW_REV *p ) { - int rc = mbgextio_req_data( pmctl, GPS_SW_REV ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_SW_REV ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.sw_rev; + _mbg_swab_sw_rev( p ); + } return rc; @@ -693,12 +2410,27 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_sw_rev( MBG_MSG_CTL *pmctl, SW_REV *p ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_bvar_stat( MBG_MSG_CTL *pmctl, BVAR_STAT *p ) +/** + * @brief Read the status of buffered variables + * + * @note Only supported if ::GPS_MODEL_HAS_BVAR_STAT + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_bvar_stat( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, BVAR_STAT *p ) { - int rc = mbgextio_req_data( pmctl, GPS_BVAR_STAT ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_BVAR_STAT ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.bvar_stat; + _mbg_swab_bvar_stat( p ); + } return rc; @@ -707,12 +2439,30 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_bvar_stat( MBG_MSG_CTL *pmctl, BVAR_STAT /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_time( MBG_MSG_CTL *pmctl, TTM *p ) +/** + * @brief Read the current time as ::TTM strucure + * + * @note This function is only supported by GPS receivers. + * + * The returned time is not very accurate since the response time + * transmission delay can't be determmined. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_time( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, TTM *p ) { - int rc = mbgextio_req_data( pmctl, GPS_TIME ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_TIME ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.ttm; + _mbg_swab_ttm( p ); + } return rc; @@ -721,42 +2471,92 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_time( MBG_MSG_CTL *pmctl, TTM *p ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_time( MBG_MSG_CTL *pmctl, const TTM *p ) +/** + * @brief Set the device's time by sending a ::TTM strucure + * + * @note The function is not supported by all devices. Time has to be + * passed as local time according to the device's ::TZDL settings. + * New time is only set with some tens of ms accuracy. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_time( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const TTM *p ) { - GPS_CMD cmd = GPS_TIME; + GPS_CMD cmd = GPS_TIME | OPT_GPS_ACK_CODE; + TTM *p_data = &pmctl->xmt.pmb->u.msg_data.ttm; -#if _MBGEXTIO_DIRECT_RC + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + *p_data = *p; + _mbg_swab_ttm( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_time -#else - int rc; - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) +/*HDR*/ +/** + * @brief Set the device's position by sending an ::LLA array + * + * @note This function is only supported by GPS receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] lla Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_pos_lla( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const LLA lla ) +{ + GPS_CMD cmd = GPS_POS_LLA | OPT_GPS_ACK_CODE; + double *p_lla = pmctl->xmt.pmb->u.msg_data.lla; + int i; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + for ( i = 0; i < N_LLA; i++ ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + p_lla[i] = lla[i]; + swap_double( &p_lla[i] ); + _mbg_swab_double( &p_lla[i] ); } - return 0; + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( LLA ) ); -#endif - -} // mbgextio_set_time +} // mbgextio_set_pos_lla /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_pos_lla( MBG_MSG_CTL *pmctl, LLA lla ) +/** + * @brief Read the current receiver position as ::LLA array + * + * @note This function is only supported by GPS receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] lla Pointer to the data array to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_pos_lla( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, LLA lla ) { - int rc = mbgextio_req_data( pmctl, GPS_POS_LLA ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_POS_LLA ); - if ( rc == TR_COMPLETE ) + if ( ( rc == MBG_SUCCESS ) && lla ) { MSG_DATA *pmb = &pmctl->rcv.pmb->u.msg_data; int i; @@ -764,9 +2564,7 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_pos_lla( MBG_MSG_CTL *pmctl, LLA lla ) for ( i = 0; i < N_LLA; i++ ) { swap_double( &pmb->lla[i] ); - - if ( lla ) - lla[i] = pmb->lla[i]; + lla[i] = pmb->lla[i]; } } @@ -777,140 +2575,235 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_pos_lla( MBG_MSG_CTL *pmctl, LLA lla ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_pos_lla( MBG_MSG_CTL *pmctl, const LLA lla ) +/** + * @brief Read the current receiver position as ::XYZ array + * + * @note This function is only supported by GPS receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] xyz Pointer to the data array to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_pos_xyz( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XYZ xyz ) { - GPS_CMD cmd = GPS_POS_LLA; - -#if _MBGEXTIO_DIRECT_RC + int rc = mbgextio_req_data( pmctl, p_addr, GPS_POS_XYZ ); - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) lla, sizeof( LLA ) ); - -#else - int rc; - - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) lla, sizeof( LLA ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); - - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + if ( ( rc == MBG_SUCCESS ) && xyz ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. - } + MSG_DATA *pmb = &pmctl->rcv.pmb->u.msg_data; + int i; - return 0; + for ( i = 0; i < N_XYZ; i++ ) + { + swap_double( &pmb->xyz[i] ); + xyz[i] = pmb->xyz[i]; + } + } -#endif + return rc; -} // mbgextio_set_pos_lla +} // mbgextio_get_pos_xyz /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_tzdl( MBG_MSG_CTL *pmctl, TZDL *p ) +/** + * @brief Read the current receiver position as ::POS structure + * + * @note This function is only supported by GPS receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p_pos Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_pos( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, POS *p_pos ) { - int rc = mbgextio_req_data( pmctl, GPS_TZDL ); + int rc = mbgextio_get_pos_xyz( pmctl, p_addr, p_pos->xyz ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.tzdl; + if ( rc != MBG_SUCCESS ) + goto out; - return rc; -} // mbgextio_get_tzdl + rc = mbgextio_get_pos_lla( pmctl, p_addr, p_pos->lla ); + if ( rc != MBG_SUCCESS ) + goto out; + lla_to_dms( p_pos ); -/*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_tzdl( MBG_MSG_CTL *pmctl, const TZDL *p ) -{ - GPS_CMD cmd = GPS_TZDL; +out: + return rc; -#if _MBGEXTIO_DIRECT_RC +} // mbgextio_get_pos - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); -#else - int rc; - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); +/*HDR*/ +/** + * @brief Read the local time conversion parameters in ::TZDL format + * + * @note Some devices may not support ::TZDL settings + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_tzdl( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, TZDL *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_TZDL ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.tzdl; + _mbg_swab_tzdl( p ); } - return 0; - -#endif + return rc; } // mbgextio_get_tzdl /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_port_parm( MBG_MSG_CTL *pmctl, PORT_PARM *p ) +/** + * @brief Set the local time conversion parameters in ::TZDL format + * + * @note Some devices may not support ::TZDL settings + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_tzdl( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const TZDL *p ) { - int rc = mbgextio_req_data( pmctl, GPS_PORT_PARM ); + GPS_CMD cmd = GPS_TZDL | OPT_GPS_ACK_CODE; + TZDL *p_data = &pmctl->xmt.pmb->u.msg_data.tzdl; - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.port_parm; + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - return rc; + *p_data = *p; + _mbg_swab_tzdl( p_data ); -} // mbgextio_get_port_parm + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_tzdl /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_port_parm( MBG_MSG_CTL *pmctl, const PORT_PARM *p ) +/** + * @brief Read serial port parameters in ::PORT_PARM format + * + * @deprecated This function is deprecated since the ::PORT_PARM structure + * supports only 2 serial ports, and does not not support configuration + * of a string type. The function ::mbgextio_get_serial_settings should + * be used instead. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_serial_settings + * @see ::mbgextio_save_serial_settings + * @see ::mbgextio_set_port_parm + * @see ::mbgextio_setup_receiver_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_port_parm( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PORT_PARM *p ) { - GPS_CMD cmd = GPS_PORT_PARM; - -#if _MBGEXTIO_DIRECT_RC + int rc = mbgextio_req_data( pmctl, p_addr, GPS_PORT_PARM ); - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.port_parm; + _mbg_swab_port_parm( p ); + } -#else + return rc; - MBG_MSG_BUFF *pmb = pmctl->xmt.pmb; - int rc; +} // mbgextio_get_port_parm - pmb->u.msg_data.port_parm = *p; - pmb->hdr.len = sizeof( pmb->u.msg_data.port_parm ); - pmb->hdr.cmd = cmd | GPS_REQACK; - xmt_tbuff( pmctl ); - rc = mbgextio_rcv_msg( pmctl, cmd ); +/*HDR*/ +/** + * @brief Send serial port parameters in ::PORT_PARM format + * + * @deprecated This function is deprecated since the ::PORT_PARM structure + * supports only 2 serial ports, and does not not support configuration + * of a string type. The function ::mbgextio_save_serial_settings should + * be used instead. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_serial_settings + * @see ::mbgextio_save_serial_settings + * @see ::mbgextio_get_port_parm + * @see ::mbgextio_setup_receiver_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_port_parm( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const PORT_PARM *p ) +{ + GPS_CMD cmd = GPS_PORT_PARM | OPT_GPS_ACK_CODE; + PORT_PARM *p_data = &pmctl->xmt.pmb->u.msg_data.port_parm; - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) - { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. - } + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - return 0; + *p_data = *p; + _mbg_swab_port_parm( p_data ); -#endif + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); } // mbgextio_set_port_parm /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_synth( MBG_MSG_CTL *pmctl, SYNTH *p ) +/** + * @brief Read the frequency synthesizer settings + * + * @note Some devices may not provide a frequency synthesizer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_synth + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_synth( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, SYNTH *p ) { - int rc = mbgextio_req_data( pmctl, GPS_SYNTH ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_SYNTH ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.synth; + _mbg_swab_synth( p ); + } return rc; @@ -919,43 +2812,60 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_synth( MBG_MSG_CTL *pmctl, SYNTH *p ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_synth( MBG_MSG_CTL *pmctl, const SYNTH *p ) +/** + * @brief Write the frequency synthesizer settings + * + * @note Some devices may not provide a frequency synthesizer + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_synth + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_synth( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const SYNTH *p ) { - GPS_CMD cmd = GPS_SYNTH; - -#if _MBGEXTIO_DIRECT_RC - - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); - -#else - int rc; - - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); + GPS_CMD cmd = GPS_SYNTH | OPT_GPS_ACK_CODE; + SYNTH *p_data = &pmctl->xmt.pmb->u.msg_data.synth; - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) - { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. - } + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - return 0; + *p_data = *p; + _mbg_swab_synth( p_data ); -#endif + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); } // mbgextio_set_synth /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_ant_info( MBG_MSG_CTL *pmctl, ANT_INFO *p ) +/** + * @brief Read the GPS antenna info structure + * + * @note This is only supported by GPS receivers. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ant_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, ANT_INFO *p ) { - int rc = mbgextio_req_data( pmctl, GPS_ANT_INFO ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_ANT_INFO ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.ant_info; + _mbg_swab_ant_info( p ); + } return rc; @@ -964,24 +2874,34 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_ant_info( MBG_MSG_CTL *pmctl, ANT_INFO * /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_ucap( MBG_MSG_CTL *pmctl, TTM *p ) +/** + * @brief Read a user capture event in ::TTM format + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_clr_ucap_buff + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ucap( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, TTM *p ) { - int rc; + int rc = xmt_cmd( pmctl, p_addr, GPS_UCAP ); /* request a set of data */ - xmt_cmd( pmctl, GPS_UCAP ); /* request a set of data */ + if ( rc != MBG_SUCCESS ) + goto out; - // Attention: Older firmware versions may reply with GPS_TIME - // messages instead of GPS_UCAP messages, and may not send a reply + // Attention: Older firmware versions may reply with GPS_TIME + // messages instead of GPS_UCAP messages, and may not send a reply // at all if no capture event is available in the on-board FIFO. for (;;) { - rc = mbgextio_rcv_msg( pmctl, -1 ); - - if ( rc < 0 ) - break; + rc = mbgextio_rcv_msg( pmctl, p_addr, -1 ); - if ( rc != TR_COMPLETE ) - continue; + if ( rc != MBG_SUCCESS ) + goto out; if ( pmctl->rcv.pmb->hdr.cmd == GPS_UCAP ) break; @@ -998,11 +2918,15 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_ucap( MBG_MSG_CTL *pmctl, TTM *p ) // is empty. This is indicated with 0xFF in the seconds field of // the GPS time structure. if ( pmctl->rcv.pmb->hdr.len > 0 ) + { *p = pmctl->rcv.pmb->u.msg_data.ttm; + _mbg_swab_ttm( p ); + } else _ttm_time_set_unavail( p ); // no capture event available } +out: return rc; } // mbgextio_get_ucap @@ -1010,12 +2934,29 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_ucap( MBG_MSG_CTL *pmctl, TTM *p ) /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_enable_flags( MBG_MSG_CTL *pmctl, ENABLE_FLAGS *p ) +/** + * @brief Read the enable flags controlling when output signals are enabled + * + * @note Some devices may not support ::ENABLE_FLAGS + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_enable_flags + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_enable_flags( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, ENABLE_FLAGS *p ) { - int rc = mbgextio_req_data( pmctl, GPS_ENABLE_FLAGS ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_ENABLE_FLAGS ); - if ( ( rc == TR_COMPLETE ) && p ) + if ( ( rc == MBG_SUCCESS ) && p ) + { *p = pmctl->rcv.pmb->u.msg_data.enable_flags; + _mbg_swab_enable_flags( p ); + } return rc; @@ -1024,487 +2965,2629 @@ _MBG_API_ATTR int _MBG_API mbgextio_get_enable_flags( MBG_MSG_CTL *pmctl, ENABLE /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_enable_flags( MBG_MSG_CTL *pmctl, const ENABLE_FLAGS *p ) +/** + * @brief Send the enable flags controlling when output signals are enabled + * + * @note Some devices may not support ::ENABLE_FLAGS + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_enable_flags + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_enable_flags( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const ENABLE_FLAGS *p ) { - GPS_CMD cmd = GPS_ENABLE_FLAGS; + GPS_CMD cmd = GPS_ENABLE_FLAGS | OPT_GPS_ACK_CODE; + ENABLE_FLAGS *p_data = &pmctl->xmt.pmb->u.msg_data.enable_flags; -#if _MBGEXTIO_DIRECT_RC + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + *p_data = *p; + _mbg_swab_enable_flags( p_data ); -#else - int rc; + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); +} // mbgextio_set_enable_flags + + + +/*HDR*/ +/** + * @brief Read GPS status info + * + * @note This is only supported by GPS receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_stat_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, STAT_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_STAT_INFO ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.stat_info; + _mbg_swab_stat_info( p ); } - return 0; + return rc; -#endif +} // mbgextio_get_stat_info -} // mbgextio_set_enable_flags + + +/*HDR*/ +/** + * @brief Read the configured length of the antenna cable + * + * This is only supported by GPS/GNSS receivers, check ::GPS_MODEL_HAS_ANT_CABLE_LENGTH + * + * @note Some older devices may not reply to this request unless + * the application has registered itself as terminal application + * (GPS_AUTO_ON is not sufficient). + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_ant_cable_len + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ant_cable_len( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, ANT_CABLE_LEN *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_ANT_CABLE_LENGTH ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ant_cable_len; + _mbg_swab_ant_cable_len( p ); + } + + return rc; + +} // mbgextio_get_ant_cable_len /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_stat_info( MBG_MSG_CTL *pmctl, STAT_INFO *p ) +/** + * @brief Send the GPS antenna cable length configuration + * + * This is only supported by GPS/GNSS receivers, check ::GPS_MODEL_HAS_ANT_CABLE_LENGTH + + * @note Different devices may accept different maximum values, so the + * written value should be re-read using ::mbgextio_get_ant_cable_len + * to check if the parameter has been accepted. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ant_cable_len + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ant_cable_len( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const ANT_CABLE_LEN *p ) { - int rc = mbgextio_req_data( pmctl, GPS_STAT_INFO ); + GPS_CMD cmd = GPS_ANT_CABLE_LENGTH | OPT_GPS_ACK_CODE; + ANT_CABLE_LEN *p_data = &pmctl->xmt.pmb->u.msg_data.ant_cable_len; - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.stat_info; + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_ant_cable_len( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ant_cable_len + + + +/*HDR*/ +/** + * @brief Read configuration info and supported features of the device's IRIG output + * + * @note This is only supported if ::GPS_HAS_IRIG_TX is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_irig_tx_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_irig_tx_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, IRIG_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_IRIG_TX_INFO ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.irig_tx_info; + _mbg_swab_irig_info( p ); + } return rc; -} // mbgextio_get_stat_info +} // mbgextio_get_irig_tx_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_ant_cable_len( MBG_MSG_CTL *pmctl, ANT_CABLE_LEN *p ) +/** + * @brief Send new configuration settings for the device's IRIG output + * + * @note This is only supported if ::GPS_HAS_IRIG_TX is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_irig_tx_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_irig_tx_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const IRIG_SETTINGS *p ) { - int rc = mbgextio_req_data( pmctl, GPS_ANT_CABLE_LENGTH ); + GPS_CMD cmd = GPS_IRIG_TX_SETTINGS | OPT_GPS_ACK_CODE; + IRIG_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.irig_tx_settings; - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.ant_cable_len; + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_irig_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_irig_tx_settings + + + +/*HDR*/ +/** + * @brief Read configuration info and supported features for the device's IRIG input + * + * @note This is only supported if ::GPS_HAS_IRIG_RX is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_irig_rx_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_irig_rx_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, IRIG_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_IRIG_RX_INFO ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.irig_rx_info; + _mbg_swab_irig_info( p ); + } return rc; -} // mbgextio_get_ant_cable_len +} // mbgextio_get_irig_rx_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_ant_cable_len( MBG_MSG_CTL *pmctl, const ANT_CABLE_LEN *p ) +/** + * @brief Send new configuration settings for the device's IRIG input + * + * @note This is only supported if ::GPS_HAS_IRIG_RX is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_irig_rx_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_irig_rx_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const IRIG_SETTINGS *p ) { - GPS_CMD cmd = GPS_ANT_CABLE_LENGTH; + GPS_CMD cmd = GPS_IRIG_RX_SETTINGS | OPT_GPS_ACK_CODE; + IRIG_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.irig_rx_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif -#if _MBGEXTIO_DIRECT_RC + *p_data = *p; + _mbg_swab_irig_settings( p_data ); - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); -#else - int rc; +} // mbgextio_set_irig_rx_settings - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + +/*HDR*/ +/** + * @brief Read current ref offset to %UTC configuration + * + * @note This is only supported if ::GPS_HAS_REF_OFFS is set in ::RECEIVER_INFO::features, + * usually with IRIG receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_ref_offs + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ref_offs( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_REF_OFFS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_REF_OFFS ); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.ref_offs; + _mbg_swab_mbg_ref_offs( p ); } - return 0; + return rc; -#endif +} // mbgextio_get_ref_offs -} // mbgextio_set_ant_cable_len + + +/*HDR*/ +/** + * @brief Send new ref offset to %UTC settings + * + * @note This is only supported if ::GPS_HAS_REF_OFFS is set in ::RECEIVER_INFO::features, + * usually with IRIG receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ref_offs + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ref_offs( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_REF_OFFS *p ) +{ + GPS_CMD cmd = GPS_REF_OFFS | OPT_GPS_ACK_CODE; + MBG_REF_OFFS *p_data = &pmctl->xmt.pmb->u.msg_data.ref_offs; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_mbg_ref_offs( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ref_offs /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_irig_tx_info( MBG_MSG_CTL *pmctl, IRIG_INFO *p ) +/** + * @brief Read current debug status + * + * @note This is only supported if ::GPS_HAS_DEBUG_STATUS is set in ::RECEIVER_INFO::features, + * usually with IRIG receivers + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_debug_status( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_DEBUG_STATUS *p ) { - int rc = mbgextio_req_data( pmctl, GPS_IRIG_TX_INFO ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_DEBUG_STATUS ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.irig_tx_info; + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.debug_status; + _mbg_swab_debug_status( p ); + } return rc; -} // mbgextio_get_irig_tx_info +} // mbgextio_get_debug_status /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_irig_tx_settings( MBG_MSG_CTL *pmctl, const IRIG_SETTINGS *p ) +/** + * @brief Read current optional settings and supported options + * + * @note This is only supported if ::GPS_HAS_OPT_SETTINGS is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_opt_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_opt_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_OPT_INFO *p ) { - GPS_CMD cmd = GPS_IRIG_TX_SETTINGS; + int rc = mbgextio_req_data( pmctl, p_addr, GPS_OPT_INFO ); -#if _MBGEXTIO_DIRECT_RC + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.opt_info; + _mbg_swab_mbg_opt_info( p ); + } - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + return rc; -#else - int rc; +} // mbgextio_get_opt_info + + + +/*HDR*/ +/** + * @brief Send new optional settings flags + * + * @note This is only supported if ::GPS_HAS_OPT_SETTINGS is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_opt_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_opt_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_OPT_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_OPT_SETTINGS | OPT_GPS_ACK_CODE; + MBG_OPT_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.opt_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); + *p_data = *p; + _mbg_swab_mbg_opt_settings( p_data ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_opt_settings + + + +/*HDR*/ +/** + * @brief Read information on a specific supported string format + * + * Retrieve a single entry from an array of supported string types. + * The number of supported string types is specified in ::RECEIVER_INFO::n_str_type. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::RECEIVER_INFO::n_str_type - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_all_str_type_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_str_type_info_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, STR_TYPE_INFO_IDX *p, uint16_t idx ) +{ + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_STR_TYPE_INFO_IDX, idx ); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.str_type_info_idx; + _mbg_swab_str_type_info_idx( p ); + + #if 0 //##++ TODO: check if received idx matches requested idx + if ( pii.idx != i ) + { + printf( "** Info for port %i requested, but for %i received.\n", + pii.idx, i ); + rc = ...; + } + #endif } - return 0; + return rc; -#endif +} // mbgextio_get_str_type_info_idx -} // mbgextio_set_irig_tx_settings + + +/*HDR*/ +/** + * @brief Read an array of all supported string types + * + * The number of supported string types is specified in ::RECEIVER_INFO::n_str_type. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] stii An array which can hold at least ::RECEIVER_INFO::n_str_type entries + * @param[in] p_ri Pointer to a valid ::RECEIVER_INFO structure + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_str_type_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_all_str_type_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, STR_TYPE_INFO_IDX stii[], + const RECEIVER_INFO *p_ri ) +{ + int rc = MBG_SUCCESS; + uint16_t i; + + for ( i = 0; i < p_ri->n_str_type; i++ ) + { + rc = mbgextio_get_str_type_info_idx( pmctl, p_addr, &stii[i], i ); + + if ( rc != MBG_SUCCESS ) + break; + } + + return rc; + +} // mbgextio_get_all_str_type_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_irig_rx_info( MBG_MSG_CTL *pmctl, IRIG_INFO *p ) +/** + * @brief Read current settings and capabilities of a specific serial port + * + * The number of serial ports provided by the device is specified in ::RECEIVER_INFO::n_com_ports. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::RECEIVER_INFO::n_com_ports - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_all_port_info + * @see ::mbgextio_set_port_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_port_info_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PORT_INFO_IDX *p, uint16_t idx ) { - int rc = mbgextio_req_data( pmctl, GPS_IRIG_RX_INFO ); + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_PORT_INFO_IDX, idx ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.irig_rx_info; + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.port_info_idx; + _mbg_swab_port_info_idx( p ); + + #if 0 //##++ TODO: check if received idx matches requested idx + if ( pii.idx != i ) + { + printf( "** Info for port %i requested, but for %i received.\n", + pii.idx, i ); + rc = ...; + } + #endif + } return rc; -} // mbgextio_get_irig_rx_info +} // mbgextio_get_port_info_idx /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_irig_rx_settings( MBG_MSG_CTL *pmctl, const IRIG_SETTINGS *p ) +/** + * @brief Read an array of current settings and capabilities of all serial ports + * + * The number of serial ports provided by the device is specified in ::RECEIVER_INFO::n_com_ports. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] pii An array which can hold at least ::RECEIVER_INFO::n_com_ports entries + * @param[in] p_ri Pointer to a valid ::RECEIVER_INFO structure + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_port_info_idx + * @see ::mbgextio_set_port_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_all_port_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PORT_INFO_IDX pii[], + const RECEIVER_INFO *p_ri ) { - GPS_CMD cmd = GPS_IRIG_RX_SETTINGS; + int rc = MBG_SUCCESS; + uint16_t i; -#if _MBGEXTIO_DIRECT_RC + for ( i = 0; i < p_ri->n_com_ports; i++ ) + { + rc = mbgextio_get_port_info_idx( pmctl, p_addr, &pii[i], i ); - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + if ( rc != MBG_SUCCESS ) + break; + } -#else - int rc; + return rc; + +} // mbgextio_get_all_port_info + + + +/*HDR*/ +/** + * @brief Send configuration settings for a specific serial port + * + * The number of serial ports provided by the device is specified in ::RECEIVER_INFO::n_com_ports. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * @param[in] idx Index of the serial port to be configured, 0..::RECEIVER_INFO::n_com_ports - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_port_info_idx + * @see ::mbgextio_get_all_port_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_port_settings_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const PORT_SETTINGS *p, uint16_t idx ) +{ + GPS_CMD cmd = GPS_PORT_SETTINGS_IDX | OPT_GPS_ACK_CODE; + PORT_SETTINGS_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.port_settings_idx; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); + p_data->port_settings = *p; + p_data->idx = idx; + _mbg_swab_port_settings_idx( p_data ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_port_settings_idx + + + +/*HDR*/ +/** + * @brief Read current settings and capabilities of a specific programmable pulse output + * + * The number of supported pulse outputs is specified in ::RECEIVER_INFO::n_prg_out. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::RECEIVER_INFO::n_prg_out - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_all_pout_info + * @see ::mbgextio_set_pout_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_pout_info_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, POUT_INFO_IDX *p, uint16_t idx ) +{ + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_POUT_INFO_IDX, idx ); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.pout_info_idx; + _mbg_swab_pout_info_idx_on_get( p ); + + #if 0 //##++ TODO: check if received idx matches requested idx + if ( pii.idx != i ) + { + printf( "** Info for port %i requested, but for %i received.\n", + pii.idx, i ); + rc = ...; + } + #endif } - return 0; + return rc; -#endif +} // mbgextio_get_pout_info_idx -} // mbgextio_set_irig_rx_settings + + +/*HDR*/ +/** + * @brief Read an array of current settings and capabilities of all programmable pulse outputs + * + * The number of supported pulse outputs is specified in ::RECEIVER_INFO::n_prg_out. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] pii An array which can hold at least ::RECEIVER_INFO::n_prg_out entries + * @param[in] p_ri Pointer to a valid ::RECEIVER_INFO structure + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_pout_info_idx + * @see ::mbgextio_set_pout_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_all_pout_info( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr, + POUT_INFO_IDX *pii, const RECEIVER_INFO *p_ri ) +{ + int rc = MBG_SUCCESS; + uint16_t i; + + memset( pii, 0, p_ri->n_prg_out * sizeof( *pii ) ); + + for ( i = 0; i < p_ri->n_prg_out; i++ ) + { + rc = mbgextio_get_pout_info_idx( pmctl, p_addr, &pii[i], i ); + + if ( rc != MBG_SUCCESS ) + break; + } + + return rc; + +} // mbgextio_get_all_pout_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_ref_offs( MBG_MSG_CTL *pmctl, MBG_REF_OFFS *p ) +/** + * @brief Send configuration settings for a specific programmable pulse output + * + * The number of supported pulse outputs is specified in ::RECEIVER_INFO::n_prg_out. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * @param[in] idx Index of the pulse output to be configured, 0..RECEIVER_INFO::n_prg_out - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_pout_info_idx + * @see ::mbgextio_get_all_pout_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_pout_settings_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const POUT_SETTINGS *p, uint16_t idx ) { - int rc = mbgextio_req_data( pmctl, GPS_REF_OFFS ); + GPS_CMD cmd = GPS_POUT_SETTINGS_IDX | OPT_GPS_ACK_CODE; + POUT_SETTINGS_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.pout_settings_idx; - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.ref_offs; + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + p_data->pout_settings = *p; + p_data->idx = idx; + _mbg_swab_pout_settings_idx_on_set( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_pout_settings_idx + + + +/*HDR*/ +/** + * @brief Clear the user capture event buffer on-board the device + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ucap + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_clr_ucap_buff( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr ) +{ + return mbgextio_xmt_cmd( pmctl, p_addr, GPS_CLR_UCAP_BUFF ); + +} // mbgextio_clr_ucap_buff + + + +/*HDR*/ +/** + * @brief Read time scale configuration parameters + * + * @note Some devices may not support a configurable time scale + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_time_scale_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_time_scale_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_TIME_SCALE_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_TIME_SCALE ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.time_scale_info; + _mbg_swab_mbg_time_scale_info( p ); + } return rc; -} // mbgextio_get_ref_offs +} // mbgextio_get_time_scale_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_ref_offs( MBG_MSG_CTL *pmctl, const MBG_REF_OFFS *p ) +/** + * @brief Send new time scale configuration settings + * + * @note Some devices may not support a configurable time scale + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_time_scale_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_time_scale_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_TIME_SCALE_SETTINGS *p ) { - GPS_CMD cmd = GPS_REF_OFFS; + GPS_CMD cmd = GPS_TIME_SCALE | OPT_GPS_ACK_CODE; + MBG_TIME_SCALE_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.time_scale_settings; -#if _MBGEXTIO_DIRECT_RC + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_mbg_time_scale_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_time_scale_settings + + + +/*HDR*/ +/** + * @brief Clear the on-board event log + * + * @note Some devices don't provide an on-board event log + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_num_evt_log_entries + * @see ::mbgextio_get_first_evt_log_entry + * @see ::mbgextio_get_next_evt_log_entry + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_clr_evt_log( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr ) +{ + return mbgextio_xmt_cmd( pmctl, p_addr, GPS_CLR_EVT_LOG ); + +} // mbgextio_clr_evt_log - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); -#else - int rc; - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); +/*HDR*/ +/** + * @brief Read the current number of entries in the on-board event log + * + * @note Some devices don't provide an on-board event log + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_clr_evt_log + * @see ::mbgextio_get_first_evt_log_entry + * @see ::mbgextio_get_next_evt_log_entry + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_num_evt_log_entries( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_NUM_EVT_LOG_ENTRIES *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NUM_EVT_LOG_ENTRIES ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.num_evt_log_entries; + _mbg_swab_mbg_num_evt_log_entries( p ); } - return 0; + return rc; -#endif +} // mbgextio_get_num_evt_log_entries -} // mbgextio_set_ref_offs + + +/*HDR*/ +/** + * @brief Return the first entry from the on-board event log + * + * This resets an internal counter, so subsequent calls to + * ::mbgextio_get_next_evt_log_entry will retrieve the following entries. + * + * @note Some devices don't provide an on-board event log + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_clr_evt_log + * @see ::mbgextio_get_num_evt_log_entries + * @see ::mbgextio_get_next_evt_log_entry + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_first_evt_log_entry( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_EVT_LOG_ENTRY *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_FIRST_EVT_LOG_ENTRY ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.evt_log_entry; + _mbg_swab_mbg_evt_log_entry( p ); + } + + return rc; + +} // mbgextio_get_first_evt_log_entry /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_debug_status( MBG_MSG_CTL *pmctl, MBG_DEBUG_STATUS *p ) +/** + * @brief Return the next entry from the on-board event log + * + * This increments an internal counter, so subsequent calls will + * return subsequent entries. ::mbgextio_get_first_evt_log_entry + * should be called first to reset the counter and retrieve the + * oldest log entry. + * + * @note Some devices don't provide an on-board event log + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_clr_evt_log + * @see ::mbgextio_get_num_evt_log_entries + * @see ::mbgextio_get_first_evt_log_entry + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_next_evt_log_entry( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_EVT_LOG_ENTRY *p ) { - int rc = mbgextio_req_data( pmctl, GPS_DEBUG_STATUS ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NEXT_EVT_LOG_ENTRY ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.debug_status; + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.evt_log_entry; + _mbg_swab_mbg_evt_log_entry( p ); + } return rc; -} // mbgextio_get_debug_status +} // mbgextio_get_next_evt_log_entry /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_opt_info( MBG_MSG_CTL *pmctl, MBG_OPT_INFO *p ) +/** + * @brief Read the current IMS state and supported IMS features + * + * @note This is only supported if ::GPS_HAS_IMS is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ims_sensor_state_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ims_state( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_IMS_STATE *p ) { - int rc = mbgextio_req_data( pmctl, GPS_OPT_INFO ); + int rc = mbgextio_req_data( pmctl, p_addr, GPS_IMS_STATE ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.opt_info; + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ims_state; + _mbg_swab_mbg_ims_state( p ); + } return rc; -} // mbgextio_get_opt_info +} // mbgextio_get_ims_state /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_opt_settings( MBG_MSG_CTL *pmctl, const MBG_OPT_SETTINGS *p ) +/** + * @brief Read sensor values from a specified sensor on the device + * + * Info on supported sensors can be retrieved using ::mbgextio_get_ims_state. + * Valid range for the sensor index is [0..::MBG_IMS_STATE::num_sensors - 1]. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx The index of the array element to read + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ims_state + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ims_sensor_state_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_IMS_SENSOR_STATE_IDX *p, uint16_t idx ) { - GPS_CMD cmd = GPS_OPT_SETTINGS; + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_IMS_SENSOR_STATE_IDX, idx ); -#if _MBGEXTIO_DIRECT_RC + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ims_sensor_state_idx; + _mbg_swab_mbg_ims_sensor_state_idx( p ); + } - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + return rc; -#else - int rc; +} // mbgextio_get_ims_sensor_state_idx - rc = mbgextio_xmt_msg( pmctl, (uint16_t) ( cmd | GPS_ACK ), (const uint8_t *) p, sizeof( *p ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + +/*HDR*/ +/** + * @brief Set the XMR holdover interval + * + * @todo In which case is this supported? + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_holdover_interval_counter + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_holdover_interval( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const XMR_HOLDOVER_INTV *p ) +{ + GPS_CMD cmd = GPS_XMR_HOLDOVER_INTV | OPT_GPS_ACK_CODE; + XMR_HOLDOVER_INTV *p_data = &pmctl->xmt.pmb->u.msg_data.xmr_holdover_intv; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_xmr_holdover_intv( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_holdover_interval + + + +/*HDR*/ +/** + * @brief Read the XMR holdover interval counter + * + * @todo In which case is this supported? + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_holdover_interval + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_holdover_interval_counter( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XMR_HOLDOVER_INTV *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_XMR_HOLDOVER_INTV ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.xmr_holdover_intv; + _mbg_swab_xmr_holdover_intv( p ); + } + + return rc; + +} // mbgextio_get_holdover_interval_counter + + + +/*HDR*/ +/** + * @brief Read the local time conversion configuration in ::TZCODE format + * + * @note Some devices may not support ::TZCODE configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_tzcode + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_tzcode( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, TZCODE *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, PZF_TZCODE ); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.tzcode; + _mbg_swab_tzcode( p ); } - return 0; + return rc; + +} // mbgextio_get_tzcode + + + +/*HDR*/ +/** + * @brief Set the local time conversion configuration in ::TZCODE format + * + * @note Some devices may not support ::TZCODE configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_tzcode + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_tzcode( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const TZCODE *p ) +{ + GPS_CMD cmd = PZF_TZCODE | OPT_GPS_ACK_CODE; + TZCODE *p_data = &pmctl->xmt.pmb->u.msg_data.tzcode; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_tzcode( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); +} // mbgextio_set_tzcode + + + +/*HDR*/ +/** + * @brief Send new configuration settings for the device's HAVEQUICK output + * + * @note This is only supported if ::GPS_HAS_HAVEQUICK is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_hq_tx_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, HAVEQUICK_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_HAVEQUICK_TX_SETTINGS | OPT_GPS_ACK_CODE; + HAVEQUICK_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.havequick_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_havequick_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_hq_tx_settings + + + +/*HDR*/ +/** + * @brief Send new configuration settings for the device's HAVEQUICK input + * + * @note This is only supported if ::MULTI_REF_HAVEQUICK is > 0 in ::XMULTI_REF_INSTANCES::n_inst //##+++++++++++++++++++++++ ??? + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_hq_rx_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, HAVEQUICK_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_HAVEQUICK_RX_SETTINGS | OPT_GPS_ACK_CODE; + HAVEQUICK_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.havequick_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_havequick_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_hq_rx_settings + + + +/*HDR*/ +/** + * @brief Read the distance from transmitter ::TR_DISTANCE format + * + * @note Some devices may not support ::TR_DISTANCE configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_tr_distance + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_tr_distance( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, TR_DISTANCE *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, PZF_TR_DISTANCE ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.tr_distance; + _mbg_swab_tr_distance( p ); + } + + return rc; + +} // mbgextio_get_tr_distance + + + +/*HDR*/ +/** + * @brief Set the transmitter distance (km) in ::TR_DISTANCE format + * + * @note Some devices may not support ::TR_DISTANCE configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_tr_distance + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_tr_distance( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const TR_DISTANCE *p ) +{ + GPS_CMD cmd = PZF_TR_DISTANCE | OPT_GPS_ACK_CODE; + TR_DISTANCE *p_data = &pmctl->xmt.pmb->u.msg_data.tr_distance; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_tr_distance( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_tr_distance + + + +/*HDR*/ +/** + * @brief Read current GNSS mode settings and supported features + * + * @note Some devices may not support GNSS configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to a ::MBG_GNSS_MODE_INFO structure to be filled up + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_gnss_mode_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_gnss_mode_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_GNSS_MODE_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_GNSS_MODE ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.gnss_mode_info; + _mbg_swab_dummy( p ); + } + + return rc; + +} // mbgextio_get_gnss_mode_info + + + +/*HDR*/ +/** + * @brief Write GNSS mode settings + * + * @note Some devices may not support GNSS configuration + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[in] p Pointer to a ::MBG_GNSS_MODE_SETTINGS structure to be sent + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_gnss_mode_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_gnss_mode_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_GNSS_MODE_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_GNSS_MODE | OPT_GPS_ACK_CODE; + MBG_GNSS_MODE_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.gnss_mode_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_dummy( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_gnss_mode_settings + + + +/*HDR*/ +/** + * @brief Read an array of all supported string types //##+++++++++++++++++++++++++++++++++++++++++++++++ TODO + * + * The number of supported string types is specified in ::RECEIVER_INFO::n_str_type. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] stii An array which can hold at least ::RECEIVER_INFO::n_str_type entries + * @param[in] p_ri Pointer to a valid ::RECEIVER_INFO structure + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_str_type_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_all_gnss_sat_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, STR_TYPE_INFO_IDX stii[], + const RECEIVER_INFO *p_ri ) +{ + int rc = MBG_SUCCESS; + uint16_t i; + + for ( i = 0; i < p_ri->n_str_type; i++ ) + { + rc = mbgextio_get_str_type_info_idx( pmctl, p_addr, &stii[i], i ); + + if ( rc != MBG_SUCCESS ) + break; + } + + return rc; + +} // mbgextio_get_all_gnss_sat_info + + + +/*HDR*/ +/** + * @brief Fill up a GNSS info structure //##+++++++++++++++++++++++++++++++++++++++++++++++ TODO + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[out] p_si Pointer to a ::STAT_INFO the data structure to return the received data + * @param[out] p_gmi Index of the NTP peer state to be configured, 0..::NTP_CLNT_MODE_INFO::n_supp_peers - 1 + * @param[out] p_gsii Blah ... //##++++++++++ TODO + * @param[in] p_ri Pointer to a valid ::RECEIVER_INFO structure + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_sys_state + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_get_ntp_clnt_mode_info + */ +int mbgextio_chk_get_gnss_info( MBG_MSG_CTL *pmctl, STAT_INFO *p_si, + MBG_GNSS_MODE_INFO *p_gmi, GNSS_SAT_INFO_IDX *p_gsii, + const RECEIVER_INFO *p_ri ) +{ + int n_gnss_supp = 0; + int rc = -1; +#if 0 + int i = -1; + +#if 0 //##++++++++++++++ + if ( !_pcps_has_stat_info( p_dev ) ) + return 0; #endif -} // mbgextio_set_opt_settings + rc = mbgextio_get_stat_info( pmctl, p_si ); + + if ( rc < 0 ) + goto fail; + + +//##+++++++++++++ if ( _pcps_is_gnss( p_dev ) ) + { + if ( ( rc = mbgextio_get_gnss_mode_info( pmctl, p_gmi ) ) < 0 ) + goto fail; + +// mbgextio_get_all_gnss_sat_info + for ( i = 0; i < p_gmi-> ; i++ ) + rc = mbgextio_get_all_gnss_sat_info( pmctl, p_gsii, p_gmi ); + + if ( rc < 0 ) + goto fail; + } +//##++++ else + { +//##++++ if ( _pcps_has_stat_info_svs( p_dev ) ) // GPS is supported + { + GNSS_SAT_INFO *p_gsi; + + // setup GNSS info from stat_info so we can use the same printing routine + p_gmi->supp_gnss_types = MBG_GNSS_TYPE_MSK_GPS; + p_gmi->settings.gnss_set = p_gmi->supp_gnss_types; + + p_gsi = &p_gsii->gnss_sat_info; + p_gsi->gnss_type = GNSS_TYPE_GPS; + p_gsi->svs_in_view = p_si->svs_in_view; + p_gsi->good_svs = p_si->good_svs; + } + } + + n_gnss_supp = num_bits_set( p_gmi->supp_gnss_types ); + + if ( n_gnss_supp > N_GNSS_TYPES ) + { + //##++++ show warning: device supports more GNSS types than this program + n_gnss_supp = N_GNSS_TYPES; + } + + return n_gnss_supp; + + +fail: +#endif + return rc; + +} // mbg_chk_get_gnss_info /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_str_type_info_idx( MBG_MSG_CTL *pmctl, - STR_TYPE_INFO_IDX *p, uint16_t idx ) +/** + * @brief Read the supported XMR features in ::XMULTI_REF_INFO_IDX format + * + * Only if ::GPS_HAS_XMULTI_REF is set in ::RECEIVER_INFO::features. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_xmr_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_xmr_info_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XMULTI_REF_INFO_IDX *p ) { - int rc; + uint16_t idx = p->idx; + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_XMR_INFO_IDX, idx ); - xmt_cmd_us( pmctl, GPS_STR_TYPE_INFO_IDX, idx ); + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.xmulti_ref_info_idx; + _mbg_swab_dummy( p ); //##++++++ _mbg_xmr_info_idx( p ); + } + + return rc; - rc = mbgextio_rcv_msg( pmctl, GPS_STR_TYPE_INFO_IDX ); +} // mbgextio_get_xmr_info_idx - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.str_type_info_idx; + + +/*HDR*/ +/** + * @brief Read the supported XMR features ::XMULTI_REF_STATUS_IDX format + * + * @note Only for devices which supports Multi references + * @note GPS_HAS_XMULTI_REF feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_xmr_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_xmr_status_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XMULTI_REF_STATUS_IDX *p ) +{ + uint16_t idx = p->idx; + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_XMR_STATUS_IDX, idx ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.xmulti_ref_status_idx; + //##++++++ _mbg_xmr_status_idx( p ); + } return rc; -} // mbgextio_get_str_type_info_idx +} // mbgextio_get_xmr_status_idx + + + +/*HDR*/ +/** + * @brief Save the supported XMR features ::XMULTI_REF_INFO_IDX format + * + * @note Some devices may not support Multi Refernce Sources + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_xmr_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const XMULTI_REF_SETTINGS_IDX *p ) +{ + GPS_CMD cmd = GPS_XMR_SETTINGS_IDX | OPT_GPS_ACK_CODE; + XMULTI_REF_SETTINGS_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.xmulti_ref_settings_idx; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + //##++++++ _mbg_swab_xmr_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_xmr_settings /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_port_info_idx( MBG_MSG_CTL *pmctl, - PORT_INFO_IDX *p, uint16_t idx ) +/** + * @brief Read the lan interface configuration ::LAN_IF_INFO format + * + * @note ptp or xmr with ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_lan_if_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, LAN_IF_INFO *p ) { int rc; + rc = mbgextio_req_data( pmctl, p_addr, GPS_LAN_IF_INFO ); - xmt_cmd_us( pmctl, GPS_PORT_INFO_IDX, idx ); + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.lan_if_info; + //##++++++ _mbg_xmr_status_idx( p ); + } + + return rc; - rc = mbgextio_rcv_msg( pmctl, GPS_PORT_INFO_IDX ); +} // mbgextio_get_lan_if_info - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.port_info_idx; + + +/*HDR*/ +/** + * @brief Read the lan ipv4 configuration state ::IP4_SETTINGS format + * + * @note ptp or xmr with ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ip4_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, IP4_SETTINGS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_IP4_SETTINGS ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ip4_settings; + //##++++++ _mbg_xmr_status_idx( p ); + } return rc; -} // mbgextio_get_port_info_idx +} // mbgextio_get_ip4_settings /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_port_settings_idx( MBG_MSG_CTL *pmctl, - const PORT_SETTINGS *p, uint16_t idx ) +/** + * @brief Save the current ipv4 settings ::IP4_SETTINGS format + * + * @note ptp or xmr with ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ip4_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const IP4_SETTINGS *p ) { - GPS_CMD cmd = GPS_PORT_SETTINGS_IDX; - MBG_MSG_BUFF *pmb = pmctl->xmt.pmb; - int rc; + GPS_CMD cmd = GPS_IP4_SETTINGS | OPT_GPS_ACK_CODE; + IP4_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.ip4_settings; - pmb->u.msg_data.port_settings_idx.port_settings = *p; - pmb->u.msg_data.port_settings_idx.idx = idx; + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + //##++++++ _mbg_swab_xmr_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ip4_settings - pmb->hdr.len = sizeof( pmb->u.msg_data.port_settings_idx ); - pmb->hdr.cmd = cmd | GPS_REQACK; - rc = xmt_tbuff( pmctl ); -#if _MBGEXTIO_DIRECT_RC + +/*HDR*/ +/** + * @brief Read the current lan ipv4 state ::IP4_SETTINGS format + * + * @note ptp or xmr with ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ip4_state( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, IP4_SETTINGS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_IP4_STATE ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ip4_settings; + //##++++++ _mbg_xmr_status_idx( p ); + } return rc; -#else +} // mbgextio_get_ip4_state - rc = mbgextio_rcv_msg( pmctl, cmd ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + +/*HDR*/ +/** + * @brief Read the current state of PTP device ::PTP_STATE format + * + * @note ptp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ptp_state( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PTP_STATE *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_PTP_STATE); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.ptp_state; } - return 0; + return rc; -#endif +} // mbgextio_get_ptp_state -} // mbgextio_set_port_settings_idx + + +/*HDR*/ +/** + * @brief Read the ptp configuration ::PTP_CFG_INFO format + * + * @note ptp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ptp_cfg_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PTP_CFG_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_PTP_CFG ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ptp_cfg_info; + } + + return rc; + +} // mbgextio_get_ptp_cfg_info + + + +/*HDR*/ +/** + * @brief Read the ptp configuration ::PTP_UC_MASTER_CFG_LIMITS format + * + * @note ptp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ptp_uc_master_cfg_limits( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PTP_UC_MASTER_CFG_LIMITS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_PTP_UC_MASTER_CFG_LIMITS ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ptp_uc_master_cfg_limits; + } + + return rc; + +} // mbgextio_get_ptp_uc_master_cfg_limits + + + +/*HDR*/ +/** + * @brief Read the ptp configuration ::ALL_PTP_UC_MASTER_INFO_IDX format + * + * @note ptp feature must set and read number of ptp unicast masters before. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] ptp_uc_master_info_idx Pointer to an array of ::PTP_UC_MASTER_INFO_IDX structures to be filled up + * @param[in] ptp_uc_master_cfg_limits Pointer to a ::PTP_UC_MASTER_CFG_LIMITS structure read before + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see //##+++++++++++++ TODO + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_all_ptp_uc_master_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, PTP_UC_MASTER_INFO_IDX *ptp_uc_master_info_idx, + const PTP_UC_MASTER_CFG_LIMITS *ptp_uc_master_cfg_limits ) +{ + int rc= MBG_SUCCESS; + uint16_t i; + + for ( i = 0; i < ptp_uc_master_cfg_limits->n_supp_master; i++ ) + { + xmt_cmd_us( pmctl, p_addr, GPS_PTP_UC_MASTER_CFG, i ); + + rc = mbgextio_rcv_msg( pmctl, p_addr, GPS_PTP_UC_MASTER_CFG ); //##+++++++++++++++++++++++++ + + if ( ( rc == MBG_SUCCESS ) && ptp_uc_master_info_idx ) + { + PTP_UC_MASTER_INFO_IDX *p = &ptp_uc_master_info_idx[i]; + *p = pmctl->rcv.pmb->u.msg_data.ptp_uc_master_info_idx; + _mbg_swab_dummy( p ); + } + else + break; + } + + return rc; + +} // mbgextio_get_all_ptp_uc_master_info + + + +/*HDR*/ +/** + * @brief Send configuration settings for PTP + * + * @note This is only supported by PTP devices + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ptp_cfg_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ptp_cfg_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const PTP_CFG_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_PTP_CFG | OPT_GPS_ACK_CODE; + PTP_CFG_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.ptp_cfg_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_ptp_cfg_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ptp_cfg_settings + + + +/*HDR*/ +/** + * @brief Read the ntp global information ::NTP_GLB_INFO format + * + * @note ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_clnt_mode_info + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_set_ntp_peer_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ntp_glb_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, NTP_GLB_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NTP_GLB_CFG ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ntp_glb_info; + } + + return rc; + +} // mbgextio_get_ntp_glb_info + + + +/*HDR*/ +/** + * @brief Send the NTP global configuration + * + * @note This is only supported by NTP devices + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ant_cable_len + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ntp_glb_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const NTP_GLB_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_NTP_GLB_CFG | OPT_GPS_ACK_CODE; + NTP_GLB_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.ntp_glb_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_ntp_glb_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ntp_glb_info + + + +/*HDR*/ +/** + * @brief Read the ntp global information ::NTP_CLNT_MODE_INFO format + * + * @note ntp feature must set + * @note ntp client mode must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_glb_info + * @see ::mbgextio_set_ntp_clnt_mode_cfg + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_set_ntp_peer_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ntp_clnt_mode_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, NTP_CLNT_MODE_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NTP_CLNT_MODE_CFG ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ntp_clnt_mode_info; + } + + return rc; + +} // mbgextio_get_ntp_clnt_mode_info + + + +/*HDR*/ +/** + * @brief Send the NTP client mode configuration + * + * @note This is only supported by NTP devices + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_glb_info + * @see ::mbgextio_get_ntp_clnt_mode_info + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_set_ntp_peer_settings_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ntp_clnt_mode_cfg( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const NTP_CLNT_MODE_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_NTP_CLNT_MODE_CFG | OPT_GPS_ACK_CODE; + NTP_CLNT_MODE_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.ntp_clnt_mode_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_ntp_clnt_mode_settings( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ntp_clnt_mode_cfg /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_pout_info_idx( MBG_MSG_CTL *pmctl, - POUT_INFO_IDX *p, uint16_t idx ) +/** + * @brief Read the ntp peer settings of current idx ::NTP_PEER_SETTINGS format + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the NTP peer settings to be configured, 0 ... NTP_CLNT_MODE_INFO::n_supp_peers - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_glb_info + * @see ::mbgextio_set_ntp_peer_settings_idx + * @see ::mbgextio_get_ntp_clnt_mode_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ntp_peer_settings_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, NTP_PEER_SETTINGS_IDX *p, uint16_t idx ) { int rc; + rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NTP_PEER_SETTINGS_IDX, idx ); - xmt_cmd_us( pmctl, GPS_POUT_INFO_IDX, idx ); + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ntp_peer_settings_idx; + } - rc = mbgextio_rcv_msg( pmctl, GPS_POUT_INFO_IDX ); + return rc; - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.pout_info_idx; +} // mbgextio_get_ntp_peer_settings_idx + + + +/*HDR*/ +/** + * @brief Send configuration settings for a specific NTP peer + * + * The number of supported NTP peers is specified in ::NTP_CLNT_MODE_INFO::n_supp_peers -1. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * @param[in] idx Index of the NTP peer to be configured, 0 ... NTP_CLNT_MODE_INFO::n_supp_peers - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_glb_info + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_get_ntp_clnt_mode_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_ntp_peer_settings_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const NTP_PEER_SETTINGS *p, uint16_t idx ) +{ + GPS_CMD cmd = GPS_NTP_PEER_SETTINGS_IDX | OPT_GPS_ACK_CODE; + NTP_PEER_SETTINGS_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.ntp_peer_settings_idx; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + p_data->peer_settings = *p; + p_data->idx = idx; + _mbg_swab_ntp_peer_settings_idx( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_ntp_peer_settings_idx + + + +/*HDR*/ +/** + * @brief Read the current system state of ntp device ::NTP_SYS_STATE format + * + * @note ntp feature must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_ntp_clnt_mode_cfg + * @see ::mbgextio_get_ntp_glb_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ntp_sys_state( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, NTP_SYS_STATE *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NTP_SYS_STATE); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ntp_sys_state; + //##++++++ _mbg_xmr_status_idx( p ); + } return rc; -} // mbgextio_get_pout_info_idx +} // mbgextio_get_ntp_sys_state /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_pout_settings_idx( MBG_MSG_CTL *pmctl, - const POUT_SETTINGS *p, uint16_t idx ) +/** + * @brief Read the NTP peer state of current idx ::NTP_PEER_STATE format + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the NTP peer state to be configured, 0 ... NTP_CLNT_MODE_INFO::n_supp_peers - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_ntp_sys_state + * @see ::mbgextio_get_ntp_peer_settings_idx + * @see ::mbgextio_get_ntp_clnt_mode_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_ntp_peer_state_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, NTP_PEER_STATE_IDX *p, uint16_t idx ) { - GPS_CMD cmd = GPS_POUT_SETTINGS_IDX; - MBG_MSG_BUFF *pmb = pmctl->xmt.pmb; int rc; + rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NTP_PEER_STATE_IDX, idx ); - pmb->u.msg_data.pout_settings_idx.pout_settings = *p; - pmb->u.msg_data.pout_settings_idx.idx = idx; + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ntp_peer_state_idx; + } - pmb->hdr.len = sizeof( pmb->u.msg_data.pout_settings_idx ); - pmb->hdr.cmd = cmd | GPS_REQACK; - rc = xmt_tbuff( pmctl ); + return rc; + +} // mbgextio_get_ntp_peer_state_idx + + +/*HDR*/ +/** + * @brief Read the network global config information ::MBG_NET_GLB_CFG_INFO format + * + * @note GPS_HAS_NET_CFG must set + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_net_glb_cfg_settings + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_net_glb_cfg_info( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_NET_GLB_CFG_INFO *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_NET_GLB_CFG); -#if _MBGEXTIO_DIRECT_RC + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.net_glb_cfg_info; + _mbg_swab_dummy( p ); + } return rc; -#else +} // mbgextio_get_net_glb_cfg_info + +/*HDR*/ +/** + * @brief Set the device's global network configuration settings + * + * @note The function is not supported by all devices. //##++++++++++++ + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_net_glb_cfg_info + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_net_glb_cfg_settings( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_NET_GLB_CFG_SETTINGS *p ) +{ + GPS_CMD cmd = GPS_NET_GLB_CFG | OPT_GPS_ACK_CODE; + MBG_NET_GLB_CFG_SETTINGS *p_data = &pmctl->xmt.pmb->u.msg_data.net_glb_cfg_settings; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_dummy( p ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); - rc = mbgextio_rcv_msg( pmctl, cmd ); +} // mbgextio_set_net_glb_cfg_settings - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + + +/*HDR*/ +/** + * @brief Read the network dns server ::MBG_IP_ADDR_IDX format + * + * The number of DNS server provided by the device is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srvr + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srvr - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_net_dns_srvr_idx + * @see ::mbgextio_get_net_stat_dns_srvr_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_net_dns_srvr_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_IP_ADDR_IDX *p, uint16_t idx ) +{ + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NET_DNS_SRVR, idx ); + + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.ip_addr_idx; + _mbg_swab_dummy( p ); } - return 0; + return rc; -#endif +} // mbgextio_get_net_dns_srvr_idx -} // mbgextio_set_pout_settings_idx + + +/*HDR*/ +/** + * @brief Send the network DNS server address in ::MBG_IP_ADDR format + * + * The number of DNS search domains supported by the device + * is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * @param[in] idx Index of the serial port to be configured, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srvr - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_net_dns_srvr_idx + * @see ::mbgextio_get_net_stat_dns_srvr_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_net_dns_srvr_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_IP_ADDR *p, uint16_t idx ) +{ + GPS_CMD cmd = GPS_NET_DNS_SRVR | OPT_GPS_ACK_CODE; + MBG_IP_ADDR_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.ip_addr_idx; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + p_data->addr = *p; + p_data->idx = idx; + _mbg_swab_dummy( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, p_data, sizeof( *p_data ) ); + +} // mbgextio_set_net_dns_srvr_idx /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_clr_ucap_buff( MBG_MSG_CTL *pmctl ) +/** + * @brief Read the network DNS search domain in ::MBG_NET_NAME_IDX format + * + * The number of DNS search domains supported by the device + * is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_net_dns_srch_dom_idx + * @see ::mbgextio_get_net_stat_dns_srch_dom_stat_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_net_dns_srch_dom_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_NET_NAME_IDX *p, uint16_t idx ) { - return mbgextio_xmt_cmd( pmctl, GPS_CLR_UCAP_BUFF ); + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NET_DNS_SRCH_DOM, idx ); -} // mbgextio_clr_ucap_buff + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.net_name_idx; + _mbg_swab_dummy( p ); + } + + return rc; + +} // mbgextio_get_net_dns_srch_dom_idx /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_get_time_scale_info( MBG_MSG_CTL *pmctl, - MBG_TIME_SCALE_INFO *p ) +/** + * @brief Send the network DNS search domain in ::MBG_NET_NAME format + * + * The number of DNS search domains supported by the device + * is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * @param[in] idx Index of the serial port to be configured, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_net_dns_srch_dom_idx + * @see ::mbgextio_get_net_stat_dns_srch_dom_stat_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_net_dns_srch_dom_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, const MBG_NET_NAME *p, uint16_t idx ) { - int rc; + GPS_CMD cmd = GPS_NET_DNS_SRCH_DOM | OPT_GPS_ACK_CODE; + MBG_NET_NAME_IDX *p_data = &pmctl->xmt.pmb->u.msg_data.net_name_idx; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif - xmt_cmd( pmctl, GPS_TIME_SCALE ); + p_data->net_name = *p; + p_data->idx = idx; + _mbg_swab_dummy( p_data ); - rc = mbgextio_rcv_msg( pmctl, GPS_TIME_SCALE ); + return mbgextio_xmt_msg( pmctl, p_addr, cmd, p_data, sizeof( *p_data ) ); - if ( ( rc == TR_COMPLETE ) && p ) - *p = pmctl->rcv.pmb->u.msg_data.time_scale_info; +} // mbgextio_set_net_dns_srch_dom_idx + + + +/*HDR*/ +/** + * @brief Read the current network DNS server in ::MBG_IP_ADDR_IDX format + * + * The number of DNS servers supported by the device + * is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srvr + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srvr - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_net_dns_srvr_idx + * @see ::mbgextio_set_net_dns_srvr_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_net_stat_dns_srvr_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_IP_ADDR_IDX *p, uint16_t idx ) +{ + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NET_STAT_DNS_SRVR, idx ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.ip_addr_idx; + _mbg_swab_dummy( p ); + } return rc; -} // mbgextio_get_time_scale_info +} // mbgextio_get_net_stat_dns_srvr_idx /*HDR*/ -_MBG_API_ATTR int _MBG_API mbgextio_set_time_scale_settings( MBG_MSG_CTL *pmctl, - const MBG_TIME_SCALE_SETTINGS *p ) +/** + * @brief Read the current network DNS search domain in ::MBG_NET_NAME_IDX format + * + * The number of DNS search domains supported by the device + * is specified in ::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::MBG_NET_GLB_CFG_INFO::num_dns_srch_dom - 1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_net_dns_srch_dom_idx + * @see ::mbgextio_get_net_dns_srch_dom_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_net_stat_dns_srch_dom_stat_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, MBG_NET_NAME_IDX *p, uint16_t idx ) { - GPS_CMD cmd = GPS_TIME_SCALE; + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_NET_STAT_DNS_SRCH_DOM, idx ); -#if _MBGEXTIO_DIRECT_RC + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.net_name_idx; + _mbg_swab_dummy( p ); + } - return mbgextio_xmt_msg( pmctl, cmd, (const uint8_t *) p, sizeof( *p ) ); + return rc; -#else +} // mbgextio_get_net_stat_dns_srch_dom_stat_idx - MBG_MSG_BUFF *pmb = pmctl->xmt.pmb; - int rc; - pmb->u.msg_data.time_scale_settings = *p; - pmb->hdr.len = sizeof( pmb->u.msg_data.time_scale_settings ); - pmb->hdr.cmd = cmd | GPS_REQACK; - xmt_tbuff( pmctl ); +/*HDR*/ +/** + * @brief Read the ::XBP_LIMITS to check which XBP features are supported + * + * @note Only supported if ::GPS_HAS_XBP is set in ::RECEIVER_INFO::features + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_xbp_node_limits + * @see ::mbgextio_get_xbp_node_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_xbp_limits( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XBP_LIMITS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_XBP_LIMITS ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.xbp_limits; + _mbg_swab_dummy( p ); + } + + return rc; + +} // mbgextio_get_xbp_limits + + +/*HDR*/ +/** + * @brief Read the ::XBP_NODE_LIMITS to check how many ports are provided + * + * @note Only supported if ::XBP_FEAT_MASK_NODES is set in ::XBP_LIMITS::features. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_xbp_limits + * @see ::mbgextio_get_xbp_node_info_idx + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_xbp_node_limits( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XBP_NODE_LIMITS *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_XBP_NODE_LIMITS ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.xbp_node_limits; + _mbg_swab_dummy( p ); + } + + return rc; - rc = mbgextio_rcv_msg( pmctl, cmd ); +} // mbgextio_get_xbp_node_limits + + +/*HDR*/ +/** + * @brief Read the ::XBP_NODE_INFO for a specific node in ::XBP_NODE_INFO_IDX format + * + * The supported number of nodes is provided by ::XBP_NODE_LIMITS::node_count. + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * @param[in] idx Index of the array element to be retrieved, 0..::XBP_NODE_LIMITS::node_count-1 + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_xbp_limits + * @see ::mbgextio_get_xbp_node_limits + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_xbp_node_info_idx( MBG_MSG_CTL *pmctl, + const XBP_ADDR *p_addr, XBP_NODE_INFO_IDX *p, uint16_t idx ) +{ + int rc = mbgextio_req_data_idx( pmctl, p_addr, GPS_XBP_NODE_INFO_IDX, idx ); - if ( ( rc != TR_COMPLETE ) || !( pmctl->rcv.pmb->hdr.cmd & GPS_ACK ) ) + if ( ( rc == MBG_SUCCESS ) && p ) { - return -1; // no ack packet received - // data has not been set or, if GPS_AUTO_ON has been - // transmitted before, an automatic frame (current time, capture) - // has been sent before the acknowledge code. + *p = pmctl->rcv.pmb->u.msg_data.xbp_node_info_idx; + _mbg_swab_dummy( p ); } - return 0; + return rc; -#endif +} // mbgextio_get_xbp_node_info_idx -} // mbgextio_set_time_scale_settings + + +static /*HDR*/ +void handle_xbp_node( XBP_NODE_INFO *p_ni, long idx ) +{ + XBP_ADDR * p_addr = &p_ni->addr; + RECEIVER_INFO *p_ri = &p_ni->ri; + int i; + + printf( "Node info %li:", idx ); + + printf( " addr %02X:", p_addr->hop_count ); + + for ( i = 0; i < MAX_XBP_CASC_LVL; i++ ) + printf( "%02X ", p_addr->addr[i] ); + + printf( ", state: %i", p_ni->state ); + + printf( ", name: \"%s\"", p_ri->model_name ); + + printf( "\n" ); + +} // handle_xbp_node + + + +/*HDR*/ +/** + * @brief Read the ::XBP_NODE_INFO for a specific node in ::XBP_NODE_INFO_IDX format + * //##+++++++++++++++++++++++++ + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_setup_xbp_node_list( MBG_MSG_CTL *pmctl, const XBP_ADDR *p_addr ) +{ + static int recursion_count; + + XBP_NODE_LIMITS node_limits; + int rc = MBG_SUCCESS; + int16_t i; + + // This function can be called recursively, so first of all check the recursion level. + if ( recursion_count > MAX_XBP_CASC_LVL ) //##++++ TODO: or even ">=" ?? + { + // This should never happen, but we check this anyway. + rc = MBG_ERR_XBP_CASC_LVL; + goto out; + } + + recursion_count++; + + rc = mbgextio_get_xbp_node_limits( pmctl, p_addr, &node_limits ); + + if ( rc != MBG_SUCCESS ) + goto out_dec; + + for ( i = 0; i < node_limits.node_count; i++ ) + { + XBP_NODE_INFO_IDX node_info_idx; + + rc = mbgextio_get_xbp_node_info_idx( pmctl, p_addr, &node_info_idx, i ); + + if ( rc != MBG_SUCCESS ) + break; + + #if 1 && DEBUG + if ( (ulong) node_info_idx.idx != (ulong) i ) + printf( "** Warning: received XBP index %li for idx %li in %s\n", + (ulong) node_info_idx.idx, (ulong) i, __func__ ); + #endif + + handle_xbp_node( &node_info_idx.node_info, node_info_idx.idx ); + } + +out_dec: + if ( recursion_count > 0 ) + recursion_count--; + else + { + // recursion count is unexpectedly 0 + } + +out: + return rc; + +} // mbgextio_setup_xbp_node_list + + + +/*HDR*/ +/** + * @brief Read a ::UTC parameter structure from a device. + * + * The ::UTC parameter structure contains the current UTC/GPS time offset + * as well as information on an eventually upcoming leap second. + * + * @note Only supported by GPS/GNSS and some PZF receivers //##+++++++ TODO Associated feature flag? + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to return the received data + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_set_utc_param + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_get_utc_param( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr, UTC *p ) +{ + int rc = mbgextio_req_data( pmctl, p_addr, GPS_UTC ); + + if ( ( rc == MBG_SUCCESS ) && p ) + { + *p = pmctl->rcv.pmb->u.msg_data.utc; + _mbg_swab_utc_parm( p ); + } + + return rc; + +} // mbgextio_get_utc_param + + + +/*HDR*/ +/** + * @brief Write a ::UTC parameter structure to a device. + * + * The ::UTC parameter structure contains the current UTC/GPS time offset + * as well as information on an eventually upcoming leap second. + * + * @note Only supported by GPS/GNSS and some PZF receivers //##+++++++ TODO Associated feature flag? + * + * @param[in,out] pmctl Pointer to a valid message control structure + * @param[in] p_addr Pointer to an XBP address specifier, or NULL + * @param[out] p Pointer to the data structure to be sent to the device + * + * @return One of the @ref MBG_RETURN_CODES + * + * @see ::mbgextio_get_utc_param + */ +_NO_MBG_API_ATTR int _MBG_API mbgextio_set_utc_param( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr, const UTC *p ) +{ + GPS_CMD cmd = GPS_UTC | OPT_GPS_ACK_CODE; + UTC *p_data = &pmctl->xmt.pmb->u.msg_data.utc; + + #if _USE_MUTEX + _mbg_mutex_acquire( &pmctl->xmt.xmt_mutex ); + #endif + + *p_data = *p; + _mbg_swab_utc_parm( p_data ); + + return mbgextio_xmt_msg( pmctl, p_addr, cmd, NULL, sizeof( *p_data ) ); + +} // mbgextio_set_utc_param |