xref: /haiku/src/add-ons/input_server/devices/wacom/TabletDevice.cpp (revision 89755088d790ff4fe36f8aa77dacb2bd15507108)
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 0x20:
138 			SetDevice(12700.0, 10600.0, DEVICE_INTUOS);
139 			break;
140 		case 0x21:
141 			SetDevice(20320.0, 16240.0);
142 			break;
143 		case 0x22:
144 			SetDevice(30480.0, 24060.0);
145 			break;
146 		case 0x23:
147 			SetDevice(30480.0, 31680.0);
148 			break;
149 		case 0x24:
150 			SetDevice(45720.0, 31680.0);
151 			break;
152 		case 0x30:
153 			SetDevice(5408.0, 4056.0, DEVICE_PL500);
154 			break;
155 		case 0x31:
156 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
157 			break;
158 		case 0x32:
159 			SetDevice(6126.0, 4604.0, DEVICE_PL500);
160 			break;
161 		case 0x33:
162 			SetDevice(6260.0, 5016.0, DEVICE_PL500);
163 			break;
164 		case 0x34:
165 			SetDevice(6144.0, 4608.0, DEVICE_PL500);
166 			break;
167 		case 0x35:
168 			SetDevice(7220.0, 5780.0, DEVICE_PL500);
169 			break;
170 		case 0x3F:
171 			SetDevice(87200.0, 65600.0, DEVICE_CINTIQ);
172 			break;
173 		case 0x41:
174 			SetDevice(12700.0, 10600.0);
175 			break;
176 		case 0x42:
177 			SetDevice(20320.0, 16240.0);
178 			break;
179 		case 0x43:
180 			SetDevice(30480.0, 24060.0);
181 			break;
182 		case 0x44:
183 			SetDevice(30480.0, 31680.0);
184 			break;
185 		case 0x45:
186 			SetDevice(45720.0, 31680.0);
187 			break;
188 		case 0x47:	// some I2 6x8 report as 0x47
189 			SetDevice(20320.0, 16240.0);
190 			break;
191 		case 0x60:
192 			SetDevice(5104.0, 3712.0, DEVICE_GRAPHIRE);
193 			break;
194 		case 0x61: // PenStation
195 //			SetDevice(3403.0, 2475.0, DEVICE_GRAPHIRE); // this version was untested
196 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION); // this version came from "beer"
197 			break;
198 		case 0x62: // Volito
199 			SetDevice(5040.0, 3712.0, DEVICE_VOLITO);
200 			break;
201 		case 0x64:	// PenPartner.1
202 //			SetDevice(3450.0, 2100.0, DEVICE_PENSTATION);
203 			SetDevice(3248.0, 2320.0, DEVICE_PENSTATION);
204 			break;
205 		case 0xB0:
206 			SetDevice(25400.0, 20320.0, DEVICE_INTUOS3);
207 			break;
208 		case 0xB1:
209 			// tested:
210 			SetDevice(20320.0, 15230.0, DEVICE_INTUOS3);
211 			// Frans:
212 //			SetDevice(40640.0, 30480.0, DEVICE_INTUOS3);
213 			break;
214 		case 0xB2:
215 			SetDevice(60960.0, 45720.0, DEVICE_INTUOS3);
216 			break;
217 		default:
218 			status = B_BAD_VALUE;
219 			break;
220 	}
221 	return status;
222 }
223 
224 // SetDevice
225 void
226 TabletDevice::SetDevice(float maxX, float maxY, uint32 mode)
227 {
228 	fDeviceMode = mode;
229 	fMaxX = maxX;
230 	fMaxY = maxY;
231 	fJitterX = JITTER_X;
232 	fJitterY = JITTER_Y;
233 }
234 
235 // ReadData
236 void
237 TabletDevice::ReadData(const uchar* data, bool& hasContact, uint32& mode,
238 	uint32& buttons, float& x, float& y, float& pressure,
239 	int32& clicks, int32& eraser, float& wheelX, float& wheelY,
240 	float& tiltX, float& tiltY) const
241 {
242 	hasContact = false;
243 	buttons = 0;
244 	mode = MODE_PEN;
245 	bool firstButton = false;
246 	bool secondButton = false;
247 	bool thirdButton = false;
248 	uint16 xPos = 0;
249 	uint16 yPos = 0;
250 
251 	switch (fDeviceMode) {
252 		case DEVICE_PENPARTNER: {
253 			xPos = data[2] << 8 | data[1];
254 			yPos = data[4] << 8 | data[3];
255 
256 			eraser = (data[5] & 0x20);
257 
258 			int8 pressureData = data[6];
259 			pressure = (float)(pressureData + 120) / 240.0;
260 
261 			firstButton = ((pressureData > -80) && !(data[5] & 0x20));
262 			secondButton = (data[5] & 0x40);
263 
264 			hasContact = true;
265 			break;
266 		}
267 		case DEVICE_GRAPHIRE: {
268 			xPos = data[3] << 8 | data[2];
269 			yPos = data[5] << 8 | data[4];
270 
271 			hasContact = (data[1] & 0x80);
272 
273 			uint16 pressureData = data[7] << 8 | data[6];
274 			pressure = (float)pressureData / 511.0;
275 			eraser = (data[1] & 0x20);
276 
277 			// mouse wheel support
278 			if (data[1] & 0x40) {	// mouse is on tablet!
279 				wheelY = (float)(int8)data[6];
280 				mode = MODE_MOUSE;
281 				// override contact to loose it as soon as possible
282 				// when mouse is lifted from tablet
283 				hasContact = (uint8)data[7] >= 30;
284 				pressure = 0.0;
285 				eraser = 0;
286 			}
287 
288 			firstButton = pressure > 0.0 ? true : (data[1] & 0x01);
289 			secondButton = (data[1] & 0x02);
290 			thirdButton = (data[1] & 0x04);
291 
292 			break;
293 		}
294 		case DEVICE_INTUOS:
295 		case DEVICE_INTUOS3:
296 		case DEVICE_CINTIQ:
297 			if ((data[0] == 0x02) && !(((data[1] >> 5) & 0x03) == 0x02)) {
298 				xPos = data[2] << 8 | data[3];
299 				yPos = data[4] << 8 | data[5];
300 				uint16 pressureData = data[6] << 2 | ((data[7] >> 6) & 0x03);
301 				pressure = (float)pressureData / 1023.0;
302 
303 				// mouse and wheel support
304 				if (data[1] == 0xf0) {	// mouse is on tablet!
305 					mode = MODE_MOUSE;
306 
307 					if (data[8] == 0x02)
308 						wheelY = 1.0;
309 					else if (data[8] == 0x01)
310 						wheelY = -1.0;
311 
312 					firstButton = (data[8] & 0x04);
313 					secondButton = (data[8] & 0x10);
314 					thirdButton = (data[8] & 0x08);
315 
316 					// override contact to loose it as soon as possible
317 					// when mouse is lifted from tablet
318 					hasContact = data[9] <= 0x68;
319 					pressure = 0.0;
320 					eraser = 0;
321 				} else {
322 //					eraser = (data[1] & 0x20); // eraser is een tool-id
323 //					firstButton = (pressureData > 0) && data[9] <= 0x68;// > 180);
324 //					firstButton = (pressureData > 180);
325 					firstButton = (data[6] > 0);
326 					secondButton = (data[1] & 0x02);
327 					thirdButton = (data[1] & 0x04);
328 					hasContact = (data[1] & 0x40);
329 					// convert tilt (-128 ... 127)
330 //					int8 tiltDataX = ((data[7] & 0x3f) << 2) | ((data[8] & 0x80) >> 6);
331 					int8 tiltDataX = ((data[7] & 0x3f) << 1) | ((data[8] & 0x80) >> 7);
332 					int8 tiltDataY = data[8] & 0x7f;
333 //					int8 tiltDataY = 0;
334 					// convert to floats
335 					tiltX = (float)(tiltDataX - 64) / 64.0;
336 					tiltY = (float)(tiltDataY - 64) / 64.0;
337 				}
338 			}
339 			break;
340 		case DEVICE_PL500: {
341 			hasContact = ( data[1] & 0x20);
342 			xPos = data[2] << 8 | data[3];
343 			yPos = data[5] << 8 | data[6];
344 			firstButton = (data[4] & 0x08);
345 			secondButton = (data[4] & 0x10);
346 			thirdButton = (data[4] & 0x20);
347 			uint16 pressureData = (data[4] & 0x04) >> 2 | (data[7] & 0x7f) << 1;
348 			pressure = (float)pressureData / 511.0;
349 			break;
350 		}
351 		case DEVICE_VOLITO: {
352 			eraser = 0;
353 			thirdButton = 0;
354 
355 			xPos = data[3] << 8 | data[2];
356 			yPos = data[5] << 8 | data[4];
357 
358 			hasContact = (data[1] & 0x80);
359 
360 			firstButton = (data[1] & 0x01) == 1;
361 			secondButton = data[1] & 0x04;
362 
363 			uint16 pressureData = data[7] << 8 | data[6];
364 			pressure = (float)pressureData / 511.0;
365 
366 			if (data[1] & 0x40) {	// mouse is on tablet
367 				wheelY = 0;
368 				mode = MODE_MOUSE;
369 				hasContact = (uint8)data[7] >= 30;
370 				pressure = 0.0;
371 				secondButton = data[1] & 0x02;
372 			}
373 
374 			break;
375 		}
376 		case DEVICE_PENSTATION: {
377 			xPos = data[3] << 8 | data[2];
378 			yPos = data[5] << 8 | data[4];
379 			hasContact = (data[1] & 0x10);
380 			uint16 pressureData = data[7] << 8 | data[6];
381 			pressure = (float)pressureData / 511.0;
382 			firstButton = (data[1] & 0x01);
383 			secondButton = (data[1] & 0x02);
384 			thirdButton = (data[1] & 0x04);
385 			break;
386 		}
387 	}
388 	if (pressure > 1.0)
389 		pressure = 1.0;
390 	else if (pressure < 0.0)
391 		pressure = 0.0;
392 	buttons = (firstButton ? B_PRIMARY_MOUSE_BUTTON : 0)
393 			   | (secondButton ? B_SECONDARY_MOUSE_BUTTON : 0)
394 			   | (thirdButton ? B_TERTIARY_MOUSE_BUTTON : 0);
395 	x = (float)xPos;
396 	y = (float)yPos;
397 }
398 
399 // SetStatus
400 void
401 TabletDevice::SetStatus(uint32 mode, uint32 buttons, float x, float y,
402 	float pressure, int32 clicks, uint32 modifiers, int32 eraser,
403 	float wheelX, float wheelY, float tiltX, float tiltY, const uchar* data)
404 {
405 	if (fActive) {
406 		uint32 what = B_MOUSE_MOVED;
407 		if (buttons > fButtons)
408 			what = B_MOUSE_DOWN;
409 		else if (buttons < fButtons)
410 			what = B_MOUSE_UP;
411 
412 
413 #if DEBUG
414 	float tabletX = x;
415 	float tabletY = y;
416 #endif
417 		x /= fMaxX;
418 		y /= fMaxY;
419 
420 		float deltaX = 0.0;
421 		float deltaY = 0.0;
422 
423 		float absDeltaX = 0.0;
424 		float absDeltaY = 0.0;
425 
426 		float unfilteredX = x;
427 		float unfilteredY = y;
428 
429 		if (fHasContact) {
430 			deltaX = x - fPosX;
431 			deltaY = y - fPosY;
432 
433 			absDeltaX = fabsf(deltaX);
434 			absDeltaY = fabsf(deltaY);
435 
436 #if 0 //DEBUG
437 fParent->LogString() << "x: " << x << ", y: " << y << ", pressure: " << pressure << "\n";
438 fParent->LogString() << "tilt x: " << tiltX << ", tilt y: " << tiltY << "\n\n";
439 #endif
440 			// apply a bit of filtering
441 			if (absDeltaX < fJitterX)
442 				x = fPosX;
443 			if (absDeltaY < fJitterY)
444 				y = fPosY;
445 		}
446 
447 		// only do send message if something changed
448 		if (x != fPosX || y != fPosY || fButtons != buttons || pressure != fPressure
449 			|| fEraser != eraser || fTiltX != tiltX || fTiltY != tiltY) {
450 
451 			bigtime_t now = system_time();
452 
453 			// common fields for any mouse message
454 			BMessage* event = new BMessage(what);
455 			event->AddInt64("when", now);
456 			event->AddInt32("buttons", buttons);
457 			if (mode == MODE_PEN) {
458 				event->AddFloat("x", x);
459 				event->AddFloat("y", y);
460 				event->AddFloat("be:tablet_x", unfilteredX);
461 				event->AddFloat("be:tablet_y", unfilteredY);
462 				event->AddFloat("be:tablet_pressure", pressure);
463 				event->AddInt32("be:tablet_eraser", eraser);
464 				if (_DeviceSupportsTilt()) {
465 					event->AddFloat("be:tablet_tilt_x", tiltX);
466 					event->AddFloat("be:tablet_tilt_y", tiltY);
467 				}
468 				// adjust mouse coordinates as well
469 				// to have the mouse appear at the pens
470 				// last position when switching
471 				fFakeMouseX = unfilteredX;
472 				fFakeMouseY = unfilteredY;
473 			} else if (mode == MODE_MOUSE) {
474 				// apply acceleration
475 				float accelerationX = fJitterX * ACCELERATION_KICK_IN;
476 //				if (absDeltaX > accelerationX)
477 					deltaX *= absDeltaX / accelerationX;
478 				float accelerationY = fJitterY * ACCELERATION_KICK_IN;
479 //				if (absDeltaY > accelerationY)
480 					deltaY *= absDeltaY / accelerationY;
481 				// calculate screen coordinates
482 				fFakeMouseX = min_c(1.0, max_c(0.0, fFakeMouseX + deltaX));
483 				fFakeMouseY = min_c(1.0, max_c(0.0, fFakeMouseY + deltaY));
484 				event->AddFloat("x", fFakeMouseX);
485 				event->AddFloat("y", fFakeMouseY);
486 				event->AddFloat("be:tablet_x", fFakeMouseX);
487 				event->AddFloat("be:tablet_y", fFakeMouseY);
488 			}
489 			event->AddInt32("modifiers", modifiers);
490 
491 #if DEBUG
492 if (data) {
493 	event->AddData("raw usb data", B_RAW_TYPE, data, 12);
494 }
495 event->AddFloat("tablet x", tabletX);
496 event->AddFloat("tablet y", tabletY);
497 #endif
498 			// additional fields for mouse down or up
499 			if (what == B_MOUSE_DOWN) {
500 				if (now - fLastClickTime < fParent->DoubleClickSpeed()) {
501 					fClicks++;
502 					if (fClicks > 3)
503 						fClicks = 1;
504 				} else {
505 					fClicks = 1;
506 				}
507 				fLastClickTime = now;
508 				event->AddInt32("clicks", fClicks);
509 			} else if (what == B_MOUSE_UP)
510 				event->AddInt32("clicks", 0);
511 
512 			status_t ret = fParent->EnqueueMessage(event);
513 			if (ret < B_OK)
514 				PRINT(("EnqueueMessage(): %s\n", strerror(ret)));
515 
516 			// apply values to members
517 			fPosX = x;
518 			fPosY = y;
519 			fButtons = buttons;
520 			fPressure = pressure;
521 			fModifiers = modifiers;
522 			fEraser = eraser;
523 			fTiltX = tiltX;
524 			fTiltY = tiltY;
525 		}
526 
527 		// separate wheel changed message
528 		if (fWheelX != wheelX || fWheelY != wheelY) {
529 			BMessage* event = new BMessage(B_MOUSE_WHEEL_CHANGED);
530 			event->AddInt64("when", system_time());
531 			event->AddFloat("be:wheel_delta_x", wheelX);
532 			event->AddFloat("be:wheel_delta_y", wheelY);
533 			fParent->EnqueueMessage(event);
534 
535 			// apply values to members
536 			fWheelX = wheelX;
537 			fWheelY = wheelY;
538 		}
539 	}
540 }
541 
542 // SetContact
543 void
544 TabletDevice::SetContact(bool contact)
545 {
546 	fHasContact = contact;
547 }
548 
549 // poll_usb_device
550 int32
551 TabletDevice::poll_usb_device(void* arg)
552 {
553 	TabletDevice* tabletDevice = (TabletDevice*)arg;
554 	DeviceReader* reader = tabletDevice->fReader;
555 
556 	if (!reader || reader->InitCheck() < B_OK)
557 		return B_BAD_VALUE;
558 
559 	int dataBytes = reader->MaxPacketSize();
560 	if (dataBytes > 128)
561 		return B_BAD_VALUE;
562 
563 	uchar data[max_c(12, dataBytes)];
564 
565 	while (tabletDevice->IsActive()) {
566 
567 		status_t ret = reader->ReadData(data, dataBytes);
568 
569 		if (ret == dataBytes) {
570 			// data we read from the wacom device
571 			uint32 mode;
572 			bool hasContact = false;
573 			uint32 buttons = 0;
574 			float x = 0.0;
575 			float y = 0.0;
576 			float pressure = 0.0;
577 			int32 clicks = 0;
578 			int32 eraser = 0;
579 			float wheelX = 0.0;
580 			float wheelY = 0.0;
581 			float tiltX = 0.0;
582 			float tiltY = 0.0;
583 			// let the device extract all information from the data
584 			tabletDevice->ReadData(data, hasContact, mode, buttons,
585 								   x, y, pressure, clicks, eraser,
586 								   wheelX, wheelY, tiltX, tiltY);
587 			if (hasContact) {
588 				// apply the changes to the device
589 				tabletDevice->SetStatus(mode, buttons, x, y, pressure,
590 										clicks, modifiers(), eraser,
591 										wheelX, wheelY, tiltX, tiltY, data);
592 			} else
593 				PRINT(("device has no contact\n"));
594 			tabletDevice->SetContact(hasContact);
595 		} else {
596 			PRINT(("failed to read %ld bytes, read: %ld or %s\n",
597 				dataBytes, ret, strerror(ret)));
598 
599 			if (ret < B_OK) {
600 				if (ret == B_TIMED_OUT)
601 					snooze(SNOOZE_AMOUNT);
602 				else if (ret == B_INTERRUPTED)
603 					snooze(SNOOZE_AMOUNT);
604 				else {
605 					return ret;
606 				}
607 			}
608 		}
609 	}
610 
611 	return B_OK;
612 }
613 
614 // _DeviceSupportsTilt
615 bool
616 TabletDevice::_DeviceSupportsTilt() const
617 {
618 	bool tilt = false;
619 	switch (fDeviceMode) {
620 		case DEVICE_INTUOS:
621 		case DEVICE_INTUOS3:
622 		case DEVICE_CINTIQ:
623 			tilt = true;
624 			break;
625 	}
626 	return tilt;
627 }
628 
629 // _GetName
630 void
631 TabletDevice::_GetName(uint16 productID, const char** name) const
632 {
633 	switch (productID) {
634 		case 0x00:
635 			*name = "Wacom USB";
636 			break;
637 		case 0x03:	// driver does not support this yet
638 			*name = "Wacom Cintiq Partner USB";
639 			break;
640 		case 0x10:
641 			*name = "Wacom Graphire USB";
642 			break;
643 		case 0x11:
644 			*name = "Wacom Graphire2 4x5\" USB";
645 			break;
646 		case 0x12:
647 			*name = "Wacom Graphire2 5x7\" USB";
648 			break;
649 		case 0x13:
650 			*name = "Wacom Graphire3 4x5\" USB";
651 			break;
652 		case 0x14:
653 			*name = "Wacom Graphire3 6x8\" USB";
654 			break;
655 		case 0x15:
656 			*name = "Wacom Graphire4 4x5\" USB";
657 			break;
658 		case 0x16:
659 			*name = "Wacom Graphire4 6x8\" USB";
660 			break;
661 		case 0x20:
662 			*name = "Wacom Intuos 4x5\" USB";
663 			break;
664 		case 0x21:
665 			*name = "Wacom Intuos 6x8\" USB";
666 			break;
667 		case 0x22:
668 			*name = "Wacom Intuos 9x12\" USB";
669 			break;
670 		case 0x23:
671 			*name = "Wacom Intuos 12x12\" USB";
672 			break;
673 		case 0x24:
674 			*name = "Wacom Intuos 12x18\" USB";
675 			break;
676 		case 0x30:
677 			*name = "Wacom PL400 USB";
678 			break;
679 		case 0x31:
680 			*name = "Wacom PL500 USB";
681 			break;
682 		case 0x32:
683 			*name = "Wacom PL600 USB";
684 			break;
685 		case 0x33:
686 			*name = "Wacom PL600SX USB";
687 			break;
688 		case 0x34:
689 			*name = "Wacom PL550 USB";
690 			break;
691 		case 0x35:
692 			*name = "Wacom PL800 USB";
693 			break;
694 
695 		case 0x3F:
696 			*name = "Wacom Cintiq 21UX USB";
697 			break;
698 
699 		case 0x41:
700 			*name = "Wacom Intuos2 4x5\" USB";
701 			break;
702 		case 0x42:
703 			*name = "Wacom Intuos2 6x8\" USB";
704 			break;
705 		case 0x43:
706 			*name = "Wacom Intuos2 9x12\" USB";
707 			break;
708 		case 0x44:
709 			*name = "Wacom Intuos2 12x12\" USB";
710 			break;
711 		case 0x45:
712 			*name = "Wacom Intuos2 12x18\" USB";
713 			break;
714 		case 0x47:	// some I2 6x8s seem to report as 0x47
715 			*name = "Wacom Intuos2 6x8\" USB";
716 			break;
717 
718 		case 0x60:
719 			*name = "Wacom Volito USB";
720 			break;
721 		case 0x61:
722 			*name = "Wacom PenStation USB";
723 			break;
724 		case 0x62:
725 			*name = "Wacom Volito2 USB";
726 			break;
727 		case 0x64:
728 			*name = "Wacom PenPartner.1 USB";
729 			break;
730 
731 		case 0xB0:
732 			*name = "Wacom Intuos3 4x5 USB";
733 			break;
734 		case 0xB1:
735 			*name = "Wacom Intuos3 6x8 USB";
736 			break;
737 		case 0xB2:
738 			*name = "Wacom Intuos3 9x12 USB";
739 			break;
740 		default:
741 			*name = "<unkown wacom tablet>";
742 			break;
743 	}
744 }
745 
746