Add preliminary support for IBM/Lenovo TrackPoint.

PR:		kern/147237 (based on the initial patch for 8.x)
Tested by:	glebius (device detection and suspend/resume)
MFC after:	1 month
This commit is contained in:
Jung-uk Kim 2013-03-18 23:22:47 +00:00
parent b060ba5024
commit 569d8f7e27
4 changed files with 281 additions and 4 deletions

View File

@ -26,7 +26,7 @@
.\"
.\" $FreeBSD$
.\"
.Dd March 27, 2012
.Dd March 18, 2013
.Dt PSM 4
.Os
.Sh NAME
@ -339,6 +339,12 @@ at boot-time.
This will enable
.Nm
to handle packets from guest devices (sticks) and extra buttons.
Similarly, extended support for IBM/Lenovo TrackPoint can be enabled
by setting
.Va hw.psm.trackpoint_support
to
.Em 1
at boot-time.
.Pp
Tap and drag gestures can be disabled by setting
.Va hw.psm.tap_enabled
@ -832,8 +838,8 @@ In contrast, some pad products, e.g.\& some versions of ALPS GlidePoint
and Interlink VersaPad, treat the tapping action
as fourth button events.
.Pp
It is reported that ALPS GlidePoint, Synaptics Touchpad, and
Interlink VersaPad require
It is reported that ALPS GlidePoint, Synaptics Touchpad, IBM/Lenovo
TrackPoint, and Interlink VersaPad require
.Em INITAFTERSUSPEND
flag in order to recover from suspended state.
This flag is automatically set when one of these devices is detected by the

View File

