Delete old c files.

git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41130 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Clemens Zeidler
2011-03-28 02:23:07 +00:00
parent 278a2b8aaa
commit 5a63a4576e
6 changed files with 0 additions and 2994 deletions
@@ -1,314 +0,0 @@
#include "movement_maker.h"
#include <Debug.h>
#if 1
# define INFO(x...) dprintf(x)
#else
# define INFO(x...)
#endif
typedef union {
float value;
/* FIXME: Assumes 32 bit int. */
unsigned int word;
} ieee_float_shape_type;
/* Get a 32 bit int from a float. */
#define GET_FLOAT_WORD(i,d) \
do { \
ieee_float_shape_type gf_u; \
gf_u.value = (d); \
(i) = gf_u.word; \
} while (0)
/* Set a float from a 32 bit int. */
#define SET_FLOAT_WORD(d,i) \
do { \
ieee_float_shape_type sf_u; \
sf_u.word = (i); \
(d) = sf_u.value; \
} while (0)
static const float huge = 1.0e30;
float
floorf(float x)
{
int32 i0,j0;
uint32 i;
GET_FLOAT_WORD(i0,x);
j0 = ((i0>>23)&0xff)-0x7f;
if (j0<23) {
if (j0<0) { /* raise inexact if x != 0 */
if (huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */
if (i0>=0) {i0=0;}
else if ((i0&0x7fffffff)!=0)
{ i0=0xbf800000;}
}
} else {
i = (0x007fffff)>>j0;
if ((i0&i)==0) return x; /* x is integral */
if (huge+x>(float)0.0) { /* raise inexact flag */
if (i0<0) i0 += (0x00800000)>>j0;
i0 &= (~i);
}
}
} else {
if (j0==0x80) return x+x; /* inf or NaN */
else return x; /* x is integral */
}
SET_FLOAT_WORD(x,i0);
return x;
}
float
ceilf(float x)
{
int32 i0,j0;
uint32 i;
GET_FLOAT_WORD(i0,x);
j0 = ((i0>>23)&0xff)-0x7f;
if (j0<23) {
if (j0<0) { /* raise inexact if x != 0 */
if (huge+x>(float)0.0) {/* return 0*sign(x) if |x|<1 */
if (i0<0) {i0=0x80000000;}
else if (i0!=0) { i0=0x3f800000;}
}
} else {
i = (0x007fffff)>>j0;
if ((i0&i)==0) return x; /* x is integral */
if (huge+x>(float)0.0) { /* raise inexact flag */
if (i0>0) i0 += (0x00800000)>>j0;
i0 &= (~i);
}
}
} else {
if (j0==0x80) return x+x; /* inf or NaN */
else return x; /* x is integral */
}
SET_FLOAT_WORD(x,i0);
return x;
}
static const float one = 1.0, tiny=1.0e-30;
float
sqrtf(float x)
{
float z;
int32 sign = (int)0x80000000;
int32 ix,s,q,m,t,i;
uint32 r;
GET_FLOAT_WORD(ix,x);
/* take care of Inf and NaN */
if ((ix&0x7f800000)==0x7f800000) {
return x*x+x; /* sqrt(NaN)=NaN, sqrt(+inf)=+inf
sqrt(-inf)=sNaN */
}
/* take care of zero */
if (ix<=0) {
if ((ix&(~sign))==0) return x;/* sqrt(+-0) = +-0 */
else if (ix<0)
return (x-x)/(x-x); /* sqrt(-ve) = sNaN */
}
/* normalize x */
m = (ix>>23);
if (m==0) { /* subnormal x */
for(i=0;(ix&0x00800000)==0;i++) ix<<=1;
m -= i-1;
}
m -= 127; /* unbias exponent */
ix = (ix&0x007fffff)|0x00800000;
if (m&1) /* odd m, double x to make it even */
ix += ix;
m >>= 1; /* m = [m/2] */
/* generate sqrt(x) bit by bit */
ix += ix;
q = s = 0; /* q = sqrt(x) */
r = 0x01000000; /* r = moving bit from right to left */
while(r!=0) {
t = s+r;
if (t<=ix) {
s = t+r;
ix -= t;
q += r;
}
ix += ix;
r>>=1;
}
/* use floating add to find out rounding direction */
if (ix!=0) {
z = one-tiny; /* trigger inexact flag */
if (z>=one) {
z = one+tiny;
if (z>one)
q += 2;
else
q += (q&1);
}
}
ix = (q>>1)+0x3f000000;
ix += (m <<23);
SET_FLOAT_WORD(z,ix);
return z;
}
int32
make_small(float value)
{
if (value > 0)
return floorf(value);
else
return ceilf(value);
}
void
get_raw_movement(movement_maker *move, uint32 posX, uint32 posY)
{
const float acceleration = 0.8;
const float translation = 12.0;
int diff;
float xDelta, yDelta;
if (move->movementStarted) {
move->movementStarted = false;
// init delta tracking
move->previousX = posX;
move->previousY = posY;
// deltas are automatically reset
}
// accumulate delta and store current pos, reset if pos did not change
diff = posX - move->previousX;
// lessen the effect of small diffs
if ((diff > -3 && diff < -1) || (diff > 1 && diff < 3))
diff /= 2;
if (diff == 0)
move->deltaSumX = 0.0;
else
move->deltaSumX += diff;
diff = posY - move->previousY;
// lessen the effect of small diffs
if ((diff > -3 && diff < -1) || (diff > 1 && diff < 3))
diff /= 2;
if (diff == 0)
move->deltaSumY = 0.0;
else
move->deltaSumY += diff;
move->previousX = posX;
move->previousY = posY;
// compute current delta and reset accumulated delta if
// abs() is greater than 1
xDelta = move->deltaSumX / translation;
yDelta = move->deltaSumY / translation;
if (xDelta > 1.0) {
move->deltaSumX = 0.0;
xDelta = 1.0 + (xDelta - 1.0) * acceleration;
} else if (xDelta < -1.0) {
move->deltaSumX = 0.0;
xDelta = -1.0 + (xDelta + 1.0) * acceleration;
}
if (yDelta > 1.0) {
move->deltaSumY = 0.0;
yDelta = 1.0 + (yDelta - 1.0) * acceleration;
} else if (yDelta < -1.0) {
move->deltaSumY = 0.0;
yDelta = -1.0 + (yDelta + 1.0) * acceleration;
}
move->xDelta = make_small(xDelta);
move->yDelta = make_small(yDelta);
}
void
compute_acceleration(movement_maker *move, int8 accel_factor)
{
// acceleration
float acceleration = 1;
if (accel_factor != 0) {
acceleration = 1 + sqrtf(move->xDelta * move->xDelta
+ move->yDelta * move->yDelta) * accel_factor / 50.0;
}
move->xDelta = make_small(move->xDelta * acceleration);
move->yDelta = make_small(move->yDelta * acceleration);
}
void
get_movement(movement_maker *move, uint32 posX, uint32 posY)
{
get_raw_movement(move, posX, posY);
// INFO("SYN: pos: %lu x %lu, delta: %ld x %ld, sums: %ld x %ld\n",
// posX, posY, move->xDelta, move->yDelta,
// move->deltaSumX, move->deltaSumY);
move->xDelta = move->xDelta * move->speed;
move->yDelta = move->yDelta * move->speed;
}
void
get_scrolling(movement_maker *move, uint32 posX, uint32 posY)
{
int32 stepsX = 0, stepsY = 0;
get_raw_movement(move, posX, posY);
compute_acceleration(move, move->scroll_acceleration);
if (move->scrolling_xStep > 0) {
move->scrolling_x += move->xDelta;
stepsX = make_small(move->scrolling_x / move->scrolling_xStep);
move->scrolling_x -= stepsX * move->scrolling_xStep;
move->xDelta = stepsX;
} else {
move->scrolling_x = 0;
move->xDelta = 0;
}
if (move->scrolling_yStep > 0) {
move->scrolling_y += move->yDelta;
stepsY = make_small(move->scrolling_y / move->scrolling_yStep);
move->scrolling_y -= stepsY * move->scrolling_yStep;
move->yDelta = -1 * stepsY;
} else {
move->scrolling_y = 0;
move->yDelta = 0;
}
}
void
start_new_movment(movement_maker *move)
{
if (move->scrolling_xStep <= 0)
move->scrolling_xStep = 1;
if (move->scrolling_yStep <= 0)
move->scrolling_yStep = 1;
move->movementStarted = true;
move->scrolling_x = 0;
move->scrolling_y = 0;
}
@@ -1,836 +0,0 @@
/*
* Copyright 2011, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* The alps_model_info struct and all the hardware specs are taken from the
* linux driver, thanks a lot!
*
* Authors:
* Clemens Zeidler ([email protected])
*/
#include "ps2_alps.h"
#include <stdlib.h>
#include <string.h>
#include <keyboard_mouse_driver.h>
#include "ps2_service.h"
const char* kALPSPath[4] = {
"input/touchpad/ps2/alps_0",
"input/touchpad/ps2/alps_1",
"input/touchpad/ps2/alps_2",
"input/touchpad/ps2/alps_3"
};
enum button_ids
{
kLeftButton = 0x01,
kRightButton = 0x02
};
typedef struct alps_model_info {
uint8 id[3];
uint8 firstByte;
uint8 maskFirstByte;
uint8 flags;
} alps_model_info;
#define ALPS_OLDPROTO 0x01 // old style input
#define ALPS_DUALPOINT 0x02 // touchpad has trackstick
#define ALPS_PASS 0x04 // device has a pass-through port
#define ALPS_WHEEL 0x08 // hardware wheel present
#define ALPS_FW_BK_1 0x10 // front & back buttons present
#define ALPS_FW_BK_2 0x20 // front & back buttons present
#define ALPS_FOUR_BUTTONS 0x40 // 4 direction button present
#define ALPS_PS2_INTERLEAVED 0x80 // 3-byte PS/2 packet interleaved with
// 6-byte ALPS packet
static const struct alps_model_info gALPSModelInfos[] = {
// {{0x32, 0x02, 0x14}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
// Toshiba Salellite Pro M10
// {{0x33, 0x02, 0x0a}, 0x88, 0xf8, ALPS_OLDPROTO},
// UMAX-530T
{{0x53, 0x02, 0x0a}, 0xf8, 0xf8, 0},
{{0x53, 0x02, 0x14}, 0xf8, 0xf8, 0},
{{0x60, 0x03, 0xc8}, 0xf8, 0xf8, 0},
// HP ze1115
{{0x63, 0x02, 0x0a}, 0xf8, 0xf8, 0},
{{0x63, 0x02, 0x14}, 0xf8, 0xf8, 0},
// {{0x63, 0x02, 0x28}, 0xf8, 0xf8, ALPS_FW_BK_2},
// Fujitsu Siemens S6010
// {{0x63, 0x02, 0x3c}, 0x8f, 0x8f, ALPS_WHEEL},
// Toshiba Satellite S2400-103
// {{0x63, 0x02, 0x50}, 0xef, 0xef, ALPS_FW_BK_1},
// NEC Versa L320
{{0x63, 0x02, 0x64}, 0xf8, 0xf8, 0},
// {{0x63, 0x03, 0xc8}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
// Dell Latitude D800
{{0x73, 0x00, 0x0a}, 0xf8, 0xf8, ALPS_DUALPOINT},
// ThinkPad R61 8918-5QG, x301
{{0x73, 0x02, 0x0a}, 0xf8, 0xf8, 0},
// {{0x73, 0x02, 0x14}, 0xf8, 0xf8, ALPS_FW_BK_2},
// Ahtec Laptop
// {{0x20, 0x02, 0x0e}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
// XXX
// {{0x22, 0x02, 0x0a}, 0xf8, 0xf8, ALPS_PASS | ALPS_DUALPOINT},
// {{0x22, 0x02, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT},
// Dell Latitude D600
// {{0x62, 0x02, 0x14}, 0xcf, 0xcf, ALPS_PASS | ALPS_DUALPOINT
// | ALPS_PS2_INTERLEAVED},
// Dell Latitude E5500, E6400, E6500, Precision M4400
// {{0x73, 0x02, 0x50}, 0xcf, 0xcf, ALPS_FOUR_BUTTONS},
// Dell Vostro 1400
// {{0x52, 0x01, 0x14}, 0xff, 0xff, ALPS_PASS | ALPS_DUALPOINT
// | ALPS_PS2_INTERLEAVED},
// Toshiba Tecra A11-11L
{{0, 0, 0}, 0, 0, 0}
};
static alps_model_info* sFoundModel = NULL;
#define PS2_MOUSE_CMD_SET_SCALE11 0xe6
#define PS2_MOUSE_CMD_SET_SCALE21 0xe7
#define PS2_MOUSE_CMD_SET_RES 0xe8
#define PS2_MOUSE_CMD_GET_INFO 0xe9
#define PS2_MOUSE_CMD_SET_STREAM 0xea
#define PS2_MOUSE_CMD_SET_POLL 0xf0
#define PS2_MOUSE_CMD_SET_RATE 0xf3
// touchpad proportions
#define SPEED_FACTOR 4.5
#define EDGE_MOTION_WIDTH 10
#define EDGE_MOTION_SPEED (5 * SPEED_FACTOR)
// increase the touchpad size a little bit
#define AREA_START_X 50
#define AREA_END_X 985
#define AREA_WIDTH_X (AREA_END_X - AREA_START_X)
#define AREA_START_Y 45
#define AREA_END_Y 735
#define AREA_WIDTH_Y (AREA_END_Y - AREA_START_Y)
#define TAP_TIMEOUT 200000
#define MIN_PRESSURE 15
#define MAX_PRESSURE 115
#define ALPS_HISTORY_SIZE 256
typedef struct {
uint8 buttons;
uint32 xPosition;
uint32 yPosition;
uint8 zPressure;
// absolut mode
bool finger;
// absolut w mode
uint8 wValue;
} touch_event;
static bool
edge_motion(mouse_movement *movement, touch_event *event, bool validStart)
{
int32 xdelta = 0;
int32 ydelta = 0;
if (event->xPosition < AREA_START_X + EDGE_MOTION_WIDTH)
xdelta = -EDGE_MOTION_SPEED;
else if (event->xPosition > AREA_END_X - EDGE_MOTION_WIDTH)
xdelta = EDGE_MOTION_SPEED;
if (event->yPosition < AREA_START_Y + EDGE_MOTION_WIDTH)
ydelta = -EDGE_MOTION_SPEED;
else if (event->yPosition > AREA_END_Y - EDGE_MOTION_WIDTH)
ydelta = EDGE_MOTION_SPEED;
if (xdelta && validStart)
movement->xdelta = xdelta;
if (ydelta && validStart)
movement->ydelta = ydelta;
if ((xdelta || ydelta) && !validStart)
return false;
return true;
}
/*! If a button has been clicked (movement->buttons must be set accordingly),
this function updates the click_count of the \a cookie, as well as the
\a movement's clicks field.
Also, it sets the cookie's button state from movement->buttons.
*/
static inline void
update_buttons(alps_cookie *cookie, mouse_movement *movement)
{
// set click count correctly according to double click timeout
if (movement->buttons != 0 && cookie->buttons_state == 0) {
if (cookie->click_last_time + cookie->click_speed > movement->timestamp)
cookie->click_count++;
else
cookie->click_count = 1;
cookie->click_last_time = movement->timestamp;
}
if (movement->buttons != 0)
movement->clicks = cookie->click_count;
cookie->buttons_state = movement->buttons;
}
static inline void
no_touch_to_movement(alps_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
uint32 buttons = event->buttons;
TRACE("ALPS: no touch event\n");
cookie->scrolling_started = false;
cookie->movement_started = false;
if (cookie->tapdrag_started
&& (movement->timestamp - cookie->tap_time) < TAP_TIMEOUT) {
buttons = 0x01;
}
// if the movement stopped switch off the tap drag when timeout is expired
if ((movement->timestamp - cookie->tap_time) > TAP_TIMEOUT) {
cookie->tapdrag_started = false;
cookie->valid_edge_motion = false;
TRACE("ALPS: tap drag gesture timed out\n");
}
if (abs(cookie->tap_delta_x) > 15 || abs(cookie->tap_delta_y) > 15) {
cookie->tap_started = false;
cookie->tap_clicks = 0;
}
if (cookie->tap_started || cookie->double_click) {
TRACE("ALPS: tap gesture\n");
cookie->tap_clicks++;
if (cookie->tap_clicks > 1) {
TRACE("ALPS: empty click\n");
buttons = 0x00;
cookie->tap_clicks = 0;
cookie->double_click = true;
} else {
buttons = 0x01;
cookie->tap_started = false;
cookie->tapdrag_started = true;
cookie->double_click = false;
}
}
movement->buttons = buttons;
update_buttons(cookie, movement);
}
static inline void
move_to_movement(alps_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
touchpad_settings *settings = &cookie->settings;
bool isStartOfMovement = false;
float pressure = 0;
TRACE("ALPS: movement event\n");
if (!cookie->movement_started) {
isStartOfMovement = true;
cookie->movement_started = true;
start_new_movment(&cookie->movement_maker);
}
get_movement(&cookie->movement_maker, event->xPosition, event->yPosition);
movement->xdelta = cookie->movement_maker.xDelta;
movement->ydelta = cookie->movement_maker.yDelta;
// tap gesture
cookie->tap_delta_x += cookie->movement_maker.xDelta;
cookie->tap_delta_y += cookie->movement_maker.yDelta;
if (cookie->tapdrag_started) {
movement->buttons = kLeftButton;
movement->clicks = 0;
cookie->valid_edge_motion = edge_motion(movement, event,
cookie->valid_edge_motion);
TRACE("ALPS: tap drag\n");
} else {
TRACE("ALPS: movement set buttons\n");
movement->buttons = event->buttons;
}
// use only a fraction of pressure range, the max pressure seems to be
// to high
pressure = 20 * (event->zPressure - MIN_PRESSURE)
/ (MAX_PRESSURE - MIN_PRESSURE - 40);
if (!cookie->tap_started
&& isStartOfMovement
&& settings->tapgesture_sensibility > 0.
&& settings->tapgesture_sensibility > (20 - pressure)) {
TRACE("ALPS: tap started\n");
cookie->tap_started = true;
cookie->tap_time = system_time();
cookie->tap_delta_x = 0;
cookie->tap_delta_y = 0;
}
update_buttons(cookie, movement);
}
/*! Checks if this is a scrolling event or not, and also actually does the
scrolling work if it is.
\return \c true if this was a scrolling event, \c false if not.
*/
static inline bool
check_scrolling_to_movement(alps_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
touchpad_settings *settings = &cookie->settings;
bool isSideScrollingV = false;
bool isSideScrollingH = false;
// if a button is pressed don't allow to scroll, we likely be in a drag
// action
if (cookie->buttons_state != 0)
return false;
if ((AREA_END_X - AREA_WIDTH_X * settings->scroll_rightrange
< event->xPosition && !cookie->movement_started
&& settings->scroll_rightrange > 0.000001)
|| settings->scroll_rightrange > 0.999999) {
isSideScrollingV = true;
}
if ((AREA_START_Y + AREA_WIDTH_Y * settings->scroll_bottomrange
> event->yPosition && !cookie->movement_started
&& settings->scroll_bottomrange > 0.000001)
|| settings->scroll_bottomrange > 0.999999) {
isSideScrollingH = true;
}
if ((event->wValue == 0 || event->wValue == 1)
&& settings->scroll_twofinger) {
// two finger scrolling is enabled
isSideScrollingV = true;
isSideScrollingH = settings->scroll_twofinger_horizontal;
}
if (!isSideScrollingV && !isSideScrollingH) {
cookie->scrolling_started = false;
return false;
}
TRACE("ALPS: scroll event\n");
cookie->tap_started = false;
cookie->tap_clicks = 0;
cookie->tapdrag_started = false;
cookie->valid_edge_motion = false;
if (!cookie->scrolling_started) {
cookie->scrolling_started = true;
start_new_movment(&cookie->movement_maker);
}
get_scrolling(&cookie->movement_maker, event->xPosition,
event->yPosition);
movement->wheel_ydelta = cookie->movement_maker.yDelta;
movement->wheel_xdelta = cookie->movement_maker.xDelta;
if (isSideScrollingV && !isSideScrollingH)
movement->wheel_xdelta = 0;
else if (isSideScrollingH && !isSideScrollingV)
movement->wheel_ydelta = 0;
cookie->buttons_state = movement->buttons;
return true;
}
static status_t
event_to_movement(alps_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
if (!movement)
return B_ERROR;
movement->xdelta = 0;
movement->ydelta = 0;
movement->buttons = 0;
movement->wheel_ydelta = 0;
movement->wheel_xdelta = 0;
movement->modifiers = 0;
movement->clicks = 0;
movement->timestamp = system_time();
if ((movement->timestamp - cookie->tap_time) > TAP_TIMEOUT) {
TRACE("ALPS: tap gesture timed out\n");
cookie->tap_started = false;
if (!cookie->double_click
|| (movement->timestamp - cookie->tap_time) > 2 * TAP_TIMEOUT) {
cookie->tap_clicks = 0;
}
}
if (event->buttons & kLeftButton) {
cookie->tap_clicks = 0;
cookie->tapdrag_started = false;
cookie->tap_started = false;
cookie->valid_edge_motion = false;
}
if (event->zPressure >= MIN_PRESSURE && event->zPressure < MAX_PRESSURE
&& ((event->wValue >=4 && event->wValue <=7)
|| event->wValue == 0 || event->wValue == 1)
&& (event->xPosition != 0 || event->yPosition != 0)) {
// The touch pad is in touch with at least one finger
if (!check_scrolling_to_movement(cookie, event, movement))
move_to_movement(cookie, event, movement);
} else
no_touch_to_movement(cookie, event, movement);
return B_OK;
}
/* Data taken from linux driver:
ALPS absolute Mode - new format
byte 0: 1 ? ? ? 1 ? ? ?
byte 1: 0 x6 x5 x4 x3 x2 x1 x0
byte 2: 0 x10 x9 x8 x7 ? fin ges
byte 3: 0 y9 y8 y7 1 M R L
byte 4: 0 y6 y5 y4 y3 y2 y1 y0
byte 5: 0 z6 z5 z4 z3 z2 z1 z0
*/
// debug
static int minX = 50000;
static int minY = 50000;
static int maxX = 0;
static int maxY = 0;
static int maxZ = 0;
// debug end
static status_t
get_alps_movment(alps_cookie *cookie, mouse_movement *movement)
{
status_t status;
touch_event event;
uint8 event_buffer[PS2_PACKET_ALPS];
status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK)
return status;
if (!cookie->dev->active) {
TRACE("ALPS: read_event: Error device no longer active\n");
return B_ERROR;
}
if (packet_buffer_read(cookie->ring_buffer, event_buffer,
cookie->dev->packet_size) != cookie->dev->packet_size) {
TRACE("ALPS: error copying buffer\n");
return B_ERROR;
}
event.buttons = event_buffer[3] & 3;
event.zPressure = event_buffer[5];
// finger on touchpad
if (event_buffer[2] & 0x2) {
// finger with normal width
event.wValue = 4;
} else {
event.wValue = 3;
}
// tabgesture
if (event_buffer[2] & 0x1) {
event.zPressure = 80;
event.wValue = 4;
}
event.xPosition = event_buffer[1] | ((event_buffer[2] & 0x78) << 4);
event.yPosition = event_buffer[4] | ((event_buffer[3] & 0x70) << 3);
// debug
if (event.xPosition < minX)
minX = event.xPosition;
if (event.yPosition < minY)
minY = event.yPosition;
if (event.xPosition > maxX)
maxX = event.xPosition;
if (event.yPosition > maxY)
maxY = event.yPosition;
if (event.zPressure > maxZ)
maxZ = event.zPressure;
dprintf("x %i %i, y %i %i, z %i %i, fin %i ges %i\n", minX, maxX, minY, maxY,
maxZ, event.zPressure, event_buffer[2] & 0x2, event_buffer[2] & 0x1);
// debug end
event.yPosition = AREA_END_Y - event.yPosition;
status = event_to_movement(cookie, &event, movement);
// check for trackpoint even (z pressure 127)
if (sFoundModel->flags & ALPS_DUALPOINT && event.zPressure == 127) {
movement->xdelta = event.xPosition > 383 ? event.xPosition - 768
: event.xPosition;
event.yPosition = AREA_END_Y - event.yPosition;
movement->ydelta = event.yPosition > 255
? event.yPosition - 512 : event.yPosition;
event.yPosition = AREA_END_Y - event.yPosition;
movement->wheel_xdelta = 0;
movement->wheel_ydelta = 0;
}
return status;
}
static void
default_settings(touchpad_settings *set)
{
memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
}
status_t
probe_alps(ps2_dev* dev)
{
int i;
uint8 val[3];
TRACE("ALPS: probe\n");
return B_ERROR;
val[0] = 0;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_RES, val, 1, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
!= B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
!= B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE11, NULL, 0, NULL, 0)
!= B_OK)
return B_ERROR;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3)
!= B_OK)
return B_ERROR;
if (val[0] != 0 || val[1] != 0 || (val[2] != 10 && val[2] != 100))
return B_ERROR;
val[0] = 0;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_RES, val, 1, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
!= B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
!= B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_SCALE21, NULL, 0, NULL, 0)
!= B_OK)
return B_ERROR;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3)
!= B_OK)
return B_ERROR;
for (i = 0; ; i++) {
const alps_model_info* info = &gALPSModelInfos[i];
if (info->id[0] == 0) {
INFO("ALPS not supported: %2.2x %2.2x %2.2x\n", val[0], val[1],
val[2]);
return B_ERROR;
}
if (info->id[0] == val[0] && info->id[1] == val[1]
&& info->id[2] == val[2]) {
sFoundModel = (alps_model_info*)info;
INFO("ALPS found: %2.2x %2.2x %2.2x\n", val[0], val[1], val[2]);
break;
}
}
dev->name = kALPSPath[dev->idx];
dev->packet_size = -1;
return B_OK;
}
status_t
alps_open(const char *name, uint32 flags, void **_cookie)
{
alps_cookie *cookie;
ps2_dev *dev;
int i;
uint8 val[3];
uint8 arg;
for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
if (0 == strcmp(ps2_device[i].name, name)) {
dev = &ps2_device[i];
break;
}
}
if (dev == NULL) {
TRACE("ps2: dev = NULL\n");
return B_ERROR;
}
if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
return B_BUSY;
cookie = (alps_cookie*)malloc(sizeof(alps_cookie));
if (cookie == NULL)
goto err1;
*_cookie = cookie;
memset(cookie, 0, sizeof(alps_cookie));
cookie->dev = dev;
dev->cookie = cookie;
dev->disconnect = &alps_disconnect;
dev->handle_int = &alps_handle_int;
default_settings(&cookie->settings);
cookie->movement_maker.speed = 1 * SPEED_FACTOR;
cookie->movement_maker.scrolling_xStep = cookie->settings.scroll_xstepsize;
cookie->movement_maker.scrolling_yStep = cookie->settings.scroll_ystepsize;
cookie->movement_maker.scroll_acceleration
= cookie->settings.scroll_acceleration;
cookie->movement_started = false;
cookie->scrolling_started = false;
cookie->tap_started = false;
cookie->double_click = false;
cookie->valid_edge_motion = false;
dev->packet_size = PS2_PACKET_ALPS;
cookie->ring_buffer
= create_packet_buffer(ALPS_HISTORY_SIZE * dev->packet_size);
if (cookie->ring_buffer == NULL) {
TRACE("ALPS: can't allocate mouse actions buffer\n");
goto err2;
}
// create the mouse semaphore, used for synchronization between
// the interrupt handler and the read operation
cookie->sem = create_sem(0, "ps2_alps_sem");
if (cookie->sem < 0) {
TRACE("ALPS: failed creating semaphore!\n");
goto err3;
}
// switch tap mode off
arg = 0x00;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_GET_INFO, NULL, 0, val, 3) != B_OK
|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_MOUSE_CMD_SET_RES, &arg, 1, NULL, 0) != B_OK)
goto err4;
// init the alps device to absolut mode
if (ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0) != B_OK
|| ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
goto err4;
if (ps2_dev_command(dev, PS2_MOUSE_CMD_SET_STREAM, NULL, 0, NULL, 0) != B_OK)
goto err4;
if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
goto err4;
atomic_or(&dev->flags, PS2_FLAG_ENABLED);
TRACE("ALPS: open %s success\n", name);
return B_OK;
err4:
delete_sem(cookie->sem);
err3:
delete_packet_buffer(cookie->ring_buffer);
err2:
free(cookie);
err1:
atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
TRACE("ALPS: open %s failed\n", name);
return B_ERROR;
}
status_t
alps_close(void *_cookie)
{
status_t status;
alps_cookie *cookie = _cookie;
ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
150000);
delete_packet_buffer(cookie->ring_buffer);
delete_sem(cookie->sem);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
// Reset the touchpad so it generate standard ps2 packets instead of
// extended ones. If not, BeOS is confused with such packets when rebooting
// without a complete shutdown.
status = ps2_reset_mouse(cookie->dev);
if (status != B_OK) {
INFO("ps2: reset failed\n");
return B_ERROR;
}
TRACE("ALPS: close %s done\n", cookie->dev->name);
return B_OK;
}
status_t
alps_freecookie(void *_cookie)
{
free(_cookie);
return B_OK;
}
status_t
alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
{
alps_cookie *cookie = _cookie;
mouse_movement movement;
status_t status;
switch (op) {
case MS_READ:
TRACE("ALPS: MS_READ get event\n");
if ((status = get_alps_movment(cookie, &movement)) != B_OK)
return status;
return user_memcpy(buffer, &movement, sizeof(movement));
case MS_IS_TOUCHPAD:
TRACE("ALPS: MS_IS_TOUCHPAD\n");
return B_OK;
case MS_SET_TOUCHPAD_SETTINGS:
TRACE("ALPS: MS_SET_TOUCHPAD_SETTINGS");
user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
cookie->movement_maker.scrolling_xStep
= cookie->settings.scroll_xstepsize;
cookie->movement_maker.scrolling_yStep
= cookie->settings.scroll_ystepsize;
cookie->movement_maker.scroll_acceleration
= cookie->settings.scroll_acceleration;
return B_OK;
case MS_SET_CLICKSPEED:
TRACE("ALPS: ioctl MS_SETCLICK (set click speed)\n");
return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
default:
TRACE("ALPS: unknown opcode: %ld\n", op);
return B_BAD_VALUE;
}
}
static status_t
alps_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
static status_t
alps_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
int32
alps_handle_int(ps2_dev *dev)
{
alps_cookie *cookie = dev->cookie;
uint8 val;
val = cookie->dev->history[0].data;
//dprintf("%i %i %i %i %i %i %i %i\n", (val >> 7) & 0x1, (val >> 6) & 0x1, (val >> 5) & 0x1, (val >> 4) & 0x1, (val >> 3) & 0x1,
// (val >> 2) & 0x1, (val >> 1) & 0x1, (val >> 0) & 0x1);
if (cookie->packet_index == 0
&& (val & sFoundModel->maskFirstByte) != sFoundModel->firstByte) {
INFO("ALPS: bad header, trying resync\n");
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
// data packages starting with a 0
if (cookie->packet_index > 1 && (val & 0x80)) {
INFO("ALPS: bad package data, trying resync\n");
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
cookie->packet_buffer[cookie->packet_index] = val;
cookie->packet_index++;
if (cookie->packet_index >= 6) {
cookie->packet_index = 0;
if (packet_buffer_write(cookie->ring_buffer,
cookie->packet_buffer, cookie->dev->packet_size)
!= cookie->dev->packet_size) {
// buffer is full, drop new data
return B_HANDLED_INTERRUPT;
}
release_sem_etc(cookie->sem, 1, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
return B_HANDLED_INTERRUPT;
}
void
alps_disconnect(ps2_dev *dev)
{
alps_cookie *cookie = dev->cookie;
// the mouse device might not be opened at this point
INFO("ALPS: alps_disconnect %s\n", dev->name);
if ((dev->flags & PS2_FLAG_OPEN) != 0)
release_sem(cookie->sem);
}
device_hooks gALPSDeviceHooks = {
alps_open,
alps_close,
alps_freecookie,
alps_ioctl,
alps_read,
alps_write,
};
@@ -1,418 +0,0 @@
/*
* Copyright 2004-2009 Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors (in chronological order):
* Stefano Ceccherini ([email protected])
* Axel Dörfler, [email protected]
* Marcus Overhagen <[email protected]>
*/
/*! PS/2 bus manager */
#include <string.h>
#include "ps2_common.h"
#include "ps2_service.h"
#include "ps2_dev.h"
isa_module_info *gIsa = NULL;
bool gActiveMultiplexingEnabled = false;
sem_id gControllerSem;
static int32 sIgnoreInterrupts = 0;
uint8
ps2_read_ctrl(void)
{
return gIsa->read_io_8(PS2_PORT_CTRL);
}
uint8
ps2_read_data(void)
{
return gIsa->read_io_8(PS2_PORT_DATA);
}
void
ps2_write_ctrl(uint8 ctrl)
{
TRACE("ps2: ps2_write_ctrl 0x%02x\n", ctrl);
gIsa->write_io_8(PS2_PORT_CTRL, ctrl);
}
void
ps2_write_data(uint8 data)
{
TRACE("ps2: ps2_write_data 0x%02x\n", data);
gIsa->write_io_8(PS2_PORT_DATA, data);
}
status_t
ps2_wait_read(void)
{
int i;
for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
if (ps2_read_ctrl() & PS2_STATUS_OUTPUT_BUFFER_FULL)
return B_OK;
snooze(50);
}
return B_ERROR;
}
status_t
ps2_wait_write(void)
{
int i;
for (i = 0; i < PS2_CTRL_WAIT_TIMEOUT / 50; i++) {
if (!(ps2_read_ctrl() & PS2_STATUS_INPUT_BUFFER_FULL))
return B_OK;
snooze(50);
}
return B_ERROR;
}
// #pragma mark -
void
ps2_flush(void)
{
int i;
acquire_sem(gControllerSem);
atomic_add(&sIgnoreInterrupts, 1);
for (i = 0; i < 64; i++) {
uint8 ctrl;
uint8 data;
ctrl = ps2_read_ctrl();
if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL))
break;
data = ps2_read_data();
TRACE("ps2: ps2_flush: ctrl 0x%02x, data 0x%02x (%s)\n", ctrl, data, (ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb");
snooze(100);
}
atomic_add(&sIgnoreInterrupts, -1);
release_sem(gControllerSem);
}
static status_t
ps2_selftest()
{
status_t res;
uint8 in;
res = ps2_command(PS2_CTRL_SELF_TEST, NULL, 0, &in, 1);
if (res != B_OK || in != 0x55) {
INFO("ps2: controller self test failed, status 0x%08lx, data 0x%02x\n", res, in);
return B_ERROR;
}
return B_OK;
}
static status_t
ps2_setup_command_byte()
{
status_t res;
uint8 cmdbyte;
res = ps2_command(PS2_CTRL_READ_CMD, NULL, 0, &cmdbyte, 1);
TRACE("ps2: get command byte: res 0x%08lx, cmdbyte 0x%02x\n", res, cmdbyte);
if (res != B_OK)
cmdbyte = 0x47;
cmdbyte |= PS2_BITS_TRANSLATE_SCANCODES | PS2_BITS_KEYBOARD_INTERRUPT
| PS2_BITS_AUX_INTERRUPT;
cmdbyte &= ~(PS2_BITS_KEYBOARD_DISABLED | PS2_BITS_MOUSE_DISABLED);
res = ps2_command(PS2_CTRL_WRITE_CMD, &cmdbyte, 1, NULL, 0);
TRACE("ps2: set command byte: res 0x%08lx, cmdbyte 0x%02x\n", res, cmdbyte);
return res;
}
static status_t
ps2_setup_active_multiplexing(bool *enabled)
{
status_t res;
uint8 in, out;
out = 0xf0;
res = ps2_command(0xd3, &out, 1, &in, 1);
if (res)
goto fail;
// Step 1, if controller is good, in does match out.
// This test failes with MS Virtual PC.
if (in != out)
goto no_support;
out = 0x56;
res = ps2_command(0xd3, &out, 1, &in, 1);
if (res)
goto fail;
// Step 2, if controller is good, in does match out.
if (in != out)
goto no_support;
out = 0xa4;
res = ps2_command(0xd3, &out, 1, &in, 1);
if (res)
goto fail;
// Step 3, if the controller doesn't support active multiplexing,
// then in data does match out data (0xa4), else it's version number.
if (in == out)
goto no_support;
// With some broken USB legacy emulation, it's 0xac, and with
// MS Virtual PC, it's 0xa6. Since current active multiplexing
// specification version is 1.1 (0x11), we validate the data.
if (in > 0x9f) {
TRACE("ps2: active multiplexing v%d.%d detected, but ignored!\n", (in >> 4), in & 0xf);
goto no_support;
}
INFO("ps2: active multiplexing v%d.%d enabled\n", (in >> 4), in & 0xf);
*enabled = true;
goto done;
no_support:
TRACE("ps2: active multiplexing not supported\n");
*enabled = false;
done:
// Some controllers get upset by the d3 command and will continue data
// loopback, thus we need to send a harmless command (enable keyboard
// interface) next.
// This fixes bug report #1175
res = ps2_command(0xae, NULL, 0, NULL, 0);
if (res != B_OK) {
INFO("ps2: active multiplexing d3 workaround failed, status 0x%08lx\n", res);
}
return B_OK;
fail:
TRACE("ps2: testing for active multiplexing failed\n");
*enabled = false;
// this should revert the controller into legacy mode,
// just in case it has switched to multiplexed mode
return ps2_selftest();
}
status_t
ps2_command(uint8 cmd, const uint8 *out, int outCount, uint8 *in, int inCount)
{
status_t res;
int i;
acquire_sem(gControllerSem);
atomic_add(&sIgnoreInterrupts, 1);
#ifdef TRACE_PS2
TRACE("ps2: ps2_command cmd 0x%02x, out %d, in %d\n", cmd, outCount, inCount);
for (i = 0; i < outCount; i++)
TRACE("ps2: ps2_command out 0x%02x\n", out[i]);
#endif
res = ps2_wait_write();
if (res == B_OK)
ps2_write_ctrl(cmd);
for (i = 0; res == B_OK && i < outCount; i++) {
res = ps2_wait_write();
if (res == B_OK)
ps2_write_data(out[i]);
else
TRACE("ps2: ps2_command out byte %d failed\n", i);
}
for (i = 0; res == B_OK && i < inCount; i++) {
res = ps2_wait_read();
if (res == B_OK)
in[i] = ps2_read_data();
else
TRACE("ps2: ps2_command in byte %d failed\n", i);
}
#ifdef TRACE_PS2
for (i = 0; i < inCount; i++)
TRACE("ps2: ps2_command in 0x%02x\n", in[i]);
TRACE("ps2: ps2_command result 0x%08lx\n", res);
#endif
atomic_add(&sIgnoreInterrupts, -1);
release_sem(gControllerSem);
return res;
}
// #pragma mark -
static int32
ps2_interrupt(void* cookie)
{
uint8 ctrl;
uint8 data;
bool error;
ps2_dev *dev;
ctrl = ps2_read_ctrl();
if (!(ctrl & PS2_STATUS_OUTPUT_BUFFER_FULL))
return B_UNHANDLED_INTERRUPT;
if (atomic_get(&sIgnoreInterrupts)) {
TRACE("ps2: ps2_interrupt ignoring, ctrl 0x%02x (%s)\n", ctrl,
(ctrl & PS2_STATUS_AUX_DATA) ? "aux" : "keyb");
return B_HANDLED_INTERRUPT;
}
data = ps2_read_data();
if ((ctrl & PS2_STATUS_AUX_DATA) != 0) {
uint8 idx;
if (gActiveMultiplexingEnabled) {
idx = ctrl >> 6;
error = (ctrl & 0x04) != 0;
TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (mouse %d)\n",
ctrl, data, idx);
} else {
idx = 0;
error = (ctrl & 0xC0) != 0;
TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (aux)\n", ctrl,
data);
}
dev = &ps2_device[PS2_DEVICE_MOUSE + idx];
} else {
TRACE("ps2: ps2_interrupt ctrl 0x%02x, data 0x%02x (keyb)\n", ctrl,
data);
dev = &ps2_device[PS2_DEVICE_KEYB];
error = (ctrl & 0xC0) != 0;
}
dev->history[1] = dev->history[0];
dev->history[0].time = system_time();
dev->history[0].data = data;
dev->history[0].error = error;
return ps2_dev_handle_int(dev);
}
// #pragma mark - driver interface
status_t
ps2_init(void)
{
status_t status;
TRACE("ps2: init\n");
status = get_module(B_ISA_MODULE_NAME, (module_info **)&gIsa);
if (status < B_OK)
return status;
gControllerSem = create_sem(1, "ps/2 keyb ctrl");
ps2_flush();
status = ps2_dev_init();
if (status < B_OK)
goto err1;
status = ps2_service_init();
if (status < B_OK)
goto err2;
status = install_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt,
NULL, 0);
if (status)
goto err3;
status = install_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL,
0);
if (status)
goto err4;
// While this might have fixed bug #1185, we can't do this unconditionally
// as it obviously messes up many controllers which couldn't reboot anymore
// after that
//ps2_selftest();
status = ps2_setup_command_byte();
if (status) {
INFO("ps2: setting up command byte failed\n");
goto err5;
}
ps2_flush();
status = ps2_setup_active_multiplexing(&gActiveMultiplexingEnabled);
if (status) {
INFO("ps2: setting up active multiplexing failed\n");
goto err5;
}
if (gActiveMultiplexingEnabled) {
if (ps2_dev_command_timeout(&ps2_device[PS2_DEVICE_MOUSE], 0xe6,
NULL, 0, NULL, 0, 100000) == B_TIMED_OUT) {
INFO("ps2: accessing multiplexed mouse port 0 timed out, ignoring it!\n");
} else {
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]);
}
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE + 1]);
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE + 2]);
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE + 3]);
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]);
} else {
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_MOUSE]);
ps2_service_notify_device_added(&ps2_device[PS2_DEVICE_KEYB]);
}
TRACE("ps2: init done!\n");
return B_OK;
err5:
remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL);
err4:
remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL);
err3:
ps2_service_exit();
err2:
ps2_dev_exit();
err1:
delete_sem(gControllerSem);
put_module(B_ISA_MODULE_NAME);
TRACE("ps2: init failed!\n");
return B_ERROR;
}
void
ps2_uninit(void)
{
TRACE("ps2: uninit\n");
remove_io_interrupt_handler(INT_PS2_MOUSE, &ps2_interrupt, NULL);
remove_io_interrupt_handler(INT_PS2_KEYBOARD, &ps2_interrupt, NULL);
ps2_service_exit();
ps2_dev_exit();
delete_sem(gControllerSem);
put_module(B_ISA_MODULE_NAME);
}
@@ -1,525 +0,0 @@
/*
* Copyright 2001-2010 Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors (in chronological order):
* Elad Lahav ([email protected])
* Stefano Ceccherini ([email protected])
* Axel Dörfler, [email protected]
* Marcus Overhagen <[email protected]>
* Clemens Zeidler <[email protected]>
*/
/*! PS/2 mouse device driver
A PS/2 mouse is connected to the IBM 8042 controller, and gets its
name from the IBM PS/2 personal computer, which was the first to
use this device. All resources are shared between the keyboard, and
the mouse, referred to as the "Auxiliary Device".
I/O:
~~~
The controller has 3 I/O registers:
1. Status (input), mapped to port 64h
2. Control (output), mapped to port 64h
3. Data (input/output), mapped to port 60h
Data:
~~~~
A packet read from the mouse data port is composed of
three bytes:
byte 0: status byte, where
- bit 7: Y overflow (1 = true)
- bit 6: X overflow (1 = true)
- bit 5: MSB of Y offset
- bit 4: MSB of X offset
- bit 3: Syncronization bit (always 1)
- bit 2: Middle button (1 = down)
- bit 1: Right button (1 = down)
- bit 0: Left button (1 = down)
byte 1: X position change, since last probed (-127 to +127)
byte 2: Y position change, since last probed (-127 to +127)
Intellimouse mice send a four byte packet, where the first three
bytes are the same as standard mice, and the last one reports the
Z position, which is, usually, the wheel movement.
Interrupts:
~~~~~~~~~~
The PS/2 mouse device is connected to interrupt 12.
The controller uses 3 consecutive interrupts to inform the computer
that it has new data. On the first the data register holds the status
byte, on the second the X offset, and on the 3rd the Y offset.
*/
#include <stdlib.h>
#include <string.h>
#include <keyboard_mouse_driver.h>
#include "ps2_service.h"
#include "ps2_standard_mouse.h"
const char* kStandardMousePath[4] = {
"input/mouse/ps2/standard_0",
"input/mouse/ps2/standard_1",
"input/mouse/ps2/standard_2",
"input/mouse/ps2/standard_3"
};
const char* kIntelliMousePath[4] = {
"input/mouse/ps2/intelli_0",
"input/mouse/ps2/intelli_1",
"input/mouse/ps2/intelli_2",
"input/mouse/ps2/intelli_3"
};
/*! Set sampling rate of the ps2 port.
*/
static inline status_t
ps2_set_sample_rate(ps2_dev *dev, uint8 rate)
{
return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &rate, 1, NULL, 0);
}
/*! Converts a packet received by the mouse to a "movement".
*/
static void
ps2_packet_to_movement(standard_mouse_cookie *cookie, uint8 packet[],
mouse_movement *pos)
{
int buttons = packet[0] & 7;
int xDelta = ((packet[0] & 0x10) ? ~0xff : 0) | packet[1];
int yDelta = ((packet[0] & 0x20) ? ~0xff : 0) | packet[2];
int xDeltaWheel = 0;
int yDeltaWheel = 0;
bigtime_t currentTime = system_time();
if (buttons != 0 && cookie->buttons_state == 0) {
if (cookie->click_last_time + cookie->click_speed > currentTime)
cookie->click_count++;
else
cookie->click_count = 1;
cookie->click_last_time = currentTime;
}
cookie->buttons_state = buttons;
if (cookie->flags & F_MOUSE_TYPE_INTELLIMOUSE) {
yDeltaWheel = packet[3] & 0x07;
if (packet[3] & 0x08)
yDeltaWheel |= ~0x07;
}
/*
if (cookie->flags & F_MOUSE_TYPE_2WHEELS) {
switch (packet[3] & 0x0F) {
case 0x01: yDeltaWheel = +1; break; // wheel 1 down
case 0x0F: yDeltaWheel = -1; break; // wheel 1 up
case 0x02: xDeltaWheel = +1; break; // wheel 2 down
case 0x0E: xDeltaWheel = -1; break; // wheel 2 up
}
}
*/
// TRACE("packet: %02x %02x %02x %02x: xd %d, yd %d, 0x%x (%d), w-xd %d, "
// "w-yd %d\n", packet[0], packet[1], packet[2], packet[3],
// xDelta, yDelta, buttons, cookie->click_count, xDeltaWheel,
// yDeltaWheel);
if (pos) {
pos->xdelta = xDelta;
pos->ydelta = yDelta;
pos->buttons = buttons;
pos->clicks = cookie->click_count;
pos->modifiers = 0;
pos->timestamp = currentTime;
pos->wheel_ydelta = yDeltaWheel;
pos->wheel_xdelta = xDeltaWheel;
TRACE("ps2: ps2_packet_to_movement xdelta: %d, ydelta: %d, buttons %x, "
"clicks: %d, timestamp %Ld\n",
xDelta, yDelta, buttons, cookie->click_count, currentTime);
}
}
/*! Read a mouse event from the mouse events chain buffer.
*/
static status_t
standard_mouse_read_event(standard_mouse_cookie *cookie,
mouse_movement *movement)
{
uint8 packet[PS2_MAX_PACKET_SIZE];
status_t status;
TRACE("ps2: standard_mouse_read_event\n");
status = acquire_sem_etc(cookie->standard_mouse_sem, 1, B_CAN_INTERRUPT, 0);
TRACE("ps2: standard_mouse_read_event acquired\n");
if (status < B_OK)
return status;
if (!cookie->dev->active) {
TRACE("ps2: standard_mouse_read_event: Error device no longer "
"active\n");
return B_ERROR;
}
if (packet_buffer_read(cookie->standard_mouse_buffer, packet,
cookie->dev->packet_size) != cookie->dev->packet_size) {
TRACE("ps2: error copying buffer\n");
return B_ERROR;
}
if (!(packet[0] & 8))
panic("ps2: got broken data from packet_buffer_read\n");
ps2_packet_to_movement(cookie, packet, movement);
return B_OK;
}
// #pragma mark -
void
standard_mouse_disconnect(ps2_dev *dev)
{
// the mouse device might not be opened at this point
INFO("ps2: ps2_standard_mouse_disconnect %s\n", dev->name);
if (dev->flags & PS2_FLAG_OPEN)
release_sem(((standard_mouse_cookie *)dev->cookie)->standard_mouse_sem);
}
/*! Interrupt handler for the mouse device. Called whenever the I/O
controller generates an interrupt for the PS/2 mouse. Reads mouse
information from the data port, and stores it, so it can be accessed
by read() operations. The full data is obtained using 3 consecutive
calls to the handler, each holds a different byte on the data port.
*/
int32
standard_mouse_handle_int(ps2_dev *dev)
{
standard_mouse_cookie *cookie = (standard_mouse_cookie*)dev->cookie;
const uint8 data = dev->history[0].data;
TRACE("ps2: standard mouse: %1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\t%1x\n",
data >> 7 & 1, data >> 6 & 1, data >> 5 & 1,
data >> 4 & 1, data >> 3 & 1, data >> 2 & 1,
data >> 1 & 1, data >> 0 & 1);
if (cookie->packet_index == 0 && !(data & 8)) {
INFO("ps2: bad mouse data, trying resync\n");
return B_HANDLED_INTERRUPT;
}
// Workarounds for active multiplexing keyboard controllers
// that lose data or send them to the wrong port.
if (cookie->packet_index == 0 && (data & 0xc0)) {
INFO("ps2: strange mouse data, x/y overflow, trying resync\n");
return B_HANDLED_INTERRUPT;
}
if (cookie->packet_index == 1) {
int xDelta
= ((cookie->packet_buffer[0] & 0x10) ? 0xFFFFFF00 : 0) | data;
if (xDelta < -100 || xDelta > 100) {
INFO("ps2: strange mouse data, x-delta %d, trying resync\n",
xDelta);
cookie->packet_index = 0;
return B_HANDLED_INTERRUPT;
}
}
if (cookie->packet_index == 2) {
int yDelta
= ((cookie->packet_buffer[0] & 0x20) ? 0xFFFFFF00 : 0) | data;
if (yDelta < -100 || yDelta > 100) {
INFO("ps2: strange mouse data, y-delta %d, trying resync\n",
yDelta);
cookie->packet_index = 0;
return B_HANDLED_INTERRUPT;
}
}
cookie->packet_buffer[cookie->packet_index++] = data;
if (cookie->packet_index != dev->packet_size) {
// packet not yet complete
return B_HANDLED_INTERRUPT;
}
// complete packet is assembled
cookie->packet_index = 0;
if (packet_buffer_write(cookie->standard_mouse_buffer,
cookie->packet_buffer, dev->packet_size) != dev->packet_size) {
// buffer is full, drop new data
return B_HANDLED_INTERRUPT;
}
release_sem_etc(cookie->standard_mouse_sem, 1, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
// #pragma mark -
status_t
probe_standard_mouse(ps2_dev * dev)
{
status_t status;
uint8 deviceId = 0;
// get device id
status = ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
&deviceId, 1);
if (status != B_OK) {
INFO("ps2: probe_mouse get device id failed\n");
return B_ERROR;
}
TRACE("ps2: probe_mouse device id: %2x\n", deviceId);
// check for MS Intellimouse
if (deviceId == 0) {
uint8 alternate_device_id;
status = ps2_set_sample_rate(dev, 200);
status |= ps2_set_sample_rate(dev, 100);
status |= ps2_set_sample_rate(dev, 80);
status |= ps2_dev_command(dev, PS2_CMD_GET_DEVICE_ID, NULL, 0,
&alternate_device_id, 1);
if (status == 0) {
TRACE("ps2: probe_mouse alternate device id: %2x\n",
alternate_device_id);
deviceId = alternate_device_id;
}
}
if (deviceId == PS2_DEV_ID_STANDARD
|| deviceId == PS2_DEV_ID_TOUCHPAD_RICATECH) {
INFO("ps2: probe_mouse Standard PS/2 mouse found\n");
dev->name = kStandardMousePath[dev->idx];
dev->packet_size = PS2_PACKET_STANDARD;
} else if (deviceId == PS2_DEV_ID_INTELLIMOUSE) {
dev->name = kIntelliMousePath[dev->idx];
dev->packet_size = PS2_PACKET_INTELLIMOUSE;
INFO("ps2: probe_mouse Extended PS/2 mouse found\n");
} else {
INFO("ps2: probe_mouse Error unknown device id.\n");
return B_ERROR;
}
return B_OK;
}
// #pragma mark - Device functions
status_t
standard_mouse_open(const char *name, uint32 flags, void **_cookie)
{
standard_mouse_cookie *cookie;
status_t status;
ps2_dev *dev = NULL;
int i;
TRACE("ps2: standard_mouse_open %s\n", name);
for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
if (0 == strcmp(ps2_device[i].name, name)) {
dev = &ps2_device[i];
break;
}
/*if (0 == strcmp(g_passthrough_dev.name, name)) {
dev = &g_passthrough_dev;
isSynapticsPTDevice = true;
break;
}*/
}
if (dev == NULL) {
TRACE("ps2: dev = NULL\n");
return B_ERROR;
}
if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
return B_BUSY;
cookie = malloc(sizeof(standard_mouse_cookie));
if (cookie == NULL)
goto err1;
*_cookie = cookie;
memset(cookie, 0, sizeof(*cookie));
cookie->dev = dev;
dev->cookie = cookie;
dev->disconnect = &standard_mouse_disconnect;
dev->handle_int = &standard_mouse_handle_int;
if (strstr(dev->name, "standard") != NULL)
cookie->flags = F_MOUSE_TYPE_STANDARD;
if (strstr(dev->name, "intelli") != NULL)
cookie->flags = F_MOUSE_TYPE_INTELLIMOUSE;
cookie->standard_mouse_buffer
= create_packet_buffer(MOUSE_HISTORY_SIZE * dev->packet_size);
if (cookie->standard_mouse_buffer == NULL) {
TRACE("ps2: can't allocate mouse actions buffer\n");
goto err2;
}
// create the mouse semaphore, used for synchronization between
// the interrupt handler and the read operation
cookie->standard_mouse_sem = create_sem(0, "ps2_standard_mouse_sem");
if (cookie->standard_mouse_sem < 0) {
TRACE("ps2: failed creating PS/2 mouse semaphore!\n");
goto err3;
}
status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
if (status < B_OK) {
INFO("ps2: cannot enable mouse %s\n", name);
goto err4;
}
atomic_or(&dev->flags, PS2_FLAG_ENABLED);
TRACE("ps2: standard_mouse_open %s success\n", name);
return B_OK;
err4:
delete_sem(cookie->standard_mouse_sem);
err3:
delete_packet_buffer(cookie->standard_mouse_buffer);
err2:
free(cookie);
err1:
atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
TRACE("ps2: standard_mouse_open %s failed\n", name);
return B_ERROR;
}
status_t
standard_mouse_close(void *_cookie)
{
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
TRACE("ps2: standard_mouse_close %s enter\n", cookie->dev->name);
ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
150000);
delete_packet_buffer(cookie->standard_mouse_buffer);
delete_sem(cookie->standard_mouse_sem);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
TRACE("ps2: standard_mouse_close %s done\n", cookie->dev->name);
return B_OK;
}
status_t
standard_mouse_freecookie(void *_cookie)
{
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
free(cookie);
return B_OK;
}
static status_t
standard_mouse_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
static status_t
standard_mouse_write(void *cookie, off_t pos, const void *buffer,
size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
status_t
standard_mouse_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
{
standard_mouse_cookie *cookie = (standard_mouse_cookie*)_cookie;
switch (op) {
case MS_NUM_EVENTS:
{
int32 count;
TRACE("ps2: ioctl MS_NUM_EVENTS\n");
get_sem_count(cookie->standard_mouse_sem, &count);
return count;
}
case MS_READ:
{
mouse_movement movement;
status_t status;
TRACE("ps2: ioctl MS_READ\n");
if ((status = standard_mouse_read_event(cookie, &movement)) < B_OK)
return status;
// TRACE("%s %d %d %d %d\n", cookie->dev->name,
// movement.xdelta, movement.ydelta, movement.buttons,
// movement.clicks);
return user_memcpy(buffer, &movement, sizeof(movement));
}
case MS_SET_TYPE:
TRACE("ps2: ioctl MS_SET_TYPE not implemented\n");
return B_BAD_VALUE;
case MS_SET_MAP:
TRACE("ps2: ioctl MS_SET_MAP (set mouse mapping) not "
"implemented\n");
return B_BAD_VALUE;
case MS_GET_ACCEL:
TRACE("ps2: ioctl MS_GET_ACCEL (get mouse acceleration) not "
"implemented\n");
return B_BAD_VALUE;
case MS_SET_ACCEL:
TRACE("ps2: ioctl MS_SET_ACCEL (set mouse acceleration) not "
"implemented\n");
return B_BAD_VALUE;
case MS_SET_CLICKSPEED:
TRACE("ps2: ioctl MS_SETCLICK (set click speed)\n");
return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
default:
TRACE("ps2: ioctl unknown mouse opcode: %ld\n", op);
return B_DEV_INVALID_IOCTL;
}
}
device_hooks gStandardMouseDeviceHooks = {
standard_mouse_open,
standard_mouse_close,
standard_mouse_freecookie,
standard_mouse_ioctl,
standard_mouse_read,
standard_mouse_write,
};
@@ -1,858 +0,0 @@
/*
* Copyright 2008-2010, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors (in chronological order):
* Clemens Zeidler ([email protected])
* Axel Dörfler, [email protected]
*/
//! PS/2 synaptics touchpad
#include "ps2_synaptics.h"
#include <malloc.h>
#include <string.h>
#include <stdlib.h>
#include <keyboard_mouse_driver.h>
#include "ps2_service.h"
const char* kSynapticsPath[4] = {
"input/touchpad/ps2/synaptics_0",
"input/touchpad/ps2/synaptics_1",
"input/touchpad/ps2/synaptics_2",
"input/touchpad/ps2/synaptics_3"
};
static touchpad_info sTouchpadInfo;
static ps2_dev *sPassthroughDevice = &ps2_device[PS2_DEVICE_SYN_PASSTHROUGH];
static void
default_settings(touchpad_settings *set)
{
memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
}
static status_t
send_touchpad_arg_timeout(ps2_dev *dev, uint8 arg, bigtime_t timeout)
{
int8 i;
uint8 val[8];
for (i = 0; i < 4; i++) {
val[2 * i] = (arg >> (6 - 2 * i)) & 3;
val[2 * i + 1] = 0xE8;
}
return ps2_dev_command_timeout(dev, 0xE8, val, 7, NULL, 0, timeout);
}
static status_t
send_touchpad_arg(ps2_dev *dev, uint8 arg)
{
return send_touchpad_arg_timeout(dev, arg, 4000000);
}
static status_t
set_touchpad_mode(ps2_dev *dev, uint8 mode)
{
uint8 sample_rate = SYN_CHANGE_MODE;
send_touchpad_arg(dev, mode);
return ps2_dev_command(dev, PS2_CMD_SET_SAMPLE_RATE, &sample_rate, 1,
NULL, 0);
}
static bool
edge_motion(mouse_movement *movement, touch_event *event, bool validStart)
{
int32 xdelta = 0;
int32 ydelta = 0;
if (event->xPosition < SYN_AREA_START_X + SYN_EDGE_MOTION_WIDTH)
xdelta = -SYN_EDGE_MOTION_SPEED;
else if (event->xPosition > SYN_AREA_END_X - SYN_EDGE_MOTION_WIDTH)
xdelta = SYN_EDGE_MOTION_SPEED;
if (event->yPosition < SYN_AREA_START_Y + SYN_EDGE_MOTION_WIDTH)
ydelta = -SYN_EDGE_MOTION_SPEED;
else if (event->yPosition > SYN_AREA_END_Y - SYN_EDGE_MOTION_WIDTH)
ydelta = SYN_EDGE_MOTION_SPEED;
if (xdelta && validStart)
movement->xdelta = xdelta;
if (ydelta && validStart)
movement->ydelta = ydelta;
if ((xdelta || ydelta) && !validStart)
return false;
return true;
}
/*! If a button has been clicked (movement->buttons must be set accordingly),
this function updates the click_count of the \a cookie, as well as the
\a movement's clicks field.
Also, it sets the cookie's button state from movement->buttons.
*/
static inline void
update_buttons(synaptics_cookie *cookie, mouse_movement *movement)
{
// set click count correctly according to double click timeout
if (movement->buttons != 0 && cookie->buttons_state == 0) {
if (cookie->click_last_time + cookie->click_speed > movement->timestamp)
cookie->click_count++;
else
cookie->click_count = 1;
cookie->click_last_time = movement->timestamp;
}
if (movement->buttons != 0)
movement->clicks = cookie->click_count;
cookie->buttons_state = movement->buttons;
}
static inline void
no_touch_to_movement(synaptics_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
uint32 buttons = event->buttons;
TRACE("SYNAPTICS: no touch event\n");
cookie->scrolling_started = false;
cookie->movement_started = false;
if (cookie->tapdrag_started
&& (movement->timestamp - cookie->tap_time) < SYN_TAP_TIMEOUT) {
buttons = 0x01;
}
// if the movement stopped switch off the tap drag when timeout is expired
if ((movement->timestamp - cookie->tap_time) > SYN_TAP_TIMEOUT) {
cookie->tapdrag_started = false;
cookie->valid_edge_motion = false;
TRACE("SYNAPTICS: tap drag gesture timed out\n");
}
if (abs(cookie->tap_delta_x) > 15 || abs(cookie->tap_delta_y) > 15) {
cookie->tap_started = false;
cookie->tap_clicks = 0;
}
if (cookie->tap_started || cookie->double_click) {
TRACE("SYNAPTICS: tap gesture\n");
cookie->tap_clicks++;
if (cookie->tap_clicks > 1) {
TRACE("SYNAPTICS: empty click\n");
buttons = 0x00;
cookie->tap_clicks = 0;
cookie->double_click = true;
} else {
buttons = 0x01;
cookie->tap_started = false;
cookie->tapdrag_started = true;
cookie->double_click = false;
}
}
movement->buttons = buttons;
update_buttons(cookie, movement);
}
static inline void
move_to_movement(synaptics_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
touchpad_settings *settings = &cookie->settings;
bool isStartOfMovement = false;
float pressure = 0;
TRACE("SYNAPTICS: movement event\n");
if (!cookie->movement_started) {
isStartOfMovement = true;
cookie->movement_started = true;
start_new_movment(&cookie->movement_maker);
}
get_movement(&cookie->movement_maker, event->xPosition, event->yPosition);
movement->xdelta = cookie->movement_maker.xDelta;
movement->ydelta = cookie->movement_maker.yDelta;
// tap gesture
cookie->tap_delta_x += cookie->movement_maker.xDelta;
cookie->tap_delta_y += cookie->movement_maker.yDelta;
if (cookie->tapdrag_started) {
movement->buttons = kLeftButton;
movement->clicks = 0;
cookie->valid_edge_motion = edge_motion(movement, event,
cookie->valid_edge_motion);
TRACE("SYNAPTICS: tap drag\n");
} else {
TRACE("SYNAPTICS: movement set buttons\n");
movement->buttons = event->buttons;
}
// use only a fraction of pressure range, the max pressure seems to be
// to high
pressure = 20 * (event->zPressure - MIN_PRESSURE)
/ (MAX_PRESSURE - MIN_PRESSURE - 100);
if (!cookie->tap_started
&& isStartOfMovement
&& settings->tapgesture_sensibility > 0.
&& settings->tapgesture_sensibility > (20 - pressure)) {
TRACE("SYNAPTICS: tap started\n");
cookie->tap_started = true;
cookie->tap_time = system_time();
cookie->tap_delta_x = 0;
cookie->tap_delta_y = 0;
}
update_buttons(cookie, movement);
}
/*! Checks if this is a scrolling event or not, and also actually does the
scrolling work if it is.
\return \c true if this was a scrolling event, \c false if not.
*/
static inline bool
check_scrolling_to_movement(synaptics_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
touchpad_settings *settings = &cookie->settings;
bool isSideScrollingV = false;
bool isSideScrollingH = false;
// if a button is pressed don't allow to scroll, we likely be in a drag
// action
if (cookie->buttons_state != 0)
return false;
if ((SYN_AREA_END_X - SYN_AREA_WIDTH_X * settings->scroll_rightrange
< event->xPosition && !cookie->movement_started
&& settings->scroll_rightrange > 0.000001)
|| settings->scroll_rightrange > 0.999999) {
isSideScrollingV = true;
}
if ((SYN_AREA_START_Y + SYN_AREA_WIDTH_Y * settings->scroll_bottomrange
> event->yPosition && !cookie->movement_started
&& settings->scroll_bottomrange > 0.000001)
|| settings->scroll_bottomrange > 0.999999) {
isSideScrollingH = true;
}
if ((event->wValue == 0 || event->wValue == 1)
&& settings->scroll_twofinger) {
// two finger scrolling is enabled
isSideScrollingV = true;
isSideScrollingH = settings->scroll_twofinger_horizontal;
}
if (!isSideScrollingV && !isSideScrollingH) {
cookie->scrolling_started = false;
return false;
}
TRACE("SYNAPTICS: scroll event\n");
cookie->tap_started = false;
cookie->tap_clicks = 0;
cookie->tapdrag_started = false;
cookie->valid_edge_motion = false;
if (!cookie->scrolling_started) {
cookie->scrolling_started = true;
start_new_movment(&cookie->movement_maker);
}
get_scrolling(&cookie->movement_maker, event->xPosition,
event->yPosition);
movement->wheel_ydelta = cookie->movement_maker.yDelta;
movement->wheel_xdelta = cookie->movement_maker.xDelta;
if (isSideScrollingV && !isSideScrollingH)
movement->wheel_xdelta = 0;
else if (isSideScrollingH && !isSideScrollingV)
movement->wheel_ydelta = 0;
cookie->buttons_state = movement->buttons;
return true;
}
static status_t
event_to_movement(synaptics_cookie *cookie, touch_event *event,
mouse_movement *movement)
{
if (!movement)
return B_ERROR;
movement->xdelta = 0;
movement->ydelta = 0;
movement->buttons = 0;
movement->wheel_ydelta = 0;
movement->wheel_xdelta = 0;
movement->modifiers = 0;
movement->clicks = 0;
movement->timestamp = system_time();
if ((movement->timestamp - cookie->tap_time) > SYN_TAP_TIMEOUT) {
TRACE("SYNAPTICS: tap gesture timed out\n");
cookie->tap_started = false;
if (!cookie->double_click
|| (movement->timestamp - cookie->tap_time) > 2 * SYN_TAP_TIMEOUT) {
cookie->tap_clicks = 0;
}
}
if (event->buttons & kLeftButton) {
cookie->tap_clicks = 0;
cookie->tapdrag_started = false;
cookie->tap_started = false;
cookie->valid_edge_motion = false;
}
if (event->zPressure >= MIN_PRESSURE && event->zPressure < MAX_PRESSURE
&& ((event->wValue >=4 && event->wValue <=7)
|| event->wValue == 0 || event->wValue == 1)
&& (event->xPosition != 0 || event->yPosition != 0)) {
// The touch pad is in touch with at least one finger
if (!check_scrolling_to_movement(cookie, event, movement))
move_to_movement(cookie, event, movement);
} else
no_touch_to_movement(cookie, event, movement);
return B_OK;
}
static status_t
get_synaptics_movment(synaptics_cookie *cookie, mouse_movement *movement)
{
status_t status;
touch_event event;
uint8 event_buffer[PS2_MAX_PACKET_SIZE];
uint8 wValue0, wValue1, wValue2, wValue3, wValue;
uint32 val32;
uint32 xTwelfBit, yTwelfBit;
status = acquire_sem_etc(cookie->synaptics_sem, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK)
return status;
if (!cookie->dev->active) {
TRACE("SYNAPTICS: read_event: Error device no longer active\n");
return B_ERROR;
}
if (packet_buffer_read(cookie->synaptics_ring_buffer, event_buffer,
cookie->dev->packet_size) != cookie->dev->packet_size) {
TRACE("SYNAPTICS: error copying buffer\n");
return B_ERROR;
}
event.buttons = event_buffer[0] & 3;
event.zPressure = event_buffer[2];
if (sTouchpadInfo.capExtended) {
wValue0 = event_buffer[3] >> 2 & 1;
wValue1 = event_buffer[0] >> 2 & 1;
wValue2 = event_buffer[0] >> 4 & 1;
wValue3 = event_buffer[0] >> 5 & 1;
wValue = wValue0;
wValue = wValue | (wValue1 << 1);
wValue = wValue | (wValue2 << 2);
wValue = wValue | (wValue3 << 3);
event.wValue = wValue;
event.gesture = false;
} else {
bool finger = event_buffer[0] >> 5 & 1;
if (finger) {
// finger with normal width
event.wValue = 4;
}
event.gesture = event_buffer[0] >> 2 & 1;
}
event.xPosition = event_buffer[4];
event.yPosition = event_buffer[5];
val32 = event_buffer[1] & 0x0F;
event.xPosition += val32 << 8;
val32 = event_buffer[1] >> 4 & 0x0F;
event.yPosition += val32 << 8;
xTwelfBit = event_buffer[3] >> 4 & 1;
event.xPosition += xTwelfBit << 12;
yTwelfBit = event_buffer[3] >> 5 & 1;
event.yPosition += yTwelfBit << 12;
status = event_to_movement(cookie, &event, movement);
return status;
}
static void
query_capability(ps2_dev *dev)
{
uint8 val[3];
send_touchpad_arg(dev, 0x02);
ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
sTouchpadInfo.capExtended = val[0] >> 7 & 1;
TRACE("SYNAPTICS: extended mode %2x\n", val[0] >> 7 & 1);
TRACE("SYNAPTICS: sleep mode %2x\n", val[2] >> 4 & 1);
sTouchpadInfo.capSleep = val[2] >> 4 & 1;
TRACE("SYNAPTICS: four buttons %2x\n", val[2] >> 3 & 1);
sTouchpadInfo.capFourButtons = val[2] >> 3 & 1;
TRACE("SYNAPTICS: multi finger %2x\n", val[2] >> 1 & 1);
sTouchpadInfo.capMultiFinger = val[2] >> 1 & 1;
TRACE("SYNAPTICS: palm detection %2x\n", val[2] & 1);
sTouchpadInfo.capPalmDetection = val[2] & 1;
TRACE("SYNAPTICS: pass through %2x\n", val[2] >> 7 & 1);
sTouchpadInfo.capPassThrough = val[2] >> 7 & 1;
}
// #pragma mark - exported functions
status_t
synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size)
{
synaptics_cookie *synapticsCookie = dev->parent_dev->cookie;
status_t status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL,
0, NULL, 0);
if (status < B_OK) {
INFO("SYNAPTICS: cannot disable touchpad %s\n", dev->parent_dev->name);
return B_ERROR;
}
synapticsCookie->packet_index = 0;
if (size == 4)
synapticsCookie->mode |= SYN_FOUR_BYTE_CHILD;
else
synapticsCookie->mode &= ~SYN_FOUR_BYTE_CHILD;
set_touchpad_mode(dev->parent_dev, synapticsCookie->mode);
status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
if (status < B_OK) {
INFO("SYNAPTICS: cannot enable touchpad %s\n", dev->parent_dev->name);
return B_ERROR;
}
return status;
}
status_t
passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int outCount,
uint8 *in, int inCount, bigtime_t timeout)
{
status_t status;
uint8 passThroughCmd = SYN_PASSTHROUGH_CMD;
uint8 val;
uint32 passThroughInCount = (inCount + 1) * 6;
uint8 passThroughIn[passThroughInCount];
int8 i;
TRACE("SYNAPTICS: passthrough command 0x%x\n", cmd);
status = ps2_dev_command(dev->parent_dev, PS2_CMD_DISABLE, NULL, 0,
NULL, 0);
if (status != B_OK)
return status;
for (i = -1; i < outCount; i++) {
if (i == -1)
val = cmd;
else
val = out[i];
status = send_touchpad_arg_timeout(dev->parent_dev, val, timeout);
if (status != B_OK)
return status;
if (i != outCount -1) {
status = ps2_dev_command_timeout(dev->parent_dev,
PS2_CMD_SET_SAMPLE_RATE, &passThroughCmd, 1, NULL, 0, timeout);
if (status != B_OK)
return status;
}
}
status = ps2_dev_command_timeout(dev->parent_dev, PS2_CMD_SET_SAMPLE_RATE,
&passThroughCmd, 1, passThroughIn, passThroughInCount, timeout);
if (status != B_OK)
return status;
for (i = 0; i < inCount + 1; i++) {
uint8 *inPointer = &passThroughIn[i * 6];
if (!IS_SYN_PT_PACKAGE(inPointer)) {
TRACE("SYNAPTICS: not a pass throught package\n");
return B_OK;
}
if (i == 0)
continue;
in[i - 1] = passThroughIn[i * 6 + 1];
}
status = ps2_dev_command(dev->parent_dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
if (status != B_OK)
return status;
return B_OK;
}
status_t
probe_synaptics(ps2_dev *dev)
{
uint8 val[3];
uint8 deviceId;
status_t status;
TRACE("SYNAPTICS: probe\n");
status = send_touchpad_arg(dev, 0x00);
if (status != B_OK)
return status;
status = ps2_dev_command(dev, 0xE9, NULL, 0, val, 3);
if (status != B_OK)
return status;
sTouchpadInfo.minorVersion = val[0];
deviceId = val[1];
if (deviceId != SYN_TOUCHPAD) {
TRACE("SYNAPTICS: not found\n");
return B_ERROR;
}
TRACE("SYNAPTICS: Touchpad found id:l %2x\n", deviceId);
sTouchpadInfo.majorVersion = val[2] & 0x0F;
TRACE("SYNAPTICS: version %d.%d\n", sTouchpadInfo.majorVersion,
sTouchpadInfo.minorVersion);
// version >= 4.0?
if (sTouchpadInfo.minorVersion <= 2
&& sTouchpadInfo.majorVersion <= 3) {
TRACE("SYNAPTICS: too old touchpad not supported\n");
return B_ERROR;
}
dev->name = kSynapticsPath[dev->idx];
return B_OK;
}
// #pragma mark - Device functions
status_t
synaptics_open(const char *name, uint32 flags, void **_cookie)
{
status_t status;
synaptics_cookie *cookie;
ps2_dev *dev;
int i;
for (dev = NULL, i = 0; i < PS2_DEVICE_COUNT; i++) {
if (0 == strcmp(ps2_device[i].name, name)) {
dev = &ps2_device[i];
break;
}
}
if (dev == NULL) {
TRACE("ps2: dev = NULL\n");
return B_ERROR;
}
if (atomic_or(&dev->flags, PS2_FLAG_OPEN) & PS2_FLAG_OPEN)
return B_BUSY;
cookie = (synaptics_cookie*)malloc(sizeof(synaptics_cookie));
if (cookie == NULL)
goto err1;
*_cookie = cookie;
memset(cookie, 0, sizeof(synaptics_cookie));
cookie->dev = dev;
dev->cookie = cookie;
dev->disconnect = &synaptics_disconnect;
dev->handle_int = &synaptics_handle_int;
default_settings(&cookie->settings);
cookie->movement_maker.speed = 1;
cookie->movement_maker.scrolling_xStep = cookie->settings.scroll_xstepsize;
cookie->movement_maker.scrolling_yStep = cookie->settings.scroll_ystepsize;
cookie->movement_maker.scroll_acceleration
= cookie->settings.scroll_acceleration;
cookie->movement_started = false;
cookie->scrolling_started = false;
cookie->tap_started = false;
cookie->double_click = false;
cookie->valid_edge_motion = false;
dev->packet_size = PS2_PACKET_SYNAPTICS;
cookie->synaptics_ring_buffer
= create_packet_buffer(SYNAPTICS_HISTORY_SIZE * dev->packet_size);
if (cookie->synaptics_ring_buffer == NULL) {
TRACE("ps2: can't allocate mouse actions buffer\n");
goto err2;
}
// create the mouse semaphore, used for synchronization between
// the interrupt handler and the read operation
cookie->synaptics_sem = create_sem(0, "ps2_synaptics_sem");
if (cookie->synaptics_sem < 0) {
TRACE("SYNAPTICS: failed creating semaphore!\n");
goto err3;
}
query_capability(dev);
// create pass through dev
if (sTouchpadInfo.capPassThrough) {
TRACE("SYNAPTICS: pass through detected\n");
sPassthroughDevice->parent_dev = dev;
sPassthroughDevice->idx = dev->idx;
ps2_service_notify_device_added(sPassthroughDevice);
}
// Set Mode
if (sTouchpadInfo.capExtended)
cookie->mode = SYN_ABSOLUTE_W_MODE;
else
cookie->mode = SYN_ABSOLUTE_MODE;
status = set_touchpad_mode(dev, cookie->mode);
if (status < B_OK) {
INFO("SYNAPTICS: cannot set mode %s\n", name);
goto err4;
}
status = ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0);
if (status < B_OK) {
INFO("SYNAPTICS: cannot enable touchpad %s\n", name);
goto err4;
}
atomic_or(&dev->flags, PS2_FLAG_ENABLED);
TRACE("SYNAPTICS: open %s success\n", name);
return B_OK;
err4:
delete_sem(cookie->synaptics_sem);
err3:
delete_packet_buffer(cookie->synaptics_ring_buffer);
err2:
free(cookie);
err1:
atomic_and(&dev->flags, ~PS2_FLAG_OPEN);
TRACE("SYNAPTICS: synaptics_open %s failed\n", name);
return B_ERROR;
}
status_t
synaptics_close(void *_cookie)
{
status_t status;
synaptics_cookie *cookie = _cookie;
ps2_dev_command_timeout(cookie->dev, PS2_CMD_DISABLE, NULL, 0, NULL, 0,
150000);
delete_packet_buffer(cookie->synaptics_ring_buffer);
delete_sem(cookie->synaptics_sem);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_OPEN);
atomic_and(&cookie->dev->flags, ~PS2_FLAG_ENABLED);
// Reset the touchpad so it generate standard ps2 packets instead of
// extended ones. If not, BeOS is confused with such packets when rebooting
// without a complete shutdown.
status = ps2_reset_mouse(cookie->dev);
if (status != B_OK) {
INFO("ps2: reset failed\n");
return B_ERROR;
}
if (sTouchpadInfo.capPassThrough)
ps2_service_notify_device_removed(sPassthroughDevice);
TRACE("SYNAPTICS: close %s done\n", cookie->dev->name);
return B_OK;
}
status_t
synaptics_freecookie(void *_cookie)
{
free(_cookie);
return B_OK;
}
static status_t
synaptics_read(void *cookie, off_t pos, void *buffer, size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
static status_t
synaptics_write(void *cookie, off_t pos, const void *buffer, size_t *_length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
status_t
synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
{
synaptics_cookie *cookie = _cookie;
mouse_movement movement;
status_t status;
switch (op) {
case MS_READ:
TRACE("SYNAPTICS: MS_READ get event\n");
if ((status = get_synaptics_movment(cookie, &movement)) != B_OK)
return status;
return user_memcpy(buffer, &movement, sizeof(movement));
case MS_IS_TOUCHPAD:
TRACE("SYNAPTICS: MS_IS_TOUCHPAD\n");
return B_OK;
case MS_SET_TOUCHPAD_SETTINGS:
TRACE("SYNAPTICS: MS_SET_TOUCHPAD_SETTINGS");
user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
cookie->movement_maker.scrolling_xStep
= cookie->settings.scroll_xstepsize;
cookie->movement_maker.scrolling_yStep
= cookie->settings.scroll_ystepsize;
cookie->movement_maker.scroll_acceleration
= cookie->settings.scroll_acceleration;
return B_OK;
case MS_SET_CLICKSPEED:
TRACE("SYNAPTICS: ioctl MS_SETCLICK (set click speed)\n");
return user_memcpy(&cookie->click_speed, buffer, sizeof(bigtime_t));
default:
TRACE("SYNAPTICS: unknown opcode: %ld\n", op);
return B_DEV_INVALID_IOCTL;
}
}
int32
synaptics_handle_int(ps2_dev *dev)
{
synaptics_cookie *cookie = dev->cookie;
uint8 val;
val = cookie->dev->history[0].data;
if ((cookie->packet_index == 0 || cookie->packet_index == 3)
&& (val & 8) != 0) {
INFO("SYNAPTICS: bad mouse data, trying resync\n");
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
if (cookie->packet_index == 0 && val >> 6 != 0x02) {
TRACE("SYNAPTICS: first package begins not with bit 1, 0\n");
return B_UNHANDLED_INTERRUPT;
}
if (cookie->packet_index == 3 && val >> 6 != 0x03) {
TRACE("SYNAPTICS: third package begins not with bit 1, 1\n");
cookie->packet_index = 0;
return B_UNHANDLED_INTERRUPT;
}
cookie->packet_buffer[cookie->packet_index] = val;
cookie->packet_index++;
if (cookie->packet_index >= 6) {
cookie->packet_index = 0;
// check if package is a pass through package if true pass it
// too the pass through interrupt handle
if (sPassthroughDevice->active
&& sPassthroughDevice->handle_int != NULL
&& IS_SYN_PT_PACKAGE(cookie->packet_buffer)) {
status_t status;
sPassthroughDevice->history[0].data = cookie->packet_buffer[1];
sPassthroughDevice->handle_int(sPassthroughDevice);
sPassthroughDevice->history[0].data = cookie->packet_buffer[4];
sPassthroughDevice->handle_int(sPassthroughDevice);
sPassthroughDevice->history[0].data = cookie->packet_buffer[5];
status = sPassthroughDevice->handle_int(sPassthroughDevice);
if (cookie->dev->packet_size == 4) {
sPassthroughDevice->history[0].data = cookie->packet_buffer[2];
status = sPassthroughDevice->handle_int(sPassthroughDevice);
}
return status;
}
if (packet_buffer_write(cookie->synaptics_ring_buffer,
cookie->packet_buffer, cookie->dev->packet_size)
!= cookie->dev->packet_size) {
// buffer is full, drop new data
return B_HANDLED_INTERRUPT;
}
release_sem_etc(cookie->synaptics_sem, 1, B_DO_NOT_RESCHEDULE);
return B_INVOKE_SCHEDULER;
}
return B_HANDLED_INTERRUPT;
}
void
synaptics_disconnect(ps2_dev *dev)
{
synaptics_cookie *cookie = dev->cookie;
// the mouse device might not be opened at this point
INFO("SYNAPTICS: synaptics_disconnect %s\n", dev->name);
if ((dev->flags & PS2_FLAG_OPEN) != 0)
release_sem(cookie->synaptics_sem);
}
device_hooks gSynapticsDeviceHooks = {
synaptics_open,
synaptics_close,
synaptics_freecookie,
synaptics_ioctl,
synaptics_read,
synaptics_write,
};
@@ -1,43 +0,0 @@
/*
* Copyright 2009-2010, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Authors:
* Clemens Zeidler (haiku@Clemens-Zeidler.de)
*/
#include <malloc.h>
#include <string.h>
#include <keyboard_mouse_driver.h>
#include "ps2_trackpoint.h"
const char* kTrackpointPath[4] = {
"input/mouse/ps2/ibm_trackpoint_0",
"input/mouse/ps2/ibm_trackpoint_1",
"input/mouse/ps2/ibm_trackpoint_2",
"input/mouse/ps2/ibm_trackpoint_3"
};
status_t
probe_trackpoint(ps2_dev* dev)
{
uint8 val[2];
TRACE("TRACKPOINT: probe\n");
ps2_dev_command(dev, 0xE1, NULL, 0, val, 2);
if (val[0] != 0x01) {
TRACE("TRACKPOINT: not found\n");
return B_ERROR;
}
dev->name = kTrackpointPath[dev->idx];
dev->packet_size = 3;
TRACE("TRACKPOINT: version 0x%x found\n", val[1]);
return B_OK;
}