Linux Audio

Check our new training course

Linux kernel drivers training

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