Tscope5
serialport_internal.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // __ ______
4 // / /_______________ ____ ___ / ____/
5 // / __/ ___/ ___/ __ \/ __ \/ _ \ /___ )
6 // / /_(__ ) /__/ /_/ / /_/ / __/ ____/ /
7 // \__/____/\___/\____/ .___/\___/ /_____/
8 // /_/
9 //
10 /// \file serialport_internal.c
11 /// Definitions of internal serial port functions.
12 ////////////////////////////////////////////////////////////////////////////////
13 
14 #include "../include/tscope5/serialport_internal.h"
15 #include "../include/tscope5/system_internal.h"
16 #include "../include/tscope5/timer_internal.h"
17 
18 #ifndef TS5_WINDOWS
19  #include <sys/types.h>
20  #include <sys/ioctl.h>
21  #include <dirent.h>
22  #include <termios.h>
23  #include <errno.h>
24 
25 #endif
26 
27 /// Is the serialport subsystem installed?
29 
30 
31 ////////////////////////////////////////////////////////////////////////////////
32 /// Do some checks at the start of each serialport function.
33 ///
34 /// \param calling_function Name the function that calls
35 /// for this check or installation.
36 ///
37 /// Checks whether the serialport subsystem is installed.
38 /// If not, the serialport subsystem is installed.
39 ////////////////////////////////////////////////////////////////////////////////
40 void ts5_check_serialport(char *calling_function)
41 {
42  ts5_log(TS5_LOGLEVEL_6, "%s: ts5_check_serialport\n", calling_function);
44  ts5_install_serialport(calling_function);
45  }
46 }
47 
48 
49 ////////////////////////////////////////////////////////////////////////////////
50 /// Do some checks at the start of each serialport function.
51 ///
52 /// \param calling_function Name the function that calls
53 /// for this check or installation.
54 /// \param portname Name of the port.
55 ///
56 /// Checks whether this serial port is already opened.
57 /// If so, return the port number.
58 /// If not, open it and return the port number.
59 ////////////////////////////////////////////////////////////////////////////////
60 int ts5_check_serialport2(char *calling_function, char *portname)
61 {
62  ts5_log(TS5_LOGLEVEL_6, "%s: ts5_check_serialport2(%s)\n",
63  calling_function, portname);
64 
65  int i;
66  int portnum = -1;
67 
68  // is the port already open? if so return the port index
69  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
70  if (!strcmp(portname, _ts5_status.timer.serialport[i].portname)) {
71  portnum = i;
72  }
73  }
74 
75  // if the port is not open try to open it
76  if (portnum == -1) {
77 
78  TS5_SERIALPORT *port;
79  port = (TS5_SERIALPORT *)al_malloc(sizeof(TS5_SERIALPORT));
80 
81  #ifdef TS5_WINDOWS
82  *port = CreateFile(portname,
83  GENERIC_READ | GENERIC_WRITE,
84  0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
85 
86  // asynchronous:
87  // replace FILE_ATTRIBUTE_NORMAL
88  // by FILE_FLAG_OVERLAPPED
89  // http://msdn.microsoft.com/en-us/library/aa365683(VS.85).aspx
90 
91  if (*port == INVALID_HANDLE_VALUE) {
92  ts5_fatal("%s: could not open serial port %s\n",
93  calling_function, portname);
94  }
95 
96  #else
97  *port = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
98 
99  if (*port == -1) {
100  ts5_fatal("%s: could not open serial port %s\n",
101  calling_function, portname);
102  }
103 
104  // get exclusive io
105  if (ioctl(*port, TIOCEXCL) == -1) {
106  ts5_fatal("%s: could not get exclusivity on serial port %s\n",
107  calling_function, portname);
108  }
109  #endif
110 
111  // if we get here we apparently managed to open the port
112  _ts5_status.timer.num_serialports++;
113  portnum = _ts5_status.timer.num_serialports - 1;
114 
115  // update status pointer
116  TS5_SERIALPORT_STATUS *serialport_status;
117  serialport_status = al_realloc(_ts5_status.timer.serialport,
118  _ts5_status.timer.num_serialports * sizeof(TS5_SERIALPORT_STATUS));
119 
120  if (serialport_status == NULL) {
121  ts5_fatal("%s: %s\n", calling_function,
122  "failed to allocate memory for serial port settings");
123  }
124 
125  _ts5_status.timer.serialport =
126  (TS5_SERIALPORT_STATUS *) serialport_status;
127 
128  // fill in status values
129  strcpy(_ts5_status.timer.serialport[portnum].portname, portname);
130  _ts5_status.timer.serialport[portnum].port = port;
131 
132  _ts5_status.timer.serialport[portnum].num_buttons = 127;
133  _ts5_status.timer.serialport[portnum].num_defined_buttons = 0;
134  _ts5_status.timer.serialport[portnum].num_active_buttons = 0;
135 
136  _ts5_status.timer.serialport[portnum].button_press_defined =
137  (int *)al_malloc(sizeof(int)
138  * _ts5_status.timer.serialport[portnum].num_buttons);
139 
140  _ts5_status.timer.serialport[portnum].button_press_active =
141  (int *)al_malloc(sizeof(int)
142  * _ts5_status.timer.serialport[portnum].num_buttons);
143 
144  _ts5_status.timer.serialport[portnum].button_release_defined =
145  (int *)al_malloc(sizeof(int)
146  * _ts5_status.timer.serialport[portnum].num_buttons);
147 
148  _ts5_status.timer.serialport[portnum].button_release_active =
149  (int *)al_malloc(sizeof(int)
150  * _ts5_status.timer.serialport[portnum].num_buttons);
151 
152  int j;
153  for (j=0; j<_ts5_status.timer.serialport[portnum].num_buttons; j++) {
154  _ts5_status.timer.serialport[portnum].button_press_defined[j] = 0;
155  _ts5_status.timer.serialport[portnum].button_press_active[j] = 0;
156  _ts5_status.timer.serialport[portnum].button_release_defined[j] = 0;
157  _ts5_status.timer.serialport[portnum].button_release_active[j] = 0;
158  }
159 
160  _ts5_status.timer.serialport[portnum].is_trigger_input_device = 0;
161  _ts5_status.timer.serialport[portnum].is_trigger_output_device = 0;
162  _ts5_status.timer.serialport[portnum].simulate_trigger_input = 0;
163  _ts5_status.timer.serialport[portnum].trigger_simulation_interval = 2.0;
164  _ts5_status.timer.serialport[portnum].last_trigger_simulation =
165  al_get_time();
166 
167  }
168 
169 
170  return portnum;
171 }
172 
173 
174 ////////////////////////////////////////////////////////////////////////////////
175 /// Install the serialport subsystem.
176 ///
177 /// \param calling_function Name the function that calls
178 /// for this check or installation.
179 ///
180 /// This function is called automatically if necessary.
181 ////////////////////////////////////////////////////////////////////////////////
182 void ts5_install_serialport(char *calling_function)
183 {
185  ts5_install_timer(calling_function);
186  }
187 
189 
191 
192  ts5_log(TS5_LOGLEVEL_1, "%s: Installing Tscope5 serialport\n",
193  calling_function);
194 
195  // initialize event source
196  al_init_user_event_source(
197  &_ts5_data.timer.serialport_response_event_source);
198 
199  al_init_user_event_source(
200  &_ts5_data.timer.serialport_trigger_event_source);
201 
202  // register event source
203  al_register_event_source(_ts5_data.timer.response_queue,
204  &_ts5_data.timer.serialport_response_event_source);
205 
206  al_register_event_source(_ts5_data.timer.trigger_queue,
207  &_ts5_data.timer.serialport_trigger_event_source);
208 
209  al_register_event_source(_ts5_data.timer.trigger_log,
210  &_ts5_data.timer.serialport_trigger_event_source);
211 
212  // initialize thread for serialport
213  _ts5_data.timer.serialport_thread =
214  al_create_thread(ts5_serialport_threadfunc, NULL);
215 
216  al_start_thread(_ts5_data.timer.serialport_thread);
217 
218  atexit(ts5_uninstall_serialport);
219  }
220 }
221 
222 
223 ////////////////////////////////////////////////////////////////////////////////
224 /// Uninstall the serialport subsystem.
225 ///
226 /// This function is called automatically at the end of the program.
227 ////////////////////////////////////////////////////////////////////////////////
229 {
231 
232  ts5_log(TS5_LOGLEVEL_1, "Uninstalling Tscope5 serialport\n");
233 
234  // remove thread
235  al_join_thread(_ts5_data.timer.serialport_thread, NULL);
236  al_destroy_thread(_ts5_data.timer.serialport_thread);
237 
238  // unregister event source
239  al_unregister_event_source(_ts5_data.timer.response_queue,
240  &_ts5_data.timer.serialport_response_event_source);
241 
242  al_unregister_event_source(_ts5_data.timer.trigger_queue,
243  &_ts5_data.timer.serialport_trigger_event_source);
244 
245  al_unregister_event_source(_ts5_data.timer.trigger_log,
246  &_ts5_data.timer.serialport_trigger_event_source);
247 
248  // remove event source
249  al_destroy_user_event_source(
250  &_ts5_data.timer.serialport_response_event_source);
251 
252  al_destroy_user_event_source(
253  &_ts5_data.timer.serialport_trigger_event_source);
254 
255  // close ports
256  int i;
257  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
258 
259  #ifdef TS5_WINDOWS
260  if (!CloseHandle(*(_ts5_status.timer.serialport[i].port))) {
261  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
262  "ts5_uninstall_serialport",
263  "could not uninstall serial port", i);
264  }
265 
266  #else
267  tcdrain(*(_ts5_status.timer.serialport[i].port));
268 
269  if (close(*(_ts5_status.timer.serialport[i].port)) == -1) {
270  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
271  "ts5_uninstall_serialport",
272  "could not uninstall serial port", i);
273  }
274 
275  #endif
276 
277  al_free(_ts5_status.timer.serialport[i].button_press_defined);
278  _ts5_status.timer.serialport[i].button_press_defined = NULL;
279 
280  al_free(_ts5_status.timer.serialport[i].button_press_active);
281  _ts5_status.timer.serialport[i].button_press_active = NULL;
282 
283  al_free(_ts5_status.timer.serialport[i].button_release_defined);
284  _ts5_status.timer.serialport[i].button_release_defined = NULL;
285 
286  al_free(_ts5_status.timer.serialport[i].button_release_active);
287  _ts5_status.timer.serialport[i].button_release_active = NULL;
288 
289  al_free(_ts5_status.timer.serialport[i].port);
290  }
291 
292  al_free(_ts5_status.timer.serialport);
293  _ts5_status.timer.serialport = NULL;
294  _ts5_status.timer.num_serialports = 0;
295  _ts5_status.timer.serialport_is_response_device = 0;
296  _ts5_status.timer.serialport_is_trigger_device = 0;
297 
299  }
300 }
301 
302 
303 
304 ////////////////////////////////////////////////////////////////////////////////
305 /// Serial port thread function.
306 ///
307 /// \warning This is an internal function, no checks are performed.
308 ////////////////////////////////////////////////////////////////////////////////
310 {
311  ALLEGRO_EVENT serialport_event;
312  double previous_time;
313  double current_time = al_get_time();
314 
315  #ifdef TS5_WINDOWS
316  // SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
317 
318  // THREAD_PRIORITY_HIGHEST
319  // THREAD_PRIORITY_ABOVE_NORMAL
320  // THREAD_PRIORITY_NORMAL
321  // THREAD_PRIORITY_TIME_CRITICAL
322 
323  #endif
324 
325  while (!al_get_thread_should_stop(_ts5_data.timer.serialport_thread)) {
326 
327  int i;
328  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
329 
330 
331  // monitor responses and triggers
332  if (_ts5_status.timer.serialport[i].num_defined_buttons ||
333  _ts5_status.timer.serialport[i].is_trigger_input_device) {
334 
335  char serial_buff[TS5_MAX_CHAR];
336  int num_bytes = 0;
337 
338  #ifdef TS5_WINDOWS
339  unsigned long bytes_to_read = 1;
340  unsigned long bytes_read = 0;
341 
342  if (!ReadFile(*(_ts5_status.timer.serialport[i].port),
343  serial_buff, bytes_to_read, &bytes_read, NULL)) {
344  ts5_fatal("ts5_serialport_threadfunc: read error\n");
345  }
346 
347  num_bytes = bytes_read;
348 
349  #else
350  unsigned int bytes_to_read = 1;
351  unsigned int bytes_read = 0;
352 
353  bytes_read = read(*(_ts5_status.timer.serialport[i].port),
354  serial_buff, bytes_to_read);
355 
356  if ((int)bytes_read == -1) {
357  ts5_fatal("ts5_serialport_threadfunc: read error\n");
358  }
359 
360  num_bytes = bytes_read;
361 
362  #endif
363 
364  if (num_bytes) {
365 
366  serialport_event.user.data1 = i;
367  serialport_event.user.data2 = serial_buff[0];
368  serialport_event.user.data3 =
369  (intptr_t)((current_time-previous_time)*1000000.0);
370 
371  if (_ts5_status.timer.serialport[i].num_defined_buttons) {
372  serialport_event.user.type = TS5_EVENT_SERIALPORT_BUTTON_DOWN;
373  al_emit_user_event(
374  &_ts5_data.timer.serialport_response_event_source,
375  &serialport_event, NULL);
376  }
377 
378  if (_ts5_status.timer.serialport[i].is_trigger_input_device) {
379 
380  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
381  al_emit_user_event(
382  &_ts5_data.timer.serialport_trigger_event_source,
383  &serialport_event, NULL);
384  }
385 
386  }
387  }
388 
389  // simulate triggers
390  if (_ts5_status.timer.serialport[i].simulate_trigger_input) {
391 
392  if (current_time - _ts5_status.timer.serialport[i]
393  .last_trigger_simulation
394  >= _ts5_status.timer.serialport[i]
395  .trigger_simulation_interval) {
396 
397  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
398 
399  serialport_event.user.data1 = i;
400 
401  serialport_event.user.data2 =
402  _ts5_status.timer.serialport[i].simulate_trigger_input;
403 
404  serialport_event.user.data3 =
405  (intptr_t)((current_time-previous_time)*1000000.0);
406 
407  al_emit_user_event(
408  &_ts5_data.timer.serialport_trigger_event_source,
409  &serialport_event, NULL);
410 
411  _ts5_status.timer.serialport[i].last_trigger_simulation =
412  current_time;
413  }
414  }
415 
416  }
417  previous_time = current_time;
418  current_time = al_get_time();
419 
420  // wait 50 microseconds to avoid crashes on Windows 7
421  do {
422 
423  } while (al_get_time() - current_time < 0.00005);
424  }
425 
426  return NULL;
427 }
428 
429 
int _ts5_is_timer_installed
Is the timer subsystem installed?
void * ts5_serialport_threadfunc()
Serial port thread function.
void ts5_uninstall_serialport()
Uninstall the serialport subsystem.
void ts5_install_serialport(char *calling_function)
Install the serialport subsystem.
int _ts5_is_serialport_installed
Is the serialport subsystem installed?
void ts5_check_serialport(char *calling_function)
Do some checks at the start of each serialport function.
void ts5_install_timer(char *calling_function)
Install the timer subsystem.
void ts5_log(const unsigned int level, const char *format,...)
Send info to a logging window.
Definition: system.c:45
void ts5_fatal(const char *format,...)
Exit safely with an error message.
Definition: system.c:533
int ts5_check_serialport2(char *calling_function, char *portname)
Do some checks at the start of each serialport function.