Mirror with some correction

Dependencies:   mbed FastIO FastPWM USBDevice

Revision:
74:822a92bc11d2
Parent:
73:4e8ce0b18915
Child:
75:677892300e7a
--- a/USBProtocol.h	Sat Jan 21 19:48:30 2017 +0000
+++ b/USBProtocol.h	Fri Jan 27 23:47:15 2017 +0000
@@ -1,9 +1,39 @@
 // USB Message Protocol
 //
-// This file is purely for documentation, to describe our USB protocol.
-// We use the standard HID setup with one endpoint in each direction.
-// See USBJoystick.cpp/.h for our USB descriptor arrangement.
+// 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) ------
 //
@@ -151,7 +181,9 @@
 //    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 outputs, little endian
+//    bytes 2:3 = total number of configured outputs, little endian.  This
+//                is the number of outputs with assigned functions in the
+//                active configuration.
 //    bytes 4:5 = Pinscape unit number (0-15), little endian
 //    bytes 6:7 = plunger calibration zero point, little endian
 //    bytes 8:9 = plunger calibration maximum point, little endian
@@ -160,6 +192,8 @@
 //                 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.
 //    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,
@@ -288,27 +322,42 @@
 
 // --- REAL LED WIZ MESSAGES ---
 //
-// The real LedWiz protocol has two message types, identified by the first
-// byte of the 8-byte USB packet:
+// 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.
 //
-// 64              -> SBA (64 xx xx xx xx ss uu uu)
-//                    xx = on/off bit mask for 8 outputs
-//                    ss = global flash speed setting (1-7)
-//                    uu = unused
+// "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 - to a   
-// relative speed, scaled linearly in frequency.  1 is the slowest at about 
-// 2 Hz, 7 is the fastest at about 14 Hz.)
+// 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.
+//
 //
-// 0-49 or 128-132 -> PBA (bb bb bb bb bb bb bb bb)
-//                    bb = brightness level/flash pattern for one output
+// "PBA" message:  bb bb bb bb bb bb bb bb
+//     bb = brightness level, 0-49 or 128-132
 //
-// If the first byte is any valid brightness setting, it's a PBA message.
-// Valid brightness settings are:
+// 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)
@@ -316,27 +365,17 @@
 //     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
-//     
-// A PBA message sets 8 outputs out of 32.  Which 8 are to be set is 
-// implicit in the message sequence: the first PBA sets outputs 1-8, the 
-// second sets 9-16, and so on, rolling around after each fourth PBA.  
-// An SBA also resets the implicit "bank" for the next PBA to outputs 1-8.
 //
-// Note that there's no special first byte to indicate the PBA message
-// type, as there is in an SBA.  The first byte of a PBA is simply the
-// first output setting.  The way the LedWiz creators conceived this, an
-// SBA message is distinguishable from a PBA because there's no such thing
-// as a brightness level 64, hence 64 is never valid as a byte in an PBA
-// message, hence a message starting with 64 must be something other than
-// an PBA message.
+// 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.
 //
-// Our extended protocol uses the same principle, taking advantage of the
-// many other byte values that are also invalid in PBA messages.  To be a 
-// valid PBA message, the first byte must be in the range 0-49 or 129-132.  
-// As already mentioned, byte value 64 indicates an SBA message, so we
-// can't use that one for private extensions.  This still leaves many
-// other byte values for us, though, namely 50-63, 65-128, and 133-255.
-
+//
 
 // --- PRIVATE EXTENDED MESSAGES ---
 //
@@ -386,9 +425,9 @@
 //             (see above; see also USBJoystick.cpp), then resumes sending normal 
 //             joystick reports.
 //
-//        5 -> Turn all outputs off and restore LedWiz defaults.  Sets output ports
-//             1-32 to OFF and LedWiz brightness/mode setting 48, sets outputs 33 and
-//             higher to brightness level 0, and sets the LedWiz global flash speed to 2.
+//        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 automatically reboots the
@@ -433,26 +472,7 @@
 //                 1 = turn relay on
 //                 2 = pulse the relay as though the power-on delay timer fired
 //
