diff --git a/contrib/fwupd.spec.in b/contrib/fwupd.spec.in index 31f9b64ab..f242438c1 100644 --- a/contrib/fwupd.spec.in +++ b/contrib/fwupd.spec.in @@ -293,6 +293,7 @@ mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg %endif %{_libdir}/fwupd-plugins-3/libfu_plugin_unifying.so %{_libdir}/fwupd-plugins-3/libfu_plugin_upower.so +%{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_raw.so %{_libdir}/fwupd-plugins-3/libfu_plugin_wacom_usb.so %ghost %{_localstatedir}/lib/fwupd/gnupg %if 0%{?have_uefi} diff --git a/plugins/meson.build b/plugins/meson.build index 7d84f63f5..dbd509a35 100644 --- a/plugins/meson.build +++ b/plugins/meson.build @@ -12,6 +12,7 @@ subdir('test') subdir('udev') subdir('unifying') subdir('upower') +subdir('wacom-raw') subdir('wacom-usb') subdir('superio') diff --git a/plugins/wacom-raw/README.md b/plugins/wacom-raw/README.md new file mode 100644 index 000000000..878b99542 --- /dev/null +++ b/plugins/wacom-raw/README.md @@ -0,0 +1,26 @@ +Wacom RAW Support +================= + +Introduction +------------ + +This plugin updates integrated Wacom AES and EMR devices. They are typically +connected using I²C and not USB. + +GUID Generation +--------------- + +The HID DeviceInstanceId values are used, e.g. `HIDRAW\VEN_056A&DEV_4875`. + +Additionally, for supported AES devices an extra GUID is added for the hardware +ID (e.g. `WACOM\HWID_%04X`) to further disambiguate the panels. + +Quirk use +--------- +This plugin uses the following plugin-specific quirks: + +| Quirk | Description | Minimum fwupd version | +|-------------------------|-------------------------------------|-----------------------| +| `WacomI2cFlashBlockSize`| Block size to transfer firmware | 1.2.4 | +| `WacomI2cFlashBaseAddr` | Base address for firmware | 1.2.4 | +| `WacomI2cFlashSize` | Maximum size of the firmware zone | 1.2.4 | diff --git a/plugins/wacom-raw/data/hid-recorder.txt b/plugins/wacom-raw/data/hid-recorder.txt new file mode 100644 index 000000000..a76b65db5 --- /dev/null +++ b/plugins/wacom-raw/data/hid-recorder.txt @@ -0,0 +1,470 @@ +# WCOM4875:00 056A:4875 +# 0x05, 0x0d, // Usage Page (Digitizers) 0 +# 0x09, 0x04, // Usage (Touch Screen) 2 +# 0xa1, 0x01, // Collection (Application) 4 +# 0x85, 0x0c, // Report ID (12) 6 +# 0x95, 0x01, // Report Count (1) 8 +# 0x75, 0x08, // Report Size (8) 10 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 12 +# 0x15, 0x00, // Logical Minimum (0) 15 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 17 +# 0x09, 0x54, // Usage (Contact Count) 19 +# 0x81, 0x02, // Input (Data,Var,Abs) 21 +# 0x05, 0x0d, // Usage Page (Digitizers) 23 +# 0x09, 0x22, // Usage (Finger) 25 +# 0xa1, 0x02, // Collection (Logical) 27 +# 0x09, 0x42, // Usage (Tip Switch) 29 +# 0x15, 0x00, // Logical Minimum (0) 31 +# 0x25, 0x01, // Logical Maximum (1) 33 +# 0x75, 0x01, // Report Size (1) 35 +# 0x95, 0x01, // Report Count (1) 37 +# 0x81, 0x02, // Input (Data,Var,Abs) 39 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 41 +# 0x09, 0x47, // Usage (Confidence) 43 +# 0x81, 0x02, // Input (Data,Var,Abs) 45 +# 0x95, 0x05, // Report Count (5) 47 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 49 +# 0x75, 0x10, // Report Size (16) 51 +# 0x09, 0x51, // Usage (Contact Id) 53 +# 0x95, 0x01, // Report Count (1) 55 +# 0x81, 0x02, // Input (Data,Var,Abs) 57 +# 0x05, 0x01, // Usage Page (Generic Desktop) 59 +# 0x75, 0x10, // Report Size (16) 61 +# 0x95, 0x01, // Report Count (1) 63 +# 0x55, 0x0e, // Unit Exponent (-2) 65 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 67 +# 0x09, 0x30, // Usage (X) 69 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 71 +# 0x35, 0x00, // Physical Minimum (0) 74 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 76 +# 0x81, 0x02, // Input (Data,Var,Abs) 79 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 81 +# 0x09, 0x31, // Usage (Y) 84 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 86 +# 0x81, 0x02, // Input (Data,Var,Abs) 89 +# 0xc0, // End Collection 91 +# 0x05, 0x0d, // Usage Page (Digitizers) 92 +# 0x09, 0x22, // Usage (Finger) 94 +# 0xa1, 0x02, // Collection (Logical) 96 +# 0x09, 0x42, // Usage (Tip Switch) 98 +# 0x15, 0x00, // Logical Minimum (0) 100 +# 0x25, 0x01, // Logical Maximum (1) 102 +# 0x75, 0x01, // Report Size (1) 104 +# 0x95, 0x01, // Report Count (1) 106 +# 0x81, 0x02, // Input (Data,Var,Abs) 108 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 110 +# 0x09, 0x47, // Usage (Confidence) 112 +# 0x81, 0x02, // Input (Data,Var,Abs) 114 +# 0x95, 0x05, // Report Count (5) 116 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 118 +# 0x75, 0x10, // Report Size (16) 120 +# 0x09, 0x51, // Usage (Contact Id) 122 +# 0x95, 0x01, // Report Count (1) 124 +# 0x81, 0x02, // Input (Data,Var,Abs) 126 +# 0x05, 0x01, // Usage Page (Generic Desktop) 128 +# 0x75, 0x10, // Report Size (16) 130 +# 0x95, 0x01, // Report Count (1) 132 +# 0x55, 0x0e, // Unit Exponent (-2) 134 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 136 +# 0x09, 0x30, // Usage (X) 138 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 140 +# 0x35, 0x00, // Physical Minimum (0) 143 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 145 +# 0x81, 0x02, // Input (Data,Var,Abs) 148 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 150 +# 0x09, 0x31, // Usage (Y) 153 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 155 +# 0x81, 0x02, // Input (Data,Var,Abs) 158 +# 0xc0, // End Collection 160 +# 0x05, 0x0d, // Usage Page (Digitizers) 161 +# 0x09, 0x22, // Usage (Finger) 163 +# 0xa1, 0x02, // Collection (Logical) 165 +# 0x09, 0x42, // Usage (Tip Switch) 167 +# 0x15, 0x00, // Logical Minimum (0) 169 +# 0x25, 0x01, // Logical Maximum (1) 171 +# 0x75, 0x01, // Report Size (1) 173 +# 0x95, 0x01, // Report Count (1) 175 +# 0x81, 0x02, // Input (Data,Var,Abs) 177 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 179 +# 0x09, 0x47, // Usage (Confidence) 181 +# 0x81, 0x02, // Input (Data,Var,Abs) 183 +# 0x95, 0x05, // Report Count (5) 185 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 187 +# 0x75, 0x10, // Report Size (16) 189 +# 0x09, 0x51, // Usage (Contact Id) 191 +# 0x95, 0x01, // Report Count (1) 193 +# 0x81, 0x02, // Input (Data,Var,Abs) 195 +# 0x05, 0x01, // Usage Page (Generic Desktop) 197 +# 0x75, 0x10, // Report Size (16) 199 +# 0x95, 0x01, // Report Count (1) 201 +# 0x55, 0x0e, // Unit Exponent (-2) 203 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 205 +# 0x09, 0x30, // Usage (X) 207 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 209 +# 0x35, 0x00, // Physical Minimum (0) 212 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 214 +# 0x81, 0x02, // Input (Data,Var,Abs) 217 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 219 +# 0x09, 0x31, // Usage (Y) 222 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 224 +# 0x81, 0x02, // Input (Data,Var,Abs) 227 +# 0xc0, // End Collection 229 +# 0x05, 0x0d, // Usage Page (Digitizers) 230 +# 0x09, 0x22, // Usage (Finger) 232 +# 0xa1, 0x02, // Collection (Logical) 234 +# 0x09, 0x42, // Usage (Tip Switch) 236 +# 0x15, 0x00, // Logical Minimum (0) 238 +# 0x25, 0x01, // Logical Maximum (1) 240 +# 0x75, 0x01, // Report Size (1) 242 +# 0x95, 0x01, // Report Count (1) 244 +# 0x81, 0x02, // Input (Data,Var,Abs) 246 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 248 +# 0x09, 0x47, // Usage (Confidence) 250 +# 0x81, 0x02, // Input (Data,Var,Abs) 252 +# 0x95, 0x05, // Report Count (5) 254 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 256 +# 0x75, 0x10, // Report Size (16) 258 +# 0x09, 0x51, // Usage (Contact Id) 260 +# 0x95, 0x01, // Report Count (1) 262 +# 0x81, 0x02, // Input (Data,Var,Abs) 264 +# 0x05, 0x01, // Usage Page (Generic Desktop) 266 +# 0x75, 0x10, // Report Size (16) 268 +# 0x95, 0x01, // Report Count (1) 270 +# 0x55, 0x0e, // Unit Exponent (-2) 272 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 274 +# 0x09, 0x30, // Usage (X) 276 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 278 +# 0x35, 0x00, // Physical Minimum (0) 281 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 283 +# 0x81, 0x02, // Input (Data,Var,Abs) 286 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 288 +# 0x09, 0x31, // Usage (Y) 291 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 293 +# 0x81, 0x02, // Input (Data,Var,Abs) 296 +# 0xc0, // End Collection 298 +# 0x05, 0x0d, // Usage Page (Digitizers) 299 +# 0x09, 0x22, // Usage (Finger) 301 +# 0xa1, 0x02, // Collection (Logical) 303 +# 0x09, 0x42, // Usage (Tip Switch) 305 +# 0x15, 0x00, // Logical Minimum (0) 307 +# 0x25, 0x01, // Logical Maximum (1) 309 +# 0x75, 0x01, // Report Size (1) 311 +# 0x95, 0x01, // Report Count (1) 313 +# 0x81, 0x02, // Input (Data,Var,Abs) 315 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 317 +# 0x09, 0x47, // Usage (Confidence) 319 +# 0x81, 0x02, // Input (Data,Var,Abs) 321 +# 0x95, 0x05, // Report Count (5) 323 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 325 +# 0x75, 0x10, // Report Size (16) 327 +# 0x09, 0x51, // Usage (Contact Id) 329 +# 0x95, 0x01, // Report Count (1) 331 +# 0x81, 0x02, // Input (Data,Var,Abs) 333 +# 0x05, 0x01, // Usage Page (Generic Desktop) 335 +# 0x75, 0x10, // Report Size (16) 337 +# 0x95, 0x01, // Report Count (1) 339 +# 0x55, 0x0e, // Unit Exponent (-2) 341 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 343 +# 0x09, 0x30, // Usage (X) 345 +# 0x26, 0xc8, 0x35, // Logical Maximum (13768) 347 +# 0x35, 0x00, // Physical Minimum (0) 350 +# 0x46, 0x72, 0x0d, // Physical Maximum (3442) 352 +# 0x81, 0x02, // Input (Data,Var,Abs) 355 +# 0x46, 0x90, 0x07, // Physical Maximum (1936) 357 +# 0x09, 0x31, // Usage (Y) 360 +# 0x26, 0x40, 0x1e, // Logical Maximum (7744) 362 +# 0x81, 0x02, // Input (Data,Var,Abs) 365 +# 0xc0, // End Collection 367 +# 0x05, 0x0d, // Usage Page (Digitizers) 368 +# 0x27, 0xff, 0xff, 0x00, 0x00, // Logical Maximum (65535) 370 +# 0x75, 0x10, // Report Size (16) 375 +# 0x95, 0x01, // Report Count (1) 377 +# 0x09, 0x56, // Usage (Scan Time) 379 +# 0x81, 0x02, // Input (Data,Var,Abs) 381 +# 0x85, 0x0c, // Report ID (12) 383 +# 0x09, 0x55, // Usage (Contact Max) 385 +# 0x75, 0x08, // Report Size (8) 387 +# 0x95, 0x01, // Report Count (1) 389 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 391 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 394 +# 0x85, 0x0a, // Report ID (10) 396 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 398 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 401 +# 0x96, 0x00, 0x01, // Report Count (256) 403 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 406 +# 0xc0, // End Collection 408 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 409 +# 0x09, 0x11, // Usage (Vendor Usage 0x11) 412 +# 0xa1, 0x01, // Collection (Application) 414 +# 0x85, 0x03, // Report ID (3) 416 +# 0xa1, 0x02, // Collection (Logical) 418 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 420 +# 0x75, 0x08, // Report Size (8) 422 +# 0x15, 0x00, // Logical Minimum (0) 424 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 426 +# 0x95, 0x27, // Report Count (39) 429 +# 0x81, 0x02, // Input (Data,Var,Abs) 431 +# 0xc0, // End Collection 433 +# 0x85, 0x02, // Report ID (2) 434 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 436 +# 0x95, 0x01, // Report Count (1) 438 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 440 +# 0x85, 0x03, // Report ID (3) 442 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 444 +# 0x95, 0x3f, // Report Count (63) 446 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 448 +# 0x85, 0x04, // Report ID (4) 450 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 452 +# 0x95, 0x0f, // Report Count (15) 454 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 456 +# 0x85, 0x07, // Report ID (7) 458 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 460 +# 0x96, 0x00, 0x01, // Report Count (256) 462 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 465 +# 0x85, 0x08, // Report ID (8) 467 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 469 +# 0x96, 0x87, 0x00, // Report Count (135) 471 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 474 +# 0x85, 0x09, // Report ID (9) 476 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 478 +# 0x96, 0x3f, 0x00, // Report Count (63) 480 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 483 +# 0x85, 0x0d, // Report ID (13) 485 +# 0x09, 0x00, // Usage (Vendor Usage 0x00) 487 +# 0x95, 0x07, // Report Count (7) 489 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 491 +# 0xc0, // End Collection 493 +# 0x05, 0x0d, // Usage Page (Digitizers) 494 +# 0x09, 0x0e, // Usage (Device Configuration) 496 +# 0xa1, 0x01, // Collection (Application) 498 +# 0x85, 0x0e, // Report ID (14) 500 +# 0x09, 0x23, // Usage (Device Settings) 502 +# 0xa1, 0x02, // Collection (Logical) 504 +# 0x09, 0x52, // Usage (Inputmode) 506 +# 0x09, 0x53, // Usage (Device Index) 508 +# 0x15, 0x00, // Logical Minimum (0) 510 +# 0x25, 0x0a, // Logical Maximum (10) 512 +# 0x75, 0x08, // Report Size (8) 514 +# 0x95, 0x02, // Report Count (2) 516 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 518 +# 0xc0, // End Collection 520 +# 0xc0, // End Collection 521 +# 0x05, 0x0d, // Usage Page (Digitizers) 522 +# 0x09, 0x02, // Usage (Pen) 524 +# 0xa1, 0x01, // Collection (Application) 526 +# 0x85, 0x06, // Report ID (6) 528 +# 0xa4, // Push 530 +# 0x09, 0x20, // Usage (Stylus) 531 +# 0xa1, 0x00, // Collection (Physical) 533 +# 0x09, 0x42, // Usage (Tip Switch) 535 +# 0x09, 0x44, // Usage (Barrel Switch) 537 +# 0x09, 0x45, // Usage (Eraser) 539 +# 0x09, 0x3c, // Usage (Invert) 541 +# 0x09, 0x5a, // Usage (Secondary Barrel Switch) 543 +# 0x09, 0x32, // Usage (In Range) 545 +# 0x15, 0x00, // Logical Minimum (0) 547 +# 0x25, 0x01, // Logical Maximum (1) 549 +# 0x75, 0x01, // Report Size (1) 551 +# 0x95, 0x06, // Report Count (6) 553 +# 0x81, 0x02, // Input (Data,Var,Abs) 555 +# 0x95, 0x02, // Report Count (2) 557 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 559 +# 0x05, 0x01, // Usage Page (Generic Desktop) 561 +# 0x09, 0x30, // Usage (X) 563 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 565 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 570 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 575 +# 0x55, 0x0d, // Unit Exponent (-3) 577 +# 0x75, 0x10, // Report Size (16) 579 +# 0x95, 0x01, // Report Count (1) 581 +# 0x81, 0x02, // Input (Data,Var,Abs) 583 +# 0x09, 0x31, // Usage (Y) 585 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 587 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 592 +# 0x81, 0x02, // Input (Data,Var,Abs) 597 +# 0x45, 0x00, // Physical Maximum (0) 599 +# 0x65, 0x00, // Unit (None) 601 +# 0x55, 0x00, // Unit Exponent (0) 603 +# 0x05, 0x0d, // Usage Page (Digitizers) 605 +# 0x09, 0x30, // Usage (Tip Pressure) 607 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 609 +# 0x75, 0x10, // Report Size (16) 612 +# 0x81, 0x02, // Input (Data,Var,Abs) 614 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 616 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 619 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 621 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 624 +# 0x75, 0x10, // Report Size (16) 627 +# 0x81, 0x02, // Input (Data,Var,Abs) 629 +# 0x05, 0x0d, // Usage Page (Digitizers) 631 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 633 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 635 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 640 +# 0x75, 0x20, // Report Size (32) 645 +# 0x81, 0x02, // Input (Data,Var,Abs) 647 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 649 +# 0x09, 0x00, // Usage (Undefined) 652 +# 0x75, 0x08, // Report Size (8) 654 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 656 +# 0x15, 0x00, // Logical Minimum (0) 659 +# 0x81, 0x02, // Input (Data,Var,Abs) 661 +# 0x05, 0x0d, // Usage Page (Digitizers) 663 +# 0x09, 0x3b, // Usage (Battery Strength) 665 +# 0x81, 0x02, // Input (Data,Var,Abs) 667 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 669 +# 0x55, 0x00, // Unit Exponent (0) 671 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 673 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 676 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 679 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 682 +# 0x75, 0x08, // Report Size (8) 685 +# 0x09, 0x3d, // Usage (X Tilt) 687 +# 0x81, 0x02, // Input (Data,Var,Abs) 689 +# 0x09, 0x3e, // Usage (Y Tilt) 691 +# 0x81, 0x02, // Input (Data,Var,Abs) 693 +# 0xc0, // End Collection 695 +# 0xb4, // Pop 696 +# 0x85, 0x13, // Report ID (19) 697 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 699 +# 0x09, 0xc5, // Usage (Vendor Usage 0xc5) 702 +# 0x96, 0x00, 0x01, // Report Count (256) 704 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 707 +# 0xc0, // End Collection 709 +# 0x06, 0x11, 0xff, // Usage Page (Vendor Usage Page 0xff11) 710 +# 0x09, 0x02, // Usage (Vendor Usage 0x02) 713 +# 0xa1, 0x01, // Collection (Application) 715 +# 0x85, 0x0b, // Report ID (11) 717 +# 0xa4, // Push 719 +# 0x09, 0x20, // Usage (Vendor Usage 0x20) 720 +# 0xa1, 0x00, // Collection (Physical) 722 +# 0x09, 0x42, // Usage (Vendor Usage 0x42) 724 +# 0x09, 0x44, // Usage (Vendor Usage 0x44) 726 +# 0x09, 0x45, // Usage (Vendor Usage 0x45) 728 +# 0x09, 0x3c, // Usage (Vendor Usage 0x3c) 730 +# 0x09, 0x5a, // Usage (Vendor Usage 0x5a) 732 +# 0x09, 0x32, // Usage (Vendor Usage 0x32) 734 +# 0x15, 0x00, // Logical Minimum (0) 736 +# 0x25, 0x01, // Logical Maximum (1) 738 +# 0x75, 0x01, // Report Size (1) 740 +# 0x95, 0x06, // Report Count (6) 742 +# 0x81, 0x02, // Input (Data,Var,Abs) 744 +# 0x95, 0x02, // Report Count (2) 746 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 748 +# 0x05, 0x01, // Usage Page (Generic Desktop) 750 +# 0x09, 0x30, // Usage (X) 752 +# 0x27, 0x70, 0x86, 0x00, 0x00, // Logical Maximum (34416) 754 +# 0x47, 0x70, 0x86, 0x00, 0x00, // Physical Maximum (34416) 759 +# 0x65, 0x11, // Unit (Centimeter,SILinear) 764 +# 0x55, 0x0d, // Unit Exponent (-3) 766 +# 0x75, 0x10, // Report Size (16) 768 +# 0x95, 0x01, // Report Count (1) 770 +# 0x81, 0x02, // Input (Data,Var,Abs) 772 +# 0x09, 0x31, // Usage (Y) 774 +# 0x27, 0x9f, 0x4b, 0x00, 0x00, // Logical Maximum (19359) 776 +# 0x47, 0x9f, 0x4b, 0x00, 0x00, // Physical Maximum (19359) 781 +# 0x81, 0x02, // Input (Data,Var,Abs) 786 +# 0x45, 0x00, // Physical Maximum (0) 788 +# 0x65, 0x00, // Unit (None) 790 +# 0x55, 0x00, // Unit Exponent (0) 792 +# 0x05, 0x0d, // Usage Page (Digitizers) 794 +# 0x09, 0x30, // Usage (Tip Pressure) 796 +# 0x26, 0xff, 0x0f, // Logical Maximum (4095) 798 +# 0x75, 0x10, // Report Size (16) 801 +# 0x81, 0x02, // Input (Data,Var,Abs) 803 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 805 +# 0x09, 0x5b, // Usage (Vendor Usage 0x5b) 808 +# 0x16, 0x00, 0x80, // Logical Minimum (-32768) 810 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 813 +# 0x75, 0x10, // Report Size (16) 816 +# 0x81, 0x02, // Input (Data,Var,Abs) 818 +# 0x05, 0x0d, // Usage Page (Digitizers) 820 +# 0x09, 0x5b, // Usage (Transducer Serial Number) 822 +# 0x17, 0x00, 0x00, 0x00, 0x80, // Logical Minimum (-2147483648) 824 +# 0x27, 0xff, 0xff, 0xff, 0x7f, // Logical Maximum (2147483647) 829 +# 0x75, 0x20, // Report Size (32) 834 +# 0x81, 0x02, // Input (Data,Var,Abs) 836 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 838 +# 0x09, 0x00, // Usage (Undefined) 841 +# 0x75, 0x08, // Report Size (8) 843 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 845 +# 0x15, 0x00, // Logical Minimum (0) 848 +# 0x81, 0x02, // Input (Data,Var,Abs) 850 +# 0x05, 0x0d, // Usage Page (Digitizers) 852 +# 0x09, 0x3b, // Usage (Battery Strength) 854 +# 0x81, 0x02, // Input (Data,Var,Abs) 856 +# 0x65, 0x14, // Unit (Degrees,EngRotation) 858 +# 0x55, 0x00, // Unit Exponent (0) 860 +# 0x16, 0xa6, 0xff, // Logical Minimum (-90) 862 +# 0x26, 0x5a, 0x00, // Logical Maximum (90) 865 +# 0x36, 0xa6, 0xff, // Physical Minimum (-90) 868 +# 0x46, 0x5a, 0x00, // Physical Maximum (90) 871 +# 0x75, 0x08, // Report Size (8) 874 +# 0x09, 0x3d, // Usage (X Tilt) 876 +# 0x81, 0x02, // Input (Data,Var,Abs) 878 +# 0x09, 0x3e, // Usage (Y Tilt) 880 +# 0x81, 0x02, // Input (Data,Var,Abs) 882 +# 0xc0, // End Collection 884 +# 0xb4, // Pop 885 +# 0x06, 0x00, 0xff, // Usage Page (Vendor Defined Page 1) 886 +# 0x75, 0x08, // Report Size (8) 889 +# 0x15, 0x00, // Logical Minimum (0) 891 +# 0x26, 0xff, 0x00, // Logical Maximum (255) 893 +# 0x85, 0x05, // Report ID (5) 896 +# 0x09, 0x00, // Usage (Undefined) 898 +# 0x95, 0x3a, // Report Count (58) 900 +# 0x81, 0x02, // Input (Data,Var,Abs) 902 +# 0x85, 0x10, // Report ID (16) 904 +# 0x09, 0x00, // Usage (Undefined) 906 +# 0x95, 0x14, // Report Count (20) 908 +# 0x81, 0x02, // Input (Data,Var,Abs) 910 +# 0x85, 0x0f, // Report ID (15) 912 +# 0x09, 0x00, // Usage (Undefined) 914 +# 0x95, 0x28, // Report Count (40) 916 +# 0x81, 0x02, // Input (Data,Var,Abs) 918 +# 0x85, 0x0f, // Report ID (15) 920 +# 0x09, 0x00, // Usage (Undefined) 922 +# 0x95, 0x07, // Report Count (7) 924 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 926 +# 0x85, 0x11, // Report ID (17) 928 +# 0x09, 0x00, // Usage (Undefined) 930 +# 0x95, 0x09, // Report Count (9) 932 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 934 +# 0x85, 0x05, // Report ID (5) 936 +# 0x09, 0x00, // Usage (Undefined) 938 +# 0x95, 0x08, // Report Count (8) 940 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 942 +# 0x85, 0x10, // Report ID (16) 944 +# 0x09, 0x00, // Usage (Undefined) 946 +# 0x96, 0x3f, 0x00, // Report Count (63) 948 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 951 +# 0x85, 0x0b, // Report ID (11) 953 +# 0x09, 0x00, // Usage (Undefined) 955 +# 0x96, 0x3f, 0x00, // Report Count (63) 957 +# 0xb1, 0x02, // Feature (Data,Var,Abs) 960 +# 0xc0, // End Collection 962 +# 0x05, 0x01, // Usage Page (Generic Desktop) 963 +# 0x09, 0x02, // Usage (Mouse) 965 +# 0xa1, 0x01, // Collection (Application) 967 +# 0x85, 0x01, // Report ID (1) 969 +# 0x09, 0x01, // Usage (Pointer) 971 +# 0xa1, 0x00, // Collection (Physical) 973 +# 0x05, 0x09, // Usage Page (Button) 975 +# 0x19, 0x01, // Usage Minimum (1) 977 +# 0x29, 0x02, // Usage Maximum (2) 979 +# 0x15, 0x00, // Logical Minimum (0) 981 +# 0x25, 0x01, // Logical Maximum (1) 983 +# 0x95, 0x02, // Report Count (2) 985 +# 0x75, 0x01, // Report Size (1) 987 +# 0x81, 0x02, // Input (Data,Var,Abs) 989 +# 0x95, 0x01, // Report Count (1) 991 +# 0x75, 0x06, // Report Size (6) 993 +# 0x81, 0x03, // Input (Cnst,Var,Abs) 995 +# 0x05, 0x01, // Usage Page (Generic Desktop) 997 +# 0x09, 0x30, // Usage (X) 999 +# 0x09, 0x31, // Usage (Y) 1001 +# 0x26, 0xff, 0x7f, // Logical Maximum (32767) 1003 +# 0x75, 0x10, // Report Size (16) 1006 +# 0x95, 0x02, // Report Count (2) 1008 +# 0x81, 0x02, // Input (Data,Var,Abs) 1010 +# 0xc0, // End Collection 1012 +# 0xc0, // End Collection 1013 diff --git a/plugins/wacom-raw/fu-plugin-wacom-raw.c b/plugins/wacom-raw/fu-plugin-wacom-raw.c new file mode 100644 index 000000000..75c928462 --- /dev/null +++ b/plugins/wacom-raw/fu-plugin-wacom-raw.c @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include "fu-plugin-vfuncs.h" +#include "fu-wacom-aes-device.h" +#include "fu-wacom-emr-device.h" +#include "fu-wacom-common.h" + +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_set_build_hash (plugin, FU_BUILD_HASH); + fu_plugin_add_udev_subsystem (plugin, "hidraw"); +} + +gboolean +fu_plugin_update_detach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_detach (device, error); +} + +gboolean +fu_plugin_update_attach (FuPlugin *plugin, FuDevice *device, GError **error) +{ + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_attach (device, error); +} + +gboolean +fu_plugin_udev_device_added (FuPlugin *plugin, FuUdevDevice *device, GError **error) +{ + /* interesting device? */ + if (g_strcmp0 (fu_udev_device_get_subsystem (device), "hidraw") != 0) + return TRUE; + + /* wacom */ + if (fu_udev_device_get_vendor (device) != FU_WACOM_DEVICE_VID) + return TRUE; + + /* no actual device to open */ + if (g_udev_device_get_device_file (fu_udev_device_get_dev (device)) == NULL) + return TRUE; + + /* EMR */ + if (fu_device_has_guid (FU_DEVICE (device), "WacomEMR")) { + g_autoptr(FuWacomEmrDevice) dev = fu_wacom_emr_device_new (device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + + /* AES */ + if (fu_device_has_guid (FU_DEVICE (device), "WacomAES")) { + g_autoptr(FuWacomAesDevice) dev = fu_wacom_aes_device_new (device); + g_autoptr(FuDeviceLocker) locker = fu_device_locker_new (dev, error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add (plugin, FU_DEVICE (dev)); + } + + /* not supported */ + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Only EMR or AES devices are supported"); + return FALSE; +} + +gboolean +fu_plugin_update (FuPlugin *plugin, + FuDevice *device, + GBytes *blob_fw, + FwupdInstallFlags flags, + GError **error) +{ + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new (device, error); + if (locker == NULL) + return FALSE; + return fu_device_write_firmware (device, blob_fw, error); +} diff --git a/plugins/wacom-raw/fu-wacom-aes-device.c b/plugins/wacom-raw/fu-wacom-aes-device.c new file mode 100644 index 000000000..5cfb55e5c --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-aes-device.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-chunk.h" +#include "fu-wacom-common.h" +#include "fu-wacom-aes-device.h" + +struct _FuWacomAesDevice { + FuWacomDevice parent_instance; + guint32 hwid; +}; + +G_DEFINE_TYPE (FuWacomAesDevice, fu_wacom_aes_device, FU_TYPE_WACOM_DEVICE) + +static gboolean +fu_wacom_aes_device_obtain_hwid (FuWacomAesDevice *self, GError **error) +{ + guint8 cmd[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; + guint8 buf[FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ] = { 0x0 }; + + cmd[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; + cmd[1] = 0x01; /* ?? */ + cmd[2] = 0x01; /* ?? */ + cmd[3] = 0x0f; /* ?? */ + + if (!fu_wacom_device_set_feature (FU_WACOM_DEVICE (self), + cmd, sizeof(cmd), error)) { + g_prefix_error (error, "failed to send: "); + return FALSE; + } + buf[0] = FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID; + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to receive: "); + return FALSE; + } + if (buf[1] == 0xff) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "firmware does not support this feature"); + return FALSE; + } + + /* check magic number */ + if (memcmp (buf, "\x34\x12\x78\x56\x65\x87\x21\x43", 8) != 0) { + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "incorrect magic number"); + return FALSE; + } + + /* format the value */ + self->hwid = ((guint32) buf[9]) << 24 | + ((guint32) buf[8]) << 16 | + ((guint32) buf[11]) << 8 | + ((guint32) buf[10]); + return TRUE; + +} + +static gboolean +fu_wacom_aes_query_operation_mode (FuWacomAesDevice *self, GError **error) +{ + guint8 buf[FU_WACOM_RAW_FW_REPORT_SZ] = { + FU_WACOM_RAW_FW_REPORT_ID, + FU_WACOM_RAW_FW_CMD_QUERY_MODE, + }; + + /* 0x00=runtime, 0x02=bootloader */ + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), buf, sizeof(buf), error)) + return FALSE; + if (buf[1] == 0x00) { + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } + if (buf[1] == 0x02) { + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; + } + + /* unsupported */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "Failed to query operation mode, got 0x%x", + buf[1]); + return FALSE; +} + +static gboolean +fu_wacom_aes_device_setup (FuDevice *device, GError **error) +{ + FuWacomAesDevice *self = FU_WACOM_AES_DEVICE (device); + + /* find out if in bootloader mode already */ + if (!fu_wacom_aes_query_operation_mode (self, error)) + return FALSE; + + /* get firmware version */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version (device, "0.0"); + } else { + guint32 fw_ver; + guint8 data[FU_WACOM_RAW_STATUS_REPORT_SZ] = { + FU_WACOM_RAW_STATUS_REPORT_ID, + 0x0 + }; + g_autofree gchar *version = NULL; + g_autoptr(GError) error_local = NULL; + + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + data, sizeof(data), error)) + return FALSE; + fw_ver = fu_common_read_uint16 (data + 11, G_LITTLE_ENDIAN); + version = g_strdup_printf ("%04x.%02x", fw_ver, data[13]); + fu_device_set_version (device, version); + + /* get the optional 32 byte HWID and add it as a GUID */ + if (!fu_wacom_aes_device_obtain_hwid (self, &error_local)) { + g_debug ("failed to get HwID: %s", error_local->message); + } else { + g_autofree gchar *guid = NULL; + guid = g_strdup_printf ("WACOM\\HWID_%04X", self->hwid); + fu_device_add_guid (device, guid); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_aes_device_erase_all (FuWacomAesDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ALL_ERASE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, + 2000 * 1000, /* this takes a long time */ + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, error)) { + g_prefix_error (error, "failed to send eraseall command: "); + return FALSE; + } + g_usleep (2 * G_USEC_PER_SEC); + return TRUE; +} + +static gboolean +fu_wacom_aes_device_write_block (FuWacomAesDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + guint16 datasz, + GError **error) +{ + guint blocksz = fu_wacom_device_get_block_sz (FU_WACOM_DEVICE (self)); + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_WRITE_FLASH, + .echo = (guint8) idx + 1, + .addr = GUINT32_TO_LE(address), + .size8 = datasz / 8, + .data = { 0x00 }, + }; + FuWacomRawResponse rsp = { 0x00 }; + + /* check size */ + if (datasz != blocksz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "block size 0x%x != 0x%x untested", + datasz, (guint) blocksz); + return FALSE; + } + memcpy (&req.data, data, datasz); + + /* write */ + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 1000, + FU_WACOM_DEVICE_CMD_FLAG_NONE, error)) { + g_prefix_error (error, "failed to write block %u: ", idx); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_aes_device_write_firmware (FuDevice *device, GPtrArray *chunks, GError **error) +{ + FuWacomAesDevice *self = FU_WACOM_AES_DEVICE (device); + + /* erase */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_wacom_aes_device_erase_all (self, error)) + return FALSE; + + /* write */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (!fu_wacom_aes_device_write_block (self, + chk->idx, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + return TRUE; +} + +static void +fu_wacom_aes_device_init (FuWacomAesDevice *self) +{ + fu_device_set_name (FU_DEVICE (self), "Embedded Wacom AES Device"); +} + +static void +fu_wacom_aes_device_class_init (FuWacomAesDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuWacomDeviceClass *klass_wac_device = FU_WACOM_DEVICE_CLASS (klass); + klass_device->setup = fu_wacom_aes_device_setup; + klass_wac_device->write_firmware = fu_wacom_aes_device_write_firmware; +} + +FuWacomAesDevice * +fu_wacom_aes_device_new (FuUdevDevice *device) +{ + FuWacomAesDevice *self = g_object_new (FU_TYPE_WACOM_AES_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/wacom-raw/fu-wacom-aes-device.h b/plugins/wacom-raw/fu-wacom-aes-device.h new file mode 100644 index 000000000..07c61c5b8 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-aes-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_AES_DEVICE_H +#define __FU_WACOM_AES_DEVICE_H + +#include "fu-wacom-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WACOM_AES_DEVICE (fu_wacom_aes_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacomAesDevice, fu_wacom_aes_device, FU, WACOM_AES_DEVICE, FuWacomDevice) + +FuWacomAesDevice *fu_wacom_aes_device_new (FuUdevDevice *device); + +G_END_DECLS + +#endif /* __FU_WACOM_AES_DEVICE_H */ diff --git a/plugins/wacom-raw/fu-wacom-common.c b/plugins/wacom-raw/fu-wacom-common.c new file mode 100644 index 000000000..07fd250a3 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-common.c @@ -0,0 +1,108 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include + +#include "fu-wacom-common.h" + +gboolean +fu_wacom_common_check_reply (const FuWacomRawRequest *req, + const FuWacomRawResponse *rsp, + GError **error) +{ + if (rsp->report_id != FU_WACOM_RAW_BL_REPORT_ID_GET) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "report ID failed, expected 0x%02x, got 0x%02x", + (guint) FU_WACOM_RAW_BL_REPORT_ID_GET, + req->report_id); + return FALSE; + } + if (req->cmd != rsp->cmd) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "cmd failed, expected 0x%02x, got 0x%02x", + req->cmd, rsp->cmd); + return FALSE; + } + if (req->echo != rsp->echo) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "echo failed, expected 0x%02x, got 0x%02x", + req->echo, rsp->echo); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_common_rc_set_error (const FuWacomRawResponse *rsp, GError **error) +{ + if (rsp->resp == FU_WACOM_RAW_RC_OK) + return TRUE; + if (rsp->resp == FU_WACOM_RAW_RC_BUSY) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_BUSY, + "device is busy"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_MCUTYPE) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "MCU type does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_PID) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "PID does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM1) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum1 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_CHECKSUM2) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_INVALID_DATA, + "checksum2 does not match"); + return FALSE; + } + if (rsp->resp == FU_WACOM_RAW_RC_TIMEOUT) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_TIMED_OUT, + "command timed out"); + return FALSE; + } + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "unknown error 0x%02x", rsp->resp); + return FALSE; +} + +gboolean +fu_wacom_common_block_is_empty (const guint8 *data, guint16 datasz) +{ + for (guint16 i = 0; i < datasz; i++) { + if (data[i] != 0xff) + return FALSE; + } + return TRUE; +} diff --git a/plugins/wacom-raw/fu-wacom-common.h b/plugins/wacom-raw/fu-wacom-common.h new file mode 100644 index 000000000..c142c0e48 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-common.h @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_COMMON_H +#define __FU_WACOM_COMMON_H + +#include + +G_BEGIN_DECLS + +#define FU_WACOM_DEVICE_VID 0x056A +#define FU_WACOM_RAW_CMD_RETRIES 1000 + +#define FU_WACOM_RAW_STATUS_REPORT_ID 0x04 +#define FU_WACOM_RAW_STATUS_REPORT_SZ 16 + +#define FU_WACOM_RAW_FW_REPORT_ID 0x02 +#define FU_WACOM_RAW_FW_CMD_QUERY_MODE 0x00 +#define FU_WACOM_RAW_FW_CMD_DETACH 0x02 +#define FU_WACOM_RAW_FW_REPORT_SZ 2 + +#define FU_WACOM_RAW_FW_MAINTAIN_REPORT_ID 0x09 +#define FU_WACOM_RAW_FW_MAINTAIN_REPORT_SZ 64 + +#define FU_WACOM_RAW_BL_REPORT_ID_SET 0x07 +#define FU_WACOM_RAW_BL_REPORT_ID_GET 0x08 + +#define FU_WACOM_RAW_BL_CMD_ERASE_FLASH 0x00 +#define FU_WACOM_RAW_BL_CMD_WRITE_FLASH 0x01 +#define FU_WACOM_RAW_BL_CMD_VERIFY_FLASH 0x02 +#define FU_WACOM_RAW_BL_CMD_ATTACH 0x03 +#define FU_WACOM_RAW_BL_CMD_GET_BLVER 0x04 +#define FU_WACOM_RAW_BL_CMD_GET_MPUTYPE 0x05 +#define FU_WACOM_RAW_BL_CMD_CHECK_MODE 0x07 +#define FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM 0x0e +#define FU_WACOM_RAW_BL_CMD_ALL_ERASE 0x90 + +#define FU_WACOM_RAW_RC_OK 0x00 +#define FU_WACOM_RAW_RC_BUSY 0x80 +#define FU_WACOM_RAW_RC_MCUTYPE 0x0c +#define FU_WACOM_RAW_RC_PID 0x0d +#define FU_WACOM_RAW_RC_CHECKSUM1 0x81 +#define FU_WACOM_RAW_RC_CHECKSUM2 0x82 +#define FU_WACOM_RAW_RC_TIMEOUT 0x87 +#define FU_WACOM_RAW_RC_IN_PROGRESS 0xff + +#define FU_WACOM_RAW_ECHO_DEFAULT g_random_int_range(0xa0,0xfe) + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 cmd; + guint8 echo; + guint32 addr; + guint8 size8; + guint8 data[128]; + guint8 data_unused[121]; +} FuWacomRawRequest; + +typedef struct __attribute__((packed)) { + guint8 report_id; + guint8 cmd; + guint8 echo; + guint8 resp; + guint8 data_unused[132]; +} FuWacomRawResponse; + +gboolean fu_wacom_common_rc_set_error (const FuWacomRawResponse *rsp, + GError **error); +gboolean fu_wacom_common_check_reply (const FuWacomRawRequest *req, + const FuWacomRawResponse *rsp, + GError **error); +gboolean fu_wacom_common_block_is_empty (const guint8 *data, + guint16 datasz); + +G_END_DECLS + +#endif /* __FU_WACOM_COMMON_H */ diff --git a/plugins/wacom-raw/fu-wacom-device.c b/plugins/wacom-raw/fu-wacom-device.c new file mode 100644 index 000000000..ddeb85879 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-device.c @@ -0,0 +1,420 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include +#include +#include + +#include + +#include "fu-chunk.h" +#include "fu-wacom-common.h" +#include "fu-wacom-device.h" +#include "dfu-firmware.h" + +typedef struct +{ + gint fd; + guint flash_block_size; + guint32 flash_base_addr; + guint32 flash_size; +} FuWacomDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE (FuWacomDevice, fu_wacom_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_wacom_device_get_instance_private (o)) + +static void +fu_wacom_device_to_string (FuDevice *device, GString *str) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + g_string_append (str, " FuWacomDevice:\n"); + g_string_append_printf (str, " fd:\t\t\t%i\n", priv->fd); + g_string_append_printf (str, " flash-block-size:\t0x%04x\n", priv->flash_block_size); + g_string_append_printf (str, " flash-base-addr:\t0x%04x\n", priv->flash_base_addr); + g_string_append_printf (str, " flash-size:\t\t0x%04x\n", priv->flash_size); +} + +guint +fu_wacom_device_get_block_sz (FuWacomDevice *self) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + return priv->flash_block_size; +} + +guint +fu_wacom_device_get_base_addr (FuWacomDevice *self) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + return priv->flash_base_addr; +} + +gboolean +fu_wacom_device_check_mpu (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_GET_MPUTYPE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, error)) { + g_prefix_error (error, "failed to get MPU type: "); + return FALSE; + } + + /* W9013 */ + if (rsp.resp == 0x2e) { + fu_device_add_guid (FU_DEVICE (self), "WacomEMR_W9013"); + return TRUE; + } + + /* W9021 */ + if (rsp.resp == 0x45) { + fu_device_add_guid (FU_DEVICE (self), "WacomEMR_W9021"); + return TRUE; + } + + /* unsupported */ + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "MPU is not W9013 or W9021: 0x%x", + rsp.resp); + return FALSE; +} + +static gboolean +fu_wacom_device_open (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + GUdevDevice *udev_device = fu_udev_device_get_dev (FU_UDEV_DEVICE (device)); + + /* open device */ + priv->fd = g_open (g_udev_device_get_device_file (udev_device), O_RDWR); + if (priv->fd < 0) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "failed to open %s", + g_udev_device_get_device_file (udev_device)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_device_close (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + if (!g_close (priv->fd, error)) + return FALSE; + priv->fd = 0; + return TRUE; +} + +static gboolean +fu_wacom_device_probe (FuUdevDevice *device, GError **error) +{ + /* set the physical ID */ + if (!fu_udev_device_set_physical_id (device, "hid", error)) + return FALSE; + return TRUE; +} + +static gboolean +fu_wacom_device_detach (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + guint8 buf[FU_WACOM_RAW_FW_REPORT_SZ] = { + FU_WACOM_RAW_FW_REPORT_ID, + FU_WACOM_RAW_FW_CMD_DETACH, + }; + if (!fu_wacom_device_set_feature (self, buf, sizeof(buf), error)) { + g_prefix_error (error, "failed to switch to bootloader mode: "); + return FALSE; + } + g_usleep (300 * 1000); + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_device_attach (FuDevice *device, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomRawRequest req = { + .report_id = FU_WACOM_RAW_BL_REPORT_ID_SET, + .cmd = FU_WACOM_RAW_BL_CMD_ATTACH, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + if (!fu_wacom_device_set_feature (self, (const guint8 *) &req, sizeof(req), error)) { + g_prefix_error (error, "failed to switch to runtime mode: "); + return FALSE; + } + /* only required on AES, but harmless for EMR */ + g_usleep (300 * 1000); + fu_device_remove_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_wacom_device_check_mode (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_CHECK_MODE, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (self, &req, &rsp, 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, error)) { + g_prefix_error (error, "failed to check mode: "); + return FALSE; + } + if (rsp.resp != 0x06) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "check mode failed, mode=0x%02x", + rsp.resp); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_device_set_version_bootloader (FuWacomDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_GET_BLVER, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + FuWacomRawResponse rsp = { 0x00 }; + g_autofree gchar *version = NULL; + if (!fu_wacom_device_cmd (self, &req, &rsp, 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, error)) { + g_prefix_error (error, "failed to get bootloader version: "); + return FALSE; + } + version = g_strdup_printf ("%u", rsp.resp); + fu_device_set_version_bootloader (FU_DEVICE (self), version); + return TRUE; +} + +static gboolean +fu_wacom_device_write_firmware (FuDevice *device, GBytes *fw, GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS (device); + DfuElement *element; + DfuImage *image; + GBytes *fw_new; + g_autoptr(DfuFirmware) firmware = dfu_firmware_new (); + g_autoptr(GPtrArray) chunks = NULL; + + /* parse hex file */ + if (!dfu_firmware_parse_data (firmware, fw, DFU_FIRMWARE_PARSE_FLAG_NONE, error)) + return FALSE; + if (dfu_firmware_get_format (firmware) != DFU_FIRMWARE_FORMAT_INTEL_HEX) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "expected firmware format is 'ihex', got '%s'", + dfu_firmware_format_to_string (dfu_firmware_get_format (firmware))); + return FALSE; + } + + /* use the correct image from the firmware */ + image = dfu_firmware_get_image_default (firmware); + if (image == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no firmware image"); + return FALSE; + } + element = dfu_image_get_element_default (image); + if (element == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no element in image"); + return FALSE; + } + g_debug ("using element at addr 0x%0x", + (guint) dfu_element_get_address (element)); + + /* check start address and size */ + if (dfu_element_get_address (element) != priv->flash_base_addr) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "base addr invalid: 0x%05x", + (guint) dfu_element_get_address (element)); + return FALSE; + } + fw_new = dfu_element_get_contents (element); + if (g_bytes_get_size (fw_new) > priv->flash_size) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "size is invalid: 0x%05x", + (guint) g_bytes_get_size (fw_new)); + return FALSE; + } + + /* we're in bootloader mode now */ + if (!fu_wacom_device_check_mode (self, error)) + return FALSE; + if (!fu_wacom_device_set_version_bootloader (self, error)) + return FALSE; + + /* flash chunks */ + chunks = fu_chunk_array_new_from_bytes (fw_new, priv->flash_base_addr, + 0x00, /* page_sz */ + priv->flash_block_size); + return klass->write_firmware (device, chunks, error); +} + +gboolean +fu_wacom_device_set_feature (FuWacomDevice *self, + const guint8 *data, + guint datasz, + GError **error) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + + /* Set Feature */ + fu_common_dump_raw (G_LOG_DOMAIN, "SetFeature", data, datasz); + if (ioctl (priv->fd, HIDIOCSFEATURE(datasz), data) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to SetFeature"); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_device_get_feature (FuWacomDevice *self, + guint8 *data, + guint datasz, + GError **error) +{ + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + if (ioctl (priv->fd, HIDIOCGFEATURE(datasz), data) < 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to GetFeature"); + return FALSE; + } + fu_common_dump_raw (G_LOG_DOMAIN, "GetFeature", data, datasz); + return TRUE; +} + +gboolean +fu_wacom_device_cmd (FuWacomDevice *self, + FuWacomRawRequest *req, FuWacomRawResponse *rsp, + gulong delay_us, FuWacomDeviceCmdFlags flags, + GError **error) +{ + req->report_id = FU_WACOM_RAW_BL_REPORT_ID_SET; + if (!fu_wacom_device_set_feature (self, (const guint8 *)req, sizeof(*req), error)) { + g_prefix_error (error, "failed to send: "); + return FALSE; + } + if (delay_us > 0) + g_usleep (delay_us); + rsp->report_id = FU_WACOM_RAW_BL_REPORT_ID_GET; + if (!fu_wacom_device_get_feature (self, (guint8 *)rsp, sizeof(*rsp), error)) { + g_prefix_error (error, "failed to receive: "); + return FALSE; + } + if (flags & FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK) + return TRUE; + if (!fu_wacom_common_check_reply (req, rsp, error)) + return FALSE; + + /* wait for the command to complete */ + if (flags & FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING && + rsp->resp != FU_WACOM_RAW_RC_OK) { + for (guint i = 0; i < FU_WACOM_RAW_CMD_RETRIES; i++) { + if (delay_us > 0) + g_usleep (delay_us); + if (!fu_wacom_device_get_feature (self, (guint8 *)rsp, sizeof(*rsp), error)) + return FALSE; + if (!fu_wacom_common_check_reply (req, rsp, error)) + return FALSE; + if (rsp->resp != FU_WACOM_RAW_RC_IN_PROGRESS && + rsp->resp != FU_WACOM_RAW_RC_BUSY) + break; + } + } + return fu_wacom_common_rc_set_error (rsp, error); +} + +static gboolean +fu_wacom_device_set_quirk_kv (FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuWacomDevice *self = FU_WACOM_DEVICE (device); + FuWacomDevicePrivate *priv = GET_PRIVATE (self); + if (g_strcmp0 (key, "WacomI2cFlashBlockSize") == 0) { + priv->flash_block_size = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "WacomI2cFlashBaseAddr") == 0) { + priv->flash_base_addr = fu_common_strtoull (value); + return TRUE; + } + if (g_strcmp0 (key, "WacomI2cFlashSize") == 0) { + priv->flash_size = fu_common_strtoull (value); + return TRUE; + } + g_set_error_literal (error, + G_IO_ERROR, + G_IO_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_wacom_device_init (FuWacomDevice *self) +{ + fu_device_add_flag (FU_DEVICE (self), FWUPD_DEVICE_FLAG_UPDATABLE); +} + +static void +fu_wacom_device_class_init (FuWacomDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuUdevDeviceClass *klass_device_udev = FU_UDEV_DEVICE_CLASS (klass); + klass_device->to_string = fu_wacom_device_to_string; + klass_device->open = fu_wacom_device_open; + klass_device->close = fu_wacom_device_close; + klass_device->write_firmware = fu_wacom_device_write_firmware; + klass_device->attach = fu_wacom_device_attach; + klass_device->detach = fu_wacom_device_detach; + klass_device->set_quirk_kv = fu_wacom_device_set_quirk_kv; + klass_device_udev->probe = fu_wacom_device_probe; +} diff --git a/plugins/wacom-raw/fu-wacom-device.h b/plugins/wacom-raw/fu-wacom-device.h new file mode 100644 index 000000000..383ceb3b7 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-device.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_DEVICE_H +#define __FU_WACOM_DEVICE_H + +#include "fu-wacom-common.h" +#include "fu-udev-device.h" +#include "dfu-element.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WACOM_DEVICE (fu_wacom_device_get_type ()) +G_DECLARE_DERIVABLE_TYPE (FuWacomDevice, fu_wacom_device, FU, WACOM_DEVICE, FuUdevDevice) + +struct _FuWacomDeviceClass +{ + FuUdevDeviceClass parent_class; + gboolean (*write_firmware) (FuDevice *self, + GPtrArray *chunks, + GError **error); +}; + +typedef enum { + FU_WACOM_DEVICE_CMD_FLAG_NONE = 0, + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING = 1 << 0, + FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK = 1 << 1, +} FuWacomDeviceCmdFlags; + +gboolean fu_wacom_device_set_feature (FuWacomDevice *self, + const guint8 *data, + guint datasz, + GError **error); +gboolean fu_wacom_device_get_feature (FuWacomDevice *self, + guint8 *data, + guint datasz, + GError **error); +gboolean fu_wacom_device_cmd (FuWacomDevice *self, + FuWacomRawRequest *req, + FuWacomRawResponse *rsp, + gulong delay_us, + FuWacomDeviceCmdFlags flags, + GError **error); +gboolean fu_wacom_device_erase_all (FuWacomDevice *self, + GError **error); +gboolean fu_wacom_device_check_mpu (FuWacomDevice *self, + GError **error); +guint fu_wacom_device_get_block_sz (FuWacomDevice *self); +guint fu_wacom_device_get_base_addr (FuWacomDevice *self); + +G_END_DECLS + +#endif /* __FU_WACOM_DEVICE_H */ diff --git a/plugins/wacom-raw/fu-wacom-emr-device.c b/plugins/wacom-raw/fu-wacom-emr-device.c new file mode 100644 index 000000000..b554c4640 --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-emr-device.c @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#include "config.h" + +#include +#include + +#include "fu-chunk.h" +#include "fu-wacom-common.h" +#include "fu-wacom-emr-device.h" + +struct _FuWacomEmrDevice { + FuWacomDevice parent_instance; +}; + +G_DEFINE_TYPE (FuWacomEmrDevice, fu_wacom_emr_device, FU_TYPE_WACOM_DEVICE) + +static gboolean +fu_wacom_emr_device_setup (FuDevice *device, GError **error) +{ + FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE (device); + + /* check MPU type */ + if (!fu_wacom_device_check_mpu (FU_WACOM_DEVICE (self), error)) + return FALSE; + + /* get firmware version */ + if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version (device, "0.0"); + } else { + guint16 fw_ver; + guint8 data[19] = { 0x03, 0x0 }; /* 0x03 is an unknown ReportID */ + g_autofree gchar *version = NULL; + if (!fu_wacom_device_get_feature (FU_WACOM_DEVICE (self), + data, sizeof(data), error)) + return FALSE; + fw_ver = fu_common_read_uint16 (data + 11, G_LITTLE_ENDIAN); + fu_device_remove_flag (device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + version = fu_common_version_from_uint16 (fw_ver, FU_VERSION_FORMAT_PAIR); + fu_device_set_version (device, version); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_wacom_emr_device_calc_checksum (guint8 init1, const guint8 *buf, guint8 bufsz) +{ + guint8 sum = 0; + sum += init1; + for (guint i = 0; i < bufsz; i++) + sum += buf[i]; + return ~sum + 1; +} + +static gboolean +fu_wacom_emr_device_w9013_erase_data (FuWacomEmrDevice *self, GError **error) +{ + FuWacomRawResponse rsp = { 0x00 }; + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM, + .echo = FU_WACOM_RAW_ECHO_DEFAULT, + 0x00 + }; + guint8 *buf = (guint8 *) &req.addr; + buf[0] = 0x00; /* erased block */ + buf[1] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *) &req, 4); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, error)) { + g_prefix_error (error, "failed to erase datamem: "); + return FALSE; + } + g_usleep (50); + return TRUE; +} + +static gboolean +fu_wacom_emr_device_w9013_erase_code (FuWacomEmrDevice *self, + guint8 idx, + guint8 block_nr, + GError **error) +{ + FuWacomRawResponse rsp = { 0x00 }; + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ERASE_FLASH, + .echo = idx, + 0x00 + }; + guint8 *buf = (guint8 *) &req.addr; + buf[0] = block_nr; + buf[1] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *) &req, 4); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, error)) { + g_prefix_error (error, "failed to erase codemem: "); + return FALSE; + } + g_usleep (50); + return TRUE; +} + +static gboolean +fu_wacom_device_w9021_erase_all (FuWacomEmrDevice *self, GError **error) +{ + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_ALL_ERASE, + .echo = 0x01, + .addr = 0x00, + }; + FuWacomRawResponse rsp = { 0x00 }; + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, + 2000 * 1000, /* this takes a long time */ + FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, error)) { + g_prefix_error (error, "failed to send eraseall command: "); + return FALSE; + } + if (!fu_wacom_common_rc_set_error (&rsp, error)) { + g_prefix_error (error, "failed to erase"); + return FALSE; + } + g_usleep (50); + return TRUE; +} + +static gboolean +fu_wacom_emr_device_write_block (FuWacomEmrDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + guint16 datasz, + GError **error) +{ + guint blocksz = fu_wacom_device_get_block_sz (FU_WACOM_DEVICE (self)); + FuWacomRawRequest req = { + .cmd = FU_WACOM_RAW_BL_CMD_WRITE_FLASH, + .echo = (guint8) idx + 1, + .addr = GUINT32_TO_LE(address), + .size8 = datasz / 8, + .data = { 0x00 }, + }; + FuWacomRawResponse rsp = { 0x00 }; + + /* check size */ + if (datasz > sizeof(req.data)) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "data size 0x%x too large for packet", + datasz); + return FALSE; + } + if (datasz != blocksz) { + g_set_error (error, + G_IO_ERROR, + G_IO_ERROR_FAILED, + "block size 0x%x != 0x%x untested", + datasz, (guint) blocksz); + return FALSE; + } + + /* data */ + memcpy (&req.data, data, datasz); + + /* cmd and data checksums */ + req.data[blocksz + 0] = fu_wacom_emr_device_calc_checksum (0x05 + 0x00 + 0x4c + 0x00, + (const guint8 *) &req, 8); + req.data[blocksz + 1] = fu_wacom_emr_device_calc_checksum (0x00, data, datasz); + if (!fu_wacom_device_cmd (FU_WACOM_DEVICE (self), &req, &rsp, 50, + FU_WACOM_DEVICE_CMD_FLAG_NONE, error)) { + g_prefix_error (error, "failed to write at 0x%x: ", address); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_emr_device_write_firmware (FuDevice *device, GPtrArray *chunks, GError **error) +{ + FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE (device); + guint8 idx = 0; + + /* erase W9013 */ + if (fu_device_has_guid (device, "WacomEMR_W9013")) { + fu_device_set_status (device, FWUPD_STATUS_DEVICE_ERASE); + if (!fu_wacom_emr_device_w9013_erase_data (self, error)) + return FALSE; + for (guint i = 127; i >= 8; i--) { + if (!fu_wacom_emr_device_w9013_erase_code (self, idx++, i, error)) + return FALSE; + } + } + + /* erase W9021 */ + if (fu_device_has_guid (device, "WacomEMR_W9021")) { + if (!fu_wacom_device_w9021_erase_all (self, error)) + return FALSE; + } + + /* write */ + fu_device_set_status (device, FWUPD_STATUS_DEVICE_WRITE); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index (chunks, i); + if (fu_wacom_common_block_is_empty (chk->data, chk->data_sz)) + continue; + if (!fu_wacom_emr_device_write_block (self, + chk->idx, + chk->address, + chk->data, + chk->data_sz, + error)) + return FALSE; + fu_device_set_progress_full (device, (gsize) i, (gsize) chunks->len); + } + + fu_device_set_progress (device, 100); + return TRUE; +} + +static void +fu_wacom_emr_device_init (FuWacomEmrDevice *self) +{ + fu_device_set_name (FU_DEVICE (self), "Embedded Wacom EMR Device"); +} + +static void +fu_wacom_emr_device_class_init (FuWacomEmrDeviceClass *klass) +{ + FuDeviceClass *klass_device = FU_DEVICE_CLASS (klass); + FuWacomDeviceClass *klass_wac_device = FU_WACOM_DEVICE_CLASS (klass); + klass_device->setup = fu_wacom_emr_device_setup; + klass_wac_device->write_firmware = fu_wacom_emr_device_write_firmware; +} + +FuWacomEmrDevice * +fu_wacom_emr_device_new (FuUdevDevice *device) +{ + FuWacomEmrDevice *self = g_object_new (FU_TYPE_WACOM_EMR_DEVICE, NULL); + fu_device_incorporate (FU_DEVICE (self), FU_DEVICE (device)); + return self; +} diff --git a/plugins/wacom-raw/fu-wacom-emr-device.h b/plugins/wacom-raw/fu-wacom-emr-device.h new file mode 100644 index 000000000..6a7cc35be --- /dev/null +++ b/plugins/wacom-raw/fu-wacom-emr-device.h @@ -0,0 +1,21 @@ +/* + * Copyright (C) 2018-2019 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1+ + */ + +#ifndef __FU_WACOM_EMR_DEVICE_H +#define __FU_WACOM_EMR_DEVICE_H + +#include "fu-wacom-device.h" + +G_BEGIN_DECLS + +#define FU_TYPE_WACOM_EMR_DEVICE (fu_wacom_emr_device_get_type ()) +G_DECLARE_FINAL_TYPE (FuWacomEmrDevice, fu_wacom_emr_device, FU, WACOM_EMR_DEVICE, FuUdevDevice) + +FuWacomEmrDevice *fu_wacom_emr_device_new (FuUdevDevice *device); + +G_END_DECLS + +#endif /* __FU_WACOM_EMR_DEVICE_H */ diff --git a/plugins/wacom-raw/meson.build b/plugins/wacom-raw/meson.build new file mode 100644 index 000000000..ec0b63f70 --- /dev/null +++ b/plugins/wacom-raw/meson.build @@ -0,0 +1,31 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginWacomRaw"'] + +install_data(['wacom-raw.quirk'], + install_dir: join_paths(datadir, 'fwupd', 'quirks.d') +) + +shared_module('fu_plugin_wacom_raw', + fu_hash, + sources : [ + 'fu-plugin-wacom-raw.c', + 'fu-wacom-common.c', + 'fu-wacom-device.c', + 'fu-wacom-aes-device.c', + 'fu-wacom-emr-device.c', + ], + include_directories : [ + include_directories('../..'), + include_directories('../dfu'), + include_directories('../../src'), + include_directories('../../libfwupd'), + ], + install : true, + install_dir: plugin_dir, + c_args : cargs, + dependencies : [ + plugin_deps, + ], + link_with : [ + dfu, + ], +) diff --git a/plugins/wacom-raw/wacom-raw.quirk b/plugins/wacom-raw/wacom-raw.quirk new file mode 100644 index 000000000..51ceee1a8 --- /dev/null +++ b/plugins/wacom-raw/wacom-raw.quirk @@ -0,0 +1,35 @@ +# Devices that do "replug" and thus don't change VID:PID to the bootloader +# need to have an extra GUID of WacomAES or WacomEMR added so that the flash +# constants are set correctly. + +# Dell XPS-15 9575 +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_4875] +Plugin = wacom_raw +Guid = WacomAES + +# AES bootloader mode +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_0094] +Plugin = wacom_raw +Guid = WacomAES +Flags = is-bootloader + +# EMR bootloader mode +[DeviceInstanceId=HIDRAW\VEN_056A&DEV_012B] +Plugin = wacom_raw +Guid = WacomEMR +Flags = is-bootloader + +[Guid=WacomEMR_W9013] +WacomI2cFlashBlockSize=64 +WacomI2cFlashBaseAddr=0x2000 +WacomI2cFlashSize=0x1e000 + +[Guid=WacomEMR_W9021] +WacomI2cFlashBlockSize=256 +WacomI2cFlashBaseAddr=0x3000 +WacomI2cFlashSize=0x3c000 + +[Guid=WacomAES] +WacomI2cFlashBlockSize=128 +WacomI2cFlashBaseAddr=0x8000 +WacomI2cFlashSize=0x24000