]> xenbits.xen.org Git - xenclient/ioemu.git/commitdiff
Initial check-in for PS/2 pass-through support.
authorMark Hemment <markhem@isildur.uk.xensource.com>
Thu, 23 Apr 2009 13:40:47 +0000 (14:40 +0100)
committerMark Hemment <markhem@isildur.uk.xensource.com>
Thu, 23 Apr 2009 13:40:47 +0000 (14:40 +0100)
This driver supports vanilla keyboard and aux devices, along with Synaptic
touchpads.  Currently, no support for ALPS touchpads.  Disabled by default.
Add;
                extra-hvm = ps2-passthrough=
to config (both PVM and SVM) to enable.

console.h
dom0_driver.c
hw/pc.c
pass2.c [new file with mode: 0644]
pass2.h [new file with mode: 0644]
vl.c
xen-hooks.mak

index 5fbcf78215bf6d19d3775b64525366f56c54503f..f7f321df04e4e6679476d3a4bfeb3ce6cc67e120 100644 (file)
--- a/console.h
+++ b/console.h
@@ -191,4 +191,12 @@ void hid_linux_add_binding(const int *, void (*)(void*), void *);
 void hid_linux_reset_keyboard(void);
 void hid_linux_probe(int grab);
 
+/* pass2 */
+extern void    pa2_binding_add(const int *, void (*)(void*), void *);
+extern void    pa2_kbd_reset(void);
+extern void    pa2_kbd_secure(void (*)(int));
+extern int     pa2_aux_grab(int);
+extern int     pa2_kbd_grab(int);
+extern void    pa2_probe(int);
+extern void    pa2_init(void);
 #endif
index 4c3c60623ad30c62472395bff83110145bdf23f5..72373737c1887f888dde55149a53abb337e4c6a2 100644 (file)
@@ -44,6 +44,7 @@
 
 extern int vga_passthrough;
 extern int kbd_passthrough;
+extern int ps2_passthrough;
 
 static void dom0_driver_state_change(const char *path, void *opaque);
 static void dom0_driver_command(const char *path, void *opaque);
@@ -111,6 +112,16 @@ static struct dom0_driver_handler dom0_driver_hid_linux =
     .add_binding = hid_linux_add_binding,
 };
 
+static struct dom0_driver_handler dom0_driver_pa2 = {
+       .init                   = pa2_init,
+       .probe                  = pa2_probe,
+       .grab_keyboard          = pa2_kbd_grab,
+       .grab_mouse             = pa2_aux_grab,
+       .secure_keyboard        = pa2_kbd_secure,
+       .reset_keyboard         = pa2_kbd_reset,
+       .add_binding            = pa2_binding_add,
+};
+
 static struct dom0_driver driver;
 static struct dom0_driver_handler *dom0_driver_handler = NULL;
 
