-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathUSBProtocol.h
1631 lines (1617 loc) · 91.2 KB
/
USBProtocol.h
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
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
// USB Message Protocol
//
// This file is purely for documentation, to describe our USB protocol
// for incoming messages (host to device). We use the standard HID setup
// with one endpoint in each direction. See USBJoystick.cpp and .h for
// the USB descriptors.
//
// Our incoming message protocol is an extended version of the protocol
// used by the LedWiz. Our protocol is designed to be 100% backwards
// compatible with clients using the original LedWiz wire protocol, as long
// as they only send well-formed messages in the original protocol. The
// "well-formed" part is an important condition, because our extensions to
// the original protocol all consist of messages that aren't defined in the
// original protocol and are meaningless to a real LedWiz.
//
// The protocol compatibility ensures that all original LedWiz clients can
// also transparently access a Pinscape unit. Clients will simply think the
// Pinscape unit is an LedWiz, thus they'll be able to operate 32 of our
// ports. We designate the first 32 ports (ports 1-32) as the ones accessible
// through the LedWiz protocol.
//
// In addition the wire-level protocol compatibility, we can provide legacy
// LedWiz clients with access to more than 32 ports by emulating multiple
// virtual LedWiz units. We can't do this across the wire protocol, since
// the KL25Z USB interface constrains us to a single VID/PID (which is how
// LedWiz clients distinguish units). However, virtuall all legacy LedWiz
// clients access the device through a shared library, LEDWIZ.DLL, rather
// than directly through USB. LEDWIZ.DLL is distributed by the LedWiz's
// manufacturer and has a published client interface. We can thus provide
// a replacement DLL that contains the logic needed to recognize a Pinscape
// unit and represent it to clients as multiple LedWiz devices. This allows
// old clients to access our full complement of ports without any changes
// to the clients. We define some extended message types (SBX and PBX)
// specifically to support this DLL feature.
//
// ------ OUTGOING MESSAGES (DEVICE TO HOST) ------
//
// General note: 16-bit and 32-bit fields in our reports are little-endian
// unless otherwise specified.
//
// 1. Joystick reports
// In most cases, our outgoing messages are HID joystick reports, using the
// format defined in USBJoystick.cpp. This allows us to be installed on
// Windows as a standard USB joystick, which all versions of Windows support
// using in-the-box drivers. This allows a completely transparent, driverless,
// plug-and-play installation experience on Windows. Our joystick report
// looks like this (see USBJoystick.cpp for the formal HID report descriptor):
//
// ss status bits:
// 0x01 -> plunger enabled
// 0x02 -> night mode engaged
// 0x04,0x08,0x10 -> power sense status: meaningful only when
// the TV-on timer is used. Figure (ss>>2) & 0x07 to
// isolate the status bits. The resulting value is:
// 1 -> latch was on at last check
// 2 -> latch was off at last check, SET pin high
// 3 -> latch off, SET pin low, ready to check status
// 4 -> TV timer countdown in progress
// 5 -> TV relay is on
// 6 -> sending IR signals designated as TV ON signals
// 0x20 -> IR learning mode in progress
// 0x40 -> configuration saved successfully (see below)
// 00 2nd byte of status (reserved)
// 00 3rd byte of status (reserved)
// 00 always zero for joystick reports
// bb joystick buttons, low byte (buttons 1-8, 1 bit per button)
// bb joystick buttons, 2nd byte (buttons 9-16)
// bb joystick buttons, 3rd byte (buttons 17-24)
// bb joystick buttons, high byte (buttons 25-32)
// xx low byte of X position = nudge/accelerometer X axis
// xx high byte of X position
// yy low byte of Y position = nudge/accelerometer Y axis
// yy high byte of Y position
// zz low byte of Z position = plunger position
// zz high byte of Z position
//
// The X, Y, and Z values are 16-bit signed integers. The accelerometer
// values are on an abstract scale, where 0 represents no acceleration,
// negative maximum represents -1g on that axis, and positive maximum
// represents +1g on that axis. For the plunger position, 0 is the park
// position (the rest position of the plunger) and positive values represent
// retracted (pulled back) positions. A negative value means that the plunger
// is pushed forward of the park position.
//
// Status bit 0x40 is set after a successful configuration update via special
// command 65 6 (save config to flash). The device always reboots after this
// command, so if the host wants to receive a status update verifying the
// save, it has to request a non-zero reboot delay in the message to allow
// us time to send at least one of these status reports after the save.
// This bit is only sent after a successful save, which means that the flash
// write succeeded and the written sectors verified as correct.
// NOTE: older firmware versions didn't support this status bit, so clients
// can't interpret the lack of a response as a failure for older versions.
// To determine if the flag is supported, check the config report feature
// flags.
//
//
// 2. Special reports
// We subvert the joystick report format in certain cases to report other
// types of information, when specifically requested by the host. This allows
// our custom configuration UI on the Windows side to query additional
// information that we don't normally send via the joystick reports. We
// define a custom vendor-specific "status" field in the reports that we
// use to identify these special reports, as described below.
//
// Normal joystick reports always have 0 in the high bit of the 2nd byte
// of the report. Special non-joystick reports always have 1 in the high bit
// of the first byte. (This byte is defined in the HID Report Descriptor
// as an opaque vendor-defined value, so the joystick interface on the
// Windows side simply ignores it.)
//
// 2A. Plunger sensor status report
// Software on the PC can request a detailed status report from the plunger
// sensor. The status information is meant as an aid to installing and
// adjusting the sensor device for proper performance. For imaging sensor
// types, the status report includes a complete current image snapshot
// (an array of all of the pixels the sensor is currently imaging). For
// all sensor types, it includes the current plunger position registered
// on the sensor, and some timing information.
//
// To request the sensor status, the host sends custom protocol message 65 3
// (see below). The device replies with a message in this format:
//
// bytes 0:1 = 0x87FF
// byte 2 = 0 -> first status report packet
// bytes 3:4 = number of pixels to be sent in following messages, as
// an unsigned 16-bit little-endian integer. This is 0 if
// the sensor isn't an imaging type.
// bytes 5:6 = current plunger position registered on the sensor. This
// is on the *native* scale for the sensor, which might be
// different from joystick units. By default, the native
// scale is the number of pixels for an imaging sensor, or
// 4096 for other sensor types. The actual native scale can
// be reported separately via a second status report message
// (see below).
// byte 7 = bit flags:
// 0x01 = normal orientation detected
// 0x02 = reversed orientation detected
// 0x04 = calibration mode is active (no pixel packets
// are sent for this reading)
// 0x08 = speed is reported in bytes 14:15
// bytes 8:9:10 = average time for each sensor read, in 10us units.
// This is the average time it takes to complete the I/O
// operation to read the sensor, to obtain the raw sensor
// data for instantaneous plunger position reading. For
// an imaging sensor, this is the time it takes for the
// sensor to capture the image and transfer it to the
// microcontroller. For an analog sensor (e.g., an LVDT
// or potentiometer), it's the time to complete an ADC
// sample.
// bytes 11:12:13 = time it took to process the current frame, in 10us
// units. This is the software processing time that was
// needed to analyze the raw data read from the sensor.
// This is typically only non-zero for imaging sensors,
// where it reflects the time required to scan the pixel
// array to find the indicated plunger position. The time
// is usually zero or negligible for analog sensor types,
// since the only "analysis" is a multiplication to rescale
// the ADC sample.
// bytes 14:15 = speed report; only valid if bit 0x08 is set in the
// flags (older versions, before September 2024, didn't
// report this field). Little-endian integer, in joystick
// speed axis reporting units (normalize distance units
// per 10ms, same as normal joystick reports)
//
// An optional second message provides additional information:
//
// bytes 0:1 = 0x87FF
// byte 2 = 1 -> second status report packet
// bytes 3:4 = Native sensor scale. This is the actual native scale
// used for the position report in the first status report
// packet above.
// bytes 5:6 = Jitter window lower bound, in native sensor scale units.
// bytes 7:8 = Jitter window upper bound, in native sensor scale units.
// The jitter window bounds reflect the current jitter filter
// status as of this reading.
// bytes 9:10 = Raw sensor reading before jitter filter was applied.
// bytes 11:12 = Auto-exposure time in microseconds
//
// An optional third message provides additional information specifically
// for bar-code sensors:
//
// bytes 0:1 = 0x87FF
// byte 2 = 2 -> bar code sensor extra status report
// byte 3 = number of bits in bar code
// byte 4 = bar code type:
// 1 = Gray code/Manchester bit coding
// bytes 5:6 = pixel offset of first bit
// byte 7 = width in pixels of each bit
// bytes 8:9 = raw bar code bits
// bytes 10:11 = mask of successfully read bar code bits; a '1' bit means
// that the bit was read successfully, '0' means the bit was
// unreadable
//
// Another optional third message provides additional information
// specifically for digital quadrature sensors:
//
// bytes 0:1 = 0x87FF
// byte 2 = 3 -> digital quadrature sensor extra status report
// byte 3 = "A" channel reading (0 or 1)
// byte 4 = "B" channel reading (0 or 1)
//
// Yet another optional third message provides additional information for
// VCNL4010 sensors:
//
// bytes 0:1 = 0x87FF
// bytes 2 = 4 -> VCNL4010 extra status report
// bytes 3:4 = last proximity count reading (16-bit, little-endian), jitter filtered
// bytes 5:6 = last proximity count reading, original unfiltered value
//
// If the sensor is an imaging sensor type, the first and second sensor
// reports will be followed by a series of pixel reports giving the live
// image view on the sensor. The imaging sensor types have too many pixels
// to send in a single USB transaction, so the device breaks up the array
// into as many packets as needed and sends them in sequence. For non-
// imaging sensors, the "number of pixels" field in the lead packet is
// zero, so obviously no pixel packets will follow. If the "calibration
// active" bit in the flags byte is set, no pixel packets are sent even
// if the sensor is an imaging type, since the transmission time for the
// pixels would interfere with the calibration process. If pixels are sent,
// they're sent in order starting at the first pixel. The format of each
// pixel packet is:
//
// bytes 0:1 = 11-bit index, with high 5 bits set to 10000. For
// example, 0x8004 (encoded little endian as 0x04 0x80)
// indicates index 4. This is the starting pixel number
// in the report. The first report will be 0x00 0x80 to
// indicate pixel #0.
// bytes 2 = 8-bit unsigned int brightness level of pixel at index
// bytes 3 = brightness of pixel at index+1
// etc for the rest of the packet
//
// Note that we currently only support one-dimensional imaging sensors
// (i.e., pixel arrays that are 1 pixel wide). The report format doesn't
// have any provision for a two-dimensional layout. The KL25Z probably
// isn't powerful enough to do real-time image analysis on a 2D image
// anyway, so it's unlikely that we'd be able to make 2D sensors work at
// all, but if we ever add such a thing we'll have to upgrade the report
// format here accordingly.
//
//
// 2B. Configuration report.
// This is requested by sending custom protocol message 65 4 (see below).
// In reponse, the device sends one report to the host using this format:
//
// bytes 0:1 = 0x8800. This has the bit pattern 10001 in the high
// 5 bits, which distinguishes it from regular joystick
// reports and from other special report types.
// bytes 2:3 = total number of configured outputs, little endian. This
// is the number of outputs with assigned functions in the
// active configuration.
// byte 4 = Pinscape unit number (0-15)
// byte 5 = reserved (currently always zero)
// bytes 6:7 = plunger calibration zero point, little endian
// bytes 8:9 = plunger calibration maximum point, little endian
// byte 10 = plunger calibration release time, in milliseconds
// byte 11 = bit flags:
// 0x01 -> configuration loaded; 0 in this bit means that
// the firmware has been loaded but no configuration
// has been sent from the host
// 0x02 -> SBX/PBX extension features: 1 in this bit means
// that these features are present in this version.
// 0x04 -> new accelerometer features supported (adjustable
// dynamic range, auto-centering on/off, adjustable
// auto-centering time)
// 0x08 -> flash write status flag supported (see flag 0x40
// in normal joystick status report)
// 0x10 -> joystick report timing features supports
// (configurable joystick report interval, acceler-
// ometer stutter counter)
// 0x20 -> chime logic is supported
// bytes 12:13 = available RAM, in bytes, little endian. This is the amount
// of unused heap (malloc'able) memory. The firmware generally
// allocates all of the dynamic memory it needs during startup,
// so the free memory figure doesn't tend to fluctuate during
// normal operation. The dynamic memory used is a function of
// the set of features enabled.
//
// 2C. Device ID report.
// This is requested by sending custom protocol message 65 7 (see below).
// In response, the device sends one report to the host using this format:
//
// bytes 0:1 = 0x9000. This has bit pattern 10010 in the high 5 bits
// to distinguish this from other report types.
// byte 2 = ID type. This is the same ID type sent in the request.
// bytes 3-12 = requested ID. The ID is 80 bits in big-endian byte
// order. For IDs longer than 80 bits, we truncate to the
// low-order 80 bits (that is, the last 80 bits).
//
// ID type 1 = CPU ID. This is the globally unique CPU ID
// stored in the KL25Z CPU.
//
// ID type 2 = OpenSDA ID. This is the globally unique ID
// for the connected OpenSDA controller, if known. This
// allow the host to figure out which USB MSD (virtual
// disk drive), if any, represents the OpenSDA module for
// this Pinscape USB interface. This is primarily useful
// to determine which MSD to write in order to update the
// firmware on a given Pinscape unit.
//
// 2D. Configuration variable report.
// This is requested by sending custom protocol message 65 9 (see below).
// In response, the device sends one report to the host using this format:
//
// bytes 0:1 = 0x9800. This has bit pattern 10011 in the high 5 bits
// to distinguish this from other report types.
// byte 2 = Variable ID. This is the same variable ID sent in the
// query message, to relate the reply to the request.
// bytes 3-8 = Current value of the variable, in the format for the
// individual variable type. The variable formats are
// described in the CONFIGURATION VARIABLES section below.
//
// 2E. Software build information report.
// This is requested by sending custom protocol message 65 10 (see below).
// In response, the device sends one report using this format:
//
// bytes 0:1 = 0xA000. This has bit pattern 10100 in the high 5 bits
// (and 10100000 in the high 8 bits) to distinguish it from
// other report types.
// bytes 2:5 = Build date. This is returned as a 32-bit integer,
// little-endian as usual, encoding a decimal value
// in the format YYYYMMDD giving the date of the build.
// E.g., Feb 16 2016 is encoded as 20160216 (decimal).
// bytes 6:9 = Build time. This is a 32-bit integer, little-endian,
// encoding a decimal value in the format HHMMSS giving
// build time on a 24-hour clock.
//
// 2F. Button status report.
// This is requested by sending custom protocol message 65 13 (see below).
// In response, the device sends one report using this format:
//
// bytes 0:1 = 0xA1. This has bit pattern 10100 in the high 5 bits (and
// 10100001 in the high 8 bits) to distinguish it from other
// report types.
// byte 2 = number of button reports
// byte 3 = Physical status of buttons 1-8, 1 bit each. The low-order
// bit (0x01) is button 1. Each bit is 0 if the button is off,
// 1 if on. This reflects the physical status of the button
// input pins, after debouncing but before any logical state
// processing. Pulse mode and shifting have no effect on the
// physical state; this simply indicates whether the button is
// electrically on (shorted to GND) or off (open circuit).
// byte 4 = buttons 9-16
// byte 5 = buttons 17-24
// byte 6 = buttons 25-32
// byte 7 = buttons 33-40
// byte 8 = buttons 41-48
//
// 2G. IR sensor data report.
// This is requested by sending custom protocol message 65 12 (see below).
// That command puts controller in IR learning mode for a short time, during
// which it monitors the IR sensor and send these special reports to relay the
// readings. The reports contain the raw data, plus the decoded command code
// and protocol information if the controller is able to recognize and decode
// the command.
//
// bytes 0:1 = 0xA2. This has bit pattern 10100 in the high 5 bits (and
// 10100010 in the high 8 bits) to distinguish it from other
// report types.
// byte 2 = number of raw reports that follow
// bytes 3:4 = first raw report, as a little-endian 16-bit int. The
// value represents the time of an IR "space" or "mark" in
// 2us units. The low bit is 0 for a space and 1 for a mark.
// To recover the time in microseconds, mask our the low bit
// and multiply the result by 2. Received codes always
// alternate between spaces and marks. A space is an interval
// where the IR is off, and a mark is an interval with IR on.
// If the value is 0xFFFE (after masking out the low bit), it
// represents a timeout, that is, a time greater than or equal
// to the maximum that can be represented in this format,
// which is 131068us. None of the IR codes we can parse have
// any internal signal component this long, so a timeout value
// is generally seen only during a gap between codes where
// nothing is being transmitted.
// bytes 4:5 = second raw report
// (etc for remaining reports)
//
// If byte 2 is 0x00, it indicates that learning mode has expired without
// a code being received, so it's the last report sent for the learning
// session.
//
// If byte 2 is 0xFF, it indicates that a code has been successfully
// learned. The rest of the report contains the learned code instead
// of the raw data:
//
// byte 3 = protocol ID, which is an integer giving an internal code
// identifying the IR protocol that was recognized for the
// received data. See IRProtocolID.h for a list of the IDs.
// byte 4 = bit flags:
// 0x02 -> the protocol uses "dittos"
// bytes 5:6:7:8:9:10:11:12 = a little-endian 64-bit int containing
// the code received. The code is essentially the data payload
// of the IR packet, after removing bits that are purely
// structural, such as toggle bits and error correction bits.
// The mapping between the IR bit stream and our 64-bit is
// essentially arbitrary and varies by protocol, but it always
// has round-trip fidelity: using the 64-bit code value +
// protocol ID + flags to send an IR command will result in
// the same IR bit sequence being sent, modulo structural bits
// that need to be updates in the reconstruction (such as toggle
// bits or sequencing codes).
//
// 2H. Plunger sensor diagnostics
// This report type completely replaces the normal plunger sensor reports
// when the diagnostic mode is engaged. Since this mode must be explicitly
// engaged, and thus the host knows when to read them, we skip the usual
// status flag indicators in order to use all available bytes. The purpose
// of this mode is to relay every raw plunger sensor reading back to the PC
// host, to allow the PC to see in detail what the firmware is reading from
// the sensor. The report therefore packs a varying number of sensor readings,
// so that it can pass back all readings collected since the last report.
// Up to 10 readings are passed back; if more than 10 readings were collected
// since the previous report, only the most recent 10 are included. Readings
// are ordered newest to oldest.
//
// byte 0 = number of plunger reports
// byte 1 = unused, 0
// byte 2:3 = first reading (newest)
// byte 4:5 = second reading
// ...
// byte 20:21 = 10th reading (oldest)
//
// WHY WE USE A HACKY APPROACH TO DIFFERENT REPORT TYPES
//
// The HID report system was specifically designed to provide a clean,
// structured way for devices to describe the data they send to the host.
// Our approach isn't clean or structured; it ignores the promises we
// make about the contents of our report via the HID Report Descriptor
// and stuffs our own different data format into the same structure.
//
// We use this hacky approach only because we can't use the standard USB
// HID mechanism for varying report types, which is to provide multiple
// report descriptors and tag each report with a type byte that indicates
// which descriptor applies. We can't use that standard approach because
// we want to be 100% LedWiz compatible. The snag is that some Windows
// LedWiz clients parse the USB HID descriptors as part of identifying a
// USB HID device as a valid LedWiz unit, and will only recognize the device
// if certain properties of the HID descriptors match those of a real LedWiz.
// One of the features that's important to some clients is the descriptor
// link structure, which is affected by the layout of HID Report Descriptor
// entries. In order to match the expected layout, we can only define a
// single kind of output report. Since we have to use Joystick reports for
// the sake of VP and other pinball software, and we're only allowed the
// one report type, we have to make that one report type the Joystick type.
// That's why we overload the joystick reports with other meanings. It's a
// hack, but at least it's a fairly reliable and isolated hack, in that our
// special reports are only generated when clients specifically ask for
// them. Plus, even if a client who doesn't ask for a special report
// somehow gets one, the worst that happens is that they get a momentary
// spurious reading from the accelerometer and plunger.
// ------- INCOMING MESSAGES (HOST TO DEVICE) -------
//
// For LedWiz compatibility, our incoming message format conforms to the
// basic USB format used by real LedWiz units. This is simply 8 data
// bytes, all private vendor-specific values (meaning that the Windows HID
// driver treats them as opaque and doesn't attempt to parse them).
//
// Within this basic 8-byte format, we recognize the full protocol used
// by real LedWiz units, plus an extended protocol that we define privately.
// The LedWiz protocol leaves a large part of the potential protocol space
// undefined, so we take advantage of this undefined region for our
// extensions. This ensures that we can properly recognize all messages
// intended for a real LedWiz unit, as well as messages from custom host
// software that knows it's talking to a Pinscape unit.
// --- REAL LED WIZ MESSAGES ---
//
// The real LedWiz protocol has two message types, "SBA" and "PBA". The
// message type can be determined from the first byte of the 8-byte message
// packet: if the first byte 64 (0x40), it's an SBA message. If the first
// byte is 0-49 or 129-132, it's a PBA message. All other byte values are
// invalid in the original protocol and have undefined behavior if sent to
// a real LedWiz. We take advantage of this to extend the protocol with
// our new features by assigning new meanings to byte patterns that have no
// meaning in the original protocol.
//
// "SBA" message: 64 xx xx xx xx ss 00 00
// xx = on/off bit mask for 8 outputs
// ss = global flash speed setting (valid values 1-7)
// 00 = unused/reserved; client should set to zero (not enforced, but
// strongly recommended in case of future additions)
//
// If the first byte has value 64 (0x40), it's an SBA message. This type of
// message sets all 32 outputs individually ON or OFF according to the next
// 32 bits (4 bytes) of the message, and sets the flash speed to the value in
// the sixth byte. The flash speed sets the global cycle rate for flashing
// outputs - outputs with their values set to the range 128-132. The speed
// parameter is in ad hoc units that aren't documented in the LedWiz API, but
// observations of real LedWiz units show that the "speed" is actually the
// period, each unit representing 0.25s: so speed 1 is a 0.25s period, or 4Hz,
// speed 2 is a 0.5s period or 2Hz, etc., up to speed 7 as a 1.75s period or
// 0.57Hz. The period is the full waveform cycle time.
//
//
// "PBA" message: bb bb bb bb bb bb bb bb
// bb = brightness level, 0-49 or 128-132
//
// Note that there's no prefix byte indicating this message type. This
// message is indicated simply by the first byte being in one of the valid
// ranges.
//
// Each byte gives the new brightness level or flash pattern for one part.
// The valid values are:
//
// 0-48 = fixed brightness level, linearly from 0% to 100% intensity
// 49 = fixed brightness level at 100% intensity (same as 48)
// 129 = flashing pattern, fade up / fade down (sawtooth wave)
// 130 = flashing pattern, on / off (square wave)
// 131 = flashing pattern, on for 50% duty cycle / fade down
// 132 = flashing pattern, fade up / on for 50% duty cycle
//
// This message sets new brightness/flash settings for 8 ports. There's
// no port number specified in the message; instead, the port is given by
// the protocol state. Specifically, the device has an internal register
// containing the base port for PBA messages. On reset AND after any SBA
// message is received, the base port is set to 0. After any PBA message
// is received and processed, the base port is incremented by 8, resetting
// to 0 when it reaches 32. The bytes of the message set the brightness
// levels for the base port, base port + 1, ..., base port + 7 respectively.
//
//
// --- PRIVATE EXTENDED MESSAGES ---
//
// All of our extended protocol messages are identified by the first byte:
//
// 65 -> Miscellaneous control message. The second byte specifies the specific
// operation:
//
// 0 -> No Op - does nothing. (This can be used to send a test message on the
// USB endpoint.)
//
// 1 -> Set the device's LedWiz unit number and plunger status, and save the
// changes to flash. The device automatically reboots after the changes
// are saved if the unit number is changed, since this changes the USB
// product ID code. The additional bytes of the message give the
// parameters:
//
// third byte = new LedWiz unit number (0-15, corresponding to nominal
// LedWiz unit numbers 1-16)
// fourth byte = plunger on/off (0=disabled, 1=enabled)
//
// Note that this command is from the original version and isn't typically
// used any more, since the same information has been subsumed into more
// generalized option settings via the config variable system.
//
// 2 -> Begin plunger calibration mode. The device stays in this mode for about
// 15 seconds, and sets the zero point and maximum retraction points to the
// observed endpoints of sensor readings while the mode is running. After
// the time limit elapses, the device automatically stores the results in
// non-volatile flash memory and exits the mode.
//
// 3 -> Send pixel dump. The device sends one complete image snapshot from the
// plunger sensor, as as series of pixel dump messages. (The message format
// isn't big enough to allow the whole image to be sent in one message, so
// the image is broken up into as many messages as necessary.) The device
// then resumes sending normal joystick messages. If the plunger sensor
// isn't an imaging type, or no sensor is installed, no pixel messages are
// sent. Parameters:
//
// third byte = bit flags:
// 0x01 = low res mode. The device rescales the sensor pixel array
// sent in the dump messages to a low-resolution subset. The
// size of the subset is determined by the device. This has
// no effect on the sensor operation; it merely reduces the
// USB transmission time to allow for a faster frame rate for
// viewing in the config tool.
//
// fourth byte = extra exposure time in 100us (.1ms) increments. For
// imaging sensors, we'll add this delay to the minimum exposure
// time. This allows the caller to explicitly adjust the exposure
// level for calibration purposes.
//
// 4 -> Query configuration. The device sends a special configuration report,
// (see above; see also USBJoystick.cpp), then resumes sending normal
// joystick reports.
//
// 5 -> Turn all outputs off and restore LedWiz defaults. Sets all output
// ports to OFF and LedWiz brightness/mode setting 48, and sets the LedWiz
// global flash speed to 2.
//
// 6 -> Save configuration to flash. This saves all variable updates sent via
// type 66 messages since the last reboot, then optionally reboots the
// device to put the changes into effect. If the flash write succeeds,
// we set the "flash write OK" bit in our status reports, which we
// continue sending between the successful write and the delayed reboot.
// We don't set the bit or reboot if the write fails. If the "do not
// reboot" flag is set, we still set the flag on success for the delay
// time, then clear the flag.
//
// third byte = delay time in seconds. The device will wait this long
// before disconnecting, to allow the PC to test for the success bit
// in the status report, and to perform any cleanup tasks while the
// device is still attached (e.g., modifying Windows device driver
// settings)
//
// fourth byte = flags:
// 0x01 -> do not reboot
//
// 7 -> Query device ID. The device replies with a special device ID report
// (see above; see also USBJoystick.cpp), then resumes sending normal
// joystick reports.
//
// The third byte of the message is the ID index to retrieve:
//
// 1 = CPU ID: returns the KL25Z globally unique CPU ID.
//
// 2 = OpenSDA ID: returns the OpenSDA TUID. This must be patched
// into the firmware by the PC host when the .bin file is
// installed onto the device. This will return all 'X' bytes
// if the value wasn't patched at install time.
//
// 8 -> Engage/disengage night mode. The third byte of the message is 1 to
// engage night mode, 0 to disengage night mode. The current mode isn't
// stored persistently; night mode is always off after a reset.
//
// 9 -> Query configuration variable. The second byte is the config variable
// number (see the CONFIGURATION VARIABLES section below). For the array
// variables (button assignments, output ports), the third byte is the
// array index. The device replies with a configuration variable report
// (see above) with the current setting for the requested variable.
//
// 10 -> Query software build information. No parameters. This replies with
// the software build information report (see above).
//
// 11 -> TV ON relay manual control. This allows testing and operating the
// relay from the PC. This doesn't change the power-up configuration;
// it merely allows the relay to be controlled directly. The third
// byte specifies the relay operation to perform:
//
// 0 = turn relay off
// 1 = turn relay on
// 2 = pulse the relay as though the power-on delay timer fired
//
// 12 -> Learn IR code. The device enters "IR learning mode". While in
// learning mode, the device reports the raw signals read through
// the IR sensor to the PC through the special IR learning report
// (see "2G" above). If a signal can be decoded through a known
// protocol, the device sends a final "2G" report with the decoded
// command, then terminates learning mode. If no signal can be
// decoded within a timeout period, the mode automatically ends,
// and the device sends a final IR learning report with zero raw
// signals to indicate termination. After initiating IR learning
// mode, the user should point the remote control with the key to
// be learned at the IR sensor on the KL25Z, and press and hold the
// key on the remote for a few seconds. Holding the key for a few
// moments is important because it lets the decoder sense the type
// of auto-repeat coding the remote uses. The learned code can be
// written to an IR config variable slot to program the controller
// to send the learned command on events like TV ON or a button
// press.
//
// 13 -> Get button status report. The device sends one button status
// report in response (see section "2F" above).
//
// 14 -> Manually center the accelerometer. This sets the accelerometer
// zero point to the running average of readings over the past few
// seconds.
//
// 15 -> Set up ad hoc IR command, part 1. This sets up the first part
// of an IR command to transmit. The device stores the data in an
// internal register for later use in message 65 16. Send the
// remainder of the command data with 65 16.
//
// byte 3 = IR protocol ID
// byte 4 = flags (IRFlagXxx bit flags)
// byte 5-8 = low-order 32 bits of command code, little-endian
//
// 16 -> Finish and send an ad hoc IR command. Use message 65 15 first
// to set up the start of the command data, then send this message
// to fill in the rest of the data and transmit the command. Upon
// receiving this message, the device performs the transmission.
//
// byte 3-6 = high-order 32 bits of command code, little-endian
//
// 17 -> Send a pre-programmed IR command. This immediately transmits an
// IR code stored in a command slot.
//
// byte 3 = command number (1..MAX_IR_CODES)
//
// 18 -> Special diagnostic commands. These are mostly for internal
// development/debugging work. Byte 3 contains a diagnostic code
// number, essentially a sub-command. The remaining bytes are
// arguments that vary according to the byte 3 sub-command.
//
// Byte 3 values:
//
// 0x00 -> cancel diagnostic modes
//
// 0x01 -> enter plunger sensor diagnostic mode. The device
// replaces the usual joystick reports with special
// plunger sensor reports that pass back all plunger
// sensor readings as long as the mode is engaged.
//
//
// 66 -> Set configuration variable. The second byte of the message is the config
// variable number, and the remaining bytes give the new value for the variable.
// The value format is specific to each variable; see the CONFIGURATION VARIABLES
// section below for a list of the variables and their formats. This command
// only sets the value in RAM; it doesn't write the value to flash and doesn't
// put the change into effect. To save the new settings, the host must send a
// type 65 subtype 6 message (see above). That saves the settings to flash and
// reboots the device, which makes the new settings active.
//
// 67 -> "SBX". This is an extended form of the original LedWiz SBA message. This
// version is specifically designed to support a replacement LEDWIZ.DLL on the
// host that exposes one Pinscape device as multiple virtual LedWiz devices,
// in order to give legacy clients access to more than 32 ports. Each virtual
// LedWiz represents a block of 32 ports. The format of this message is the
// same as for the original SBA, with the addition of one byte:
//
// 67 xx xx xx xx ss pp 00
// xx = on/off switches for 8 ports, one bit per port
// ss = global flash speed setting for this bank of ports, 1-7
// pp = port group: 0 for ports 1-32, 1 for ports 33-64, etc
// 00 = unused/reserved; client should set to zero
//
// As with SBA, this sets the on/off switch states for a block of 32 ports.
// SBA always addresses ports 1-32; SBX can address any set of 32 ports.
//
// We keep a separate speed setting for each group of 32 ports. The purpose
// of the SBX extension is to allow a custom LEDWIZ.DLL to expose multiple
// virtual LedWiz units to legacy clients, so clients will expect each unit
// to have its separate flash speed setting. Each block of 32 ports maps to
// a virtual unit on the client side, so each block needs its own speed state.
//
// 68 -> "PBX". This is an extended form of the original LedWiz PBA message; it's
// the PBA equivalent of our SBX extension above.
//
// 68 pp ee ee ee ee ee ee
// pp = port group: 0 for ports 1-8, 1 for 9-16, etc
// qq = sequence number: 0 for the first 8 ports in the group, etc
// ee = brightness/flash values, 6 bits per port, packed into the bytes
//
// The port group 'pp' selects a group of 8 ports. Note that, unlike PBA,
// the port group being updated is explicitly coded in the message, which makes
// the message stateless. This eliminates any possibility of the client and
// host getting out of sync as to which ports they're talking about. This
// message doesn't affect the PBA port address state.
//
// The brightness values are *almost* the same as in PBA, but not quite. We
// remap the flashing state values as follows:
//
// 0-48 = brightness level, 0% to 100%, on a linear scale
// 49 = brightness level 100% (redundant with 48)
// 60 = PBA 129 equivalent, sawtooth
// 61 = PBA 130 equivalent, square wave (on/off)
// 62 = PBA 131 equivalent, on/fade down
// 63 = PBA 132 equivalent, fade up/on
//
// We reassign the brightness levels like this because it allows us to pack
// every possible value into 6 bits. This allows us to fit 8 port settings
// into six bytes. The 6-bit fields are packed into the 8 bytes consecutively
// starting with the low-order bit of the first byte. An efficient way to
// pack the 'ee' fields given the brightness values is to shift each group of
// four bytes into a uint, then shift the uint into three 'ee' bytes:
//
// unsigned int tmp1 = bri[0] | (bri[1]<<6) | (bri[2]<<12) | (bri[3]<<18);
// unsigned int tmp2 = bri[4] | (bri[5]<<6) | (bri[6]<<12) | (bri[7]<<18);
// unsigned char port_group = FIRST_PORT_TO_ADDRESS / 8;
// unsigned char msg[8] = {
// 68, pp,
// tmp1 & 0xFF, (tmp1 >> 8) & 0xFF, (tmp1 >> 16) & 0xFF,
// tmp2 & 0xFF, (tmp2 >> 8) & 0xFF, (tmp2 >> 16) & 0xFF
// };
//
// 200-228 -> Set extended output brightness. This sets outputs N to N+6 to the
// respective brightness values in the 2nd through 8th bytes of the message
// (output N is set to the 2nd byte value, N+1 is set to the 3rd byte value,
// etc). Each brightness level is a linear brightness level from 0-255,
// where 0 is 0% brightness and 255 is 100% brightness. N is calculated as
// (first byte - 200)*7 + 1:
//
// 200 = outputs 1-7
// 201 = outputs 8-14
// 202 = outputs 15-21
// ...
// 228 = outputs 197-203
//
// This message is the way to address ports 33 and higher. Original LedWiz
// protocol messages can't access ports above 32, since the protocol is
// hard-wired for exactly 32 ports.
//
// Note that the extended output messages differ from regular LedWiz commands
// in two ways. First, the brightness is the ONLY attribute when an output is
// set using this mode. There's no separate ON/OFF state per output as there
// is with the SBA/PBA messages. To turn an output OFF with this message, set
// the intensity to 0. Setting a non-zero intensity turns it on immediately
// without regard to the SBA status for the port. Second, the brightness is
// on a full 8-bit scale (0-255) rather than the LedWiz's approximately 5-bit
// scale, because there are no parts of the range reserved for flashing modes.
//
// Outputs 1-32 can be controlled by EITHER the regular LedWiz SBA/PBA messages
// or by the extended messages. The latest setting for a given port takes
// precedence. If an SBA/PBA message was the last thing sent to a port, the
// normal LedWiz combination of ON/OFF and brightness/flash mode status is used
// to determine the port's physical output setting. If an extended brightness
// message was the last thing sent to a port, the LedWiz ON/OFF status and
// flash modes are ignored, and the fixed brightness is set. Outputs 33 and
// higher inherently can't be addressed or affected by SBA/PBA messages.
//
// (The precedence scheme is designed to accommodate a mix of legacy and DOF
// software transparently. The behavior described is really just to ensure
// transparent interoperability; it's not something that host software writers
// should have to worry about. We expect that anyone writing new software will
// just use the extended protocol and ignore the old LedWiz commands, since
// the extended protocol is easier to use and more powerful.)
// ------- CONFIGURATION VARIABLES -------
//
// Message type 66 (see above) sets one configuration variable. The second byte
// of the message is the variable ID, and the rest of the bytes give the new
// value, in a variable-specific format. 16-bit values are little endian.
// Any bytes at the end of the message not otherwise specified are reserved
// for future use and should always be set to 0 in the message data.
//
// Variable IDs:
//
// 0 -> QUERY ONLY: Describe the configuration variables. The device
// sends a config variable query report with the following fields:
//
// byte 3 -> number of scalar (non-array) variables (these are
// numbered sequentially from 1 to N)
// byte 4 -> number of array variables (these are numbered
// sequentially from 256-N to 255)
//
// The description query is meant to allow the host to capture all
// configuration settings on the device without having to know what
// the variables mean or how many there are. This is useful for
// backing up the settings in a file on the PC, for example, or for
// capturing them to restore after a firmware update. This allows
// more flexible interoperability between unsynchronized versions
// of the firmware and the host software.
//
// 1 -> USB device ID. This sets the USB vendor and product ID codes
// to use when connecting to the PC. For LedWiz emulation, use
// vendor 0xFAFA and product 0x00EF + unit# (where unit# is the
// nominal LedWiz unit number, from 1 to 16). If you have any
// REAL LedWiz units in your system, we recommend starting the
// Pinscape LedWiz numbering at 8 to avoid conflicts with the
// real LedWiz units. If you don't have any real LedWiz units,
// you can number your Pinscape units starting from 1.
//
// If LedWiz emulation isn't desired or causes host conflicts,
// use our private ID: Vendor 0x1209, product 0xEAEA. (These IDs
// are registered with http://pid.codes, a registry for open-source
// USB devices, so they're guaranteed to be free of conflicts with
// other properly registered devices). The device will NOT appear
// as an LedWiz if you use the private ID codes, but DOF (R3 or
// later) will still recognize it as a Pinscape controller.
//
// bytes 3:4 -> USB Vendor ID
// bytes 5:6 -> USB Product ID
//
// 2 -> Pinscape Controller unit number for DOF. The Pinscape unit
// number is independent of the LedWiz unit number, and indepedent
// of the USB vendor/product IDs. DOF (R3 and later) uses this to
// identify the unit for the extended Pinscape functionality.
// For easiest DOF configuration, we recommend numbering your
// units sequentially starting at 1 (regardless of whether or not
// you have any real LedWiz units).
//
// byte 3 -> unit number, from 1 to 16
//
// 3 -> Joystick report settings.
//
// byte 3 -> Enable joystick interface: 1 to enable, 0 to disable
// byte 4 -> Joystick axis format, as a USBJoystick::AXIS_FORMAT_XXX value
// bytes 5:8 -> Reporting interval in microseconds
//
// When joystick reports are disabled, the device registers as a generic HID
// device, and only sends the private report types used by the Windows config
// tool. It won't appear to Windows as a USB game controller or joystick.
//
// Note that this doesn't affect whether the device also registers a keyboard
// interface. A keyboard interface will appear if and only if any buttons
// (including virtual buttons, such as the ZB Launch Ball feature) are assigned
// to generate keyboard key input.
//
// 4 -> Accelerometer settings
//
// byte 3 -> orientation:
// 0 = ports at front (USB ports pointing towards front of cabinet)
// 1 = ports at left
// 2 = ports at right
// 3 = ports at rear
// byte 4 -> dynamic range
// 0 = +/- 1G (2G hardware mode, but rescales joystick reports to 1G
// range; compatible with older versions)
// 1 = +/- 2G (2G hardware mode)
// 2 = +/- 4G (4G hardware mode)
// 3 = +/- 8G (8G hardware mode)
// byte 5 -> Auto-centering mode
// 0 = auto-centering on, 5 second timer (default, compatible
// with older versions)
// 1-60 = auto-centering on with the given time in seconds
// 61-245 = reserved
// 255 = auto-centering off; manual centering only
// byte 6 -> joystick report stutter count: 1 (or 0) means that we
// take a fresh accelerometer on every joystick report; 2 means
// that we take a new reading on every other report, and repeat
// the prior readings on alternate reports; etc
//
// 5 -> Plunger sensor type.
//
// byte 3 -> plunger type:
// 0 = none (disabled)
// 1 = TSL1410R linear image sensor, 1280x1 pixels, serial mode, edge detection
// 3 = TSL1412R linear image sensor, 1536x1 pixels, serial mode, edge detection
// 5 = Potentiometer with linear taper, or any other device that
// represents the position reading with a single analog voltage
// 6 = AEDR8300 optical quadrature sensor, 75lpi
// *7 = AS5304 magnetic quadrature sensor, 160 steps per 2mm
// 8 = TSL1401CL linear image sensor, 128x1 pixel, bar code detection
// 9 = VL6180X time-of-flight distance sensor
// **10 = AEAT-6012-A06 magnetic rotary encoder
// **11 = TCD1103GFG Toshiba linear CCD, 1500x1 pixels, edge detection
// **12 = VCNL4010 Vishay IR proximity sensor
//
// byte 4 -> extra sensor-speicifc parameter data
// TSL141xx - scan mode (edge-sensing algorithm) selection; see
// edgeSensor.h for a list of the available modes
// VCNL4010 - IRED current, in units of 10mA, valid range 1..20
//
// * The sensor types marked with asterisks (*) are reserved for types
// that aren't currently implemented but could be added in the future.
// Selecting these types will effectively disable the plunger.
//
// ** The sensor types marked with double asterisks (**) are
// Experimental, meaning that the code isn't finished yet and isn't
// meant for use in a live pin cab.
//
// Sensor types 2 and 4 were formerly reserved for TSL14xx sensors in
// parallel wiring mode, but support for these is no longer planned, as
// the KL25Z's single ADC sampler negates any speed advantage from using
// the sensors' parallel mode. Those slots could be reassigned for
// other sensors, since they were never enabled in any release version.
//
// 6 -> Plunger pin assignments.
//
// byte 3 -> pin assignment 1
// byte 4 -> pin assignment 2
// byte 5 -> pin assignment 3/misc
// byte 6 -> pin assignment 4/misc
//
// All of the pins use the standard GPIO port format (see "GPIO pin number
// mappings" below). The actual use of the four pins depends on the plunger
// type, as shown below. "NC" means that the pin isn't used at all for the
// corresponding plunger type. "GPIO" means that any GPIO pin will work.
// AnalogIn, InterruptIn, and PWM mean that only pins with the respective
// capabilities can be chosen.
//
// Plunger Type Pin 1 Pin 2 Pin 3/Misc Pin 4
//
// TSL1410R/1412R/1401CL SI (GPIO) CLK (GPIO) AO (AnalogIn) NC
// Potentiometer AO (AnalogIn) NC NC NC
// AEDR8300 A (InterruptIn) B (InterruptIn) NC NC
// AS5304 A (InterruptIn) B (InterruptIn) NC NC
// VL6180X SDA (GPIO) SCL (GPIO) GPIO0/CE (GPIO) NC
// AEAT-6012-A06 CS (GPIO) CLK (GPIO) DO (GPIO) NC
// TCD1103GFG fM (PWM) OS (AnalogIn) ICG (GPIO) SH (GPIO)
// VCNL4010 SDA (GPIO) SCL (GPIO) NC NC
//
// 7 -> Plunger calibration button pin assignments.
//
// byte 3 -> features enabled/disabled: bit mask consisting of:
// 0x01 button input is enabled
// 0x02 lamp output is enabled
// byte 4 -> DigitalIn pin for the button switch
// byte 5 -> DigitalOut pin for the indicator lamp
//
// Note that setting a pin to NC (Not Connected) will disable it even if the
// corresponding feature enable bit (in byte 3) is set.
//
// 8 -> ZB Launch Ball setup. This configures the ZB Launch Ball feature.
//
// byte 3 -> LedWiz port number (1-255) mapped to "ZB Launch Ball" in DOF
// byte 4 -> key type
// byte 5 -> key code
// bytes 6:7 -> "push" distance, in 1/1000 inch increments (16 bit little endian)
//
// Set the port number to 0 to disable the feature. The key type and key code
// fields use the same conventions as for a button mapping (see below). The
// recommended push distance is 63, which represents .063" ~ 1/16".
//
// 9 -> TV ON relay setup. This requires external circuitry implemented on the
// Expansion Board (or an equivalent circuit as described in the Build Guide).
//
// byte 3 -> "power status" input pin (DigitalIn)
// byte 4 -> "latch" output (DigitalOut)
// byte 5 -> relay trigger output (DigitalOut)
// bytes 6:7 -> delay time in 10ms increments (16 bit little endian);
// e.g., 550 (0x26 0x02) represents 5.5 seconds
//