@ -260,6 +260,38 @@ typedef struct synapticsaction {
int in_vscroll;
} synapticsaction_t;
enum {
TRACKPOINT_SYSCTL_SENSITIVITY,
TRACKPOINT_SYSCTL_NEGATIVE_INERTIA,
TRACKPOINT_SYSCTL_UPPER_PLATEAU,
TRACKPOINT_SYSCTL_BACKUP_RANGE,
TRACKPOINT_SYSCTL_DRAG_HYSTERESIS,
TRACKPOINT_SYSCTL_MINIMUM_DRAG,
TRACKPOINT_SYSCTL_UP_THRESHOLD,
TRACKPOINT_SYSCTL_THRESHOLD,
TRACKPOINT_SYSCTL_JENKS_CURVATURE,
TRACKPOINT_SYSCTL_Z_TIME,
TRACKPOINT_SYSCTL_PRESS_TO_SELECT,
TRACKPOINT_SYSCTL_SKIP_BACKUPS
};
typedef struct trackpointinfo {
struct sysctl_ctx_list sysctl_ctx;
struct sysctl_oid *sysctl_tree;
int sensitivity;
int inertia;
int uplateau;
int reach;
int draghys;
int mindrag;
int upthresh;
int threshold;
int jenks;
int ztime;
int pts;
int skipback;
} trackpointinfo_t;
/* driver control block */
struct psm_softc { /* Driver status information */
int unit;
@ -274,6 +306,8 @@ struct psm_softc { /* Driver status information */
synapticshw_t synhw; /* Synaptics hardware information */
synapticsinfo_t syninfo; /* Synaptics configuration */
synapticsaction_t synaction; /* Synaptics action context */
int tphw; /* TrackPoint hardware information */
trackpointinfo_t tpinfo; /* TrackPoint configuration */
mousemode_t mode; /* operation mode */
mousemode_t dflt_mode; /* default operation mode */
mousestatus_t status; /* accumulated mouse movement */
@ -344,6 +378,9 @@ TUNABLE_INT("hw.psm.tap_enabled", &tap_enabled);
static int synaptics_support = 0;
TUNABLE_INT("hw.psm.synaptics_support", &synaptics_support);
static int trackpoint_support = 0;
TUNABLE_INT("hw.psm.trackpoint_support", &trackpoint_support);
static int verbose = PSM_DEBUG;
TUNABLE_INT("debug.psm.loglevel", &verbose);
@ -432,6 +469,7 @@ static probefunc_t enable_4dmouse;
static probefunc_t enable_4dplus;
static probefunc_t enable_mmanplus;
static probefunc_t enable_synaptics;
static probefunc_t enable_trackpoint;
static probefunc_t enable_versapad;
static struct {
@ -466,6 +504,8 @@ static struct {
0x80, MOUSE_PS2_PACKETSIZE, enable_kmouse },
{ MOUSE_MODEL_VERSAPAD, /* Interlink electronics VersaPad */
0xe8, MOUSE_PS2VERSA_PACKETSIZE, enable_versapad },
{ MOUSE_MODEL_TRACKPOINT, /* IBM/Lenovo TrackPoint */
0xc0, MOUSE_PS2_PACKETSIZE, enable_trackpoint },
{ MOUSE_MODEL_GENERIC,
0xc0, MOUSE_PS2_PACKETSIZE, NULL },
};
@ -708,6 +748,7 @@ model_name(int model)
{ MOUSE_MODEL_4DPLUS, "4D+ Mouse" },
{ MOUSE_MODEL_SYNAPTICS, "Synaptics Touchpad" },
{ MOUSE_MODEL_GENERIC, "Generic PS/2 mouse" },
{ MOUSE_MODEL_TRACKPOINT, "IBM/Lenovo TrackPoint" },
{ MOUSE_MODEL_UNKNOWN, "Unknown" },
};
int i;
@ -1452,7 +1493,7 @@ psmattach(device_t dev)
sc->config |= PSM_CONFIG_INITAFTERSUSPEND;
break;
default:
if (sc->synhw.infoMajor >= 4)
if (sc->synhw.infoMajor >= 4 || sc->tphw > 0)
sc->config |= PSM_CONFIG_INITAFTERSUSPEND;
break;
}
@ -3442,6 +3483,7 @@ psmsoftintr(void *arg)
goto next;
break;
case MOUSE_MODEL_TRACKPOINT:
case MOUSE_MODEL_GENERIC:
default:
break;
@ -4474,6 +4516,233 @@ enable_synaptics(KBDC kbdc, struct psm_softc *sc)
return (TRUE);
}
/* IBM/Lenovo TrackPoint */
static int
trackpoint_command(KBDC kbdc, int cmd, int loc, int val)
{
const int seq[] = { 0xe2, cmd, loc, val };
int i;
for (i = 0; i < nitems(seq); i++)
if (send_aux_command(kbdc, seq[i]) != PSM_ACK)
return (EIO);
return (0);
}
#define PSM_TPINFO(x) offsetof(struct psm_softc, tpinfo.x)
#define TPMASK 0
#define TPLOC 1
#define TPINFO 2
static int
trackpoint_sysctl(SYSCTL_HANDLER_ARGS)
{
static const int data[][3] = {
{ 0x00, 0x4a, PSM_TPINFO(sensitivity) },
{ 0x00, 0x4d, PSM_TPINFO(inertia) },
{ 0x00, 0x60, PSM_TPINFO(uplateau) },
{ 0x00, 0x57, PSM_TPINFO(reach) },
{ 0x00, 0x58, PSM_TPINFO(draghys) },
{ 0x00, 0x59, PSM_TPINFO(mindrag) },
{ 0x00, 0x5a, PSM_TPINFO(upthresh) },
{ 0x00, 0x5c, PSM_TPINFO(threshold) },
{ 0x00, 0x5d, PSM_TPINFO(jenks) },
{ 0x00, 0x5e, PSM_TPINFO(ztime) },
{ 0x01, 0x2c, PSM_TPINFO(pts) },
{ 0x08, 0x2d, PSM_TPINFO(skipback) }
};
struct psm_softc *sc;
int error, newval, *oldvalp;
const int *tp;
if (arg1 == NULL || arg2 < 0 || arg2 >= nitems(data))
return (EINVAL);
sc = arg1;
tp = data[arg2];
oldvalp = (int *)((intptr_t)sc + tp[TPINFO]);
newval = *oldvalp;
error = sysctl_handle_int(oidp, &newval, 0, req);
if (error != 0)
return (error);
if (newval == *oldvalp)
return (0);
if (newval < 0 || newval > (tp[TPMASK] == 0 ? 255 : 1))
return (EINVAL);
error = trackpoint_command(sc->kbdc, tp[TPMASK] == 0 ? 0x81 : 0x47,
tp[TPLOC], tp[TPMASK] == 0 ? newval : tp[TPMASK]);
if (error != 0)
return (error);
*oldvalp = newval;
return (0);
}
static void
trackpoint_sysctl_create_tree(struct psm_softc *sc)
{
if (sc->tpinfo.sysctl_tree != NULL)
return;
/* Attach extra trackpoint sysctl nodes under hw.psm.trackpoint */
sysctl_ctx_init(&sc->tpinfo.sysctl_ctx);
sc->tpinfo.sysctl_tree = SYSCTL_ADD_NODE(&sc->tpinfo.sysctl_ctx,
SYSCTL_STATIC_CHILDREN(_hw_psm), OID_AUTO, "trackpoint", CTLFLAG_RD,
0, "IBM/Lenovo TrackPoint");
/* hw.psm.trackpoint.sensitivity */
sc->tpinfo.sensitivity = 0x64;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"sensitivity", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_SENSITIVITY,
trackpoint_sysctl, "I",
"Sensitivity");
/* hw.psm.trackpoint.negative_inertia */
sc->tpinfo.inertia = 0x06;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"negative_inertia", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_NEGATIVE_INERTIA,
trackpoint_sysctl, "I",
"Negative inertia factor");
/* hw.psm.trackpoint.upper_plateau */
sc->tpinfo.uplateau = 0x61;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"upper_plateau", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_UPPER_PLATEAU,
trackpoint_sysctl, "I",
"Transfer function upper plateau speed");
/* hw.psm.trackpoint.backup_range */
sc->tpinfo.reach = 0x0a;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"backup_range", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_BACKUP_RANGE,
trackpoint_sysctl, "I",
"Backup range");
/* hw.psm.trackpoint.drag_hysteresis */
sc->tpinfo.draghys = 0xff;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"drag_hysteresis", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_DRAG_HYSTERESIS,
trackpoint_sysctl, "I",
"Drag hysteresis");
/* hw.psm.trackpoint.minimum_drag */
sc->tpinfo.mindrag = 0x14;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"minimum_drag", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_MINIMUM_DRAG,
trackpoint_sysctl, "I",
"Minimum drag");
/* hw.psm.trackpoint.up_threshold */
sc->tpinfo.upthresh = 0xff;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"up_threshold", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_UP_THRESHOLD,
trackpoint_sysctl, "I",
"Up threshold for release");
/* hw.psm.trackpoint.threshold */
sc->tpinfo.threshold = 0x08;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"threshold", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_THRESHOLD,
trackpoint_sysctl, "I",
"Threshold");
/* hw.psm.trackpoint.jenks_curvature */
sc->tpinfo.jenks = 0x87;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"jenks_curvature", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_JENKS_CURVATURE,
trackpoint_sysctl, "I",
"Jenks curvature");
/* hw.psm.trackpoint.z_time */
sc->tpinfo.ztime = 0x26;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"z_time", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_Z_TIME,
trackpoint_sysctl, "I",
"Z time constant");
/* hw.psm.trackpoint.press_to_select */
sc->tpinfo.pts = 0x00;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"press_to_select", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_PRESS_TO_SELECT,
trackpoint_sysctl, "I",
"Press to Select");
/* hw.psm.trackpoint.skip_backups */
sc->tpinfo.skipback = 0x00;
SYSCTL_ADD_PROC(&sc->tpinfo.sysctl_ctx,
SYSCTL_CHILDREN(sc->tpinfo.sysctl_tree), OID_AUTO,
"skip_backups", CTLTYPE_INT|CTLFLAG_RW|CTLFLAG_ANYBODY,
sc, TRACKPOINT_SYSCTL_SKIP_BACKUPS,
trackpoint_sysctl, "I",
"Skip backups from drags");
}
static int
enable_trackpoint(KBDC kbdc, struct psm_softc *sc)
{
int id;
kbdc = sc->kbdc;
if (send_aux_command(kbdc, 0xe1) != PSM_ACK ||
read_aux_data(kbdc) != 0x01)
return (FALSE);
id = read_aux_data(kbdc);
if (id < 0x01)
return (FALSE);
if (sc != NULL)
sc->tphw = id;
if (!trackpoint_support)
return (FALSE);
if (sc != NULL) {
/* Create sysctl tree. */
trackpoint_sysctl_create_tree(sc);
trackpoint_command(kbdc, 0x81, 0x4a, sc->tpinfo.sensitivity);
trackpoint_command(kbdc, 0x81, 0x4d, sc->tpinfo.inertia);
trackpoint_command(kbdc, 0x81, 0x60, sc->tpinfo.uplateau);
trackpoint_command(kbdc, 0x81, 0x57, sc->tpinfo.reach);
trackpoint_command(kbdc, 0x81, 0x58, sc->tpinfo.draghys);
trackpoint_command(kbdc, 0x81, 0x59, sc->tpinfo.mindrag);
trackpoint_command(kbdc, 0x81, 0x5a, sc->tpinfo.upthresh);
trackpoint_command(kbdc, 0x81, 0x5c, sc->tpinfo.threshold);
trackpoint_command(kbdc, 0x81, 0x5d, sc->tpinfo.jenks);
trackpoint_command(kbdc, 0x81, 0x5e, sc->tpinfo.ztime);
if (sc->tpinfo.pts == 0x01)
trackpoint_command(kbdc, 0x47, 0x2c, 0x01);
if (sc->tpinfo.skipback == 0x01)
trackpoint_command(kbdc, 0x47, 0x2d, 0x08);
sc->hw.hwid = id;
sc->hw.buttons = 3;
}
return (TRUE);
}
/* Interlink electronics VersaPad */
static int
enable_versapad(KBDC kbdc, struct psm_softc *sc)

View File

@ -141,6 +141,7 @@ typedef struct synapticshw {
#define MOUSE_MODEL_4D 11
#define MOUSE_MODEL_4DPLUS 12
#define MOUSE_MODEL_SYNAPTICS 13
#define MOUSE_MODEL_TRACKPOINT 14
typedef struct mousemode {
int protocol; /* MOUSE_PROTO_XXX */

View File

@ -245,6 +245,7 @@ static symtab_t rmodels[] = {
{ "4D Mouse", MOUSE_MODEL_4D, 0 },
{ "4D+ Mouse", MOUSE_MODEL_4DPLUS, 0 },
{ "Synaptics Touchpad", MOUSE_MODEL_SYNAPTICS, 0 },
{ "TrackPoint", MOUSE_MODEL_TRACKPOINT, 0 },
{ "generic", MOUSE_MODEL_GENERIC, 0 },
{ NULL, MOUSE_MODEL_UNKNOWN, 0 },
};