1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
|
#include <assert.h>
#include "stdafx.h"
#include "cphidget.h"
#include "cphidgetmanager.h"
#include "cusb.h"
#include "cphidgetlist.h"
CThread_func_return_t CentralThreadFunction(CThread_func_arg_t arg);
static CThread CentralThread;
static int checkForDevicesEventInitialized = PFALSE;
static EVENT checkForDevicesEvent;
/* used by OSX to pause traffic during sleep */
int pause_usb_traffic = PFALSE;
int usb_write_paused = PFALSE;
int usb_read_paused = PFALSE;
#ifdef _MACOSX
void macPeriodicTimerFunction(CFRunLoopTimerRef timer, void *Handle);
void macFindActiveDevicesSource(void *nothing);
CFRunLoopTimerRef timer = NULL;
CFRunLoopSourceRef findActiveDevicesSource = NULL;
#endif
int
StartCentralThread()
{
#ifdef _WINDOWS
if (CentralThread.m_ThreadHandle) {
int threadStatus = 0;
int result = 0;
result = GetExitCodeThread(CentralThread.m_ThreadHandle,
(LPDWORD)&threadStatus);
if (result) {
if (threadStatus != STILL_ACTIVE) {
CloseHandle(CentralThread.m_ThreadHandle);
CentralThread.m_ThreadHandle = 0;
}
}
}
#endif
if(checkForDevicesEventInitialized == PFALSE)
{
checkForDevicesEventInitialized = PTRUE;
CThread_create_event(&checkForDevicesEvent);
}
#ifdef _MACOSX
if(findActiveDevicesSource == NULL)
{
CFRunLoopSourceContext sourceContext = {0};
sourceContext.perform = macFindActiveDevicesSource;
findActiveDevicesSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sourceContext);
}
if(timer == NULL)
timer = CFRunLoopTimerCreate(kCFAllocatorDefault, 0, 0.250, 0, 0, macPeriodicTimerFunction, NULL);
#endif
//start the thread
if (!CentralThread.m_ThreadHandle || CentralThread.thread_status == FALSE)
{
#ifdef _MACOSX
CentralThread.macInitDone = PFALSE;
#endif
CThread_reset_event(&checkForDevicesEvent);
if (CThread_create(&CentralThread, CentralThreadFunction, 0))
return EPHIDGET_UNEXPECTED;
CentralThread.thread_status = TRUE;
}
//if it was already running, signal it to check for device matches NOW
else {
#ifdef _MACOSX
//make sure mac thread stuff is initialized
while(!CentralThread.macInitDone)
SLEEP(10);
//run findActiveDevices in the context of the central thread
CFRunLoopSourceSignal(findActiveDevicesSource);
CFRunLoopWakeUp(CentralThread.runLoop);
#else
//signal thread to poll and findActiveDevices
CThread_set_event(&checkForDevicesEvent);
#endif
}
return EPHIDGET_OK;
}
int
JoinCentralThread()
{
if(CentralThread.m_ThreadHandle && !CThread_is_my_thread(CentralThread))
{
#ifdef _MACOSX
while(!CentralThread.macInitDone)
SLEEP(10);
CPhidgetManager_teardownNotifications();
CFRunLoopRemoveTimer(CentralThread.runLoop, timer, kCFRunLoopDefaultMode);
CFRunLoopRemoveSource(CentralThread.runLoop, findActiveDevicesSource, kCFRunLoopDefaultMode);
CFRunLoopStop(CentralThread.runLoop);
CentralThread.macInitDone = PFALSE;
#endif
CThread_join(&CentralThread);
CentralThread.m_ThreadHandle = 0;
}
return EPHIDGET_OK;
}
/*
* registers a device to recieve events and be polled by the central
* thread This needs to start the central thread if it's not yet
* running.
*/
int
RegisterLocalDevice(CPhidgetHandle phid)
{
int result;
TESTPTR(phid)
if(!phidgetLocksInitialized)
{
CThread_mutex_init(&activeDevicesLock);
CThread_mutex_init(&attachedDevicesLock);
phidgetLocksInitialized = PTRUE;
}
CThread_mutex_lock(&activeDevicesLock);
if(phid->specificDevice == PHIDGETOPEN_SERIAL || phid->specificDevice == PHIDGETOPEN_LABEL)
result = CList_addToList((CListHandle *)&ActiveDevices, phid, CPhidget_areEqual);
else
result = CList_addToList((CListHandle *)&ActiveDevices, phid, CPhidgetHandle_areEqual);
if (result)
{
CThread_mutex_unlock(&activeDevicesLock);
return result;
}
CThread_mutex_unlock(&activeDevicesLock);
result = StartCentralThread();
return result;
}
#ifdef _MACOSX
//this is run every 250ms from the CentralThread runLoop
void macPeriodicTimerFunction(CFRunLoopTimerRef timer, void *Handle) {
CPhidgetList *trav = 0;
//looks for any devices that have set PHIDGET_USB_ERROR_FLAG and reenumerate them
CThread_mutex_lock(&activeDevicesLock);
for (trav=ActiveDevices; trav; trav = trav->next)
{
if(CPhidget_statusFlagIsSet(trav->phid->status, PHIDGET_ATTACHED_FLAG))
{
if(CPhidget_statusFlagIsSet(trav->phid->status, PHIDGET_USB_ERROR_FLAG))
{
LOG(PHIDGET_LOG_WARNING,"PHIDGET_USB_ERROR_FLAG is set - cycling device through a reenumeration.");
reenumerateDevice(trav->phid);
}
}
}
CThread_mutex_unlock(&activeDevicesLock);
//need to always keep checking because a device could be opened by another app
//and we want to notice when it becomes accessible.
if(ActiveDevices) {
findActiveDevices(); //this looks for attached active devices and opens them
}
return;
}
//This is run when a new Phidget is registered when the CentralThread is already running
void macFindActiveDevicesSource(void *nothing) {
if(ActiveDevices) {
findActiveDevices(); //this looks for attached active devices and opens them
}
return;
}
#endif
//The central thread should stop itself when there are no more active devices...?
//Or we can stop it in unregisterlocaldevice
CThread_func_return_t CentralThreadFunction(CThread_func_arg_t lpdwParam)
{
#ifdef _MACOSX
CentralThread.runLoop = CFRunLoopGetCurrent();
//setup notifications of Phidget attach/detach
CPhidgetManager_setupNotifications(CentralThread.runLoop);
CFRunLoopAddTimer(CentralThread.runLoop, timer, kCFRunLoopDefaultMode);
CFRunLoopAddSource(CentralThread.runLoop, findActiveDevicesSource, kCFRunLoopDefaultMode);
CentralThread.macInitDone = PTRUE;
//start run loop - note that this blocks until JoinCentralThread() is called.
CFRunLoopRun();
#else
//loop as long as there are active devices, or a phidget manager
while(ActiveDevices || ActivePhidgetManagers) {
CPhidgetManager_poll(); //this will update the list, as well as sending out attach and detach events
findActiveDevices(); //this looks for attached active devices and opens them
//Wait for signal of newly registered device, or 250ms
//we don't really care about the reason so we don't check the return value
CThread_wait_on_event(&checkForDevicesEvent, 250);
CThread_reset_event(&checkForDevicesEvent);
}
#endif
//if we actually get here, it means there are no active devices, and no phidgetmanagers, so free up the
//last of the memory we are using, and exit the thread...
CThread_mutex_lock(&attachedDevicesLock);
CList_emptyList((CListHandle *)&AttachedDevices, TRUE, CPhidget_free);
CThread_mutex_unlock(&attachedDevicesLock);
LOG(PHIDGET_LOG_INFO,"Central Thread exiting");
CentralThread.thread_status = FALSE;
return EPHIDGET_OK;
}
//The read thread
CThread_func_return_t ReadThreadFunction(CThread_func_arg_t lpdwParam)
{
CPhidgetHandle phid = (CPhidgetHandle)lpdwParam;
int result = EPHIDGET_OK;
LOG(PHIDGET_LOG_INFO,"ReadThread running");
if (!phid)
{
LOG(PHIDGET_LOG_ERROR,"ReadThread exiting - Invalid device handle");
return (CThread_func_return_t)EPHIDGET_INVALIDARG;
}
//quit read thread if it's not needed
switch(phid->deviceID)
{
case PHIDCLASS_SERVO:
if(phid->deviceVersion < 313)
goto exit_not_needed;
break;
case PHIDCLASS_INTERFACEKIT:
if(phid->deviceIDSpec == PHIDID_INTERFACEKIT_0_0_4 && phid->deviceVersion < 704)
goto exit_not_needed;
break;
case PHIDCLASS_LED:
if(phid->deviceIDSpec == PHIDID_LED_64)
goto exit_not_needed;
break;
case PHIDCLASS_TEXTLCD:
if(phid->deviceIDSpec != PHIDID_TEXTLCD_ADAPTER)
goto exit_not_needed;
break;
case PHIDCLASS_TEXTLED:
exit_not_needed:
LOG(PHIDGET_LOG_INFO,"ReadThread exiting normally (Not Needed for this device)");
goto exit;
default:
break;
}
while (CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG))
{
if(pause_usb_traffic)
{
usb_read_paused = PTRUE;
SLEEP(20);
continue;
}
else
usb_read_paused = PFALSE;
result=CPhidget_read(phid);
switch(result)
{
case EPHIDGET_OK:
case EPHIDGET_TRYAGAIN:
break;
case EPHIDGET_NOTATTACHED:
LOG(PHIDGET_LOG_INFO,"ReadThread exiting normally (Phidget detach detected in CPhidget_read)");
goto exit;
case EPHIDGET_INTERRUPTED:
LOG(PHIDGET_LOG_INFO,"ReadThread exiting normally (signaled by CPhidget_close)");
goto exit;
case EPHIDGET_TIMEOUT:
//Set the error flag for devices that should never have a USB timeout, then exit
//continue for devices where it is ok
switch(phid->deviceID)
{
case PHIDCLASS_INTERFACEKIT:
switch(phid->deviceIDSpec)
{
case PHIDID_INTERFACEKIT_0_16_16:
if(phid->deviceVersion >= 601)
goto timeout_not_ok;
else
goto timeout_ok;
case PHIDID_INTERFACEKIT_0_8_8_w_LCD:
goto timeout_ok;
default:
goto timeout_not_ok;
}
case PHIDCLASS_RFID:
if(phid->deviceVersion >= 201)
goto timeout_not_ok;
else
goto timeout_ok;
case PHIDCLASS_ENCODER:
if(phid->deviceIDSpec == PHIDID_ENCODER_HS_4ENCODER_4INPUT)
goto timeout_not_ok;
else
goto timeout_ok;
case PHIDCLASS_GENERIC:
goto timeout_ok;
default:
goto timeout_not_ok;
}
timeout_not_ok:
CPhidget_setStatusFlag(&phid->status, PHIDGET_USB_ERROR_FLAG, &phid->lock);
LOG(PHIDGET_LOG_ERROR,"ReadThread exiting - unexpected timeout (could be an ESD event)");
goto exit;
timeout_ok:
LOG(PHIDGET_LOG_VERBOSE,"CUSBReadPacket expected time out"); //verbose because it could happen a LOT
break;
case EPHIDGET_UNEXPECTED:
default:
LOG(PHIDGET_LOG_ERROR,"ReadThread exiting - CPhidget_read returned : %d",result);
CPhidget_setStatusFlag(&phid->status, PHIDGET_USB_ERROR_FLAG, &phid->lock);
goto exit;
}
}
LOG(PHIDGET_LOG_INFO,"ReadThread exiting normally (Phidget detached)");
exit:
phid->readThread.thread_status = FALSE;
return (CThread_func_return_t)(size_t)result;
}
//The write thread
CThread_func_return_t WriteThreadFunction(CThread_func_arg_t lpdwParam)
{
CPhidgetHandle phid = (CPhidgetHandle)lpdwParam;
int result = EPHIDGET_OK, wait_return = 0;
LOG(PHIDGET_LOG_INFO,"WriteThread running");
if (!phid)
{
LOG(PHIDGET_LOG_ERROR,"WriteThread exiting - Invalid device handle");
return (CThread_func_return_t)EPHIDGET_INVALIDARG;
}
//quit write thread if it's not needed
switch(phid->deviceID)
{
case PHIDCLASS_INTERFACEKIT:
if(phid->deviceIDSpec == PHIDID_LINEAR_TOUCH
|| phid->deviceIDSpec == PHIDID_ROTARY_TOUCH)
goto exit_not_needed;
break;
case PHIDCLASS_RFID:
if(phid->deviceIDSpec == PHIDID_RFID)
goto exit_not_needed;
break;
case PHIDCLASS_ENCODER:
if(phid->deviceIDSpec == PHIDID_ENCODER_1ENCODER_1INPUT
|| phid->deviceIDSpec == PHIDID_ENCODER_HS_1ENCODER)
goto exit_not_needed;
break;
case PHIDCLASS_ACCELEROMETER:
case PHIDCLASS_TEMPERATURESENSOR:
case PHIDCLASS_PHSENSOR:
case PHIDCLASS_WEIGHTSENSOR:
exit_not_needed:
LOG(PHIDGET_LOG_INFO,"WriteThread exiting normally (Not Needed for this device)");
goto exit;
default:
break;
}
while (CPhidget_statusFlagIsSet(phid->status, PHIDGET_ATTACHED_FLAG))
{
//if awdc_enabled is true, then we timeout in 200ms and do a write, otherwise no timeout
wait_return = CThread_wait_on_event(&phid->writeAvailableEvent, 200);
switch (wait_return) {
case WAIT_TIMEOUT:
//putting this in the timeout so actual writes are not missed.
if(phid->writeStopFlag)
{
LOG(PHIDGET_LOG_INFO,"WriteThread exiting normally (signaled by writeStopFlag)");
goto exit;
}
if(!phid->awdc_enabled) //only fall through to writting out if awdc is set
break;
case WAIT_OBJECT_0: //writeAvailable
if(pause_usb_traffic)
{
usb_write_paused = PTRUE;
break;
}
else
usb_write_paused = PFALSE;
if((result = CPhidget_write(phid)))
{
switch(result)
{
case EPHIDGET_NOTATTACHED:
LOG(PHIDGET_LOG_INFO,"WriteThread exiting normally (Phidget detach detected in CPhidget_write)");
break;
case EPHIDGET_INTERRUPTED:
LOG(PHIDGET_LOG_INFO,"WriteThread exiting normally (signaled by CPhidget_close)");
break;
case EPHIDGET_TIMEOUT:
LOG(PHIDGET_LOG_ERROR,"WriteThread exiting - unexpected timeout (could be an ESD event)");
CPhidget_setStatusFlag(&phid->status, PHIDGET_USB_ERROR_FLAG, &phid->lock);
break;
case EPHIDGET_UNEXPECTED:
default:
LOG(PHIDGET_LOG_ERROR,"WriteThread exiting - CPhidget_write returned : %d",result);
CPhidget_setStatusFlag(&phid->status, PHIDGET_USB_ERROR_FLAG, &phid->lock);
break;
}
goto exit;
}
break;
default:
LOG(PHIDGET_LOG_ERROR,"WriteThread exiting - wait on phid->writeAvailableEvent failed");
CPhidget_setStatusFlag(&phid->status, PHIDGET_USB_ERROR_FLAG, &phid->lock);
result = EPHIDGET_UNEXPECTED;
goto exit;
}
}
LOG(PHIDGET_LOG_INFO,"WriteThread exiting normally (Phidget detached)");
exit:
phid->writeStopFlag = FALSE;
phid->writeThread.thread_status = FALSE;
return (CThread_func_return_t)(size_t)result;
}
int
CThread_create(CThread *cp, CThread_func_t fp, CThread_func_arg_t arg)
{
#ifdef _WINDOWS
cp->m_ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fp, arg, 0, &cp->m_ThreadIdentifier);
if(cp->m_ThreadHandle) return EPHIDGET_OK;
else return GetLastError();
#else
return pthread_create(&cp->m_ThreadHandle, NULL, fp, arg);
#endif
}
int
CThread_create_detached(CThread *cp, CThread_func_t fp, CThread_func_arg_t arg)
{
#ifdef _WINDOWS
cp->m_ThreadHandle = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)fp, arg, 0, &cp->m_ThreadIdentifier);
if(cp->m_ThreadHandle) return EPHIDGET_OK;
else return GetLastError();
#else
pthread_attr_t attr;
int err;
if((err = pthread_attr_init(&attr)) == 0)
{
if((err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) == 0)
{
return pthread_create(&cp->m_ThreadHandle, &attr, fp, arg);
}
else
{
LOG(PHIDGET_LOG_ERROR, "pthread_attr_setdetachstate failed with error: %d",err);
return err;
}
}
else
{
LOG(PHIDGET_LOG_ERROR, "pthread_attr_init failed with error: %d",err);
return err;
}
#endif
}
int
CThread_is_my_thread(CThread cp)
{
#ifdef _WINDOWS
return (cp.m_ThreadIdentifier == GetCurrentThreadId());
#else
return pthread_equal(cp.m_ThreadHandle, pthread_self());
#endif
}
void
CThread_join(CThread *cp)
{
#ifdef _WINDOWS
DWORD ec;
while (GetExitCodeThread(cp->m_ThreadHandle, &ec) && ec == STILL_ACTIVE)
SLEEP(10);
#else
if (cp->thread_status == TRUE)
pthread_join(cp->m_ThreadHandle, 0);
#endif
}
/*void
CThread_kill(CThread *cp)
{
#ifdef _WINDOWS
TerminateThread(cp->m_ThreadHandle, 0);
#else
pthread_cancel(cp->m_ThreadHandle);
#endif
}*/
int
CThread_mutex_init(CThread_mutex_t *mp)
{
#ifdef _WINDOWS
InitializeCriticalSection(mp);
return 1;
#else
return pthread_mutex_init(mp, NULL) == 0;
#endif
}
int
CThread_mutex_destroy(CThread_mutex_t *mp)
{
#ifdef _WINDOWS
DeleteCriticalSection(mp);
return 1;
#else
return pthread_mutex_destroy(mp) == 0;
#endif
}
void
CThread_mutex_lock(CThread_mutex_t *mp)
{
#ifdef _WINDOWS
EnterCriticalSection(mp);
#else
pthread_mutex_lock(mp);
#endif
}
void
CThread_mutex_unlock(CThread_mutex_t *mp)
{
#ifdef _WINDOWS
LeaveCriticalSection(mp);
#else
pthread_mutex_unlock(mp);
#endif
}
void CThread_create_event(EVENT *ev)
{
#ifdef _WINDOWS
*ev = CreateEvent(NULL, FALSE, FALSE, NULL);
#else
pthread_mutex_init(&ev->mutex, NULL);
pthread_cond_init(&ev->condition, NULL);
ev->ready_to_go = PFALSE;
#endif
}
int CThread_destroy_event(EVENT *ev)
{
#ifdef _WINDOWS
return CloseHandle(*ev);
#else
if(pthread_mutex_destroy(&ev->mutex)) return 0;
if(pthread_cond_destroy(&ev->condition)) return 0;
return 1;
#endif
}
int CThread_wait_on_event(EVENT *ev, EVENT_TIME time)
{
#ifdef _WINDOWS
return WaitForSingleObject(*ev, time);
#else
int retval;
struct timespec timeout;
struct timeval now;
// Lock the mutex.
pthread_mutex_lock(&ev->mutex);
// If the predicate is already set, then the while loop is bypassed;
// otherwise, the thread sleeps until the predicate is set.
if(ev->ready_to_go == PFALSE)
{
if(time == INFINITE)
retval = pthread_cond_wait(&ev->condition, &ev->mutex);
else {
gettimeofday(&now,0);
timeout.tv_sec = now.tv_sec + time/1000;
timeout.tv_nsec = now.tv_usec*1000 + (time%1000 * 1000000);
if(timeout.tv_nsec >= 1000000000)
{
timeout.tv_sec++;
timeout.tv_nsec -= 1000000000;
}
retval = pthread_cond_timedwait(&ev->condition, &ev->mutex, &timeout);
}
switch(retval)
{
case ETIMEDOUT:
pthread_mutex_unlock(&ev->mutex);
return WAIT_TIMEOUT;
case 0:
pthread_mutex_unlock(&ev->mutex);
return WAIT_OBJECT_0;
case EINVAL:
pthread_mutex_unlock(&ev->mutex);
return WAIT_FAILED;
default:
pthread_mutex_unlock(&ev->mutex);
return WAIT_FAILED;
}
}
pthread_mutex_unlock(&ev->mutex);
return WAIT_OBJECT_0;
#endif
}
void CThread_reset_event(EVENT *ev)
{
#ifdef _WINDOWS
ResetEvent(*ev);
#else
// Reset the predicate and release the mutex.
pthread_mutex_lock(&ev->mutex);
ev->ready_to_go = PFALSE;
pthread_mutex_unlock(&ev->mutex);
#endif
}
void CThread_set_event(EVENT *ev)
{
#ifdef _WINDOWS
SetEvent(*ev);
#else
// At this point, there should be work for the other thread to do.
pthread_mutex_lock(&ev->mutex);
ev->ready_to_go = PTRUE;
// Signal the other thread to begin work.
pthread_cond_signal(&ev->condition);
pthread_mutex_unlock(&ev->mutex);
#endif
}
|