Tscope5
cedrusbox_internal.c
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // __ ______
4 // / /_______________ ____ ___ / ____/
5 // / __/ ___/ ___/ __ \/ __ \/ _ \ /___ )
6 // / /_(__ ) /__/ /_/ / /_/ / __/ ____/ /
7 // \__/____/\___/\____/ .___/\___/ /_____/
8 // /_/
9 //
10 /// \file cedrusbox_internal.c
11 /// Definitions of internal cedrusbox functions.
12 ////////////////////////////////////////////////////////////////////////////////
13 
14 
15 #include "../include/tscope5/cedrusbox.h"
16 #include "../include/tscope5/cedrusbox_internal.h"
17 #include "../include/tscope5/system_internal.h"
18 #include "../include/tscope5/timer_internal.h"
19 
20 /// Is the cedrusbox subsystem installed?
22 
23 #ifdef TS5_WINDOWS
25 
26 #else
28 
29 #endif
30 
31 
32 ////////////////////////////////////////////////////////////////////////////////
33 /// Do some checks at the start of each cedrusbox function.
34 ///
35 /// \param calling_function Name the function that calls for
36 /// this check or installation.
37 ///
38 /// Checks whether the cedrusbox subsystem is installed.
39 /// If not, the cedrusbox subsystem is installed.
40 ////////////////////////////////////////////////////////////////////////////////
41 void ts5_check_cedrusbox(char *calling_function)
42 {
43  ts5_log(TS5_LOGLEVEL_6, "%s: ts5_check_cedrusbox\n", calling_function);
45  ts5_install_cedrusbox(calling_function);
46  }
47 }
48 
49 
50 ////////////////////////////////////////////////////////////////////////////////
51 /// Install the cedrusbox subsystem.
52 ///
53 /// \param calling_function Name the function that calls for
54 /// this check or installation.
55 ///
56 /// This function is called automatically if necessary.
57 ////////////////////////////////////////////////////////////////////////////////
58 void ts5_install_cedrusbox(char *calling_function)
59 {
61  ts5_install_timer(calling_function);
62  }
63 
65 
67  ts5_log(TS5_LOGLEVEL_1, "%s: Installing Tscope5 cedrusbox\n",
68  calling_function);
69  }
70  else {
71  goto cedrusbox_is_installed;
72  }
73 
74  // scan the serial ports for possible cedrusboxes
75  // this is OS-dependent
76  int num_serial_devices=0;
77 
78  #ifdef TS5_WINDOWS
79  num_serial_devices = ts5_install_cedrusbox_windows(calling_function);
80 
81  #else
82  num_serial_devices = ts5_install_cedrusbox_posix(calling_function);
83 
84  #endif
85 
86  // are the serial devices we found cedrusboxes?
87  int i;
88  int device_type[TS5_CEDRUSBOX_MAXPORT];
89 
90  for (i=0; i<num_serial_devices; i++) {
91 
92  char serial_buff[TS5_MAX_CHAR];
93 
94  // flush
96 
97  // set xid mode
98  ts5_write_cedrusbox(i, "c10", 3);
99 
100  // get the device ID
102  ts5_write_cedrusbox(i, "_d2", 3);
103  ts5_read_cedrusbox(i, serial_buff, 6);
104  device_type[i] = serial_buff[0] - 47; // lumina 1, SV1 2, RBX 3
105 
106  if (device_type[i]==3) {
108  ts5_write_cedrusbox(i, "_d3", 6);
109  ts5_read_cedrusbox(i, serial_buff, 1);
110  device_type[i] += serial_buff[0] - 49;
111  }
112 
113  switch (device_type[i]) {
114 
115  case TS5_CEDRUSBOX_LUMINA:
116  sprintf(serial_buff, "lumina");
117  break;
118 
119  case TS5_CEDRUSBOX_SV1:
120  sprintf(serial_buff, "sv1");
121  break;
122 
123  case TS5_CEDRUSBOX_RB530:
124  sprintf(serial_buff, "rb530");
125  break;
126 
127  case TS5_CEDRUSBOX_RB730:
128  sprintf(serial_buff, "rb730");
129  break;
130 
131  case TS5_CEDRUSBOX_RB830:
132  sprintf(serial_buff, "rb830");
133  break;
134 
135  case TS5_CEDRUSBOX_RB834:
136  sprintf(serial_buff, "rb834");
137  break;
138 
139  default:
140 
141  device_type[i] = TS5_CEDRUSBOX_NODEVICE;
142 
143  #ifdef TS5_WINDOWS
144  if (!CloseHandle(_ts5_cedrusbox_handle[i])) {
145  ts5_log(TS5_LOGLEVEL_1, "%s: %s\n", calling_function,
146  "could not remove unknown device");
147  }
148 
149  #else
150  tcdrain(_ts5_cedrusbox_fd[i]);
151  tcsetattr(_ts5_cedrusbox_fd[i], TCSANOW,
152  &_ts5_cedrusbox_oldoptions[i]);
153 
154  if (close(_ts5_cedrusbox_fd[i]) == -1) {
155  ts5_log(TS5_LOGLEVEL_1, "%s: %s\n", calling_function,
156  "could not remove unknown device");
157  }
158  #endif
159 
160  continue;
161  }
162 
163  ts5_log(TS5_LOGLEVEL_1,"%s: cedrusbox ID %d (%s)\n",
164  calling_function, device_type[i], serial_buff);
165 
166  // get firmware revision
167  char minor, major;
169  ts5_write_cedrusbox(i, "_d4", 3);
170  ts5_read_cedrusbox(i, serial_buff, 6);
171  major = serial_buff[0];
172 
174  ts5_write_cedrusbox(i, "_d5", 3);
175  ts5_read_cedrusbox(i, serial_buff, 6);
176  minor = serial_buff[0];
177  ts5_log(TS5_LOGLEVEL_1,"%s: cedrusbox firmware revision: %c.%c\n",
178  calling_function, major, minor);
179 
180  // reset base timer
182  ts5_write_cedrusbox(i, "e1", 2);
183 
184  // reset rt time
186  ts5_write_cedrusbox(i, "e5", 2);
187 
188  // if we get here mark it as a cedrusbox
189  _ts5_status.timer.num_cedrusboxes++;
190  }
191 
192  // abort if we could not detect any cedrusboxes
193  if (!_ts5_status.timer.num_cedrusboxes) {
194  ts5_fatal("%s: could not install Tscope5 cedrusboxes\n",
195  calling_function);
196  }
197 
198  // initialize response event source
199  al_init_user_event_source(&_ts5_data.timer.cedrusbox_response_event_source);
200 
201  // register response event source
202  al_register_event_source(_ts5_data.timer.response_queue,
203  &_ts5_data.timer.cedrusbox_response_event_source);
204 
205  // initialize trigger event source
206  al_init_user_event_source(
207  &_ts5_data.timer.cedrusbox_trigger_event_source);
208 
209  // register trigger event source
210  al_register_event_source(_ts5_data.timer.trigger_queue,
211  &_ts5_data.timer.cedrusbox_trigger_event_source);
212 
213  // register trigger event source
214  al_register_event_source(_ts5_data.timer.trigger_log,
215  &_ts5_data.timer.cedrusbox_trigger_event_source);
216 
217  // initialize thread
218  _ts5_data.timer.cedrusbox_thread =
219  al_create_thread(ts5_cedrusbox_threadfunc, NULL);
220 
221  al_start_thread(_ts5_data.timer.cedrusbox_thread);
222 
223  // assign response codes
224  _ts5_status.timer.cedrusbox =
225  (TS5_CEDRUSBOX_STATUS *)
226  al_malloc(sizeof(TS5_CEDRUSBOX_STATUS)
227  * _ts5_status.timer.num_cedrusboxes);
228 
229  int j=0;
230  for (i=0; i<num_serial_devices; i++) {
231 
232  if (device_type[i]==TS5_CEDRUSBOX_NODEVICE) {
233  continue;
234  }
235 
236  _ts5_status.timer.cedrusbox[j].port_num = i;
237  _ts5_status.timer.cedrusbox[j].type = device_type[i];
238 
239  switch (_ts5_status.timer.cedrusbox[j].type) {
240 
241  case TS5_CEDRUSBOX_LUMINA:
242  _ts5_status.timer.cedrusbox[j].num_buttons = 4;
243  break;
244 
245  case TS5_CEDRUSBOX_SV1:
246  _ts5_status.timer.cedrusbox[j].num_buttons = 1;
247  break;
248 
249  case TS5_CEDRUSBOX_RB530:
250  _ts5_status.timer.cedrusbox[j].num_buttons = 5;
251  break;
252 
253  case TS5_CEDRUSBOX_RB730:
254  _ts5_status.timer.cedrusbox[j].num_buttons = 7;
255  break;
256 
257  case TS5_CEDRUSBOX_RB830:
258  _ts5_status.timer.cedrusbox[j].num_buttons = 8;
259  break;
260 
261  case TS5_CEDRUSBOX_RB834:
262  _ts5_status.timer.cedrusbox[j].num_buttons = 8;
263  break;
264 
265  default:
266  ts5_fatal("%s: unknown cedrus device type\n", calling_function);
267  }
268 
269  _ts5_status.timer.cedrusbox[j].num_defined_buttons = 0;
270  _ts5_status.timer.cedrusbox[j].num_active_buttons = 0;
271 
272  _ts5_status.timer.cedrusbox[j].button_press_defined =
273  (int *)al_malloc(sizeof(int)
274  * _ts5_status.timer.cedrusbox[j].num_buttons);
275 
276  _ts5_status.timer.cedrusbox[j].button_press_active =
277  (int *)al_malloc(sizeof(int)
278  * _ts5_status.timer.cedrusbox[j].num_buttons);
279 
280  _ts5_status.timer.cedrusbox[j].button_release_defined =
281  (int *)al_malloc(sizeof(int)
282  * _ts5_status.timer.cedrusbox[j].num_buttons);
283 
284  _ts5_status.timer.cedrusbox[j].button_release_active =
285  (int *)al_malloc(sizeof(int)
286  * _ts5_status.timer.cedrusbox[j].num_buttons);
287 
288  int k;
289  for (k=0; k<_ts5_status.timer.cedrusbox[j].num_buttons; k++) {
290  _ts5_status.timer.cedrusbox[j].button_press_defined[k] = 0;
291  _ts5_status.timer.cedrusbox[j].button_press_active[k] = 0;
292  _ts5_status.timer.cedrusbox[j].button_release_defined[k] = 0;
293  _ts5_status.timer.cedrusbox[j].button_release_active[k] = 0;
294  }
295 
296  _ts5_status.timer.cedrusbox[j].is_trigger_input_device = 0;
297  _ts5_status.timer.cedrusbox[j].simulate_trigger_input = 0;
298  _ts5_status.timer.cedrusbox[j].trigger_simulation_interval = 2.0;
299  _ts5_status.timer.cedrusbox[j].last_trigger_simulation = al_get_time();
300 
301  j++;
302  }
303  atexit(ts5_uninstall_cedrusbox);
304 
305  cedrusbox_is_installed:
306  ;
307 
308 }
309 
310 
311 ////////////////////////////////////////////////////////////////////////////////
312 /// Uninstall the cedrusbox subsystem.
313 ///
314 /// This function is called automatically at the end of the program.
315 ////////////////////////////////////////////////////////////////////////////////
317 {
319  goto cedrusbox_is_uninstalled;
320  }
321 
322  ts5_log(TS5_LOGLEVEL_1, "Uninstalling Tscope5 cedrusbox\n");
323 
324  int i;
325 
326  // reset voice keys
327  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
329  }
330 
331  // remove thread
332  al_join_thread(_ts5_data.timer.cedrusbox_thread, NULL);
333  al_destroy_thread(_ts5_data.timer.cedrusbox_thread);
334 
335  // unregister event source
336  al_unregister_event_source(_ts5_data.timer.response_queue,
337  &_ts5_data.timer.cedrusbox_response_event_source);
338 
339  // remove event source
340  al_destroy_user_event_source(
341  &_ts5_data.timer.cedrusbox_response_event_source);
342 
343  // unregister event source
344  al_unregister_event_source(_ts5_data.timer.trigger_queue,
345  &_ts5_data.timer.cedrusbox_trigger_event_source);
346 
347  al_unregister_event_source(_ts5_data.timer.trigger_log,
348  &_ts5_data.timer.cedrusbox_trigger_event_source);
349 
350  // remove event source
351  al_destroy_user_event_source(
352  &_ts5_data.timer.cedrusbox_trigger_event_source);
353 
354  // close devices
355  #ifdef TS5_WINDOWS
357 
358  #else
360 
361  #endif
362 
363  // remove button definitions
364  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
365 
366  // free data structure
367  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
368 
369  al_free(_ts5_status.timer.cedrusbox[i].button_press_defined);
370  _ts5_status.timer.cedrusbox[i].button_press_defined = NULL;
371 
372  al_free(_ts5_status.timer.cedrusbox[i].button_press_active);
373  _ts5_status.timer.cedrusbox[i].button_press_active = NULL;
374 
375  al_free(_ts5_status.timer.cedrusbox[i].button_release_defined);
376  _ts5_status.timer.cedrusbox[i].button_release_defined = NULL;
377 
378  al_free(_ts5_status.timer.cedrusbox[i].button_release_active);
379  _ts5_status.timer.cedrusbox[i].button_release_active = NULL;
380  }
381 
382  al_free(_ts5_status.timer.cedrusbox);
383  _ts5_status.timer.cedrusbox=NULL;
384  _ts5_status.timer.num_cedrusboxes = 0;
385  _ts5_status.timer.cedrusbox_is_response_device = 0;
386 
388  }
389 
390  cedrusbox_is_uninstalled:
391  ;
392 }
393 
394 
395 ////////////////////////////////////////////////////////////////////////////////
396 /// Read text from a cedrusbox.
397 ///
398 /// \param port Port number the cedrusbox is attached to.
399 /// \param buff Character buffer.
400 /// \param bytes_to_read Number of bytes to be written.
401 ///
402 /// \return Length of the string that is sent to the port.
403 ///
404 /// \warning This is an internal function, no checks are performed.
405 ////////////////////////////////////////////////////////////////////////////////
406 unsigned int ts5_read_cedrusbox(int port, char *buff,
407  unsigned long bytes_to_read)
408 {
409  ts5_log(TS5_LOGLEVEL_5, "ts5_read_cedrusbox(%d,%s,%lu)\n",
410  port, buff, bytes_to_read);
411 
412  unsigned int retval;
413 
414  #ifdef TS5_WINDOWS
415  retval = ts5_read_cedrusbox_windows(port, buff, bytes_to_read);
416 
417  #else
418  retval = ts5_read_cedrusbox_posix(port, buff, bytes_to_read);
419 
420  #endif
421 
422  return retval;
423 }
424 
425 
426 ////////////////////////////////////////////////////////////////////////////////
427 /// Write text to a cedrusbox.
428 ///
429 /// \param port Port number the cedrusbox is attached to.
430 /// \param buff Character buffer.
431 /// \param bytes_to_write Number of bytes to be written.
432 ///
433 /// \return Length of the string that is read.
434 ///
435 /// \warning This is an internal function, no checks are performed.
436 ////////////////////////////////////////////////////////////////////////////////
437 unsigned int ts5_write_cedrusbox(int port, char *buff,
438  unsigned long bytes_to_write)
439 {
440  ts5_log(TS5_LOGLEVEL_5, "ts5_write_cedrusbox(%d,%s,%lu)\n",
441  port, buff, bytes_to_write);
442 
443  unsigned int retval;
444 
445  #ifdef TS5_WINDOWS
446  retval = ts5_write_cedrusbox_windows(port, buff, bytes_to_write);
447 
448  #else
449  retval = ts5_write_cedrusbox_posix(port, buff, bytes_to_write);
450 
451  #endif
452 
453  return retval;
454 }
455 
456 
457 ////////////////////////////////////////////////////////////////////////////////
458 /// Flush the output of a cedrusbox.
459 ///
460 /// \param port Port number the cedrusbox is attached to.
461 ///
462 /// \warning This is an internal function, no checks are performed.
463 ////////////////////////////////////////////////////////////////////////////////
464 void ts5_fflush_cedrusbox(int port)
465 {
466  ts5_log(TS5_LOGLEVEL_5, "ts5_fflush_cedrusbox(%d)\n", port);
467 
468  #ifdef TS5_WINDOWS
470 
471  #else
473 
474  #endif
475 }
476 
477 
478 ////////////////////////////////////////////////////////////////////////////////
479 /// Cedrusbox thread function.
480 ///
481 /// \warning This is an internal function, no checks are performed.
482 ////////////////////////////////////////////////////////////////////////////////
484 {
485  ALLEGRO_EVENT cedrusbox_event;
486  double previous_time;
487  double current_time = al_get_time();
488 
489  long j=0;
490  while (!al_get_thread_should_stop(_ts5_data.timer.cedrusbox_thread)) {
491 
492  char serial_buff[6] = {0};
493 
494  int i;
495  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
496 
497  unsigned int num_bytes =
498  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
499  serial_buff, 6);
500 
501  j++;
502 
503  // sv-1 voice key is slower
504  // often we need to read twice to get all six bytes
505  if (num_bytes>0 && num_bytes<6 && _ts5_status.timer.cedrusbox[i].type == TS5_CEDRUSBOX_SV1) {
506  ts5_wait(0.001);
507  num_bytes +=
508  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
509  &serial_buff[num_bytes], 6-num_bytes);
510  if(num_bytes<6) {
511  num_bytes=6;
512  }
513  }
514 
515  // lumina box is even slower
516  // does not send "TA" as documented
517  // but response code for a fifth button
518  // we convert this to TA
519  if (num_bytes>0 && num_bytes<6 && _ts5_status.timer.cedrusbox[i].type == TS5_CEDRUSBOX_LUMINA) {
520  ts5_wait(0.003);
521  num_bytes +=
522  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
523  &serial_buff[num_bytes], 6-num_bytes);
524 
525  if(num_bytes<6) {
526  num_bytes=6;
527  }
528 
529  if (serial_buff[0]=='k' && serial_buff[1]==-80) {
530  serial_buff[0] = 'T';
531  serial_buff[1] = 'A';
532  serial_buff[2] = 0;
533  num_bytes = 2;
534  }
535 
536  if (serial_buff[0]=='k' && serial_buff[1]==-96) {
537  num_bytes = 0;
538  }
539  }
540 
541  // monitor responses
542  if (num_bytes==6 && serial_buff[0]=='k') {
543 
544  // action (bit 4)
545  int action = ((unsigned char)(serial_buff[1]) & 16) >> 4;
546 
547  if (action==0) {
548  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_BUTTON_UP;
549  }
550 
551  if (action==1) {
552  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_BUTTON_DOWN;
553  }
554 
555  // box
556  cedrusbox_event.user.data1 = i;
557 
558  // button (bit 5-7)
559  cedrusbox_event.user.data2 =
560  ((unsigned char)serial_buff[1] >> 5) - 1;
561 
562  // timing error
563  cedrusbox_event.user.data3 =
564  (intptr_t)((current_time-previous_time)*1000000.0);
565 
566  // cedrus time stamp (byte 2-5)
567  intptr_t cedrus_time = 0;
568 
569  cedrus_time += (unsigned char)(serial_buff[2]);
570  cedrus_time += (unsigned char)(serial_buff[3]) * (256);
571  cedrus_time += (unsigned char)(serial_buff[4]) * (256*256);
572  cedrus_time += (unsigned char)(serial_buff[5]) * (256*256*256);
573 
574  cedrusbox_event.user.data4 = cedrus_time;
575 
576  al_emit_user_event(
577  &_ts5_data.timer.cedrusbox_response_event_source,
578  &cedrusbox_event, NULL);
579  }
580 
581  // monitor triggers
582  if (!strcmp(serial_buff, "TA")) {
583 
584  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_TRIGGER;
585 
586  // box
587  cedrusbox_event.user.data1 = i;
588 
589  // value, always TA
590  cedrusbox_event.user.data2 = serial_buff[0];
591 
592  // timing error
593  cedrusbox_event.user.data3 =
594  (intptr_t)((current_time-previous_time)*1000000.0);
595 
596  al_emit_user_event(
597  &_ts5_data.timer.cedrusbox_trigger_event_source,
598  &cedrusbox_event, NULL);
599  }
600 
601  // simulate triggers
602  if (_ts5_status.timer.cedrusbox[i].simulate_trigger_input) {
603 
604  if (current_time - _ts5_status.timer.cedrusbox[i]
605  .last_trigger_simulation
606  >= _ts5_status.timer.cedrusbox[i]
607  .trigger_simulation_interval) {
608 
609  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_TRIGGER;
610 
611  cedrusbox_event.user.data1 = i;
612 
613  cedrusbox_event.user.data2 = 'T';
614 
615  cedrusbox_event.user.data3 =
616  (intptr_t)((current_time-previous_time)*1000000.0);
617 
618  al_emit_user_event(
619  &_ts5_data.timer.cedrusbox_trigger_event_source,
620  &cedrusbox_event, NULL);
621 
622  _ts5_status.timer.cedrusbox[i].last_trigger_simulation =
623  current_time;
624  }
625  }
626  }
627  previous_time = current_time;
628  current_time = al_get_time();
629  }
630 
631  return NULL;
632 }
633 
int _ts5_is_cedrusbox_installed
Is the cedrusbox subsystem installed?
int ts5_install_cedrusbox_posix(char *calling_function)
Posix-specific (Mac OS X, Linux) cedrusbox function.
void ts5_fflush_cedrusbox_windows(int port)
Windows-specific cedrusbox function.
void ts5_uninstall_cedrusbox()
Uninstall the cedrusbox subsystem.
Definitions of Windows-specific cedrusbox functions.
void ts5_wait(double waittime)
Wait for a number of seconds.
Definition: timer.c:220
int _ts5_is_timer_installed
Is the timer subsystem installed?
Definitions of Posix-specific (Mac OS X, Linux) cedrusbox functions.
void ts5_install_cedrusbox(char *calling_function)
Install the cedrusbox subsystem.
unsigned int ts5_read_cedrusbox(int port, char *buff, unsigned long bytes_to_read)
Read text from a cedrusbox.
void ts5_uninstall_cedrusbox_posix()
Posix-specific (Mac OS X, Linux) cedrusbox function.
int ts5_install_cedrusbox_windows(char *calling_function)
Windows-specific cedrusbox function.
void ts5_fflush_cedrusbox(int port)
Flush the output of a cedrusbox.
unsigned int ts5_write_cedrusbox_windows(int port, char *buff, unsigned long bytes_to_write)
Windows-specific cedrusbox 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_cedrusbox_threadfunc()
Cedrusbox thread function.
unsigned int ts5_read_cedrusbox_posix(int port, char *buff, unsigned long bytes_to_read)
Posix-specific (Mac OS X, Linux) cedrusbox function.
void ts5_fflush_cedrusbox_posix(int port)
Posix-specific (Mac OS X, Linux) cedrusbox function.
unsigned int ts5_write_cedrusbox(int port, char *buff, unsigned long bytes_to_write)
Write text to a cedrusbox.
void ts5_uninstall_cedrusbox_windows()
Windows-specific cedrusbox function.
void ts5_check_cedrusbox(char *calling_function)
Do some checks at the start of each cedrusbox function.
unsigned int ts5_read_cedrusbox_windows(int port, char *buff, unsigned long bytes_to_read)
Windows-specific cedrusbox function.
void ts5_fatal(const char *format,...)
Exit safely with an error message.
Definition: system.c:529
unsigned int ts5_write_cedrusbox_posix(int port, char *buff, unsigned long bytes_to_write)
Posix-specific (Mac OS X, Linux) cedrusbox function.