File Index
Files and directories
Sign in
FILE

classmate-laptop.c

File preview with syntax highlighting.

linux-6.19.14/drivers/platform/x86/classmate-laptop.c C
   1// SPDX-License-Identifier: GPL-2.0-or-later
   2/*
   3 *  Copyright (C) 2009  Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
   4 */
   5
   6
   7#include <linux/init.h>
   8#include <linux/module.h>
   9#include <linux/slab.h>
  10#include <linux/workqueue.h>
  11#include <linux/acpi.h>
  12#include <linux/backlight.h>
  13#include <linux/input.h>
  14#include <linux/rfkill.h>
  15#include <linux/sysfs.h>
  16
  17struct cmpc_accel {
  18	int sensitivity;
  19	int g_select;
  20	int inputdev_state;
  21};
  22
  23#define CMPC_ACCEL_DEV_STATE_CLOSED	0
  24#define CMPC_ACCEL_DEV_STATE_OPEN	1
  25
  26#define CMPC_ACCEL_SENSITIVITY_DEFAULT		5
  27#define CMPC_ACCEL_G_SELECT_DEFAULT		0
  28
  29#define CMPC_ACCEL_HID		"ACCE0000"
  30#define CMPC_ACCEL_HID_V4	"ACCE0001"
  31#define CMPC_TABLET_HID		"TBLT0000"
  32#define CMPC_IPML_HID	"IPML200"
  33#define CMPC_KEYS_HID		"FNBT0000"
  34
  35/*
  36 * Generic input device code.
  37 */
  38
  39typedef void (*input_device_init)(struct input_dev *dev);
  40
  41static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
  42				       input_device_init idev_init)
  43{
  44	struct input_dev *inputdev;
  45	int error;
  46
  47	inputdev = input_allocate_device();
  48	if (!inputdev)
  49		return -ENOMEM;
  50	inputdev->name = name;
  51	inputdev->dev.parent = &acpi->dev;
  52	idev_init(inputdev);
  53	error = input_register_device(inputdev);
  54	if (error) {
  55		input_free_device(inputdev);
  56		return error;
  57	}
  58	dev_set_drvdata(&acpi->dev, inputdev);
  59	return 0;
  60}
  61
  62static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
  63{
  64	struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
  65	input_unregister_device(inputdev);
  66	return 0;
  67}
  68
  69/*
  70 * Accelerometer code for Classmate V4
  71 */
  72static acpi_status cmpc_start_accel_v4(acpi_handle handle)
  73{
  74	union acpi_object param[4];
  75	struct acpi_object_list input;
  76	acpi_status status;
  77
  78	param[0].type = ACPI_TYPE_INTEGER;
  79	param[0].integer.value = 0x3;
  80	param[1].type = ACPI_TYPE_INTEGER;
  81	param[1].integer.value = 0;
  82	param[2].type = ACPI_TYPE_INTEGER;
  83	param[2].integer.value = 0;
  84	param[3].type = ACPI_TYPE_INTEGER;
  85	param[3].integer.value = 0;
  86	input.count = 4;
  87	input.pointer = param;
  88	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
  89	return status;
  90}
  91
  92static acpi_status cmpc_stop_accel_v4(acpi_handle handle)
  93{
  94	union acpi_object param[4];
  95	struct acpi_object_list input;
  96	acpi_status status;
  97
  98	param[0].type = ACPI_TYPE_INTEGER;
  99	param[0].integer.value = 0x4;
 100	param[1].type = ACPI_TYPE_INTEGER;
 101	param[1].integer.value = 0;
 102	param[2].type = ACPI_TYPE_INTEGER;
 103	param[2].integer.value = 0;
 104	param[3].type = ACPI_TYPE_INTEGER;
 105	param[3].integer.value = 0;
 106	input.count = 4;
 107	input.pointer = param;
 108	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
 109	return status;
 110}
 111
 112static acpi_status cmpc_accel_set_sensitivity_v4(acpi_handle handle, int val)
 113{
 114	union acpi_object param[4];
 115	struct acpi_object_list input;
 116
 117	param[0].type = ACPI_TYPE_INTEGER;
 118	param[0].integer.value = 0x02;
 119	param[1].type = ACPI_TYPE_INTEGER;
 120	param[1].integer.value = val;
 121	param[2].type = ACPI_TYPE_INTEGER;
 122	param[2].integer.value = 0;
 123	param[3].type = ACPI_TYPE_INTEGER;
 124	param[3].integer.value = 0;
 125	input.count = 4;
 126	input.pointer = param;
 127	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
 128}
 129
 130static acpi_status cmpc_accel_set_g_select_v4(acpi_handle handle, int val)
 131{
 132	union acpi_object param[4];
 133	struct acpi_object_list input;
 134
 135	param[0].type = ACPI_TYPE_INTEGER;
 136	param[0].integer.value = 0x05;
 137	param[1].type = ACPI_TYPE_INTEGER;
 138	param[1].integer.value = val;
 139	param[2].type = ACPI_TYPE_INTEGER;
 140	param[2].integer.value = 0;
 141	param[3].type = ACPI_TYPE_INTEGER;
 142	param[3].integer.value = 0;
 143	input.count = 4;
 144	input.pointer = param;
 145	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
 146}
 147
 148static acpi_status cmpc_get_accel_v4(acpi_handle handle,
 149				     int16_t *x,
 150				     int16_t *y,
 151				     int16_t *z)
 152{
 153	union acpi_object param[4];
 154	struct acpi_object_list input;
 155	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 156	int16_t *locs;
 157	acpi_status status;
 158
 159	param[0].type = ACPI_TYPE_INTEGER;
 160	param[0].integer.value = 0x01;
 161	param[1].type = ACPI_TYPE_INTEGER;
 162	param[1].integer.value = 0;
 163	param[2].type = ACPI_TYPE_INTEGER;
 164	param[2].integer.value = 0;
 165	param[3].type = ACPI_TYPE_INTEGER;
 166	param[3].integer.value = 0;
 167	input.count = 4;
 168	input.pointer = param;
 169	status = acpi_evaluate_object(handle, "ACMD", &input, &output);
 170	if (ACPI_SUCCESS(status)) {
 171		union acpi_object *obj;
 172		obj = output.pointer;
 173		locs = (int16_t *) obj->buffer.pointer;
 174		*x = locs[0];
 175		*y = locs[1];
 176		*z = locs[2];
 177		kfree(output.pointer);
 178	}
 179	return status;
 180}
 181
 182static void cmpc_accel_handler_v4(struct acpi_device *dev, u32 event)
 183{
 184	if (event == 0x81) {
 185		int16_t x, y, z;
 186		acpi_status status;
 187
 188		status = cmpc_get_accel_v4(dev->handle, &x, &y, &z);
 189		if (ACPI_SUCCESS(status)) {
 190			struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
 191
 192			input_report_abs(inputdev, ABS_X, x);
 193			input_report_abs(inputdev, ABS_Y, y);
 194			input_report_abs(inputdev, ABS_Z, z);
 195			input_sync(inputdev);
 196		}
 197	}
 198}
 199
 200static ssize_t cmpc_accel_sensitivity_show_v4(struct device *dev,
 201					      struct device_attribute *attr,
 202					      char *buf)
 203{
 204	struct acpi_device *acpi;
 205	struct input_dev *inputdev;
 206	struct cmpc_accel *accel;
 207
 208	acpi = to_acpi_device(dev);
 209	inputdev = dev_get_drvdata(&acpi->dev);
 210	if (!inputdev)
 211		return -ENXIO;
 212
 213	accel = dev_get_drvdata(&inputdev->dev);
 214	if (!accel)
 215		return -ENXIO;
 216
 217	return sysfs_emit(buf, "%d\n", accel->sensitivity);
 218}
 219
 220static ssize_t cmpc_accel_sensitivity_store_v4(struct device *dev,
 221					       struct device_attribute *attr,
 222					       const char *buf, size_t count)
 223{
 224	struct acpi_device *acpi;
 225	struct input_dev *inputdev;
 226	struct cmpc_accel *accel;
 227	unsigned long sensitivity;
 228	int r;
 229
 230	acpi = to_acpi_device(dev);
 231	inputdev = dev_get_drvdata(&acpi->dev);
 232	if (!inputdev)
 233		return -ENXIO;
 234
 235	accel = dev_get_drvdata(&inputdev->dev);
 236	if (!accel)
 237		return -ENXIO;
 238
 239	r = kstrtoul(buf, 0, &sensitivity);
 240	if (r)
 241		return r;
 242
 243	/* sensitivity must be between 1 and 127 */
 244	if (sensitivity < 1 || sensitivity > 127)
 245		return -EINVAL;
 246
 247	accel->sensitivity = sensitivity;
 248	cmpc_accel_set_sensitivity_v4(acpi->handle, sensitivity);
 249
 250	return strnlen(buf, count);
 251}
 252
 253static struct device_attribute cmpc_accel_sensitivity_attr_v4 = {
 254	.attr = { .name = "sensitivity", .mode = 0660 },
 255	.show = cmpc_accel_sensitivity_show_v4,
 256	.store = cmpc_accel_sensitivity_store_v4
 257};
 258
 259static ssize_t cmpc_accel_g_select_show_v4(struct device *dev,
 260					   struct device_attribute *attr,
 261					   char *buf)
 262{
 263	struct acpi_device *acpi;
 264	struct input_dev *inputdev;
 265	struct cmpc_accel *accel;
 266
 267	acpi = to_acpi_device(dev);
 268	inputdev = dev_get_drvdata(&acpi->dev);
 269	if (!inputdev)
 270		return -ENXIO;
 271
 272	accel = dev_get_drvdata(&inputdev->dev);
 273	if (!accel)
 274		return -ENXIO;
 275
 276	return sysfs_emit(buf, "%d\n", accel->g_select);
 277}
 278
 279static ssize_t cmpc_accel_g_select_store_v4(struct device *dev,
 280					    struct device_attribute *attr,
 281					    const char *buf, size_t count)
 282{
 283	struct acpi_device *acpi;
 284	struct input_dev *inputdev;
 285	struct cmpc_accel *accel;
 286	unsigned long g_select;
 287	int r;
 288
 289	acpi = to_acpi_device(dev);
 290	inputdev = dev_get_drvdata(&acpi->dev);
 291	if (!inputdev)
 292		return -ENXIO;
 293
 294	accel = dev_get_drvdata(&inputdev->dev);
 295	if (!accel)
 296		return -ENXIO;
 297
 298	r = kstrtoul(buf, 0, &g_select);
 299	if (r)
 300		return r;
 301
 302	/* 0 means 1.5g, 1 means 6g, everything else is wrong */
 303	if (g_select != 0 && g_select != 1)
 304		return -EINVAL;
 305
 306	accel->g_select = g_select;
 307	cmpc_accel_set_g_select_v4(acpi->handle, g_select);
 308
 309	return strnlen(buf, count);
 310}
 311
 312static struct device_attribute cmpc_accel_g_select_attr_v4 = {
 313	.attr = { .name = "g_select", .mode = 0660 },
 314	.show = cmpc_accel_g_select_show_v4,
 315	.store = cmpc_accel_g_select_store_v4
 316};
 317
 318static int cmpc_accel_open_v4(struct input_dev *input)
 319{
 320	struct acpi_device *acpi;
 321	struct cmpc_accel *accel;
 322
 323	acpi = to_acpi_device(input->dev.parent);
 324	accel = dev_get_drvdata(&input->dev);
 325	if (!accel)
 326		return -ENXIO;
 327
 328	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
 329	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
 330
 331	if (ACPI_SUCCESS(cmpc_start_accel_v4(acpi->handle))) {
 332		accel->inputdev_state = CMPC_ACCEL_DEV_STATE_OPEN;
 333		return 0;
 334	}
 335	return -EIO;
 336}
 337
 338static void cmpc_accel_close_v4(struct input_dev *input)
 339{
 340	struct acpi_device *acpi;
 341	struct cmpc_accel *accel;
 342
 343	acpi = to_acpi_device(input->dev.parent);
 344	accel = dev_get_drvdata(&input->dev);
 345
 346	cmpc_stop_accel_v4(acpi->handle);
 347	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
 348}
 349
 350static void cmpc_accel_idev_init_v4(struct input_dev *inputdev)
 351{
 352	set_bit(EV_ABS, inputdev->evbit);
 353	input_set_abs_params(inputdev, ABS_X, -255, 255, 16, 0);
 354	input_set_abs_params(inputdev, ABS_Y, -255, 255, 16, 0);
 355	input_set_abs_params(inputdev, ABS_Z, -255, 255, 16, 0);
 356	inputdev->open = cmpc_accel_open_v4;
 357	inputdev->close = cmpc_accel_close_v4;
 358}
 359
 360#ifdef CONFIG_PM_SLEEP
 361static int cmpc_accel_suspend_v4(struct device *dev)
 362{
 363	struct input_dev *inputdev;
 364	struct cmpc_accel *accel;
 365
 366	inputdev = dev_get_drvdata(dev);
 367	accel = dev_get_drvdata(&inputdev->dev);
 368
 369	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN)
 370		return cmpc_stop_accel_v4(to_acpi_device(dev)->handle);
 371
 372	return 0;
 373}
 374
 375static int cmpc_accel_resume_v4(struct device *dev)
 376{
 377	struct input_dev *inputdev;
 378	struct cmpc_accel *accel;
 379
 380	inputdev = dev_get_drvdata(dev);
 381	accel = dev_get_drvdata(&inputdev->dev);
 382
 383	if (accel->inputdev_state == CMPC_ACCEL_DEV_STATE_OPEN) {
 384		cmpc_accel_set_sensitivity_v4(to_acpi_device(dev)->handle,
 385					      accel->sensitivity);
 386		cmpc_accel_set_g_select_v4(to_acpi_device(dev)->handle,
 387					   accel->g_select);
 388
 389		if (ACPI_FAILURE(cmpc_start_accel_v4(to_acpi_device(dev)->handle)))
 390			return -EIO;
 391	}
 392
 393	return 0;
 394}
 395#endif
 396
 397static int cmpc_accel_add_v4(struct acpi_device *acpi)
 398{
 399	int error;
 400	struct input_dev *inputdev;
 401	struct cmpc_accel *accel;
 402
 403	accel = kmalloc(sizeof(*accel), GFP_KERNEL);
 404	if (!accel)
 405		return -ENOMEM;
 406
 407	accel->inputdev_state = CMPC_ACCEL_DEV_STATE_CLOSED;
 408
 409	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
 410	cmpc_accel_set_sensitivity_v4(acpi->handle, accel->sensitivity);
 411
 412	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
 413	if (error)
 414		goto failed_sensitivity;
 415
 416	accel->g_select = CMPC_ACCEL_G_SELECT_DEFAULT;
 417	cmpc_accel_set_g_select_v4(acpi->handle, accel->g_select);
 418
 419	error = device_create_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
 420	if (error)
 421		goto failed_g_select;
 422
 423	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel_v4",
 424					    cmpc_accel_idev_init_v4);
 425	if (error)
 426		goto failed_input;
 427
 428	inputdev = dev_get_drvdata(&acpi->dev);
 429	dev_set_drvdata(&inputdev->dev, accel);
 430
 431	return 0;
 432
 433failed_input:
 434	device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
 435failed_g_select:
 436	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
 437failed_sensitivity:
 438	kfree(accel);
 439	return error;
 440}
 441
 442static void cmpc_accel_remove_v4(struct acpi_device *acpi)
 443{
 444	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr_v4);
 445	device_remove_file(&acpi->dev, &cmpc_accel_g_select_attr_v4);
 446	cmpc_remove_acpi_notify_device(acpi);
 447}
 448
 449static SIMPLE_DEV_PM_OPS(cmpc_accel_pm, cmpc_accel_suspend_v4,
 450			 cmpc_accel_resume_v4);
 451
 452static const struct acpi_device_id cmpc_accel_device_ids_v4[] = {
 453	{CMPC_ACCEL_HID_V4, 0},
 454	{"", 0}
 455};
 456
 457static struct acpi_driver cmpc_accel_acpi_driver_v4 = {
 458	.name = "cmpc_accel_v4",
 459	.class = "cmpc_accel_v4",
 460	.ids = cmpc_accel_device_ids_v4,
 461	.ops = {
 462		.add = cmpc_accel_add_v4,
 463		.remove = cmpc_accel_remove_v4,
 464		.notify = cmpc_accel_handler_v4,
 465	},
 466	.drv.pm = &cmpc_accel_pm,
 467};
 468
 469
 470/*
 471 * Accelerometer code for Classmate versions prior to V4
 472 */
 473static acpi_status cmpc_start_accel(acpi_handle handle)
 474{
 475	union acpi_object param[2];
 476	struct acpi_object_list input;
 477	acpi_status status;
 478
 479	param[0].type = ACPI_TYPE_INTEGER;
 480	param[0].integer.value = 0x3;
 481	param[1].type = ACPI_TYPE_INTEGER;
 482	input.count = 2;
 483	input.pointer = param;
 484	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
 485	return status;
 486}
 487
 488static acpi_status cmpc_stop_accel(acpi_handle handle)
 489{
 490	union acpi_object param[2];
 491	struct acpi_object_list input;
 492	acpi_status status;
 493
 494	param[0].type = ACPI_TYPE_INTEGER;
 495	param[0].integer.value = 0x4;
 496	param[1].type = ACPI_TYPE_INTEGER;
 497	input.count = 2;
 498	input.pointer = param;
 499	status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
 500	return status;
 501}
 502
 503static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
 504{
 505	union acpi_object param[2];
 506	struct acpi_object_list input;
 507
 508	param[0].type = ACPI_TYPE_INTEGER;
 509	param[0].integer.value = 0x02;
 510	param[1].type = ACPI_TYPE_INTEGER;
 511	param[1].integer.value = val;
 512	input.count = 2;
 513	input.pointer = param;
 514	return acpi_evaluate_object(handle, "ACMD", &input, NULL);
 515}
 516
 517static acpi_status cmpc_get_accel(acpi_handle handle,
 518				  unsigned char *x,
 519				  unsigned char *y,
 520				  unsigned char *z)
 521{
 522	union acpi_object param[2];
 523	struct acpi_object_list input;
 524	struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
 525	unsigned char *locs;
 526	acpi_status status;
 527
 528	param[0].type = ACPI_TYPE_INTEGER;
 529	param[0].integer.value = 0x01;
 530	param[1].type = ACPI_TYPE_INTEGER;
 531	input.count = 2;
 532	input.pointer = param;
 533	status = acpi_evaluate_object(handle, "ACMD", &input, &output);
 534	if (ACPI_SUCCESS(status)) {
 535		union acpi_object *obj;
 536		obj = output.pointer;
 537		locs = obj->buffer.pointer;
 538		*x = locs[0];
 539		*y = locs[1];
 540		*z = locs[2];
 541		kfree(output.pointer);
 542	}
 543	return status;
 544}
 545
 546static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
 547{
 548	if (event == 0x81) {
 549		unsigned char x, y, z;
 550		acpi_status status;
 551
 552		status = cmpc_get_accel(dev->handle, &x, &y, &z);
 553		if (ACPI_SUCCESS(status)) {
 554			struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
 555
 556			input_report_abs(inputdev, ABS_X, x);
 557			input_report_abs(inputdev, ABS_Y, y);
 558			input_report_abs(inputdev, ABS_Z, z);
 559			input_sync(inputdev);
 560		}
 561	}
 562}
 563
 564static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
 565					   struct device_attribute *attr,
 566					   char *buf)
 567{
 568	struct acpi_device *acpi;
 569	struct input_dev *inputdev;
 570	struct cmpc_accel *accel;
 571
 572	acpi = to_acpi_device(dev);
 573	inputdev = dev_get_drvdata(&acpi->dev);
 574	if (!inputdev)
 575		return -ENXIO;
 576
 577	accel = dev_get_drvdata(&inputdev->dev);
 578	if (!accel)
 579		return -ENXIO;
 580
 581	return sysfs_emit(buf, "%d\n", accel->sensitivity);
 582}
 583
 584static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
 585					    struct device_attribute *attr,
 586					    const char *buf, size_t count)
 587{
 588	struct acpi_device *acpi;
 589	struct input_dev *inputdev;
 590	struct cmpc_accel *accel;
 591	unsigned long sensitivity;
 592	int r;
 593
 594	acpi = to_acpi_device(dev);
 595	inputdev = dev_get_drvdata(&acpi->dev);
 596	if (!inputdev)
 597		return -ENXIO;
 598
 599	accel = dev_get_drvdata(&inputdev->dev);
 600	if (!accel)
 601		return -ENXIO;
 602
 603	r = kstrtoul(buf, 0, &sensitivity);
 604	if (r)
 605		return r;
 606
 607	accel->sensitivity = sensitivity;
 608	cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
 609
 610	return strnlen(buf, count);
 611}
 612
 613static struct device_attribute cmpc_accel_sensitivity_attr = {
 614	.attr = { .name = "sensitivity", .mode = 0660 },
 615	.show = cmpc_accel_sensitivity_show,
 616	.store = cmpc_accel_sensitivity_store
 617};
 618
 619static int cmpc_accel_open(struct input_dev *input)
 620{
 621	struct acpi_device *acpi;
 622
 623	acpi = to_acpi_device(input->dev.parent);
 624	if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
 625		return 0;
 626	return -EIO;
 627}
 628
 629static void cmpc_accel_close(struct input_dev *input)
 630{
 631	struct acpi_device *acpi;
 632
 633	acpi = to_acpi_device(input->dev.parent);
 634	cmpc_stop_accel(acpi->handle);
 635}
 636
 637static void cmpc_accel_idev_init(struct input_dev *inputdev)
 638{
 639	set_bit(EV_ABS, inputdev->evbit);
 640	input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
 641	input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
 642	input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
 643	inputdev->open = cmpc_accel_open;
 644	inputdev->close = cmpc_accel_close;
 645}
 646
 647static int cmpc_accel_add(struct acpi_device *acpi)
 648{
 649	int error;
 650	struct input_dev *inputdev;
 651	struct cmpc_accel *accel;
 652
 653	accel = kmalloc(sizeof(*accel), GFP_KERNEL);
 654	if (!accel)
 655		return -ENOMEM;
 656
 657	accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
 658	cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
 659
 660	error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 661	if (error)
 662		goto failed_file;
 663
 664	error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
 665					    cmpc_accel_idev_init);
 666	if (error)
 667		goto failed_input;
 668
 669	inputdev = dev_get_drvdata(&acpi->dev);
 670	dev_set_drvdata(&inputdev->dev, accel);
 671
 672	return 0;
 673
 674failed_input:
 675	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 676failed_file:
 677	kfree(accel);
 678	return error;
 679}
 680
 681static void cmpc_accel_remove(struct acpi_device *acpi)
 682{
 683	device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
 684	cmpc_remove_acpi_notify_device(acpi);
 685}
 686
 687static const struct acpi_device_id cmpc_accel_device_ids[] = {
 688	{CMPC_ACCEL_HID, 0},
 689	{"", 0}
 690};
 691
 692static struct acpi_driver cmpc_accel_acpi_driver = {
 693	.name = "cmpc_accel",
 694	.class = "cmpc_accel",
 695	.ids = cmpc_accel_device_ids,
 696	.ops = {
 697		.add = cmpc_accel_add,
 698		.remove = cmpc_accel_remove,
 699		.notify = cmpc_accel_handler,
 700	}
 701};
 702
 703
 704/*
 705 * Tablet mode code.
 706 */
 707static acpi_status cmpc_get_tablet(acpi_handle handle,
 708				   unsigned long long *value)
 709{
 710	union acpi_object param;
 711	struct acpi_object_list input;
 712	unsigned long long output;
 713	acpi_status status;
 714
 715	param.type = ACPI_TYPE_INTEGER;
 716	param.integer.value = 0x01;
 717	input.count = 1;
 718	input.pointer = &param;
 719	status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
 720	if (ACPI_SUCCESS(status))
 721		*value = output;
 722	return status;
 723}
 724
 725static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
 726{
 727	unsigned long long val = 0;
 728	struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
 729
 730	if (event == 0x81) {
 731		if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) {
 732			input_report_switch(inputdev, SW_TABLET_MODE, !val);
 733			input_sync(inputdev);
 734		}
 735	}
 736}
 737
 738static void cmpc_tablet_idev_init(struct input_dev *inputdev)
 739{
 740	unsigned long long val = 0;
 741	struct acpi_device *acpi;
 742
 743	set_bit(EV_SW, inputdev->evbit);
 744	set_bit(SW_TABLET_MODE, inputdev->swbit);
 745
 746	acpi = to_acpi_device(inputdev->dev.parent);
 747	if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) {
 748		input_report_switch(inputdev, SW_TABLET_MODE, !val);
 749		input_sync(inputdev);
 750	}
 751}
 752
 753static int cmpc_tablet_add(struct acpi_device *acpi)
 754{
 755	return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
 756					   cmpc_tablet_idev_init);
 757}
 758
 759static void cmpc_tablet_remove(struct acpi_device *acpi)
 760{
 761	cmpc_remove_acpi_notify_device(acpi);
 762}
 763
 764#ifdef CONFIG_PM_SLEEP
 765static int cmpc_tablet_resume(struct device *dev)
 766{
 767	struct input_dev *inputdev = dev_get_drvdata(dev);
 768
 769	unsigned long long val = 0;
 770	if (ACPI_SUCCESS(cmpc_get_tablet(to_acpi_device(dev)->handle, &val))) {
 771		input_report_switch(inputdev, SW_TABLET_MODE, !val);
 772		input_sync(inputdev);
 773	}
 774	return 0;
 775}
 776#endif
 777
 778static SIMPLE_DEV_PM_OPS(cmpc_tablet_pm, NULL, cmpc_tablet_resume);
 779
 780static const struct acpi_device_id cmpc_tablet_device_ids[] = {
 781	{CMPC_TABLET_HID, 0},
 782	{"", 0}
 783};
 784
 785static struct acpi_driver cmpc_tablet_acpi_driver = {
 786	.name = "cmpc_tablet",
 787	.class = "cmpc_tablet",
 788	.ids = cmpc_tablet_device_ids,
 789	.ops = {
 790		.add = cmpc_tablet_add,
 791		.remove = cmpc_tablet_remove,
 792		.notify = cmpc_tablet_handler,
 793	},
 794	.drv.pm = &cmpc_tablet_pm,
 795};
 796
 797
 798/*
 799 * Backlight code.
 800 */
 801
 802static acpi_status cmpc_get_brightness(acpi_handle handle,
 803				       unsigned long long *value)
 804{
 805	union acpi_object param;
 806	struct acpi_object_list input;
 807	unsigned long long output;
 808	acpi_status status;
 809
 810	param.type = ACPI_TYPE_INTEGER;
 811	param.integer.value = 0xC0;
 812	input.count = 1;
 813	input.pointer = &param;
 814	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
 815	if (ACPI_SUCCESS(status))
 816		*value = output;
 817	return status;
 818}
 819
 820static acpi_status cmpc_set_brightness(acpi_handle handle,
 821				       unsigned long long value)
 822{
 823	union acpi_object param[2];
 824	struct acpi_object_list input;
 825	acpi_status status;
 826	unsigned long long output;
 827
 828	param[0].type = ACPI_TYPE_INTEGER;
 829	param[0].integer.value = 0xC0;
 830	param[1].type = ACPI_TYPE_INTEGER;
 831	param[1].integer.value = value;
 832	input.count = 2;
 833	input.pointer = param;
 834	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
 835	return status;
 836}
 837
 838static int cmpc_bl_get_brightness(struct backlight_device *bd)
 839{
 840	acpi_status status;
 841	acpi_handle handle;
 842	unsigned long long brightness;
 843
 844	handle = bl_get_data(bd);
 845	status = cmpc_get_brightness(handle, &brightness);
 846	if (ACPI_SUCCESS(status))
 847		return brightness;
 848	else
 849		return -1;
 850}
 851
 852static int cmpc_bl_update_status(struct backlight_device *bd)
 853{
 854	acpi_status status;
 855	acpi_handle handle;
 856
 857	handle = bl_get_data(bd);
 858	status = cmpc_set_brightness(handle, bd->props.brightness);
 859	if (ACPI_SUCCESS(status))
 860		return 0;
 861	else
 862		return -1;
 863}
 864
 865static const struct backlight_ops cmpc_bl_ops = {
 866	.get_brightness = cmpc_bl_get_brightness,
 867	.update_status = cmpc_bl_update_status
 868};
 869
 870/*
 871 * RFKILL code.
 872 */
 873
 874static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
 875					unsigned long long *value)
 876{
 877	union acpi_object param;
 878	struct acpi_object_list input;
 879	unsigned long long output;
 880	acpi_status status;
 881
 882	param.type = ACPI_TYPE_INTEGER;
 883	param.integer.value = 0xC1;
 884	input.count = 1;
 885	input.pointer = &param;
 886	status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
 887	if (ACPI_SUCCESS(status))
 888		*value = output;
 889	return status;
 890}
 891
 892static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
 893					unsigned long long value)
 894{
 895	union acpi_object param[2];
 896	struct acpi_object_list input;
 897	acpi_status status;
 898	unsigned long long output;
 899
 900	param[0].type = ACPI_TYPE_INTEGER;
 901	param[0].integer.value = 0xC1;
 902	param[1].type = ACPI_TYPE_INTEGER;
 903	param[1].integer.value = value;
 904	input.count = 2;
 905	input.pointer = param;
 906	status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
 907	return status;
 908}
 909
 910static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
 911{
 912	acpi_status status;
 913	acpi_handle handle;
 914	unsigned long long state;
 915	bool blocked;
 916
 917	handle = data;
 918	status = cmpc_get_rfkill_wlan(handle, &state);
 919	if (ACPI_SUCCESS(status)) {
 920		blocked = state & 1 ? false : true;
 921		rfkill_set_sw_state(rfkill, blocked);
 922	}
 923}
 924
 925static int cmpc_rfkill_block(void *data, bool blocked)
 926{
 927	acpi_status status;
 928	acpi_handle handle;
 929	unsigned long long state;
 930	bool is_blocked;
 931
 932	handle = data;
 933	status = cmpc_get_rfkill_wlan(handle, &state);
 934	if (ACPI_FAILURE(status))
 935		return -ENODEV;
 936	/* Check if we really need to call cmpc_set_rfkill_wlan */
 937	is_blocked = state & 1 ? false : true;
 938	if (is_blocked != blocked) {
 939		state = blocked ? 0 : 1;
 940		status = cmpc_set_rfkill_wlan(handle, state);
 941		if (ACPI_FAILURE(status))
 942			return -ENODEV;
 943	}
 944	return 0;
 945}
 946
 947static const struct rfkill_ops cmpc_rfkill_ops = {
 948	.query = cmpc_rfkill_query,
 949	.set_block = cmpc_rfkill_block,
 950};
 951
 952/*
 953 * Common backlight and rfkill code.
 954 */
 955
 956struct ipml200_dev {
 957	struct backlight_device *bd;
 958	struct rfkill *rf;
 959};
 960
 961static int cmpc_ipml_add(struct acpi_device *acpi)
 962{
 963	int retval;
 964	struct ipml200_dev *ipml;
 965	struct backlight_properties props;
 966
 967	ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
 968	if (ipml == NULL)
 969		return -ENOMEM;
 970
 971	memset(&props, 0, sizeof(struct backlight_properties));
 972	props.type = BACKLIGHT_PLATFORM;
 973	props.max_brightness = 7;
 974	ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
 975					     acpi->handle, &cmpc_bl_ops,
 976					     &props);
 977	if (IS_ERR(ipml->bd)) {
 978		retval = PTR_ERR(ipml->bd);
 979		goto out_bd;
 980	}
 981
 982	ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
 983				&cmpc_rfkill_ops, acpi->handle);
 984	/*
 985	 * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
 986	 * This is OK, however, since all other uses of the device will not
 987	 * dereference it.
 988	 */
 989	if (ipml->rf) {
 990		retval = rfkill_register(ipml->rf);
 991		if (retval) {
 992			rfkill_destroy(ipml->rf);
 993			ipml->rf = NULL;
 994		}
 995	}
 996
 997	dev_set_drvdata(&acpi->dev, ipml);
 998	return 0;
 999
