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 "synproto.h"
37 #include "synapticsstr.h"
38 #include "ps2comm.h"
39 #include <xf86.h>
40 
41 /* Wait for the channel to go silent, which means we're in sync */
42 static void
ALPS_sync(int fd)43 ALPS_sync(int fd)
44 {
45     byte buffer[64];
46 
47     while (xf86WaitForInput(fd, 250000) > 0) {
48         xf86ReadSerial(fd, &buffer, 64);
49     }
50 }
51 
52 /*
53  * send the ALPS init sequence, ie 4 consecutive "disable"s before the "enable"
54  * This "magic knock" is performed both for the trackpad and for the pointing
55  * stick. Not all models have a pointing stick, but trying to initialize it
56  * anyway doesn't seem to hurt.
57  */
58 static void
ALPS_initialize(int fd)59 ALPS_initialize(int fd)
60 {
61     xf86FlushInput(fd);
62     ps2_putbyte(fd, PS2_CMD_SET_DEFAULT);
63     ps2_putbyte(fd, PS2_CMD_SET_SCALING_2_1);
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_DISABLE);
67 
68     ps2_putbyte(fd, PS2_CMD_DISABLE);
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_ENABLE);
73 
74     ps2_putbyte(fd, PS2_CMD_SET_SCALING_1_1);
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_DISABLE);
78 
79     ps2_putbyte(fd, PS2_CMD_DISABLE);
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_ENABLE);
84 
85     ALPS_sync(fd);
86 }
87 
88 static Bool
ALPSQueryHardware(InputInfoPtr pInfo)89 ALPSQueryHardware(InputInfoPtr pInfo)
90 {
91     ALPS_initialize(pInfo->fd);
92     return TRUE;
93 }
94 
95 static Bool
ALPS_packet_ok(struct CommData * comm)96 ALPS_packet_ok(struct CommData *comm)
97 {
98     /* ALPS absolute mode packets start with 0b11111mrl */
99     if ((comm->protoBuf[0] & 0xf8) == 0xf8)
100         return TRUE;
101     return FALSE;
102 }
103 
104 static Bool
ALPS_get_packet(struct CommData * comm,InputInfoPtr pInfo)105 ALPS_get_packet(struct CommData *comm, InputInfoPtr pInfo)
106 {
107     int c;
108 
109     while ((c = XisbRead(comm->buffer)) >= 0) {
110         unsigned char u = (unsigned char) c;
111 
112         comm->protoBuf[comm->protoBufTail++] = u;
113 
114         if (comm->protoBufTail == 3) {  /* PS/2 packet received? */
115             if ((comm->protoBuf[0] & 0xc8) == 0x08) {
116                 comm->protoBufTail = 0;
117                 return TRUE;
118             }
119         }
120 
121         if (comm->protoBufTail >= 6) {  /* Full packet received */
122             comm->protoBufTail = 0;
123             if (ALPS_packet_ok(comm))
124                 return TRUE;
125             while ((c = XisbRead(comm->buffer)) >= 0);  /* If packet is invalid, re-sync */
126         }
127     }
128 
129     return FALSE;
130 }
131 
132 /*
133  * ALPS abolute Mode
134  * byte 0: 1 1 1 1 1 mid0 rig0 lef0
135  * byte 1: 0 x6 x5 x4 x3 x2 x1 x0
136  * byte 2: 0 x10 x9 x8 x7 up1 fin ges
137  * byte 3: 0 y9 y8 y7 1 mid1 rig1 lef1
138  * byte 4: 0 y6 y5 y4 y3 y2 y1 y0
139  * byte 5: 0 z6 z5 z4 z3 z2 z1 z0
140  *
141  * On a dualpoint, {mid,rig,lef}0 are the stick, 1 are the pad.
142  * We just 'or' them together for now.
143  *
144  * The touchpad on an 'Acer Aspire' has 4 buttons:
145  *   left,right,up,down.
146  * This device always sets {mid,rig,lef}0 to 1 and
147  * reflects left,right,down,up in lef1,rig1,mid1,up1.
148  */
149 static void
ALPS_process_packet(unsigned char * packet,struct SynapticsHwState * hw)150 ALPS_process_packet(unsigned char *packet, struct SynapticsHwState *hw)
151 {
152     int x = 0, y = 0, z = 0;
153     int left = 0, right = 0, middle = 0;
154     int i;
155 
156     hw->millis = GetTimeInMillis();
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 
189         if (back && forward) {
190             middle = 1;
191             back = 0;
192             forward = 0;
193         }
194         hw->down = back;
195         hw->up = forward;
196     }
197     else {
198         left |= (packet[0]) & 1;
199         right |= (packet[0] >> 1) & 1;
200         middle |= (packet[0] >> 2) & 1;
201         middle |= (packet[3] >> 2) & 1;
202     }
203 
204     hw->left = left;
205     hw->right = right;
206     hw->middle = middle;
207 }
208 
209 static Bool
ALPSReadHwState(InputInfoPtr pInfo,struct CommData * comm,struct SynapticsHwState * hwRet)210 ALPSReadHwState(InputInfoPtr pInfo,
211                 struct CommData *comm, struct SynapticsHwState *hwRet)
212 {
213     unsigned char *buf = comm->protoBuf;
214     struct SynapticsHwState *hw = comm->hwState;
215 
216     if (!ALPS_get_packet(comm, pInfo))
217         return FALSE;
218 
219     ALPS_process_packet(buf, hw);
220 
221     SynapticsCopyHwState(hwRet, hw);
222     return TRUE;
223 }
224 
225 struct SynapticsProtocolOperations alps_proto_operations = {
226     NULL,
227     NULL,
228     ALPSQueryHardware,
229     ALPSReadHwState,
230     NULL,
231     NULL
232 };
233