-//       12 -> Select virtual LedWiz unit.  This selects a bank of 32 ports that
-//             will be addressed by subsequent SBA and PBA messages.  After this
-//             command is sent, all SBA and PBA messages will address the bank of
-//             ports selected by this command.  Send this command again with a new
-//             bank number to address other ports with SBA/PBA messages.
-//
-//             The rationale for this command is to allow legacy software that only
-//             uses the original LedWiz protocol to access more than 32 ports.  To
-//             do this, we must replace the LEDWIZ.DLL interface library on the PC
-//             with a new version that exposes each Pinscape unit as multiple virtual
-//             LedWiz devices.  The DLL creates a virtual LedWiz unit (each with its
-//             own unit number) for each bank of 32 ports on the Pincape unit.  When
-//             the DLL receives an SBA or PBA command addressed to one of the virtual
-//             LedWiz units, it first sends a "select virtual unit" command (i.e.,
-//             this message) to Pinscape, selecting the appropriate bank of 32 ports
-//             represented by the virtual unit being accessed by the client, then
-//             follows with the SBA/PBA command the client sent.
-//
-//             The third byte of the message is the bank number to select.  Bank 0
-//             is ports 1-32, bank 1 is ports 33-64, and so on.
+//       12 -> Unused
 //
 //       13 -> Get button status report.  The device sends one button status report
 //             in response (see section "2F" above).
@@ -466,6 +486,68 @@
 //        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, 
@@ -792,6 +874,48 @@
 //       byte 3 = button number - 1..MAX_BUTTONS, or 0 for none.
 //
 //
+// SPECIAL DIAGNOSTICS VARIABLES:  These work like the array variables below,
+// the only difference being that we don't report these in the number of array
+// variables reported in the "variable 0" query.
+//
+// 220 -> Performance/diagnostics variables.  Items marked "read only" can't
+//        be written; any SET VARIABLE messages on these are ignored.  Items
+//        marked "diagnostic only" refer to counters or statistics that are
+//        collected only when the diagnostics are enabled via the diags.h
+//        macro ENABLE_DIAGNOSTICS.  These will simply return zero otherwise.
+//
+//          byte 3 = diagnostic index (see below)
+//
+//        Diagnostic index values:
+//
+//          1 -> Main loop cycle time [read only, diagnostic only]
+//               Retrieves the average time of one iteration of the main
+//               loop, in microseconds, as a uint32.  This excludes the
+//               time spent processing incoming messages, as well as any
+//               time spent waiting for a dropped USB connection to be
+//               restored.  This includes all subroutine time and polled
+//               task time, such as processing button and plunger input,
+//               sending USB joystick reports, etc.
+//
+//          2 -> Main loop message read time [read only, diagnostic only]
+//               Retrieves the average time spent processing incoming USB
+//               messages per iteration of the main loop, in microseconds, 
+//               as a uint32.  This only counts the processing time when 
+//               messages are actually present, so the average isn't reduced
+//               by iterations of the main loop where no messages are found.
+//               That is, if we run a million iterations of the main loop,
+//               and only five of them have messages at all, the average time
+//               includes only those five cycles with messages to process.
+//
+//          3 -> PWM update polling time [read only, diagnostic only]
+//               Retrieves the average time, as a uint32 in microseconds,
+//               spent in the PWM update polling routine.
+//
+//          4 -> LedWiz update polling time [read only, diagnostic only]
+//               Retrieves the average time, as a uint32 in microseconds,
+//               units, spent in the LedWiz flash cycle update routine.
+//
+//
 // ARRAY VARIABLES:  Each variable below is an array.  For each get/set message,
 // byte 3 gives the array index.  These are grouped at the top end of the variable 
 // ID range to distinguish this special feature.  On QUERY, set the index byte to 0