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