summaryrefslogtreecommitdiff
path: root/set-serial-port.c
blob: a9835e1eee0753495c807147dc90d464ce6ffa42 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379

/**************************************************************************
 *
 * Example program demonstrating how to configure the serial port
 * of a Meinberg device using the Meinberg binary protocol.
 *
 **************************************************************************/

#include <mbgextio.h>
#include <extiohlp.h>



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