xref: /haiku/src/add-ons/input_server/devices/easypen/EasyPenInputDevice.cpp (revision caed67a8cba83913b9c21ac2b06ebc6bd1cb3111)
1 /*
2  * Copyright 2006-2010, Haiku.
3  * Distributed under the terms of the MIT License.
4  *
5  * Authors:
6  *		Jérôme Duval
7  *
8  * References:
9  *   Google search "technic doc genius" , http://www.bebits.com/app/2152
10  */
11 
12 #include <stdlib.h>
13 #include <string.h>
14 #include <unistd.h>
15 
16 #include <Debug.h>
17 #include <InterfaceDefs.h>
18 #include <SerialPort.h>
19 
20 #include "EasyPenInputDevice.h"
21 #include "keyboard_mouse_driver.h"
22 
23 /*
24 IN ABSOLUTE MODE (MM Series)
25 
26                       Least                          Most
27 
28   Bytes       Bit to  Significant                    Significant   Bit to
29 
30 Transmitted   Start   Bit                            Bit           Stop
31 
32                       0   1   2   3    4    5    6    7   8
33 
34 -----------   ----- ---------------------------------------------  ------
35 
36 1st Byte       0     Fa  Fb  Fc  Sy   Sx   T    PR   PH  P          1
37 
38 2nd Byte       0     X0  X1  X2  X3   X4   X5   X6   0   P          1
39 
40 3rd Byte       0     X7  X8  X9  X10  X11  X12  X13  0   P          1
41 
42 4th Byte       0     Y0  Y1  Y2  Y3   Y4   Y5   Y6   0   P          1
43 
44 5th Byte       0     Y7  Y8  Y9  Y10  Y11  Y12  Y13  0   P          1
45 
46 */
47 
48 #if DEBUG
49         inline void LOG(const char *fmt, ...) { char buf[1024]; va_list ap; va_start(ap, fmt); vsprintf(buf, fmt, ap); va_end(ap); \
50                 fputs(buf, EasyPenInputDevice::sLogFile); fflush(EasyPenInputDevice::sLogFile); }
51         #define LOG_ERR(text...) LOG(text)
52 FILE *EasyPenInputDevice::sLogFile = NULL;
53 #else
54         #define LOG(text...)
55         #define LOG_ERR(text...) fprintf(stderr, text)
56 #endif
57 
58 #define CALLED() LOG("%s\n", __PRETTY_FUNCTION__)
59 
60 const static uint32 kTabletThreadPriority = B_FIRST_REAL_TIME_PRIORITY + 4;
61 const static char* kDeviceName = "Genius EasyPen";
62 
63 struct tablet_device {
64 	tablet_device(BSerialPort *port);
65 	~tablet_device();
66 
67 	input_device_ref device_ref;
68 	thread_id device_watcher;
69 	bool active;
70 	BSerialPort *serial;
71 	EasyPenInputDevice *owner;
72 };
73 
74 
75 extern "C"
76 BInputServerDevice *
77 instantiate_input_device()
78 {
79 	return new EasyPenInputDevice();
80 }
81 
82 
83 EasyPenInputDevice::EasyPenInputDevice()
84 {
85 #if DEBUG
86 	sLogFile = fopen("/var/log/easypen_device_log.log", "a");
87 #endif
88 }
89 
90 
91 EasyPenInputDevice::~EasyPenInputDevice()
92 {
93 	CALLED();
94 	for (int32 i = 0; i < fDevices.CountItems(); i++)
95 		delete (tablet_device *)fDevices.ItemAt(i);
96 
97 #if DEBUG
98 	fclose(sLogFile);
99 #endif
100 }
101 
102 
103 status_t
104 EasyPenInputDevice::InitCheck()
105 {
106 	CALLED();
107 
108 	BSerialPort *serial = new BSerialPort();
109 	int count = serial->CountDevices();
110 	char devName[B_OS_NAME_LENGTH];
111 	char byte;
112 
113 	for (int i=0; i<count; i++) {
114 		serial->GetDeviceName(i, devName);
115 		serial->SetDataRate(B_9600_BPS);
116 		serial->SetDataBits(B_DATA_BITS_8);
117 		serial->SetStopBits(B_STOP_BITS_1);
118 		serial->SetParityMode(B_ODD_PARITY);
119 		serial->SetFlowControl(B_HARDWARE_CONTROL+B_SOFTWARE_CONTROL);
120 		serial->SetBlocking(true);
121 		serial->SetTimeout(800000);
122 
123 		if (serial->Open(devName) < B_OK) {
124 			LOG("Fail to open %s\n", devName);
125 			continue;
126 		}
127 		snooze(250000);
128 		serial->Write("z9", 2); // 8 data bits,odd  parity <command>                     z 9
129 		serial->Write("", 1); //	Reset                <command>                NUL
130 		serial->Write("DP", 2);		// mode command    D   trigger command    P
131 		snooze(250000);
132 		serial->ClearInput();
133 		serial->Write("twP", 3);	// Self-Test, Send Test Results, trigger command    P
134 		serial->Read(&byte, 1);
135 		if ((byte & 0xf7) != 0x87) {
136 			LOG("Self test failed for %s %x\n", devName, byte & 0xf7);
137 			continue;
138 		}
139 		tablet_device *device = new tablet_device(serial);
140 		device->owner = this;
141 
142 		input_device_ref *devices[2];
143 		devices[0] = &device->device_ref;
144 		devices[1] = NULL;
145 
146 		fDevices.AddItem(device);
147 		RegisterDevices(devices);
148 
149 		serial = new BSerialPort();
150 	}
151 
152 	delete serial;
153 
154 	LOG("Found %ld devices\n", fDevices.CountItems());
155 
156 	get_click_speed(kDeviceName, &fClickSpeed);
157 
158 	return fDevices.CountItems() > 0 ? B_OK : B_ERROR;
159 }
160 
161 
162 status_t
163 EasyPenInputDevice::Start(const char *name, void *cookie)
164 {
165 	tablet_device *device = (tablet_device *)cookie;
166 
167 	LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
168 
169 	char threadName[B_OS_NAME_LENGTH];
170 	snprintf(threadName, B_OS_NAME_LENGTH, "%s watcher", name);
171 
172 	device->active = true;
173 	device->device_watcher = spawn_thread(DeviceWatcher, threadName,
174 		kTabletThreadPriority, device);
175 
176 	if (device->device_watcher < B_OK)
177 		return device->device_watcher;
178 	resume_thread(device->device_watcher);
179 
180 	return B_OK;
181 }
182 
183 
184 status_t
185 EasyPenInputDevice::Stop(const char *name, void *cookie)
186 {
187 	tablet_device *device = (tablet_device *)cookie;
188 
189 	LOG("%s(%s)\n", __PRETTY_FUNCTION__, name);
190 
191 	device->active = false;
192 	if (device->device_watcher >= 0) {
193 		suspend_thread(device->device_watcher);
194 		resume_thread(device->device_watcher);
195 		status_t dummy;
196 		wait_for_thread(device->device_watcher, &dummy);
197 	}
198 
199 	return B_OK;
200 }
201 
202 
203 status_t
204 EasyPenInputDevice::Control(const char *name, void *cookie,
205 						  uint32 command, BMessage *message)
206 {
207 	LOG("%s(%s, code: %lu)\n", __PRETTY_FUNCTION__, name, command);
208 
209 	if (command == B_CLICK_SPEED_CHANGED)
210 		get_click_speed(kDeviceName, &fClickSpeed);
211 
212 	return B_OK;
213 }
214 
215 
216 int32
217 EasyPenInputDevice::DeviceWatcher(void *arg)
218 {
219 	tablet_device *dev = (tablet_device *)arg;
220 	EasyPenInputDevice *owner = dev->owner;
221 
222 	tablet_movement movements;
223 	tablet_movement old_movements;
224 	BMessage *message;
225 	uint8 byte;
226 	uint8 bytes[4];
227 	bigtime_t lastClickTimeStamp = 0LL;
228 
229 	memset(&movements, 0, sizeof(movements));
230 	memset(&old_movements, 0, sizeof(old_movements));
231 
232 	dev->serial->Write(":@FRb", 5);
233 
234 	while (dev->active) {
235 		byte = 0;
236 		while(!byte)
237 			dev->serial->Read(&byte, 1);
238 		if (byte & 0x40 || !(byte & 0x80)) { // 7th bit on or 8th bit off
239 			snooze(10000); // this is a realtime thread, and something is wrong...
240 			continue;
241 		}
242 
243 		if (dev->serial->Read(bytes, 4) != 4)
244 			continue;
245 
246 		if (*(uint32*)bytes & 0x80808080)
247 			continue;
248 
249 		movements.buttons = byte & 0x7;
250 		movements.timestamp = system_time();
251 		movements.xpos = (bytes[1] << 7) | bytes[0];
252 		movements.ypos = (bytes[3] << 7) | bytes[2];
253 
254 		uint32 buttons = old_movements.buttons ^ movements.buttons;
255 
256 		LOG("%s: buttons: 0x%lx, x: %f, y: %f, clicks:%ld, wheel_x:%ld, wheel_y:%ld\n", dev->device_ref.name, movements.buttons,
257 			movements.xpos, movements.ypos, movements.clicks, movements.wheel_xdelta, movements.wheel_ydelta);
258 
259 		movements.xpos /= 1950.0;
260 		movements.ypos /= 1500.0;
261 
262 		LOG("%s: x: %f, y: %f, \n", dev->device_ref.name, movements.xpos, movements.ypos);
263 
264 		if (buttons != 0) {
265 			message = new BMessage(B_MOUSE_UP);
266 			if ((buttons & movements.buttons) > 0) {
267 				message->what = B_MOUSE_DOWN;
268 				if(lastClickTimeStamp + owner->fClickSpeed <= movements.timestamp)
269 					movements.clicks = 1;
270 				else
271 					movements.clicks++;
272 				lastClickTimeStamp = movements.timestamp;
273 				message->AddInt32("clicks", movements.clicks);
274 				LOG("B_MOUSE_DOWN\n");
275 			} else {
276 				LOG("B_MOUSE_UP\n");
277 			}
278 
279 			message->AddInt64("when", movements.timestamp);
280 			message->AddInt32("buttons", movements.buttons);
281 			message->AddFloat("x", movements.xpos);
282 			message->AddFloat("y", movements.ypos);
283 			owner->EnqueueMessage(message);
284 		}
285 
286 		if (movements.xpos != 0.0 || movements.ypos != 0.0) {
287 			message = new BMessage(B_MOUSE_MOVED);
288 			if (message) {
289 				message->AddInt64("when", movements.timestamp);
290 				message->AddInt32("buttons", movements.buttons);
291 				message->AddFloat("x", movements.xpos);
292 				message->AddFloat("y", movements.ypos);
293 				message->AddFloat("be:tablet_x", movements.xpos);
294 				message->AddFloat("be:tablet_y", movements.ypos);
295 				message->AddFloat("be:tablet_pressure", movements.pressure);
296 				message->AddInt32("be:tablet_eraser", (movements.switches & B_ERASER) != 0);
297 				if (movements.tilt_x != 0.0 || movements.tilt_y != 0.0) {
298 					message->AddFloat("be:tablet_tilt_x", movements.tilt_x);
299 					message->AddFloat("be:tablet_tilt_y", movements.tilt_y);
300 				}
301 
302 				owner->EnqueueMessage(message);
303 			}
304 		}
305 
306 		if ((movements.wheel_ydelta != 0) || (movements.wheel_xdelta != 0)) {
307 			message = new BMessage(B_MOUSE_WHEEL_CHANGED);
308 			if (message) {
309 				message->AddInt64("when", movements.timestamp);
310 				message->AddFloat("be:wheel_delta_x", movements.wheel_xdelta);
311 				message->AddFloat("be:wheel_delta_y", movements.wheel_ydelta);
312 
313 				owner->EnqueueMessage(message);
314 			}
315 		}
316 
317 		old_movements = movements;
318 
319 	}
320 
321 	return 0;
322 }
323 
324 
325 // tablet_device
326 tablet_device::tablet_device(BSerialPort *port)
327 {
328 	serial = port;
329 	device_watcher = -1;
330 	active = false;
331 	device_ref.name = strdup(kDeviceName);
332 	device_ref.type = B_POINTING_DEVICE;
333 	device_ref.cookie = this;
334 };
335 
336 
337 tablet_device::~tablet_device()
338 {
339 	free(device_ref.name);
340 }
341 
342