Linux Audio

Check our new training course

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