1000out_bd:
1001	kfree(ipml);
1002	return retval;
1003}
1004
1005static void cmpc_ipml_remove(struct acpi_device *acpi)
1006{
1007	struct ipml200_dev *ipml;
1008
1009	ipml = dev_get_drvdata(&acpi->dev);
1010
1011	backlight_device_unregister(ipml->bd);
1012
1013	if (ipml->rf) {
1014		rfkill_unregister(ipml->rf);
1015		rfkill_destroy(ipml->rf);
1016	}
1017
1018	kfree(ipml);
1019}
1020
1021static const struct acpi_device_id cmpc_ipml_device_ids[] = {
1022	{CMPC_IPML_HID, 0},
1023	{"", 0}
1024};
1025
1026static struct acpi_driver cmpc_ipml_acpi_driver = {
1027	.name = "cmpc",
1028	.class = "cmpc",
1029	.ids = cmpc_ipml_device_ids,
1030	.ops = {
1031		.add = cmpc_ipml_add,
1032		.remove = cmpc_ipml_remove
1033	}
1034};
1035
1036
1037/*
1038 * Extra keys code.
1039 */
1040static int cmpc_keys_codes[] = {
1041	KEY_UNKNOWN,
1042	KEY_WLAN,
1043	KEY_SWITCHVIDEOMODE,
1044	KEY_BRIGHTNESSDOWN,
1045	KEY_BRIGHTNESSUP,
1046	KEY_VENDOR,
1047	KEY_UNKNOWN,
1048	KEY_CAMERA,
1049	KEY_BACK,
1050	KEY_FORWARD,
1051	KEY_UNKNOWN,
1052	KEY_WLAN, /* NL3: 0x8b (press), 0x9b (release) */
1053	KEY_MAX
1054};
1055
1056static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
1057{
1058	struct input_dev *inputdev;
1059	int code = KEY_MAX;
1060
1061	if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
1062		code = cmpc_keys_codes[event & 0x0F];
1063	inputdev = dev_get_drvdata(&dev->dev);
1064	input_report_key(inputdev, code, !(event & 0x10));
1065	input_sync(inputdev);
1066}
1067
1068static void cmpc_keys_idev_init(struct input_dev *inputdev)
1069{
1070	int i;
1071
1072	set_bit(EV_KEY, inputdev->evbit);
1073	for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
1074		set_bit(cmpc_keys_codes[i], inputdev->keybit);
1075}
1076
1077static int cmpc_keys_add(struct acpi_device *acpi)
1078{
1079	return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
1080					   cmpc_keys_idev_init);
1081}
1082
1083static void cmpc_keys_remove(struct acpi_device *acpi)
1084{
1085	cmpc_remove_acpi_notify_device(acpi);
1086}
1087
1088static const struct acpi_device_id cmpc_keys_device_ids[] = {
1089	{CMPC_KEYS_HID, 0},
1090	{"", 0}
1091};
1092
1093static struct acpi_driver cmpc_keys_acpi_driver = {
1094	.name = "cmpc_keys",
1095	.class = "cmpc_keys",
1096	.ids = cmpc_keys_device_ids,
1097	.ops = {
1098		.add = cmpc_keys_add,
1099		.remove = cmpc_keys_remove,
1100		.notify = cmpc_keys_handler,
1101	}
1102};
1103
1104
1105/*
1106 * General init/exit code.
1107 */
1108
1109static int cmpc_init(void)
1110{
1111	int r;
1112
1113	r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
1114	if (r)
1115		goto failed_keys;
1116
1117	r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
1118	if (r)
1119		goto failed_bl;
1120
1121	r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
1122	if (r)
1123		goto failed_tablet;
1124
1125	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
1126	if (r)
1127		goto failed_accel;
1128
1129	r = acpi_bus_register_driver(&cmpc_accel_acpi_driver_v4);
1130	if (r)
1131		goto failed_accel_v4;
1132
1133	return r;
1134
1135failed_accel_v4:
1136	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1137
1138failed_accel:
1139	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
1140
1141failed_tablet:
1142	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
1143
1144failed_bl:
1145	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
1146
1147failed_keys:
1148	return r;
1149}
1150
1151static void cmpc_exit(void)
1152{
1153	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver_v4);
1154	acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
1155	acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
1156	acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
1157	acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
1158}
1159
1160module_init(cmpc_init);
1161module_exit(cmpc_exit);
1162
1163static const struct acpi_device_id cmpc_device_ids[] __maybe_unused = {
1164	{CMPC_ACCEL_HID, 0},
1165	{CMPC_ACCEL_HID_V4, 0},
1166	{CMPC_TABLET_HID, 0},
1167	{CMPC_IPML_HID, 0},
1168	{CMPC_KEYS_HID, 0},
1169	{"", 0}
1170};
1171
1172MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
1173MODULE_DESCRIPTION("Support for Intel Classmate PC ACPI devices");
1174MODULE_LICENSE("GPL");
~/ Read-only file preview.