drivers/hid/i2c-hid/i2c-hid-acpi.c

Source file repositories/reference/linux-study-clean/drivers/hid/i2c-hid/i2c-hid-acpi.c

File Facts

System
Linux kernel
Corpus path
drivers/hid/i2c-hid/i2c-hid-acpi.c
Extension
.c
Size
3913 bytes
Lines
144
Domain
Driver Families
Bucket
drivers/hid
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

struct i2c_hid_acpi {
	struct i2chid_ops ops;
	struct acpi_device *adev;
};

static const struct acpi_device_id i2c_hid_acpi_blacklist[] = {
	/*
	 * The CHPN0001 ACPI device, which is used to describe the Chipone
	 * ICN8505 controller, has a _CID of PNP0C50 but is not HID compatible.
	 */
	{ "CHPN0001" },
	/*
	 * The IDEA5002 ACPI device causes high interrupt usage and spurious
	 * wakeups from suspend.
	 */
	{ "IDEA5002" },
	{ }
};

/* HID I²C Device: 3cdff6f7-4267-4555-ad05-b30a3d8938de */
static guid_t i2c_hid_guid =
	GUID_INIT(0x3CDFF6F7, 0x4267, 0x4555,
		  0xAD, 0x05, 0xB3, 0x0A, 0x3D, 0x89, 0x38, 0xDE);

static int i2c_hid_acpi_get_descriptor(struct i2c_hid_acpi *ihid_acpi)
{
	struct acpi_device *adev = ihid_acpi->adev;
	acpi_handle handle = acpi_device_handle(adev);
	union acpi_object *obj;
	u16 hid_descriptor_address;

	if (acpi_match_device_ids(adev, i2c_hid_acpi_blacklist) == 0)
		return -ENODEV;

	obj = acpi_evaluate_dsm_typed(handle, &i2c_hid_guid, 1, 1, NULL,
				      ACPI_TYPE_INTEGER);
	if (!obj) {
		acpi_handle_err(handle, "Error _DSM call to get HID descriptor address failed\n");
		return -ENODEV;
	}

	hid_descriptor_address = obj->integer.value;
	ACPI_FREE(obj);

	return hid_descriptor_address;
}

static void i2c_hid_acpi_restore_sequence(struct i2chid_ops *ops)
{
	struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);

	i2c_hid_acpi_get_descriptor(ihid_acpi);
}

static void i2c_hid_acpi_shutdown_tail(struct i2chid_ops *ops)
{
	struct i2c_hid_acpi *ihid_acpi = container_of(ops, struct i2c_hid_acpi, ops);

	acpi_device_set_power(ihid_acpi->adev, ACPI_STATE_D3_COLD);
}

static int i2c_hid_acpi_probe(struct i2c_client *client)
{
	struct device *dev = &client->dev;
	struct i2c_hid_acpi *ihid_acpi;
	u16 hid_descriptor_address;
	int ret;

	ihid_acpi = devm_kzalloc(&client->dev, sizeof(*ihid_acpi), GFP_KERNEL);
	if (!ihid_acpi)
		return -ENOMEM;

	ihid_acpi->adev = ACPI_COMPANION(dev);
	ihid_acpi->ops.shutdown_tail = i2c_hid_acpi_shutdown_tail;
	ihid_acpi->ops.restore_sequence = i2c_hid_acpi_restore_sequence;

	ret = i2c_hid_acpi_get_descriptor(ihid_acpi);
	if (ret < 0)
		return ret;
	hid_descriptor_address = ret;

	acpi_device_fix_up_power(ihid_acpi->adev);

	return i2c_hid_core_probe(client, &ihid_acpi->ops,
				  hid_descriptor_address, 0);
}

static const struct acpi_device_id i2c_hid_acpi_match[] = {
	{ "ACPI0C50" },
	{ "PNP0C50" },

Annotation

Implementation Notes