alpscomm.c revision 302b15bd
1/*
2 * Copyright © 2001 Stefan Gmeiner
3 * Copyright © 2003 Neil Brown
4 * Copyright © 2003-2005,2007 Peter Osterlund
5 *
6 * Permission to use, copy, modify, distribute, and sell this software
7 * and its documentation for any purpose is hereby granted without
8 * fee, provided that the above copyright notice appear in all copies
9 * and that both that copyright notice and this permission notice
10 * appear in supporting documentation, and that the name of Red Hat
11 * not be used in advertising or publicity pertaining to distribution
12 * of the software without specific, written prior permission.  Red
13 * Hat makes no representations about the suitability of this software
14 * for any purpose.  It is provided "as is" without express or implied
15 * warranty.
16 *
17 * THE AUTHORS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
18 * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN
19 * NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY SPECIAL, INDIRECT OR
20 * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
21 * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
22 * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
23 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
24 *
25 * Authors:
26 *      Stefan Gmeiner (riddlebox@freesurf.ch)
27 *      Neil Brown (neilb@cse.unsw.edu.au)
28 *      Peter Osterlund (petero2@telia.com)
29 */
30
31#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
35#include <xorg-server.h>
36#include "alpscomm.h"
37#include "synproto.h"
38#include "synaptics.h"
39#include "synapticsstr.h"
40#include <xf86.h>
41
42
43/* Wait for the channel to go silent, which means we're in sync */
44static void
45ALPS_sync(int fd)
46{
47    byte buffer[64];
48    while (xf86WaitForInput(fd, 250000) > 0) {
49	xf86ReadSerial(fd, &buffer, 64);
50    }
51}
52
53/*
54 * send the ALPS init sequence, ie 4 consecutive "disable"s before the "enable"
55 * This "magic knock" is performed both for the trackpad and for the pointing
56 * stick. Not all models have a pointing stick, but trying to initialize it
57 * anyway doesn't seem to hurt.
58 */
59static void
60ALPS_initialize(int fd)
61{
62    xf86FlushInput(fd);
63    ps2_putbyte(fd, PS2_CMD_SET_DEFAULT);
64    ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
65    ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
66    ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
67    ps2_putbyte(fd, PS2_CMD_DISABLE);
68
69    ps2_putbyte(fd, PS2_CMD_DISABLE);
70    ps2_putbyte(fd, PS2_CMD_DISABLE);
71    ps2_putbyte(fd, PS2_CMD_DISABLE);
72    ps2_putbyte(fd, PS2_CMD_DISABLE);
73    ps2_putbyte(fd, PS2_CMD_ENABLE);
74
75    ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
76    ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
77    ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
78    ps2_putbyte(fd, PS2_CMD_DISABLE);
79
80    ps2_putbyte(fd, PS2_CMD_DISABLE);
81    ps2_putbyte(fd, PS2_CMD_DISABLE);
82    ps2_putbyte(fd, PS2_CMD_DISABLE);
83    ps2_putbyte(fd, PS2_CMD_DISABLE);
84    ps2_putbyte(fd, PS2_CMD_ENABLE);
85
86    ALPS_sync(fd);
87}
88
89static Bool
90ALPSQueryHardware(InputInfoPtr pInfo)
91{
92    ALPS_initialize(pInfo->fd);
93    return TRUE;
94}
95
96static Bool
97ALPS_packet_ok(struct CommData *comm)
98{
99    /* ALPS absolute mode packets start with 0b11111mrl */
100    if ((comm->protoBuf[0] & 0xf8) == 0xf8)
101	return TRUE;
102    return FALSE;
103}
104
105static Bool
106ALPS_get_packet(struct CommData *comm, InputInfoPtr pInfo)
107{
108    int c;
109
110    while ((c = XisbRead(comm->buffer)) >= 0) {
111	unsigned char u = (unsigned char)c;
112
113	comm->protoBuf[comm->protoBufTail++] = u;
114
115	if (comm->protoBufTail == 3) { /* PS/2 packet received? */
116	    if ((comm->protoBuf[0] & 0xc8) == 0x08) {
117		comm->protoBufTail = 0;
118		return TRUE;
119	    }
120	}
121
122	if (comm->protoBufTail >= 6) { /* Full packet received */
123	    comm->protoBufTail = 0;
124	    if (ALPS_packet_ok(comm))
125		return TRUE;
126	    while ((c = XisbRead(comm->buffer)) >= 0)
127		;		   /* If packet is invalid, re-sync */
128	}
129    }
130
131    return FALSE;
132}
133
134/*
135 * ALPS abolute Mode
136 * byte 0: 1 1 1 1 1 mid0 rig0 lef0
137 * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
138 * byte 2: 0 x10 x9 x8 x7 up1 fin ges
139 * byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1
140 * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
141 * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
142 *
143 * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
144 * We just 'or' them together for now.
145 *
146 * The touchpad on an 'Acer Aspire' has 4 buttons:
147 *   left,right,up,down.
148 * This device always sets {mid,rig,lef}0 to 1 and
149 * reflects left,right,down,up in lef1,rig1,mid1,up1.
150 */
151static void
152ALPS_process_packet(unsigned char *packet, struct SynapticsHwState *hw)
153{
154    int x = 0, y = 0, z = 0;
155    int left = 0, right = 0, middle = 0;
156    int i;
157
158    x = (packet[1] & 0x7f) | ((packet[2] & 0x78) << (7-3));
159    y = (packet[4] & 0x7f) | ((packet[3] & 0x70) << (7-4));
160    z = packet[5];
161
162    if (z == 127) {    /* DualPoint stick is relative, not absolute */
163	hw->left  = packet[3] & 1;
164	hw->right = (packet[3] >> 1) & 1;
165	return;
166    }
167
168    /* Handle normal packets */
169    hw->x = hw->y = hw->z = hw->numFingers = hw->fingerWidth = 0;
170    hw->left = hw->right = hw->up = hw->down = hw->middle = FALSE;
171    for (i = 0; i < 8; i++)
172	hw->multi[i] = FALSE;
173
174    if (z > 0) {
175	hw->x = x;
176	hw->y = y;
177    }
178    hw->z = z;
179    hw->numFingers = (z > 0) ? 1 : 0;
180    hw->fingerWidth = 5;
181
182    left  |= (packet[2]     ) & 1;
183    left  |= (packet[3]     ) & 1;
184    right |= (packet[3] >> 1) & 1;
185    if (packet[0] == 0xff) {
186	int back    = (packet[3] >> 2) & 1;
187	int forward = (packet[2] >> 2) & 1;
188	if (back && forward) {
189	    middle = 1;
190	    back = 0;
191	    forward = 0;
192	}
193	hw->down = back;
194	hw->up = forward;
195    } else {
196	left   |= (packet[0]     ) & 1;
197	right  |= (packet[0] >> 1) & 1;
198	middle |= (packet[0] >> 2) & 1;
199	middle |= (packet[3] >> 2) & 1;
200    }
201
202    hw->left = left;
203    hw->right = right;
204    hw->middle = middle;
205}
206
207static Bool
208ALPSReadHwState(InputInfoPtr pInfo,
209		struct SynapticsProtocolOperations *proto_ops,
210		struct CommData *comm, struct SynapticsHwState *hwRet)
211{
212    unsigned char *buf = comm->protoBuf;
213    struct SynapticsHwState *hw = &(comm->hwState);
214
215    if (!ALPS_get_packet(comm, pInfo))
216	return FALSE;
217
218    ALPS_process_packet(buf, hw);
219
220    *hwRet = *hw;
221    return TRUE;
222}
223
224static Bool
225ALPSAutoDevProbe(InputInfoPtr pInfo)
226{
227    return FALSE;
228}
229
230struct SynapticsProtocolOperations alps_proto_operations = {
231    NULL,
232    NULL,
233    ALPSQueryHardware,
234    ALPSReadHwState,
235    ALPSAutoDevProbe,
236    SynapticsDefaultDimensions
237};
238