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