cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

classmate-laptop.c (27055B)


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