--- /dev/null
+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