@@ -664,7 +675,11 @@ void dom0_driver_init(const char *position)
 {
     memset(&driver, 0, sizeof (driver));
 
-    dom0_driver_handler = &dom0_driver_hid_linux;
+    if (ps2_passthrough) {
+       dom0_driver_handler = &dom0_driver_pa2;
+    } else {
+       dom0_driver_handler = &dom0_driver_hid_linux;
+    }
     dom0_driver_handler->init();
 
     if (vga_passthrough)
diff --git a/hw/pc.c b/hw/pc.c
index 49a833640333e83b94c7513a7ee1ce10a95b5cfa..57251aec9b1e8280f942c1a795daee5db1b9b096 100644 (file)
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -54,6 +54,7 @@ static IOAPICState *ioapic;
 static PCIDevice *i440fx_state;
 
 extern int vga_passthrough;
+extern int ps2_passthrough;
 extern int kbd_passthrough;
 
 static void xen_relocator_hook(target_phys_addr_t *prot_addr_upd,
@@ -1064,7 +1065,9 @@ vga_bios_error:
         tpm_tis_init(&i8259[11]);
 #endif
 
-    if (kbd_passthrough)
+    if (ps2_passthrough) {
+       pa2_i8042_init(i8259[1], i8259[12], 0x60);
+    } else if (kbd_passthrough)
       pt_i8042_init(i8259[1], i8259[12], 0x60);
     else
       i8042_init(i8259[1], i8259[12], 0x60);
diff --git a/pass2.c b/pass2.c
new file mode 100644 (file)
index 0000000..3a64653
--- /dev/null
+++ b/pass2.c
@@ -0,0 +1,11182 @@
+/*
+ * PS/2 Pass-through driver.  Version: 0.9
+ * A host which has focus to a device (KBD or AUX) talks directly to the h/w
+ * while this driver "monitors" the conversation.
+ * For a host which does not have focus to a device this driver is able to
+ * hold the 'expected' conversation to keep the host happy.  When a host
+ * obtains focus to a device, it is programmed to behave as the host expects.
+ * This works for vanilla PS/2 devices (KBD and AUX), and also for the
+ * Synaptics touchpad.
+ * Currently, there are a number of limitations;
+ *     o switching focus part way through a cmd works only for a number
+ *       of cmds.  should wait until the PVM is up (login screen) before
+ *       switching to a SVM, and that SVM should be up too.  This avoids
+ *       interrupting the initialization conversations between the hosts
+ *       and h/w.
+ *     o switching VMs takes around a second.  this will be reduced in
+ *       later versions.
+ *     o tested with Vista and Ubuntu as primary VM's, but only with
+ *       Vista with secondary VMs present.
+ *     o not yet tested with AppServer....XXXX
+ */
+
+#include <linux/input.h>
+
+#include "hw.h"
+
+/*
+ * Kludge; pass2.h should be pulled in from the kernel's headers (it contains
+ * ioctls and common structures between IOEMU and the kernel-side driver).
+ * Keep IOEMU compile-time separate from the kernel for now by including
+ * a copy of pass2.h.
+ * Ugh; u32 and u8 aren't part of IOEMU.  This will go away...
+ */
+
+typedef uint32_t       u32;            /* XXX */
+typedef        unsigned char   u8;
+#include "pass2.h"
+#include "qemu-common.h"
+#include "console.h"
+#include "qemu-timer.h"
+#include "qemu-xen.h"
+
+#include "isa.h"
+#include "pc.h"
+#include "sysemu.h"
+
+/*
+ * Comment out define of PA2_DEBUG to compile non-debug.
+ */
+
+/* #define     PA2_DEBUG */
+#define        PA2_MIN(a, b)   ((a) < (b) ? (a) : (b))
+
+/**
+ ** Constants.
+ **/
+
+/*
+ * Keyboard Controller Commands
+ */
+
+#define        PA2_CTL_CMD_MODE_READ           0x20    /* read mode bits */
+#define        PA2_CTL_CMD_MODE_WRITE          0x60    /* write mode bits */
+#define        PA2_CTL_CMD_VERSION_GET         0xA1    /* get controller version */
+#define        PA2_CTL_CMD_AUX_DISABLE         0xA7    /* disable AUX interface */
+#define        PA2_CTL_CMD_AUX_ENABLE          0xA8    /* enable AUX interface */
+#define        PA2_CTL_CMD_AUX_TEST            0xA9    /* AUX interface test */
+#define        PA2_CTL_CMD_SELF_TEST           0xAA    /* controller self test */
+#define        PA2_CTL_CMD_KBD_TEST            0xAB    /* KBD interface test */
+#define        PA2_CTL_CMD_KBD_DISABLE         0xAD    /* KBD interface disable */
+#define        PA2_CTL_CMD_KBD_ENABLE          0xAE    /* KBD interface enable */
+#define        PA2_CTL_CMD_INPORT_READ         0xC0    /* read input port */
+#define        PA2_CTL_CMD_OUTPORT_READ        0xD0    /* read output port */
+#define        PA2_CTL_CMD_OUTPORT_WRITE       0xD1    /* write output port */
+#define        PA2_CTL_CMD_OBUF_WRITE          0xD2    /* write output buffer to AUX */
+#define        PA2_CTL_CMD_WRITE_AUX_OBUF      0xD3    /* write to output buffer as if
+                                                  initiated by the AUX */
+#define        PA2_CTL_CMD_WRITE_AUX           0xD4    /* write following to AUX */
+#define        PA2_CTL_CMD_DISABLE_A20         0xDD    /* HP vectra only ? */
+#define        PA2_CTL_CMD_ENABLE_A20          0xDF    /* HP vectra only ? */
+#define        PA2_CTL_CMD_RESEND              0xFE    /* resend last byte */
+#define        PA2_CTL_CMD_RESET               0xFF    /* internal reset */
+
+/*
+ * Controller Replies.
+ * PA2_CTL_REPLY_SELF          success self-test
+ */
+
+#define        PA2_CTL_REPLY_SELF      0x55
+
+/*
+ * Keyboard Commands
+ * Typematic cmds are accepted by this driver, and (if focused) passed
+ * through to the h/w, but are not restored when focus is switched.
+ */
+
+#define PA2_KBD_CMD_LEDS_SET           0xED    /* Set keyboard leds */
+#define PA2_KBD_CMD_ECHO               0xEE    /* Reply with echo response */
+#define        PA2_KBD_CMD_SCAN_CODE_SET       0xF0    /* not implemented XXX */
+#define PA2_KBD_CMD_GET_ID             0xF2    /* get keyboard ID */
+#define PA2_KBD_CMD_RATE_SET           0xF3    /* Set typematic rate */
+#define PA2_KBD_CMD_ENABLE             0xF4    /* Enable scanning */
+#define PA2_KBD_CMD_RESET_DISABLE      0xF5    /* reset and disable scanning */
+#define PA2_KBD_CMD_RESET_ENABLE       0xF6    /* reset and enable scanning */
+#define        PA2_KBD_CMD_RESET_TYPEMATIC     0xF7    /* ignored XXX */
+#define        PA2_KBD_CMD_ALL_BREAK           0xF8
+#define        PA2_KBD_CMD_ALL_MAKE            0xF9
+#define        PA2_KBD_CMD_ALL_TYPE_MAKE       0xFA
+#define        PA2_KBD_CMD_SET_KEY_TYPE        0xFB
+#define        PA2_KBD_CMD_SET_KEY_BREAK       0xFC
+#define        PA2_KBD_CMD_SET_KEY_MAKE        0xFD
+#define        PA2_KBD_CMD_RESEND              0xFE
+#define PA2_KBD_CMD_RESET              0xFF    /* Reset */
+
+/*
+ * Keyboard Replies
+ * PA2_KBD_REPLY_OVERRUN1      Key Detection Error or Overrun Error for Scan
+ *                             Code Set 1, replaces last key in the keyboard
+ *                             buffer if the buffer is full.
+ * PA2_KBD_REPLY_BAT_SUCC      AT Completion Code, keyboard sends this to
+ *                             indicate the keyboard test was successful.
+ * PA2_KBD_REPLY_ECHO_RESP     Echo Response, response to the Echo command.
+ * PA2_KBD_REPLY_BC_PREFIX     Break Code Prefix in Scan Code Sets 2 and 3.
+ * PA2_KBD_REPLY_ACK           Acknowledge, keyboard sends this whenever a
+ *                             valid command or data byte is received (except
+ *                             on Echo and Resend commands).
+ * PA2_KBD_REPLY_BAT_FAIL      BAT Failure Code, keyboard sends this to
+ *                             indicate the keyboard test failed and stops
+ *                             scanning until a response or reset is sent.
+ * PA2_KBD_REPLY_RESEND                Resend, keyboard request resend of data when
+ *                             data sent to it is invalid or arrives with
+ *                             invalid parity.
+ * PA2_KBD_REPLY_OVERUN2       Key Detection Error or Overrun Error for Scan
+ *                             Code Set 2 or 3, replaces last key in the
+ *                             keyboard buffer if the buffer is full.
+ */
+
+#define        PA2_KBD_REPLY_OVERRUN1          0x00
+#define PA2_KBD_REPLY_BAT_SUCC         0xAA
+#define        PA2_KBD_REPLY_ECHO_RESP         0xEE
+#define        PA2_KBD_REPLY_BC_PREFIX         0xF0
+#define PA2_KBD_REPLY_ACK              0xFA
+#define        PA2_KBD_REPLY_BAT_FAIL          0xFC
+#define PA2_KBD_REPLY_RESEND           0xFE
+#define        PA2_KBD_REPLY_OVERUN2           0xFF
+
+/*
+ * Status Register Bits
+ */
+
+#define PA2_CTL_STAT_OBF               0x01    /* KBD output buffer full */
+#define PA2_CTL_STAT_IBF               0x02    /* KBD input buffer full */
+#define PA2_CTL_STAT_SELFTEST          0x04    /* Self test successful */
+#define PA2_STAT_CMD                   0x08    /* Set when last write was a
+                                                * cmd */
+#define PA2_CTL_STAT_UNLOCKED          0x10    /* Zero if keyboard locked */
+#define PA2_CTL_STAT_AUX_OBF           0x20    /* AUX output buffer full */
+#define PA2_CTL_STAT_GTO               0x40    /* receive/xmit timeout */
+#define PA2_CTL_STAT_PERR              0x80    /* Parity error */
+
+/*
+ * Controller Mode Register Bits.
+ */
+
+#define PA2_CTL_MODE_INT_KBD           0x01    /* KBD data generate IRQ1 */
+#define PA2_CTL_MODE_INT_AUX           0x02    /* AUX data generate IRQ12 */
+#define PA2_CTL_MODE_SYS               0x04    /* The system flag (?) */
+#define PA2_CTL_MODE_NO_KEYLOCK                0x08    /* keylock doesn't affect the
+                                                * KBD */
+#define PA2_CTL_MODE_DISABLE_KBD       0x10    /* Disable keyboard interface */
+#define PA2_CTL_MODE_DISABLE_AUX       0x20    /* Disable aux interface */
+#define PA2_CTL_MODE_KCC               0x40    /* Scan code conversion to PC
+                                                * format */
+#define PA2_CTL_MODE_RFU               0x80
+
+/*
+ * AUX status bits.
+ * A status request returns 3 bytes.  The first byte's status bits are
+ * below.  The second byte contains the resolution in two LSb, and the third
+ * byte is the sample rate.
+ */
+
+#define        PA2_AUX_STAT_REMOTE             0x40    /* 1 = remote, 0 = stream */
+#define        PA2_AUX_STAT_ENABLE             0x20    /* 1 = data reporting enabled.
+                                                * effects stream mode only */
+#define        PA2_AUX_STAT_SCALING            0x10    /* 1 = scale is 2:1, 0 = 1:1 */
+#define        PA2_AUX_STAT_LEFT               0x04    /* 1 left btn pressed */
+#define        PA2_AUX_STAT_MIDDLE             0x02    /* 1 mid btn pressed */
+#define        PA2_AUX_STAT_RIGHT              0x01    /* 1 right btn pressed */
+
+/*
+ * AUX Commands
+ * AUX_SYNAP_E1 and AUX_SYNAP_E2 are Synaptic cmds.  Not 100% what they do...
+ */
+
+#define        PA2_AUX_CMD_SYNAP_E1            0xE1    /* XXX */
+#define        PA2_AUX_CMD_SYNAP_E2            0xE2    /* XXX */
+
+#define PA2_AUX_CMD_SCALE11_SET                0xE6    /* Set 1:1 scaling */
+#define PA2_AUX_CMD_SCALE21_SET                0xE7    /* Set 2:1 scaling */
+#define PA2_AUX_CMD_RES_SET            0xE8    /* Set resolution */
+#define PA2_AUX_CMD_STATUS_GET         0xE9    /* Get scaling factor */
+#define PA2_AUX_CMD_STREAM_SET         0xEA    /* Set stream mode */
+#define PA2_AUX_CMD_POLL               0xEB    /* Poll */
+#define PA2_AUX_CMD_WRAP_RESET         0xEC    /* Reset wrap mode */
+#define PA2_AUX_CMD_WRAP_SET           0xEE    /* Set wrap mode */
+#define PA2_AUX_CMD_REMOTE_SET         0xF0    /* Set remote mode */
+#define PA2_AUX_CMD_TYPE_GET           0xF2    /* Get type */
+#define PA2_AUX_CMD_SAMPLE_SET         0xF3    /* Set sample rate */
+#define PA2_AUX_CMD_DEV_ENABLE         0xF4    /* Enable aux device */
+#define PA2_AUX_CMD_DEV_DISABLE                0xF5    /* Disable aux device */
+#define PA2_AUX_CMD_DEFAULT_SET                0xF6
+#define PA2_AUX_CMD_ACK                        0xFA    /* Command byte ACK. */
+#define PA2_AUX_CMD_RESET              0xFF    /* Reset aux device */
+
+/*
+ * AUX replies.
+ */
+
+#define        PA2_AUX_REPLY_ACK               0xFA    /* ack */
+#define        PA2_AUX_REPLY_ERROR             0xFC    /* error - also failed BAT */
+#define        PA2_AUX_REPLY_RESEND            0xFE
+#define        PA2_AUX_REPLY_BAT_SUCC          0xAA    /* successful BAT */
+
+/*
+ * Outport bits
+ */
+
+#define        PA2_OUTPORT_SYSR        0x01
+#define        PA2_OUTPORT_GA20        0x02
+#define        PA2_OUTPORT_AXDO        0x04
+#define        PA2_OUTPORT_ACLK        0x08
+#define        PA2_OUTPORT_OUTB        0x10
+#define        PA2_OUTPORT_AUXB        0x20
+#define        PA2_OUTPORT_KCLK        0x40
+#define        PA2_OUTPORT_KBDO        0x80
+
+/*
+ * Synaptics "special" get request arguments.
+ */
+
+#define        PA2_SYNAP_GET_IDENTIFY          0x00
+#define        PA2_SYNAP_GET_MODES             0x01
+#define        PA2_SYNAP_GET_CAPS              0x02
+#define        PA2_SYNAP_GET_MODEL             0x03
+#define        PA2_SYNAP_GET_UNKNOWN_XXX1      0x04
+#define        PA2_SYNAP_GET_SERIAL_PREFIX     0x06
+#define        PA2_SYNAP_GET_SERIAL_SUFFIX     0x07
+#define        PA2_SYNAP_GET_RESOLUTION        0x08
+#define        PA2_SYNAP_GET_EXTENDED_MODEL    0x09
+#define        PA2_SYNAP_GET_UNKNOWN_XXX3      0x0C
+#define        PA2_SYNAP_GET_UNKNOWN_XXX4      0x0D
+
+/*
+ * Interrupt vectors.
+ */
+
+#define        PA2_KBD_IRQ             1
+#define        PA2_AUX_IRQ             12
+
+#define        PA2_DATA_PORT           0x60
+#define        PA2_CMD_PORT            0x64
+#define        PA2_STATUS_PORT         0x64
+
+/*
+ * Format of a AUX data reporting.  Current does not support Synaptic's
+ * historical mode. XXX
+ */
+enum pa2_aux_fmt_e { PA2_AUX_FMT_DEFAULT, PA2_AUX_FMT_ABS,
+                       PA2_AUX_FMT_ABS_WMODE };
+typedef enum pa2_aux_fmt_e     pa2_aux_fmt_t;
+
+/*
+ * The last motion data reported for the AUX device.
+ * Currently, not necessary to maintain as is never referenced (but is updated).
+ * If PA2_AUX_POLL cmd is to be supported when the AUX device isn't active in
+ * this VM, then could use this to report the last known position.
+ */
+
+typedef struct pa2_aux_data_s {
+       pa2_aux_fmt_t   pad_format;
+       uint8_t         pad_counter;
+       uint8_t         pad_byte1;
+       uint8_t         pad_xdelta;
+       uint8_t         pad_ydelta;
+       uint16_t        pad_x;
+       uint16_t        pad_y;
+       uint8_t         pad_z;
+       uint8_t         pad_w;
+} pa2_aux_data_t;
+
+/*
+ * Indexes into various arrays.  Not all arrays have/need PA2_INDEX_CTL.
+ * PA2_INDEX_KBD               - index into arrays for KBD device
+ * PA2_INDEX_AUX               - index into arrays for AUX device
+ * PA2_INDEX_CTL               - index into arrays for CTL.
+ * PA2_INDEX_WILD              - allowed called func to decide if KBD or AUX
+ *                               is the intended target
+ * PA2_INDEX_RAW               - ignored any queued data/status and read
+ *                               directly from h/w (used internally to
+ *                               test if input buffer is empty for next byte).
+ */
+
+#define        PA2_INDEX_KBD           0
+#define        PA2_INDEX_AUX           1
+#define        PA2_INDEX_CTL           2
+#define        PA2_INDEX_WILD          -1
+#define        PA2_INDEX_RAW           -2
+
+/*
+ * Constants for the data queues.
+ * Size is always a power of two to allow easy masking of indices into
+ * ph_irqbuf[].
+ */
+
+#define        PA2_IRQBUF_POWER        8
+#define        PA2_IRQBUF_SIZE (1 << PA2_IRQBUF_POWER)
+#define        PA2_IRQBUF_MASK (PA2_IRQBUF_SIZE - 1)
+
+/*
+ * Head of data.
+ *    ph_irqbuf                - array of data
+ *    ph_start         - first valid entry in ph_irqbuf[]
+ *    ph_end           - first invalid entry in ph_irqbuf[]
+ *    ph_overflow      - ph_irqbuf[] would have overflowed
+ * New data items are placed at ph_end.
+ * Data items are read from ph_start.
+ */
+
+typedef struct pa2_hdata_s {
+       pa2_entry_t     ph_irqbuf[PA2_IRQBUF_SIZE];
+       uint            ph_start;
+       uint            ph_end;
+       uint            ph_overflow;
+} pa2_hdata_t;
+
+/*
+ * Function types.  These are called when a cmd is accepted (pa2_fcmd_t),
+ * completed (pa2_fdone_t), any data input/output (pa2_fwrite_t/pa2_fread_t),
+ * and if the cmd needs to be redone (due to host gaining focus part way
+ * through cmd).
+ */
+
+struct pa2_s;          /* XXX */
+struct pa2_cmd_s;      /* XXX */
+typedef int    (pa2_fcmd_t)(struct pa2_s *, struct pa2_cmd_s *);
+typedef        void    (pa2_fdone_t)(struct pa2_s *, struct pa2_cmd_s *);
+typedef        int     (pa2_fwrite_t)(struct pa2_s *, struct pa2_cmd_s *, uint8_t);
+typedef        int     (pa2_fread_t)(struct pa2_s *, struct pa2_cmd_s *, uint8_t);
+typedef        int     (pa2_fredo_t)(struct pa2_s *);
+
+/*
+ * Where does the output of a command go to.  Some cmds can be "run in
+ * parallel" on the h/w.  eg. a read of the outport (PA2_CTL_CMD_OUTPORT_READ),
+ * and a simulated write to the AUX device (PA2_CTL_CMD_WRITE_AUX_OBUF).
+ * To determine if cmds can be run in parallel, each has its own output
+ * destination.
+ *     PA2_OUTPUT_KBD          - normal output buffer
+ *     PA2_OUTPUT_AUX          - AUX output buffer
+ *     PA2_OUTPUT_NONE         - no output generated
+ *     PA2_OUTPUT_MAX          - gives size for arrays...
+ */
+
+enum pa2_output_dest_e { PA2_OUTPUT_KBD, PA2_OUTPUT_AUX,
+                        PA2_OUTPUT_NONE, PA2_OUTPUT_MAX };
+typedef enum pa2_output_dest_e pa2_dest_t;
+
+/*
+ * Command descriptor.
+ * This is used in building the tables for the three sets of cmds; controller,
+ * keyboard, and aux.
+ *
+ * pcc_cmd             - command code
+ * pcc_name            - name given to the command
+ * pcc_bytes_in                - number of bytes we expect from the h/w,
+ *                       excluding ACK (if XXX set in pp_flags).
+ * pcc_bytes_out       - number of bytes we'll be sending to the h/w for
+ *                       the cmd.
+ * pcc_output_dest     - specifices where the output goes; kbd or aux.
+ * pcc_seq             - for aux cmds only; specifies how many argument bytes
+ *                       the aux cmd has.  Such as SET_RES and SET_SAMPLE.
+ */
+
+typedef struct pa2_cmd_des_s {
+       const uint8_t   pcc_cmd;
+       const char      *pcc_name;
+       const uint8_t   pcc_bytes_in;
+       const uint8_t   pcc_bytes_out;
+       const uint8_t   pcc_seq;
+       pa2_dest_t      pcc_output_dest;
+       const uint32_t  pcc_flags;      /* combine with pc_flags */
+       pa2_fwrite_t    *pcc_func_write;
+       pa2_fread_t     *pcc_func_read;
+       pa2_fcmd_t      *pcc_func_cmd;
+       pa2_fdone_t     *pcc_func_done;
+       pa2_fredo_t     *pcc_func_redo;
+} pa2_cmd_des_t;
+
+/*
+ * pp_cmd              - controller cmd being processed
+ * pp_irqbuf           - status and data received from kernel, trigger by
+ *                       a h/w interrupt.
+ * pp_start            - first empty position in pp_irqbuf[].
+ * pp_end              - first populated position in pp_irqbuf[].
+ * pp_expected_input   - expected response from device.  This is the next
+ *                       byte expected, and is used when switching focus
+ *                       between guests when a cmd is active.
+ * pc_aux_res_set_seq          - consective number of PA2_AUX_CMD_RES_SET seen
+ * pc_aux_special_byte         - special command byte encoded in four
+ *                               args to PA2_AUX_CMD_RES_SET
+ */
+
+struct pa_syp_0x28_s;          /* XXX */
+typedef struct pa2_cmd_s {
+       uint8_t         pc_cmd;
+       uint8_t         pc_active;
+       uint8_t         pc_bytes_in;
+       uint8_t         pc_bytes_out;
+       uint8_t         pc_counter;
+       uint8_t         pc_expected_input;
+       uint8_t         pc_tmp_buf[16];
+       uint16_t        pc_flags;
+
+
+       pa2_cmd_des_t   *pc_cmd_desp;
+
+       /* should ref these straight from the table - no need to copy XXX */
+       pa2_fwrite_t    *pc_func_write;
+       pa2_fread_t     *pc_func_read;
+       pa2_fcmd_t      *pc_func_cmd;
+       pa2_fdone_t     *pc_func_done;
+       pa2_fredo_t     *pc_func_redo;
+
+       uint8_t         pc_aux_cmd;
+       uint8_t         pc_aux_seq;
+       uint8_t         pc_aux_res_set_seq;
+       uint8_t         pc_aux_special_byte;
+       pa2_cmd_des_t   *pc_aux_cmd_desp;
+       pa2_fdone_t     *pc_aux_func_done;
+       pa2_fwrite_t    *pc_aux_func_write;
+       pa2_fwrite_t    *pc_aux_func_read;
+
+       struct pa_syp_0x28_s    *pc_aux_p28;
+} pa2_cmd_t;
+
+/*
+ * pc_flags.
+ */
+
+#define        PA2_CMD_FLG_INPUT       0x0001  /* expect bytes from h/w (true? XXX */
+#define        PA2_CMD_FLG_OUTPUT      0x0002  /* expects bytes to send to h/w XXX */
+#define        PA2_CMD_FLG_ACK         0x0004  /* expect ACK from h/w */
+#define        PA2_CMD_FLG_CTRL        0x0008  /* ctl cmd */
+#define        PA2_CMD_FLG_KBD         0x0010  /* KBD cmd */
+#define        PA2_CMD_FLG_AUX         0x0020  /* AUX cmd */
+#define        PA2_CMD_FLG_AUX_BYTE    0x0040  /* AUX cmd */
+#define        PA2_CMD_FLG_WR_ACTIVE   0x0080  /* cmd in write progress */
+#define        PA2_CMD_FLG_SHORT       0x0100  /* short form cmd - presently, only 
+                                          used for PA2_AUX_CMD_STATUS_GET */
+#define        PA2_CMD_FLG_EXPECTED    0x0200  /* pc_expected_input is valid */
+#define        PA2_CMD_FLG_PENDING_AUX 0x0400  /* pending PA2_CTL_CMD_WRITE_AUX */
+#define        PA2_CMD_FLG_SPECIAL     0x1000  /* synaptic special cmd seq */
+
+/*
+ * Global data (constant).
+ *  pg_ctl_test_result_kbd     result of PA2_CTL_CMD_KBD_TEST
+ *  pg_ctl_test_result_aux     result of PA2_CTL_CMD_AUX_TEST
+ *  pg_kbd_id                  result of PA2_KBD_CMD_GET_ID
+ */
+
+/*
+ * When the pointing device is a Synaptic touchpad, this structure contains
+ * the variable state associated with the touchpad.
+ *    ps_mode_byte     - the touchpad mode bytes, whose bits are defined
+ *                       by PA2_SYNAP_BIT_*
+ *    ps_e1_byte1
+ *    ps_e1_byte2
+ *    ps_hv_flgs       - flags describing the state of the members of this
+ *                       structure.
+ */
+
+typedef struct pa2_syp_state_s {
+       uint8_t         ps_mode_byte;
+       uint8_t         ps_e1_byte1;
+       uint8_t         ps_e1_byte2;
+       uint8_t         ps_hv_flgs;
+} pa2_syp_state_t;
+
+/*
+ * No idea about the E1's.
+ * On first access, from a power-cycled system, they have;
+ *     0x01    (ps_e1_byte1)
+ *     0x0e    (ps_e1_byte2)
+ */
+
+/*
+ * ps_hv_flgs
+ */
+
+#define        PA2_HV_SYNAP_STATE_MODE_BYTE    0x01    /* have ps_mode_byte */
+#define        PA2_HV_SYNAP_STATE_E1_BYTE1     0x02    /* have ps_e1_byte1 */
+#define        PA2_HV_SYNAP_STATE_E1_BYTE2     0x04    /* have ps_e1_byte2 */
+
+/*
+ * "special" bits for Synaptics' mode byte.
+ * In the currently available set of docs, bit-5 is marked as reserved
+ * for future use.  However, with version 7 of the touchpad (at least),
+ * it disables the special cmd sequence (SET_RES * 4 + GET_STATUS) making
+ * it into a "normal" GET_STATUS.  I'm sure this behaviour is definied by
+ * a capability bit somewhere....but for now, assume this is always the
+ * case (hopefully, will get full docs in the future).  Note, it is
+ * cleared by a reset but not by a set_defaults.
+ */
+
+#define        PA2_SYNAP_BIT_WMODE             0x01    /* wide */
+#define        PA2_SYNAP_BIT_PSIZE             0x02    /* packsize */
+#define        PA2_SYNAP_BIT_GEST              0x04    /* gesture */
+#define        PA2_SYNAP_BIT_BAUD              0x08    /* baud/sleep */
+#define        PA2_SYNAP_BIT_TRANSPARENT       0x20    /* reserved engineered */
+#define        PA2_SYNAP_BIT_RATE              0x40    /* rate */
+#define        PA2_SYNAP_BIT_ABS               0x80    /* absolute */
+
+/*
+ * Seeing undocumented Synaptic extended cmd sequence;
+ *     RES_SET * 4 + SAMPLE_SET 0x28
+ * (0x14 is documented, 0x28 isn't).  During initialization (on the PVM)
+ * this driver was capturing the output of different 0x28 cmds and storing
+ * the result here.  That was taking a loooong time, so now it is hard
+ * coded in pa2_aux_once_init() as the returned values are mostly the same.
+ * Need to invesigate this deeper (ie. talk to Synaptic), but for now
+ * blindly continue with the dumb method as it appears to keep the Windows
+ * Synaptic driver happy.  Note; ubuntu uses different 0x028 cmds than
+ * Windows (Vista).  Currently, this means ubuntu is not supported as a
+ * SVM. XXX
+ *    p28_status       - set if p28_bytes[] is populated for this entry
+ *    p28_special      - the 'special' cmd byte encoded in the RES_SET * 4
+ *    p28_bytes                - the response.  usually 5 bytes, but can be 11.
+ */
+
+typedef struct pa_syp_0x28_s {
+       uint8_t         p28_status;
+       uint8_t         p28_special;
+       uint8_t         p28_bytes[12];
+} pa_syp_0x28_t;
+
+/*
+ * The constant values from a synaptic touchpad, mostly read by "information
+ * queries" (4 * RES_SET + SAMPLE_GET).
+ * The first instance of this driver loads this array.  Subsequent instances
+ * are given a copy of this structure so they have the neccesary responses
+ * to the queries when they are not the active instance.
+ *
+ * First byte of identify response (PA2_SYNAP_GET_IDENTIFY);
+ *    pcy_info_minor   minor version number
+ * Third byte of identify response (PA2_SYNAP_GET_IDENTIFY);
+ *    pcy_info_mod_code        model code
+ *    pcy_info_major   major version number
+ * First byte of extended capability bits (PA2_SYNAP_GET_CAPS);
+ *    pcy_capExtended  set if extended capability bits are supported, also
+ *                     also signifies that the touchpad supports W mode.
+ *    pcy_capQueries   number of extended queries supported; 0x8 + capQueries.
+ *    pcy_capReserved1
+ *    pcy_capMidBut    set if touchpad has a middle button
+ *    pcy_capReserved2
+ * Third byte of extended capability bits (PA2_SYNAP_GET_CAPS);
+ *    pcy_capReserved3
+ *    pcy_capSleep     set if sleep mode is supported
+ *    pcy_capFourBut   set if four buttons supported
+ *    pcy_capBallistics        only used by TouchStyks (? XXX)
+ *    pcy_capMultiFinger       set if multi-finger detection is supported
+ *    pcy_capPalmDetect        set if palm detection is supported
+ */
+
+typedef struct pa2_syp_const_s {
+       uint8_t         pcy_info_minor;
+       uint8_t         pcy_info_major;
+       uint8_t         pcy_info_mod_code;
+       uint8_t         pcy_info_xupmm;
+       uint8_t         pcy_info_yupmm;
+       uint8_t         pcy_capExtended;
+       uint8_t         pcy_capQueries;
+       uint8_t         pcy_capReserved1;
+       uint8_t         pcy_capMidBut;
+       uint8_t         pcy_capReserved2;
+       uint8_t         pcy_capReserved3;
+       uint8_t         pcy_capSleep;
+       uint8_t         pcy_capFourBut;
+       uint8_t         pcy_capBallistics;
+       uint8_t         pcy_capMultiFinger;
+       uint8_t         pcy_capPalmDetect;
+
+       uint32_t        pcy_model_id;
+       uint64_t        pcy_serial_num;
+
+                               /* we're not getting this as doc'ed XXX */
+       uint8_t         pcy_modes_first_byte;
+
+       uint8_t         pcy_unknown11;
+       uint8_t         pcy_unknown12;
+       uint8_t         pcy_unknown13;
+
+       uint8_t         pcy_unknown21;
+       uint8_t         pcy_unknown22;
+       uint8_t         pcy_unknown23;
+
+       uint8_t         pcy_unknown31;
+       uint8_t         pcy_unknown32;
+       uint8_t         pcy_unknown33;
+
+       uint8_t         pcy_unknown41;
+       uint8_t         pcy_unknown42;
+       uint8_t         pcy_unknown43;
+
+       uint8_t         pcy_initial_e1_byte1;
+       uint8_t         pcy_initial_e1_byte2;
+
+       /* values in undefined fields */
+       uint8_t         pcy_nibble1;    /* read serial num XXX */
+       uint8_t         pcy_byte1;      /* read serial num XXX */
+       uint8_t         pcy_resol;      /* read resolutions XXX */
+       uint32_t        pcy_hv_flgs;
+
+       pa_syp_0x28_t   pcy_28_resp[20];
+} pa2_syp_const_t;
+
+#define        PA2_HV_SYNAP_INFO_MIN   0x00001         /* info_minor */
+#define        PA2_HV_SYNAP_INFO_MAJ   0x00002         /* info_major */
+#define        PA2_HV_SYNAP_MODEL_CD   0x00004         /* model_code */
+#define        PA2_HV_SYNAP_XUPMM      0x00008         /* info_xupmm */
+#define        PA2_HV_SYNAP_YUPMM      0x00010         /* info_yupmm */
+#define        PA2_HV_SYNAP_CAPS_FIRST_BYTE    0x00020
+#define        PA2_HV_SYNAP_CAPS_THIRD_BYTE    0x00040 
+#define        PA2_HV_SYNAP_MODEL_00   0x00080         /* model; 0-7 */
+#define        PA2_HV_SYNAP_MODEL_08   0x00100         /* model; 8-15 */
+#define        PA2_HV_SYNAP_MODEL_16   0x00200         /* model; 16-23 */
+#define        PA2_HV_SYNAP_SER_00     0x00400         /* serial; 0-7 */
+#define        PA2_HV_SYNAP_SER_08     0x00800         /* serial; 8-15 */
+#define        PA2_HV_SYNAP_SER_16     0x01000         /* serial; 16-23 */
+#define        PA2_HV_SYNAP_SER_24     0x02000         /* serial; 24-31 */
+#define        PA2_HV_SYNAP_SER_32     0x04000         /* serial; 32-35 */
+
+#define        PA2_HV_SYNAP_SER_NB     0x08000         /* pcy_nibble1 */
+#define        PA2_HW_SYNAP_SER_BYTE   0x10000         /* pcy_byte1 */
+#define        PA2_HW_SYNAP_RESOL      0x20000         /* pcy_hv_flgs */
+
+#define        PA2_HV_SYNAP_UNKNOWN1   0x0100000
+#define        PA2_HV_SYNAP_EXT_MODEL  0x0200000
+#define        PA2_HV_SYNAP_UNKNOWN3   0x0400000
+#define        PA2_HV_SYNAP_UNKNOWN4   0x0800000
+
+#define        PA2_HV_SYNAP_CONST_E1_BYTE1     0x1000000
+#define        PA2_HV_SYNAP_CONST_E1_BYTE2     0x2000000
+
+#define        PA2_HV_SYNAP_MODES_B1   0x4000000
+
+
+/*
+ * KBD and AUX state.
+ * This is the non-constant state.  If a Synaptic touchpad is identified, then
+ * pss_synaptic will also be populated (and PA2_CONST_SYNAP_YES set in
+ * corrsponding pa2_const_t's psc_flgs).
+ *
+ * These are actually synaptic state
+ * pss_setscale_last           - 0 if setscale11, 1 if setscale12
+ */
+
+typedef struct pa2_state_s {
+       uint8_t         pss_status_last;        /* status given to guest */
+       uint8_t         pss_status;             /* s/w calculate */
+       uint8_t         pss_mode;
+       uint8_t         pss_kbd_rate;
+       uint8_t         pss_kbd_leds;
+       uint8_t         pss_outport;
+       uint8_t         pss_aux_status;
+       uint8_t         pss_aux_res;
+       uint8_t         pss_aux_sample;
+       uint8_t         pss_aux_wrap_mode;      /* no have flag */
+       uint8_t         pss_setscale_last;
+       uint16_t        pss_hv_flgs;
+       pa2_syp_state_t pss_synaptic;
+} pa2_state_t;
+
+/*
+ * pss_hv_flgs
+ */
+
+#define        PA2_HV_STATE_AUX_REMOTE         0x0001  /* aux_status */
+#define        PA2_HV_STATE_AUX_ENABLE         0x0002  /* aux_status */
+#define        PA2_HV_STATE_AUX_SCALE          0x0004  /* aux_status */
+#define        PA2_HV_STATE_MODE               0x0008  /* mode bits are in sync */
+#define        PA2_HV_STATE_OUTPORT            0x0010  /* outport */
+#define        PA2_HV_STATE_AUX_RES            0x0020  /* aux_res */
+#define        PA2_HV_STATE_AUX_SAMP           0x0080  /* aux_samp */
+#define        PA2_HV_STATE_KBD_RATE           0x0100  /* kbd_rate */
+#define        PA2_HV_STATE_KBD_LEDS           0x0200  /* kbd_leds */
+
+/*
+ * Once known, these are constants.
+ * psc_inport          - value from inport (PA2_CTL_CMD_INPORT_READ)
+ * psc_kbd_id          - KBD ID (PA2_KBD_CMD_GET_ID).
+ * psc_aux_type                - AUX type (PA2_AUX_CMD_TYPE_GET).
+ * psc_ctl_test                - result of CTL test (PA2_CTL_CMD_SELF_TEST)
+ * psc_ctl_version     - result of CTL version cmd (PA2_CTL_CMD_VERSION_GET)
+ * psc_ctl_kbd_test    - result of CTL KBD test (PA2_CTL_CMD_KBD_TEST)
+ * psc_ctl_aux_test    - result of CTL AUX test (PA2_CTL_CMD_AUX_TEST)
+ * psc_hv_flgs         - have flags (fields that are populated)
+ * psc_flgs            - other flags
+ *
+ * On Lenovo T400, a PA2_CTL_CMD_VERSION_GET isn't responded to by the h/w.
+ * If this occurs, PA2_CONST_NO_CTL_VERSION is set so hosts without focus
+ * can not reply inkind.
+ */
+
+typedef struct pa2_const_s {
+       uint8_t         psc_inport;
+       uint16_t        psc_kbd_id;
+       uint8_t         psc_aux_type;
+       uint8_t         psc_ctl_test;
+       uint8_t         psc_ctl_version;
+       uint8_t         psc_ctl_kbd_test;
+       uint8_t         psc_ctl_aux_test;
+       uint16_t        psc_hv_flgs;            /* have flags */
+       uint8_t         psc_flgs;
+       pa2_syp_const_t psc_synap;
+} pa2_const_t;
+
+/* psc_hv_flgs */
+#define        PA2_HV_CONST_INPORT             0x0001          /* psc_kbd_id */
+#define        PA2_HV_CONST_KBD_ID             0x0002          /* psc_kbd_id */
+#define        PA2_HV_CONST_AUX_TYPE           0x0004          /* psc_aux_type */
+#define        PA2_HV_CONST_CTL_TEST           0x0010          /* psc_ctl_test */
+#define        PA2_HV_CONST_CTL_VERSION        0x0020          /* psc_ctl_version */
+#define        PA2_HV_CONST_CTL_KBD_TEST       0x0040          /* psc_ctl_aux_test */
+#define        PA2_HV_CONST_CTL_AUX_TEST       0x0080          /* psc_ctl_aux_test */
+
+/* psc_flgs */
+#define        PA2_CONST_SYNAP_PROBED          0x0001  /* have probe for synaptic? */
+#define        PA2_CONST_SYNAP_YES             0x0002  /* synaptic present */
+#define        PA2_CONST_NO_CTL_VERSION        0x0004
+
+/*
+ * One structure to bind them all together...
+ *    pp_fd[]          - file descriptors to communicate with kernel driver
+ *    pp_active[]      - has focus (KBD/AUX)
+ *    pp_grabbing[]    - device being grabbed or dropped (KBD/AUX).
+ *    pp_init          - set when performing first time initializing.
+ *    pp_limit         - timeout limit waiting for response...more work needed
+ *                       here XXX
+ *    pp_aux_data      - AUX data (last reported abs/rel position)
+ *    pp_data_last     - last data reported to host (should be split between
+ *                       KBD/AUX XXX)
+ *    pp_byte_write_last - last byte written by host (should be split between
+ *                       KBD/AUX XXX)
+ *    pp_keystate      - status of keys.  This is incomplete...XXX
+ */
+
+typedef struct pa2_s {
+       int             pp_fd[3];
+       uint            pp_active[2];
+       uint            pp_grabbing[2];
+       uint            pp_losing_focus[2];
+
+       pa2_state_t     pp_state_cur;
+       pa2_const_t     pp_state_const;
+       pa2_cmd_t       pp_cmds[PA2_OUTPUT_MAX];
+       pa2_hdata_t     pp_irqbuf[2];
+
+       uint            pp_init;
+       uint            pp_limit;
+
+       pa2_aux_data_t  pp_aux_data;
+
+       uint32_t        pp_flags;
+       uint8_t         pp_data_last;
+       uint8_t         pp_byte_write_last;
+
+       uint8_t         pp_keystate[2][128];
+       uint8_t         pp_key_esc;             /* escape seq */
+
+       /*
+        * Integration with rest of io-emu.
+        */
+
+       qemu_irq        pp_kbd_irq;
+       qemu_irq        pp_aux_irq;
+       uint32_t        pp_iobase;
+
+#if    defined(PA2_DEBUG)
+       uint            pp_status_count;
+#endif /* PA2_DEBUG */
+} pa2_t;
+
+/*
+ * pp_flags
+ *  PA2_FLG_KBD_IRQ            KBD interrupt raised
+ *  PA2_FLG_AUX_IRQ            AUX interrupt raised
+ *  PA2_FLG_UNREP_STATUS       haven't reported the last status read.  used
+ *                             by DEBUG only
+ *  PA2_FLG_SYNAPTIC_PROBE     seen synaptic probe (GET_IDENTITY) from this
+ *                             guest
+ */
+
+#define        PA2_FLG_KBD_IRQ         0x0001
+#define        PA2_FLG_AUX_IRQ         0x0002
+#define        PA2_FLG_UNREP_STATUS    0x0004
+#define        PA2_FLG_SYNAPTIC_PROBE  0x0010
+
+/*
+ * THE global!
+ */
+
+static pa2_t           pa2_state;
+
+/*
+ * These are the sample byte sequence seen during Synaptic initialization.
+ * They are sent as the 'special byte' in the special cmd sequence;
+ *     RES_SET * 4 + SAMPLE_SET (0x28)
+ * They are undocumented, but for most the response is 6 bytes.  Usually;
+ *     0xFA 0x84 0xFA 0x00 0xC4 0x00
+ * But for 0x2C, the response is 12 bytes;
+ *     0xFA 0x84 0xFA 0x00 0xC4 0x00
+ *     0x00 0x84 0x21 0x00 0xC4 0x00
+ * although I have seen shorted when the 3 byte is not 0xFA;
+ *     0xFA 0x84 0xFC 0x00 0xC4 0x00 0x00
+ * and for 0x42, have seen;
+ *     0xFA 0x84 0xFE 0x00 0xC4 0x00
+ * 0xFA is an "success" code.  0xFE is for a resend, and 0xFC is a failure code.
+ * I'm guessing these replies are calibration data, and so will be different
+ * for each touchpad (and depended upon humidity, etc).  But quering them
+ * during aux_once_init() is time consuming, leading to a long delay before
+ * the PVM starts.  For now, I'll hardcode replies, but need to get
+ * doc from Synaptic on this and code a more flexible solution.
+ */
+
+static uint8_t pa2_28_queries[] = {
+       0xE2, 0x81, 0x41, 0xE7,
+       0xE2, 0x81, 0x42, 0x00,
+       0xE2, 0x81, 0x43, 0x00,
+       0xE2, 0x2C,
+       0xEA,
+       0xE2, 0x47, 0x2C, 0x01,
+       0xEA,
+       0xE2, 0x81, 0x58, 0xFE,
+       0xEA,
+       0xE2, 0x81, 0x4A, 0x80,
+       0xEA,
+       0xE2, 0x81, 0x5E, 0x38,
+       0xEA,
+       0xE2, 0x81, 0x5A, 0xFF
+};
+
+/**
+ ** Defaults.
+ **/
+
+/*
+ * Some notes on Typematic Rate/delay, found at;
+ * http://docs.huihoo.com/help-pc/hw-keyboard_commands.html
+ * Set Typematic Rate/Delay, keyboard responds with ACK and waits
+ * for rate/delay byte.   Upon receipt of the rate/delay byte the
+ * keyboard responds with an ACK, then sets the new typematic
+ * values and scanning continues if scanning was enabled.
+ *          
+ * bit 7               always zero
+ * bits 5-6            typematic delay
+ * bits 3-4            B in period formula
+ * bits 1-2            A in period formula
+ * bit 1               typematic rate indicator
+ *
+ * delay = (rate+1) * 250   (in milliseconds)
+ * rate = (8+A) * (2**B) * 4.17  (in seconds, Â± 20%)
+ * Defaults to 10.9 characters per second and a 500ms delay.  If a
+ * command byte (byte with high bit set) is received instead of an
+ * option byte this command is cancelled.
+ *     So, default for a keyboard reset is;
+ *     A = 3, B = 1, rate=1
+ * Giving at setting of;
+ *     00101011
+ */
+
+#define        PA2_KBD_DEFAULT_RATE    0x2B
+
+/*
+ * Values seen read from a cold booted Synaptic touchpad.
+ * As have no documentation on the E1 cmd, have no idea what these are...
+ */
+
+#define        PA2_SYNAP_E1_DEFAULT1           0x01
+#define        PA2_SYNAP_E1_DEFAULT2           0x0E
+
+/**
+ ** Debug support.
+ **/
+
+#if    defined(PA2_DEBUG)
+static FILE    *debug_file;
+static uint    debug_level;
+
+#define        ASSERT(cond)            \
+       do {                                                            \
+               char    *file;                                          \
+                                                                       \
+               if ((cond) == 0) {                                      \
+                       file = basename(__FILE__);                      \
+                       fprintf(debug_file, "BUG: %s :%d: \"%s\"\n",    \
+                                file, __LINE__, #cond);                \
+               }                                                       \
+       } while (0)
+
+/*
+ * Importance of debug message.
+ */
+
+#define        PA2_DBG_FUNC_EXIT       1
+#define        PA2_DBG_FUNC_ENTER      2
+#define        PA2_DBG_LOW             3
+#define        PA2_DBG_MID             4
+#define        PA2_DBG_HIGH            5
+#define        PA2_DBG_ERR             6
+
+#define        PA2_DBG_ALL     6
+
+/*static uint  pa2_dbg_lvl = PA2_DBG_ALL; */
+static uint    pa2_dbg_lvl = PA2_DBG_MID;
+
+#define        PRINT_DEBUG(lvl, format, args...)       \
+       do {                                                            \
+               pa2_t           *fp;                                    \
+                                                                       \
+               fp = &pa2_state;                                        \
+               if (debug_file == NULL) {                               \
+                       break;                                          \
+               }                                                       \
+               if ((lvl) < pa2_dbg_lvl) {                              \
+                       break;                                          \
+               }                                                       \
+               if (fp->pp_init == 1) {                                 \
+                       fprintf(debug_file, "I - ");                    \
+               } else if (fp->pp_grabbing[0] == 1 ||                   \
+                          fp->pp_grabbing[1] == 1) {                   \
+                       fprintf(debug_file, "G - ");                    \
+               } else if (fp->pp_active[0] == 1 ||                     \
+                          fp->pp_active[1] == 1) {                     \
+                       fprintf(debug_file, "A - ");                    \
+               }                                                       \
+               fprintf(debug_file, format, ## args);                   \
+               fflush(debug_file);                                     \
+       } while (0)
+
+#define        PRINT_DEBUG_ENTER(format, args...)      \
+       do {                                                            \
+               if (debug_file != NULL &&                               \
+                   PA2_DBG_FUNC_ENTER >= pa2_dbg_lvl) {                \
+                       fprintf(debug_file, "Enter: %s", __func__);     \
+                       fprintf(debug_file, format, ## args);           \
+                       fflush(debug_file);                             \
+               }                                                       \
+       } while (0)
+
+#define        PRINT_DEBUG_EXIT(format, args...)       \
+       do {                                                            \
+               if (debug_file != NULL &&                               \
+                   PA2_DBG_FUNC_EXIT >= pa2_dbg_lvl) {         \
+                       fprintf(debug_file, "Exit: %s", __func__);      \
+                       fprintf(debug_file, format, ## args);           \
+                       fflush(debug_file);                             \
+               }                                                       \
+       } while (0)
+
+#else
+
+#define        ASSERT(cond)                            do { } while (0)
+#define        PRINT_DEBUG(lvl, format, args...)       do { } while (0)
+#define        PRINT_DEBUG_ENTER(format, args...)      do { } while (0)
+#define        PRINT_DEBUG_EXIT(format, args...)       do { } while (0)
+
+#endif /* PA2_DEBUG */
+
+/**
+ ** General support functions.
+ **/
+
+/*
+ * Output error message to stderr (which can be re-directed by qemu to a log
+ * file), and (if enabled) to our debug log file.
+ */
+
+static __attribute__ ((format (printf, 1, 2))) void
+pa2_error(
+       const char      *fmt,
+       ...)
+{
+       va_list         ap;
+       int             error;
+
+       error = 1;
+       ASSERT(error == 0);
+       fprintf(stderr, "pa2: ");
+       va_start(ap, fmt);
+#if    defined(PA2_DEBUG)
+       if (debug_file != NULL) {
+               vfprintf(debug_file, fmt, ap);
+       }
+#endif /* PA2_DEBUG */
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+       return;
+}
+
+/**
+ ** Interfacing with the guest's interrupt routines.
+ **/
+
+/*
+ * Lower a raised interrupt.
+ */
+
+static void
+pa2_irq_lower(
+       pa2_t           *fp)
+{
+       /* mutually excluisve */
+       if (fp->pp_flags & PA2_FLG_KBD_IRQ) {
+               ASSERT((fp->pp_flags & PA2_FLG_AUX_IRQ) == 0);
+               PRINT_DEBUG(PA2_DBG_MID, "pa2_irq_lower: lowering kbd\n");
+               fp->pp_flags &= ~PA2_FLG_KBD_IRQ;
+               qemu_set_irq(fp->pp_kbd_irq, 0);
+       } else if (fp->pp_flags & PA2_FLG_AUX_IRQ) {
+               ASSERT((fp->pp_flags & PA2_FLG_KBD_IRQ) == 0);
+               PRINT_DEBUG(PA2_DBG_MID, "pa2_irq_lower: lowering aux\n");
+               fp->pp_flags &= ~PA2_FLG_AUX_IRQ;
+               qemu_set_irq(fp->pp_aux_irq, 0);
+       }
+       return;
+}
+
+/*
+ * Examine next input record, and decide if need to raise an interrupt, and
+ * if so which one.
+ */
+
+static void
+pa2_irq_raise(
+       pa2_t           *fp)
+{
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       uint            i;
+
+       if (fp->pp_flags & (PA2_FLG_KBD_IRQ | PA2_FLG_AUX_IRQ)) {
+               goto out;
+       }
+
+       /*
+        * If data is pending for a device, check mode bit to see if interrupts
+        * are enabled.  Only one raised interrupt.
+        * KBD comes first (PA2_KBD_INDEX < PA2_INDEX_AUX) so its interrupts
+        * take priority.  Needed as the AUX can spam a large number of
+        * interrupts.
+        * What if a bad keyboard spamming interrupts?  Not much can be done,
+        * but could have a round-robin... XXX
+        */
+
+       for (i = 0; i < 2; i++) {
+
+               /*
+                * Do not raise ints while grabbing.
+                * While grabbing a device, all output belongs internally
+                * and should not be exposed to a guest.
+                * (Grabbing includes releasing).
+                */
+
+               if (fp->pp_grabbing[i] == 1) {
+                       continue;
+               }
+               php = &fp->pp_irqbuf[i];
+               if (php->ph_start == php->ph_end) {
+                       continue;
+               }
+
+               pdp = &php->ph_irqbuf[php->ph_start];
+               if (i == PA2_INDEX_KBD) {
+                       ASSERT((fp->pp_flags & PA2_FLG_AUX_IRQ) == 0);
+                       if (fp->pp_state_cur.pss_mode & PA2_CTL_MODE_INT_KBD) {
+                               PRINT_DEBUG(PA2_DBG_MID,
+                                           "pa2_irq_raise: raising kbd\n");
+                               fp->pp_flags |= PA2_FLG_KBD_IRQ;
+                               qemu_set_irq(fp->pp_kbd_irq, 1);
+                               break;
+                       }
+                       PRINT_DEBUG(PA2_DBG_LOW,
+                                   "irq_raise: kbd ints disabled\n");
+                       continue;
+               }
+
+               /* else PA2_INDEX_AUX */
+               ASSERT(!(fp->pp_flags & PA2_FLG_KBD_IRQ));
+               if (fp->pp_state_cur.pss_mode & PA2_CTL_MODE_INT_AUX) {
+                       PRINT_DEBUG(PA2_DBG_MID, "irq_raise: raising aux\n");
+                       fp->pp_flags |= PA2_FLG_AUX_IRQ;
+                       qemu_set_irq(fp->pp_aux_irq, 1);
+                       break;
+               }
+               PRINT_DEBUG(PA2_DBG_LOW, "irq_raise: aux ints disabled\n");
+       } 
+
+out:
+       return;
+}
+
+/**
+ ** Queue handling functions.
+ **/
+
+/*
+ * Place a record into the input queue.
+ * The status register's output buffer bits (PA2_CTL_STAT_OBF, PA2_CTL_STAT_AUX_OBF)
+ * are maintained here.  Other bits are the responsibility of the caller.
+ */
+
+/* XXX fwd XXX */
+static int     pa2_cmd_process(pa2_t *, pa2_cmd_t *, uint8_t, uint8_t);
+static uint32_t pa2_status_read(pa2_t *, int);
+
+/*
+ * Queue data at end of KBD input.  If there is a cmd assocaited with the
+ * KBD, then run the data through it.
+ * This is used when the KBD is non-active.
+ */
+
+static int
+pa2_kbd_append(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       uint            next;
+       int             ret;
+       uint8_t         status;
+
+       status = fp->pp_state_cur.pss_status & (PA2_CTL_STAT_UNLOCKED |
+                                               PA2_CTL_STAT_SELFTEST);
+       status |= PA2_CTL_STAT_OBF;
+       php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+       next = (php->ph_end + 1) & PA2_IRQBUF_MASK;
+       if (php->ph_start != next) {
+               pdp = &php->ph_irqbuf[php->ph_end];
+               php->ph_end = next;
+               pdp->pe_status = status;
+               pdp->pe_data = byte;
+               ret = 0;
+               if (cmdp != NULL) {
+                       ret = pa2_cmd_process(fp, cmdp, status, byte);
+               }
+               fp->pp_state_cur.pss_status = status;
+               pa2_irq_raise(fp);
+       } else {
+               pa2_error("Overflow!!!\n");
+               php->ph_overflow++;
+               ret = 1;
+               /* send a RESEND to the guest? XXX */
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_ack(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       return pa2_kbd_append(fp, cmdp, PA2_KBD_REPLY_ACK);
+}
+
+static int
+pa2_aux_append(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       uint            next;
+       int             ret;
+       uint8_t         status;
+
+       status = fp->pp_state_cur.pss_status & (PA2_CTL_STAT_UNLOCKED |
+                                 PA2_CTL_STAT_SELFTEST);
+       status |= (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF);
+       php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+       next = (php->ph_end + 1) & PA2_IRQBUF_MASK;
+       if (php->ph_start != next) {
+               pdp = &php->ph_irqbuf[php->ph_end];
+               php->ph_end = next;
+               pdp->pe_status = status;
+               pdp->pe_data = byte;
+               ret = 0;
+               if (cmdp != NULL) {
+                       ret = pa2_cmd_process(fp, cmdp, status, byte);
+               }
+               fp->pp_state_cur.pss_status = status;
+               pa2_irq_raise(fp);
+       } else {
+               pa2_error("Overflow!!!\n");
+               php->ph_overflow++;
+               ret = 1;
+               /* send a RESEND to the guest? XXX */
+       }
+       return ret;
+}
+
+static int
+pa2_aux_ack(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       return pa2_aux_append(fp, cmdp, PA2_AUX_REPLY_ACK);
+}
+
+/**
+ ** Key binding handling.
+ **/
+
+typedef struct pa2_binding_s {
+       uint                    pbg_table_len;
+       void                    (*pdg_callback)(void *);
+       void                    *pbg_payload;
+       struct pa2_binding_s    *pbg_nextp;
+       uint8_t pbg_table[0];
+} pa2_binding_t;
+
+static pa2_binding_t   *pa2_binding;
+static uint8_t         pa2_binding_bitmap[256 / 8];
+
+static void
+pa2_key_inject(
+       pa2_t           *fp,
+       uint            keycode,
+       uint            escape,
+       uint            make)
+{
+       ASSERT(make == 0 || make == 1);
+       ASSERT(escape == 0 || escape == 1);
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "key_inject: 0x%X 0x%X 0x%X\n",
+                       keycode, escape, make);
+       if (escape == 1) {
+               pa2_kbd_append(fp, NULL, 0xE0); /* magic XXX */
+       }
+       if (make == 0) {
+               pa2_kbd_append(fp, NULL, keycode | 0x80);
+       } else {
+               pa2_kbd_append(fp, NULL, keycode & 0x7F);
+       }
+       return;
+}
+
+void   pa2_kbd_reset(void);    /* XXX */
+
+static int
+pa2_binding_search(
+       pa2_t           *fp,
+       uint8_t         new_key)
+{
+       pa2_binding_t   *bp;
+       int             ret;
+       uint            index;
+       uint            i;
+
+       ret = 0;
+       PRINT_DEBUG(PA2_DBG_MID, "binding_search(0x%X)\n", new_key);
+
+       /*
+        * If new key is not present in bitmap, then no need to search
+        * bindings.
+        */
+
+       if (!(pa2_binding_bitmap[new_key >> 3] & (new_key & 0x3))) {
+               goto out;
+       }
+
+       /*
+        * Search to see if there is a binding that matches make(d) keys.
+        */
+
+       for (bp = pa2_binding; bp != NULL; bp = bp->pbg_nextp) {
+               for (i = 0; i < bp->pbg_table_len; i++) {
+                       index = bp->pbg_table[i];
+                       if (fp->pp_keystate[0][index] == 0) {
+                               break;
+                       }
+               }
+               if (i != bp->pbg_table_len) {
+                       continue;
+               }
+
+               /*
+                * Match found.
+                * Do we continue looking for other matches?
+                * For now, assume one is enough.
+                */
+       
+               PRINT_DEBUG(PA2_DBG_HIGH, "Found key match\n");
+               bp->pdg_callback(bp->pbg_payload);
+               pa2_kbd_reset();
+               ret = 1;
+               break;
+       }
+
+out:
+       return ret;
+}
+
+/*
+ * Here, the keys we are passed are Linux input keys (as in
+ * ./include/linux/input.h), but we are talking directly to the keyboard
+ * and so are dealing in scancodes.
+ * Currently, the keys being passed in match the scancodes but will need
+ * to add conversion table in the future. XXX
+ */
+
+static int
+pa2_binding_insert(
+       pa2_t           *fp,
+       const int       *keys,
+       void            (*callback)(void *),
+       void            *payload)
+{
+       pa2_binding_t   *bp;
+       int             ret;
+       uint            i;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "pa2_add_binding()\n");
+       ret = 0;
+       i = 0;
+       while (keys[i] != -1) {
+               i++;
+       }
+       if (i == 0) {
+               goto out;
+       }
+       bp = malloc(sizeof (pa2_binding_t) + i);
+       if (bp == NULL) {
+               pa2_error("cannot allocate memory for key binding\n");
+               ret = 1;
+               goto out;
+       }
+       bp->pbg_table_len = i;
+       bp->pdg_callback = callback;
+       bp->pbg_payload = payload;
+       for (i = 0; i < bp->pbg_table_len; i++) {
+               ASSERT(keys[i] <= 0xFF);
+               bp->pbg_table[i] = (uint8_t)keys[i];
+               pa2_binding_bitmap[keys[i] >> 3] |= (keys[i] & 0x3);
+       }
+       bp->pbg_nextp = pa2_binding;
+       pa2_binding = bp;
+
+out:
+       return ret;
+}
+
+/*
+ * Write to data port.
+ * Unlike the below cmd write, a data write (to the data port) doesn't clear
+ * any queued data from  the device.
+ */
+
+static int
+pa2_reg_data_write(
+       pa2_t           *fp,
+       uint            index,
+       uint8_t         byte)
+{
+       pa2_hdata_t     *php;
+       pa2_data_t      arg;
+       int             ret;
+
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX);
+       ASSERT(fp->pp_active[index] == 1);
+       ret = 0;
+       arg.pd_clear = 0;       /* data write doesn't clear queued data */
+       arg.pd_data = byte;
+       if (ioctl(fp->pp_fd[index], PA2_IOCTL_WR_DATA, &arg) == -1) {
+               ASSERT(0);
+               pa2_error("error writing data\n");
+               ret = 1;
+       }
+       fp->pp_byte_write_last = byte;  /* only on success? XXX */
+       return ret;
+}
+
+/*
+ * Write cmd to data port.
+ * A cmd clears any queued data (while the above data write, doesn't).
+ */
+
+static int
+pa2_reg_cmd_data_write(
+       pa2_t           *fp,
+       uint            index,
+       uint8_t         cmd)
+{
+       pa2_hdata_t     *php;
+       pa2_data_t      arg;
+       int             ret;
+
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX);
+       ASSERT(fp->pp_active[index] == 1);
+       ret = 0;
+       arg.pd_clear = 0;       /* cmd write clears queued data */
+       arg.pd_data = cmd;
+       php = NULL;
+       php = &fp->pp_irqbuf[index];
+       php->ph_start = php->ph_end;
+       pa2_irq_lower(fp);
+       if (ioctl(fp->pp_fd[index], PA2_IOCTL_WR_DATA, &arg) == -1) {
+               ASSERT(0);
+               pa2_error("error writing data\n");
+               ret = 1;
+       }
+       fp->pp_byte_write_last = cmd;   /* only on success? XXX */
+       return ret;
+}
+
+/*
+ * Write to cmd port.
+ * This should abort any current cmd....XXX
+ */
+
+static int
+pa2_reg_cmd_write(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       int             ret;
+
+       ASSERT(fp->pp_active[0] == 1 || fp->pp_active[1] == 1);
+       ret = 0;
+       if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_WR_CMD, &cmd) == -1) {
+               ASSERT(0);
+               pa2_error("error writing cmd reg\n");
+               ret = 1;
+       }
+       return ret;
+}
+
+/* fwd XXX */
+static int     pa2_ctl_mode_write_do(pa2_t *, uint8_t);
+static int     pa2_ctl_aux_enable_do(pa2_t *);
+static int     pa2_ctl_kbd_enable_do(pa2_t *);
+static int     pa2_ctl_kbd_disable_do(pa2_t *);
+static int     pa2_ctl_outport_write_do(pa2_t *, uint8_t);
+static int     pa2_ctl_aux_disable_do(pa2_t *);
+
+/**
+ ** Cmd/Done functions for controller commands.
+ **/
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Read 8042 Command Byte.  Current cmd byte is placed in port 60h.
+ */
+
+static void
+pa2_ctl_mode_read_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_READ);
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_MODE);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_mode_read_done: 0x%X\n", ssp->pss_mode);
+
+       /*
+        * Should not have changed pss_mode, so whether interrupts are raised
+        * or not should not have changed.  However, incase of bug (mode
+        * getting out of sync with our s/w copy), call pa2_irq_raise(fp)
+        * and let it make the decision.
+        */
+
+       pa2_irq_raise(fp);
+       return;
+}
+
+static int
+pa2_ctl_mode_read_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_READ);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_mode_read_cmd\n");
+       ret = 0;
+       ssp = &fp->pp_state_cur;
+       cmdp->pc_expected_input = ssp->pss_mode;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if (fp->pp_active[0] == 1 || fp->pp_active[1] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_MODE);
+               ret = pa2_kbd_append(fp, cmdp, ssp->pss_mode);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_mode_read_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_READ);
+       ASSERT(cmdp->pc_counter == 0);
+       ssp = &fp->pp_state_cur;
+       ASSERT(!(ssp->pss_hv_flgs & PA2_HV_STATE_MODE) ||
+                  ssp->pss_mode == byte);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_mode_read_read\n");
+       ssp->pss_mode = byte;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_MODE;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_mode_read_redo(
+       pa2_t           *fp)
+{
+       /*
+        * Returned the mode, as we know it, to the caller so nothing
+        * to be done here.
+        */
+
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_mode_read_redo\n");
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+/*
+ * Write to Keyboard Controller's Command Byte (aka mode).
+ * The next byte written to the data port is placed in the controller's command
+ * byte.
+ */
+
+static void
+pa2_ctl_mode_write_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_WRITE);
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_MODE);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_mode_write_done: 0x%X\n", ssp->pss_mode);
+       /* why raise ints? XXX */
+       pa2_irq_raise(fp);
+       return;
+}
+
+static int
+pa2_ctl_mode_write_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_mode_write_cmd\n");
+       ret = 0;
+       /* should depend on what mode bits are being changed... XXX */
+       if (fp->pp_active[0] == 1 || fp->pp_active[1] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_mode_write_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_MODE_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_mode_write_write\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_KBD] == 1 &&
+           fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+       } else {
+               /*
+                * Need to lock XXXX
+                */
+
+               /* should depend on what mode bits are being changed... XXX */
+               if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+                       ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+               } else if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+                       ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+               }
+       }
+       ssp->pss_mode = byte;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_MODE;
+       cmdp->pc_counter++;
+       return ret;
+}
+
+static int
+pa2_ctl_mode_write_redo(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_mode_write_redo\n");
+       ssp = &fp->pp_state_cur;
+       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode);
+       return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+pa2_ctl_version_get_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_VERSION_GET);
+       ASSERT(cmdp->pc_counter == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_VERSION);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_version_get_done: 0x%X\n",
+                   scp->psc_ctl_version);
+       return;
+}
+
+static int
+pa2_ctl_version_get_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+       uint8_t         status;
+       uint8_t         byte;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_VERSION_GET);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_version_get_cmd\n");
+       scp = &fp->pp_state_const;
+       ret = 0;
+       if (fp->pp_init == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else if (scp->psc_flgs & PA2_CONST_NO_CTL_VERSION) {
+               ASSERT(!(scp->psc_hv_flgs & PA2_HV_CONST_CTL_VERSION));
+               /* adjust length.... XXX */
+       } else {
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_VERSION);
+               ret = pa2_kbd_append(fp, cmdp, scp->psc_ctl_version);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_version_get_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_VERSION_GET);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_version_get_read\n");
+       scp = &fp->pp_state_const;
+       scp->psc_ctl_version = byte;
+       scp->psc_hv_flgs |= PA2_HV_CONST_CTL_VERSION;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_version_get_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_version_get_redo\n");
+       return 0;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+pa2_ctl_aux_disable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_DISABLE);
+       ASSERT(fp->pp_state_cur.pss_mode & PA2_CTL_MODE_DISABLE_AUX);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_disable_done\n");
+       return;
+}
+
+static int
+pa2_ctl_aux_disable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_DISABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_aux_disable_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+        fp->pp_state_cur.pss_mode |= PA2_CTL_MODE_DISABLE_AUX;
+       return ret;
+}
+
+static int
+pa2_ctl_aux_disable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_disable_redo\n");
+       ret = pa2_ctl_aux_disable_do(fp);
+       return ret;
+}
+
+/* --------------------------------------------------------------------- */
+
+static void
+pa2_ctl_aux_enable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_ENABLE);
+       ASSERT(!(fp->pp_state_cur.pss_mode & PA2_CTL_MODE_DISABLE_AUX));
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_enable_done\n");
+       pa2_irq_raise(fp);
+       return;
+}
+
+static int
+pa2_ctl_aux_enable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_ENABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_aux_enable_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+               fp->pp_state_cur.pss_mode &= ~PA2_CTL_MODE_DISABLE_AUX;
+       return ret;
+}
+
+static int
+pa2_ctl_aux_enable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ASSERT(fp->pp_active[PA2_INDEX_AUX] == 1);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_enable_redo\n");
+       ret = pa2_ctl_aux_enable_do(fp);
+       return ret;
+}
+
+/* ---------------------------------------------------------------------- */
+
+/*
+ * Checks the interface to the AUX device.
+ *     00      - no error
+ *     01      - clock line low
+ *     02      - clock line high
+ *     03      - data line low
+ *     04      - data line high
+ *     FF      - no AUX device
+ */
+
+static void
+pa2_ctl_aux_test_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_TEST);
+       ASSERT(cmdp->pc_counter == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_AUX_TEST);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_test_done: 0x%X\n",
+                   scp->psc_ctl_aux_test);
+       return;
+}
+
+static int
+pa2_ctl_aux_test_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_TEST);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_aux_test_cmd\n");
+       if (fp->pp_init == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               scp = &fp->pp_state_const;
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_AUX_TEST);
+               ret = pa2_kbd_append(fp, cmdp, scp->psc_ctl_aux_test);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_aux_test_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_AUX_TEST);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_aux_test_write\n");
+       scp = &fp->pp_state_const;
+       scp->psc_hv_flgs |= PA2_HV_CONST_CTL_AUX_TEST;
+       scp->psc_ctl_aux_test = byte;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_aux_test_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_aux_test_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+/*
+ * Controller self-test.
+ * Return 0x55 (PA2_CTL_REPLY_SELF) if no errors.
+ * Only done during our init.
+ */
+
+static void
+pa2_ctl_self_test_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_SELF_TEST);
+       ASSERT(cmdp->pc_counter == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_TEST);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_self_test_done: 0x%X\n",
+                   scp->psc_ctl_test);
+       return;
+}
+
+static int
+pa2_ctl_self_test_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_SELF_TEST);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_self_test_cmd\n");
+       if (fp->pp_init == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               scp = &fp->pp_state_const;
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_TEST);
+               ret = pa2_kbd_append(fp, cmdp, scp->psc_ctl_test);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_self_test_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_SELF_TEST);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_self_test_read\n");
+       if (fp->pp_init == 1) {
+               scp = &fp->pp_state_const;
+               scp->psc_ctl_test = byte;
+               ASSERT(!(scp->psc_hv_flgs & PA2_HV_CONST_CTL_TEST));
+               scp->psc_hv_flgs |= PA2_HV_CONST_CTL_TEST;
+       }
+
+       /*
+        * Seen this while experimenting; a self-test causes interrupts to
+        * be disabled, and scancode conversion bit to clear.
+        * It may also cause the devices to be marked disabled. XXX
+        */
+
+       ssp = &fp->pp_state_cur;
+       ssp->pss_mode &= ~PA2_CTL_MODE_INT_KBD;
+       ssp->pss_mode &= ~PA2_CTL_MODE_INT_AUX;
+       ssp->pss_mode &= ~PA2_CTL_MODE_KCC;             /* not sure XXX */
+       ssp->pss_mode |= PA2_CTL_MODE_DISABLE_KBD;
+       ssp->pss_mode |= PA2_CTL_MODE_DISABLE_AUX;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_self_test_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_self_test_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+/*
+ * KBD device test.  Only run during our init phase.  The result of which
+ * is used to return to later callers.
+ * Returns same as for AUX test.
+ */
+
+static void
+pa2_ctl_kbd_test_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_TEST);
+       ASSERT(cmdp->pc_counter == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_KBD_TEST);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_test_done: 0x%X\n",
+                   scp->psc_ctl_kbd_test);
+       return;
+}
+
+static int
+pa2_ctl_kbd_test_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_TEST);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_kbd_test_cmd\n");
+       scp = &fp->pp_state_const;
+       if (fp->pp_init == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_KBD_TEST);
+               ret = pa2_kbd_append(fp, cmdp, scp->psc_ctl_kbd_test);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_test_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_TEST);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_kbd_test_read\n");
+       if (fp->pp_init == 1) {
+               scp = &fp->pp_state_const;
+               scp->psc_ctl_kbd_test = byte;
+               ASSERT(!(scp->psc_hv_flgs & PA2_HV_CONST_CTL_KBD_TEST));
+               scp->psc_hv_flgs |= PA2_HV_CONST_CTL_KBD_TEST;
+       }
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_kbd_test_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_test_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_kbd_enable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_ENABLE);
+       ASSERT(!(fp->pp_state_cur.pss_mode & PA2_CTL_MODE_DISABLE_KBD));
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_enable_done\n");
+       return;
+}
+
+static int
+pa2_ctl_kbd_enable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_ENABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_kbd_enable_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       fp->pp_state_cur.pss_mode &= ~PA2_CTL_MODE_DISABLE_KBD;
+       /* pa2_irq_raise(fp); no longer required? XXXX */
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_enable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_enable_redo\n");
+       ret = pa2_ctl_kbd_enable_do(fp);
+       return ret;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_kbd_disable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_DISABLE);
+       ASSERT(fp->pp_state_cur.pss_mode & PA2_CTL_MODE_DISABLE_KBD);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_disable_done\n");
+       return;
+}
+
+static int
+pa2_ctl_kbd_disable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_KBD_DISABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_kbd_disable_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       fp->pp_state_cur.pss_mode |= PA2_CTL_MODE_DISABLE_KBD;
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_disable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_kbd_disable_redo\n");
+       ret = pa2_ctl_kbd_disable_do(fp);
+       return ret;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_inport_read_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_INPORT_READ);
+       ASSERT(cmdp->pc_counter == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_INPORT);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_inport_read_done: 0x%X\n",
+                   scp->psc_inport);
+       return;
+}
+
+static int
+pa2_ctl_inport_read_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_INPORT_READ);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_inport_read_cmd\n");
+       if (fp->pp_active[0] == 1 || fp->pp_active[1] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               scp = &fp->pp_state_const;
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_INPORT);
+               ret = pa2_kbd_append(fp, cmdp, scp->psc_inport);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_inport_read_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_INPORT_READ);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_inport_read_read\n");
+       scp = &fp->pp_state_const;
+       scp->psc_inport = byte;
+       scp->psc_hv_flgs |= PA2_HV_CONST_INPORT;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_inport_read_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_inport_read_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_outport_read_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_READ);
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_outport_read_done: 0x%X\n",
+                   ssp->pss_outport);
+       return;
+}
+
+static int
+pa2_ctl_outport_read_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_READ);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_outport_read\n");
+       if (fp->pp_active[0] == 1 || fp->pp_active[1] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+               ret = pa2_kbd_append(fp, cmdp, ssp->pss_outport);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_outport_read_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_READ);
+       ASSERT(cmdp->pc_counter == 0);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_outport_read_read\n");
+       if (ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT) {
+               ASSERT(ssp->pss_outport == byte);
+       }
+       ssp->pss_outport = byte;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_OUTPORT;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_outport_read_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_outport_read_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_outport_write_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_WRITE);
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_outport_write_done: 0x%X\n",
+                   ssp->pss_outport);
+       return;
+}
+
+static int
+pa2_ctl_outport_write_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+       uint8_t         byte;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_outport_write_cmd\n");
+       /* lets never write to this port for now XXX */
+       ret = 0;
+       /* should depend on what bits are being modified XXX */
+       if (fp->pp_active[PA2_INDEX_KBD] == 1 ||
+           fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_outport_write_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OUTPORT_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_HIGH, "ctl_outport_write_write: 0x%X\n", byte);
+       ret = 0;
+       /* should depend on what bits are being modified XXX */
+       if (fp->pp_active[PA2_INDEX_KBD] == 1 ||
+           fp->pp_active[PA2_INDEX_AUX] == 1) {
+       PRINT_DEBUG(PA2_DBG_HIGH, "Here1\n");
+               ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+       } else {
+               ioport_set_a20(!!(byte & PA2_OUTPORT_GA20));
+               if (!(byte & PA2_OUTPORT_SYSR)) {
+                       qemu_system_reset_request();
+               }
+       }
+       ssp = &fp->pp_state_cur;
+       ssp->pss_outport = byte;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_OUTPORT;
+       cmdp->pc_counter++;
+       return ret;
+}
+
+static int
+pa2_ctl_outport_write_redo(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_outport_write_redo\n");
+       ret = pa2_ctl_outport_write_do(fp, ssp->pss_outport);
+       return ret;
+}
+
+/*****************************************************************/
+
+/*
+ * Write Keyboard Output Register: on PS/2 systems the next data byte written
+ * to port 60h input register is written to port 60h output register as if
+ * initiated by a device; invokes interrupt if enabled.
+ */
+
+static void
+pa2_ctl_obuf_write_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       uint8_t         byte;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OBUF_WRITE);
+       ASSERT(cmdp->pc_counter == 1);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_obuf_write_done\n");
+       return;
+}
+
+static int
+pa2_ctl_obuf_write_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+       uint8_t         byte;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OBUF_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_obuf_write_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_obuf_write_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_OBUF_WRITE);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_obuf_write_read\n");
+       ret = 0;
+       cmdp->pc_counter++;
+       if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+       } else {
+               ret = pa2_kbd_append(fp, cmdp, byte);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_obuf_write_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_obuf_write_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+/*
+ * Write Auxiliary Output Register: on PS/2 systems the next data byte
+ * written to port 60h input register is written to port 60h output
+ * register as if initiated by a device; invokes interrupt if enabled
+ */
+
+static void
+pa2_ctl_write_aux_obuf_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX_OBUF);
+       ASSERT(cmdp->pc_counter == 2);
+       PRINT_DEBUG(PA2_DBG_MID, "write_aux_obuf_done\n");
+       ASSERT(cmdp->pc_aux_func_read != NULL);
+       cmdp->pc_aux_func_read = NULL;
+       return;
+}
+
+static int
+pa2_ctl_write_aux_obuf_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX_OBUF);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_write_aux_obuf_cmd\n");
+       ret = 0;
+       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       ASSERT(cmdp->pc_aux_func_read == NULL);
+       cmdp->pc_aux_func_read = cmdp->pc_func_read;
+       return ret;
+}
+
+static int
+pa2_ctl_write_aux_obuf_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX_OBUF);
+       ASSERT(cmdp->pc_counter == 1);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_write_aux_obuf_read\n");
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_ctl_write_aux_obuf_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX_OBUF);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_write_aux_obuf_write\n");
+       cmdp->pc_counter++;
+       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+       } else {
+               ret = pa2_aux_append(fp, cmdp, byte);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_write_aux_obuf_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_write_aux_obuf_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+/*
+ * The PA2_CTL_CMD_WRITE_AUX command is always delayed, until we know what
+ * the following AUX cmd is.
+ */
+
+static int
+pa2_ctl_write_aux_pending(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       uint8_t         status;
+       uint            i;
+       int             ret;
+
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ret = 0;
+       if (cmdp->pc_active == 0) {
+               cmdp->pc_flags &= ~PA2_CMD_FLG_PENDING_AUX;
+               goto out;
+       }
+       ASSERT(fp->pp_active[PA2_INDEX_AUX] == 1);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_write_aux_pending\n");
+       cmdp->pc_flags &= ~PA2_CMD_FLG_PENDING_AUX;
+       ret = pa2_reg_cmd_write(fp, PA2_CTL_CMD_WRITE_AUX);
+       if (ret == 0) {
+               /* wait for input buffer to become empty */
+               ret = 1;
+               for (i = 0; i < 100; i++) {
+                       status = pa2_status_read(fp, PA2_INDEX_RAW);
+                       if (status & PA2_CTL_STAT_IBF) {
+                               usleep(100);
+                               continue;
+                       }
+                       ret = 0;
+                       break;
+               }
+       }
+
+out:
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static void
+pa2_ctl_write_aux_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       /*
+        * pc_cmd will have been overwritten with command that followed.
+        */
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX);
+       /* ASSERT(cmdp->pc_counter == 1); XXX */
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_write_aux_done\n");
+       return;
+}
+
+static int
+pa2_ctl_write_aux_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_write_aux_cmd\n");
+       cmdp->pc_flags |= PA2_CMD_FLG_PENDING_AUX;
+       return 0;
+}
+
+static int
+pa2_ctl_write_aux_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       /* should never be called XXXX */
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_write_aux_write\n");
+       cmdp->pc_counter++;
+       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_CTL, byte);
+       } else {
+               ret = pa2_aux_append(fp, cmdp, byte);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_write_aux_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_write_aux_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_disable_a20_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_DISABLE_A20);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_disable_a20_done\n");
+       /* XXX */
+       return;
+}
+
+static int
+pa2_ctl_disable_a20_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_DISABLE_A20);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_disable_a20_cmd\n");
+       /* just ignore this for now XXX */
+       /* fp->pp_state_cur.pss_mode XXX */
+       return 0;
+}
+
+static int
+pa2_ctl_disable_a20_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_disable_a20_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+static void
+pa2_ctl_enable_a20_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_ENABLE_A20);
+       PRINT_DEBUG(PA2_DBG_MID, "pa2_ctl_enable_a20_done\n");
+       /* XXX */
+       return;
+}
+
+static int
+pa2_ctl_enable_a20_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_ENABLE_A20);
+       PRINT_DEBUG(PA2_DBG_LOW, "pa2_ctl_enable_a20_cmd\n");
+       /* just ignore this for now XXX */
+       /* fp->pp_state_cur.pss_mode XXX */
+       return 0;
+}
+
+static int
+pa2_ctl_enable_a20_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_enable_a20_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+
+/*
+ * I think this is a keyboard command - not a keyboard controller cmd XXXX
+ */
+
+static void
+pa2_ctl_resend_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_RESEND);
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_resend_done\n");
+       return;
+}
+
+static int
+pa2_ctl_resend_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int     ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_RESEND);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_resend_cmd\n");
+#if    0
+       if (fp->pp_active[0] == 1 || fp->pp_active[1] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_append(fp, cmdp, fp->pp_byte_write_last);
+       }
+       return ret;
+#else
+       return 0;
+#endif
+}
+
+static int
+pa2_ctl_resend_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_RESEND);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_resend_read\n");
+       ASSERT(fp->pp_byte_write_last == byte);
+       return 0;
+}
+
+static int
+pa2_ctl_resend_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_resend_redo\n");
+       return 0;
+}
+
+/* --------------------------------- */
+
+static void
+pa2_ctl_reset_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_RESET);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_reset_done\n");
+       return;
+}
+
+static int
+pa2_ctl_reset_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       uint8_t         status;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_RESET);
+       PRINT_DEBUG(PA2_DBG_LOW, "ctl_reset_cmd\n");
+       ret = 0;
+       /* should be both XXX */
+       if (fp->pp_active[PA2_INDEX_KBD] == 1 || fp->pp_active[PA2_INDEX_AUX] == 1) {
+               ret = pa2_reg_cmd_write(fp, cmdp->pc_cmd);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_reset_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "ctl_reset_redo\n");
+       return 0;
+}
+
+/*****************************************************************/
+/* --------------------------------- */
+
+static /* const XXX */ pa2_cmd_des_t pa2_ctl_cmd[] = {
+/* 0x20 */     { PA2_CTL_CMD_MODE_READ, "CTL_CMD_MODE_READ",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_mode_read_read,
+                 pa2_ctl_mode_read_cmd, pa2_ctl_mode_read_done,
+                 pa2_ctl_mode_read_redo
+               },
+
+/* 0x60 */     { PA2_CTL_CMD_MODE_WRITE, "CTL_CMD_MODE_WRITE",
+                 0, 1, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 pa2_ctl_mode_write_write, NULL,
+                 pa2_ctl_mode_write_cmd, pa2_ctl_mode_write_done,
+                 pa2_ctl_mode_write_redo
+               },
+/* 0xA1 */     { PA2_CTL_CMD_VERSION_GET, "CTL_CMD_GET_VERSION",
+                 1, 1, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_version_get_read,
+                 pa2_ctl_version_get_cmd, pa2_ctl_version_get_done,
+                 pa2_ctl_version_get_redo,
+               },
+/* 0xA7 */     { PA2_CTL_CMD_AUX_DISABLE, "CTL_CMD_AUX_DISABLE",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_aux_disable_cmd, pa2_ctl_aux_disable_done,
+                 pa2_ctl_aux_disable_redo,
+               },
+/* 0xA8 */     { PA2_CTL_CMD_AUX_ENABLE, "CTL_CMD_AUX_ENABLE",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_aux_enable_cmd, pa2_ctl_aux_enable_done,
+                 pa2_ctl_aux_enable_redo,
+               },
+/* 0xA9 */     { PA2_CTL_CMD_AUX_TEST, "CTL_CMD_AUX_TEST",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_aux_test_read,
+                 pa2_ctl_aux_test_cmd, pa2_ctl_aux_test_done,
+                 pa2_ctl_aux_test_redo,
+               },
+/* 0xAA */     { PA2_CTL_CMD_SELF_TEST, "CTL_CMD_SELF_TEST",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_self_test_read,
+                 pa2_ctl_self_test_cmd, pa2_ctl_self_test_done,
+                 pa2_ctl_self_test_redo,
+               },
+/* 0xAB */     { PA2_CTL_CMD_KBD_TEST, "CTL_CMD_KBD_TEST",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_kbd_test_read,
+                 pa2_ctl_kbd_test_cmd, pa2_ctl_kbd_test_done,
+                 pa2_ctl_kbd_test_redo,
+               },
+/* 0xAE */     { PA2_CTL_CMD_KBD_ENABLE, "CTL_CMD_KBD_ENABLE",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_kbd_enable_cmd, pa2_ctl_kbd_enable_done,
+                 pa2_ctl_kbd_enable_redo,
+               },
+/* 0xAD */     { PA2_CTL_CMD_KBD_DISABLE, "CTL_CMD_KBD_DISABLE",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_kbd_disable_cmd, pa2_ctl_kbd_disable_done,
+                 pa2_ctl_kbd_disable_redo,
+               },
+/* 0xC0 */     { PA2_CTL_CMD_INPORT_READ, "CTL_CMD_INPORT_READ",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_inport_read_read,
+                 pa2_ctl_inport_read_cmd, pa2_ctl_inport_read_done,
+                 pa2_ctl_inport_read_redo,
+               },
+/* 0xD0 */     { PA2_CTL_CMD_OUTPORT_READ, "CTL_CMD_OUTPORT_READ",
+                 1, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 NULL, pa2_ctl_outport_read_read,
+                 pa2_ctl_outport_read_cmd, pa2_ctl_outport_read_done,
+                 pa2_ctl_outport_read_redo,
+               },
+/* 0xD1 */     { PA2_CTL_CMD_OUTPORT_WRITE, "CTL_CMD_OUTPORT_WRITE",
+                 0, 1, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 pa2_ctl_outport_write_write, NULL,
+                 pa2_ctl_outport_write_cmd, pa2_ctl_outport_write_done,
+                 pa2_ctl_outport_write_redo,
+               },
+/* 0xD2 */     { PA2_CTL_CMD_OBUF_WRITE, "CTL_CMD_OBUF_WRITE",
+                 0, 1, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_CTRL,
+                 pa2_ctl_obuf_write_write, NULL,
+                 pa2_ctl_obuf_write_cmd, pa2_ctl_obuf_write_done,
+                 pa2_ctl_obuf_write_redo,
+               },
+/* 0xD3 */     { PA2_CTL_CMD_WRITE_AUX_OBUF, "CTL_CMD_WRITE_AUX_OBUF",
+                 1, 1, 0, PA2_OUTPUT_AUX,
+                 PA2_CMD_FLG_CTRL,
+                 pa2_ctl_write_aux_obuf_write, pa2_ctl_write_aux_obuf_read,
+                 pa2_ctl_write_aux_obuf_cmd, pa2_ctl_write_aux_obuf_done,
+                 pa2_ctl_write_aux_obuf_redo,
+               },
+/* 0xD4 */     { PA2_CTL_CMD_WRITE_AUX, "CTL_CMD_WRITE_AUX",
+                 0, 1, 0, PA2_OUTPUT_AUX,
+                 0, /* or NONE & pick-up from AUX cmd */
+                 pa2_ctl_write_aux_write, NULL,
+                 pa2_ctl_write_aux_cmd, pa2_ctl_write_aux_done,
+                 pa2_ctl_write_aux_redo,
+               },
+/* 0xDD */     { PA2_CTL_CMD_DISABLE_A20, "CTL_CMD_DISABLE_A20",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_disable_a20_cmd, pa2_ctl_disable_a20_done,
+                 pa2_ctl_disable_a20_redo,
+               },
+/* 0xDF */     { PA2_CTL_CMD_ENABLE_A20, "CTL_CMD_ENABLE_A20",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_enable_a20_cmd, pa2_ctl_enable_a20_done,
+                 pa2_ctl_enable_a20_redo,
+               },
+/* 0xFE */     { PA2_CTL_CMD_RESEND, "CTL_CMD_RESEND",
+                 1, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 pa2_ctl_resend_read, NULL,
+                 pa2_ctl_resend_cmd, pa2_ctl_resend_done,
+                 pa2_ctl_resend_redo,
+               },
+/* 0xFF */     { PA2_CTL_CMD_RESET, "CTL_CMD_RESET",
+                 0, 0, 0, PA2_OUTPUT_NONE,
+                 0,
+                 NULL, NULL,
+                 pa2_ctl_reset_cmd, pa2_ctl_reset_done,
+                 pa2_ctl_reset_redo,
+               },
+};
+
+/* fwd XXX */
+static int     pa2_kbd_leds_set_do(pa2_t *, uint8_t);
+static int     pa2_kbd_rate_set_do(pa2_t *, uint8_t);
+static int     pa2_kbd_enable_do(pa2_t *);
+static int     pa2_kbd_reset_enable_do(pa2_t *);
+static int     pa2_kbd_reset_disable_do(pa2_t *);
+static int     pa2_kbd_reset_do(pa2_t *);
+
+/**
+ ** Keyboard cmd done functions.
+ **/
+
+static void
+pa2_kbd_leds_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_LEDS_SET);
+       ASSERT(cmdp->pc_counter == 3);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_KBD_LEDS);
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_leds_set_done: 0x%X\n",
+                   ssp->pss_kbd_leds);
+       return;
+}
+
+static int
+pa2_kbd_leds_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_LEDS_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_leds_set_cmd\n");
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_leds_set_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_LEDS_SET);
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_leds_set_write\n");
+       cmdp->pc_counter++;
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_tmp_buf[0] = byte;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_leds_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_LEDS_SET);
+       ASSERT(cmdp->pc_counter == 0 || cmdp->pc_counter == 2);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_leds_set_read\n");
+       ASSERT(byte == PA2_KBD_REPLY_ACK);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       if (cmdp->pc_counter++ == 2) {
+               ssp->pss_kbd_leds = cmdp->pc_tmp_buf[0];
+               ssp->pss_hv_flgs |= PA2_HV_STATE_KBD_LEDS;
+       }
+       return 0;
+}
+
+static int
+pa2_kbd_leds_set_redo(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_KBD_LEDS);
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_leds_seet_redo\n");
+       ret = pa2_kbd_leds_set_do(fp, ssp->pss_kbd_leds);
+       return ret;
+}
+
+/************************************************************/
+
+/*
+ * 0xEE        - Diagnostic Echo, keyboard echoes the EE byte back to the system
+ *       without an acknowledgement.
+ */
+
+static void
+pa2_kbd_echo_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ECHO);
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_echo_done\n");
+       return;
+}
+
+static int
+pa2_kbd_echo_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ECHO);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_ACK));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ECHO_RESP;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_echo_cmd\n");
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_append(fp, cmdp, PA2_KBD_REPLY_ECHO_RESP);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_echo_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ECHO);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_KBD_REPLY_ECHO_RESP);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_echo_read\n");
+       cmdp->pc_counter++;
+       return 0;
+}
+
+/* no-op */
+static int
+pa2_kbd_echo_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_echo_redo\n");
+       return 0;
+}
+
+/************************************************************/
+
+/*
+ * 0xF2 - PS/2 Read Keyboard ID, keyboard responds with an ACK and a two
+ *       byte keyboard ID of 83AB.
+ */
+
+static void
+pa2_kbd_get_id_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_GET_ID);
+       ASSERT(cmdp->pc_counter == 3);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID);
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_get_id_done: 0x%X\n", scp->psc_kbd_id);
+       return;
+}
+
+static int
+pa2_kbd_get_id_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       /* causes scanning to be resumed if previously disabled XXX */
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_GET_ID);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_get_id_cmd\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = 0;
+       if (fp->pp_init == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               scp = &fp->pp_state_const;
+               ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID);
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_get_id_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+       uint8_t         status;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_GET_ID);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_get_id_read\n");
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ret = 0;
+       scp = &fp->pp_state_const;
+       status = fp->pp_state_cur.pss_status & PA2_CTL_STAT_UNLOCKED;
+       switch (cmdp->pc_counter++) {
+       case 0:         /* ACK */
+               ASSERT(byte == PA2_KBD_REPLY_ACK);
+               if (fp->pp_init == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID);
+                       ret = pa2_kbd_append(fp, cmdp,
+                                              scp->psc_kbd_id & 0xFF);
+               }
+               cmdp->pc_expected_input = scp->psc_kbd_id & 0xFF;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               break;
+       case 1:
+               if (scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID) {
+                       ASSERT((scp->psc_kbd_id & 0xFF) == byte);
+               }
+               scp->psc_kbd_id &= ~0xFF;
+               scp->psc_kbd_id |= byte;
+               if (fp->pp_init == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID);
+                       ret = pa2_kbd_append(fp, cmdp,
+                                              (scp->psc_kbd_id >> 8) & 0xFF);
+               }
+               cmdp->pc_expected_input = (scp->psc_kbd_id >> 8) & 0xFF;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               break;
+
+       case 2:
+               if (scp->psc_hv_flgs & PA2_HV_CONST_KBD_ID) {
+                       ASSERT((scp->psc_kbd_id >> 8) == byte);
+               }
+               if (fp->pp_init == 1) {
+                       scp->psc_kbd_id &= ~(0xFF << 8);
+                       scp->psc_kbd_id |= ((uint16_t)byte << 8);
+                       scp->psc_hv_flgs |= PA2_HV_CONST_KBD_ID;
+               }
+               break;
+
+       default:
+               pa2_error("kbd_get_id_read(): Bad event\n");
+               ret = 1;
+               break;
+       }
+       return ret;
+}
+
+/* no-op */
+static int
+pa2_kbd_get_id_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_get_id_redo\n");
+       return 0;
+}
+
+/************************************************************/
+
+static void
+pa2_kbd_rate_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RATE_SET);
+       ASSERT(cmdp->pc_counter == 3);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_rate_set_done: 0x%X\n",
+                   ssp->pss_kbd_rate);
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_KBD_RATE);
+       return;
+}
+
+static int
+pa2_kbd_rate_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RATE_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_rate_set_cmd\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_rate_set_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RATE_SET);
+       ASSERT(cmdp->pc_counter == 1);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_rate_set_write\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_counter++;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_KBD, byte);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       cmdp->pc_tmp_buf[0] = byte;
+       return ret;
+}
+
+static int
+pa2_kbd_rate_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RATE_SET);
+       ASSERT(cmdp->pc_counter == 0 || cmdp->pc_counter == 2);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_rate_set_read\n");
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ASSERT(byte == PA2_KBD_REPLY_ACK);
+       if (cmdp->pc_counter++ == 2) {
+               ssp->pss_kbd_rate = cmdp->pc_tmp_buf[0];
+               ssp->pss_hv_flgs |= PA2_HV_STATE_KBD_RATE;
+       }
+       return 0;
+}
+
+static int
+pa2_kbd_rate_set_redo(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_KBD_RATE);
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_rate_set_redo\n");
+       ret = pa2_kbd_rate_set_do(fp, ssp->pss_kbd_rate);
+       return ret;
+}
+
+/************************************************************/
+
+/* 0xF4 - Enable Keyboard, cause the keyboard to clear its output buffer
+ *       and last typematic key and then respond with an ACK.  The
+ *       keyboard then begins scanning.
+ */
+
+static void
+pa2_kbd_enable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ENABLE);
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_enable_done\n");
+       ASSERT(!(fp->pp_state_cur.pss_status & PA2_CTL_STAT_UNLOCKED));
+       return;
+}
+
+static int
+pa2_kbd_enable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ENABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_enable_cmd\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+
+       /* clear the keyboard's output buffer XXX */
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_enable_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ENABLE);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_KBD_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_enable_read\n");
+       cmdp->pc_counter++;
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       fp->pp_state_cur.pss_status &= ~PA2_CTL_STAT_UNLOCKED;
+       return 0;
+}
+
+static int
+pa2_kbd_enable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_enable_redo\n");
+       ret = pa2_kbd_enable_do(fp);
+       return ret;
+}
+
+/************************************************************/
+
+/* 
+ * Default w/Disable, resets keyboard to power-on condition by clearing the
+ * output buffer, resetting typematic rate/delay, resetting last typematic
+ * key and setting default key types.  The keyboard responds with an ACK and
+ * waits for the next instruction.
+ */
+
+static void
+pa2_kbd_reset_disable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_DISABLE);
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(fp->pp_state_cur.pss_kbd_rate == PA2_KBD_DEFAULT_RATE);
+       ASSERT(fp->pp_state_cur.pss_status & PA2_CTL_STAT_UNLOCKED);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_reset_disable_done\n");
+       return;
+}
+
+static int
+pa2_kbd_reset_disable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_DISABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_disable_cmd\n");
+       /* clear the input buffer XXX */
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_reset_disable_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_DISABLE);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_KBD_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_disable_read\n");
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       ssp->pss_status |= PA2_CTL_STAT_UNLOCKED;
+       ssp->pss_kbd_rate = PA2_KBD_DEFAULT_RATE;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_KBD_RATE;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_kbd_reset_disable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_reset_disable_redo\n");
+       ret = pa2_kbd_reset_disable_do(fp);
+       return ret;
+}
+
+/************************************************************/
+
+/* F6  Set Default, resets to power-on condition by clearing the output buffer,
+ *     resetting typematic rate/delay and last typematic key and sets default
+ *     key types.  The keyboard responds with an ACK and continues scanning.
+ */
+
+static void
+pa2_kbd_reset_enable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_ENABLE);
+       ASSERT(!(fp->pp_state_cur.pss_status & PA2_CTL_STAT_UNLOCKED));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       /* XXX more asserting XXX */
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_reset_enable_done\n");
+       return;
+}
+
+static int
+pa2_kbd_reset_enable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_ENABLE);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_enable_cmd\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_reset_enable_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET_ENABLE);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_KBD_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_enable_read\n");
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       ssp->pss_kbd_rate = PA2_KBD_DEFAULT_RATE;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_KBD_RATE;
+       ssp->pss_status &= ~PA2_CTL_STAT_UNLOCKED;
+       return 0;
+}
+
+static int
+pa2_kbd_reset_enable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_reset_enable_redo\n");
+       ret = pa2_kbd_reset_enable_do(fp);
+       return ret;
+}
+
+/************************************************************/
+
+/* FF  Reset, Keyboard sends ACK and waits for system to receive it then
+ *     begins a program reset and Basic Assurance Test (BAT).  Keyboard
+ *     returns a one byte completion code then sets default Scan Code Set 2.
+ */
+
+static void
+pa2_kbd_reset_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET);
+       ASSERT(cmdp->pc_counter == 2);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_done\n");
+       return;
+}
+
+static int
+pa2_kbd_reset_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_cmd\n");
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       cmdp->pc_expected_input = PA2_KBD_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_KBD, cmdp->pc_cmd);
+       } else {
+               ret = pa2_kbd_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_reset_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_RESET);
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_reset_read\n");
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ret = 0;
+       switch (cmdp->pc_counter++) {
+       case 0:                 /* ACK */
+               ASSERT(byte == PA2_KBD_REPLY_ACK);
+               cmdp->pc_expected_input = PA2_KBD_REPLY_BAT_SUCC;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 0) {
+                       ret = pa2_kbd_append(fp, cmdp, PA2_KBD_REPLY_BAT_SUCC);
+               }
+               break;
+
+       case 1:
+               ASSERT(byte == PA2_KBD_REPLY_BAT_SUCC ||
+                      byte == PA2_KBD_REPLY_BAT_FAIL);
+               if (byte == PA2_KBD_REPLY_BAT_SUCC) {
+                       ssp = &fp->pp_state_cur;
+                       ssp->pss_kbd_rate = PA2_KBD_DEFAULT_RATE;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_KBD_RATE;
+               }
+               break;
+
+       default:
+               pa2_error("kbd_reset_read(): error\n");
+               ret = 1;
+               break;
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_reset_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "kbd_reset_redo\n");
+       ret = pa2_kbd_reset_do(fp);
+       return ret;
+}
+
+/************************************************************/
+
+static pa2_cmd_des_t   pa2_kbd_cmd[] = {
+/* 0xED */     { PA2_KBD_CMD_LEDS_SET, "KBD_CMD_LEDS_SET",
+                 0, 2, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_OUTPUT | PA2_CMD_FLG_ACK,
+                 pa2_kbd_leds_set_write, pa2_kbd_leds_set_read,
+                 pa2_kbd_leds_set_cmd, pa2_kbd_leds_set_done,
+                 pa2_kbd_leds_set_redo,
+               },
+/* 0xEE */     { PA2_KBD_CMD_ECHO, "KBD_CMD_ECHO",
+                 1, 1, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_OUTPUT,
+                 NULL, pa2_kbd_echo_read,
+                 pa2_kbd_echo_cmd, pa2_kbd_echo_done,
+                 pa2_kbd_echo_redo,
+               },
+/* 0xF2 */     { PA2_KBD_CMD_GET_ID, "KBD_CMD_GET_ID",
+                 2, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_ACK,
+                 NULL, pa2_kbd_get_id_read,
+                 pa2_kbd_get_id_cmd, pa2_kbd_get_id_done,
+                 pa2_kbd_get_id_redo,
+               },
+/* 0xF3 */     { PA2_KBD_CMD_RATE_SET, "KBD_CMD_RATE_SET",
+                 0, 2, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_OUTPUT | PA2_CMD_FLG_ACK,
+                 pa2_kbd_rate_set_write, pa2_kbd_rate_set_read,
+                 pa2_kbd_rate_set_cmd, pa2_kbd_rate_set_done,
+                 pa2_kbd_rate_set_redo,
+               },
+/* 0xF4 */     { PA2_KBD_CMD_ENABLE, "KBD_CMD_ENABLE",
+                 0, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_ACK,
+                 NULL, pa2_kbd_enable_read,
+                 pa2_kbd_enable_cmd, pa2_kbd_enable_done,
+                 pa2_kbd_enable_redo,
+               },
+/* 0xF5 */     { PA2_KBD_CMD_RESET_DISABLE, "KBD_CMD_RESET_DISABLE",
+                 0, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_ACK,
+                 NULL, pa2_kbd_reset_disable_read,
+                 pa2_kbd_reset_disable_cmd, pa2_kbd_reset_disable_done,
+                 pa2_kbd_reset_disable_redo,
+               },
+/* 0xF6 */     { PA2_KBD_CMD_RESET_ENABLE, "KBD_CMD_RESET_ENABLE",
+                 0, 0, 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_ACK,
+                 NULL, NULL,
+                 pa2_kbd_reset_enable_cmd, pa2_kbd_reset_enable_done,
+                 pa2_kbd_reset_enable_redo,
+               },
+/* 0xFF */     { PA2_KBD_CMD_RESET, "KBD_CMD_RESET",
+                 1, 0 , 0, PA2_OUTPUT_KBD,
+                 PA2_CMD_FLG_ACK,
+                 NULL, pa2_kbd_reset_read,
+                 pa2_kbd_reset_cmd, pa2_kbd_reset_done,
+                 pa2_kbd_reset_redo,
+               },
+};
+
+/* fwd XX */
+static int     pa2_aux_scale11_set_do(pa2_t *);
+static int     pa2_aux_scale21_set_do(pa2_t *);
+static int     pa2_aux_res_set_do(pa2_t *, uint8_t);
+static int     pa2_aux_stream_set_do(pa2_t *);
+static int     pa2_aux_synap_e2_do(pa2_t *);
+static int     pa2_aux_wrap_reset_do(pa2_t *);
+static int     pa2_aux_wrap_set_do(pa2_t *);
+static int     pa2_aux_remote_set_do(pa2_t *);
+static int     pa2_aux_dev_enable_do(pa2_t *);
+static int     pa2_aux_default_set_do(pa2_t *);
+static int     pa2_aux_reset_do(pa2_t *);
+
+
+/**
+ ** Aux cmd done functions.
+ **/
+
+/**********************************************************************/
+
+/*
+ * Appears Synaptic have used AUX cmds 0xE1 and 0xE2 differently in
+ * different touchpad versions.  In the version I have for testing (major 7)
+ * E1 returns ACK plus 2 bytes, while E2 writes to those bytes.  I'm
+ * assuming these bytes are constant after being written to.
+ */
+
+static void
+pa2_aux_synap_e1_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E1);
+       ASSERT(cmdp->pc_counter == 3);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e1_done\n");
+       return;
+}
+
+static int
+pa2_aux_synap_e1_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E1);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_flgs & PA2_CONST_SYNAP_YES);
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e1_cmd\n");
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+
+       /*
+        * Does this depend on the mode_byte? XXX
+        */
+
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (ret != 0) {
+               goto out;
+       }
+       if ((scp->psc_flgs & PA2_CONST_SYNAP_YES) &&
+           (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+               if (cmdp->pc_active == 1) {
+                       ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                                cmdp->pc_aux_cmd);
+               } else {
+                       ret = pa2_aux_ack(fp, cmdp);
+               }
+       } else {
+               /* more needed XXX */
+               /* send error on next bad? XXX */
+               ret = pa2_aux_append(fp, cmdp, PA2_AUX_REPLY_RESEND);
+       }
+
+out:
+       return ret;
+}
+
+static int
+pa2_aux_synap_e1_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_const_t *syp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e1_read: 0x%X\n", byte);
+       ret = 0;
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       syp = &fp->pp_state_const.psc_synap;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       switch (cmdp->pc_counter++) {
+       case 0:                 /* ACK */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+                       break;
+               }
+               cmdp->pc_expected_input = sp->ps_e1_byte1;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 1) {
+                       break;
+               }
+               if (!(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_E1_BYTE1)) {
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE1);
+                       sp->ps_e1_byte1 = syp->pcy_initial_e1_byte1;
+                       sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE1;
+               }
+               ret = pa2_aux_append(fp, cmdp, sp->ps_e1_byte1);
+               break;
+
+       case 1:
+               sp->ps_e1_byte1 = byte;
+               sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE1;
+
+               cmdp->pc_expected_input = sp->ps_e1_byte2;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+
+               if (cmdp->pc_active == 1) {
+                       if (syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE1) {
+                               break;
+                       }
+                       syp->pcy_initial_e1_byte1 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_CONST_E1_BYTE1;
+                       break;
+               }
+               if (!(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_E1_BYTE2)) {
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE2);
+                       sp->ps_e1_byte2 = syp->pcy_initial_e1_byte2;
+                       sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE2;
+               }
+               ret = pa2_aux_append(fp, cmdp, sp->ps_e1_byte2);
+               break;
+
+       case 2:
+               sp->ps_e1_byte2 = byte;
+               sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE2;
+               if (cmdp->pc_active == 1) {
+                       if (syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE2) {
+                               break;
+                       }
+                       syp->pcy_initial_e1_byte2 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_CONST_E1_BYTE2;
+               }
+               break;
+
+       default:
+               pa2_error("Extra byte for 0xE1: 0x%X\n", byte);
+               ret = 1;
+               break;
+       }
+       return ret;
+}
+
+static int
+pa2_aux_synap_e1_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_synap_e1_redo\n");
+       return 0;
+}
+
+/**********************************************************************/
+
+static void
+pa2_aux_synap_e2_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E2);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e2_done\n");
+       return;
+}
+
+static int
+pa2_aux_synap_e2_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E2);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_flgs & PA2_CONST_SYNAP_YES);
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e2_cmd\n");
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (ret != 0) {
+               goto out;
+       }
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       if ((scp->psc_flgs & PA2_CONST_SYNAP_YES) &&
+           (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+               if (cmdp->pc_active == 1) {
+                       ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                                cmdp->pc_aux_cmd);
+               } else {
+                       ret = pa2_aux_ack(fp, cmdp);
+               }
+       } else {
+               /* more needed XXX */
+               /* send error on next bad? XXX */
+               ret = pa2_aux_append(fp, cmdp, PA2_AUX_REPLY_RESEND);
+       }
+
+out:
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_aux_synap_e2_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E2);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e2_read: 0x%X\n", byte);
+       ret = 0;
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       switch (cmdp->pc_counter++) {
+       case 0:                 /* ACK for cmd */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+               }
+               break;
+
+       case 1:                 /* ACK for byte 1 */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+               }
+#if    0
+               sp->ps_e1_byte1 = cmdp->pc_tmp_buf[0];
+               sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE1;
+#endif
+               break;
+
+       case 2:
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+#if    0
+               sp->ps_e1_byte2 = cmdp->pc_tmp_buf[1];
+               sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_E1_BYTE2;
+#endif
+               break;
+
+       default:
+               pa2_error("Extra byte for 0xE2: 0x%X\n", byte);
+               ret = 1;
+       }
+       return 0;
+}
+
+static int
+pa2_aux_synap_e2_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SYNAP_E2);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_synap_e2_write: 0x%X (%d)\n", byte, cmdp->pc_counter);
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (ret != 0) {
+               goto out;
+       }
+       cmdp->pc_tmp_buf[cmdp->pc_counter] = byte;
+       switch (cmdp->pc_counter++) {
+       case 0:
+               cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 1) {
+                       ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+               } else {
+                       ret = pa2_aux_ack(fp, cmdp);
+               }
+               break;
+
+       case 1:
+               cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 1) {
+                       ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+               } else {
+                       ret = pa2_aux_ack(fp, cmdp);
+               }
+               break;
+
+       default:
+               pa2_error("XXXXX");
+               ret = 1;
+               break;
+       }
+
+out:
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_aux_synap_e2_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_synap_e2_redo\n");
+       ret = pa2_aux_synap_e2_do(fp);
+       return ret;
+}
+
+/*
+ * This clears the PS2 scaling bit.
+ * Synaptics doesn't honour this bit (for data reporting), but will correctly
+ * reflect its value for a PA2_AUX_CMD_STATUS_GET.  So, if we have the status,
+ * clear this bit.
+ */
+
+/*
+ * Note: pp_outbuf_pos is reset for each cmd.  As arguments to AUX cmds are
+ * sent via a CCMD_WRITE_AUX, this resets pp_outbuf_pos (and pp_inbuf_pos).
+ * As AUX cmds are counted as output, pp_outbuf_pos will be 1 for AUX cmds
+ * that take no arguments, and one for those that do take arguements (or,
+ * rather, argument as no AUX cmds take more than one input).
+ */
+
+static void
+pa2_aux_scale11_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE11_SET);
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(!(fp->pp_state_cur.pss_aux_status & PA2_AUX_STAT_SCALING));
+       PRINT_DEBUG(PA2_DBG_MID, "aux_scale11_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_scale11_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE11_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_scale11_set_cmd\n");
+       ssp = &fp->pp_state_cur;
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (ret == 0) {
+               if (cmdp->pc_active == 1) {
+                       ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                                cmdp->pc_aux_cmd);
+               } else {
+                       ret = pa2_aux_ack(fp, cmdp);
+               }
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_aux_scale11_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE11_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_scale11_set_read\n");
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       if (byte == PA2_AUX_REPLY_ACK) {
+               ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;
+               ssp->pss_setscale_last = 0;
+               ssp->pss_aux_status &= ~PA2_AUX_STAT_SCALING;
+       }
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_scale11_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_scale11_set_redo\n");
+       ret = pa2_aux_scale11_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_scale21_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE21_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(fp->pp_state_cur.pss_aux_status & PA2_AUX_STAT_SCALING);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_scale21_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_scale21_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE21_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_scale21_set_cmd\n");
+       ssp = &fp->pp_state_cur;
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                        cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_scale21_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SCALE21_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_scale21_set_read\n");
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       if (byte == PA2_AUX_REPLY_ACK) {
+               ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;
+               ssp->pss_setscale_last = 1;
+               ssp->pss_aux_status |= PA2_AUX_STAT_SCALING;
+       }
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_scale21_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_scale21_set_redo\n");
+       ret = pa2_aux_scale21_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_res_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RES_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_counter == 2);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_res_set_done: 0x%X (%u)\n",
+              ssp->pss_aux_res, cmdp->pc_aux_res_set_seq);
+       return;
+}
+
+static int
+pa2_aux_res_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RES_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_res_set_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                            cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_res_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RES_SET);
+       ASSERT(cmdp->pc_counter == 0 || cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_res_set_read\n");
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_res_set_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RES_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux_res_set_write: 0x%X\n", byte);
+       ASSERT((byte & ~0x3) == 0);
+
+       /*
+        * Might not actually be setting the res, but the preamble for a
+        * synaptic special cmd.  Store a copy of the current res
+        * to restore.
+        */
+
+       ssp = &fp->pp_state_cur;
+       ssp->pss_aux_res = byte & 0x3;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_RES;
+       cmdp->pc_aux_special_byte <<= 2;
+       cmdp->pc_aux_special_byte |= (byte & 0x3);
+       cmdp->pc_aux_res_set_seq++;
+       if (cmdp->pc_aux_res_set_seq > 4) {
+               /* correct? XXX */
+               cmdp->pc_aux_res_set_seq = 4;
+       }
+       ret = 0;
+       cmdp->pc_counter++;
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_res_set_redo(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_res_set_redo\n");
+       ssp = &fp->pp_state_cur;
+       ret = pa2_aux_res_set_do(fp, ssp->pss_aux_res);
+       return ret;
+}
+
+/*******************************************************************/
+
+/* should be moved to a synaptic area XXX */
+static void
+pa2_synaptic_print_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       /* special byte should be inside synaptic area XXX */
+       ssp = &fp->pp_state_cur;
+       switch (cmdp->pc_aux_special_byte) {
+       case PA2_SYNAP_GET_IDENTIFY:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_IDENTIFY\n");
+               break;
+
+       case PA2_SYNAP_GET_MODES:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_MODES\n");
+               break;
+
+       case PA2_SYNAP_GET_CAPS:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_CAPS\n");
+               break;
+
+       case PA2_SYNAP_GET_MODEL:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_MODEL\n");
+               break;
+
+       case PA2_SYNAP_GET_UNKNOWN_XXX1:
+               PRINT_DEBUG(PA2_DBG_MID, "PA2_SYNAP_GET_UNKNOWN_XXX1\n");
+               break;
+
+       case PA2_SYNAP_GET_SERIAL_PREFIX:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_SERIAL_PREFIX\n");
+               break;
+
+       case PA2_SYNAP_GET_SERIAL_SUFFIX:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_SERIAL_SUFFIX\n");
+               break;
+
+       case PA2_SYNAP_GET_RESOLUTION:
+               PRINT_DEBUG(PA2_DBG_MID, "SYNAP_GET_RESOLUTION\n");
+               break;
+
+       case PA2_SYNAP_GET_EXTENDED_MODEL:
+               PRINT_DEBUG(PA2_DBG_MID, "PA2_SYNAP_GET_EXTENDED_MODEL\n");
+               break;
+
+       case PA2_SYNAP_GET_UNKNOWN_XXX3:
+               PRINT_DEBUG(PA2_DBG_MID, "PA2_SYNAP_GET_UNKNOWN_XXX3\n");
+               break;
+
+       case PA2_SYNAP_GET_UNKNOWN_XXX4:
+               PRINT_DEBUG(PA2_DBG_MID, "PA2_SYNAP_GET_UNKNOWN_XXX4\n");
+               break;
+
+       default:
+               PRINT_DEBUG(PA2_DBG_MID, "\n");
+               pa2_error("Unknown special: 0x%X\n", cmdp->pc_aux_special_byte);
+               break;
+       }
+       return;
+}
+
+static void
+pa2_aux_get_status_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STATUS_GET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ssp = &fp->pp_state_cur;
+       if (cmdp->pc_flags & PA2_CMD_FLG_SPECIAL) {
+               ASSERT(cmdp->pc_aux_res_set_seq == 4);
+               cmdp->pc_flags &= ~PA2_CMD_FLG_SPECIAL;
+               cmdp->pc_aux_res_set_seq = 0;
+               cmdp->pc_aux_special_byte = 0;
+               /* more debug XXX */
+               PRINT_DEBUG(PA2_DBG_MID, "aux_get_status_done: for "
+                                              "special\n");
+       } else {
+               if (cmdp->pc_flags & PA2_CMD_FLG_SHORT) {
+                       sp = &ssp->pss_synaptic;
+                       ASSERT(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_MODE_BYTE);
+                       PRINT_DEBUG(PA2_DBG_MID,
+                   "aux_get_status_done: mode_byte 0x%X\n", sp->ps_mode_byte);
+                       cmdp->pc_flags &= ~PA2_CMD_FLG_SHORT;
+               } else {
+                       PRINT_DEBUG(PA2_DBG_MID,
+                   "aux_get_status_done: 0x%X 0x%X 0x%X \n",
+                   ssp->pss_aux_status, ssp->pss_aux_res, ssp->pss_aux_sample);
+               }
+       }
+       return;
+}
+
+static int
+pa2_aux_get_status_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_state_t *sp;
+       int             ret;
+       int             foo;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STATUS_GET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL));
+       ASSERT(cmdp->pc_counter == 0);
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_get_status_cmd\n");
+
+       /*
+        * Bit-5 in the mode_byte disables this special cmd sequence.
+        * Should have probe for synaptic too, and have found it! XXX
+        */
+
+       scp = &fp->pp_state_const;
+       if (scp->psc_flgs &
+           (PA2_CONST_SYNAP_YES | PA2_CONST_SYNAP_PROBED)) {
+               sp = &ssp->pss_synaptic;
+ PRINT_DEBUG(PA2_DBG_MID, "STATUS: 0x%X 0x%X\n", scp->psc_flgs, sp->ps_mode_byte);
+               foo = 0;
+               if (cmdp->pc_aux_res_set_seq == 4) {
+                       foo = 1;
+                       if (!(sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+               PRINT_DEBUG(PA2_DBG_MID,
+                           "Setting PA2_CMD_FLG_SPECIAL (0x%X)\n",
+                               sp->ps_mode_byte);
+                               ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL));
+                               cmdp->pc_flags |= PA2_CMD_FLG_SPECIAL;
+                       }
+               }
+
+               /*
+                * shorter reply XXX
+                */
+
+#if    1
+               if (foo == 0 && !(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL) &&
+                   (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+               PRINT_DEBUG(PA2_DBG_MID, "Short get status cmd\n");
+                       cmdp->pc_bytes_in = 1;  /* ? XXX */
+                       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_SHORT));
+                       cmdp->pc_flags |= PA2_CMD_FLG_SHORT;
+               }
+#endif
+       }
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                        cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_aux_get_status_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_const_t *syp;
+       pa2_syp_state_t *sp;
+       uint8_t         data;
+       uint8_t         counter;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STATUS_GET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_get_status_read: 0x%X\n", byte);
+       ret = 0;
+       counter = cmdp->pc_counter++;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       syp = &fp->pp_state_const.psc_synap;
+       sp = &ssp->pss_synaptic;
+       if (!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL)) {
+               switch (counter) {
+               case 0:                 /* ACK */
+                       ASSERT(byte == PA2_AUX_REPLY_ACK);
+                       cmdp->pc_expected_input = ssp->pss_aux_status;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 0) {
+                               ASSERT(ssp->pss_hv_flgs &
+                                      PA2_HV_STATE_AUX_RES);
+                               /* if short, then no XXX */
+                               ret = pa2_aux_append(fp, cmdp,
+                                               ssp->pss_aux_status);
+                       }
+                       break;
+
+               case 1:                 /* aux status */
+                       if (cmdp->pc_flags & PA2_CMD_FLG_SHORT) {
+                               if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SAMP) {
+                                       ASSERT(ssp->pss_aux_sample == byte);
+                               }
+                               ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SAMP;
+                               ssp->pss_aux_sample = byte;
+                               break;
+                       }
+
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_REMOTE) {
+                               ASSERT((byte & PA2_AUX_STAT_REMOTE) ==
+                                      (ssp->pss_aux_status & PA2_AUX_STAT_REMOTE));
+                       }
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_ENABLE) {
+                               ASSERT((byte & PA2_AUX_STAT_ENABLE) ==
+                                      (ssp->pss_aux_status & PA2_AUX_STAT_ENABLE));
+                       }
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SCALE) {
+                               ASSERT((byte & PA2_AUX_STAT_SCALING) ==
+                                      (ssp->pss_aux_status & PA2_AUX_STAT_SCALING));
+                       }
+       /* should use pp_tmp_buf here XXX */
+                       ssp->pss_aux_status = byte;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_REMOTE;;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_ENABLE;;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;;
+
+                       cmdp->pc_expected_input = ssp->pss_aux_res;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+                       ret = pa2_aux_append(fp, cmdp, ssp->pss_aux_res);
+                       break;
+
+               case 2:                 /* resolution */
+                       if (cmdp->pc_flags & PA2_CMD_FLG_SHORT) {
+       PRINT_DEBUG(PA2_DBG_HIGH, "unknown1: 0x%X\n", byte);
+                               break;
+                       }
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES) {
+                               ASSERT(ssp->pss_aux_res == byte);
+                       }
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_RES;
+                       ssp->pss_aux_res = byte;
+
+                       cmdp->pc_expected_input = ssp->pss_aux_sample;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SAMP);
+                       ret = pa2_aux_append(fp, cmdp, ssp->pss_aux_sample);
+                       break;
+
+               case 3:
+                       if (cmdp->pc_flags & PA2_CMD_FLG_SHORT) {
+       PRINT_DEBUG(PA2_DBG_HIGH, "unknown2: 0x%X\n", byte);
+                               break;
+                       }
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SAMP) {
+                               ASSERT(ssp->pss_aux_sample == byte);
+       PRINT_DEBUG(PA2_DBG_HIGH, "sample: 0x%X 0x%X\n", ssp->pss_aux_sample, byte);
+                       }
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SAMP;
+                       ssp->pss_aux_sample = byte;
+                       break;
+
+               case 4:
+                       pa2_error("Unexpected byte in aux_get_status_read\n");
+                       break;
+               }
+               goto out;
+       }
+
+       switch (counter) {
+       case 0:                 /* ACK */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               PRINT_DEBUG(PA2_DBG_LOW, "\tSPECIAL GET STATUS: ");
+               pa2_synaptic_print_cmd(fp, cmdp);
+               switch (cmdp->pc_aux_special_byte) {
+               case PA2_SYNAP_GET_IDENTIFY:
+                       cmdp->pc_expected_input = syp->pcy_info_minor;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (fp->pp_init == 0) {
+                               fp->pp_flags |= PA2_FLG_SYNAPTIC_PROBE;
+                       }
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_INFO_MIN);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_info_minor);
+                       break;
+
+               case PA2_SYNAP_GET_MODES:
+                       cmdp->pc_expected_input = syp->pcy_modes_first_byte;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODES_B1);
+                       ret = pa2_aux_append(fp, cmdp,
+                                            syp->pcy_modes_first_byte);
+                       break;
+
+               case PA2_SYNAP_GET_CAPS:
+                       data = syp->pcy_capExtended << 7;
+                       data |= (syp->pcy_capQueries << 4);
+                       data |= (syp->pcy_capReserved1 << 3);
+                       data |= (syp->pcy_capMidBut << 2);
+                       data |= (syp->pcy_capReserved2);
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_FIRST_BYTE);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_MODEL:
+                       data = (syp->pcy_model_id >> 16) & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_16);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX1:
+                       cmdp->pc_expected_input = syp->pcy_unknown11;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN1);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown11);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_PREFIX:
+                       data = (syp->pcy_serial_num >> 24) & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_24);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_SUFFIX:
+                       data = (syp->pcy_serial_num >> 16) & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_16);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_RESOLUTION:
+                       cmdp->pc_expected_input = syp->pcy_info_xupmm;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_XUPMM);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_info_xupmm);
+                       break;
+
+               case PA2_SYNAP_GET_EXTENDED_MODEL:
+                       cmdp->pc_expected_input = syp->pcy_unknown21;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_EXT_MODEL);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown21);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX3:
+                       cmdp->pc_expected_input = syp->pcy_unknown31;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN3);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown31);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX4:
+                       cmdp->pc_expected_input = syp->pcy_unknown41;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               goto out;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN4);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown41);
+                       break;
+
+               default:
+                       pa2_error("Unknown special byte: 0x%X\n",
+                                   cmdp->pc_aux_special_byte);
+                       break;
+               }
+               break;
+
+       case 1:
+               switch (cmdp->pc_aux_special_byte) {
+               case PA2_SYNAP_GET_IDENTIFY:
+                       syp->pcy_info_minor = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_INFO_MIN;
+                       PRINT_DEBUG(PA2_DBG_LOW, "\tinfo_minor: 0x%X\n",
+                                   syp->pcy_info_minor);
+                       cmdp->pc_expected_input = 0x47;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 0) {
+                               ret = pa2_aux_append(fp, cmdp, 0x47);
+                       }
+                       break;
+
+               case PA2_SYNAP_GET_MODES:
+                       ASSERT(byte == 0x3B);
+                       if (byte != 0x3B) {
+                               PRINT_DEBUG(PA2_DBG_MID, "\t0x3B = 0x%X\n",
+                                               byte);
+                       }
+                       syp->pcy_modes_first_byte = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_MODES_B1;
+                       cmdp->pc_expected_input = 0x47;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 0) {
+                               ret = pa2_aux_append(fp, cmdp, 0x47);
+                       }
+                       break;
+
+               case PA2_SYNAP_GET_CAPS:
+                       syp->pcy_capExtended = (byte & 0x80) >> 7;
+                       syp->pcy_capQueries = (byte & 0x70) >> 4;
+                       syp->pcy_capReserved1 = (byte & 0x08) >> 3;
+                       syp->pcy_capMidBut = (byte & 0x04) >> 2;
+                       syp->pcy_capReserved2 = (byte & 0x03);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_CAPS_FIRST_BYTE;
+                       cmdp->pc_expected_input = 0x47;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 0) {
+                               ret = pa2_aux_append(fp, cmdp, 0x47);
+                       }
+                       break;
+
+               case PA2_SYNAP_GET_MODEL:
+                       syp->pcy_model_id &= ~(0xFF << 16);
+                       syp->pcy_model_id |= ((uint32_t)byte << 16);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_MODEL_16;
+                       data = (syp->pcy_model_id >> 8) & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_08);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX1:
+                       syp->pcy_unknown11 = byte;
+                       /* XXXX have flag for this? XXX */
+                       cmdp->pc_expected_input = syp->pcy_unknown12;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN1);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown12);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_PREFIX:
+                       syp->pcy_serial_num &= ~(0xFF << 24);
+                       syp->pcy_serial_num |= ((uint32_t)byte << 24);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_24;
+                       data = ((syp->pcy_serial_num >> (32 - 4)) & 0xF0) |
+                              syp->pcy_nibble1;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_32);
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_NB);
+                       /* store undeinfed bits somehere XXXX */
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_SUFFIX:
+                       syp->pcy_serial_num &= ~(0xFF << 16);
+                       syp->pcy_serial_num |= ((uint32_t)byte << 16);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_16;
+                       data = (syp->pcy_serial_num >> 8) & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_08);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_RESOLUTION:
+                       syp->pcy_info_xupmm = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_XUPMM;
+                       cmdp->pc_expected_input = 0x80;         /* XXX */
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ret = pa2_aux_append(fp, cmdp, 0x80);   /* XXX */
+                       break;
+
+               case PA2_SYNAP_GET_EXTENDED_MODEL:
+                       syp->pcy_unknown21 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown22;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_EXT_MODEL);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown22);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX3:
+                       syp->pcy_unknown31 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown32;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN3);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown32);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX4:
+                       syp->pcy_unknown41 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown42;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN4);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown42);
+                       break;
+               }
+               break;
+
+       case 2:
+               switch (cmdp->pc_aux_special_byte) {
+               case PA2_SYNAP_GET_IDENTIFY:
+                       /*
+                        * For a synaptic touchpad, second byte will be 0x47.
+                        * For non-synaptic device, it will be $0x00 (the
+                        * current resolution).
+                        */
+
+                       ASSERT(byte == 0x47 || byte == 0);
+                       if (byte == 0x47) {     /* magic XXX */
+                               if (fp->pp_init == 1) {
+                                       scp = &fp->pp_state_const;
+                                       scp->psc_flgs |= PA2_CONST_SYNAP_YES;
+                               }
+                       }
+                       data = (syp->pcy_info_mod_code << 4) |
+                              syp->pcy_info_major;
+                       cmdp->pc_expected_input = syp->pcy_unknown42;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_INFO_MAJ);
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_CD);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_MODES:
+                       ASSERT(byte == 0x47);
+                       cmdp->pc_expected_input = sp->ps_mode_byte;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_MODE_BYTE);
+                       ret = pa2_aux_append(fp, cmdp, sp->ps_mode_byte);
+                       break;
+
+               case PA2_SYNAP_GET_CAPS:
+                       ASSERT(byte == 0x47);
+                       data = syp->pcy_capReserved3 << 5;
+                       data |= syp->pcy_capSleep << 4;
+                       data |= syp->pcy_capFourBut << 3;
+                       data |= syp->pcy_capBallistics << 2;
+                       data |= syp->pcy_capMultiFinger << 1;
+                       data |= syp->pcy_capPalmDetect;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_THIRD_BYTE);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_MODEL:
+                       syp->pcy_model_id &= ~(0xFF << 8);
+                       syp->pcy_model_id |= ((uint16_t)byte << 8);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_MODEL_08;
+                       data = syp->pcy_model_id & 0xFF;
+                       cmdp->pc_expected_input = data;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_00);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX1:
+                       syp->pcy_unknown12 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown13;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN1);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown13);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_PREFIX:
+                       syp->pcy_serial_num &= ~(0xFULL << 32);
+                       syp->pcy_serial_num |= ((uint64_t)byte << 32) & 0xF;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_32;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_NB;
+                       cmdp->pc_expected_input = syp->pcy_byte1;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HW_SYNAP_SER_BYTE);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_byte1);
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_SUFFIX:
+                       syp->pcy_serial_num &= ~(0xFF << 16);
+                       syp->pcy_serial_num |= ((uint32_t)byte << 16);
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_08;
+
+                       data = syp->pcy_serial_num & 0xFF;
+                       cmdp->pc_expected_input = syp->pcy_byte1;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_00);
+                       ret = pa2_aux_append(fp, cmdp, data);
+                       break;
+
+               case PA2_SYNAP_GET_RESOLUTION:
+                       ASSERT(byte == 0x80);
+                       cmdp->pc_expected_input = syp->pcy_info_yupmm;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_YUPMM);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_info_yupmm);
+                       break;
+
+               case PA2_SYNAP_GET_EXTENDED_MODEL:
+                       syp->pcy_unknown22 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown23;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_EXT_MODEL);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown23);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX3:
+                       syp->pcy_unknown32 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown33;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN3);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown33);
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX4:
+                       syp->pcy_unknown42 = byte;
+                       cmdp->pc_expected_input = syp->pcy_unknown43;
+                       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+                       if (cmdp->pc_active == 1) {
+                               break;
+                       }
+                       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN4);
+                       ret = pa2_aux_append(fp, cmdp, syp->pcy_unknown43);
+                       break;
+               }
+               break;
+
+       case 3:
+               switch (cmdp->pc_aux_special_byte) {
+               case PA2_SYNAP_GET_IDENTIFY:
+                       syp->pcy_info_mod_code = byte >> 4;
+                       syp->pcy_info_major = byte & 0xF;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_INFO_MAJ;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_MODEL_CD;
+                       break;
+
+               case PA2_SYNAP_GET_MODES:
+                       sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_MODE_BYTE;
+                       sp->ps_mode_byte = byte;
+       PRINT_DEBUG(PA2_DBG_HIGH, "sp->ps_mode_byte = 0x%X\n", sp->ps_mode_byte);
+
+                       /*
+                        * If the absolute bit is set, this makes each
+                        * motion report 6 bytes.
+                        */
+
+                       if (sp->ps_mode_byte & PA2_SYNAP_BIT_ABS) {
+                               fp->pp_aux_data.pad_format = PA2_AUX_FMT_ABS;
+                               if (sp->ps_mode_byte & PA2_SYNAP_BIT_WMODE) {
+                                       fp->pp_aux_data.pad_format = PA2_AUX_FMT_ABS_WMODE;
+                               }
+                       }
+                       break;
+
+               case PA2_SYNAP_GET_CAPS:
+                       syp->pcy_capReserved3 = (byte & 0xE0) >> 5;
+                       syp->pcy_capSleep = (byte & 0x10) >> 4;
+                       syp->pcy_capFourBut = (byte & 0x08) >> 3;
+                       syp->pcy_capBallistics = (byte & 0x04) >> 2;
+                       syp->pcy_capMultiFinger = (byte & 0x02) >> 1;
+                       syp->pcy_capPalmDetect = byte & 0x1;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_CAPS_THIRD_BYTE;
+                       break;
+
+               case PA2_SYNAP_GET_MODEL:
+                       syp->pcy_model_id &= ~0xFF;
+                       syp->pcy_model_id |= byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_MODEL_00;
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX1:
+                       syp->pcy_unknown13 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_UNKNOWN1;
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_PREFIX:
+                       syp->pcy_byte1 = byte;
+                       syp->pcy_hv_flgs |= PA2_HW_SYNAP_SER_BYTE;
+                       break;
+
+               case PA2_SYNAP_GET_SERIAL_SUFFIX:
+                       syp->pcy_serial_num &= ~0xFF;
+                       syp->pcy_serial_num |= byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_SER_00;
+                       break;
+
+               case PA2_SYNAP_GET_RESOLUTION:
+                       syp->pcy_info_yupmm = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_YUPMM;
+                       break;
+
+               case PA2_SYNAP_GET_EXTENDED_MODEL:
+                       syp->pcy_unknown23 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_EXT_MODEL;
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX3:
+                       syp->pcy_unknown33 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_UNKNOWN3;
+                       break;
+
+               case PA2_SYNAP_GET_UNKNOWN_XXX4:
+                       syp->pcy_unknown43 = byte;
+                       syp->pcy_hv_flgs |= PA2_HV_SYNAP_UNKNOWN4;
+                       break;
+               }
+               break;
+       }
+
+out:
+       return ret;
+}
+
+static int
+pa2_aux_get_status_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_get_status_redo\n");
+       return 0;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_stream_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STREAM_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(fp->pp_state_cur.pss_aux_status & PA2_AUX_STAT_REMOTE));
+       /* need to assert have remote XXX */
+       PRINT_DEBUG(PA2_DBG_MID, "aux_stream_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_stream_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+       uint8_t         status;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STREAM_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "aux_stream_set_cmd\n");
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                        cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_stream_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_STREAM_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_stream_set_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       fp->pp_state_cur.pss_hv_flgs |= PA2_HV_STATE_AUX_REMOTE;;
+       fp->pp_state_cur.pss_aux_status &= ~PA2_AUX_STAT_REMOTE;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_stream_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_stream_set_redo\n");
+       ret = pa2_aux_stream_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_poll_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_POLL);
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "aux_poll_done\n");
+       return;
+}
+
+static int
+pa2_aux_poll_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_POLL);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_poll_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX,
+                                        cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_poll_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_POLL);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_poll_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       /* if not active, send some data XXX */
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_poll_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_poll_redo\n");
+       return 0;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_wrap_reset_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_RESET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(fp->pp_state_cur.pss_aux_wrap_mode == 0);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_wrap_reset_done\n");
+       return;
+}
+
+static int
+pa2_aux_wrap_reset_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_RESET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_reset_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_wrap_reset_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_RESET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_reset_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       fp->pp_state_cur.pss_aux_wrap_mode = 0;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_wrap_reset_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_wrap_reset_redo\n");
+       ret = pa2_aux_wrap_reset_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+/* Wrap Mode:
+ * This is an "echoing" mode in which every byte received by the mouse is sent
+ * back to the guest. Even if the byte represents a valid command, the mouse
+ * will not respond to that command--it will only echo that byte back to the
+ * guest. There are two exceptions to this: the "Reset" command and "Reset
+ * Wrap Mode" command. The mouse treats these as valid commands and does not
+ * echo them back to the guest. 
+ * Credit the above. XXX
+ * Currently, wrap mode cmds accepted but wrap not supported. XXX
+ */
+
+static void
+pa2_aux_wrap_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(fp->pp_state_cur.pss_aux_wrap_mode == 1);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_wrap_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_set_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_wrap_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_WRAP_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_set_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       fp->pp_state_cur.pss_aux_wrap_mode = 1;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_wrap_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_wrap_set_redo\n");
+       ret = pa2_aux_wrap_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_remote_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_REMOTE_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       ASSERT(fp->pp_state_cur.pss_aux_status & PA2_AUX_STAT_REMOTE);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_remote_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_remote_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_REMOTE_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_MID, "aux_remote_set_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_remote_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_REMOTE_SET);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_remote_set_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       fp->pp_state_cur.pss_aux_status |= PA2_AUX_STAT_REMOTE;
+       cmdp->pc_counter++;
+       return 0;
+}
+
+static int
+pa2_aux_remote_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_remote_set_redo\n");
+       ret = pa2_aux_remote_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_type_get_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_const_t     *scp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_TYPE_GET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 2);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_type_get_done\n");
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_AUX_TYPE);
+       return;
+}
+
+static int
+pa2_aux_type_get_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_TYPE_GET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_type_get_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_type_get_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_const_t     *scp;
+       uint8_t         status;
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_TYPE_GET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_type_get_read\n");
+       ret = 0;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       scp = &fp->pp_state_const;
+       switch(cmdp->pc_counter++) {
+       case    0:              /* ACK */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               cmdp->pc_expected_input = scp->psc_aux_type;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_AUX_TYPE);
+                       ret = pa2_aux_append(fp, cmdp, scp->psc_aux_type);
+               }
+               break;
+
+       case 1:
+               if (scp->psc_hv_flgs & PA2_HV_CONST_AUX_TYPE) {
+                       ASSERT(scp->psc_aux_type == byte);
+               }
+               scp->psc_aux_type = byte;
+               scp->psc_hv_flgs |= PA2_HV_CONST_AUX_TYPE;
+               break;
+
+       default:
+               pa2_error("pa2_aux_type_get_read\n");
+               ret = 1;
+               break;
+       }
+       return ret;
+}
+
+static int
+pa2_aux_type_get_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_type_get_redo\n");
+       return 0;
+}
+
+/*******************************************************************/
+
+/*
+ * Set sample rate.  Followed by one argument, this cmd sets the sample rate
+ * in samples per sec.  Legal values are 10, 20, 40, 60, 80, 100 and 200.
+ * When part of a 'special' cmd sequence, this cmd is also used to set
+ * Synaptic's mode byte.
+ * A quick test has shown that even when being used to set the mode byte, the
+ * sample rate is set too.
+ */
+
+static void
+pa2_aux_sample_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SAMPLE_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ssp = &fp->pp_state_cur;
+       if (cmdp->pc_flags & PA2_CMD_FLG_SPECIAL) {
+               /* only if byte was 0x14 XXX */
+               ASSERT(cmdp->pc_aux_res_set_seq == 4);
+               cmdp->pc_flags &= ~PA2_CMD_FLG_SPECIAL;
+               cmdp->pc_aux_res_set_seq = 0;
+               cmdp->pc_aux_special_byte = 0;
+               sp = &fp->pp_state_cur.pss_synaptic;
+               PRINT_DEBUG(PA2_DBG_MID, "aux_sample_set_done (special): "
+                           "0x%X\n", sp->ps_mode_byte);
+       } else {
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SAMP);
+               PRINT_DEBUG(PA2_DBG_MID, "aux_sample_set_done: 0x%X\n",
+                           ssp->pss_aux_sample);
+       }
+       return;
+}
+
+static int
+pa2_aux_sample_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SAMPLE_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "pa2_aux_sample_set_cmd\n");
+       if (cmdp->pc_aux_res_set_seq == 4) {
+               PRINT_DEBUG(PA2_DBG_MID,
+                           "Setting PA2_CMD_FLG_SPECIAL\n");
+               ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL));
+               cmdp->pc_flags |= PA2_CMD_FLG_SPECIAL;
+       }
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_sample_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_syp_const_t *syp;
+       pa_syp_0x28_t   *p28p;
+       uint            i;
+       int             ret;
+       uint8           data;
+       uint8           counter;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SAMPLE_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_sample_set_read: 0x%X\n", byte);
+       ret = 0;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       ssp = &fp->pp_state_cur;
+       counter = cmdp->pc_counter++;
+       switch(counter) {
+       case 0:                 /* ACK of cmd */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+               }
+               break;
+
+       case 1:                 /* ACK of arg */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+                       break;
+               }
+
+               /*
+                * Even when part of the special cmd sequence, the Synaptic
+                * touchpad appears to set the sample rate, unless the
+                * XXXBITXXX is set.
+                */
+               sp = &fp->pp_state_cur.pss_synaptic;
+               if (!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL) ||
+                   (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+                       ssp->pss_aux_sample = cmdp->pc_tmp_buf[0];
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SAMP;
+               }
+               if (!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL)) {
+                       break;
+               }
+               ASSERT(cmdp->pc_tmp_buf[0] == 0x14 ||
+                      cmdp->pc_tmp_buf[0] == 0x28);
+               if (cmdp->pc_tmp_buf[0] != 0x14 &&
+                   cmdp->pc_tmp_buf[0] != 0x28) {      /* magics XXX */
+                       break;
+               }
+               if (cmdp->pc_tmp_buf[0] == 0x14) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "\tSPECIAL SET STATUS: "
+                               "mode_byte = 0x%X\n",
+                               cmdp->pc_aux_special_byte);
+                       sp->ps_mode_byte = cmdp->pc_aux_special_byte;
+                       sp->ps_hv_flgs |= PA2_HV_SYNAP_STATE_MODE_BYTE;
+                       if (sp->ps_mode_byte & PA2_SYNAP_BIT_ABS) {
+                               fp->pp_aux_data.pad_format = PA2_AUX_FMT_ABS;
+                               if (sp->ps_mode_byte & PA2_SYNAP_BIT_WMODE) {
+                                       fp->pp_aux_data.pad_format = PA2_AUX_FMT_ABS_WMODE;
+                               }
+                       }
+                       break;
+               }
+
+               /*
+                * This appears to trigger a transmission of 6/12
+                * bytes.  Is this calibration data? XXX
+                */
+
+               PRINT_DEBUG(PA2_DBG_HIGH, "\tSPECIAL2 STATUS: "
+                       "byte = 0x%X\n",
+                       cmdp->pc_aux_special_byte);
+               p28p = NULL;
+               syp = &fp->pp_state_const.psc_synap;
+               for (i = 0; i < sizeof (syp->pcy_28_resp) /
+                               sizeof (syp->pcy_28_resp[0]); i++) {
+                       p28p = &syp->pcy_28_resp[i];
+                       /* no gaps XXX */
+                       if (p28p->p28_status == 0) {
+                               break;
+                       }
+                       if (p28p->p28_special == cmdp->pc_aux_special_byte) {
+                               break;
+                       }
+                       p28p = NULL;
+               }
+               if (p28p == NULL) {
+                       pa2_error("out of space for special cmds\n");
+                       /* send NAK XXX */
+                       break;
+               }
+               if (p28p->p28_status == 0) {
+                       ASSERT(cmdp->pc_active == 1);
+                       ASSERT(p28p->p28_special == 0);
+                       p28p->p28_status = 1;
+                       p28p->p28_special = cmdp->pc_aux_special_byte;
+               }
+               ASSERT(cmdp->pc_aux_p28 == NULL);
+               cmdp->pc_aux_p28 = p28p;
+               cmdp->pc_expected_input = p28p->p28_bytes[0];
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               cmdp->pc_bytes_in = 6;
+               if (cmdp->pc_aux_special_byte == 0x2C) {
+                       cmdp->pc_bytes_in = 12;
+               }
+               cmdp->pc_flags |= PA2_CMD_FLG_INPUT;
+               if (cmdp->pc_active == 0) {
+                       ret = pa2_aux_append(fp, cmdp, p28p->p28_bytes[0]);
+               }
+               break;
+
+       case 2:
+       case 3:
+       case 4:
+       case 5:
+       case 6:
+       case 7:
+       case 8:
+       case 9:
+       case 10:
+       case 11:
+       case 12:
+       case 13:
+               ASSERT(cmdp->pc_aux_p28 != NULL);
+               p28p = cmdp->pc_aux_p28;
+               if (p28p == NULL) {
+                       break;
+               }
+               if (fp->pp_init == 0) {
+                       ASSERT(p28p->p28_bytes[counter - 2] == byte);
+                       if (p28p->p28_bytes[counter - 2] != byte) {
+                               PRINT_DEBUG(PA2_DBG_HIGH, "special2: 0x%X 0x%X\n", p28p->p28_bytes[counter - 2], byte);
+                       }
+               }
+               p28p->p28_bytes[counter - 2] = byte;
+               if (counter == 7) {
+                       if (cmdp->pc_aux_special_byte != 0x2C) {
+                               cmdp->pc_aux_p28 = NULL;
+                               break;
+                       }
+               }
+               if (counter == 13) {
+                       cmdp->pc_aux_p28 = NULL;
+                       break;
+               }
+               data = p28p->p28_bytes[counter - 1];
+               cmdp->pc_expected_input = data;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 0) {
+                       ret = pa2_aux_append(fp, cmdp, data);
+               }
+               break;
+
+       default:
+               pa2_error("pa2_aux_sample_set_read\n");
+               ret = 1;
+               break;
+       }
+       return ret;
+}
+
+static int
+pa2_aux_sample_set_write(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_SAMPLE_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_sample_set_write: 0x%X\n", byte);
+       cmdp->pc_counter++;
+       cmdp->pc_tmp_buf[0] = byte;
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_data_write(fp, PA2_INDEX_AUX, byte);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_sample_set_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_sample_set_redo\n");
+       return -1;                      /* XXXXXXXXXXXXXXXXXXXXX */
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_dev_enable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_ENABLE);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_aux_status & PA2_AUX_STAT_ENABLE);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_dev_enable_done\n");
+       return;
+}
+
+static int
+pa2_aux_dev_enable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_ENABLE);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_dev_enable_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_dev_enable_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_ENABLE);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_dev_enable_read: 0x%X\n", byte);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_counter++;
+       if (byte == PA2_AUX_REPLY_ACK) {
+               ssp = &fp->pp_state_cur;
+               ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;
+               ssp->pss_aux_status |= PA2_AUX_STAT_ENABLE;
+       }
+       return 0;
+}
+
+static int
+pa2_aux_dev_enable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_dev_enable_redo\n");
+       ret = pa2_aux_dev_enable_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_dev_disable_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_DISABLE);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ssp = &fp->pp_state_cur;
+       ASSERT(!(ssp->pss_aux_status & PA2_AUX_STAT_ENABLE));
+       PRINT_DEBUG(PA2_DBG_MID, "aux_dev_disable_done\n");
+       return;
+}
+
+static int
+pa2_aux_dev_disable_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_DISABLE);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_dev_disable_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_dev_disable_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEV_DISABLE);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_dev_disable_read\n");
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       cmdp->pc_counter++;
+       if (byte == PA2_AUX_REPLY_ACK) {
+               ssp = &fp->pp_state_cur;
+               ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;
+               ssp->pss_aux_status &= ~PA2_AUX_STAT_ENABLE;
+       }
+       return 0;
+}
+
+static int
+pa2_aux_dev_disable_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_dev_disable_redo\n");
+       ret = pa2_aux_dev_disable_redo(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+/*
+ * Helper function that loads the values of a AUX RESET into our soft state.
+ */
+
+static void
+pa2_aux_set_state_default(
+       pa2_t           *fp,
+       int             full_reset)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_state_t *sp;
+
+       PRINT_DEBUG_ENTER("\n");
+       ssp = &fp->pp_state_cur;
+       ssp->pss_aux_sample = 100;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SAMP;
+       ssp->pss_aux_res = 0x02;
+       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_RES;
+       ssp->pss_aux_status &= ~PA2_AUX_STAT_SCALING;
+       ssp->pss_aux_status &= ~PA2_AUX_STAT_REMOTE;
+       ssp->pss_aux_status &= ~PA2_AUX_STAT_ENABLE;
+       ssp->pss_aux_wrap_mode = 0;
+       /* only if not s/w initial XXX */
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_REMOTE;;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_ENABLE;;
+                       ssp->pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;;
+
+       /*
+        * It is not clear if synatic clears the ABS bit during a reset or
+        * set defaults.  should probe during init. XXX
+        */
+
+       scp = &fp->pp_state_const;
+       if (scp->psc_flgs & PA2_CONST_SYNAP_YES) {
+               /*
+                * If last setscale was setscale21, then do not clear bits.
+                * Discovered via investigation...
+                */
+               if (ssp->pss_setscale_last != 1) {      /* magic XXX */
+                       if (full_reset) {
+                               sp = &ssp->pss_synaptic;
+                               sp->ps_mode_byte &= ~PA2_SYNAP_BIT_TRANSPARENT;
+                       }
+                       sp = &ssp->pss_synaptic;
+                       if (sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_MODE_BYTE) {
+                               sp->ps_mode_byte &= ~PA2_SYNAP_BIT_ABS;
+                               fp->pp_aux_data.pad_format = PA2_AUX_FMT_DEFAULT;
+                       }
+               }
+       }
+
+       /*
+        * A "last setscale was SET_SCALE21" appears to be cleared by a
+        * reset.
+        */
+
+       ssp->pss_setscale_last = 0;
+       return;
+}
+
+static void
+pa2_aux_default_set_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEFAULT_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_default_set_done\n");
+       return;
+}
+
+static int
+pa2_aux_default_set_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEFAULT_SET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_default_set_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_default_set_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_DEFAULT_SET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_default_set_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_counter++;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       if (byte == PA2_AUX_REPLY_ACK) {
+               pa2_aux_set_state_default(fp, 0);
+       }
+       return 0;
+}
+
+static int
+pa2_aux_default_set_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_default_set_redo\n");
+       ret = pa2_aux_default_set_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static void
+pa2_aux_ack_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_ACK);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 1);
+       PRINT_DEBUG(PA2_DBG_MID, "aux_ack_done\n");
+       return;
+}
+
+static int
+pa2_aux_ack_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_ACK);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_ack_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_ack_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_ACK);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_ack_read\n");
+       ASSERT(byte == PA2_AUX_REPLY_ACK);
+       cmdp->pc_counter++;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       return 0;
+}
+
+static int
+pa2_aux_ack_redo(
+       pa2_t           *fp)
+{
+       PRINT_DEBUG(PA2_DBG_MID, "aux_ack_redo\n");
+       return 0;
+}
+
+/*******************************************************************/
+
+/*
+ * Perform a software reset and recalibration.  Response is ACK, followed
+ * by $AA, $00 after a calibration delay of 300–500ms.
+ * The default settings are;
+ *     sample rate is 100 per sec
+ *     resolution is 4 (encoded as 0x02)
+ *     scaling is 1:1
+ *     stream mode is selected
+ *     data reporting is disabled
+ * We only peform a reset when initializing the device. XXX
+ * No delay when a non-active guest requests a reset. XXX
+ */
+
+static void
+pa2_aux_reset_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RESET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 3);
+       ssp = &fp->pp_state_cur;
+       ASSERT(ssp->pss_aux_sample == 100);
+       ASSERT(ssp->pss_aux_res == 0x02);
+       ASSERT(!(ssp->pss_aux_status & PA2_AUX_STAT_SCALING));
+       ASSERT(!(ssp->pss_aux_status & PA2_AUX_STAT_REMOTE));
+       ASSERT(!(ssp->pss_aux_status & PA2_AUX_STAT_ENABLE));
+       /* abs mode disabled XXX */
+       PRINT_DEBUG(PA2_DBG_MID, "aux_reset_done\n");
+       return;
+}
+
+static int
+pa2_aux_reset_cmd(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RESET);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED));
+       ASSERT(cmdp->pc_counter == 0);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_reset_cmd\n");
+       cmdp->pc_expected_input = PA2_AUX_REPLY_ACK;
+       cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+       ret = pa2_ctl_write_aux_pending(fp, cmdp);
+       if (cmdp->pc_active == 1) {
+               ret = pa2_reg_cmd_data_write(fp, PA2_INDEX_AUX, cmdp->pc_aux_cmd);
+       } else {
+               ret = pa2_aux_ack(fp, cmdp);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_reset_read(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ASSERT(cmdp->pc_aux_cmd == PA2_AUX_CMD_RESET);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX));
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_EXPECTED);
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_reset_read\n");
+       ret = 0;
+       cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+       switch (cmdp->pc_counter++) {
+       case 0:                 /* ACK */
+               ASSERT(byte == PA2_AUX_REPLY_ACK);
+               cmdp->pc_expected_input = PA2_AUX_REPLY_BAT_SUCC;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 0) {
+                       ret = pa2_aux_append(fp, cmdp, PA2_AUX_REPLY_BAT_SUCC);
+               }
+               break;
+
+       case 1:                 /* BAT result */
+               ASSERT(byte == PA2_AUX_REPLY_BAT_SUCC ||
+                          byte == PA2_AUX_REPLY_ERROR);
+               cmdp->pc_expected_input = 0;
+               cmdp->pc_flags |= PA2_CMD_FLG_EXPECTED;
+               if (cmdp->pc_active == 0) {
+                       ret = pa2_aux_append(fp, cmdp, 0);
+               }
+               break;
+
+       case 2:                 /* device index */
+
+               ASSERT(byte == 0);
+               pa2_aux_set_state_default(fp, 1);
+               break;
+
+       default:
+               /* pa2_error()  XXX */
+               ret = 1;
+       }
+       return ret;
+}
+
+static int
+pa2_aux_reset_redo(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_reset_redo\n");
+       ret = pa2_aux_reset_do(fp);
+       return ret;
+}
+
+/*******************************************************************/
+
+static pa2_cmd_des_t   pa2_aux_cmd[] = {
+/* 0xE1 */     { PA2_AUX_CMD_SYNAP_E1, "AUX_CMD_SYNAP_E1",
+                 2, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_synap_e1_read,
+                 pa2_aux_synap_e1_cmd, pa2_aux_synap_e1_done,
+                 pa2_aux_synap_e1_redo,
+               },
+/* 0xE2 */     { PA2_AUX_CMD_SYNAP_E2, "AUX_CMD_SYNAP_E2",
+                 0, 0, 2, PA2_OUTPUT_AUX,
+                 0,
+                 pa2_aux_synap_e2_write, pa2_aux_synap_e2_read,
+                 pa2_aux_synap_e2_cmd, pa2_aux_synap_e2_done,
+                 pa2_aux_synap_e2_redo,
+               },
+/* 0xE6 */     { PA2_AUX_CMD_SCALE11_SET, "AUX_CMD_SCALE11_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_scale11_set_read,
+                 pa2_aux_scale11_set_cmd, pa2_aux_scale11_set_done,
+                 pa2_aux_scale11_set_redo,
+               },
+/* 0xE7 */     { PA2_AUX_CMD_SCALE21_SET, "AUX_CMD_SCALE21_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_scale21_set_read,
+                 pa2_aux_scale21_set_cmd, pa2_aux_scale21_set_done,
+                 pa2_aux_scale21_set_redo,
+               },
+/* 0xE8 */     { PA2_AUX_CMD_RES_SET, "AUX_CMD_RES_SET",
+                 0, 0, 2, PA2_OUTPUT_AUX,
+                 0,
+                 pa2_aux_res_set_write, pa2_aux_res_set_read,
+                 pa2_aux_res_set_cmd, pa2_aux_res_set_done,
+                 pa2_aux_res_set_redo,
+               },
+/* 0xE9 */     { PA2_AUX_CMD_STATUS_GET, "AUX_CMD_STATUS_GET",
+                 3, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_get_status_read,
+                 pa2_aux_get_status_cmd, pa2_aux_get_status_done,
+                 pa2_aux_get_status_redo,
+               },
+/* 0xEA */     { PA2_AUX_CMD_STREAM_SET, "AUX_CMD_STREAM_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_stream_set_read,
+                 pa2_aux_stream_set_cmd, pa2_aux_stream_set_done,
+                 pa2_aux_stream_set_redo,
+               },
+/* 0xEB */     { PA2_AUX_CMD_POLL, "AUX_CMD_POLL",
+                 0, 0, 1, PA2_OUTPUT_AUX,      /* 3 or 6 bytes XXX */
+                 0,
+                 NULL, pa2_aux_poll_read,
+                 pa2_aux_poll_cmd, pa2_aux_poll_done,
+                 pa2_aux_poll_redo,
+               },
+/* 0xEC */     { PA2_AUX_CMD_WRAP_RESET, "AUX_CMD_WRAP_RESET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_wrap_reset_read,
+                 pa2_aux_wrap_reset_cmd, pa2_aux_wrap_reset_done,
+                 pa2_aux_wrap_reset_redo,
+               },
+/* 0xEE */     { PA2_AUX_CMD_WRAP_SET, "AUX_CMD_WRAP_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_wrap_set_read,
+                 pa2_aux_wrap_set_cmd, pa2_aux_wrap_set_done,
+                 pa2_aux_wrap_set_redo,
+               },
+/* 0xF0 */     { PA2_AUX_CMD_REMOTE_SET, "AUX_CMD_REMOTE_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_remote_set_read,
+                 pa2_aux_remote_set_cmd, pa2_aux_remote_set_done,
+                 pa2_aux_remote_set_redo,
+               },
+/* 0xF2 */     { PA2_AUX_CMD_TYPE_GET, "AUX_CMD_TYPE_GET",
+                 1, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_type_get_read,
+                 pa2_aux_type_get_cmd, pa2_aux_type_get_done,
+                 pa2_aux_type_get_redo,
+               },
+/* 0xF3 */     { PA2_AUX_CMD_SAMPLE_SET, "AUX_CMD_SAMPLE_SET",
+                 0, 0, 2, PA2_OUTPUT_AUX,
+                 0,
+                 pa2_aux_sample_set_write, pa2_aux_sample_set_read,
+                 pa2_aux_sample_set_cmd, pa2_aux_sample_set_done,
+                 pa2_aux_sample_set_redo,
+               },
+/* 0xF4 */     { PA2_AUX_CMD_DEV_ENABLE, "AUX_CMD_DEV_ENABLE",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_dev_enable_read,
+                 pa2_aux_dev_enable_cmd, pa2_aux_dev_enable_done,
+                 pa2_aux_dev_enable_redo,
+               },
+/* 0xF5 */     { PA2_AUX_CMD_DEV_DISABLE, "AUX_CMD_DEV_DISABLE",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_dev_disable_read,
+                 pa2_aux_dev_disable_cmd, pa2_aux_dev_disable_done,
+                 pa2_aux_dev_disable_redo,
+               },
+/* 0xF6 */     { PA2_AUX_CMD_DEFAULT_SET, "AUX_CMD_DEFAULT_SET",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_default_set_read,
+                 pa2_aux_default_set_cmd, pa2_aux_default_set_done,
+                 pa2_aux_default_set_redo,
+               },
+/* 0xFA */     { PA2_AUX_CMD_ACK, "AUX_CMD_ACK",
+                 0, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_ack_read,
+                 pa2_aux_ack_cmd, pa2_aux_ack_done,
+                 pa2_aux_ack_redo,
+               },
+/* 0xFF */     { PA2_AUX_CMD_RESET, "AUX_CMD_RESET",
+                 2, 0, 1, PA2_OUTPUT_AUX,
+                 0,
+                 NULL, pa2_aux_reset_read,
+                 pa2_aux_reset_cmd, pa2_aux_reset_done,
+                 pa2_aux_reset_redo,
+               },
+};
+
+#if    0
+/*
+ * New cmd started before old one finished.  This function aborts the "inflight"
+ * cmd state.
+ */
+
+static void
+pa2_cmd_abort(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+
+       ssp = &fp->pp_state_cur;
+       if (cmdp->pc_flags & PA2_CMD_FLG_KBD) {
+               ssp->pss_mode &= ~PA2_CTL_MODE_DISABLE_KBD;
+       }
+       if (cmdp->pc_flags & PA2_CMD_FLG_AUX) {
+               ssp->pss_mode &= ~PA2_CTL_MODE_DISABLE_AUX;
+       }
+       return;
+}
+#endif
+
+/*
+ * All input/output from cmd has been received/sent, so process its completion.
+ */
+
+static void
+pa2_cmd_done(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       pa2_state_t     *ssp;
+       pa2_fredo_t     *redo;
+       uint8_t         active;
+       int             ret;
+       uint            index;
+
+       PRINT_DEBUG_ENTER("\n");
+       ASSERT(cmdp->pc_cmd != 0);
+       ASSERT(cmdp->pc_bytes_in == 0);
+       ASSERT(cmdp->pc_bytes_out == 0);
+ /* some of the flg bits are cleared by the done routiens - should assert afterwards XXX */
+       ASSERT((cmdp->pc_flags & ~(PA2_CMD_FLG_CTRL | PA2_CMD_FLG_AUX |
+                                  PA2_CMD_FLG_AUX_BYTE |
+                                  PA2_CMD_FLG_KBD |
+                                  PA2_CMD_FLG_SHORT |
+                                  PA2_CMD_FLG_SPECIAL |
+                                  PA2_CMD_FLG_WR_ACTIVE)) == 0);
+       PRINT_DEBUG(PA2_DBG_MID, "pa2_cmd_done: %s - 0x%X 0x%X\n",
+                   cmdp->pc_cmd_desp->pcc_name, cmdp->pc_flags, fp->pp_flags);
+
+       /*
+        * An AUX cmd enables a disabled AUX interface, and a KBD cmd
+        * (at least, may be ctl cmds? XXX) enables the KBD interface.
+        * When does this enable actually happen?  Start or end of the cmd?
+        * Lets assume end, as that makes more sense.
+        */
+
+       ssp = &fp->pp_state_cur;
+       if (cmdp->pc_flags & PA2_CMD_FLG_KBD) {
+               ssp->pss_mode &= ~PA2_CTL_MODE_DISABLE_KBD;
+       }
+       if (cmdp->pc_flags & PA2_CMD_FLG_AUX) {
+               ssp->pss_mode &= ~PA2_CTL_MODE_DISABLE_AUX;
+       }
+
+       /*
+        * Should always have a completion function.
+        */
+
+       ASSERT(cmdp->pc_func_done != NULL);
+       if (cmdp->pc_func_done != NULL) {
+               cmdp->pc_func_done(fp, cmdp);
+       }
+
+       /*
+        * If not an AUX cmd, then finished.
+        */
+
+       if (cmdp->pc_aux_cmd == 0) {
+               goto clean;
+       }
+
+       /*
+        * Either the AUX cmd, or an argument byte.
+        */
+
+       ASSERT(cmdp->pc_flags & (PA2_CMD_FLG_AUX | PA2_CMD_FLG_AUX_BYTE));
+       /* assert only one set XXX */
+       cmdp->pc_flags &= ~(PA2_CMD_FLG_AUX | PA2_CMD_FLG_AUX_BYTE);
+
+       /*
+        * Aux cmd processing could be done in PA2_CTL_CMD_WRITE_AUX XXX
+        */
+
+       ASSERT(cmdp->pc_aux_seq > 0);
+       if (cmdp->pc_aux_seq > 0) {
+               cmdp->pc_aux_seq--;
+       } else {
+               pa2_error("XXXX\n");
+       }
+       if (cmdp->pc_aux_seq == 0) {
+               ASSERT(cmdp->pc_aux_func_done != NULL);
+               if (cmdp->pc_aux_func_done) {
+                       cmdp->pc_aux_func_done(fp, cmdp);
+                       cmdp->pc_aux_func_done = NULL;
+               }
+               cmdp->pc_aux_func_write = NULL;
+               cmdp->pc_aux_func_read = NULL;
+               cmdp->pc_aux_cmd = 0;
+               ASSERT(cmdp->pc_flags == 0);
+       } else {
+               cmdp->pc_flags |= PA2_CMD_FLG_AUX_BYTE;
+       }
+       goto clean1;
+       
+clean:
+       cmdp->pc_flags = 0;
+clean1:
+       ASSERT(cmdp->pc_cmd_desp != NULL);
+       cmdp->pc_cmd = 0;
+       active = cmdp->pc_active;
+       cmdp->pc_active = 0;
+       cmdp->pc_counter = 0;
+       cmdp->pc_cmd_desp = NULL;
+       cmdp->pc_func_write = NULL;
+       cmdp->pc_func_read = NULL;
+       cmdp->pc_func_cmd = NULL;
+       cmdp->pc_func_done = NULL;
+       redo = cmdp->pc_func_redo;
+       cmdp->pc_func_redo = NULL;
+       /*
+        * Keyboard completion separated out? XXX
+        */
+
+
+       /*
+        * If cmd started inactive, but device is now active, then need
+        * to redo.
+        */
+
+       if (cmdp->pc_flags == 0) {              /* XXXX nasty XXX */
+               index = cmdp - &fp->pp_cmds[0];
+               if (active == 0 && fp->pp_active[index] == 1) {
+                       ASSERT(redo != NULL);
+                       if (redo != NULL) {
+                               ret = redo(fp);
+                       }
+               }
+       }
+       return;
+}
+
+static void
+pa2_cmd_abort(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp)
+{
+       cmdp->pc_cmd = 0;               /* PA2_CTL_CMD_NULL XXX */
+       cmdp->pc_active = 0;
+       cmdp->pc_bytes_in = 0;
+       cmdp->pc_bytes_out = 0;
+       cmdp->pc_counter = 0;
+       cmdp->pc_flags = 0;
+       cmdp->pc_expected_input = 0;
+       cmdp->pc_flags = 0;
+       cmdp->pc_cmd_desp = NULL;
+
+       cmdp->pc_func_write = NULL;
+       cmdp->pc_func_read = NULL;
+       cmdp->pc_func_cmd = NULL;
+       cmdp->pc_func_done = NULL;
+       cmdp->pc_func_redo = NULL;
+
+       cmdp->pc_aux_cmd = 0;
+       cmdp->pc_aux_seq = 0;
+       cmdp->pc_aux_res_set_seq = 0;
+       cmdp->pc_aux_special_byte = 0;
+       cmdp->pc_aux_cmd_desp = NULL;
+       cmdp->pc_aux_func_done = NULL;
+       cmdp->pc_aux_func_write = NULL;
+       cmdp->pc_aux_func_read = NULL;
+       cmdp->pc_aux_p28 = NULL;
+       return;
+}
+
+/*
+ * Taken given KBD cmd and prepare to run.
+ */
+
+static pa2_cmd_t *
+pa2_kbd_cmd_load(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       pa2_cmd_t       *cmdp;
+       pa2_cmd_des_t   *ccp;
+       pa2_dest_t      index;
+       uint            i;
+
+       PRINT_DEBUG_ENTER("\n");
+       ccp = NULL;
+       for (i = 0; i < sizeof (pa2_kbd_cmd) / sizeof (pa2_kbd_cmd[0]); i++) {
+               ccp = &pa2_kbd_cmd[i];
+               if (ccp->pcc_cmd == cmd) {
+                       break;
+               }
+               ccp = NULL;
+       }
+       ASSERT(ccp != NULL);
+       if (ccp == NULL) {
+               pa2_error("KBD command not found: 0x%X\n", cmd);
+               cmdp = NULL;
+               goto out;
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "kbd cmd: %s\n", ccp->pcc_name);
+
+       index = ccp->pcc_output_dest;
+       ASSERT(index == PA2_OUTPUT_KBD);
+       cmdp = &fp->pp_cmds[index];
+
+       ASSERT(cmdp->pc_cmd == 0);
+       ASSERT(cmdp->pc_cmd_desp == NULL);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(cmdp->pc_func_done == NULL);
+       ASSERT(cmdp->pc_func_cmd == NULL);
+       ASSERT(cmdp->pc_func_write == NULL);
+       ASSERT(cmdp->pc_func_read == NULL);
+       ASSERT(!(cmdp->pc_flags & (PA2_CMD_FLG_OUTPUT | PA2_CMD_FLG_INPUT)));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_ACK));
+       ASSERT(cmdp->pc_aux_cmd == 0);
+       ASSERT(cmdp->pc_aux_p28 == NULL);
+
+       if (cmdp->pc_cmd != 0) {                /* no-op XXX */
+               pa2_cmd_abort(fp, cmdp);
+       }
+
+       cmdp->pc_cmd = ccp->pcc_cmd;
+       cmdp->pc_cmd_desp = ccp;
+       cmdp->pc_counter = 0;
+       cmdp->pc_active = fp->pp_active[PA2_INDEX_KBD];
+
+       cmdp->pc_aux_cmd = 0;
+       cmdp->pc_aux_p28 = NULL;
+
+       /*
+        * Clear any bad state that may be present due to a bug in this file,
+        * or a bad guest.
+        */
+
+       cmdp->pc_func_done = ccp->pcc_func_done;
+       cmdp->pc_func_cmd = ccp->pcc_func_cmd;
+       cmdp->pc_func_write = ccp->pcc_func_write;
+       cmdp->pc_func_read = ccp->pcc_func_read;
+       cmdp->pc_flags = PA2_CMD_FLG_KBD;
+
+       cmdp->pc_bytes_out = ccp->pcc_bytes_out;
+       cmdp->pc_flags |= ccp->pcc_flags;
+       ASSERT(cmdp->pc_bytes_out == 0 ||
+              (cmdp->pc_flags & PA2_CMD_FLG_OUTPUT));
+       ASSERT(cmdp->pc_bytes_out != 0 ||
+              !(cmdp->pc_flags & PA2_CMD_FLG_OUTPUT));
+       cmdp->pc_bytes_in = ccp->pcc_bytes_in;
+       cmdp->pc_flags |= PA2_CMD_FLG_INPUT;
+
+       /*
+        * No ack for an echo.
+        */
+
+       ASSERT(cmdp->pc_cmd != PA2_KBD_CMD_ECHO ||
+              !(cmdp->pc_flags & PA2_CMD_FLG_ACK));
+       ASSERT(cmdp->pc_cmd == PA2_KBD_CMD_ECHO ||
+              (cmdp->pc_flags & PA2_CMD_FLG_ACK));
+
+out:
+       PRINT_DEBUG(PA2_DBG_FUNC_EXIT, "pa2_kbd_cmd_load\n");
+       return cmdp;
+}
+
+/*
+ * Taken given KBD cmd and prepare to run.
+ */
+
+static pa2_cmd_t *
+pa2_aux_cmd_load(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       pa2_state_t     *ssp;
+       pa2_cmd_t       *cmdp;
+       pa2_cmd_des_t   *ccp;
+       uint            i;
+
+       PRINT_DEBUG(PA2_DBG_FUNC_ENTER, "pa2_aux_cmd_load\n");
+       ccp = NULL;
+       for (i = 0; i < sizeof (pa2_aux_cmd) / sizeof (pa2_aux_cmd[0]); i++) {
+               ccp = &pa2_aux_cmd[i];
+               if (ccp->pcc_cmd == cmd) {
+                       break;
+               }
+               ccp = NULL;
+       }
+       cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+       if (ccp == NULL) {
+               pa2_error("unrecongized AUX cmd: 0x%X\n", cmd);
+               pa2_cmd_abort(fp, cmdp);
+               goto out;
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "aux cmd: %s\n", ccp->pcc_name);
+
+       ASSERT(ccp->pcc_output_dest == PA2_OUTPUT_AUX);
+       ASSERT(cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX);
+       ASSERT(cmdp->pc_cmd_desp != NULL);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(cmdp->pc_bytes_out == 1);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_PENDING_AUX);
+       ASSERT(!(cmdp->pc_flags &
+               ~(PA2_CMD_FLG_OUTPUT | PA2_CMD_FLG_PENDING_AUX)));
+       ASSERT(cmdp->pc_bytes_in == 0);
+       ASSERT(cmdp->pc_func_done != NULL);
+       /*ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_ACK)); XXX */
+       ASSERT(cmdp->pc_aux_seq == 0);
+       ASSERT(cmdp->pc_func_read == NULL);
+       ASSERT(cmdp->pc_aux_func_done == NULL);
+       ASSERT(cmdp->pc_aux_func_write == NULL);
+       ASSERT(cmdp->pc_aux_func_read == NULL);
+       ASSERT(cmdp->pc_aux_p28 == NULL);
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_CTRL));
+       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_AUX));
+
+       /*
+        * Clear any bad state that may be present due to a bug in this file,
+        * or a bad guest.
+        *
+        * Output flag always set, as this cmd byte is written as data.
+        */
+
+       cmdp->pc_aux_cmd = ccp->pcc_cmd;
+       cmdp->pc_aux_cmd_desp = ccp;
+       cmdp->pc_counter = 0;
+       cmdp->pc_active = fp->pp_active[PA2_INDEX_AUX];
+       cmdp->pc_func_cmd = ccp->pcc_func_cmd;
+       cmdp->pc_func_read = NULL;
+       cmdp->pc_func_write = NULL;
+       cmdp->pc_flags &= (PA2_CMD_FLG_OUTPUT | PA2_CMD_FLG_PENDING_AUX);
+       cmdp->pc_flags |= PA2_CMD_FLG_ACK | PA2_CMD_FLG_INPUT |
+                         PA2_CMD_FLG_OUTPUT;
+       cmdp->pc_flags |= PA2_CMD_FLG_AUX;
+       cmdp->pc_flags |= ccp->pcc_flags;
+       cmdp->pc_bytes_in = ccp->pcc_bytes_in;
+       cmdp->pc_aux_seq = ccp->pcc_seq;
+       cmdp->pc_aux_func_done = ccp->pcc_func_done;
+       cmdp->pc_aux_func_write = ccp->pcc_func_write;
+       cmdp->pc_aux_func_read = ccp->pcc_func_read;
+       cmdp->pc_aux_p28 = NULL;
+
+       ASSERT(cmdp->pc_aux_seq != 0);
+
+       /*
+        * Could test for this later, in the completion handlers? XXX
+        * Do this in the cmd function. XXX
+        */
+
+       /*
+        * If not part of the preamble (PA2_AUX_CMD_RES_SET), nor an actual
+        * "special" command, then clear any leftovers around special
+        * command handling/reconginisation. XXX sp
+        */
+
+       ssp = &fp->pp_state_cur;
+       if (cmdp->pc_aux_cmd != PA2_AUX_CMD_RES_SET &&
+           cmdp->pc_aux_cmd != PA2_AUX_CMD_STATUS_GET &&
+           cmdp->pc_aux_cmd != PA2_AUX_CMD_SAMPLE_SET) {
+               ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL));
+               PRINT_DEBUG(PA2_DBG_LOW, "aux_cmd_load: Clearing special\n");
+               cmdp->pc_aux_res_set_seq = 0;
+               cmdp->pc_aux_special_byte = 0;
+       }
+
+out:
+       PRINT_DEBUG(PA2_DBG_FUNC_EXIT, "pa2_aux_cmd_load\n");
+       return cmdp;
+}
+
+/*
+ * Load a cmd directed at the controller.
+ */
+
+static pa2_cmd_t *
+pa2_ctl_cmd_load(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       pa2_cmd_t       *cmdp;
+       pa2_cmd_des_t   *ccp;
+       pa2_dest_t      index;
+       uint            i;
+
+       /*
+        * For now, dumb linear search.  Will speed up later. XXX
+        */
+
+       ccp = NULL;
+       for (i = 0; i < sizeof (pa2_ctl_cmd) / sizeof (pa2_ctl_cmd[0]); i++) {
+               ccp = &pa2_ctl_cmd[i];
+               if (ccp->pcc_cmd != cmd) {
+                       ccp = NULL;
+                       continue;
+               }
+               break;
+       }
+       if (ccp == NULL) {
+               pa2_error("ctl_cmd_load: bad cmd: 0x%X\n", cmd);
+               /* abort any current cmds? XXX */
+               cmdp = NULL;
+               goto out;
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "ctl cmd: %s\n", ccp->pcc_name);
+
+       /*
+        * What type of output, if any, does this command generate?
+        */
+
+       index = ccp->pcc_output_dest;
+       cmdp = &fp->pp_cmds[index];
+       ASSERT(cmdp->pc_cmd == 0);
+       ASSERT(cmdp->pc_cmd_desp == 0);
+       ASSERT(cmdp->pc_counter == 0);
+       ASSERT(cmdp->pc_bytes_in == 0);
+       ASSERT(cmdp->pc_bytes_out == 0);
+       if (ccp->pcc_cmd != PA2_CTL_CMD_WRITE_AUX) {
+               ASSERT(cmdp->pc_flags == 0);
+               if (cmdp->pc_flags != 0) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "pc_flags: 0x%X\n", cmdp->pc_flags);
+               }
+       }
+       ASSERT(cmdp->pc_func_done == NULL);
+       ASSERT(cmdp->pc_func_cmd == NULL);
+       ASSERT(cmdp->pc_func_write == NULL);
+       ASSERT(cmdp->pc_func_read == NULL);
+       ASSERT(cmdp->pc_aux_p28 == NULL);
+
+       if (ccp->pcc_cmd == PA2_CTL_CMD_WRITE_AUX) {
+#if    0
+               ASSERT(!(cmdp->pc_flags & ~PA2_CMD_FLG_AUX_BYTE));
+#endif
+       } else {
+               ASSERT(cmdp->pc_flags == 0);
+       }
+
+       /*
+        * If there is a cmd in progress, then need to abort it.
+        * The setting of cmdp->* and fp->* members is done such that it
+        * assumes little that the previous cmd left everything in a sane
+        * state.  This helps to workaround bugs in the code....(which are
+        * hopefully highlighted, in DEBUG mode, by the above ASSERT()s).
+        */
+
+       if (cmdp->pc_cmd != 0) {
+               pa2_cmd_abort(fp, cmdp);
+       }
+
+       cmdp->pc_cmd = ccp->pcc_cmd;
+       cmdp->pc_cmd_desp = ccp;
+       cmdp->pc_counter = 0;
+
+       /*
+        * May be part of a sequence, so ensure only unwanted flags are cleared.
+        */
+
+       if (cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX) {
+               cmdp->pc_flags &= (PA2_CMD_FLG_SPECIAL | PA2_CMD_FLG_AUX_BYTE);
+       } else {
+               cmdp->pc_flags = 0;
+       }
+       cmdp->pc_flags |= ccp->pcc_flags;
+       cmdp->pc_bytes_in = ccp->pcc_bytes_in;
+       cmdp->pc_bytes_out = ccp->pcc_bytes_out;
+       cmdp->pc_func_done = ccp->pcc_func_done;
+       cmdp->pc_func_cmd = ccp->pcc_func_cmd;
+       cmdp->pc_func_write = ccp->pcc_func_write;
+       cmdp->pc_func_read = ccp->pcc_func_read;
+       cmdp->pc_aux_p28 = NULL;
+
+       /*
+        * It is incorrect to set pc_active, but leave for now. XXX
+        */
+
+       cmdp->pc_active = 1;            /* XXX */
+       if (fp->pp_active[PA2_INDEX_AUX] == 0 &&
+           fp->pp_active[PA2_INDEX_KBD] == 0) {
+               cmdp->pc_active = 0;
+       }
+
+       if (index == PA2_OUTPUT_AUX) {
+               if (!(cmdp->pc_flags & PA2_CMD_FLG_AUX_BYTE)) {
+                       cmdp->pc_active = fp->pp_active[PA2_INDEX_AUX];
+               }
+       }
+
+       /*
+        * If output is "kbd", or the cmd is AUX_OBUF, then the ctrl flag
+        * should be set.
+        */
+
+       ASSERT(!(index == PA2_OUTPUT_KBD ||
+              cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX_OBUF) ||
+              (cmdp->pc_flags & PA2_CMD_FLG_CTRL));
+       if (cmdp->pc_bytes_out != 0) {
+               cmdp->pc_flags |= PA2_CMD_FLG_OUTPUT;
+       }
+       if (cmdp->pc_bytes_in != 0) {
+               cmdp->pc_flags |= PA2_CMD_FLG_INPUT;
+       }
+
+out:
+       return cmdp;
+}
+
+/*
+ * Run input through current command.
+ * Consume any expected ACK/input, and complete cmd if no more input/output
+ * expected.
+ */
+
+int
+pa2_cmd_process(
+       pa2_t           *fp,
+       pa2_cmd_t       *cmdp,
+       uint8_t         status,
+       uint8_t         data)
+{
+       int             ret;
+
+       /*
+        * Expecting ack?
+        */
+
+       PRINT_DEBUG_ENTER(": 0x%X 0x%X\n", status, data);
+       if ((cmdp->pc_flags & PA2_CMD_FLG_ACK) && data == PA2_AUX_REPLY_ACK) {
+               cmdp->pc_flags &= ~PA2_CMD_FLG_ACK;
+               PRINT_DEBUG(PA2_DBG_LOW,
+                           "cmd_process: consumed expected ack\n");
+       } else if (cmdp->pc_flags & PA2_CMD_FLG_ACK) {
+               cmdp->pc_flags &= ~PA2_CMD_FLG_ACK;
+               pa2_error("cmd_process: didn't receive expected ack: 0x%X\n",
+                           data);
+       } else if (cmdp->pc_flags & PA2_CMD_FLG_INPUT) {
+               /*
+                * Should sort this out; ACK isn't countered as an input
+                * byte. XXXX  This makes the code uglier than it needs to be.
+                */
+
+               ASSERT(cmdp->pc_bytes_in != 0);
+               ASSERT(cmdp->pc_cmd != 0);
+               if (cmdp->pc_bytes_in == 0) {
+                       pa2_error("cmd_process: input marked but zero\n");
+               } else {
+                       cmdp->pc_bytes_in--;
+               }
+       }
+
+       /*
+        * Notify of the read.
+        */
+
+       ret = 1;
+       if (cmdp == &fp->pp_cmds[PA2_OUTPUT_AUX]) {
+               ASSERT(cmdp->pc_aux_func_read != NULL);
+               if (cmdp->pc_aux_func_read != NULL) {
+                       ret = cmdp->pc_aux_func_read(fp, cmdp, data);
+               }
+       } else {
+               ASSERT(cmdp == &fp->pp_cmds[PA2_OUTPUT_KBD]);
+               ASSERT(cmdp->pc_func_read != NULL);
+               if (cmdp->pc_func_read != NULL) {
+                       ret = cmdp->pc_func_read(fp, cmdp, data);
+               }
+       }
+
+       /*
+        * Has the input completed?
+        */
+
+       if ((cmdp->pc_flags & PA2_CMD_FLG_INPUT) && cmdp->pc_bytes_in == 0) {
+               cmdp->pc_flags &= ~PA2_CMD_FLG_INPUT;
+               PRINT_DEBUG(PA2_DBG_HIGH,
+                           "cmd_process: cmd input completed 0x%X\n",
+                           cmdp->pc_flags);
+
+               if ((cmdp->pc_flags & PA2_CMD_FLG_AUX) &&
+                   !(cmdp->pc_flags & PA2_CMD_FLG_WR_ACTIVE)) {
+                       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_OUTPUT));
+               }
+
+               /*
+                * If no output required, then this command is now completed.
+                * Note; for when not active, this will be running from the
+                * intial write and as this initial write is still in
+                * progress (haven't yet returned) do not process the
+                * completion here, but back in the caller.
+                */
+
+               if (!(cmdp->pc_flags & (PA2_CMD_FLG_OUTPUT |
+                                       PA2_CMD_FLG_WR_ACTIVE))) {
+                       pa2_cmd_done(fp, cmdp);
+               }
+       }
+       return ret;
+}
+
+/*
+ * Read interrupt records, returning the number read.
+ * Can be called from our interrupt handler, or when out of records reading
+ * the status or data ports.
+ * "num" is the number of records to read - 0 means all.
+ */
+
+/*
+ * Only needed for debug. XXX
+ */
+
+static void
+pa2_aux_data(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_aux_data_t  *ap;
+
+       PRINT_DEBUG(PA2_DBG_MID, "pa2_aux_data: 0x%X\n", data);
+
+       ssp = &fp->pp_state_cur;        
+       sp = &ssp->pss_synaptic;
+       ap = &fp->pp_aux_data;
+       switch (ap->pad_format) {
+       case PA2_AUX_FMT_DEFAULT:
+               ASSERT(!(sp->ps_mode_byte & PA2_SYNAP_BIT_ABS));
+               switch (ap->pad_counter++) {
+               case 0:
+                       if (data & 0x08) {
+                               ap->pad_byte1 = data;
+                       } else {
+                               ap->pad_counter = 0;
+                               ASSERT(0);
+                       }
+                       break;
+
+               case 1:
+                       ap->pad_xdelta = data;
+                       break;
+
+               case 2:
+                       ap->pad_counter = 0;
+                       ap->pad_ydelta = data;
+               }
+               break;
+
+       case PA2_AUX_FMT_ABS:
+               ASSERT(sp->ps_mode_byte & PA2_SYNAP_BIT_ABS);
+               ASSERT(!(sp->ps_mode_byte & PA2_SYNAP_BIT_WMODE));
+               switch (ap->pad_counter++) {
+               case 0:
+maybe1:
+                       if (!(data & 0x80) || (data & 0x48)) {
+                               ap->pad_counter = 0;
+                               ASSERT(0);
+                               break;
+                       }
+                       ap->pad_counter++;
+                       ap->pad_byte1 = data;
+                       break;
+               case 1:
+                       ap->pad_x &= ~0xF00;
+                       ap->pad_x |= ((data & 0x0F) << 8);
+                       ap->pad_y &= ~0xF00;
+                       ap->pad_y |= ((data & 0xF0) << 4);
+                       break;
+
+               case 2:
+                       ap->pad_z = data;
+                       break;
+
+               case 3:
+                       if ((data & 0xC0) != 0xC0 || (data & 0x08)) {
+                               ap->pad_counter = 0;
+                               goto maybe1;
+                       }
+                       ap->pad_x &= ~(1 << 12);
+                       ap->pad_x |= (data & 0x10) << 8;
+                       ap->pad_y &= ~(1 << 12);
+                       ap->pad_y |= (data & 0x20) << 7;
+                       break;
+
+               case 4:
+                       ap->pad_x &= ~0xFF;
+                       ap->pad_x |= data;
+                       break;
+
+               case 5:
+                       ap->pad_y &= ~0xFF;
+                       ap->pad_y |= data;
+                       ap->pad_counter = 0;
+                       break;
+               }
+               break;
+
+       case PA2_AUX_FMT_ABS_WMODE:
+               ASSERT(sp->ps_mode_byte & PA2_SYNAP_BIT_ABS);
+               ASSERT(sp->ps_mode_byte & PA2_SYNAP_BIT_WMODE);
+               switch (ap->pad_counter++) {
+               case 0:
+maybe2:
+                       if (!(data & 0x80) || (data & 0x48)) {
+                               ap->pad_counter = 0;
+                               ASSERT(0);
+                               break;
+                       }
+                       ap->pad_byte1 = data;
+                       ap->pad_w &= ~0xE;
+                       ap->pad_w |= (data & 0xC) >> 2;
+                       ap->pad_w |= (data & 0x4) >> 1;
+                       break;
+
+               case 1:
+                       ap->pad_x &= ~0xF00;
+                       ap->pad_x |= ((data & 0x0F) << 8);
+                       ap->pad_y &= ~0xF00;
+                       ap->pad_y |= ((data & 0xF0) << 4);
+                       break;
+
+               case 2:
+                       ap->pad_z = data;
+                       break;
+
+               case 3:
+                       if ((data & 0xC0) != 0xC0 || (data & 0x08)) {
+                               ap->pad_counter = 0;
+                               goto maybe2;
+                       }
+                       ap->pad_x &= ~(1 << 12);
+                       ap->pad_x |= (data & 0x10) << 8;
+                       ap->pad_y &= ~(1 << 12);
+                       ap->pad_y |= (data & 0x20) << 7;
+                       ap->pad_w &= ~0x01;
+                       ap->pad_w |= (data & 0x4) >> 2;
+                       break;
+
+               case 4:
+                       ap->pad_x &= ~0xFF;
+                       ap->pad_x |= data;
+                       break;
+
+               case 5:
+                       ap->pad_y &= ~0xFF;
+                       ap->pad_y |= data;
+                       ap->pad_counter = 0;
+                       break;
+               }
+               break;
+       }
+       return;
+}
+
+/*
+ * Data from KBD; key makes and breaks.
+ * Returns whether to pass the make/break to the guest.  1 = yes, 0 = no
+ */
+
+static uint
+pa2_kbd_data(
+       pa2_t           *fp,
+       uint            data)
+{
+       uint8_t         esc;
+       uint            pass_on;
+
+       pass_on = 1;
+       switch (data) {
+       case 0xE0:
+               fp->pp_key_esc = 1;
+               break;
+
+               /* need to handle E1, etc XXX */
+
+       default:
+               esc = fp->pp_key_esc;
+               if (fp->pp_key_esc) {
+                       fp->pp_key_esc = 0;
+                       break;
+               }
+               if (data & 0x80) {
+                       /* break */
+                       fp->pp_keystate[esc][data & 0x7F] = 0;
+                       break;
+               }
+               /* make */
+               if (fp->pp_keystate[esc][data] != 1) {
+                       fp->pp_keystate[esc][data] = 1;
+                       if (pa2_binding_search(fp, data) != 0) {
+                               pass_on = 0;
+                       }
+               }
+               break;
+       }
+       return pass_on;
+}
+
+/*
+ * Pull a record out of the kernel for given device (index).
+ * Can be called when the kernel notifies there is data available, or when
+ * reading status/data ports and current queue is empty.
+ * If num is zero, reads all available records from kernel, otherwise reads
+ * up to 'num' records.
+ * Returns the number of records read, not including any 'forced' record (a
+ * forced recored being one with status only - no data available for given
+ * device).
+ */
+
+static uint
+pa2_irq_read(
+       pa2_t           *fp,
+       int             index,
+       uint            num,
+       uint8_t         *statusp)
+{
+       static uint     rate_limit;
+       pa2_cmd_t       *cmdp;
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       pa2_rec_hd_t    phd;
+       pa2_entry_t     prec[16];
+       pa2_entry_t     *precp;
+       uint8_t         byte;
+       uint            count;
+       uint            next;
+       uint            i;
+       int             fd;
+       int             ret;
+
+       ASSERT(index == PA2_INDEX_AUX || index == PA2_INDEX_KBD);
+       ASSERT(fp->pp_active[index] == 1);
+       PRINT_DEBUG(PA2_DBG_HIGH, "irq_read: %d\n", index);
+       if (num == 0) {
+               num = UINT_MAX;
+       }
+
+       count = 0;
+       phd.ph_records = &prec[0];
+       do {
+               phd.ph_rec_size = PA2_MIN(sizeof (prec) / sizeof (prec[0]),
+                                         num);
+               phd.ph_rec_num = 0;
+               phd.ph_focus = 0;
+               phd.ph_overflow = 0;
+               phd.ph_forced = 0;
+               ret = ioctl(fp->pp_fd[index], PA2_IOCTL_RD_RECORD, &phd);
+               if (ret == -1) {
+                       break;
+               }
+               ASSERT(phd.ph_overflow == 0);
+               if (phd.ph_overflow != 0) {
+                       pa2_error("Kernel reports buffer overflow!\n");
+               }
+
+               /*
+                * Is the kernel driver telling us to lose
+                * focus?
+                */
+
+               if (phd.ph_focus != 0 && fp->pp_losing_focus[index] == 0) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Told to lose focus\n");
+                       fp->pp_losing_focus[index] = 1;
+               }
+               precp = &prec[0];
+               for (i = 0; i < phd.ph_rec_num; i++, precp++) {
+
+                       /*
+                        * If there are no queued records in the kernel, then
+                        * the kernel driver reads the status register.  If
+                        * it finds data, then it reads that too, otherwise
+                        * just the status is returned in a "forced" record
+                        * that is not appened to a queue (as it has no data)
+                        * This allows guests to poll the output/input status
+                        * bits.
+                        */
+
+                       if (!(precp->pe_status & PA2_CTL_STAT_OBF)) {
+                               ASSERT(phd.ph_forced == 1);
+                               if (statusp != NULL) {
+                                       *statusp = precp->pe_status;
+                               }
+                               continue;
+                       }
+                       PRINT_DEBUG(PA2_DBG_MID, "irq_read: 0x%X 0x%X\n",
+                                   precp->pe_status, precp->pe_data);
+
+                       /*
+                        * Which command does this data go with?
+                        * If we're reading from the KBD, then only expect the
+                        * OBF flag set.  From AUX, expect both OBF and AUX_OBF.
+                        */
+
+                       if (index == PA2_INDEX_KBD) {
+                               ASSERT(!(precp->pe_status &
+                                        PA2_CTL_STAT_AUX_OBF));
+                               cmdp = &fp->pp_cmds[PA2_OUTPUT_KBD];
+                       } else {
+                               ASSERT(precp->pe_status & PA2_CTL_STAT_AUX_OBF);
+                               cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+                       }
+
+                       /*
+                        * If there is an active cmd for this input, then
+                        * run the data through it.
+                        */
+
+                       if (cmdp->pc_cmd != 0) {
+                               pa2_cmd_process(fp, cmdp,
+                                       precp->pe_status, precp->pe_data);
+                       } else {
+                               cmdp = NULL;
+                       }
+
+                       /*
+                        * If no active cmd, then the data is a make/break
+                        * code for the KBD, or positional data for the AUX.
+                        * For KBD, might be a key combination for special
+                        * XC action.  In this case it is not passed through
+                        * to the VM.
+                        */
+
+                       if (cmdp == NULL && index == PA2_INDEX_KBD) {
+                               if (pa2_kbd_data(fp, precp->pe_data) == 0) {
+                                       continue;
+                               }
+                       }
+                       if (cmdp == NULL && index == PA2_INDEX_AUX) {
+                               pa2_aux_data(fp, precp->pe_data);
+                       }
+                       php = &fp->pp_irqbuf[index];
+                       next = (php->ph_end + 1) & PA2_IRQBUF_MASK;
+                       if (next == php->ph_start) {
+                               /* rate limit message */
+                               if ((rate_limit++ & 0x1FF) == 0) {
+                                       pa2_error("Overflow!!!\n");
+                               }
+                               php->ph_overflow++;
+                               continue;
+                       }
+                       pdp = &php->ph_irqbuf[php->ph_end];
+                       pdp->pe_data = precp->pe_data;
+                       pdp->pe_status = precp->pe_status;
+                       php->ph_end = next;
+               }
+
+               /*
+                * If the record returned was a "forced" record (no data
+                * indicated in the status) then we're emptied the kernel's
+                * buffer and should read no more.
+                */
+
+               if (phd.ph_forced) {
+                       ASSERT(phd.ph_rec_num == 1);
+                       break;
+               }
+               ASSERT(num >= phd.ph_rec_num);
+               num -= phd.ph_rec_num;
+               count += phd.ph_rec_num;
+               if (phd.ph_rec_size != phd.ph_rec_num) {
+                       break;
+               }
+       } while (num != 0);
+
+       /*
+        * If no more data, the current cmd is complete, and we're told to
+        * lose focus, then do it.
+        */
+
+       if (fp->pp_losing_focus[index] == 1 &&  /* XXX */
+           fp->pp_cmds[index].pc_cmd == 0 &&
+           !(fp->pp_flags & PA2_FLG_KBD_IRQ) &&
+           !(fp->pp_flags & PA2_FLG_AUX_IRQ)) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Telling kernel we're "
+                               "giving up focus\n");
+               byte = 0;
+               if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_GRAB, &byte) == -1) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "ioctl error\n");
+               }
+               qemu_set_fd_handler(fp->pp_fd[0], NULL, NULL, fp);
+               qemu_set_fd_handler(fp->pp_fd[1], NULL, NULL, fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "Told kernel we're "
+                               "given up focus\n");
+               fp->pp_active[0] = 0;
+               fp->pp_active[1] = 0;
+       }
+       pa2_irq_raise(fp);
+       return count;
+}
+
+/*
+ * Report the last unreported status read.
+ * This is used to reduce the large number of status reads from a guest
+ * (often, polling for data or waiting for input buf bit to clear).  Only
+ * used/needed when compiled debug.
+ */
+
+static void
+pa2_report_last_status(
+       pa2_t           *fp)
+{
+#if    defined(PA2_DEBUG)
+       pa2_state_t     *ssp;
+
+       if (!(fp->pp_flags & PA2_FLG_UNREP_STATUS)) {
+               goto out;
+       }
+       fp->pp_flags &= ~PA2_FLG_UNREP_STATUS;
+       ssp = &fp->pp_state_cur;
+       if (fp->pp_status_count == 1) {
+               PRINT_DEBUG(PA2_DBG_MID, "lread_status: 0x%X\n",
+                           ssp->pss_status_last);
+       } else {
+               /*
+                * A high number of status reg reads by a guest often
+                * means we've got something wrong...
+                */
+
+               ASSERT(fp->pp_status_count < 200);
+               PRINT_DEBUG(PA2_DBG_MID,
+                           "lread_status: 0x%X (%d times)\n",
+                           ssp->pss_status_last, fp->pp_status_count);
+               fp->pp_status_count = 1;
+       }
+
+out:
+       return;
+#else
+       return;
+#endif /* PA2_DEBUG */
+}
+
+static uint32_t
+pa2_status_read(
+       pa2_t           *fp,
+       int             index)
+{
+       pa2_state_t     *ssp;
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       int             ret;
+       uint            i;
+       uint8_t         status;
+       uint8_t         byte;
+
+       PRINT_DEBUG_ENTER("\n");
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX ||
+              index == PA2_INDEX_WILD || index == PA2_INDEX_RAW);
+
+       /*
+        * If there is a stored record, then return that.  Otherwise,
+        * read directly from the hardware.
+        */
+
+       status = 0;
+       ssp = &fp->pp_state_cur;
+       do {
+               /*
+                * If there is a posted interrupt, then return the status
+                * from the corresponding queue.
+                * If this is a "raw" read, then only interested in the status
+                * of the actual hardware.  ie. when testing if the input buffer
+                * is empty before sending the next byte when running cmds
+                * internally (pa2.*_do() functions).
+                */
+
+               php = NULL;
+               if (index != PA2_INDEX_RAW) {
+                       if (index != PA2_INDEX_AUX &&
+                           (fp->pp_flags & PA2_FLG_KBD_IRQ)) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+                               ASSERT(php->ph_start != php->ph_end);
+                               /* ASSERT(fp->pp_active[PA2_INDEX_KBD] == 1); */
+                       }
+                       if (index != PA2_INDEX_KBD &&
+                           (fp->pp_flags & PA2_FLG_AUX_IRQ)) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+                               ASSERT(php->ph_start != php->ph_end);
+                               /* ASSERT(fp->pp_active[PA2_INDEX_AUX] == 1); */
+                       }
+                       if (php != NULL && php->ph_start != php->ph_end) {
+                               break;
+                       }
+
+                       /*
+                        * No interrupt.  Return status for any queue that
+                        * has data.
+                        */
+
+                       for (i = 0; i < 2; i++) {
+                               if (i != index && index != PA2_INDEX_WILD) {
+                                       continue;
+                               }
+                               php = &fp->pp_irqbuf[i];
+                               if (php->ph_start == php->ph_end) {
+                                       continue;
+                               }
+                               break;
+                       }
+                       if (i != 2) {
+                               break;
+                       }
+               }
+
+               /*
+                * All buffers are empty, or this is a raw read.
+                * Read a record, incase there is one waiting in the kernel
+                * driver.
+                */
+
+               php = NULL;
+               for (i = 0; i < 2; i++) {
+                       if (fp->pp_active[i] == 0) {
+                               continue;
+                       }
+                       if (pa2_irq_read(fp, i, 1, &status) == 1) {
+                               break;
+                       }
+               }
+
+               /*
+                * If read a record with data, then return it's associated
+                * status.
+                */
+
+               if (i != 2) {
+                       continue;
+               }
+               break;
+       } while (1);
+       pdp = NULL;
+       if (php != NULL) {
+               if (php->ph_start != php->ph_end) {
+                       pdp = &php->ph_irqbuf[php->ph_start];
+                       status = pdp->pe_status;
+                       goto out;
+               }
+       }
+
+       /*
+        * If have no record to return a status from, and all devices are
+        * non-active, then return our calculated status.  As no data, assert
+        * outbuf flags are clear.
+        */
+
+       if (pdp == NULL && fp->pp_active[PA2_INDEX_KBD] == 0 &&
+           fp->pp_active[PA2_INDEX_AUX] == 0) {
+               status = ssp->pss_status;
+               ASSERT(!(status & (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)));
+               goto out;
+       }
+
+
+out:
+#if    defined(PA2_DEBUG)
+       /*
+        * Combine debug messages to avoid the many (hundreds) generated
+        * while waiting for a RESET to complete, etc.
+        */
+
+       if (fp->pp_flags & PA2_FLG_UNREP_STATUS) {
+               if (status == ssp->pss_status_last &&
+                   fp->pp_status_count < 100) {
+                       fp->pp_status_count++;
+               } else {
+                       ASSERT(fp->pp_status_count != 0);
+                       ASSERT(status != ssp->pss_status_last ||
+                              fp->pp_status_count == 100);
+                       if (fp->pp_status_count == 1) {
+                               PRINT_DEBUG(PA2_DBG_LOW,
+                                           "read_status: 0x%X\n",
+                                           ssp->pss_status_last);
+                       } else {
+                               PRINT_DEBUG(PA2_DBG_LOW,
+                                           "read_status: 0x%X (%d times)\n",
+                                           ssp->pss_status_last,
+                                           fp->pp_status_count);
+                       }
+                       fp->pp_status_count = 1;
+               }
+       } else {
+               fp->pp_flags |= PA2_FLG_UNREP_STATUS;
+               fp->pp_status_count = 1;
+       }
+#endif /* PA2_DEBUG */
+
+       ssp->pss_status_last = status;
+       PRINT_DEBUG_EXIT("\n");
+       return status;
+}
+
+/*
+ * Function called when guest reads the status register.
+ */
+
+static uint32_t
+pa2_guest_status_read(
+       void            *opq,
+       uint32_t        addr)
+{
+       pa2_t           *fp;
+
+       fp = (pa2_t *)opq;
+       ASSERT(addr == PA2_STATUS_PORT);
+       return pa2_status_read(fp, PA2_INDEX_WILD);
+}
+
+static uint32_t
+pa2_data_read(
+       pa2_t           *fp,
+       int             index)
+{
+       pa2_state_t     *ssp;
+       pa2_cmd_t       *cmdp;
+       pa2_hdata_t     *php;
+       pa2_entry_t     *pdp;
+       int             success;
+       int             ret;
+       uint32_t        data;
+       uint            i;
+       uint8_t         status;
+       uint8_t         byte;
+
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX ||
+              index == PA2_INDEX_WILD);
+       PRINT_DEBUG_ENTER("\n");
+       pa2_report_last_status(fp);
+       do {
+
+               /*
+                * Use the last status reported to the guest to determine
+                * whether it is the KBD or AUX device data the caller is
+                * expecting.
+                * If no indication in the status, then return any data we
+                * have queue - giving a preference for KBD data.
+                */
+
+               php = NULL;
+               ssp = &fp->pp_state_cur;
+               if (ssp->pss_status_last & PA2_CTL_STAT_OBF) {
+                       if (index != PA2_INDEX_AUX) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+                               if (php->ph_start == php->ph_end) {
+                                       php = NULL;
+                               }
+                       }
+                       if (php == NULL && index != PA2_INDEX_KBD &&
+                           (ssp->pss_status_last & PA2_CTL_STAT_AUX_OBF)) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+                               if (php->ph_start == php->ph_end) {
+                                       php = NULL;
+                               }
+                       }
+               }
+
+               /*
+                * If there is a pending interrupt, then return the data
+                * from the corresponding queue.
+                */
+
+               if (php == NULL) {
+                       if (index != PA2_INDEX_AUX &&
+                           (fp->pp_flags & PA2_FLG_KBD_IRQ)) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+                               ASSERT(php->ph_start != php->ph_end);
+                       } else if (index != PA2_INDEX_KBD &&
+                                  (fp->pp_flags & PA2_FLG_AUX_IRQ)) {
+                               php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+                               ASSERT(php->ph_start != php->ph_end);
+                       }
+               }
+
+               if (php && php->ph_start != php->ph_end) {
+                       pdp = &php->ph_irqbuf[php->ph_start];
+                       php->ph_start = (php->ph_start + 1) & PA2_IRQBUF_MASK;
+                       status = pdp->pe_status;
+                       data = pdp->pe_data;
+                       break;
+               }
+
+               /*
+                * We have no data.  Attempt to read a record from the
+                * kernel.
+                */
+
+               success = 0;
+               for (i = 0; i < 2; i++) {
+                       if (fp->pp_active[i] == 0) {
+                               continue;
+                       }
+                       PRINT_DEBUG(PA2_DBG_HIGH,
+                                   "read_data: pa2_irq_read()\n");
+                       if (pa2_irq_read(fp, i, 1, NULL) == 1) {
+                               success = 1;
+                       }
+               }
+               if (success != 0) {
+                       continue;
+               }
+
+               /*
+                * Currently, the above always succeeds.  When it can fail,
+                * (no data, as indicated by status register bits) we'll
+                * return the last data.
+                * Should return our calcualted data. XXXX
+                */
+
+               data = fp->pp_data_last;
+
+               /*
+                * Should only be updating this for a guest call, not an
+                * internal one. XXXX
+                */
+
+               status = ssp->pss_status_last;  /* ->pp_status? XXX */
+               break;
+       } while (1);
+       fp->pp_data_last = data;
+       PRINT_DEBUG(PA2_DBG_MID, "read_data: 0x%X (0x%X)\n", data, status);
+
+       /*
+        * Clear the data available in our s/w status register.
+        */
+
+       ssp->pss_status &= ~PA2_CTL_STAT_OBF;
+       ssp->pss_status &= ~PA2_CTL_STAT_AUX_OBF;
+       for (i = 0; i < 2; i++) {
+               php = &fp->pp_irqbuf[i];
+               if (php->ph_start == php->ph_end) {
+                       continue;
+               }
+               ssp->pss_status |= PA2_CTL_STAT_OBF;
+               if (i == 0) {
+                       break;
+               }
+               ssp->pss_status |= PA2_CTL_STAT_AUX_OBF;
+       }
+
+       /*
+        * Reading from the data register clears an asserted interrupt.
+        */
+
+       pa2_irq_lower(fp);
+
+       /*
+        * Raise an interrupt associated with the next interrupt.
+        */
+
+       pa2_irq_raise(fp);
+       PRINT_DEBUG_EXIT("\n");
+       return data;
+}
+
+static uint32_t
+pa2_guest_data_read(
+       void            *opq,
+       uint32_t        addr)
+{
+       pa2_t           *fp;
+
+       fp = (pa2_t *)opq;
+       ASSERT(addr == PA2_DATA_PORT);
+       return pa2_data_read(fp, PA2_INDEX_WILD);
+}
+
+static void
+pa2_data_write(
+       pa2_t           *fp,
+       uint32_t        data)
+{
+       pa2_cmd_t       *cmdp;
+       uint8_t         byte;
+       pa2_dest_t      index;
+       int             ret;
+
+       pa2_report_last_status(fp);
+       PRINT_DEBUG(PA2_DBG_HIGH, "pa2_data_write: 0x%X\n", data);
+
+       /*
+        * Cases;
+        *      - Controller command's data.
+        *      - Controller command, but the data is destined for the AUX
+        *        device (CCMD_WRITE_AUX or PA2_CTL_CMD_WRITE_AUX_OBUF).
+        *      - Data for the AUX device (after one of the above cmds).
+        *        (really, the same case as the first).
+        *      - No current controller command, so this write is a keyboard
+        *        command.
+        *      - This is a data write for the keyboard after a keyboard cmd
+        *        (really, the same case as the first).
+        */
+
+       cmdp = NULL;
+       for (index = 0; index < PA2_OUTPUT_MAX; index++) {
+               if (fp->pp_cmds[index].pc_cmd == 0) {
+                       continue;
+               }
+               if (fp->pp_cmds[index].pc_flags & PA2_CMD_FLG_OUTPUT) {
+                       ASSERT(cmdp == NULL);
+                       cmdp = &fp->pp_cmds[index];
+#if    !defined(PA2_DEBUG)
+                       break;
+#endif
+               }
+       }
+
+       /*
+        * If no command expecting bytes, then this is a KBD cmd.
+        * Currently, an unknown cmd is ignored.  It should be NAK'ed. XXX
+        */
+
+       byte = data;
+       fp->pp_state_cur.pss_status &= ~PA2_STAT_CMD;
+       if (cmdp == NULL) {
+               cmdp = pa2_kbd_cmd_load(fp, byte);
+               if (cmdp != NULL) {
+                       ASSERT(cmdp->pc_func_cmd != NULL);
+                       ret = cmdp->pc_func_cmd(fp, cmdp);
+                       goto process;
+               }
+               goto out;
+       }
+
+       /*
+        * Is this output for the AUX device?
+        */
+
+       if (cmdp->pc_cmd == PA2_CTL_CMD_WRITE_AUX) {
+               ASSERT(cmdp == &fp->pp_cmds[PA2_OUTPUT_AUX]);
+
+               /*
+                * Currently, an unknown cmd is ignored.  It should be
+                * NAK'ed. XXX
+                */
+
+               if (cmdp->pc_aux_cmd == 0) {
+                       cmdp = pa2_aux_cmd_load(fp, byte);
+                       if (cmdp == NULL) {
+                               goto out;
+                       }
+                       ASSERT(cmdp->pc_func_cmd != NULL);
+                       cmdp->pc_flags |= PA2_CMD_FLG_WR_ACTIVE;
+                       ret = cmdp->pc_func_cmd(fp, cmdp);
+                       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_WR_ACTIVE);
+                       cmdp->pc_flags &= ~PA2_CMD_FLG_WR_ACTIVE;
+                       goto process;
+               }
+
+               /*
+                * If this is not a requested write to the AUX, then is
+                * an argumemnt to a previous AUX cmd.
+                * This looks ugly. XXX
+                * This is where the func's data write should be called. XXX
+                */
+
+               ASSERT(cmdp->pc_flags & PA2_CMD_FLG_AUX_BYTE);
+               ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_ACK));
+               ASSERT(cmdp->pc_aux_seq != 0);
+               cmdp->pc_flags |= PA2_CMD_FLG_ACK;
+               cmdp->pc_flags |= PA2_CMD_FLG_INPUT;
+               ASSERT(cmdp->pc_aux_func_write != NULL);
+               if (cmdp->pc_aux_func_write != NULL) {
+                       ret = cmdp->pc_aux_func_write(fp, cmdp, byte);
+               }
+       } else {
+               if (!(cmdp->pc_flags & PA2_CMD_FLG_CTRL)) {
+                       ASSERT(!(cmdp->pc_flags & PA2_CMD_FLG_ACK));
+                       cmdp->pc_flags |= PA2_CMD_FLG_ACK;
+                       cmdp->pc_flags |= PA2_CMD_FLG_INPUT;
+               }
+               ASSERT(cmdp->pc_func_write != NULL);
+               if (cmdp->pc_func_write != NULL) {
+                       ret = cmdp->pc_func_write(fp, cmdp, byte);
+               }
+       }
+
+process:
+       PRINT_DEBUG(PA2_DBG_LOW, "data_write: 0x%X %d 0x%X (%d)\n",
+                   data, cmdp->pc_bytes_out, cmdp->pc_flags, (int)ret);
+
+       if (cmdp->pc_flags & PA2_CMD_FLG_OUTPUT) {
+               ASSERT(cmdp->pc_bytes_out != 0);
+               if (cmdp->pc_bytes_out != 0) {
+                       cmdp->pc_bytes_out--;
+               } else {
+                       pa2_error("data_write: len too short\n");
+               }
+       }
+
+       if ((cmdp->pc_flags & PA2_CMD_FLG_OUTPUT) &&
+            cmdp->pc_bytes_out == 0) {
+               cmdp->pc_flags &= ~PA2_CMD_FLG_OUTPUT;
+               PRINT_DEBUG(PA2_DBG_HIGH,
+                           "write_data: Out phase completed\n");
+
+               /*
+                * If no input need, then completed.
+                */
+
+               if (!(cmdp->pc_flags & (PA2_CMD_FLG_INPUT |
+                                       PA2_CMD_FLG_ACK |
+                                       PA2_CMD_FLG_WR_ACTIVE))) {
+                       ASSERT(cmdp->pc_bytes_in == 0);
+                       /* aux len assert XXX */
+                       PRINT_DEBUG(PA2_DBG_HIGH,
+                                   "data_write: cmd completed\n");
+                       pa2_cmd_done(fp, cmdp);
+               }
+       }
+
+out:
+       return;
+}
+
+static void
+pa2_guest_data_write(
+       void            *opq,
+       uint32_t        addr,
+       uint32_t        val)
+{
+       pa2_t           *fp;
+
+       fp = (pa2_t *)opq;
+       ASSERT(addr == PA2_DATA_PORT);
+       pa2_data_write(fp, val);
+       return;
+}
+
+/*
+ * Write to the command register.
+ */
+
+static void
+pa2_cmd_write(
+       pa2_t           *fp,
+       uint32_t        cmd)
+{
+       pa2_cmd_t       *cmdp;
+       int             ret;
+       uint8_t         byte;
+
+       PRINT_DEBUG(PA2_DBG_FUNC_ENTER, "pa2_cmd_write\n");
+       pa2_report_last_status(fp);
+
+       /*
+        * Found a matching command?
+        * If not, write the byte to the command register - if we're active,
+        * that way something right might happen...
+        * Hmm, probably should NACK it. XXX
+        */
+
+       ASSERT(cmd <= 0xFF);
+       byte = cmd;
+       cmdp = pa2_ctl_cmd_load(fp, byte);
+       ASSERT(cmdp != NULL);
+       if (cmdp == NULL) {
+               if (fp->pp_active[PA2_INDEX_KBD] == 1 ||
+                   fp->pp_active[PA2_INDEX_AUX] == 1) {
+                       ret = pa2_reg_cmd_write(fp, byte);
+               } else {
+                       ASSERT(0);
+               }
+               fp->pp_state_cur.pss_status &= ~PA2_STAT_CMD;
+               goto out;
+       }
+
+       /*
+        * Before calling func, mark cmd as write active.  If we're
+        * not active (pp_active) then cmd could complete in a
+        * simulated read and cmd completion would run before this
+        * returned - not what is wanted/required.
+        */
+
+       fp->pp_state_cur.pss_status |= PA2_STAT_CMD;
+       ASSERT(cmdp->pc_func_cmd != NULL);
+       cmdp->pc_flags |= PA2_CMD_FLG_WR_ACTIVE;
+       ret = cmdp->pc_func_cmd(fp, cmdp);
+       ASSERT(ret == 0);
+       ASSERT(cmdp->pc_flags & PA2_CMD_FLG_WR_ACTIVE);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_WR_ACTIVE;
+
+       /*
+        * If cmd has no outbytes, then the output stage has finished.
+        */
+
+       if (cmdp->pc_flags & PA2_CMD_FLG_OUTPUT) {
+               ASSERT(cmdp->pc_bytes_out != 0);
+               goto out;
+       }
+       ASSERT(cmdp->pc_bytes_out == 0);
+       cmdp->pc_flags &= ~PA2_CMD_FLG_OUTPUT;
+       PRINT_DEBUG(PA2_DBG_LOW,
+                   "cmd_write: output phase completed: %s: 0x%X\n",
+                   cmdp->pc_cmd_desp->pcc_name, cmdp->pc_flags);
+
+       /*
+        * If no input expected, clear cmd.  It's done.
+        */
+
+       if (!(cmdp->pc_flags & (PA2_CMD_FLG_INPUT | PA2_CMD_FLG_ACK))) {
+               ASSERT(cmdp->pc_bytes_in == 0);
+               PRINT_DEBUG(PA2_DBG_MID, "write_cmd: cmd done\n");
+               pa2_cmd_done(fp, cmdp);
+       }
+
+out:
+       PRINT_DEBUG_EXIT("\n");
+       return;
+}
+
+static void
+pa2_guest_cmd_write(
+       void            *opq,
+       uint32_t        addr,
+       uint32_t        cmd)
+{
+       pa2_t           *fp;
+
+       fp = (pa2_t *)opq;
+       ASSERT(addr == PA2_CMD_PORT);
+       pa2_cmd_write(opq, cmd);
+       return;
+}
+
+/*
+ * Interrupt from i8042.
+ */
+
+static void
+pa2_irq(
+       void            *opq)
+{
+       pa2_t           *fp;
+       unsigned int    index;
+
+       /*
+        * Read interrupt records.  If none found, then nothing else to do.
+        * Remember first free record, so can examine it later.
+        */
+
+       PRINT_DEBUG_ENTER("\n");
+       fp = &pa2_state;
+       index = (unsigned int)opq;      /* 64-bit XXX */
+       (void) pa2_irq_read(fp, index, 0, NULL);
+       PRINT_DEBUG_EXIT("\n");
+       return;
+}
+
+/*
+ * Wait for input buffer to become empty.
+ * Need to check timeouts...XXX
+ */
+
+static int
+pa2_wait_on_input_buffer(
+       pa2_t           *fp)
+{
+       uint32_t        status;
+       uint            i;
+       int             ret;
+
+       ret = 1;
+       for (i = 0; i < fp->pp_limit; i++) {
+               status = pa2_status_read(fp, PA2_INDEX_RAW);
+               if (status & PA2_CTL_STAT_IBF) {
+                       usleep(100);
+                       continue;
+               }
+               ret = 0;
+               break;
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_wait_on_output_buf(
+       pa2_t           *fp)
+{
+       uint8_t         status;
+       uint            i;
+       int             ret;
+
+       ret = 1;
+       for (i = 0; i < fp->pp_limit; i++) {
+               status = pa2_status_read(fp, PA2_INDEX_KBD);
+               if (!(status & PA2_CTL_STAT_OBF) ||
+                   (status & PA2_CTL_STAT_AUX_OBF)) {
+                       usleep(100);
+                       continue;
+               }
+               ret = 0;
+               break;
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_wait_on_aux_output_buf(
+       pa2_t           *fp)
+{
+       uint8_t         status;
+       uint            i;
+       int             ret;
+
+       ret = 1;
+       for (i = 0; i < fp->pp_limit; i++) {
+               status = pa2_status_read(fp, PA2_INDEX_AUX);
+               if ((status & (PA2_CTL_STAT_AUX_OBF | PA2_CTL_STAT_OBF)) !=
+                             (PA2_CTL_STAT_AUX_OBF | PA2_CTL_STAT_OBF)) {
+                       usleep(100);
+                       continue;
+               }
+               ret = 0;
+               break;
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_cmd_write_do(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       int             ret;
+
+       /*
+        * Send away, even if input buffer flag doesn't clear.
+        * Sending can help to get the h/w out of a bad state.
+        */
+
+       ret = pa2_wait_on_input_buffer(fp);
+       ASSERT(ret == 0);
+       pa2_cmd_write(fp, cmd);
+#if    0
+       ret |= pa2_wait_on_input_buffer(fp);
+#endif
+       return ret;
+}
+
+static int
+pa2_data_write_do(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       int             ret;
+
+       /*
+        * Send away, even if input buffer flag doesn't clear.
+        * Sending can help to get the h/w out of a bad state.
+        */
+
+       ret = pa2_wait_on_input_buffer(fp);
+       pa2_data_write(fp, data);
+       /* EXPLAIN XXXXXX */
+       ret |= pa2_wait_on_input_buffer(fp);
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_output_read_do(
+       pa2_t           *fp,
+       uint8_t         *byte)
+{
+       int             ret;
+
+       ret = pa2_wait_on_output_buf(fp);
+       if (ret == 0) {
+               *byte = pa2_data_read(fp, PA2_INDEX_KBD);
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_output_aux_read_do(
+       pa2_t           *fp,
+       uint8_t         *byte)
+{
+       int             ret;
+
+       ret = pa2_wait_on_aux_output_buf(fp);
+       if (ret == 0) {
+               *byte = pa2_data_read(fp, PA2_INDEX_AUX);
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+/*
+ * Send an AUX cmd, and consume the reply ACK.
+ * Returns 0 on success, and 1 on failure.
+ */
+
+static int
+pa2_aux_cmd_do(
+       pa2_t           *fp,
+       uint8_t         cmd)
+{
+       uint8_t         ack;
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_WRITE_AUX);
+       if (ret == 0) {
+               ret = pa2_data_write_do(fp, cmd);
+               if (ret == 0) {
+                       ret = pa2_output_aux_read_do(fp, &ack);
+                       ASSERT(ack == PA2_AUX_REPLY_ACK);
+                       if (ack != PA2_AUX_REPLY_ACK) {
+                               ret = 1;
+                       }
+               }
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+/*
+ * Send an AUX arg, and consume the reply ACK.
+ * Returns 0 on success, and 1 on failure.
+ * This really is the same as pa2_aux_cmd_do(), but kept separate so can
+ * tell (easier) why an assert is firing.
+ */
+
+static int
+pa2_aux_arg_do(
+       pa2_t           *fp,
+       uint8_t         arg)
+{
+       uint8_t         ack;
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_WRITE_AUX);
+       if (ret == 0) {
+               ret = pa2_data_write_do(fp, arg);
+               if (ret == 0) {
+                       ret = pa2_output_aux_read_do(fp, &ack);
+                       ASSERT(ack == PA2_AUX_REPLY_ACK);
+                       if (ack != PA2_AUX_REPLY_ACK) {
+                               ret = 1;
+                       }
+               }
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_disable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_KBD_DISABLE);
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_enable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_KBD_ENABLE);
+       return ret;
+}
+
+static int
+pa2_ctl_aux_disable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_AUX_DISABLE);
+       return ret;
+}
+
+static int
+pa2_ctl_aux_enable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_AUX_ENABLE);
+       return ret;
+}
+
+/*
+ * Probe for ctl version.  Only done during init, and result stored for future
+ * queries.
+ * I believe this cmd isn't implemented for most hw.  So, if we don't get a
+ * response cache this via PA2_CONST_NO_CTL_VERSION.
+ */
+
+static int
+pa2_ctl_version_get_do(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       uint8_t         byte;
+       int             ret;
+
+       ASSERT(fp->pp_init == 1);
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_VERSION_GET);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &byte);
+               scp = &fp->pp_state_const;
+               if (ret == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_VERSION);
+               } else {
+                       ASSERT(!(scp->psc_hv_flgs & PA2_HV_CONST_CTL_VERSION));
+                       ASSERT(!(scp->psc_flgs & PA2_CONST_NO_CTL_VERSION));
+                       scp->psc_flgs |= PA2_CONST_NO_CTL_VERSION;
+                       ret = 0;        /* XXX */
+               }
+       }
+       return ret;
+}
+
+/*
+ * Have the controller test itself.
+ */
+
+static int
+pa2_ctl_self_test_do(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       uint            limit;
+       uint8_t         data;
+       int             ret;
+
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       scp = &fp->pp_state_const;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_SELF_TEST);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &data);
+               if (ret == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_TEST);
+                       ASSERT(data == PA2_CTL_REPLY_SELF);
+                       if (data != PA2_CTL_REPLY_SELF) {
+                               ret = 1;
+                       }
+               }
+       }
+       fp->pp_limit = limit;
+       return ret;
+}
+
+static int
+pa2_ctl_kbd_test_do(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       int             limit;
+       uint8_t         data;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "ctl_kbd_test_do\n");
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       scp = &fp->pp_state_const;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_KBD_TEST);
+       while (ret == 0) {
+               ret = pa2_output_read_do(fp, &data);
+               if (ret == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_KBD_TEST);
+                       if (data == 0x00) {
+                               break;
+                       }
+                       pa2_error("Failed KBD test: 0x%X\n", data);
+                       ret = 1;
+               }
+               break;
+       }
+       fp->pp_limit = limit;
+       return ret;
+}
+
+static int
+pa2_ctl_reset_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_RESET);
+       return ret;
+}
+
+static int
+pa2_kbd_reset_do(
+       pa2_t           *fp)
+{
+       uint8_t         byte;
+       uint            limit;
+       uint            state;
+       int             ret;
+
+       state = 0;
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_RESET);
+       while (ret == 0 && state != 2) {
+               ret = pa2_output_read_do(fp, &byte);
+               if (ret == 0) {
+                       switch (state) {
+                       case 0:
+                               if (byte != PA2_KBD_REPLY_ACK) {
+                                       ret = 1;
+                               }
+                               break;
+
+                       case 1:
+                               if (byte != PA2_KBD_REPLY_BAT_SUCC) {
+                                       ret = 1;
+                               }
+                               break;
+                       }
+                       state++;
+               }
+       }
+       fp->pp_limit = limit;
+       ASSERT(ret == 0);
+       return ret; 
+}
+
+/*
+ * Reset keyboard.
+ */
+
+static int
+pa2_kbd_reset_disable_do(
+       pa2_t           *fp)
+{
+       uint8_t         ack;
+       uint            limit;
+       uint            state;
+       int             ret;
+
+       state = 0;
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_RESET_DISABLE);
+       while (ret == 0) {
+               ret = pa2_output_read_do(fp, &ack);
+               if (ret == 0) {
+                       if (ack == PA2_KBD_REPLY_ACK) {
+                               break;
+                       }
+                       ret = 1;
+               }
+               break;
+       }
+       fp->pp_limit = limit;
+       ASSERT(ret == 0);
+       return ret; 
+}
+
+static int
+pa2_kbd_reset_enable_do(
+       pa2_t           *fp)
+{
+       uint8_t         ack;
+       uint            limit;
+       uint            state;
+       int             ret;
+
+       state = 0;
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_RESET_ENABLE);
+       while (ret == 0) {
+               ret = pa2_output_read_do(fp, &ack);
+               if (ret == 0) {
+                       if (ack == PA2_KBD_REPLY_ACK) {
+                               break;
+                       }
+                       ret = 1;
+               }
+               break;
+       }
+       fp->pp_limit = limit;
+       ASSERT(ret == 0);
+       return ret; 
+}
+
+static int
+pa2_aux_default_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_DEFAULT_SET);
+       ASSERT(ret == 0);
+       return ret; 
+}
+
+/*
+ * Test the controller can talk to the keyboard.
+ */
+
+/*
+ * Set LEDS to given state.
+ */
+
+static int
+pa2_kbd_leds_set_do(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       uint8_t         byte;
+       int             state;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_leds_set_do\n");
+       state = 0;
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_LEDS_SET);
+       while (ret == 0 && state != 2) {
+               ret = pa2_output_read_do(fp, &byte);
+               if (ret != 0) {
+                       break;
+               }
+               switch (state) {
+               case 0:         /* ACK of cmd */
+                       ASSERT(byte == PA2_KBD_REPLY_ACK);
+                       ret = pa2_data_write_do(fp, data);
+                       break;
+               case 1:         /* ACK of data */
+                       ASSERT(byte == PA2_KBD_REPLY_ACK);
+                       break;
+               }
+               if (byte != PA2_AUX_REPLY_ACK) {
+                       ret = 1;
+               }
+               state++;
+       }
+       return ret;
+}
+
+/*
+ * Set key rate to given value.
+ */
+
+static int
+pa2_kbd_rate_set_do(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       uint8_t         byte;
+       int             state;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_rate_set_do: 0x%X\n", data);
+       state = 0;
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_RATE_SET);
+       while (ret == 0 && state != 2) {
+               ret = pa2_output_read_do(fp, &byte);
+               if (ret != 0) {
+                       break;
+               }
+               switch (state) {
+               case 0:         /* ACK of cmd */
+                       ASSERT(byte == PA2_KBD_REPLY_ACK);
+                       ret = pa2_data_write_do(fp, data);
+                       break;
+
+               case 1:         /* ACK of data */
+                       ASSERT(byte == PA2_KBD_REPLY_ACK);
+                       break;
+               }
+               if (byte != PA2_KBD_REPLY_ACK) {
+                       ret = 1;
+               }
+               state++;
+       }
+       return ret;
+}
+
+static int
+pa2_kbd_enable_do(
+       pa2_t           *fp)
+{
+       uint8_t         byte;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "kbd_enable_do()\n");
+       ret = pa2_data_write_do(fp, PA2_KBD_CMD_ENABLE);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &byte);
+               if (ret == 0) {
+                       ASSERT(byte == PA2_KBD_REPLY_ACK);
+                       if (byte != PA2_KBD_REPLY_ACK) {
+                               ret = 1;
+                       }
+               }
+       }
+       return ret;
+}
+
+/*
+ * Reset aux.
+ */
+
+static int
+pa2_aux_reset_do(
+       pa2_t           *fp)
+{
+       uint            limit;
+       int             ret;
+       uint8_t         byte;
+
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_RESET);
+       if (ret == 0) {
+               ret = pa2_output_aux_read_do(fp, &byte);
+               if (ret == 0) {
+                       ASSERT(byte == PA2_AUX_REPLY_BAT_SUCC);
+                       if (byte != PA2_AUX_REPLY_BAT_SUCC) {
+                               ret = 1;
+                       }
+               }
+               if (ret == 0) {
+                       ret = pa2_output_aux_read_do(fp, &byte);
+                       if (ret == 0) {
+                               ASSERT(byte == 0);
+                               if (byte != 0) {
+                                       ret = 1;
+                               }
+                       }
+               }
+       }
+       fp->pp_limit = limit;
+       return ret; 
+}
+
+static int
+pa2_aux_res_set_do(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       uint            limit;
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_MID, "aux_res_set_do: 0x%X\n", data);
+       limit = fp->pp_limit;
+       fp->pp_limit = 500;
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_RES_SET);
+       if (ret == 0) {
+               ret = pa2_aux_arg_do(fp, data);
+       }
+       fp->pp_limit = limit;
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_aux_dev_disable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_DEV_DISABLE);
+       return ret;
+}
+
+static int
+pa2_aux_dev_enable_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_DEV_ENABLE);
+       return ret;
+}
+
+static int
+pa2_aux_sample_set_do(
+       pa2_t           *fp,
+       uint8_t         data)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_sample_set_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SAMPLE_SET);
+       if (ret == 0) {
+               ret = pa2_aux_arg_do(fp, data);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_wrap_reset_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_reset_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_WRAP_RESET);
+       return ret;
+}
+
+static int
+pa2_aux_wrap_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_wrap_set_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_WRAP_SET);
+       return ret;
+}
+
+static int
+pa2_aux_stream_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_stream_set_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_STREAM_SET);
+       return ret;
+}
+
+static int
+pa2_aux_remote_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_remote_set_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_REMOTE_SET);
+       return ret;
+}
+
+static int
+pa2_aux_scale11_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       PRINT_DEBUG(PA2_DBG_LOW, "aux_scale11_set_do\n");
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SCALE11_SET);
+       return ret;
+}
+
+static int
+pa2_aux_scale21_set_do(
+       pa2_t           *fp)
+{
+       int             ret;
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SCALE21_SET);
+       return ret;
+}
+
+
+static int
+pa2_ctl_mode_write_do(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       int             ret;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_MODE_WRITE);
+       if (ret == 0) {
+               ret = pa2_data_write_do(fp, byte);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_mode_read_do(
+       pa2_t           *fp)
+{
+       int             ret;
+       uint8_t         byte;
+
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_MODE_READ);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &byte);
+       }
+       return ret;
+}
+
+/*
+ * Test the controller can talk to the aux.
+ */
+
+static int
+pa2_aux_test_do(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       uint8_t         data;
+       int             ret;
+
+       scp = &fp->pp_state_const;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_AUX_TEST);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &data);
+               if (ret == 0) {
+                       ASSERT(scp->psc_hv_flgs & PA2_HV_CONST_CTL_AUX_TEST);
+                       if (data != 0x00) {
+                               pa2_error("Failed AUX test: 0x%X\n", data);
+                               ret = 1;
+                       }
+               }
+       }
+       return ret;
+}
+
+/*
+ * Load the mode register, so we can stay in sync with the h/w.
+ */
+
+static int
+pa2_mode_read_do(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       uint8_t         data;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_MODE_READ);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &data);
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_MODE);
+       }
+       return ret;
+}
+
+static int
+pa2_outport_read_do(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       uint8_t         byte;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_OUTPORT_READ);
+       if (ret == 0) {
+               ret = pa2_output_read_do(fp, &byte);
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+       }
+       return ret;
+}
+
+static int
+pa2_ctl_outport_write_do(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       ret = pa2_cmd_write_do(fp, PA2_CTL_CMD_OUTPORT_WRITE);
+       if (ret == 0) {
+               ret = pa2_data_write_do(fp, byte);
+               ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_OUTPORT);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_status_get_do(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       pa2_cmd_t       *cmdp;
+       pa2_const_t     *scp;
+       pa2_syp_state_t *sp;
+       uint32_t        flags;
+       uint8_t         byte;
+       uint8_t         foo;
+       uint            special;
+       uint            short_form;
+       int             state;
+       int             ret;
+
+       state = 0;
+       short_form = 0;
+       special = 0;
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       scp = &fp->pp_state_const;
+       if (scp->psc_flgs & (PA2_CONST_SYNAP_YES | PA2_CONST_SYNAP_PROBED)) {
+               cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+               foo = 0;
+               if (cmdp->pc_aux_res_set_seq == 4) {
+                       foo = 1;
+                       if (!(sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+                               special = 1;
+                       }
+                       if (foo == 0 &&
+                           !(cmdp->pc_flags & PA2_CMD_FLG_SPECIAL) &&
+                           (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) {
+                               short_form = 1;
+                       }
+               }
+       }
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_STATUS_GET);
+       if (ret != 0) {
+               goto out;
+       }
+       if (scp->psc_flgs & (PA2_CONST_SYNAP_YES | PA2_CONST_SYNAP_PROBED)) {
+#if    0
+               cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+               special = (cmdp->pc_flags & PA2_CMD_FLG_SPECIAL);
+               sp = &ssp->pss_synaptic;
+               if (special == 0) {
+#if    1
+                       if (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT) {
+                               short_form = 1;
+                       }
+#endif
+               } else {
+                       if (sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT) {
+                               special = 1;
+                       }
+               }
+#endif
+       }
+       while (ret == 0 && (short_form ? state != 1 : state != 3)) {
+               ret = pa2_output_aux_read_do(fp, &byte);
+               ASSERT(ret == 0);
+               if (ret != 0) {
+                       break;
+               }
+               flags = ssp->pss_hv_flgs;
+               switch (state) {
+               /* handle when special cmds XXX */
+
+               case 2:
+                       ASSERT(special || (flags & PA2_HV_STATE_AUX_SAMP));
+                       /* FALLTRHOUGH */
+
+               case 1:
+                       if (short_form) {
+                               /* unknown XXX */
+                       } else {
+                               ASSERT(special || (flags & PA2_HV_STATE_AUX_RES));
+                       }
+                       /* FALLTRHOUGH */
+
+               case 0:
+                       if (short_form) {
+                               /*ASSERT(flags & PA2_HV_STATE_AUX_SAMP); mode_byte XXX */
+                               break;
+                       }
+                       ASSERT(special || (flags & PA2_HV_STATE_AUX_REMOTE));
+                       ASSERT(special || (flags & PA2_HV_STATE_AUX_ENABLE));
+                       ASSERT(special || (flags & PA2_HV_STATE_AUX_SCALE));
+                       break;
+               }
+               state++;
+       }
+out:
+       return ret;
+}
+
+
+static int
+pa2_aux_type_get_do(
+       pa2_t           *fp)
+{
+       uint8_t         byte;
+       int             ret;
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_TYPE_GET);
+       if (ret == 0) {
+               ret = pa2_output_aux_read_do(fp, &byte);
+       }
+       return ret;
+}
+
+/**
+ ** Synaptic special cmd sequence functions.
+ **/
+
+/*
+ * Encode the given byte in the preamble for a synaptic special cmd.
+ * The setscale1:1 is to break any preceeding sequence, and then four
+ * setres to encode the given byte.
+ */
+
+static int
+pa2_synaptic_cmd_preamble(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       int             ret;
+       uint            i;
+       uint8_t         data;
+
+       /*
+        * Should always have RES when we reach here.
+        */
+
+       ASSERT(fp->pp_state_cur.pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+
+       /* caller? XXX */
+       (void) pa2_ctl_aux_disable_do(fp);
+
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SCALE11_SET);
+ ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SCALE11_SET);
+       for (i = 0; i < 4 && ret == 0; i++) {
+               data = (byte >> ((3 - i) * 2)) & 0x3;
+               ret = pa2_aux_res_set_do(fp, data);
+       }
+       ASSERT(ret == 0);
+       return ret;
+}
+
+static int
+pa2_synaptic_cmd_get(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       uint8_t         res;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;        
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+       res = ssp->pss_aux_res;
+       ret = pa2_synaptic_cmd_preamble(fp, byte);
+       if (ret == 0) {
+#if    0
+               (void) pa2_ctl_kbd_disable_do(fp);
+#endif
+               ret = pa2_aux_status_get_do(fp);
+       }
+
+       /*
+        * Not sure about this...
+        * If the series of set resolutions (acutally, its the final one)
+        * would have changed the resolution, then restore the original value.
+        * I think the transparent bit, in the synaptic mode-byte, can change
+        * this behaviour... XXX
+        */
+#if    0
+       if (ssp->pss_aux_res != res) {
+               ret = pa2_aux_res_set_do(fp, res);
+               ASSERT(ssp->pss_aux_res == res);
+       }
+#endif
+       return ret;
+}
+
+static int
+pa2_synaptic_cmd_set(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       uint8_t         res;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;        
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+       res = ssp->pss_aux_res;
+       ret = pa2_synaptic_cmd_preamble(fp, byte);
+       if (ret == 0) {
+               ret = pa2_aux_sample_set_do(fp, 0x14);
+       }
+
+#if    0
+       /*
+        * See comment in func above.
+        */
+
+       if (ssp->pss_aux_res != res) {
+               ret = pa2_aux_res_set_do(fp, res);
+               ASSERT(ssp->pss_aux_res == res);
+       }
+#endif
+       return ret;
+}
+
+static int
+pa2_synaptic_ext_cmd_set(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       uint8_t         res;
+       uint8_t         data;
+       uint            len;
+       uint            i;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;        
+       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_RES);
+       res = ssp->pss_aux_res;
+       ret = pa2_synaptic_cmd_preamble(fp, byte);
+       if (ret == 0) {
+               ret = pa2_aux_sample_set_do(fp, 0x28);  /* XXX */
+       }
+       len = 6;
+       if (byte == 0x2C) {
+               len = 12;
+       }
+
+       for (i = 0; i < len; i++) {
+               ret = pa2_output_aux_read_do(fp, &data);
+               if (ret != 0) {
+                       break;
+               }
+       }
+#if    0
+       /*
+        * See comment in func above.
+        */
+
+       if (ssp->pss_aux_res != res) {
+               ret = pa2_aux_res_set_do(fp, res);
+               ASSERT(ssp->pss_aux_res == res);
+       }
+#endif
+       return ret;
+}
+
+static int
+pa2_synaptic_mode_byte(
+       pa2_t           *fp,
+       uint8_t         byte)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;        
+       sp = &ssp->pss_synaptic;
+       ret = pa2_synaptic_cmd_set(fp, byte);
+       return ret;
+}
+
+/*
+ * Probe for synaptic touchpad.
+ * This sends special cmd sequence for the identify.  The state machine
+ * check for the middle byte of the response to be $0x47.  If it is, then
+ * the AUX device is marked as a synaptic touchpad.
+ * Only called when probing, during first initialization.
+ */
+
+static int
+pa2_synaptic_probe_do(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       int             ret;
+
+       ASSERT(fp->pp_init == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(fp->pp_active[PA2_INDEX_AUX] == 1);
+       ASSERT(!(scp->psc_flgs & PA2_CONST_SYNAP_PROBED));
+       scp->psc_flgs |= PA2_CONST_SYNAP_PROBED;
+       ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_IDENTIFY);
+       if (ret == 0) {
+               if (scp->psc_flgs & PA2_CONST_SYNAP_YES) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Synaptic touchpad\n");
+               } else {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Non-synaptic device\n");
+               }
+       } else {
+               scp->psc_flgs &= ~PA2_CONST_SYNAP_PROBED;
+       }
+       return ret;
+}
+
+static void
+pa2_state_dump(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       pa2_hdata_t     *php;
+       uint            i;
+
+       ssp = &fp->pp_state_cur;
+       PRINT_DEBUG(PA2_DBG_HIGH, "State dump:\n");
+       PRINT_DEBUG(PA2_DBG_HIGH, "\tpss_mode: 0x%X\n", ssp->pss_mode);
+       PRINT_DEBUG(PA2_DBG_HIGH, "\tpss_status: 0x%X\n", ssp->pss_status);
+       PRINT_DEBUG(PA2_DBG_HIGH, "\tpss_outport: 0x%X\n", ssp->pss_outport);
+       if (fp->pp_flags & PA2_FLG_KBD_IRQ) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "\tKBD raised\n");
+       }
+       if (fp->pp_flags & PA2_FLG_AUX_IRQ) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "\tAUX raised\n");
+       }
+       for (i = 0; i < 2; i++) {
+               php = &fp->pp_irqbuf[i];
+               if (php->ph_start == php->ph_end) {
+                       continue;
+               }
+               if (i == PA2_INDEX_KBD) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "\tKBD has data\n");
+               } else {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "\tAUX has data\n");
+               }
+       }
+       return;
+}
+
+static int
+pa2_synaptic_e1_do(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       uint8_t         mode_byte;
+       uint8_t         byte;
+       uint            state;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       mode_byte = sp->ps_mode_byte;
+       if (!(mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) { 
+               ret = pa2_synaptic_mode_byte(fp, mode_byte |
+                                                  PA2_SYNAP_BIT_TRANSPARENT);
+               /* XXXX */
+       }
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "pa2_synaptic_e1_get()\n");
+       state = 0;
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SYNAP_E1);
+       while (ret == 0 && state != 2) {
+               ret = pa2_output_aux_read_do(fp, &byte);
+               switch (state) {
+               case 0:
+                       ASSERT(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_E1_BYTE1);
+                       /* also check syn const XXX */
+                       break;
+
+               case 1:
+                       ASSERT(sp->ps_hv_flgs & PA2_HV_SYNAP_STATE_E1_BYTE1);
+                       /* also check syn const XXX */
+                       break;
+               }
+               state++;
+       }
+
+out:
+       if (!(mode_byte & PA2_SYNAP_BIT_TRANSPARENT)) { 
+               ret |= pa2_synaptic_mode_byte(fp, mode_byte);
+       }
+       return ret;
+}
+
+static int
+pa2_aux_synap_e2_do(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       uint8_t         byte;
+       uint            state;
+       int             ret;
+
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       ASSERT(sp->ps_mode_byte & PA2_SYNAP_BIT_TRANSPARENT);
+       PRINT_DEBUG(PA2_DBG_HIGH, "pa2_aux_synap_e2_do()\n");
+       state = 0;
+       ret = pa2_aux_cmd_do(fp, PA2_AUX_CMD_SYNAP_E2);
+       if (ret == 0) {
+               ret = pa2_aux_arg_do(fp, 0x40);
+       }
+       return ret;
+}
+
+static int
+pa2_synaptic_const_load(
+       pa2_t           *fp)
+{
+       pa2_state_t     *ssp;
+       pa2_const_t     *scp;
+       pa2_syp_const_t *syp;
+       pa2_syp_state_t *sp;
+       int             ret;
+
+       ASSERT(fp->pp_init == 1);
+       scp = &fp->pp_state_const;
+       ASSERT(scp->psc_flgs & PA2_CONST_SYNAP_YES);
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       syp = &fp->pp_state_const.psc_synap;
+       ret = 0;
+       if (!(sp->ps_hv_flgs & PA2_HV_SYNAP_MODES_B1)) {
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_MODES);
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODES_B1);
+       if (ret != 0) {
+               goto out;
+       }
+
+       /*
+        * Should have major, minor, modelcode, from the
+        * identify done as the probe.
+        */
+
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_INFO_MIN);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_INFO_MAJ);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_CD);
+
+       /*
+        * Capabilities.
+        */
+
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_FIRST_BYTE)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_THIRD_BYTE));
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_CAPS);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_FIRST_BYTE);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CAPS_THIRD_BYTE);
+
+       /*
+        * Capabilities.
+        */
+
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_16)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_08));
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_00));
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_MODEL);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_16);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_08);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_MODEL_00);
+
+       /*
+        * Serial number prefix.
+        */
+
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_24)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_32));
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_SERIAL_PREFIX);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_24);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_32);
+
+       /*
+        * Serial number suffix.
+        */
+               
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_16)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_08));
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_00));
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_SERIAL_SUFFIX);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_16);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_08);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_SER_00);
+
+       /*
+        * Resolutions.
+        */
+
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_XUPMM)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_YUPMM));
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_RESOLUTION);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_XUPMM);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_YUPMM);
+
+       /*
+        * Unknowns
+        */
+
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN1)) {
+               ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_UNKNOWN_XXX1);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN1);
+
+       if (syp->pcy_capExtended && syp->pcy_capQueries >= 1) {
+               if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_EXT_MODEL)) {
+                       ret = pa2_synaptic_cmd_get(fp,
+                                                 PA2_SYNAP_GET_EXTENDED_MODEL);
+                       if (ret != 0) {
+                               goto out;
+                       }
+               }
+               ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_EXT_MODEL);
+       }
+
+       if (syp->pcy_capExtended && syp->pcy_capQueries >= 4) {
+               if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN3)) {
+                       ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_UNKNOWN_XXX3);
+                       if (ret != 0) {
+                               goto out;
+                       }
+               }
+               ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN3);
+       }
+
+       if (syp->pcy_capExtended && syp->pcy_capQueries >= 4) {
+               if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN4)) {
+                       ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_UNKNOWN_XXX4);
+                       if (ret != 0) {
+                               goto out;
+                       }
+               }
+               ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_UNKNOWN4);
+       }
+
+#if    0
+       if (!(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE1)) {
+               ASSERT(!(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE2));
+               ret = pa2_synaptic_e1_do(fp);
+               if (ret != 0) {
+                       goto out;
+               }
+       }
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE1);
+       ASSERT(syp->pcy_hv_flgs & PA2_HV_SYNAP_CONST_E1_BYTE2);
+#endif
+
+out:
+       return ret;
+}
+
+/*
+ * Drain any queued input from device.
+ * The device (KBD or AUX) should have been disabled before calling here.
+ */
+
+static void
+pa2_drain(
+       pa2_t           *fp,
+       int             index)
+{
+       uint            i;
+       uint8_t         status;
+       uint8_t         byte;
+
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX);
+       for (i = 0; i < 200; i++) {
+               status = pa2_status_read(fp, index);
+               if (!(status & PA2_CTL_STAT_OBF)) {
+                       break;
+               }
+               if (status & PA2_CTL_STAT_AUX_OBF) {
+                       ASSERT(index == PA2_INDEX_AUX);
+                       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+                               (void) pa2_output_aux_read_do(fp, &byte);
+                               continue;
+                       }
+                       ASSERT(0);
+                       break;
+               }
+               ASSERT(index == PA2_INDEX_KBD);
+               if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+                       (void) pa2_output_read_do(fp, &byte);
+                       continue;
+               }
+               ASSERT(0);
+               break;
+       }
+       return;
+}
+
+
+static int
+pa2_i8042_sane_init(
+       pa2_t           *fp)
+{
+       uint8_t         status;
+       uint8_t         byte;
+       uint            limit;
+       uint            done;
+       uint            ret;
+       uint            i;
+       uint            j;
+
+       /*
+        * Get into sane state; don't want to be blasted with
+        * interrutps, so ensure KBD and AUX are disabled.
+        */
+
+       ASSERT(fp->pp_active[PA2_INDEX_KBD] == 1 ||
+              fp->pp_active[PA2_INDEX_AUX] == 1);
+       done = 0;
+       ret = 0;
+       limit = 2;
+       while (done == 0) {
+               for (i = 0; i < 10; i++) {
+                       status = pa2_status_read(fp, PA2_INDEX_WILD);
+                       if ((status & (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)) ==
+                           (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)) {
+                               if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+                                       (void) pa2_output_aux_read_do(fp, &byte);
+                               } else {
+                                       usleep(10);
+                                       /* after certain amount of time, what to do? XXX */
+                                       (void) pa2_output_aux_read_do(fp, &byte);
+                               }
+                               limit = 4;
+                               continue;
+                       }
+                       if (status & PA2_CTL_STAT_OBF) {
+                               if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+                                       (void) pa2_output_read_do(fp, &byte);
+                               } else {
+                                       usleep(10);
+                                       (void) pa2_output_read_do(fp, &byte);
+                               }
+                               limit = 4;
+                               continue;
+                       }
+                       break;
+               }
+               ret = 0;
+#if    0
+               if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+                       ret |= pa2_ctl_kbd_disable_do(fp);
+               }
+               if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+                       ret |= pa2_ctl_aux_disable_do(fp);
+               }
+#endif
+               /* drain */
+               for (i = 0; i < limit; i++) {
+                       status = pa2_status_read(fp, PA2_INDEX_WILD);
+                       if (!(status & PA2_CTL_STAT_OBF)) {
+                               usleep(20);
+                               continue;
+                       }
+                       break;
+               }
+               /* select based upon pp_active[] XXX */
+               if ((status & (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)) ==
+                   (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)) {
+                       if (fp->pp_active[PA2_INDEX_AUX] == 1) {
+                               (void) pa2_output_aux_read_do(fp, &byte);
+                       } else {
+                               usleep(10);
+                               (void) pa2_output_aux_read_do(fp, &byte);
+                       }
+                       limit = 4;
+                       continue;
+               }
+               if (status & PA2_CTL_STAT_OBF) {
+                       if (fp->pp_active[PA2_INDEX_KBD] == 1) {
+                               (void) pa2_output_read_do(fp, &byte);
+                       } else {
+                               usleep(10);
+                               (void) pa2_output_read_do(fp, &byte);
+                       }
+                       limit = 4;
+                       continue;
+               }
+               if (ret != 0) {
+                       continue;
+               }
+               done = 1;
+               break;
+       }
+       return ret;
+}
+
+static int
+pa2_aux_init(
+       pa2_t           *fp)
+{
+       pa2_state_t     saved;
+       pa2_const_t     *scp;
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_syp_const_t *syp;
+       pa2_cmd_t       cmd;
+       pa2_cmd_t       *cmdp;
+       pa2_hdata_t     *php;
+       uint            step;
+       uint            done;
+       uint            fail_count;
+       uint8_t         lock;
+       uint8_t         aux_dev_enabled;
+       uint8_t         mode;
+       int             fd;
+       int             ret;
+
+       ret = 0;
+       step = 0;
+       done = 0;
+       fail_count = 0;
+       scp = &fp->pp_state_const;
+       ssp = &fp->pp_state_cur;
+
+       cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+       cmd = *cmdp;
+
+       fp->pp_active[PA2_INDEX_AUX] = 1;
+       fp->pp_grabbing[PA2_INDEX_AUX] = 1;
+       if (qemu_set_fd_handler(fp->pp_fd[PA2_INDEX_AUX], pa2_irq, NULL,
+                                       (void *)PA2_INDEX_AUX) != 0) {
+               pa2_error("Cannot set AUX handler\n");
+               ret = EAGAIN;
+               goto out;
+       }
+
+       memcpy(&saved, ssp, sizeof (saved));
+       sp = &ssp->pss_synaptic;
+       mode = ssp->pss_mode;
+       PRINT_DEBUG(PA2_DBG_MID, "Aux_init: mode_byte 0x%X\n", mode);
+       aux_dev_enabled = ssp->pss_aux_status & PA2_AUX_STAT_ENABLE;
+
+       fd = fp->pp_fd[PA2_INDEX_CTL];
+       lock = 1;
+       if (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+               ret = EAGAIN;
+               goto out;
+       }
+
+       while (done == 0) {
+               ret = 0;
+               PRINT_DEBUG(PA2_DBG_HIGH, "aux_init: %u\n", step);
+               switch (step) {
+
+               case 0:
+                       /*
+                        * Clear Synaptic mode_byte.  This puts us in
+                        * sync with the h/w settings, and so for the following
+                        * dev reset we know what state is affected (the
+                        * setting of mode_byte modifies the reset).
+                        */
+
+                       if (scp->psc_flgs & PA2_CONST_SYNAP_YES) {
+                               ret = pa2_synaptic_mode_byte(fp, 0);
+                       }
+                       break;
+
+               case 1:
+                       /*
+                        * Have the reset clear Synaptic too.
+                        * Could be this is a first re-open of an already
+                        * initialized synaptic touchpad, so always set
+                        * the scale for a full reset.
+                        */
+
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_scale11_set_do(fp);
+                       break;
+
+               case 3:
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_dev_disable_do(fp);
+                       break;
+
+               case 4:
+                       pa2_drain(fp, PA2_INDEX_AUX);
+                       break;
+
+               case 5:
+                       /*
+                        * After the reset, know what some settings are.
+                        * If these match the saved settings, then can
+                        * avoid unnecessary actions.
+                        */
+
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_REMOTE);
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SCALE);
+                       if ((ssp->pss_aux_status & PA2_AUX_STAT_REMOTE) ==
+                           (saved.pss_aux_status & PA2_AUX_STAT_REMOTE)) {
+                               saved.pss_hv_flgs |= PA2_HV_STATE_AUX_REMOTE;
+                       }
+                       if ((ssp->pss_aux_status & PA2_AUX_STAT_SCALING) ==
+                           (saved.pss_aux_status & PA2_AUX_STAT_SCALING)) {
+                               saved.pss_hv_flgs |= PA2_HV_STATE_AUX_SCALE;
+                       }
+                       memcpy(ssp, &saved, sizeof(saved));
+                       break;
+
+               case 7:
+                       /* only AUX XXX */
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode);
+                       break;
+
+               case 8:
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_res_set_do(fp, ssp->pss_aux_res);
+                       break;
+
+               case 9:
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_sample_set_do(fp, ssp->pss_aux_sample);
+                       break;
+
+               case 10:
+
+                       /*
+                        * AUX has been reset, which sets STREAM (!REMOTE),
+                        * so only need this step if the status is REMOTE.
+                        */
+
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_REMOTE);
+                       if (ssp->pss_aux_status & PA2_AUX_STAT_REMOTE) {
+                               (void) pa2_ctl_aux_disable_do(fp);
+                               ret = pa2_aux_remote_set_do(fp);
+                       }
+                       break;
+
+               case 12:
+                       if (ssp->pss_aux_wrap_mode == 1) {
+                               (void) pa2_ctl_aux_disable_do(fp);
+                               ret = pa2_aux_wrap_set_do(fp);
+                       } else {
+                               (void) pa2_ctl_aux_disable_do(fp);
+                               ret = pa2_aux_wrap_reset_do(fp);
+                       }
+                       break;
+
+               case 14:
+                       /*
+                        * AUX has been reset, which sets scaling to 1:1,
+                        * so only need this step if scaling is 21.
+                        */
+
+                       ASSERT(ssp->pss_hv_flgs & PA2_HV_STATE_AUX_SCALE);
+                       if (ssp->pss_aux_status & PA2_AUX_STAT_SCALING) {
+                               (void) pa2_ctl_aux_disable_do(fp);
+                               ret = pa2_aux_scale21_set_do(fp);
+                               break;
+                       }
+                       ASSERT(ssp->pss_setscale_last == 0);
+                       /* clear the buttons XXX */
+                       break;
+
+               case 18:
+
+                       /*
+                        * If we're seen a synaptic mode (GET_IDENTITY) from
+                        * this host, then set the mode_byte.  Otherwise this
+                        * is a no-op.
+                        */
+
+                       if (!(fp->pp_flags & PA2_FLG_SYNAPTIC_PROBE)) {
+                               break;
+                       }
+
+                       /*
+                        * Hmm, something seeing a guest that doesn't have
+                        * focus is leaving the transparent bit set.  Must have
+                        * a bug somewhere in Synaptic emulation.  If the XXX
+                        * transpoarent bit is left set the touchpad doesn't
+                        * generate any packet data.  So, for now, force
+                        * clear this bit.
+                        */
+
+                       sp->ps_mode_byte &= ~PA2_SYNAP_BIT_TRANSPARENT;
+                       ret = pa2_synaptic_mode_byte(fp, sp->ps_mode_byte);
+                       break;
+
+               case 20:
+                       /*
+                        * Value written via E2. XXX
+                        */
+
+                       break;
+
+               case 22:
+                       if (aux_dev_enabled) {
+                               (void) pa2_ctl_kbd_disable_do(fp);
+                               ret = pa2_aux_dev_enable_do(fp);
+                       } else {
+                               ASSERT(!(ssp->pss_aux_status & PA2_AUX_STAT_ENABLE));
+                       }
+                       break;
+
+               case 24:
+                       if (!(mode & PA2_CTL_MODE_DISABLE_KBD)) {
+                               pa2_ctl_kbd_enable_do(fp);
+                       }
+                       if (!(mode & PA2_CTL_MODE_DISABLE_AUX)) {
+                               pa2_ctl_aux_enable_do(fp);
+                       }
+                       break;
+
+               case 25:
+                       php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+                       php->ph_start = php->ph_end;
+                       ASSERT(!(fp->pp_flags & PA2_FLG_AUX_IRQ));
+                       fp->pp_grabbing[PA2_INDEX_AUX] = 0;
+                       *cmdp = cmd;
+
+                       /*
+                        * The cmd continues "inactive"...
+                        */
+
+                       done = 1;
+                       break;
+
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               if (fail_count++ < 8) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying init step: %u\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot init aux.  Step %u keeps failing\n", step);
+               step++;
+               fail_count = 0;
+       }
+
+       lock = 0;
+       while (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+       }
+
+out:
+       return ret;
+}
+
+static int
+pa2_aux_once_init(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_syp_const_t *syp;
+       pa_syp_0x28_t   *p28p;
+       pa2_hdata_t     *php;
+       uint8_t         lock;
+       uint            step;
+       uint            done;
+       uint            fail_count;
+       uint            i;
+       uint            j;
+       int             fd;
+       int             ret;
+
+       ret = 0;
+       step = 0;
+       done = 0;
+       fail_count = 0;
+       scp = &fp->pp_state_const;
+       ssp = &fp->pp_state_cur;
+       sp = &ssp->pss_synaptic;
+       syp = &fp->pp_state_const.psc_synap;
+
+       ASSERT(fp->pp_init == 1);
+
+       /* should retry on failure...have kernel interruptible XXX */
+       lock = 1;
+       fd = fp->pp_fd[PA2_INDEX_CTL];
+       if (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+               ret = EAGAIN;
+               goto out;
+       }
+
+       while (done == 0) {
+       uint8_t status;
+               ret = 0;
+               pa2_outport_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "aux_once_init: %u 0x%X 0x%X\n", step,
+                                                       ssp->pss_mode, ssp->pss_outport);
+               ret = pa2_ctl_mode_read_do(fp);
+               ret = pa2_outport_read_do(fp);
+               status = pa2_status_read(fp, PA2_INDEX_RAW);
+               PRINT_DEBUG(PA2_DBG_MID, "aux_once_init-mode: 0x%X 0x%X 0x%X\n", ssp->pss_mode, ssp->pss_outport, status);
+               switch (step) {
+
+               case 0:
+                       /*
+                        * Clear Synaptic mode_byte.  This puts us in
+                        * sync with the h/w settings, and so for the following
+                        * dev reset we know what state is affected (the
+                        * setting of mode_byte modifies the reset).
+                        */
+                       if (scp->psc_flgs & PA2_CONST_SYNAP_YES) {
+                               ret = pa2_synaptic_mode_byte(fp, 0);
+                       }
+                       break;
+
+               case 1:
+                       /*
+                        * Have the reset clear Synaptic too.
+                        * Could be this is a first re-open of an already
+                        * initialized synaptic touchpad, so always set
+                        * the scale for a full reset.
+                        */
+
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_scale11_set_do(fp);
+                       break;
+
+               case 2:
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_reset_do(fp);
+                       break;
+
+               case 3:
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_dev_disable_do(fp);
+                       break;
+
+               case 4:
+                       pa2_drain(fp, PA2_INDEX_AUX);
+                       break;
+
+               case 6:
+                       ASSERT(!(scp->psc_hv_flgs & PA2_HV_CONST_AUX_TYPE));
+                       ret = pa2_aux_type_get_do(fp);
+                       break;
+
+               case 7:
+                       /* only AUX XXX */
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode);
+                       break;
+
+               case 9:
+                       /*
+                        * Handle synaptic...
+                        */
+
+                       ASSERT(!(scp->psc_flgs & PA2_CONST_SYNAP_PROBED));
+                       ret = pa2_synaptic_probe_do(fp);
+                       break;
+
+               case 10:
+                       ASSERT(sp->ps_mode_byte == 0);
+                       ret = pa2_synaptic_const_load(fp);
+                       break;
+
+#if    0
+               case 11:
+                       if (!(scp->psc_flgs & PA2_CONST_SYNAP_YES)) {
+                               break;
+                       }
+       sp->ps_mode_byte = PA2_SYNAP_BIT_ABS | PA2_SYNAP_BIT_WMODE | PA2_SYNAP_BIT_RATE;
+               PRINT_DEBUG(PA2_DBG_HIGH, "Setting1 mode_byte to 0x%X\n",
+                       sp->ps_mode_byte);
+                       ret = pa2_synaptic_mode_byte(fp,
+                                                    sp->ps_mode_byte);
+
+                       ret = pa2_aux_dev_enable_do(fp);
+                       ret = pa2_aux_dev_disable_do(fp);
+                       ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_MODES);
+
+       sp->ps_mode_byte = PA2_SYNAP_BIT_TRANSPARENT | PA2_SYNAP_BIT_ABS | PA2_SYNAP_BIT_WMODE | PA2_SYNAP_BIT_RATE;
+               PRINT_DEBUG(PA2_DBG_HIGH, "Setting2 mode_byte to 0x%X\n",
+                       sp->ps_mode_byte);
+                       ret = pa2_synaptic_mode_byte(fp,
+                                                    sp->ps_mode_byte);
+
+                       ret = pa2_aux_scale21_set_do(fp);
+                       ret = pa2_aux_reset_do(fp);
+
+                       ret = pa2_synaptic_cmd_get(fp, PA2_SYNAP_GET_IDENTIFY);
+                       ret = pa2_synaptic_e1_do(fp);
+                       ret = pa2_aux_sample_set_do(fp, 0x28);
+
+                       ret = pa2_aux_synap_e2_do(fp);
+                       ret = pa2_aux_res_set_do(fp, 0x3);
+
+
+                       ret = pa2_aux_scale21_set_do(fp);
+                       ret = pa2_aux_scale11_set_do(fp);
+                       ret = pa2_aux_dev_disable_do(fp);
+
+                       for (i = 0; i < sizeof (pa2_28_queries); i++) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Quering: 0x%x\n", pa2_28_queries[i]);
+                               pa2_synaptic_ext_cmd_set(fp, pa2_28_queries[i]);
+                       }
+                       ret = pa2_synaptic_mode_byte(fp,
+                                                    sp->ps_mode_byte &
+                                                   ~PA2_SYNAP_BIT_TRANSPARENT);
+                       break;
+#else
+               case 11:
+                       if (!(scp->psc_flgs & PA2_CONST_SYNAP_YES)) {
+                               break;
+                       }
+                       for (i = 0; i < sizeof (pa2_28_queries); i++) {
+                               for (j = 0; j < sizeof (syp->pcy_28_resp) /
+                                               sizeof (syp->pcy_28_resp[0]);
+                                               j++) {
+                                       p28p = &syp->pcy_28_resp[j];
+                                       if (p28p->p28_status == 0) {
+                                               break;
+                                       }
+                                       if (p28p->p28_special ==
+                                           pa2_28_queries[i]) {
+                                               break;
+                                       }
+                                       p28p = NULL;
+                               }
+                               ASSERT(p28p != NULL);
+                               if (p28p == NULL) {
+                                       break;
+                               }
+                               p28p->p28_status = 1;
+                               p28p->p28_special = pa2_28_queries[i];
+                               /* p28p->p28_bytes[0] = 0xFA; XXX */
+                               p28p->p28_bytes[0] = 0x84;
+                               p28p->p28_bytes[1] = 0xFA;
+                               p28p->p28_bytes[2] = 0x00;
+                               p28p->p28_bytes[3] = 0xC4;
+                               p28p->p28_bytes[4] = 0x00;
+                               if (p28p->p28_special != 0x2C) {
+                                       continue;
+                               }
+                               p28p->p28_bytes[5] = 0x00;
+                               p28p->p28_bytes[6] = 0x84;
+                               p28p->p28_bytes[7] = 0x21;
+                               p28p->p28_bytes[8] = 0x00;
+                               p28p->p28_bytes[9] = 0xC4;
+                               p28p->p28_bytes[10] = 0x00;
+                       }
+                       break;
+#endif
+
+               case 12:
+                       /*
+                        * Reset.
+                        */
+
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_scale11_set_do(fp);
+                       break;
+
+               case 14:
+                       /*
+                        * Reset.
+                        */
+
+                       (void) pa2_ctl_aux_disable_do(fp);
+                       ret = pa2_aux_dev_disable_do(fp);
+                       break;
+
+               case 15:
+                       pa2_drain(fp, PA2_INDEX_AUX);
+                       break;
+
+               case 16:
+                       php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+                       php->ph_start = php->ph_end;
+                       ASSERT(!(fp->pp_flags & PA2_FLG_AUX_IRQ));
+                       done = 1;
+                       break;
+
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               if (fail_count++ < 8) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying init step: %u\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot init aux.  Step %u keeps failing\n", step);
+               step++;
+               fail_count = 0;
+       }
+
+       lock = 0;
+       while (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+       }
+
+out:
+       return ret;
+}
+
+
+/*
+ * Intialize the KBD.
+ */
+
+static int
+pa2_kbd_init(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       pa2_state_t     *ssp;
+       uint8_t         lock;
+       uint            step;
+       uint            done;
+       uint            fail_count;
+       int             fd;
+       int             ret;
+       uint8_t         kbd_disabled;
+       uint8_t         kbd_rate;
+       uint8_t         kbd_leds;
+       uint8_t         byte;
+
+       ASSERT(fp->pp_active[PA2_INDEX_KBD] == 1);
+       ASSERT(fp->pp_grabbing[PA2_INDEX_KBD] == 1);
+
+       ret = 0;
+       step = 0;
+       done = 0;
+       fail_count = 0;
+       scp = &fp->pp_state_const;
+       ssp = &fp->pp_state_cur;
+       kbd_disabled = ssp->pss_mode & PA2_CTL_MODE_DISABLE_KBD;
+       kbd_rate = ssp->pss_kbd_rate;
+       kbd_leds = ssp->pss_kbd_leds;
+
+       fd = fp->pp_fd[PA2_INDEX_CTL];
+       lock = 1;
+       PRINT_DEBUG(PA2_DBG_HIGH, "Taking cmd lock: %u\n", step);
+       if (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+               ret = EAGAIN;
+               goto out;
+       }
+       while (done == 0) {
+               ret = 0;
+               PRINT_DEBUG(PA2_DBG_HIGH, "kbd_init: %u\n", step);
+               switch (step) {
+
+               case 0:
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_reset_disable_do(fp);
+                       break;
+
+               case 1:
+                       pa2_drain(fp, PA2_INDEX_KBD);
+                       break;
+
+               case 2:
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_rate_set_do(fp, kbd_rate);
+                       break;
+
+               case 3:
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_leds_set_do(fp, kbd_leds);
+                       break;
+
+               case 4:
+                       /* only KBD XXX */
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode);
+                       break;
+
+               case 5:
+
+                       /*
+                        * Enable scanning.  Should be part of our status. XXX
+                        */
+
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_enable_do(fp);
+                       break;
+
+               case 6:
+                       done = 1;
+                       break;
+
+               default:
+                       break;
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               if (fail_count++ < 10) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying init step: %d\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot init kbd.  Step %u keeps failing\n", step);
+               fail_count = 0;
+               step++;
+       }
+#if    0
+       if (kbd_disabled == 0) {
+#endif
+               pa2_ctl_kbd_enable_do(fp);
+#if    0
+       }
+#endif
+
+       lock = 0;
+       while (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+       }
+
+out:
+       return ret;
+}
+
+static int
+pa2_kbd_once_init(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       pa2_state_t     *ssp;
+       uint8_t         lock;
+       uint            step;
+       uint            done;
+       uint            fail_count;
+       int             fd;
+       int             ret;
+       uint8_t         kbd_disabled;
+       uint8_t         kbd_rate;
+       uint8_t         kbd_leds;
+       uint8_t         byte;
+
+       ASSERT(fp->pp_active[PA2_INDEX_KBD] == 1);
+       ASSERT(fp->pp_grabbing[PA2_INDEX_KBD] == 1);
+
+       ret = 0;
+       step = 0;
+       done = 0;
+       fail_count = 0;
+       scp = &fp->pp_state_const;
+       ssp = &fp->pp_state_cur;
+       kbd_disabled = ssp->pss_mode & PA2_CTL_MODE_DISABLE_KBD;
+       kbd_rate = ssp->pss_kbd_rate;
+       kbd_leds = ssp->pss_kbd_leds;
+
+       fd = fp->pp_fd[PA2_INDEX_CTL];
+       lock = 1;
+       PRINT_DEBUG(PA2_DBG_HIGH, "Taking cmd lock: %u\n", step);
+       if (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+               ret = EAGAIN;
+               goto out;
+       }
+       while (done == 0) {
+               ret = 0;
+               pa2_outport_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "kbd_once_init: %u 0x%X 0x%X\n", step,
+                       ssp->pss_mode, ssp->pss_outport);
+               switch (step) {
+
+               case 0:
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_reset_disable_do(fp);
+                       break;
+
+               case 1:
+                       pa2_drain(fp, PA2_INDEX_KBD);
+                       break;
+
+               case 4:
+                       /* only KBD XXX */
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode | PA2_CTL_MODE_INT_KBD);
+                       break;
+
+               case 5:
+
+                       /*
+                        * Enable scanning.  Should be part of our status. XXX
+                        */
+
+                       (void) pa2_ctl_kbd_disable_do(fp);
+                       ret = pa2_kbd_enable_do(fp);
+                       break;
+
+               case 6:
+                       done = 1;
+                       break;
+
+               default:
+                       break;
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               if (fail_count++ < 10) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying init step: %d\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot init kbd.  Step %u keeps failing\n", step);
+               fail_count = 0;
+               step++;
+       }
+#if    0
+       if (kbd_disabled == 0) {
+#endif
+               pa2_ctl_kbd_enable_do(fp);
+#if    0
+       }
+#endif
+
+       lock = 0;
+       while (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+       }
+
+out:
+       return ret;
+}
+
+/*
+ * Initialize the ctl.
+ * Only done once under the context of the primary VM.
+ */
+
+static int
+pa2_ctl_init(
+       pa2_t           *fp)
+{
+       pa2_const_t     *scp;
+       pa2_state_t     *ssp;
+       uint            step;
+       uint            done;
+       uint            fail_count;
+       int             ret;
+       uint8_t         mode;
+
+       ASSERT(fp->pp_init == 1);
+       ASSERT(fp->pp_active[PA2_INDEX_KBD] == 1);
+       ASSERT(fp->pp_active[PA2_INDEX_AUX] == 1);
+       ASSERT(fp->pp_grabbing[PA2_INDEX_KBD] == 1);
+       ASSERT(fp->pp_grabbing[PA2_INDEX_AUX] == 1);
+
+       step = 0;
+       done = 0;
+       fail_count = 0;
+       scp = &fp->pp_state_const;
+       ssp = &fp->pp_state_cur;
+       mode = ssp->pss_mode;
+       while (done == 0) {
+               if (step != 0) {
+                       pa2_ctl_mode_read_do(fp);
+               }
+               if (step > 11) {
+                       pa2_outport_read_do(fp);
+               }
+               PRINT_DEBUG(PA2_DBG_MID, "ctl_init: %u 0x%X 0x%X\n", step, ssp->pss_mode, ssp->pss_outport);
+               ret = 0;
+               switch (step) {
+               case 0:
+                       ret = pa2_i8042_sane_init(fp);
+                       ret = pa2_ctl_mode_write_do(fp, ssp->pss_mode);
+                       break;
+
+               case 1:
+               case 2:
+                       /*
+                        * Lets be paraniod, and reset the controller twice.
+                        */
+
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "ctl_reset1: 0x%X\n", ssp->pss_mode);
+                       ret = pa2_ctl_reset_do(fp);
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "ctl_reset2: 0x%X\n", ssp->pss_mode);
+                       break;
+
+               case 4:
+                       ret = pa2_i8042_sane_init(fp);
+#if    1
+                       ret |= pa2_kbd_reset_disable_do(fp);
+#else
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "kbd_reset1: 0x%X\n", ssp->pss_mode);
+                       ret |= pa2_kbd_reset_do(fp);
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "kbd_reset2: 0x%X\n", ssp->pss_mode);
+#endif
+                       break;
+
+               case 5:
+                       ret = pa2_i8042_sane_init(fp);
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "aux_reset1: 0x%X\n", ssp->pss_mode);
+                       ret |= pa2_aux_dev_disable_do(fp);
+               ret = pa2_ctl_mode_read_do(fp);
+               PRINT_DEBUG(PA2_DBG_MID, "aux_reset2: 0x%X\n", ssp->pss_mode);
+                       break;
+
+               case 6:
+                       ret = pa2_ctl_self_test_do(fp);
+                       break;
+
+               case 7:
+                       ret = pa2_ctl_kbd_test_do(fp);
+                       break;
+
+               case 8:
+                       ret = pa2_aux_test_do(fp);
+                       break;
+
+               case 9:
+                       ret = pa2_ctl_version_get_do(fp);
+                       break;
+
+               case 10:
+                       ret = pa2_ctl_outport_write_do(fp, ssp->pss_outport);
+                       break;
+
+               case 11:
+                       ret = pa2_ctl_mode_write_do(fp, mode);
+                       break;
+
+               case 12:
+                       /* leave with devices disabled */
+                       done = 1;
+                       break;
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               fail_count++;
+               if (fail_count < 4) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying init step: %d\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot init ctl.  Step %u keeps failing\n", step);
+               step++;
+       }
+       return ret;
+}
+
+/*
+ * Before releasing a device, disable it and interrupts.
+ */
+
+static int
+pa2_dev_disable(
+       pa2_t           *fp,
+       int             index)
+{
+       pa2_state_t     saved;
+       pa2_state_t     *ssp;
+       uint8_t         mode;
+       uint8_t         lock;
+       uint            done;
+       uint            step;
+       uint            fail_count;
+       int             fd;
+       int             ret;
+
+       ASSERT(index == PA2_INDEX_KBD || index == PA2_INDEX_AUX);
+       ASSERT(fp->pp_active[index] == 1);
+       ASSERT(fp->pp_grabbing[index] == 0);
+       ssp = &fp->pp_state_cur;
+       memcpy(&saved, ssp, sizeof (saved));
+
+       ret = 0;
+       lock = 1;
+       fd = fp->pp_fd[PA2_INDEX_CTL];
+       if (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+               ret = EAGAIN;
+               goto out;
+       }
+       fp->pp_grabbing[index] = 1;
+       done = 0;
+       step = 0;
+       fail_count = 0;
+       while (done == 0) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "dev_disable: %u\n", step);
+               ret = 0;
+               switch(step) {
+
+               case 0:
+               case 1:
+                       if (index == PA2_INDEX_KBD) {
+                               (void) pa2_ctl_kbd_disable_do(fp);
+                               (void) pa2_kbd_reset_disable_do(fp);
+                       } else {
+                               (void) pa2_ctl_aux_disable_do(fp);
+                               (void) pa2_aux_dev_disable_do(fp);
+                       }
+                       break;
+
+               case 2:
+                       /* drain */
+                       pa2_drain(fp, PA2_INDEX_KBD);
+                       break;
+
+#if    0
+               case 6:
+                       /*
+                        * Disable interrupts.
+                        * Combine this with above? XXX
+                        */
+
+                       if (ssp->pss_hv_flgs & PA2_HV_STATE_MODE) {
+                               mode = ssp->pss_mode;
+                               if (index == PA2_INDEX_KBD) {
+                                       mode &= ~PA2_CTL_MODE_INT_KBD;
+                               } else {
+                                       mode &= ~PA2_CTL_MODE_INT_AUX;
+                               }
+                               ret = pa2_ctl_mode_write_do(fp, mode);
+                       }
+                       break;
+#endif
+               case 8:
+                       done = 1;
+                       break;
+               }
+               ASSERT(ret == 0);
+               if (ret == 0) {
+                       step++;
+                       fail_count = 0;
+                       continue;
+               }
+               fail_count++;
+               if (fail_count < 4) {
+                       PRINT_DEBUG(PA2_DBG_HIGH, "Retrying disable step: %d\n",
+                                   step);
+                       continue;
+               }
+               pa2_error("Cannot disable.  Step %u keeps failing\n", step);
+               step++;
+               fail_count = 0;
+       }
+       lock = 0;
+       while (ioctl(fd, PA2_IOCTL_INIT_CMD, &lock) == -1) {
+               ASSERT(0);
+       }
+       memcpy(ssp, &saved, sizeof (saved));
+       fp->pp_grabbing[index] = 0;
+
+out:
+       return ret;
+}
+
+/*
+ * Should be able to return failure.. XXX
+ */
+
+void
+pa2_i8042_init(
+       qemu_irq        kbd_irq,
+       qemu_irq        aux_irq,
+       uint32_t        iobase)
+{
+       pa2_t           *fp;
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_const_t     *scp;
+       char            buffer[1024];
+       int             ret;
+
+#if    defined(PA2_DEBUG)
+       fprintf(stderr, "pa2: i8042_init()\n");
+#endif
+
+       fp = &pa2_state;
+       fp->pp_kbd_irq = kbd_irq;
+       fp->pp_aux_irq = aux_irq;
+       fp->pp_iobase = iobase;
+
+       fp->pp_fd[PA2_INDEX_KBD] = -1;
+       fp->pp_fd[PA2_INDEX_AUX] = -1;
+       fp->pp_fd[PA2_INDEX_CTL] = -1;
+
+       /*
+        * Some sane values...
+        */
+
+       ssp = &fp->pp_state_cur;
+       scp = &fp->pp_state_const;
+       sp = &ssp->pss_synaptic;
+       fp->pp_state_cur.pss_mode = PA2_CTL_MODE_DISABLE_AUX;
+       fp->pp_state_cur.pss_status = PA2_CTL_STAT_UNLOCKED;
+
+       fp->pp_limit = 50;              /* XXX */
+
+       ssp->pss_outport = PA2_OUTPORT_SYSR | PA2_OUTPORT_AXDO |
+                          PA2_OUTPORT_ACLK | PA2_OUTPORT_OUTB |
+                          PA2_OUTPORT_AUXB | PA2_OUTPORT_KCLK |
+                          PA2_OUTPORT_KBDO;
+       ssp->pss_kbd_rate = PA2_KBD_DEFAULT_RATE;
+       ssp->pss_kbd_leds = 0x0;                /* XXX */
+       ssp->pss_aux_status = 0;
+       ssp->pss_aux_wrap_mode = 0;
+       ssp->pss_aux_sample = 100;
+       ssp->pss_aux_res = 0x02;
+
+       scp->psc_ctl_test = PA2_CTL_REPLY_SELF;
+
+       scp->psc_kbd_id = 0x83 || (0xAB << 8);  /* magic XXX */
+
+       sp->ps_e1_byte1 = PA2_SYNAP_E1_DEFAULT1;
+       sp->ps_e1_byte2 = PA2_SYNAP_E1_DEFAULT2;
+
+       /*
+        * The default is relative format.
+        * This consists of 3 bytes.  The first is various status bits,
+        * then second is the Y delta, and the third the Y delta.
+        */
+
+       fp->pp_aux_data.pad_format = PA2_AUX_FMT_DEFAULT;
+
+#if    defined(PA2_DEBUG)
+       snprintf(buffer, sizeof (buffer), "/tmp/mouse.%d", domid);
+       debug_file = fopen(buffer, "w+");
+       if (debug_file != NULL) {
+               setvbuf(debug_file, NULL, _IONBF, 0);
+               PRINT_DEBUG(PA2_DBG_HIGH, "Started: %d\n", domid);
+       }
+#endif
+
+       ret = register_ioport_read(iobase, 1, 1, pa2_guest_data_read, fp);
+       if (ret != 0) {
+               goto err;
+       }
+       ret = register_ioport_write(iobase, 1, 1, pa2_guest_data_write, fp);
+       if (ret != 0) {
+               goto err1;
+       }
+       ret = register_ioport_read(iobase + 4, 1, 1, pa2_guest_status_read, fp);
+       if (ret != 0) {
+               goto err2;
+       }
+       ret = register_ioport_write(iobase + 4, 1, 1, pa2_guest_cmd_write, fp);
+       if (ret != 0) {
+               goto err2;
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "Started - done\n");
+       return;
+
+err2:
+       isa_unassign_ioport(iobase + 4, 1);
+
+err1:
+       isa_unassign_ioport(iobase, 1);
+
+err:
+       pa2_error("Error initializing!!!\n");
+       return;
+}
+
+/**
+ ** Dom0_driver interface.
+ **/
+
+/*
+ * Add key combination into our trap table.
+ */
+
+void
+pa2_binding_add(
+       const int       *keys,
+       void            (*callback)(void *),
+       void            *payload)
+{
+       pa2_t           *fp;
+       int             ret;
+
+       ASSERT(keys != NULL);
+       ASSERT(callback != NULL);
+       fp = &pa2_state;
+       ret = pa2_binding_insert(fp, keys, callback, payload);
+       ASSERT(ret == 0);
+
+       /* need to be able to return an error XXX */
+       return;
+}
+
+/*
+ * Reset KBD keys.
+ * This empties the kbd input stream (need to call into kernel to complete
+ * this XXX), and then clears any made keys by sending their break sequence
+ * to the guest.
+ * While injecting keyboard could be sending new make/break keys, but as
+ * we're single threaded there, no danger of racing...
+ */
+
+void
+pa2_kbd_reset(
+       void)
+{
+       pa2_t           *fp;
+       pa2_state_t     *ssp;
+       pa2_hdata_t     *php;
+       uint            esc;
+       uint            i;
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "pa2_kbd_reset()\n");
+       fp = &pa2_state;
+       fp->pp_key_esc = 0;
+       ASSERT(fp->pp_cmds[PA2_OUTPUT_KBD].pc_cmd == 0);
+       php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+       php->ph_start = php->ph_end;
+       ssp = &fp->pp_state_cur;
+       if ((ssp->pss_status & (PA2_CTL_STAT_OBF | PA2_CTL_STAT_AUX_OBF)) ==
+               PA2_CTL_STAT_OBF) {
+               ssp->pss_status &= ~PA2_CTL_STAT_OBF;
+       }
+       if (fp->pp_flags & PA2_FLG_KBD_IRQ) {
+               pa2_irq_lower(fp);
+       }
+       for (esc = 0; esc < 2; esc++) {
+               for (i = 0; i < sizeof (fp->pp_keystate[0]); i++) {
+                       if (fp->pp_keystate[esc][i] == 0) {
+                               continue;
+                       }
+                       fp->pp_keystate[esc][i] = 0;
+                       pa2_key_inject(fp, i, esc, 0);
+               }
+       }
+       return;
+}
+
+void
+pa2_kbd_secure(
+       void            (*ugh)(int))
+{
+       pa2_t           *fp;
+
+       fp = &pa2_state;
+       PRINT_DEBUG(PA2_DBG_HIGH, "pa2_grab_secure_kbd()\n");
+       return;
+}
+
+int
+pa2_aux_grab(
+       int             grab)
+{
+       pa2_t           *fp;
+       pa2_cmd_t       cmd;
+       pa2_cmd_t       *cmdp;
+       pa2_hdata_t     *php;
+       uint8_t         saved_cmd;
+       uint8_t         byte;
+       uint            i;
+
+       fp = &pa2_state;
+       PRINT_DEBUG_ENTER("\n");
+       byte = 0;
+       if (grab != 0) {
+               byte = 1;
+       }
+       if (byte == fp->pp_active[PA2_INDEX_AUX]) {
+               ASSERT(0);
+               return 1;
+       }
+       if (byte == 0) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Releasing AUX\n");
+               pa2_state_dump(fp);
+
+               /*
+                * Drop all queued input.
+                */
+
+               php = &fp->pp_irqbuf[PA2_INDEX_AUX];
+               php->ph_start = php->ph_end;
+               if (fp->pp_flags & PA2_FLG_AUX_IRQ) {
+                       pa2_irq_lower(fp);
+                       ASSERT(!(fp->pp_flags & PA2_FLG_AUX_IRQ));
+               }
+
+               /*
+                * If there is a cmd in progress, then take a copy so can
+                * restore after disabling the device.
+                */
+
+               saved_cmd = 0;
+               cmdp = &fp->pp_cmds[PA2_OUTPUT_AUX];
+               if (cmdp->pc_cmd != 0) {
+                       ASSERT(cmdp->pc_active == 1);
+                       cmd = *cmdp;
+                       /* should abort cmd XXX */
+                       saved_cmd = 1;
+               }
+
+               pa2_dev_disable(fp, PA2_INDEX_AUX);
+               fp->pp_active[PA2_INDEX_AUX] = 0;
+               qemu_set_fd_handler(fp->pp_fd[PA2_INDEX_AUX], NULL, NULL,
+                                   (void *)PA2_INDEX_AUX);
+
+               /*
+                * If have a a cmd in progress, then send any expected
+                * input to the guest.
+                */
+
+               if (saved_cmd == 1) {
+                       *cmdp = cmd;
+                       cmdp->pc_active = 0;
+                       if (cmdp->pc_flags & PA2_CMD_FLG_EXPECTED) {
+                               PRINT_DEBUG(PA2_DBG_HIGH, "Completing aux cmd %s\n",
+                                       cmdp->pc_cmd_desp->pcc_name);
+                               cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+                               pa2_aux_append(fp, cmdp,
+                                              cmdp->pc_expected_input);
+                       }
+               }
+       } else {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Grabbing AUX\n");
+               pa2_state_dump(fp);
+       }
+
+       if (ioctl(fp->pp_fd[PA2_INDEX_AUX], PA2_IOCTL_GRAB, &byte) == -1) {
+               pa2_error("Cannot grab!\n");
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "Grab ioctl done\n");
+       /*
+        * Window XXX
+        */
+
+       /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXX */
+       if (byte == 1) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Initializing AUX\n");
+               pa2_aux_init(fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "Initialized AUX\n");
+       }
+       if (byte == 0) {
+               pa2_state_dump(fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "Released AUX\n");
+       } else {
+               pa2_state_dump(fp);
+               PRINT_DEBUG(PA2_DBG_HIGH, "Grabbed AUX\n");
+       }
+       PRINT_DEBUG_EXIT("\n");
+       return 1;
+}
+
+int
+pa2_kbd_grab(
+       int             grab)
+{
+       pa2_grab_t      gp;
+       pa2_t           *fp;
+       pa2_state_t     *ssp;
+       pa2_syp_state_t *sp;
+       pa2_cmd_t       cmd;
+       pa2_cmd_t       *cmdp;
+       pa2_hdata_t     *php;
+       uint8_t         saved_cmd;
+       uint8_t         byte;
+       uint            i;
+
+       fp = &pa2_state;
+       PRINT_DEBUG_ENTER("\n");
+       byte = 0;
+       if (grab != 0) {
+               byte = 1;
+       }
+       if (byte == fp->pp_active[PA2_INDEX_KBD]) {
+               ASSERT(0);
+               return 1;
+       }
+
+       /*
+        * Releasing KBD?
+        * First, complete any outstanding KBD cmd.
+        * What about ctl cmds?
+        */
+
+       memset(&gp, 0, sizeof (gp));
+       if (byte == 0) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Releasing KBD\n");
+               pa2_state_dump(fp);
+
+               /*
+                * Drop all queued input.
+                */
+
+               php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+               php->ph_start = php->ph_end;
+               if (fp->pp_flags & PA2_FLG_KBD_IRQ) {
+                       pa2_irq_lower(fp);
+                       ASSERT(!(fp->pp_flags & PA2_FLG_KBD_IRQ));
+               }
+               ssp = &fp->pp_state_cur;
+               sp = &ssp->pss_synaptic;
+               gp.pg_grab = 0;
+               gp.pg_aux_sample = ssp->pss_aux_sample;
+               gp.pg_aux_res = ssp->pss_aux_res;
+
+               /*
+                * If we've seen the synaptic probe from the guest's driver
+                * (GET_IDENTITY), then tell this to the grabbing guest.
+                */
+
+               gp.pg_flags = 0;
+               if (fp->pp_flags & PA2_FLG_SYNAPTIC_PROBE) {
+                       gp.pg_flags = PA2_GRAB_SYNAPTIC;
+                       gp.pg_syn_mode_byte = sp->ps_mode_byte;
+               }
+
+               /*
+                * If there is a cmd in progress, then take a copy so can
+                * restore after disabling the device.
+                */
+
+               saved_cmd = 0;
+               cmdp = &fp->pp_cmds[PA2_OUTPUT_KBD];
+               if (cmdp->pc_cmd != 0) {
+                       ASSERT(cmdp->pc_active == 1);
+                       cmd = *cmdp;
+                       /* should abort cmd XXX */
+                       saved_cmd = 1;
+               }
+
+               pa2_dev_disable(fp, PA2_INDEX_KBD);
+               fp->pp_active[PA2_INDEX_KBD] = 0;
+               qemu_set_fd_handler(fp->pp_fd[PA2_INDEX_KBD], NULL, NULL,
+                                   (void *)PA2_INDEX_KBD);
+
+               /*
+                * If have a a cmd in progress, then send any expected
+                * input to the guest.
+                */
+
+               if (saved_cmd == 1) {
+                       *cmdp = cmd;
+                       cmdp->pc_active = 0;
+                       if (cmdp->pc_flags & PA2_CMD_FLG_EXPECTED) {
+                               PRINT_DEBUG(PA2_DBG_HIGH, "Completing kbd cmd %s\n",
+                                       cmdp->pc_cmd_desp->pcc_name);
+                               cmdp->pc_flags &= ~PA2_CMD_FLG_EXPECTED;
+                               pa2_kbd_append(fp, cmdp,
+                                              cmdp->pc_expected_input);
+                       }
+               }
+       } else {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Grabbing KBD\n");
+               gp.pg_grab = 1;
+               pa2_state_dump(fp);
+       }
+
+       if (ioctl(fp->pp_fd[PA2_INDEX_KBD], PA2_IOCTL_GRAB, &gp) == -1) {
+               pa2_error("Cannot grab!\n");
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "Grab ioctl done\n");
+
+       if (byte == 1) {
+               fp->pp_active[PA2_INDEX_KBD] = 1;
+               fp->pp_grabbing[PA2_INDEX_KBD] = 1;
+               qemu_set_fd_handler(fp->pp_fd[PA2_INDEX_KBD], pa2_irq, NULL,
+                                       (void *)PA2_INDEX_KBD);
+               pa2_kbd_init(fp);
+               php = &fp->pp_irqbuf[PA2_INDEX_KBD];
+               php->ph_start = php->ph_end;
+               ASSERT(!(fp->pp_flags & PA2_FLG_KBD_IRQ));
+               fp->pp_grabbing[PA2_INDEX_KBD] = 0;
+               PRINT_DEBUG(PA2_DBG_HIGH, "Grabbed KBD\n");
+       } else {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Released KBD\n");
+               pa2_state_dump(fp);
+       }
+       PRINT_DEBUG_EXIT("\n");
+       return 1;
+}
+
+void
+pa2_probe(
+       int     grab)
+{
+
+       return;
+}
+
+/*
+ * Called from dom0_driver to initialize for this guest.
+ *
+ * If this is the first initialization, then we'll run through our own
+ * initilization to collect constant data from the devices (mainly, the
+ * capabilities, etc, from a synaptic touchpad).  The constant results of
+ * the initilization are stored in the kernel driver, ready for any other
+ * guests to extract.
+ * On a final close, the kernel driver will drop its cached result and the
+ * next first open will need to before the initilization again.
+ *
+ * Note, this init function needs to be able to return failure.  Currently,
+ * the API doesn't allow this. XXX markhe
+ */
+
+void
+pa2_init(
+       void)
+{
+       pa2_t           *fp;
+       pa2_const_t     *scp;
+       pa2_data_init_t buf;
+       uint8_t         byte;
+       int             ret;
+
+       ret = 0;
+       fp = &pa2_state;
+       PRINT_DEBUG_ENTER("\n");
+       ASSERT(fp->pp_fd[PA2_INDEX_KBD] == -1); 
+       ASSERT(fp->pp_fd[PA2_INDEX_AUX] == -1); 
+       ASSERT(fp->pp_fd[PA2_INDEX_CTL] == -1); 
+       fp->pp_fd[PA2_INDEX_KBD] = -1;
+       fp->pp_fd[PA2_INDEX_AUX] = -1;
+       fp->pp_fd[PA2_INDEX_CTL] = -1;
+       fp->pp_fd[PA2_INDEX_KBD] = open("/proc/8042-kbd", O_RDWR);
+       if (fp->pp_fd[PA2_INDEX_KBD] == -1) {
+               pa2_error("error opening kbd ctl file\n");
+               ret = -1;
+               goto out;
+       }
+       (void) fcntl(fp->pp_fd[PA2_INDEX_KBD], F_SETFD, FD_CLOEXEC);
+
+       fp->pp_fd[PA2_INDEX_AUX] = open("/proc/8042-aux", O_RDWR);
+       if (fp->pp_fd[PA2_INDEX_AUX] == -1) {
+               pa2_error("error opening aux ctl file\n");
+               ret = -1;
+               goto out;
+       }
+       fcntl(fp->pp_fd[PA2_INDEX_AUX], F_SETFD, FD_CLOEXEC);
+       fp->pp_fd[PA2_INDEX_CTL] = open("/proc/8042-ctl", O_RDWR);
+       if (fp->pp_fd[PA2_INDEX_CTL] == -1) {
+               pa2_error("error opening aux ctl file\n");
+               ret = -1;
+               goto out;
+       }
+       (void) fcntl(fp->pp_fd[PA2_INDEX_CTL], F_SETFD, FD_CLOEXEC);
+       ASSERT(fp->pp_active[PA2_INDEX_KBD] == 0);
+       ASSERT(fp->pp_active[PA2_INDEX_AUX] == 0);
+
+       /*
+        * First take the initialization lock.  This is allowed, even if
+        * someone else has grabbed the devices.  If someone else has
+        * grabbed, then they must have the initialization earlier.
+        */
+
+       byte = 1;
+       PRINT_DEBUG(PA2_DBG_HIGH, "Taking init lock\n");
+       if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_INIT_LOCK, &byte) == -1) {
+               pa2_error("error locking ctl file\n");
+               ret = -1;
+               goto out;
+       }
+
+       /*
+        * Query the driver for cached results
+        * If there is no cached result, then the length is 0 and we need to
+        * perform first time initialization.
+        * As all copies of ioemu running should be from the same binary,
+        * a valid result len should be the size of our pa2_state_t
+        * structure.
+        */
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "Querying init\n");
+       if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_INIT_QUERY, &buf) == -1) {
+               pa2_error("error querying ctl file\n");
+               ret = -1;
+               goto unlock;
+       }
+       PRINT_DEBUG(PA2_DBG_HIGH, "Init query result: 0x%X\n", buf.di_len);
+
+       if (buf.di_len != 0) {
+               PRINT_DEBUG(PA2_DBG_HIGH, "Loading constants\n");
+               ASSERT (buf.di_len <= PA2_DATA_INIT_MAX);
+               if (buf.di_len != sizeof (pa2_const_t)) {
+                       pa2_error("illegal init data length\n");
+                       ret = -1;
+                       goto unlock;
+               }
+               memcpy(&fp->pp_state_const, &buf.di_data[0], buf.di_len);
+               goto unlock;
+       }
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "First time initialization\n");
+       if (sizeof (pa2_const_t) > PA2_DATA_INIT_MAX) {
+               pa2_error("pa2_const_t has become larger than %d\n",
+                         PA2_DATA_INIT_MAX);
+               ret = -1;
+               goto unlock;
+       }
+       fp->pp_active[PA2_INDEX_KBD] = 1;
+       fp->pp_active[PA2_INDEX_AUX] = 1;
+       fp->pp_grabbing[PA2_INDEX_KBD] = 1;
+       fp->pp_grabbing[PA2_INDEX_AUX] = 1;
+       fp->pp_init = 1;
+       pa2_ctl_init(fp);
+       pa2_kbd_once_init(fp);
+       pa2_aux_once_init(fp);
+       fp->pp_active[PA2_INDEX_KBD] = 0;
+       fp->pp_active[PA2_INDEX_AUX] = 0;
+       fp->pp_init = 0;
+       fp->pp_grabbing[PA2_INDEX_KBD] = 0;
+       fp->pp_grabbing[PA2_INDEX_AUX] = 0;
+
+       PRINT_DEBUG(PA2_DBG_HIGH, "Saving constants\n");
+       /* double-check size - buffer overrun XXX */
+       buf.di_len = sizeof (pa2_const_t);
+       memset(&buf.di_data[0], 0, PA2_DATA_INIT_MAX);
+       memcpy(&buf.di_data[0], &fp->pp_state_const, sizeof (pa2_const_t));
+       if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_INIT_SET, &buf) == -1) {
+               pa2_error("error querying ctl file\n");
+               ret = -1;
+               /* FALLTHROUGH */
+       }
+
+unlock:
+       /*
+        * Unlock.
+        */
+
+       byte = 0;
+       if (ioctl(fp->pp_fd[PA2_INDEX_CTL], PA2_IOCTL_INIT_LOCK, &byte) == -1) {
+               pa2_error("error unlocking ctl file\n");
+               ret = -1;
+       }
+
+out:
+       if (ret != 0) {
+               if (fp->pp_fd[PA2_INDEX_KBD] != -1) {
+                       (void) close(fp->pp_fd[PA2_INDEX_KBD]);
+                       fp->pp_fd[PA2_INDEX_KBD] = -1;
+               }
+               if (fp->pp_fd[PA2_INDEX_AUX] != -1) {
+                       (void) close(fp->pp_fd[PA2_INDEX_AUX]);
+                       fp->pp_fd[PA2_INDEX_AUX] = -1;
+               }
+               if (fp->pp_fd[PA2_INDEX_CTL] != -1) {
+                       (void) close(fp->pp_fd[PA2_INDEX_CTL]);
+                       fp->pp_fd[PA2_INDEX_CTL] = -1;
+               }
+       }
+       PRINT_DEBUG_EXIT("\n");
+       /* should be able to return an error XXX */
+       return;
+}
diff --git a/pass2.h b/pass2.h
new file mode 100644 (file)
index 0000000..a06517e
--- /dev/null
+++ b/pass2.h
@@ -0,0 +1,69 @@
+#ifndef _K_PASS2_H
+#define _K_PASS2_H
+
+#define        PA2_IOCTL_GRAB          _IO('p', 0x02)
+#define        PA2_IOCTL_WR_DATA       _IO('p', 0x04)
+#define        PA2_IOCTL_WR_CMD        _IO('p', 0x06)
+#define        PA2_IOCTL_RD_RECORD     _IO('p', 0x07)
+
+#define        PA2_IOCTL_INIT_LOCK     _IO('p', 0x09)
+#define        PA2_IOCTL_INIT_QUERY    _IO('p', 0x10)
+#define        PA2_IOCTL_INIT_SET      _IO('p', 0x11)
+
+#define        PA2_IOCTL_INIT_CMD      _IO('p', 0x12)
+
+typedef struct pa2_entry_s {
+       u8      pe_status;
+       u8      pe_data;
+} pa2_entry_t;
+
+/*
+ * Data payload for PA2_IOCTL_WR_DATA.
+ */
+
+typedef struct pa2_data_s {
+       u8              pd_clear;
+       u8              pd_data;
+} pa2_data_t;
+
+/*
+ * Record header, used by PA2_IOCTL_RD_RECORD ioctl().
+ */
+
+typedef struct pa2_rec_hd_s {
+       u32             ph_rec_size;    /* in records (input) */
+       u32             ph_rec_num;     /* in records (output) */
+       u8              ph_focus;
+       u8              ph_overflow;
+       u8              ph_forced;
+       pa2_entry_t     *ph_records;
+} pa2_rec_hd_t;
+
+/*
+ *  pg_grab            - 0 = release, 1 = grab device.
+ */
+
+typedef struct pa2_grab_s {
+       u8              pg_grab;
+       u8              pg_flags;
+       u8              pg_aux_sample;
+       u8              pg_aux_res;
+       u8              pg_syn_mode_byte;
+} pa2_grab_t;
+
+/* pg_flags */
+#define        PA2_GRAB_SYNAPTIC       0x01
+
+/*
+ * Init data payload.  Used to pass discovered static data from primary
+ * VMs to secondaries.
+ */
+
+#define        PA2_DATA_INIT_MAX       2048
+
+typedef struct pa2_data_init_s {
+       u32             di_len;
+       u8              di_data[PA2_DATA_INIT_MAX];
+} pa2_data_init_t;
+
+#endif /* _K_PASS2_H */
diff --git a/vl.c b/vl.c
index cbe48f7327dafc5f6e34584186f2ba34581670d5..ea5c259faad41258531e8fe7f433709514cb5ba4 100644 (file)
--- a/vl.c
+++ b/vl.c
@@ -211,6 +211,7 @@ int opengl_enabled = 0;
 #endif
 const char *dom0_input = NULL;
 int vga_passthrough = 0;
