Linux Audio

Check our new training course

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