text: /*
   * apple.c - Apple ACPI quirks
   * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de>
   *
   * This program is free software; you can redistribute it and/or modify
   * it under the terms of the GNU General Public License (version 2) as
   * published by the Free Software Foundation.
   */
  
  #include <linux/acpi.h>
  #include <linux/bitmap.h>
  #include <linux/platform_data/x86/apple.h>
  #include <linux/uuid.h>
  
  /* Apple _DSM device properties GUID */
  static const guid_t apple_prp_guid =
  	GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c,
  		  0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b);
  
  /**
   * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties
   * @adev: ACPI device for which to retrieve the properties
   *
   * Invoke Apple's custom _DSM once to check the protocol version and once more
   * to retrieve the properties.  They are marshalled up in a single package as
   * alternating key/value elements, unlike _DSD which stores them as a package
   * of 2-element packages.  Convert to _DSD format and make them available under
   * the primary fwnode.
   */
  void acpi_extract_apple_properties(struct acpi_device *adev)
  {
  	unsigned int i, j = 0, newsize = 0, numprops, numvalid;
  	union acpi_object *props, *newprops;
  	unsigned long *valid = NULL;
  	void *free_space;
  
  	if (!x86_apple_machine)
  		return;
  
  	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0,
  					NULL, ACPI_TYPE_BUFFER);
  	if (!props)
  		return;
  
  	if (!props->buffer.length)
  		goto out_free;
  
  	if (props->buffer.pointer[0] != 3) {
  		acpi_handle_info(adev->handle, FW_INFO
  				 "unsupported properties version %*ph\n",
  				 props->buffer.length, props->buffer.pointer);
  		goto out_free;
  	}
  
  	ACPI_FREE(props);
  	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1,
  					NULL, ACPI_TYPE_PACKAGE);
  	if (!props)
  		return;
  
  	numprops = props->package.count / 2;
  	if (!numprops)
  		goto out_free;
  
  	valid = bitmap_zalloc(numprops, GFP_KERNEL);
  	if (!valid)
  		goto out_free;
  
  	/* newsize = key length + value length of each tuple */
  	for (i = 0; i < numprops; i++) {
  		union acpi_object *key = &props->package.elements[i * 2];
  		union acpi_object *val = &props->package.elements[i * 2 + 1];
  
  		if ( key->type != ACPI_TYPE_STRING ||
  		    (val->type != ACPI_TYPE_INTEGER &&
  		     val->type != ACPI_TYPE_BUFFER))
  			continue; /* skip invalid properties */
  
  		__set_bit(i, valid);
  		newsize += key->string.length + 1;
  		if ( val->type == ACPI_TYPE_BUFFER)
  			newsize += val->buffer.length;
  	}
  
  	numvalid = bitmap_weight(valid, numprops);
  	if (numprops > numvalid)
  		acpi_handle_info(adev->handle, FW_INFO
  				 "skipped %u properties: wrong type\n",
  				 numprops - numvalid);
  	if (numvalid == 0)
  		goto out_free;
  
  	/* newsize += top-level package + 3 objects for each key/value tuple */
  	newsize	+= (1 + 3 * numvalid) * sizeof(union acpi_object);
  	newprops = ACPI_ALLOCATE_ZEROED(newsize);
  	if (!newprops)
  		goto out_free;
  
  	/* layout: top-level package | packages | key/value tuples | strings */
  	newprops->type = ACPI_TYPE_PACKAGE;
  	newprops->package.count = numvalid;
  	newprops->package.elements = &newprops[1];
  	free_space = &newprops[1 + 3 * numvalid];
  
  	for_each_set_bit(i, valid, numprops) {
  		union acpi_object *key = &props->package.elements[i * 2];
  		union acpi_object *val = &props->package.elements[i * 2 + 1];
  		unsigned int k = 1 + numvalid + j * 2; /* index into newprops */
  		unsigned int v = k + 1;
  
  		newprops[1 + j].type = ACPI_TYPE_PACKAGE;
  		newprops[1 + j].package.count = 2;
  		newprops[1 + j].package.elements = &newprops[k];
  
  		newprops[k].type = ACPI_TYPE_STRING;
  		newprops[k].string.length = key->string.length;
  		newprops[k].string.pointer = free_space;
  		memcpy(free_space, key->string.pointer, key->string.length);
  		free_space += key->string.length + 1;
  
  		newprops[v].type = val->type;
  		if (val->type == ACPI_TYPE_INTEGER) {
  			newprops[v].integer.value = val->integer.value;
  		} else {
  			newprops[v].buffer.length = val->buffer.length;
  			newprops[v].buffer.pointer = free_space;
  			memcpy(free_space, val->buffer.pointer,
  			       val->buffer.length);
  			free_space += val->buffer.length;
  		}
  		j++; /* count valid properties */
  	}
  	WARN_ON(free_space != (void *)newprops + newsize);
  
  	adev->data.pointer = newprops;
  	acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
  
  out_free:
  	ACPI_FREE(props);
  	bitmap_free(valid);
  }
  