1# Copyright 2021 The ChromiumOS Authors 2# Use of this source code is governed by a BSD-style license that can be 3# found in the LICENSE file. 4"""Plugin for merging HWID information into ConfigBundle format""" 5 6import copy 7import logging as _logging 8import re 9import yaml 10 11from chromiumos.config.api.component_pb2 import Component 12from chromiumos.config.payload.config_bundle_pb2 import ConfigBundle 13 14from common import config_bundle_utils as cbu 15from .merge_plugin import MergePlugin 16 17# Create named logger 18logging = _logging.getLogger(__name__) 19 20 21def _include_item(item): 22 """Predicate to determine whether to include a HWID item. 23 24 Modify this to add additional exclusion conditions. 25 """ 26 27 # Status values that indicate we should exclude the item 28 bad_status_values = set([]) 29 30 if not item['values']: 31 return False 32 33 if item.get('status', '').lower() in bad_status_values: 34 return False 35 36 return True 37 38 39def _set_support_status(component, item): 40 """Set the supported_status field of a component from its status.""" 41 42 status = item.get('status') 43 if not status: 44 return component 45 46 status_map = { 47 'deprecated': component.SupportStatus.STATUS_DEPRECATED, 48 'supported': component.SupportStatus.STATUS_SUPPORTED, 49 'unqualified': component.SupportStatus.STATUS_UNQUALIFIED, 50 'unsupported': component.SupportStatus.STATUS_UNSUPPORTED, 51 } 52 53 component.support_status = status_map.get( 54 status.lower(), 55 component.SupportStatus.STATUS_UNKNOWN, 56 ) 57 58 return component 59 60 61def _maybe_delete(val, key): 62 """Delete key from dict, but degrade to noop if it's not present.""" 63 if key in val: 64 del val[key] 65 66 67def _del_if_empty(val, key): 68 """Delete a key from a dict if it's empty""" 69 if key in val and not val[key]: 70 del val[key] 71 72 73def _non_null_items(items): 74 """Unwrap a HWID item block into a dictionary for non-null values. 75 76 a HWID item block looks like: 77 items: 78 storage_device: 79 status: unsupported 80 values: 81 class: '0x010101' 82 device: '0xff00' 83 sectors: '5000000' 84 vendor: '0xbeef' 85 some_hardware: 86 values: 87 FAKE_RAM_CHIP: 88 values: 89 class: '0x010101' 90 device: '0xff00' 91 sectors: '250000000' 92 vendor: '0xabcd' 93 94 We'll iterate over the items and check whether it should be excluded 95 based on _include_item. 96 97 The resulting output is a dict with unwrapped item values: 98 { 99 'storage_device': {'class' : '0x010101', ...}, 100 ... 101 } 102 """ 103 return {key: item for key, item in items.items() if _include_item(item)} 104 105 106class MergeHwid(MergePlugin): 107 """Merge plugin for HWID files. 108 109 After calling merge(), residual() can be called to get an object containing 110 any remaining values in the HWID data. 111 """ 112 113 def __init__(self, hwid_path=None, hwid_data=None): 114 """Create a new HWID merger. 115 116 Args: 117 hwid_path (str): Path to the HWID file on disk to read 118 hwid_data (dict): HWID data specified directly 119 """ 120 121 if hwid_path and hwid_data: 122 raise RuntimeError('Only one of hwid_path or hwid_data can be specified') 123 124 if hwid_path: 125 with open(hwid_path, 'r', encoding='utf-8') as hwid_file: 126 self.data = yaml.load(hwid_file, Loader=yaml.SafeLoader) 127 else: 128 self.data = copy.deepcopy(hwid_data) 129 130 # We're not interested in the details of the HWID encoding so remove those 131 # fields from the residual 132 _maybe_delete(self.data, 'pattern') 133 _maybe_delete(self.data, 'encoded_fields') 134 _maybe_delete(self.data, 'encoding_patterns') 135 136 # And remove any other fields that would just be residual noise 137 _maybe_delete(self.data, 'checksum') 138 _maybe_delete(self.data, 'image_id') 139 _maybe_delete(self.data, 'rules') 140 _maybe_delete(self.data, 'project') 141 142 def residual(self): 143 """Get any remaining data that hasn't been parsed.""" 144 return self.data 145 146 def merge(self, bundle: ConfigBundle): 147 """Merge our data into the given ConfigBundle instance.""" 148 components = self.data['components'] 149 for component_type in list(components.keys()): 150 value = components[component_type] 151 if not value: 152 _del_if_empty(components, component_type) 153 continue 154 155 # yapf: disable 156 callback = { 157 'audio_codec': MergeHwid._merge_audio, 158 'battery': MergeHwid._merge_battery, 159 'bluetooth': MergeHwid._merge_bluetooth, 160 'cellular': MergeHwid._merge_cellular, 161 'cpu': MergeHwid._merge_cpu, 162 'display_panel': MergeHwid._merge_display_panel, 163 'dram': MergeHwid._merge_dram, 164 'ec_flash_chip': MergeHwid._merge_ec_flash, 165 'embedded_controller': MergeHwid._merge_ec, 166 'flash_chip': MergeHwid._merge_flash, 167 'storage': MergeHwid._merge_storage, 168 'stylus': MergeHwid._merge_stylus, 169 'touchpad': MergeHwid._merge_touchpad, 170 'tpm': MergeHwid._merge_tpm, 171 'touchscreen': MergeHwid._merge_touchscreen, 172 'usb_hosts': MergeHwid._merge_usb_hosts, 173 'video': MergeHwid._merge_video, 174 'wireless': MergeHwid._merge_wireless, 175 }.get(component_type) 176 # yapf: enable 177 178 if callback: 179 value['items'] = _non_null_items(value['items']) 180 MergeHwid._iterate_items(bundle, value['items'], callback) 181 182 _del_if_empty(value, 'items') 183 _del_if_empty(components, component_type) 184 185 _del_if_empty(self.data, 'components') 186 187 @staticmethod 188 def _iterate_items(bundle, items, callback): 189 """Iterate items in an item dict and call callback on them. 190 191 Handle boilerplate for callbacks to delete empty/touched 192 fields as we go to leave an informative residual. 193 194 Callbacks must take a ConfigBundle and an item and return 195 an iterable of fields in the item that were touched/used. 196 197 Args: 198 bundle: ConfigBundle instance to modify 199 items: dict of label => [item] 200 callback: callback to process a single item. 201 """ 202 203 # Copy keys list so that we can remove from items as we go. 204 for label in list(items.keys()): 205 item = items[label] 206 touched = callback(bundle, label, item) 207 208 values = item.get('values', {}) 209 for name in touched: 210 _maybe_delete(values, name) 211 212 _del_if_empty(item, 'values') 213 _del_if_empty(items, label) 214 215 @staticmethod 216 def _merge_audio(bundle, label, item): 217 """Merge audio_codec items.""" 218 values = item.get('values', {}) 219 220 component = cbu.find_component(bundle, id_value=label, create=True) 221 component = _set_support_status(component, item) 222 223 # save HWID values 224 component.hwid_type = 'audio_codec' 225 component.hwid_label = label 226 227 # configure component 228 component.name = values.get('name', label) 229 component.audio_codec.name = component.name 230 231 return ['name'] 232 233 @staticmethod 234 def _merge_battery(bundle, label, item): 235 """Merge battery items.""" 236 values = item.get('values', {}) 237 238 touched = set() 239 240 component = cbu.find_component(bundle, id_value=label, create=True) 241 component = _set_support_status(component, item) 242 component.name = component.id.value 243 244 # save HWID values 245 component.hwid_type = 'battery' 246 component.hwid_label = label 247 248 # lookup or create manufacturer 249 if 'manufacturer' in values: 250 # We seem to have some bad manufacturer values in the battery component. 251 # If we can't decode them to a valid unicode string, skip them. 252 mfgr = values['manufacturer'] 253 if isinstance(mfgr, bytes): 254 try: 255 mfgr = mfgr.decode() 256 except UnicodeDecodeError as exception: 257 logging.error("Error decoding '%s' to string: %s", mfgr, exception) 258 return set() 259 260 component.manufacturer_id.MergeFrom( 261 cbu.find_partner(bundle, mfgr, create=True).id) 262 touched.add('manufacturer') 263 264 # set model 265 if 'model_name' in values: 266 component.battery.model = values['model_name'] 267 touched.add('model_name') 268 269 # set battery technology 270 if 'technology' in values: 271 tech = values['technology'].lower().replace('-', '') 272 273 tech_map = { 274 'liion': Component.Battery.LI_ION, 275 'lipoly': Component.Battery.LI_POLY, 276 } 277 278 if tech in tech_map: 279 component.battery.technology = tech_map[tech] 280 touched.add('technology') 281 282 return touched 283 284 @staticmethod 285 def _merge_bluetooth(bundle, label, item): 286 """Merge bluetooth items.""" 287 values = item.get('values', {}) 288 289 touched = set() 290 291 component = cbu.find_component(bundle, id_value=label, create=True) 292 component = _set_support_status(component, item) 293 component.name = component.id.value 294 295 # save HWID values 296 component.hwid_type = 'bluetooth' 297 component.hwid_label = label 298 299 # lookup or create manufacturer 300 if 'manufacturer' in values: 301 component.manufacturer_id.MergeFrom( 302 cbu.find_partner(bundle, values['manufacturer'], create=True).id) 303 touched.add('manufacturer') 304 305 fields = [ 306 ('vendor_id', 'idVendor'), 307 ('product_id', 'idProduct'), 308 ('bcd_device', 'bcdDevice'), 309 ] 310 311 for attr, key in fields: 312 if key in values: 313 setattr(component.bluetooth.usb, attr, values[key]) 314 touched.add(key) 315 316 return touched 317 318 @staticmethod 319 def _merge_cellular(bundle, label, item): 320 """Merge cellular items.""" 321 values = item.get('values', {}) 322 323 touched = set() 324 325 component = cbu.find_component(bundle, id_value=label, create=True) 326 component = _set_support_status(component, item) 327 component.name = component.id.value 328 329 # save HWID values 330 component.hwid_type = 'cellular' 331 component.hwid_label = label 332 333 # lookup or create manufacturer 334 if 'manufacturer' in values: 335 component.manufacturer_id.MergeFrom( 336 cbu.find_partner(bundle, values['manufacturer'], create=True).id) 337 touched.add('manufacturer') 338 339 fields = [ 340 ('vendor_id', 'idVendor'), 341 ('product_id', 'idProduct'), 342 ('bcd_device', 'bcdDevice'), 343 ] 344 345 for attr, key in fields: 346 if key in values: 347 setattr(component.cellular.usb, attr, values[key]) 348 touched.add(key) 349 350 return touched 351 352 @staticmethod 353 def _merge_cpu(bundle, label, item): 354 """Merge cpu items.""" 355 values = item.get('values', {}) 356 357 touched = set() 358 359 component = cbu.find_component(bundle, id_value=label, create=True) 360 component = _set_support_status(component, item) 361 component.name = component.id.value 362 363 # save HWID values 364 component.hwid_type = 'cpu' 365 component.hwid_label = label 366 367 model_re = re.compile( # reversed from HWID cpu model values 368 '(a[0-9]?-[0-9]+[a-z]?|(m3-|i3-|i5-|i7-)*[0-9y]{4,5}[uy]?|n[0-9]{4})') 369 370 component.soc.cores = int(values.get('cores', 0)) 371 touched.add('cores') 372 373 if 'model' in values: 374 component.soc.model = values['model'] 375 touched.add('model') 376 377 model_string = values['model'].lower() 378 if 'intel' in model_string or 'amd' in model_string: 379 component.soc.family.arch = component.soc.X86_64 380 elif 'aarch64' in model_string or 'armv8' in model_string: 381 component.soc.family.arch = component.soc.ARM64 382 elif 'armv7' in model_string: 383 component.soc.family.arch = component.soc.ARM 384 else: 385 logging.warning('unknown family for cpu model \'%s\'', model_string) 386 387 match = model_re.search(model_string) 388 if match: 389 component.soc.family.name = match.group(0).upper() 390 391 return touched 392 393 @staticmethod 394 def _merge_display_panel(bundle, label, item): 395 """Merge display panel items.""" 396 values = item.get('values', {}) 397 398 component = cbu.find_component(bundle, id_value=label, create=True) 399 component = _set_support_status(component, item) 400 component.name = component.id.value 401 402 # save HWID values 403 component.hwid_type = 'display_panel' 404 component.hwid_label = label 405 406 if 'vendor' in values: 407 component.manufacturer_id.MergeFrom( 408 cbu.find_partner(bundle, values['vendor'], create=True).id) 409 410 component.display_panel.product_id = values.get('product_id', '') 411 component.display_panel.properties.width_px = int(values.get('width', 0)) 412 component.display_panel.properties.height_px = int(values.get('height', 0)) 413 414 return set(['vendor', 'product_id', 'width', 'height']) 415 416 @staticmethod 417 def _merge_dram(bundle, label, item): 418 """Merge dram items.""" 419 values = item.get('values', {}) 420 421 component = cbu.find_component(bundle, id_value=label, create=True) 422 component = _set_support_status(component, item) 423 component.name = component.id.value 424 425 # save HWID values 426 component.hwid_type = 'dram' 427 component.hwid_label = label 428 429 component.memory.part_number = values.get('part', '') 430 component.memory.profile.size_megabytes = int(values.get('size', 0)) 431 432 if 'timing' in values: 433 memory_type = values['timing'].split('-')[0] 434 if memory_type == 'LPDDR4': 435 component.memory.profile.type = component.memory.LP_DDR4 436 elif memory_type == 'LPDDR3': 437 component.memory.profile.type = component.memory.LP_DDR3 438 elif memory_type == 'DDR4': 439 component.memory.profile.type = component.memory.DDR4 440 elif memory_type == 'DDR3': 441 component.memory.profile.type = component.memory.DDR3 442 elif memory_type == 'DDR2': 443 component.memory.profile.type = component.memory.DDR2 444 elif memory_type == 'DDR': 445 component.memory.profile.type = component.memory.DDR 446 447 return set(['part', 'size', 'timing']) 448 449 @staticmethod 450 def _merge_ec_flash(bundle, label, item): 451 """Merge EC flash items.""" 452 values = item.get('values', {}) 453 454 touched = set() 455 456 component = cbu.find_component(bundle, id_value=label, create=True) 457 component = _set_support_status(component, item) 458 component.name = component.id.value 459 460 # save HWID values 461 component.hwid_type = 'ec_flash_chip' 462 component.hwid_label = label 463 464 component.ec_flash_chip.part_number = values.get('name', '') 465 touched.add('name') 466 467 if 'vendor' in values: 468 component.manufacturer_id.MergeFrom( 469 cbu.find_partner(bundle, values['vendor'], create=True).id) 470 touched.add('vendor') 471 472 return touched 473 474 @staticmethod 475 def _merge_ec(bundle, label, item): 476 """Merge EC items.""" 477 values = item.get('values', {}) 478 479 touched = set() 480 481 component = cbu.find_component(bundle, id_value=label, create=True) 482 component = _set_support_status(component, item) 483 component.name = component.id.value 484 485 # save HWID values 486 component.hwid_type = 'embedded_controller' 487 component.hwid_label = label 488 489 component.ec.part_number = values.get('name', '') 490 touched.add('name') 491 492 if 'vendor' in values: 493 component.manufacturer_id.MergeFrom( 494 cbu.find_partner(bundle, values['vendor'], create=True).id) 495 touched.add('vendor') 496 497 return touched 498 499 @staticmethod 500 def _merge_flash(bundle, label, item): 501 """Merge flash items.""" 502 values = item.get('values', {}) 503 504 touched = set() 505 506 component = cbu.find_component(bundle, id_value=label, create=True) 507 component = _set_support_status(component, item) 508 component.name = component.id.value 509 510 # save HWID values 511 component.hwid_type = 'flash_chip' 512 component.hwid_label = label 513 514 component.system_flash_chip.part_number = values.get('name', '') 515 touched.add('name') 516 517 if 'vendor' in values: 518 component.manufacturer_id.MergeFrom( 519 cbu.find_partner(bundle, values['vendor'], create=True).id) 520 touched.add('vendor') 521 522 return touched 523 524 @staticmethod 525 def _merge_storage(bundle, label, item): 526 """Merge storage items.""" 527 values = item.get('values', {}) 528 529 touched = set() 530 531 component = cbu.find_component(bundle, id_value=label, create=True) 532 component = _set_support_status(component, item) 533 component.name = values.get('model', label) 534 touched.add('model') 535 536 # save HWID values 537 component.hwid_type = 'storage' 538 component.hwid_label = label 539 540 component.storage.emmc5_fw_ver = values.get('emmc5_fw_ver', '') 541 component.storage.manfid = values.get('manfid', '') 542 component.storage.name = values.get('name', '') 543 component.storage.oemid = values.get('oemid', '') 544 component.storage.prv = values.get('prv', '') 545 component.storage.sectors = values.get('sectors', '') 546 547 touched.update( 548 ['emmc5_fw_ver', 'manfid', 'name', 'oemid', 'prv', 'sectors']) 549 550 pcie_fields = ['class', 'device', 'vendor'] 551 if all(field in values for field in pcie_fields): 552 component.storage.type = component.storage.NVME 553 component.storage.pci.vendor_id = values['vendor'] 554 component.storage.pci.device_id = values['device'] 555 component.storage.pci.class_id = values['class'] 556 touched.update(pcie_fields) 557 558 if values.get('type', '').lower() == 'mmc': 559 component.storage.type = component.storage.EMMC 560 touched.add('type') 561 562 if values.get('vendor', '').lower() == 'ata': 563 component.storage.type = component.storage.SATA 564 touched.add('vendor') 565 566 return touched 567 568 @staticmethod 569 def _merge_stylus(bundle, label, item): 570 """Merge stylus items.""" 571 values = item.get('values', {}) 572 573 touched = set() 574 575 component = cbu.find_component(bundle, id_value=label, create=True) 576 component = _set_support_status(component, item) 577 component.name = values.get('name', label) 578 touched.add('name') 579 580 # save HWID values 581 component.hwid_type = 'stylus' 582 component.hwid_label = label 583 584 i2c_keys = ['product', 'vendor'] 585 if all(key in values for key in i2c_keys): 586 component.stylus.i2c.product = values['product'] 587 component.stylus.i2c.vendor = values['vendor'] 588 touched.update(i2c_keys) 589 590 usb_keys = ['product_id', 'vendor_id'] 591 if all(key in values for key in usb_keys): 592 component.stylus.usb.product_id = values['product_id'] 593 component.stylus.usb.vendor_id = values['vendor_id'] 594 touched.update(usb_keys) 595 596 if 'bcd_device' in values: 597 component.stylus.usb.bcd_device = values['bcd_device'] 598 touched.add('bcd_device') 599 600 if 'version' in values: 601 component.stylus.usb.bcd_device = values['version'] 602 touched.add('version') 603 604 return touched 605 606 @staticmethod 607 def _merge_touchpad(bundle, label, item): 608 """Merge touchpad items.""" 609 values = item.get('values', {}) 610 611 touched = set() 612 613 component = cbu.find_component(bundle, id_value=label, create=True) 614 component = _set_support_status(component, item) 615 component.name = values.get('model', label) 616 touched.add('model') 617 618 # save HWID values 619 component.hwid_type = 'touchpad' 620 component.hwid_label = label 621 622 # Check for USB based touchpad 623 # We don't receive an explicit type for the touchpad bus type, so 624 # we assume that if we have a product and vendor id, that it's USB, 625 # otherwise it's I2C (rare) 626 if 'product' in values and 'vendor' in values: 627 component.touchpad.type = component.touchpad.USB 628 component.touchpad.product_id = component.name 629 component.touchpad.usb.vendor_id = values['vendor'] 630 component.touchpad.usb.product_id = values['product'] 631 touched.update(['vendor', 'product']) 632 633 elif 'fw_version' in values and 'fw_csum' in values: 634 # i2c based touchpad 635 component.touchpad.type = component.touchpad.I2C 636 component.touchpad.product_id = values.get('product_id', '') 637 component.touchpad.fw_version = values['fw_version'] 638 component.touchpad.fw_checksum = values['fw_csum'] 639 touched.update(['fw_version', 'fw_csum', 'product_id']) 640 641 return touched 642 643 @staticmethod 644 def _merge_tpm(bundle, label, item): 645 """Merge tpm items.""" 646 values = item.get('values', {}) 647 648 touched = set() 649 650 component = cbu.find_component(bundle, id_value=label, create=True) 651 component = _set_support_status(component, item) 652 component.name = label 653 654 # save HWID values 655 component.hwid_type = 'tpm' 656 component.hwid_label = label 657 658 component.tpm.manufacturer_info = values.get('manufacturer_info', '') 659 component.tpm.version = values.get('version', '') 660 661 touched.update(['manufacturer_info', 'version']) 662 return touched 663 664 @staticmethod 665 def _merge_touchscreen(bundle, label, item): 666 """Merge touchscreen items.""" 667 values = item.get('values', {}) 668 669 touched = set() 670 671 component = cbu.find_component(bundle, id_value=label, create=True) 672 component = _set_support_status(component, item) 673 component.name = values.get('name', label) 674 touched.add('name') 675 676 # save HWID values 677 component.hwid_type = 'touchscreen' 678 component.hwid_label = label 679 680 def oneof(values, keys, default=""): 681 for key in keys: 682 if key in values: 683 return values[key] 684 return default 685 686 component.touchscreen.product_id = label 687 component.touchscreen.usb.product_id = oneof( 688 values, 689 ['product', 'product_id'], 690 ) 691 component.touchscreen.usb.vendor_id = oneof(values, ['vendor', 'vendor_id']) 692 component.touchscreen.usb.bcd_device = values.get('bcd_device', '') 693 694 if all([ 695 component.touchscreen.usb.product_id, 696 component.touchscreen.usb.vendor_id 697 ]): 698 component.touchscreen.type = component.touchscreen.USB 699 700 touched.update( 701 ['product', 'product_id', 'vendor', 'vendor_id', 'bcd_device']) 702 return touched 703 704 @staticmethod 705 def _merge_usb_hosts(bundle, label, item): 706 """Merge USB host items.""" 707 values = item.get('values', {}) 708 709 touched = set() 710 711 component = cbu.find_component(bundle, id_value=label, create=True) 712 component = _set_support_status(component, item) 713 component.name = values.get('product', label) 714 touched.add('product') 715 716 # save HWID values 717 component.hwid_type = 'usb_hosts' 718 component.hwid_label = label 719 720 def get_oneof(obj, keys, default=None): 721 """Get one of a set of keys from values, or return a default value.""" 722 for key in keys: 723 if key in obj: 724 touched.add(key) 725 return obj[key] 726 return default 727 728 if 'manufacturer' in values: 729 component.manufacturer_id.MergeFrom( 730 cbu.find_partner(bundle, values['manufacturer'], create=True).id) 731 touched.add('manufacturer') 732 733 host = component.usb_host 734 host.product_id = get_oneof(values, ['idProduct', 'device'], '') 735 host.vendor_id = get_oneof(values, ['idVendor', 'vendor'], '') 736 host.bcd_device = get_oneof(values, ['bcdDevice', 'revision_id'], '') 737 738 return touched 739 740 @staticmethod 741 def _merge_video(bundle, label, item): 742 """Merge video items.""" 743 values = item.get('values', {}) 744 745 touched = set() 746 747 component = cbu.find_component(bundle, id_value=label, create=True) 748 component = _set_support_status(component, item) 749 component.name = values.get('product', label) 750 touched.add('product') 751 752 # save HWID values 753 component.hwid_type = 'video' 754 component.hwid_label = label 755 756 usb_fields = ['bcdDevice', 'idProduct', 'idVendor'] 757 pci_fields = ['vendor', 'device', 'revision_id'] 758 759 if values.get('bus_type') == 'usb' or \ 760 all(key in values for key in usb_fields): 761 762 if 'manufacturer' in values: 763 component.manufacturer_id.MergeFrom( 764 cbu.find_partner(bundle, values['manufacturer'], create=True).id) 765 766 component.camera.usb.vendor_id = values.get('idVendor', '') 767 component.camera.usb.product_id = values.get('idProduct', '') 768 component.camera.usb.bcd_device = values.get('bcdDevice', '') 769 touched.update(usb_fields) 770 771 if values.get('bus_type') == 'pci' or \ 772 all(key in values for key in pci_fields): 773 774 component.camera.pci.vendor_id = values.get('vendor', '') 775 component.camera.pci.device_id = values.get('device', '') 776 component.camera.pci.revision_id = values.get('revision_id', '') 777 touched.update(pci_fields) 778 779 touched.add('bus_type') 780 return touched 781 782 @staticmethod 783 def _merge_wireless(bundle, label, item): 784 """Merge wireless items.""" 785 values = item.get('values', {}) 786 787 touched = set() 788 789 component = cbu.find_component(bundle, id_value=label, create=True) 790 component = _set_support_status(component, item) 791 component.name = label 792 793 # save HWID values 794 component.hwid_type = 'wireless' 795 component.hwid_label = label 796 797 component.wifi.pci.vendor_id = values.get('vendor', '') 798 component.wifi.pci.device_id = values.get('device', '') 799 component.wifi.pci.revision_id = values.get('revision_id', '') 800 touched.update(['vendor', 'device', 'revision_id']) 801 return touched 802