xref: /haiku/src/add-ons/input_server/devices/wacom/TabletDevice.cpp (revision a5bf12376daeded4049521eb17a6cc41192250d9)
1 /*
2  * Copyright 2003-2008 Stephan Aßmus <superstippi@gmx.de>. All rights reserved.
3  * Distributed under the terms of the MIT license.
4  *
5  * Copyright 2000-2002  Olaf van Es. All Rights Reserved.
6  * Distributed under the terms of the MIT license.
7  *
8  * These people have added and tested device ids:
9  *
10  *		Frans van Nispen	<frans@xentronix.com>
11  *		Stefan Werner		<stefan@keindesign.de>
12  *		Hiroyuki Tsutsumi	<???>
13  */
14 
15 #include <stdio.h>
16 #include <stdlib.h>
17 #include <string.h>
18 
19 #include <File.h>
20 #include <InterfaceDefs.h>
21 #include <Screen.h>
22 #include <View.h>
23 
24 #include "DeviceReader.h"
25 #include "MasterServerDevice.h"
26 
27 #include "TabletDevice.h"
28 
29 #define SNOOZE_AMOUNT 2500
30 #define JITTER_X .0007
31 #define JITTER_Y .0007
32 #define ACCELERATION_KICK_IN 2.3
33 
34 // constructor
35 TabletDevice::TabletDevice(MasterServerDevice* parent, DeviceReader* reader)
36 	: PointingDevice(parent, reader),
37 	  fThreadID(B_ERROR),
38 	  fDeviceMode(DEVICE_UNKOWN),
39 	  fMaxX(1.0),
40 	  fMaxY(1.0),
41 	  fPosX(0.5),
42 	  fPosY(0.5),
43 	  fFakeMouseX(0.5),
44 	  fFakeMouseY(0.5),
45 	  fButtons(0),
46 	  fPressure(0.0),
47 	  fModifiers(0),
48 	  fEraser(0),
49 	  fTiltX(0.0),
50 	  fTiltY(0.0),
51 	  fClicks(0),
52 	  fHasContact(false)
53 {
54 }
55 
56 // destructor
57 TabletDevice::~TabletDevice()
58 {
59 	// cleanup
60 	Stop();
61 }
62 
63 // InitCheck
64 status_t
65 TabletDevice::InitCheck()
66 {
67 	status_t status = PointingDevice::InitCheck();
68 	if (status >= B_OK)
69 		status = DetectDevice(fReader);
70 	return status;
71 }
72 
73 // Start
74 status_t
75 TabletDevice::Start()
76 {
77 	status_t status = B_NO_INIT;
78 	if (fReader) {
79 		fActive = true;
80 		// get a nice name for our polling thread
81 		const char* name;
82 		_GetName(fReader->ProductID(), &name);
83 		// start generating events
84 		fThreadID = spawn_thread(poll_usb_device, name, 104, this);
85 		if (fThreadID >= B_OK) {
86 			resume_thread(fThreadID);
87 			status = B_OK;
88 		} else
89 			status = fThreadID;
90 	}
91 	return status;
92 }
93 
94 // Stop
95 status_t
96 TabletDevice::Stop()
97 {
98 	status_t err = B_OK;
99 
100 	fActive = false;
101 	if (fThreadID >= B_OK)
102 		wait_for_thread(fThreadID, &err);
103 	fThreadID = B_ERROR;
104 
105 	return err;
106 }
107 
108 // DetectDevice
109 status_t
110 TabletDevice::DetectDevice(const DeviceReader* reader)
111 {
112 	status_t status = B_OK;
113 	switch (reader->ProductID()) {
114 		case 0x00:
115 			SetDevice(5040.0, 3780.0, DEVICE_PENPARTNER);
116 			break;
117 		case 0x03:
118 			SetDevice(2048.0, 15360.0, DEVICE_PL500);
119 			break;
120 		case 0x10:
121 		case 0x11:
122 		case 0x13:
123 			SetDevice(10206.0, 7422.0, DEVICE_GRAPHIRE);
124 			break;
125 		case 0x12:	// Graphire 3 4x5
126 			SetDevice(13918.0, 10206.0, DEVICE_GRAPHIRE);
127 			break;
128 		case 0x14:	// Graphire 3 6x8
129 			SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE);
130 			break;
131 		case 0x15:	// Graphire 4 4x5 (tested)
132 			SetDevice(10208.0, 7024.0, DEVICE_GRAPHIRE);
133 			break;
134 		case 0x16:	// Graphire 4 6x8 (tested)
135 			SetDevice(16704.0, 12064.0, DEVICE_GRAPHIRE);
136 			break;
137 		case 0x17:	// BambooFun 4x5 (from Linux Wacom Project)
138 			SetDevice(14760.0, 9225.0, DEVICE_BAMBOO);
139 			break;
140 		case 0x18:	// BambooFun 6x8 (from Linux Wacom Project)
141 			SetDevice(21648.0, 13530.0, DEVICE_BAMBOO);
142 			break;
143 		case 0x20:
144 			SetDevice(12700.0, 10600.0, DEVICE_INTUOS);
145 			break;
146 		case 0x21:
147 			SetDevice(20320.0, 16240.0);
148 			break;
149 		case 0x22:
150 			SetDevice(30480.0, 24060.0);
151 			break;
152 		case 0x23:
153 			SetDevice(30480.0, 31680.0);
154 			break;
155 		case 0x24:
156 			SetDevice(45720.0, 31680.0);
157 			break;
158 		case 0x30:
159 			SetDevice(5408.0, 4056.0, DEVICE_PL500);
160 			break;
161 		case 0x31:
162 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
163 			break;
164 		case 0x32:
165 			SetDevice(6126.0, 4604.0, DEVICE_PL500);
166 			break;
167 		case 0x33:
168 			SetDevice(6260.0, 5016.0, DEVICE_PL500);
169 			break;
170 		case 0x34:
171 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
172 			break;
173 		case 0x35:
174 			SetDevice(7220.0, 5780.0, DEVICE_PL500);
175 			break;
176 		case 0x3F:
177 			SetDevice(87200.0, 65600.0, DEVICE_CINTIQ);
178 			break;
179 		case 0x41:
180 			SetDevice(12700.0, 10600.0);
181 			break;
182 		case 0x42:
183 			SetDevice(20320.0, 16240.0);
184 			break;
185 		case 0x43:
186 			SetDevice(30480.0, 24060.0);
187 			break;
188 		case 0x44:
189 			SetDevice(30480.0, 31680.0);
190 			break;
191 		case 0x45:
192 			SetDevice(45720.0, 31680.0);
193 			break;
194 		case 0x47:	// some I2 6x8 report as 0x47
195 			SetDevice(20320.0, 16240.0);
196 			break;
197 		case 0x60:
198 			SetDevice(5104.0, 3712.0, DEVICE_GRAPHIRE);
199 			break;
200 		case 0x61: // PenStation
201 //			SetDevice(3403.0, 2475.0, DEVICE_GRAPHIRE); // this version was untested
202 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION); // this version came from "beer"
203 			break;
204 		case 0x62: // Volito
205 			SetDevice(5040.0, 3712.0, DEVICE_VOLITO);
206 			break;
207 		case 0x64:	// PenPartner.1
208 //			SetDevice(3450.0, 2100.0, DEVICE_PENSTATION);
209 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION);
210 			break;
211 		case 0x65:	// Bamboo (from Linux Wacom Project)
212 			SetDevice(14760.0, 9225.0, DEVICE_BAMBOO);
213 			break;
214 		case 0x69:	// Bamboo1 (from Linux Wacom Project)
215 			SetDevice(5104.0,  3712.0, DEVICE_BAMBOO);
216 			break;
217 		case 0xB0:
218 			SetDevice(25400.0, 20320.0, DEVICE_INTUOS3);
219 			break;
220 		case 0xB1:
221 			SetDevice(40640.0, 30480.0, DEVICE_INTUOS3);
222 			break;
223 		case 0xB2:
224 			SetDevice(60960.0, 45720.0, DEVICE_INTUOS3);
225 			break;
226 		default:
227 			status = B_BAD_VALUE;
228 			break;
229 	}
230 	return status;
231 }
232 
233 // SetDevice
234 void
235 TabletDevice::SetDevice(float maxX, float maxY, uint32 mode)
236 {
237 	fDeviceMode = mode;
238 	fMaxX = maxX;
239 	fMaxY = maxY;
240 	fJitterX = JITTER_X;
241 	fJitterY = JITTER_Y;
242 }
243 
244 // ReadData
245 void
246 TabletDevice::ReadData(const uchar* data, bool& hasContact, uint32& mode,
247 	uint32& buttons, float& x, float& y, float& pressure,
248 	int32& clicks, int32& eraser, float& wheelX, float& wheelY,
249 	float& tiltX, float& tiltY) const
250 {
251 	hasContact = false;
252 	buttons = 0;
253 	mode = MODE_PEN;
254 	bool firstButton = false;
255 	bool secondButton = false;
256 	bool thirdButton = false;
257 	uint16 xPos = 0;
258 	uint16 yPos = 0;
259 
260 	switch (fDeviceMode) {
261 		case DEVICE_PENPARTNER: {
262 			xPos = data[2] << 8 | data[1];
263 			yPos = data[4] << 8 | data[3];
264 
265 			eraser = (data[5] & 0x20);
266 
267 			int8 pressureData = data[6];
268 			pressure = (float)(pressureData + 120) / 240.0;
269 
270 			firstButton = ((pressureData > -80) && !(data[5] & 0x20));
271 			secondButton = (data[5] & 0x40);
272 
273 			hasContact = true;
274 			break;
275 		}
276 		case DEVICE_GRAPHIRE:
277 		case DEVICE_BAMBOO:
278 		{
279 			xPos = data[3] << 8 | data[2];
280 			yPos = data[5] << 8 | data[4];
281 
282 			hasContact = (data[1] & 0x80);
283 
284 			uint16 pressureData = data[7] << 8 | data[6];
285 			pressure = (float)pressureData / 511.0;
286 			eraser = (data[1] & 0x20);
287 
288 			// mouse wheel support
289 			if (data[1] & 0x40) {	// mouse is on tablet!
290 				wheelY = (float)(int8)data[6];
291 				mode = MODE_MOUSE;
292 				// override contact to loose it as soon as possible
293 				// when mouse is lifted from tablet
294 				hasContact = (uint8)data[7] >= 30;
295 				pressure = 0.0;
296 				eraser = 0;
297 			}
298 
299 			firstButton = pressure > 0.0 ? true : (data[1] & 0x01);
300 			secondButton = (data[1] & 0x02);
301 			thirdButton = (data[1] & 0x04);
302 
303 			break;
304 		}
305 		case DEVICE_INTUOS:
306 		case DEVICE_INTUOS3:
307 		case DEVICE_CINTIQ:
308 			if ((data[0] == 0x02) && !(((data[1] >> 5) & 0x03) == 0x02)) {
309 				if (fDeviceMode == DEVICE_INTUOS3) {
310 					xPos = (data[2] << 9) | (data[3] << 1)
311 						| ((data[9] >> 1) & 1);
312 					yPos = (data[4] << 9) | (data[5] << 1) | (data[9] & 1);
313 				} else {
314 					xPos = (data[2] << 8) | data[3];
315 					yPos = (data[4] << 8) | data[5];
316 				}
317 				uint16 pressureData = data[6] << 2 | ((data[7] >> 6) & 0x03);
318 				pressure = (float)pressureData / 1023.0;
319 
320 				// mouse and wheel support
321 				if (data[1] == 0xf0) {	// mouse is on tablet!
322 					mode = MODE_MOUSE;
323 
324 					if (data[8] == 0x02)
325 						wheelY = 1.0;
326 					else if (data[8] == 0x01)
327 						wheelY = -1.0;
328 
329 					firstButton = (data[8] & 0x04);
330 					secondButton = (data[8] & 0x10);
331 					thirdButton = (data[8] & 0x08);
332 
333 					// override contact to loose it as soon as possible
334 					// when mouse is lifted from tablet
335 					hasContact = data[9] <= 0x68;
336 					pressure = 0.0;
337 					eraser = 0;
338 				} else {
339 //					eraser = (data[1] & 0x20); // eraser is een tool-id
340 //					firstButton = (pressureData > 0) && data[9] <= 0x68;// > 180);
341 //					firstButton = (pressureData > 180);
342 					firstButton = (data[6] > 0);
343 					secondButton = (data[1] & 0x02);
344 					thirdButton = (data[1] & 0x04);
345 					hasContact = (data[1] & 0x40);
346 					// convert tilt (-128 ... 127)
347 //					int8 tiltDataX = ((data[7] & 0x3f) << 2) | ((data[8] & 0x80) >> 6);
348 					int8 tiltDataX = ((data[7] & 0x3f) << 1) | ((data[8] & 0x80) >> 7);
349 					int8 tiltDataY = data[8] & 0x7f;
350 //					int8 tiltDataY = 0;
351 					// convert to floats
352 					tiltX = (float)(tiltDataX - 64) / 64.0;
353 					tiltY = (float)(tiltDataY - 64) / 64.0;
354 				}
355 			}
356 			break;
357 		case DEVICE_PL500: {
358 			hasContact = ( data[1] & 0x20);
359 			xPos = data[2] << 8 | data[3];
360 			yPos = data[5] << 8 | data[6];
361 			firstButton = (data[4] & 0x08);
362 			secondButton = (data[4] & 0x10);
363 			thirdButton = (data[4] & 0x20);
364 			uint16 pressureData = (data[4] & 0x04) >> 2 | (data[7] & 0x7f) << 1;
365 			pressure = (float)pressureData / 511.0;
366 			break;
367 		}
368 		case DEVICE_VOLITO: {
369 			eraser = 0;
370 			thirdButton = 0;
371 
372 			xPos = data[3] << 8 | data[2];
373 			yPos = data[5] << 8 | data[4];
374 
375 			hasContact = (data[1] & 0x80);
376 
377 			firstButton = (data[1] & 0x01) == 1;
378 			secondButton = data[1] & 0x04;
379 
380 			uint16 pressureData = data[7] << 8 | data[6];
381 			pressure = (float)pressureData / 511.0;
382 
383 			if (data[1] & 0x40) {	// mouse is on tablet
384 				wheelY = 0;
385 				mode = MODE_MOUSE;
386 				hasContact = (uint8)data[7] >= 30;
387 				pressure = 0.0;
388 				secondButton = data[1] & 0x02;
389 			}
390 
391 			break;
392 		}
393 		case DEVICE_PENSTATION: {
394 			xPos = data[3] << 8 | data[2];
395 			yPos = data[5] << 8 | data[4];
396 			hasContact = (data[1] & 0x10);
397 			uint16 pressureData = data[7] << 8 | data[6];
398 			pressure = (float)pressureData / 511.0;
399 			firstButton = (data[1] & 0x01);
400 			secondButton = (data[1] & 0x02);
401 			thirdButton = (data[1] & 0x04);
402 			break;
403 		}
404 	}
405 	if (pressure > 1.0)
406 		pressure = 1.0;
407 	else if (pressure < 0.0)
408 		pressure = 0.0;
409 	buttons = (firstButton ? B_PRIMARY_MOUSE_BUTTON : 0)
410 			   | (secondButton ? B_SECONDARY_MOUSE_BUTTON : 0)
411 			   | (thirdButton ? B_TERTIARY_MOUSE_BUTTON : 0);
412 	x = (float)xPos;
413 	y = (float)yPos;
414 }
415 
416 // SetStatus
417 void
418 TabletDevice::SetStatus(uint32 mode, uint32 buttons, float x, float y,
419 	float pressure, int32 clicks, uint32 modifiers, int32 eraser,
420 	float wheelX, float wheelY, float tiltX, float tiltY, const uchar* data)
421 {
422 	if (fActive) {
423 		uint32 what = B_MOUSE_MOVED;
424 		if (buttons > fButtons)
425 			what = B_MOUSE_DOWN;
426 		else if (buttons < fButtons)
427 			what = B_MOUSE_UP;
428 
429 
430 #if DEBUG
431 	float tabletX = x;
432 	float tabletY = y;
433 #endif
434 		x /= fMaxX;
435 		y /= fMaxY;
436 
437 		float deltaX = 0.0;
438 		float deltaY = 0.0;
439 
440 		float absDeltaX = 0.0;
441 		float absDeltaY = 0.0;
442 
443 		float unfilteredX = x;
444 		float unfilteredY = y;
445 
446 		if (fHasContact) {
447 			deltaX = x - fPosX;
448 			deltaY = y - fPosY;
449 
450 			absDeltaX = fabsf(deltaX);
451 			absDeltaY = fabsf(deltaY);
452 
453 #if 0 //DEBUG
454 fParent->LogString() << "x: " << x << ", y: " << y << ", pressure: " << pressure << "\n";
455 fParent->LogString() << "tilt x: " << tiltX << ", tilt y: " << tiltY << "\n\n";
456 #endif
457 			// apply a bit of filtering
458 			if (absDeltaX < fJitterX)
459 				x = fPosX;
460 			if (absDeltaY < fJitterY)
461 				y = fPosY;
462 		}
463 
464 		// only do send message if something changed
465 		if (x != fPosX || y != fPosY || fButtons != buttons || pressure != fPressure
466 			|| fEraser != eraser || fTiltX != tiltX || fTiltY != tiltY) {
467 
468 			bigtime_t now = system_time();
469 
470 			// common fields for any mouse message
471 			BMessage* event = new BMessage(what);
472 			event->AddInt64("when", now);
473 			event->AddInt32("buttons", buttons);
474 			if (mode == MODE_PEN) {
475 				event->AddFloat("x", x);
476 				event->AddFloat("y", y);
477 				event->AddFloat("be:tablet_x", unfilteredX);
478 				event->AddFloat("be:tablet_y", unfilteredY);
479 				event->AddFloat("be:tablet_pressure", pressure);
480 				event->AddInt32("be:tablet_eraser", eraser);
481 				if (_DeviceSupportsTilt()) {
482 					event->AddFloat("be:tablet_tilt_x", tiltX);
483 					event->AddFloat("be:tablet_tilt_y", tiltY);
484 				}
485 				// adjust mouse coordinates as well
486 				// to have the mouse appear at the pens
487 				// last position when switching
488 				fFakeMouseX = unfilteredX;
489 				fFakeMouseY = unfilteredY;
490 			} else if (mode == MODE_MOUSE) {
491 				// apply acceleration
492 				float accelerationX = fJitterX * ACCELERATION_KICK_IN;
493 //				if (absDeltaX > accelerationX)
494 					deltaX *= absDeltaX / accelerationX;
495 				float accelerationY = fJitterY * ACCELERATION_KICK_IN;
496 //				if (absDeltaY > accelerationY)
497 					deltaY *= absDeltaY / accelerationY;
498 				// calculate screen coordinates
499 				fFakeMouseX = min_c(1.0, max_c(0.0, fFakeMouseX + deltaX));
500 				fFakeMouseY = min_c(1.0, max_c(0.0, fFakeMouseY + deltaY));
501 				event->AddFloat("x", fFakeMouseX);
502 				event->AddFloat("y", fFakeMouseY);
503 				event->AddFloat("be:tablet_x", fFakeMouseX);
504 				event->AddFloat("be:tablet_y", fFakeMouseY);
505 			}
506 			event->AddInt32("modifiers", modifiers);
507 
508 #if DEBUG
509 if (data) {
510 	event->AddData("raw usb data", B_RAW_TYPE, data, 12);
511 }
512 event->AddFloat("tablet x", tabletX);
513 event->AddFloat("tablet y", tabletY);
514 #endif
515 			// additional fields for mouse down or up
516 			if (what == B_MOUSE_DOWN) {
517 				if (now - fLastClickTime < fParent->DoubleClickSpeed()) {
518 					fClicks++;
519 					if (fClicks > 3)
520 						fClicks = 1;
521 				} else {
522 					fClicks = 1;
523 				}
524 				fLastClickTime = now;
525 				event->AddInt32("clicks", fClicks);
526 			} else if (what == B_MOUSE_UP)
527 				event->AddInt32("clicks", 0);
528 
529 			status_t ret = fParent->EnqueueMessage(event);
530 			if (ret < B_OK)
531 				PRINT(("EnqueueMessage(): %s\n", strerror(ret)));
532 
533 			// apply values to members
534 			fPosX = x;
535 			fPosY = y;
536 			fButtons = buttons;
537 			fPressure = pressure;
538 			fModifiers = modifiers;
539 			fEraser = eraser;
540 			fTiltX = tiltX;
541 			fTiltY = tiltY;
542 		}
543 
544 		// separate wheel changed message
545 		if (fWheelX != wheelX || fWheelY != wheelY) {
546 			BMessage* event = new BMessage(B_MOUSE_WHEEL_CHANGED);
547 			event->AddInt64("when", system_time());
548 			event->AddFloat("be:wheel_delta_x", wheelX);
549 			event->AddFloat("be:wheel_delta_y", wheelY);
550 			fParent->EnqueueMessage(event);
551 
552 			// apply values to members
553 			fWheelX = wheelX;
554 			fWheelY = wheelY;
555 		}
556 	}
557 }
558 
559 // SetContact
560 void
561 TabletDevice::SetContact(bool contact)
562 {
563 	fHasContact = contact;
564 }
565 
566 // poll_usb_device
567 int32
568 TabletDevice::poll_usb_device(void* arg)
569 {
570 	TabletDevice* tabletDevice = (TabletDevice*)arg;
571 	DeviceReader* reader = tabletDevice->fReader;
572 
573 	if (!reader || reader->InitCheck() < B_OK)
574 		return B_BAD_VALUE;
575 
576 	int dataBytes = reader->MaxPacketSize();
577 	if (dataBytes > 128)
578 		return B_BAD_VALUE;
579 
580 	uchar data[max_c(12, dataBytes)];
581 
582 	while (tabletDevice->IsActive()) {
583 
584 		status_t ret = reader->ReadData(data, dataBytes);
585 
586 		if (ret == dataBytes) {
587 			// data we read from the wacom device
588 			uint32 mode;
589 			bool hasContact = false;
590 			uint32 buttons = 0;
591 			float x = 0.0;
592 			float y = 0.0;
593 			float pressure = 0.0;
594 			int32 clicks = 0;
595 			int32 eraser = 0;
596 			float wheelX = 0.0;
597 			float wheelY = 0.0;
598 			float tiltX = 0.0;
599 			float tiltY = 0.0;
600 			// let the device extract all information from the data
601 			tabletDevice->ReadData(data, hasContact, mode, buttons,
602 								   x, y, pressure, clicks, eraser,
603 								   wheelX, wheelY, tiltX, tiltY);
604 			if (hasContact) {
605 				// apply the changes to the device
606 				tabletDevice->SetStatus(mode, buttons, x, y, pressure,
607 										clicks, modifiers(), eraser,
608 										wheelX, wheelY, tiltX, tiltY, data);
609 			} else
610 				PRINT(("device has no contact\n"));
611 			tabletDevice->SetContact(hasContact);
612 		} else {
613 			PRINT(("failed to read %ld bytes, read: %ld or %s\n",
614 				dataBytes, ret, strerror(ret)));
615 
616 			if (ret < B_OK) {
617 				if (ret == B_TIMED_OUT)
618 					snooze(SNOOZE_AMOUNT);
619 				else if (ret == B_INTERRUPTED)
620 					snooze(SNOOZE_AMOUNT);
621 				else {
622 					return ret;
623 				}
624 			}
625 		}
626 	}
627 
628 	return B_OK;
629 }
630 
631 // _DeviceSupportsTilt
632 bool
633 TabletDevice::_DeviceSupportsTilt() const
634 {
635 	bool tilt = false;
636 	switch (fDeviceMode) {
637 		case DEVICE_INTUOS:
638 		case DEVICE_INTUOS3:
639 		case DEVICE_CINTIQ:
640 			tilt = true;
641 			break;
642 	}
643 	return tilt;
644 }
645 
646 // _GetName
647 void
648 TabletDevice::_GetName(uint16 productID, const char** name) const
649 {
650 	switch (productID) {
651 		case 0x00:
652 			*name = "Wacom USB";
653 			break;
654 		case 0x03:	// driver does not support this yet
655 			*name = "Wacom Cintiq Partner USB";
656 			break;
657 		case 0x10:
658 			*name = "Wacom Graphire USB";
659 			break;
660 		case 0x11:
661 			*name = "Wacom Graphire2 4x5\" USB";
662 			break;
663 		case 0x12:
664 			*name = "Wacom Graphire2 5x7\" USB";
665 			break;
666 		case 0x13:
667 			*name = "Wacom Graphire3 4x5\" USB";
668 			break;
669 		case 0x14:
670 			*name = "Wacom Graphire3 6x8\" USB";
671 			break;
672 		case 0x15:
673 			*name = "Wacom Graphire4 4x5\" USB";
674 			break;
675 		case 0x16:
676 			*name = "Wacom Graphire4 6x8\" USB";
677 			break;
678 		case 0x17:
679 			*name = "Wacom BambooFun 4x5\" USB";
680 			break;
681 		case 0x18:
682 			*name = "Wacom BambooFun 6x8\" USB";
683 			break;
684 		case 0x20:
685 			*name = "Wacom Intuos 4x5\" USB";
686 			break;
687 		case 0x21:
688 			*name = "Wacom Intuos 6x8\" USB";
689 			break;
690 		case 0x22:
691 			*name = "Wacom Intuos 9x12\" USB";
692 			break;
693 		case 0x23:
694 			*name = "Wacom Intuos 12x12\" USB";
695 			break;
696 		case 0x24:
697 			*name = "Wacom Intuos 12x18\" USB";
698 			break;
699 		case 0x30:
700 			*name = "Wacom PL400 USB";
701 			break;
702 		case 0x31:
703 			*name = "Wacom PL500 USB";
704 			break;
705 		case 0x32:
706 			*name = "Wacom PL600 USB";
707 			break;
708 		case 0x33:
709 			*name = "Wacom PL600SX USB";
710 			break;
711 		case 0x34:
712 			*name = "Wacom PL550 USB";
713 			break;
714 		case 0x35:
715 			*name = "Wacom PL800 USB";
716 			break;
717 
718 		case 0x3F:
719 			*name = "Wacom Cintiq 21UX USB";
720 			break;
721 
722 		case 0x41:
723 			*name = "Wacom Intuos2 4x5\" USB";
724 			break;
725 		case 0x42:
726 			*name = "Wacom Intuos2 6x8\" USB";
727 			break;
728 		case 0x43:
729 			*name = "Wacom Intuos2 9x12\" USB";
730 			break;
731 		case 0x44:
732 			*name = "Wacom Intuos2 12x12\" USB";
733 			break;
734 		case 0x45:
735 			*name = "Wacom Intuos2 12x18\" USB";
736 			break;
737 		case 0x47:	// some I2 6x8s seem to report as 0x47
738 			*name = "Wacom Intuos2 6x8\" USB";
739 			break;
740 
741 		case 0x60:
742 			*name = "Wacom Volito USB";
743 			break;
744 		case 0x61:
745 			*name = "Wacom PenStation USB";
746 			break;
747 		case 0x62:
748 			*name = "Wacom Volito2 USB";
749 			break;
750 		case 0x64:
751 			*name = "Wacom PenPartner.1 USB";
752 			break;
753 		case 0x65:
754 			*name = "Wacom Bamboo USB";
755 			break;
756 		case 0x69:
757 			*name = "Wacom Bamboo1 USB";
758 			break;
759 
760 		case 0xB0:
761 			*name = "Wacom Intuos3 4x5 USB";
762 			break;
763 		case 0xB1:
764 			*name = "Wacom Intuos3 6x8 USB";
765 			break;
766 		case 0xB2:
767 			*name = "Wacom Intuos3 9x12 USB";
768 			break;
769 		default:
770 			*name = "<unkown wacom tablet>";
771 			break;
772 	}
773 }
774 
775