/************************************************************************** * * Example program demonstrating how to configure the serial port * of a Meinberg device using the Meinberg binary protocol. * **************************************************************************/ #include #include // NOTE: Future versions of the API will provide this type, // and the local declaration here will then becomme obsolete, // but for now we already use it in the subsequent code. typedef int16_t MBG_MSG_IDX; // A local array of the names of known string modes that may // be supported by a the serial port of a device. static const char *mode_names[N_STR_MODE] = DEFAULT_ENG_MODE_NAMES; static /*HDR*/ /** * @brief Print the list of time string formats supported by a device. * * The function ::mbgextio_get_serial_settings must have been called before * to read the current settings and configuration options and store them * in a ::RECEIVER_PORT_CFG structure. * * @param[in,out] pmctl Pointer to a valid message control structure. * @param[in] p_rpcfg Pointer to a ::RECEIVER_PORT_CFG structure containing valid data. * * @return One of the @ref MBG_RETURN_CODES */ void print_supp_str_types( MBG_MSG_CTL *pmctl, const RECEIVER_PORT_CFG *p_rpcfg ) { // Set up a pointer to the RECEIVER_INFO structure associated // with the device to access basic device information. const RECEIVER_INFO *p_ri = mbgextio_get_receiver_info_addr( pmctl ); int i; printf( "Supported String Types with Index:\n" ); for ( i = 0; i < p_ri->n_str_type; i++ ) { // Set up a pointer to the STR_TYPE_INFO structure for index i. const STR_TYPE_INFO *p_sti = &p_rpcfg->stii[i].str_type_info; printf( " %2i %s\n", i, p_sti->long_name ); } } // print_supp_str_types static /*HDR*/ /** * @brief Print the serial setting of a specific port of the device. * * The function ::mbgextio_get_serial_settings must have been called before * to read the current settings and configuration options and store them * in a ::RECEIVER_PORT_CFG structure. * * @param[in,out] pmctl Pointer to a valid message control structure. * @param[in] port_idx Index of the serial port for which to print the settings. * @param[in] p_rpcfg Pointer to a ::RECEIVER_PORT_CFG structure containing valid data. * * @return One of the @ref MBG_RETURN_CODES */ void print_serial_settings( MBG_MSG_CTL *pmctl, MBG_MSG_IDX port_idx, const RECEIVER_PORT_CFG *p_rpcfg ) { // A pointer to the PORT_INFO structure of the specific port // that provides a sub-structure with the current settings // as well as the supported configuration options. const PORT_INFO *p_pi = &p_rpcfg->pii[port_idx].port_info; // And another pointer to the configuration settings. const PORT_SETTINGS *p_ps = &p_pi->port_settings; // The port configuration provides an index to an array of supported string // types. This pointer will be set to the associated STR_TYPE_INFO entry. const STR_TYPE_INFO *p_sti; // Display the configuration settings. printf( " COM%i: %5lu Baud, %s", port_idx, (ulong) p_ps->parm.baud_rate, p_ps->parm.framing ); // Make sure the indices included in the settings read from the device // don't exceed the maximum numbers supported by this program. if ( p_ps->str_type >= MAX_PARM_STR_TYPE ) { printf( " (string type index %i exceeds max %i)", p_ps->str_type, MAX_PARM_STR_TYPE - 1 ); goto out; } if ( p_ps->mode >= N_STR_MODE ) { printf( " (string mode %i exceeds max %i)", p_ps->mode, N_STR_MODE - 1 ); goto out; } // The port configuration provides an index to an array of supported string // types, so set up a pointer to the associated STR_TYPE_INFO entry. p_sti = &p_rpcfg->stii[p_ps->str_type].str_type_info; // Now we can print the name of the string type, and the configured string mode. printf( ", \"%s\" string %s", p_sti->long_name, mode_names[p_ps->mode] ); out: printf( "\n" ); } // print_serial_settings static /*HDR*/ /** * @brief Check if new serial port setting yield a valid configuration. * * The function ::mbgextio_get_serial_settings must have been called before * to read the current settings and configuration options and store them * in a ::RECEIVER_PORT_CFG structure. * * @param[in,out] pmctl Pointer to a valid message control structure. * @param[in] p_rpcfg Pointer to a ::RECEIVER_PORT_CFG structure containing valid data. * @param[in] port_idx Index of the serial port for which to check the settings. * @param[in] new_str_type_idx New string type index to be set. Supported values depend on device. * @param[in] new_str_mode New string mode to be set. See enum STR_MODES. * * @return One of the @ref MBG_RETURN_CODES */ int check_serial_port_settings( MBG_MSG_CTL *pmctl, RECEIVER_PORT_CFG *p_rpcfg, MBG_MSG_IDX port_idx, int new_str_type_idx, int new_str_mode ) { // Set up a pointer to the RECEIVER_INFO structure associated // with the device to access basic device information. const RECEIVER_INFO *p_ri = mbgextio_get_receiver_info_addr( pmctl ); // A pointer to the PORT_INFO structure of the specific port // that provides a sub-structure with the current settings // as well as the supported configuration options. PORT_INFO *p_pi; // A pointer that will be set to the address of a specific // string type info in the array read from the device. const STR_TYPE_INFO *p_sti; int rc; // First make sure the given port_idx doesn't exceed the number of ports // supported by the device. if ( port_idx >= p_ri->n_com_ports ) { rc = MBG_ERR_N_COM_EXCEEDS_SUPP; goto out; } // Get a pointer to the PORT_INFO structure associated with port_idx, // which provides a mask of string types supported by this port. p_pi = &p_rpcfg->pii[port_idx].port_info; if ( !( p_pi->supp_str_types & ( 1UL << new_str_type_idx ) ) ) { rc = MBG_ERR_NOT_SUPP_BY_DEV; goto out; } // The new string type index is valid. Now check if the // new mode is supported, which depends on the string type. p_sti = &p_rpcfg->stii[new_str_type_idx].str_type_info; if ( !( p_sti->supp_modes & ( 1UL << new_str_mode ) ) ) { rc = MBG_ERR_NOT_SUPP_BY_DEV; goto out; } // All parameters are in range and supported. rc = MBG_SUCCESS; out: return rc; } // check_serial_port_settings static /*HDR*/ /** * @brief Example function to print and change a serial port configuration. * * The function ::mbgextio_get_serial_settings must have been called before * to read the current settings and configuration options and store them * in a ::RECEIVER_PORT_CFG structure. * * @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_serial_settings * @see ::mbgextio_save_serial_settings */ int do_configure_serial_port( MBG_MSG_CTL *pmctl, XBP_ADDR *p_addr ) { // Index of the serial port to be configured. // Can be 0..RECEIVER_INFO::n_com_ports-1. MBG_MSG_IDX port_idx = 1; // Index of the new string type to be set. Depends on device, // but usually 0 refers to the Meinberg Standard Time String. int new_str_type_idx = 0; // New string mode to be set. See enum STR_MODES. int new_str_mode = STR_PER_SEC; // Set up a pointer to the RECEIVER_INFO structure associated // with the device to access basic device information. const RECEIVER_INFO *p_ri = mbgextio_get_receiver_info_addr( pmctl ); // This structure takes up the current configuration and // the supported configuration options of all serial ports // of a device. RECEIVER_PORT_CFG rpcfg; // A pointer to the PORT_INFO structure of the specific port // that provides a sub-structure with the current settings // as well as the supported configuration options. PORT_INFO *p_pi; // And another pointer to the configuration settings to be updated. PORT_SETTINGS *p_ps; int i; // Read all serial port configuration info from the device. int rc = mbgextio_get_serial_settings( pmctl, p_addr, &rpcfg ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "Failed to read serial settings: %s\n", mbg_strerror( rc ) ); goto out; } // Print a list of time string types supported by the device. print_supp_str_types( pmctl, &rpcfg ); printf( "\n" ); // Print the current configuration of all serial ports. // show_all_serial_settings( pmctl, &rpcfg, "Current " ); printf( "Current Serial Port Settings:\n" ); for ( i = 0; i < p_ri->n_com_ports; i++ ) print_serial_settings( pmctl, i, &rpcfg ); printf( "\n" ); // Check if the new settings to be sent to the device are valid. rc = check_serial_port_settings( pmctl, &rpcfg, port_idx, new_str_type_idx, new_str_mode ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "New serial port settings are invalid: %s\n", mbg_strerror( rc ) ); goto out; } // Set up a pointer to the settings for the port, and // update the relevant settings. p_pi = &rpcfg.pii[port_idx].port_info; p_ps = &p_pi->port_settings; p_ps->str_type = new_str_type_idx; p_ps->mode = new_str_mode; // Write the new settings to the device. rc = mbgextio_save_serial_settings( pmctl, p_addr, &rpcfg, port_idx ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "Failed to write new settings for COM%i: %s\n", port_idx, mbg_strerror( rc ) ); goto out; } printf( "Successfully wrote new settings for COM%i.\n", port_idx ); printf( "\n" ); // Re-read the new settings. rc = mbgextio_get_serial_settings( pmctl, p_addr, &rpcfg ); if ( mbg_rc_is_error( rc ) ) { fprintf( stderr, "Failed to re-read serial settings: %s\n", mbg_strerror( rc ) ); goto out; } // Print the new settings for the port that has been changed. print_serial_settings( pmctl, port_idx, &rpcfg ); out: return rc; } // do_configure_serial_port int main( int argc, char *argv[] ) { // Device "handle" set up when a device is opened. MBG_MSG_CTL *pmctl = NULL; // A sub-device address, not used here, so NULL XBP_ADDR *p_addr = NULL; // Default baud rate and framing used to connect to a device. // Newer devices may use MBG_DEFAULT_BAUDRATE_HS by default. BAUD_RATE baudrate = MBG_DEFAULT_BAUDRATE; const char framing[] = MBG_DEFAULT_FRAMING; // Name of the serial port of the computer, to which the device // is connected, e.g. COM1 (Windows), or /dev/ttyS0 (Linux). const char *port_name; int rc; // Check if a port name has been specified on the command line. if ( argc < 2 ) { fprintf( stderr, "Serial port identifier expected as parameter, exiting.\n" ); return 1; } // Now open a connection to the device. Here we connect via a serial line. // For other types of connection use one of the other use mbgextio_open_...() // functions. See mbgextio.h. port_name = argv[1]; rc = mbgextio_open_serial( port_name, &pmctl, baudrate, framing ); if ( mbg_rc_is_error( rc ) ) // Connection couldn't be established. { fprintf( stderr, "Failed to connect via %s: %s\n", port_name, mbg_strerror( rc ) ); return 2; } // Now configure the serial port of the connected device. rc = do_configure_serial_port( pmctl, p_addr ); // Finally close the connection-> mbgextio_close_connection( &pmctl ); return mbg_rc_is_error( rc ) ? 3 : 0; } // main