#include <sys/param.h>
#include <sys/stream.h>
#include <sys/vuid_event.h>
#include "vuidmice.h"
#include <sys/vuid_wheel.h>
#include <sys/mouse.h>
#include <sys/strsun.h>
#include <sys/ddi.h>
#include <sys/sunddi.h>
#define PS2_BUTTONMASK 7
#define PS2_BUTTON_L (uchar_t)0x01
#define PS2_BUTTON_R (uchar_t)0x02
#define PS2_BUTTON_M (uchar_t)0x04
#define PS2_DATA_XSIGN (uchar_t)0x10
#define PS2_DATA_YSIGN (uchar_t)0x20
#define PS2_START 0
#define PS2_BUTTON 1
#define PS2_MAYBE_REATTACH 2
#define PS2_DELTA_Y 3
#define PS2_WHEEL_DELTA_Z 4
#define PS2_WHEEL5_DELTA_Z 5
#define PS2_WAIT_RESET_COMPLETE 6
#define PS2_WAIT_FOR_AA 7
#define PS2_WAIT_FOR_00 8
#define PS2_WAIT_SETRES0_ACK1 9
#define PS2_WAIT_SETRES0_ACK2 10
#define PS2_WAIT_SCALE1_1_ACK 11
#define PS2_WAIT_SCALE1_2_ACK 12
#define PS2_WAIT_SCALE1_3_ACK 13
#define PS2_WAIT_STATREQ_ACK 14
#define PS2_WAIT_STATUS_1 15
#define PS2_WAIT_STATUS_BUTTONS 16
#define PS2_WAIT_STATUS_REV 17
#define PS2_WAIT_STATUS_3 18
#define PS2_WAIT_WHEEL_SMPL1_CMD_ACK 19
#define PS2_WAIT_WHEEL_SMPL1_RATE_ACK 20
#define PS2_WAIT_WHEEL_SMPL2_CMD_ACK 21
#define PS2_WAIT_WHEEL_SMPL2_RATE_ACK 22
#define PS2_WAIT_WHEEL_SMPL3_CMD_ACK 23
#define PS2_WAIT_WHEEL_SMPL3_RATE_ACK 24
#define PS2_WAIT_WHEEL_DEV_CMD 25
#define PS2_WAIT_WHEEL_DEV_ACK 26
#define PS2_WAIT_WHEEL5_SMPL1_CMD_ACK 27
#define PS2_WAIT_WHEEL5_SMPL1_RATE_ACK 28
#define PS2_WAIT_WHEEL5_SMPL2_CMD_ACK 29
#define PS2_WAIT_WHEEL5_SMPL2_RATE_ACK 30
#define PS2_WAIT_WHEEL5_SMPL3_CMD_ACK 31
#define PS2_WAIT_WHEEL5_SMPL3_RATE_ACK 32
#define PS2_WAIT_WHEEL5_DEV_CMD 33
#define PS2_WAIT_WHEEL5_DEV_ACK 34
#define PS2_WAIT_SETRES3_CMD 35
#define PS2_WAIT_SETRES3_ACK1 36
#define PS2_WAIT_SETRES3_ACK2 37
#define PS2_WAIT_STREAM_ACK 38
#define PS2_WAIT_ON_ACK 39
#define MOUSE_MODE_PLAIN 0
#define MOUSE_MODE_WHEEL 1
#define MOUSE_MODE_WHEEL5 2
#define PS2_FLAG_NO_EXTN 0x08
#define PS2_FLAG_INIT_DONE 0x01
#define PS2_FLAG_INIT_TIMEOUT 0x02
#define PS2_INIT_TMOUT_RESET 500000
#define PS2_INIT_TMOUT_PER_CMD 200000
#define PS2_INIT_TMOUT_PER_GROUP 500000
#define PS2_MAX_INIT_COUNT 5
static void vuidmice_send_wheel_event(queue_t *const, uchar_t,
uchar_t, uchar_t, int);
extern void VUID_PUTNEXT(queue_t *const, uchar_t, uchar_t, uchar_t, int);
extern void uniqtime32(struct timeval32 *);
static void VUID_INIT_TIMEOUT(void *q);
static void
vuid_set_timeout(queue_t *const qp, clock_t time)
{
if (STATEP->init_tid != 0)
(void) quntimeout(qp, STATEP->init_tid);
STATEP->init_tid = qtimeout(qp, VUID_INIT_TIMEOUT,
qp, drv_usectohz(time));
}
static void
vuid_cancel_timeout(queue_t *const qp)
{
(void) quntimeout(qp, STATEP->init_tid);
STATEP->init_tid = 0;
}
static void
vuidmice_send_wheel_event(queue_t *const qp, uchar_t event_id,
uchar_t event_pair_type, uchar_t event_pair, int event_value)
{
mblk_t *bp;
Firm_event *fep;
if ((bp = allocb((int)sizeof (Firm_event), BPRI_HI)) == NULL) {
return;
}
fep = (void *)bp->b_wptr;
fep->id = vuid_id_addr(vuid_first(VUID_WHEEL)) |
vuid_id_offset(event_id);
fep->pair_type = event_pair_type;
fep->pair = event_pair;
fep->value = event_value;
uniqtime32(&fep->time);
bp->b_wptr += sizeof (Firm_event);
if (canput(qp->q_next)) {
putnext(qp, bp);
} else {
(void) putbq(qp, bp);
}
}
static void
sendButtonEvent(queue_t *const qp)
{
static int bmap[3] = {1, 3, 2};
uint_t b;
for (b = 0; b < STATEP->nbuttons; b++) {
uchar_t mask = 0x1 << b;
if ((STATEP->buttons & mask) != (STATEP->oldbuttons & mask))
VUID_PUTNEXT(qp, (uchar_t)BUT(bmap[b]), FE_PAIR_NONE, 0,
(STATEP->buttons & mask ? 1 : 0));
}
}
void
put1(queue_t *const qp, int c)
{
mblk_t *bp;
if (bp = allocb(1, BPRI_MED)) {
*bp->b_wptr++ = (char)c;
putnext(qp, bp);
}
}
static void
vuidmice_start_wdc_or_setres(queue_t *qp)
{
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
if (STATEP->inited & PS2_FLAG_NO_EXTN) {
STATEP->state = PS2_WAIT_SETRES3_ACK1;
put1(WR(qp), MSESETRES);
} else {
STATEP->state = PS2_WAIT_WHEEL_SMPL1_CMD_ACK;
put1(WR(qp), MSECHGMOD);
}
}
int
VUID_OPEN(queue_t *const qp)
{
STATEP->format = VUID_FIRM_EVENT;
STATEP->vuid_mouse_mode = MOUSE_MODE_PLAIN;
STATEP->inited = 0;
STATEP->nbuttons = 3;
STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
while ((STATEP->state != PS2_START) &&
!(STATEP->inited & PS2_FLAG_INIT_TIMEOUT)) {
if (qwait_sig(qp) == 0)
break;
}
STATEP->init_count = 0;
return (0);
}
void
VUID_CLOSE(queue_t *const qp)
{
if (STATEP->init_tid != 0)
vuid_cancel_timeout(qp);
}
static void
VUID_INIT_TIMEOUT(void *q)
{
queue_t *qp = q;
STATEP->init_tid = 0;
if ((STATEP->state == PS2_WAIT_WHEEL_SMPL1_CMD_ACK) ||
(STATEP->state == PS2_WAIT_WHEEL_SMPL1_RATE_ACK) ||
(STATEP->state == PS2_WAIT_WHEEL_SMPL2_RATE_ACK) ||
(STATEP->state == PS2_WAIT_WHEEL_SMPL3_RATE_ACK)) {
STATEP->inited |= PS2_FLAG_NO_EXTN;
}
if (STATEP->state >= PS2_WAIT_STATREQ_ACK &&
STATEP->state <= PS2_WAIT_STATUS_REV) {
#if defined(VUID3PS2)
STATEP->nbuttons = 3;
#else
STATEP->nbuttons = 2;
#endif
vuidmice_start_wdc_or_setres(qp);
return;
}
if (++STATEP->init_count >= PS2_MAX_INIT_COUNT) {
STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
STATEP->state = PS2_WAIT_FOR_AA;
} else {
STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
}
}
void
VUID_QUEUE(queue_t *const qp, mblk_t *mp)
{
int code, length;
clock_t now;
clock_t elapsed;
clock_t mouse_timeout;
mouse_timeout = drv_usectohz(250000);
now = ddi_get_lbolt();
elapsed = now - STATEP->last_event_lbolt;
STATEP->last_event_lbolt = now;
while (mp->b_rptr < mp->b_wptr) {
length = MBLKL(mp);
code = *mp->b_rptr++;
switch (STATEP->state) {
restart:
case PS2_START:
if (!(STATEP->inited & PS2_FLAG_INIT_DONE)) {
STATEP->sync_byte = code & 0x8;
STATEP->inited |= PS2_FLAG_INIT_DONE;
}
if ((code ^ STATEP->sync_byte) & 0x08) {
STATEP->state = PS2_START;
break;
}
STATEP->buttons = code & PS2_BUTTONMASK;
if (STATEP->buttons != STATEP->oldbuttons) {
sendButtonEvent(qp);
STATEP->oldbuttons = STATEP->buttons;
}
if (code & PS2_DATA_YSIGN)
STATEP->deltay = -1 & ~0xff;
else
STATEP->deltay = 0;
if (code & PS2_DATA_XSIGN)
STATEP->deltax = -1 & ~0xff;
else
STATEP->deltax = 0;
if (code == MSE_AA)
STATEP->state = PS2_MAYBE_REATTACH;
else
STATEP->state = PS2_BUTTON;
break;
case PS2_WAIT_FOR_AA:
if (code == MSE_ACK) {
STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
STATEP->init_count = 0;
STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
break;
}
if (code != MSE_AA)
break;
STATEP->state = PS2_WAIT_FOR_00;
break;
case PS2_WAIT_FOR_00:
if (code != MSE_00)
break;
STATEP->inited &= ~PS2_FLAG_INIT_TIMEOUT;
STATEP->init_count = 0;
STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
break;
case PS2_MAYBE_REATTACH:
if (code == MSE_00) {
STATEP->state = PS2_WAIT_RESET_COMPLETE;
put1(WR(qp), MSERESET);
break;
}
case PS2_BUTTON:
if (elapsed > mouse_timeout)
goto restart;
STATEP->deltax |= code & 0xff;
STATEP->state = PS2_DELTA_Y;
break;
case PS2_DELTA_Y:
if (elapsed > mouse_timeout) {
goto restart;
}
STATEP->deltay |= code & 0xff;
if (STATEP->vuid_mouse_mode == MOUSE_MODE_WHEEL) {
STATEP->state = PS2_WHEEL_DELTA_Z;
break;
} else if (STATEP->vuid_mouse_mode ==
MOUSE_MODE_WHEEL5) {
STATEP->state = PS2_WHEEL5_DELTA_Z;
break;
}
goto packet_complete;
case PS2_WHEEL5_DELTA_Z:
if (code & 0x10) {
VUID_PUTNEXT(qp, (uchar_t)BUT(4),
FE_PAIR_NONE, 0, 1);
VUID_PUTNEXT(qp, (uchar_t)BUT(4),
FE_PAIR_NONE, 0, 0);
} else if (code & 0x20) {
VUID_PUTNEXT(qp, (uchar_t)BUT(5),
FE_PAIR_NONE, 0, 1);
VUID_PUTNEXT(qp, (uchar_t)BUT(5),
FE_PAIR_NONE, 0, 0);
}
case PS2_WHEEL_DELTA_Z:
code &= 0xf;
if (STATEP->wheel_state_bf & (1 <<
VUIDMICE_VERTICAL_WHEEL_ID)) {
if (code == 0xf) {
code |= 0xfffffff0;
vuidmice_send_wheel_event(qp, 0,
FE_PAIR_NONE, 0, -code);
} else if (code == 0x01) {
vuidmice_send_wheel_event(qp, 0,
FE_PAIR_NONE, 0, -code);
}
}
if (STATEP->wheel_state_bf &
(1 << VUIDMICE_HORIZONTAL_WHEEL_ID)) {
if (code == 0x09) {
vuidmice_send_wheel_event(qp, 1,
FE_PAIR_NONE, 0, 1);
} else if (code == 0x07) {
vuidmice_send_wheel_event(qp, 1,
FE_PAIR_NONE, 0, -1);
}
}
packet_complete:
STATEP->state = PS2_START;
if (mp->b_wptr > mp->b_rptr &&
((mp->b_rptr[0] ^ STATEP->sync_byte) & 0x08)) {
break;
}
if (STATEP->deltax)
VUID_PUTNEXT(qp, (uchar_t)LOC_X_DELTA,
FE_PAIR_ABSOLUTE, (uchar_t)LOC_X_ABSOLUTE,
STATEP->deltax);
if (STATEP->deltay)
VUID_PUTNEXT(qp, (uchar_t)LOC_Y_DELTA,
FE_PAIR_ABSOLUTE, (uchar_t)LOC_Y_ABSOLUTE,
STATEP->deltay);
STATEP->deltax = STATEP->deltay = 0;
break;
case PS2_WAIT_RESET_COMPLETE:
if (length == 1) {
if (code == MSE_ACK)
break;
if (++STATEP->init_count >=
PS2_MAX_INIT_COUNT) {
STATEP->inited |= PS2_FLAG_INIT_TIMEOUT;
STATEP->state = PS2_WAIT_FOR_AA;
} else {
put1(WR(qp), MSERESET);
}
break;
} else if (length != 2) {
break;
}
mp->b_rptr += 1;
STATEP->state = PS2_WAIT_SETRES0_ACK1;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
put1(WR(qp), MSESETRES);
break;
case PS2_WAIT_SETRES0_ACK1:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_SETRES0_ACK2;
put1(WR(qp), 0);
break;
case PS2_WAIT_SETRES0_ACK2:
case PS2_WAIT_SCALE1_1_ACK:
case PS2_WAIT_SCALE1_2_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state++;
put1(WR(qp), MSESCALE1);
break;
case PS2_WAIT_SCALE1_3_ACK:
if (code != MSE_ACK) {
break;
}
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_STATREQ_ACK;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
put1(WR(qp), MSESTATREQ);
break;
case PS2_WAIT_STATREQ_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_STATUS_1;
break;
case PS2_WAIT_STATUS_1:
STATEP->state = PS2_WAIT_STATUS_BUTTONS;
break;
case PS2_WAIT_STATUS_BUTTONS:
if (code != 0) {
STATEP->nbuttons = (uchar_t)code;
STATEP->state = (uchar_t)PS2_WAIT_STATUS_REV;
} else {
#if defined(VUID3PS2)
STATEP->nbuttons = 3;
#else
STATEP->nbuttons = 2;
#endif
STATEP->state = PS2_WAIT_STATUS_3;
}
break;
case PS2_WAIT_STATUS_REV:
case PS2_WAIT_STATUS_3:
vuid_cancel_timeout(qp);
vuidmice_start_wdc_or_setres(qp);
break;
case PS2_WAIT_WHEEL_SMPL1_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_SMPL1_RATE_ACK;
put1(WR(qp), 200);
break;
case PS2_WAIT_WHEEL_SMPL1_RATE_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_SMPL2_CMD_ACK;
put1(WR(qp), MSECHGMOD);
break;
case PS2_WAIT_WHEEL_SMPL2_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_SMPL2_RATE_ACK;
put1(WR(qp), 100);
break;
case PS2_WAIT_WHEEL_SMPL2_RATE_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_SMPL3_CMD_ACK;
put1(WR(qp), MSECHGMOD);
break;
case PS2_WAIT_WHEEL_SMPL3_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_SMPL3_RATE_ACK;
put1(WR(qp), 80);
break;
case PS2_WAIT_WHEEL_SMPL3_RATE_ACK:
if (code != MSE_ACK) {
break;
}
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_WHEEL_DEV_CMD;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
put1(WR(qp), MSEGETDEV);
break;
case PS2_WAIT_WHEEL_DEV_CMD:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL_DEV_ACK;
break;
case PS2_WAIT_WHEEL_DEV_ACK:
vuid_cancel_timeout(qp);
if (code != 0x03) {
STATEP->state = PS2_WAIT_SETRES3_ACK1;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
put1(WR(qp), MSESETRES);
break;
}
STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL;
STATEP->wheel_state_bf |= VUID_WHEEL_STATE_ENABLED;
STATEP->state = PS2_WAIT_WHEEL5_SMPL1_CMD_ACK;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_GROUP);
put1(WR(qp), MSECHGMOD);
break;
case PS2_WAIT_WHEEL5_SMPL1_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_SMPL1_RATE_ACK;
put1(WR(qp), 200);
break;
case PS2_WAIT_WHEEL5_SMPL1_RATE_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_SMPL2_CMD_ACK;
put1(WR(qp), MSECHGMOD);
break;
case PS2_WAIT_WHEEL5_SMPL2_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_SMPL2_RATE_ACK;
put1(WR(qp), 200);
break;
case PS2_WAIT_WHEEL5_SMPL2_RATE_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_SMPL3_CMD_ACK;
put1(WR(qp), MSECHGMOD);
break;
case PS2_WAIT_WHEEL5_SMPL3_CMD_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_SMPL3_RATE_ACK;
put1(WR(qp), 80);
break;
case PS2_WAIT_WHEEL5_SMPL3_RATE_ACK:
if (code != MSE_ACK) {
break;
}
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_WHEEL5_DEV_CMD;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
put1(WR(qp), MSEGETDEV);
break;
case PS2_WAIT_WHEEL5_DEV_CMD:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_WHEEL5_DEV_ACK;
break;
case PS2_WAIT_WHEEL5_DEV_ACK:
if (code == 0x04) {
STATEP->vuid_mouse_mode = MOUSE_MODE_WHEEL5;
STATEP->nbuttons = 5;
STATEP->wheel_state_bf |=
VUID_WHEEL_STATE_ENABLED <<
MOUSE_MODE_WHEEL;
}
vuid_cancel_timeout(qp);
case PS2_WAIT_SETRES3_CMD:
STATEP->state = PS2_WAIT_SETRES3_ACK1;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
put1(WR(qp), MSESETRES);
break;
case PS2_WAIT_SETRES3_ACK1:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_SETRES3_ACK2;
put1(WR(qp), 3);
break;
case PS2_WAIT_SETRES3_ACK2:
if (code != MSE_ACK) {
break;
}
vuid_cancel_timeout(qp);
STATEP->state = PS2_WAIT_STREAM_ACK;
vuid_set_timeout(qp, PS2_INIT_TMOUT_PER_CMD);
put1(WR(qp), MSESTREAM);
break;
case PS2_WAIT_STREAM_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->state = PS2_WAIT_ON_ACK;
put1(WR(qp), MSEON);
break;
case PS2_WAIT_ON_ACK:
if (code != MSE_ACK) {
break;
}
STATEP->init_count = 0;
vuid_cancel_timeout(qp);
STATEP->state = PS2_START;
break;
}
}
freemsg(mp);
}