// SPDX-License-Identifier: GPL-2.0-or-later
/*
* ACPI-WMI mapping driver
*
* Copyright (C) 2007-2008 Carlos Corbacho <carlos@strangeworlds.co.uk>
*
* GUID parsing code from ldm.c is:
* Copyright (C) 2001,2002 Richard Russon <ldm@flatcap.org>
* Copyright (c) 2001-2007 Anton Altaparmakov
* Copyright (C) 2001,2002 Jakob Kemi <jakob.kemi@telia.com>
*
* WMI bus infrastructure by Andrew Lutomirski and Darren Hart:
* Copyright (C) 2015 Andrew Lutomirski
* Copyright (C) 2017 VMware, Inc. All Rights Reserved.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/acpi.h>
#include <linux/bits.h>
#include <linux/build_bug.h>
#include <linux/device.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/list.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rwsem.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
#include <linux/types.h>
#include <linux/uuid.h>
#include <linux/wmi.h>
#include <linux/fs.h>
MODULE_AUTHOR("Carlos Corbacho");
MODULE_DESCRIPTION("ACPI-WMI Mapping Driver");
MODULE_LICENSE("GPL");
static LIST_HEAD(wmi_block_list);
struct guid_block {
guid_t guid;
union {
char object_id[2];
struct {
unsigned char notify_id;
unsigned char reserved;
};
};
u8 instance_count;
u8 flags;
} __packed;
static_assert(sizeof(typeof_member(struct guid_block, guid)) == 16);
static_assert(sizeof(struct guid_block) == 20);
static_assert(__alignof__(struct guid_block) == 1);
enum { /* wmi_block flags */
WMI_READ_TAKES_NO_ARGS,
WMI_GUID_DUPLICATED,
WMI_NO_EVENT_DATA,
};
struct wmi_block {
struct wmi_device dev;
struct list_head list;
struct guid_block gblock;
struct acpi_device *acpi_device;
struct rw_semaphore notify_lock; /* Protects notify callback add/remove */
wmi_notify_handler handler;
void *handler_data;
bool driver_ready;
unsigned long flags;
};
/*
* If the GUID data block is marked as expensive, we must enable and
* explicitily disable data collection.
*/
#define ACPI_WMI_EXPENSIVE BIT(0)
#define ACPI_WMI_METHOD BIT(1) /* GUID is a method */
#define ACPI_WMI_STRING BIT(2) /* GUID takes & returns a string */
#define ACPI_WMI_EVENT BIT(3) /* GUID is an event */
static const struct acpi_device_id wmi_device_ids[] = {
{"PNP0C14", 0},
{"pnp0c14", 0},
{ }
};
MODULE_DEVICE_TABLE(acpi, wmi_device_ids);
#define dev_to_wblock(__dev) container_of_const(__dev, struct wmi_block, dev.dev)
#define dev_to_wdev(__dev) container_of_const(__dev, struct wmi_device, dev)
/*
* GUID parsing functions
*/
static bool guid_parse_and_compare(const char *string, const guid_t *guid)
{
guid_t guid_input;
if (guid_parse(string, &guid_input))
return false;
return guid_equal(&guid_input, guid);
}
static const void *find_guid_context(struct wmi_block *wblock,
struct wmi_driver *wdriver)
{
const struct wmi_device_id *id;
id = wdriver->id_table;
if (!