Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v2.6.34-rc3 628 lines 15 kB view raw
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/workqueue.h> 23#include <acpi/acpi_drivers.h> 24#include <linux/backlight.h> 25#include <linux/input.h> 26 27MODULE_LICENSE("GPL"); 28 29 30struct cmpc_accel { 31 int sensitivity; 32}; 33 34#define CMPC_ACCEL_SENSITIVITY_DEFAULT 5 35 36 37#define CMPC_ACCEL_HID "ACCE0000" 38#define CMPC_TABLET_HID "TBLT0000" 39#define CMPC_BL_HID "IPML200" 40#define CMPC_KEYS_HID "FnBT0000" 41 42/* 43 * Generic input device code. 44 */ 45 46typedef void (*input_device_init)(struct input_dev *dev); 47 48static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name, 49 input_device_init idev_init) 50{ 51 struct input_dev *inputdev; 52 int error; 53 54 inputdev = input_allocate_device(); 55 if (!inputdev) 56 return -ENOMEM; 57 inputdev->name = name; 58 inputdev->dev.parent = &acpi->dev; 59 idev_init(inputdev); 60 error = input_register_device(inputdev); 61 if (error) { 62 input_free_device(inputdev); 63 return error; 64 } 65 dev_set_drvdata(&acpi->dev, inputdev); 66 return 0; 67} 68 69static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi) 70{ 71 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 72 input_unregister_device(inputdev); 73 return 0; 74} 75 76/* 77 * Accelerometer code. 78 */ 79static acpi_status cmpc_start_accel(acpi_handle handle) 80{ 81 union acpi_object param[2]; 82 struct acpi_object_list input; 83 acpi_status status; 84 85 param[0].type = ACPI_TYPE_INTEGER; 86 param[0].integer.value = 0x3; 87 param[1].type = ACPI_TYPE_INTEGER; 88 input.count = 2; 89 input.pointer = param; 90 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 91 return status; 92} 93 94static acpi_status cmpc_stop_accel(acpi_handle handle) 95{ 96 union acpi_object param[2]; 97 struct acpi_object_list input; 98 acpi_status status; 99 100 param[0].type = ACPI_TYPE_INTEGER; 101 param[0].integer.value = 0x4; 102 param[1].type = ACPI_TYPE_INTEGER; 103 input.count = 2; 104 input.pointer = param; 105 status = acpi_evaluate_object(handle, "ACMD", &input, NULL); 106 return status; 107} 108 109static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val) 110{ 111 union acpi_object param[2]; 112 struct acpi_object_list input; 113 114 param[0].type = ACPI_TYPE_INTEGER; 115 param[0].integer.value = 0x02; 116 param[1].type = ACPI_TYPE_INTEGER; 117 param[1].integer.value = val; 118 input.count = 2; 119 input.pointer = param; 120 return acpi_evaluate_object(handle, "ACMD", &input, NULL); 121} 122 123static acpi_status cmpc_get_accel(acpi_handle handle, 124 unsigned char *x, 125 unsigned char *y, 126 unsigned char *z) 127{ 128 union acpi_object param[2]; 129 struct acpi_object_list input; 130 struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 }; 131 unsigned char *locs; 132 acpi_status status; 133 134 param[0].type = ACPI_TYPE_INTEGER; 135 param[0].integer.value = 0x01; 136 param[1].type = ACPI_TYPE_INTEGER; 137 input.count = 2; 138 input.pointer = param; 139 status = acpi_evaluate_object(handle, "ACMD", &input, &output); 140 if (ACPI_SUCCESS(status)) { 141 union acpi_object *obj; 142 obj = output.pointer; 143 locs = obj->buffer.pointer; 144 *x = locs[0]; 145 *y = locs[1]; 146 *z = locs[2]; 147 kfree(output.pointer); 148 } 149 return status; 150} 151 152static void cmpc_accel_handler(struct acpi_device *dev, u32 event) 153{ 154 if (event == 0x81) { 155 unsigned char x, y, z; 156 acpi_status status; 157 158 status = cmpc_get_accel(dev->handle, &x, &y, &z); 159 if (ACPI_SUCCESS(status)) { 160 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 161 162 input_report_abs(inputdev, ABS_X, x); 163 input_report_abs(inputdev, ABS_Y, y); 164 input_report_abs(inputdev, ABS_Z, z); 165 input_sync(inputdev); 166 } 167 } 168} 169 170static ssize_t cmpc_accel_sensitivity_show(struct device *dev, 171 struct device_attribute *attr, 172 char *buf) 173{ 174 struct acpi_device *acpi; 175 struct input_dev *inputdev; 176 struct cmpc_accel *accel; 177 178 acpi = to_acpi_device(dev); 179 inputdev = dev_get_drvdata(&acpi->dev); 180 accel = dev_get_drvdata(&inputdev->dev); 181 182 return sprintf(buf, "%d\n", accel->sensitivity); 183} 184 185static ssize_t cmpc_accel_sensitivity_store(struct device *dev, 186 struct device_attribute *attr, 187 const char *buf, size_t count) 188{ 189 struct acpi_device *acpi; 190 struct input_dev *inputdev; 191 struct cmpc_accel *accel; 192 unsigned long sensitivity; 193 int r; 194 195 acpi = to_acpi_device(dev); 196 inputdev = dev_get_drvdata(&acpi->dev); 197 accel = dev_get_drvdata(&inputdev->dev); 198 199 r = strict_strtoul(buf, 0, &sensitivity); 200 if (r) 201 return r; 202 203 accel->sensitivity = sensitivity; 204 cmpc_accel_set_sensitivity(acpi->handle, sensitivity); 205 206 return strnlen(buf, count); 207} 208 209struct device_attribute cmpc_accel_sensitivity_attr = { 210 .attr = { .name = "sensitivity", .mode = 0660 }, 211 .show = cmpc_accel_sensitivity_show, 212 .store = cmpc_accel_sensitivity_store 213}; 214 215static int cmpc_accel_open(struct input_dev *input) 216{ 217 struct acpi_device *acpi; 218 219 acpi = to_acpi_device(input->dev.parent); 220 if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle))) 221 return 0; 222 return -EIO; 223} 224 225static void cmpc_accel_close(struct input_dev *input) 226{ 227 struct acpi_device *acpi; 228 229 acpi = to_acpi_device(input->dev.parent); 230 cmpc_stop_accel(acpi->handle); 231} 232 233static void cmpc_accel_idev_init(struct input_dev *inputdev) 234{ 235 set_bit(EV_ABS, inputdev->evbit); 236 input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0); 237 input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0); 238 input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0); 239 inputdev->open = cmpc_accel_open; 240 inputdev->close = cmpc_accel_close; 241} 242 243static int cmpc_accel_add(struct acpi_device *acpi) 244{ 245 int error; 246 struct input_dev *inputdev; 247 struct cmpc_accel *accel; 248 249 accel = kmalloc(sizeof(*accel), GFP_KERNEL); 250 if (!accel) 251 return -ENOMEM; 252 253 accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT; 254 cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity); 255 256 error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 257 if (error) 258 goto failed_file; 259 260 error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel", 261 cmpc_accel_idev_init); 262 if (error) 263 goto failed_input; 264 265 inputdev = dev_get_drvdata(&acpi->dev); 266 dev_set_drvdata(&inputdev->dev, accel); 267 268 return 0; 269 270failed_input: 271 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 272failed_file: 273 kfree(accel); 274 return error; 275} 276 277static int cmpc_accel_remove(struct acpi_device *acpi, int type) 278{ 279 struct input_dev *inputdev; 280 struct cmpc_accel *accel; 281 282 inputdev = dev_get_drvdata(&acpi->dev); 283 accel = dev_get_drvdata(&inputdev->dev); 284 285 device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr); 286 return cmpc_remove_acpi_notify_device(acpi); 287} 288 289static const struct acpi_device_id cmpc_accel_device_ids[] = { 290 {CMPC_ACCEL_HID, 0}, 291 {"", 0} 292}; 293 294static struct acpi_driver cmpc_accel_acpi_driver = { 295 .owner = THIS_MODULE, 296 .name = "cmpc_accel", 297 .class = "cmpc_accel", 298 .ids = cmpc_accel_device_ids, 299 .ops = { 300 .add = cmpc_accel_add, 301 .remove = cmpc_accel_remove, 302 .notify = cmpc_accel_handler, 303 } 304}; 305 306 307/* 308 * Tablet mode code. 309 */ 310static acpi_status cmpc_get_tablet(acpi_handle handle, 311 unsigned long long *value) 312{ 313 union acpi_object param; 314 struct acpi_object_list input; 315 unsigned long long output; 316 acpi_status status; 317 318 param.type = ACPI_TYPE_INTEGER; 319 param.integer.value = 0x01; 320 input.count = 1; 321 input.pointer = &param; 322 status = acpi_evaluate_integer(handle, "TCMD", &input, &output); 323 if (ACPI_SUCCESS(status)) 324 *value = output; 325 return status; 326} 327 328static void cmpc_tablet_handler(struct acpi_device *dev, u32 event) 329{ 330 unsigned long long val = 0; 331 struct input_dev *inputdev = dev_get_drvdata(&dev->dev); 332 333 if (event == 0x81) { 334 if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val))) 335 input_report_switch(inputdev, SW_TABLET_MODE, !val); 336 } 337} 338 339static void cmpc_tablet_idev_init(struct input_dev *inputdev) 340{ 341 unsigned long long val = 0; 342 struct acpi_device *acpi; 343 344 set_bit(EV_SW, inputdev->evbit); 345 set_bit(SW_TABLET_MODE, inputdev->swbit); 346 347 acpi = to_acpi_device(inputdev->dev.parent); 348 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 349 input_report_switch(inputdev, SW_TABLET_MODE, !val); 350} 351 352static int cmpc_tablet_add(struct acpi_device *acpi) 353{ 354 return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet", 355 cmpc_tablet_idev_init); 356} 357 358static int cmpc_tablet_remove(struct acpi_device *acpi, int type) 359{ 360 return cmpc_remove_acpi_notify_device(acpi); 361} 362 363static int cmpc_tablet_resume(struct acpi_device *acpi) 364{ 365 struct input_dev *inputdev = dev_get_drvdata(&acpi->dev); 366 unsigned long long val = 0; 367 if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val))) 368 input_report_switch(inputdev, SW_TABLET_MODE, !val); 369 return 0; 370} 371 372static const struct acpi_device_id cmpc_tablet_device_ids[] = { 373 {CMPC_TABLET_HID, 0}, 374 {"", 0} 375}; 376 377static struct acpi_driver cmpc_tablet_acpi_driver = { 378 .owner = THIS_MODULE, 379 .name = "cmpc_tablet", 380 .class = "cmpc_tablet", 381 .ids = cmpc_tablet_device_ids, 382 .ops = { 383 .add = cmpc_tablet_add, 384 .remove = cmpc_tablet_remove, 385 .resume = cmpc_tablet_resume, 386 .notify = cmpc_tablet_handler, 387 } 388}; 389 390 391/* 392 * Backlight code. 393 */ 394 395static acpi_status cmpc_get_brightness(acpi_handle handle, 396 unsigned long long *value) 397{ 398 union acpi_object param; 399 struct acpi_object_list input; 400 unsigned long long output; 401 acpi_status status; 402 403 param.type = ACPI_TYPE_INTEGER; 404 param.integer.value = 0xC0; 405 input.count = 1; 406 input.pointer = &param; 407 status = acpi_evaluate_integer(handle, "GRDI", &input, &output); 408 if (ACPI_SUCCESS(status)) 409 *value = output; 410 return status; 411} 412 413static acpi_status cmpc_set_brightness(acpi_handle handle, 414 unsigned long long value) 415{ 416 union acpi_object param[2]; 417 struct acpi_object_list input; 418 acpi_status status; 419 unsigned long long output; 420 421 param[0].type = ACPI_TYPE_INTEGER; 422 param[0].integer.value = 0xC0; 423 param[1].type = ACPI_TYPE_INTEGER; 424 param[1].integer.value = value; 425 input.count = 2; 426 input.pointer = param; 427 status = acpi_evaluate_integer(handle, "GWRI", &input, &output); 428 return status; 429} 430 431static int cmpc_bl_get_brightness(struct backlight_device *bd) 432{ 433 acpi_status status; 434 acpi_handle handle; 435 unsigned long long brightness; 436 437 handle = bl_get_data(bd); 438 status = cmpc_get_brightness(handle, &brightness); 439 if (ACPI_SUCCESS(status)) 440 return brightness; 441 else 442 return -1; 443} 444 445static int cmpc_bl_update_status(struct backlight_device *bd) 446{ 447 acpi_status status; 448 acpi_handle handle; 449 450 handle = bl_get_data(bd); 451 status = cmpc_set_brightness(handle, bd->props.brightness); 452 if (ACPI_SUCCESS(status)) 453 return 0; 454 else 455 return -1; 456} 457 458static const struct backlight_ops cmpc_bl_ops = { 459 .get_brightness = cmpc_bl_get_brightness, 460 .update_status = cmpc_bl_update_status 461}; 462 463static int cmpc_bl_add(struct acpi_device *acpi) 464{ 465 struct backlight_properties props; 466 struct backlight_device *bd; 467 468 memset(&props, 0, sizeof(struct backlight_properties)); 469 props.max_brightness = 7; 470 bd = backlight_device_register("cmpc_bl", &acpi->dev, acpi->handle, 471 &cmpc_bl_ops, &props); 472 if (IS_ERR(bd)) 473 return PTR_ERR(bd); 474 dev_set_drvdata(&acpi->dev, bd); 475 return 0; 476} 477 478static int cmpc_bl_remove(struct acpi_device *acpi, int type) 479{ 480 struct backlight_device *bd; 481 482 bd = dev_get_drvdata(&acpi->dev); 483 backlight_device_unregister(bd); 484 return 0; 485} 486 487static const struct acpi_device_id cmpc_bl_device_ids[] = { 488 {CMPC_BL_HID, 0}, 489 {"", 0} 490}; 491 492static struct acpi_driver cmpc_bl_acpi_driver = { 493 .owner = THIS_MODULE, 494 .name = "cmpc", 495 .class = "cmpc", 496 .ids = cmpc_bl_device_ids, 497 .ops = { 498 .add = cmpc_bl_add, 499 .remove = cmpc_bl_remove 500 } 501}; 502 503 504/* 505 * Extra keys code. 506 */ 507static int cmpc_keys_codes[] = { 508 KEY_UNKNOWN, 509 KEY_WLAN, 510 KEY_SWITCHVIDEOMODE, 511 KEY_BRIGHTNESSDOWN, 512 KEY_BRIGHTNESSUP, 513 KEY_VENDOR, 514 KEY_UNKNOWN, 515 KEY_CAMERA, 516 KEY_BACK, 517 KEY_FORWARD, 518 KEY_MAX 519}; 520 521static void cmpc_keys_handler(struct acpi_device *dev, u32 event) 522{ 523 struct input_dev *inputdev; 524 int code = KEY_MAX; 525 526 if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes)) 527 code = cmpc_keys_codes[event & 0x0F]; 528 inputdev = dev_get_drvdata(&dev->dev);; 529 input_report_key(inputdev, code, !(event & 0x10)); 530} 531 532static void cmpc_keys_idev_init(struct input_dev *inputdev) 533{ 534 int i; 535 536 set_bit(EV_KEY, inputdev->evbit); 537 for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++) 538 set_bit(cmpc_keys_codes[i], inputdev->keybit); 539} 540 541static int cmpc_keys_add(struct acpi_device *acpi) 542{ 543 return cmpc_add_acpi_notify_device(acpi, "cmpc_keys", 544 cmpc_keys_idev_init); 545} 546 547static int cmpc_keys_remove(struct acpi_device *acpi, int type) 548{ 549 return cmpc_remove_acpi_notify_device(acpi); 550} 551 552static const struct acpi_device_id cmpc_keys_device_ids[] = { 553 {CMPC_KEYS_HID, 0}, 554 {"", 0} 555}; 556 557static struct acpi_driver cmpc_keys_acpi_driver = { 558 .owner = THIS_MODULE, 559 .name = "cmpc_keys", 560 .class = "cmpc_keys", 561 .ids = cmpc_keys_device_ids, 562 .ops = { 563 .add = cmpc_keys_add, 564 .remove = cmpc_keys_remove, 565 .notify = cmpc_keys_handler, 566 } 567}; 568 569 570/* 571 * General init/exit code. 572 */ 573 574static int cmpc_init(void) 575{ 576 int r; 577 578 r = acpi_bus_register_driver(&cmpc_keys_acpi_driver); 579 if (r) 580 goto failed_keys; 581 582 r = acpi_bus_register_driver(&cmpc_bl_acpi_driver); 583 if (r) 584 goto failed_bl; 585 586 r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver); 587 if (r) 588 goto failed_tablet; 589 590 r = acpi_bus_register_driver(&cmpc_accel_acpi_driver); 591 if (r) 592 goto failed_accel; 593 594 return r; 595 596failed_accel: 597 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 598 599failed_tablet: 600 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); 601 602failed_bl: 603 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 604 605failed_keys: 606 return r; 607} 608 609static void cmpc_exit(void) 610{ 611 acpi_bus_unregister_driver(&cmpc_accel_acpi_driver); 612 acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver); 613 acpi_bus_unregister_driver(&cmpc_bl_acpi_driver); 614 acpi_bus_unregister_driver(&cmpc_keys_acpi_driver); 615} 616 617module_init(cmpc_init); 618module_exit(cmpc_exit); 619 620static const struct acpi_device_id cmpc_device_ids[] = { 621 {CMPC_ACCEL_HID, 0}, 622 {CMPC_TABLET_HID, 0}, 623 {CMPC_BL_HID, 0}, 624 {CMPC_KEYS_HID, 0}, 625 {"", 0} 626}; 627 628MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);