wacom-raw: Add a plugin to update Wacom embedded EMR and AES panels

This commit is contained in:
Richard Hughes 2018-11-22 10:19:05 +00:00
parent 3f243a9e9e
commit 367f4590d6
15 changed files with 1857 additions and 0 deletions

View File

@ -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}

View File

@ -12,6 +12,7 @@ subdir('test')
subdir('udev')
subdir('unifying')
subdir('upower')
subdir('wacom-raw')
subdir('wacom-usb')
subdir('superio')

View File

@ -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 |

View File

@ -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

View File

@ -0,0 +1,95 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* 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);
}

View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include <gio/gio.h>
#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;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* 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 */

View File

@ -0,0 +1,108 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <gio/gio.h>
#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;
}

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#ifndef __FU_WACOM_COMMON_H
#define __FU_WACOM_COMMON_H
#include <glib-object.h>
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 */

View File

@ -0,0 +1,420 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <fcntl.h>
#include <string.h>
#include <linux/hidraw.h>
#include <sys/ioctl.h>
#include <glib/gstdio.h>
#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;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* 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 */

View File

@ -0,0 +1,246 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* SPDX-License-Identifier: LGPL-2.1+
*/
#include "config.h"
#include <string.h>
#include <gio/gio.h>
#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;
}

View File

@ -0,0 +1,21 @@
/*
* Copyright (C) 2018-2019 Richard Hughes <richard@hughsie.com>
*
* 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 */

View File

@ -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,
],
)

View File

@ -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