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