]> xenbits.xensource.com Git - xenclient/ioemu-pq.git/commitdiff
Initial support for PS/2 pass-through support, rebased to latest.
authorMark Hemment <markhem@isildur.uk.xensource.com>
Fri, 29 May 2009 13:30:48 +0000 (14:30 +0100)
committerMark Hemment <markhem@isildur.uk.xensource.com>
Fri, 29 May 2009 13:30:48 +0000 (14:30 +0100)
  The pass2 ioemu 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.

master/ps2-passthrough [new file with mode: 0644]

diff --git a/master/ps2-passthrough b/master/ps2-passthrough
new file mode 100644 (file)
index 0000000..83f48b7
--- /dev/null
@@ -0,0 +1,11412 @@
+diff --git a/console.h b/console.h
+index 8c9b09b..f6915fd 100644
+--- a/console.h
++++ b/console.h
+@@ -363,6 +363,15 @@ void hid_linux_add_binding(const int *, void (*)(void*), void *);
+ void hid_linux_reset_keyboard(void);
+ void hid_linux_probe(int grab);
++/* pass2.c */
++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);
++
+ /* intel.c */
+ int intel_enter(void);
+ int intel_leave(void);
+diff --git a/dom0_driver.c b/dom0_driver.c
+index fdd24ab..f68f8d8 100644
+--- a/dom0_driver.c
++++ b/dom0_driver.c
+@@ -43,6 +43,7 @@
+ #include <time.h>
+ extern int vga_passthrough;
++extern int ps2_passthrough;
+ extern int intel;
+ static void dom0_driver_state_change(const char *path, void *opaque);
+@@ -91,6 +92,16 @@ struct dom0_driver_handler
+     void (*add_binding)(const int *, void (*)(void *), void *);
+ };
++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,
++};
++
+ struct dom0_driver_xs_info
+ {
+     int state;
+@@ -669,7 +680,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 9a5637c..f0c71e9 100644
+--- a/hw/pc.c
++++ b/hw/pc.c
+@@ -64,6 +64,7 @@ void tpm_tis_init(SetIRQFunc *set_irq, void *opaque, int irq);
+ extern uint8_t *acpi_tables;
+ extern size_t acpi_tables_len;
++extern int    ps2_passthrough;
+ static fdctrl_t *floppy_controller;
+ static RTCState *rtc_state;
+@@ -1105,7 +1106,11 @@ vga_bios_error:
+         tpm_tis_init(&i8259[11]);
+ #endif
+-    i8042_init(i8259[1], i8259[12], 0x60);
++    if (ps2_passthrough) {
++      pa2_i8042_init(i8259[1], i8259[12], 0x60);
++    } else {
++      i8042_init(i8259[1], i8259[12], 0x60);
++    }
+     DMA_init(0);
+ #ifdef HAS_AUDIO
+     audio_init(pci_enabled ? pci_bus : NULL, i8259);
+diff --git a/hw/pc.h b/hw/pc.h
+index 2e3cade..d0e0957 100644
+--- a/hw/pc.h
++++ b/hw/pc.h
+@@ -79,6 +79,9 @@ void i8042_mm_init(qemu_irq kbd_irq, qemu_irq mouse_irq,
+                    target_phys_addr_t base, ram_addr_t size,
+                    target_phys_addr_t mask);
++/* pass2.c */
++extern void   pa2_i8042_init(qemu_irq, qemu_irq, uint32_t);
++
+ /* mc146818rtc.c */
+ typedef struct RTCState RTCState;
+diff --git a/pass2.c b/pass2.c
+new file mode 100644
+index 0000000..9fa8baf
+--- /dev/null
++++ b/pass2.c
+@@ -0,0 +1,11180 @@
++/*
++ * 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;
++}
++
++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
+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 cbd561e..65a98a9 100644
+--- a/vl.c
++++ b/vl.c
+@@ -234,6 +234,7 @@ CharDriverState *virtcon_hds[MAX_VIRTIO_CONSOLES];
+ int win2k_install_hack = 0;
+ int rtc_td_hack = 0;
+ int vga_passthrough = 0;
++int ps2_passthrough;
+ const char *dom0_input = NULL;
+ int intel = 0;
+ #endif
+@@ -4293,6 +4294,7 @@ enum {
+     QEMU_OPTION_acpi,
+     QEMU_OPTION_vcpus,
+     QEMU_OPTION_vga_passthrough,
++    QEMU_OPTION_ps2_passthrough,
+     QEMU_OPTION_dom0_input,
+     QEMU_OPTION_intel,
+@@ -4469,6 +4471,7 @@ static const QEMUOption qemu_options[] = {
+     { "vncunused", 0, QEMU_OPTION_vncunused },
+     { "vcpus", HAS_ARG, QEMU_OPTION_vcpus },
+     { "vga-passthrough", 0, QEMU_OPTION_vga_passthrough },
++    { "ps2-passthrough", 0, QEMU_OPTION_ps2_passthrough },
+     { "dom0-input", 1, QEMU_OPTION_dom0_input },
+     { "intel", 0, QEMU_OPTION_intel },
+ #if defined(CONFIG_XEN) && !defined(CONFIG_DM)
+@@ -5306,6 +5309,9 @@ int main(int argc, char **argv, char **envp)
+                 cirrus_vga_enabled = 0;
+                 vmsvga_enabled = 0;
+                 break;
++            case QEMU_OPTION_ps2_passthrough:
++              ps2_passthrough = 1;
++              break;
+             case QEMU_OPTION_dom0_input:
+                 dom0_input = optarg;
+                 break;
+diff --git a/xen-hooks.mak b/xen-hooks.mak
+index 528bb7a..eb321c8 100644
+--- a/xen-hooks.mak
++++ b/xen-hooks.mak
+@@ -39,6 +39,7 @@ OBJS += xen_acpi_wmi.o
+ OBJS += thermal_mgmt.o
+ OBJS += dom0_driver.o
+ OBJS += hid-linux.o
++OBJS += pass2.o
+ OBJS += intel.o
+ CONFIG_AUDIO=1