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  // asynchroon:
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 
110  #endif
111 
112  // if we get here we apparently managed to open the port
113  _ts5_status.timer.num_serialports++;
114  portnum = _ts5_status.timer.num_serialports - 1;
115 
116  // update status pointer
117  TS5_SERIALPORT_STATUS *serialport_status;
118  serialport_status = al_realloc(_ts5_status.timer.serialport,
119  _ts5_status.timer.num_serialports * sizeof(TS5_SERIALPORT_STATUS));
120 
121  if (serialport_status == NULL) {
122  ts5_fatal("%s: %s\n", calling_function,
123  "failed to allocate memory for serial port settings");
124  }
125 
126  _ts5_status.timer.serialport =
127  (TS5_SERIALPORT_STATUS *) serialport_status;
128 
129  // fill in status values
130  strcpy(_ts5_status.timer.serialport[portnum].portname, portname);
131  _ts5_status.timer.serialport[portnum].port = port;
132  _ts5_status.timer.serialport[portnum].is_trigger_input_device = 0;
133  _ts5_status.timer.serialport[portnum].is_trigger_output_device = 0;
134  _ts5_status.timer.serialport[portnum].simulate_trigger_input = 0;
135  _ts5_status.timer.serialport[portnum].trigger_simulation_interval = 2.0;
136  _ts5_status.timer.serialport[portnum].last_trigger_simulation =
137  al_get_time();
138 
139  }
140  return portnum;
141 }
142 
143 
144 ////////////////////////////////////////////////////////////////////////////////
145 /// Install the serialport subsystem.
146 ///
147 /// \param calling_function Name the function that calls
148 /// for this check or installation.
149 ///
150 /// This function is called automatically if necessary.
151 ////////////////////////////////////////////////////////////////////////////////
152 void ts5_install_serialport(char *calling_function)
153 {
155  ts5_install_timer(calling_function);
156  }
157 
159 
161 
162  ts5_log(TS5_LOGLEVEL_1, "%s: Installing Tscope5 serialport\n",
163  calling_function);
164 
165  al_init_user_event_source(
166  &_ts5_data.timer.serialport_trigger_event_source);
167 
168  al_register_event_source(_ts5_data.timer.trigger_queue,
169  &_ts5_data.timer.serialport_trigger_event_source);
170 
171  al_register_event_source(_ts5_data.timer.trigger_log,
172  &_ts5_data.timer.serialport_trigger_event_source);
173 
174  _ts5_data.timer.serialport_thread =
175  al_create_thread(ts5_serialport_threadfunc, NULL);
176 
177  al_start_thread(_ts5_data.timer.serialport_thread);
178 
179  atexit(ts5_uninstall_serialport);
180  }
181 }
182 
183 
184 ////////////////////////////////////////////////////////////////////////////////
185 /// Uninstall the serialport subsystem.
186 ///
187 /// This function is called automatically at the end of the program.
188 ////////////////////////////////////////////////////////////////////////////////
190 {
192 
193  ts5_log(TS5_LOGLEVEL_1, "Uninstalling Tscope5 serialport\n");
194 
195  // remove thread
196  al_join_thread(_ts5_data.timer.serialport_thread, NULL);
197  al_destroy_thread(_ts5_data.timer.serialport_thread);
198 
199  // unregister event source
200  al_unregister_event_source(_ts5_data.timer.trigger_queue,
201  &_ts5_data.timer.serialport_trigger_event_source);
202 
203  al_unregister_event_source(_ts5_data.timer.trigger_log,
204  &_ts5_data.timer.serialport_trigger_event_source);
205 
206  // remove event source
207  al_destroy_user_event_source(
208  &_ts5_data.timer.serialport_trigger_event_source);
209 
210  // close ports
211  int i;
212  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
213 
214  #ifdef TS5_WINDOWS
215  if (!CloseHandle(*(_ts5_status.timer.serialport[i].port))) {
216  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
217  "ts5_uninstall_serialport",
218  "could not uninstall serial port", i);
219  }
220 
221  #else
222  tcdrain(*(_ts5_status.timer.serialport[i].port));
223 
224  if (close(*(_ts5_status.timer.serialport[i].port)) == -1) {
225  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
226  "ts5_uninstall_serialport",
227  "could not uninstall serial port", i);
228  }
229 
230  #endif
231 
232  al_free(_ts5_status.timer.serialport[i].port);
233  }
234 
235  al_free(_ts5_status.timer.serialport);
236 
237  _ts5_status.timer.serialport_is_trigger_device = 0;
238 
240  }
241 }
242 
243 
244 
245 ////////////////////////////////////////////////////////////////////////////////
246 /// Serial port thread function.
247 ///
248 /// \warning This is an internal function, no checks are performed.
249 ////////////////////////////////////////////////////////////////////////////////
251 {
252  ALLEGRO_EVENT serialport_event;
253  double previous_time;
254  double current_time = al_get_time();
255 
256  #ifdef TS5_WINDOWS
257  // SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
258 
259  // THREAD_PRIORITY_HIGHEST
260  // THREAD_PRIORITY_ABOVE_NORMAL
261  // THREAD_PRIORITY_NORMAL
262  // THREAD_PRIORITY_TIME_CRITICAL
263 
264  #endif
265 
266  while (!al_get_thread_should_stop(_ts5_data.timer.serialport_thread)) {
267 
268  int i;
269  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
270 
271  // monitor triggers
272  if (_ts5_status.timer.serialport[i].is_trigger_input_device) {
273 
274  char serial_buff[TS5_MAX_CHAR];
275  int num_bytes = 0;
276 
277  #ifdef TS5_WINDOWS
278  unsigned long bytes_to_read = 1;
279  unsigned long bytes_read = 0;
280 
281  if (!ReadFile(*(_ts5_status.timer.serialport[i].port),
282  serial_buff, bytes_to_read, &bytes_read, NULL)) {
283  ts5_fatal("ts5_serialport_threadfunc: read error\n");
284  }
285 
286  num_bytes = bytes_read;
287 
288  #else
289  unsigned int bytes_to_read = 1;
290  unsigned int bytes_read = 0;
291 
292  bytes_read = read(*(_ts5_status.timer.serialport[i].port),
293  serial_buff, bytes_to_read);
294 
295  if ((int)bytes_read == -1) {
296  ts5_fatal("ts5_serialport_threadfunc: read error\n");
297  }
298 
299  num_bytes = bytes_read;
300 
301  #endif
302 
303  if (num_bytes) {
304 
305  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
306 
307  serialport_event.user.data1 = i;
308  serialport_event.user.data2 = serial_buff[0];
309  serialport_event.user.data3 =
310  (intptr_t)((current_time-previous_time)*1000000.0);
311 
312  al_emit_user_event(
313  &_ts5_data.timer.serialport_trigger_event_source,
314  &serialport_event, NULL);
315  }
316  }
317 
318  // simulate triggers
319  if (_ts5_status.timer.serialport[i].simulate_trigger_input) {
320 
321  if (current_time - _ts5_status.timer.serialport[i]
322  .last_trigger_simulation
323  >= _ts5_status.timer.serialport[i]
324  .trigger_simulation_interval) {
325 
326  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
327 
328  serialport_event.user.data1 = i;
329 
330  serialport_event.user.data2 =
331  _ts5_status.timer.serialport[i].simulate_trigger_input;
332 
333  serialport_event.user.data3 =
334  (intptr_t)((current_time-previous_time)*1000000.0);
335 
336  al_emit_user_event(
337  &_ts5_data.timer.serialport_trigger_event_source,
338  &serialport_event, NULL);
339 
340  _ts5_status.timer.serialport[i].last_trigger_simulation =
341  current_time;
342  }
343  }
344 
345  }
346  previous_time = current_time;
347  current_time = al_get_time();
348 
349  // wait 50 microseconds to avoid crashes on Windows 7
350  do {
351 
352  } while (al_get_time() - current_time < 0.00005);
353  }
354 
355  return NULL;
356 }
357 
358 
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:529
int ts5_check_serialport2(char *calling_function, char *portname)
Do some checks at the start of each serialport function.