cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

i2c-hid-acpi.c (3753B)


      1/*
      2 * HID over I2C ACPI Subclass
      3 *
      4 * Copyright (c) 2012 Benjamin Tissoires <benjamin.tissoires@gmail.com>
      5 * Copyright (c) 2012 Ecole Nationale de l'Aviation Civile, France
      6 * Copyright (c) 2012 Red Hat, Inc
      7 *
      8 * This code was forked out of the core code, which was partly based on
      9 * "USB HID support for Linux":
     10 *
     11 *  Copyright (c) 1999 Andreas Gal
     12 *  Copyright (c) 2000-2005 Vojtech Pavlik <vojtech@suse.cz>
     13 *  Copyright (c) 2005 Michael Haboustak <mike-@cinci.rr.com> for Concept2, Inc
     14 *  Copyright (c) 2007-2008 Oliver Neukum
     15 *  Copyright (c) 2006-2010 Jiri Kosina
     16 *
     17 * This file is subject to the terms and conditions of the GNU General Public
     18 * License.  See the file COPYING in the main directory of this archive for
     19 * more details.
     20 */
     21
     22#include <linux/acpi.h>
     23#include <linux/device.h>
     24#include <linux/i2c.h>
     25#include <linux/kernel.h>
     26#include <linux/module.h>
     27#include <linux/pm.h>
     28#include <linux/uuid.h>
     29
     30#include "i2c-hid.h"
     31
     32struct i2c_hid_acpi {
     33	struct i2chid_ops ops;
     34	struct acpi_device *adev;
     35};
     36
     37static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
     38	/*
     39	 * The CHPN0001 ACPI device, which is used to describe the Chipone
     40	 * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
     41	 */
     42	{"CHPN0001", 0 },
     43	{ },
     44};
     45
     46/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
     47static guid_t i2c_hid_guid =
     48	GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
     49		  0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);
     50
     51static int i2c_hid_acpi_get_descriptor(struct acpi_device *adev)
     52{
     53	acpi_handle handle = acpi_device_handle(adev);
     54	union acpi_object *obj;
     55	u16 hid_descriptor_address;
     56
     57	if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
     58		return -ENODEV;
     59
     60	obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
     61				      ACPI_TYPE_INTEGER);
     62	if (!obj) {
     63		acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n");
     64		return -ENODEV;
     65	}
     66
     67	hid_descriptor_address = obj->integer.value;
     68	ACPI_FREE(obj);
     69
     70	return hid_descriptor_address;
     71}
     72
     73static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
     74{
     75	struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);
     76
     77	acpi_device_set_power(ihid_acpi->adev, ACPI_STATE_D3_COLD);
     78}
     79
     80static int i2c_hid_acpi_probe(struct i2c_client *client)
     81{
     82	struct device *dev = &client->dev;
     83	struct i2c_hid_acpi *ihid_acpi;
     84	struct acpi_device *adev;
     85	u16 hid_descriptor_address;
     86	int ret;
     87
     88	adev = ACPI_COMPANION(dev);
     89	if (!adev) {
     90		dev_err(&client->dev, "Error could not get ACPI device\n");
     91		return -ENODEV;
     92	}
     93
     94	ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
     95	if (!ihid_acpi)
     96		return -ENOMEM;
     97
     98	ihid_acpi->adev = adev;
     99	ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
    100
    101	ret = i2c_hid_acpi_get_descriptor(adev);
    102	if (ret < 0)
    103		return ret;
    104	hid_descriptor_address = ret;
    105
    106	acpi_device_fix_up_power(adev);
    107
    108	if (acpi_gbl_FADT.flags & ACPI_FADT_LOW_POWER_S0) {
    109		device_set_wakeup_capable(dev, true);
    110		device_set_wakeup_enable(dev, false);
    111	}
    112
    113	return i2c_hid_core_probe(client, &ihid_acpi->ops,
    114				  hid_descriptor_address, 0);
    115}
    116
    117static const struct acpi_device_id i2c_hid_acpi_match[] = {
    118	{"ACPI0C50", 0 },
    119	{"PNP0C50", 0 },
    120	{ },
    121};
    122MODULE_DEVICE_TABLE(acpi, i2c_hid_acpi_match);
    123
    124static struct i2c_driver i2c_hid_acpi_driver = {
    125	.driver = {
    126		.name	= "i2c_hid_acpi",
    127		.pm	= &i2c_hid_core_pm,
    128		.probe_type = PROBE_PREFER_ASYNCHRONOUS,
    129		.acpi_match_table = i2c_hid_acpi_match,
    130	},
    131
    132	.probe_new	= i2c_hid_acpi_probe,
    133	.remove		= i2c_hid_core_remove,
    134	.shutdown	= i2c_hid_core_shutdown,
    135};
    136
    137module_i2c_driver(i2c_hid_acpi_driver);
    138
    139MODULE_DESCRIPTION("HID over I2C ACPI driver");
    140MODULE_AUTHOR("Benjamin Tissoires <benjamin.tissoires@gmail.com>");
    141MODULE_LICENSE("GPL");