/*-
* Finger Sensing Pad PS/2 mouse driver.
*
* Copyright (C) 2005-2007 Asia Vital Components Co., Ltd.
* Copyright (C) 2005-2012 Tai-hwa Liang, Sentelic Corporation.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/input.h>
#include <linux/input/mt.h>
#include <linux/ctype.h>
#include <linux/libps2.h>
#include <linux/serio.h>
#include <linux/jiffies.h>
#include <linux/slab.h>
#include "psmouse.h"
#include "sentelic.h"
/*
* Timeout for FSP PS/2 command only (in milliseconds).
*/
#define FSP_CMD_TIMEOUT 200
#define FSP_CMD_TIMEOUT2 30
#define GET_ABS_X(packet) ((packet[1] << 2) | ((packet[3] >> 2) & 0x03))
#define GET_ABS_Y(packet) ((packet[2] << 2) | (packet[3] & 0x03))
/** Driver version. */
static const char fsp_drv_ver[] = "1.1.0-K";
/*
* Make sure that the value being sent to FSP will not conflict with
* possible sample rate values.
*/
static unsigned char fsp_test_swap_cmd(unsigned char reg_val)
{
switch (reg_val) {
case 10: case 20: case 40: case 60: case 80: case 100: case 200:
/*
* The requested value being sent to FSP matched to possible
* sample rates, swap the given value such that the hardware
* wouldn't get confused.
*/
return (reg_val >> 4) | (reg_val << 4);
default:
return reg_val; /* swap isn't necessary */
}
}
/*
* Make sure that the value being sent to FSP will not conflict with certain
* commands.
*/
static unsigned char fsp_test_invert_cmd(unsigned char reg_val)
{
switch (reg_val) {
case 0xe9: case 0xee: case 0xf2: case 0xff:
/*
* The requested value being sent to FSP matched to certain
* commands, inverse the given value such that the hardware
* wouldn't get confused.
*/
return ~reg_val;
default:
return reg_val; /* inversion isn't necessary */
}
}
static int fsp_reg_read(struct psmouse *psmouse, int reg_addr, int *reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char param[3];
unsigned char addr;
int rc = -1;
/*
* We need to shut off the device and switch it into command
* mode so we don't confuse our protocol handler. We don't need
* to do that for writes because sysfs set helper does this for
* us.
*/
psmouse_deactivate(psmouse);
ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
/* should return 0xfe(request for resending) */
ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
/* should return 0xfc(failed) */
ps2_sendbyte(ps2dev, 0x88, FSP_CMD_TIMEOUT2);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
if ((addr = fsp_test_invert_cmd(reg_addr)) != reg_addr) {
ps2_sendbyte(ps2dev, 0x68, FSP_CMD_TIMEOUT2);
} else if ((addr = fsp_test_swap_cmd(reg_addr)) != reg_addr) {
/* swapping is required */
ps2_sendbyte(ps2dev, 0xcc, FSP_CMD_TIMEOUT2);
/* expect 0xfe */
} else {
/* swapping isn't necessary */
ps2_sendbyte(ps2dev, 0x66, FSP_CMD_TIMEOUT2);
/* expect 0xfe */
}
/* should return 0xfc(failed) */
ps2_sendbyte(ps2dev, addr, FSP_CMD_TIMEOUT);
if (__ps2_command(ps2dev, param, PSMOUSE_CMD_GETINFO) < 0)
goto out;
*reg_val = param[2];
rc = 0;
out:
ps2_end_command(ps2dev);
psmouse_activate(psmouse);
psmouse_dbg(psmouse,
"READ REG: 0x%02x is 0x%02x (rc = %d)\n",
reg_addr, *reg_val, rc);
return rc;
}
static int fsp_reg_write(struct psmouse *psmouse, int reg_addr, int reg_val)
{
struct ps2dev *ps2dev = &psmouse->ps2dev;
unsigned char v;
int rc = -1;
ps2_begin_command(ps2dev);
if (ps2_sendbyte(ps2dev, 0xf3, FSP_CMD_TIMEOUT) < 0)
goto out;
if ((v = fsp_test_invert_cmd(reg_addr