diff --git a/src/add-ons/kernel/bus_managers/ps2/Jamfile b/src/add-ons/kernel/bus_managers/ps2/Jamfile index daae0fc5fe..d2abde2182 100644 --- a/src/add-ons/kernel/bus_managers/ps2/Jamfile +++ b/src/add-ons/kernel/bus_managers/ps2/Jamfile @@ -5,14 +5,14 @@ UsePrivateKernelHeaders ; KernelAddon ps2 : packet_buffer.cpp - ps2_alps.c - ps2_common.c - ps2_dev.c + ps2_alps.cpp + ps2_common.cpp + ps2_dev.cpp ps2_keyboard.cpp - ps2_module.c - ps2_standard_mouse.c - ps2_synaptics.c - ps2_trackpoint.c + ps2_module.cpp + ps2_standard_mouse.cpp + ps2_synaptics.cpp + ps2_trackpoint.cpp ps2_service.cpp - movement_maker.c + movement_maker.cpp ; diff --git a/src/add-ons/kernel/bus_managers/ps2/movement_maker.cpp b/src/add-ons/kernel/bus_managers/ps2/movement_maker.cpp new file mode 100644 index 0000000000..4c350a9588 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/movement_maker.cpp @@ -0,0 +1,314 @@ +#include "movement_maker.h" + +#include +#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; +} + diff --git a/src/add-ons/kernel/bus_managers/ps2/movement_maker.h b/src/add-ons/kernel/bus_managers/ps2/movement_maker.h index a9dd6c4030..9f92e77ef1 100644 --- a/src/add-ons/kernel/bus_managers/ps2/movement_maker.h +++ b/src/add-ons/kernel/bus_managers/ps2/movement_maker.h @@ -30,7 +30,6 @@ typedef struct { } movement_maker; - void get_raw_movement(movement_maker *move, uint32 posX, uint32 posY); void compute_acceleration(movement_maker *move, int8 accel_factor); void get_movement(movement_maker *move, uint32 posX, uint32 posY); diff --git a/src/add-ons/kernel/bus_managers/ps2/packet_buffer.cpp b/src/add-ons/kernel/bus_managers/ps2/packet_buffer.cpp index 930e69ed1a..a8b1b26aee 100644 --- a/src/add-ons/kernel/bus_managers/ps2/packet_buffer.cpp +++ b/src/add-ons/kernel/bus_managers/ps2/packet_buffer.cpp @@ -6,32 +6,16 @@ #include "packet_buffer.h" -#include #include #include #include -/** The idea behind this packet buffer is to have multi-threading safe - * implementation that can be used in interrupts on top of the - * ring_buffer implementation provided by the kernel. - * It uses a spinlock for synchronization. - * - * IOW if you don't have such high restrictions in your environment, - * you better don't want to use it at all. - */ - -struct packet_buffer { - struct ring_buffer *buffer; - spinlock lock; -}; - - -struct packet_buffer * +packet_buffer* create_packet_buffer(size_t size) { - struct packet_buffer *buffer + packet_buffer* buffer = (packet_buffer *)malloc(sizeof(packet_buffer)); if (buffer == NULL) return NULL; @@ -48,7 +32,7 @@ create_packet_buffer(size_t size) void -delete_packet_buffer(struct packet_buffer *buffer) +delete_packet_buffer(packet_buffer* buffer) { delete_ring_buffer(buffer->buffer); free(buffer); @@ -56,7 +40,7 @@ delete_packet_buffer(struct packet_buffer *buffer) void -packet_buffer_clear(struct packet_buffer *buffer) +packet_buffer_clear(packet_buffer* buffer) { cpu_status state = disable_interrupts(); acquire_spinlock(&buffer->lock); @@ -69,7 +53,7 @@ packet_buffer_clear(struct packet_buffer *buffer) size_t -packet_buffer_readable(struct packet_buffer *buffer) +packet_buffer_readable(packet_buffer* buffer) { cpu_status state = disable_interrupts(); acquire_spinlock(&buffer->lock); @@ -84,7 +68,7 @@ packet_buffer_readable(struct packet_buffer *buffer) size_t -packet_buffer_writable(struct packet_buffer *buffer) +packet_buffer_writable(packet_buffer* buffer) { cpu_status state = disable_interrupts(); acquire_spinlock(&buffer->lock); @@ -99,7 +83,7 @@ packet_buffer_writable(struct packet_buffer *buffer) void -packet_buffer_flush(struct packet_buffer *buffer, size_t length) +packet_buffer_flush(packet_buffer* buffer, size_t length) { cpu_status state = disable_interrupts(); acquire_spinlock(&buffer->lock); @@ -112,7 +96,7 @@ packet_buffer_flush(struct packet_buffer *buffer, size_t length) size_t -packet_buffer_read(struct packet_buffer *buffer, uint8 *data, size_t length) +packet_buffer_read(packet_buffer* buffer, uint8 *data, size_t length) { cpu_status state = disable_interrupts(); acquire_spinlock(&buffer->lock); @@ -127,7 +111,7 @@ packet_buffer_read(struct packet_buffer *buffer, uint8 *data, size_t length) size_t -packet_buffer_write(struct packet_buffer *buffer, const uint8 *data, +packet_buffer_write(packet_buffer* buffer, const uint8 *data, size_t length) { cpu_status state = disable_interrupts(); diff --git a/src/add-ons/kernel/bus_managers/ps2/packet_buffer.h b/src/add-ons/kernel/bus_managers/ps2/packet_buffer.h index 75588275db..409c63eaa5 100644 --- a/src/add-ons/kernel/bus_managers/ps2/packet_buffer.h +++ b/src/add-ons/kernel/bus_managers/ps2/packet_buffer.h @@ -6,30 +6,37 @@ #define PACKET_BUFFER_H +#include #include -struct packet_buffer; -typedef struct packet_buffer packet_buffer; +struct ring_buffer; -#ifdef __cplusplus -extern "C" { -#endif -struct packet_buffer *create_packet_buffer(size_t size); -void delete_packet_buffer(struct packet_buffer *buffer); +/** The idea behind this packet buffer is to have multi-threading safe + * implementation that can be used in interrupts on top of the + * ring_buffer implementation provided by the kernel. + * It uses a spinlock for synchronization. + * + * IOW if you don't have such high restrictions in your environment, + * you better don't want to use it at all. + */ +struct packet_buffer { + struct ring_buffer* buffer; + spinlock lock; +}; -void packet_buffer_clear(struct packet_buffer *buffer); -size_t packet_buffer_readable(struct packet_buffer *buffer); + +struct packet_buffer* create_packet_buffer(size_t size); +void delete_packet_buffer(struct packet_buffer* buffer); + +void packet_buffer_clear(struct packet_buffer* buffer); +size_t packet_buffer_readable(struct packet_buffer* buffer); size_t packet_buffer_writable(struct packet_buffer *buffer); -void packet_buffer_flush(struct packet_buffer *buffer, size_t bytes); -size_t packet_buffer_read(struct packet_buffer *buffer, uint8 *data, size_t +void packet_buffer_flush(struct packet_buffer* buffer, size_t bytes); +size_t packet_buffer_read(struct packet_buffer* buffer, uint8* data, size_t length); -size_t packet_buffer_write(struct packet_buffer *buffer, const uint8 *data, +size_t packet_buffer_write(struct packet_buffer* buffer, const uint8* data, size_t length); -#ifdef __cplusplus -} -#endif - #endif /* PACKET_BUFFER_H */ diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_alps.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_alps.cpp new file mode 100644 index 0000000000..cb5b73b2f4 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_alps.cpp @@ -0,0 +1,836 @@ +/* + * 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 (haiku@Clemens-Zeidler.de) + */ + + +#include "ps2_alps.h" + +#include +#include + +#include + +#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 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->movementMaker); + } + + get_movement(&cookie->movementMaker, event->xPosition, event->yPosition); + + movement->xdelta = cookie->movementMaker.xDelta; + movement->ydelta = cookie->movementMaker.yDelta; + + // tap gesture + cookie->tap_delta_x += cookie->movementMaker.xDelta; + cookie->tap_delta_y += cookie->movementMaker.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->movementMaker); + } + get_scrolling(&cookie->movementMaker, event->xPosition, + event->yPosition); + movement->wheel_ydelta = cookie->movementMaker.yDelta; + movement->wheel_xdelta = cookie->movementMaker.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 uint32 minX = 50000; +static uint32 minY = 50000; +static uint32 maxX = 0; +static uint32 maxY = 0; +static uint32 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 = PS2_PACKET_ALPS; + + 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->movementMaker.speed = 1 * SPEED_FACTOR; + cookie->movementMaker.scrolling_xStep = cookie->settings.scroll_xstepsize; + cookie->movementMaker.scrolling_yStep = cookie->settings.scroll_ystepsize; + cookie->movementMaker.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 = (alps_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 = (alps_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->movementMaker.scrolling_xStep + = cookie->settings.scroll_xstepsize; + cookie->movementMaker.scrolling_yStep + = cookie->settings.scroll_ystepsize; + cookie->movementMaker.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 = (alps_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->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->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 = (alps_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, +}; + diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_alps.h b/src/add-ons/kernel/bus_managers/ps2/ps2_alps.h index a80f9bfb97..8cb6165923 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_alps.h +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_alps.h @@ -14,12 +14,12 @@ typedef struct { ps2_dev* dev; sem_id sem; - packet_buffer* ring_buffer; +struct packet_buffer* ring_buffer; size_t packet_index; - uint8 packet_buffer[PS2_PACKET_ALPS]; + uint8 buffer[PS2_PACKET_ALPS]; uint8 mode; - movement_maker movement_maker; + movement_maker movementMaker; bool movement_started; bool scrolling_started; bool tap_started; @@ -50,7 +50,7 @@ status_t alps_ioctl(void *_cookie, uint32 op, void *buffer, size_t length); int32 alps_handle_int(ps2_dev *dev); void alps_disconnect(ps2_dev *dev); -device_hooks gALPSDeviceHooks; +extern device_hooks gALPSDeviceHooks; #endif /* ALPS_H */ diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_common.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_common.cpp new file mode 100644 index 0000000000..442561a34e --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_common.cpp @@ -0,0 +1,418 @@ +/* + * Copyright 2004-2009 Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors (in chronological order): + * Stefano Ceccherini (burton666@libero.it) + * Axel Dörfler, axeld@pinc-software.de + * Marcus Overhagen + */ + +/*! PS/2 bus manager */ + + +#include + +#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); +} diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c b/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp similarity index 89% rename from src/add-ons/kernel/bus_managers/ps2/ps2_dev.c rename to src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp index 4200faf2c8..3d6945b889 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_dev.c +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_dev.cpp @@ -23,49 +23,7 @@ #include -ps2_dev ps2_device[PS2_DEVICE_COUNT] = { - { - .name = "input/mouse/ps2/0", - .active = false, - .idx = 0, - .result_sem = -1, - .command = standard_command_timeout - }, - { - .name = "input/mouse/ps2/1", - .active = false, - .idx = 1, - .result_sem = -1, - .command = standard_command_timeout - }, - { - .name = "input/mouse/ps2/2", - .active = false, - .idx = 2, - .result_sem = -1, - .command = standard_command_timeout - }, - { - .name = "input/mouse/ps2/3", - .active = false, - .idx = 3, - .result_sem = -1, - .command = standard_command_timeout - }, - { - .name = "input/mouse/ps2/synaptics_passthrough", - .active = false, - .result_sem = -1, - .command = passthrough_command - }, - { - .name = "input/keyboard/at/0", - .active = false, - .result_sem = -1, - .flags = PS2_FLAG_KEYB, - .command = standard_command_timeout - } -}; +ps2_dev ps2_device[PS2_DEVICE_COUNT]; status_t @@ -149,6 +107,41 @@ dev_found: status_t ps2_dev_init(void) { + ps2_device[0].name = "input/mouse/ps2/0"; + ps2_device[0].active = false; + ps2_device[0].idx = 0; + ps2_device[0].result_sem = -1; + ps2_device[0].command = standard_command_timeout; + + ps2_device[1].name = "input/mouse/ps2/1"; + ps2_device[1].active = false; + ps2_device[1].idx = 1; + ps2_device[1].result_sem = -1; + ps2_device[1].command = standard_command_timeout; + + ps2_device[2].name = "input/mouse/ps2/2"; + ps2_device[2].active = false; + ps2_device[2].idx = 2; + ps2_device[2].result_sem = -1; + ps2_device[2].command = standard_command_timeout; + + ps2_device[3].name = "input/mouse/ps2/3"; + ps2_device[3].active = false; + ps2_device[3].idx = 3; + ps2_device[3].result_sem = -1; + ps2_device[3].command = standard_command_timeout; + + ps2_device[4].name = "input/mouse/ps2/synaptics_passthrough"; + ps2_device[4].active = false; + ps2_device[4].result_sem = -1; + ps2_device[4].command = passthrough_command; + + ps2_device[5].name = "input/keyboard/at/0"; + ps2_device[5].active = false; + ps2_device[5].result_sem = -1; + ps2_device[5].flags = PS2_FLAG_KEYB; + ps2_device[5].command = standard_command_timeout; + int i; for (i = 0; i < PS2_DEVICE_COUNT; i++) { ps2_dev *dev = &ps2_device[i]; diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_module.c b/src/add-ons/kernel/bus_managers/ps2/ps2_module.cpp similarity index 84% rename from src/add-ons/kernel/bus_managers/ps2/ps2_module.c rename to src/add-ons/kernel/bus_managers/ps2/ps2_module.cpp index 57c3211188..abb7e0b218 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_module.c +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_module.cpp @@ -45,13 +45,13 @@ std_ops(int32 op, ...) static ps2_module_info ps2_module = { - .binfo = { - .minfo = { - .name = B_PS2_MODULE_NAME, - .flags = B_KEEP_LOADED, - .std_ops = std_ops, + { + { + B_PS2_MODULE_NAME, + B_KEEP_LOADED, + std_ops }, - .rescan = NULL, + NULL }, &function1, &function2, diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.cpp new file mode 100644 index 0000000000..e9082affc2 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.cpp @@ -0,0 +1,525 @@ +/* + * Copyright 2001-2010 Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors (in chronological order): + * Elad Lahav (elad@eldarshany.com) + * Stefano Ceccherini (burton666@libero.it) + * Axel Dörfler, axeld@pinc-software.de + * Marcus Overhagen + * Clemens Zeidler + */ + + +/*! 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 +#include + +#include + +#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->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->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->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->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 = (standard_mouse_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, +}; diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.h b/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.h index 1099181b45..0de867c4e2 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.h +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_standard_mouse.h @@ -27,24 +27,20 @@ #define F_MOUSE_TYPE_INTELLIMOUSE 0x2 typedef struct { - ps2_dev* dev; + ps2_dev* dev; - sem_id standard_mouse_sem; - packet_buffer* standard_mouse_buffer; - bigtime_t click_last_time; - bigtime_t click_speed; - int click_count; - int buttons_state; - int flags; - size_t packet_index; - uint8 packet_buffer[PS2_MAX_PACKET_SIZE]; + sem_id standard_mouse_sem; +struct packet_buffer* standard_mouse_buffer; + bigtime_t click_last_time; + bigtime_t click_speed; + int click_count; + int buttons_state; + int flags; + size_t packet_index; + uint8 buffer[PS2_MAX_PACKET_SIZE]; } standard_mouse_cookie; -#ifdef __cplusplus -extern "C" { -#endif - status_t probe_standard_mouse(ps2_dev* dev); status_t standard_mouse_open(const char* name, uint32 flags, void** _cookie); @@ -56,11 +52,7 @@ status_t standard_mouse_ioctl(void* _cookie, uint32 op, void* buffer, int32 standard_mouse_handle_int(ps2_dev* dev); void standard_mouse_disconnect(ps2_dev* dev); -device_hooks gStandardMouseDeviceHooks; - -#ifdef __cplusplus -} -#endif +extern device_hooks gStandardMouseDeviceHooks; #endif /* __PS2_STANDARD_MOUSE_H */ diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp new file mode 100644 index 0000000000..2c6a4e9c03 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.cpp @@ -0,0 +1,859 @@ +/* + * Copyright 2008-2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors (in chronological order): + * Clemens Zeidler (haiku@Clemens-Zeidler.de) + * Axel Dörfler, axeld@pinc-software.de + */ + + +//! PS/2 synaptics touchpad + + +#include "ps2_synaptics.h" + +#include +#include +#include + +#include + +#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->movementMaker); + } + + get_movement(&cookie->movementMaker, event->xPosition, event->yPosition); + + movement->xdelta = cookie->movementMaker.xDelta; + movement->ydelta = cookie->movementMaker.yDelta; + + // tap gesture + cookie->tap_delta_x += cookie->movementMaker.xDelta; + cookie->tap_delta_y += cookie->movementMaker.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->movementMaker); + } + get_scrolling(&cookie->movementMaker, event->xPosition, + event->yPosition); + movement->wheel_ydelta = cookie->movementMaker.yDelta; + movement->wheel_xdelta = cookie->movementMaker.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 + = (synaptics_cookie*)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->movementMaker.speed = 1; + cookie->movementMaker.scrolling_xStep = cookie->settings.scroll_xstepsize; + cookie->movementMaker.scrolling_yStep = cookie->settings.scroll_ystepsize; + cookie->movementMaker.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 = (synaptics_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 = (synaptics_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->movementMaker.scrolling_xStep + = cookie->settings.scroll_xstepsize; + cookie->movementMaker.scrolling_yStep + = cookie->settings.scroll_ystepsize; + cookie->movementMaker.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 = (synaptics_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->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->buffer)) { + status_t status; + + sPassthroughDevice->history[0].data = cookie->buffer[1]; + sPassthroughDevice->handle_int(sPassthroughDevice); + sPassthroughDevice->history[0].data = cookie->buffer[4]; + sPassthroughDevice->handle_int(sPassthroughDevice); + sPassthroughDevice->history[0].data = cookie->buffer[5]; + status = sPassthroughDevice->handle_int(sPassthroughDevice); + + if (cookie->dev->packet_size == 4) { + sPassthroughDevice->history[0].data = cookie->buffer[2]; + status = sPassthroughDevice->handle_int(sPassthroughDevice); + } + return status; + } + + if (packet_buffer_write(cookie->synaptics_ring_buffer, + cookie->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 = (synaptics_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, +}; diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h index 8143a9000d..8cb30ef619 100644 --- a/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_synaptics.h @@ -94,12 +94,12 @@ typedef struct { ps2_dev* dev; sem_id synaptics_sem; - packet_buffer* synaptics_ring_buffer; +struct packet_buffer* synaptics_ring_buffer; size_t packet_index; - uint8 packet_buffer[PS2_PACKET_SYNAPTICS]; + uint8 buffer[PS2_PACKET_SYNAPTICS]; uint8 mode; - movement_maker movement_maker; + movement_maker movementMaker; bool movement_started; bool scrolling_started; bool tap_started; @@ -120,7 +120,6 @@ typedef struct { } synaptics_cookie; - status_t synaptics_pass_through_set_packet_size(ps2_dev *dev, uint8 size); status_t passthrough_command(ps2_dev *dev, uint8 cmd, const uint8 *out, int out_count, uint8 *in, int in_count, bigtime_t timeout); @@ -134,6 +133,6 @@ status_t synaptics_ioctl(void *_cookie, uint32 op, void *buffer, size_t length); int32 synaptics_handle_int(ps2_dev *dev); void synaptics_disconnect(ps2_dev *dev); -device_hooks gSynapticsDeviceHooks; +extern device_hooks gSynapticsDeviceHooks; #endif // PS2_SYNAPTICS_H diff --git a/src/add-ons/kernel/bus_managers/ps2/ps2_trackpoint.cpp b/src/add-ons/kernel/bus_managers/ps2/ps2_trackpoint.cpp new file mode 100644 index 0000000000..c2be0aea28 --- /dev/null +++ b/src/add-ons/kernel/bus_managers/ps2/ps2_trackpoint.cpp @@ -0,0 +1,43 @@ +/* + * Copyright 2009-2010, Haiku, Inc. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler (haiku@Clemens-Zeidler.de) + */ + + +#include +#include + +#include + +#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; +}