/*
* Copyright 2013, Haiku, Inc.
* Distributed under the terms of the MIT License.
*
* Hardware specs taken from the linux driver, thanks a lot!
* Based on ps2_alps.c
*
* Authors:
* Jérôme Duval <korli@users.berlios.de>
*/
#include "ps2_elantech.h"
#include <stdlib.h>
#include <string.h>
#include "ps2_service.h"
static int32 generate_event(timer* timer);
const bigtime_t kEventInterval = 1000 * 50;
class EventProducer {
public:
EventProducer()
{
fFired = false;
}
status_t
FireEvent(elantech_cookie* cookie, uint8* package)
{
fCookie = cookie;
memcpy(fLastPackage, package, sizeof(uint8) * PS2_PACKET_ELANTECH);
status_t status = add_timer(&fEventTimer, &generate_event,
kEventInterval, B_ONE_SHOT_RELATIVE_TIMER);
if (status == B_OK)
fFired = true;
return status;
}
bool
CancelEvent()
{
if (!fFired)
return false;
fFired = false;
return cancel_timer(&fEventTimer);
}
void
InjectEvent()
{
if (packet_buffer_write(fCookie->ring_buffer, fLastPackage,
PS2_PACKET_ELANTECH) != PS2_PACKET_ELANTECH) {
// buffer is full, drop new data
return;
}
release_sem_etc(fCookie->sem, 1, B_DO_NOT_RESCHEDULE);
}
private:
bool fFired;
uint8 fLastPackage[PS2_PACKET_ELANTECH];
timer fEventTimer;
elantech_cookie* fCookie;
};
static EventProducer gEventProducer;
static int32
generate_event(timer* timer)
{
gEventProducer.InjectEvent();
return B_HANDLED_INTERRUPT;
}
const char* kElantechPath[4] = {
"input/touchpad/ps2/elantech_0",
"input/touchpad/ps2/elantech_1",
"input/touchpad/ps2/elantech_2",
"input/touchpad/ps2/elantech_3"
};
#define ELANTECH_CMD_GET_ID 0x00
#define ELANTECH_CMD_GET_VERSION 0x01
#define ELANTECH_CMD_GET_CAPABILITIES 0x02
#define ELANTECH_CMD_GET_SAMPLE 0x03
#define ELANTECH_CMD_GET_RESOLUTION 0x04
#define ELANTECH_CMD_REGISTER_READ 0x10
#define ELANTECH_CMD_REGISTER_WRITE 0x11
#define ELANTECH_CMD_REGISTER_READWRITE 0x00
#define ELANTECH_CMD_PS2_CUSTOM_CMD 0xf8
// touchpad proportions
#define EDGE_MOTION_WIDTH 55
#define MIN_PRESSURE 0
#define REAL_MAX_PRESSURE 50
#define MAX_PRESSURE 255
#define ELANTECH_HISTORY_SIZE 256
#define STATUS_PACKET 0x0
#define HEAD_PACKET 0x1
#define MOTION_PACKET 0x2
static hardware_specs gHardwareSpecs;
static status_t
get_elantech_movement(elantech_cookie *cookie, mouse_movement *movement)
{
touch_event event;
uint8 packet[PS2_PACKET_ELANTECH];
status_t status = acquire_sem_etc(cookie->sem, 1, B_CAN_INTERRUPT, 0);
if (status < B_OK)
return status;
if (!cookie->dev->active) {
TRACE("ELANTECH: read_event: Error device no longer active\n");
return B_ERROR;
}
if (packet_buffer_read(cookie->ring_buffer, packet,
cookie->dev->packet_size) != cookie->dev->packet_size) {
TRACE("ELANTECH: error copying buffer\n");
return B_ERROR;
}
if (cookie->crcEnabled && (packet[3] & 0x08) != 0) {
TRACE("ELANTECH: bad crc buffer\n");
return B_ERROR;
} else if (!cookie->crcEnabled && ((packet[0] & 0x0c) != 0x04
|| (packet[3] & 0x1c) != 0x10)) {
TRACE("ELANTECH: bad crc buffer\n");
return B_ERROR;
}
// https://www.kernel.org/doc/html/v4.16/input/devices/elantech.html
uint8 packet_type = packet[3] & 3;
TRACE("ELANTECH: packet type %d\n", packet_type);
TRACE("ELANTECH: packet content 0x%02x%02x%02x%02x%02x%02x\n",
packet[0], packet[1], packet[2], packet[3],
packet[4], packet[5]);
switch (packet_type) {
case STATUS_PACKET:
//fingers, no palm
cookie->fingers = (packet[4] & 0x80) == 0 ? packet[1] & 0x1f: 0;
dprintf("ELANTECH: Fingers %" B_PRId32 ", raw %x (STATUS)\n",
cookie->fingers, packet[1]);
break;
case HEAD_PACKET:
dprintf("ELANTECH: Fingers %d, raw %x (HEAD)\n", (packet[3] & 0xe0) >>5, packet[3]);
// only process first finger
if ((packet[3] & 0xe0) != 0x20)
return B_OK;
event.zPressure = (packet[1] & 0xf0) | ((packet[4] & 0xf0) >> 4);
cookie->previousZ = event.zPressure;
cookie->x = event.xPosition = ((packet[1] & 0xf) << 8) | packet[2];
cookie->y = event.yPosition = ((packet[4] & 0xf) << 8) | packet[5];
dprintf("ELANTECH: Pos: %" B_PRId32 ":%" B_PRId32 "\n (HEAD)",
cookie->x, cookie->y);
TRACE("ELANTECH: buttons 0x%x x %" B_PRIu32 " y %" B_PRIu32
" z %d\n", event.buttons, event.xPosition, event.yPosition,
event.zPressure);
break;
case MOTION_PACKET:
dprintf("ELANTECH: Fingers %d, raw %x (MOTION)\n", (packet[3] & 0xe0) >>5, packet[3]); //Most likely palm
if (cookie->fingers == 0) return B_OK;
//handle overflow and delta values
if ((packet[0] & 0x10) == 1) {
event.xPosition = cookie->x += 5 * (int8)packet[1];
event.yPosition = cookie->y += 5 * (int8)packet[2];
} else {
event.xPosition = cookie->x += (int8)packet[1];
event.yPosition = cookie->y += (int8)packet[2];
}
dprintf("ELANTECH: Pos: %" B_PRId32 ":%" B_PRId32 " (Motion)\n",
cookie->x, cookie->y);
break;
default:
dprintf("ELANTECH: unknown packet type %d\n", packet_type);
return B_ERROR;
}
event.buttons = 0;
event.wValue = cookie->fingers == 1 ? 4 :0;
status = cookie->movementMaker.EventToMovement(&event, movement);
if (cookie->movementMaker.WasEdgeMotion()
|| cookie->movementMaker.TapDragStarted()) {
gEventProducer.FireEvent(cookie, packet);
}
return status;
}
static void
default_settings(touchpad_settings *set)
{
memcpy(set, &kDefaultTouchpadSettings, sizeof(touchpad_settings));
}
static status_t
synaptics_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count)
{
if (ps2_dev_sliced_command(dev, cmd) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count)
!= B_OK) {
TRACE("ELANTECH: synaptics_dev_send_command failed\n");
return B_ERROR;
}
return B_OK;
}
static status_t
elantech_dev_send_command(ps2_dev* dev, uint8 cmd, uint8 *in, int in_count)
{
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, cmd) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, in, in_count)
!= B_OK) {
TRACE("ELANTECH: elantech_dev_send_command failed\n");
return B_ERROR;
}
return B_OK;
}
status_t
probe_elantech(ps2_dev* dev)
{
uint8 val[3];
TRACE("ELANTECH: probe\n");
ps2_dev_command(dev, PS2_CMD_MOUSE_RESET_DIS);
if (ps2_dev_command(dev, PS2_CMD_DISABLE) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK) {
TRACE("ELANTECH: not found (1)\n");
return B_ERROR;
}
if (ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val, 3)
!= B_OK) {
TRACE("ELANTECH: not found (2)\n");
return B_ERROR;
}
if (val[0] != 0x3c || val[1] != 0x3 || (val[2] != 0xc8 && val[2] != 0x0)) {
TRACE("ELANTECH: not found (3)\n");
return B_ERROR;
}
val[0] = 0;
if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3)
!= B_OK) {
TRACE("ELANTECH: not found (4)\n");
return B_ERROR;
}
if (val[0] == 0x0 || val[2] == 10 || val[2] == 20 || val[2] == 40
|| val[2] == 60 || val[2] == 80 || val[2] == 100 || val[2] == 200) {
TRACE("ELANTECH: not found (5)\n");
return B_ERROR;
}
INFO("Elantech version %02X%02X%02X, under developement! Using fallback.\n",
val[0], val[1], val[2]);
dev->name = kElantechPath[dev->idx];
dev->packet_size = PS2_PACKET_ELANTECH;
return B_ERROR;
}
static status_t
elantech_write_reg(elantech_cookie* cookie, uint8 reg, uint8 value)
{
if (reg < 0x7 || reg > 0x26)
return B_BAD_VALUE;
if (reg > 0x11 && reg < 0x20)
return B_BAD_VALUE;
ps2_dev* dev = cookie->dev;
switch (cookie->version) {
case 1:
// TODO
return B_ERROR;
break;
case 2:
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_WRITE) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, reg) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, value) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
return B_ERROR;
break;
case 3:
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, reg) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, value) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
return B_ERROR;
break;
case 4:
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, reg) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, value) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_SET_SCALE11) != B_OK)
return B_ERROR;
break;
default:
TRACE("ELANTECH: read_write_reg: unknown version\n");
return B_ERROR;
}
return B_OK;
}
static status_t
elantech_read_reg(elantech_cookie* cookie, uint8 reg, uint8 *value)
{
if (reg < 0x7 || reg > 0x26)
return B_BAD_VALUE;
if (reg > 0x11 && reg < 0x20)
return B_BAD_VALUE;
ps2_dev* dev = cookie->dev;
uint8 val[3];
switch (cookie->version) {
case 1:
// TODO
return B_ERROR;
break;
case 2:
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READ) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, reg) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
3) != B_OK)
return B_ERROR;
break;
case 3:
case 4:
if (ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_REGISTER_READWRITE)
!= B_OK
|| ps2_dev_command(dev, ELANTECH_CMD_PS2_CUSTOM_CMD) != B_OK
|| ps2_dev_command(dev, reg) != B_OK
|| ps2_dev_command(dev, PS2_CMD_MOUSE_GET_INFO, NULL, 0, val,
3) != B_OK)
return B_ERROR;
break;
default:
TRACE("ELANTECH: read_write_reg: unknown version\n");
return B_ERROR;
}
if (cookie->version == 4)
*value = val[1];
else
*value = val[0];
return B_OK;
}
static status_t
get_resolution_v4(elantech_cookie* cookie, uint32* x, uint32* y)
{
uint8 val[3];
if (elantech_dev_send_command(cookie->dev, ELANTECH_CMD_GET_RESOLUTION,
val, 3) != B_OK)
return B_ERROR;
*x = (val[1] & 0xf) * 10 + 790;
*y = ((val[1] & 0xf) >> 4) * 10 + 790;
return B_OK;
}
static status_t
get_range(elantech_cookie* cookie, uint32* x_min, uint32* y_min, uint32* x_max,
uint32* y_max, uint32 *width)
{
uint8 val[3];
switch (cookie->version) {
case 1:
*x_min = 32;
*y_min = 32;
*x_max = 544;
*y_max = 344;
*width = 0;
break;
case 2:
// TODO
break;
case 3:
if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
!= B_OK) {
return B_ERROR;
}
*x_min = 0;
*y_min = 0;
*x_max = ((val[0] & 0xf) << 8) | val[1];
*y_max = ((val[0] & 0xf0) << 4) | val[2];
*width = 0;
break;
case 4:
if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_ID, val, 3)
!= B_OK) {
return B_ERROR;
}
*x_min = 0;
*y_min = 0;
*x_max = ((val[0] & 0xf) << 8) | val[1];
*y_max = ((val[0] & 0xf0) << 4) | val[2];
if (cookie->capabilities[1] < 2 || cookie->capabilities[1] > *x_max)
return B_ERROR;
*width = *x_max / (cookie->capabilities[1] - 1);
break;
}
return B_OK;
}
static status_t
enable_absolute_mode(elantech_cookie* cookie)
{
status_t status = B_OK;
switch (cookie->version) {
case 1:
status = elantech_write_reg(cookie, 0x10, 0x16);
if (status == B_OK)
status = elantech_write_reg(cookie, 0x11, 0x8f);
break;
case 2:
status = elantech_write_reg(cookie, 0x10, 0x54);
if (status == B_OK)
status = elantech_write_reg(cookie, 0x11, 0x88);
if (status == B_OK)
status = elantech_write_reg(cookie, 0x12, 0x60);
break;
case 3:
status = elantech_write_reg(cookie, 0x10, 0xb);
break;
case 4:
status = elantech_write_reg(cookie, 0x7, 0x1);
break;
}
if (cookie->version < 4) {
uint8 val;
for (uint8 retry = 0; retry < 5; retry++) {
status = elantech_read_reg(cookie, 0x10, &val);
if (status != B_OK)
break;
snooze(100);
}
}
return status;
}
status_t
elantech_open(const char *name, uint32 flags, void **_cookie)
{
TRACE("ELANTECH: open %s\n", name);
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;
uint32 x_min = 0, x_max = 0, y_min = 0, y_max = 0, width = 0;
elantech_cookie* cookie = (elantech_cookie*)malloc(
sizeof(elantech_cookie));
if (cookie == NULL)
goto err1;
memset(cookie, 0, sizeof(*cookie));
cookie->movementMaker.Init();
cookie->previousZ = 0;
*_cookie = cookie;
cookie->dev = dev;
dev->cookie = cookie;
dev->disconnect = &elantech_disconnect;
dev->handle_int = &elantech_handle_int;
default_settings(&cookie->settings);
dev->packet_size = PS2_PACKET_ELANTECH;
cookie->ring_buffer = create_packet_buffer(
ELANTECH_HISTORY_SIZE * dev->packet_size);
if (cookie->ring_buffer == NULL) {
TRACE("ELANTECH: 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_elantech_sem");
if (cookie->sem < 0) {
TRACE("ELANTECH: failed creating semaphore!\n");
goto err3;
}
uint8 val[3];
if (synaptics_dev_send_command(dev, ELANTECH_CMD_GET_VERSION, val, 3)
!= B_OK) {
TRACE("ELANTECH: get version failed!\n");
goto err4;
}
cookie->fwVersion = (val[0] << 16) | (val[1] << 8) | val[2];
if (cookie->fwVersion < 0x020030 || cookie->fwVersion == 0x020600)
cookie->version = 1;
else {
switch (val[0] & 0xf) {
case 2:
case 4:
cookie->version = 2;
break;
case 5:
cookie->version = 3;
break;
case 6:
case 7:
cookie->version = 4;
break;
default:
TRACE("ELANTECH: unknown version!\n");
goto err4;
}
}
INFO("ELANTECH: version 0x%" B_PRIu32 " (0x%" B_PRIu32 ")\n",
cookie->version, cookie->fwVersion);
if (cookie->version >= 3)
cookie->send_command = &elantech_dev_send_command;
else
cookie->send_command = &synaptics_dev_send_command;
cookie->crcEnabled = (cookie->fwVersion & 0x4000) == 0x4000;
if ((cookie->send_command)(cookie->dev, ELANTECH_CMD_GET_CAPABILITIES,
cookie->capabilities, 3) != B_OK) {
TRACE("ELANTECH: get capabilities failed!\n");
return B_ERROR;
}
if (enable_absolute_mode(cookie) != B_OK) {
TRACE("ELANTECH: failed enabling absolute mode!\n");
goto err4;
}
TRACE("ELANTECH: enabled absolute mode!\n");
if (get_range(cookie, &x_min, &y_min, &x_max, &y_max, &width) != B_OK) {
TRACE("ELANTECH: get range failed!\n");
goto err4;
}
TRACE("ELANTECH: range x %" B_PRIu32 "-%" B_PRIu32 " y %" B_PRIu32
"-%" B_PRIu32 " (%" B_PRIu32 ")\n", x_min, x_max, y_min, y_max, width);
uint32 x_res, y_res;
if (get_resolution_v4(cookie, &x_res, &y_res) != B_OK) {
TRACE("ELANTECH: get resolution failed!\n");
goto err4;
}
TRACE("ELANTECH: resolution x %" B_PRIu32 " y %" B_PRIu32 " (dpi)\n",
x_res, y_res);
gHardwareSpecs.edgeMotionWidth = EDGE_MOTION_WIDTH;
gHardwareSpecs.areaStartX = x_min;
gHardwareSpecs.areaEndX = x_max;
gHardwareSpecs.areaStartY = y_min;
gHardwareSpecs.areaEndY = y_max;
gHardwareSpecs.minPressure = MIN_PRESSURE;
gHardwareSpecs.realMaxPressure = REAL_MAX_PRESSURE;
gHardwareSpecs.maxPressure = MAX_PRESSURE;
cookie->movementMaker.SetSettings(&cookie->settings);
cookie->movementMaker.SetSpecs(&gHardwareSpecs);
if (ps2_dev_command(dev, PS2_CMD_ENABLE, NULL, 0, NULL, 0) != B_OK)
goto err4;
atomic_or(&dev->flags, PS2_FLAG_ENABLED);
TRACE("ELANTECH: 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("ELANTECH: open %s failed\n", name);
return B_ERROR;
}
status_t
elantech_close(void *_cookie)
{
gEventProducer.CancelEvent();
elantech_cookie *cookie = (elantech_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_t status = ps2_reset_mouse(cookie->dev);
if (status != B_OK) {
INFO("ps2: reset failed\n");
return B_ERROR;
}
TRACE("ELANTECH: close %s done\n", cookie->dev->name);
return B_OK;
}
status_t
elantech_freecookie(void *_cookie)
{
free(_cookie);
return B_OK;
}
status_t
elantech_ioctl(void *_cookie, uint32 op, void *buffer, size_t length)
{
elantech_cookie *cookie = (elantech_cookie*)_cookie;
mouse_movement movement;
status_t status;
switch (op) {
case MS_READ:
TRACE("ELANTECH: MS_READ get event\n");
if ((status = get_elantech_movement(cookie, &movement)) != B_OK)
return status;
return user_memcpy(buffer, &movement, sizeof(movement));
case MS_IS_TOUCHPAD:
TRACE("ELANTECH: MS_IS_TOUCHPAD\n");
return B_OK;
case MS_SET_TOUCHPAD_SETTINGS:
TRACE("ELANTECH: MS_SET_TOUCHPAD_SETTINGS\n");
user_memcpy(&cookie->settings, buffer, sizeof(touchpad_settings));
return B_OK;
case MS_SET_CLICKSPEED:
TRACE("ELANTECH: ioctl MS_SETCLICK (set click speed)\n");
return user_memcpy(&cookie->movementMaker.click_speed, buffer,
sizeof(bigtime_t));
default:
INFO("ELANTECH: unknown opcode: 0x%" B_PRIx32 "\n", op);
return B_BAD_VALUE;
}
}
static status_t
elantech_read(void* cookie, off_t pos, void* buffer, size_t* _length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
static status_t
elantech_write(void* cookie, off_t pos, const void* buffer, size_t* _length)
{
*_length = 0;
return B_NOT_ALLOWED;
}
int32
elantech_handle_int(ps2_dev* dev)
{
elantech_cookie* cookie = (elantech_cookie*)dev->cookie;
// we got a real event cancel the fake event
gEventProducer.CancelEvent();
uint8 val;
val = cookie->dev->history[0].data;
cookie->buffer[cookie->packet_index] = val;
cookie->packet_index++;
if (cookie->packet_index < PS2_PACKET_ELANTECH)
return B_HANDLED_INTERRUPT;
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;
}
void
elantech_disconnect(ps2_dev *dev)
{
elantech_cookie *cookie = (elantech_cookie*)dev->cookie;
// the mouse device might not be opened at this point
INFO("ELANTECH: elantech_disconnect %s\n", dev->name);
if ((dev->flags & PS2_FLAG_OPEN) != 0)
release_sem(cookie->sem);
}
device_hooks gElantechDeviceHooks = {
elantech_open,
elantech_close,
elantech_freecookie,
elantech_ioctl,
elantech_read,
elantech_write,
};
↑ V547 Expression '(packet[0] & 0x10) == 1' is always false.