drivers/platform/x86/lenovo/ymc.c

Source file repositories/reference/linux-study-clean/drivers/platform/x86/lenovo/ymc.c

File Facts

System
Linux kernel
Corpus path
drivers/platform/x86/lenovo/ymc.c
Extension
.c
Size
4200 bytes
Lines
167
Domain
Driver Families
Bucket
drivers/platform
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 lenovo_ymc_private {
	struct input_dev *input_dev;
};

static const struct key_entry lenovo_ymc_keymap[] = {
	/* Ignore the uninitialized state */
	{ KE_IGNORE, 0x00 },
	/* Laptop */
	{ KE_SW, 0x01, { .sw = { SW_TABLET_MODE, 0 } } },
	/* Tablet */
	{ KE_SW, 0x02, { .sw = { SW_TABLET_MODE, 1 } } },
	/* Drawing Board */
	{ KE_SW, 0x03, { .sw = { SW_TABLET_MODE, 1 } } },
	/* Tent */
	{ KE_SW, 0x04, { .sw = { SW_TABLET_MODE, 1 } } },
	{ KE_END },
};

static void lenovo_ymc_notify(struct wmi_device *wdev, union acpi_object *data)
{
	struct lenovo_ymc_private *priv = dev_get_drvdata(&wdev->dev);
	u32 input_val = 0;
	struct acpi_buffer input = { sizeof(input_val), &input_val };
	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
	union acpi_object *obj;
	acpi_status status;
	int code;

	status = wmi_evaluate_method(LENOVO_YMC_QUERY_GUID,
				LENOVO_YMC_QUERY_INSTANCE,
				LENOVO_YMC_QUERY_METHOD,
				&input, &output);

	if (ACPI_FAILURE(status)) {
		dev_warn(&wdev->dev,
			"Failed to evaluate query method: %s\n",
			acpi_format_exception(status));
		return;
	}

	obj = output.pointer;

	if (obj->type != ACPI_TYPE_INTEGER) {
		dev_warn(&wdev->dev,
			"WMI event data is not an integer\n");
		goto free_obj;
	}
	code = obj->integer.value;

	if (!sparse_keymap_report_event(priv->input_dev, code, 1, true))
		dev_warn(&wdev->dev, "Unknown key %d pressed\n", code);

free_obj:
	kfree(obj);
	ideapad_laptop_call_notifier(IDEAPAD_LAPTOP_YMC_EVENT, &code);
}

static int lenovo_ymc_probe(struct wmi_device *wdev, const void *ctx)
{
	struct lenovo_ymc_private *priv;
	struct input_dev *input_dev;
	int err;

	if (!dmi_check_system(allowed_chasis_types_dmi_table)) {
		if (force)
			dev_info(&wdev->dev, "Force loading Lenovo YMC support\n");
		else
			return -ENODEV;
	}

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

	input_dev = devm_input_allocate_device(&wdev->dev);
	if (!input_dev)
		return -ENOMEM;

	input_dev->name = "Lenovo Yoga Tablet Mode Control switch";
	input_dev->phys = LENOVO_YMC_EVENT_GUID "/input0";
	input_dev->id.bustype = BUS_HOST;
	input_dev->dev.parent = &wdev->dev;
	err = sparse_keymap_setup(input_dev, lenovo_ymc_keymap, NULL);
	if (err) {
		dev_err(&wdev->dev,
			"Could not set up input device keymap: %d\n", err);
		return err;
	}

	err = input_register_device(input_dev);

Annotation

Implementation Notes