1 /*
2  *  asus_acpi.c - Asus Laptop ACPI Extras
3  *
4  *
5  *  Copyright (C) 2002-2005 Julien Lerouge, 2003-2006 Karol Kozimor
6  *
7  *  This program is free software; you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation; either version 2 of the License, or
10  *  (at your option) any later version.
11  *
12  *  This program is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with this program; if not, write to the Free Software
19  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  *
22  *  The development page for this driver is located at
23  *  http://sourceforge.net/projects/acpi4asus/
24  *
25  *  Credits:
26  *  Pontus Fuchs   - Helper functions, cleanup
27  *  Johann Wiesner - Small compile fixes
28  *  John Belmonte  - ACPI code for Toshiba laptop was a good starting point.
29  *  �ic Burghard  - LED display support for W1N
30  *
31  */
32 
33 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
34 
35 #include <linux/kernel.h>
36 #include <linux/module.h>
37 #include <linux/slab.h>
38 #include <linux/init.h>
39 #include <linux/types.h>
40 #include <linux/proc_fs.h>
41 #include <linux/seq_file.h>
42 #include <linux/backlight.h>
43 #include <acpi/acpi_drivers.h>
44 #include <acpi/acpi_bus.h>
45 #include <asm/uaccess.h>
46 
47 #define ASUS_ACPI_VERSION "0.30"
48 
49 #define PROC_ASUS       "asus"	/* The directory */
50 #define PROC_MLED       "mled"
51 #define PROC_WLED       "wled"
52 #define PROC_TLED       "tled"
53 #define PROC_BT         "bluetooth"
54 #define PROC_LEDD       "ledd"
55 #define PROC_INFO       "info"
56 #define PROC_LCD        "lcd"
57 #define PROC_BRN        "brn"
58 #define PROC_DISP       "disp"
59 
60 #define ACPI_HOTK_NAME          "Asus Laptop ACPI Extras Driver"
61 #define ACPI_HOTK_CLASS         "hotkey"
62 #define ACPI_HOTK_DEVICE_NAME   "Hotkey"
63 
64 /*
65  * Some events we use, same for all Asus
66  */
67 #define BR_UP       0x10
68 #define BR_DOWN     0x20
69 
70 /*
71  * Flags for hotk status
72  */
73 #define MLED_ON     0x01	/* Mail LED */
74 #define WLED_ON     0x02	/* Wireless LED */
75 #define TLED_ON     0x04	/* Touchpad LED */
76 #define BT_ON       0x08	/* Internal Bluetooth */
77 
78 MODULE_AUTHOR("Julien Lerouge, Karol Kozimor");
79 MODULE_DESCRIPTION(ACPI_HOTK_NAME);
80 MODULE_LICENSE("GPL");
81 
82 static uid_t asus_uid;
83 static gid_t asus_gid;
84 module_param(asus_uid, uint, 0);
85 MODULE_PARM_DESC(asus_uid, "UID for entries in /proc/acpi/asus");
86 module_param(asus_gid, uint, 0);
87 MODULE_PARM_DESC(asus_gid, "GID for entries in /proc/acpi/asus");
88 
89 /* For each model, all features implemented,
90  * those marked with R are relative to HOTK, A for absolute */
91 struct model_data {
92 	char *name;		/* name of the laptop________________A */
93 	char *mt_mled;		/* method to handle mled_____________R */
94 	char *mled_status;	/* node to handle mled reading_______A */
95 	char *mt_wled;		/* method to handle wled_____________R */
96 	char *wled_status;	/* node to handle wled reading_______A */
97 	char *mt_tled;		/* method to handle tled_____________R */
98 	char *tled_status;	/* node to handle tled reading_______A */
99 	char *mt_ledd;		/* method to handle LED display______R */
100 	char *mt_bt_switch;	/* method to switch Bluetooth on/off_R */
101 	char *bt_status;	/* no model currently supports this__? */
102 	char *mt_lcd_switch;	/* method to turn LCD on/off_________A */
103 	char *lcd_status;	/* node to read LCD panel state______A */
104 	char *brightness_up;	/* method to set brightness up_______A */
105 	char *brightness_down;	/* method to set brightness down ____A */
106 	char *brightness_set;	/* method to set absolute brightness_R */
107 	char *brightness_get;	/* method to get absolute brightness_R */
108 	char *brightness_status;/* node to get brightness____________A */
109 	char *display_set;	/* method to set video output________R */
110 	char *display_get;	/* method to get video output________R */
111 };
112 
113 /*
114  * This is the main structure, we can use it to store anything interesting
115  * about the hotk device
116  */
117 struct asus_hotk {
118 	struct acpi_device *device;	/* the device we are in */
119 	acpi_handle handle;		/* the handle of the hotk device */
120 	char status;			/* status of the hotk, for LEDs */
121 	u32 ledd_status;		/* status of the LED display */
122 	struct model_data *methods;	/* methods available on the laptop */
123 	u8 brightness;			/* brightness level */
124 	enum {
125 		A1x = 0,	/* A1340D, A1300F */
126 		A2x,		/* A2500H */
127 		A4G,		/* A4700G */
128 		D1x,		/* D1 */
129 		L2D,		/* L2000D */
130 		L3C,		/* L3800C */
131 		L3D,		/* L3400D */
132 		L3H,		/* L3H, L2000E, L5D */
133 		L4R,		/* L4500R */
134 		L5x,		/* L5800C */
135 		L8L,		/* L8400L */
136 		M1A,		/* M1300A */
137 		M2E,		/* M2400E, L4400L */
138 		M6N,		/* M6800N, W3400N */
139 		M6R,		/* M6700R, A3000G */
140 		P30,		/* Samsung P30 */
141 		S1x,		/* S1300A, but also L1400B and M2400A (L84F) */
142 		S2x,		/* S200 (J1 reported), Victor MP-XP7210 */
143 		W1N,		/* W1000N */
144 		W5A,		/* W5A */
145 		W3V,            /* W3030V */
146 		xxN,		/* M2400N, M3700N, M5200N, M6800N,
147 							 S1300N, S5200N*/
148 		A4S,            /* Z81sp */
149 		F3Sa,		/* (Centrino) */
150 		R1F,
151 		END_MODEL
152 	} model;		/* Models currently supported */
153 	u16 event_count[128];	/* Count for each event TODO make this better */
154 };
155 
156 /* Here we go */
157 #define A1x_PREFIX "\\_SB.PCI0.ISA.EC0."
158 #define L3C_PREFIX "\\_SB.PCI0.PX40.ECD0."
159 #define M1A_PREFIX "\\_SB.PCI0.PX40.EC0."
160 #define P30_PREFIX "\\_SB.PCI0.LPCB.EC0."
161 #define S1x_PREFIX "\\_SB.PCI0.PX40."
162 #define S2x_PREFIX A1x_PREFIX
163 #define xxN_PREFIX "\\_SB.PCI0.SBRG.EC0."
164 
165 static struct model_data model_conf[END_MODEL] = {
166 	/*
167 	 * TODO I have seen a SWBX and AIBX method on some models, like L1400B,
168 	 * it seems to be a kind of switch, but what for ?
169 	 */
170 
171 	{
172 	 .name = "A1x",
173 	 .mt_mled = "MLED",
174 	 .mled_status = "\\MAIL",
175 	 .mt_lcd_switch = A1x_PREFIX "_Q10",
176 	 .lcd_status = "\\BKLI",
177 	 .brightness_up = A1x_PREFIX "_Q0E",
178 	 .brightness_down = A1x_PREFIX "_Q0F"},
179 
180 	{
181 	 .name = "A2x",
182 	 .mt_mled = "MLED",
183 	 .mt_wled = "WLED",
184 	 .wled_status = "\\SG66",
185 	 .mt_lcd_switch = "\\Q10",
186 	 .lcd_status = "\\BAOF",
187 	 .brightness_set = "SPLV",
188 	 .brightness_get = "GPLV",
189 	 .display_set = "SDSP",
190 	 .display_get = "\\INFB"},
191 
192 	{
193 	 .name = "A4G",
194 	 .mt_mled = "MLED",
195 /* WLED present, but not controlled by ACPI */
196 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
197 	 .brightness_set = "SPLV",
198 	 .brightness_get = "GPLV",
199 	 .display_set = "SDSP",
200 	 .display_get = "\\ADVG"},
201 
202 	{
203 	 .name = "D1x",
204 	 .mt_mled = "MLED",
205 	 .mt_lcd_switch = "\\Q0D",
206 	 .lcd_status = "\\GP11",
207 	 .brightness_up = "\\Q0C",
208 	 .brightness_down = "\\Q0B",
209 	 .brightness_status = "\\BLVL",
210 	 .display_set = "SDSP",
211 	 .display_get = "\\INFB"},
212 
213 	{
214 	 .name = "L2D",
215 	 .mt_mled = "MLED",
216 	 .mled_status = "\\SGP6",
217 	 .mt_wled = "WLED",
218 	 .wled_status = "\\RCP3",
219 	 .mt_lcd_switch = "\\Q10",
220 	 .lcd_status = "\\SGP0",
221 	 .brightness_up = "\\Q0E",
222 	 .brightness_down = "\\Q0F",
223 	 .display_set = "SDSP",
224 	 .display_get = "\\INFB"},
225 
226 	{
227 	 .name = "L3C",
228 	 .mt_mled = "MLED",
229 	 .mt_wled = "WLED",
230 	 .mt_lcd_switch = L3C_PREFIX "_Q10",
231 	 .lcd_status = "\\GL32",
232 	 .brightness_set = "SPLV",
233 	 .brightness_get = "GPLV",
234 	 .display_set = "SDSP",
235 	 .display_get = "\\_SB.PCI0.PCI1.VGAC.NMAP"},
236 
237 	{
238 	 .name = "L3D",
239 	 .mt_mled = "MLED",
240 	 .mled_status = "\\MALD",
241 	 .mt_wled = "WLED",
242 	 .mt_lcd_switch = "\\Q10",
243 	 .lcd_status = "\\BKLG",
244 	 .brightness_set = "SPLV",
245 	 .brightness_get = "GPLV",
246 	 .display_set = "SDSP",
247 	 .display_get = "\\INFB"},
248 
249 	{
250 	 .name = "L3H",
251 	 .mt_mled = "MLED",
252 	 .mt_wled = "WLED",
253 	 .mt_lcd_switch = "EHK",
254 	 .lcd_status = "\\_SB.PCI0.PM.PBC",
255 	 .brightness_set = "SPLV",
256 	 .brightness_get = "GPLV",
257 	 .display_set = "SDSP",
258 	 .display_get = "\\INFB"},
259 
260 	{
261 	 .name = "L4R",
262 	 .mt_mled = "MLED",
263 	 .mt_wled = "WLED",
264 	 .wled_status = "\\_SB.PCI0.SBRG.SG13",
265 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
266 	 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
267 	 .brightness_set = "SPLV",
268 	 .brightness_get = "GPLV",
269 	 .display_set = "SDSP",
270 	 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
271 
272 	{
273 	 .name = "L5x",
274 	 .mt_mled = "MLED",
275 /* WLED present, but not controlled by ACPI */
276 	 .mt_tled = "TLED",
277 	 .mt_lcd_switch = "\\Q0D",
278 	 .lcd_status = "\\BAOF",
279 	 .brightness_set = "SPLV",
280 	 .brightness_get = "GPLV",
281 	 .display_set = "SDSP",
282 	 .display_get = "\\INFB"},
283 
284 	{
285 	 .name = "L8L"
286 /* No features, but at least support the hotkeys */
287 	 },
288 
289 	{
290 	 .name = "M1A",
291 	 .mt_mled = "MLED",
292 	 .mt_lcd_switch = M1A_PREFIX "Q10",
293 	 .lcd_status = "\\PNOF",
294 	 .brightness_up = M1A_PREFIX "Q0E",
295 	 .brightness_down = M1A_PREFIX "Q0F",
296 	 .brightness_status = "\\BRIT",
297 	 .display_set = "SDSP",
298 	 .display_get = "\\INFB"},
299 
300 	{
301 	 .name = "M2E",
302 	 .mt_mled = "MLED",
303 	 .mt_wled = "WLED",
304 	 .mt_lcd_switch = "\\Q10",
305 	 .lcd_status = "\\GP06",
306 	 .brightness_set = "SPLV",
307 	 .brightness_get = "GPLV",
308 	 .display_set = "SDSP",
309 	 .display_get = "\\INFB"},
310 
311 	{
312 	 .name = "M6N",
313 	 .mt_mled = "MLED",
314 	 .mt_wled = "WLED",
315 	 .wled_status = "\\_SB.PCI0.SBRG.SG13",
316 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
317 	 .lcd_status = "\\_SB.BKLT",
318 	 .brightness_set = "SPLV",
319 	 .brightness_get = "GPLV",
320 	 .display_set = "SDSP",
321 	 .display_get = "\\SSTE"},
322 
323 	{
324 	 .name = "M6R",
325 	 .mt_mled = "MLED",
326 	 .mt_wled = "WLED",
327 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
328 	 .lcd_status = "\\_SB.PCI0.SBSM.SEO4",
329 	 .brightness_set = "SPLV",
330 	 .brightness_get = "GPLV",
331 	 .display_set = "SDSP",
332 	 .display_get = "\\_SB.PCI0.P0P1.VGA.GETD"},
333 
334 	{
335 	 .name = "P30",
336 	 .mt_wled = "WLED",
337 	 .mt_lcd_switch = P30_PREFIX "_Q0E",
338 	 .lcd_status = "\\BKLT",
339 	 .brightness_up = P30_PREFIX "_Q68",
340 	 .brightness_down = P30_PREFIX "_Q69",
341 	 .brightness_get = "GPLV",
342 	 .display_set = "SDSP",
343 	 .display_get = "\\DNXT"},
344 
345 	{
346 	 .name = "S1x",
347 	 .mt_mled = "MLED",
348 	 .mled_status = "\\EMLE",
349 	 .mt_wled = "WLED",
350 	 .mt_lcd_switch = S1x_PREFIX "Q10",
351 	 .lcd_status = "\\PNOF",
352 	 .brightness_set = "SPLV",
353 	 .brightness_get = "GPLV"},
354 
355 	{
356 	 .name = "S2x",
357 	 .mt_mled = "MLED",
358 	 .mled_status = "\\MAIL",
359 	 .mt_lcd_switch = S2x_PREFIX "_Q10",
360 	 .lcd_status = "\\BKLI",
361 	 .brightness_up = S2x_PREFIX "_Q0B",
362 	 .brightness_down = S2x_PREFIX "_Q0A"},
363 
364 	{
365 	 .name = "W1N",
366 	 .mt_mled = "MLED",
367 	 .mt_wled = "WLED",
368 	 .mt_ledd = "SLCM",
369 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
370 	 .lcd_status = "\\BKLT",
371 	 .brightness_set = "SPLV",
372 	 .brightness_get = "GPLV",
373 	 .display_set = "SDSP",
374 	 .display_get = "\\ADVG"},
375 
376 	{
377 	 .name = "W5A",
378 	 .mt_bt_switch = "BLED",
379 	 .mt_wled = "WLED",
380 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
381 	 .brightness_set = "SPLV",
382 	 .brightness_get = "GPLV",
383 	 .display_set = "SDSP",
384 	 .display_get = "\\ADVG"},
385 
386 	{
387 	 .name = "W3V",
388 	 .mt_mled = "MLED",
389 	 .mt_wled = "WLED",
390 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
391 	 .lcd_status = "\\BKLT",
392 	 .brightness_set = "SPLV",
393 	 .brightness_get = "GPLV",
394 	 .display_set = "SDSP",
395 	 .display_get = "\\INFB"},
396 
397        {
398 	 .name = "xxN",
399 	 .mt_mled = "MLED",
400 /* WLED present, but not controlled by ACPI */
401 	 .mt_lcd_switch = xxN_PREFIX "_Q10",
402 	 .lcd_status = "\\BKLT",
403 	 .brightness_set = "SPLV",
404 	 .brightness_get = "GPLV",
405 	 .display_set = "SDSP",
406 	.display_get = "\\ADVG"},
407 
408 	{
409 		.name              = "A4S",
410 		.brightness_set    = "SPLV",
411 		.brightness_get    = "GPLV",
412 		.mt_bt_switch      = "BLED",
413 		.mt_wled           = "WLED"
414 	},
415 
416 	{
417 		.name		= "F3Sa",
418 		.mt_bt_switch	= "BLED",
419 		.mt_wled	= "WLED",
420 		.mt_mled	= "MLED",
421 		.brightness_get	= "GPLV",
422 		.brightness_set	= "SPLV",
423 		.mt_lcd_switch	= "\\_SB.PCI0.SBRG.EC0._Q10",
424 		.lcd_status	= "\\_SB.PCI0.SBRG.EC0.RPIN",
425 		.display_get	= "\\ADVG",
426 		.display_set	= "SDSP",
427 	},
428 	{
429 		.name = "R1F",
430 		.mt_bt_switch = "BLED",
431 		.mt_mled = "MLED",
432 		.mt_wled = "WLED",
433 		.mt_lcd_switch = "\\Q10",
434 		.lcd_status = "\\GP06",
435 		.brightness_set = "SPLV",
436 		.brightness_get = "GPLV",
437 		.display_set = "SDSP",
438 		.display_get = "\\INFB"
439 	}
440 };
441 
442 /* procdir we use */
443 static struct proc_dir_entry *asus_proc_dir;
444 
445 static struct backlight_device *asus_backlight_device;
446 
447 /*
448  * This header is made available to allow proper configuration given model,
449  * revision number , ... this info cannot go in struct asus_hotk because it is
450  * available before the hotk
451  */
452 static struct acpi_table_header *asus_info;
453 
454 /* The actual device the driver binds to */
455 static struct asus_hotk *hotk;
456 
457 /*
458  * The hotkey driver and autoloading declaration
459  */
460 static int asus_hotk_add(struct acpi_device *device);
461 static int asus_hotk_remove(struct acpi_device *device, int type);
462 static void asus_hotk_notify(struct acpi_device *device, u32 event);
463 
464 static const struct acpi_device_id asus_device_ids[] = {
465 	{"ATK0100", 0},
466 	{"", 0},
467 };
468 MODULE_DEVICE_TABLE(acpi, asus_device_ids);
469 
470 static struct acpi_driver asus_hotk_driver = {
471 	.name = "asus_acpi",
472 	.class = ACPI_HOTK_CLASS,
473 	.owner = THIS_MODULE,
474 	.ids = asus_device_ids,
475 	.flags = ACPI_DRIVER_ALL_NOTIFY_EVENTS,
476 	.ops = {
477 		.add = asus_hotk_add,
478 		.remove = asus_hotk_remove,
479 		.notify = asus_hotk_notify,
480 		},
481 };
482 
483 /*
484  * This function evaluates an ACPI method, given an int as parameter, the
485  * method is searched within the scope of the handle, can be NULL. The output
486  * of the method is written is output, which can also be NULL
487  *
488  * returns 1 if write is successful, 0 else.
489  */
write_acpi_int(acpi_handle handle,const char * method,int val,struct acpi_buffer * output)490 static int write_acpi_int(acpi_handle handle, const char *method, int val,
491 			  struct acpi_buffer *output)
492 {
493 	struct acpi_object_list params;	/* list of input parameters (int) */
494 	union acpi_object in_obj;	/* the only param we use */
495 	acpi_status status;
496 
497 	params.count = 1;
498 	params.pointer = &in_obj;
499 	in_obj.type = ACPI_TYPE_INTEGER;
500 	in_obj.integer.value = val;
501 
502 	status = acpi_evaluate_object(handle, (char *)method, &params, output);
503 	return (status == AE_OK);
504 }
505 
read_acpi_int(acpi_handle handle,const char * method,int * val)506 static int read_acpi_int(acpi_handle handle, const char *method, int *val)
507 {
508 	struct acpi_buffer output;
509 	union acpi_object out_obj;
510 	acpi_status status;
511 
512 	output.length = sizeof(out_obj);
513 	output.pointer = &out_obj;
514 
515 	status = acpi_evaluate_object(handle, (char *)method, NULL, &output);
516 	*val = out_obj.integer.value;
517 	return (status == AE_OK) && (out_obj.type == ACPI_TYPE_INTEGER);
518 }
519 
asus_info_proc_show(struct seq_file * m,void * v)520 static int asus_info_proc_show(struct seq_file *m, void *v)
521 {
522 	int temp;
523 
524 	seq_printf(m, ACPI_HOTK_NAME " " ASUS_ACPI_VERSION "\n");
525 	seq_printf(m, "Model reference    : %s\n", hotk->methods->name);
526 	/*
527 	 * The SFUN method probably allows the original driver to get the list
528 	 * of features supported by a given model. For now, 0x0100 or 0x0800
529 	 * bit signifies that the laptop is equipped with a Wi-Fi MiniPCI card.
530 	 * The significance of others is yet to be found.
531 	 */
532 	if (read_acpi_int(hotk->handle, "SFUN", &temp))
533 		seq_printf(m, "SFUN value         : 0x%04x\n", temp);
534 	/*
535 	 * Another value for userspace: the ASYM method returns 0x02 for
536 	 * battery low and 0x04 for battery critical, its readings tend to be
537 	 * more accurate than those provided by _BST.
538 	 * Note: since not all the laptops provide this method, errors are
539 	 * silently ignored.
540 	 */
541 	if (read_acpi_int(hotk->handle, "ASYM", &temp))
542 		seq_printf(m, "ASYM value         : 0x%04x\n", temp);
543 	if (asus_info) {
544 		seq_printf(m, "DSDT length        : %d\n", asus_info->length);
545 		seq_printf(m, "DSDT checksum      : %d\n", asus_info->checksum);
546 		seq_printf(m, "DSDT revision      : %d\n", asus_info->revision);
547 		seq_printf(m, "OEM id             : %.*s\n", ACPI_OEM_ID_SIZE, asus_info->oem_id);
548 		seq_printf(m, "OEM table id       : %.*s\n", ACPI_OEM_TABLE_ID_SIZE, asus_info->oem_table_id);
549 		seq_printf(m, "OEM revision       : 0x%x\n", asus_info->oem_revision);
550 		seq_printf(m, "ASL comp vendor id : %.*s\n", ACPI_NAME_SIZE, asus_info->asl_compiler_id);
551 		seq_printf(m, "ASL comp revision  : 0x%x\n", asus_info->asl_compiler_revision);
552 	}
553 
554 	return 0;
555 }
556 
asus_info_proc_open(struct inode * inode,struct file * file)557 static int asus_info_proc_open(struct inode *inode, struct file *file)
558 {
559 	return single_open(file, asus_info_proc_show, NULL);
560 }
561 
562 static const struct file_operations asus_info_proc_fops = {
563 	.owner		= THIS_MODULE,
564 	.open		= asus_info_proc_open,
565 	.read		= seq_read,
566 	.llseek		= seq_lseek,
567 	.release	= single_release,
568 };
569 
570 /*
571  * /proc handlers
572  * We write our info in page, we begin at offset off and cannot write more
573  * than count bytes. We set eof to 1 if we handle those 2 values. We return the
574  * number of bytes written in page
575  */
576 
577 /* Generic LED functions */
read_led(const char * ledname,int ledmask)578 static int read_led(const char *ledname, int ledmask)
579 {
580 	if (ledname) {
581 		int led_status;
582 
583 		if (read_acpi_int(NULL, ledname, &led_status))
584 			return led_status;
585 		else
586 			pr_warn("Error reading LED status\n");
587 	}
588 	return (hotk->status & ledmask) ? 1 : 0;
589 }
590 
parse_arg(const char __user * buf,unsigned long count,int * val)591 static int parse_arg(const char __user *buf, unsigned long count, int *val)
592 {
593 	char s[32];
594 	if (!count)
595 		return 0;
596 	if (count > 31)
597 		return -EINVAL;
598 	if (copy_from_user(s, buf, count))
599 		return -EFAULT;
600 	s[count] = 0;
601 	if (sscanf(s, "%i", val) != 1)
602 		return -EINVAL;
603 	return count;
604 }
605 
606 /* FIXME: kill extraneous args so it can be called independently */
607 static int
write_led(const char __user * buffer,unsigned long count,char * ledname,int ledmask,int invert)608 write_led(const char __user *buffer, unsigned long count,
609 	  char *ledname, int ledmask, int invert)
610 {
611 	int rv, value;
612 	int led_out = 0;
613 
614 	rv = parse_arg(buffer, count, &value);
615 	if (rv > 0)
616 		led_out = value ? 1 : 0;
617 
618 	hotk->status =
619 	    (led_out) ? (hotk->status | ledmask) : (hotk->status & ~ledmask);
620 
621 	if (invert)		/* invert target value */
622 		led_out = !led_out;
623 
624 	if (!write_acpi_int(hotk->handle, ledname, led_out, NULL))
625 		pr_warn("LED (%s) write failed\n", ledname);
626 
627 	return rv;
628 }
629 
630 /*
631  * Proc handlers for MLED
632  */
mled_proc_show(struct seq_file * m,void * v)633 static int mled_proc_show(struct seq_file *m, void *v)
634 {
635 	seq_printf(m, "%d\n", read_led(hotk->methods->mled_status, MLED_ON));
636 	return 0;
637 }
638 
mled_proc_open(struct inode * inode,struct file * file)639 static int mled_proc_open(struct inode *inode, struct file *file)
640 {
641 	return single_open(file, mled_proc_show, NULL);
642 }
643 
mled_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)644 static ssize_t mled_proc_write(struct file *file, const char __user *buffer,
645 		size_t count, loff_t *pos)
646 {
647 	return write_led(buffer, count, hotk->methods->mt_mled, MLED_ON, 1);
648 }
649 
650 static const struct file_operations mled_proc_fops = {
651 	.owner		= THIS_MODULE,
652 	.open		= mled_proc_open,
653 	.read		= seq_read,
654 	.llseek		= seq_lseek,
655 	.release	= single_release,
656 	.write		= mled_proc_write,
657 };
658 
659 /*
660  * Proc handlers for LED display
661  */
ledd_proc_show(struct seq_file * m,void * v)662 static int ledd_proc_show(struct seq_file *m, void *v)
663 {
664 	seq_printf(m, "0x%08x\n", hotk->ledd_status);
665 	return 0;
666 }
667 
ledd_proc_open(struct inode * inode,struct file * file)668 static int ledd_proc_open(struct inode *inode, struct file *file)
669 {
670 	return single_open(file, ledd_proc_show, NULL);
671 }
672 
ledd_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)673 static ssize_t ledd_proc_write(struct file *file, const char __user *buffer,
674 		size_t count, loff_t *pos)
675 {
676 	int rv, value;
677 
678 	rv = parse_arg(buffer, count, &value);
679 	if (rv > 0) {
680 		if (!write_acpi_int
681 		    (hotk->handle, hotk->methods->mt_ledd, value, NULL))
682 			pr_warn("LED display write failed\n");
683 		else
684 			hotk->ledd_status = (u32) value;
685 	}
686 	return rv;
687 }
688 
689 static const struct file_operations ledd_proc_fops = {
690 	.owner		= THIS_MODULE,
691 	.open		= ledd_proc_open,
692 	.read		= seq_read,
693 	.llseek		= seq_lseek,
694 	.release	= single_release,
695 	.write		= ledd_proc_write,
696 };
697 
698 /*
699  * Proc handlers for WLED
700  */
wled_proc_show(struct seq_file * m,void * v)701 static int wled_proc_show(struct seq_file *m, void *v)
702 {
703 	seq_printf(m, "%d\n", read_led(hotk->methods->wled_status, WLED_ON));
704 	return 0;
705 }
706 
wled_proc_open(struct inode * inode,struct file * file)707 static int wled_proc_open(struct inode *inode, struct file *file)
708 {
709 	return single_open(file, wled_proc_show, NULL);
710 }
711 
wled_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)712 static ssize_t wled_proc_write(struct file *file, const char __user *buffer,
713 		size_t count, loff_t *pos)
714 {
715 	return write_led(buffer, count, hotk->methods->mt_wled, WLED_ON, 0);
716 }
717 
718 static const struct file_operations wled_proc_fops = {
719 	.owner		= THIS_MODULE,
720 	.open		= wled_proc_open,
721 	.read		= seq_read,
722 	.llseek		= seq_lseek,
723 	.release	= single_release,
724 	.write		= wled_proc_write,
725 };
726 
727 /*
728  * Proc handlers for Bluetooth
729  */
bluetooth_proc_show(struct seq_file * m,void * v)730 static int bluetooth_proc_show(struct seq_file *m, void *v)
731 {
732 	seq_printf(m, "%d\n", read_led(hotk->methods->bt_status, BT_ON));
733 	return 0;
734 }
735 
bluetooth_proc_open(struct inode * inode,struct file * file)736 static int bluetooth_proc_open(struct inode *inode, struct file *file)
737 {
738 	return single_open(file, bluetooth_proc_show, NULL);
739 }
740 
bluetooth_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)741 static ssize_t bluetooth_proc_write(struct file *file,
742 		const char __user *buffer, size_t count, loff_t *pos)
743 {
744 	/* Note: mt_bt_switch controls both internal Bluetooth adapter's
745 	   presence and its LED */
746 	return write_led(buffer, count, hotk->methods->mt_bt_switch, BT_ON, 0);
747 }
748 
749 static const struct file_operations bluetooth_proc_fops = {
750 	.owner		= THIS_MODULE,
751 	.open		= bluetooth_proc_open,
752 	.read		= seq_read,
753 	.llseek		= seq_lseek,
754 	.release	= single_release,
755 	.write		= bluetooth_proc_write,
756 };
757 
758 /*
759  * Proc handlers for TLED
760  */
tled_proc_show(struct seq_file * m,void * v)761 static int tled_proc_show(struct seq_file *m, void *v)
762 {
763 	seq_printf(m, "%d\n", read_led(hotk->methods->tled_status, TLED_ON));
764 	return 0;
765 }
766 
tled_proc_open(struct inode * inode,struct file * file)767 static int tled_proc_open(struct inode *inode, struct file *file)
768 {
769 	return single_open(file, tled_proc_show, NULL);
770 }
771 
tled_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)772 static ssize_t tled_proc_write(struct file *file, const char __user *buffer,
773 		size_t count, loff_t *pos)
774 {
775 	return write_led(buffer, count, hotk->methods->mt_tled, TLED_ON, 0);
776 }
777 
778 static const struct file_operations tled_proc_fops = {
779 	.owner		= THIS_MODULE,
780 	.open		= tled_proc_open,
781 	.read		= seq_read,
782 	.llseek		= seq_lseek,
783 	.release	= single_release,
784 	.write		= tled_proc_write,
785 };
786 
get_lcd_state(void)787 static int get_lcd_state(void)
788 {
789 	int lcd = 0;
790 
791 	if (hotk->model == L3H) {
792 		/* L3H and the like have to be handled differently */
793 		acpi_status status = 0;
794 		struct acpi_object_list input;
795 		union acpi_object mt_params[2];
796 		struct acpi_buffer output;
797 		union acpi_object out_obj;
798 
799 		input.count = 2;
800 		input.pointer = mt_params;
801 		/* Note: the following values are partly guessed up, but
802 		   otherwise they seem to work */
803 		mt_params[0].type = ACPI_TYPE_INTEGER;
804 		mt_params[0].integer.value = 0x02;
805 		mt_params[1].type = ACPI_TYPE_INTEGER;
806 		mt_params[1].integer.value = 0x02;
807 
808 		output.length = sizeof(out_obj);
809 		output.pointer = &out_obj;
810 
811 		status =
812 		    acpi_evaluate_object(NULL, hotk->methods->lcd_status,
813 					 &input, &output);
814 		if (status != AE_OK)
815 			return -1;
816 		if (out_obj.type == ACPI_TYPE_INTEGER)
817 			/* That's what the AML code does */
818 			lcd = out_obj.integer.value >> 8;
819 	} else if (hotk->model == F3Sa) {
820 		unsigned long long tmp;
821 		union acpi_object param;
822 		struct acpi_object_list input;
823 		acpi_status status;
824 
825 		/* Read pin 11 */
826 		param.type = ACPI_TYPE_INTEGER;
827 		param.integer.value = 0x11;
828 		input.count = 1;
829 		input.pointer = &param;
830 
831 		status = acpi_evaluate_integer(NULL, hotk->methods->lcd_status,
832 						&input, &tmp);
833 		if (status != AE_OK)
834 			return -1;
835 
836 		lcd = tmp;
837 	} else {
838 		/* We don't have to check anything if we are here */
839 		if (!read_acpi_int(NULL, hotk->methods->lcd_status, &lcd))
840 			pr_warn("Error reading LCD status\n");
841 
842 		if (hotk->model == L2D)
843 			lcd = ~lcd;
844 	}
845 
846 	return (lcd & 1);
847 }
848 
set_lcd_state(int value)849 static int set_lcd_state(int value)
850 {
851 	int lcd = 0;
852 	acpi_status status = 0;
853 
854 	lcd = value ? 1 : 0;
855 	if (lcd != get_lcd_state()) {
856 		/* switch */
857 		if (hotk->model != L3H) {
858 			status =
859 			    acpi_evaluate_object(NULL,
860 						 hotk->methods->mt_lcd_switch,
861 						 NULL, NULL);
862 		} else {
863 			/* L3H and the like must be handled differently */
864 			if (!write_acpi_int
865 			    (hotk->handle, hotk->methods->mt_lcd_switch, 0x07,
866 			     NULL))
867 				status = AE_ERROR;
868 			/* L3H's AML executes EHK (0x07) upon Fn+F7 keypress,
869 			   the exact behaviour is simulated here */
870 		}
871 		if (ACPI_FAILURE(status))
872 			pr_warn("Error switching LCD\n");
873 	}
874 	return 0;
875 
876 }
877 
lcd_proc_show(struct seq_file * m,void * v)878 static int lcd_proc_show(struct seq_file *m, void *v)
879 {
880 	seq_printf(m, "%d\n", get_lcd_state());
881 	return 0;
882 }
883 
lcd_proc_open(struct inode * inode,struct file * file)884 static int lcd_proc_open(struct inode *inode, struct file *file)
885 {
886 	return single_open(file, lcd_proc_show, NULL);
887 }
888 
lcd_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)889 static ssize_t lcd_proc_write(struct file *file, const char __user *buffer,
890 	       size_t count, loff_t *pos)
891 {
892 	int rv, value;
893 
894 	rv = parse_arg(buffer, count, &value);
895 	if (rv > 0)
896 		set_lcd_state(value);
897 	return rv;
898 }
899 
900 static const struct file_operations lcd_proc_fops = {
901 	.owner		= THIS_MODULE,
902 	.open		= lcd_proc_open,
903 	.read		= seq_read,
904 	.llseek		= seq_lseek,
905 	.release	= single_release,
906 	.write		= lcd_proc_write,
907 };
908 
read_brightness(struct backlight_device * bd)909 static int read_brightness(struct backlight_device *bd)
910 {
911 	int value;
912 
913 	if (hotk->methods->brightness_get) {	/* SPLV/GPLV laptop */
914 		if (!read_acpi_int(hotk->handle, hotk->methods->brightness_get,
915 				   &value))
916 			pr_warn("Error reading brightness\n");
917 	} else if (hotk->methods->brightness_status) {	/* For D1 for example */
918 		if (!read_acpi_int(NULL, hotk->methods->brightness_status,
919 				   &value))
920 			pr_warn("Error reading brightness\n");
921 	} else			/* No GPLV method */
922 		value = hotk->brightness;
923 	return value;
924 }
925 
926 /*
927  * Change the brightness level
928  */
set_brightness(int value)929 static int set_brightness(int value)
930 {
931 	acpi_status status = 0;
932 	int ret = 0;
933 
934 	/* SPLV laptop */
935 	if (hotk->methods->brightness_set) {
936 		if (!write_acpi_int(hotk->handle, hotk->methods->brightness_set,
937 				    value, NULL)) {
938 			pr_warn("Error changing brightness\n");
939 			ret = -EIO;
940 		}
941 		goto out;
942 	}
943 
944 	/* No SPLV method if we are here, act as appropriate */
945 	value -= read_brightness(NULL);
946 	while (value != 0) {
947 		status = acpi_evaluate_object(NULL, (value > 0) ?
948 					      hotk->methods->brightness_up :
949 					      hotk->methods->brightness_down,
950 					      NULL, NULL);
951 		(value > 0) ? value-- : value++;
952 		if (ACPI_FAILURE(status)) {
953 			pr_warn("Error changing brightness\n");
954 			ret = -EIO;
955 		}
956 	}
957 out:
958 	return ret;
959 }
960 
set_brightness_status(struct backlight_device * bd)961 static int set_brightness_status(struct backlight_device *bd)
962 {
963 	return set_brightness(bd->props.brightness);
964 }
965 
brn_proc_show(struct seq_file * m,void * v)966 static int brn_proc_show(struct seq_file *m, void *v)
967 {
968 	seq_printf(m, "%d\n", read_brightness(NULL));
969 	return 0;
970 }
971 
brn_proc_open(struct inode * inode,struct file * file)972 static int brn_proc_open(struct inode *inode, struct file *file)
973 {
974 	return single_open(file, brn_proc_show, NULL);
975 }
976 
brn_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)977 static ssize_t brn_proc_write(struct file *file, const char __user *buffer,
978 	       size_t count, loff_t *pos)
979 {
980 	int rv, value;
981 
982 	rv = parse_arg(buffer, count, &value);
983 	if (rv > 0) {
984 		value = (0 < value) ? ((15 < value) ? 15 : value) : 0;
985 		/* 0 <= value <= 15 */
986 		set_brightness(value);
987 	}
988 	return rv;
989 }
990 
991 static const struct file_operations brn_proc_fops = {
992 	.owner		= THIS_MODULE,
993 	.open		= brn_proc_open,
994 	.read		= seq_read,
995 	.llseek		= seq_lseek,
996 	.release	= single_release,
997 	.write		= brn_proc_write,
998 };
999 
set_display(int value)1000 static void set_display(int value)
1001 {
1002 	/* no sanity check needed for now */
1003 	if (!write_acpi_int(hotk->handle, hotk->methods->display_set,
1004 			    value, NULL))
1005 		pr_warn("Error setting display\n");
1006 	return;
1007 }
1008 
1009 /*
1010  * Now, *this* one could be more user-friendly, but so far, no-one has
1011  * complained. The significance of bits is the same as in proc_write_disp()
1012  */
disp_proc_show(struct seq_file * m,void * v)1013 static int disp_proc_show(struct seq_file *m, void *v)
1014 {
1015 	int value = 0;
1016 
1017 	if (!read_acpi_int(hotk->handle, hotk->methods->display_get, &value))
1018 		pr_warn("Error reading display status\n");
1019 	value &= 0x07;	/* needed for some models, shouldn't hurt others */
1020 	seq_printf(m, "%d\n", value);
1021 	return 0;
1022 }
1023 
disp_proc_open(struct inode * inode,struct file * file)1024 static int disp_proc_open(struct inode *inode, struct file *file)
1025 {
1026 	return single_open(file, disp_proc_show, NULL);
1027 }
1028 
1029 /*
1030  * Experimental support for display switching. As of now: 1 should activate
1031  * the LCD output, 2 should do for CRT, and 4 for TV-Out. Any combination
1032  * (bitwise) of these will suffice. I never actually tested 3 displays hooked
1033  * up simultaneously, so be warned. See the acpi4asus README for more info.
1034  */
disp_proc_write(struct file * file,const char __user * buffer,size_t count,loff_t * pos)1035 static ssize_t disp_proc_write(struct file *file, const char __user *buffer,
1036 		size_t count, loff_t *pos)
1037 {
1038 	int rv, value;
1039 
1040 	rv = parse_arg(buffer, count, &value);
1041 	if (rv > 0)
1042 		set_display(value);
1043 	return rv;
1044 }
1045 
1046 static const struct file_operations disp_proc_fops = {
1047 	.owner		= THIS_MODULE,
1048 	.open		= disp_proc_open,
1049 	.read		= seq_read,
1050 	.llseek		= seq_lseek,
1051 	.release	= single_release,
1052 	.write		= disp_proc_write,
1053 };
1054 
1055 static int
asus_proc_add(char * name,const struct file_operations * proc_fops,umode_t mode,struct acpi_device * device)1056 asus_proc_add(char *name, const struct file_operations *proc_fops, umode_t mode,
1057 		     struct acpi_device *device)
1058 {
1059 	struct proc_dir_entry *proc;
1060 
1061 	proc = proc_create_data(name, mode, acpi_device_dir(device),
1062 				proc_fops, acpi_driver_data(device));
1063 	if (!proc) {
1064 		pr_warn("  Unable to create %s fs entry\n", name);
1065 		return -1;
1066 	}
1067 	proc->uid = asus_uid;
1068 	proc->gid = asus_gid;
1069 	return 0;
1070 }
1071 
asus_hotk_add_fs(struct acpi_device * device)1072 static int asus_hotk_add_fs(struct acpi_device *device)
1073 {
1074 	struct proc_dir_entry *proc;
1075 	umode_t mode;
1076 
1077 	if ((asus_uid == 0) && (asus_gid == 0)) {
1078 		mode = S_IFREG | S_IRUGO | S_IWUSR | S_IWGRP;
1079 	} else {
1080 		mode = S_IFREG | S_IRUSR | S_IRGRP | S_IWUSR | S_IWGRP;
1081 		pr_warn("  asus_uid and asus_gid parameters are "
1082 			"deprecated, use chown and chmod instead!\n");
1083 	}
1084 
1085 	acpi_device_dir(device) = asus_proc_dir;
1086 	if (!acpi_device_dir(device))
1087 		return -ENODEV;
1088 
1089 	proc = proc_create(PROC_INFO, mode, acpi_device_dir(device),
1090 			   &asus_info_proc_fops);
1091 	if (proc) {
1092 		proc->uid = asus_uid;
1093 		proc->gid = asus_gid;
1094 	} else {
1095 		pr_warn("  Unable to create " PROC_INFO " fs entry\n");
1096 	}
1097 
1098 	if (hotk->methods->mt_wled) {
1099 		asus_proc_add(PROC_WLED, &wled_proc_fops, mode, device);
1100 	}
1101 
1102 	if (hotk->methods->mt_ledd) {
1103 		asus_proc_add(PROC_LEDD, &ledd_proc_fops, mode, device);
1104 	}
1105 
1106 	if (hotk->methods->mt_mled) {
1107 		asus_proc_add(PROC_MLED, &mled_proc_fops, mode, device);
1108 	}
1109 
1110 	if (hotk->methods->mt_tled) {
1111 		asus_proc_add(PROC_TLED, &tled_proc_fops, mode, device);
1112 	}
1113 
1114 	if (hotk->methods->mt_bt_switch) {
1115 		asus_proc_add(PROC_BT, &bluetooth_proc_fops, mode, device);
1116 	}
1117 
1118 	/*
1119 	 * We need both read node and write method as LCD switch is also
1120 	 * accessible from the keyboard
1121 	 */
1122 	if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status) {
1123 		asus_proc_add(PROC_LCD, &lcd_proc_fops, mode, device);
1124 	}
1125 
1126 	if ((hotk->methods->brightness_up && hotk->methods->brightness_down) ||
1127 	    (hotk->methods->brightness_get && hotk->methods->brightness_set)) {
1128 		asus_proc_add(PROC_BRN, &brn_proc_fops, mode, device);
1129 	}
1130 
1131 	if (hotk->methods->display_set) {
1132 		asus_proc_add(PROC_DISP, &disp_proc_fops, mode, device);
1133 	}
1134 
1135 	return 0;
1136 }
1137 
asus_hotk_remove_fs(struct acpi_device * device)1138 static int asus_hotk_remove_fs(struct acpi_device *device)
1139 {
1140 	if (acpi_device_dir(device)) {
1141 		remove_proc_entry(PROC_INFO, acpi_device_dir(device));
1142 		if (hotk->methods->mt_wled)
1143 			remove_proc_entry(PROC_WLED, acpi_device_dir(device));
1144 		if (hotk->methods->mt_mled)
1145 			remove_proc_entry(PROC_MLED, acpi_device_dir(device));
1146 		if (hotk->methods->mt_tled)
1147 			remove_proc_entry(PROC_TLED, acpi_device_dir(device));
1148 		if (hotk->methods->mt_ledd)
1149 			remove_proc_entry(PROC_LEDD, acpi_device_dir(device));
1150 		if (hotk->methods->mt_bt_switch)
1151 			remove_proc_entry(PROC_BT, acpi_device_dir(device));
1152 		if (hotk->methods->mt_lcd_switch && hotk->methods->lcd_status)
1153 			remove_proc_entry(PROC_LCD, acpi_device_dir(device));
1154 		if ((hotk->methods->brightness_up
1155 		     && hotk->methods->brightness_down)
1156 		    || (hotk->methods->brightness_get
1157 			&& hotk->methods->brightness_set))
1158 			remove_proc_entry(PROC_BRN, acpi_device_dir(device));
1159 		if (hotk->methods->display_set)
1160 			remove_proc_entry(PROC_DISP, acpi_device_dir(device));
1161 	}
1162 	return 0;
1163 }
1164 
asus_hotk_notify(struct acpi_device * device,u32 event)1165 static void asus_hotk_notify(struct acpi_device *device, u32 event)
1166 {
1167 	/* TODO Find a better way to handle events count. */
1168 	if (!hotk)
1169 		return;
1170 
1171 	/*
1172 	 * The BIOS *should* be sending us device events, but apparently
1173 	 * Asus uses system events instead, so just ignore any device
1174 	 * events we get.
1175 	 */
1176 	if (event > ACPI_MAX_SYS_NOTIFY)
1177 		return;
1178 
1179 	if ((event & ~((u32) BR_UP)) < 16)
1180 		hotk->brightness = (event & ~((u32) BR_UP));
1181 	else if ((event & ~((u32) BR_DOWN)) < 16)
1182 		hotk->brightness = (event & ~((u32) BR_DOWN));
1183 
1184 	acpi_bus_generate_proc_event(hotk->device, event,
1185 				hotk->event_count[event % 128]++);
1186 
1187 	return;
1188 }
1189 
1190 /*
1191  * Match the model string to the list of supported models. Return END_MODEL if
1192  * no match or model is NULL.
1193  */
asus_model_match(char * model)1194 static int asus_model_match(char *model)
1195 {
1196 	if (model == NULL)
1197 		return END_MODEL;
1198 
1199 	if (strncmp(model, "L3D", 3) == 0)
1200 		return L3D;
1201 	else if (strncmp(model, "L2E", 3) == 0 ||
1202 		 strncmp(model, "L3H", 3) == 0 || strncmp(model, "L5D", 3) == 0)
1203 		return L3H;
1204 	else if (strncmp(model, "L3", 2) == 0 || strncmp(model, "L2B", 3) == 0)
1205 		return L3C;
1206 	else if (strncmp(model, "L8L", 3) == 0)
1207 		return L8L;
1208 	else if (strncmp(model, "L4R", 3) == 0)
1209 		return L4R;
1210 	else if (strncmp(model, "M6N", 3) == 0 || strncmp(model, "W3N", 3) == 0)
1211 		return M6N;
1212 	else if (strncmp(model, "M6R", 3) == 0 || strncmp(model, "A3G", 3) == 0)
1213 		return M6R;
1214 	else if (strncmp(model, "M2N", 3) == 0 ||
1215 		 strncmp(model, "M3N", 3) == 0 ||
1216 		 strncmp(model, "M5N", 3) == 0 ||
1217 		 strncmp(model, "S1N", 3) == 0 ||
1218 		 strncmp(model, "S5N", 3) == 0)
1219 		return xxN;
1220 	else if (strncmp(model, "M1", 2) == 0)
1221 		return M1A;
1222 	else if (strncmp(model, "M2", 2) == 0 || strncmp(model, "L4E", 3) == 0)
1223 		return M2E;
1224 	else if (strncmp(model, "L2", 2) == 0)
1225 		return L2D;
1226 	else if (strncmp(model, "L8", 2) == 0)
1227 		return S1x;
1228 	else if (strncmp(model, "D1", 2) == 0)
1229 		return D1x;
1230 	else if (strncmp(model, "A1", 2) == 0)
1231 		return A1x;
1232 	else if (strncmp(model, "A2", 2) == 0)
1233 		return A2x;
1234 	else if (strncmp(model, "J1", 2) == 0)
1235 		return S2x;
1236 	else if (strncmp(model, "L5", 2) == 0)
1237 		return L5x;
1238 	else if (strncmp(model, "A4G", 3) == 0)
1239 		return A4G;
1240 	else if (strncmp(model, "W1N", 3) == 0)
1241 		return W1N;
1242 	else if (strncmp(model, "W3V", 3) == 0)
1243 		return W3V;
1244 	else if (strncmp(model, "W5A", 3) == 0)
1245 		return W5A;
1246 	else if (strncmp(model, "R1F", 3) == 0)
1247 		return R1F;
1248 	else if (strncmp(model, "A4S", 3) == 0)
1249 		return A4S;
1250 	else if (strncmp(model, "F3Sa", 4) == 0)
1251 		return F3Sa;
1252 	else
1253 		return END_MODEL;
1254 }
1255 
1256 /*
1257  * This function is used to initialize the hotk with right values. In this
1258  * method, we can make all the detection we want, and modify the hotk struct
1259  */
asus_hotk_get_info(void)1260 static int asus_hotk_get_info(void)
1261 {
1262 	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
1263 	union acpi_object *model = NULL;
1264 	int bsts_result;
1265 	char *string = NULL;
1266 	acpi_status status;
1267 
1268 	/*
1269 	 * Get DSDT headers early enough to allow for differentiating between
1270 	 * models, but late enough to allow acpi_bus_register_driver() to fail
1271 	 * before doing anything ACPI-specific. Should we encounter a machine,
1272 	 * which needs special handling (i.e. its hotkey device has a different
1273 	 * HID), this bit will be moved. A global variable asus_info contains
1274 	 * the DSDT header.
1275 	 */
1276 	status = acpi_get_table(ACPI_SIG_DSDT, 1, &asus_info);
1277 	if (ACPI_FAILURE(status))
1278 		pr_warn("  Couldn't get the DSDT table header\n");
1279 
1280 	/* We have to write 0 on init this far for all ASUS models */
1281 	if (!write_acpi_int(hotk->handle, "INIT", 0, &buffer)) {
1282 		pr_err("  Hotkey initialization failed\n");
1283 		return -ENODEV;
1284 	}
1285 
1286 	/* This needs to be called for some laptops to init properly */
1287 	if (!read_acpi_int(hotk->handle, "BSTS", &bsts_result))
1288 		pr_warn("  Error calling BSTS\n");
1289 	else if (bsts_result)
1290 		pr_notice("  BSTS called, 0x%02x returned\n", bsts_result);
1291 
1292 	/*
1293 	 * Try to match the object returned by INIT to the specific model.
1294 	 * Handle every possible object (or the lack of thereof) the DSDT
1295 	 * writers might throw at us. When in trouble, we pass NULL to
1296 	 * asus_model_match() and try something completely different.
1297 	 */
1298 	if (buffer.pointer) {
1299 		model = buffer.pointer;
1300 		switch (model->type) {
1301 		case ACPI_TYPE_STRING:
1302 			string = model->string.pointer;
1303 			break;
1304 		case ACPI_TYPE_BUFFER:
1305 			string = model->buffer.pointer;
1306 			break;
1307 		default:
1308 			kfree(model);
1309 			model = NULL;
1310 			break;
1311 		}
1312 	}
1313 	hotk->model = asus_model_match(string);
1314 	if (hotk->model == END_MODEL) {	/* match failed */
1315 		if (asus_info &&
1316 		    strncmp(asus_info->oem_table_id, "ODEM", 4) == 0) {
1317 			hotk->model = P30;
1318 			pr_notice("  Samsung P30 detected, supported\n");
1319 			hotk->methods = &model_conf[hotk->model];
1320 			kfree(model);
1321 			return 0;
1322 		} else {
1323 			hotk->model = M2E;
1324 			pr_notice("  unsupported model %s, trying default values\n",
1325 				  string);
1326 			pr_notice("  send /proc/acpi/dsdt to the developers\n");
1327 			kfree(model);
1328 			return -ENODEV;
1329 		}
1330 	}
1331 	hotk->methods = &model_conf[hotk->model];
1332 	pr_notice("  %s model detected, supported\n", string);
1333 
1334 	/* Sort of per-model blacklist */
1335 	if (strncmp(string, "L2B", 3) == 0)
1336 		hotk->methods->lcd_status = NULL;
1337 	/* L2B is similar enough to L3C to use its settings, with this only
1338 	   exception */
1339 	else if (strncmp(string, "A3G", 3) == 0)
1340 		hotk->methods->lcd_status = "\\BLFG";
1341 	/* A3G is like M6R */
1342 	else if (strncmp(string, "S5N", 3) == 0 ||
1343 		 strncmp(string, "M5N", 3) == 0 ||
1344 		 strncmp(string, "W3N", 3) == 0)
1345 		hotk->methods->mt_mled = NULL;
1346 	/* S5N, M5N and W3N have no MLED */
1347 	else if (strncmp(string, "L5D", 3) == 0)
1348 		hotk->methods->mt_wled = NULL;
1349 	/* L5D's WLED is not controlled by ACPI */
1350 	else if (strncmp(string, "M2N", 3) == 0 ||
1351 		 strncmp(string, "W3V", 3) == 0 ||
1352 		 strncmp(string, "S1N", 3) == 0)
1353 		hotk->methods->mt_wled = "WLED";
1354 	/* M2N, S1N and W3V have a usable WLED */
1355 	else if (asus_info) {
1356 		if (strncmp(asus_info->oem_table_id, "L1", 2) == 0)
1357 			hotk->methods->mled_status = NULL;
1358 		/* S1300A reports L84F, but L1400B too, account for that */
1359 	}
1360 
1361 	kfree(model);
1362 
1363 	return 0;
1364 }
1365 
asus_hotk_check(void)1366 static int asus_hotk_check(void)
1367 {
1368 	int result = 0;
1369 
1370 	result = acpi_bus_get_status(hotk->device);
1371 	if (result)
1372 		return result;
1373 
1374 	if (hotk->device->status.present) {
1375 		result = asus_hotk_get_info();
1376 	} else {
1377 		pr_err("  Hotkey device not present, aborting\n");
1378 		return -EINVAL;
1379 	}
1380 
1381 	return result;
1382 }
1383 
1384 static int asus_hotk_found;
1385 
asus_hotk_add(struct acpi_device * device)1386 static int asus_hotk_add(struct acpi_device *device)
1387 {
1388 	acpi_status status = AE_OK;
1389 	int result;
1390 
1391 	pr_notice("Asus Laptop ACPI Extras version %s\n", ASUS_ACPI_VERSION);
1392 
1393 	hotk = kzalloc(sizeof(struct asus_hotk), GFP_KERNEL);
1394 	if (!hotk)
1395 		return -ENOMEM;
1396 
1397 	hotk->handle = device->handle;
1398 	strcpy(acpi_device_name(device), ACPI_HOTK_DEVICE_NAME);
1399 	strcpy(acpi_device_class(device), ACPI_HOTK_CLASS);
1400 	device->driver_data = hotk;
1401 	hotk->device = device;
1402 
1403 	result = asus_hotk_check();
1404 	if (result)
1405 		goto end;
1406 
1407 	result = asus_hotk_add_fs(device);
1408 	if (result)
1409 		goto end;
1410 
1411 	/* For laptops without GPLV: init the hotk->brightness value */
1412 	if ((!hotk->methods->brightness_get)
1413 	    && (!hotk->methods->brightness_status)
1414 	    && (hotk->methods->brightness_up && hotk->methods->brightness_down)) {
1415 		status =
1416 		    acpi_evaluate_object(NULL, hotk->methods->brightness_down,
1417 					 NULL, NULL);
1418 		if (ACPI_FAILURE(status))
1419 			pr_warn("  Error changing brightness\n");
1420 		else {
1421 			status =
1422 			    acpi_evaluate_object(NULL,
1423 						 hotk->methods->brightness_up,
1424 						 NULL, NULL);
1425 			if (ACPI_FAILURE(status))
1426 				pr_warn("  Strange, error changing brightness\n");
1427 		}
1428 	}
1429 
1430 	asus_hotk_found = 1;
1431 
1432 	/* LED display is off by default */
1433 	hotk->ledd_status = 0xFFF;
1434 
1435 end:
1436 	if (result)
1437 		kfree(hotk);
1438 
1439 	return result;
1440 }
1441 
asus_hotk_remove(struct acpi_device * device,int type)1442 static int asus_hotk_remove(struct acpi_device *device, int type)
1443 {
1444 	asus_hotk_remove_fs(device);
1445 
1446 	kfree(hotk);
1447 
1448 	return 0;
1449 }
1450 
1451 static const struct backlight_ops asus_backlight_data = {
1452 	.get_brightness = read_brightness,
1453 	.update_status  = set_brightness_status,
1454 };
1455 
asus_acpi_exit(void)1456 static void asus_acpi_exit(void)
1457 {
1458 	if (asus_backlight_device)
1459 		backlight_device_unregister(asus_backlight_device);
1460 
1461 	acpi_bus_unregister_driver(&asus_hotk_driver);
1462 	remove_proc_entry(PROC_ASUS, acpi_root_dir);
1463 
1464 	return;
1465 }
1466 
asus_acpi_init(void)1467 static int __init asus_acpi_init(void)
1468 {
1469 	struct backlight_properties props;
1470 	int result;
1471 
1472 	result = acpi_bus_register_driver(&asus_hotk_driver);
1473 	if (result < 0)
1474 		return result;
1475 
1476 	asus_proc_dir = proc_mkdir(PROC_ASUS, acpi_root_dir);
1477 	if (!asus_proc_dir) {
1478 		pr_err("Unable to create /proc entry\n");
1479 		acpi_bus_unregister_driver(&asus_hotk_driver);
1480 		return -ENODEV;
1481 	}
1482 
1483 	/*
1484 	 * This is a bit of a kludge.  We only want this module loaded
1485 	 * for ASUS systems, but there's currently no way to probe the
1486 	 * ACPI namespace for ASUS HIDs.  So we just return failure if
1487 	 * we didn't find one, which will cause the module to be
1488 	 * unloaded.
1489 	 */
1490 	if (!asus_hotk_found) {
1491 		acpi_bus_unregister_driver(&asus_hotk_driver);
1492 		remove_proc_entry(PROC_ASUS, acpi_root_dir);
1493 		return -ENODEV;
1494 	}
1495 
1496 	memset(&props, 0, sizeof(struct backlight_properties));
1497 	props.type = BACKLIGHT_PLATFORM;
1498 	props.max_brightness = 15;
1499 	asus_backlight_device = backlight_device_register("asus", NULL, NULL,
1500 							  &asus_backlight_data,
1501 							  &props);
1502 	if (IS_ERR(asus_backlight_device)) {
1503 		pr_err("Could not register asus backlight device\n");
1504 		asus_backlight_device = NULL;
1505 		asus_acpi_exit();
1506 		return -ENODEV;
1507 	}
1508 
1509 	return 0;
1510 }
1511 
1512 module_init(asus_acpi_init);
1513 module_exit(asus_acpi_exit);
1514