OpenCores
URL https://opencores.org/ocsvn/test_project/test_project/trunk

Subversion Repositories test_project

[/] [test_project/] [trunk/] [linux_sd_driver/] [drivers/] [acpi/] [asus_acpi.c] - Blame information for rev 62

Details | Compare with Previous | View Log

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

powered by: WebSVN 2.1.0

© copyright 1999-2025 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.