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