1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * HP Compaq TC1100 Tablet WMI Extras Driver 4 * 5 * Copyright (C) 2007 Carlos Corbacho <carlos@strangeworlds.co.uk> 6 * Copyright (C) 2004 Jamey Hicks <jamey.hicks@hp.com> 7 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> 8 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> 9 */ 10 11 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 12 13 #include <linux/kernel.h> 14 #include <linux/module.h> 15 #include <linux/slab.h> 16 #include <linux/init.h> 17 #include <linux/types.h> 18 #include <linux/acpi.h> 19 #include <linux/platform_device.h> 20 21 #define GUID "C364AC71-36DB-495A-8494-B439D472A505" 22 23 #define TC1100_INSTANCE_WIRELESS 1 24 #define TC1100_INSTANCE_JOGDIAL 2 25 26 MODULE_AUTHOR("Jamey Hicks, Carlos Corbacho"); 27 MODULE_DESCRIPTION("HP Compaq TC1100 Tablet WMI Extras"); 28 MODULE_LICENSE("GPL"); 29 MODULE_ALIAS("wmi:C364AC71-36DB-495A-8494-B439D472A505"); 30 31 static struct platform_device *tc1100_device; 32 33 struct tc1100_data { 34 u32 wireless; 35 u32 jogdial; 36 }; 37 38 #ifdef CONFIG_PM 39 static struct tc1100_data suspend_data; 40 #endif 41 42 /* -------------------------------------------------------------------------- 43 Device Management 44 -------------------------------------------------------------------------- */ 45 46 static int get_state(u32 *out, u8 instance) 47 { 48 u32 tmp; 49 acpi_status status; 50 struct acpi_buffer result = { ACPI_ALLOCATE_BUFFER, NULL }; 51 union acpi_object *obj; 52 53 if (!out) 54 return -EINVAL; 55 56 if (instance > 2) 57 return -ENODEV; 58 59 status = wmi_query_block(GUID, instance, &result); 60 if (ACPI_FAILURE(status)) 61 return -ENODEV; 62 63 obj = (union acpi_object *) result.pointer; 64 if (obj && obj->type == ACPI_TYPE_INTEGER) { 65 tmp = obj->integer.value; 66 } else { 67 tmp = 0; 68 } 69 70 if (result.length > 0) 71 kfree(result.pointer); 72 73 switch (instance) { 74 case TC1100_INSTANCE_WIRELESS: 75 *out = (tmp == 3) ? 1 : 0; 76 return 0; 77 case TC1100_INSTANCE_JOGDIAL: 78 *out = (tmp == 1) ? 0 : 1; 79 return 0; 80 default: 81 return -ENODEV; 82 } 83 } 84 85 static int set_state(u32 *in, u8 instance) 86 { 87 u32 value; 88 acpi_status status; 89 struct acpi_buffer input; 90 91 if (!in) 92 return -EINVAL; 93 94 if (instance > 2) 95 return -ENODEV; 96 97 switch (instance) { 98 case TC1100_INSTANCE_WIRELESS: 99 value = (*in) ? 1 : 2; 100 break; 101 case TC1100_INSTANCE_JOGDIAL: 102 value = (*in) ? 0 : 1; 103 break; 104 default: 105 return -ENODEV; 106 } 107 108 input.length = sizeof(u32); 109 input.pointer = &value; 110 111 status = wmi_set_block(GUID, instance, &input); 112 if (ACPI_FAILURE(status)) 113 return -ENODEV; 114 115 return 0; 116 } 117 118 /* -------------------------------------------------------------------------- 119 FS Interface (/sys) 120 -------------------------------------------------------------------------- */ 121 122 /* 123 * Read/ write bool sysfs macro 124 */ 125 #define show_set_bool(value, instance) \ 126 static ssize_t \ 127 show_bool_##value(struct device *dev, struct device_attribute *attr, \ 128 char *buf) \ 129 { \ 130 u32 result; \ 131 acpi_status status = get_state(&result, instance); \ 132 if (ACPI_SUCCESS(status)) \ 133 return sprintf(buf, "%d\n", result); \ 134 return sprintf(buf, "Read error\n"); \ 135 } \ 136 \ 137 static ssize_t \ 138 set_bool_##value(struct device *dev, struct device_attribute *attr, \ 139 const char *buf, size_t count) \ 140 { \ 141 u32 tmp = simple_strtoul(buf, NULL, 10); \ 142 acpi_status status = set_state(&tmp, instance); \ 143 if (ACPI_FAILURE(status)) \ 144 return -EINVAL; \ 145 return count; \ 146 } \ 147 static DEVICE_ATTR(value, S_IRUGO | S_IWUSR, \ 148 show_bool_##value, set_bool_##value); 149 150 show_set_bool(wireless, TC1100_INSTANCE_WIRELESS); 151 show_set_bool(jogdial, TC1100_INSTANCE_JOGDIAL); 152 153 static struct attribute *tc1100_attributes[] = { 154 &dev_attr_wireless.attr, 155 &dev_attr_jogdial.attr, 156 NULL 157 }; 158 159 static const struct attribute_group tc1100_attribute_group = { 160 .attrs = tc1100_attributes, 161 }; 162 163 /* -------------------------------------------------------------------------- 164 Driver Model 165 -------------------------------------------------------------------------- */ 166 167 static int __init tc1100_probe(struct platform_device *device) 168 { 169 return sysfs_create_group(&device->dev.kobj, &tc1100_attribute_group); 170 } 171 172 173 static int tc1100_remove(struct platform_device *device) 174 { 175 sysfs_remove_group(&device->dev.kobj, &tc1100_attribute_group); 176 177 return 0; 178 } 179 180 #ifdef CONFIG_PM 181 static int tc1100_suspend(struct device *dev) 182 { 183 int ret; 184 185 ret = get_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); 186 if (ret) 187 return ret; 188 189 ret = get_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); 190 if (ret) 191 return ret; 192 193 return 0; 194 } 195 196 static int tc1100_resume(struct device *dev) 197 { 198 int ret; 199 200 ret = set_state(&suspend_data.wireless, TC1100_INSTANCE_WIRELESS); 201 if (ret) 202 return ret; 203 204 ret = set_state(&suspend_data.jogdial, TC1100_INSTANCE_JOGDIAL); 205 if (ret) 206 return ret; 207 208 return 0; 209 } 210 211 static const struct dev_pm_ops tc1100_pm_ops = { 212 .suspend = tc1100_suspend, 213 .resume = tc1100_resume, 214 .freeze = tc1100_suspend, 215 .restore = tc1100_resume, 216 }; 217 #endif 218 219 static struct platform_driver tc1100_driver = { 220 .driver = { 221 .name = "tc1100-wmi", 222 #ifdef CONFIG_PM 223 .pm = &tc1100_pm_ops, 224 #endif 225 }, 226 .remove = tc1100_remove, 227 }; 228 229 static int __init tc1100_init(void) 230 { 231 int error; 232 233 if (!wmi_has_guid(GUID)) 234 return -ENODEV; 235 236 tc1100_device = platform_device_alloc("tc1100-wmi", PLATFORM_DEVID_NONE); 237 if (!tc1100_device) 238 return -ENOMEM; 239 240 error = platform_device_add(tc1100_device); 241 if (error) 242 goto err_device_put; 243 244 error = platform_driver_probe(&tc1100_driver, tc1100_probe); 245 if (error) 246 goto err_device_del; 247 248 pr_info("HP Compaq TC1100 Tablet WMI Extras loaded\n"); 249 return 0; 250 251 err_device_del: 252 platform_device_del(tc1100_device); 253 err_device_put: 254 platform_device_put(tc1100_device); 255 return error; 256 } 257 258 static void __exit tc1100_exit(void) 259 { 260 platform_device_unregister(tc1100_device); 261 platform_driver_unregister(&tc1100_driver); 262 } 263 264 module_init(tc1100_init); 265 module_exit(tc1100_exit); 266