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  _ts5_status.timer.serialport_is_trigger_device = 1;
47 }
48 
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 /// Do some checks at the start of each serialport function.
52 ///
53 /// \param calling_function Name the function that calls
54 /// for this check or installation.
55 /// \param portname Name of the port.
56 ///
57 /// Checks whether this serial port is already opened.
58 /// If so, return the port number.
59 /// If not, open it and return the port number.
60 ////////////////////////////////////////////////////////////////////////////////
61 int ts5_check_serialport2(char *calling_function, char *portname)
62 {
63  ts5_log(TS5_LOGLEVEL_6, "%s: ts5_check_serialport2(%s)\n",
64  calling_function, portname);
65 
66  int i;
67  int portnum = -1;
68 
69  // is the port already open? if so return the port index
70  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
71  if (!strcmp(portname, _ts5_status.timer.serialport[i].portname)) {
72  portnum = i;
73  }
74  }
75 
76  // if the port is not open try to open it
77  if (portnum == -1) {
78 
79  TS5_SERIALPORT *port;
80  port = (TS5_SERIALPORT *)al_malloc(sizeof(TS5_SERIALPORT));
81 
82  #ifdef TS5_WINDOWS
83  *port = CreateFile(portname,
84  GENERIC_READ | GENERIC_WRITE,
85  0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
86 
87  if (*port == INVALID_HANDLE_VALUE) {
88  ts5_fatal("%s: could not open serial port %s\n",
89  calling_function, portname);
90  }
91 
92  #else
93  *port = open(portname, O_RDWR | O_NOCTTY | O_NDELAY);
94 
95  if (*port == -1) {
96  ts5_fatal("%s: could not open serial port %s\n",
97  calling_function, portname);
98  }
99 
100  // get exclusive io
101  if (ioctl(*port, TIOCEXCL) == -1) {
102  ts5_fatal("%s: could not get exclusivity on serial port %s\n",
103  calling_function, portname);
104  }
105 
106  #endif
107 
108  // if we get here we apparently managed to open the port
109  _ts5_status.timer.num_serialports++;
110  portnum = _ts5_status.timer.num_serialports - 1;
111 
112  // update status pointer
113  TS5_SERIALPORT_STATUS *serialport_status;
114  serialport_status = al_realloc(_ts5_status.timer.serialport,
115  _ts5_status.timer.num_serialports * sizeof(TS5_SERIALPORT_STATUS));
116 
117  if (serialport_status == NULL) {
118  ts5_fatal("%s: %s\n", calling_function,
119  "failed to allocate memory for serial port settings");
120  }
121 
122  _ts5_status.timer.serialport =
123  (TS5_SERIALPORT_STATUS *) serialport_status;
124 
125  // fill in status values
126  strcpy(_ts5_status.timer.serialport[portnum].portname, portname);
127  _ts5_status.timer.serialport[portnum].port = port;
128  _ts5_status.timer.serialport[portnum].is_trigger_input_device = 0;
129  _ts5_status.timer.serialport[portnum].is_trigger_output_device = 0;
130  _ts5_status.timer.serialport[portnum].simulate_trigger_input = 0;
131  _ts5_status.timer.serialport[portnum].trigger_simulation_interval = 2.0;
132  _ts5_status.timer.serialport[portnum].last_trigger_simulation =
133  al_get_time();
134 
135  }
136  return portnum;
137 }
138 
139 
140 ////////////////////////////////////////////////////////////////////////////////
141 /// Install the serialport subsystem.
142 ///
143 /// \param calling_function Name the function that calls
144 /// for this check or installation.
145 ///
146 /// This function is called automatically if necessary.
147 ////////////////////////////////////////////////////////////////////////////////
148 void ts5_install_serialport(char *calling_function)
149 {
151  ts5_install_timer(calling_function);
152  }
153 
155 
157  ts5_log(TS5_LOGLEVEL_1, "%s: Installing Tscope5 serialport\n",
158  calling_function);
159 
160  al_init_user_event_source(
161  &_ts5_data.timer.serialport_trigger_event_source);
162 
163  al_register_event_source(_ts5_data.timer.trigger_queue,
164  &_ts5_data.timer.serialport_trigger_event_source);
165 
166  al_register_event_source(_ts5_data.timer.trigger_log,
167  &_ts5_data.timer.serialport_trigger_event_source);
168 
169  _ts5_data.timer.serialport_thread =
170  al_create_thread(ts5_serialport_threadfunc, NULL);
171 
172  al_start_thread(_ts5_data.timer.serialport_thread);
173 
174  atexit(ts5_uninstall_serialport);
175  }
176 }
177 
178 
179 ////////////////////////////////////////////////////////////////////////////////
180 /// Uninstall the serialport subsystem.
181 ///
182 /// This function is called automatically at the end of the program.
183 ////////////////////////////////////////////////////////////////////////////////
185 {
187 
188  ts5_log(TS5_LOGLEVEL_1, "Uninstalling Tscope5 serialport\n");
189 
190  // remove thread
191  al_join_thread(_ts5_data.timer.serialport_thread, NULL);
192  al_destroy_thread(_ts5_data.timer.serialport_thread);
193 
194  // unregister event source
195  al_unregister_event_source(_ts5_data.timer.trigger_queue,
196  &_ts5_data.timer.serialport_trigger_event_source);
197 
198  al_unregister_event_source(_ts5_data.timer.trigger_log,
199  &_ts5_data.timer.serialport_trigger_event_source);
200 
201  // remove event source
202  al_destroy_user_event_source(
203  &_ts5_data.timer.serialport_trigger_event_source);
204 
205  // close ports
206  int i;
207  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
208 
209  #ifdef TS5_WINDOWS
210  if (!CloseHandle(*(_ts5_status.timer.serialport[i].port))) {
211  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
212  "ts5_uninstall_serialport",
213  "could not uninstall serial port", i);
214  }
215 
216  #else
217  tcdrain(*(_ts5_status.timer.serialport[i].port));
218 
219  if (close(*(_ts5_status.timer.serialport[i].port)) == -1) {
220  ts5_log(TS5_LOGLEVEL_1,"%s %s %d\n",
221  "ts5_uninstall_serialport",
222  "could not uninstall serial port", i);
223  }
224 
225  #endif
226 
227  al_free(_ts5_status.timer.serialport[i].port);
228  }
229 
230  al_free(_ts5_status.timer.serialport);
231 
232  _ts5_status.timer.serialport_is_trigger_device = 0;
233 
235  }
236 }
237 
238 
239 
240 ////////////////////////////////////////////////////////////////////////////////
241 /// Serial port thread function.
242 ///
243 /// \warning This is an internal function, no checks are performed.
244 ////////////////////////////////////////////////////////////////////////////////
246 {
247  ALLEGRO_EVENT serialport_event;
248  double looptime = al_get_time();
249  double now;
250 
251  while (!al_get_thread_should_stop(_ts5_data.timer.serialport_thread)) {
252 
253  int i;
254  for (i=0; i<_ts5_status.timer.num_serialports; i++) {
255 
256  now = al_get_time();
257 
258  // monitor triggers
259  if (_ts5_status.timer.serialport[i].is_trigger_input_device) {
260 
261  char serial_buff[TS5_MAX_CHAR];
262  int num_bytes;
263 
264  #ifdef TS5_WINDOWS
265  unsigned long bytes_to_read=1, bytes_read;
266 
267  if (!ReadFile(*(_ts5_status.timer.serialport[i].port),
268  serial_buff, bytes_to_read, &bytes_read, NULL)) {
269  ts5_fatal("ts5_serialport_threadfunc: read error\n");
270  }
271 
272  num_bytes = bytes_read;
273 
274  #else
275  unsigned int bytes_to_read=1, bytes_read;
276 
277  bytes_read = read(*(_ts5_status.timer.serialport[i].port),
278  serial_buff, bytes_to_read);
279 
280  if ((int)bytes_read == -1) {
281  ts5_fatal("ts5_serialport_threadfunc: read error\n");
282  }
283 
284  num_bytes = bytes_read;
285 
286  #endif
287 
288  if (num_bytes) {
289 
290  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
291 
292  serialport_event.user.data1 = i;
293  serialport_event.user.data2 = serial_buff[0];
294  serialport_event.user.data3 =
295  (intptr_t)((now-looptime)*1000000.0);
296 
297  al_emit_user_event(
298  &_ts5_data.timer.serialport_trigger_event_source,
299  &serialport_event, NULL);
300  }
301  }
302 
303  // simulate triggers
304  if (_ts5_status.timer.serialport[i].simulate_trigger_input) {
305 
306  if (now - _ts5_status.timer.serialport[i]
307  .last_trigger_simulation
308  >= _ts5_status.timer.serialport[i]
309  .trigger_simulation_interval) {
310 
311  serialport_event.user.type = TS5_EVENT_SERIALPORT_TRIGGER;
312 
313  serialport_event.user.data1 = i;
314 
315  serialport_event.user.data2 =
316  _ts5_status.timer.serialport[i].simulate_trigger_input;
317 
318  serialport_event.user.data3 =
319  (intptr_t)((now-looptime)*1000000.0);
320 
321  al_emit_user_event(
322  &_ts5_data.timer.serialport_trigger_event_source,
323  &serialport_event, NULL);
324 
325  _ts5_status.timer.serialport[i].last_trigger_simulation =
326  now;
327  }
328  }
329 
330  }
331  looptime = now;
332  }
333 
334  return NULL;
335 }
336 
337