123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761 |
- /*
- * Copyright (C) 2009 Thadeu Lima de Souza Cascardo <cascardo@holoscopio.com>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- */
- #include <linux/init.h>
- #include <linux/module.h>
- #include <linux/slab.h>
- #include <linux/workqueue.h>
- #include <acpi/acpi_drivers.h>
- #include <linux/backlight.h>
- #include <linux/input.h>
- #include <linux/rfkill.h>
- MODULE_LICENSE("GPL");
- struct cmpc_accel {
- int sensitivity;
- };
- #define CMPC_ACCEL_SENSITIVITY_DEFAULT 5
- #define CMPC_ACCEL_HID "ACCE0000"
- #define CMPC_TABLET_HID "TBLT0000"
- #define CMPC_IPML_HID "IPML200"
- #define CMPC_KEYS_HID "FnBT0000"
- /*
- * Generic input device code.
- */
- typedef void (*input_device_init)(struct input_dev *dev);
- static int cmpc_add_acpi_notify_device(struct acpi_device *acpi, char *name,
- input_device_init idev_init)
- {
- struct input_dev *inputdev;
- int error;
- inputdev = input_allocate_device();
- if (!inputdev)
- return -ENOMEM;
- inputdev->name = name;
- inputdev->dev.parent = &acpi->dev;
- idev_init(inputdev);
- error = input_register_device(inputdev);
- if (error) {
- input_free_device(inputdev);
- return error;
- }
- dev_set_drvdata(&acpi->dev, inputdev);
- return 0;
- }
- static int cmpc_remove_acpi_notify_device(struct acpi_device *acpi)
- {
- struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
- input_unregister_device(inputdev);
- return 0;
- }
- /*
- * Accelerometer code.
- */
- static acpi_status cmpc_start_accel(acpi_handle handle)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- acpi_status status;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0x3;
- param[1].type = ACPI_TYPE_INTEGER;
- input.count = 2;
- input.pointer = param;
- status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
- return status;
- }
- static acpi_status cmpc_stop_accel(acpi_handle handle)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- acpi_status status;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0x4;
- param[1].type = ACPI_TYPE_INTEGER;
- input.count = 2;
- input.pointer = param;
- status = acpi_evaluate_object(handle, "ACMD", &input, NULL);
- return status;
- }
- static acpi_status cmpc_accel_set_sensitivity(acpi_handle handle, int val)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0x02;
- param[1].type = ACPI_TYPE_INTEGER;
- param[1].integer.value = val;
- input.count = 2;
- input.pointer = param;
- return acpi_evaluate_object(handle, "ACMD", &input, NULL);
- }
- static acpi_status cmpc_get_accel(acpi_handle handle,
- unsigned char *x,
- unsigned char *y,
- unsigned char *z)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, 0 };
- unsigned char *locs;
- acpi_status status;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0x01;
- param[1].type = ACPI_TYPE_INTEGER;
- input.count = 2;
- input.pointer = param;
- status = acpi_evaluate_object(handle, "ACMD", &input, &output);
- if (ACPI_SUCCESS(status)) {
- union acpi_object *obj;
- obj = output.pointer;
- locs = obj->buffer.pointer;
- *x = locs[0];
- *y = locs[1];
- *z = locs[2];
- kfree(output.pointer);
- }
- return status;
- }
- static void cmpc_accel_handler(struct acpi_device *dev, u32 event)
- {
- if (event == 0x81) {
- unsigned char x, y, z;
- acpi_status status;
- status = cmpc_get_accel(dev->handle, &x, &y, &z);
- if (ACPI_SUCCESS(status)) {
- struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
- input_report_abs(inputdev, ABS_X, x);
- input_report_abs(inputdev, ABS_Y, y);
- input_report_abs(inputdev, ABS_Z, z);
- input_sync(inputdev);
- }
- }
- }
- static ssize_t cmpc_accel_sensitivity_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
- {
- struct acpi_device *acpi;
- struct input_dev *inputdev;
- struct cmpc_accel *accel;
- acpi = to_acpi_device(dev);
- inputdev = dev_get_drvdata(&acpi->dev);
- accel = dev_get_drvdata(&inputdev->dev);
- return sprintf(buf, "%d\n", accel->sensitivity);
- }
- static ssize_t cmpc_accel_sensitivity_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
- {
- struct acpi_device *acpi;
- struct input_dev *inputdev;
- struct cmpc_accel *accel;
- unsigned long sensitivity;
- int r;
- acpi = to_acpi_device(dev);
- inputdev = dev_get_drvdata(&acpi->dev);
- accel = dev_get_drvdata(&inputdev->dev);
- r = strict_strtoul(buf, 0, &sensitivity);
- if (r)
- return r;
- accel->sensitivity = sensitivity;
- cmpc_accel_set_sensitivity(acpi->handle, sensitivity);
- return strnlen(buf, count);
- }
- static struct device_attribute cmpc_accel_sensitivity_attr = {
- .attr = { .name = "sensitivity", .mode = 0660 },
- .show = cmpc_accel_sensitivity_show,
- .store = cmpc_accel_sensitivity_store
- };
- static int cmpc_accel_open(struct input_dev *input)
- {
- struct acpi_device *acpi;
- acpi = to_acpi_device(input->dev.parent);
- if (ACPI_SUCCESS(cmpc_start_accel(acpi->handle)))
- return 0;
- return -EIO;
- }
- static void cmpc_accel_close(struct input_dev *input)
- {
- struct acpi_device *acpi;
- acpi = to_acpi_device(input->dev.parent);
- cmpc_stop_accel(acpi->handle);
- }
- static void cmpc_accel_idev_init(struct input_dev *inputdev)
- {
- set_bit(EV_ABS, inputdev->evbit);
- input_set_abs_params(inputdev, ABS_X, 0, 255, 8, 0);
- input_set_abs_params(inputdev, ABS_Y, 0, 255, 8, 0);
- input_set_abs_params(inputdev, ABS_Z, 0, 255, 8, 0);
- inputdev->open = cmpc_accel_open;
- inputdev->close = cmpc_accel_close;
- }
- static int cmpc_accel_add(struct acpi_device *acpi)
- {
- int error;
- struct input_dev *inputdev;
- struct cmpc_accel *accel;
- accel = kmalloc(sizeof(*accel), GFP_KERNEL);
- if (!accel)
- return -ENOMEM;
- accel->sensitivity = CMPC_ACCEL_SENSITIVITY_DEFAULT;
- cmpc_accel_set_sensitivity(acpi->handle, accel->sensitivity);
- error = device_create_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
- if (error)
- goto failed_file;
- error = cmpc_add_acpi_notify_device(acpi, "cmpc_accel",
- cmpc_accel_idev_init);
- if (error)
- goto failed_input;
- inputdev = dev_get_drvdata(&acpi->dev);
- dev_set_drvdata(&inputdev->dev, accel);
- return 0;
- failed_input:
- device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
- failed_file:
- kfree(accel);
- return error;
- }
- static int cmpc_accel_remove(struct acpi_device *acpi, int type)
- {
- struct input_dev *inputdev;
- struct cmpc_accel *accel;
- inputdev = dev_get_drvdata(&acpi->dev);
- accel = dev_get_drvdata(&inputdev->dev);
- device_remove_file(&acpi->dev, &cmpc_accel_sensitivity_attr);
- return cmpc_remove_acpi_notify_device(acpi);
- }
- static const struct acpi_device_id cmpc_accel_device_ids[] = {
- {CMPC_ACCEL_HID, 0},
- {"", 0}
- };
- static struct acpi_driver cmpc_accel_acpi_driver = {
- .owner = THIS_MODULE,
- .name = "cmpc_accel",
- .class = "cmpc_accel",
- .ids = cmpc_accel_device_ids,
- .ops = {
- .add = cmpc_accel_add,
- .remove = cmpc_accel_remove,
- .notify = cmpc_accel_handler,
- }
- };
- /*
- * Tablet mode code.
- */
- static acpi_status cmpc_get_tablet(acpi_handle handle,
- unsigned long long *value)
- {
- union acpi_object param;
- struct acpi_object_list input;
- unsigned long long output;
- acpi_status status;
- param.type = ACPI_TYPE_INTEGER;
- param.integer.value = 0x01;
- input.count = 1;
- input.pointer = ¶m;
- status = acpi_evaluate_integer(handle, "TCMD", &input, &output);
- if (ACPI_SUCCESS(status))
- *value = output;
- return status;
- }
- static void cmpc_tablet_handler(struct acpi_device *dev, u32 event)
- {
- unsigned long long val = 0;
- struct input_dev *inputdev = dev_get_drvdata(&dev->dev);
- if (event == 0x81) {
- if (ACPI_SUCCESS(cmpc_get_tablet(dev->handle, &val)))
- input_report_switch(inputdev, SW_TABLET_MODE, !val);
- }
- }
- static void cmpc_tablet_idev_init(struct input_dev *inputdev)
- {
- unsigned long long val = 0;
- struct acpi_device *acpi;
- set_bit(EV_SW, inputdev->evbit);
- set_bit(SW_TABLET_MODE, inputdev->swbit);
- acpi = to_acpi_device(inputdev->dev.parent);
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
- input_report_switch(inputdev, SW_TABLET_MODE, !val);
- }
- static int cmpc_tablet_add(struct acpi_device *acpi)
- {
- return cmpc_add_acpi_notify_device(acpi, "cmpc_tablet",
- cmpc_tablet_idev_init);
- }
- static int cmpc_tablet_remove(struct acpi_device *acpi, int type)
- {
- return cmpc_remove_acpi_notify_device(acpi);
- }
- static int cmpc_tablet_resume(struct acpi_device *acpi)
- {
- struct input_dev *inputdev = dev_get_drvdata(&acpi->dev);
- unsigned long long val = 0;
- if (ACPI_SUCCESS(cmpc_get_tablet(acpi->handle, &val)))
- input_report_switch(inputdev, SW_TABLET_MODE, !val);
- return 0;
- }
- static const struct acpi_device_id cmpc_tablet_device_ids[] = {
- {CMPC_TABLET_HID, 0},
- {"", 0}
- };
- static struct acpi_driver cmpc_tablet_acpi_driver = {
- .owner = THIS_MODULE,
- .name = "cmpc_tablet",
- .class = "cmpc_tablet",
- .ids = cmpc_tablet_device_ids,
- .ops = {
- .add = cmpc_tablet_add,
- .remove = cmpc_tablet_remove,
- .resume = cmpc_tablet_resume,
- .notify = cmpc_tablet_handler,
- }
- };
- /*
- * Backlight code.
- */
- static acpi_status cmpc_get_brightness(acpi_handle handle,
- unsigned long long *value)
- {
- union acpi_object param;
- struct acpi_object_list input;
- unsigned long long output;
- acpi_status status;
- param.type = ACPI_TYPE_INTEGER;
- param.integer.value = 0xC0;
- input.count = 1;
- input.pointer = ¶m;
- status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
- if (ACPI_SUCCESS(status))
- *value = output;
- return status;
- }
- static acpi_status cmpc_set_brightness(acpi_handle handle,
- unsigned long long value)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- acpi_status status;
- unsigned long long output;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0xC0;
- param[1].type = ACPI_TYPE_INTEGER;
- param[1].integer.value = value;
- input.count = 2;
- input.pointer = param;
- status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
- return status;
- }
- static int cmpc_bl_get_brightness(struct backlight_device *bd)
- {
- acpi_status status;
- acpi_handle handle;
- unsigned long long brightness;
- handle = bl_get_data(bd);
- status = cmpc_get_brightness(handle, &brightness);
- if (ACPI_SUCCESS(status))
- return brightness;
- else
- return -1;
- }
- static int cmpc_bl_update_status(struct backlight_device *bd)
- {
- acpi_status status;
- acpi_handle handle;
- handle = bl_get_data(bd);
- status = cmpc_set_brightness(handle, bd->props.brightness);
- if (ACPI_SUCCESS(status))
- return 0;
- else
- return -1;
- }
- static const struct backlight_ops cmpc_bl_ops = {
- .get_brightness = cmpc_bl_get_brightness,
- .update_status = cmpc_bl_update_status
- };
- /*
- * RFKILL code.
- */
- static acpi_status cmpc_get_rfkill_wlan(acpi_handle handle,
- unsigned long long *value)
- {
- union acpi_object param;
- struct acpi_object_list input;
- unsigned long long output;
- acpi_status status;
- param.type = ACPI_TYPE_INTEGER;
- param.integer.value = 0xC1;
- input.count = 1;
- input.pointer = ¶m;
- status = acpi_evaluate_integer(handle, "GRDI", &input, &output);
- if (ACPI_SUCCESS(status))
- *value = output;
- return status;
- }
- static acpi_status cmpc_set_rfkill_wlan(acpi_handle handle,
- unsigned long long value)
- {
- union acpi_object param[2];
- struct acpi_object_list input;
- acpi_status status;
- unsigned long long output;
- param[0].type = ACPI_TYPE_INTEGER;
- param[0].integer.value = 0xC1;
- param[1].type = ACPI_TYPE_INTEGER;
- param[1].integer.value = value;
- input.count = 2;
- input.pointer = param;
- status = acpi_evaluate_integer(handle, "GWRI", &input, &output);
- return status;
- }
- static void cmpc_rfkill_query(struct rfkill *rfkill, void *data)
- {
- acpi_status status;
- acpi_handle handle;
- unsigned long long state;
- bool blocked;
- handle = data;
- status = cmpc_get_rfkill_wlan(handle, &state);
- if (ACPI_SUCCESS(status)) {
- blocked = state & 1 ? false : true;
- rfkill_set_sw_state(rfkill, blocked);
- }
- }
- static int cmpc_rfkill_block(void *data, bool blocked)
- {
- acpi_status status;
- acpi_handle handle;
- unsigned long long state;
- bool is_blocked;
- handle = data;
- status = cmpc_get_rfkill_wlan(handle, &state);
- if (ACPI_FAILURE(status))
- return -ENODEV;
- /* Check if we really need to call cmpc_set_rfkill_wlan */
- is_blocked = state & 1 ? false : true;
- if (is_blocked != blocked) {
- state = blocked ? 0 : 1;
- status = cmpc_set_rfkill_wlan(handle, state);
- if (ACPI_FAILURE(status))
- return -ENODEV;
- }
- return 0;
- }
- static const struct rfkill_ops cmpc_rfkill_ops = {
- .query = cmpc_rfkill_query,
- .set_block = cmpc_rfkill_block,
- };
- /*
- * Common backlight and rfkill code.
- */
- struct ipml200_dev {
- struct backlight_device *bd;
- struct rfkill *rf;
- };
- static int cmpc_ipml_add(struct acpi_device *acpi)
- {
- int retval;
- struct ipml200_dev *ipml;
- struct backlight_properties props;
- ipml = kmalloc(sizeof(*ipml), GFP_KERNEL);
- if (ipml == NULL)
- return -ENOMEM;
- memset(&props, 0, sizeof(struct backlight_properties));
- props.type = BACKLIGHT_PLATFORM;
- props.max_brightness = 7;
- ipml->bd = backlight_device_register("cmpc_bl", &acpi->dev,
- acpi->handle, &cmpc_bl_ops,
- &props);
- if (IS_ERR(ipml->bd)) {
- retval = PTR_ERR(ipml->bd);
- goto out_bd;
- }
- ipml->rf = rfkill_alloc("cmpc_rfkill", &acpi->dev, RFKILL_TYPE_WLAN,
- &cmpc_rfkill_ops, acpi->handle);
- /*
- * If RFKILL is disabled, rfkill_alloc will return ERR_PTR(-ENODEV).
- * This is OK, however, since all other uses of the device will not
- * derefence it.
- */
- if (ipml->rf) {
- retval = rfkill_register(ipml->rf);
- if (retval) {
- rfkill_destroy(ipml->rf);
- ipml->rf = NULL;
- }
- }
- dev_set_drvdata(&acpi->dev, ipml);
- return 0;
- out_bd:
- kfree(ipml);
- return retval;
- }
- static int cmpc_ipml_remove(struct acpi_device *acpi, int type)
- {
- struct ipml200_dev *ipml;
- ipml = dev_get_drvdata(&acpi->dev);
- backlight_device_unregister(ipml->bd);
- if (ipml->rf) {
- rfkill_unregister(ipml->rf);
- rfkill_destroy(ipml->rf);
- }
- kfree(ipml);
- return 0;
- }
- static const struct acpi_device_id cmpc_ipml_device_ids[] = {
- {CMPC_IPML_HID, 0},
- {"", 0}
- };
- static struct acpi_driver cmpc_ipml_acpi_driver = {
- .owner = THIS_MODULE,
- .name = "cmpc",
- .class = "cmpc",
- .ids = cmpc_ipml_device_ids,
- .ops = {
- .add = cmpc_ipml_add,
- .remove = cmpc_ipml_remove
- }
- };
- /*
- * Extra keys code.
- */
- static int cmpc_keys_codes[] = {
- KEY_UNKNOWN,
- KEY_WLAN,
- KEY_SWITCHVIDEOMODE,
- KEY_BRIGHTNESSDOWN,
- KEY_BRIGHTNESSUP,
- KEY_VENDOR,
- KEY_UNKNOWN,
- KEY_CAMERA,
- KEY_BACK,
- KEY_FORWARD,
- KEY_MAX
- };
- static void cmpc_keys_handler(struct acpi_device *dev, u32 event)
- {
- struct input_dev *inputdev;
- int code = KEY_MAX;
- if ((event & 0x0F) < ARRAY_SIZE(cmpc_keys_codes))
- code = cmpc_keys_codes[event & 0x0F];
- inputdev = dev_get_drvdata(&dev->dev);
- input_report_key(inputdev, code, !(event & 0x10));
- input_sync(inputdev);
- }
- static void cmpc_keys_idev_init(struct input_dev *inputdev)
- {
- int i;
- set_bit(EV_KEY, inputdev->evbit);
- for (i = 0; cmpc_keys_codes[i] != KEY_MAX; i++)
- set_bit(cmpc_keys_codes[i], inputdev->keybit);
- }
- static int cmpc_keys_add(struct acpi_device *acpi)
- {
- return cmpc_add_acpi_notify_device(acpi, "cmpc_keys",
- cmpc_keys_idev_init);
- }
- static int cmpc_keys_remove(struct acpi_device *acpi, int type)
- {
- return cmpc_remove_acpi_notify_device(acpi);
- }
- static const struct acpi_device_id cmpc_keys_device_ids[] = {
- {CMPC_KEYS_HID, 0},
- {"", 0}
- };
- static struct acpi_driver cmpc_keys_acpi_driver = {
- .owner = THIS_MODULE,
- .name = "cmpc_keys",
- .class = "cmpc_keys",
- .ids = cmpc_keys_device_ids,
- .ops = {
- .add = cmpc_keys_add,
- .remove = cmpc_keys_remove,
- .notify = cmpc_keys_handler,
- }
- };
- /*
- * General init/exit code.
- */
- static int cmpc_init(void)
- {
- int r;
- r = acpi_bus_register_driver(&cmpc_keys_acpi_driver);
- if (r)
- goto failed_keys;
- r = acpi_bus_register_driver(&cmpc_ipml_acpi_driver);
- if (r)
- goto failed_bl;
- r = acpi_bus_register_driver(&cmpc_tablet_acpi_driver);
- if (r)
- goto failed_tablet;
- r = acpi_bus_register_driver(&cmpc_accel_acpi_driver);
- if (r)
- goto failed_accel;
- return r;
- failed_accel:
- acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
- failed_tablet:
- acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
- failed_bl:
- acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
- failed_keys:
- return r;
- }
- static void cmpc_exit(void)
- {
- acpi_bus_unregister_driver(&cmpc_accel_acpi_driver);
- acpi_bus_unregister_driver(&cmpc_tablet_acpi_driver);
- acpi_bus_unregister_driver(&cmpc_ipml_acpi_driver);
- acpi_bus_unregister_driver(&cmpc_keys_acpi_driver);
- }
- module_init(cmpc_init);
- module_exit(cmpc_exit);
- static const struct acpi_device_id cmpc_device_ids[] = {
- {CMPC_ACCEL_HID, 0},
- {CMPC_TABLET_HID, 0},
- {CMPC_IPML_HID, 0},
- {CMPC_KEYS_HID, 0},
- {"", 0}
- };
- MODULE_DEVICE_TABLE(acpi, cmpc_device_ids);
|