Linux Audio

Check our new training course

Linux kernel drivers training

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