+int ps2_passthrough = 0;
 int intel_output;
 int kbd_passthrough = 0;
 static const char *direct_pci;
@@ -7565,6 +7566,7 @@ enum {
     QEMU_OPTION_disable_opengl,
     QEMU_OPTION_dom0_input,
     QEMU_OPTION_vga_passthrough,
+    QEMU_OPTION_ps2_passthrough,
     QEMU_OPTION_kbd_passthrough,
     QEMU_OPTION_direct_pci,
     QEMU_OPTION_pci_emulation,
@@ -7681,6 +7683,7 @@ const QEMUOption qemu_options[] = {
 #endif
     { "dom0-input", 1, QEMU_OPTION_dom0_input },
     { "vga-passthrough", 0, QEMU_OPTION_vga_passthrough },
+    { "ps2-passthrough", 0, QEMU_OPTION_ps2_passthrough },
     { "kbd-passthrough", 0, QEMU_OPTION_kbd_passthrough },
     { "vcpus", 1, QEMU_OPTION_vcpus },
     { "acpi", 0, QEMU_OPTION_acpi }, /* deprecated, for xend compatibility */
@@ -8471,6 +8474,9 @@ int main(int argc, char **argv)
             case QEMU_OPTION_vga_passthrough:
                 vga_passthrough = 1;
                 break;
+            case QEMU_OPTION_ps2_passthrough:
+                ps2_passthrough = 1;
+                break;
             case QEMU_OPTION_kbd_passthrough:
                 kbd_passthrough = 1;
                 break;
index 22b70b58db84c7655b64a07deaea3b67b49b6733..8001d6175e49c44f31c8d331e4e8ade57bac60fa 100644 (file)
@@ -36,6 +36,7 @@ OBJS += battery_mgmt.o
 OBJS += pt_pckbd.o
 OBJS += xen_acpi_wmi.o
 OBJS += hid-linux.o
+OBJS += pass2.o
 OBJS += thermal_mgmt.o
 
 ifdef CONFIG_STUBDOM