Linux Audio

Check our new training course

Loading...
Note: File does not exist in v3.1.
  1// SPDX-License-Identifier: GPL-2.0-only
  2/*
  3 * apple.c - Apple ACPI quirks
  4 * Copyright (C) 2017 Lukas Wunner <lukas@wunner.de>
  5 */
  6
  7#include <linux/acpi.h>
  8#include <linux/bitmap.h>
  9#include <linux/platform_data/x86/apple.h>
 10#include <linux/uuid.h>
 11#include "../internal.h"
 12
 13/* Apple _DSM device properties GUID */
 14static const guid_t apple_prp_guid =
 15	GUID_INIT(0xa0b5b7c6, 0x1318, 0x441c,
 16		  0xb0, 0xc9, 0xfe, 0x69, 0x5e, 0xaf, 0x94, 0x9b);
 17
 18/**
 19 * acpi_extract_apple_properties - retrieve and convert Apple _DSM properties
 20 * @adev: ACPI device for which to retrieve the properties
 21 *
 22 * Invoke Apple's custom _DSM once to check the protocol version and once more
 23 * to retrieve the properties.  They are marshalled up in a single package as
 24 * alternating key/value elements, unlike _DSD which stores them as a package
 25 * of 2-element packages.  Convert to _DSD format and make them available under
 26 * the primary fwnode.
 27 */
 28void acpi_extract_apple_properties(struct acpi_device *adev)
 29{
 30	unsigned int i, j = 0, newsize = 0, numprops, numvalid;
 31	union acpi_object *props, *newprops;
 32	unsigned long *valid = NULL;
 33	void *free_space;
 34
 35	if (!x86_apple_machine)
 36		return;
 37
 38	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 0,
 39					NULL, ACPI_TYPE_BUFFER);
 40	if (!props)
 41		return;
 42
 43	if (!props->buffer.length)
 44		goto out_free;
 45
 46	if (props->buffer.pointer[0] != 3) {
 47		acpi_handle_info(adev->handle, FW_INFO
 48				 "unsupported properties version %*ph\n",
 49				 props->buffer.length, props->buffer.pointer);
 50		goto out_free;
 51	}
 52
 53	ACPI_FREE(props);
 54	props = acpi_evaluate_dsm_typed(adev->handle, &apple_prp_guid, 1, 1,
 55					NULL, ACPI_TYPE_PACKAGE);
 56	if (!props)
 57		return;
 58
 59	numprops = props->package.count / 2;
 60	if (!numprops)
 61		goto out_free;
 62
 63	valid = bitmap_zalloc(numprops, GFP_KERNEL);
 64	if (!valid)
 65		goto out_free;
 66
 67	/* newsize = key length + value length of each tuple */
 68	for (i = 0; i < numprops; i++) {
 69		union acpi_object *key = &props->package.elements[i * 2];
 70		union acpi_object *val = &props->package.elements[i * 2 + 1];
 71
 72		if ( key->type != ACPI_TYPE_STRING ||
 73		    (val->type != ACPI_TYPE_INTEGER &&
 74		     val->type != ACPI_TYPE_BUFFER &&
 75		     val->type != ACPI_TYPE_STRING))
 76			continue; /* skip invalid properties */
 77
 78		__set_bit(i, valid);
 79		newsize += key->string.length + 1;
 80		if ( val->type == ACPI_TYPE_BUFFER)
 81			newsize += val->buffer.length;
 82		else if (val->type == ACPI_TYPE_STRING)
 83			newsize += val->string.length + 1;
 84	}
 85
 86	numvalid = bitmap_weight(valid, numprops);
 87	if (numprops > numvalid)
 88		acpi_handle_info(adev->handle, FW_INFO
 89				 "skipped %u properties: wrong type\n",
 90				 numprops - numvalid);
 91	if (numvalid == 0)
 92		goto out_free;
 93
 94	/* newsize += top-level package + 3 objects for each key/value tuple */
 95	newsize	+= (1 + 3 * numvalid) * sizeof(union acpi_object);
 96	newprops = ACPI_ALLOCATE_ZEROED(newsize);
 97	if (!newprops)
 98		goto out_free;
 99
100	/* layout: top-level package | packages | key/value tuples | strings */
101	newprops->type = ACPI_TYPE_PACKAGE;
102	newprops->package.count = numvalid;
103	newprops->package.elements = &newprops[1];
104	free_space = &newprops[1 + 3 * numvalid];
105
106	for_each_set_bit(i, valid, numprops) {
107		union acpi_object *key = &props->package.elements[i * 2];
108		union acpi_object *val = &props->package.elements[i * 2 + 1];
109		unsigned int k = 1 + numvalid + j * 2; /* index into newprops */
110		unsigned int v = k + 1;
111
112		newprops[1 + j].type = ACPI_TYPE_PACKAGE;
113		newprops[1 + j].package.count = 2;
114		newprops[1 + j].package.elements = &newprops[k];
115
116		newprops[k].type = ACPI_TYPE_STRING;
117		newprops[k].string.length = key->string.length;
118		newprops[k].string.pointer = free_space;
119		memcpy(free_space, key->string.pointer, key->string.length);
120		free_space += key->string.length + 1;
121
122		newprops[v].type = val->type;
123		if (val->type == ACPI_TYPE_INTEGER) {
124			newprops[v].integer.value = val->integer.value;
125		} else if (val->type == ACPI_TYPE_STRING) {
126			newprops[v].string.length = val->string.length;
127			newprops[v].string.pointer = free_space;
128			memcpy(free_space, val->string.pointer,
129			       val->string.length);
130			free_space += val->string.length + 1;
131		} else {
132			newprops[v].buffer.length = val->buffer.length;
133			newprops[v].buffer.pointer = free_space;
134			memcpy(free_space, val->buffer.pointer,
135			       val->buffer.length);
136			free_space += val->buffer.length;
137		}
138		j++; /* count valid properties */
139	}
140	WARN_ON(free_space != (void *)newprops + newsize);
141
142	adev->data.pointer = newprops;
143	acpi_data_add_props(&adev->data, &apple_prp_guid, newprops);
144
145out_free:
146	ACPI_FREE(props);
147	bitmap_free(valid);
148}