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 
320  ts5_log(TS5_LOGLEVEL_1, "Uninstalling Tscope5 cedrusbox\n");
321 
322  int i;
323 
324  // reset voice keys
325  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
327  }
328 
329  // remove thread
330  al_join_thread(_ts5_data.timer.cedrusbox_thread, NULL);
331  al_destroy_thread(_ts5_data.timer.cedrusbox_thread);
332 
333  // unregister event source
334  al_unregister_event_source(_ts5_data.timer.response_queue,
335  &_ts5_data.timer.cedrusbox_response_event_source);
336 
337  // remove event source
338  al_destroy_user_event_source(
339  &_ts5_data.timer.cedrusbox_response_event_source);
340 
341  // unregister event source
342  al_unregister_event_source(_ts5_data.timer.trigger_queue,
343  &_ts5_data.timer.cedrusbox_trigger_event_source);
344 
345  al_unregister_event_source(_ts5_data.timer.trigger_log,
346  &_ts5_data.timer.cedrusbox_trigger_event_source);
347 
348  // remove event source
349  al_destroy_user_event_source(
350  &_ts5_data.timer.cedrusbox_trigger_event_source);
351 
352  // close devices
353  #ifdef TS5_WINDOWS
355 
356  #else
358 
359  #endif
360 
361  // remove button definitions
362  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
363 
364  // free data structure
365  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
366 
367  al_free(_ts5_status.timer.cedrusbox[i].button_press_defined);
368  _ts5_status.timer.cedrusbox[i].button_press_defined = NULL;
369 
370  al_free(_ts5_status.timer.cedrusbox[i].button_press_active);
371  _ts5_status.timer.cedrusbox[i].button_press_active = NULL;
372 
373  al_free(_ts5_status.timer.cedrusbox[i].button_release_defined);
374  _ts5_status.timer.cedrusbox[i].button_release_defined = NULL;
375 
376  al_free(_ts5_status.timer.cedrusbox[i].button_release_active);
377  _ts5_status.timer.cedrusbox[i].button_release_active = NULL;
378  }
379 
380  al_free(_ts5_status.timer.cedrusbox);
381  _ts5_status.timer.cedrusbox=NULL;
382  _ts5_status.timer.num_cedrusboxes = 0;
383  _ts5_status.timer.cedrusbox_is_response_device = 0;
384  _ts5_status.timer.cedrusbox_is_trigger_device = 0;
385 
387  }
388  }
389 }
390 
391 
392 ////////////////////////////////////////////////////////////////////////////////
393 /// Read text from a cedrusbox.
394 ///
395 /// \param port Port number the cedrusbox is attached to.
396 /// \param buff Character buffer.
397 /// \param bytes_to_read Number of bytes to be written.
398 ///
399 /// \return Length of the string that is sent to the port.
400 ///
401 /// \warning This is an internal function, no checks are performed.
402 ////////////////////////////////////////////////////////////////////////////////
403 unsigned int ts5_read_cedrusbox(int port, char *buff,
404  unsigned long bytes_to_read)
405 {
406  ts5_log(TS5_LOGLEVEL_5, "ts5_read_cedrusbox(%d,%s,%lu)\n",
407  port, buff, bytes_to_read);
408 
409  unsigned int retval;
410 
411  #ifdef TS5_WINDOWS
412  retval = ts5_read_cedrusbox_windows(port, buff, bytes_to_read);
413 
414  #else
415  retval = ts5_read_cedrusbox_posix(port, buff, bytes_to_read);
416 
417  #endif
418 
419  return retval;
420 }
421 
422 
423 ////////////////////////////////////////////////////////////////////////////////
424 /// Write text to a cedrusbox.
425 ///
426 /// \param port Port number the cedrusbox is attached to.
427 /// \param buff Character buffer.
428 /// \param bytes_to_write Number of bytes to be written.
429 ///
430 /// \return Length of the string that is read.
431 ///
432 /// \warning This is an internal function, no checks are performed.
433 ////////////////////////////////////////////////////////////////////////////////
434 unsigned int ts5_write_cedrusbox(int port, char *buff,
435  unsigned long bytes_to_write)
436 {
437  ts5_log(TS5_LOGLEVEL_5, "ts5_write_cedrusbox(%d,%s,%lu)\n",
438  port, buff, bytes_to_write);
439 
440  unsigned int retval;
441 
442  #ifdef TS5_WINDOWS
443  retval = ts5_write_cedrusbox_windows(port, buff, bytes_to_write);
444 
445  #else
446  retval = ts5_write_cedrusbox_posix(port, buff, bytes_to_write);
447 
448  #endif
449 
450  return retval;
451 }
452 
453 
454 ////////////////////////////////////////////////////////////////////////////////
455 /// Flush the output of a cedrusbox.
456 ///
457 /// \param port Port number the cedrusbox is attached to.
458 ///
459 /// \warning This is an internal function, no checks are performed.
460 ////////////////////////////////////////////////////////////////////////////////
461 void ts5_fflush_cedrusbox(int port)
462 {
463  ts5_log(TS5_LOGLEVEL_5, "ts5_fflush_cedrusbox(%d)\n", port);
464 
465  #ifdef TS5_WINDOWS
467 
468  #else
470 
471  #endif
472 }
473 
474 
475 ////////////////////////////////////////////////////////////////////////////////
476 /// Cedrusbox thread function.
477 ///
478 /// \warning This is an internal function, no checks are performed.
479 ////////////////////////////////////////////////////////////////////////////////
481 {
482  ALLEGRO_EVENT cedrusbox_event;
483  double previous_time;
484  double current_time = al_get_time();
485 
486  long j=0;
487  while (!al_get_thread_should_stop(_ts5_data.timer.cedrusbox_thread)) {
488 
489  char serial_buff[6] = {0};
490 
491  int i;
492  for (i=0; i<_ts5_status.timer.num_cedrusboxes; i++) {
493 
494  unsigned int num_bytes =
495  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
496  serial_buff, 6);
497 
498  j++;
499 
500  // sv-1 voice key is slower
501  // often we need to read twice to get all six bytes
502  if (num_bytes>0 && num_bytes<6 && _ts5_status.timer.cedrusbox[i].type == TS5_CEDRUSBOX_SV1) {
503  ts5_wait(0.001);
504  num_bytes +=
505  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
506  &serial_buff[num_bytes], 6-num_bytes);
507  if(num_bytes<6) {
508  num_bytes=6;
509  }
510  }
511 
512  // lumina box is even slower
513  // does not send "TA" as documented
514  // but response code for a fifth button
515  // we convert this to TA
516  if (num_bytes>0 && num_bytes<6 && _ts5_status.timer.cedrusbox[i].type == TS5_CEDRUSBOX_LUMINA) {
517  ts5_wait(0.003);
518  num_bytes +=
519  ts5_read_cedrusbox(_ts5_status.timer.cedrusbox[i].port_num,
520  &serial_buff[num_bytes], 6-num_bytes);
521 
522  if(num_bytes<6) {
523  num_bytes=6;
524  }
525 
526  if (serial_buff[0]=='k' && serial_buff[1]==-80) {
527  serial_buff[0] = 'T';
528  serial_buff[1] = 'A';
529  serial_buff[2] = 0;
530  num_bytes = 2;
531  }
532 
533  if (serial_buff[0]=='k' && serial_buff[1]==-96) {
534  num_bytes = 0;
535  }
536  }
537 
538  // monitor responses
539  if (num_bytes==6 && serial_buff[0]=='k') {
540 
541  // action (bit 4)
542  int action = ((unsigned char)(serial_buff[1]) & 16) >> 4;
543 
544  if (action==0) {
545  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_BUTTON_UP;
546  }
547 
548  if (action==1) {
549  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_BUTTON_DOWN;
550  }
551 
552  // box
553  cedrusbox_event.user.data1 = i;
554 
555  // button (bit 5-7)
556  cedrusbox_event.user.data2 =
557  ((unsigned char)serial_buff[1] >> 5) - 1;
558 
559  // timing error
560  cedrusbox_event.user.data3 =
561  (intptr_t)((current_time-previous_time)*1000000.0);
562 
563  // cedrus time stamp (byte 2-5)
564  intptr_t cedrus_time = 0;
565 
566  cedrus_time += (unsigned char)(serial_buff[2]);
567  cedrus_time += (unsigned char)(serial_buff[3]) * (256);
568  cedrus_time += (unsigned char)(serial_buff[4]) * (256*256);
569  cedrus_time += (unsigned char)(serial_buff[5]) * (256*256*256);
570 
571  cedrusbox_event.user.data4 = cedrus_time;
572 
573  al_emit_user_event(
574  &_ts5_data.timer.cedrusbox_response_event_source,
575  &cedrusbox_event, NULL);
576  }
577 
578  // monitor triggers
579  if (!strcmp(serial_buff, "TA")) {
580 
581  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_TRIGGER;
582 
583  // box
584  cedrusbox_event.user.data1 = i;
585 
586  // value, always TA
587  cedrusbox_event.user.data2 = serial_buff[0];
588 
589  // timing error
590  cedrusbox_event.user.data3 =
591  (intptr_t)((current_time-previous_time)*1000000.0);
592 
593  al_emit_user_event(
594  &_ts5_data.timer.cedrusbox_trigger_event_source,
595  &cedrusbox_event, NULL);
596  }
597 
598  // simulate triggers
599  if (_ts5_status.timer.cedrusbox[i].simulate_trigger_input) {
600 
601  if (current_time - _ts5_status.timer.cedrusbox[i]
602  .last_trigger_simulation
603  >= _ts5_status.timer.cedrusbox[i]
604  .trigger_simulation_interval) {
605 
606  cedrusbox_event.user.type = TS5_EVENT_CEDRUSBOX_TRIGGER;
607 
608  cedrusbox_event.user.data1 = i;
609 
610  cedrusbox_event.user.data2 = 'T';
611 
612  cedrusbox_event.user.data3 =
613  (intptr_t)((current_time-previous_time)*1000000.0);
614 
615  al_emit_user_event(
616  &_ts5_data.timer.cedrusbox_trigger_event_source,
617  &cedrusbox_event, NULL);
618 
619  _ts5_status.timer.cedrusbox[i].last_trigger_simulation =
620  current_time;
621  }
622  }
623  }
624  previous_time = current_time;
625  current_time = al_get_time();
626  }
627 
628  return NULL;
629 }
630 
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:533
unsigned int ts5_write_cedrusbox_posix(int port, char *buff, unsigned long bytes_to_write)
Posix-specific (Mac OS X, Linux) cedrusbox function.